OPC UA PubSub over MQTT
설비 쪽 발행자와 서버 쪽 구독자가 MQTT 브로커만 공유하는 구성을 만듭니다.
publisher process Mosquitto subscriber process(publisher_id 100) ──────▶ 127.0.0.1:1883 ──────▶ (Received messages) opcua/uadp/data/100/line1 │ └──▶ mosquitto_sub (observer)- 발행자는 헤드리스 시뮬레이터의 시나리오 모드로 DataSet 두 개를 보내고 끝납니다.
- 구독자는 헤드리스 시뮬레이터를 대화형으로 실행해 받은 메시지를 확인합니다.
mosquitto_sub는 와일드카드 필터로 모든 PublisherId의 데이터를 관찰합니다.
PubSub의 DataSet, WriterGroup, DataSetWriter 개념은 OPC UA 배경 지식에서 설명합니다.
준비물
섹션 제목: “준비물”- 로컬 Mosquitto 브로커 실행에서 만든
mqtt-lab디렉터리와local.conf - PDComm OPC UA 시뮬레이터 패키지
- 터미널 창 4개
-
mqtt-lab디렉터리에 구독자 입력 파일과 발행자 시나리오 파일을 저장합니다.subscriber.jsonl {"action":"configure","config":{"pubsub":{"transport":"mqtt","role":"subscriber","broker":"127.0.0.1:1883","topic":"opcua/uadp/data/100/line1"}}}{"action":"pubsub_start"}구독자 입력은 헤드리스 시뮬레이터에 보낼 JSON 명령을 한 줄에 하나씩 담습니다.
publisher-scenario.json [{"command": {"action": "configure","config": {"pubsub": {"transport": "mqtt","role": "publisher","broker": "127.0.0.1:1883","topic": "opcua/uadp/data/100/line1","publisher_id": 100,"writer_group_id": 1,"dataset_writer_id": 1}}}},{ "command": { "action": "pubsub_start" }, "expect": { "running": true, "transport": "mqtt" } },{"command": {"action": "pubsub_publish","fields": [{ "type": "Double", "value": 21.5 },{ "type": "Boolean", "value": true },{ "type": "String", "value": "RUN" }]},"expect": { "sequence": 1 }},{"command": {"action": "pubsub_publish","fields": [{ "type": "Double", "value": 21.75 },{ "type": "Boolean", "value": true },{ "type": "String", "value": "RUN" }]},"expect": { "sequence": 2 }},{ "command": { "action": "pubsub_stop" }, "expect": { "running": false } }]시나리오 파일은 단계 배열입니다. 각 단계의
command를 차례로 실행하고,expect가 있으면 결과의 같은 이름 값과 비교합니다. 발행자는 PublisherId 100, WriterGroupId 1, DataSetWriterId 1을 쓰고, OPC UA PubSub의 토픽 관례에 따라opcua/uadp/data/100/line1로 발행합니다. -
네 터미널 모두에서 시뮬레이터 패키지 경로를 변수로 정하고
mqtt-lab으로 이동합니다.터미널 창 PDCOMM_APPS=<시뮬레이터 패키지 디렉터리>cd mqtt-lab -
첫 번째 터미널에서 브로커를 시작합니다.
터미널 창 mosquitto -c local.conf -
두 번째 터미널에서 OPC UA 데이터 토픽 전체를 관찰합니다.
터미널 창 mosquitto_sub -V 5 -t 'opcua/uadp/data/+/#' -F '%t qos=%q retain=%r content-type=%C user-properties=%P %l bytes'+는 PublisherId 단계,#은 그 아래 모든 단계와 일치합니다. -
세 번째 터미널에서 구독자를 시작합니다.
터미널 창 cat subscriber.jsonl - | "$PDCOMM_APPS/bin/pdcomm-opcua-headless"cat이 파일의 두 줄을 먼저 보내고, 이어서 키보드 입력을 시뮬레이터로 넘깁니다(-). 두 번째 출력 줄에"role":"subscriber","running":true가 있으면 구독이 준비된 것입니다.{"ok":true,"result":{"acknowledged":0,"broker":"127.0.0.1:1883","destination":null,"dropped":0,"error":null,"listen":null,"queued":0,"received":0,"rejected":0,"role":"subscriber","running":true,"sent":0,"topic":"opcua/uadp/data/100/line1","transport":"mqtt"}}Windows PowerShell에서는
bin\pdcomm-opcua-headless.exe를 실행한 뒤subscriber.jsonl의 두 줄을 붙여 넣습니다. -
네 번째 터미널에서 발행자 시나리오를 실행합니다.
터미널 창 "$PDCOMM_APPS/bin/pdcomm-opcua-headless" --scenario publisher-scenario.json단계마다 결과가 한 줄씩 나오고 종료 코드 0으로 끝납니다. 첫 줄(구성 결과)은 길어서 생략했습니다.
{"result":{"acknowledged":0,"broker":"127.0.0.1:1883","destination":null,"dropped":0,"error":null,"listen":null,"queued":0,"received":0,"rejected":0,"role":"publisher","running":true,"sent":0,"topic":"opcua/uadp/data/100/line1","transport":"mqtt"},"step":2}{"result":{"bytes":52,"fields":3,"sequence":1},"step":3}{"result":{"bytes":52,"fields":3,"sequence":2},"step":4}{"result":{"running":false},"step":5}두 번째 터미널의 관찰자에는 두 줄이 나옵니다.
opcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytesopcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytes -
세 번째 터미널(구독자)에 다음 줄을 입력합니다.
{"action":"pubsub_received"}{"ok":true,"result":{"messages":[{"bytes":52,"datasets":[{"fields":[{"type":"Double","value":21.5},{"type":"Boolean","value":true},{"type":"String","value":"RUN"}],"major":1,"minor":1,"sequence":1}],"publisher_id":100,"source":"mqtt","writer_group":1,"writer_ids":[1]},{"bytes":52,"datasets":[{"fields":[{"type":"Double","value":21.75},{"type":"Boolean","value":true},{"type":"String","value":"RUN"}],"major":1,"minor":1,"sequence":2}],"publisher_id":100,"source":"mqtt","writer_group":1,"writer_ids":[1]}]}}publisher_id100,writer_ids[1], 순번 1과 2가 발행자 설정과 일치합니다. 구독자는 자신의publisher_id(기본값 77)와 관계없이 토픽으로 메시지를 받습니다.
GUI로 구독하기
섹션 제목: “GUI로 구독하기”구독자를 GUI로 바꿔도 결과는 같습니다.
- OPC UA 시뮬레이터 GUI를 열고 PubSub 탭으로 이동합니다.
- Transport를 UADP over MQTT, Role을 Subscriber로 고릅니다.
- MQTT broker에
127.0.0.1:1883, MQTT topic에opcua/uadp/data/100/line1을 입력하고 Start PubSub를 누릅니다. - 6단계의 발행자 시나리오를 다시 실행한 뒤 Show received를 누르면 표에 두 행이 나타납니다.
화면 구성은 시뮬레이터 PubSub 화면에서 설명합니다.
- 세 번째 터미널에서
{"action":"shutdown"}을 입력해 구독자를 끝냅니다. - 두 번째 터미널의
mosquitto_sub와 첫 번째 터미널의 브로커를 Ctrl+C로 끝냅니다.
자주 나는 문제
섹션 제목: “자주 나는 문제”| 증상 | 원인 | 해결 |
|---|---|---|
pubsub_received의 messages가 비어 있음 |
구독자를 발행자보다 늦게 시작했습니다. 시뮬레이터는 보존 메시지를 쓰지 않고 매번 새 세션으로 연결합니다. | 구독자를 먼저 시작하고 발행자 시나리오를 다시 실행합니다. |
| 관찰자에는 보이는데 구독자는 받지 못함 | 구독자의 토픽이 발행 토픽과 다릅니다. | 두 파일의 topic이 같은지 확인합니다. |
시나리오가 Scenario step 2 failed: MQTT broker connection failed: …로 끝남 |
브로커에 연결하지 못했습니다. | 3단계의 브로커와 broker 주소를 확인합니다. 오류 뒷부분은 연결 문제에서 찾습니다. |