REST and OData

Overview

GENESIS64 includes a service called “ICONICS OData Connector Point Manager” (or OData Connector for short) which layers a REST API on top of FrameWorX Server and optionally also an OData v4 compliant Endpoint.

 

NOTE: ICONICS OData Connector is now considered a legacy solution. It has been superseded by the Web API provider in the Workbench. The following information will remain available for users of the OData Connector. The OData Connector is disabled by default for security reasons. Users who wish to continue using it can change the startup type of the ICONICS OData Connector Point Manager (FwxODataService) service from Disabled to Automatic or Automatic (Delayed).

 

 

The REST API can be used by clients to request data from FrameWorX without the constraint of using .NET and the FrameWorX client API.

Security

By default, the OData Connector endpoint is not secured and allows unauthenticated HTTP requests. Security between the OData Connector and FrameWorX is regulated by standard ICONICS Security and, by default, the OData Connector requests to FrameWorX are executed under the default security group.

 

With this in mind, securing the OData Connector consists of two tasks:

  1. Securing access from the OData Connector to FrameWorX. This defines what data the OData Connector can request from FrameWorX.

  2. Securing access to the HTTP endpoints. This prevents unauthenticated access to the REST API.

To secure requests from the OData Connector to FrameWorX we have two possible options. Since the OData Connector uses the default security group, the first option is to configure security for the default group as required. The second option is to configure the OData Connector to use a specific ICONICS security account when requesting data from FrameWorX.

 

This is done via the Platform Services Configuration utility, under the Passwords tab:

 

 

In the picture above, we are instructing the OData Connector to use the odata user account when requesting data from FrameWorX. Security permissions for the odata user can be then configured appropriately using the ICONICS Security Server configurator. Please note that the user name does not have to be necessarily odata and can be configured as desired.

 

Securing access to the REST endpoints is again done via the Platform Services Configuration utility. Under the Point Managers tab, locate the OData Connector Point Manager to access its settings:

 

 

The two settings used to configure access to the REST API are RequireAuthentication and Impersonate. As the name suggests, after changing the value of RequireAuthenticationto true, the OData Connector will start requesting authentication credentials to access the endpoints. Authentication is implemented as HTTP basic access authentication.

 

IMPORTANT: since basic access authentication exchanges credentials between client and server in clear text, it is highly recommended to configure the OData Connector to use HTTPS if authentication is enabled. Please see the Endpoint Protocol section below for more details.

 

When authentication is enabled, the OData Connector validates the received credentials against the ICONICS Security Server: if the credentials are valid, the request is processed - if not, the client receives an HTTP status code of 401 – Unauthorized.

 

Up to this point, credentials passed by the client are only used to authorize access to the REST endpoints. Which user account is ultimately used by the OData Connector to request data from FrameWorX is configured by the Impersonate setting. When Impersonate is set to false (the default value) the OData Connector will request data from FrameWorX under the default security group or, if configured, the ODATA -> FWX user account. When Impersonate is set to true, the OData Connector will request data from FrameWorX by logging in with Security using the credentials passed by the client.

Endpoint Protocol

By default, the OData Connector is configured to run on HTTP over port 80. To use HTTPS, it is necessary to install a valid certificate that can be used for this purpose on the machine where the OData Connector is installed. The certificate needs then to be bound to the port that will be used by the OData Connector using the netsh utility via command line:

 

netsh http add sslcert ipport=<IP:Port> certhash=<thumbprint> appid={768c5052-9306-4d2b-ace2-8aa3002bf70a}

 

Where <IP:Port> is the IP address and the port where the certificate will be bound to, and <thumbprint> is the certificate thumbprint.

GENESIS64 also installs a batch file that can create a self-signed certificate and bind it to the selected port for testing purposes. The batch file is located at:

C:\Program Files\ICONICS\GENESIS64\InstCert\ODataCert.bat

 

The following command creates a new certificate and binds is to the specified port; if port is not specified then the default 443 is used:

> ODataCert -i [port]

The following command unbinds and deletes an existing certificate:

> ODataCert -u

 

Once the certificate has been installed and bound to the desired port, the OData Connector can be configured using the EndpointProtocol, EndpointPort and EndpointMachineNamefields in the Platform Services Configuration utility:

 

·      

NOTE: changing any of the settings under the Point Managers tab in the Platform Services Configuration Utility requires a restart of the OData Connector Point Manager Service.

