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

OPC UA 샘플링과 퍼블리싱: 접속됐다고 데이터가 좋은 건 아니다

OPC UA 샘플링 주기, 퍼블리싱 주기, 큐 크기, deadband, KeepAlive/Lifetime 관계가 SCADA 클라이언트와 히스토리언이 공정을 제대로 보는지를 어떻게 결정하는지.

OPC UASCADA히스토리언문제 해결태그

클라이언트에는 접속 아이콘이 초록색이고, 엔드포인트도 맞고, 인증서도 몇 달 전에 신뢰 처리했다. 그런데도 트립 분석에서 어느 펌프의 압력이 먼저 빠졌는지 알 수가 없다. 데이터는 있었다. 히스토리언에 도달하기 전에 구독이 대부분을 버렸을 뿐이다.

이 문제는 통신 에러로는 거의 안 나온다. 평평한 트렌드, 패널보다 2초 늦는 값, 엉뚱한 태그의 노이즈만 잔뜩 쌓인 히스토리언으로 나타난다. 원인은 대부분 클라이언트 스택 기본값 그대로 방치된 다섯 개의 구독 파라미터다.

샘플링과 퍼블리싱은 서로 다른 시계다

모두가 여기서 헷갈리니 먼저 짚는다. OPC UA Part 4 기준으로 monitored item에는 SamplingInterval(서버가 원본 값을 확인하는 간격)이 있고, 그 구독에는 PublishingInterval(서버가 모인 통지를 클라이언트로 보내는 간격)이 있다. 둘은 같은 값이 아니고, 같을 필요도 없다.

서버가 압력 태그를 250 ms마다 샘플링하고 1000 ms마다 퍼블리싱한다고 하자. 이 1초 동안 서버는 값을 네 번 봤다. 클라이언트가 무엇을 받느냐는 전적으로 QueueSize에 달려 있다.

  • QueueSize 1: 클라이언트는 가장 최근 값 하나만 받는다. 나머지 셋은 사라진다. HMI 페이스플레이트에는 이걸로 충분하다.
  • QueueSize 4 이상: 네 값이 각자 source timestamp를 달고 한 번의 Publish 응답에 담겨 온다. 트립 분석에 필요한 건 이쪽이다.

사람들이 존재를 잊는 두 설정. SamplingInterval -1은 "퍼블리싱 주기를 그대로 쓴다", 0은 "서버가 지원하는 최대 속도로"라는 뜻이다. 따로 안 정하면 조용히 둘 중 하나가 되어 있는 경우가 많다. 그리고 DiscardOldest(기본값 true)는 큐가 찼을 때 어느 쪽을 버릴지 정한다. 트렌드용이라면 true로 둬라. 램프 구간에서 가장 오래된 샘플을 잃는 편이 가장 최신 샘플을 잃는 것보다 덜 아프다.

운전 화면은 최신값이 이기는 게 맞고 QueueSize 1이 대역폭을 아낀다. 반대로 이벤트 순서 재구성에는 샘플 속도보다 큐가 더 중요하다.

서버는 요청을 조용히 무시할 수 있다

100 ms 샘플링을 요청한다. 서버는 RevisedSamplingInterval: 1000을 돌려준다. MinSupportedSampleRate가 1초라서, 또는 하부 PLC 스캔이 1초라 더 빨리 샘플링해봐야 의미가 없어서다. RevisedPublishingInterval과 RevisedQueueSize도 똑같이 동작한다.

많은 클라이언트가 이 수정값을 아무 말 없이 받아들인다. 그래서 시운전 첫 확인은 항상 이거다. CreateMonitoredItems 이후 수정된 값을 다시 읽어 요청값과 맞는지 본다. 서버가 100 ms를 1초로 깎았다면 클라이언트를 아무리 튜닝해도 소용없다. 그 속도의 데이터는 애초에 존재하지 않는다. 그래도 요청하면 착각만 얻는다.

주기는 습관이 아니라 용도로 정한다

모든 태그를 1초 구독 하나에 몰아넣지 마라. 용도로 묶는다.

신호 종류시작 기준
빠른 상태모터 운전 피드백, 밸브 이동, 시퀀스 스텝250–500 ms 샘플링, 전이가 중요하면 큐 ≥ 4
운전 화면용 아날로그압력, 유량, 온도500 ms–2 s, 화면 갱신 속도에 맞춤
느린 아날로그탱크 레벨, 실내 온도, 유틸리티 적산제어·알람 목적이 아니면 5–30 s
진단값장치 온도, 펌웨어 상태, 포트 카운터30–300 s, 운전 구독과 분리
이벤트성 비트배치 단계 완료, 퍼미시브 순간 해제펄스를 잡을 만큼 빠르게 — 아니면 PLC에서 래치

내가 지키는 규칙. 빠른 구독에는 이유가 붙어야 한다. 운전자가 봐야 해서, 알람 때문에, 히스토리언 분석 때문에, 시운전 테스트 때문에. 1년 뒤 누가 서버 부하를 줄이라고 하면, 이유 없는 태그가 제일 먼저 느려질 후보다.

deadband는 퍼센트가 아니라 공학 단위로

