Connectivity Software User's Guide and Reference
Hosting Rapid Toolkit for Sparkplug Components
Rapid Toolkit for Sparkplug > Concepts > Hosting Rapid Toolkit for Sparkplug Components
In This Topic

Introduction

The code that implements the Sparkplug host application or edge node (using Rapid Toolkit for Sparkplug) cannot just be written and built - it ultimately needs to be run, and for it it needs to be hosted in some kind of executable application. There are many hosting options; the Rapid Toolkit for Sparkplug does no artificially limit them, but some are more common and practical than others. This article will specifically address certain typical hosting scenarios. However, other hosting options exist as well.

When developing a Sparkplug host application, your code needs to create an instance of the EasySparkplugConsumer Class, set its properties and then call some of the methods for data subscription or publication.

When developing a Sparkplig edge node, your code needs to create an instance of the EasySparkplugEdgeNode Class, se its properties and populate it with metrics (and optionally devices and their metrics). It then needs to call the Start Method on the edge node object. When this happens depends on the hosting scenario. The edge node is started, and it keeps running. At some later moment, when the edge node needs to be shut down, your code should call the Stop Method. The host may require additional code in order to integrate it with your edge node.

Console Host

Hosting the Sparkplug host application or edge node in a console application is probably the easiest option in terms of coding effort needed to implement it; and the same time, it is also very easy to debug. There is almost no extra code necessary to make the host application or edge node work inside the console application. 

If you enable Unsolicited User Interaction in the console (as in the examples below), make sure that your application does not prompt for any user input of its own at the console while the Sparkplug host applcation or edge node is running. The unsolicited input requests from Rapid Toolkit for Sparkplug may collide with those from your application. The unsolicited user interaction can be useful for secure MQTT communication, if you want the interactive user have control over the certificate exchange process.

Example - Sparkplug host application in a console host

The following examples illustrates a fully functional Sparkplug host application based on Rapid Toolkit for Sparkplug, hosted in a console application. The host application subscribes to all edge node metrics and all metrics on all devices of the given Sparkplug edge node, and displays the incoming data. Status information related to the broker connection comes via the same events and is also displayed.

// A fully functional Sparkplug host application running in a console.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using OpcLabs.EasySparkplug;
using System;
using System.Threading;
using OpcLabs.BaseLib;
using OpcLabs.EasySparkplug.System;

namespace SparkplugApplicationConsoleDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("EasySparkplug Application Console Demo");
            Console.WriteLine();

            // Parse command line arguments for broker URL, group ID, edge node ID, and host ID.
            
            string brokerUrlString = "mqtt://localhost";
            if (args.Length >= 1)
                brokerUrlString = args[0];

            string groupId = "easyGroup";
            if (args.Length >= 2)
                groupId = args[1];

            string edgeNodeId = "easySparkplugDemo";
            if (args.Length >= 3)
                edgeNodeId = args[2];
            
            string hostId = "";  // we use "easyApplication" in some examples
            if (args.Length >= 4)
                hostId = args[3];

            Console.WriteLine($"Broker URL: {brokerUrlString}");
            Console.WriteLine($"Group ID: {groupId}");
            Console.WriteLine($"Edge node ID: {edgeNodeId}");
            Console.WriteLine($"Host ID: {hostId}");
            Console.WriteLine();

            // Enable the console interaction by the component. The interactive user will then be able to validate remote
            // certificates and/or specify local certificate(s) to use.
            ComponentParameters componentParameters = EasySparkplugInfrastructure.Instance.Parameters;
            componentParameters.PluginSetups.FindName("ConsoleInteraction").Enabled = true;

            // Instantiate the consumer object.
            var hostDescriptor = new SparkplugHostDescriptor(brokerUrlString, hostId);
            using (var consumer = new EasySparkplugConsumer(hostDescriptor))
            {
                // Subscribe to all metrics of the specified edge node(s).
                consumer.SubscribeEdgeNodeMetric(groupId, edgeNodeId, "#",
                    (sender, eventArgs) => Console.WriteLine($"{nameof(consumer.MetricNotification)}: {eventArgs}"));

                // Subscribe to all device metrics of the specified edge node(s).
                consumer.SubscribeDeviceMetric(groupId, edgeNodeId, "#", "#",
                    (sender, eventArgs) => Console.WriteLine($"{nameof(consumer.MetricNotification)}: {eventArgs}"));

                // Let the user decide when to stop.
                var cancelled = new ManualResetEvent(initialState: false);
                Console.CancelKeyPress += (sender, eventArgs) =>
                {
                    // Signal the main thread to exit.
                    cancelled.Set();

                    // Prevent the process from terminating immediately.
                    eventArgs.Cancel = true;
                };

                Console.WriteLine("Press Ctrl+C to stop the application...");
                Console.WriteLine();
                cancelled.WaitOne();
            }
        }
    }
}
' A fully functional Sparkplug edge node running in a console host.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
' OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Threading
Imports OpcLabs.BaseLib
Imports OpcLabs.EasySparkplug
Imports OpcLabs.EasySparkplug.System

