Professional Communication
Software Development Tools

OPC Foundation member and certified logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions or issues. You do not have to own a commercial license in order to use the OPC Labs supportOur team is actively monitoring the forums, and provides replies as soon as possible.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible.

Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

Develop Sparkplug host applications and edge nodes.

Sparkplug is a trademark of Eclipse Foundation, Inc.

does EasySparkplug library support store and forward ?

More
20 Apr 2026 13:29 #14610 by support
Hello,

Rapid Toolkit for Sparkplug should fully support the Primary Host Application concept. If you do not get the results expected, then either you are doing something wrong - or there is a bug on our side.

The example code I provided, for local buffering, is already designed to work with Primary Host Application. The ProducerOnlineChanged event, which the example uses to do LockPublishing/UnlockPublishing, works both with and without the primary host application:

- Without the primary host application, the event is fired for changes reflecting whether the MQTT connection to the broker is running fine.
- With the primary host application, the event is fired according to the "online" state announced by the primary host application.

In order to work with the primary host application, you need to set a non-empty string to the PrimaryHostId on the edge node object. Do I understand it correctly that you are doing it already?

If so, the toolkit should monitor the status of the primary host application, and reflect it in the ApplicationOnline property (and using the ApplicationOnlineChanged event).
Are you saying the ApplicationOnline (which is initially 'false') goes to 'true' when you start your app, then it goes to 'false' when you stop it, but then it stays at 'false' when you start the app again? Such behavior would not be expected.

Best regards



 

Please Log in or Create an account to join the conversation.

More
20 Apr 2026 10:12 #14609 by berck
Hello,Thank you for the detailed explanation and the example — that helped clarify how local buffering can be implemented using LockPublishing / UnlockPublishing.I have a follow-up question regarding a slightly different scenario:I am not only interested in buffering when the MQTT broker connection is lost, but specifically when the Primary Host Application (Sparkplug STATE) goes offline.
  • Does EasySparkplug provide any mechanism (or recommended pattern) for store-and-forward behavior when the Primary Host Application is offline?
  • In other words, can metric updates be buffered while the host application is offline, and then published once it comes back online?
I have been experimenting with a simple EoN node (clock/counter device), and I noticed the following:
  • When I stop my host application, the node correctly detects it initially.
  • However, after that, I do not see further changes or updates in
    Code:
    edgeNode.ApplicationOnline
    .
Is this expected behavior? Or am I missing something in how the STATE handling should be implemented?Any guidance on how to properly handle host application offline/online transitions — especially in combination with buffering — would be appreciated.Thanks again for your support.Best regards

Please Log in or Create an account to join the conversation.

More
19 Apr 2026 13:48 #14608 by support
Hello,
thank you for your interest in our products.

Some application do not need this (I would call it "local" store-and-forward, since no intermediate component is involved). And yes, I agree, for some applications it is absolutely necessary.

Rapid Toolkit for Sparkplug currently does not have built-in support for this. In its basic form, it can be put together relatively easily, as the "building blocks", the necessary objects and methods, are there. We can think of providing full support in next version.

One possible approach currently is to use the LockPublishing and UnlockPublishing methods. When LockPublishing is called, any metric data updates in the edge node are simply accumulated in memory, and not really sent to the broker. When UnlockPublishing is called, the accumulated metric data (if any) are commonly sent to the server. So, it is possible to call LockPublishing when the connection to the broker is lost, and call UnlockPublishing when it is regained. The code below illustrates this. If you want to use, make sure to use the very recent NuGet package (5.83.1167 or later), because some minor change in the component was needed to make it work.

 
// This example shows how to locally store the data updates when the edge node is offline and automatically push the stored
// data to the broker when the edge node is back online.
//
// You can use any Sparkplug application, including our SparkplugCmd utility and the SparkplugApplicationConsoleDemo
// program, to subscribe to the edge node data. 
//
// Find all latest examples here: www.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// Sparkplug examples in C# on GitHub: github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, forum.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 Timer = System.Timers.Timer;
 
