본문으로 건너뛰기

이름 붙이기: 태그, 계층구조, 통합 네임스페이스(UNS)

📍 현재 위치: 4장에서는 ISA-88/95 배치(batch) 및 장비 모델을 PostgreSQL에 담았습니다. 2부에서 살아 있는 측정값을 단 하나라도 수집하기 전에, 이 5장은 모든 신호에 이름을 부여합니다 — 히스토리안(historian), 브로커(broker), 지식 그래프(knowledge graph), 그리고 규제 당국이 모두 동의할 수 있는 하나의 이름을요.

넷스케이프(Netscape)의 유명한 엔지니어 필 칼튼(Phil Karlton)은 컴퓨터 과학에서 정말 어려운 것은 단 두 가지뿐이라고 말했다고 전해집니다. 캐시 무효화(cache invalidation)와 이름 짓기(naming things)입니다. 바이오 제조 공장에서 두 번째 문제는 첫 번째 책의 약속 — 모든 숫자를 그 출처까지 추적하기 — 이 애초에 달성 가능한지를 조용히 결정합니다. 바이오리액터(bioreactor) 온도 프로브는 지식을 내보내지 않습니다. 태그(tag)를 내보냅니다. TT-101 = 37.02처럼 짧은 문자열과 실수(float) 하나입니다. 만약 같은 프로브가 Emerson DeltaV 제어 시스템(공장의 DCS, 즉 분산 제어 시스템(distributed control system))에서는 TIC_101.PV, 히스토리안에서는 BR1_TEMP, 그리고 어느 과학자가 관리하는 스프레드시트에서는 Reactor1 Temperature라고 불린다면, 당신에게는 하나의 신호가 있는 것이 아니라 세 개가 있는 것이며, 어떤 기계도 이들이 같은 것임을 알 수 없습니다.

이것을 SCADA(공장 현장의 감시 제어 시스템), MES(배치를 운영하는 제조 실행 시스템(manufacturing execution system)), ERP(비즈니스를 운영하는 전사적 자원 관리(enterprise resource planning) 시스템)에 걸쳐 만 개의 태그로 곱하면, 그 결과는 업계에서 가장 흔하면서도 가장 화려하지 않은 데이터 관리 실패 원인이 됩니다 [1].

이 장은 그 처방이며, 대부분 코드보다는 규율(discipline)에 관한 것입니다. 우리는 하나의 명명 규칙(naming convention)을 설계하고, 이를 ISA-95에 근거하게 하며, 통합 네임스페이스(Unified Namespace, UNS) 토픽 트리(topic tree)와 Sparkplug B 토픽으로 투영하고, 거버넌스(governance)가 적용된 데이터로 저장합니다. 그리고 — 이것이 실습 중심의 책이기에 — 잘못된 태그가 플랫폼에 들어오는 것을 거부하는 린터(linter)를 함께 제공합니다. 코드는 작습니다. 그 대가는 하류(downstream)의 모든 것입니다.

쉽게 말하면

태그 이름은 데이터의 우편 주소입니다. BR101.Temp.PV는 "바이오리액터 101온도 측정값의 현재 값(present value)"이며 — newark/upstream/BR101/Temp는 공장 전체가 둘러볼 수 있는 폴더 경로로 쓴 같은 주소입니다. 주소 형식을 한 번 정하고, 적어 두고, 출입문에 로봇을 세워 두어 형식에 맞지 않는 것은 모두 돌려보내십시오. 그 후에는 이후의 모든 장이 자기 편지를 알맞은 상자에 넣기만 하면 됩니다.

이 장에서 다루는 내용

우리는 왜 하나의 신호가 정확히 하나의 정규(canonical) 이름을 가져야 하는지로 시작한 다음, 4장의 ISA-95 계층구조 위에 규칙을 세웁니다. 각 태그를 UNS 경로와 Sparkplug B 토픽으로 바꾸고, ISA-5.1 계기 태그(instrument tag)와 ISA-95 7부 별칭화(aliasing)가 어떻게 현장(floor)과 클라우드(cloud)가 라벨에 대해 서로 다르게 부르면서도 연결의 끈을 잃지 않게 하는지 살펴보며, 이 사전(dictionary) 전체를 거버넌스가 적용된 데이터로 PostgreSQL에 저장합니다. 마지막으로, 동반 저장소(companion repo)의 실제 린터를 시뮬레이터의 16개 태그에 대해 실행하여 통과하는 것을 지켜보고, 실제 공장에서 벌어지는 종류의 표류(drift)를 거부하는 것을 지켜봅니다.

하나의 신호, 하나의 정규 이름

진행 중인 사례의 골든 데이터(golden data)를 열어 원시 태그를 보십시오. 다음은 동반 저장소의 datasets/fedbatch_timeseries_10min.sample.csv의 일부입니다 — 우리의 14일간 유가식(fed-batch, 런 도중 영양분을 계속 공급하는 방식으로, 연속적으로 배출·보충하는 것이 아님) CHO 세포(항체를 분비하는 중국 햄스터 난소(Chinese-hamster-ovary) 세포주) 런(run)이 생성하는 롱 포맷(long-format) 시계열입니다.

datasets/fedbatch_timeseries_10min.sample.csv (excerpt)
ts,tag,value,unit,quality,batch_id
2026-01-05 00:00:00+00:00,BR101.Agitation.PV,81.4323,rpm,192,BATCH-2026-001
2026-01-05 00:00:00+00:00,BR101.Agitation.SP,81.6008,rpm,192,BATCH-2026-001
2026-01-05 00:00:00+00:00,BR101.DO.PV,40.8224,%sat,192,BATCH-2026-001
2026-01-05 00:00:00+00:00,BR101.DO.SP,40.0,%sat,192,BATCH-2026-001

모든 행이 이미 tagunit을 담고 있습니다(quality 컬럼은 7장에서 정립하는 OPC 스타일 품질 코드를 담습니다 — 192 = 양호(Good), 64 = 불확실(Uncertain), 0 = 불량(Bad) — 그래서 하류 소비자가 결코 불량 측정값을 신뢰할 수 있는 것으로 오인하지 않습니다). 규율의 질문은 이것입니다. 누가 BR101.DO.PV가 영원히 같은 프로브, 같은 단위를 의미하도록 보장하는가 — 그리고 다음 분기에 아무도 BR101.DissolvedO2를 새로 만들지 않도록 보장하는가? 답은 규칙(convention) 더하기 등록부(register) 더하기 관문(gate)입니다. 규칙은 적법한 태그가 어떻게 생겼는지를 말하고, 등록부는 모든 적법한 태그의 단일 목록이며, 관문은 현실이 등록부에서 표류할 때 큰 소리로 실패하는 자동화입니다.

저장소가 사용하는 규칙은 의도적으로 지루하며, 점(dot)으로 구분된 세 개의 세그먼트입니다.

