.NET Application Interface
An application developer can implement IAnalysisService interface or extend the AnalysisServiceDefaultImpl abstract class to plug in a new analysis software into the Analytics Manager framework.
Based on your requirement, along with IAnalysisService, you can also inherit the IAnalysisTwxClientRequired or IAnalysisMessageSink interfaces.
* 
If you want to use additional helper methods apart from the methods defined in IAnalysisService, IAnalysisTwxClientRequired, and the IAnalysisMessageSink application interfaces, you can use the helper methods in the AnalysisServiceHelper class.
The IAnalysisService.cs Application Interface
The following methods in the IAnalysisService.cs application interface are used to manage models and jobs within the Analytics Manager framework:
namespace AnalysisServices.Contracts
{
public interface IAnalysisService : IDisposable
{
/// <summary>
/// Returns information about the application that is being exposed over REST.
/// </summary>
/// <returns></returns>
ServiceInfo GetServiceInfo();

/// <summary>
/// Returns a list of resources that the application supports.
/// An empty list if none (ideally this should never be the case)
/// </summary>
/// <returns></returns>
List<Model> GetModels();

/// <summary>
/// Called when all models are to be deleted.
/// </summary>
void DeleteAllModels();

/// <summary>
/// Called by the Agent to get an instance of the model object given the model name.
/// </summary>
/// <param name="modelName"></param>
Model GetModel(string modelName);

/// <summary>
/// Deletes a specified model
/// </summary>
/// <param name="modelName"></param>
void DeleteModel(string modelName);

/// <summary>
/// Return the inputs that are required by this model as a
/// tree structure contained in the ParamField array
/// </summary>
/// <param name="modelName"></param>
/// <returns></returns>
ParamField[] GetInputData(String modelName);

/// <summary>
/// Return the outputs that are generated by as a result of running a job
/// based on the given model as a tree structure contained in the ParamField array
/// </summary>
/// <param name="modelName"></param>
/// <returns></returns>
ParamField[] GetResultData(String modelName);

/// <summary>
/// This method is used primarily used to run jobs that are discrete i.e to be executed only
/// once as opposed to a continuous job which can continue to run indefinitely.
/// A discrete job can be run asynchronusly (in which case the method returns right away with
/// an inprocess status)or it can be run synchronously.
/// The implementation must inspect the flags on the job to determine
/// if the job is synchronous or asynchronous.
/// </summary>
/// <param name="jb"></param>
/// <returns></returns>
Job PostJob(Job jb);

/// <summary>
/// Return a list of jobs that are being held by the AnalysisService.
/// The jobs could be completed job,continuous jobs or inprogress and failed jobs.
/// </summary>
/// <returns></returns>
List<Job> GetJobs();

/// <summary>
/// Delete all jobs that are being held in the AnalysisService. If any jobs are running
/// they should be stopped and removed.
/// </summary>
void DeleteJobs();

/// <summary>
/// Get a job with the given job id.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Job GetJob(string id);

/// <summary>
/// delete the given job , stop the job if it is still running
/// </summary>
/// <param name="id"></param>
void DeleteJob(string id);

/// <summary>
/// Called by the AnalysisAgent to deploy a model file if the
/// analysis provider supports file based models.
/// </summary>
/// <param name="fileName"></param>
/// <param name="m"></param>
void DeployModel(Model m);

/// <summary>
/// a reference to a method that should be called by the analysis provider
/// to provide job result updates for Async and continuous jobs.
/// </summary>
/// <param name="jb"></param>
void SetJobUpdateDelegate(JobUpdateDelegate jud);

/// <summary>
/// a reference to a method that should be called by the analysis provider
/// to provide job result updates for Async and continuous jobs.
/// </summary>
/// <param name="jb"></param>
void SetJobFileUploadDelegate(JobFileUploadDelegate jfud);

/// <summary>
/// Called by the AnalysisSDKAgent when new input needs to be provided
/// for a continuous job .
/// </summary>
/// <param name="jobId"></param>
/// <param name="mapProps"></param>
void UpdateJob(string jobId, string json);

/// <summary>
/// The StartJob method is called by the AnalysisSDK to start a continuous job
/// This method can be used by the implementation to set up data structures for
/// executing the job, the updates to inputs are provided via the UpdateJob method.
/// This happens whenever a triggering configuration is enabled.
/// </summary>
/// <param name="jb"></param>
/// <returns></returns>
Job StartJob(Job jb);

/// <summary>
/// This method is called by the AnalysisSDKAgent when a job needs to be stopped.
/// This is called whenever a triggering configuration is disabled or deleted.
/// </summary>
/// <param name="jobid"></param>
/// <returns></returns>
bool StopJob(string jobid);

/// <summary>
/// The AnalysisProvider should implement this to dispose of resources.
/// It is not currently not invoked directly by the analysis service.
/// </summary>
/// <param name="disposing"></param>
void Dispose(bool disposing);
}

//get the number of queued jobs. If the analysis service suports queuing
//then this should return the number of jobs in the queue .
int GetQueuedJobsCount();

void Initialize(Dictionary<string,object> dict );

//return value > 0 if pid of the analysis service is known
ulong GetPID();

//abort a job
bool AbortJob(string jobId); /// <summary>

/// The delegate method that is implemented by the AnalysisSDK and set using the SetJobDelegate
/// method. The application must call this method to report the progress and status of the job.
/// </summary>
/// <param name="jobid"></param>
/// <param name="status"></param>
/// <param name="xml"></param>
/// <param name="files"></param>
public delegate void JobUpdateDelegate(Job jb);

/// <summary>
/// The delegate method that is implemented by the AnalysisSDK and set using the JobFileUploadDelegate
/// method. The application must call this method upload files asynchronously without changing the job state.
/// </summary>
/// <param name="jobid"></param>
/// <param name="status"></param>
/// <param name="xml"></param>
/// <param name="files"></param>
public delegate void JobFileUploadDelegate(string jobId,string []files);
}
The following table provides information about the methods used to manage models:
Method
Description
GetServiceInfo
This method returns information about the application.
GetModels
This method returns a list of models that are currently deployed in the compute engine.
DeleteAllModels
This method deletes all deployed models.
DeleteModel
This method deletes the specified model.
DeployModel
When the DeployModel method is called by the SDK, an instance of the Model class is passed.
The SDK populates the ResourceName, FleName, and includedFiles fields.
The FileName and includedFiles are local files. includedFiles is populated when the model contains a single primary file and other secondary files. The service should read the files, validate them for correctness, and then build the input and output data structures. The data structures should be stored on the model object if they can be generated immediately. These data structures are represented by the InputData and OutputData fields of type ParamField respectively.
See the structures of Model and ParamField below the table.
GetInputData
This method is called by the SDK to retrieve the ParamField array that represents the input data structure.
GetResultData
This method is called by the SDK to retrieve the ParamField array that represents the result data structure.
The data structure of Model is as follows:
public class Model
{
public string ResourceName { get; set; }
public string ResourceType { get; set; }
public bool Available { get; set; }
public long UpdateTimestamp { get; set; }
public ParamField[] InputData { get; set; }
public ParamField[] ResultData { get; set; }
public long CreateTimestamp { get; set; }
public string FileName { get; set; }
public string[] includedFiles { get; set; }
}
The data structure of ParamField is as follows:
public class ParamField
{
//special constructor to provide default initialization for the given fields
public string IDField { get; set; }
public string Description { get; set; }
public string ParentIDField { get; set; }
public string MaxValue { get; set; }
public string Unit { get; set; }
public int Depth { get; set; }
public string DefaultValue { get; set; }
public string BaseType { get; set; }
public int Ordinal { get; set; }
public string MinValue { get; set; }
public bool isPrimary { get; set; }

public ParamField(string idfield,string parentid,int depth,int ordinal,string basetype)
{
IDField = idfield;
ParentIDField = parentid;
Depth = depth;
Ordinal = ordinal;
BaseType = basetype;
Description = "default description";
}
}
In ParamField, the IDField, ParentIDField, Depth, Ordinal, and BaseType are mandatory fields.
The data structure arrays can be used to represent a flat structure of inputs and outputs as well as hierarchical ones.
Flat structure — To perform an analysis that requires two inputs, a and b, and an output, c, the ParamField should look like this:
ParamField[] additionInputs = new ParamField[2];
ParamField[] additionResults = new ParamField[1];
additionInputs[0] = new ParamField("a",null,1,1,"NUMBER") ;
additionInputs[1] = new ParamField("b", null, 1, 1, "NUMBER");
additionResults[0] = new ParamField("c", null, 1, 1, "NUMBER");
Hierarchical structure — To perform an analysis that requires inputs in the form of a matrix or a table with a number of columns, the ParamField should look like this:
ParamField[] table = new ParamField[4];
table[0] = new ParamField("table", null, 1, 1, "INFOTABLE");
table[1] = new ParamField("col1", "table", 2, 1, "NUMBER");
table[2] = new ParamField("col2", "table", 2, 2, "NUMBER");
table[3] = new ParamField("col3", "table", 2, 3, "NUMBER");
Here, we have created a table with 3 columns. The second parameter in the constructor for the column fields is the parentid field. In this example, the parentid is table. The third parameter, ordinal determines the order in which the fields should appear at a given level. In this example, col1 is the first column because the third parameter is 1. The ordinals for col2 and col3 are 2 and 3 respectively.
The following table provides information about the methods used to manage jobs:
Method
Description
PostJob
This method is called by the SDK to execute discrete synchronous and asynchronous jobs. For synchronous jobs, the service implementation populates the job result immediately. For asynchronous jobs, the service notifies the SDK about the update by invoking the JobUpdateDelegate method.
StartJob
This method is used to start a continuous job. It initializes data structures and threads to handle continuous jobs.
StopJob
This method is used to stop a continuous job. It also performs cleanup.
UpdateJob
This method is used to send updated inputs in the form of a JSON for a simulation job. This method notifies the SDK about the update by invoking the JobUpdateDelegate method.
* 
Input and output data is exchanged between the service and the SDK in JSON format strings. The input is always a JSON array serialized as a string and the output should be a JSON array. Sometimes, the JSON array might contain a single object.
GetJobs
This method gets a list of all jobs regardless of the state.
DeleteJobs
This method deletes all jobs from the system.
GetJob
This method returns information about the requested job.
DeleteJob
This method deletes the job with the given ID.
SetJobUpdateDelegate
This method is used to set service implementation of the JobUpdateDelegate method at startup. The service must save these instances for later use.
JobUpdateDelegate
First, populate an array of file paths generated on to the job data structure and then invoke the JobUpdateDelegate method. The files are uploaded asynchronously and they are associated with the job.
SetJobFileUploadDelegate
This method is used to set service implementation of the JobFileUploadDelegate method at startup. The service must save these instances for later use.
JobFileUploadDelegate
Invoke the JobFileUploadDelegate to upload files asynchronously and associate them with the job. Use this method if there is no job status update or files are generated asynchronously and must be uploaded after job completion.
The data structure of Job is as follows:
public abstract class Job
{
public bool isContinuous { get; set; } // true if this is a simulation job
public string id { get; set; } //id of the job, this can be changed by the service
public string key { get; set; } // key is usually the same as the id.
public string resourceServiceProvider { get; set; } //the provider that is invoking the job
public string resourceName { get; set; } //the name of resource on the server
public string status { get; set; } //the status of the job
public long updateTimeStamp { get; set; }
public bool isAsync { get; set; } // true if this is a asynchronous job
public string inputParams { get ; set ; } //the input params as a json string
public string results { get ; set ; } //results as json string .
public string assignedTo { get; set; } //the client to which this job is assigned.
public long jobStartTime { get; set; }
public long createTimeStamp { get; set; }
public string[] resultFiles { get ; set ; } //if result files are generated as a result of job execution
public string errorCode { get; set; } //application specific error code to be set by the service
public string errorMessage { get; set; } //the localized error message to be set by the service if an error occurs.
public string errorDetails { get; set; } // the exception or heap dump if the service wishes to report it.
}
Input and output data is exchanged between the service and the Analysis SDK in JSON format strings. The input is a JSON array that is serialized as a string and the output should be a JSON array.
For example:
For a flat structure of inputs, the input JSON passed for a discrete job through the PostJob method is [{"a":10.0,"b":20.0}], and the input JSON for a continuous job is {"a":10.0,"b":20.0}.
For a hierarchical structure, the input JSOn is passed as a string like this: [{"table":[{"col1":100.0,"col2":100.0,"col3":100.0},{"col1":200.0,"col2":200.0,"col3":200.0}]}]
This is a table with 2 rows and 3 columns.
The following table provides information about the cleanup method:
Method
Description
Dispose
This method is used for cleanup and it must be invoked in the driver program that constructs the IAnalysisService implementation and registers the service with the SDK. The SDK never calls this method.
The IAnalysisTwxClientRequired Application Interface
The following methods in the IAnalysisTwxClientRequired application interface are used to define contracts to get access to the ThingWorx server:
namespace AnalysisServices.Contracts
{
/// <summary>
/// Contracts for getting access to thingworx connectedclient in order to invoke services on thingworx server
/// </summary>
interface IAnalysisTwxClientRequired
{
//Set the connectedthinglient instance
// <param name="client"></param>
void SetClient(ConnectedThingClient client);

/// <summary>
/// Returns the ConnectedClient instance in order to get access to the thingworx server
/// </summary>
/// <returns></returns>
ConnectedThingClient GetClient();
}
}
Use the GetClient method to access the ThingWorx server by using the same credentials that the agent uses to access the ThingWorx server.
The IAnalysisMessageSink Application Interface
The following methods in the IAnalysisMessageSink application interface are used to define contracts to process messages from the ThingWorx server:
namespace AnalysisServices.Contracts
{
/// <summary>
/// Interface defining contracts for processing messages from thingworx server
/// </summary>
interface IAnalysisMessageSink
{
/// <summary>
/// processes the incoming message from twx server
/// </summary>
/// <param name="payLoad"></param>
void processMessage(JObject payLoad);
}
}
The AnalysisServiceDefaultImpl Application Interface
If you want to implement all the interfaces, you can use the AnalysisServiceDefaultImpl abstract class. This class implements all the above specified interfaces.
Was this helpful?