/* * 트윈 템플릿(아키타입) 레지스트리 — 파라미터화된 합성 레퍼런스 마스터 빌더. * 레퍼런스(reference-registry)가 "고정 마스터"라면, 템플릿은 "노브로 파라미터화된 마스터 빌더". * 둘 다 같은 인제스트 경로(ingestMaster)로 흐른다 — 원천이 고정이냐 합성이냐만 다르다(board-authoring §10). * 앱(operato-twin)이 bootstrap 에서 자기 아키타입을 registerTemplate 한다. 표현(map/image/model) 무관. */ import type { DomainSystem } from '@operato/ops-contract' import type { ReferenceMaster } from './reference-master.js' /** 사용자 노출 파라미터(노브) 정의 — 클라이언트가 이 스키마로 폼을 렌더한다. */ export interface KnobDef { key: string /** 'geo' = 위경도 지점(지도에서 클릭 선택). 값은 { lat, lon }. 사용자가 좌표를 몰라도 지도로 지정. */ label: string /** * 'siteEquipment' = **이 현장에 이미 있는 설비들**(`{id, kind}`). 값이 비어 있으면 만들 때 한 번 * 해석해서 채우고, 채운 결과를 노브 값으로 **남긴다**(`origin.params`). 그래야 나중에 다시 지어도 * 같은 트윈이 나온다 — 그때그때 현장을 다시 읽으면 사이에 설비가 늘고 준 만큼 다른 트윈이 된다. * * 종류를 함께 싣는 이유: 무엇을 받아들일지는 **템플릿이 종류로 판단**한다(이름으로 짐작하지 않는다). */ type: 'number' | 'text' | 'select' | 'geo' | 'siteEquipment' default: any min?: number max?: number options?: { value: string; label: string }[] help?: string } /** * 템플릿 상세(설명 페이지용, 선택) — "이 템플릿이 무엇을 모델링하나"를 구조적으로 보여줘 확신을 준다. * 순수 표시용 데이터(빌드 로직과 무관). 클라이언트가 이 스키마로 상세 뷰를 렌더한다. */ export interface TemplateDetail { /** 한 줄 요약(누구를 위한/무엇). */ summary?: string standards?: string[] /** 밸류체인 국면(반입·보관·변환·반출 등). */ phases?: { key: string; label: string; system?: string; note?: string }[] /** 공정 라우트(오퍼레이션 시퀀스). */ route?: { op: string; label?: string; resource?: string; intent?: string; bizStep?: string }[] locationTypes?: { key: string; label?: string; standardClass?: string; capabilities?: string[] }[] resourceTypes?: { key: string; label?: string; standardClass?: string; capabilities?: string[] }[] /** BOM/레시피 요약. */ recipe?: { inputs: { material: string; qty: number; key?: string }[] outputs: { material: string; qty: number; key?: string }[] /** 완성 라우트 이름(공정 라우트 참조). */ route?: string } /** * 표준 정합 매핑 — 개념이 **어느 표준의 무엇에 대응하나.** * * 표준 칸이 EPCIS·ISA-95 둘로 굳어 있었다. 에너지 템플릿은 그 둘이 앵커가 아니라 적을 자리가 없었다 — * 커널의 `StandardClass` 도 같은 이유로 열었다(`iso50001`·`iec61850`). 대응할 이름이 없으면 **비운다**. */ standardsMap?: { concept: string; epcis?: string; isa95?: string; iso50001?: string; iec61850?: string }[] /** "이 템플릿이 맞나?" 판단 도움 항목. */ fits?: string[] } export interface TwinTemplate { id: string label: string /** 커널 카탈로그가 정본 — 종류가 늘면 여기 손댈 것이 없다(`DomainSystem`). */ system: DomainSystem description?: string /** 상세 설명(선택) — 설명 페이지/패널이 렌더. */ detail?: TemplateDetail knobs: KnobDef[] /** 노브 값 → 합성 마스터(+옵션 시나리오). scenario 는 runtime.scenario.load 로 전달되는 불투명 값. */ build(params: Record): { master: ReferenceMaster; scenario?: any } } const templates = new Map() export function registerTemplate(tpl: TwinTemplate): void { templates.set(tpl.id, tpl) } export function listTemplates(): TwinTemplate[] { return [...templates.values()] } export function getTemplate(id: string): TwinTemplate | undefined { return templates.get(id) }