Using OpcCmd Utility as OPC UA PubSub Sniffer

From OPC Labs Knowledge Base

This article assumes that you have basic knowledge of the OPC UA PubSub Sniffing concepts; please read the corresponding article first.

Accessing the OPC UA PubSub Sniffer

In the OpcCmd utility, the OPC UA PubSub Sniffer is accessible as a service under the uaSubscriber command. For interactive usage, you can access it as follows:

uaSubscriber getService uaPubSubSniffer

(shortened: uas gs uapss). You can also use the above command text as a prefix, and append the desired uaPubSubSniffer command at the end, for a "one-shot" execution of the OPC UA PubSub Sniffer.

uaPubSubSniffer subscribeCapture command

The subscribeCapture command (shortened: sc) subscribes to traffic captures and displays them. Its arguments, and vast majority of its options, are identical with the uaSubscriber subscribeDataSet command, so we are not going to repeat them here.

At the end of its execution, the command displays a table, where each distinct capture header is represented by a row, and it shows the fields of the capture header, and a number of times such header has been encountered. The table is ordered first by the capture type, and then by the remaining fields of the capture header.

Limiting the capture types

If you are not interested in all messages that appear on the specified PubSub connection, but only in some message types, use the --CaptureTypes (shortened: -ct) command option. With it, you can specify the capture type either as an individual capture type, a specific combination of them, or a predefined capture type grouping.

Selecting distinct headers

If you are not interested in every message, but only in knowing what kinds of messages appear on the network (PubSub connection), use the --DistinctHeadersOnly option (shortened: -dho). With this option, the capture header of every new incoming capture notification is compared with the already recorder headers, and the notification is displayed only if the capture header has not been encountered before.

Capturing message data

In order to display the data captured with each transport message, use the --ShowCaptureData option (shortened: -scd). More information: Data capture with sniffing.

Binary data (UADP) are shown in hexadecimal format, textual data (JSON) are decoded first, and shown as strings.

Typical usage scenarios

Show all communication

(TBD)

Show all communication with UDP transport mapping:

subscribeCapture opc.udp://239.0.0.1:4840

Show all communication with MQTT transport mapping:

subscribeCapture mqtt://opcua-pubsub.demo-this.com #

Example output:

Executing for 00:01:00 (press 'X' to stop)...
Subscribing capture...
[0] IUAPubSubSniffer.Capture: None
Capture handle: 13000003
[1] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp
[2] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[String]32, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp
[3] IUAPubSubSniffer.Capture: DataSetKeyFrame; publisher=[String]32, writer=1, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp
[4] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp
[5] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[String]32, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp
[6] IUAPubSubSniffer.Capture: DataSetKeyFrame; publisher=[String]32, writer=3, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp
[7] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp
[8] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[String]32, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp
[9] IUAPubSubSniffer.Capture: DataSetKeyFrame; publisher=[String]32, writer=4, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp
[10] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp
[11] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[UInt64]31, origin=opcuademo/uadp/none, mapping=Uadp
[12] IUAPubSubSniffer.Capture: DataSetDeltaFrame; publisher=[UInt64]31, writer=1, origin=opcuademo/uadp/none, mapping=Uadp
[13] IUAPubSubSniffer.Capture: DataSetDeltaFrame; publisher=[UInt64]31, writer=3, origin=opcuademo/uadp/none, mapping=Uadp
[14] IUAPubSubSniffer.Capture: DataSetDeltaFrame; publisher=[UInt64]31, writer=4, origin=opcuademo/uadp/none, mapping=Uadp
[15] IUAPubSubSniffer.Capture: DataSetEvent; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp
[16] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/json, mapping=Json
[17] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json
[18] IUAPubSubSniffer.Capture: DataSetKeyFrame; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json
[19] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/json, mapping=Json
[20] IUAPubSubSniffer.Capture: NetworkMessage; publisher=[String]32, origin=opcuademo/json, mapping=Json
...

