@things-factory/headless-twin 10.0.10 → 10.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/engine/canonical-ingest.d.ts +25 -2
- package/dist-server/engine/canonical-ingest.js +48 -10
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/declared-stimulus.d.ts +43 -0
- package/dist-server/engine/declared-stimulus.js +57 -0
- package/dist-server/engine/declared-stimulus.js.map +1 -0
- package/dist-server/engine/index.d.ts +2 -0
- package/dist-server/engine/index.js +2 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +12 -0
- package/dist-server/engine/kpi-fold.js +21 -1
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +3 -3
- package/dist-server/engine/kpi-query.js +4 -4
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-feed-registry.d.ts +1 -1
- package/dist-server/engine/live-feed-registry.js +1 -1
- package/dist-server/engine/live-feed-registry.js.map +1 -1
- package/dist-server/engine/local-declarations.d.ts +3 -6
- package/dist-server/engine/local-declarations.js +97 -16
- package/dist-server/engine/local-declarations.js.map +1 -1
- package/dist-server/engine/measured-yield.d.ts +42 -0
- package/dist-server/engine/measured-yield.js +75 -0
- package/dist-server/engine/measured-yield.js.map +1 -0
- package/dist-server/engine/operation-basis.d.ts +16 -0
- package/dist-server/engine/operation-basis.js +20 -2
- package/dist-server/engine/operation-basis.js.map +1 -1
- package/dist-server/engine/property-effects.js +17 -0
- package/dist-server/engine/property-effects.js.map +1 -1
- package/dist-server/engine/restart-policy.d.ts +13 -0
- package/dist-server/engine/restart-policy.js +52 -0
- package/dist-server/engine/restart-policy.js.map +1 -0
- package/dist-server/engine/spec-coverage.d.ts +8 -0
- package/dist-server/engine/spec-coverage.js +3 -1
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +48 -15
- package/dist-server/engine/twin-engine.js +179 -39
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/index.js +1 -1
- package/dist-server/index.js.map +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/service/reference/control-routing.d.ts +14 -0
- package/dist-server/service/reference/control-routing.js +64 -0
- package/dist-server/service/reference/control-routing.js.map +1 -0
- package/dist-server/service/reference/index.d.ts +1 -0
- package/dist-server/service/reference/index.js +2 -0
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +67 -0
- package/dist-server/service/reference/reference-adapter.js +18 -0
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-live.js +3 -3
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +3 -3
- package/dist-server/service/reference/reference-resolver.js +35 -18
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +34 -0
- package/dist-server/service/twin-audit/command-audit.js +15 -1
- package/dist-server/service/twin-audit/command-audit.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +3 -0
- package/dist-server/service/twin-audit/twin-audit-event.js +28 -2
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.d.ts +15 -2
- package/dist-server/service/twin-control/twin-control-mutation.js +85 -34
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.js +1 -1
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.d.ts +1 -1
- package/dist-server/service/twin-instance/twin-instance.js +2 -2
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +2 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.js +1 -1
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-location.js +1 -1
- package/dist-server/service/twin-model/twin-location.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +1 -1
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-operation.js +1 -1
- package/dist-server/service/twin-model/twin-operation.js.map +1 -1
- package/package.json +3 -3
- package/server/engine/canonical-ingest.ts +78 -14
- package/server/engine/declared-stimulus.ts +66 -0
- package/server/engine/index.ts +2 -0
- package/server/engine/kpi-fold.ts +29 -1
- package/server/engine/kpi-query.ts +8 -8
- package/server/engine/live-feed-registry.ts +2 -2
- package/server/engine/local-declarations.ts +97 -19
- package/server/engine/measured-yield.ts +89 -0
- package/server/engine/operation-basis.ts +33 -2
- package/server/engine/property-effects.ts +17 -0
- package/server/engine/restart-policy.ts +55 -0
- package/server/engine/spec-coverage.ts +23 -3
- package/server/engine/twin-engine.ts +201 -42
- package/server/index.ts +1 -1
- package/server/service/reference/control-routing.ts +62 -0
- package/server/service/reference/index.ts +1 -0
- package/server/service/reference/reference-adapter.ts +78 -0
- package/server/service/reference/reference-live.ts +3 -3
- package/server/service/reference/reference-resolver.ts +46 -7
- package/server/service/twin-audit/command-audit.ts +34 -1
- package/server/service/twin-audit/twin-audit-event.ts +50 -4
- package/server/service/twin-control/twin-control-mutation.ts +75 -28
- package/server/service/twin-event/twin-event.ts +10 -2
- package/server/service/twin-instance/twin-instance.ts +16 -10
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +2 -1
- package/server/service/twin-model/twin-equipment.ts +8 -1
- package/server/service/twin-model/twin-location.ts +8 -1
- package/server/service/twin-model/twin-model-query.ts +1 -1
- package/server/service/twin-model/twin-operation.ts +8 -1
- package/test/adopt-structure-live.test.ts +7 -7
- package/test/boot-resume.test.ts +3 -3
- package/test/column-type-portability.test.ts +122 -0
- package/test/control-capability.test.ts +103 -0
- package/test/declared-stimulus.test.ts +88 -0
- package/test/ingest-running-guard.test.ts +5 -5
- package/test/instance-cache-lifecycle.test.ts +1 -1
- package/test/kpi-baseline-db.test.ts +1 -1
- package/test/kpi-query-bench.test.ts +1 -1
- package/test/lineage-survives-restart.test.ts +3 -3
- package/test/live-feed-registry.test.ts +6 -6
- package/test/local-declarations.test.ts +108 -1
- package/test/measured-yield.test.ts +90 -0
- package/test/operation-basis.test.ts +28 -1
- package/test/operational-vocabulary.test.ts +108 -0
- package/test/operations-capability-db.test.ts +4 -4
- package/test/project-structure-db.test.ts +1 -1
- package/test/projection-reaches-screen.test.ts +1 -1
- package/test/property-effects.test.ts +28 -0
- package/test/restart-policy.test.ts +111 -0
- package/test/resync-origin-site.test.ts +7 -1
- package/test/source-outcome-audit.test.ts +104 -0
- package/test/structure-revision-db.test.ts +27 -27
- package/test/tenant-registry-db.test.ts +2 -2
- package/test/twin-model-item-db.test.ts +3 -3
- package/test/twin-model-tree-db.test.ts +7 -7
- package/test/twin-origin-resync.test.ts +4 -4
- package/test/yield-loop.test.ts +144 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twin-location.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-location.ts"],"names":[],"mappings":";;;;AAAA,qCAAkI;AAClI,+CAAyD;AAEzD,iDAA4D;AAE5D,wEAAgE;AAChE,6DAAqD;AAErD;;;;;;;;;;;;;;;;GAgBG;AAMI,IAAM,YAAY,GAAlB,MAAM,YAAY;
|
|
1
|
+
{"version":3,"file":"twin-location.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-location.ts"],"names":[],"mappings":"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","sourcesContent":["import { CreateDateColumn, Entity, Index, Column, RelationId, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectType, Field, ID, Int } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinArea } from '../twin-space/twin-area.js'\n\n/*\n * TwinLocation — 이 트윈이 반영하는 **자리**. ISA-95 `OperationalLocation` / EPCIS `SGLN`.\n *\n * ── 왜 테이블인가 (ADR-0032, 2026-08-08) ────────────────────────────────────\n * 지금까지 자리는 `TwinInstance.model` JSON 안에 있었다. 그래서 ⑴ 부분 갱신이 없어 쓰는 쪽이\n * 빠뜨리면 삭제였고 ⑵ 라우트→공정·설비→자리 참조에 FK 를 못 걸어 **끊어진 참조를 화면이 보고만**\n * 했고 ⑶ 5개 드라이버에서 JSON 질의가 이식되지 않아 \"설비 몇 대?\" 조차 서버가 못 답했다.\n *\n * ── 이것은 **원본에서 온 캐시**다 (ADR-0032-D) ──────────────────────────────\n * 잘 만든 엔티티는 정본처럼 **생겼다.** 그래서 소유 침범을 부른다 — 누군가 \"여기에만 있는 컬럼\" 을\n * 더하고, 쓰기 경로를 열고, 그러면 `ADR-0028`(트윈은 만들지 않는다)이 무너진다.\n *\n * 규율 셋을 구조로 박는다:\n * · `sourceRef`·`ingestedAt` 필수 — 어디서 언제 왔는지 없는 행은 만들 수 없다\n * · 쓰기 경로는 **인제스트 하나**\n * · **전부 지우고 다시 만들 수 있다** — 원본이 낼 수 없는 값을 여기 두지 않는다\n */\n@Entity({ name: 'twin_locations' })\n@Index('ix_twin_location_0', (e: TwinLocation) => [e.domain, e.instance, e.locationId], { unique: true })\n/* 타입으로 좁혀 보기(인스펙션의 \"이 타입의 자리들\") — 공정이 타입을 가리키므로 잦은 질의다. */\n@Index('ix_twin_location_1', (e: TwinLocation) => [e.domain, e.instance, e.type])\n@ObjectType({ description: 'A location reflected into the twin (ISA-95 OperationalLocation / EPCIS SGLN). Projected from the source; never authored here.' })\nexport class TwinLocation {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Owning tenant domain.' })\n domain?: Domain\n\n @RelationId((e: TwinLocation) => e.domain)\n domainId?: string\n\n /* 이 자리가 속한 트윈. 트윈이 사라지면 그 구조도 함께 사라진다(투영이므로 재생성 가능). */\n @ManyToOne(type => TwinInstance, { onDelete: 'CASCADE' })\n @Field(type => TwinInstance, { nullable: true, description: 'Owning twin instance.' })\n instance?: TwinInstance\n\n @RelationId((e: TwinLocation) => e.instance)\n instanceRowId?: string\n\n /** 커널 계약의 `TwinModelDef.locations[].id` **그대로**. 바인딩은 id===id 다(ADR-0014). */\n @Column()\n @Field({ description: 'Location id as declared by the source (kernel TwinModelDef.locations[].id).' })\n locationId: string\n\n /**\n * **사람이 부르는 이름** — 프레임워크 엔티티 규약의 `name`/`description`.\n *\n * ── 왜 이것이 빠져 있었고, 무엇이 망가졌나 ────────────────────────────────────\n * 투영이 마스터의 `name` 을 버리고 있었다. 그래서 행에는 id 만 남고, 화면은 이름을 만들 재료가\n * 없어 `cut-station` 을 그대로 찍었다 — 그 결함이 목록·표·관계 그림마다 따로 나타나서 표면을\n * 하나씩 고치게 만들었다. 표면의 문제가 아니라 **행에 이름이 없던 문제**다.\n *\n * 원본이 이름을 말하지 않으면 비운다(id 를 이름으로 베끼지 않는다 — 그러면 \"이름이 있다\" 는\n * 거짓이 되고, 화면이 이름 없음을 알아볼 수 없게 된다).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Human name as declared by the source. Empty when the source does not name it (never copied from the id).' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Longer description when the source provides one.' })\n description?: string\n\n /**\n * 커널 카탈로그의 자리 타입 키(`storage`·`dock`·`yard-slot`…).\n *\n * **FK 를 걸지 않는다** — 타입 카탈로그는 커널이 SSOT 이고(ADR-0018) DB 로 내리면\n * **커널 어휘의 사본**이 생긴다. 문자열 + 부팅 시 검증이 맞다(ADR-0032-F).\n */\n @Column()\n @Field({ description: 'Location type key from the kernel catalog (not an FK — the catalog is the kernel SSOT).' })\n type: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Declared capacity. Absent when the source does not say.' })\n capacity?: number\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'How many things can be worked on here at once, when the source declares it.' })\n parallelism?: number\n\n /**\n * 속한 구역 — **실제 FK**.\n *\n * 커널 계약에서는 `locations[].parentId` 가 model 안에 대상이 없는 참조였다(마스터에서 구역도\n * 자리와 한 목록이었다가 인제스트가 갈라 `TwinArea` 로 보내기 때문). 커널은 그것을\n * `external: 'space.area'` 로 선언했고, **호스트에서는 평범한 FK 가 된다.**\n */\n @ManyToOne(type => TwinArea, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => TwinArea, { nullable: true, description: 'Logical area this location belongs to.' })\n area?: TwinArea\n\n @RelationId((e: TwinLocation) => e.area)\n areaRowId?: string\n\n /** GS1 식별자(SGLN 등) — 원본이 주면 싣는다. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'GS1 identifier supplied by the source (e.g. SGLN).' })\n gs1Id?: string\n\n /**\n * 이 자리의 **선언 속성** — 계약전력·요금 단가처럼 자리에 얹히는 수(ISA-95 Property).\n *\n * ── 왜 늦게 생겼나 (2026-08-18) ────────────────────────────────────────────\n * 설비 행에는 이 칸이 있었는데 자리 행에는 없었다. 그래서 수전 지점에 계약전력·요금을 넣어도 **항목\n * 패널의 「원본이 말한 값」에 그 줄이 아예 없었다** — 사용자는 값을 넣고도 화면에서 찾을 수 없었고,\n * 「이 값이 무엇을 바꾸나」는 문장도 붙을 자리가 없었다(조용한 누락).\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Properties declared on this location (ISA-95 Property: value + dataType + unit of measure). Carries locally-declared numbers too, with source: local.' })\n properties?: any\n\n /* ── 캐시라는 사실을 구조로 (ADR-0032-D) ── */\n @Column()\n @Field({ description: 'Which reference (source system) this row came from. Required — a row with no origin cannot exist.' })\n sourceRef: string\n\n /*\n * **타입 이름을 적지 않는다** (2026-08-19 Postgres 부팅 실패로 고침).\n *\n * `datetime` 은 sqlite·MySQL 의 이름이고 Postgres 는 `timestamp` 만 안다 — 거꾸로 `timestamp` 는\n * sqlite 가 모른다. **다섯 드라이버에 다 통하는 이름이 없다.** 그래서 이름 고르는 일을 TypeORM 에\n * 맡긴다(`emitDecoratorMetadata` 가 켜져 있어 `Date` 에서 드라이버별 타입을 고른다).\n */\n @Column()\n @Field({ description: 'When this row was projected from the source.' })\n ingestedAt: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n}\n"]}
|
|
@@ -361,7 +361,7 @@ let TwinModelQuery = class TwinModelQuery {
|
|
|
361
361
|
})(),
|
|
362
362
|
description: inst.description ?? null,
|
|
363
363
|
kind: inst.kind,
|
|
364
|
-
|
|
364
|
+
restartPolicy: inst.restartPolicy,
|
|
365
365
|
source: ref
|
|
366
366
|
? { referenceId: ref.source, adapterType: ref.adapterType, status: ref.status, lastSyncedAt: ref.lastSyncedAt }
|
|
367
367
|
: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twin-model-query.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-model-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Query, Resolver } from 'type-graphql'\n\nimport { ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\nimport { readBoardEquipment } from '@operato/twin-kernel'\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinSpace } from '../twin-space/twin-space.js'\nimport { TwinReference } from '../reference/twin-reference.js'\nimport { declarablePropertySpecs } from '../../engine/local-declarations.js'\nimport { operationBasis } from '../../engine/operation-basis.js'\nimport { TwinLocation } from './twin-location.js'\nimport { TwinEquipment } from './twin-equipment.js'\nimport { TwinOperation } from './twin-operation.js'\nimport { standardsForKind, type StandardId } from './standard-coverage.js'\nimport { isa95Coverage } from './isa95-coverage.js'\nimport { iec61850Coverage } from './iec61850-coverage.js'\nimport { epcisCoverage } from './epcis-coverage.js'\n\n/**\n * 이 공정에 선언된 시간을 **겹에서** 든다 — 좁은 겹이 이긴다(트윈 먼저, 없으면 현장).\n *\n * 모델에는 ISO 8601 로 구워지지만 화면에는 **사용자가 넣은 수와 단위**를 보인다: 「PT240S」를 보이면\n * 자기가 넣은 값인지 알아볼 수 없다. 선언이 없으면 빈 배열이다(빈 값을 지어내지 않는다).\n */\nfunction declaredDurationProperty(kind: string, instanceLayer?: any[], spaceLayer?: any[]): any[] {\n const find = (layer?: any[]) => {\n const d = (layer ?? []).find(x => x?.target === 'operation' && String(x?.id ?? '') === kind)\n return (d?.properties ?? []).find((p: any) => p?.id === 'operation.duration')\n }\n const p = find(instanceLayer) ?? find(spaceLayer)\n if (!p) return []\n return [{ id: 'operation.duration', value: String(p.value ?? ''), ...(p.uom ? { uom: String(p.uom) } : {}), source: 'local' }]\n}\n\n/** 표준 id → 그 표의 함수. 정책은 `standardsForKind` 가, 표는 각 파일이 가진다. */\nconst COVERAGE: Record<StandardId, (axes: string[]) => any> = { isa95: isa95Coverage, epcis: epcisCoverage, iec61850: iec61850Coverage }\nimport { unwrapState } from '../../engine/warm-start.js'\n/* 공유 모듈은 빌드 산출물로 참조한다 — 서버·클라이언트가 같은 규칙을 쓰는 자리다(같은 관행: entity-delta). */\nimport { axisValueOf } from '@things-factory/headless-twin/dist-shared/axis-read.js'\n\n/* 커널 런타임 로드 — CJS 번들(엔진과 같은 방식). 축·관계 선언의 SSOT 다. */\nconst { TWIN_AXES, TWIN_RELATIONS, axisInfo, axisSource, axisAppliesTo } = require('@operato/twin-kernel')\n\n/**\n * 축 → 그 축이 이미 **행으로 옮겨졌나**. 옮겨진 축은 DB 가 세고, 아직인 축은 문서를 세어야 한다.\n *\n * 이 표가 곧 `ADR-0032` 이주의 진척이다 — 여기 없는 축은 아직 `TwinModelDef` 문서 안에 산다.\n */\nconst ROW_BACKED: Record<string, any> = {\n locations: TwinLocation,\n equipment: TwinEquipment,\n operations: TwinOperation\n}\n\n/** `productionSpec.definition.recipes` 같은 경로를 문서에서 따라간다. */\nconst at = (o: any, path: string) => path.split('.').reduce((a: any, k: string) => (a == null ? undefined : a[k]), o)\n\nfunction countInDocument(model: any, path: string): number {\n const v = at(model, path)\n if (Array.isArray(v)) return v.length\n return v ? 1 : 0\n}\n\n/*\n * 트윈 모델 조회 — **이 트윈은 표준 모델의 어디를 채우고 있나**(`plans/twin-inspection.md` §2 ②).\n *\n * ── 답의 크기가 개념 수에 비례한다 ─────────────────────────────────────────\n * 엔티티를 **절대 싣지 않는다.** 실으면 계약이 곧 무너지고, 규모가 오는 날 이 질의가 그대로\n * 병목이 된다. 항목은 별도 질의(`twinModelEntities`)가 페이징으로 낸다.\n *\n * ── 개념 목록을 이 코드가 갖지 않는다 ───────────────────────────────────────\n * 축·관계는 **커널 카탈로그가 선언**한다(`TWIN_AXES`·`TWIN_RELATIONS`). 그래서 야드의 `dock-door`\n * 나 에너지의 계량기가 늘어도 여기 손댈 것이 없다 — 도메인마다 화면·질의가 하나씩 늘어나던\n * 구조를 끊는 것이 이 설계의 요점이다(`ADR-0031`).\n *\n * ── 어떻게 셌는지 밝힌다 ───────────────────────────────────────────────────\n * 지금은 축 셋만 행이고 나머지는 문서 안에 있다. 그 사실을 `basis` 로 **축마다** 말한다.\n * 밝히지 않으면 \"이 트윈은 3개 축뿐\" 으로 읽히고, 이주가 어디까지 왔는지도 아무도 모른다.\n */\n/** `inputs[].material` 처럼 배열을 지나는 선언 경로를 따라간다 — 배열 표기가 관계 선언의 문법이다. */\nfunction valuesAt(obj: any, field: string): any[] {\n let cur: any[] = [obj]\n for (const seg of field.split('.')) {\n const key = seg.endsWith('[]') ? seg.slice(0, -2) : seg\n const next: any[] = []\n for (const c of cur) {\n if (c == null) continue\n const v = c[key]\n if (v == null) continue\n if (Array.isArray(v)) next.push(...v)\n else next.push(v)\n }\n cur = next\n }\n return cur.filter(v => v != null)\n}\n\nconst idOf = (o: any): string | null => (typeof o === 'string' ? o : o?.id ?? o?.key ?? o?.gtin ?? null)\n\n@Resolver()\nexport class TwinModelQuery {\n /**\n * 이 선이 **얼마나 이어져 있나** — 그리고 **얼마나 끊겨 있나.**\n *\n * 선이 \"선언되었고 양끝이 존재한다\" 만 말하면 지도는 그림일 뿐이다. 관계에서 가장 중요한 정보는\n * 대개 **부재**다 — 기준 자리가 비어 있는 설비 열두 대, 어느 라우트에도 안 실린 공정. 그것이\n * 조감도에서 보여야 사람이 끊긴 데를 찾아다니지 않는다.\n *\n * 셋을 센다:\n * · `linked` — 값이 있고 그 값이 상대편에 **실제로 있다**\n * · `dangling` — 값은 있는데 **가리키는 것이 없다**(끊어진 참조 — 가장 나쁜 부류)\n * · `missing` — 값 자체가 없다(선택 관계면 정상, 필수 관계면 결손)\n *\n * 문서 어휘로 푼다 — 관계 선언의 필드 이름이 원본 문서의 어휘이기 때문이다(행은 자기 컬럼\n * 이름을 쓴다). 그 사실을 `fillBasis` 로 함께 낸다.\n */\n private edgeFill(model: any, r: any): any {\n const fromInfo = axisInfo(r.from)\n const toInfo = axisInfo(r.target.axis)\n if (!fromInfo || !toInfo) return {}\n\n /*\n * **문서로 셀 수 없는 관계는 세지 않는다.** 선 채움(이어짐·끊어짐·빈 것)은 두 축을 문서에서\n * 살펴 센다. 그런데 일정·실적은 문서에 없다(관측 축) — 그 관계를 문서로 세면 언제나 0 이 나오고,\n * 화면은 그것을 \"전부 끊어졌다\" 로 읽는다. 셀 수 없으면 **수를 내지 않는 편이 정직하다.**\n */\n if (axisSource(r.from) !== 'document' || axisSource(r.target.axis) !== 'document') return {}\n\n const listOf = (info: any) => {\n const raw = at(model, info.path)\n return Array.isArray(raw) ? raw : raw ? [raw] : []\n }\n const targets = new Set(listOf(toInfo).map(idOf).filter(Boolean) as string[])\n let linked = 0\n let dangling = 0\n let missing = 0\n for (const item of listOf(fromInfo)) {\n const vals = valuesAt(item, r.field).map(v => (typeof v === 'object' ? idOf(v) : v)).filter(v => v != null).map(String)\n if (!vals.length) { missing++; continue }\n /* 한 항목이 여럿을 가리킬 수 있다(`steps[]`). 하나라도 끊겼으면 끊긴 쪽으로 센다 —\n \"대체로 이어져 있다\" 는 진단이 아니다. */\n if (vals.every(v => targets.has(v))) linked++\n else dangling++\n }\n return { linked, dangling, missing, fillBasis: 'document' }\n }\n\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'What this twin reflects and where it fills the standard model: per-concept counts, how each was counted, and the declared relations between concepts. Never carries entities — use twinModelEntities for items.'\n })\n async twinModelSummary(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return null\n\n /* 어느 원본을 반영하나 — 없을 수 있다(템플릿으로 세운 트윈). 그 사실도 사실이다. */\n const ref = await getRepository(TwinReference)\n .findOne({ where: { domain: { id: domainId }, source: instanceId } })\n .catch(() => null)\n\n const model = inst.model ?? {}\n /*\n * **한 요청에 저널은 한 번만 접는다.**\n *\n * 관측 축(`orders`·`tasks`)은 상태에서 센다. 그 둘이 각자 `recover()` 를 부르고 있었고, 커널이\n * 멈춰 있으면 그것은 **저널 전체를 다시 접는 일**이다(23,731건짜리 트윈에서 4.6초, 두 번이면 곱절).\n * 트윈을 전부 멈춰도 이 화면이 5~7초 걸린 이유가 이것이다 — 부하가 아니라 같은 일을 두 번 한 것이다.\n *\n * 한 번만 접어 나눠 쓴다. 값이 같아지는 것은 덤이 아니라 **옳음**이다: 두 번 접으면 그 사이에\n * 들어온 이벤트 때문에 오더와 작업이 서로 다른 시점의 사실이 될 수 있다.\n */\n let statePromise: Promise<{ snap: any; folded: boolean; observing: boolean }> | null = null\n const stateOnce = () =>\n (statePromise ??= (async () => {\n const observing = TwinEngine.isRunning(domainId, instanceId)\n let snap = observing ? unwrapState(TwinEngine.snapshot(domainId, instanceId)) : null\n let folded = false\n if (!snap) {\n try {\n snap = unwrapState(await TwinEngine.recover(domainId, instanceId))\n folded = !!snap\n } catch {\n /* 다시 계산할 것이 없다 — 0 으로 메우지 않는다(카드는 \"모름\" 을 낸다). */\n }\n }\n return { snap, folded, observing }\n })())\n const concepts = await Promise.all(\n TWIN_AXES.map(async (a: any) => {\n /*\n * **이 종류에 없는 개념인가** — 「아직 없음」과 「해당 없음」은 다른 말이다.\n *\n * 에너지 트윈에는 옮겨 다니는 물건도, 배정할 사람도, 공정도 없다(커널이 축마다 선언한다).\n * 그것을 「로트 0」으로 그리면 사용자는 채우러 갈 곳을 찾고, 적합성 표는 채우지 못한 것으로\n * 센다 — 둘 다 거짓이다. 수는 `null`(모름이 아니라 셀 대상이 아님)이고 이유를 코드로 낸다.\n *\n * 세지도 않는다: 없는 개념을 행에서 세는 것은 질의만 늘리고 답을 바꾸지 않는다.\n */\n if (!axisAppliesTo(a.axis, inst.kind)) {\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: false,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: null,\n notApplicable: true,\n emptyBecause: 'not-applicable',\n basis: 'declaration'\n }\n }\n const entity = ROW_BACKED[a.axis]\n /* 문서 축만 문서로 센다 — 경로 없는 축을 세면 모델 루트를 훑는다(상태 축은 아래에서 센다). */\n const inDocument = axisSource(a.axis) === 'document' ? countInDocument(model, a.path) : 0\n const inRows = entity\n ? await getRepository(entity).count({ where: { domain: { id: domainId } as any, instance: { id: inst.id } as any } })\n : undefined\n /*\n * **둘을 함께 세고 어긋나면 말한다.**\n *\n * 행으로 옮긴 축이라도 그 트윈이 투영을 거치지 않았으면 행은 0이다(인제스트 없이\n * warm-start 된 트윈이 그렇다). 행만 보고 답하면 문서에 자리 6개가 있는데 화면은\n * **0 이라고 말한다** — 조용한 거짓이고, 이 프로젝트가 내내 잡아 온 바로 그 부류다.\n *\n * 그래서 어느 하나로 메우지 않는다. 행 수를 답하되 문서 수를 함께 내고, 다르면\n * `stale` 로 표시한다 — 그 차이 자체가 \"이 트윈은 투영 이후 인제스트되지 않았다\" 는 정보다.\n */\n const stale = entity !== undefined && inRows !== inDocument\n /*\n * **그릇인가** — 문서에 객체 하나로 있는 축(`productionSpec`).\n *\n * `kind` 로는 갈리지 않는다: 커널은 공정·레시피·라우트도 `spec` 이라 부른다(같은 선언 부류라는\n * 뜻이다). 그릇인지 아닌지는 **모양**이 말한다 — 목록이 되는 축은 배열이고, 한 벌뿐인 선언은\n * 객체 하나다. 개념 지도는 개체를 세는 면이므로 그릇은 자기 면에서 통째로 읽는다.\n */\n const raw = axisSource(a.axis) === 'document' ? at(model, a.path) : undefined\n /*\n * **관측 축은 상태가 센다**(일정·실적 — ISA-95 Part 4). 커널이 멈춰 있으면 `null` 이다:\n * 0 으로 내면 \"작업이 없는 공장\" 으로 읽히고, 그건 거짓이다(관측이 없는 것과 0 건은 다르다).\n * 이 화면이 관측 열에서 내내 지켜 온 구분을 카드에서도 지킨다.\n */\n if (axisSource(a.axis) === 'state') {\n /* 스냅샷은 `{revision, state}` 봉투다 — 벗기지 않으면 축이 한 겹 안에 있어 언제나 `null` 이\n 된다(커널은 도는데 카드가 \"모름\" 으로 보였다). 벗기는 규칙의 집은 `warm-start` 하나다. */\n /*\n * 관측 중인지는 **런타임이 있는지**로 본다 — 캐시된 스냅샷은 관측이 아니다.\n *\n * 동작하지 않으면 저널을 접어 답한다(라이브=권위 · 히스토리=파생). 카드는 그 차이를 `basis` 로\n * 말한다: 파생을 권위처럼 내면 다시 계산한 수를 \"지금 이만큼\" 으로 읽게 된다.\n */\n const { snap, folded, observing } = await stateOnce()\n /* 축이 사는 자리는 **커널 선언이 말한다**(`path`) — 축 이름으로만 읽으면 중첩된 축이\n 언제나 「관측 없음」이 된다(에너지의 수요 구간이 그렇다). 규칙은 `axisValueOf` 한 곳. */\n const bucket = snap ? axisValueOf(snap, a) ?? null : null\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: false,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: Array.isArray(bucket) ? bucket.length : null,\n /*\n * **비었으면 왜 비었는지 말한다.** 0 과 `null` 만 내면 화면은 \"없다\" 까지만 말하고, 사용자는\n * \"안 적어 놨나 · 아직 안 일어났나 · 못 세었나\" 를 구별할 수 없다. 이 화면이 관측 열에서\n * 내내 지켜 온 구분(§observedAbsence)을 카드에서도 지킨다.\n */\n ...(Array.isArray(bucket) ? (bucket.length ? {} : { emptyBecause: 'none-yet' }) : { emptyBecause: 'not-observed' }),\n basis: folded ? 'journal' : 'state',\n observing,\n /* 선언이 아니라 관측인 축 — 화면이 \"원본이 침묵했다\" 로 읽지 않게 사실로 말한다. */\n observedOnly: true,\n standardUndeclared: !a.standardClass?.isa95 && !a.standardClass?.epcis && !a.standardClass?.iso55000\n }\n }\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: !Array.isArray(raw) && raw != null,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: entity ? inRows : inDocument,\n /*\n * 비었으면 왜 비었는지 — 선언 쪽은 세 갈래다.\n * · `not-declared` 이 트윈의 모델이 그 축을 아예 말하지 않는다(대개 템플릿이 안 적었다)\n * · `declared-empty` 선언은 있는데 목록이 비어 있다(적었다가 지운 것도 사실이다)\n * · `not-projected` 문서엔 있는데 행에 없다 — 투영을 거치지 않은 트윈이다(`stale` 과 같은 사실)\n *\n * 셋을 \"0\" 하나로 합치면 사용자가 손볼 곳을 찾지 못한다: 첫째는 템플릿, 둘째는 원본,\n * 셋째는 다시 읽기다. 셋 다 다른 일이다.\n */\n ...((entity ? inRows : inDocument)\n ? {}\n : {\n emptyBecause:\n entity && inDocument > 0 ? 'not-projected' : raw == null ? 'not-declared' : 'declared-empty'\n }),\n /* 'rows' = DB 가 셌다(질의 가능) · 'document' = 문서를 세었다(아직 이주 전). */\n basis: entity ? 'rows' : 'document',\n ...(entity ? { documentCount: inDocument, stale } : {}),\n /* 표준 투영이 없는 축은 **숨기지 않고** 그 사실을 낸다(ADR-0031). */\n standardUndeclared: !a.standardClass?.isa95 && !a.standardClass?.epcis && !a.standardClass?.iso55000\n }\n })\n )\n\n /*\n * **원본이 모르는데 실행 중인 것** — 커널은 마스터에 없던 자원도 이벤트로 알게 되고\n * `origin: 'observed'` 를 단다(`observed-reducer`). 그것을 표에 밀어 넣지 않는다 — 그러면\n * 틱 루프에서 DB 로 쓰는 **두 번째 쓰기 경로**가 생기고, 실패하면 조용히 어긋난다\n * (쓰기 경로는 인제스트 하나, ADR-0032-D).\n *\n * 대신 **여기서 비교해 낸다.** 이 차이는 숨겨야 할 불일치가 아니라 인스펙션이 답해야 할\n * 질문이다 — \"원본이 말한 적 없는 설비가 실제로 실행 중이다.\"\n *\n * 트윈이 안 실행 중이면 비교할 상대가 없다 — 그때는 `null` 이다(0 이 아니다. 없는 것과\n * 세어 보지 못한 것은 다르다).\n */\n const snap = TwinEngine.snapshot(domainId, instanceId)\n const observedOnly = snap\n ? {\n equipment: (snap.equipment ?? []).filter((e: any) => e?.origin === 'observed').map((e: any) => e.id),\n locations: (snap.locations ?? []).filter((l: any) => l?.origin === 'observed').map((l: any) => l.id),\n persons: (snap.persons ?? []).filter((p: any) => p?.origin === 'observed').map((p: any) => p.id)\n }\n : null\n\n /* 지도의 선 — 선언된 관계 중 **양끝이 이 트윈에 실재하는 것만**. 없는 선을 그리지 않는다. */\n /* 선의 양끝 판정은 **문서든 행이든 있으면 있는 것**으로 본다 — 투영이 낡았다고 관계가 없어지진 않는다. */\n const nonEmpty = new Set(concepts.filter(c => (c.count ?? 0) > 0 || ((c as any).documentCount ?? 0) > 0).map(c => c.axis))\n /* 선은 **얼마나 이어졌고 얼마나 끊겼는지**까지 말한다 — 관계에서 가장 중요한 정보는 대개 부재다. */\n const edges = await Promise.all(\n TWIN_RELATIONS.filter((r: any) => r.target.kind === 'axis' && nonEmpty.has(r.from) && nonEmpty.has(r.target.axis)).map(\n async (r: any) => ({ from: r.from, to: r.target.axis, via: r.via, optional: !!r.optional, ...this.edgeFill(model, r) })\n )\n )\n\n return {\n instanceId,\n /* 사람이 부르는 이름 — 결선 줄이 업무키만 보이지 않게(업무키는 곁에 남는다). */\n name: inst.name ?? null,\n /*\n * **생산 선언에서 갈 곳이 없는 두 값** — GS1 발행자와 대표 산출물.\n *\n * 나머지 선언(자재·레시피·라우트·공정)은 각자 개념 축이 있고, 자재의 GS1 참조는 자재가 보인다.\n * 이 둘만 자기 축이 없다: 회사 접두사는 이 트윈의 **정체**(발행자)이고, 대표 레시피는 이 공장이\n * 무엇을 만드는 곳인가다. 둘 다 결선 줄(어느 원본을 반영하나)과 같은 성격이라 그 줄에 둔다.\n */\n /*\n * **표준 적합성** — 채운 것과 빠진 것을 함께.\n *\n * 이 화면은 \"이 트윈이 표준 모델의 어디를 채우고 있나\" 를 묻는다. 그런데 지금까지 답은 우리\n * 축만 세었다 — 없는 것은 화면에 없으므로 아무도 묻지 않았다. 기준(표준의 목록)을 함께 내면\n * 그 질문이 실제로 답을 갖는다(`isa95-coverage.ts` 의 머리말 참조).\n */\n /* 종류가 기대는 표준을 **모두** 낸다 — 하나만 말하면 그 트윈의 다른 기준이 보이지 않는다. */\n standards: standardsForKind(inst.kind).map(id => COVERAGE[id]((TWIN_AXES as any[]).map(a => a.axis))),\n production: (() => {\n const spec: any = (inst.model as any)?.productionSpec\n if (!spec) return null\n const key = spec.recipeKey ?? null\n const material = (spec.definition?.materials ?? []).find((m: any) => (m.key ?? m.id) === key)\n return {\n companyPrefix: spec.companyPrefix ?? null,\n recipeKey: key,\n /* 이름은 자재 선언이 말한다(i18n 키일 수 있으므로 번역은 화면의 일). */\n recipeLabel: material?.label ?? material?.name ?? null\n }\n })(),\n description: inst.description ?? null,\n kind: inst.kind,\n realityMode: inst.realityMode,\n source: ref\n ? { referenceId: ref.source, adapterType: ref.adapterType, status: ref.status, lastSyncedAt: ref.lastSyncedAt }\n : null,\n /*\n * **다시 읽을 수 있는가** — 이 트윈이 자기 원본을 기억하고 있나(`TwinInstance.origin`).\n *\n * 없는 트윈이 실제로 있다(이 자리가 생기기 전에 만들어진 것). 그때 화면이 버튼을 그대로\n * 내면 누를 때마다 실패하고, 사용자는 무엇이 잘못됐는지 알 수 없다. 그래서 **할 수 있는지를\n * 먼저 답한다** — 못 하는 것을 못 한다고 말하는 것도 이 화면의 일이다.\n */\n origin: inst.origin ?? null,\n canResync: !!(inst.origin as any)?.kind,\n concepts,\n edges,\n /* 원본이 모르는데 관측에는 있는 것. `null` = 트윈이 실행 중이 아니어서 비교하지 못했다. */\n observedOnly,\n /*\n * 축 셋만 행이고 나머지는 문서에서 셌다는 사실. 축마다 `basis` 가 있지만 여기서도 한 번 말한다 —\n * 화면이 \"이 숫자는 무엇으로 셌나\" 를 한눈에 보여야 규모 한계가 드러난다(ADR-0032 이주 압력).\n */\n basis: {\n rows: Object.keys(ROW_BACKED),\n document: TWIN_AXES.filter((a: any) => !ROW_BACKED[a.axis]).map((a: any) => a.axis),\n /* 투영이 낡은 축 — 있으면 이 트윈은 인제스트를 한 번 더 태워야 한다. */\n stale: concepts.filter((c: any) => c.stale).map((c: any) => c.axis)\n }\n }\n }\n /**\n * **이 트윈에 무엇을 선언할 수 있나** — 값을 넣는 화면이 도메인의 모양을 알지 않게.\n *\n * 커널이 정한 명세(속성 id·단위·범위·설명)를 그대로 낸다. 화면이 「에너지 트윈이면 계약전력·요금\n * 단가…」를 알고 있으면 도메인이 늘 때마다 화면을 고쳐야 하고 단위가 두 곳에 적힌다(ADR-0031).\n *\n * 지금 무엇이 선언돼 있는지도 함께 낸다(`declared`) — 화면이 두 번 묻지 않게, 그리고 「원천이 말한\n * 값」과 「우리가 정한 값」을 한자리에서 견줄 수 있게(`PropertySource`, ADR-0034).\n *\n * 어휘가 없는 종류는 `properties: []` 다 — 「아직 없다」와 「아무 값이나 받는다」는 다르다.\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n \"What can be declared on this twin and what already is: the kernel's property specification (id, unit, range, note) plus the current local declarations at both scopes — this twin and its facility — each with the value it overrode. Empty properties mean this kind has no declaration vocabulary yet — not that anything goes.\"\n })\n async twinDeclarations(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return null\n const model = (inst.model ?? {}) as any\n /*\n * 겹이 둘이다 — **현장의 것과 이 트윈의 것**. 화면이 둘을 함께 봐야 「이 값이 어느 범위의 것인가」를\n * 말할 수 있고, 철회 버튼이 어느 겹을 겨눌지 고를 수 있다(요금을 현장에 넣었는데 트윈에서 철회하려\n * 하면 「겹에 없다」로 끝난다 — 사용자는 이유를 알 수 없다).\n */\n const space = inst.spaceId\n ? await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId: inst.spaceId } })\n : null\n /*\n * 돌고 있으면 커널이 「무엇으로 돌고 있나」를 스스로 답한다. 그 보고는 **작업이 새로 생길 때** 채워지\n * 므로 재기동 직후에는 비는데, 이력에서 배운 종류는 그때도 남아 있다(실측으로 이 빈칸에 부딪혔다).\n */\n const operations = operationBasis(\n model,\n TwinEngine.kernel(domainId, instanceId),\n await TwinEngine.measuredOperationKinds(domainId, instanceId)\n )\n return {\n instanceId,\n kind: inst.kind,\n spaceId: inst.spaceId,\n spaceName: space?.name,\n properties: declarablePropertySpecs(inst.kind),\n declared: (inst.localDeclarations ?? []) as any[],\n declaredAtSpace: (space?.localDeclarations ?? []) as any[],\n /* 어디에 얹을 수 있나 — 모델의 자리·설비·공정. 화면이 대상을 지어내지 않게 목록으로 준다. */\n targets: [\n ...(model.locations ?? []).map((l: any) => ({ target: 'location', id: l.id, name: l.name, type: l.type, properties: l.properties ?? [] })),\n ...(readBoardEquipment(model) ?? []).map((e: any) => ({ target: 'equipment', id: e.id, name: e.name, type: e.type ?? e.kind, properties: e.properties ?? [] })),\n /*\n * 공정은 **자원이 아니다** — 값은 종류에 붙고, 그 종류에 명세 행이 없어도 된다(창고·야드에는 행이\n * 0개다). 그래서 대상 목록을 커널의 자기보고까지 합쳐 만든다(`operationBasis`).\n *\n * 지금 값은 **겹에서** 든다(사용자가 넣은 수와 단위 그대로). 모델에는 ISO 8601 로 구워지지만\n * 화면에 「PT240S」를 보이면 자기가 넣은 값인지 알아볼 수 없다.\n */\n ...operations.map(op => ({\n target: 'operation',\n id: op.kind,\n name: op.label,\n properties: declaredDurationProperty(op.kind, inst.localDeclarations as any[], space?.localDeclarations as any[])\n }))\n ],\n /*\n * 이 트윈이 돌리는 공정과 **그 시간이 어디서 오나** — 선언 화면이 「무엇이 상수로 도나」를 보여야\n * 사람이 채울 곳을 안다. `basis` 가 비면 아직 돌지 않은 것이다(공정이 없는 것이 아니다).\n */\n operations\n }\n }\n /**\n * **이 현장에 무엇을 선언할 수 있나** — 현장 값은 트윈의 것이 아니므로 트윈 없이 묻는다.\n *\n * ── 왜 트윈으로 묻지 않나 (2026-08-18) ─────────────────────────────────────\n * 처음에는 트윈 화면에서 범위만 골라 현장 값을 넣게 했다. 그런데 **현장 값을 넣으려고 트윈을 골라야\n * 하는 것**은 개념이 거꾸로다 — 트윈은 그 값을 읽는 쪽이다(사용자가 그 어긋남을 지적했다).\n *\n * 어휘와 대상은 **그 현장 트윈들의 합집합**이다: 한 현장에 여러 종류가 살고(에너지·창고·야드), 요금은\n * 에너지 트윈만 읽는다. 합집합으로 내되 **어느 트윈이 그 대상을 가졌는지** 함께 낸다 — 아무 트윈도\n * 읽지 않는 값을 넣는 일을 화면이 미리 보여 줄 수 있어야 한다.\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'What can be declared for a whole facility (space) and what already is: the union of the property specifications of the twin kinds living there, the union of their declarable targets (with which twins carry each), and the current facility-scope declarations.'\n })\n async spaceDeclarations(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })\n if (!space) return null\n const twins = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, spaceId } })\n\n /* 어휘 합집합 — 같은 속성이 여러 종류에 있어도 한 번만 낸다(명세는 커널 하나에서 온다). */\n const byId = new Map<string, any>()\n for (const t of twins) for (const spec of declarablePropertySpecs(t.kind)) if (!byId.has(spec.id)) byId.set(spec.id, spec)\n\n /* 대상 합집합 — 같은 id 가 여러 트윈에 있으면 **그 트윈들을 함께** 든다(현장 값은 그 전부에 얹힌다). */\n const targets = new Map<string, { target: string; id: string; name?: string; type?: string; twins: string[] }>()\n for (const t of twins) {\n const model = (t.model ?? {}) as any\n const add = (target: 'location' | 'equipment' | 'operation', e: any) => {\n const key = `${target}:${e.id}`\n const cur = targets.get(key)\n if (cur) { cur.twins.push(t.instanceId); return }\n targets.set(key, { target, id: e.id, name: e.name, type: e.type ?? e.kind, twins: [t.instanceId] })\n }\n for (const l of model.locations ?? []) add('location', l)\n for (const e of readBoardEquipment(model) ?? []) add('equipment', e)\n /*\n * 공정도 대상이다 — 「우리 현장의 적재는 4분」은 그 현장 창고 전부가 읽어야 하는 수다. 목록은 각\n * 트윈의 것을 합집합으로 든다(돌고 있으면 커널이 스스로 답한다).\n */\n for (const op of operationBasis(\n model,\n TwinEngine.kernel(domainId, t.instanceId),\n await TwinEngine.measuredOperationKinds(domainId, t.instanceId)\n ))\n add('operation', { id: op.kind, name: op.label })\n }\n\n return {\n spaceId,\n spaceName: space.name,\n properties: [...byId.values()],\n declared: (space.localDeclarations ?? []) as any[],\n targets: [...targets.values()],\n /* 이 현장에 사는 트윈들 — 화면이 「누가 이 값을 읽나」를 말할 수 있게. */\n twins: twins.map(t => ({ instanceId: t.instanceId, kind: t.kind, name: t.name }))\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"twin-model-query.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-model-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Query, Resolver } from 'type-graphql'\n\nimport { ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\nimport { readBoardEquipment } from '@operato/twin-kernel'\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinSpace } from '../twin-space/twin-space.js'\nimport { TwinReference } from '../reference/twin-reference.js'\nimport { declarablePropertySpecs } from '../../engine/local-declarations.js'\nimport { operationBasis } from '../../engine/operation-basis.js'\nimport { TwinLocation } from './twin-location.js'\nimport { TwinEquipment } from './twin-equipment.js'\nimport { TwinOperation } from './twin-operation.js'\nimport { standardsForKind, type StandardId } from './standard-coverage.js'\nimport { isa95Coverage } from './isa95-coverage.js'\nimport { iec61850Coverage } from './iec61850-coverage.js'\nimport { epcisCoverage } from './epcis-coverage.js'\n\n/**\n * 이 공정에 선언된 시간을 **겹에서** 든다 — 좁은 겹이 이긴다(트윈 먼저, 없으면 현장).\n *\n * 모델에는 ISO 8601 로 구워지지만 화면에는 **사용자가 넣은 수와 단위**를 보인다: 「PT240S」를 보이면\n * 자기가 넣은 값인지 알아볼 수 없다. 선언이 없으면 빈 배열이다(빈 값을 지어내지 않는다).\n */\nfunction declaredDurationProperty(kind: string, instanceLayer?: any[], spaceLayer?: any[]): any[] {\n const find = (layer?: any[]) => {\n const d = (layer ?? []).find(x => x?.target === 'operation' && String(x?.id ?? '') === kind)\n return (d?.properties ?? []).find((p: any) => p?.id === 'operation.duration')\n }\n const p = find(instanceLayer) ?? find(spaceLayer)\n if (!p) return []\n return [{ id: 'operation.duration', value: String(p.value ?? ''), ...(p.uom ? { uom: String(p.uom) } : {}), source: 'local' }]\n}\n\n/** 표준 id → 그 표의 함수. 정책은 `standardsForKind` 가, 표는 각 파일이 가진다. */\nconst COVERAGE: Record<StandardId, (axes: string[]) => any> = { isa95: isa95Coverage, epcis: epcisCoverage, iec61850: iec61850Coverage }\nimport { unwrapState } from '../../engine/warm-start.js'\n/* 공유 모듈은 빌드 산출물로 참조한다 — 서버·클라이언트가 같은 규칙을 쓰는 자리다(같은 관행: entity-delta). */\nimport { axisValueOf } from '@things-factory/headless-twin/dist-shared/axis-read.js'\n\n/* 커널 런타임 로드 — CJS 번들(엔진과 같은 방식). 축·관계 선언의 SSOT 다. */\nconst { TWIN_AXES, TWIN_RELATIONS, axisInfo, axisSource, axisAppliesTo } = require('@operato/twin-kernel')\n\n/**\n * 축 → 그 축이 이미 **행으로 옮겨졌나**. 옮겨진 축은 DB 가 세고, 아직인 축은 문서를 세어야 한다.\n *\n * 이 표가 곧 `ADR-0032` 이주의 진척이다 — 여기 없는 축은 아직 `TwinModelDef` 문서 안에 산다.\n */\nconst ROW_BACKED: Record<string, any> = {\n locations: TwinLocation,\n equipment: TwinEquipment,\n operations: TwinOperation\n}\n\n/** `productionSpec.definition.recipes` 같은 경로를 문서에서 따라간다. */\nconst at = (o: any, path: string) => path.split('.').reduce((a: any, k: string) => (a == null ? undefined : a[k]), o)\n\nfunction countInDocument(model: any, path: string): number {\n const v = at(model, path)\n if (Array.isArray(v)) return v.length\n return v ? 1 : 0\n}\n\n/*\n * 트윈 모델 조회 — **이 트윈은 표준 모델의 어디를 채우고 있나**(`plans/twin-inspection.md` §2 ②).\n *\n * ── 답의 크기가 개념 수에 비례한다 ─────────────────────────────────────────\n * 엔티티를 **절대 싣지 않는다.** 실으면 계약이 곧 무너지고, 규모가 오는 날 이 질의가 그대로\n * 병목이 된다. 항목은 별도 질의(`twinModelEntities`)가 페이징으로 낸다.\n *\n * ── 개념 목록을 이 코드가 갖지 않는다 ───────────────────────────────────────\n * 축·관계는 **커널 카탈로그가 선언**한다(`TWIN_AXES`·`TWIN_RELATIONS`). 그래서 야드의 `dock-door`\n * 나 에너지의 계량기가 늘어도 여기 손댈 것이 없다 — 도메인마다 화면·질의가 하나씩 늘어나던\n * 구조를 끊는 것이 이 설계의 요점이다(`ADR-0031`).\n *\n * ── 어떻게 셌는지 밝힌다 ───────────────────────────────────────────────────\n * 지금은 축 셋만 행이고 나머지는 문서 안에 있다. 그 사실을 `basis` 로 **축마다** 말한다.\n * 밝히지 않으면 \"이 트윈은 3개 축뿐\" 으로 읽히고, 이주가 어디까지 왔는지도 아무도 모른다.\n */\n/** `inputs[].material` 처럼 배열을 지나는 선언 경로를 따라간다 — 배열 표기가 관계 선언의 문법이다. */\nfunction valuesAt(obj: any, field: string): any[] {\n let cur: any[] = [obj]\n for (const seg of field.split('.')) {\n const key = seg.endsWith('[]') ? seg.slice(0, -2) : seg\n const next: any[] = []\n for (const c of cur) {\n if (c == null) continue\n const v = c[key]\n if (v == null) continue\n if (Array.isArray(v)) next.push(...v)\n else next.push(v)\n }\n cur = next\n }\n return cur.filter(v => v != null)\n}\n\nconst idOf = (o: any): string | null => (typeof o === 'string' ? o : o?.id ?? o?.key ?? o?.gtin ?? null)\n\n@Resolver()\nexport class TwinModelQuery {\n /**\n * 이 선이 **얼마나 이어져 있나** — 그리고 **얼마나 끊겨 있나.**\n *\n * 선이 \"선언되었고 양끝이 존재한다\" 만 말하면 지도는 그림일 뿐이다. 관계에서 가장 중요한 정보는\n * 대개 **부재**다 — 기준 자리가 비어 있는 설비 열두 대, 어느 라우트에도 안 실린 공정. 그것이\n * 조감도에서 보여야 사람이 끊긴 데를 찾아다니지 않는다.\n *\n * 셋을 센다:\n * · `linked` — 값이 있고 그 값이 상대편에 **실제로 있다**\n * · `dangling` — 값은 있는데 **가리키는 것이 없다**(끊어진 참조 — 가장 나쁜 부류)\n * · `missing` — 값 자체가 없다(선택 관계면 정상, 필수 관계면 결손)\n *\n * 문서 어휘로 푼다 — 관계 선언의 필드 이름이 원본 문서의 어휘이기 때문이다(행은 자기 컬럼\n * 이름을 쓴다). 그 사실을 `fillBasis` 로 함께 낸다.\n */\n private edgeFill(model: any, r: any): any {\n const fromInfo = axisInfo(r.from)\n const toInfo = axisInfo(r.target.axis)\n if (!fromInfo || !toInfo) return {}\n\n /*\n * **문서로 셀 수 없는 관계는 세지 않는다.** 선 채움(이어짐·끊어짐·빈 것)은 두 축을 문서에서\n * 살펴 센다. 그런데 일정·실적은 문서에 없다(관측 축) — 그 관계를 문서로 세면 언제나 0 이 나오고,\n * 화면은 그것을 \"전부 끊어졌다\" 로 읽는다. 셀 수 없으면 **수를 내지 않는 편이 정직하다.**\n */\n if (axisSource(r.from) !== 'document' || axisSource(r.target.axis) !== 'document') return {}\n\n const listOf = (info: any) => {\n const raw = at(model, info.path)\n return Array.isArray(raw) ? raw : raw ? [raw] : []\n }\n const targets = new Set(listOf(toInfo).map(idOf).filter(Boolean) as string[])\n let linked = 0\n let dangling = 0\n let missing = 0\n for (const item of listOf(fromInfo)) {\n const vals = valuesAt(item, r.field).map(v => (typeof v === 'object' ? idOf(v) : v)).filter(v => v != null).map(String)\n if (!vals.length) { missing++; continue }\n /* 한 항목이 여럿을 가리킬 수 있다(`steps[]`). 하나라도 끊겼으면 끊긴 쪽으로 센다 —\n \"대체로 이어져 있다\" 는 진단이 아니다. */\n if (vals.every(v => targets.has(v))) linked++\n else dangling++\n }\n return { linked, dangling, missing, fillBasis: 'document' }\n }\n\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'What this twin reflects and where it fills the standard model: per-concept counts, how each was counted, and the declared relations between concepts. Never carries entities — use twinModelEntities for items.'\n })\n async twinModelSummary(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return null\n\n /* 어느 원본을 반영하나 — 없을 수 있다(템플릿으로 세운 트윈). 그 사실도 사실이다. */\n const ref = await getRepository(TwinReference)\n .findOne({ where: { domain: { id: domainId }, source: instanceId } })\n .catch(() => null)\n\n const model = inst.model ?? {}\n /*\n * **한 요청에 저널은 한 번만 접는다.**\n *\n * 관측 축(`orders`·`tasks`)은 상태에서 센다. 그 둘이 각자 `recover()` 를 부르고 있었고, 커널이\n * 멈춰 있으면 그것은 **저널 전체를 다시 접는 일**이다(23,731건짜리 트윈에서 4.6초, 두 번이면 곱절).\n * 트윈을 전부 멈춰도 이 화면이 5~7초 걸린 이유가 이것이다 — 부하가 아니라 같은 일을 두 번 한 것이다.\n *\n * 한 번만 접어 나눠 쓴다. 값이 같아지는 것은 덤이 아니라 **옳음**이다: 두 번 접으면 그 사이에\n * 들어온 이벤트 때문에 오더와 작업이 서로 다른 시점의 사실이 될 수 있다.\n */\n let statePromise: Promise<{ snap: any; folded: boolean; observing: boolean }> | null = null\n const stateOnce = () =>\n (statePromise ??= (async () => {\n const observing = TwinEngine.isRunning(domainId, instanceId)\n let snap = observing ? unwrapState(TwinEngine.snapshot(domainId, instanceId)) : null\n let folded = false\n if (!snap) {\n try {\n snap = unwrapState(await TwinEngine.recover(domainId, instanceId))\n folded = !!snap\n } catch {\n /* 다시 계산할 것이 없다 — 0 으로 메우지 않는다(카드는 \"모름\" 을 낸다). */\n }\n }\n return { snap, folded, observing }\n })())\n const concepts = await Promise.all(\n TWIN_AXES.map(async (a: any) => {\n /*\n * **이 종류에 없는 개념인가** — 「아직 없음」과 「해당 없음」은 다른 말이다.\n *\n * 에너지 트윈에는 옮겨 다니는 물건도, 배정할 사람도, 공정도 없다(커널이 축마다 선언한다).\n * 그것을 「로트 0」으로 그리면 사용자는 채우러 갈 곳을 찾고, 적합성 표는 채우지 못한 것으로\n * 센다 — 둘 다 거짓이다. 수는 `null`(모름이 아니라 셀 대상이 아님)이고 이유를 코드로 낸다.\n *\n * 세지도 않는다: 없는 개념을 행에서 세는 것은 질의만 늘리고 답을 바꾸지 않는다.\n */\n if (!axisAppliesTo(a.axis, inst.kind)) {\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: false,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: null,\n notApplicable: true,\n emptyBecause: 'not-applicable',\n basis: 'declaration'\n }\n }\n const entity = ROW_BACKED[a.axis]\n /* 문서 축만 문서로 센다 — 경로 없는 축을 세면 모델 루트를 훑는다(상태 축은 아래에서 센다). */\n const inDocument = axisSource(a.axis) === 'document' ? countInDocument(model, a.path) : 0\n const inRows = entity\n ? await getRepository(entity).count({ where: { domain: { id: domainId } as any, instance: { id: inst.id } as any } })\n : undefined\n /*\n * **둘을 함께 세고 어긋나면 말한다.**\n *\n * 행으로 옮긴 축이라도 그 트윈이 투영을 거치지 않았으면 행은 0이다(인제스트 없이\n * warm-start 된 트윈이 그렇다). 행만 보고 답하면 문서에 자리 6개가 있는데 화면은\n * **0 이라고 말한다** — 조용한 거짓이고, 이 프로젝트가 내내 잡아 온 바로 그 부류다.\n *\n * 그래서 어느 하나로 메우지 않는다. 행 수를 답하되 문서 수를 함께 내고, 다르면\n * `stale` 로 표시한다 — 그 차이 자체가 \"이 트윈은 투영 이후 인제스트되지 않았다\" 는 정보다.\n */\n const stale = entity !== undefined && inRows !== inDocument\n /*\n * **그릇인가** — 문서에 객체 하나로 있는 축(`productionSpec`).\n *\n * `kind` 로는 갈리지 않는다: 커널은 공정·레시피·라우트도 `spec` 이라 부른다(같은 선언 부류라는\n * 뜻이다). 그릇인지 아닌지는 **모양**이 말한다 — 목록이 되는 축은 배열이고, 한 벌뿐인 선언은\n * 객체 하나다. 개념 지도는 개체를 세는 면이므로 그릇은 자기 면에서 통째로 읽는다.\n */\n const raw = axisSource(a.axis) === 'document' ? at(model, a.path) : undefined\n /*\n * **관측 축은 상태가 센다**(일정·실적 — ISA-95 Part 4). 커널이 멈춰 있으면 `null` 이다:\n * 0 으로 내면 \"작업이 없는 공장\" 으로 읽히고, 그건 거짓이다(관측이 없는 것과 0 건은 다르다).\n * 이 화면이 관측 열에서 내내 지켜 온 구분을 카드에서도 지킨다.\n */\n if (axisSource(a.axis) === 'state') {\n /* 스냅샷은 `{revision, state}` 봉투다 — 벗기지 않으면 축이 한 겹 안에 있어 언제나 `null` 이\n 된다(커널은 도는데 카드가 \"모름\" 으로 보였다). 벗기는 규칙의 집은 `warm-start` 하나다. */\n /*\n * 관측 중인지는 **런타임이 있는지**로 본다 — 캐시된 스냅샷은 관측이 아니다.\n *\n * 동작하지 않으면 저널을 접어 답한다(라이브=권위 · 히스토리=파생). 카드는 그 차이를 `basis` 로\n * 말한다: 파생을 권위처럼 내면 다시 계산한 수를 \"지금 이만큼\" 으로 읽게 된다.\n */\n const { snap, folded, observing } = await stateOnce()\n /* 축이 사는 자리는 **커널 선언이 말한다**(`path`) — 축 이름으로만 읽으면 중첩된 축이\n 언제나 「관측 없음」이 된다(에너지의 수요 구간이 그렇다). 규칙은 `axisValueOf` 한 곳. */\n const bucket = snap ? axisValueOf(snap, a) ?? null : null\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: false,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: Array.isArray(bucket) ? bucket.length : null,\n /*\n * **비었으면 왜 비었는지 말한다.** 0 과 `null` 만 내면 화면은 \"없다\" 까지만 말하고, 사용자는\n * \"안 적어 놨나 · 아직 안 일어났나 · 못 세었나\" 를 구별할 수 없다. 이 화면이 관측 열에서\n * 내내 지켜 온 구분(§observedAbsence)을 카드에서도 지킨다.\n */\n ...(Array.isArray(bucket) ? (bucket.length ? {} : { emptyBecause: 'none-yet' }) : { emptyBecause: 'not-observed' }),\n basis: folded ? 'journal' : 'state',\n observing,\n /* 선언이 아니라 관측인 축 — 화면이 \"원본이 침묵했다\" 로 읽지 않게 사실로 말한다. */\n observedOnly: true,\n standardUndeclared: !a.standardClass?.isa95 && !a.standardClass?.epcis && !a.standardClass?.iso55000\n }\n }\n return {\n axis: a.axis,\n label: a.label,\n kind: a.kind,\n container: !Array.isArray(raw) && raw != null,\n standardClass: a.standardClass,\n typeRole: a.typeRole ?? null,\n count: entity ? inRows : inDocument,\n /*\n * 비었으면 왜 비었는지 — 선언 쪽은 세 갈래다.\n * · `not-declared` 이 트윈의 모델이 그 축을 아예 말하지 않는다(대개 템플릿이 안 적었다)\n * · `declared-empty` 선언은 있는데 목록이 비어 있다(적었다가 지운 것도 사실이다)\n * · `not-projected` 문서엔 있는데 행에 없다 — 투영을 거치지 않은 트윈이다(`stale` 과 같은 사실)\n *\n * 셋을 \"0\" 하나로 합치면 사용자가 손볼 곳을 찾지 못한다: 첫째는 템플릿, 둘째는 원본,\n * 셋째는 다시 읽기다. 셋 다 다른 일이다.\n */\n ...((entity ? inRows : inDocument)\n ? {}\n : {\n emptyBecause:\n entity && inDocument > 0 ? 'not-projected' : raw == null ? 'not-declared' : 'declared-empty'\n }),\n /* 'rows' = DB 가 셌다(질의 가능) · 'document' = 문서를 세었다(아직 이주 전). */\n basis: entity ? 'rows' : 'document',\n ...(entity ? { documentCount: inDocument, stale } : {}),\n /* 표준 투영이 없는 축은 **숨기지 않고** 그 사실을 낸다(ADR-0031). */\n standardUndeclared: !a.standardClass?.isa95 && !a.standardClass?.epcis && !a.standardClass?.iso55000\n }\n })\n )\n\n /*\n * **원본이 모르는데 실행 중인 것** — 커널은 마스터에 없던 자원도 이벤트로 알게 되고\n * `origin: 'observed'` 를 단다(`observed-reducer`). 그것을 표에 밀어 넣지 않는다 — 그러면\n * 틱 루프에서 DB 로 쓰는 **두 번째 쓰기 경로**가 생기고, 실패하면 조용히 어긋난다\n * (쓰기 경로는 인제스트 하나, ADR-0032-D).\n *\n * 대신 **여기서 비교해 낸다.** 이 차이는 숨겨야 할 불일치가 아니라 인스펙션이 답해야 할\n * 질문이다 — \"원본이 말한 적 없는 설비가 실제로 실행 중이다.\"\n *\n * 트윈이 안 실행 중이면 비교할 상대가 없다 — 그때는 `null` 이다(0 이 아니다. 없는 것과\n * 세어 보지 못한 것은 다르다).\n */\n const snap = TwinEngine.snapshot(domainId, instanceId)\n const observedOnly = snap\n ? {\n equipment: (snap.equipment ?? []).filter((e: any) => e?.origin === 'observed').map((e: any) => e.id),\n locations: (snap.locations ?? []).filter((l: any) => l?.origin === 'observed').map((l: any) => l.id),\n persons: (snap.persons ?? []).filter((p: any) => p?.origin === 'observed').map((p: any) => p.id)\n }\n : null\n\n /* 지도의 선 — 선언된 관계 중 **양끝이 이 트윈에 실재하는 것만**. 없는 선을 그리지 않는다. */\n /* 선의 양끝 판정은 **문서든 행이든 있으면 있는 것**으로 본다 — 투영이 낡았다고 관계가 없어지진 않는다. */\n const nonEmpty = new Set(concepts.filter(c => (c.count ?? 0) > 0 || ((c as any).documentCount ?? 0) > 0).map(c => c.axis))\n /* 선은 **얼마나 이어졌고 얼마나 끊겼는지**까지 말한다 — 관계에서 가장 중요한 정보는 대개 부재다. */\n const edges = await Promise.all(\n TWIN_RELATIONS.filter((r: any) => r.target.kind === 'axis' && nonEmpty.has(r.from) && nonEmpty.has(r.target.axis)).map(\n async (r: any) => ({ from: r.from, to: r.target.axis, via: r.via, optional: !!r.optional, ...this.edgeFill(model, r) })\n )\n )\n\n return {\n instanceId,\n /* 사람이 부르는 이름 — 결선 줄이 업무키만 보이지 않게(업무키는 곁에 남는다). */\n name: inst.name ?? null,\n /*\n * **생산 선언에서 갈 곳이 없는 두 값** — GS1 발행자와 대표 산출물.\n *\n * 나머지 선언(자재·레시피·라우트·공정)은 각자 개념 축이 있고, 자재의 GS1 참조는 자재가 보인다.\n * 이 둘만 자기 축이 없다: 회사 접두사는 이 트윈의 **정체**(발행자)이고, 대표 레시피는 이 공장이\n * 무엇을 만드는 곳인가다. 둘 다 결선 줄(어느 원본을 반영하나)과 같은 성격이라 그 줄에 둔다.\n */\n /*\n * **표준 적합성** — 채운 것과 빠진 것을 함께.\n *\n * 이 화면은 \"이 트윈이 표준 모델의 어디를 채우고 있나\" 를 묻는다. 그런데 지금까지 답은 우리\n * 축만 세었다 — 없는 것은 화면에 없으므로 아무도 묻지 않았다. 기준(표준의 목록)을 함께 내면\n * 그 질문이 실제로 답을 갖는다(`isa95-coverage.ts` 의 머리말 참조).\n */\n /* 종류가 기대는 표준을 **모두** 낸다 — 하나만 말하면 그 트윈의 다른 기준이 보이지 않는다. */\n standards: standardsForKind(inst.kind).map(id => COVERAGE[id]((TWIN_AXES as any[]).map(a => a.axis))),\n production: (() => {\n const spec: any = (inst.model as any)?.productionSpec\n if (!spec) return null\n const key = spec.recipeKey ?? null\n const material = (spec.definition?.materials ?? []).find((m: any) => (m.key ?? m.id) === key)\n return {\n companyPrefix: spec.companyPrefix ?? null,\n recipeKey: key,\n /* 이름은 자재 선언이 말한다(i18n 키일 수 있으므로 번역은 화면의 일). */\n recipeLabel: material?.label ?? material?.name ?? null\n }\n })(),\n description: inst.description ?? null,\n kind: inst.kind,\n restartPolicy: inst.restartPolicy,\n source: ref\n ? { referenceId: ref.source, adapterType: ref.adapterType, status: ref.status, lastSyncedAt: ref.lastSyncedAt }\n : null,\n /*\n * **다시 읽을 수 있는가** — 이 트윈이 자기 원본을 기억하고 있나(`TwinInstance.origin`).\n *\n * 없는 트윈이 실제로 있다(이 자리가 생기기 전에 만들어진 것). 그때 화면이 버튼을 그대로\n * 내면 누를 때마다 실패하고, 사용자는 무엇이 잘못됐는지 알 수 없다. 그래서 **할 수 있는지를\n * 먼저 답한다** — 못 하는 것을 못 한다고 말하는 것도 이 화면의 일이다.\n */\n origin: inst.origin ?? null,\n canResync: !!(inst.origin as any)?.kind,\n concepts,\n edges,\n /* 원본이 모르는데 관측에는 있는 것. `null` = 트윈이 실행 중이 아니어서 비교하지 못했다. */\n observedOnly,\n /*\n * 축 셋만 행이고 나머지는 문서에서 셌다는 사실. 축마다 `basis` 가 있지만 여기서도 한 번 말한다 —\n * 화면이 \"이 숫자는 무엇으로 셌나\" 를 한눈에 보여야 규모 한계가 드러난다(ADR-0032 이주 압력).\n */\n basis: {\n rows: Object.keys(ROW_BACKED),\n document: TWIN_AXES.filter((a: any) => !ROW_BACKED[a.axis]).map((a: any) => a.axis),\n /* 투영이 낡은 축 — 있으면 이 트윈은 인제스트를 한 번 더 태워야 한다. */\n stale: concepts.filter((c: any) => c.stale).map((c: any) => c.axis)\n }\n }\n }\n /**\n * **이 트윈에 무엇을 선언할 수 있나** — 값을 넣는 화면이 도메인의 모양을 알지 않게.\n *\n * 커널이 정한 명세(속성 id·단위·범위·설명)를 그대로 낸다. 화면이 「에너지 트윈이면 계약전력·요금\n * 단가…」를 알고 있으면 도메인이 늘 때마다 화면을 고쳐야 하고 단위가 두 곳에 적힌다(ADR-0031).\n *\n * 지금 무엇이 선언돼 있는지도 함께 낸다(`declared`) — 화면이 두 번 묻지 않게, 그리고 「원천이 말한\n * 값」과 「우리가 정한 값」을 한자리에서 견줄 수 있게(`PropertySource`, ADR-0034).\n *\n * 어휘가 없는 종류는 `properties: []` 다 — 「아직 없다」와 「아무 값이나 받는다」는 다르다.\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n \"What can be declared on this twin and what already is: the kernel's property specification (id, unit, range, note) plus the current local declarations at both scopes — this twin and its facility — each with the value it overrode. Empty properties mean this kind has no declaration vocabulary yet — not that anything goes.\"\n })\n async twinDeclarations(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return null\n const model = (inst.model ?? {}) as any\n /*\n * 겹이 둘이다 — **현장의 것과 이 트윈의 것**. 화면이 둘을 함께 봐야 「이 값이 어느 범위의 것인가」를\n * 말할 수 있고, 철회 버튼이 어느 겹을 겨눌지 고를 수 있다(요금을 현장에 넣었는데 트윈에서 철회하려\n * 하면 「겹에 없다」로 끝난다 — 사용자는 이유를 알 수 없다).\n */\n const space = inst.spaceId\n ? await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId: inst.spaceId } })\n : null\n /*\n * 돌고 있으면 커널이 「무엇으로 돌고 있나」를 스스로 답한다. 그 보고는 **작업이 새로 생길 때** 채워지\n * 므로 재기동 직후에는 비는데, 이력에서 배운 종류는 그때도 남아 있다(실측으로 이 빈칸에 부딪혔다).\n */\n const operations = operationBasis(\n model,\n TwinEngine.kernel(domainId, instanceId),\n await TwinEngine.measuredOperationKinds(domainId, instanceId)\n )\n return {\n instanceId,\n kind: inst.kind,\n spaceId: inst.spaceId,\n spaceName: space?.name,\n properties: declarablePropertySpecs(inst.kind),\n declared: (inst.localDeclarations ?? []) as any[],\n declaredAtSpace: (space?.localDeclarations ?? []) as any[],\n /* 어디에 얹을 수 있나 — 모델의 자리·설비·공정. 화면이 대상을 지어내지 않게 목록으로 준다. */\n targets: [\n ...(model.locations ?? []).map((l: any) => ({ target: 'location', id: l.id, name: l.name, type: l.type, properties: l.properties ?? [] })),\n ...(readBoardEquipment(model) ?? []).map((e: any) => ({ target: 'equipment', id: e.id, name: e.name, type: e.type ?? e.kind, properties: e.properties ?? [] })),\n /*\n * 공정은 **자원이 아니다** — 값은 종류에 붙고, 그 종류에 명세 행이 없어도 된다(창고·야드에는 행이\n * 0개다). 그래서 대상 목록을 커널의 자기보고까지 합쳐 만든다(`operationBasis`).\n *\n * 지금 값은 **겹에서** 든다(사용자가 넣은 수와 단위 그대로). 모델에는 ISO 8601 로 구워지지만\n * 화면에 「PT240S」를 보이면 자기가 넣은 값인지 알아볼 수 없다.\n */\n ...operations.map(op => ({\n target: 'operation',\n id: op.kind,\n name: op.label,\n properties: declaredDurationProperty(op.kind, inst.localDeclarations as any[], space?.localDeclarations as any[])\n }))\n ],\n /*\n * 이 트윈이 돌리는 공정과 **그 시간이 어디서 오나** — 선언 화면이 「무엇이 상수로 도나」를 보여야\n * 사람이 채울 곳을 안다. `basis` 가 비면 아직 돌지 않은 것이다(공정이 없는 것이 아니다).\n */\n operations\n }\n }\n /**\n * **이 현장에 무엇을 선언할 수 있나** — 현장 값은 트윈의 것이 아니므로 트윈 없이 묻는다.\n *\n * ── 왜 트윈으로 묻지 않나 (2026-08-18) ─────────────────────────────────────\n * 처음에는 트윈 화면에서 범위만 골라 현장 값을 넣게 했다. 그런데 **현장 값을 넣으려고 트윈을 골라야\n * 하는 것**은 개념이 거꾸로다 — 트윈은 그 값을 읽는 쪽이다(사용자가 그 어긋남을 지적했다).\n *\n * 어휘와 대상은 **그 현장 트윈들의 합집합**이다: 한 현장에 여러 종류가 살고(에너지·창고·야드), 요금은\n * 에너지 트윈만 읽는다. 합집합으로 내되 **어느 트윈이 그 대상을 가졌는지** 함께 낸다 — 아무 트윈도\n * 읽지 않는 값을 넣는 일을 화면이 미리 보여 줄 수 있어야 한다.\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'What can be declared for a whole facility (space) and what already is: the union of the property specifications of the twin kinds living there, the union of their declarable targets (with which twins carry each), and the current facility-scope declarations.'\n })\n async spaceDeclarations(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n const domainId = context.state.domain.id\n const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })\n if (!space) return null\n const twins = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, spaceId } })\n\n /* 어휘 합집합 — 같은 속성이 여러 종류에 있어도 한 번만 낸다(명세는 커널 하나에서 온다). */\n const byId = new Map<string, any>()\n for (const t of twins) for (const spec of declarablePropertySpecs(t.kind)) if (!byId.has(spec.id)) byId.set(spec.id, spec)\n\n /* 대상 합집합 — 같은 id 가 여러 트윈에 있으면 **그 트윈들을 함께** 든다(현장 값은 그 전부에 얹힌다). */\n const targets = new Map<string, { target: string; id: string; name?: string; type?: string; twins: string[] }>()\n for (const t of twins) {\n const model = (t.model ?? {}) as any\n const add = (target: 'location' | 'equipment' | 'operation', e: any) => {\n const key = `${target}:${e.id}`\n const cur = targets.get(key)\n if (cur) { cur.twins.push(t.instanceId); return }\n targets.set(key, { target, id: e.id, name: e.name, type: e.type ?? e.kind, twins: [t.instanceId] })\n }\n for (const l of model.locations ?? []) add('location', l)\n for (const e of readBoardEquipment(model) ?? []) add('equipment', e)\n /*\n * 공정도 대상이다 — 「우리 현장의 적재는 4분」은 그 현장 창고 전부가 읽어야 하는 수다. 목록은 각\n * 트윈의 것을 합집합으로 든다(돌고 있으면 커널이 스스로 답한다).\n */\n for (const op of operationBasis(\n model,\n TwinEngine.kernel(domainId, t.instanceId),\n await TwinEngine.measuredOperationKinds(domainId, t.instanceId)\n ))\n add('operation', { id: op.kind, name: op.label })\n }\n\n return {\n spaceId,\n spaceName: space.name,\n properties: [...byId.values()],\n declared: (space.localDeclarations ?? []) as any[],\n targets: [...targets.values()],\n /* 이 현장에 사는 트윈들 — 화면이 「누가 이 값을 읽나」를 말할 수 있게. */\n twins: twins.map(t => ({ instanceId: t.instanceId, kind: t.kind, name: t.name }))\n }\n }\n}\n"]}
|
|
@@ -115,7 +115,7 @@ tslib_1.__decorate([
|
|
|
115
115
|
tslib_1.__metadata("design:type", String)
|
|
116
116
|
], TwinOperation.prototype, "sourceRef", void 0);
|
|
117
117
|
tslib_1.__decorate([
|
|
118
|
-
(0, typeorm_1.Column)(
|
|
118
|
+
(0, typeorm_1.Column)(),
|
|
119
119
|
(0, type_graphql_1.Field)({ description: 'When this row was projected from the source.' }),
|
|
120
120
|
tslib_1.__metadata("design:type", Date)
|
|
121
121
|
], TwinOperation.prototype, "ingestedAt", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twin-operation.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-operation.ts"],"names":[],"mappings":";;;;AAAA,qCAAkI;AAClI,+CAAyD;AAEzD,iDAA4D;AAE5D,wEAAgE;AAEhE;;;;;;;;;;;GAWG;AAII,IAAM,aAAa,GAAnB,MAAM,aAAa;CA+HzB,CAAA;AA/HY,sCAAa;AAGf;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,CAAC;;yCACC;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;sCACvE,cAAM;6CAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;;+CAC1B;AAIjB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,+BAAY,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACxD,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,+BAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;sCAC3E,+BAAY;+CAAA;AAGvB;IADC,IAAA,oBAAU,EAAC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;;oDACvB;AAKtB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gGAAgG,EAAE,CAAC;;mDACrG;AAepB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0GAA0G,EAAE,CAAC;;2CACtI;AAIb;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;;kDACvE;AAKpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;;6CACtE;AAQf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mFAAmF,EAAE,CAAC;;mDACvG;AAIrB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6EAA6E,EAAE,CAAC;;mDACjG;AAUrB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gIAAgI,EAAE,CAAC;;+CACxJ;AAQjB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0EAA0E,EAAE,CAAC;;kDACxH;AAIjB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;;iDACrH;AAgBhB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sIAAsI,EAAE,CAAC;;6DACzK;AAI5B;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kKAAkK,EAAE,CAAC;;iEACjM;AAIhC;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4JAA4J,EAAE,CAAC;;4DAChM;AAK3B;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;;0CACpG;AAKZ;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,+DAA+D,EAAE,CAAC;;gDACvE;AAIjB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC5B,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;sCAC3D,IAAI;iDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;gDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;gDAAA;wBA9HL,aAAa;IAHzB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;IACnC,IAAA,eAAK,EAAC,qBAAqB,EAAE,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5G,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,wIAAwI,EAAE,CAAC;GACzJ,aAAa,CA+HzB","sourcesContent":["import { CreateDateColumn, Entity, Index, Column, RelationId, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectType, Field, ID, Int } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\n\n/*\n * TwinOperation — 이 트윈이 반영하는 **공정 명세**. ISA-95 `OperationsSegment`.\n * 이 현장에서 각 작업이 **어디서·무엇으로·얼마나 걸리나**(선언값).\n *\n * ── 왜 이 표가 인스펙션의 중심인가 ──────────────────────────────────────────\n * 인스펙션 화면의 존재 이유는 `선언 | 관측` 두 열이고(`plans/twin-inspection.md` §2.4),\n * 그 첫 열이 여기서 온다. 둘째 열은 저널에서 접힌다(`kpi-fold.workTime`).\n * 실적은 보이는데 기준이 안 보이면 \"절단이 5분 걸렸다\" 를 보고도 빠른지 느린지 알 수 없다.\n *\n * 자리·설비와 같은 규율 — **원본에서 온 캐시**, `sourceRef`·`ingestedAt` 필수,\n * 쓰기 경로는 인제스트 하나(`twin-location.ts` 머리말).\n */\n@Entity({ name: 'twin_operations' })\n@Index('ix_twin_operation_0', (e: TwinOperation) => [e.domain, e.instance, e.operationKey], { unique: true })\n@ObjectType({ description: 'An operation spec reflected into the twin (ISA-95 OperationsSegment): where it runs, with what, and how long the source says it takes.' })\nexport class TwinOperation {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Owning tenant domain.' })\n domain?: Domain\n\n @RelationId((e: TwinOperation) => e.domain)\n domainId?: string\n\n @ManyToOne(type => TwinInstance, { onDelete: 'CASCADE' })\n @Field(type => TwinInstance, { nullable: true, description: 'Owning twin instance.' })\n instance?: TwinInstance\n\n @RelationId((e: TwinOperation) => e.instance)\n instanceRowId?: string\n\n /** 커널 계약의 `OperationDef.key` 그대로. 라우트의 단계가 이 키로 가리킨다. */\n @Column()\n @Field({ description: 'Operation key as declared by the source (kernel OperationDef.key). Route steps reference this.' })\n operationKey: string\n\n /**\n * **사람이 부르는 이름** — 프레임워크 엔티티 규약의 `name`/`description`.\n *\n * ── 왜 이것이 빠져 있었고, 무엇이 망가졌나 ────────────────────────────────────\n * 투영이 마스터의 `name` 을 버리고 있었다. 그래서 행에는 id 만 남고, 화면은 이름을 만들 재료가\n * 없어 `cut-station` 을 그대로 찍었다 — 그 결함이 목록·표·관계 그림마다 따로 나타나서 표면을\n * 하나씩 고치게 만들었다. 표면의 문제가 아니라 **행에 이름이 없던 문제**다.\n *\n * 원본이 이름을 말하지 않으면 비운다(id 를 이름으로 베끼지 않는다 — 그러면 \"이름이 있다\" 는\n * 거짓이 되고, 화면이 이름 없음을 알아볼 수 없게 된다).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Human name as declared by the source. Empty when the source does not name it (never copied from the id).' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Longer description when the source provides one.' })\n description?: string\n\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"Intent: 'transport' | 'process' | 'dwell'.\" })\n intent?: string\n\n /*\n * 어디서·무엇으로 — 자리·설비의 **항목이 아니라 타입**을 가리킨다(\"절단은 cut-station 에서 수행된다\").\n * 그래서 FK 가 아니다. 화면은 이것을 \"그 타입의 자리·설비 목록\" 으로 푼다(관계 선언의 `type` 대상).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Location type this operation runs at (a catalog type key, not an item reference).' })\n locationType?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Resource type that performs it (a catalog type key, not an item reference).' })\n resourceType?: string\n\n /**\n * 소요시간 — **ISO 8601 기간 문자열이 정본**(`PT1H10M`).\n *\n * 분으로 저장하면 `PT1H10M30S` 의 초가 사라지고 그 손실은 조용하다. 사람이 읽을 분은\n * 화면이 곁들여 보여 주고, 못 읽는 값은 `—` 로 낸다(0 으로 채우면 \"즉시 끝나는 공정\" 이라는 거짓).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Declared duration as an ISO 8601 period (e.g. PT1H10M). The string is canonical — storing minutes would silently drop seconds.' })\n duration?: string\n\n /**\n * 변동성·파라미터(수율·셋업)는 형이 도메인마다 달라 **원형 그대로** 싣는다.\n * 여기를 컬럼으로 펴려면 커널이 그 형을 먼저 고정해야 한다 — 아직 아니다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Declared variability (distribution/min/max/mode) as given by the source.' })\n variability?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Declared parameters (yield, setup duration…) as given by the source.' })\n parameters?: any\n\n /**\n * **네 자원 명세 — 선언한 것만 제약이 된다.**\n *\n * ISA-95 는 한 공정이 무엇을 필요로 하는지를 넷으로 말한다: 설비(`resourceType`)·인원·자산·자재.\n * 커널도 그 넷을 그대로 본다 — 선언이 있으면 잡아야 작업이 시작되고, 없으면 그 자원을 보지 않는다.\n *\n * 이 셋이 투영에서 **떨어져 있었다.** 그래서 조회 화면의 \"원본이 말한 값\" 열에 넷 중 하나만\n * 나왔고, 사람이 필요한 공정을 보고도 **사람이 필요 없다고 결론 내게** 되어 있었다. 원본이 말한\n * 것을 화면이 말하지 않으면 그 화면은 원본을 반영하는 것이 아니다.\n *\n * 모양은 커널 계약 그대로 싣는다 — 인제스트 경계에서 모양을 바꾸면 그 자리에서 방언이 생긴다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Personnel this operation requires (ISA-95 PersonnelSpecification): class + quantity. Absent means the operation runs without people.' })\n personnelSpecification?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Physical assets this operation requires (ISA-95 PhysicalAssetSpecification). Absent means no asset constraint — an empty pallet is never the reason a job waits.' })\n physicalAssetSpecification?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Materials this operation consumes or produces (ISA-95 MaterialSpecification). Absent means material shortage never shows up in the forecast for this step.' })\n materialSpecification?: any\n\n /** 라우트 안에서의 순서 — 라우트가 이 공정을 몇 번째로 부르나(라우트 표가 서면 그리로 옮긴다). */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Sequence within its route, when the source declares one.' })\n seq?: number\n\n /* ── 캐시라는 사실을 구조로 (ADR-0032-D) ── */\n @Column()\n @Field({ description: 'Which reference (source system) this row came from. Required.' })\n sourceRef: string\n\n @Column({ type: 'datetime' })\n @Field({ description: 'When this row was projected from the source.' })\n ingestedAt: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n}\n"]}
|
|
1
|
+
{"version":3,"file":"twin-operation.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-operation.ts"],"names":[],"mappings":"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","sourcesContent":["import { CreateDateColumn, Entity, Index, Column, RelationId, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectType, Field, ID, Int } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\n\n/*\n * TwinOperation — 이 트윈이 반영하는 **공정 명세**. ISA-95 `OperationsSegment`.\n * 이 현장에서 각 작업이 **어디서·무엇으로·얼마나 걸리나**(선언값).\n *\n * ── 왜 이 표가 인스펙션의 중심인가 ──────────────────────────────────────────\n * 인스펙션 화면의 존재 이유는 `선언 | 관측` 두 열이고(`plans/twin-inspection.md` §2.4),\n * 그 첫 열이 여기서 온다. 둘째 열은 저널에서 접힌다(`kpi-fold.workTime`).\n * 실적은 보이는데 기준이 안 보이면 \"절단이 5분 걸렸다\" 를 보고도 빠른지 느린지 알 수 없다.\n *\n * 자리·설비와 같은 규율 — **원본에서 온 캐시**, `sourceRef`·`ingestedAt` 필수,\n * 쓰기 경로는 인제스트 하나(`twin-location.ts` 머리말).\n */\n@Entity({ name: 'twin_operations' })\n@Index('ix_twin_operation_0', (e: TwinOperation) => [e.domain, e.instance, e.operationKey], { unique: true })\n@ObjectType({ description: 'An operation spec reflected into the twin (ISA-95 OperationsSegment): where it runs, with what, and how long the source says it takes.' })\nexport class TwinOperation {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Owning tenant domain.' })\n domain?: Domain\n\n @RelationId((e: TwinOperation) => e.domain)\n domainId?: string\n\n @ManyToOne(type => TwinInstance, { onDelete: 'CASCADE' })\n @Field(type => TwinInstance, { nullable: true, description: 'Owning twin instance.' })\n instance?: TwinInstance\n\n @RelationId((e: TwinOperation) => e.instance)\n instanceRowId?: string\n\n /** 커널 계약의 `OperationDef.key` 그대로. 라우트의 단계가 이 키로 가리킨다. */\n @Column()\n @Field({ description: 'Operation key as declared by the source (kernel OperationDef.key). Route steps reference this.' })\n operationKey: string\n\n /**\n * **사람이 부르는 이름** — 프레임워크 엔티티 규약의 `name`/`description`.\n *\n * ── 왜 이것이 빠져 있었고, 무엇이 망가졌나 ────────────────────────────────────\n * 투영이 마스터의 `name` 을 버리고 있었다. 그래서 행에는 id 만 남고, 화면은 이름을 만들 재료가\n * 없어 `cut-station` 을 그대로 찍었다 — 그 결함이 목록·표·관계 그림마다 따로 나타나서 표면을\n * 하나씩 고치게 만들었다. 표면의 문제가 아니라 **행에 이름이 없던 문제**다.\n *\n * 원본이 이름을 말하지 않으면 비운다(id 를 이름으로 베끼지 않는다 — 그러면 \"이름이 있다\" 는\n * 거짓이 되고, 화면이 이름 없음을 알아볼 수 없게 된다).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Human name as declared by the source. Empty when the source does not name it (never copied from the id).' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Longer description when the source provides one.' })\n description?: string\n\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"Intent: 'transport' | 'process' | 'dwell'.\" })\n intent?: string\n\n /*\n * 어디서·무엇으로 — 자리·설비의 **항목이 아니라 타입**을 가리킨다(\"절단은 cut-station 에서 수행된다\").\n * 그래서 FK 가 아니다. 화면은 이것을 \"그 타입의 자리·설비 목록\" 으로 푼다(관계 선언의 `type` 대상).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Location type this operation runs at (a catalog type key, not an item reference).' })\n locationType?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Resource type that performs it (a catalog type key, not an item reference).' })\n resourceType?: string\n\n /**\n * 소요시간 — **ISO 8601 기간 문자열이 정본**(`PT1H10M`).\n *\n * 분으로 저장하면 `PT1H10M30S` 의 초가 사라지고 그 손실은 조용하다. 사람이 읽을 분은\n * 화면이 곁들여 보여 주고, 못 읽는 값은 `—` 로 낸다(0 으로 채우면 \"즉시 끝나는 공정\" 이라는 거짓).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Declared duration as an ISO 8601 period (e.g. PT1H10M). The string is canonical — storing minutes would silently drop seconds.' })\n duration?: string\n\n /**\n * 변동성·파라미터(수율·셋업)는 형이 도메인마다 달라 **원형 그대로** 싣는다.\n * 여기를 컬럼으로 펴려면 커널이 그 형을 먼저 고정해야 한다 — 아직 아니다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Declared variability (distribution/min/max/mode) as given by the source.' })\n variability?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Declared parameters (yield, setup duration…) as given by the source.' })\n parameters?: any\n\n /**\n * **네 자원 명세 — 선언한 것만 제약이 된다.**\n *\n * ISA-95 는 한 공정이 무엇을 필요로 하는지를 넷으로 말한다: 설비(`resourceType`)·인원·자산·자재.\n * 커널도 그 넷을 그대로 본다 — 선언이 있으면 잡아야 작업이 시작되고, 없으면 그 자원을 보지 않는다.\n *\n * 이 셋이 투영에서 **떨어져 있었다.** 그래서 조회 화면의 \"원본이 말한 값\" 열에 넷 중 하나만\n * 나왔고, 사람이 필요한 공정을 보고도 **사람이 필요 없다고 결론 내게** 되어 있었다. 원본이 말한\n * 것을 화면이 말하지 않으면 그 화면은 원본을 반영하는 것이 아니다.\n *\n * 모양은 커널 계약 그대로 싣는다 — 인제스트 경계에서 모양을 바꾸면 그 자리에서 방언이 생긴다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Personnel this operation requires (ISA-95 PersonnelSpecification): class + quantity. Absent means the operation runs without people.' })\n personnelSpecification?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Physical assets this operation requires (ISA-95 PhysicalAssetSpecification). Absent means no asset constraint — an empty pallet is never the reason a job waits.' })\n physicalAssetSpecification?: any\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Materials this operation consumes or produces (ISA-95 MaterialSpecification). Absent means material shortage never shows up in the forecast for this step.' })\n materialSpecification?: any\n\n /** 라우트 안에서의 순서 — 라우트가 이 공정을 몇 번째로 부르나(라우트 표가 서면 그리로 옮긴다). */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Sequence within its route, when the source declares one.' })\n seq?: number\n\n /* ── 캐시라는 사실을 구조로 (ADR-0032-D) ── */\n @Column()\n @Field({ description: 'Which reference (source system) this row came from. Required.' })\n sourceRef: string\n\n /*\n * **타입 이름을 적지 않는다** (2026-08-19 Postgres 부팅 실패로 고침).\n *\n * `datetime` 은 sqlite·MySQL 의 이름이고 Postgres 는 `timestamp` 만 안다 — 거꾸로 `timestamp` 는\n * sqlite 가 모른다. **다섯 드라이버에 다 통하는 이름이 없다.** 그래서 이름 고르는 일을 TypeORM 에\n * 맡긴다(`emitDecoratorMetadata` 가 켜져 있어 `Date` 에서 드라이버별 타입을 고른다).\n */\n @Column()\n @Field({ description: 'When this row was projected from the source.' })\n ingestedAt: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/headless-twin",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.12",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"things-factory": true,
|
|
6
6
|
"author": "heartyoh <heartyoh@hatiolab.com>",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"clean:shared": "rm -rf dist-shared tsconfig.shared.tsbuildinfo"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@operato/twin-kernel": "^0.7.
|
|
30
|
+
"@operato/twin-kernel": "^0.7.38",
|
|
31
31
|
"@things-factory/auth-base": "^10.0.10",
|
|
32
32
|
"@things-factory/cache-service": "^10.0.10",
|
|
33
33
|
"@things-factory/env": "^10.0.8",
|
|
34
34
|
"@things-factory/shell": "^10.0.10"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "3cc678c07e9031a820662d43410f93b796ca012f"
|
|
37
37
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { ingest, ingestEnergyRecords, ingestEnergyEquipmentRecords,
|
|
2
|
-
isEnergyEquipmentRecord, type AdapterRule, type EnergyRecord,
|
|
3
|
-
type EnergyEquipmentRecord, type IngestResult } from '@operato/twin-kernel'
|
|
1
|
+
import { ingest, ingestEnergyRecords, ingestEnergyEquipmentRecords, ingestOperationalRecords, isAggregationRecord,
|
|
2
|
+
isEnergyRecord, isEnergyEquipmentRecord, isOperationalRecord, type AdapterRule, type EnergyRecord,
|
|
3
|
+
type EnergyEquipmentRecord, type OperationalRecord, type IngestResult } from '@operato/twin-kernel'
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
* 정규 레코드 → 커널 검증 ACL → CanonicalEnvelope (P1 seam 코어).
|
|
7
7
|
* 순수(커널만 의존; shell·integration-base 무의존 → node:test 검증 가능).
|
|
8
8
|
* 매핑(레거시 스키마 → 정규)은 이 앞 단계(jsonata step 또는 커넥터 openLiveFeed)가 끝낸다.
|
|
9
|
-
* 여기 도착하는 레코드는 이미 정규
|
|
9
|
+
* 여기 도착하는 레코드는 이미 정규 필드다 — 어휘마다 자기 필드 이름이 계약이다:
|
|
10
|
+
* EPCIS 품목 { epc, action, bizStep, disposition?, readPoint?, bizLocation? }
|
|
11
|
+
* 에너지 계량 { meterId, kW?, kWh?, … } · 설비 에너지 { equipmentId, generatedKW?, soc?, … }
|
|
12
|
+
* 담김(적재) { parentID, childEPCs[] | childQuantityList[], action, bizStep, … }
|
|
13
|
+
* 운영 사실 { taskId | moverId | personId | assetId | orderId, status, … } // vocabulary-guard: allow 저널 와이어 필드
|
|
10
14
|
* 커널은 objectEvent 구성 + EPCIS 검증(위반=오염 차단) + 봉투 래핑만(방언 없음).
|
|
11
15
|
*
|
|
12
16
|
* 두 소비처가 이 단일 코어를 공유(중복 금지):
|
|
@@ -15,7 +19,7 @@ import { ingest, ingestEnergyRecords, ingestEnergyEquipmentRecords, isEnergyReco
|
|
|
15
19
|
* SoT: design/integration/face2-adapters.md §5(seam)·§7(매핑=밖, 검증=커널).
|
|
16
20
|
*/
|
|
17
21
|
|
|
18
|
-
/** 고정 정규 매핑(방언 아님 — 필드명 자체가 정규 계약). sourceType
|
|
22
|
+
/** 고정 정규 매핑(방언 아님 — 필드명 자체가 정규 계약). `sourceType` 으로 어느 품목 사실인지 고른다. */
|
|
19
23
|
const TWIN_INGEST_RULES: AdapterRule[] = [
|
|
20
24
|
{
|
|
21
25
|
sourceType: 'twin',
|
|
@@ -28,9 +32,30 @@ const TWIN_INGEST_RULES: AdapterRule[] = [
|
|
|
28
32
|
readPoint: '$.readPoint',
|
|
29
33
|
bizLocation: '$.bizLocation'
|
|
30
34
|
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
/*
|
|
38
|
+
* **담김의 사실** — 무엇이 무엇에 실렸나(팔레트·상자). 오래 비어 있던 자리다: 관측 리듀서는 이 사실을
|
|
39
|
+
* 받으면 자식 물품에 `parent` 를 붙이는데, 문이 `ObjectEvent` 하나만 알아서 원본은 적재를 말할 길이
|
|
40
|
+
* 없었다(시뮬 원본을 붙여 돌려 보고서야 로그로 드러났다).
|
|
41
|
+
*/
|
|
42
|
+
sourceType: 'twin-aggregation',
|
|
43
|
+
mapping: {
|
|
44
|
+
type: 'AggregationEvent',
|
|
45
|
+
action: '$.action',
|
|
46
|
+
bizStep: '$.bizStep',
|
|
47
|
+
disposition: '$.disposition',
|
|
48
|
+
parentID: '$.parentID',
|
|
49
|
+
childEPCs: '$.childEPCs',
|
|
50
|
+
/* 낱개 식별자 없이 **수량으로** 담긴 자식(「이 품번 40개」) — 입고·포장이 이 모양으로 온다. */
|
|
51
|
+
childQuantityList: '$.childQuantityList',
|
|
52
|
+
readPoint: '$.readPoint',
|
|
53
|
+
bizLocation: '$.bizLocation'
|
|
54
|
+
}
|
|
31
55
|
}
|
|
32
56
|
]
|
|
33
57
|
|
|
58
|
+
/** 개체 관측 — 물건 하나가 어디서 무엇이 됐나. */
|
|
34
59
|
export interface CanonicalRecord {
|
|
35
60
|
epc: string
|
|
36
61
|
action: string // ADD | OBSERVE | DELETE
|
|
@@ -40,13 +65,34 @@ export interface CanonicalRecord {
|
|
|
40
65
|
bizLocation?: string
|
|
41
66
|
}
|
|
42
67
|
|
|
68
|
+
/**
|
|
69
|
+
* 담김 관측 — **부모와 자식**으로 말한다. 자리 필드는 개체 관측과 같은 이름이다(문자열).
|
|
70
|
+
*
|
|
71
|
+
* 이름은 표준의 것을 그대로 쓴다(`parentID`·`childEPCs`) — 여기서 다시 지으면 매핑이 번역이 되고,
|
|
72
|
+
* 같은 사실에 두 어휘가 생긴다.
|
|
73
|
+
*/
|
|
74
|
+
export interface CanonicalAggregationRecord {
|
|
75
|
+
parentID: string
|
|
76
|
+
/** 개체로 담긴 자식. 수량으로만 담겼으면 없다(둘 중 하나는 있어야 한다). */
|
|
77
|
+
childEPCs?: string[]
|
|
78
|
+
/** 수량으로 담긴 자식 — `{ epcClass, quantity, uom? }`. 값의 옳고 그름은 커널 검증이 본다. */
|
|
79
|
+
childQuantityList?: { epcClass: string; quantity?: number; uom?: string }[]
|
|
80
|
+
action: string // ADD(담다) | DELETE(풀다) | OBSERVE
|
|
81
|
+
bizStep: string
|
|
82
|
+
disposition?: string
|
|
83
|
+
readPoint?: string
|
|
84
|
+
bizLocation?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
43
87
|
/** 정규 레코드[] → 검증된 CanonicalEnvelope[] (+ 거부분). 순수 함수. 단일→객체도 배열로 정규화. */
|
|
44
88
|
export function ingestCanonicalRecords(
|
|
45
89
|
records:
|
|
46
90
|
| CanonicalRecord
|
|
91
|
+
| CanonicalAggregationRecord
|
|
47
92
|
| EnergyRecord
|
|
48
93
|
| EnergyEquipmentRecord
|
|
49
|
-
|
|
|
94
|
+
| OperationalRecord
|
|
95
|
+
| (CanonicalRecord | CanonicalAggregationRecord | EnergyRecord | EnergyEquipmentRecord | OperationalRecord)[]
|
|
50
96
|
| undefined
|
|
51
97
|
| null,
|
|
52
98
|
tenantId: string,
|
|
@@ -54,29 +100,47 @@ export function ingestCanonicalRecords(
|
|
|
54
100
|
): IngestResult {
|
|
55
101
|
const arr = Array.isArray(records) ? records : records ? [records] : []
|
|
56
102
|
/*
|
|
57
|
-
* **어휘가
|
|
58
|
-
* 그래서 계측을 이 길로 넣으면 `epc` 가 없어 거부되거나(그나마 나은 쪽)
|
|
103
|
+
* **어휘가 넷이다** — 물류(EPCIS)·에너지 계량·설비 에너지·운영 사실. 예전에는 이 함수가 무조건
|
|
104
|
+
* `ObjectEvent` 를 만들었고, 그래서 계측을 이 길로 넣으면 `epc` 가 없어 거부되거나(그나마 나은 쪽)
|
|
105
|
+
* 엉뚱한 물품 관측이 됐다.
|
|
106
|
+
*
|
|
107
|
+
* 물류 어휘 안에는 모양이 둘이다(개체 관측 `epc` / 담김 `parentID`+`childEPCs`).
|
|
59
108
|
*
|
|
60
|
-
* 판정은 커널이 한 곳에서 한다(`isEnergyRecord`) — 여기서 다시 짐작하면
|
|
61
|
-
* 봉투는 같은 것을 쓰므로 아래 소비처(미러·저널·방송)는 이 갈림을 알
|
|
109
|
+
* 판정은 커널이 한 곳에서 한다(`isEnergyRecord`·`isOperationalRecord`·`isAggregationRecord`) — 여기서 다시 짐작하면
|
|
110
|
+
* 소비처마다 답이 달라진다. 봉투는 같은 것을 쓰므로 아래 소비처(미러·저널·방송)는 이 갈림을 알
|
|
111
|
+
* 필요가 없다.
|
|
62
112
|
*/
|
|
63
113
|
const energy = arr.filter(isEnergyRecord) as EnergyRecord[]
|
|
64
114
|
/* 세 번째 어휘 — 설비가 낸 자기 에너지 상태(발전·저장·감축 여지·개폐 위치). 계량과 갈라 두는
|
|
65
115
|
이유는 커널에 적혀 있다: 계량은 구간에 누적되고, 이것들은 그 설비의 지금이다. */
|
|
66
116
|
const energyEquipment = arr.filter(isEnergyEquipmentRecord) as unknown as EnergyEquipmentRecord[]
|
|
67
|
-
|
|
117
|
+
/*
|
|
118
|
+
* 네 번째 어휘 — **운영 사실**(작업·설비·사람·자산·오더·품질). 관측 리듀서는 이것을 오래전부터
|
|
119
|
+
* 접었는데 **들어올 문이 없었다**: 원본이 「이 작업이 끝났다」를 말할 길이 없어 시뮬 커널만 그것을
|
|
120
|
+
* 아는 상태가 남았다(같은 화면이 두 구동에서 다른 것을 말한다).
|
|
121
|
+
*/
|
|
122
|
+
const operational = arr.filter(isOperationalRecord) as OperationalRecord[]
|
|
123
|
+
const epcis = arr.filter(r => !isEnergyRecord(r) && !isEnergyEquipmentRecord(r) && !isOperationalRecord(r)) as (CanonicalRecord | CanonicalAggregationRecord)[]
|
|
68
124
|
|
|
125
|
+
/* 품목 어휘 안에서도 갈림이 있다(개체 관측 / 담김) — 그 판정도 커널의 것을 부른다. */
|
|
69
126
|
const epcisResult = epcis.length
|
|
70
|
-
? ingest(
|
|
127
|
+
? ingest(
|
|
128
|
+
epcis.map(r => ({ ...r, sourceType: isAggregationRecord(r) ? 'twin-aggregation' : 'twin' })),
|
|
129
|
+
TWIN_INGEST_RULES,
|
|
130
|
+
{ tenantId, defaultEventTime }
|
|
131
|
+
)
|
|
71
132
|
: { accepted: [], rejected: [] }
|
|
72
133
|
const energyResult = energy.length ? ingestEnergyRecords(energy, { tenantId, defaultEventTime }) : { accepted: [], rejected: [] }
|
|
73
134
|
const equipmentResult = energyEquipment.length
|
|
74
135
|
? ingestEnergyEquipmentRecords(energyEquipment, { tenantId, defaultEventTime })
|
|
75
136
|
: { accepted: [], rejected: [] }
|
|
137
|
+
const operationalResult = operational.length
|
|
138
|
+
? ingestOperationalRecords(operational, { tenantId, defaultEventTime })
|
|
139
|
+
: { accepted: [], rejected: [] }
|
|
76
140
|
|
|
77
141
|
/* 거부분을 합쳐 돌려준다 — 어느 어휘에서 거부됐는지는 이유 문구가 말한다. */
|
|
78
142
|
return {
|
|
79
|
-
accepted: [...epcisResult.accepted, ...energyResult.accepted, ...equipmentResult.accepted],
|
|
80
|
-
rejected: [...epcisResult.rejected, ...energyResult.rejected, ...equipmentResult.rejected]
|
|
143
|
+
accepted: [...epcisResult.accepted, ...energyResult.accepted, ...equipmentResult.accepted, ...operationalResult.accepted],
|
|
144
|
+
rejected: [...epcisResult.rejected, ...energyResult.rejected, ...equipmentResult.rejected, ...operationalResult.rejected]
|
|
81
145
|
}
|
|
82
146
|
}
|