REST Endpoints

The OData Connector exposes several REST endpoints that allow access to different types of FrameWorX Server data. Endpoints can be accessed both via HTTP GET or HTTP POST, where POST usually allows submitting more than one request at the time, thus increasing efficiency.

 

In the next sections, we are going to describe the most commonly used endpoints. For the complete list – including parameter descriptions and examples - the OData Connector exposes online help pages at:

http://localhost/ODataConnector/rest/Help/Html?method=get

http://localhost/ODataConnector/rest/Help/Html?method=post

Real-time Data

This endpoint allows to read the current value of data points or to write to data points.

 

Reading real-time values (HTTP GET)

 

The RealtimeData endpoint accepts the name of the point to read as a query string parameter:

GET http://localhost/ODataConnector/rest/RealtimeData?PointName=svrsim:sine double med -100 100 HTTP/1.1

Host: localhost

Connection: keep-alive

 

The response is a single-value array containing a JSON object carrying the name, value, timestamp and quality of the requested point:

HTTP/1.1 200 OK

Content-Length: 136

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Tue, 22 May 2018 19:16:34 GMT

 

[{

    "PointName": "svrsim:sine double med -100 100",

    "Value": 66.498011645316581,

    "Timestamp": "2018-05-22T19:16:34.2406812+00:00",

    "Quality": 0

}]

 

Reading Real-time Values (HTTP POST)

 

For increased efficiency, it is possible to request multiple points at the same time from the RealtimeData endpoint when using HTTP POST. The POST body must contain a JSON object with a single property called PointName, which carries an array of point names:

POST http://localhost/ODataConnector/rest/RealtimeData HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 89

Cache-Control: no-cache

Content-Type: application/json

 

{ "PointName": [ "svrsim:sine double med -100 100", "svrsim:ramp double med -100 100" ] }

 

The response is an array containing a JSON object for each point requested, each object carrying the name, value, timestamp and quality of the requested point:

HTTP/1.1 200 OK

Content-Length: 272

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Access-Control-Allow-Origin: *

Access-Control-Expose-Headers: *

Date: Tue, 22 May 2018 19:24:56 GMT

 

[{

    "PointName": "svrsim:sine double med -100 100",

    "Value": -70.186717085644588,

    "Timestamp": "2018-05-22T19:24:55.8691661+00:00",

    "Quality": 0

}, {

    "PointName": "svrsim:ramp double med -100 100",

    "Value": 25.265000000037261,

    "Timestamp": "2018-05-22T19:24:56.3694864+00:00",

    "Quality": 0

}]

 

Writing Real-time Values

 

One or more points can be written to using the RealtimeData/Write endpoint via HTTP POST. The POST body must contain a JSON array of objects containing the name of the point and the value to write:

POST http://localhost/ODataConnector/rest/RealtimeData/Write HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 105

Cache-Control: no-cache

Content-Type: application/json

 

[ { "PointName": "svrsim:out int32", "Value": 42 }, { "PointName": "svrsim:out double", "Value": 42.0 } ]

 

The response is an array containing a JSON object for each write operation, each object carrying the point name, whether the write succeeded and an error message in the case the write failed:

HTTP/1.1 200 OK

Content-Length: 138

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 13:43:58 GMT

 

[{

    "PointName": "svrsim:out int32",

    "Success": true,

    "ErrorMessage": null

}, {

    "PointName": "svrsim:out double",

    "Success": true,

    "ErrorMessage": null

}]

Dataset Data

This endpoint allows to read dataset points, like the ones exposed by GridWorX. The DatasetData endpoint accepts the name of the point to read as a query string parameter. If the dataset point itself has parameters, the parameter values can be included in the point name:

GET http://localhost/ODataConnector/rest/DatasetData?PointName=db:Northwind.OrdersByCustomerID<@CustomerID=ALFKI> HTTP/1.1

Host: localhost

Connection: keep-alive

Cache-Control: no-cache

Content-Type: application/json

 

The response is an array of JSON objects, the objects have properties with the same name and equivalent data type as the columns of the dataset:

HTTP/1.1 200 OK

Content-Length: 2051

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 13:54:45 GMT

 

