Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only publish changed data. #3

Open
JimmyPesto opened this issue May 28, 2019 · 11 comments
Open

Only publish changed data. #3

JimmyPesto opened this issue May 28, 2019 · 11 comments
Assignees
Labels
feature-request New feature or request

Comments

@JimmyPesto
Copy link

JimmyPesto commented May 28, 2019

I would like to have faster updates on my process data than 500ms. Even if this timeout is adjustable I'm not interested in data that has not changed.
Depending on how much data is published, maybe this could increase performance.

Newly connected clients subscribing the "changed data" need to be initialized with the current state of all values.
Maybe a message to a fixed topic on the PLC side can trigger publishing all data. (This topic could also be included in a retained message of the "changed data" topic so new clients get this information after subscribing.)

@martinboers
Copy link
Member

Hi Jimmy. Thanks for the feature request.

If your data is updating faster than 500ms, then I'm guessing this is generated from a program running in a real-time task on the PLC?
The potential problem with this is that this MQTT Client app is implemented (partly) as a PLCnext RSC service. The potential latency of the RSC interface means that these services aren't suitable for hard-real-time programs. So if your data is (potentially) changing at faster than 500ms, and you want to publish every change, then this app is probably not the best solution.

But if you want to try, then there are a couple of possibilities:

  1. If your real-time code is written using PLCnext Engineer, then we are hoping to have an MQTT Client Function Block library available in the next few months. The design of this library has not been finalised, but I expect that you will be able to publish MQTT data from your PLC program whenever you want - e.g. only when data changes.

  2. If your real-time code is written in C++, then you could subscribe to the MQTT Client RSC service directly from your C++ component, and publish your data to the RSC service whenever you want - e.g. only when data changes.

Would either of these suit your application?

@OWarneke OWarneke added the feature-request New feature or request label Jun 6, 2019
@JimmyPesto
Copy link
Author

JimmyPesto commented Sep 3, 2019

Hey Martin,

I hope my question fits here, but I'm wondering why the MQTT-Client is based on a RSC service instead of a PLM component providing a program running on the ESM? This should then have hard real-time access to GDS as far as I understand.
How would a PLM C++ component access the GDS?

Just to get this right, accessing the GDS with RSC is the bottleneck with the current MQTT application here?

Is there an update on the C# MQTT-Client?

@martinboers
Copy link
Member

martinboers commented Sep 3, 2019

Hi Jimmy,

Great questions.

As you are probably aware, the MQTT Client app in the PLCnext Store is built from two separate Github projects - this "MQTT GDS Connector" project, and the "MQTT Client" project.

To answer your first question, I will start with the MQTT Client project, which is the foundation that we built the app on.

MQTT Client

As described in the "MQTT Diary" video series, we wanted to implement the Paho MQTT Client library on the PLCnext Control, so we could make MQTT Client services available to other Components and Programs on the PLC.

The MQTT Client on the PLCnext Control is a "service" - it doesn't do any cyclic processing on its own, it only responds to requests from clients of that service.


Aside: A Note about terminology.

That last sentence in the previous section is confusing, and it's not my fault :-)

Our "MQTT Client" project is called that because it implements the Paho MQTT C++ Client library. We couldn't really call it anything else, because it is an MQTT Client. However (as you will see below), it made sense to implement this on the PLC as an RSC Service. And this RSC Service, like all RSC Services on the PLC, can have one or more RSC Clients. So the MQTT Client is an RSC Service that can have RSC Clients (please read that again if it didn't make sense - it's important!). This combination of "clients" and "servers" can get confusing. I will try to be clear and use the terms "MQTT Client" or "RSC Service" when referring to the MQTT Client/RSC Service, and the term "RSC Client" when referring to RSC Clients (like the MQTT-GDS Connector).


Since the MQTT Client is an RSC service, it can respond to RSC client requests pretty much as fast as those clients can send them. We haven't tested it, but I expect that it could handle hundreds of RSC client requests each second on an AXC F 2152. But, at the end of the day, the MQTT Client is exchanging messages with an MQTT Server over a (potentially low bandwidth) network. So the network is likely to be the bottleneck in the whole process, not the speed of the MQTT Client/RSC Service itself.