Namespace Global.SparkplugApplicationConsoleDemo
    Friend Class Program
        Shared Sub Main(args As String())
            Console.WriteLine("EasySparkplug Application Console Demo")
            Console.WriteLine()

            ' Parse command line arguments for broker URL, group ID, edge node ID, and host ID.

            Dim brokerUrlString As String = "mqtt://localhost"
            If args.Length >= 1 Then
                brokerUrlString = args(0)
            End If

            Dim groupId As String = "easyGroup"
            If args.Length >= 2 Then
                groupId = args(1)
            End If

            Dim edgeNodeId As String = "easySparkplugDemo"
            If args.Length >= 3 Then
                edgeNodeId = args(2)
            End If

            Dim hostId As String = "" ' we use "easyApplication" in some examples
            If args.Length >= 4 Then
                hostId = args(3)
            End If

            Console.WriteLine($"Broker URL: {brokerUrlString}")
            Console.WriteLine($"Group ID: {groupId}")
            Console.WriteLine($"Edge node ID: {edgeNodeId}")
            Console.WriteLine($"Host ID: {hostId}")
            Console.WriteLine()

            ' Enable the console interaction by the component. The interactive user will then be able to validate remote
            ' certificates and/or specify local certificate(s) to use.
            Dim componentParameters As ComponentParameters = EasySparkplugInfrastructure.Instance.Parameters
            componentParameters.PluginSetups.FindName("ConsoleInteraction").Enabled = True

            ' Instantiate the consumer object.
            Dim hostDescriptor = New SparkplugHostDescriptor(brokerUrlString, hostId)
            Using consumer As New EasySparkplugConsumer(hostDescriptor)
                ' Subscribe to all metrics of the specified edge node(s).
                consumer.SubscribeEdgeNodeMetric(groupId, edgeNodeId, "#",
                    Sub(sender, EventArgs) Console.WriteLine($"{NameOf(consumer.MetricNotification)}: {EventArgs}"))

                ' Subscribe to all device metrics of the specified edge node(s).
                consumer.SubscribeDeviceMetric(groupId, edgeNodeId, "#", "#",
                    Sub(sender, EventArgs) Console.WriteLine($"{NameOf(consumer.MetricNotification)}: {EventArgs}"))

                ' Let the user decide when to stop.
                Dim cancelled = New ManualResetEvent(initialState:=False)
                AddHandler Console.CancelKeyPress,
                    Sub(sender, EventArgs) _
                        ' Signal the main thread to exit.
                        cancelled.Set()

                        ' Prevent the process from terminating immediately.
                        EventArgs.Cancel = True
                    End Sub

                Console.WriteLine("Press Ctrl+C to stop the edge node...")
                Console.WriteLine()
                cancelled.WaitOne()
            End Using
        End Sub
    End Class
End Namespace

Examples - Sparkplug edge node in a console host

The following example illustrates a fully functional Sparkplug edge node based on Rapid Toolkit for Sparkplug, hosted in a console application. The edge node also outputs information such as the state of its connection to the broker, errors in publishing, and the birth/rebirth and death events.

// A fully functional Sparkplug edge node running in a console host.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using OpcLabs.BaseLib;
using OpcLabs.BaseLib.Collections.Generic.Extensions;
using OpcLabs.EasySparkplug;
using OpcLabs.EasySparkplug.Services;
using OpcLabs.EasySparkplug.System;
using SparkplugEdgeNodeDemoLibrary;

namespace SparkplugEdgeNodeConsoleDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("EasySparkplug Edge Node Console Demo");
            Console.WriteLine();

            // Parse command line arguments for broker URL, group ID, edge node ID, and primary host ID.

            string brokerUrlString = "mqtt://localhost";
            if (args.Length >= 1)
                brokerUrlString = args[0];

            string groupId = "easyGroup";
            if (args.Length >= 2)
                groupId = args[1];

            string edgeNodeId = "easySparkplugDemo";
            if (args.Length >= 3)
                edgeNodeId = args[2];

            string primaryHostId = "";  // we use "easyApplication" in some examples
            if (args.Length >= 4)
                primaryHostId = args[3];

            Console.WriteLine($"Broker URL: {brokerUrlString}");
            Console.WriteLine($"Group ID: {groupId}");
            Console.WriteLine($"Edge node ID: {edgeNodeId}");
            Console.WriteLine($"Primary host ID: {primaryHostId}");
            Console.WriteLine();

            // Enable the console interaction by the component. The interactive user will then be able to validate remote
            // certificates and/or specify local certificate(s) to use.
            ComponentParameters componentParameters = EasySparkplugInfrastructure.Instance.Parameters;
            componentParameters.PluginSetups.FindName("ConsoleInteraction").Enabled = true;

            // Instantiate the edge node object.
            using (var edgeNode = new EasySparkplugEdgeNode(brokerUrlString, groupId, edgeNodeId))
            {
                // Configure the primary host ID the edge node will use.
                // Leave the primary host ID empty if the edge node is not serving a primary host.
                edgeNode.PrimaryHostId = primaryHostId;

                // Add metrics from the demo library to the edge node.
                edgeNode.Metrics.AddRange(DemoMetrics.Create());

                // Add devices to the edge node, with metrics from the demo library.
                edgeNode.Devices.Add(new SparkplugDevice("data", DataMetrics.Create()));
                edgeNode.Devices.Add(new SparkplugDevice("console", ConsoleMetrics.Create()));
                edgeNode.Devices.Add(new SparkplugDevice("demo", DemoMetrics.Create()));

                // Hook events to the edge node object.
                edgeNode.ApplicationOnlineChanged += (sender, eventArgs) =>
                    Console.WriteLine($"{nameof(edgeNode.ApplicationOnlineChanged)}: {edgeNode.ApplicationOnline}");
                edgeNode.MetricNotification += (sender, eventArgs) =>
                    Console.WriteLine($"{nameof(edgeNode.MetricNotification)}: {eventArgs}");
                edgeNode.PublishingError += (sender, eventArgs) => 
                    Console.WriteLine($"{nameof(edgeNode.PublishingError)}: {eventArgs}");
                edgeNode.Starting += (sender, eventArgs) => Console.WriteLine(nameof(edgeNode.Starting));
                edgeNode.Stopped += (sender, eventArgs) => Console.WriteLine(nameof(edgeNode.Stopped));
                edgeNode.SystemConnectionStateChanged += (sender, eventArgs) =>
                    Console.WriteLine($"{nameof(edgeNode.SystemConnectionStateChanged)}: {eventArgs}");

                // Obtain monitoring services and hook events to them.
                ISparkplugProducerMonitoring edgeNodeMonitoring = edgeNode.GetService<ISparkplugProducerMonitoring>();
                if (!(edgeNodeMonitoring is null))
                {
                    // Monitor the edge node itself.
                    edgeNodeMonitoring.Birth += (sender, eventArgs) =>
                        Console.WriteLine($"{sender}.{nameof(edgeNodeMonitoring.Birth)}");
                    edgeNodeMonitoring.Death += (sender, eventArgs) =>
                        Console.WriteLine($"{sender}.{nameof(edgeNodeMonitoring.Death)}");
                    edgeNodeMonitoring.Rebirth += (sender, eventArgs) =>
                        Console.WriteLine($"{sender}.{nameof(edgeNodeMonitoring.Rebirth)}");

                    // Monitor all devices in the edge node.
                    foreach (SparkplugDevice device in edgeNode.Devices)
                    {
                        ISparkplugProducerMonitoring deviceMonitoring = device.GetService<ISparkplugProducerMonitoring>();
                        if (!(deviceMonitoring is null))
                        {
                            deviceMonitoring.Birth += (sender, eventArgs) =>
                                Console.WriteLine($"{sender}.{nameof(deviceMonitoring.Birth)}");
                            deviceMonitoring.Death += (sender, eventArgs) =>
                                Console.WriteLine($"{sender}.{nameof(deviceMonitoring.Death)}");
                            deviceMonitoring.Rebirth += (sender, eventArgs) =>
                                Console.WriteLine($"{sender}.{nameof(deviceMonitoring.Rebirth)}");
                        }
                    }
                }

                // Start the edge node.
                edgeNode.Start();

                // Let the user decide when to stop.
                var cancelled = new ManualResetEvent(initialState: false);
                Console.CancelKeyPress += (sender, eventArgs) =>
                {
                    // Signal the main thread to exit.
                    cancelled.Set();

                    // Prevent the process from terminating immediately.
                    eventArgs.Cancel = true;
                };

                Console.WriteLine("Press Ctrl+C to stop the edge node...");
                Console.WriteLine();
                cancelled.WaitOne();

                // Stop the edge node.
                edgeNode.Stop();
            }
        }
    }
}
' A fully functional Sparkplug edge node running in a console host.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
' OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Threading
Imports Microsoft.Extensions.DependencyInjection
Imports OpcLabs.BaseLib
Imports OpcLabs.BaseLib.Collections.Generic.Extensions
Imports OpcLabs.EasySparkplug
Imports OpcLabs.EasySparkplug.Services
Imports OpcLabs.EasySparkplug.System
Imports SparkplugEdgeNodeDemoLibrary

