Musson Industrial's Tag-Stream Module

Embr-Tag-Stream

Musson Industrial is excited to announce the release of embr-tag-stream, an Ignition module that provides an API for high-speed streaming of tag changes and alarms via SSE (server-sent events).

Motivation

SSE provides a simple API for one-way, push-only communication (server -> client, think of a one-way websocket). Client code is simple, message overhead is low, reconnection is automatic, and security concerns are minimal due to the strict one-way nature of the connection.

How to Use

The general flow goes like this:

  1. POST to /embr/tag/stream/subscribe with a list of tags.
  2. The server responds with a session_id (and some extra tag information).
  3. Subscribe to the session at /embr/tag/stream/session/{session_id}.
  4. Tag change and alarm events will be published as they happen, in the following formats:

Message Format

event: tag_change
data: {"tag_id":0,"v":"Tag Value!","q": 192,"t":1717624491517}

event: alarm_event
data: {"tag_id":0,"count":1,"displayPath":"","eventData":{"eventValue":"false","name":"AlarmName","eventTime":"Thu Jun 20 13:17:37 EDT 2024","priority":"Critical","displayPath":""},"extension":{"isShelved":"false"},"id":"454d77c5-44f8-4e52-ab3c-e1ed5c950583","isAcked":false,"isCleared":false,"isShelved":false,"label":"AlarmName","name":"AlarmName","notes":"","priority":"Critical","source":"prov:default:/tag:Tag1:/alm:AlarmName","state":"Active, Unacknowledged","values":[{"isShelved":"false"}]}

Example

An example client is included with the module and can be accessed at http://{gatewayAddress}/res/embr-tag-stream/index.html.

Example Client Script

let eventSource = null;
function createSession() {
    // Close the existing session, if it exists.
    if (eventSource) {
        eventSource.close();
    }

    // Create the body of the session request.
    const requestBody = JSON.stringify({
        "tagPaths": subscribedTags // List of tags to subscribe to.
    })
    console.log(`post-body: ${requestBody}`)

    // Create a new session.
    fetch("/embr/tag/stream/session", {
        method: "POST",
        mode: "cors",
        body: requestBody
    }).then( response => {
        // Get the response as JSON.
        return response.json()
    }).then( sessionInfo => {
        console.log(`post-response: ${sessionInfo}`)

        // Connect to the event source.
        const url = `/embr/tag/stream/session/${sessionInfo.data.session_id}`
        eventSource = new EventSource(url);

        // When a tag change occurs, display the tags new value.
        eventSource.addEventListener('tag_change', (e) => {
            const tagChangeData = JSON.parse(e.data)
            const tagPath = sessionInfo.data.tags[tagChangeData.tag_id].tag_path
            // ... Do something with the tag change information
        })

        // When the session is opened...
        eventSource.onopen = function (e) {
            console.log('EventSource opened.');
        }
    
        // If an error occurs...
        eventSource.onerror = function (e) {
            console.log('EventSource errored.');
        }
    })
}

Disclaimer: Authentication

The current version of the module has no authentication requirements. If you install this module, anyone with access the the /embr/tag/stream/session path can subscribe to your tags.

Authentication isn't a requirement for me for this at the moment, but I would eventually like to have BasicAuthentication support on the session creation url, with a Gateway configuration page for selecting a UserSource and required roles. I will probably wait until 8.3 (I don't really want to mess with Wicket/persistence layer with 8.3 close around the corner).

Contributions are always welcome!

Download

Full documentation and the latest release is available here.


Sponsorship

If you benefit from this module for commercial use, we ask you to consider sponsoring our efforts through GitHub sponsors. Your support will allow us to continue development of open-source modules and tools that benefit the entire community (plus there are some bonuses for sponsors :slightly_smiling_face:).

5 Likes