Why did we decide to implement the MQTT Client as an RSC Service? Well, the MQTT Client itself does not need to do any real-time cyclic processing, so it does not need to use the ESM, or the Task Manager, or the Linux PREEMPT-RT patch. Also, it did not make sense to limit the MQTT Client to use GDS variables as the only source and destination of message data in the PLC. We wanted to cater for at least the following use cases:

  1. PLCnext Engineer projects that want to access Paho MQTT Client functions using IEC 61131 Function Blocks.
  2. C++ applications that want to access Paho MQTT Client functions using an RSC Service.
  3. Applications written in other languages (e.g. Rust), that can access RSC services on the PLC and need MQTT Client functions.
  4. Applications that exchange GDS data with an MQTT Server, in a similar way to the Proficloud TSD service.

All of these use cases can be satisfied by wrapping the Paho MQTT C++ Client library as an RSC Service on the PLC. Cases 2 and 3 in the above list don't need anything else, and cases 1 and 4 can be achieved with the addition of the following components:

  1. A wrapper on the MQTT Client/RSC service that allows MQTT Client calls to be made using IEC 61131 Function Blocks (this wrapper can be written using C# ... discussed later).
  2. An MQTT-GDS Connector that exchanges data between GDS variables and topics on an MQTT Server.

The second item in the above list is this MQTT-GDS Connector project.

MQTT-GDS Connector

This project simply demonstrates one possible use of the MQTT Client/RSC Service. This project uses GDS variables as the source and destination of MQTT message data, similar to the way that the Proficloud TSD service uses GDS variables as the source of data for the Proficloud time-series database.

It is possible to imagine other endpoints for MQTT message data (e.g. OPC UA variables, Sqlite database records), and each of these could be connected to the MQTT Client using "MQTT Connector" components for these endpoints, if anyone cares to develop them.

In the case of the MQTT-GDS Connector, this was developed as an ACF component, partly because it was intended to be similar to the Proficloud TSD service which sends data over the network cyclically, but no more than a few times every second, and this does not need the performance of the ESM.

IEC 61131 wrapper (C# MQTT Client)

We originally intended to build the IEC 61131 function blocks for the MQTT Client shortly after releasing the MQTT Client app in the PLCnext Store. However, we hit some technical challenges, which required input from our PLCnext Runtime development team. Unfortunately this task has not progressed as quickly as we hoped. I'm not sure when we will have something completed, but I still hope it will be before the end of the year.

Writing your own RSC Client

If the MQTT-GDS Connector does not suit your needs, and you can't wait for the IEC 61131 function block library, then you can write your own RSC Client that uses the MQTT Client/RSC service.

One common request is for MQTT messages to be sent when GDS data changes, rather than cyclically. This is very achievable. I would recommend doing this from a C++ component (either an ACF or a PLM component - a PLM component can access the GDS in exactly the same way as shown in this project). Rather than scan the GDS variables cyclically, this component would subscribe to another RSC service called the "Data Subscription Service". Using this service, the component will be notified of every change to any GDS variable that is registered with the Data Subscription RSC Service. I am not sure if there is currently any public documentation for this RSC Service - if you are interested in using it, please let me know and I'll see what we can give you. I think that the component still needs to check subscriptions cyclically, but in this case the cycle time of the component Worker Thread can be quite short (although still non-deterministic) because it is not reading the entire set of GDS variables on every cycle - it is only checking for new notifications from the subscription service.

Finally, it is not recommended to call any RSC service from a real-time C++ program running on the ESM, because the program will probably trip the Task watchdog timer. Any RSC service call has the potential to be just too slow for a deterministic, real-time task. And real-time C++ programs don't need to use RSC services to access the GDS - they can define their own GDS Port variables, and then data can be copied automatically between these Port variables and Port variables defined in other programs. The connections between port variables in different programs are configured using .gds.config files. Components can also define GDS ports, which can also be connected to other GDS ports using .gds.config files. But both Program and Component port definitions must be hard-coded - it is currently not possible to create (or destroy) GDS ports while the PLCnext process is running.

Hope this helps.

@JimmyPesto
Copy link
Author

JimmyPesto commented Sep 9, 2019

Hey Martin,
thanks for your detailed answer!

The "MQTT Diary" video series is really great. I watched this before and started to get a picture of this huge framework and its components.
I see why MQTT-Client is a RSC-Service and MQTT-GDS Connector is a RSC-Client.
I'm still struggling with the difference between an implementation of a GDS-Connector as a ACF or PLM.
They would both have (nearly?) the same performance in accessing the GDS, but a PLM runs as a real-time C++ program on the ESM and therefore should not call RSC services because of potential watchdog timeouts?

So a possible solution would be to build an own RSC-client-component which would cyclic check the "Data Subscription Service" for changes in the monitored GDS-variables.
If changed values are detected it would then call the RSC-service/MQTT-Client to send an update.

Thanks again for this great feedback! ;)

@martinboers
Copy link
Member

Hi Jimmy,

Thanks for the feedback.

Regarding the possible solution that you suggest, I will first address the question of ACF vs. PLM:

We are currently considering a tutorial series in a similar format to the Sample Runtime series, but on the topic of "Extension Components". If that series gets made, it will show the differences between ACF and PLM. But I will try to go through some of the steps here.

First, it is important to understand that on a PLCnext Control we talk about Components and Programs, and it is important to understand the difference.

In the context of PLCnext Control:

  • Components - cannot be scheduled to run on the ESM, and so are not considered "real-time".
  • Programs - can only be run on the ESM.

But how does this relate to "ACF" and "PLM"?

Imagine that you are designing an "Automation Runtime Platform" (ARP) to run on a Linux operating system, just like the one that is running on a PLCnext Control.

PART 1 - Components

One of the first requirements is that developers should be able to extend your ARP with custom code that will run in a non-real-time context - so they can implement things like an OPC UA server, or an MQTT Client service. So:

  1. You decide that these bits of code that will run in a non-real-time context will be called "Components".
  2. You build an "Application Component Framework" (ACF) into the ARP. The ACF includes:
    • The definition of a C++ base-class called ComponentBase, that custom ARP Components must inherit.
    • A simple way for the ACF to find custom Components and start them - for example, an XML file containing a path to the Shared Object library containing the custom component. Let's call this XML file an "ACF Configuration File", and give it the extension .acf.config

So now developers can build what we might call "ACF Components", which can be instantiated and run on the ARP in a non-realtime context. Perfect for many applications and services.

PART 2 - Programs

But ... now we decide that developers should also be allowed to build C++ code that runs on the ESM, for deterministic real-time applications. How should we do this?

Let's make use of the Component framework that we already built in Part 1. We now invent a special type of Component, which includes a "Program Provider". This special type of Component provides Programs that can be instantiated and run on the ESM.

Important point: The Component will not run on the ESM, only the Programs that this type of Component provides.

We will call this special type of component a "Program Component", and it must inherit from ComponentBase (like all other Components), but also from ProgramComponentBase.

Because this Program Component inherits from ComponentBase, it can be started by the ACF just like every other Component, by providing an .acf.config file in the correct format. But the ACF doesn't know anything about "Programs", or how to instantiate them on the ESM, so the Programs defined by the Program Component will never be instantiated ... the ACF (on its own) just isn't aware that such a things as "Programs" even exist.

Since the ACF on its own isn't enough to instantiate programs, we need to add something else to the ARP. So we add a "Program Library Manager" (PLM) to the ARP, which runs alongside the ACF. The PLM knows about Program Components, and how to instantiate the Programs that these special Components provide.

So we want the PLM - not the ACF - to instantiate our Program Component, and then the PLM can instantiate the Program(s) that are provided by that Component. We can tell the PLM about our Program Component by replacing the .acf.config file with a .plm.config file. This XML file includes not only the path to the Shared Object library containing the Program Component, but also all the information about what Programs are provided by that Component, and even what GDS ports are defined for those Program(s).

PART 2a - Programs are Special

When designing an application, it must be remembered that Program instances run in a Task on the ESM, and each Task is expected to complete its execution within a fixed period of time (usually much less than 1 second). This means that Programs should never make blocking calls, and should avoid making other calls that could prevent the task from completing in the maximum expected time. This includes calls to non-realtime Components, like RSC services.

But - a Program Component (or "PLM Component") that provides Programs is not running on the ESM, and so does not have the same execution time restrictions as a Program. This fact can be used by developers to get around the real-time restrictions on Programs - for example, a PLM Component can create a connection to a database, and then execute synchronous (blocking) queries on that database on behalf of the Programs that the Component provides. Because every Program has a reference to the Component that provided it, data can easily be exchanged between a Program and its associated PLM Component.


So to answer your question:

They [ACF and PLM components] would both have (nearly?) the same performance in accessing the GDS ...

Yes. ACF and PLM components have identical performance in every respect.

... but a PLM runs as a real-time C++ program on the ESM and therefore should not call RSC services because of potential watchdog timeouts?

Not quite. The PLM Component does not run in real-time on the ESM - it is the Programs provided by the PLM Component that run on the ESM. PLM components can call RSC services just like "standard" ACF components. But the Programs provided by PLM components should not call RSC services, because of potential watchdog timeouts.

@martinboers
Copy link
Member

... and:

So a possible solution would be to build an own RSC-client-component which would cyclic check the "Data Subscription Service" for changes in the monitored GDS-variables.
If changed values are detected it would then call the RSC-service/MQTT-Client to send an update.

Yes, that's exactly right. This RSC-client-component could be either an ACF component (like our GDS connector), or a PLM component. If your solution requires the use of Programs, then it must be a PLM component. If (like our GDS connector) your solution does not need real-time Programs, then the component can be either an ACF or PLM component.

@JimmyPesto
Copy link
Author

Again, thanks for that detailed explanation.

This fact can be used by developers to get around the real-time restrictions on Programs - for example, a PLM Component can create a connection to a database, and then execute synchronous (blocking) queries on that database on behalf of the Programs that the Component provides. Because every Program has a reference to the Component that provided it, data can easily be exchanged between a Program and its associated PLM Component.

As my C++ skills are lacking I'm still wondering how this is done in a non blocking way.
The Program could call a function of its PLM Component like "queue this data and process/send it when you are able to".
And again this Component is based on an own cycle processing the queue?

As for the moment, I'm only looking for a way to publish changed data from GDS as fast as possible. So an ACF component with a fast cycle time and access to the "Data Subscription Service" would totally do the job.
When the "Data Subscription Service" is finished, is it published here on GitHub?

@martinboers
Copy link
Member

Hi Jimmy,

The Program could call a function of its PLM Component like "queue this data and process/send it when you are able to".

Yes, that's the idea. It's no good having a Program calling a (potentially) blocking method on its Component, because this would not solve the problem. So the program must either call a Component member function that doesn't block - like you suggest - or communicate with the Component in some other suitable way (e.g. reading and writing Component member variables directly)

And again this Component is based on an own cycle processing the queue?

Yes. All programs have their "Execute" method called whenever the corresponding ESM Task is executed. Components can implement their own cyclic execution using a Worker Thread, just like the GDS Connector does. But this Worker Thread is a non-real-time thread, which has no relationship to the Program task, or to the ESM. So there is likely to be a large "jitter" in the Component Worker Thread callback frequency, but since (in our case) MQTT messages will probably be sent over an Internet connection to a remote MQTT Broker, precise Worker Thread timing is not so important.

So an ACF component with a fast cycle time and access to the "Data Subscription Service" would totally do the job.

Yes, I agree.

When the "Data Subscription Service" is finished, is it published here on GitHub?

This service exists already on the PLC - it's part of the firmware - but there is no public documentation for this service at the moment. I will check what we can give you.

@martinboers
Copy link
Member

martinboers commented Sep 19, 2019

Hi @JimmyPesto, can you please confirm that it's OK for me to send you an email at the address that you used to register on the PLCnext Community?
I have some basic documentation for the Subscription RSC Service that I can send you.

@JimmyPesto
Copy link
Author

This would be great, thanks.

@martinboers
Copy link
Member

This feature has now been added to version 1.2.0 in the PLCnext Store.
The source code will appear in Github in the coming weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants