구독
- 시뮬레이터 서버에 클라이언트 세션을 열고 변수 두 개를 구독합니다.
- 값을 바꾸고 Publish로 데이터 변경 알림을 받습니다.
- 받은 알림을 확인하고 구독을 지웁니다.
준비물
섹션 제목: “준비물”- 실습 환경의
apps패키지와 환경 변수OPCUA_APPS - 터미널 한 개
이 튜토리얼은 헤드리스 시뮬레이터에 JSON 명령을 한 줄씩 입력합니다. 한 프로세스 안에서 시뮬레이터 서버와 클라이언트가 함께 동작합니다. 응답도 한 줄씩 나옵니다. 성공하면 "ok":true와 result, 실패하면 "ok":false와 error가 있습니다. 포트, 시각, 세션 ID는 실행할 때마다 다릅니다.
1. 시뮬레이터 실행과 연결
섹션 제목: “1. 시뮬레이터 실행과 연결”헤드리스 시뮬레이터를 실행합니다.
"$OPCUA_APPS/bin/pdcomm-opcua-headless"서버를 시작합니다. 기본 구성은 127.0.0.1의 빈 포트에서 듣습니다.
{"action":"server_start"}{"ok":true,"result":{"audit":[],"endpoint":"opc.tcp://localhost:58927/pdcomm/simulator","error":null,"running":true,"secure":true,"sessions":0}}같은 시뮬레이터의 클라이언트로 서버에 연결합니다. URL을 지정하지 않으면 자기 서버에 연결합니다. 기본 보안 정책은 None입니다.
{"action":"connect"}{"ok":true,"result":{"connected":true,"identity":"anonymous","items":[],"mode":"None","pending_cancelled":0,"policy":"None","session":"ns=1;s=pdcomm.diagnostics/session/2bce1a9ad4fba143f97f8aad9fe8327a","subscription":null,"url":"opc.tcp://localhost:58927/pdcomm/simulator"}}2. 구독 만들기
섹션 제목: “2. 구독 만들기”Simulator.Double과 Simulator.Counter를 게시 주기 250 ms로 구독합니다.
{"action":"subscribe","nodes":["ns=2;s=Simulator.Double","ns=2;s=Simulator.Counter"],"interval_ms":250}{"ok":true,"result":{"items":[{"good":true,"handle":1,"monitored_item":1,"node":"ns=2;s=Simulator.Double","status":"Good (0x00000000)","subscription":1},{"good":true,"handle":2,"monitored_item":2,"node":"ns=2;s=Simulator.Counter","status":"Good (0x00000000)","subscription":1}],"subscription":1}}시뮬레이터가 CreateSubscription으로 구독 1을 만들고, CreateMonitoredItems로 노드마다 MonitoredItem을 추가했습니다. handle은 클라이언트 핸들(clientHandle)이고 monitored_item은 서버가 정한 ID입니다. 같은 세션에서 다시 subscribe하면 같은 구독에 항목을 더합니다.
3. 값 바꾸고 알림 받기
섹션 제목: “3. 값 바꾸고 알림 받기”Simulator.Double에 12.5를 씁니다.
{"action":"write","node":"ns=2;s=Simulator.Double","type":"Double","value":12.5}{"ok":true,"result":{"good":true,"node":"ns=2;s=Simulator.Double","status":"Good (0x00000000)"}}Publish를 두 번 보냅니다.
{"action":"publish","count":2}{"ok":true,"result":{"dropped":0,"notifications":[{"good":true,"handle":1,"kind":"data","node":"ns=2;s=Simulator.Double","sequence":1,"server_time":1791997609191,"source_time":1791997609053,"status":"Good (0x00000000)","type":"Double","value":0.0},{"good":true,"handle":2,"kind":"data","node":"ns=2;s=Simulator.Counter","sequence":1,"server_time":1791997609194,"source_time":1791997609055,"status":"Good (0x00000000)","type":"UInt32","value":1},{"good":true,"handle":1,"kind":"data","node":"ns=2;s=Simulator.Double","sequence":2,"server_time":1791997609441,"source_time":1791997609196,"status":"Good (0x00000000)","type":"Double","value":12.5}],"queued":3}}- 첫 알림 메시지(
"sequence":1)에는 구독을 만든 순간의 값이 들어 있습니다. MonitoredItem을 만들면 서버가 현재 값을 한 번 보냅니다. - 두 번째 메시지(
"sequence":2)에는 바뀐 값 12.5가 들어 있습니다. source_time과server_time은 Unix 밀리초입니다.queued는 시뮬레이터가 보관 중인 알림 수입니다.
Simulator.Counter는 시뮬레이션 주기(기본 500 ms)마다 1씩 늘어납니다. 잠시 뒤 다시 Publish를 보냅니다.
{"action":"publish"}{"ok":true,"result":{"dropped":0,"notifications":[{"good":true,"handle":2,"kind":"data","node":"ns=2;s=Simulator.Counter","sequence":3,"server_time":1791997609697,"source_time":1791997609555,"status":"Good (0x00000000)","type":"UInt32","value":2}],"queued":4}}Publish 응답은 바뀐 값이 있으면 게시 주기마다, 없으면 keep-alive 주기(게시 주기의 3배) 안에 옵니다. keep-alive 응답에는 알림이 없습니다.
4. 받은 알림 확인
섹션 제목: “4. 받은 알림 확인”시뮬레이터가 보관한 알림 전체를 봅니다.
{"action":"notifications"}{"ok":true,"result":{"dropped":0,"entries":[{"good":true,"handle":1,"kind":"data","node":"ns=2;s=Simulator.Double","sequence":1,"server_time":1791997609191,"source_time":1791997609053,"status":"Good (0x00000000)","type":"Double","value":0.0},{"good":true,"handle":2,"kind":"data","node":"ns=2;s=Simulator.Counter","sequence":1,"server_time":1791997609194,"source_time":1791997609055,"status":"Good (0x00000000)","type":"UInt32","value":1},{"good":true,"handle":1,"kind":"data","node":"ns=2;s=Simulator.Double","sequence":2,"server_time":1791997609441,"source_time":1791997609196,"status":"Good (0x00000000)","type":"Double","value":12.5},{"good":true,"handle":2,"kind":"data","node":"ns=2;s=Simulator.Counter","sequence":3,"server_time":1791997609697,"source_time":1791997609555,"status":"Good (0x00000000)","type":"UInt32","value":2}],"limit":1000}}보관 한도(limit)는 1000개입니다. 넘치면 오래된 알림부터 버리고 dropped를 늘립니다. {"action":"notifications_clear"}로 비웁니다.
5. 구독 지우기
섹션 제목: “5. 구독 지우기”{"action":"unsubscribe"}{"ok":true,"result":{"deleted":1,"status":"Good (0x00000000)"}}구독이 없으면 Publish를 보낼 수 없습니다.
{"action":"publish"}{"error":"No subscription exists","ok":false}6. 정리
섹션 제목: “6. 정리”연결을 끊고 프로그램을 끝냅니다.
{"action":"disconnect"}{"action":"shutdown"}{"ok":true,"result":{"client_closed":null,"pubsub_stopped":false,"server_stopped":true}}실패한 명령이 하나라도 있었으면 헤드리스 시뮬레이터는 종료 코드 1로 끝납니다. 이 튜토리얼은 일부러 실패한 Publish가 있으므로 종료 코드가 1입니다.
자주 생기는 오류
섹션 제목: “자주 생기는 오류”| 응답 | 원인과 조치 |
|---|---|
Publishing interval must be 50 to 10000 ms and three intervals below the timeout |
게시 주기 세 번이 클라이언트 제한 시간(기본 5000 ms)보다 깁니다. 예를 들어 "interval_ms":2000은 거부됩니다. 주기를 줄이거나 client.timeout_ms를 늘립니다. |
No subscription exists |
구독을 만들기 전이나 지운 뒤에 publish를 보냈습니다. |
The client is not connected |
connect 전에 명령을 보냈습니다. |
Stop the endpoint before changing its configuration |
연결된 상태에서 configure로 클라이언트 구성을 바꾸려 했습니다. disconnect 뒤에 바꿉니다. |
GUI에서는 Subscriptions 탭에서 같은 작업을 합니다. 시뮬레이터 GUI를 참고합니다.