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

OPC UA HistoryRead로 히스토리언 공백 메우기 — 중복 없이 복구하는 법

OPC UA HistoryRead로 SCADA·히스토리언 누락 구간을 복구하는 법. 구간을 정확히 잡고, returnBounds로 경계 샘플을 처리하고, Bad 품질을 보존하며, continuation point로 서버 부하 없이 페이징한다.

OPC UA히스토리언SCADA문제 해결시운전

운전자가 2주짜리 에너지 트렌드를 열었더니 화요일 밤에 직선 공백이 있다. 스위치 펌웨어를 올리는 동안 게이트웨이의 OPC UA 세션이 끊겼고, 한 시간 뒤 다시 붙었는데, 월말 리포트 수치가 모자란 다음에야 아무도 못 알아챘다는 게 드러난다. 이제 누군가 데이터를 "다시 넣어 달라"고 한다. 히스토리언이 망가지는 대부분의 출발점이 바로 이 요청이다. 아무렇게나 하면 경계 샘플이 두 번 들어가고, Bad 품질이 조용히 사라지고, 장애 구간 전체가 last known value의 직선으로 채워진다.

HistoryRead는 큰 구독이 아니다. live subscription은 HMI를 현재 상태로 유지하는 기능이고, sampling interval, publishing interval, queue size로 튜닝한다. HistoryRead는 표준의 다른 파트 — OPC UA Part 11, Historical Access — 에 정의돼 있고, 확인할 질문 자체가 다르다.

  • 누락된 샘플을 system of record로 실제로 들고 있는 서버는 어디인가.
  • 타임스탬프가 source time(PLC가 샘플링한 시각)인가, server time(게이트웨이가 값을 받은 시각)인가.
  • Bad, Uncertain 샘플이 왕복을 견디는가, 아니면 import 필터가 조용히 버리는가.
  • 30일 요청 하나가 운영 서버를 멈춰 세우지 않도록 범위를 자를 수 있는가.

백필은 큰 read가 아니라 통제된 복구다. 범위를 정하고, 기록하고, 원칙적으로 되돌릴 수 있어야 한다.

구간은 "어제"가 아니라 초 단위로 잡는다

트렌드가 비어 보인다고 "어제 데이터"를 통째로 넣지 않는다. 태그 그룹별로 실제 누락 구간을 먼저 좁힌다. 근거는 대개 어딘가에 이미 로그로 남아 있다.

  • SCADA 드라이버의 disconnect/reconnect 이벤트, OPC UA session 종료나 secure channel 갱신 실패 로그.
  • 히스토리언 자체의 archive, interface buffer 로그.
  • 태그 품질이 Good에서 Bad 또는 Uncertain으로 바뀐 시각. 사람이 기억하는 "2시쯤"보다 장애를 훨씬 정확하게 감싼다.
  • 같은 시간대의 시간 동기화 알람. 지금 읽으려는 타임스탬프를 얼마나 믿을지가 여기서 갈린다.

작업표에는 시작 포함, 종료 제외로 적는다. 예: 2026-06-27T02:14:10Z <= t < 2026-06-27T02:43:00Z. 세 번째 복구 시도에서 양쪽 다 <=로 적어 둔 탓에 샘플 하나가 계속 두 번 들어가기 전까지는 그냥 깐깐해 보일 뿐이다.

경계 샘플은 returnBounds에 맡긴다

백필 오류는 대개 구간 양 끝에서 난다. 트렌드 연속성 때문에 장애 직전 마지막 값과 복구 직후 첫 값이 필요하지만, 이 값들이 누락 구간 안의 샘플처럼 다시 적재되면 안 된다. 이걸 손으로 — 앞쪽 context window, 누락 구간, 뒤쪽 context window를 읽고 뭘 남길지 눈으로 판단 — 처리하다 보면 압박 상황에서 틀린다.

스펙이 이미 해준다. ReadRawModifiedDetails에 필요한 필드가 다 있다.

필드설정값
startTime / endTime좁혀 잡은 구간, 의도상 종료 제외
numValuesPerNode호출당 개수 상한. 5000 정도가 무난하다. 0은 "전부"라서, 30일을 블로킹 요청 하나로 끌어오는 길이다
returnBoundstrue. 구간 바깥 경계값을 서버가 플래그를 붙여 반환하므로, 직접 추측할 필요가 없다
isReadModifiedraw 값은 false. 이전 보정 이력을 감사할 때만 true

returnBounds = true면 경계 값이 bound로 표시돼 돌아온다. 그래서 히스토리언은 중복 샘플을 archive에 넣지 않고도 경계를 넘겨 보간할 수 있다. 깨끗한 복구와, 없던 상태 전이로 지저분해진 audit trail의 차이가 여기서 갈린다.

서버가 실제로 무엇을 저장하는지 확인한다

live 값이 멀쩡하다고 history도 쓸 만한 것은 아니다. 일부 노드만 historize하거나, 짧은 rolling buffer만 들고 있거나, raw sample을 원했는데 처리된 aggregate를 주는 제품도 있다. 절차서를 확정하기 전에 확인한다.

