Channels (Core Services)

A channel is a way to receive continuous updates about a stream or stream set. Rather than using a typical HTTP request, channels are accessed using the Web Socket protocol. There are numerous Web Socket client libraries in several languages available for use with channels.

Channels use the "wss://" scheme instead of "https://" to indicate the use of secure Web Sockets. Below are some examples of channel URLs:

Once connected, the server will send messages containing all the stream value changes since the last message. The payload of these messages is the same as the "GetEnd" method for streams and stream sets. Any streams whose values have not changed since the last message will not be included in the message. Value changes are not guaranteed to be sent in chronological order.

Below is an example of a channel message:

{
    "Links": {},
    "Items": [
        {
            "WebId": "I1AbEDqD5loBNH0erqeqJodtALAaqQoQHk26BGgMQAVXYR0Ag8rvbPp_xUEC80vMunHcq2g",
            "Name": "Water",
            "Links": {
                "Self": "https://myserver/piwebapi/attributes/I1AbEDqD5loBNH0erqeqJodtALAaqQoQHk26BGgMQAVXYR0Ag8rvbPp_xUEC80vMunHcq2g"
            },
            "UnitsAbbreviation": "m",
            "Items": [
                {
                    "Timestamp": "2014-07-22T14:00:00Z",
                    "Value": 2.3,
                    "Good": "true",
                    "Questionable": "false",
                    "Substituted": "false"
                },
                {
                    "Timestamp": "2014-07-22T15:00:00Z",
                    "Value": 2.5,
                    "Good": "true",
                    "Questionable": "false",
                    "Substituted": "false"
                }
            ]
        }
    ]
}

For PI Points and AF Attributes with PI Point Data Reference, channels will also send messages about modifications or deletions of previous values. Modified values will have the "Substituted" flag set to true, and deleted values will have the "Good" flag set to false and a digital state of "No Data".

Polling Interval

During operation, the Channel will periodically poll the PI System’s data update queue. The interval between these polls is called the "polling interval". If there are any data updates detected while polling, PI Web API will send them to the client in a message. The polling interval can be changed using the ChannelPollingInterval configuration value. The units are in milliseconds and the default value is 1000.

Query Parameters

If the URL parameter "includeInitialValues" is set to true, the server will send an initial message containing the current values of all the streams. Below is an example:

If the URL parameter "heartbeatRate" is set, the Channel will periodically send an empty message to confirm that the connection is still alive. The "heartbeatRate" parameter is specified as an integer multiple of the polling interval. After that many polling intervals without a data update, an empty message will be sent. By default, no empty messages will be sent to the client.

Below is an example:

In this case, an empty message will be sent after every five polling intervals without a data update.

Sample Clients

Below are some sample clients for channels:

Sample JavaScript Client (Requires Browser with support for Web Sockets)

var uri = "wss://myserver/piwebapi/streams/{webId}/channel";

var webSocket = new WebSocket(uri);

webSocket.onopen = function(event)
{
    console.log("Connection opened.");
};

webSocket.onerror = function(event)
{
    console.log("Connection aborted.");
};

webSocket.onclose = function(event)
{
    console.log("Connection closed.");
};

webSocket.onmessage = function(event)
{
    console.log("Message received: " + event.data);
};

Sample C# Client (Requires .NET 4.0 or newer)

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Press any key to close.");

        CancellationTokenSource cancellationSource = new CancellationTokenSource();

        Task runTask = RunClient(cancellationSource.Token);

        Console.ReadKey();

        cancellationSource.Cancel();

        runTask.Wait();
    }

    public static async Task RunClient(CancellationToken cancellationToken)
    {
        Uri uri = new Uri("wss://myserver/piwebapi/streams/{webId}/channel");

        WebSocketReceiveResult receiveResult;
        byte[] receiveBuffer = new byte[65536];
        ArraySegment<byte> receiveSegment = new ArraySegment<byte>(receiveBuffer);

        using (ClientWebSocket webSocket = new ClientWebSocket())
        {
            try
            {
                await webSocket.ConnectAsync(uri, CancellationToken.None);
            }
            catch (WebSocketException)
            {
                Console.WriteLine("Could not connect to server.");
                return;
            }

            while (true)
            {
                try
                {
                    receiveResult = await webSocket.ReceiveAsync(receiveSegment, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    break;
                }

                if (receiveResult.MessageType != WebSocketMessageType.Text)
                {
                    await webSocket.CloseAsync(
                        WebSocketCloseStatus.InvalidMessageType,
                        "Message type is not text.", 
                        CancellationToken.None);
                    return;
                }
                else if (receiveResult.Count > receiveBuffer.Length)
                {
                    await webSocket.CloseAsync(
                        WebSocketCloseStatus.InvalidPayloadData,
                        "Message is too long.", 
                        CancellationToken.None);
                    return;
                }

                string message = Encoding.UTF8.GetString(receiveBuffer, 0, receiveResult.Count);

                Console.WriteLine(message);
            }

            await webSocket.CloseAsync(
                WebSocketCloseStatus.NormalClosure, 
                "Closing connection.", 
                CancellationToken.None);
        }
    }
}
Enabling Operational Intelligence