← 전체 글
OPC UA/약 9분 읽기/ 조회

Quality는 Good인데 값은 멈췄다: OPC UA StatusCode와 Timestamp 읽기

값만 보는 HMI가 숨기는 frozen data, gateway cache 값, clock drift를 OPC UA StatusCode severity 비트와 SourceTimestamp, ServerTimestamp로 잡아내는 방법.

OPC UASCADA태그문제 해결프로젝트 노트

HMI의 탱크 레벨이 3.42 m로 보인다. 40분째 3.42 m다. Operator는 펌프가 꺼져 있다고 생각한다. 실제로는 펌프가 트립됐고 레벨은 올라가고 있는데, gateway 뒤의 OPC UA server가 40분 전 SourceTimestamp가 찍힌 마지막 캐시 DataValue를 Good status로 계속 내보내고 있다. HMI tag를 value 하나에만 매핑해 둔 탓에 아무도 그 timestamp를 못 본다.

이 주제가 존재하는 이유가 바로 이 장애다. OPC UA의 모든 read와 subscription notification은 DataValue다. value, StatusCode, SourceTimestamp, ServerTimestamp가 한 묶음으로 온다(IEC 62541-4). value만 tag에 매핑하면 값을 믿어도 되는지 알려 주는 나머지 세 필드를 버리는 셈이다.

StatusCode는 32비트, 먼저 볼 건 상위 2비트뿐

OPC UA StatusCode는 32비트 숫자다. 가장 먼저 봐야 할 건 최상위 2비트, 즉 severity다.

  • 00Good (0x00000000 대)
  • 01Uncertain (0x40000000 대)
  • 10Bad (0x80000000 대)

나머지 비트는 그런지 알려 주는 sub-code다. 모든 걸 "bad quality" 하나로 뭉개는 driver는 이 정보를 버리고, 하필 제일 급할 때 그 대가를 치른다. Bad_NoCommunication(0x80310000)은 케이블, switch, 죽은 PLC다 — 현장으로 가야 한다. Bad_NodeIdUnknown(0x80340000)은 주소가 사라진 것이고, 거의 항상 PLC download 뒤 NodeId가 밀린 경우다. 공장을 아무리 돌아도 안 고쳐진다. HMI에서 똑같이 빨갛게 뜬 두 tag, 조치는 완전히 다르다.

시운전 중에 구분해서 남길 만한 것들:

심볼 이름Severity실제 의미
GoodGood값이 살아 있고 쓸 수 있음
UncertainLastUsableValueUncertainServer가 stale 가능성을 경고 중 — Good으로 취급 금지
Bad_NoCommunicationBadPLC / remote I/O / gateway 통신 두절
Bad_NodeIdUnknownBad주소 매핑 깨짐, 보통 program download 뒤
Bad_OutOfServiceBad해당 item을 일부러 scan에서 제외
Bad_UserAccessDeniedBad통신이 아니라 access level, role, security policy 문제

빠뜨리기 쉬운 게 UncertainLastUsableValue다. Server가 "지금 보여 주는 건 마지막으로 갖고 있던 값"이라고 정직하게 말하는 상태다. HMI가 Uncertain을 Good과 똑같이 그리면, server가 경고하려던 stale-value 버그를 그대로 다시 만든 것이다.

두 timestamp는 따로가 아니라 서로 비교해서 읽는다

SourceTimestamp는 source가 값을 샘플링하거나 변했다고 본 시각이다. ServerTimestamp는 OPC UA server가 처리한 시각이다. 둘 다 UTC이고(1601년부터 100 ns 틱, Windows FILETIME 인코딩), client가 local time으로 보여 준다면 그 변환이 "시간이 틀렸다"는 오해가 생기는 또 하나의 지점이다.

진단은 둘 사이의 간격에 있다.

  • ServerTimestamp는 최신, SourceTimestamp는 오래됨 → server는 살아 있는데 원천 값이 안 움직인다. Frozen source이거나 캐시가 값을 붙들고 있다. 위의 탱크 레벨이 이 경우다.
  • ServerTimestamp도 오래됨 → 문제는 server 쪽이다. Subscription이 굶고 있거나, session이 끊겼거나, publishing이 멈췄다.
  • SourceTimestamp가 ServerTimestamp보다 앞섬 → 데이터 문제가 아니라 source와 server 사이 clock 불일치다.