[{

    "OrderID": 10643,

    "CustomerID": "ALFKI",

    "EmployeeID": 6,

    "OrderDate": "1997-08-25T00:00:00",

    "RequiredDate": "1997-09-22T00:00:00",

    "ShippedDate": "1997-09-02T00:00:00",

    "ShipVia": 1,

    "Freight": 29.4600,

    "ShipName": "Alfreds Futterkiste",

    "ShipAddress": "Obere Str. 57",

    "ShipCity": "Berlin",

    "ShipRegion": null,

    "ShipPostalCode": "12209",

    "ShipCountry": "Germany"

}, 

...

{

    "OrderID": 11011,

    "CustomerID": "ALFKI",

    "EmployeeID": 3,

    "OrderDate": "1998-04-09T00:00:00",

    "RequiredDate": "1998-05-07T00:00:00",

    "ShippedDate": "1998-04-13T00:00:00",

    "ShipVia": 1,

    "Freight": 1.2100,

    "ShipName": "Alfred's Futterkiste",

    "ShipAddress": "Obere Str. 57",

    "ShipCity": "Berlin",

    "ShipRegion": null,

    "ShipPostalCode": "12209",

    "ShipCountry": "Germany"

}]

Historical Data

This endpoint allows to read historical data, for example data that has been logged in Hyper Historian.

 

Reading Raw Historical Data (HTTP GET)

 

The HistoricalData endpoint accepts the point name, start time and end time as query string parameters:

GET http://localhost/ODataConnector/rest/HistoricalData?PointName=hh:\Configuration\Signals:SineFast&StartDate=2018-05-23T11:25:00-0400&EndDate=2018-05-23T11:27:00-0400 HTTP/1.1

Host: localhost

Connection: keep-alive

Cache-Control: no-cache

Content-Type: application/json

 

The response is an array of JSON objects, each object representing a sample carrying the name, value, timestamp and quality of the requested point:

HTTP/1.1 200 OK

Content-Length: 65103

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 15:28:08 GMT

 

[{

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 45.894377320185733,

    "Timestamp": "2018-05-23T15:25:00.011+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 52.433772224711312,

    "Timestamp": "2018-05-23T15:25:00.26+00:00",

    "Quality": 0

},

...

{

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 44.669442277460647,

    "Timestamp": "2018-05-23T15:26:59.963+00:00",

    "Quality": 0

}]

 

Reading Aggregated Historical Data (HTTP GET)

 

When also specifying an aggregate name and a processing interval as query string parameters, the HistoricalData endpoint returns aggregated data:

GET http://localhost/ODataConnector/rest/HistoricalData?PointName=hh:\Configuration\Signals:SineFast&StartDate=2018-05-23T11:27:00-0400&EndDate=2018-05-23T11:30:00-0400&AggregateName=Average&ProcessingInterval=00:01:00 HTTP/1.1

Host: localhost

Connection: keep-alive

Cache-Control: no-cache

Content-Type: application/json

 

The response is an array of JSON objects, each object representing a sample carrying the name, value, timestamp and quality of the requested point:

HTTP/1.1 200 OK

Content-Length: 397

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 15:33:42 GMT

 

[{

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 50.000000000059146,

    "Timestamp": "2018-05-23T15:27:00+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 50.000000000059146,

    "Timestamp": "2018-05-23T15:28:00+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:SineFast",

    "Value": 50.019209953947929,

    "Timestamp": "2018-05-23T15:29:00+00:00",

    "Quality": 0

}]

 

Reading Historical Data (HTTP POST)

 

For increased efficiency, it is possible to request more than one point at the same time using HTTP POST. The HistoricalData endpoint can be used or, if the goal is just to consume the data in a forward-only way, there is an even more efficient endpoint at ForwardOnlyHistoricalData that allows to read large amounts of historical data with a lower memory impact on the OData Connector Service.

 

To read historical data using the standard HistoricalData endpoint the POST body must be a JSON object containing all the relative parameters. The PointName property must be an array and may contain multiple point names – when multiple point names are specified, the response will contain all the samples for the first point, followed by all the samples for the second point and so on. When omitting the AggregateName and ProcessingInterval parameters the response will contain raw data samples, otherwise aggregated samples will be returned:

 

POST http://localhost/ODataConnector/rest/HistoricalData HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 171

Cache-Control: no-cache

 

{ "PointName": [ "hh:\\Configuration\\Signals:Sine", "hh:\\Configuration\\Signals:Ramp" ], "StartDate": "2018-05-23T11:25:00-0400", "EndDate": "2018-05-23T11:27:00-0400" }

 