Namespace Global.SparkplugEdgeNodeConsoleDemo
    Friend Class Program
        Shared Sub Main(args As String())
            Console.WriteLine("EasySparkplug Edge Node Console Demo")
            Console.WriteLine()

            ' Parse command line arguments for broker URL, group ID, edge node ID, and primary host ID.

            Dim brokerUrlString As String = "mqtt://localhost"
            If args.Length >= 1 Then
                brokerUrlString = args(0)
            End If

            Dim groupId As String = "easyGroup"
            If args.Length >= 2 Then
                groupId = args(1)
            End If

            Dim edgeNodeId As String = "easySparkplugDemo"
            If args.Length >= 3 Then
                edgeNodeId = args(2)
            End If

            Dim primaryHostId As String = "" ' we use "easyApplication" in some examples
            If args.Length >= 4 Then
                primaryHostId = args(3)
            End If

            Console.WriteLine($"Broker URL: {brokerUrlString}")
            Console.WriteLine($"Group ID: {groupId}")
            Console.WriteLine($"Edge node ID: {edgeNodeId}")
            Console.WriteLine($"Primary host ID: {primaryHostId}")
            Console.WriteLine()

            ' Enable the console interaction by the component. The interactive user will then be able to validate remote
            ' certificates and/or specify local certificate(s) to use.
            Dim componentParameters As ComponentParameters = EasySparkplugInfrastructure.Instance.Parameters
            componentParameters.PluginSetups.FindName("ConsoleInteraction").Enabled = True

            ' Instantiate the edge node object.
            Using edgeNode As New EasySparkplugEdgeNode(brokerUrlString, groupId, edgeNodeId)
                ' Configure the primary host ID the edge node will use.
                ' Leave the primary host ID empty if the edge node is not serving a primary host.
                edgeNode.PrimaryHostId = primaryHostId

                ' Add metrics from the demo library to the edge node.
                edgeNode.Metrics.AddRange(DemoMetrics.Create())

                ' Add devices to the edge node, with metrics from the demo library.
                edgeNode.Devices.Add(New SparkplugDevice("data", DataMetrics.Create()))
                edgeNode.Devices.Add(New SparkplugDevice("console", ConsoleMetrics.Create()))
                edgeNode.Devices.Add(New SparkplugDevice("demo", DemoMetrics.Create()))

                ' Hook events to the edge node object.
                AddHandler edgeNode.ApplicationOnlineChanged, Sub(sender, EventArgs) _
                    Console.WriteLine($"{NameOf(edgeNode.ApplicationOnlineChanged)}: {edgeNode.ApplicationOnline}")
                AddHandler edgeNode.MetricNotification, Sub(sender, EventArgs) _
                    Console.WriteLine($"{NameOf(edgeNode.MetricNotification)}: {EventArgs}")
                AddHandler edgeNode.PublishingError, Sub(sender, EventArgs) _
                    Console.WriteLine($"{NameOf(edgeNode.PublishingError)}: {EventArgs}")
                AddHandler edgeNode.Starting, Sub(sender, EventArgs) Console.WriteLine(NameOf(edgeNode.Starting))
                AddHandler edgeNode.Stopped, Sub(sender, EventArgs) Console.WriteLine(NameOf(edgeNode.Stopped))
                AddHandler edgeNode.SystemConnectionStateChanged, Sub(sender, EventArgs) _
                    Console.WriteLine($"{NameOf(edgeNode.SystemConnectionStateChanged)}: {EventArgs}")

                ' Obtain monitoring services and hook events to them.
                Dim edgeNodeMonitoring As ISparkplugProducerMonitoring = edgeNode.GetService(Of ISparkplugProducerMonitoring)()

                If Not edgeNodeMonitoring Is Nothing Then
                    ' Monitor the edge node itself.
                    AddHandler edgeNodeMonitoring.Birth, Sub(sender, EventArgs) _
                        Console.WriteLine($"{sender}.{NameOf(edgeNodeMonitoring.Birth)}")
                    AddHandler edgeNodeMonitoring.Death, Sub(sender, EventArgs) _
                        Console.WriteLine($"{sender}.{NameOf(edgeNodeMonitoring.Death)}")
                    AddHandler edgeNodeMonitoring.Rebirth, Sub(sender, EventArgs) _
                        Console.WriteLine($"{sender}.{NameOf(edgeNodeMonitoring.Rebirth)}")

                    ' Monitor all devices in the edge node.
                    For Each device As SparkplugDevice In edgeNode.Devices
                        Dim deviceMonitoring As ISparkplugProducerMonitoring = device.GetService(Of ISparkplugProducerMonitoring)()
                        If Not deviceMonitoring Is Nothing Then
                            AddHandler deviceMonitoring.Birth, Sub(sender, EventArgs) _
                                Console.WriteLine($"{sender}.{NameOf(deviceMonitoring.Birth)}")
                            AddHandler deviceMonitoring.Death, Sub(sender, EventArgs) _
                                Console.WriteLine($"{sender}.{NameOf(deviceMonitoring.Death)}")
                            AddHandler deviceMonitoring.Rebirth, Sub(sender, EventArgs) _
                                Console.WriteLine($"{sender}.{NameOf(deviceMonitoring.Rebirth)}")
                        End If
                    Next
                End If

                ' Start the edge node.
                edgeNode.Start()

                ' Let the user decide when to stop.
                Dim cancelled = New ManualResetEvent(initialState:=False)
                AddHandler Console.CancelKeyPress,
                    Sub(sender, EventArgs) _
                        ' Signal the main thread to exit.
                        cancelled.Set()

                        ' Prevent the process from terminating immediately.
                        EventArgs.Cancel = True
                    End Sub

                Console.WriteLine("Press Ctrl+C to stop the edge node...")
                Console.WriteLine()
                cancelled.WaitOne()

                ' Stop the edge node.
                edgeNode.Stop()
            End Using
        End Sub
    End Class