DataChangeFilter는 통신량을 줄이지만, 넓은 deadband는 진짜 움직임을 가린다. 나는 거의 항상 DeadbandType을 Percent가 아니라 Absolute로 둔다. 절대값이라야 계측기 실제 노이즈나 공정 허용 오차와 바로 비교할 수 있어서다. "5 mm"는 근거를 댈 수 있지만 "2%"는 매번 계산해봐야 한다.

  • 노이즈가 ±2 mm인 탱크 레벨 → 화면 표시용으로 5 mm 절대 deadband.
  • 펌프 트립 분석용 압력 → 훨씬 촘촘하게, 시운전 기간엔 아예 deadband 없이.
  • 적산값은 천천히 오르므로 변경량 기준만 두면 몇 시간씩 누락된다 — 이건 주기적 보고를 강제한다.
  • 밸브 위치 피드백에 deadband가 너무 넓으면 드리프트하는 액추에이터도 멈춰 있는 것처럼 보인다. 필터 없느니만 못하다.

그리고 OPC UA가 압축까지 하는 히스토리언으로 들어간다면, deadband가 직렬로 두 개가 된다. 두 층이 겹치면 각각이 걸러낼 것처럼 보이는 양보다 더 많은 정보가 빠진다. 의도적으로 맞춰라.

KeepAlive와 Lifetime: 끊긴 링크를 얼마나 빨리 알아차리나

이 둘이 운전자가 멈춘 값을 살아 있다고 착각하느냐를 결정한다. 아무 변화가 없어도 서버는 MaxKeepAliveCount 퍼블리싱 주기마다 빈 keep-alive를 보낸다. 클라이언트가 확인 응답을 멈추면 서버는 LifetimeCount 주기 뒤에 구독을 없앤다.

Part 4는 LifetimeCount가 최소 MaxKeepAliveCount의 3배 이상이어야 하고 클라이언트가 이를 지켜야 한다고 정한다. 구체적으로 PublishingInterval 1초, MaxKeepAliveCount 10이면 keep-alive는 10초마다 오고, 침묵이 LifetimeCount × 1초 지나면 구독이 죽는다. 이 값들을 너무 길게 두면 끊긴 링크가 1분 넘게 정상처럼 보이고, lifetime을 keep-alive 대비 너무 짧게 두면 서버가 값을 거절한다. 운전상 중요한 숫자는 keep-alive 주기다 — 멈춘 값이 현재값 행세를 할 수 있는 시간의 상한이다.

값 말고 타임스탬프와 품질을 본다

화면 숫자가 바뀐다고 운전에 쓸 데이터라는 뜻은 아니다. 모든 값에는 source timestamp, server timestamp, quality가 붙어 온다. 시운전 때 셋 다 확인한다.

  • PLC 값이 바뀔 때 source timestamp가 같이 움직이는가, 아니면 서버가 도착 시각만 찍는가?
  • HMI가 Bad와 Uncertain 품질을 분명히 보여주는가, 아니면 마지막 정상값을 현재값처럼 그리는가?
  • 히스토리언은 어느 타임스탬프를 저장하는가 — source, server, 아니면 수집기 시계?

시퀀스 분석에 1초 단위 타임스탬프는 너무 거칠다. 느린 유틸리티 적산에는 충분하다. 기대 해상도를 태그 표준에 적어 둬라. 고장 분석 중에 알게 되지 말고.

어느 프로젝트에나 나오는 실수들

  • 속도가 다른 수천 태그를 구독 하나에 몰아넣기.
  • PLC나 게이트웨이가 갱신할 수 있는 속도보다 빠르게 샘플링 — 위의 수정값 확인 참고.
  • 나중에 이벤트 재구성에 쓸 값을 QueueSize 1로 받기.
  • 다른 현장의 deadband를 공학 단위 확인 없이 복사.
  • 재접속은 되는데 namespace나 NodeId 변경 뒤 monitored item이 Bad로 남음.
  • KeepAlive와 Lifetime이 너무 길어 멈춘 값이 정상처럼 보임.
  • HMI와 히스토리언이 정반대 요구인데 클라이언트 설정 하나를 공유.

서버가 진단을 제공하면 켜라. 거절된 monitored item, 수정된 샘플링 주기, publish 요청 적체, 큐 오버플로, 세션 재연결 횟수. 이 숫자들이 운전자보다 먼저 문제를 짚어 준다.

장비 없이 할 수 있는 시운전 테스트

대표 태그 몇 개를 골라, 클라이언트 진단과 HMI·히스토리언 출력을 보면서 흔들어 본다.

  1. item을 만든 뒤, 서버가 샘플링을 요청보다 느리게 수정하지 않았는지 확인한다.
  2. 퍼블리싱 주기보다 빠르게 디지털 값을 토글한다 — 모든 전이가 보이나, 마지막 값만 보이나?
  3. 아날로그를 램프시키고 원본과 히스토리언이 실제로 저장한 값을 비교한다.
  4. Bad 품질을 만들거나 장치를 끊고, HMI가 마지막 정상값을 현재값처럼 계속 보여주지 않는지 확인한다.
  5. 클라이언트를 재시작해 구독 주기, 큐, 필터, 품질 처리가 그대로 복구되는지 본다.

2번이나 4번에서 걸리는 태그가 있으면, 공정을 두고 거짓말하는 구독을 하나 찾은 것이다. 다음 트립 분석 중에 발견하는 것보다 벤치에서 토글하다 찾는 편이 낫다.