← 전체 글
태그/약 11분 읽기/ 조회

Bad Quality를 0으로 바꾸지 마라: 운전자를 속이지 않는 SCADA Tag Quality 처리

SCADA tag quality가 실제로 어떻게 동작하는지 — OPC DA quality word, OPC UA StatusCode, DNP3 flag — 그리고 죽은 신호가 정상 공정 값처럼 보이지 않게 표시·알람·저장하는 방법.

SCADA태그HMI알람문제 해결

야간 근무 중에 flow meter 하나가 Modbus 링크에서 떨어졌다. 게이트웨이가 timeout 났고, driver는 레지스터에 0을 썼고, HMI에는 0.0 m³/h가 떴다. 운전자는 이걸 "펌프가 제품을 못 보내고 있다"로 읽고 bypass를 열었다가 sump로 넘쳤다. 계측기는 내내 멀쩡했다 — 화면의 숫자는 통신 장애를 공정 값으로 위장한 것뿐이었다.

Tag quality 문제는 이 사고 하나에 다 들어 있다. 값과 품질은 서로 다른 두 신호다. 품질을 버리는 순간, 시스템 스스로도 믿지 않는 숫자를 운전자한테 믿으라고 떠넘기는 셈이다.

품질은 머릿속이 아니라 프로토콜 안에 있다

제대로 된 SCADA 프로토콜은 값과 함께 품질 필드를 같이 싣는다. 그게 어디 있는지 알면 추측할 필요가 없다.

OPC DA는 품질을 16-bit word에 담지만 실무에선 상위 바이트만 본다. 0xC0으로 마스킹하면 0xC0이 Good, 0x40이 Uncertain, 0x00이 Bad다. substatus nibble이 이유를 알려준다 — 0x18은 comms failure, 0x0C는 device failure, 0x1C는 last-known-value. 그래서 0xD8은 아무 숫자가 아니라 "local override 힌트가 붙은 Good"이다. 많은 연동이 값만 보고 quality word는 마스킹조차 안 한다.

OPC UA는 이걸 32-bit StatusCode로 바꿨다(IEC 62541-4, Data Access는 Part 8). 상위 두 비트가 severity다 — 0x00000000 Good, 0x40000000 Uncertain, 0x80000000 Bad. 전체 표를 외울 필요 없이 code & 0xC0000000이면 어느 버킷인지 나온다. 이름 붙은 코드는 로그에 그대로 뜨니 알아 두면 좋다: Bad_NoCommunication(0x80310000), Bad_NotConnected(0x808A0000), Bad_OutOfService(0x808D0000), Uncertain_LastUsableValue(0x40900000), Bad_SensorFailure(0x80510000). limit bit(bit 8–9)도 있는데, 값이 range 한계에 붙어 있다는 뜻이다 — 기술적으론 "good"이지만 saturation으로 멈춘 analog를 잡는 데 쓸모 있다.

DNP3(IEEE 1815)는 quality code 대신 point마다 flags octet를 보낸다: ONLINE(bit 0), RESTART, COMM_LOST, REMOTE_FORCED, LOCAL_FORCED, analog은 OVER_RANGE, REFERENCE_ERR까지. ONLINE = 1이 곧 "good"이다. COMM_LOST가 서 있으면 outstation이 이 point는 stale이라고 알려주는 거다 — 무시하지 말고 읽어서 태그에 표시해라.

Modbus는 아무것도 안 싣는다. Holding register는 16-bit 값이 전부다. Modbus 태그의 품질은 전적으로 driver가 timeout과 exception 응답으로 합성한다 — 그래서 위 flow meter 사고가 DNP3가 아니라 Modbus에서 난 거다. Modbus를 쓴다면 driver의 timeout과 "on-failure" 동작이 곧 품질 모델이니, 뭘로 설정돼 있는지 직접 확인해라.

운전자가 실제로 구분해야 하는 상태

제품마다 이름은 다르지만, 이걸 회색 하나로 뭉개는 게 진짜 실수다. 통신 끊긴 계측기, 일부러 bypass한 point, 입력 하나 빠진 계산은 각각 다른 대응이 필요하다.

상태어디서 오는가화면 처리
Good최신 값, 정상 source일반 표시
통신 불량driver / 장비 / route 다운bad quality 표시, 공정 알람 판정 중단
Stale마지막 값이 최대 허용 시간 초과마지막 값 + 경과 시간, 오래됨 표시
Uncertain연결됐지만 값 신뢰도 낮음경고 스타일 + 이유
Substituted / forced수동, simulation, LOCAL_FORCED현장 값이 아님을 명확히
Out of service의도적 사용 제외승인·기록 있을 때만 불필요 알람 억제
Calc bad입력 또는 계산 실패사용 불가로 표시, 절대 0 아님

Bad quality를 값으로 바꾸지 마라

Bad quality를 0, false, ""로 바꾸면 화면은 깔끔해지고 진단 정보는 조용히 사라진다. 실패는 늘 같은 모양이다.

  • 죽은 flow meter가 zero flow로 읽혀 엉뚱한 대응을 부른다(위 사고).
  • Feedback 잃은 valve가 닫힘처럼 보인다.
  • Temperature 태그 손실이 false low alarm으로 바뀐다.
  • 입력 하나가 죽어서 derived "running" 태그가 뒤집힌다.
  • Historian trend가 gap 대신 0으로 떨어지는 깨끗한 선을 그린다.