항목확인 내용
Historizing 속성해당 Variable 노드가 실제로 history를 수집한다고 표시되는지
가장 오래된 샘플보관 기간이 장애를 덮는지. 6시간만 들고 있으면 주말 장애엔 소용없다
raw vs. processedPart 13 aggregate가 아니라 ReadRawModifiedDetails를 호출하고 있는지
AccessLevelHistoryRead 비트(0x04)가 켜져 있고, UserAccessLevel이 내 세션에 허용하는지. live read 권한은 history 권한을 증명하지 않는다
StatusCodeBad, Uncertain 샘플이 빠지지 않고 돌아오는지
continuation point큰 결과가 첫 페이지에서 잘리지 않고 끝까지 페이징되는지

보관 기간이 짧으면 그 한계를 절차서에 굵게 써 둔다. 7일 창을 가정한 백필은 첫 정비 정지 때 무너진다 — 하필 그게 백필이 정말 필요한 순간이다.

복구를 서비스 거부로 만들지 않는다

2,000개 태그의 30일 요청 하나는 디스크, 압축 해제, 권한 검사, 네트워크 전송을 동시에 때린다. HistoryRead는 그 서버가 계속 돌려야 하는 live subscription보다 훨씬 무겁다. 배치는 재시도 가능할 만큼 작게 나누고, numValuesPerNode로 매 호출을 제한하고, 요청 크기를 키우는 대신 continuation point를 따라간다. 가능하면 비피크 시간에 돌리고, 그동안 서버 CPU, 디스크 지연, rejected request 카운터를 지켜본다.

쓸 만한 백필 도구는 중지·재개·진행률 표시가 된다. 나쁜 도구는 같은 과대 요청을 계속 재시도하면서 현장 전체 운전자에게 서버를 느리게 만든다 — 그러면 사건이 둘로 늘어난다.

품질과 타임스탬프도 값이다

숫자만 넣었다고 끝난 게 아니다. 운영을 건드리기 전에 정한다.

  • Bad 품질 — Bad 값으로 쓸지, 건너뛸지. 문제 분석과 감사 관점에서는 Bad 구간을 보존하는 편이 장애를 감춘 깔끔한 대시보드보다 정직하다. aggregate subinterval에 데이터가 없으면 서버는 값을 지어내지 말고 Good_NoData 상태로 알려야 한다.
  • Uncertain 품질 — OEE, 배치, 에너지 리포트에 허용할지.
  • 중복 타임스탬프 — 덮어쓸지, 무시할지, 두 번째 이벤트로 남길지. 전체 작업 전에 5개 태그로 시험한다. 히스토리언이 중복을 허용하면 상태 전이 한 번이 두 번 집계될 수 있다.
  • source vs. insertion time — 목적지가 둘 다 저장하는지. 게이트웨이가 PLC 샘플 시각이 아니라 수신 시각으로 historize하는 순간 이 구분이 절실해진다.

아날로그 태그에서 장애 구간의 직선은 빈 구간보다 나쁘다. 서버가 last known value를 Good 품질로 반복해서 준다면 리포트에 들어가기 전에 의심하라. 공백은 사실을 말하지만, 직선은 조용히 거짓말한다.

실제로 물어뜯는 실패들

live는 되는데 history가 비었다. 대개 노드가 historized가 아니거나, 서비스 계정에 CurrentRead는 있고 HistoryRead 비트가 없다. 구간이 틀렸다고 단정하기 전에 노드의 Historizing 속성과 UserAccessLevel을 본다.

값이 서버 타임스탬프로 온다. 일부 게이트웨이는 PLC 샘플 시각이 아니라 수신 시각으로 찍어서, 이벤트가 통신 지연만큼 밀린다. 신뢰하기 전에 몇 개를 PLC 이벤트 로그와 비교한다.

continuation point를 놓쳤다. 페이징된 응답이 성공처럼 보이지만 첫 페이지만 들어갔다. 큰 read 뒤에 수치가 지나치게 깨끗하면 여기부터 본다.

Bad 품질이 사라졌다. Good만 필터링하는 import 스크립트는 트렌드를 예쁘게 만들면서, 기록하려던 장애의 증거를 지운다.

실제 상황 전에 한 번은 예행연습한다

계획에 없던 장애가 즉흥 대응을 강요하기 전에, 절차를 의도적으로 한 번 검증한다. 동작이 다른 태그 10개를 고른다 — 빠른 아날로그, 느린 아날로그, 디지털 상태, 알람 상태, 배치나 lot context 태그. 짧은 통제 구간 동안 히스토리언 interface를 멈추거나 클라이언트를 차단한 뒤 복구하고, 누락 구간이 실제로 보이는지 확인한다. 문서화한 절차로 백필하고, 작업 전후의 트렌드·raw sample 개수·품질·타임스탬프를 비교하고, 어떤 리포트도 경계 샘플을 중복 집계하지 않는지 확인한다.

작업마다 짧은 기록을 남긴다 — NodeId 목록, UTC 기준 누락 구간, 원인, endpoint와 서버 식별 정보, 사용 계정, raw/processed 모드, 읽은 값과 적재한 값, 품질·중복 규칙, 검토자. 형식 문서가 아니라, 다음 엔지니어가 왜 archive가 나중에 바뀌었는지 이해하는 최소한의 근거다. 예행연습을 건너뛰면 첫 실제 백필은 장애 회의 중에 하는 라이브 실험이 된다.