End Namespace

Windows Forms Host

Sparkplug host applications and edge nodes developed with Rapid Toolkit for Sparkplug can be hosted inside a Windows desktop application developed using Windows Forms (and in fact, the application can also be developed using WPF or other tools). This hosting option provides a possibility of integrating the Sparkplug host application or edge node with rich user interface, inside the same process.

Windows Service Host

Hosting your Sparkplug host application or edge node in a Windows service is meant mainly for "head-less", fully automated operations. The Windows service has several advantages over other hosting options. The service can be started and stopped in various ways provided by the operating system, and it can even start together with the operating system. The service can operate without any interactive user being logged in, and it can be configured to run under a specific user account. See e.g. the ServiceBase Class documentation for more information on developing Windows Services in this way.

Windows Services developed in this way, i.e. with the use of ServiceBase Class, represent the "old" way of developing service-like applications in .NET. This approach is available in .NET Framework. In new projects and in .NET 8+, it is recommended to use the Worker Service approach, described further below.

Worker Service Host

The "worker service" (Worker services in .NET) is a basically a modern way of creating a service-like application in .NET - not just Windows service, but eventually also a Linux daemon, etc. Rapid Toolkit for Sparkplug host applications and edge nodes can be hosted in the .NET worker service as well.

 

Sparkplug is a trademark of Eclipse Foundation, Inc. "MQTT" is a trademark of the OASIS Open standards consortium. Other related terms are trademarks of their respective owners. Any use of these terms on this site is for descriptive purposes only and does not imply any sponsorship, endorsement or affiliation.

See Also

Reference