The response is an array of JSON objects, each object representing a sample carrying the name, value, timestamp and quality of the requested points:

HTTP/1.1 200 OK

Content-Length: 124896

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 19:00:09 GMT

 

[{

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 2.1995849579170397,

    "Timestamp": "2018-05-23T15:25:00.011+00:00",

    "Quality": 0

},

...

{

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 2.1279758529826935,

    "Timestamp": "2018-05-23T15:26:59.963+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:Ramp",

    "Value": 29.738333333333333,

    "Timestamp": "2018-05-23T15:25:00.011+00:00",

    "Quality": 0

},

...

{

    "PointName": "hh:\\Configuration\\Signals:Ramp",

    "Value": 29.659999999999997,

    "Timestamp": "2018-05-23T15:26:59.963+00:00",

    "Quality": 0

}]

 

The ForwardOnlyHistoricalData endpoint allows to read historical data in a paged, forward-only way that is more efficient. To request data from this endpoint, a client-generated unique ID string must be passed in the query string via the uid parameter. The query string also accepts two paging parameters called $top and $skip. $top specifies how many samples to return in the page response, while $skip specifies how many samples to skip. The POST body is identical as the one used for the HistoricalData endpoint, with the difference that it is only required for the first POST request – all requests for subsequent pages using the same uid can omit the POST body.

 

The following request asks the server for aggregated data for a 5 minutes interval with a 1-minute processing interval for two tags (a total of 10 samples). The first request asks for a page size of 2 samples:

POST http://localhost/ODataConnector/rest/ForwardOnlyHistoricalData?uid=00001&$skip=0&$top=2 HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 229

Cache-Control: no-cache

Content-Type: application/json

 

{ "PointName": [ "hh:\\Configuration\\Signals:Sine", "hh:\\Configuration\\Signals:Ramp" ], "StartDate": "2018-05-23T11:25:00-0400", "EndDate": "2018-05-23T11:30:00-0400", AggregateName: "Average", ProcessingInterval: "00:01:00" }

 

And the server responds with the first two samples:

HTTP/1.1 200 OK

Content-Length: 257

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 19:25:04 GMT

 

[{

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 50.000000000004746,

    "Timestamp": "2018-05-23T15:25:00+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 49.871412141169763,

    "Timestamp": "2018-05-23T15:26:00+00:00",

    "Quality": 0

}]

 

The next request skips the first two samples and requests the next two. Note that the POST body is empty – the server knows which historical reader the request belongs to thanks to the uid parameter:

POST http://localhost/ODataConnector/rest/ForwardOnlyHistoricalData?uid=00001&$skip=2&$top=2 HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 0

Cache-Control: no-cache

Content-Type: application/json

 

And we get the next two samples:

HTTP/1.1 200 OK

Content-Length: 257

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 19:25:16 GMT

[{

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 50.000000000011866,

    "Timestamp": "2018-05-23T15:27:00+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:Sine",

    "Value": 50.000000000011866,

    "Timestamp": "2018-05-23T15:28:00+00:00",

    "Quality": 0

}]

 

We keep requesting pages until the last page:

POST http://localhost/ODataConnector/rest/ForwardOnlyHistoricalData?uid=00001&$skip=8&$top=2 HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 0

Cache-Control: no-cache

Content-Type: application/json

 

And we receive the last two samples:

HTTP/1.1 200 OK

Content-Length: 256

Content-Type: application/json; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Wed, 23 May 2018 19:25:30 GMT

 

[{

    "PointName": "hh:\\Configuration\\Signals:Ramp",

    "Value": 49.86833333333324,

    "Timestamp": "2018-05-23T15:28:00+00:00",

    "Quality": 0

}, {

    "PointName": "hh:\\Configuration\\Signals:Ramp",

    "Value": 50.211805555555571,

    "Timestamp": "2018-05-23T15:29:00+00:00",

    "Quality": 0

}]

OData Endpoint

The OData Connector also implements an OData v4 compliant endpoint. From www.odata.org, OData is:

[…] an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs. OData helps you focus on your business logic while building RESTful APIs without having to worry about the various approaches to define request and response headers, status codes, HTTP methods, URL conventions, media types, payload formats, query options, etc.

