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 theodatauser account when requesting data from FrameWorX. Security permissions for theodatauser can be then configured appropriately using the ICONICS Security Server configurator. Please note that the user name does not have to be necessarilyodataand 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 areRequireAuthenticationandImpersonate. As the name suggests, after changing the value ofRequireAuthenticationtotrue, the OData Connector will start requesting authentication credentials to access the endpoints. Authentication is implemented asHTTP 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 theImpersonatesetting. WhenImpersonateis set tofalse(the default value) the OData Connector will request data from FrameWorX under the default security group or, if configured, the ODATA -> FWX user account. WhenImpersonateis set totrue, 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 thenetshutility 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 specifiedport; ifportis 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 theEndpointProtocol, EndpointPortandEndpointMachineNamefields in the Platform Services Configuration utility:

·      

  • EndpointProtocolmust be changed tohttps

  • EndpointPortmust be changed to the port to which the certificate has been bound (the default is 443)

  • EndpointMachineNamemust be changed to the value of the common name (CN) attribute of the certificate.

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. TheDatasetDataendpoint 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)

TheHistoricalDataendpoint 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, theHistoricalDataendpoint 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. TheHistoricalDataendpoint 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 atForwardOnlyHistoricalDatathat allows to read large amounts of historical data with a lower memory impact on the OData Connector Service.

To read historical data using the standardHistoricalDataendpoint 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

}]

TheForwardOnlyHistoricalDataendpoint 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 theHistoricalDataendpoint, 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. Fromwww.odata.org, OData is:

[…] anISO/IEC approved,OASIS standardthat 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 utilitycan 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.

  • EnableODataEndpoint. This parameter enables or disables the OData endpoint. When enabled, metadata for the endpoint is available at http://localhost/ODataConnector/odata/$metadata

  • ODataEndpointCustomPrefix. This parameter allows to specify a custom prefix for the OData endpoint. For example, if it were set to “v1”, the metadata for OData endpoint would be available at http://localhost/ODataConnector/odata/v1/$metadata

  • ODataConfigurationLoadTimeoutMins. For performance reasons, the OData Connector loads the OData-enabled assets in the AssetWorX hierarchy in memory during startup and keeps them cached: changes in the AssetWorX structure are not reflected in the OData Connector until the OData Connector service is restarted. This parameter determines the timeout for loading the AssetWorX structure in memory.

  • ·ODataPropertiesRefreshPeriodMins. For performance reasons, Equipment Properties that have a real-time data source associated in the AssetWorX configuration have their value queried and cached by the OData Connector. This means that the OData Connector will expose a cached value for properties, and not the actual current value. This parameter determines how often the OData Connector re-queries the real-time data sources associated with the Equipment Properties to refresh the cached value. This polling behavior can be disabled via the ODataUseSubscriptionForPropertyValues parameter.

  • ODataEnableAssetOnlineChangesDetection. This parameter is currently not used.

  • ·ODataUseSubscriptionForPropertyValues. When a periodic polling for Equipment Property values is not desirable (for example in cases where it is necessary to expose the actual value of an Equipment Property as quickly as possible through the OData interface) it is possible to configure the OData Connector to subscribe to all Equipment Properties with real-time data sources and that have the OData checkbox selected during startup, and to keep subscription for the lifetime of the OData Connector service. This subscription behavior is enabled when this property is set to true, and it disables the default periodic polling behavior.

  • ODataSubscriptionScanRateMsec. This parameter configures the scan rate used by the OData Connector when subscribing to Equipment Properties when ODataUseSubscriptionForPropertyValues is set to true.

  • ODataLoadExtededAssetProperties. Configures whether extended properties of Equipment Properties (i.e.: EURangeMin, EuRangeMax, TimeZone, etc.) are loaded and exposed by the OData Connector.

  • ODataEnableExtendedPropertiesRefresh.Configures whether extended properties for Equipment Properties should refresh or not. This setting applies to both polled or subscribed refresh of Equipment Properties.

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 athttp://localhost/ODataConnector/odata/$metadata.

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

<EntityTypeName="Asset"OpenType="true">

  <Key>

    <PropertyRefName="Id" />

  </Key>

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

  <PropertyName="ParentId"Type="Edm.Int32" />

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

  <PropertyName="DisplayName"Type="Edm.String" />

  <PropertyName="CustomIdentifier"Type="Edm.String" />

  <PropertyName="Path"Type="Edm.String" />

  <PropertyName="Class"Type="Edm.String" />

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

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

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

  <NavigationPropertyName="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:

<EntityTypeName="DatasetProperty">

  <Key>

    <PropertyRefName="Id" />

  </Key>

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

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

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

  <NavigationPropertyName="Asset"Type="Model.Asset" />

</EntityType>

<EntityTypeName="HistoryProperty">

  <Key>

    <PropertyRefName="Id" />

  </Key>

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

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

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

  <NavigationPropertyName="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:

GEThttp://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:

GEThttp://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:

  • Parent()gets the parent of an asset

  • Children()gets the direct children of an asset (not recursive)

GEThttp://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

}

GEThttp://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:

GEThttp://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:

GEThttp://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:

GEThttp://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:

GEThttp://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:

GEThttp://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):

GEThttp://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:

GEThttp://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:

GEThttp://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:

  • GetRawData(startTime, endTime)– retrieves the raw data for the property for the specified interval

  • GetAggregatedData(startTime, endTime, interval, aggregate)– retrieves aggregated data for the property for the specified interval

Requesting raw data:

GEThttp://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:

GEThttp://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:

·       drepresents the number of days in the interval

·       hrepresents the number of hours in the interval

·       mrepresents the number of minutes in the interval

