Real-time stream with SignalR

In all examples, the variables token and workspaceId are needed.

  • Variable token can be obtained by creating a new API Token in Nedap Harmony
  • Variable workspaceId is shown on the API Tokens page in Nedap Harmony

❗️

Examples do not handle connection issues

Please note that all examples are bare minimums, and do not handle connection issues, both during set-up and when the connection is active. Please refer to the documentation on the Microsoft website for more details. Note that automatic reconnect has finite retries by default.

📘

.NET Core / .NET 8

Nedap Harmony uses the .NET Core / .NET 8 version of SignalR. Please ensure you install the proper client for this:

  • JavaScript: @microsoft/signalr
  • C# / dotnet: Microsoft.AspNetCore.SignalR.Client

🚧

Make sure to fix the transport and skipNegotiation parameter

In the configuration of SignalR, please define the transport to be WebSockets and set skipNegotiation to true. This is necessary for reliable connections.

JavaScript example

The following example shows a simple implementation in JavaScript. It can be ran using node.js.

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

const token = "...";
const workspaceId = "...";

let connection = new signalR.HubConnectionBuilder()
    .withUrl(`https://nedap-harmony.com/api/hub/external/v1?workspaceId=${workspaceId}`, {
        accessTokenFactory: () => token,
       	transport: signalR.HttpTransportType.WebSockets,
        skipNegotiation: true,
      })
    .build();

connection.onclose(() => console.log('Connection closed'))

connection.start()
    .then(() => {
        connection
          .stream('ActionStream')
          .subscribe({
            next: (item) => {
              console.log(item)
            },
            complete: () => {
              console.warn('Server closed the connection unexpectedly, retrying...')
              this.onConnected()
            },
            error: (err) => {
              console.error(err)
            },
          });
    })

❗️

JavaScript in the browser

It is not possible to directly consume the Nedap Harmony APIs and streams from in-browser JavaScript application. This is for security purposes; the access token would be open and exposed to all the users of your JavaScript application. This is why you always need a backend to 'hide' the Nedap Harmony API Token.

C# / dotnet Example

The following example shows a simple implementation in C# / dotnet.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.WebUtilities;

public class Program
{
	static HubConnection connection;
	static string token = "...";
	static string workspaceId = "...";
	
	public static void Main()
	{
		var uri = "https://nedap-harmony.com/api/hub/external/v1";

		var queryString = new Dictionary<string, string>
		{
			{"workspaceId", workspaceId},
		};

		var uriWithQueryString = QueryHelpers.AddQueryString(uri, queryString);
		
		connection = new HubConnectionBuilder()
			.WithUrl(uriWithQueryString, options =>
					 {
						 options.AccessTokenProvider = () => Task.FromResult(token);
             options.Transports = HttpTransportType.WebSockets;
             options.SkipNegotiation = true;
					 })
      .Build();

		connection.Closed += (error) =>
		{
			Console.WriteLine($"Connection closed: {error}");
			return Task.CompletedTask;
		};
		
		Run().Wait();
	}
	
	static async Task Run()
	{
		await connection.StartAsync();
		
		var cancellationTokenSource = new CancellationTokenSource();
		var channel = await connection.StreamAsChannelAsync<object>("ActionStream", cancellationTokenSource.Token);

		// Wait asynchronously for data to become available
		while (await channel.WaitToReadAsync())
		{
			// Read all currently available data synchronously, before waiting for more data
			while (channel.TryRead(out var readPointAction))
			{
				Console.WriteLine(readPointAction);
			}
		}

		Console.WriteLine("Streaming completed");
	}
}