화요일 21시 40분부터 trend가 비어 있다
Operator가 surge tank level의 12개월 trend를 연다. 반장이 level 변동이 원래 이 정도였는지, 펌프 정비 후에 심해진 건지 물었기 때문이다. Trend는 오늘부터 3월 중순까지 깔끔하게 그려지다가 거기서 끝난다. 그 앞은 아무것도 없다. 이 계측기는 2019년부터 계속 돌고 있었는데도 그렇다.
고장 난 것은 없다. 석 달 전에 naming 정리 작업이 끝나면서 TK101_LT_PV가 Area1.Tank101.Level.Percent로 바뀌었을 뿐이다. Rename은 정상적으로 됐다. 현재 값도 정상이다. Archive도 멀쩡하다. 2019년 sample은 전부 디스크에 그대로 있다. 다만 operator가 지금 검색창에 치는 이름으로는 그 데이터에 닿을 수 없다.
문제는 이 한 문단이 전부다. 히스토리언 rename은 화면 정리도 아니고 단순 설정 변경도 아니다. 데이터 이관이고, 이관 대상은 자기 이력을 다시 찾을 수 있는 능력이다.
우리 히스토리언이 tag를 무엇으로 보는지부터 확인한다
다른 것보다 먼저 답해야 할 질문이 하나 있다. Archive가 sample을 저장할 때 tag 이름을 저장하는가, 아니면 이름이 붙어 있을 뿐인 숫자 identity를 저장하는가.
이 답이 나머지 전부를 결정하고, 제품마다 생각보다 크게 다르다.
PI Data Archive는 archive event를 tag 문자열이 아니라 정수 PointID에 붙인다. Point 이름을 바꿔도 이력은 따라온다. Archive는 이름이 뭔지 신경 쓰지 않는다. 대신 이름을 들고 있던 쪽이 깨진다. PI Point data reference를 쓰는 PI AF attribute, PI Vision과 ProcessBook 화면, DataLink 시트, 조회 시점에 이름으로 resolve하는 자체 client가 전부 여기 해당한다. 데이터는 살아남고 참조가 죽는다.
Ignition tag historian은 반대다. Historize된 값은 sqlth_data_* 파티션 테이블에 들어가고, 그 행은 sqlth_te의 id를 참조한다. tagpath는 이 sqlth_te에 있다. Historize 중인 tag의 이름을 바꾸면 Ignition은 기존 sqlth_te 행에 retired timestamp를 찍고 새 path로 행을 하나 더 만든다. 이력의 양쪽 다 존재하고 양쪽 다 조회되지만, cutover를 가로지르는 tag path는 하나도 없다. 이어 붙이려면 sqlth_te를 offline에서 UPDATE 해야 한다. Backup을 떠 놓고, gateway의 history quarantine을 먼저 비운 상태에서 해야 하는 작업이지 평일 오후에 라이브로 할 작업이 아니다.
이름 문자열 자체가 identity인 제품에서는 UI에 "rename"이라고 적혀 있어도 실제로는 삭제 후 생성이다.
Vendor 매뉴얼 설명을 그대로 믿지 말고, 이 글도 믿지 말고 직접 확인하는 게 낫다. 개발 히스토리언에서 10분이면 된다. 값을 몇 개 써 넣고, archive에 들어갈 때까지 기다린 뒤, point 이름을 바꾸고, rename 전에 시작해서 rename 후에 끝나는 구간을 조회해 본다. 거기서 나오는 결과가 계획의 전제가 된다.
세 가지 방식, 그리고 기본값
| 방식 | 동작 | 맞는 경우 | 대가 |
|---|---|---|---|
| Alias 유지 | 새 표시 이름이 기존 point를 가리키고 저장 identity는 그대로 | 표시 이름만 정리하거나, 연동 쪽을 우리가 못 건드릴 때 | 내부 이름을 노출하는 도구에는 옛 이름이 계속 보인다. 어휘를 두 벌 관리하게 된다 |
| In-place rename | Point identity는 그대로 두고 이름만 변경, sample은 붙어 있다 | 히스토리언이 point ID로 관리하고 rename을 audit해 줄 때 | 이름을 key로 쓰던 소비자가 한꺼번에 깨진다 |
| 새 point + 이력 연결 | 새 tag로 수집을 시작하고 기존 tag는 읽기 전용 | 전사 표준 변경, 히스토리언 통합 | Trend와 report가 cutover 시각을 기준으로 구간을 나눠 조회해야 한다 |
히스토리언이 point ID로 관리하면 in-place rename을 기본으로 하고, 그렇지 않으면 새 point 방식을 기본으로 한다. 이름이 key인 제품에서 "in-place rename"은 결국 기존 point를 조용히 지우는 새 point 방식이기 때문이다. 일은 똑같이 하면서 안전장치만 없는 셈이다.
Alias 방식은 1차 정리용으로 생각보다 쓸 만하다. Operator 화면에는 오늘 당장 읽기 좋은 이름이 올라가고, 연동 쪽 작업은 한 번의 작업 시간에 몰아넣는 대신 몇 달에 걸쳐 순서대로 처리할 수 있다. 대가는 분명하다. 이름 두 벌이 돌아다니면 다음 엔지니어는 둘 다 외워야 하고, 마이그레이션을 끝까지 못 하면 namespace는 정리된 게 아니라 더 나빠진다.
의존성 찾기
Point 설정부터 열지 않는다. 옛 이름을 읽는 곳의 목록부터 연다. 그 목록은 언제나 tag 목록보다 길다.
기계적으로 되는 부분은 쉽다. Point list를 export해서 옛 이름 컬럼만 뽑고, report 공유 폴더, SCADA project export, script 디렉터리, MES 설정 repo를 통째로 grep한다.
grep -rIl -F -f old_tag_names.txt /mnt/reports /srv/scada-export /opt/mes/config
마지막으로 이 작업을 했을 때 40개 남짓한 파일이 나왔다. 그중 3분의 1 정도는 2년 동안 아무도 연 적 없는 spreadsheet였는데, 매주 월요일 아침마다 scheduled task가 열심히 refresh하고 있었다.
정작 문제가 되는 것은 grep으로 안 나오는 쪽이다.
- Operator가 저장한 trend group. Client profile이나 개인 workspace에 있어서 export한 project에는 안 들어 있는 경우가 많다. 6년치 trend를 저장해 둔 operator 한 명이 걸어다니는 의존성 목록이고, 이걸 아는 방법은 물어보는 것뿐이다.
- 입력을 이름으로 resolve하는 calculated tag와 event frame template. 조용히 실패한다. 계산이 에러를 내는 게 아니라 그냥 flatline이 된다.
- Alarm에서 trend로 넘어가는 navigation. 대개 이름 치환으로 만들어져 있고, 하필 그게 제일 필요한 상황에서 깨진다.
- Service account로 도는 정기 export. 자체 query 정의나 자체 credential을 캐시하고 있어서, 내 계정으로 테스트할 때는 멀쩡해 보인다.
- 종이로 남은 것들. P&ID, FAT 보고서, loop folder, 판넬에 붙어 있는 작업 절차서. 작업 시간 안에 바꿀 수 없는 것들이고, 옛 이름을 지우는 대신 찾을 수 있게 남겨야 하는 이유가 바로 이것이다.
각 의존성에 대해 셋 중 하나를 적는다. 자동으로 바뀜, 손으로 바꿔야 함, 못 바꿈. 세 번째 항목의 개수가 old-name alias를 얼마나 오래 살려 둬야 하는지를 결정한다.
이름을 바꾸는 건가, 의미를 바꾸는 건가
Rename 작업을 하다 보면 그 point의 다른 문제까지 전부 보인다. 한 번에 다 고치고 싶은 마음이 드는데, 고치는 건 좋지만 변경 기록의 같은 줄에 묶으면 안 된다.
TK101_LT_PV를 Area1.Tank101.Level.Percent로 바꾸는 것은 rename이다. 같은 단계에서 값의 단위가 percent에서 mm로 바뀌면 그것은 데이터 의미 변경이고, cutover를 가로지르는 모든 조회가 한 이름 아래 서로 다른 물리량 두 개를 돌려주게 된다. 저장되는 내용을 바꾸는 항목은 전부 마찬가지다. Exception과 compression deviation, digital state set, bad quality 처리가 여기 들어간다. 특히 rename이 아니라 point를 새로 만들면 compression 설정은 이전 엔지니어가 맞춰 둔 값이 아니라 point class 기본값으로 돌아온다. 새 tag는 시간당 저장 event 수가 달라지고, cutover 전후를 고해상도로 비교하면 sampling 방식이 다른 두 데이터를 비교하는 셈이 된다.
Point list를 어차피 열어 놓은 김에 해 둘 만한 일이 하나 더 있다. 새 이름이 정말 우리가 채택했다고 말하는 표준과 맞는지 확인하는 것이다. 계기 번호가 ISA-5.1을 따르고 설비 계층이 ISA-95 / IEC 62264의 equipment model을 따른다면, 그 사실을 naming convention 문서에 적고 새 이름을 거기에 맞춘다. 리드 엔지니어 머릿속에만 있는 "표준"은 4년 뒤에 정리 프로젝트를 한 번 더 만든다.
Cutover
Tag 몇 개, 연동 범위도 좁은 작업이면 검토된 spreadsheet와 정비 시간이면 충분하다. Site-wide면 rollback을 포함한 정식 변경 작업으로 잡아야 한다.
- Old-to-new mapping을 동결한다. 이후 추가는 없다. 추가는 다음 변경 건이지 이번 건이 아니다.
- 히스토리언 point configuration, 제품에 설정 DB가 있다면 그것까지, 그리고 손댈 report 정의를 전부 backup한다.
- 이름 충돌은 현재 이름뿐 아니라 retired name까지 놓고 확인한다.
- 정한 방식대로 alias, rename, 새 point 생성을 적용한다.
- Collector나 OPC source mapping을 reload 전에 먼저 바꾼다. 옛 이름을 보고 있는 collector는 옛 point를 친절하게 다시 만들어 준다.
- 새 이름으로 현재 값이 good quality로 들어오는지 확인한다.
- 과거 구간을 admin console이 아니라 operator가 실제로 쓰는 도구로 조회한다.
- Cutover 시각을 포함하는 기간으로 주요 production report를 돌린다.
- 첫 운영 shift가 확인해 줄 때까지 rollback을 유지한다.
7번에는 함정이 하나 있다. Interpolated 조회는 거짓말을 한다. 구멍이 있는 구간을 interpolated로 요청하면 히스토리언은 구멍 앞 마지막 sample과 뒤 첫 sample을 그럴듯한 직선으로 이어서 그려 준다. 검증용 screenshot은 완벽해 보인다. Recorded/raw 조회로 확인해야 한다. PI라면 InterpolatedValues가 아니라 RecordedValues, 다른 제품이면 raw retrieval mode를 쓴다. 그래야 구멍이 구멍으로 보인다.
그리고 cutover 시각은 UTC로 기록한다. 히스토리언, report engine, MES가 DST 경계에서 로컬 시각에 대해 같은 의견을 낼 거라고 기대하면 안 된다. "3월 14일 21시 40분"이라고만 적힌 mapping table은 딱 쓸모없어질 만큼 애매하다.
옛 이름은 찾을 수 있게 남긴다
모든 곳에서 옛 이름을 지우는 것이 가장 과감한 선택이고, 대개 틀린 선택이다. 6개월 뒤에 누군가는 판넬 앞에 서서 2019년 loop 도면에 적힌 이름을 읽고 있을 것이다.
이전 이름은 point description, extended attribute, 또는 전용 alias field에 저장한다. Old-name alias는 최소 한 번의 정비 주기 동안 읽기 전용으로 남긴다. 여기서 정비 주기는 turnaround나 연간 shutdown처럼 그 현장의 실제 주기를 말한다. 30일이 딱 떨어지는 숫자라서 30일로 잡는 게 아니다. 변경 요청 번호는 point 설정 comment에 남긴다. Cross reference는 CSV로 export해서 개인 폴더가 아니라 인수인계 패키지에 넣는다.
예외 없는 규칙이 하나 있다. Retired된 히스토리언 tag 이름을 다른 계측값에 절대 재사용하지 않는다. 재사용하면 2019년 tank level 데이터가 2026년 flow 데이터처럼 보인다. 그리고 그게 문제가 되는 날은 사고 조사나 품질 hold 상황이다. 거기서 "그 tag 이름은 재사용된 겁니다"라고 적고 싶은 사람은 없다.
실제로 터지는 것들
실패 양상은 지루할 만큼 일정하고, 그중 히스토리언 엔진 장애는 하나도 없다.
- Report 파일은 고쳤는데 scheduled service account가 옛 이름이 든 query cache를 계속 들고 있다.
- Calculated tag가 옛 input 참조를 그대로 갖고 있다가 원본 point가 retire되는 순간 flatline이 된다. 에러도 alarm도 없이 직선만 그어지고, 일주일쯤 아무도 모른다.
- 새 tag 값은 맞지만 compression 설정이 달라서, 작년 run과 비교했을 때 나온 차이가 전부 sampling 때문에 생긴 것이다.
- Dashboard는 현재 값은 보여 주는데 지난 분기를 조회하면 아무것도 안 나온다.
- Source mapping을 안 바꾼 collector가 옛 point를 다시 만든다.
- Operator가 저장해 둔 trend group을 열고 직선을 보더니, 멀쩡한 transmitter에 작업 요청을 낸다.
전부 못 찾은 의존성이다. 그래서 rename 자체보다 의존성 조사에 일정을 더 준다.
이미 한 번 이런 작업을 거친 namespace를 넘겨받았다면 제일 먼저 확인할 것은 이렇다. Tag를 몇 개 골라 알려진 rename 날짜를 가로지르는 구간을 raw retrieval로 조회해 보고 무엇이 나오는지 본다. 아무도 검증하지 않은 과거의 마이그레이션은 히스토리언 장기 이력이 조용히 못 쓰게 되는 가장 흔한 원인이고, 보통 누군가 처음으로 장기 trend를 뽑아 보려는 해가 되어서야 드러난다.