Unsubscribing capture (capture handle 13000003)...
PubSub header counts (sequence): 35 element(s)
╒═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤═════╕
│[]                                                                                                                                   │Value│
╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╪═════╡
│None                                                                                                                                 │1    │
│TransportMessage; origin=opcuademo/json, mapping=Json                                                                                │150  │
│TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp                                                                           │150  │
│NetworkMessage; publisher=[UInt16]30, origin=opcuademo/uadp/none, mapping=Uadp                                                       │30   │
│NetworkMessage; publisher=[UInt64]31, origin=opcuademo/uadp/none, mapping=Uadp                                                       │30   │
│NetworkMessage; publisher=[String]30, origin=opcuademo/json, mapping=Json                                                            │30   │
│NetworkMessage; publisher=[String]31, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/json, mapping=Json                │30   │
│NetworkMessage; publisher=[String]31, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/json, mapping=Json                │30   │
│NetworkMessage; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json                │30   │
│NetworkMessage; publisher=[String]32, origin=opcuademo/json, mapping=Json                                                            │30   │
│NetworkMessage; publisher=[String]32, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp           │30   │
│NetworkMessage; publisher=[String]32, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp           │30   │
│NetworkMessage; publisher=[String]32, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp           │30   │
│DataSetKeyFrame; publisher=[UInt16]30, group=101, origin=opcuademo/uadp/none, mapping=Uadp                                           │20   │
│DataSetKeyFrame; publisher=[UInt64]31, writer=1, origin=opcuademo/uadp/none, mapping=Uadp                                            │1    │
│DataSetKeyFrame; publisher=[UInt64]31, writer=3, origin=opcuademo/uadp/none, mapping=Uadp                                            │1    │
│DataSetKeyFrame; publisher=[UInt64]31, writer=4, origin=opcuademo/uadp/none, mapping=Uadp                                            │1    │
│DataSetKeyFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]31, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/json, mapping=Json               │30   │
│DataSetKeyFrame; publisher=[String]31, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/json, mapping=Json               │30   │
│DataSetKeyFrame; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json               │30   │
│DataSetKeyFrame; publisher=[String]32, origin=opcuademo/json, mapping=Json                                                           │90   │
│DataSetKeyFrame; publisher=[String]32, writer=1, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp│30   │
│DataSetKeyFrame; publisher=[String]32, writer=3, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp│30   │
│DataSetKeyFrame; publisher=[String]32, writer=4, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp│30   │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=1, origin=opcuademo/uadp/none, mapping=Uadp                                          │19   │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=3, origin=opcuademo/uadp/none, mapping=Uadp                                          │19   │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=4, origin=opcuademo/uadp/none, mapping=Uadp                                          │19   │
│DataSetDeltaFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json                                               │29   │
│DataSetDeltaFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json                                               │29   │
│DataSetDeltaFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json                                               │29   │
│DataSetEvent; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp                                              │7    │
│DataSetKeepAlive; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp                                          │18   │
╘═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╧═════╛
Command finished: subscribeCapture|sc (60.3 seconds). Observed 1066 event(s), start index 0. You can use the "events?" ("ev?") command to view their details.

Discover who communicates what

(TBD)

Discover who communicates what with UDP transport mapping:

subscribeCapture opc.udp://239.0.0.1:4840 --CaptureTypes AllPubSubMessages --DistinctHeadersOnly !wait Infinite

Discover who communicates what with MQTT transport mapping:

subscribeCapture mqtt://opcua-pubsub.demo-this.com # --CaptureTypes AllPubSubMessages --DistinctHeadersOnly !wait Infinite

Example output:

Executing for Infinite (press 'X' to stop)...
Subscribing capture...
8/15/2022 1:03:30 PM Distinct header: None
Capture handle: 13000002
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]32, writer=1, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]32, writer=3, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]32, writer=4, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[UInt64]31, writer=1, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[UInt64]31, writer=3, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[UInt64]31, writer=4, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetKeepAlive; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]32, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]31, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[String]31, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetDeltaFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:32 PM Distinct header: DataSetKeyFrame; publisher=[UInt16]30, group=101, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:58 PM Distinct header: DataSetEvent; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp
8/15/2022 1:03:58 PM Distinct header: DataSetKeyFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:58 PM Distinct header: DataSetKeyFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json
8/15/2022 1:03:58 PM Distinct header: DataSetKeyFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json

