JavaScript

JavaScript clients can update parameter and event definitions and write data to streams using RESTful APIs. Quix supports WebSockets for clients that want to receive telemetry data and parameters/events updates in real-time.

Connecting to Quix

Quix web APIs are secured with OAuth2.0 bearer scheme. Therefore, all HTTP requests to Quix must contain a valid bearer token. You can generate a personal access token (PAT) for use as a bearer token from the portal by following the following steps.

  • Navigate to your profile by clicking on your avatar and selecting "Profile" from the drop-down menu.
  • Select "Personal Access Tokens" tab on the profile page.
  • Click on "Generate Token" button to open a dialog to set a name and an expiry date for the PAT and click on "Create" to generate the PAT.

Writing data to Quix

Clients write data to Quix using streams opened on existing topics. Therefore, you need to first create a topic in the portal to hold your data streams.

Once you have a topic, your clients can start writing data to Quix by

  • creating a stream in your topic
  • sending data to that stream
  • closing the stream

Creating a stream

To write data to Quix, you need to open a stream to your topic. Following is an example of creating a stream using JavaScript and Node.js.

const https = require(https);

const data = JSON.stringify({
    Name: "Your Stream Name",
    Location: "your/location",
    Metadata: {
        Property1: "Value 1",
        Property2: "Value 2"
    },
    Parents = ["parent-stream-1", "parent-stream-2"],
    TimeOfRecording: "2021-02-06T00:15:15Z"
});

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams',
    method: 'POST',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    res.on('data', d => {
        let json = JSON.parse(d);
        let streamId = json.streamId;
    });
});

req.write(data);
req.end();

Upon completing the request successfully, you will receive the stream id in the response body. You are going to need this stream id when you are writing data to the stream.

In the request data, Location is also an optional, but an important property. Location allows you to organise your streams under directories in the data catalogue.

When you are creating the stream, you can add optional metadata about the stream to the stream definition like Property1 and Property2 in the preceding example.

Field Parents is also optional. If the current stream is derived from one or more streams (e.g. by transforming data from one stream using an analytics model), you can reference the original streams using this field.

TimeOfRecording is an optional field that allows you to specify the actual time the data was recorded. This field is useful if you are streaming data that was recorded in the past.

Writing parameter data to a stream

After you have created the stream, you can start writing data to that stream using the following HTTP request.

const https = require(https);

const data = JSON.stringify({
    Timestamps: [1591733989000000000, 1591733990000000000, 1591733991000000000],
    NumericValues: {
        "ParameterA": [1, 2, 3],
        "ParameterB": [5, 8, 9]
    },
    StringValues: {
        "ParameterC": ["hello", "world", "!"]
    },
    BinaryValues: {
        "ParameterD": [
            Buffer.from("hello").toString('base64'),
            Buffer.from(" Quix").toString('base64'),
            Buffer.from("!").toString('base64')
        ]
    }
    TagValues: {
        "ParameterA": [null, null, "tag-1"]
    }
});

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams/your-stream-id/parameters/data',
    method: 'POST',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    console.log(`Status Code: ${res.statusCode}`);
});

req.write(data);
req.end();

In the preceding example, data has two different parameter types, numeric and strings. If your data only contains numeric data, you do not need to include the StringValues property. In the case of binary values, the items in the array must be a base64 encoded string.

TagValues is another optional field in the data request that allows you to add context to data points by means of tagging them. Index of the Timestamps array is used when matching the parameter data values as well as tag values. Therefore, the order of the arrays is important.

Defining parameters

In the above examples, parameters are created in Quix as you write data to the stream. However, what if you would like to add more information like acceptable value ranges, measurement units, etc. to your parameters? You can use the following HTTP request to update your parameter definitions.

const https = require(https);

const data = JSON.stringify([
    {
        Id: "ParameterA",
        Name: "Parameter A",
        Description: "Temperature measurements from unit 1234A",
        MinimumValue: 0.0,
        MaximumValue: 100.0,
        Unit: "°C",
        CustomProperties: "{\"OptimalMinimum\": 30.0, \"OptimalMaximum\": 50.0}",
        Location: "/chassis/engine"
    }
]);

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams/your-stream-id/parameters',
    method: 'PUT',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    console.log(`Status Code: ${res.statusCode}`);
});

req.write(data);
req.end();

In the preceding request, the Id must match the parameter id you set when writing data to the stream. Name allows you to set a more readable name for the parameter. You can also add a description, minimum and maximum values, unit of measurement to your parameter. Location allows you to organise/group your parameters in a hierarchical manner like with the streams. If you have a custom parameter definition that is not covered by the primary fields of the request, you can use CustomProperties field to add your custom definition as a string.

Writing event data to a stream

Writing event data to a stream is similar to writing parameter data using the web api. The main difference in the two requests is in the request body.

const data = JSON.stringify([
    {
        Id: "EventA",
        Timestamp: 1591733989000000000,
        Value: "Lap1",
        Tags: {
            TagA: "val1",
            TagB: "val2"
        }
    },
    {
        Id: "EventA",
        Timestamp: 1591734989000000000,
        Value: "Lap2",
        Tags: {
            TagA: "val1",
            TagB: "val2"
        }
    },
    {
        Id: "EventA",
        Timestamp: 1591735989000000000,
        Value: "Lap3",
        Tags: {
            TagA: "val1"
        }
    },
]);

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams/your-stream-id/events/data',
    method: 'POST',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    console.log(`Status Code: ${res.statusCode}`);
});

req.write(data);
req.end();

In the preceding example, tags in the event data request are optional. Tags add context to your data points and help you to execute efficient queries over them on your data like using indexes in traditional databases.

Defining events