오래된 SourceTimestamp를 장애로 부르기 전에 한 가지 단서를 봐야 한다. Tag 종류에 달렸다. Batch step number는 step이 안 바뀌면 6분 동안 같은 SourceTimestamp를 갖는 게 정상이다. 반대로 1초 analog가 안 움직이면 그건 죽은 거다. Tag가 어느 종류인지 알아야 하고, 이건 시운전 때 tag별로 적어 둔다.

  • Analog process value: publish마다 또는 유의미한 변화마다 갱신(server 설정 의존)
  • Discrete status: 전이할 때만 갱신
  • Counter / totalizer: 증가 시점 또는 샘플링 시점에 갱신
  • Recipe / batch state: step 내내 그대로일 수 있음 — 오래된 게 정상
  • Heartbeat: 정해진 주기로 반드시 갱신 — 이게 stale 판단 기준

마지막 줄이 싼 보험이다. Server 쪽에서 1초마다 증가하는 heartbeat tag 하나면 "항상 움직여야 하는 값" 하나가 생긴다. 그게 멈추면 경로가 죽은 것이다 — tag마다 추측할 필요가 없다.

FAT에서 다들 건너뛰는 건 disconnect 시험이다

Critical tag마다 시운전 시험은 여섯 단계인데, 가치는 전부 마지막 두 단계에 있다.

  1. Source에서 안전한 값 변경을 만든다.
  2. 화면 값이 바뀌는지 확인한다.
  3. SourceTimestamp가 기대한 시점에 갱신되는지 본다.
  4. ServerTimestamp와 client 수신 시각 차이가 공정 기준 안에 드는지 본다.
  5. Source 경로를 끊고, quality가 Good으로 얼지 않고 Bad로 바뀌는지 확인한다.
  6. 다시 연결하고, HMI 재시작 없이 quality와 timestamp가 회복되는지 확인한다.

1~4단계는 어지간한 시스템에서 다 통과한다. 5, 6단계에서 마지막 값을 Good으로 계속 내보내는 gateway, 재구독을 안 하는 reconnect 로직, stale 값을 아무 표시 없이 보여 주는 HMI가 걸린다. 값이 바뀔 수 있다는 것만 시험했다면 쉬운 절반만 시험한 것이다.

로직을 디버그하기 전에 clock 계층부터 맞춘다

OPC UA 값, alarm, historian sample, MES event는 결국 downstream에서 timestamp로 줄 세워진다. PLC, OPC UA server, SCADA server, historian가 5분씩 어긋나면 그 비교가 전부 거짓말이 된다. 같은 5분 offset이 한 화면에선 historian backfill 오류로, 다른 화면에선 alarm 순서 뒤바뀜으로, MES에선 lot trace 깨짐으로 보인다 — clock 하나 틀려서 "버그" 세 개가 나온다.

Application 로직을 건드리기 전에 지루한 계층부터 확인한다.

  • OPC UA server host의 NTP / 공장 time source — 정말 동기화 중인가, 아니면 설정만 되고 조용히 실패 중인가?
  • Server time과 SCADA server time 차이 — alarm, historian 시험 시작 전에 실제로 재 본다.
  • Clock 값뿐 아니라 time zone과 DST 설정.
  • Client가 UTC를 local로 어떻게 렌더링하는지 — "시간 틀림" 신고 상당수는 순수 표시 문제다.
  • 일반 OS time service가 없는 embedded gateway의 시간 기준 — 명시적으로 적어 둔다.

기준 값을 남겨 둔다

Critical interface마다 프로젝트 파일에 몇 줄 남긴다. Endpoint URL과 security policy, subscription과 sampling interval, quality/timestamp를 historize하는지, tag 종류별 stale timeout, controlled-change와 disconnect 시험에서 관찰한 SourceTimestamp/ServerTimestamp. 다음에 누가 HMI가 "느리다"고 하면, 논쟁 대신 known-good 기록과 비교하면 된다. 벤더가 자기네 status 처리가 "spec대로"라고 우기면, 이 code들이 무엇을 뜻해야 하는지 규정한 IEC 62541-8(Data Access)을 근거로 대면 된다.