← 전체 글
히스토리언/약 11분 읽기/ 조회

원본을 덮어쓰지 않고 잘못된 히스토리언 데이터를 고치는 방법

계기 고장으로 태그가 11시간 동안 full scale에 붙어 있었다. 품질 검토를 견디는 audit trail을 남기면서 이 구간을 보정하는 방법과, raw sample을 반드시 남겨야 하는 이유.

히스토리언SCADAMES프로젝트 노트문제 해결

Burnout-upscale 방식의 입력 카드에 물린 K형 thermocouple이 단선됐다. 카드는 설계대로 채널을 full scale로 올렸고, 정비가 반응기까지 오는 11시간 동안 historian은 850 °C를 성실하게 기록했다. Historian은 아무 잘못이 없다. 선에 실려 온 값이 그것이었다.

그다음 품질팀이 batch report를 뽑고, 왜 반응기가 반 교대 동안 release limit 위에 있었는지 묻는다.

그 구간을 고칠지 말지로 싸우는 사람은 없다. 매번 싸움이 되는 지점은, 고치고 난 뒤 원래 값이 어디로 갔느냐다.

보정은 절대 제자리에서 하지 않는다

이 규칙 하나는 양보하지 않는다. 보정값은 새 layer로 들어간다. 새 version이든, substitute flag든, 조회 시점에 join되는 correction record든 상관없다. Raw sample은 있던 자리에 그대로 남는다.

21 CFR Part 11.10(e)의 표현이 정확하다. Audit trail은 시스템이 자동으로 생성하고 timestamp를 찍어야 하며, "record changes shall not obscure previously recorded information" — 변경이 이전 기록을 가려서는 안 된다. 제약 규제를 겨냥한 문장이지만 설계 원칙으로는 시멘트 공장에도 그대로 맞는다. ALCOA+의 Original, 그리고 데이터 변경은 변경한 사람과 사유까지 추적 가능해야 한다는 EU GMP Annex 11의 요구도 같은 이야기다.

Raw layer를 남겨 두고 후회한 적은 없다. 반대쪽으로 후회한 적은 있다. 어떤 현장에서 엔지니어가 고장 난 transmitter 구간을 archive에서 직접 "정리"했고, 18개월 뒤 문제가 된 lot 구간에 실제로 무엇이 기록돼 있었는지 고객에게 증명할 방법이 없었다. 그 보정 자체는 거의 확실히 맞았다. 그리고 방어는 불가능했다.

Correction table을 만들기 전에 historian이 이미 제공하는 기능부터 확인한다

매뉴얼을 안 읽어서 SQL로 correction table을 직접 만드는 현장이 절반은 된다.

데이터가 OPC UA로 들어온다면 Part 11 (Historical Access)이 이미 이 문제를 다룬다. HistoryUpdate service의 UpdateDataDetailsInsert, Replace, Update 동작을 받고, 그렇게 밀려난 이전 값은 ReadModifiedDetails로 다시 읽을 수 있다. 여기에 ModificationInfo가 붙는다. Update type, user name, modification time이다. "이 값이 원래 무엇이었나"라는 질문에 공유 폴더의 엑셀이 아니라 server가 답한다.

주요 historian 제품에는 대개 자기 버전이 있다. PI archive event는 Substituted, Questionable flag와 annotation을 값마다 들고 다니므로, flag를 읽는 client에서는 보정된 값이 보정된 값으로 보인다. 문제는 그 flag를 안 읽는 client다. 설계 전에 쓰는 제품부터 확인한다.

정말 기능이 없을 때만 custom correction table을 만든다. 만들었다면 무엇을 떠안았는지는 알고 있어야 한다. 이제 raw tag를 읽는 모든 report가 correction table을 join해야 한다. 누군가 새 report를 만들면서 join을 빠뜨리는 날, 이 절차는 조용히 죽는다. 알려 주는 장치는 없다.

고치기 전에 종류를 나눈다

보정마다 위험이 다른데 전부 "manual edit"으로 묶으면 audit log를 읽을 수 없게 된다. 변경 record에 category를 같이 저장한다.

보정 종류실제로 터지는 문제
계기 고장Transmitter가 몇 시간 동안 21.5 mA, historian은 full scale로 기록품질 release가 막히거나, 더 나쁘게는 대체값으로 승인된다
누락 구간Historian connector 정지로 archive에 gap 발생추정값이 실측처럼 보고된다
잘못된 contextRun 종료 후 batch ID 정정값이 다른 lot로 옮겨가는데 MES는 모른다
Counter 보정PLC 재기동으로 totalizer가 0으로 reset교대 생산량이 두 번 세지거나 한 run이 통째로 사라진다
늦은 수동 입력Offline lab 결과를 다음 날 아침에 입력Sample time과 entry time이 뒤섞인다