OData services are described in terms of an Entity Model, which means entities and entity sets describe the endpoints exposed by the service. For this reason, the ICONICS OData Connector has been built to expose entities from AssetWorX via OData, namely Equipment (assets) and Equipment Properties (asset properties).

 

Equipment in AssetWorX can now be exposed via OData with a simple checkbox in Workbench Desktop:

 

 

If an Equipment is configured not to be exposed via OData (the OData checkbox is unchecked), children Equipment will also not be exposed. Equipment Properties have a similar configuration:

 

Enabling the OData Endpoint

To enable the OData Endpoint, open the Platform Services Configuration Utility, switch to the Point Managers tab and select the OData Connector Point Manager from the list. Identify the EnableODataEndpoint parameter and change it to true:

 

Configuration Parameters

The parameters exposed by the Platform Services Configuration utility can be used to configure the OData endpoint behavior. Any change to the configuration parameters requires a restart of the OData Connector Point Manager Service after the changes have been applied.

Runtime

Since goals of OData as a protocol include interoperability and standardization of the REST API, all the information required to query the ICONICS OData Connector OData endpoint is exposed via the service’s metadata at http://localhost/ODataConnector/odata/$metadata.

 

The entity model is relatively simple with only three entities, the main one being the Asset:

<EntityType Name="Asset" OpenType="true">

  <Key>

    <PropertyRef Name="Id" />

  </Key>

  <Property Name="Id" Type="Edm.Int32" Nullable="false" />

  <Property Name="ParentId" Type="Edm.Int32" />

  <Property Name="Name" Type="Edm.String" />

  <Property Name="DisplayName" Type="Edm.String" />

  <Property Name="CustomIdentifier" Type="Edm.String" />

  <Property Name="Path" Type="Edm.String" />

  <Property Name="Class" Type="Edm.String" />

  <Property Name="HasDatasetProperties" Type="Edm.Boolean" Nullable="false" />

  <Property Name="HasHistoryProperties" Type="Edm.Boolean" Nullable="false" />

  <NavigationProperty Name="DatasetProperties" Type="Collection(Model.DatasetProperty)" />

  <NavigationProperty Name="HistoryProperties" Type="Collection(Model.HistoryProperty)" />

</EntityType>

 

Asset has a few explicit properties (like Name, Display Name, etc.) but it is also defined as an open type which, in OData, allows instances of the type to expose dynamic properties that are not explicitly defined in the entity type definition. By taking advantage of this, the OData Connector can “inline” Equipment Properties as actual properties of asset entities.

 

Let’s consider, for example, the default AssetWorX configuration that is installed with GENESIS64. The Heating Control, Ingredient Selector and Pump Control assets children of Ingredient Charger all expose different properties:

 

 

When queried via the OData endpoint, these assets are returned as:

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets",

  "value": [

    {

      "Id": 190,

      "ParentId": 189,

      "Name": "Ingredient selector",

      "DisplayName": "",

      "CustomIdentifier": "190",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Ingredient selector/",

      "Class": "Ingredient selector valves/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "SelectedMaterial": -50.90414157503709,

      "SwitchingInProgress": -50.90414157503709

    },

    {

      "Id": 191,

      "ParentId": 189,

      "Name": "Pump control",

      "DisplayName": "",

      "CustomIdentifier": "191",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Pump control/",

      "Class": "Variable speed pump/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "PumpHealth": -50.90414157503709,

      "CurrentFlow": -50.90414157503709,

      "TargetFlow": -50.90414157503709

    },

    {

      "Id": 192,

      "ParentId": 189,

      "Name": "Heating control",

      "DisplayName": "",

      "CustomIdentifier": "192",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Heating control/",

      "Class": "Heating control/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "TargetTemperature": -50.90414157503709,

      "CurrentTemperature": -50.90414157503709,

      "HeatingLevel": -50.90414157503709

    }

  ]

}

 

The inlined Equipment Properties have been highlighted in the JSON above.

 

NOTE: Equipment Properties that are connected to a writeable tag or configured as a static value using the AssetWorX database cache are also writeable via the OData endpoint.

 

Along with the Asset entity type, the ICONICS OData Connector OData endpoint exposes two other entity types: DatasetProperty and HistoryProperty, which are both children of Asset:

<EntityType Name="DatasetProperty">

  <Key>

    <PropertyRef Name="Id" />

  </Key>

  <Property Name="Id" Type="Edm.Int32" Nullable="false" />

  <Property Name="ParentId" Type="Edm.Int32" Nullable="false" />

  <Property Name="Name" Type="Edm.String" />

  <NavigationProperty Name="Asset" Type="Model.Asset" />

</EntityType>

 

<EntityType Name="HistoryProperty">

  <Key>

    <PropertyRef Name="Id" />

  </Key>

  <Property Name="Id" Type="Edm.Int32" Nullable="false" />

  <Property Name="ParentId" Type="Edm.Int32" Nullable="false" />

  <Property Name="Name" Type="Edm.String" />

  <NavigationProperty Name="Asset" Type="Model.Asset" />

</EntityType>

 

The entity sets associated to these two types contain, respectively, all the Equipment Properties that have a dataset tag associated with them (Dataset tab in the Equipment Property configuration in Workbench) and a historical tag associated with them (Historical Data tab in the Equipment Property configuration in Workbench).

Working with Assets

The Assets entity sets returns the full list of Equipment from AssetWorX that are enabled and have their “OData” checkbox checked:

GET http://localhost/ODataConnector/odata/Assets

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets",

  "value": [

    {

      "Id": 1,

      "ParentId": null,

      "Name": "Company",

      "DisplayName": "",

      "CustomIdentifier": "1",

      "Path": "Company/",

      "Class": null,

      "HasDatasetProperties": false,

      "HasHistoryProperties": false

    },

    {

      "Id": 2,

      "ParentId": null,

      "Name": "WaterIrrigation",

      "DisplayName": "",

      "CustomIdentifier": "2",

      "Path": "WaterIrrigation/",

      "Class": null,

      "HasDatasetProperties": false,

      "HasHistoryProperties": false

    },
    ...
    {

      "Id": 232,

      "ParentId": 228,

      "Name": "Sub Area A",

      "DisplayName": "",

      "CustomIdentifier": "232",

      "Path": "Alarms/Plant Area B/Sub Area A/",

      "Class": null,

      "HasDatasetProperties": false,

      "HasHistoryProperties": false

    }

  ]

}

 

As per OData protocol, it is possible to retrieve a single entity by ID:

 

GET http://localhost/ODataConnector/odata/Assets(189)

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 189,

  "ParentId": 181,

  "Name": "Ingredient charger",

  "DisplayName": "",

  "CustomIdentifier": "189",

  "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/",

  "Class": "Ingredient charger/",

  "HasDatasetProperties": false,

  "HasHistoryProperties": false

}

 

The ICONICS OData Connector OData entity model also specifies to custom entity-bound functions for the Asset entity type:

GET http://localhost/ODataConnector/odata/Assets(189)/Model.Parent()

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 181,

  "ParentId": 106,

  "Name": "Ingredients mixing tank",

  "DisplayName": "",

  "CustomIdentifier": "181",

  "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/",

  "Class": null,

  "HasDatasetProperties": false,

  "HasHistoryProperties": false

}

GET http://localhost/ODataConnector/odata/Assets(189)/Model.Children()

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets",

  "value": [

    {

      "Id": 190,

      "ParentId": 189,

      "Name": "Ingredient selector",

      "DisplayName": "",

      "CustomIdentifier": "190",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Ingredient selector/",

      "Class": "Ingredient selector valves/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "SelectedMaterial": -50.90414157503709,

      "SwitchingInProgress": -50.90414157503709

    },

    {

      "Id": 191,

      "ParentId": 189,

      "Name": "Pump control",

      "DisplayName": "",

      "CustomIdentifier": "191",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Pump control/",

      "Class": "Variable speed pump/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "PumpHealth": -50.90414157503709,

      "CurrentFlow": -50.90414157503709,

      "TargetFlow": -50.90414157503709

    },

    {

      "Id": 192,

      "ParentId": 189,

      "Name": "Heating control",

      "DisplayName": "",

      "CustomIdentifier": "192",

      "Path": "Company/Foxboro bakery/North/Baking line/Ingredients mixing tank/Ingredient charger/Heating control/",

      "Class": "Heating control/",

      "HasDatasetProperties": false,

      "HasHistoryProperties": false,

      "TargetTemperature": -50.90414157503709,

      "CurrentTemperature": -50.90414157503709,

      "HeatingLevel": -50.90414157503709

    }

  ]

}

Updating a Property