<UNIT>.<Measurement>.<Attr> e.g. BR101.Temp.PV
  • UNIT은 4장에서 시드(seed)한 ISA-95 모델의 장비 한 조각입니다 — BR101(생산 바이오리액터), N1SEED(시드 트레인(seed-train) 용기), PA01(단백질 A(Protein A) 포획 스키드(capture skid) — 프레임에 장착되어 미리 배관이 갖춰진, 자족적인 장비 유닛으로 첫 크로마토그래피 정제 단계를 수행합니다 — 이며, 그 자신의 신호 — mAU 단위의 PA01.UV280.PV, PA01.Phase.State — 는 하류 크로마토그래피 및 맥락화 장에서 다룹니다).
  • Measurement는 측정되는 물리적 대상입니다 — Temp, pH, DO, Agitation, Titer(항체 농도, g/L 단위 — Temp나 DO와 달리 연속 프로브로 읽는 대신 보통 단백질 A HPLC로 오프라인 측정되는 제품 품질 신호).
  • Attr은 이 특정 숫자의 역할입니다. PV(현재값/공정값(present/process value)), SP(설정값(setpoint)), MV(조작값/출력값(manipulated/output value)), Rate, 또는 State.

그 마지막 세그먼트는 보이는 것보다 더 중요합니다. 설정값(우리가 요청한 것)과 공정값(우리가 얻은 것)의 차이는 지시(instruction)와 측정(measurement)의 차이이며 — 이 둘을 뒤섞는 것은 고전적인 데이터 무결성(data-integrity) 오류입니다. BR101.Temp.SPBR101.Temp.PV를 별개의 이름 붙은 신호로 유지하는 것은 ALCOA+ 원칙(ALCOA+는 귀속 가능(Attributable), 판독 가능(Legible), 동시 기록(Contemporaneous), 원본(Original), 정확(Accurate) — 더하여 완전(Complete), 일관(Consistent), 영속(Enduring), 가용(Available) — 한 데이터를 가리키는 규제 당국의 약칭입니다)의 명명 계층(naming-layer)에서의 표현입니다. 값은 그 진정한 출처에 귀속(attributable)될 수 있어야 하며, 그 출처가 사람이든, 여기서처럼 특정 계기에서 자동으로 생성된 측정값이든 마찬가지입니다 [2][3].

이름을 ISA-95에 근거하게 하기

밑에 지도가 없는 규칙은 그저 문자열 형식일 뿐입니다. BR101이 의미를 갖는 이유는 그것이 ISA-95 장비 계층구조 — 기업(enterprise) → 사이트(site) → 영역(area) → 작업 센터/유닛(work-center/unit) — 의 실제 위치로 해석(resolve)되기 때문입니다. 이는 바로 이 목적을 위해 표준이 정의한 기술 중립적(technology-agnostic) 주소 공간입니다 [4]. 우리의 시드된 모델은 생산 바이오리액터를 Newark 사이트 → 상류(upstream) 영역 → BR101에 두고, 단백질 A 스키드를 Newark → 하류(downstream) → PA01에 둡니다. 태그 사전의 일은 그 배치를 모든 신호와 함께 들고 다니는 것이어서, 숫자가 결코 고아(orphan)가 되지 않게 하는 것입니다.

동반 코드에서 그 매핑은 examples/chapters/04-naming-uns/naming.py의 맨 위에, 센서 태그를 지니는 유닛들을 담은 작고 명시적인 테이블로 존재합니다 — 시드된 ISA-95 모델과 일관된 부분집합입니다.

examples/chapters/04-naming-uns/naming.py
TAG_RE = re.compile(r"^[A-Z][A-Z0-9]+\.[A-Za-z][A-Za-z0-9]+\.(PV|SP|MV|Rate|State)$")

# the equipment hierarchy the tags live under (matches the seeded ISA-95 model)
UNIT_AREA = {"BR101": ("newark", "upstream"), "N1SEED": ("newark", "upstream"),
"PA01": ("newark", "downstream"), "PBR201": ("newark", "upstream")}
QUDT = {"degC": "DEG_C", "pH": "PH", "%sat": "PERCENT", "rpm": "REV-PER-MIN",
"kg": "KiloGM", "bar": "BAR", "L": "L", "%": "PERCENT", "g/L": "GM-PER-L"}