In the above examples, events are created in Quix as you write data to the stream. If you want to add more descriptions to your events, you can use event definitions api similar to parameter definitions to update your events.

const https = require(https);

const data = JSON.stringify([
    {
        Id: "EventA",
        Name: "Event A",
        Description: "New lap event",
        CustomProperties: "{\"Tarmac\": \"Open-graded\"}",
        Location: "/drive/lap",
        Level: "Information"
    }
]);

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams/your-stream-id/events',
    method: 'PUT',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    console.log(`Status Code: ${res.statusCode}`);
});

req.write(data);
req.end();

In the preceding request, the Id must match the event id you set when writing events to the stream. Name allows you to set a more readable name for the event. Location allows you to organise/group your events in a hierarchy like with the parameters. If you have a custom event definition that is not covered by the primary fields of the request, you can use CustomProperties field to add your custom definition as a string. You can also set an optional event Level. Accepted event levels are Trace, Debug, Information, Warning, Error and Critical. Event level defaults to Information if not specified.

Closing a stream

After finishing sending data, you can proceed to close the stream using the request below.

const https = require(https);

const options = {
    hostname: 'your-workspace-id.portal.quix.ai',
    path: '/topics/your-topic-name/streams/your-stream-id/close',
    method: 'POST',
    headers: {
        'Authorization': 'Bearer your_access_token',
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, res => {
    console.log(`Status Code: ${res.statusCode}`);
});

req.end();

Reading data from Quix

Quix supports real-time data streaming over WebSockets. JavaScript clients can receive updates on parameter and event definition updates, parameter data and event data as they happen. Following examples use SignalR client library to connect to Quix over WebSockets.

Setting up SignalR

If you are using a package manager like npm, you can install SignalR using npm install @microsoft/signalr. For other installation options that don't depend on a platform like Node.js such as consuming SignalR from a CDN please refer to SignalR documentation.

Following code snippet shows how you can connect to Quix after SignalR has been setup.

var signalR = require("@microsoft/signalr");

const options = {
    accessTokenFactory: () => 'your_access_token'
};

const connection = new signalR.HubConnectionBuilder()
    .withUrl("https://reader-your-workspace-id.portal.quix.ai/hub", options)
    .build();

connection.start().then(() => console.log("SignalR connected."));

If the connection is successful, you should see the console log "SignalR connected".

Reading data from a stream

Before you can read data from a stream, you need to subscribe to an event like parameter definition, event definition, parameter data or event data.

Following is an example of establishing a connection to Quix, subscribing to a parameter data stream, reading data from that stream, and unsubscribing from the event using a SignalR client.

var signalR = require("@microsoft/signalr");

const options = {
    accessTokenFactory: () => 'your_access_token'
};

const connection = new signalR.HubConnectionBuilder()
    .withUrl("https://reader-your-workspace-id.portal.quix.ai/hub", options)
    .build();

// Establish connection
connection.start().then(() => {
    console.log("Connected to Quix.");

    // Subscribe to parameter data stream.
    connection.invoke("SubscribeToParameter", "your-topic-name", "your-stream-id", "your-parameter-id");

    // Read data from the stream.
    connection.on("ParameterDataReceived", data => {
        let model = JSON.parse(data);
        console.log("Received data from stream: " + model.streamId);

        // Unsubscribe from stream.
        connection.invoke("UnsubscribeFromParameter", "your-topic-name", "your-stream-id", "your-parameter-id");
    });
});

Following is a list of subscriptions available for SignalR clients.

  • SubscribeToParameter(topicName, streamId, parameterId): Subscribes to a parameter data stream. Use UnsubscribeFromParameter(topicname, streamId, parameterId) to unsubscribe.
  • SubscribeToParameterDefinitions(topicName, streamId): Subscribes to parameter definition updates.
  • SubscribeToEvent(topicName, streamId, eventId): Subscribes to an event data stream. Use UnsubscribeFromEvent(topicName, streamId, eventId) to unsubscribe.
  • SubscribeToEventDefinitions(topicName, streamId): Subscribes to event definition updates.
  • UnsubscribeFromStream(topicName, streamId): Unsubscribes from all subscriptions of the specified stream.

Following is a list of SignalR events supported by Quix and their payloads.

  • ParameterDataReceived: Add a listener to this event to receive parameter data from a stream. Following is a sample payload for this event.
{
  topicName: 'topic-1',
  streamId: 'b45969d2-4624-4ab7-9779-c8f90ce79420',
  timestamps: [ 1591733989000000000, 1591733990000000000, 1591733991000000000 ],
  numericValues: { ParameterA: [ 1, 2, 3 ] },
  stringValues: {},
  tagValues: { ParameterA: [ null, null, 'tag-1' ] }
}
  • ParameterDefinitionsUpdated: Add a listener to this event to receive data from SubscribeToParameterDefinitions subscription. Following is a sample payload of this event.
{
    topicName: 'topic-1',
    streamId: 'b45969d2-4624-4ab7-9779-c8f90ce79420',
    definitions: [
        {
            Id: 'ParameterA',
            Name: 'Parameter A',
            Description: 'Description of parameter A',
            MinimumValue: null,
            MaximumValue: 100.0,
            Unit: 'kmh',
            CustomProperties: null,
            Localtion: '/car/general'
        }
    ]
}
  • EventDataReceived: Add a listener to this event to receive data from SubscribeToEvent subscription. Following is a sample payload of this event.
{
    topicName: 'topic-1',
    streamId: 'b45969d2-4624-4ab7-9779-c8f90ce79420'
    id: 'EventA',
    timestamp: 1591733990000000000,
    value: 'val-a',
    tags: {
        tag1: 'val1'
    }
}