First, let’s create an Equipment Property that is connected to a writeable tag. Open Workbench and navigate to Assets > Equipment > Company, right-click and select “Add Equipment Property”. Name the property Count, switch to the Real Time Data tab, select Dynamic Tag as Source Type and connect the Real time data tag to svrsim:out.int32:

 

 

Restart the ICONICS OData Connector Point Manager service (remember that for performance reasons the AssetWorX hierarchy is only read once during startup and cached) and request the Company asset:

 

GET http://localhost/ODataConnector/odata/Assets(1)

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 1,

  "ParentId": null,

  "Name": "Company",

  "DisplayName": "",

  "CustomIdentifier": "1",

  "Path": "Company/",

  "Class": null,

  "HasDatasetProperties": false,

  "HasHistoryProperties": false,

  "Count": 0

}

 

As described in the OData Protocol, properties of an entity can be written using HTTP PATCH. The PATCH body must carry the name of the property and the value to write as a JSON object:

PATCH http://localhost/ODataConnector/odata/Assets(1) HTTP/1.1

Host: localhost

Connection: keep-alive

Content-Length: 27

Cache-Control: no-cache

Content-Type: application/json

 

{ "Count": 42 }

 

After the PATCH has completed, we can verify that the property was indeed updated:

GET http://localhost/ODataConnector/odata/Assets(1)

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 1,

  "ParentId": null,

  "Name": "Company",

  "DisplayName": "",

  "CustomIdentifier": "1",

  "Path": "Company/",

  "Class": null,

  "HasDatasetProperties": false,

  "HasHistoryProperties": false,

  "Count": 42

}

 

NOTE: The OData protocol allows multiple properties to be updated with a single PATCH request, however the ICONICS OData Connector only allows to update one property per PATCH request. If more than one property is passed in the PATCH body the ICONICS OData Connector will respond with a 400 Bad Request error.

Working with Datasets

First, let’s create an Equipment Property that is connected to a parameterized dataset. Open Workbench and navigate to Assets > Equipment > Company, right-click and select “Add Equipment Property”. Name the property Orders, switch to the Dataset tab and connect the Dataset tag to:

 

db:Northwind.OrdersByCustomerID<@CustomerID=<<customerid>>>

 

 

Note that we are connecting the @CustomerID parameter of the underlying dataset point with the AssetWorX parameter <<customerid>>, which we must define under the General tab:

 

 

After restarting the ICONICS OData Connector Point Manager we can verify that the Company asset now has dataset properties:

 

GET http://localhost/ODataConnector/odata/Assets(1)

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 1,

  "ParentId": null,

  "Name": "Company",

  "DisplayName": "",

  "CustomIdentifier": "1",

  "Path": "Company/",

  "Class": null,

  "HasDatasetProperties": true,

  "HasHistoryProperties": false,

  "Count": 42

}

 

And we can request all the dataset properties for the asset using the associated navigation property:

 

GET http://localhost/ODataConnector/odata/Assets(1)/DatasetProperties

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.DatasetProperty)",

  "value": [

    {

      "Id": 148,

      "ParentId": 1,

      "Name": "Orders"

    }

  ]

}

 

There is a custom entity-bound function called GetParameters() in the OData Connector entity model that allows to retrieve the list of parameters for a dataset property:

 

GET http://localhost/ODataConnector/odata/DatasetProperties(148)/Model.GetParameters()

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.DatasetParameter)",

  "value": [

    {

      "Name": "customerid",

      "Value": "ALFKI"

    }

  ]

}

 

Another function called GetData(parameters) can be invoked to retrieve the dataset data. parameters must be a JSON array containing all the required parameter values (the list of which can be retrieved using the GetParameters function above):

 