UNIT_AREA는 실제로 센서 태그를 지니는 유닛만을 나열하므로, 시드된 전체 모델과 겹치지만 동일하지는 않습니다. 유가식 시드는 TFF01FILL-LINE-01(여기서는 라이브 태그가 없는 하류 유닛)도 정의하는 반면, 테이블 속 PBR201은 강화된(intensified)/연속 관류(perfusion) 변형 바이오리액터로 — 연속 사례를 위한 비계(scaffolding)이지 유가식 시드의 일부가 아닙니다. 잠시 멈춰 볼 만한 두 가지 설계 결정이 있습니다. 첫째, TAG_RE기계가 읽을 수 있는 계약(contract)입니다. 태그는 대문자로 시작하는 유닛 코드, 측정 항목, 그리고 닫힌 집합(closed set)에 속하는 속성 역할 중 하나일 때에만 적법합니다. 그 닫힌 집합이 바로 동의어(synonym)의 느린 부패를 멈추는 것입니다. 둘째, QUDT 테이블은 각 공장 측정 단위(unit-of-measure) 문자열을 QUDT 단위 IRI에 매핑합니다 — QUDT(Quantities, Units, Dimensions and Types)는 단위의 공개 어휘이고, IRI(국제화 자원 식별자(Internationalized Resource Identifier))는 URL과 유사한 전역 식별자입니다(예를 들어 degChttp://qudt.org/vocab/unit/DEG_C). 단위는 값의 정체성의 일부입니다 — "37"은 그것이 섭씨라는 것을 알기 전까지는 무의미합니다 — 그리고 지금 그것을 전역 어휘(global vocabulary)에 못 박아 두는 것이 나중에 19장의 지식 그래프 — 배치, 장비, 자재, 결과를 잇는 RDF 그래프 — 가 시스템들을 가로질러 추론할 수 있게 합니다. 위 데이터에서 %sat과 단순 %의 구분은 바로 이 테이블이 종이 위에서 한 번 해결하도록 강제하는 종류의 단위 모호성이며, 일탈(deviation) 조사 도중에 다투는 일을 막아 줍니다.

태그에서 토픽으로: 통합 네임스페이스

태그 문자열의 해부: 자산, 측정, 역할

태그를 어디로든 투영하기 전에, 태그 문자열 그 자체를 해부해 둘 가치가 있습니다. 왜냐하면 이후의 모든 형태가 그 세 세그먼트로부터 만들어지기 때문입니다. 정규 태그 BR101.Temp.PV는 불투명한 라벨이 아닙니다. 그것은 작은 문법 — <asset>.<measurement>.<role> — 이며, 점으로 구분된 각 세그먼트가 하중을 견딥니다.

정규 태그 문자열 BR101.Temp.PV를 세 개의 색상 세그먼트 알약 — 자산, 측정, 역할 — 으로 분해하고, 그 아래에 두 가지 병렬 형태를 보여 줍니다: 슬래시 구분 UNS 경로 newark/upstream/BR101/Temp와 다섯 레벨 Sparkplug B 토픽으로, 둘 다 같은 세그먼트에서 다시 투영됩니다.

자산(asset) 세그먼트(BR101)는 ISA-95 유닛입니다. 측정에 대해서는 아무것도 모르고 장비에 대해서는 모든 것을 아는 유일한 세그먼트이며, 그래서 그것만이 UNS 경로의 sitearea 레벨을 결정합니다. 측정(measurement) 세그먼트(Temp)는 물리량이며, 두 전송 형태 모두에 그대로 옮겨집니다. 역할(role) 세그먼트(PV)는 가장 자주 잘못 다뤄지는 것입니다. 이는 공정값설정값(SP), 출력(MV), Rate, 또는 State와 구별하며, TAG_RE 패턴이 못 박는 닫힌 집합입니다. 문법을 왼쪽에서 오른쪽으로 읽으면 태그는 어떤 장비, 어떤 양, 어떤 종류의 숫자인지를 — 별도 조회 없이 — 알려 줍니다.

이 그림은 또한 이 장의 핵심을 한눈에 보여 줍니다. UNS 경로Sparkplug 토픽은 같은 세그먼트의 재투영(re-projection)이지, 누군가 손으로 입력하는 독립적인 문자열이 아닙니다. assetmeasurement는 UNS 경로에 그대로 나타나고, asset은 Sparkplug 장비 id로 다시 등장합니다. 두 형태 모두 도출되기 때문에, 주소 공간은 등록부에서 표류할 수 없습니다.

이 내용이 삼부작에서 놓인 자리

여기서 해부하는 정규 태그는 세 권 모두를 관통하는 한 줄기 흐름에 대한 코드 차원의 답입니다. 첫 번째 책은 같은 이름 짓기·정체성 문제를 물리적으로 다루는데, 거기서는 전역 네임스페이스가 제품 자체에 새겨집니다 — 포장과 직렬화. 두 번째 책은 그것을 데이터 포인트로 다룹니다 — 한 시스템의 라벨이 다른 시스템의 라벨로 해소되게 만드는 매핑 기록 — 의미론적 상호운용성. 이 장은 바로 그 기록이 강제된 행(row)과 린터(linter)가 되는 곳입니다. gov.tag_dictionary 항목과 naming.py가 바로 그 두 장이 설명하던 구현입니다.

태그에서 UNS 경로와 Sparkplug 토픽으로

태그 사전은 신호를 일관되게(consistent) 만듭니다. 통합 네임스페이스(Unified Namespace)는 신호를 둘러볼 수 있게(browsable) 만듭니다. 제조 IoT 커뮤니티에서 널리 알려진 UNS 개념은 단일 실시간 계층구조 — 비즈니스 그 자체처럼 의미론적으로(semantically) 조직되고 브로커 중립적(broker-agnostic)인 — 이며, 어떤 시스템이든 무언가의 현재 상태를 찾으러 가는 단 하나의 장소가 됩니다 [5]. 우리가 따르는 강력한 권고는, 평행한 분류 체계(taxonomy)를 새로 만드는 대신 그 계층구조를 ISA-95 레벨 — 기업 / 사이트 / 영역 / 라인(Line) / 셀(Cell) — 에 맞춰 형성하라는 것입니다 [6].

구체적으로, UNS는 MQTT 토픽의 트리입니다. MQTT는 OASIS 및 ISO/IEC 20922로 표준화된 경량 발행/구독(publish/subscribe) 전송(transport)입니다 [7]발행/구독은 발신자가 이름 붙은 토픽에 메시지를 게시하면 관심 있는 임의의 수의 수신자가 그것을 구독하는 것을 뜻하며, 양쪽 모두 서로에 대해 알 필요가 없습니다. 토픽은 슬래시로 구분된 경로일 뿐이며, 폴더와 같습니다. 커뮤니티의 모범 사례는 계층적으로 일반에서 구체로(general-to-specific) 가는 레벨, 전부 소문자, 공백 없음입니다 — 그래야 경로가 예측 가능하고 단일 레벨(+) 및 다중 레벨(#) 와일드카드(wildcard)가 유용한 일을 합니다 [8]. 그 경로를 손에 쥐면, 대시보드(dashboard)는 newark/upstream/+/Temp를 구독하여 — 여기서 +는 임의의 단일 레벨, 즉 임의의 유닛을 대신합니다 — 상류 영역의 모든 리액터 온도를 일일이 이름 붙이지 않고도 즉시 받을 수 있습니다.

naming.py는 정규 태그로부터 직접 UNS 경로를 도출하므로, 주소 공간은 사전과 결코 어긋날 수 없습니다.

examples/chapters/04-naming-uns/naming.py
def uns_path(tag: str) -> str:
unit, measurement, _attr = tag.split(".")
site, area = UNIT_AREA[unit]
return f"{site}/{area}/{unit}/{measurement}"


def sparkplug_topic(tag: str, group: str = "newark", edge: str = "edge1") -> str:
unit = tag.split(".")[0]
return f"spBv1.0/{group}/DDATA/{edge}/{unit}"

아래 그림은 하나의 신호가 어떻게 세 가지 뷰(view)로 동시에 투영되는지 보여 줍니다.

바이오리액터 온도 신호가 정렬된 세 가지 뷰로 투영된 모습: 정규 점 표기 태그, ISA-95 기업-사이트-영역-유닛 계층구조에 근거한 슬래시 구분 UNS 토픽 트리, 그리고 고정 차수(fixed-arity)의 Sparkplug B 토픽.

하나의 신호, 세 개의 조율된 주소: 정규 태그는 기록 시스템(system of record)이고, UNS 경로는 사람과 대시보드가 둘러볼 수 있는 트리이며, Sparkplug B 토픽은 전송 포맷(wire format)입니다. 셋 모두 같은 사전에서 생성되므로 서로 어긋날 수 없습니다.

저자가 AI의 도움을 받아 직접 제작한 그림입니다.

Sparkplug B: 더 엄격한 사촌

평범한 MQTT 토픽은 자유 형식(free-form)인데, 이는 그것의 매력이자 위험입니다. 두 팀이 트리를 서로 다르게 조직하는 것을 아무것도 막지 못합니다. Sparkplug B(Eclipse Sparkplug 3.0 명세)는 MQTT 위에 놓인 의견이 강한(opinionated) 프로파일로, 토픽 구조를 정확히 다섯 레벨 — spBv1.0/<group_id>/<message_type>/<edge_node_id>/<device_id> — 로 고정하고, 발행자(publisher)가 살아 있는지 구독자(subscriber)가 항상 알 수 있도록 출생/사망(birth/death) 세션 상태 관리를 추가합니다 [9]. 이것이 sparkplug_topic()spBv1.0/newark/DDATA/edge1/BR101을 반환하는 이유입니다. newark 그룹, DDATA(장비 데이터(device-data)) 메시지, 엣지 노드(edge node) edge1(장비를 대신해 발행하는 공장 엣지의 게이트웨이)에서, 장비 BR101에 대한 것입니다.

두 가지 토픽 형태는 중복되는 것이 아니라 — 서로 다른 청중을 위해 봉사합니다. UNS 경로는 사람과 대시보드가 둘러볼 수 있는 단일 진실 공급원(single source of truth)이고, Sparkplug 토픽은 엣지 게이트웨이(edge gateway)가 실제로 발행하는, 규율 잡힌 고정 차수 전송 포맷이며, 이는 다음 장에서 실제로 연결합니다. 하나의 사전에서 둘 다 생성하는 것이 바로 공장이 유연한 둘러보기 트리 엄격한 전송 계약을 둘이 결코 서로 모순되지 않게 유지하는 방법입니다 [6].

하나를 위한 두 이름: ISA-5.1과 7부 별칭화

지금까지 우리는 깨끗한 백지를 가정했습니다. 실제 공장은 깨끗한 백지가 아닙니다. P&ID(배관 계장도(piping-and-instrumentation diagram)) 상의 계기 루프(instrument loop)는 히스토리안이 존재하기도 전부터 TT-101이라고 불려 왔습니다 — 이는 ISA-5.1, 즉 계측 기호 및 식별 표준이 정의한 형태의 태그이며, 거기서 첫 글자는 측정 변수를 나타내고 나머지는 루프를 식별합니다(TT = 온도 트랜스미터(temperature transmitter), PIC = 압력 지시 컨트롤러(pressure indicating controller)) [10]. DCS는 이를 TIC_101.PV로 노출합니다. ERP는 이를 비용 센터(cost-center) 자산 번호로만 압니다. 모두에게 BR101.Temp.PV로 이름을 바꾸라고 요구하는 것은 아무도 자금을 대지 않을 다년간의 변경 관리(change-control) 프로젝트입니다.

표준이 축복한 답은 하나의 이름을 강요하는 것이 아니라 그것들이 동등하다고 선언하는 것입니다. ISA-95 7부, 즉 별칭 서비스 모델(Alias Service Model)은 바로 서로 다른 시스템이 같은 객체에 사용하는 서로 다른 식별자(identifier)를 조화시키기 위해 존재하며, 그래서 하나의 물리적 신호가 여러 이름을 지니더라도 플랫폼이 자기 일관성(self-consistent)을 유지하게 합니다 [11]. 우리 세계에서 이것은 정규 BR101.Temp.PV가 기록 시스템이고, TT-101, TIC_101.PV, 그리고 ERP 자산 번호가 모두 그것으로 해석되는 별칭(alias)이라는 것을 뜻합니다. 태그 사전은 그 해석의 자연스러운 보금자리이지만, 그 tag 기본 키에는 폐기/유효(retire/effective) 컬럼이 없으므로, 저장소는 이 매핑을 동반하는 유효 일자 부여 gov.tag_alias 테이블 — 각 행은 새 이름이 넘겨받기 시작하는 날짜(effective)를 기록하므로, 질의가 어느 시점 기준으로든 두 이름 중 어느 것이든 해석할 수 있습니다(old_tag → new_tag, effective, reason; 27장) — 과 둘 중 어느 이름이든 해석하는 gov.v_tag_current 뷰로 실현합니다. 그것을 (접착 코드(glue code)에 파묻는 대신) 데이터로 저장하는 것이 매핑을 감사 가능(auditable)하게 만드는 것입니다.

사전을 거버넌스가 적용된 데이터로 저장하기

파이썬 파일 안에만 존재하는 규칙은 제안입니다. 데이터베이스 스스로가 강제하는 기본 키(primary key)와 테스트 스위트(test suite)가 실행하는 린터를 갖춘, 거버넌스가 적용된 데이터베이스 테이블에 존재하는 규칙은 정책(policy)입니다. 동반 저장소의 examples/platform/db/40-gov.sql은 전체 스택이 가동되면 사전이 안착할 스키마(schema)를 생성합니다 — 각 컬럼을 그 하류 소비자(consumer)에 묶는 주석들에 주목하십시오.

examples/platform/db/40-gov.sql
-- The tag dictionary (Ch 5): every signal's canonical name, asset, unit, the
-- QUDT unit IRI, its UNS path and Sparkplug topic, and a deadband. The naming
-- linter (chapters/04-naming-uns/naming.py, exercised by tests/test_chapters.py
-- under `make test`) rejects any tag not matching the convention.
CREATE TABLE gov.tag_dictionary (
tag text PRIMARY KEY, -- BR101.Temp.PV
asset text NOT NULL, -- BR101
measurement text NOT NULL, -- Temp
unit text NOT NULL, -- degC
qudt_unit text, -- http://qudt.org/vocab/unit/DEG_C
uns_path text NOT NULL, -- newark/upstream/BR101/Temp
sparkplug_topic text NOT NULL, -- spBv1.0/newark/DDATA/edge1/BR101
data_type text NOT NULL DEFAULT 'Double',
deadband numeric NOT NULL DEFAULT 0
);

tag 컬럼은 기본 키입니다. 이제 데이터베이스 스스로가 "하나의 정규 이름, 한 번만 사용"을 강제합니다. uns_pathsparkplug_topic은 읽기 시점에 다시 계산되는 것이 아니라 저장되므로, 7장의 발행자와 17장의 맥락화(contextualization) 뷰가 린터가 승인한 동일한 토픽을 읽습니다. deadband 컬럼은 조용하지만 중요한 세부 사항입니다 — 이는 그 아래로는 측정값을 발행할 가치가 없는 변화 임계값(change threshold)이며, 이를 태그 옆에 저장하는 것이 수집 정책(capture policy)을 컬렉터(collector) 설정 곳곳에 흩어 놓는 대신 거버넌스 아래에 유지합니다. 같은 gov 스키마는 관할 정책(jurisdiction policy)(26장)과 공급자 등록부(supplier register)(25장)도 담고 있어서, 데이터 에 관한 모든 데이터가 한곳에 자리합니다.

naming.pybuild_dictionary()는 라이브 태그 집합에서 이 테이블이 기대하는 바로 그 행 — 자산, 측정, 단위, QUDT IRI, UNS 경로, Sparkplug 토픽 — 을 생성하며, 이것이 바로 가동 중인 스택, 시드 데이터, 그리고 책이 결코 조용히 서로 어긋날 수 없게 하는 방법입니다.

같은 행, 트리플과 형상으로

거버넌스된 행은 SQL 사실일 뿐 아니라 의미론적 사실로 가는 한 걸음 앞에 있습니다. 19장 지식그래프는 바로 이런 종류의 기록을 RDF 트리플(각 사실이 주어-술어-목적어 진술)로 적재하며, 위 사전 항목은 그것들 몇 개로 매핑됩니다 — 태그는 IRI가 되고, qudt_unit 컬럼은 QUDT 어휘로 들어가는 실제 엣지가 되며, ISA-95 배치(placement)는 쿼리가 걸을 수 있는 관계가 됩니다.

# the BR101.Temp.PV dictionary row, the same fact as RDF triples
bp:BR101.Temp.PV a bp:Tag ;
bp:onAsset bp:BR101 ;
bp:measures bp:Temperature ;
qudt:unit unit:DEG_C ;
bp:unsPath "newark/upstream/BR101/Temp" .
bp:BR101 a bp:Unit ; bp:inArea bp:Newark-Upstream .

관계형 기본 키가 암시만 하는 두 가지를, 그래프는 점검 가능하게 만들 수 있습니다. 첫째, 정규식이 강제하는 닫힌 속성 집합은, 온톨로지 용어로는 PV, SP, MV, Rate, State를 고정 열거(enumeration)로 모델링하는 클래스와 분류 체계의 결정이며 — 이것을 SHACL 형상(문자열을 린터가 검증하듯 그래프를 검증하는 형상 제약 언어)이 선언적으로 진술하므로, 같은 계약이 전선과 그래프를 둘 다 지킵니다.

# shapes.ttl — the graph-side mirror of TAG_RE's closed attribute set
bp:TagShape a sh:NodeShape ;
sh:targetClass bp:Tag ;
sh:property [ sh:path bp:onAsset ; sh:minCount 1 ; sh:class bp:Unit ] ;
sh:property [ sh:path bp:role ;
sh:in ( "PV" "SP" "MV" "Rate" "State" ) ] .

린터가 파이썬에서 실행하는 멤버십 검사 — BR101이 계층구조에 있는 실제 유닛인가? — 는 출하 관문과 SHACL 장이 SHACL이 존재하는 이유라고 주장하는 폐쇄 세계(closed-world) 질문입니다. 누락되었거나 목록 밖인 값은 열린 질문이 아니라 지금 실패입니다. 둘째, 단 하나의 SPARQL 역량 질문(competency question) — 상류 영역의 모든 온도 신호를 달라 — 은 newark/upstream/+/Temp MQTT 와일드카드와 같은 의도에 답하되, 라이브 브로커가 아니라 그래프 위에서 답합니다.

SELECT ?tag WHERE { ?tag bp:onAsset ?u ; bp:measures bp:Temperature .
?u bp:inArea bp:Newark-Upstream . }

와일드카드와 쿼리가 같은 집합을 반환한다는 것이 두 형태를 하나의 사전에 접지하는 것의 보상 전부입니다. 나중에 로트를 그 세포은행까지 거슬러 걷는 관계와 계보의 척추와, 대시보드가 둘러보는 토픽 트리는, 이 장이 못 박는 정체성의 두 투영입니다. 앞 절의 별칭 매핑은 같은 발상의 세 번째 형태입니다 — effective로 일자가 붙은 gov.tag_alias 행은 정확히 PROV-O 식 출처(provenance) 엣지(어떤 일자 기준 old_tag wasRevisionOf new_tag)이므로, "이 배치가 운전된 날 어떤 이름이 정규였는가?"는 고고학이 아니라 데이터로 답할 수 있습니다.

태그 사전 항목의 해부: 거버넌스가 적용된 하나의 행

gov.tag_dictionary의 채워진 행 하나 — 실제 골든 데이터셋에서 생성된 BR101.Temp.PV의 항목(그 태그는 BATCH-2026-001에서 품질 192(양호(Good))로 degC 측정값 37.0145를 담습니다) — 를 보십시오. 모든 컬럼이 별개의, 거버넌스가 적용된 일을 하며, 이들이 함께 모여서 온도 측정값이 결코 고아 숫자가 되지 않게 하는 이유가 됩니다.

BR101.Temp.PV에 대한 gov.tag_dictionary의 한 행을 필드별로 해부하는 신원 카드: 태그 기본 키(강조 표시), 자산, 측정, 단위, QUDT 단위 IRI, UNS 경로, Sparkplug 토픽, 데이터 타입과 데드밴드, 그리고 이 행이 어디에 존재하는지에 관한 패널.

gov.tag_dictionary의 한 행: tag은 데이터베이스가 강제하는 기본 키이고, qudt_unit은 단위를 전역 어휘에 못 박으며, uns_pathsparkplug_topic은 — 다시 계산되지 않고 — 저장되어, 모든 하류 독자가 린터가 승인한 주소를 받습니다.

저자가 AI의 도움을 받아 직접 제작한 그림입니다.

필드는 세 그룹으로 나뉩니다. 신원(identity) 그룹 — tag, asset, measurement — 은 이것이 어떤 신호이며 어디에 사는가에 답합니다. tag은 정규 이름이자 기본 키이고, asset은 ISA-95 유닛 BR101로 해석되며, measurement는 물리량 Temp입니다. 의미(meaning) 그룹 — unit, qudt_unit, data_type — 은 그 숫자가 무엇을 뜻하는가에 답합니다. 공장 문자열 degC, 그것의 기계가 읽을 수 있는 IRI http://qudt.org/vocab/unit/DEG_C, 그리고 값 타입 Double입니다. 주소 지정 및 정책(addressing-and-policy) 그룹 — uns_path, sparkplug_topic, deadband — 은 그것이 어디로 이동하며 언제 발행할 가치가 있는가에 답합니다. 둘러볼 수 있는 경로 newark/upstream/BR101/Temp, 전송 토픽 spBv1.0/newark/DDATA/edge1/BR101, 그리고 그 아래로는 측정값이 억제되는 변화 임계값(기본값 0)입니다. 결정적으로, uns_pathsparkplug_topicbuild_dictionary()가 한 번 도출하는 저장된 컬럼이지 읽기 시점에 평가되는 공식이 아닙니다 — 그래서 누군가의 이후 질의에 생긴 오타가 거버넌스가 축복한 토픽과 다른 토픽으로 신호를 조용히 다시 보내는 일이 결코 일어날 수 없습니다.

린터: 출입문의 로봇

여기가 바로 이 실습 중심의 책이 제목값을 하는 곳입니다. 규칙은 무언가가 그것을 모든 변경에 대해, 영원히, 자동으로 강제할 때에만 실재합니다. 그 무언가가 린터이며 — 정말로 아주 작습니다. examples/chapters/04-naming-uns/naming.py에서 가져왔습니다.

examples/chapters/04-naming-uns/naming.py
def lint_tag(tag: str) -> str | None:
"""Return None if the tag is valid, else a reason string."""
if not TAG_RE.match(tag):
return f"'{tag}' does not match <UNIT>.<Measurement>.<Attr>"
unit = tag.split(".")[0]
if unit not in UNIT_AREA:
return f"unit '{unit}' is not in the equipment hierarchy"
return None


def lint_dataset() -> tuple[pd.DataFrame, list[str]]:
df = pd.read_parquet(DATA / "fedbatch_timeseries.parquet")
tags_units = dict(df.groupby("tag")["unit"].first())
problems = [f"{t}: {lint_tag(t)}" for t in tags_units if lint_tag(t)]
return build_dictionary(tags_units), problems

두 가지 검사, 두 가지 실패 모드입니다. 첫째, 태그가 구조적 계약 TAG_RE에 부합하는가? 둘째, 잘 형성되었더라도 그 유닛 코드가 계층구조 속 실제 장비 조각에 대응하는가? 존재하지 않는 용기를 위한 완벽한 형태의 태그는 잘못 형성된 태그만큼이나 잘못된 것입니다 — 그것은 글씨체만 좋은 고아입니다.

린터는 저장소가 fedbatch_timeseries.parquet로 제공하는 전체 데이터셋을 읽습니다 — 위 …10min.sample.csv 발췌와 같은 데이터(동일한 ts,tag,value,unit,quality,batch_id 스키마)이되, 사람이 읽을 수 있는 일부 조각이 아니라 make data가 결정론적으로 재생성하는, 콤팩트한 바이너리 포맷의 완전한 집합입니다. 이것을 실제 골든 데이터셋에 대해 실행하면(python chapters/04-naming-uns/naming.py) 실제로 테스트된 출력을 얻습니다 — 생성된 사전의 처음 여덟 행과 깨끗한 건강 진단서입니다.

tag asset measurement unit qudt_unit uns_path sparkplug_topic
BR101.Agitation.PV BR101 Agitation rpm http://qudt.org/vocab/unit/REV-PER-MIN newark/upstream/BR101/Agitation spBv1.0/newark/DDATA/edge1/BR101
BR101.Agitation.SP BR101 Agitation rpm http://qudt.org/vocab/unit/REV-PER-MIN newark/upstream/BR101/Agitation spBv1.0/newark/DDATA/edge1/BR101
BR101.DO.PV BR101 DO %sat http://qudt.org/vocab/unit/PERCENT newark/upstream/BR101/DO spBv1.0/newark/DDATA/edge1/BR101
BR101.DO.SP BR101 DO %sat http://qudt.org/vocab/unit/PERCENT newark/upstream/BR101/DO spBv1.0/newark/DDATA/edge1/BR101
BR101.FeedA.PV BR101 FeedA kg http://qudt.org/vocab/unit/KiloGM newark/upstream/BR101/FeedA spBv1.0/newark/DDATA/edge1/BR101
BR101.FeedB.PV BR101 FeedB kg http://qudt.org/vocab/unit/KiloGM newark/upstream/BR101/FeedB spBv1.0/newark/DDATA/edge1/BR101
BR101.OffgasCO2.PV BR101 OffgasCO2 % http://qudt.org/vocab/unit/PERCENT newark/upstream/BR101/OffgasCO2 spBv1.0/newark/DDATA/edge1/BR101
BR101.OffgasO2.PV BR101 OffgasO2 % http://qudt.org/vocab/unit/PERCENT newark/upstream/BR101/OffgasO2 spBv1.0/newark/DDATA/edge1/BR101

16 tags; lint problems: 0

유가식 태그 열여섯 개 전부가 통과합니다. 더 중요한 것은, 저장소의 테스트 스위트가 계약의 양쪽 절반을 모두 단언(assert)한다는 점입니다 — 실제 태그가 통과한다는 것 린터가 실제로 쓰레기를 거부한다는 것입니다. examples/tests/test_chapters.py에서 가져왔습니다.

examples/tests/test_chapters.py
def test_ch04_naming_linter_passes_on_real_tags():
import naming

dictionary, problems = naming.lint_dataset()
assert len(dictionary) == 16
assert problems == [] # every simulator tag obeys the convention
assert naming.lint_tag("badtag") is not None # and the linter actually rejects bad ones
assert naming.uns_path("BR101.Temp.PV") == "newark/upstream/BR101/Temp"

관문이 중요한지 보려면, 다음은 실제로 공장에서 나타나는 종류의 표류에 대해 실행한 lint_tag()입니다.

'BR101.Temp.PV' -> None
'TT101' -> 'TT101' does not match <UNIT>.<Measurement>.<Attr>
'br101.temp.pv' -> 'br101.temp.pv' does not match <UNIT>.<Measurement>.<Attr>
'TK205.Temp.PV' -> unit 'TK205' is not in the equipment hierarchy
'BR101.Temp.Value' -> 'BR101.Temp.Value' does not match <UNIT>.<Measurement>.<Attr>

원시 P&ID 태그(TT101), 소문자 오타(br101.temp.pv), 잘 형성되었지만 알 수 없는 용기(TK205), 그리고 동의어 침입(PV 대신 Value) 각각이 구체적인 이유와 함께 잡힙니다. 동반 저장소에서 이 계약은 테스트 스위트에 연결되어 있습니다 — make testtest_ch04_naming_linter_passes_on_real_tags를 실행하며, 어떤 실제 태그라도 규칙을 어기면 실패합니다 — 그래서 명명은 모두가 무시하는 위키 페이지가 아니라 빨갛게 변하는 단언이 됩니다. 같은 검사를 당신만의 배포에서 프리커밋 훅(pre-commit hook)이나 CI 관문으로 승격시키는 것이 다음 단계이며, GxP(우수 규범(Good Practice) — GMP/GLP/GCP 규제 활동을 아우르는 우산) SOP(표준 운영 절차(Standard Operating Procedure) — 통제되고 승인된 문서화된 절차)가 궁극적으로 요구하게 될 단계입니다.

왜 중요한가

명명은 ALCOA+가 성공하거나 조용히 실패하는 첫 번째 장소입니다. 어떤 숫자를 가리키며 어떤 계기가, 어떤 단위로, 어떤 장비에서, 어떤 배치에서 그것을 생산했는지 말할 수 없다면, 그것을 귀속 가능(attributable) 하게도, 출처까지 추적 가능(traceable to its source) 하게도 만들 수 없습니다 — 그리고 영국 MHRA(의약품·의료제품 규제청(Medicines and Healthcare products Regulatory Agency))와 미국 FDA의 데이터 무결성 지침은 모두 그 추적 가능성을 근간으로 취급합니다 [2][3]. 린터가 거부하는 소문자 오타와 PV 자리의 Value 동의어가 바로 잠재된 귀속 가능성(attributability) 실패이며, 잘 형성되었지만 알 수 없는 용기(TK205)는 정규식만의 검사가 놓칠 더 미묘한 함정입니다 — 이것이 바로 린터의 두 번째 검사, 즉 장비 계층구조 소속 여부가 존재하는 이유입니다. 이 책 속 이후의 모든 능력 — 히스토리안의 조인(join), 크로마토그래피(chromatography) 단계 이벤트, 지식 그래프의 계보(lineage) 질의, 감사 추적(audit trail)의 해시 체인(hash chain) — 은 각 데이터 패킷(packet) 위의 주소가 올바르고 고유하다고 가정합니다. 명명을 제대로 하면 그 계층들이 조립(compose)됩니다. 잘못하면 일탈 조사 도중 손으로 동의어를 조화시키는 데 프로그램의 남은 시간을 쓰게 되는데, 이는 동료 심사(peer-reviewed)를 거친 바이오프로세싱 4.0(Bioprocessing 4.0) 문헌이 MES/DCS/SCADA 환경 전반에 풍토병처럼 만연하다고 지목하는 바로 그 실패 모드입니다 [1].

따라서 관문의 가치는 그것이 영리하다는 데 있는 것이 아니라 깜빡이지 않고 이른다는 데 있습니다. 커밋 시점에 거부된 나쁜 태그는 한 줄짜리 수정 비용이지만, 일탈 조사 도중 발견된 같은 태그는 문서화된 데이터 무결성 지적과, 이미 그 고아를 접수한 모든 하류 시스템에 걸친 수동 조화 비용이 됩니다. 검사를 왼쪽으로 옮기는 것 — 때때로 일어나는 사람의 검토에서 항상 일어나는 CI 단언으로 — 이 바로 사람들이 존중하는 규칙과 그들이 조용히 우회하는 규칙 사이의 전부의 차이입니다.

명명은 모든 모델이 기대는 조인 키입니다

이 사전의 가장 멀리 떨어진 하류 소비자는 분석 및 머신러닝 계층이며, 그것이 명명 규율에 의존하는 방식은 물리기 전까지는 놓치기 쉽습니다. 라만 소프트 센서나 규격 이탈(OOS) 예측기는 빽빽한 히스토리안 스트림을 드문드문한 오프라인 분석값에 조인하여 학습되며 — 그 조인은 바로 이 장이 못 박는 정체성, 즉 정규 tagbatch_id로 키가 매겨집니다. 5권은 그 의존성을 명시합니다. 바이오공정 ML에서 가장 해로운 실수는 데이터 누수(data leakage)(떼어 둔 판독값이 학습에 몰래 들어가 보고된 점수가 환상이 되는 것)이며, 그 처방은 배치 그룹화 분할(batch-grouped split) — 배치의 모든 행이 학습으로 온전히 가거나 시험으로 온전히 가는 것으로, scikit-learn의 GroupKFold나 배치 하나 빼기(leave-one-batch-out) 방식을 씁니다 — 이고, 이는 batch_id가 같은 런의 세 가지 철자가 아니라 안정적이고 단일 값인 거버넌스된 식별자일 때에만 가능합니다 [1]. DissolvedO2DO 동의어는 하나의 특징 컬럼을 절반씩만 채워진 둘로 쪼개고, 거버넌스되지 않은 batch_id는 배치가 학습/시험 경계를 보이지 않게 가로지르도록 둡니다. 명명 관문은 ML 데이터 장이 의지하는 누수 없는 분할과 그룹 교차검증의, 화려하지 않은 전제 조건입니다.

또 다른 두 가지 모델 관심사가 곧장 gov.tag_dictionary의 컬럼으로 거슬러 올라갑니다. 첫째는 모델 계보(model lineage)입니다. 규제 대상 모델은 자신이 어떤 신호를, 어떤 단위로 학습했는지를 정확히 기록해야 하며 — 정규 태그와 그 qudt_unit IRI가 바로 그 기록이므로, 여기서 해소된 %sat% 모호성은 나중에 모델의 입력을 조용히 다시 스케일할 수 없는 모호성입니다. 둘째는 드리프트(drift)이며, 여기서 명명은 날카롭게 유지할 가치가 있는 선을 긋습니다. 모델은 자신의 입력에서 데이터 드리프트(data drift) — 입력 분포가 학습 집합에서 멀어지는 것으로, 모집단 안정성 지수(Population Stability Index) 같은 통계가 라벨 없이 잡습니다 — 를 감시하며, 각 태그의 히스토리안 quality 코드와 deadband가 그 신호를 신뢰 가능하게 유지하는 것의 일부입니다. 그러나 공정 드리프트(process drift), 즉 살아 있는 배양액 자체가 배치마다 배회하는 것은, 같은 잘 명명된 신호 위에서 측정되는 다른 것입니다 [1]. MLOps 장이 둘을 분리하며, 두 검출기 모두 자신이 감시하는 입력이 하나의 안정적 이름을 가진다고 가정합니다. 같은 quality 및 적용 도메인(applicability domain) 반사는 추론 시점에도 나타납니다. Uncertain으로 플래그된 판독값을 신뢰하기를 거부하는 모델은, 린터가 커밋 시점에 하는 일을 실시간으로 하는 것입니다 — 보증할 수 없는 입력을 거부하는 것이지요.

실제 현장에서는

솔직한 그림은, 오픈 소스가 여기서 대부분의 길을, 그것도 유난히 깔끔하게 데려다준다는 것입니다. 규칙은 그저 규율이고, UNS는 무료 MQTT 브로커 위의 토픽 트리이며, 사전은 Postgres 테이블이고, 린터는 당신이 온전히 소유하는 파이썬 오십 줄입니다(복사하여 직접 적용할 수 있습니다). 이름을 잘 붙이기 위해 반드시 사야 하는 상용 제품은 없습니다 — 이것이 순수 OSS 답이 진정으로 완전한 몇 안 되는 계층 중 하나가 명명인 이유입니다.

OSS가 건네주지 않는 것은 그것을 둘러싼 거버넌스이며, 이것이 솔직한 부분입니다. 상용 UNS 및 네임스페이스 도구 — HighByte Intelligence Hub, Ignition 플랫폼의 태그 시스템, AVEVA의 자산 프레임워크(asset framework) — 는 그래픽 모델러(graphical modeler), 역할 기반 변경 관리(role-based change control), 번들된 별칭화 서비스를 함께 제공하며, 우리의 gov.tag_dictionary + 린터 접근법은 당신이 그 주위에 변경 관리를 연결해야만 그것을 재현합니다. 우리가 기대는 표준들은 실재하며 현행입니다. ISA-95 1부는 2025년판(ANSI/ISA-95.00.01-2025) 기준으로 현행이고 [4], 7부의 별칭 서비스 모델은 시스템들을 가로질러 이름을 조화시키는 축복받은 방법이며 [11], ISA-5.1은 여전히 P&ID 상의 루프 태그를 다스리고 [10], Sparkplug 3.0과 MQTT는 사실상의(de-facto) UNS 전송입니다 [9][7]. 하지만 표준은 계약이지 집행자가 아닙니다. 집행자는 당신의 CI 관문이며 — GxP 맥락에서는, 어떤 태그도 그것을 통과하지 않고는 생산에 들어가지 않는다고 말하는 검증된(validated) SOP입니다 — 이는 21 CFR Part 11과 EU Annex 11이 전자 기록(electronic records)에 대해 기대하는 종류의 전산화 시스템 통제(computerized-system control)입니다. 신축(greenfield) 부지야말로 첫 프로브가 배선되기 전에 하나의 네임스페이스를 정착시키는 것이 일관된 데이터 플랫폼과 10년간의 조화 작업 사이의 차이를 만드는, 바로 그런 종류의 장소입니다. 우리는 우리의 시뮬레이션 부지를 newark라고 이름 붙입니다 — 첫 프로브가 배선되기 전에 명명이 정착되는 그 신축 부지 현실에 대한 끄덕임입니다.

더 어려운 경우는 기존(brownfield) 부지이며, 거기서 명명은 기술 이전(tech transfer) — 검증된 공정을 개발 실험실이나 보내는(sending) 부지에서 받는(receiving) 제조 부지로 옮기는 것 — 과 만납니다. 15 mL Ambr나 벤치 리액터에서 특성화되어 2,000 L 교반조로 스케일업된 공정은 용기 크기만 바꾸는 것이 아니라, 어떤 계측기가 존재하며 그것이 무엇이라 불리는지를 바꾸며, 받는 부지가 보내는 부지의 태그 체계를 공유하는 일은 거의 없습니다. 바로 이것이 정규 이름 더하기 별칭 설계가 한 공장을 넘어 중요한 이유입니다. UNS 경로의 sitearea 세그먼트는 부지 — boston/development 스키드와 우리의 newark/upstream/BR101 트레인 — 가 같은 측정을 부지별로 구별되는 주소 아래 지니도록 하고, ISA-95 7부 별칭 테이블은 어느 쪽도 개명하지 않고 보내는 부지의 TT-101을 받는 부지의 정규 태그와 조화시킵니다. 그리고 받는 부지에서 제어 또는 데이터 시스템의 적격성 평가IQ/OQ/PQ 순서(설치·운전·성능 적격성 평가(Installation, Operational, Performance Qualification): 시스템이 올바르게 설치되었고, 올바르게 작동하며, 일상 사용에서 올바르게 수행됨을 문서화된 증거로 입증하는 것) — 가 모든 신호가 승인된 이름으로 승인된 단위로 포착됨을 검증해야 하므로, 동결된 태그 사전이 바로 그 프로토콜이 시험하는 준공(as-built) 규격입니다. 트레인의 하류 절반도 같은 부담을 집니다. Protein A 캡처 스키드 PA01은 자신의 신호(PA01.UV280.PV(mAU 단위), PA01.Phase.State)가 이 규칙을 타며, 단일 사용이든 자체 충전이든 하나의 단위공정으로서 그 태그가 바이오리액터의 것만큼 깨끗하게 부지 간에 이전되어야 합니다 — 명명이야말로 캡처 크로마토그래피 단계가 스케일에서 같은 작업임을 입증하게 하는 것입니다.

생산을 위한 솔직한 메모 하나 더입니다. 우리의 린터는 구조와 장비 소속(membership)을 검사하지, 의미론적(semantic) 중복은 검사하지 않습니다. 누군가 두 번째 측정 이름을 계층구조에 추가하면, 린터는 BR101.DO.PV와 가상의 BR101.DissolvedOxygen.PV를 둘 다 기꺼이 받아들일 것입니다. 그것을 잡아내는 것 — 진정한 동의어 탐지(synonym detection) — 은 7부 별칭 테이블이 채워지고 사람이 검토하는 것을 필요로 하며, 이는 거버넌스이지 정규식(regex)이 아닙니다.

핵심 용어

  • 태그(Tag) — 이름 붙은 신호로, 여기서는 <UNIT>.<Measurement>.<Attr>(예: BR101.Temp.PV); 데이터 포인트의 우편 주소.
  • 정규 이름(Canonical name) — 한 신호에 대한 하나의 공식 태그; 사전의 기본 키이자 기록 시스템.
  • 통합 네임스페이스(Unified Namespace, UNS) — 현재 공장 상태에 대한 단일 진실 공급원인 실시간, 브로커 중립적 계층구조로, 비즈니스처럼 조직되며 (여기서는) ISA-95 레벨에 맞춰 형성됨 [5].
  • MQTT 토픽(MQTT topic) — 슬래시로 구분된 발행/구독 경로; +(단일 레벨)와 #(다중 레벨) 와일드카드를 지원 [7][8].
  • Sparkplug B — 토픽을 spBv1.0/<group>/<msg_type>/<edge>/<device>로 고정하고 출생/사망 상태를 추가하는 의견이 강한 MQTT 프로파일 [9].
  • ISA-5.1 계기 태그(ISA-5.1 instrument tag) — 첫 글자가 측정 변수인 P&ID 루프 라벨(예: TT-101, PIC) [10].
  • ISA-95 7부 별칭화(ISA-95 Part 7 aliasing) — 서로 다른 시스템 식별자를 하나의 정규 객체와 동등하다고 선언하는 별칭 서비스 모델 [11].
  • QUDT 단위 IRI(QUDT unit IRI) — 값의 단위를 기계가 읽을 수 있게 못 박는 측정 단위의 전역 식별자(예: .../unit/DEG_C).
  • 태그 사전(Tag dictionary) — 모든 적법한 태그와 그 계층구조 배치, 단위, UNS 경로, Sparkplug 토픽의 거버넌스 등록부(gov.tag_dictionary).
  • 데드밴드(Deadband) — 그 아래로는 새 측정값을 발행할 가치가 없는 변화 임계값을 담는 gov.tag_dictionary 컬럼; 이를 태그 옆에 저장하는 것이 수집 정책을 컬렉터 설정 곳곳에 흩어 놓는 대신 거버넌스 아래에 유지합니다.
  • 저장된(도출된) 컬럼(Stored vs. derived column)uns_pathsparkplug_topicbuild_dictionary()가 한 번 계산해 컬럼으로 영속화(persist)한 것이지 읽기 시점에 다시 계산되는 것이 아니므로, 이후의 질의가 거버넌스가 승인하지 않은 토픽으로 신호를 다시 보낼 수 없습니다.
  • 린터(naming.py) — 규칙에 부합하지 않거나 장비 계층구조에 존재하지 않는 태그를 거부하는 검사; 저장소에서는 make test 아래 test_ch04_naming_linter_passes_on_real_tags 테스트로 실행되며, 생산에서 프리커밋 훅이나 CI 관문으로 승격시키기에 자연스러운 대상.
  • SHACL 형상(SHACL shape) — 린터가 태그 문자열을 검증하듯 RDF 트리플을 검증하는 선언적 제약(형상 제약 언어); 여기서 bp:TagShapeTAG_RE의 닫힌 속성 집합과 계층구조 멤버십 검사를 지식그래프로 옮겨 비춥니다.
  • 배치 그룹화 분할(batch-grouped split) — 배치의 모든 행을 학습 또는 시험에 온전히 두는 ML 검증 규율(GroupKFold / 배치 하나 빼기)로, batch_id가 하나의 거버넌스된 식별자일 때에만 작동하며, 누수 없는 모델의 전제 조건입니다 [1].
  • 데이터 드리프트 대 공정 드리프트(data drift vs. process drift)데이터 드리프트는 모델의 입력 분포가 학습 집합에서 멀어지는 것(라벨 없이 감시)이고, 공정 드리프트는 살아 있는 배양액 자체가 배치마다 배회하는 것 — 같은 잘 명명된 신호 위에서 측정되는 다른 두 가지입니다(MLOps 참조).
  • 기술 이전 / IQ-OQ-PQ(Tech transfer / IQ-OQ-PQ) — 검증된 공정을 받는 부지로 옮기고 그 시스템의 적격성을 평가하는 것(설치·운전·성능 적격성 평가); 동결된 태그 사전이 그 프로토콜이 시험하는 준공 명명 규격이며, 7부 별칭 테이블이 부지 간 라벨을 조화시킵니다.

다음 이야기

이제 모든 신호는 플랫폼 전체가 동의하는 이름, ISA-95 트리 속의 자리, 둘러볼 수 있는 UNS 경로, 타고 갈 Sparkplug 토픽, 그리고 그 모든 것을 정직하게 유지하는 출입문의 로봇을 갖습니다. 우리는 주소록을 만들었습니다. 다음 장 OT 언어로 말하기: OPC UA, MQTT, 그리고 Sparkplug B는 우리가 방금 설계한 전송 포맷을 이어받아 실제 연결 백본(backbone)을 세웁니다 — 자기 기술적(self-describing) OPC UA 서버, Mosquitto 브로커, 그리고 진짜 인증서 보안(certificate security)을 갖춘 Sparkplug B 발행자 — 그래서 주소가 붙은 그 신호들이 시뮬레이션된 바이오리액터에서 스택으로 살아서 흐르기 시작합니다.