Pertaining to the discussion started in Issue #957, this Pull Request seeks to add capabilities for Subscription to sequentially invoke its MonitoredItem and FastDataChange callbacks to maintain order properly. The key goal is to avoid out-of-order monitored items. To this effect, the Sequential Publishing capability is added to Subscription Resolves #957 ### SequentialPublishing A toggle which forces Subscription to raise callbacks only in a sequential order by the SequenceNumber of the incoming message. It limits the number of tasks that can process publish responses to 1, and enforces a strict +1 Sequence Number requirement before releasing a message. #### Backwards compatibility The new property has been added as DataMember at the end to maintain backwards compatibility with the DataContract, where such may be used. The relevant copy constructor has also been updated. The feature is disabled by default, so the existing behavior is the default. Users must explicitly set the property on the Subscription object to enable these new features. #### Leveraging existing "time machine" in SaveMessageInCache There is already a mechanism for re-ordering messages that entered in the wrong order. This change makes use of that existing "time machine" which sorts messages into chronological order (by the sequence number) to only pull off messages with a proper +1 sequence number each time. #### KeepAlive messages advancing sequence number KeepAlive messages do not contain notifications, and do not enter the proper branch in the code to be pulled out. They will not interrupt sequential flow. Since the next message in the sequence with data will "re-use" that sequence number, it can be expected to maintain sequence. #### Delayed messages When sequential publishing is enabled, if a message is genuinely missing from the sequence, it will "hold up" the messages until it either arrives or is pulled out of the incoming messages list by being too old. In either case, it is considered "processed" for the sequence purposes at that point and the rest of the messages may proceed. The automatic republish request mechanism is also leveraged here for that purpose as well. #### SequenceNumber roll-over after max value As specified in the OPC UA spec, at 1ms publishing, this would take almost 50 days of uptime. Naturally at any "reasonable" publishing interval this time is even longer. The matter itself is also not addressed throughout the whole existing code that handled it before this change. (Though some consideration can be seen in some places, such as placing messages at the end of the incoming message linked list if no better place was found) #### Locking on m_cache to get the semaphore I've considered making a separate locking object for the semaphore management, but decided it is not needed. The message worker would need to obtain m_cache lock later anyway, and momentarily obtaining it is not harmful for the overall flow. Only in cases of serious contention between new PublishResponses coming in and workers attempting to work would this cause any problems. #### Callbacks that "take a long time" This change will naturally suffer from callbacks that take a long time to invoke. In our use case, each callback message is passed into an ActionBlock for handling, so our callbacks are fast. But Other clients may invoke much more code on these callbacks. Sequential publishing means this capacity is possibly bottlenecked. Sequential publishing should be used for its intended purpose and with callbacks that do not hold up their calling thread for a long time. To ensure proper sequential callbacks, they cannot just be started in order, they have to be processed fully in order. #### Changing this property while the Subscription is active There is limited support to on-the-fly changes to this property, and this is not the intended use case. It is meant to be set once and ideally never changed while messages are being processed. If users need certain items properly sequenced and some items they can accept out-of-order or ignore outdated messages, they can do this by defining two separate Subscription objects, same as they would if they needed different Publishing Intervals. #### Why semaphore? At first I had considered using ActionBlock from TPL DataFlow and limiting its concurrency to the number of desired message workers, but I had opted not to add a dependency on DataFlow only for this purpose. (Referencing all of DataFlow just for ActionBlock's automatic concurrency limiting is wasteful. I had also considered using a Limited Concurrency Task Scheduler implementation and queueing the message workers on that instead of the normal ThreadPool via Task.Run, but that is similarly more complex than it needs to be. #### Why async Task? What was wrong with Void? While being on a new Task from the ThreadPool anyway, the worker would still occupy a task from the ThreadPool if it was using the synchronous Wait() method, which is a blocking call. By using WaitAsync, the ThreadPool task goes "back" to the ThreadPool until the semaphore is released. ### Disclaimer - I am not an OPC UA expert Please do not assume I have "done all my homework" with regard to these changes. I have attempted to learn as much as I can in the time I was working on this change, but I fully expect to have missed a few spots and specifics of the OPC UA protocol. I have done some testing in our application to verify the out-of-order problem is solved, and that no significant delay is introduced by the additional work done to facilitate this.
Official OPC UA .NET Standard Stack from the OPC Foundation
Overview
This OPC UA reference implementation is targeting .NET Standard specification.
.NET Standard allows to develop apps that run on all common platforms available today, including Linux, iOS, Android (via Xamarin) and Windows 7/8/8.1/10 (including embedded/IoT editions) without requiring platform-specific modifications.
One of the reference implementations inside this project has been certified for compliance through an OPC Foundation Certification Test Lab to prove its high quality. Fixes and enhancements since the certification process have been tested and verified for compliance using the Compliance Test Tool (CTT) V1.04.
Furthermore, cloud applications and services (such as ASP.NET, DNX, Azure Websites, Azure Webjobs, Azure Nano Server and Azure Service Fabric) are also supported.
More samples based on the official Nuget packages can be found in the OPC UA .NET Standard Samples repository. For development there is also a preview Nuget feed available.
For more information and license terms, see here.
Features included
Core and Libraries
- Fully ported Core OPC UA Stack and Libraries (Client, Server, Configuration, Complex Types & GDS assemblies).
- Reference sample Server and Client.
- X.509 Certificate support for client and server authentication.
- SHA-2 support (up to SHA512) including security profile Basic256Sha256, Aes128Sha256RsaOaep and Aes256Sha256RsaPss for configurations with high security needs.
- Anonymous, username and X.509 certificate user authentication.
- UA-TCP & HTTPS transports (client and server).
- Reverse Connect for the UA-TCP transport (client and server).
- Folder & OS-level (X509Store) Certificate Stores with Global Discovery Server and Server Push support.
- Sessions and Subscriptions.
- PubSub Library.
Samples and Nuget packages
- OPC UA Reference Server and Reference Client for .NET Framework.
- OPC UA Console Reference Server for .NET Core.
- The OPC UA Reference Server has been certified for compliance through an OPC Foundation Certification Test Lab. Fixes and enhancements since the certification process have been tested and verified for compliance using the Compliance Test Tool (CTT) Version 1.03.340.380. All releases are verified for compliance with the latest official Compliance Test Tool by the maintainers.
- OPC UA Console Reference Publisher and Console Reference Subscriber for .NET Core and .NET Framework.
- An official OPC UA Nuget package of the core, client, server and configuration libraries is available for integration in .NET projects.
Project Information
General Project Info
Build Status
Code Quality
Getting Started
All the tools you need for .NET Standard come with the .NET Core tools. See Get started with .NET Core for what you need.
How to build and run the reference samples in Visual Studio on Windows
- Open the UA Reference.sln solution file using Visual Studio 2017.
- Choose a project in the Solution Explorer and set it with a right click as
Startup Project. - Hit
F5to build and execute the sample.
How to build and run the console samples on Windows, Linux and iOS
This section describes how to run the ConsoleReferenceServer sample application.
Please follow instructions in this article to setup the dotnet command line environment for your platform. As of today .NET Core SDK 2.1 is required for Visual Studio 2017 and .NET Core SDK 3.1 is required for Visual Studio 2019.
Prerequisites
- Once the
dotnetcommand is available, navigate to the root folder in your local copy of the repository and executedotnet restore 'UA Reference.sln'. This command calls into NuGet to restore the tree of dependencies.
Start the server
- Open a command prompt.
- Navigate to the folder Applications/ConsoleReferenceServer.
- To run the server sample type
dotnet run --project ConsoleReferenceServer.csproj -a.- The server is now running and waiting for connections.
Remarks
Self signed certificates for the sample applications
All required application certificates for OPC UA are created at the first start of each application in a directory or OS-level certificate store and remain in use until deleted from the store. Please read Certificates for more information about certificates and stores
Local Discovery Server
By default all sample applications are configured to register with a Local Discovery Server (LDS). A reference implementation of a LDS for Windows can be downloaded here. To setup trust with the LDS the certificates need to be exchanged or registration will fail.
Contributing
We strongly encourage community participation and contribution to this project. First, please fork the repository and commit your changes there. Once happy with your changes you can generate a 'pull request'.
You must agree to the contributor license agreement before we can accept your changes. The CLA and "I AGREE" button is automatically displayed when you perform the pull request. You can preview CLA here.