GET http://localhost/ODataConnector/odata/DatasetProperties(148)/Model.GetData(parameters=@p)?@p=[{"@odata.type":"Model.DatasetParameter","Name":"customerid","Value":"ANATR"}]

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.DatasetRow)",

  "value": [

    {

      "OrderID": 10308,

      "CustomerID": "ANATR",

      "EmployeeID": 7,

      "OrderDate": "1996-09-18T00:00:00-04:00",

      "RequiredDate": "1996-10-16T00:00:00-04:00",

      "ShippedDate": "1996-09-24T00:00:00-04:00",

      "ShipVia": 3,

      "Freight": 1.61,

      "ShipName": "Ana Trujillo Emparedados y helados",

      "ShipAddress": "Avda. de la Constitución 2222",

      "ShipCity": "México D.F.",

      "ShipPostalCode": "05021",

      "ShipCountry": "Mexico"

    },

    ...

    {

      "OrderID": 10926,

      "CustomerID": "ANATR",

      "EmployeeID": 4,

      "OrderDate": "1998-03-04T00:00:00-05:00",

      "RequiredDate": "1998-04-01T00:00:00-05:00",

      "ShippedDate": "1998-03-11T00:00:00-05:00",

      "ShipVia": 3,

      "Freight": 39.92,

      "ShipName": "Ana Trujillo Emparedados y helados",

      "ShipAddress": "Avda. de la Constitución 2222",

      "ShipCity": "México D.F.",

      "ShipPostalCode": "05021",

      "ShipCountry": "Mexico"

    }

  ]

}

Working with Historical Data

First, let’s create an Equipment Property that is connected to n historical tag. Make sure that ICONICS Hyper Historian Logger is started, then open Workbench and navigate to Assets > Equipment > Company, right-click and select “Add Equipment Property”. Name the property SineHistory, switch to the Historical Data tab and connect the Historical data tag to:

 

hh:\Configuration\Signals:Sine

 

 

After restarting the ICONICS OData Connector Point Manager we can verify that the Company asset now has history properties:

 

GET http://localhost/ODataConnector/odata/Assets(1)

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Assets/$entity",

  "Id": 1,

  "ParentId": null,

  "Name": "Company",

  "DisplayName": "",

  "CustomIdentifier": "1",

  "Path": "Company/",

  "Class": null,

  "HasDatasetProperties": true,

  "HasHistoryProperties": true,

  "Count": 42

}

 

And we can request all the history properties for the asset using the associated navigation property:

 

GET http://localhost/ODataConnector/odata/Assets(1)/HistoryProperties

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.HistoryProperty)",

  "value": [

    {

      "Id": 149,

      "ParentId": 1,

      "Name": "SineHistory"

    }

  ]

}

 

There are two custom entity-bound functions to the HistoryProperty entity class:

Requesting raw data:

 

GET http://localhost/ODataConnector/odata/HistoryProperties(149)/Model.GetRawData(startTime=@s,endTime=@e)?@s=2018-05-25T16:15:00-04:00&@e=2018-05-25T16:20:00-04:00

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.DataValue)",

  "value": [

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:15:00.228-04:00",

      "Value": 0.0033172470977902435

    },

    ...

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:19:59.978-04:00",

      "Value": 0.035526368222335236

    }

  ]

}

 

Requesting aggregated data:

 

GET http://localhost/ODataConnector/odata/HistoryProperties(149)/Model.GetAggregatedData(startTime=@s,endTime=@e,interval=@i,aggregate=@a)?@s=2018-05-25T16:15:00-04:00&@e=2018-05-25T16:20:00-04:00&@i=duration'P0DT0H1M0S'&@a='average'

 

{

  "@odata.context": "http://localhost/ODataConnector/odata/$metadata#Collection(Model.DataValue)",

  "value": [

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:15:00.001-04:00",

      "Value": 0.032563129078877096

    },

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:16:00.001-04:00",

      "Value": 0.032563129078877096

    },

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:17:00.001-04:00",

      "Value": 0.03257493481251637

    },

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:18:00.001-04:00",

      "Value": 0.032563129078877096

    },

    {

      "Quality": 0,

      "Timestamp": "2018-05-25T16:19:00.001-04:00",

      "Value": 0.032563129078877096

    }

  ]

}

 

NOTE: the interval parameter is a timespan, and by OData protocol conventions it needs to be passed in the format:

duration'PdDThHmMsS'

 

Where:

·       d represents the number of days in the interval

·       h represents the number of hours in the interval

·       m represents the number of minutes in the interval

·       s represents the number of seconds in the interval

 

The example above used a 1-minute interval, which is represented as P0DT0H1M0S.

Full Configuration Parameters for the OData Connector

This section explains all the OData Connector parameters that can be configured via the Platform Services Configuration utility.

See Also:

GET RealtimeData

GET DatasetData

GET FaultCauses

GET FaultIncidents

GET HistoricalAlarms

GET HistoricalData

GET QualityChartData

GET QualityHistogramData