... | @@ -1164,237 +1164,159 @@ elt.log.CiiLogManager.set_log_level(name_or_logger: Union\[str, logging.Logger\] |
... | @@ -1164,237 +1164,159 @@ elt.log.CiiLogManager.set_log_level(name_or_logger: Union\[str, logging.Logger\] |
|
|
|
|
|
### PYBIND errors \[ICD waf build\]
|
|
### PYBIND errors \[ICD waf build\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Trying to build your MAL Application, you get errors like below related to the PYBIND module.
|
|
Trying to build your MAL Application, you get errors like below related to the PYBIND module.
|
|
|
|
|
|
|
|
```
|
|
|
|
icd/python/bindings/src/ModProto-benchmark.cpp:18:25: error: expected initializer before ‘-’ token
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>icd/python/bindings/src/ModProto-benchmark.cpp:18:25: error: expected initializer before ‘-’ token</p><p>PYBIND11_MODULE(ModProto-benchmark, modproto-benchmark) {</p></th></tr></thead><tbody></tbody></table>
|
|
PYBIND11_MODULE(ModProto-benchmark, modproto-benchmark) {
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Check the name of your ICD file:
|
|
Check the name of your ICD file:
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>> find icd</p><p>icd</p><p>icd/wscript</p><p>icd/src</p><p>icd/src/proto-benchmark.xml</p></th></tr></thead><tbody></tbody></table>
|
|
```
|
|
|
|
> find icd
|
|
|
|
icd
|
|
|
|
icd/wscript
|
|
|
|
icd/src
|
|
|
|
icd/src/proto-benchmark.xml
|
|
|
|
```
|
|
|
|
|
|
The icd file name contains a minus, which is actually reflected in the above error message.
|
|
The icd file name contains a minus, which is actually reflected in the above error message.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rename the file to something like this:
|
|
Rename the file to something like this:
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>> find icd</p><p>icd</p><p>icd/wscript</p><p>icd/src</p><p>icd/src/protobenchmark.xml</p></th></tr></thead><tbody></tbody></table>
|
|
```
|
|
|
|
> find icd
|
|
|
|
icd
|
|
|
|
icd/wscript
|
|
|
|
icd/src
|
|
|
|
icd/src/protobenchmark.xml
|
|
|
|
```
|
|
|
|
|
|
In general, due to the many code generation steps taking place, your freedom in ICD file naming is limited.
|
|
In general, due to the many code generation steps taking place, your freedom in ICD file naming is limited.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### multiple XMLs found \[ICD waf build\]
|
|
### multiple XMLs found \[ICD waf build\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Trying to build your ICD module, you see this error:
|
|
Trying to build your ICD module, you see this error:
|
|
|
|
|
|
|
|
```
|
|
|
|
Waf: Entering directory \`/home/eltdev/repos/hlcc/build'
|
|
|
|
Error: multiple XMLs found, just one supported.
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> Waf: Entering directory \`/home/eltdev/repos/hlcc/build'
|
|
|
|
> Error: multiple XMLs found, just one supported.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while in fact you have only one XML file in your ICD directory.
|
|
while in fact you have only one XML file in your ICD directory.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Check the file name of your ICD file:
|
|
Check the file name of your ICD file:
|
|
|
|
|
|
make sure it starts with an uppercase letter.
|
|
make sure it starts with an uppercase letter.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The error message is misleading (will be improved, [ECII-426](https://jira.eso.org/browse/ECII-426)).
|
|
The error message is misleading (will be improved, [ECII-426](https://jira.eso.org/browse/ECII-426)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The code generator for malicd_topics fails when the ICD file name starts with lowercase.
|
|
The code generator for malicd_topics fails when the ICD file name starts with lowercase.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For more information, see also: [KB: PYBIND errors \[ICD waf build\]](onenote:#KB%20PYBIND%20errors%20[ICD%20waf%20build]§ion-id={F524F9BE-F51D-4A01-9976-93359FCC4966}&page-id={0FEE4FB9-C58B-4E8A-A276-2EC4367CFB30}&end&base-path=https://europeansouthernobservatory.sharepoint.com/sites/ELT_Control/SiteAssets/ELT_Control%20Notebook/Documentation/ELT%20Control%20KnowledgeBase.one)
|
|
For more information, see also: [KB: PYBIND errors \[ICD waf build\]](onenote:#KB%20PYBIND%20errors%20[ICD%20waf%20build]§ion-id={F524F9BE-F51D-4A01-9976-93359FCC4966}&page-id={0FEE4FB9-C58B-4E8A-A276-2EC4367CFB30}&end&base-path=https://europeansouthernobservatory.sharepoint.com/sites/ELT_Control/SiteAssets/ELT_Control%20Notebook/Documentation/ELT%20Control%20KnowledgeBase.one)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### g++: internal compiler error: Killed \[waf build\]
|
|
### g++: internal compiler error: Killed \[waf build\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Trying to build ("waf build") your mal-application, the build fails and you get this error message:
|
|
Trying to build ("waf build") your mal-application, the build fails and you get this error message:
|
|
|
|
|
|
|
|
```
|
|
|
|
Software/CcsLibs/CcsTestData/python/bindings/src/ModCcstestdata.cpp:18:1: note: in expansion of macro ‘PYBIND11_MODULE’
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>Software/CcsLibs/CcsTestData/python/bindings/src/ModCcstestdata.cpp:18:1: note: in expansion of macro ‘PYBIND11_MODULE’</p><p>PYBIND11_MODULE(ModCcstestdata, modccstestdata) {</p><p>^</p><p>g++: internal compiler error: Killed (program cc1plus)</p><p>Please submit a full bug report,</p><p>with preprocessed source if appropriate.</p><p>See <<a href="http://bugzilla.redhat.com/bugzilla">http://bugzilla.redhat.com/bugzilla</a>> for instructions.</p></th></tr></thead><tbody></tbody></table>
|
|
PYBIND11_MODULE(ModCcstestdata, modccstestdata) {
|
|
|
|
^
|
|
|
|
g++: internal compiler error: Killed (program cc1plus)
|
|
|
|
Please submit a full bug report,
|
|
|
|
with preprocessed source if appropriate.
|
|
|
|
See <http://bugzilla.redhat.com/bugzilla> for instructions.
|
|
|
|
```
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The cpp compiler runs out of memory and crashes.
|
|
The cpp compiler runs out of memory and crashes.
|
|
|
|
|
|
You can see the effect by running htop in a separate terminal:
|
|
You can see the effect by running htop in a separate terminal:
|
|
|
|
|
|
All memory (including swap space) is consumed by the g++ compiler, which consequently crashes.
|
|
All memory (including swap space) is consumed by the g++ compiler, which consequently crashes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The issue is under investigation: [ECII-109](https://jira.eso.org/browse/ECII-109)
|
|
The issue is under investigation: [ECII-109](https://jira.eso.org/browse/ECII-109)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Until the problem is solved, use the following workarounds (in increasing order of invasiveness):
|
|
Until the problem is solved, use the following workarounds (in increasing order of invasiveness):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. Make sure to not have elasticsearch running
|
|
1. Make sure to not have elasticsearch running
|
|
|
|
|
|
| systemctl stop elasticsearch |
|
|
```
|
|
|------------------------------|
|
|
systemctl stop elasticsearch
|
|
|
|
```
|
|
>
|
|
|
|
|
|
|
|
2. Prevent waf from spawning parallel build processes:
|
|
2. Prevent waf from spawning parallel build processes:
|
|
|
|
|
|
| waf -j1 build |
|
|
```
|
|
|---------------|
|
|
waf -j1 build
|
|
|
|
```
|
|
>
|
|
|
|
|
|
|
|
3. Add temporary swap space to your machine
|
|
3. Add temporary swap space to your machine
|
|
|
|
|
|
> As root:
|
|
As root:
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th>fallocate -l 8G /swapfile<br />
|
|
|
|
dd if=/dev/zero of=/swapfile bs=1024 count=8388608<br />
|
|
|
|
chmod 600 /swapfile<br />
|
|
|
|
mkswap /swapfile<br />
|
|
|
|
swapon /swapfile</th></tr></thead><tbody></tbody></table>
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
fallocate -l 8G /swapfile
|
|
|
|
dd if=/dev/zero of=/swapfile bs=1024 count=8388608
|
|
|
|
chmod 600 /swapfile
|
|
|
|
mkswap /swapfile
|
|
|
|
swapon /swapfile
|
|
|
|
|
|
> And to remove the swap space after building:
|
|
```
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th>swapoff -v /swapfile<br />
|
|
And to remove the swap space after building:
|
|
rm -f /swapfile </th></tr></thead><tbody></tbody></table>
|
|
|
|
|
|
|
|
>
|
|
```
|
|
|
|
swapoff -v /swapfile
|
|
|
|
rm -f /swapfile
|
|
|
|
```
|
|
|
|
|
|
4. One of the C++ things that take very long are the OPC MAL mappings.
|
|
4. One of the C++ things that take very long are the OPC MAL mappings.
|
|
|
|
|
|
> As of DevEnv 2.1.17, it is possible to suppress MAL mappings, with this modification the wscript of your ICD module:
|
|
As of DevEnv 2.1.17, it is possible to suppress MAL mappings, with this modification the wscript of your ICD module:
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> wscript
|
|
|
|
|
|
|
|
| declare_malicd(use='icds.base', mal_opts = { 'opcua_disabled': True } ) |
|
|
In `wscript`
|
|
|-------------------------------------------------------------------------|
|
|
|
|
|
|
|
|
>
|
|
```
|
|
|
|
declare_malicd(use='icds.base', mal_opts = { 'opcua_disabled': True } )
|
|
|
|
```
|
|
|
|
|
|
5. Ask your system administrator to assign more RAM to your VM.
|
|
5. Ask your system administrator to assign more RAM to your VM.
|
|
|
|
|
|
> By default, ECM VMs come with 4 GB, but you want
|
|
By default, ECM VMs come with 4 GB, but you want
|
|
|
|
|
|
- 8 (if you don't run databases like elasticsearch on your host)
|
|
- 8 (if you don't run databases like elasticsearch on your host)
|
|
|
|
|
|
- 12 (if you run databases like elasticsearch on your host).
|
|
- 12 (if you run databases like elasticsearch on your host).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### More Frames expected \[CII MAL ZMQ\]
|
|
### More Frames expected \[CII MAL ZMQ\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In your application you get errors like this:
|
|
In your application you get errors like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Oct 16, 2019 11:26:21 AM elt.mal.zpb.ps.ZpbSubscriber events
|
|
Oct 16, 2019 11:26:21 AM elt.mal.zpb.ps.ZpbSubscriber events
|
|
|
|
|
|
WARNING: Remote data entity type hash does not match (1040672065 != 1708154137).
|
|
WARNING: Remote data entity type hash does not match (1040672065 != 1708154137).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Oct 16, 2019 11:26:21 AM elt.mal.zpb.ps.ZpbSubscriber events
|
|
Oct 16, 2019 11:26:21 AM elt.mal.zpb.ps.ZpbSubscriber events
|
|
|
|
|
|
WARNING: Failed to process message.
|
|
WARNING: Failed to process message.
|
... | @@ -1404,231 +1326,155 @@ java.lang.RuntimeException: more frames expected |
... | @@ -1404,231 +1326,155 @@ java.lang.RuntimeException: more frames expected |
|
at elt.mal.zpb.ps.ZpbSubscriber.requireMoreFrames(ZpbSubscriber.java:82)
|
|
at elt.mal.zpb.ps.ZpbSubscriber.requireMoreFrames(ZpbSubscriber.java:82)
|
|
|
|
|
|
at elt.mal.zpb.ps.ZpbSubscriber.events(ZpbSubscriber.java:114)
|
|
at elt.mal.zpb.ps.ZpbSubscriber.events(ZpbSubscriber.java:114)
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The first warning message indicates that your client has received a piece of data (= MAL entity type) on a channel that should not carry such data. This means you are running two publishers, publishing different types of data, on the same channel.
|
|
The first warning message indicates that your client has received a piece of data (= MAL entity type) on a channel that should not carry such data. This means you are running two publishers, publishing different types of data, on the same channel.
|
|
|
|
|
|
|
|
**Example of topic definition with port-clash**
|
|
|
|
|
|
> **Example of topic definition with port-clash**
|
|
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p> <pubsub_topic></p><p> <topic_name>sm:current_pos</topic_name></p><p> <topic_type>sm_current_pos</topic_type></p><p> <address_uri>zpb.ps://134.171.2.220:57110/test</address_uri></p><p> <qos latency_ms="1" deadline_ms="100"/></p><p> <performance rate_hz="10" latency_ms="1" synchronous="false" /></p><p> <mal></p><p> <zpb /></p><p> </mal></p><p> </pubsub_topic></p><p> </p><p> <pubsub_topic></p><p> <topic_name>hp:global_status</topic_name></p><p> <topic_type>hp_global_status</topic_type></p><p> <address_uri>zpb.ps://134.171.2.220:57110/test</address_uri></p><p> <qos latency_ms="10" deadline_ms="100"/></p><p> <performance rate_hz="1" latency_ms="10" synchronous="false" /></p><p> <mal></p><p> <zpb /></p><p> </mal></p><p> </pubsub_topic> </p></th></tr></thead><tbody></tbody></table>
|
|
```
|
|
|
|
<pubsub_topic>
|
|
|
|
<topic_name>sm:current_pos</topic_name>
|
|
|
|
<topic_type>sm_current_pos</topic_type>
|
|
|
|
<address_uri>zpb.ps://134.171.2.220:57110/test</address_uri>
|
|
|
|
<qos latency_ms="1" deadline_ms="100"/>
|
|
|
|
<performance rate_hz="10" latency_ms="1" synchronous="false" />
|
|
|
|
<mal>
|
|
|
|
<zpb />
|
|
|
|
</mal>
|
|
|
|
</pubsub_topic>
|
|
|
|
|
|
|
|
<pubsub_topic>
|
|
|
|
<topic_name>hp:global_status</topic_name>
|
|
|
|
<topic_type>hp_global_status</topic_type>
|
|
|
|
<address_uri>zpb.ps://134.171.2.220:57110/test</address_uri>
|
|
|
|
<qos latency_ms="10" deadline_ms="100"/>
|
|
|
|
<performance rate_hz="1" latency_ms="10" synchronous="false" />
|
|
|
|
<mal>
|
|
|
|
<zpb />
|
|
|
|
</mal>
|
|
|
|
</pubsub_topic>
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
The second warning and the error trace are just a consequence of the first warning.
|
|
The second warning and the error trace are just a consequence of the first warning.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Check your topics.xml file, and make sure each channel has its own exclusive topic name.
|
|
Check your topics.xml file, and make sure each channel has its own exclusive topic name.
|
|
|
|
|
|
In the above example, e.g.:
|
|
In the above example, e.g.:
|
|
|
|
|
|
> zpb.ps://134.171.2.220:57110/test1
|
|
```
|
|
>
|
|
zpb.ps://134.171.2.220:57110/test1
|
|
> and
|
|
```
|
|
>
|
|
and
|
|
> zpb.ps://134.171.2.220:57110/test2
|
|
```
|
|
|
|
zpb.ps://134.171.2.220:57110/test2
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Address in use \[CII MAL ZMQ\]
|
|
### Address in use \[CII MAL ZMQ\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Running your application, you see this error message:
|
|
Running your application, you see this error message:
|
|
|
|
|
|
|
|
```
|
|
ZMQException: Errno 48 : Address already in use
|
|
ZMQException: Errno 48 : Address already in use
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution A**
|
|
**Solution A**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Another instance of your application is still running.
|
|
Another instance of your application is still running.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution B**
|
|
**Solution B**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Another instance of your application has non-gracefully terminated without freeing the network port.
|
|
Another instance of your application has non-gracefully terminated without freeing the network port.
|
|
|
|
|
|
Ensure your application always performs a call to "mal.close()" on shutdown.
|
|
Ensure your application always performs a call to "mal.close()" on shutdown.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution C**
|
|
**Solution C**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This could be really a Usage Error due to wrong configuration.
|
|
This could be really a Usage Error due to wrong configuration.
|
|
|
|
|
|
The error message is in fact misleading.
|
|
The error message is in fact misleading.
|
|
|
|
|
|
|
|
**Example**
|
|
|
|
|
|
> **Example**
|
|
```
|
|
|
|
eltcii33 [09:38:27] eeltdev:~/mschilli > mal-esotests-testclient1 pub sAddr=zpb://eltcii28:12333/Sample tSlow=100 nSamp=100
|
|
|
|
pub:sys: Available MAL Flavours loaded: [dds, opc, zpb]
|
|
|
|
pub:config: sAddr=zpb://eltcii28:12333/Sample
|
|
|
|
pub:config: nSamp=100
|
|
|
|
pub:config: tSlow=100
|
|
|
|
Internal Error: org.eso.elt.mal.MalException: org.zeromq.ZMQException: Errno 48 : Address already in use
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>eltcii33 [09:38:27] eeltdev:~/mschilli > mal-esotests-testclient1 pub sAddr=zpb://eltcii28:12333/Sample tSlow=100 nSamp=100</p><p>pub:sys: Available MAL Flavours loaded: [dds, opc, zpb]</p><p>pub:config: sAddr=zpb://eltcii28:12333/Sample</p><p>pub:config: nSamp=100</p><p>pub:config: tSlow=100</p><p>Internal Error: org.eso.elt.mal.MalException: org.zeromq.ZMQException: Errno 48 : Address already in use</p></th></tr></thead><tbody></tbody></table>
|
|
```
|
|
|
|
|
|
> **Reason**
|
|
**Reason**
|
|
>
|
|
|
|
> The above code is trying, on host eltcii33, to publish with an endpoint eltcii28.
|
|
|
|
>
|
|
|
|
> **Fix**
|
|
|
|
>
|
|
|
|
> On eltcii33, the endpoint must be eltcii33.
|
|
|
|
|
|
|
|
|
|
The above code is trying, on host eltcii33, to publish with an endpoint eltcii28.
|
|
|
|
|
|
|
|
**Fix**
|
|
|
|
|
|
|
|
On eltcii33, the endpoint must be eltcii33.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Scheme not supported \[CII MAL\]
|
|
### Scheme not supported \[CII MAL\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Running your application, you get this error message:
|
|
Running your application, you get this error message:
|
|
|
|
|
|
|
|
```
|
|
elt.mal.SchemeNotSupportedException: middleware not supported
|
|
elt.mal.SchemeNotSupportedException: middleware not supported
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution A**
|
|
**Solution A**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In your code, you've misspelled the middleware name, e.g. "opc" instead of "opcua"
|
|
In your code, you've misspelled the middleware name, e.g. "opc" instead of "opcua"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution B**
|
|
**Solution B**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The middleware is supported in fact, but failed to load.
|
|
The middleware is supported in fact, but failed to load.
|
|
|
|
|
|
- E.g. in DDS, you are using a Qos profile xml file which has some illegal syntax inside.
|
|
- E.g. in DDS, you are using a Qos profile xml file which has some illegal syntax inside.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Choosing a NIC \[CII MAL DDS\]
|
|
### Choosing a NIC \[CII MAL DDS\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
I'm using MAL-DDS, and I have two network cards (NICs) installed. MAL uses the wrong one, i.e. my network traffic goes into the "office" network, but should go into the "control" network.
|
|
I'm using MAL-DDS, and I have two network cards (NICs) installed. MAL uses the wrong one, i.e. my network traffic goes into the "office" network, but should go into the "control" network.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
As multicast addresses are by definition not associated with hardware (ie they map to MAC addresses which have no corresponding Ethernet card), there is no means for the OS to resolve which NIC the IGMP subscription should be sent down. Thus the NIC must be specified, or the default is used (which is the office network).
|
|
As multicast addresses are by definition not associated with hardware (ie they map to MAC addresses which have no corresponding Ethernet card), there is no means for the OS to resolve which NIC the IGMP subscription should be sent down. Thus the NIC must be specified, or the default is used (which is the office network).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Therefore, DDS allows you to specify which NIC you want to use for outgoing traffic.
|
|
Therefore, DDS allows you to specify which NIC you want to use for outgoing traffic.
|
|
|
|
|
|
Therefore, this boils down to configuring DDS.
|
|
Therefore, this boils down to configuring DDS.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
1. Get the XML file shown in Solution #1 on this page:
|
|
1. Get the XML file shown in Solution #1 on this page:
|
|
|
|
|
|
> <https://community.rti.com/howto/control-or-restrict-network-interfaces-nics-used-discovery-and-data-distribution>
|
|
<https://community.rti.com/howto/control-or-restrict-network-interfaces-nics-used-discovery-and-data-distribution>
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
2. Continue with this article:
|
|
2. Continue with this article:
|
|
|
|
|
|
> [KB: Configuring MAL-DDS](onenote:#KB%20Configuring%20MAL-DDS§ion-id={35F80A9B-72FF-4F78-8793-5C85A09EA1FD}&page-id={3DBEC40D-B2DB-4226-917F-23EAE6A4B01C}&end&base-path=https://europeansouthernobservatory.sharepoint.com/sites/CCS/SiteAssets/CCS%20Notebook/General.one)
|
|
[KB: Configuring MAL-DDS](onenote:#KB%20Configuring%20MAL-DDS§ion-id={35F80A9B-72FF-4F78-8793-5C85A09EA1FD}&page-id={3DBEC40D-B2DB-4226-917F-23EAE6A4B01C}&end&base-path=https://europeansouthernobservatory.sharepoint.com/sites/CCS/SiteAssets/CCS%20Notebook/General.one)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Configuring DDS \[CII MAL DDS\]
|
|
### Configuring DDS \[CII MAL DDS\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some of the middlewares usable through MAL offer a variety of configuration options.
|
|
Some of the middlewares usable through MAL offer a variety of configuration options.
|
|
|
|
|
|
This article explains how to define and use configuration for the DDS middleware.
|
|
This article explains how to define and use configuration for the DDS middleware.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
To configure DDS, the following 3 things are necessary:
|
|
To configure DDS, the following 3 things are necessary:
|
|
|
|
|
|
i\) put the desired config into an external XML file
|
|
i\) put the desired config into an external XML file
|
... | @@ -1637,145 +1483,88 @@ ii\) define the NDDS_QOS_PROFILES environment variable, so DDS finds the XML fil |
... | @@ -1637,145 +1483,88 @@ ii\) define the NDDS_QOS_PROFILES environment variable, so DDS finds the XML fil |
|
|
|
|
|
iii\) pass 2 keys to the MAL factory, so DDS finds the right profile in the XML file
|
|
iii\) pass 2 keys to the MAL factory, so DDS finds the right profile in the XML file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*(For a discussion about making this easier, see [ECII-77](https://jira.eso.org/browse/ECII-77))*
|
|
*(For a discussion about making this easier, see [ECII-77](https://jira.eso.org/browse/ECII-77))*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution**
|
|
**Solution**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. Create the XML file.
|
|
1. Create the XML file.
|
|
|
|
|
|
> Examples are often easily found in the Connext documentation and user forums.
|
|
Examples are often easily found in the Connext documentation and user forums.
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
2. In your code, let MAL load the DDS-specific properties.
|
|
2. In your code, let MAL load the DDS-specific properties.
|
|
|
|
|
|
> Example (C++):
|
|
Example (C++):
|
|
|
|
|
|
<table><colgroup><col style="width: 100%" /></colgroup><thead><tr class="header"><th><p>auto publisher = factory.getPublisher<elt::telem::Temperature>(uri, {</p><p> std::make_shared<mal::ps::qos::Latency>(std::chrono::milliseconds(100)),</p><p> std::make_shared<mal::ps::qos::Deadline>(std::chrono::seconds(1)) }, {</p><p> {"dds.domain", "100"}, { "dds.qos.profile.library", “test"}, { "dds.qos.profile.name", “UDPv4_properties"}} });</p></th></tr></thead><tbody></tbody></table>
|
|
```
|
|
|
|
auto publisher = factory.getPublisher<elt::telem::Temperature>(uri, {
|
|
|
|
std::make_shared<mal::ps::qos::Latency>(std::chrono::milliseconds(100)),
|
|
|
|
std::make_shared<mal::ps::qos::Deadline>(std::chrono::seconds(1)) }, {
|
|
|
|
{"dds.domain", "100"}, { "dds.qos.profile.library", “test"}, { "dds.qos.profile.name", “UDPv4_properties"}} });
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
3. Before running your code:
|
|
3. Before running your code:
|
|
|
|
|
|
| export NDDS_QOS_PROFILES=\<path of XML file> |
|
|
```
|
|
|----------------------------------------------|
|
|
export NDDS_QOS_PROFILES=\<path of XML file>
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Variable Tracking exceeded \[CII MAL\]
|
|
### Variable Tracking exceeded \[CII MAL\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Problem**
|
|
**Problem**
|
|
|
|
|
|
|
|
When building an ICD using CII-MAL, you see this warning message:
|
|
|
|
|
|
When building an ICD using CII-MAL,
|
|
|
|
|
|
|
|
> you see this warning message:
|
|
|
|
>
|
|
|
|
> variable tracking size limit exceeded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
variable tracking size limit exceeded
|
|
|
|
```
|
|
|
|
|
|
**Background**
|
|
**Background**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The -fvar-tracking-assignments is automatically enabled by GCC when optimizations are enabled.
|
|
The -fvar-tracking-assignments is automatically enabled by GCC when optimizations are enabled.
|
|
|
|
|
|
There is a limit on how many variables can be tracked by the compiler. The warning tells you that
|
|
There is a limit on how many variables can be tracked by the compiler. The warning tells you that more vars would need to be tracked than what's supported.
|
|
|
|
|
|
more vars would need to be tracked than what's supported.
|
|
|
|
|
|
|
|
You can disable the tracking manually with -fno-var-tracking-assignments.
|
|
You can disable the tracking manually with -fno-var-tracking-assignments.
|
|
|
|
|
|
There are two easy ways to do it on the overall project.
|
|
There are two easy ways to do it on the overall project.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution A**
|
|
**Solution A**
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Export CXXFLAGS=-fno-var-tracking-assignments
|
|
Export CXXFLAGS=-fno-var-tracking-assignments
|
|
|
|
```
|
|
|
|
|
|
And then rerun “waf configure” and continue with the build.
|
|
And then rerun “waf configure” and continue with the build.
|
|
|
|
|
|
Note: you have to have the exported variable each time you do a “waf configure” as that is the point at which such flags are saved
|
|
Note: you have to have the exported variable each time you do a “waf configure” as that is the point at which such flags are saved
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Solution B**
|
|
**Solution B**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If you want it to be inside the project then put the flags fixed inside your project, so in the top level wscript (where you define the project) add this configure section before the project declaration:
|
|
If you want it to be inside the project then put the flags fixed inside your project, so in the top level wscript (where you define the project) add this configure section before the project declaration:
|
|
|
|
|
|
|
|
```
|
|
def configure(cnf):
|
|
def configure(cnf):
|
|
|
|
|
|
cnf.env.append_value('CXXFLAGS', \['-fno-var-tracking-assignments'\])
|
|
cnf.env.append_value('CXXFLAGS', \['-fno-var-tracking-assignments'\])
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Multithreading in Java
|
|
### Multithreading in Java
|
|
|
|
|
|
|
|
Look at this document:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The document is available on the web:
|
|
|
|
|
|
|
|
> <http://www.eso.org/~mschilli/JmmVisibility.rtf>
|
|
|
|
|
|
|
|
|
|
- [Java JMM Visibility](Documents/Java-JMMVisibility.pdf)
|
|
|
|
|
|
|
|
|
|
<img src="media/image1.png" style="width:8.5625in;height:11.0625in" alt="Machine generated alternative text: JMM Visibility, Marcus Schilling, November 2012. All rights reserved. Introduction This page is meant to raise awareness on thread-safety issues in multi-threaded Java applications. It is essential that you ensure your code contains proper memory barriers (= synchronization points), especially on multi-core machines. For more details, see Chapter 17 of the Java Language Specification (and section 17.4.5. Happens-before in particular) · Examples of non-thread-safe java code · (Almost complete list of) Ways to code a memory barrier in Java · Examples of thread-safe java code · Performance considerations Examples of non-thread-safe java code In the example scenario there are 2 threads A and B . First, Thread B instantiates class Bad. Then, a few seconds later, Thread A executes method a() . Then, a few seconds later, Thread B executes b() . class Bad { String name = "bob"; void a() {name = "alice";} void b() {print(name);} } ==> Thread B will print "bob" or "alice". class Bad { String[] names = {"alice", "bob"}; void a() {names[0] = "albert";} void b() {print(names[0]);} } ==> Thread B will print "alice" or "albert". class Bad { volatile String[] names = {"alice", "bob"}; void a() {names[0] = "albert";} void b() {print(names[0]);} } ==> Thread B will print "alice" or "albert". class Bad { volatile List names = new ArrayList(); void a() {names.add("alice");} void b() {print(names.size());} } ==> Thread B will print "0" or "1". " /><img src="media/image2.png" style="width:8.5625in;height:11.0625in" alt="Machine generated alternative text: class Bad { class Data { int x; } volatile Data data = new Data (); void a() {data.x = 1;} void b() {print(data.x);} } ==> Thread B will print "0" or "1". ========================= The above examples may seem surprisingly trivial. You need to be aware that the problem is not about concurrent modification of shared data. Except the example with the ArrayList, all the read and write operations in the examples are atomic (per se, because reference access is atomic). So, there are no race conditions in the above examples. And it's even more obvious from the fact that our threads run only every few seconds. The problem is the visibility in terms of the java memory model (called jmm visibility hereafter): There is no guarantee that Thread B will see the modifications done by Thread A . Each thread has its own copy of the main memory that needs to be refreshed from main memory and flushed to main memory. The flushing and refreshing is triggered when a thread passes a memory barrier. To have a jmm visibility guarantee, both threads must pass a memory barrier (or synchronization point). Next, it is CRUCIAL to be aware that the amount of refresh (= the changeset, = which subset of the dirty memory gets refreshed) fully depends on which memory barrier you use, there is no such thing like one single all-in-one refresh that brings your reader thread fully up-to-date. This means in order to have a jmm visibility guarantee, both threads must pass the same memory barrier, namely the one that includes all modifications needed by the reader . class Bad { List one = new ArrayList(); List two = new ArrayList(); void a() { synchronized(one) {one.add("alice");} synchronized(two) {two.add("bob"); } } void b() { synchronized(one) {print(one.size() + two.size()); } } } ==> Thread B will print "1" or "2". class Bad { class Data { int x; } volatile List list = new Vector(); void a() { Data data = new Data(); synchronized(data) { list.add(data); data.x = 1; } } void b() { print (list.get(0).x); } } ==> Thread B will print "0" or "1". In big classes with much code, this particular problem can be difficult and tedious to find. The code may look thread-safe, but in reality there are two threads passing different memory barriers, and thus are actually not synchronizing their data! Another remark: The jmm visibility has nothing to do with the access modifiers public , protected , " /><img src="media/image3.png" style="width:8.5625in;height:11.0625in" alt="Machine generated alternative text: or private . You can put any of them onto your variables without any impact on the jmm visibility . The only connection to our problem is this: Depending on how you coded your memory barrier, making a field private can prevent another Thread C from bypassing your memory barrier. Example: class Unstable { public int x; synchronized void a() {x=1;} synchronized void b() {print(x);} } ==> Thread C will see "x == 0" or "x == 1". ========================= (Almost complete list of) Ways to code a memory barrier in Java Flush (by Thread A) Refresh (by Thread B) Note Write access to volatile variable Read Access to volatile variable Both threads must, see above, access the same variable End of a synchronized section Begin of a synchronized section Both threads must, see above, synchronize on the same variable Write access to an AtomicInteger (etc.) Read Access to an AtomicInteger (etc.) Both threads must, see above, access the same variable Examples of thread-safe java code class Okay { volatile String name = "bob"; void a() {name = "alice";} void b() {print(name);} } class Okay { String name = "bob"; synchronized void a() {name = "alice";} synchronized void b() {print(name);} } class Okay { volatile String[] names = {"alice", "bob"}; synchronized void a() {names[0] = "albert";} synchronized void b() {print(names[0]);} } class Okay { volatile List names = Collections.synchronizedList(new ArrayList()); void a() {names.add("alice");} " /><img src="media/image4.png" style="width:8.5625in;height:11.0625in" alt="Machine generated alternative text: void b() {print(names.size());} } class Okay { class Data { volatile int x; } volatile Data data = new Data (); void a() {data.x = 1;} void b() {print(data.x);} } ========================= Performance considerations 1) volatile is cheaper than synchronize 2) BUT this is no longer true if you have loads of volatiles . 3) THUS, a single synchronize is cheaper than multiple volatiles . class Slower { volatile int x; volatile int y; volatile int z; void a() {x=1; y=1; z=1} void b() {print (x); print (y); print(z);} } class Faster { int x; int y; int z; synchronized void a() {x=1; y=1; z=1} synchronized void b() {print (x); print (y); print(z);} } 4) HOWEVER, code protected by synchronize is mutex, it cannot be executed in parallel. THUS, massive use of synchronize has massive impact on the scalability of your code. AND, synchronize has the power to create deadlocks, which volatile cannot. With these pros and cons in mind, you might conclude that you'll prefer some performance penalty with volatile over potential deadlocking and loss of scalability with synchronize . And I tend to agree with you. So, forget about synchronize ? You still need it to avoid race conditions , that is when one thread i) makes a modification to a shared data structure and ii) this modification should look atomic to another thread, but iii) cannot be performed atomically per se (so for any data structure beyond references, simple data types, and Atomic variables). This is something that volatile cannot give you. 5) THEREFORE, you will need to find a compromise between the use of synchronize and the use of volatile . Use synchronized sections but keep them short. Use volatile fields but not too often. 6) final is cheaper than volatile - if you can, use final instead of volatile class Slower { volatile String[] names = {"alice", "bob"}; " /><img src="media/image5.png" style="width:8.5625in;height:11.0625in" alt="Machine generated alternative text: synchronized void a() {names[0] = "albert";} synchronized void b() {print(names[0]);} } class Faster { final String[] names = {"alice", "bob"}; synchronized void a() {names[0] = "albert";} synchronized void b() {print(names[0]);} } " />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Summary of OPC/UA MAL in C++
|
|
### Summary of OPC/UA MAL in C++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This article covers integration of OPC/UA in CII MAL specifically for OPC/UA Data Access and Subscription profiles. OPC/UA method invocation is also supported in CII MAL but is not described in this article, likewise details of the Python (and Java) support are not provided. Only C++ is considered here.
|
|
This article covers integration of OPC/UA in CII MAL specifically for OPC/UA Data Access and Subscription profiles. OPC/UA method invocation is also supported in CII MAL but is not described in this article, likewise details of the Python (and Java) support are not provided. Only C++ is considered here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OPC/UA communication middleware is exposed in CII MAL as either Publish/Subscribe or Request/Reply APIs.
|
|
OPC/UA communication middleware is exposed in CII MAL as either Publish/Subscribe or Request/Reply APIs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The XML ICD definition of types in CII is used to map sets of data points together that are read/written as a group.
|
|
The XML ICD definition of types in CII is used to map sets of data points together that are read/written as a group.
|
|
|
|
|
|
Each attribute in the defined type is connected to a corresponding data point in the OPC/UA data space via a URI. Thus the CII URI for a complex type will contain specific addresses of multiple nodes in the OPC/UA data space.
|
|
Each attribute in the defined type is connected to a corresponding data point in the OPC/UA data space via a URI. Thus the CII URI for a complex type will contain specific addresses of multiple nodes in the OPC/UA data space.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Pub/Sub API for OPC/UA Clients:
|
|
#### Pub/Sub API for OPC/UA Clients:
|
|
|
|
|
|
The CII MAL Pub/Sub API utilizes OPC/UA Data Access Reads and Writes, as well OPC/UA subscription. A Publisher will directly trigger an OPC DA write, while a Subscriber will work in one of two ways, depending on the type associated with the subscriber:
|
|
The CII MAL Pub/Sub API utilizes OPC/UA Data Access Reads and Writes, as well OPC/UA subscription. A Publisher will directly trigger an OPC DA write, while a Subscriber will work in one of two ways, depending on the type associated with the subscriber:
|
... | @@ -1786,27 +1575,27 @@ The CII MAL Pub/Sub API utilizes OPC/UA Data Access Reads and Writes, as well OP |
... | @@ -1786,27 +1575,27 @@ The CII MAL Pub/Sub API utilizes OPC/UA Data Access Reads and Writes, as well OP |
|
|
|
|
|
- If the subscriber CII URI contains multiple nodes (i.e. the XML ICD type contains multiple attributes) then the Subscriber launches a thread to perform periodic polling of data from the OPC/UA server. The rate is based on the properties passed in creating the subscriber. e.g.
|
|
- If the subscriber CII URI contains multiple nodes (i.e. the XML ICD type contains multiple attributes) then the Subscriber launches a thread to perform periodic polling of data from the OPC/UA server. The rate is based on the properties passed in creating the subscriber. e.g.
|
|
|
|
|
|
> try {
|
|
```
|
|
>
|
|
try {
|
|
> subscriber = factory.getSubscriber\<T>(opcua_uri, ::elt::mal::ps::qos::QoS::DEFAULT,
|
|
|
|
>
|
|
subscriber = factory.getSubscriber\<T>(opcua_uri, ::elt::mal::ps::qos::QoS::DEFAULT,
|
|
> {{"opc.ps.outstandingPublishRequests","5"},{"opc.asyncLoopExecutionPeriodMs","50"},
|
|
|
|
>
|
|
{{"opc.ps.outstandingPublishRequests","5"},{"opc.asyncLoopExecutionPeriodMs","50"},
|
|
> {"opc.asyncCallSubmitTimeoutMs","1000"},
|
|
|
|
>
|
|
{"opc.asyncCallSubmitTimeoutMs","1000"},
|
|
> {"opc.ps.pollingPeriodMs","20000"},
|
|
|
|
>
|
|
{"opc.ps.pollingPeriodMs","20000"},
|
|
> {"opc.asyncCallRetryPeriodMs","250"}});
|
|
|
|
>
|
|
{"opc.asyncCallRetryPeriodMs","250"}});
|
|
> }
|
|
|
|
>
|
|
}
|
|
> catch(...) {
|
|
|
|
>
|
|
catch(...) {
|
|
> throw;
|
|
|
|
>
|
|
throw;
|
|
> }
|
|
|
|
>
|
|
}
|
|
>
|
|
```
|
|
|
|
|
|
#### Request/Reply API for OPC/UA Clients:
|
|
#### Request/Reply API for OPC/UA Clients:
|
|
|
|
|
... | @@ -1814,50 +1603,40 @@ As OPC/UA Data Access read and write essentially follow a synchronous request/re |
... | @@ -1814,50 +1603,40 @@ As OPC/UA Data Access read and write essentially follow a synchronous request/re |
|
|
|
|
|
The ICD is termed "virtual" in CII nomenclature as it does not require definition as an XML ICD using the service syntax, rather the same types defined for the Pub/Sub API may be used with a CII MAL OPC/UA Request/Reply.
|
|
The ICD is termed "virtual" in CII nomenclature as it does not require definition as an XML ICD using the service syntax, rather the same types defined for the Pub/Sub API may be used with a CII MAL OPC/UA Request/Reply.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This approach means OPC/UA Data Access read (and write) are synchronous, and may be called as needed by the application.
|
|
This approach means OPC/UA Data Access read (and write) are synchronous, and may be called as needed by the application.
|
|
|
|
|
|
|
|
```
|
|
|
|
namespace mal {
|
|
|
|
namespace rr {
|
|
|
|
namespace da {
|
|
|
|
|
|
> namespace mal {
|
|
class DataAccess : public ::elt::mal::rr::RrEntity {
|
|
>
|
|
|
|
> namespace rr {
|
|
public:
|
|
>
|
|
|
|
> namespace da {
|
|
[...]
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> class DataAccess : public ::elt::mal::rr::RrEntity {
|
|
|
|
>
|
|
|
|
> public:
|
|
|
|
>
|
|
|
|
> \[...\]
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> template \<typename T>
|
|
|
|
>
|
|
|
|
> void read(::elt::mal::ps::DataEntity\<T>& value) {
|
|
|
|
>
|
|
|
|
> readUnsafe(&value);
|
|
|
|
>
|
|
|
|
> }
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> template \<typename T>
|
|
|
|
>
|
|
|
|
> void write(const ::elt::mal::ps::DataEntity\<T>& value) {
|
|
|
|
>
|
|
|
|
> writeUnsafe(&value);
|
|
|
|
>
|
|
|
|
> }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
|
|
|
void read(::elt::mal::ps::DataEntity\<T>& value) {
|
|
|
|
|
|
|
|
readUnsafe(&value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
|
|
|
void write(const ::elt::mal::ps::DataEntity\<T>& value) {
|
|
|
|
|
|
|
|
writeUnsafe(&value);
|
|
|
|
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
A test application showing its use is here:
|
|
A test application showing its use is here:
|
|
|
|
|
|
<https://gitlab.eso.org/cosylab/elt-cii/mal/mal-test/-/blob/develop/cpp/mal-test-performance/opcua/mal-opcua-da-speed/src/common.cpp> |
|
<https://gitlab.eso.org/cosylab/elt-cii/mal/mal-test/-/blob/develop/cpp/mal-test-performance/opcua/mal-opcua-da-speed/src/common.cpp> |
|
|
|
\ No newline at end of file |