namespace SparkplugDocExamples.EdgeNode._EasySparkplugEdgeNode
{
    class CustomStoreAndForward
    {
        static public void Main1()
        {
            // Note that the default port for the "mqtt" scheme is 1883.
            var hostDescriptor = new SparkplugHostDescriptor("mqtt://localhost");
 
            // Instantiate the edge node object and hook the SystemConnectionStateChanged event.
            var edgeNode = new EasySparkplugEdgeNode(hostDescriptor, "easyGroup", "easySparkplugDemo");
            edgeNode.SystemConnectionStateChanged += (sender, eventArgs) =>
            {
                // Display the new connection state (such as when the connection to the broker succeeds or fails).
                Console.WriteLine($"{nameof(EasySparkplugEdgeNode.SystemConnectionStateChanged)}: {eventArgs}");
            };
 
            // Create a read-only data metric.
            var metric = SparkplugMetric.CreateIn(edgeNode, "ReadThisMetric")
                .ValueType<int>()
                .Writable(false);
 
            // Count how many data updates have been buffered while the edge node is offline.
            int metricBufferedCount = 0;
 
            // Hook the ProducerOnlineChanged event to handle system connection state changes.
            edgeNode.ProducerOnlineChanged += (sender, eventArgs) =>
            {
                var node = (EasySparkplugEdgeNode)sender;
 
                // Display the new producer online state.
                Console.WriteLine($"{nameof(EasySparkplugEdgeNode.ProducerOnlineChanged)}; {nameof(node.ProducerOnline)}={node.ProducerOnline}");
 
                // If the edge node is back online, unlock the metric the publishing to push all the buffered data to the
                // broker.
                if (node.ProducerOnline)
                    node.UnlockPublishing();
                // Otherwise, lock the publishing and start buffering the data updates.
                else
                {
                    node.LockPublishing();
                    metricBufferedCount = 0;
                }
            };
 
            metric.Starting += (sender, args) =>
            {
                // Lock the publishing initially (ProducerOnline will change to true when the connection to the broker, and
                // we will unlock then).
                edgeNode.LockPublishing();
 
                // Create a timer for pushing the data to the metric. In a real edge node or device, the activity may also come
                // from other sources.
                var timer = new Timer
                {
                    Interval = 1000,    // 1 second
                    AutoReset = true,
                };
 
                // Update the read data of the metric with random value whenever the timer interval elapses.
                var random = new Random();
                timer.Elapsed += (s, a) =>
                {
                    // If the edge node is online, always update the metric with new data.
                    if (edgeNode.ProducerOnline)
                        metric.UpdateReadData(random.Next());
                    // Otherwise, if the edge node is offline, and we have not buffered too many updates, update the metric
                    // with new data (which will be buffered until the edge node is back online).
                    else if (metricBufferedCount < 100)
                    {
                        metric.UpdateReadData(random.Next());
                        metricBufferedCount++;
                    }
                };
 
                // Associate the timer with the data variable.
                metric.State = timer;
 
                timer.Start();
            };
            metric.Stopped += (sender, args) =>
            {
                // Obtain the timer associated with the metric.
                var timer = (Timer)((SparkplugMetric)sender).State;
 
                // Stop the timer.
                timer.Stop();
            };
 
 
            // Start the edge node.
            Console.WriteLine("The edge node is starting...");
            edgeNode.Start();
 
            Console.WriteLine("The edge node is started.");
            Console.WriteLine();
 
            // Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the edge node...");
            Console.ReadLine();
 
            // Stop the edge node.
            Console.WriteLine("The edge node is stopping...");
            edgeNode.Stop();
 
            Console.WriteLine("The edge node is stopped.");
        }
    }
}
 



It should be considered how many data gets buffered, for two reasons:
    - If the MQTT disconnection is long, there will be ever increasing demand on memory to store the data. There should be some limit to that.
    - When UnlockPublishing is called, all accumulated metric data changes are published in a single payload. This works fine inside our library (the max size is really high), but there is a possibility that the payload size will exceed some limits on the broker or receiver side. 

This is currently handled in the example by not buffering more than 100 updates; extra ones are lost. 
    
Let me know you thoughts on this.

Best regards
 
The following user(s) said Thank You: berck

Please Log in or Create an account to join the conversation.

More
17 Apr 2026 07:26 #14606 by berck
I'm currently evaluating EasySparkplug and I’m trying to understand whether it supports a store-and-forward mechanism for handling temporary connectivity loss.Specifically:
  • Does EasySparkplug provide built-in store-and-forward functionality (buffering messages while the MQTT broker is unavailable)?
  • If yes, how is this configured or implemented?
  • If not, what would be the recommended approach to achieve store-and-forward when using EasySparkplug (e.g. external buffering, integration with OPC Publisher, custom logic, etc.)?
My use case involves ensuring no data loss when the connection to the broker drops temporarily, which is pretty critical in a Sparkplug B environment.Thanks in advance for any clarification.

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.446 seconds