"2분기 communication outage backfill 전부 보여 줘"는 query다. "manual edit 전부 보여 줘"는 아무도 안 읽는 4,000행짜리 목록이다.

실측값과 추정값은 같은 값이 아니다

여기서는 까다롭게 군다. Transmitter가 NAMUR NE 43 fault current 영역에 있었다면 — 21.0 mA 이상이거나 3.6 mA 이하 — 그 구간에는 측정 자체가 없었다. 고칠 값이 있는 게 아니라 설명할 구멍이 있는 것이다.

값과 함께 출처를 저장한다. 검증된 계기에서 온 measured replacement, sample time과 analysis time이 모두 있는 lab result, 정상 sample 사이의 interpolation, mass balance나 totalizer delta로 역산한 값, operator의 종이 log. 고객이나 규제기관으로 나가는 report는 앞의 둘과 뒤의 셋을 구분할 수 있어야 한다.

고르라면 나는 눈에 보이는 gap이 있는 report를 낸다. Interpolation routine이 그려 준 매끈한 곡선보다 낫다. Gap은 대화를 시작시키고, 그럴듯한 선은 대화를 잘못된 방향으로 끝낸다.

보정이 깨지는 곳은 경계다

구간 한가운데는 거의 항상 멀쩡하다. 사고는 양 끝에서 난다.

보정 interval은 항상 UTC로 저장한다. DST를 쓰는 현장이라면 가을에 01:30이 두 번 지나가고, local time으로 입력한 보정은 한 시간을 빠뜨리거나 두 번 고친다. 정확히 3,600초 어긋난 report를 두고 오후를 통째로 쓴 팀을 본 적이 있다.

그리고 문서로 정해 둔다. Interval이 양 끝에서 inclusive인지, record에 실리는 timestamp가 event time인지 entry time인지 approval time인지 historian server time인지 — 이 넷은 서로 다른 값이다 — 보정 구간을 archive가 어떻게 보간하는지. Step tag와 interpolated tag는 같은 보정에서 다른 report 합계를 만든다.

Counter는 따로 다뤄야 한다. PLC 재기동으로 totalizer가 0으로 돌아가면 그 구간 delta는 음수가 되고, 대부분의 report 로직은 음수 delta를 0으로 clamp하면서 생산량을 조용히 먹는다. 눈에 보이는 bad sample을 고쳐도 이건 안 고쳐진다. Rollover 구간의 delta를 복구하고, 그 아래로 물린 calculated tag를 전부 다시 계산해야 한다.

누가 서명하는가

Requester가 구간을 찾아 보정안을 낸다. Reviewer가 근거를 확인한다. Work order, calibration 성적서, network incident ticket, alarm log, lab sample ID다. Approver가 report에 써도 된다고 승인한다.

3인 현장이면 한 사람이 셋 다 한다. 그건 괜찮고, 아닌 척할 필요도 없다. 안 되는 건 공용 engineer 계정이다. 그 순간 audit trail에 남는 건 사람이 아니라 역할이고, 누구도 그걸로 방어할 수 없다.

Reason code는 자유 입력이 아니라 고정 enum이어야 한다. Comment field도 같이 두되, 2년 뒤 그 comment를 쓴 사람이 퇴사한 상태에서 log를 검색 가능하게 만드는 건 enum 쪽이다.

값이 바뀌었다고 일이 끝난 게 아니다

보정은 report가 그 값에 동의할 때 끝난다. 보정 구간과 앞뒤 한 시간을 포함한 trend, shift와 batch report, historian total을 소비하는 MES reconciliation, 보정된 신호를 읽는 calculated tag를 전부 확인한다.

특히 MES 경계를 본다. 보정으로 quality value가 lot A에서 lot B로 옮겨가면 historian 수정은 전파되지 않는다. ISA-95 level 3 기록에는 여전히 예전 연결이 남아 있고, 이제 두 시스템이 같은 물리적 자재를 두고 다른 말을 한다. 누군가 손으로 닫아야 한다.

Release 판정에 걸리는 건이라면 before/after report output을 저장한다. Audit trail은 숫자가 바뀌었다는 사실을 보여 주고, 저장된 report는 그 변경이 무엇을 바꿨는지 보여 준다.

각자 시스템에서 다음으로 확인할 것 하나. 최근 12개월 correction record를 뽑아 reason code로 묶어 본다. 계기 고장이 압도적이면 보정 절차는 잘 돌아가고 있고 정비 계획이 안 돌아가는 것이다. Communication outage가 압도적이면 approval workflow를 더 다듬지 말고 connector를 고치러 가야 한다. 그리고 log가 비어서 나오면, 깨끗한 공장이라는 뜻이 아니라 아무도 audit trail을 켜지 않았다는 뜻이다.