The execution has been terminated by the user.
Unsubscribing capture (capture handle 13000002)...
PubSub header counts (sequence): 20 element(s)
╒═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤═════╕
│[]                                                                                                                                   │Value│
╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╪═════╡
│None                                                                                                                                 │1    │
│DataSetKeyFrame; publisher=[UInt16]30, group=101, origin=opcuademo/uadp/none, mapping=Uadp                                           │9    │
│DataSetKeyFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json                                                 │1    │
│DataSetKeyFrame; publisher=[String]31, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/json, mapping=Json               │19   │
│DataSetKeyFrame; publisher=[String]31, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/json, mapping=Json               │19   │
│DataSetKeyFrame; publisher=[String]31, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/json, mapping=Json               │19   │
│DataSetKeyFrame; publisher=[String]32, origin=opcuademo/json, mapping=Json                                                           │57   │
│DataSetKeyFrame; publisher=[String]32, writer=1, class=eae79794-1af7-4f96-8401-4096cd1d8908, origin=opcuademo/uadp/none, mapping=Uadp│19   │
│DataSetKeyFrame; publisher=[String]32, writer=3, class=96976b7b-0db7-46c3-a715-0979884b55ae, origin=opcuademo/uadp/none, mapping=Uadp│19   │
│DataSetKeyFrame; publisher=[String]32, writer=4, class=cc7cb5f4-4272-45c2-9a4d-f85a8b331f6a, origin=opcuademo/uadp/none, mapping=Uadp│19   │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=1, origin=opcuademo/uadp/none, mapping=Uadp                                          │9    │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=3, origin=opcuademo/uadp/none, mapping=Uadp                                          │9    │
│DataSetDeltaFrame; publisher=[UInt64]31, writer=4, origin=opcuademo/uadp/none, mapping=Uadp                                          │9    │
│DataSetDeltaFrame; publisher=[String]30, writer=1, origin=opcuademo/json, mapping=Json                                               │18   │
│DataSetDeltaFrame; publisher=[String]30, writer=2, origin=opcuademo/json, mapping=Json                                               │18   │
│DataSetDeltaFrame; publisher=[String]30, writer=3, origin=opcuademo/json, mapping=Json                                               │18   │
│DataSetEvent; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp                                              │1    │
│DataSetKeepAlive; publisher=[UInt64]31, writer=51, origin=opcuademo/uadp/none, mapping=Uadp                                          │13   │
╘═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╧═════╛
Command finished: subscribeCapture|sc (39.8 seconds). Observed 0 event(s), start index 0.

How can the above output be interpreted? It basically tells us, among other things, that

  • There are 5 publishers, with publisher Ids [UInt16]30, [UInt64]31, [String]30, [String]31 and [String]32.
  • The first two publishers are generating messages in the UADP message mapping, the remaining publishers in JSON.
  • The 'origin' field tells us the MQTT topic the publishers are sending messages to.
  • The 'writer' field tells us the dataset writer Id(s) the publishers are configured to publish.
  • We can see that some publishers, but not all, are also using delta frames. Some are also sending events, or keep-alive messages.

Capture and show message data

In order to view the original messages as they are being communicated on the specified PubSub connection, use the --ShowCaptureData option. In our example, we are not interested in further analysis of the received messages, and we therefore limit the capture to the TransportMessage type, by using the --CaptureTypes option. By removing this option, you can view the captured data together with their analysis as OPC UA PubSub messages.

Capture and show message data with UDP transport mapping:

subscribeCapture opc.udp://239.0.0.1:4840 --CaptureTypes TransportMessage --ShowCaptureData

Capture and show message data with MQTT transport mapping:

subscribeCapture mqtt://opcua-pubsub.demo-this.com # --CaptureTypes TransportMessage --ShowCaptureData

Example output:

Executing for 00:01:00 (press 'X' to stop)...
Subscribing capture...
[0] IUAPubSubSniffer.Capture: None
Capture handle: 13000001
[1] IUAPubSubSniffer.Capture: TransportMessage; origin=opcuademo/uadp/none, mapping=Uadp
Byte[]: D1 0C 02 00 00 00 33 32 94 97 E7 EA F7 1A 96 4F 84 01 40 96 CD 1D 89 08 01 01 00 01 04 00 01 00 06 66 1F 00 00 06 E7 06 00 00 0D C0 FF 94 44 96 B0 D8 01
...

See also

OPC UA PubSub Sniffing concepts

Using OpcCmd Utility as OPC UA PubSub Subscriber