OPCUADSOutput
Warning
The OPCUADataSource is only available in distributions where open62541 is installed.
The OPCUADSOutput DataSource can be used to stream application data over the OPC UA protocol. This allows interfacing the application with any OPC UA-based monitoring or control applications.
Warning
This DataSource writes to OPCUA synchronously on the same thread as the MARTe RealTimeThread, which may negatively impact the real-time performance of the application. Decoupling mechanisms are discussed later in the tutorial (see the sections on RealTimeThreadAsyncBridge and RealTimeThreadSynchronisation).
The DataSource only supports structured types as defined in Structured types.
Given that the OPCUA server ports need to be unique in order to avoid port clashes, the configuration file ../Configurations/MassSpring/RTApp-MassSpring-40.cfg will be automatically updated from a Makefile.cfg.
The OPCUA types are hosted using a MARTe2 OPCUAServer, which in this example is instantiated in the same configuration file as the application.
The objective of this example is to monitor the application data using OPCUA.
Makefile.cfg. 1+OPCUAServer = {
2 Class = OPCUA::OPCUAServer
3 StackSize = 1048576
4 CPUs = 0x1
5 Run = 1
6 Authentication = None
7 Port = OPCUA_PORT
8 AddressSpace = {
9 MassSpringDemo = {
10 Type = MonitorControl
11 NumberOfElements = 1
12 }
13 }
14}
15$MassSpringApp = {
16 Class = RealTimeApplication
1 +GAMWriter = {
2 Class = IOGAM
3 InputSignals = {
4 Time = {
5 DataSource = DDB1
6 Type = uint32
7 }
8 ReferencePosition = {
9 DataSource = DDB1
10 Type = float64
11 DataSource = OPCUAWriter
12 Type = MonitorControl
13 Trigger = 1
14 TriggerSignal = "Monitor.Time"
15 }
16 }
17 }
Makefile.cfg. 1 +OPCUAWriter = {
2 Class = OPCUADataSource::OPCUADSOutput
3 Address = OPCUA_FULL_URL
4 Authentication = None
5 Signals = {
6 Monitor = {
7 NamespaceIndex = 1
8 Type = MonitorControl
9 Path = "MassSpringDemo"
10 }
11 }
12 }
Running the application
Start the application with:
make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-40_Gen.cfg -l RealTimeLoader -s State1
Once the application is running, inspect the screen output and verify that the application is running without any issues. The log should show entries similar to the following:
...
$ [Warning - Threads.cpp:181]: Failed to change the thread priority (likely due to insufficient permissions)
$ [Information - RealTimeLoader.cpp:111]: Started application in state State1
$ [Information - MARTeApp.cpp:135]: Application starting
$ [Information - LoggerBroker.cpp:152]: Time [0:0]: 0
$ [Information - LoggerBroker.cpp:152]: Time [0:0]:1000000
...
Open another terminal and check that the OPCUA records are being updated with the application data by running another MARTe2 application.
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-40-Monitor_Gen.cfg -l RealTimeLoader -s State1
The output should be similar to the following:
$ [Information - LoggerBroker.cpp:152]: Monitor.Time [0:0]:115290000
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.ReferencePosition [0:0]:1.972000
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.Position [0:0]:1.916281
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.PositionDisturbed [0:0]:1.911329
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.PositionFiltered [0:0]:1.917298
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.PositionM [0:0]:1.923568
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.Velocity [0:0]:-0.125965
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.VelocityM [0:0]:-0.194689
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.PositionErr [0:0]:-0.054702
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.Force [0:0]:13.292912
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.ForceAverage [0:0]:15.260224
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.ForceStdDev [0:0]:2.372243
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.ForceMax [0:0]:19.272639
$ [Information - LoggerBroker.cpp:152]: Monitor.Control.ForceMin [0:0]:9.151042
Note
You can also use any OPCUA client to connect to the MARTe2 OPCUA server and monitor the application data.
For example, if the Python opcua library is installed on your system, you can use the Python script located in ../Test/Integrated/opcua_monitor.py to connect to the server and print the values of the nodes being written by the application.
OPCUA_PORT=`awk '/\+OPCUAServer/,/}/ {if ($1=="Port") print $3}' ../Configurations/MassSpring/RTApp-MassSpring-40_Gen.cfg`;echo "OPCUA_PORT=$OPCUA_PORT"
python3.6 ../Test/Integrated/opcua_monitor.py -p $OPCUA_PORT -s MassSpringDemo
As explained in the OPCUADSOutput documentation, it is possible to associate a timestamp signal with one or more output signals. When a timestamp signal is configured, its value is used to populate the OPCUA SourceTimestamp field when writing the associated nodes.
Exercises
Ex. 1: Statistics monitoring
Monitor the statistics signals as part of another OPCUA structure.
Edit the file
../Configurations/MassSpring/RTApp-MassSpring-41.cfgand add a new DataSource instance to write the signalMonitorPerffrom theGAMWriterPerfIOGAM.
Important
The OPCUADSOutput does not allow writing multiple structured signals, so a new DataSource needs to be added to the configuration file.
Start the sender application.
make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-41_Gen.cfg -l RealTimeLoader -s State1
Open another terminal and check that the OPCUA records are being updated with the application data by running another MARTe2 application.
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-41-Monitor_Gen.cfg -l RealTimeLoader -s State1
Solution
The solution is to add a new OPCUADSOutput instance.
1 +OPCUAWriterPerf = {
2 Class = OPCUADataSource::OPCUADSOutput
3 Address = OPCUA_FULL_URL
4 Authentication = None
5 Signals = {
6 MonitorPerf = {
7 NamespaceIndex = 1
8 Type = MonitorPerformance
9 Path = "MassSpringDemoPerformance"
10 }
11 }
12 }
The outputs from the monitor application (running on the other terminal) should be similar to the following:
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMHist.ReadTime [0:0]:9848
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMHist.ExecTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMHist.WriteTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathExpr.ReadTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathExpr.ExecTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathExpr.WriteTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMConversion.ReadTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMConversion.ExecTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMConversion.WriteTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMFilterMovingAvg.ReadTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMFilterMovingAvg.ExecTime [0:0]:9849
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMFilterMovingAvg.WriteTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTrigger.ReadTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTrigger.ExecTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTrigger.WriteTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTriggerSecond.ReadTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTriggerSecond.ExecTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMMathTriggerSecond.WriteTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMDisplay.ReadTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMDisplay.ExecTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMDisplay.WriteTime [0:0]:9850
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMWriter.ReadTime [0:0]:9851
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMWriter.ExecTime [0:0]:9851
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.GAMWriter.WriteTime [0:0]:9896
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTime [0:0]:10001
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeAverage [0:0]:9998
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeMovingAverage [0:0]:9998.599052
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeStdDev [0:0]:141
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeMax [0:0]:10076
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeMin [0:0]:9918
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.CycleTimeHistogram [0:10]:{ 2 1 0 0 0 137 0 0 1 2 1 }
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.FreeTimeHistogram [0:10]:{ 0 0 1 0 0 1 0 1 0 29 112 }
$ [Information - LoggerBroker.cpp:152]: MonitorPerf.Perf.Thread1.GAMsExecutionTime [0:0]:58
Ex. 2: Timestamping
The objective of this exercise is to use another signal to timestamp OPCUA against a given signal instead of the timestamp that is automatically set by the library.
Edit the file
../Configurations/MassSpring/RTApp-MassSpring-42.cfgand add a new signal namedAbsoluteTimeStampto theOPCUAWriterDataSource.Make sure that the signal contains the property
DefaultTimestampSignalset to1.Modify the IOGAM
GAMWriterto copy theAbsoluteTimeStampsignal from the DDB1 into the new signal in the OPCUAWriter DataSource.
Note
As explained in the LinuxTimer documentation, the AbsoluteTimeStamp signal is the counter multiplied by the period (counting from the Epoch). If you are running on a CODAC Core System, you may use the TcnTimeProvider to obtain the absolute time.
Start the sender application.
make -C ../Configurations/MassSpring/ -f Makefile.cfg
./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-42_Gen.cfg -l RealTimeLoader -s State1
Open another terminal and check that the OPCUA records are being updated with the application data by running the following Python application (assuming that the Python
opcualibrary is installed on your system):
OPCUA_PORT=`awk '/\+OPCUAServer/,/}/ {if ($1=="Port") print $3}' ../Configurations/MassSpring/RTApp-MassSpring-42_Gen.cfg`;echo "OPCUA_PORT=$OPCUA_PORT"
python3.6 ../Test/Integrated/opcua_monitor.py -p $OPCUA_PORT -s MassSpringDemo --print_time_stamps
Solution
The solution is to add the AbsoluteTime to the DataSource and set the DefaultTimestampSignal property.
1 +OPCUAWriter = {
2 Class = OPCUADataSource::OPCUADSOutput
3 Address = OPCUA_FULL_URL
4 Authentication = None
5 Signals = {
6 AbsoluteTime = {
7 Type = uint64
8 DefaultTimestampSignal = 1
9 }
10 Monitor = {
11 NamespaceIndex = 1
12 Type = MonitorControl
13 Path = "MassSpringDemo"
14 }
15 }
16 }
The outputs from the monitor application (running on the other terminal) should be similar to the following (note that the timestamps are near the Epoch):
- MassSpringDemo
- Time:22100000 | src_ts:1970-01-01 01:51:12.614091 | srv_ts:None
- Control
- ReferencePosition:0.8280000000000002 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- Position:0.7308889950318065 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- PositionDisturbed:0.7329734151785503 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- PositionFiltered:0.726084634618206 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- PositionM:0.7292609341046103 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- Velocity:0.3796677587971925 | src_ts:1970-01-01 01:51:12.614101 | srv_ts:None
- VelocityM:0.3941233025599467 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- PositionErr:-0.10212367380322629 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- Force:7.6191469671596765 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- ForceAverage:4.3079819265028485 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- ForceStdDev:1.8327930024983468 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- ForceMax:7.6191469671596765 | src_ts:1970-01-01 01:51:12.614111 | srv_ts:None
- ForceMin:1.325131643231978 | src_ts:1970-01-01 01:51:12.614121 | srv_ts:None