Sparkplug B에서 돈이 드는 실수는 페이로드가 아니라 첫 주에 정해버린 group_id입니다. 대시보드 세 개와 히스토리언, 분석 잡이 이미 spBv1.0/BrewhouseA/#를 구독한 뒤에 이 이름을 바꾸면, 그 문자열 하나 때문에 모든 구독을 쫓아다니며 고쳐야 합니다. 프로토콜이 이름 변경을 막지는 않지만, 배포 구조가 그 대가를 톡톡히 치르게 만듭니다. 그래서 토픽 네임스페이스는 천천히 볼 가치가 있는 부분입니다.
다섯 필드 중 진짜 중요한 것
Sparkplug B는 토픽 구조를 고정해 둡니다:
spBv1.0/[group_id]/[message_type]/[edge_node_id]/[device_id]
message_type은 내가 설계하는 게 아닙니다. NBIRTH, NDATA, NDEATH, DBIRTH, DDATA, DDEATH, NCMD, DCMD 중 하나로 정해져 있습니다. 내가 실제로 정하는 건 group_id, edge_node_id, device_id 세 개이고, 이건 결국 이 설비를 어떻게 유지보수할지와 맞물립니다.
| 필드 | 대응 | 함정 |
|---|---|---|
group_id | 사이트 / 영역 / 프로젝트 경계 | 모든 구독이 와일드카드에 이 값을 넣는다. 바꾸면 전부 재구독. 한 번에 정한다. |
edge_node_id | 단독으로 재기동할 수 있는 게이트웨이·IPC | DESKTOP-7F3K나 통합업체 노트북 이름이 아니라 유지보수 대상 자산 이름으로. |
device_id | 그 노드 뒤의 PLC·스키드·계량기·드라이브 | DBIRTH/DDATA/DDEATH에만 존재. 한 노드가 여러 논리 디바이스를 대변할 때 쓴다. |
값을 하나 보냈을 때 어느 물리 판넬에서 온 건지 알아내려고 스프레드시트를 열어야 한다면 네임스페이스가 실패한 겁니다. L3N02/D14보다 Line3/PalletizerPLC가 낫습니다.
엣지 노드 경계는 재기동이 덜 아픈 지점에
게이트웨이 하나가 모든 PLC에 닿을 수 있다고 해서 공장 전체를 엣지 노드 하나에 몰지 마세요. 중요한 건 재기동의 파급 범위입니다. 엣지 노드를 내리면 NDEATH가 나가고, 그 아래 모든 디바이스가 SCADA 호스트에서 stale 처리되어야 합니다. 그래서 노드는 실제로 한 덩어리로 재기동할 만한 단위 — 라인 하나, 스키드 하나, 셀 하나, 프로토콜 게이트웨이 하나 — 와 맞아야 합니다.
너무 넓으면 게이트웨이 일반 재부팅 한 번에 공장 절반이 offline으로 뜨고, 너무 잘게 나누면 작은 스키드 하나가 엣지 노드 40개, birth 인증서 40개, 감시 대상 40개를 만듭니다. 저는 "같은 고장과 같은 정비 창을 공유하는 디바이스 묶음"을 기준으로 잡습니다.
bdSeq와 NDEATH가 핵심이다
여기를 건너뛰고서 네트워크가 한 번 끊긴 뒤 호스트에 유령 online 노드가 남는 이유를 궁금해하는 경우가 많습니다.
엣지 노드는 MQTT CONNECT 시점에 자신의 NDEATH를 Last Will and Testament(LWT) 로 등록합니다. 이 메시지에는 bdSeq(birth/death sequence) metric이 들어 있고, 짝이 되는 NBIRTH에도 같은 bdSeq가 들어갑니다. 이 짝 맞춤 덕분에 호스트는 "이 NDEATH가 지금 살아 있다고 믿는 세션의 것"인지 "예전 세션에서 남은 오래된 will"인지 구분합니다. 새 MQTT 세션마다 bdSeq를 증가시키세요(8비트 롤링 값). 이걸 틀리면 재접속 후 호스트가 죽은 노드를 online으로 자신 있게 표시합니다.
헷갈리기 쉬운 숫자 두 개:
- 페이로드
seq(0–255, 255 다음 0으로 wrap)는NBIRTH에서 반드시 0이고 이후 모든 메시지마다 증가합니다. 중간에 빠지면 호스트는 뭔가 놓쳤다고 보고 birth 재요청을 해야 합니다. 세션 도중에 리셋하지 마세요. - metric alias는
NBIRTH에서 배정됩니다(metric 이름 → 정수). 이후NDATA는 전체 이름 대신 alias만 보내 페이로드를 줄입니다. 즉NDATA만 받은 구독자는 아무것도 해석할 수 없고, birth를 반드시 잡았어야 합니다. 이게 설계 의도이고, birth 신뢰성이 선택이 아닌 이유입니다.
metric 이름은 사람들이 실제로 사는 곳
페이로드 안의 metric은 SCADA 포인트, 히스토리언 태그, 분석가가 매일 만지는 것입니다. 새벽 2시에 공장을 지원하는 사람 기준으로 이름을 지으세요:
Pump101/RunFeedback
Pump101/StartCommand
Pump101/DischargePressure
Tank204/Level
Tank204/Level/HighAlarmActive
Mixer03/SpeedSetpoint
HMI·PLC 태그 이름과 맞추되, PLC 심볼이 P101_RunFb라면 metric은 Pump101/RunFeedback처럼 더 읽기 좋게 가도 됩니다. 단 그 매핑을 문서로 남기세요. 18개월 뒤엔 아무도 기억 못 합니다.
그리고 원시 값만 보내지 마세요. 0은 bad quality, stale, PLC 단절과 다릅니다. 중요한 설비라면 command·feedback 상태, auto/manual 또는 local/remote, 인터록/permissive 요약, 필요한 곳의 alarm-active, 하위 디바이스 통신 상태까지 포함하세요. Sparkplug metric에는 quality property가 있으니 구독자에게 추측시키지 말고 그걸 쓰세요.
모든 태그를 1초 태그로 만들지 마라
MQTT는 발행이 너무 쉬워서 그게 문제입니다. 모든 레지스터를 1초로 쏘면 아무도 안 보는 데이터까지 히스토리언에 쌓입니다. 신호에 맞춰 주기를 정하세요.
| 신호 | 발행 방식 |
|---|---|
| 설비 상태 | 변경 시 + 느린 heartbeat(예: 60초)로 노드 생존 확인 |
| 아날로그 PV | deadband 적용 변경 시(저는 보통 2%부터) + 주기적 refresh |
| 고속 머신 시퀀스 | 원시 고속 프레임이 정말 필요하지 않으면 요약 결과·이벤트 상태로 |
| 가동/에너지 카운터 | 주기적 + 리셋·롤오버 시 |
| 알람 상태 | 변경 시 — 그리고 버스트 상황의 순서를 확인(그때 깨진다) |
타임스탬프 소스를 명시적으로 정하세요. PLC 시계, 엣지 노드 시계, 브로커 수집 시각. 서로 드리프트가 있고, 이걸 몰래 섞는 히스토리언은 아무도 못 믿는 트렌드를 만듭니다.
retain과 STATE 하나만 기억하기
Sparkplug 메시지는 retain = false로 발행됩니다. 의도된 겁니다. retain된 NDATA는 뒤늦게 붙은 구독자에게 birth 맥락도 없이 오래된 값을 쥐여줍니다. 예외는 STATE 하나입니다. SCADA 호스트 자신의 online/offline 메시지(Sparkplug 3.0에서 spBv1.0/STATE/[host_id], JSON online/timestamp 페이로드)는 retain되어, 엣지 노드가 접속하는 순간 호스트 상태를 알 수 있습니다. "대시보드가 빨리 채워지게" 데이터 토픽에 retain을 거는 사람이 있다면 그건 기능이 아니라 버그입니다.
커미셔닝: happy path 말고 고장을 시험하라
happy path — 브로커 켜짐, 게이트웨이 켜짐, 올바른 순서로 기동 — 는 언제나 됩니다. 공장은 올바른 순서로 고장 나지 않습니다. 그래서 값어치 하는 점검은:
- 엣지 노드 재기동 →
seq= 0, 새bdSeq의NBIRTH확인. - 노드 네트워크 차단 → 호스트가
NDEATH(LWT)를 받고 디바이스를 stale 처리하는지 확인. "마지막 값 영원히"가 아니라. - 디바이스 하나만 재기동 → 예상된
DBIRTH가 나오고 같은 노드의 다른 디바이스는 영향 없는지 확인. - 기대한 metric이 모두 birth 페이로드에 있고, 재기동 사이 데이터 타입이 안정적인지 확인(birth에서
Int32인데 data에서Float이면 엄격한 구독자는 깨진다). - 브로커 failover 후 노드와 호스트가 유령 online 없이 복구되는지 확인.
네임스페이스, birth/death 핸드셰이크, retain 규칙이 맞으면 좋은 Sparkplug 배포는 평상시엔 조용하고 고장 때만 원하는 방식으로 시끄럽습니다. 지원 인력은 토픽과 마지막 birth만 보고 세 가지를 답할 수 있어야 합니다. 이 값을 보낸 자산은 무엇인가, metric 목록을 마지막으로 증명한 게 언제인가, 그리고 이 값은 live·stale·bad 중 무엇인가.