·       srepresents 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.

  • EndpointMachineName. Sets the machine name for the OData Connector endpoint. See the Endpoint Protocol section of this document for more details.

  • EndpointPort. Sets the port for the OData Connector endpoint. See the Endpoint Protocol section of this document for more details.

  • EndpointProtocol. Sets the protocol for the OData Connector endpoint, supported protocols are either http or https. See the Endpoint Protocol section of this document for more details.

  • RequireAuthentication. When set to true, the OData Connector will start requesting authentication credentials to access the endpoints. Authentication is implemented as HTTP basic access authentication. 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. Please see the security section in this document for more details.

  • Impersonate. When 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. This requires RequireAuthentication to be set to true as well.

  • DataRetrievalTimeoutSecs. Sets the timeout to apply to real-time read operations from the OData Connector.

  • EnableCrossOriginResourceSharing. Configures whether to enable Cross Origin Resource Sharing. CORS should be enabled in order to consume REST and OData endpoints from applications not running on the same machine as the OData Connector.

  • AllowedCrossOriginResourceSharingDomains. When CORS is enabled this parameter configures which domains are allowed for Cross Origin calls. Domains must be semicolon separated.

  • EnableErrorDetails. When enabled, errors reported by the OData Connector to clients will include details and stack trace of the exception occurred on the server.

  • EnableHelp. Enables help pages for the OData Connector (located at http://localhost/OdataConnector/rest/Help/Html?method=get and http://localhost/OdataConnector/rest/Help/Html?method=post)

  • GCCollectScheduleMinutesAfterMidnight. Instructs the server to manually run a forced Garbage Collection with Large Object Heap compaction. When set to a negative value (default) this feature is disabled. A value greater than or equal to 0 specifies the number of minutes after local midnight to wait before scheduling the garbage collection. This forced garbage collection should not be necessary for most systems as .NET Framework should be managing the memory usage and releasing the memory as needed over time.  However, some systems may want to use this daily check to prevent the perception that the OData Connector is leaking memory or to better manage a high-memory system.

  • WriteTimeoutMsec. Sets the timeout to apply to real-time write operations from the OData Connector.

  • MaxReceivedMessageSizeBytes. Defines the maximum received message size, in bytes.

  • EnableODataEndpoint. This parameter enables or disables the OData endpoint. When enabled, metadata for the endpoint is available at http://localhost/ODataConnector/odata/$metadata

  • ODataEndpointCustomPrefix. This parameter allows to specify a custom prefix for the OData endpoint. For example, if it were set to “v1”, the metadata for OData endpoint would be available at http://localhost/ODataConnector/odata/v1/$metadata

  • ODataConfigurationLoadTimeoutMins. For performance reasons, the OData Connector loads the OData-enabled assets in the AssetWorX hierarchy in memory during startup and keeps them cached: changes in the AssetWorX structure are not reflected in the OData Connector until the OData Connector service is restarted. This parameter determines the timeout for loading the AssetWorX structure in memory.

  • ODataPropertiesRefreshPeriodMins. For performance reasons, Equipment Properties that have a real-time data source associated in the AssetWorX configuration have their value queried and cached by the OData Connector. This means that the OData Connector will expose a cached value for properties, and not the actual current value. This parameter determines how often the OData Connector re-queries the real-time data sources associated with the Equipment Properties to refresh the cached value. This polling behavior can be disabled via the ODataUseSubscriptionForPropertyValues parameter.

  • ODataUseSubscriptionForPropertyValues. When a periodic polling for Equipment Property values is not desirable (for example in cases where it is necessary to expose the actual value of an Equipment Property as quickly as possible through the OData interface) it is possible to configure the OData Connector to subscribe to all Equipment Properties with real-time data sources and that have the OData checkbox selected during startup, and to keep subscription for the lifetime of the OData Connector service. This subscription behavior is enabled when this property is set to true, and it disables the default periodic polling behavior.

  • ODataSubscriptionScanRateMsec. This parameter configures the scan rate used by the OData Connector when subscribing to Equipment Properties when ODataUseSubscriptionForPropertyValues is set to true.

  • ·ODataLoadExtededAssetProperties. Configures whether extended properties of Equipment Properties (i.e.: EURangeMin, EuRangeMax, TimeZone, etc.) are loaded and exposed by the OData Connector.

  • ODataEnableExtendedPropertiesRefresh. Configures whether extended properties for Equipment Properties should refresh or not. This setting applies to both polled or subscribed refresh of Equipment Properties.

  • AsyncClientCacheSlidingTimeoutMins. Communication between the OData Connector and AssetWorX is performed – in a similar fashion as the regular REST interface – using FrameWorX Communication and in particular FwxAsyncClient. Since creating a connection and initializing security requires some roundtrips, this advanced parameter specifies how long an initialized FwxAsyncClient instance should be retained to reduce latency of subsequent calls.

  • EnableRequestTracing. Configures whether to enable or disable more detailed tracing for HTTP requests to the OData Connector. Request traces will be logged to TraceWorX along with normal trace logs.

  • MaxConcurrentRequests. Configures the maximum number of concurrent requests allowed. When left to the default of -1 it sets the maximum number of concurrent requests to 100 x number of CPU cores.

  • RealTimeWaitOnBadQualityDelaySecs. Allows to configure an interval for which to wait for other updates after a real-time read comes back with bad quality. If a good quality update comes within the configured interval, that value will be returned instead of the initial bad quality value.

  • SecurityStatusPollingPeriodMins. WhenRequireAuthenticationis set to false (thus enabling anonymous access) the OData Connector periodically polls the security server to check if security has been enabled. If security is enabled in the security server, the OData Connector will automatically forceRequireAuthentication.

See Also:

GET RealtimeData

GET DatasetData

GET FaultCauses

GET FaultIncidents

GET HistoricalAlarms

GET HistoricalData

GET QualityChartData

GET QualityHistogramData