받는 시스템이 정말로 quality를 못 싣는다면 — flat Modbus map을 제3자 MES로 넘기는 경우가 흔하다 — companion tag를 명시적으로 둔다: P101_FLOW 옆에 P101_FLOW_Q. 지저분하다. 그래도 틀린 걸 아는 숫자를 보내는 것보단 안전하다.

HMI: 숫자는 남기되 신뢰도를 표시해라

마지막 값은 화면에 남겨라 — 운전자가 판단할 뭔가는 있어야 한다 — 대신 그 값이 제어 판단용이 아니라는 걸 분명히 해라. Ignition은 tag quality가 Good이 아닌 component에 대각선 hatch overlay를 씌운다. 다른 플랫폼은 테두리나 X를 쓴다. 뭘 쓰든 알람 팔레트와 겹치는 색은 피해라.

Run feedback이 bad quality인데 pump faceplate에 정지를 띄우면 두 번 거짓말하는 거다. 운전 피드백 확인 불가라고 읽히게 하고, command output이 나가 있는지도 같이 보여줘라 — "운전 명령을 냈는데 확인이 안 된다"는 "멈춰 있다"와 완전히 다른 상황이니까. 중요한 analog는 faceplate에 마지막 갱신 후 경과 시간을 넣어라. 40분 된 값이면 40분 됐다고 써 있어야 한다.

알람: 못 믿는 데이터로 limit을 판정하지 마라

Bad quality에 전역 규칙 하나만 두면 alarm flood가 나거나 위험이 가려진다. 알람 종류별로 나눠라.

Analog process alarm: 입력 품질이 bad인 동안 high/low limit 판정을 중단해라. Driver가 Bad_NoCommunication으로 flag한 값을 두고 온도가 "HIGH"라고 정직하게 말할 순 없다. 그 point 손실이 중요하면 별도 instrument/data-quality alarm을 올리고, 판정을 멈춘 시점을 기록하고, 복구 시엔 부드럽게 재개해서 comms 복귀 순간 오래된 transition이 한꺼번에 쏟아지지 않게 해라.

Discrete alarm과 interlock: unknown feedback을 절대 safe state로 읽지 마라. Bad-quality 입력 위에 세운 permissive는 보수적으로 떨어뜨리고 이유를 보여줘라. 그리고 comms 실패는 rollup 해라 — PLC rack 2 통신 불량 하나가 조치 가능한 알람이다. 같은 스위치 하나가 죽어서 뜨는 개별 tag bad 알람 300개는 그걸 파묻는 노이즈다.

히스토리언과 보고서

Historian이 status 없이 값만 저장하면 통신 장애가 gap을 가로지르는 매끄러운 보간 선이 되고, 6개월 뒤 누군가 그 위에 규제 보고서를 만든다. Sample마다 quality를 같이 저장하고, trend가 bad-quality 구간을 이어진 선이 아니라 gap이나 음영으로 그리게 해라. 적산과 평균은 bad-quality 구간을 제외할지, 추정할지, 별도 보고할지 미리 정해라 — 아무 표시 없이 실제 생산처럼 평균에 넣지 마라.

계산 태그는 품질이 조용히 죽는 자리다. 계산식이 숫자 하나만 반환하면서 status가 떨어져 나가니까. 규칙을 명시해라: level이나 density가 bad인 tank-mass 계산은 0 kg이 아니라 계산 불가로 읽혀야 한다. MCC status가 빠진 line "Available" 태그는 false가 아니라 bad-with-reason으로 읽혀야 한다. 플랫폼이 quality를 propagate 하면 그걸 쓰고, 아니면 derived value마다 status 태그를 옆에 붙여라.

본전 뽑는 5분짜리 시험

인수 전에, simulation 태그가 아니라 실제 장비로 이걸 해라.

  1. PLC 통신 경로 하나를 뽑고 HMI에서 tag quality가 뒤집히는지 본다.
  2. False process alarm 벽이 아니라 data-quality alarm이 뜨는지 확인한다.
  3. Trend가 장애 구간을 gap이나 bad 구간으로 보여주는지 본다.
  4. 통신을 복구하고 timestamp·quality·alarm state가 깨끗하게 돌아오는지 — stale-transition 폭발 없이 — 확인한다.
  5. Substituted value를 force하고 화면 보고서에 표시가 뜨는지 확인한다.
  6. 계산 태그 입력 하나를 끊고 결과가 정상처럼 안 읽히는지 확인한다.
  7. 끝나고 event log를 읽어라 — 다음 주에 이 장애를 설명할 만큼 남아 있나?

이 시험은 싸고, 진짜 버그를 자주 드러낸다: 너무 길게 잡힌 driver timeout, 다시 안 읽는 scan class, 벤치 테스트 때 넣고 안 지운 "map failure to zero". 운전자가 실재하지도 않던 값을 보고 bypass를 여는 밤보다, 시운전하는 오후에 찾는 게 낫다.