@things-factory/headless-twin 10.0.6 → 10.0.7
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/README.md +37 -24
- package/dist-server/engine/entity-delta.d.ts +13 -1
- package/dist-server/engine/entity-delta.js +130 -16
- package/dist-server/engine/entity-delta.js.map +1 -1
- package/dist-server/engine/index.d.ts +4 -0
- package/dist-server/engine/index.js +4 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +20 -3
- package/dist-server/engine/kpi-fold.js +21 -4
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +9 -0
- package/dist-server/engine/kpi-query.js +118 -19
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/kpi-target.d.ts +141 -0
- package/dist-server/engine/kpi-target.js +137 -0
- package/dist-server/engine/kpi-target.js.map +1 -0
- package/dist-server/engine/live-attentions.d.ts +4 -2
- package/dist-server/engine/live-attentions.js +5 -1
- package/dist-server/engine/live-attentions.js.map +1 -1
- package/dist-server/engine/measured-estimator.d.ts +50 -0
- package/dist-server/engine/measured-estimator.js +78 -0
- package/dist-server/engine/measured-estimator.js.map +1 -0
- package/dist-server/engine/model-basis.d.ts +23 -0
- package/dist-server/engine/model-basis.js +100 -0
- package/dist-server/engine/model-basis.js.map +1 -0
- package/dist-server/engine/oee-accumulator.d.ts +2 -2
- package/dist-server/engine/oee-accumulator.js +4 -4
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/spec-coverage.d.ts +49 -0
- package/dist-server/engine/spec-coverage.js +60 -0
- package/dist-server/engine/spec-coverage.js.map +1 -0
- package/dist-server/engine/structure-diff.d.ts +25 -0
- package/dist-server/engine/structure-diff.js +63 -0
- package/dist-server/engine/structure-diff.js.map +1 -0
- package/dist-server/engine/travel-estimator.d.ts +57 -0
- package/dist-server/engine/travel-estimator.js +120 -0
- package/dist-server/engine/travel-estimator.js.map +1 -0
- package/dist-server/engine/twin-engine.d.ts +153 -17
- package/dist-server/engine/twin-engine.js +473 -81
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +5 -5
- package/dist-server/engine/warm-start.js +4 -4
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -2
- package/dist-server/service/index.js +21 -14
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/reference-live.js +2 -1
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +307 -1
- package/dist-server/service/reference/reference-master.js +96 -8
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.js +3 -3
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/template-registry.d.ts +1 -1
- package/dist-server/service/reference/template-registry.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js +3 -3
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +11 -0
- package/dist-server/service/twin-event/twin-event.js +6 -1
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +50 -7
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.js +1 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +9 -0
- package/dist-server/service/twin-journal/twin-journal-query.js +54 -3
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +4 -3
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-area.js +1 -1
- package/dist-server/service/twin-space/twin-space-area.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.js +3 -3
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-space/twin-space.d.ts +11 -0
- package/dist-server/service/twin-space/twin-space.js +5 -0
- package/dist-server/service/twin-space/twin-space.js.map +1 -1
- package/dist-server/service/twin-structure/index.d.ts +2 -0
- package/dist-server/service/twin-structure/index.js +6 -0
- package/dist-server/service/twin-structure/index.js.map +1 -0
- package/dist-server/service/twin-structure/twin-structure.d.ts +28 -0
- package/dist-server/service/twin-structure/twin-structure.js +97 -0
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -0
- package/dist-server/service/twin-target/index.d.ts +4 -0
- package/dist-server/service/twin-target/index.js +8 -0
- package/dist-server/service/twin-target/index.js.map +1 -0
- package/dist-server/service/twin-target/twin-target-resolver.d.ts +7 -0
- package/dist-server/service/twin-target/twin-target-resolver.js +143 -0
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -0
- package/dist-server/service/twin-target/twin-target.d.ts +25 -0
- package/dist-server/service/twin-target/twin-target.js +112 -0
- package/dist-server/service/twin-target/twin-target.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/engine/entity-delta.ts +135 -15
- package/server/engine/index.ts +4 -0
- package/server/engine/kpi-fold.ts +40 -8
- package/server/engine/kpi-query.ts +129 -19
- package/server/engine/kpi-target.ts +226 -0
- package/server/engine/live-attentions.ts +12 -2
- package/server/engine/measured-estimator.ts +91 -0
- package/server/engine/model-basis.ts +94 -0
- package/server/engine/oee-accumulator.ts +5 -5
- package/server/engine/spec-coverage.ts +85 -0
- package/server/engine/structure-diff.ts +88 -0
- package/server/engine/travel-estimator.ts +133 -0
- package/server/engine/twin-engine.ts +498 -84
- package/server/engine/warm-start.ts +8 -8
- package/server/service/index.ts +7 -0
- package/server/service/reference/reference-live.ts +2 -1
- package/server/service/reference/reference-master.ts +383 -10
- package/server/service/reference/reference-resolver.ts +3 -3
- package/server/service/reference/template-registry.ts +1 -1
- package/server/service/twin-attention/twin-attention-query.ts +1 -1
- package/server/service/twin-control/twin-control-mutation.ts +1 -1
- package/server/service/twin-event/twin-event-keys.ts +2 -2
- package/server/service/twin-event/twin-event.ts +15 -1
- package/server/service/twin-forecast/twin-forecast-query.ts +56 -8
- package/server/service/twin-instance/twin-instance.ts +1 -1
- package/server/service/twin-journal/twin-journal-query.ts +52 -4
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +4 -3
- package/server/service/twin-space/twin-space-area.ts +1 -1
- package/server/service/twin-space/twin-space-resolver.ts +3 -3
- package/server/service/twin-space/twin-space.ts +14 -0
- package/server/service/twin-structure/index.ts +3 -0
- package/server/service/twin-structure/twin-structure.ts +93 -0
- package/server/service/twin-target/index.ts +5 -0
- package/server/service/twin-target/twin-target-resolver.ts +124 -0
- package/server/service/twin-target/twin-target.ts +97 -0
- package/test/capability-mapping.test.ts +5 -5
- package/test/duration-estimators.test.ts +144 -0
- package/test/entity-delta.test.ts +150 -19
- package/test/ingest-bench.test.ts +9 -9
- package/test/kpi-fold.test.ts +232 -3
- package/test/live-mirror-parity.test.ts +53 -19
- package/test/master-to-twin.test.ts +87 -13
- package/test/model-basis.test.ts +86 -0
- package/test/oee-accumulator.test.ts +9 -9
- package/test/scale-twin-bench.test.ts +22 -22
- package/test/spec-coverage.test.ts +113 -0
- package/test/streamline-e2e.test.ts +10 -10
- package/test/structure-revision-db.test.ts +310 -0
- package/test/twin-event-keys.test.ts +2 -2
- package/test/vocabulary-guard.test.ts +43 -0
- package/test/warm-start.test.ts +9 -9
|
@@ -23,15 +23,15 @@ function planWarmStart(state, purpose, canHydrate) {
|
|
|
23
23
|
return { action: 'skip', reason: 'bench' };
|
|
24
24
|
if (!state)
|
|
25
25
|
return { action: 'skip', reason: 'no-state' };
|
|
26
|
-
const
|
|
26
|
+
const locations = state.locations ?? [];
|
|
27
27
|
const items = state.items ?? [];
|
|
28
|
-
const
|
|
28
|
+
const equipment = state.equipment ?? [];
|
|
29
29
|
/* 셋 다 비었으면 심을 것이 없다 — 빈 주입으로 로그만 남기지 않는다. */
|
|
30
|
-
if (
|
|
30
|
+
if (locations.length === 0 && items.length === 0 && equipment.length === 0)
|
|
31
31
|
return { action: 'skip', reason: 'no-state' };
|
|
32
32
|
/* 지원 여부는 마지막에 본다 — 심을 게 있는데 못 심는 상황이라야 경고할 값어치가 있다. */
|
|
33
33
|
if (!canHydrate)
|
|
34
34
|
return { action: 'skip', reason: 'unsupported' };
|
|
35
|
-
return { action: 'hydrate', seed: {
|
|
35
|
+
return { action: 'hydrate', seed: { locations, items, equipment }, itemCount: items.length, equipmentCount: equipment.length };
|
|
36
36
|
}
|
|
37
37
|
//# sourceMappingURL=warm-start.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-start.js","sourceRoot":"","sources":["../../server/engine/warm-start.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAyBH,sCAmBC;AA1BD;;;;;;GAMG;AACH,SAAgB,aAAa,CAC3B,
|
|
1
|
+
{"version":3,"file":"warm-start.js","sourceRoot":"","sources":["../../server/engine/warm-start.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAyBH,sCAmBC;AA1BD;;;;;;GAMG;AACH,SAAgB,aAAa,CAC3B,KAA6F,EAC7F,OAA2B,EAC3B,UAAmB;IAEnB,sDAAsD;IACtD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;IAEzD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,EAAE,CAAA;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAA;IAC/B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,EAAE,CAAA;IACvC,6CAA6C;IAC7C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;IAEzH,uDAAuD;IACvD,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA;IAEjE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,MAAM,EAAE,CAAA;AAChI,CAAC","sourcesContent":["/*\n * 웜스타트 판정 — **순수**. \"기동하는 커널에 직전 상태를 심을 것인가, 심는다면 무엇을\" 만 정한다.\n * 실제 주입(hydrateObserved 호출)과 로그는 엔진이 한다.\n *\n * ── 왜 떼어냈나 ─────────────────────────────────────────────────────────────\n * 이 판정이 틀리면 증상이 정반대 두 방향으로 나온다: 심어야 할 때 안 심으면 **있는 재고가 0 으로**\n * 보이고(2026-07-31 hatiolab-wms: 저널에 입고 540·출고 94 인데 재고 화면이 비어 있었다), 심지\n * 말아야 할 벤치에 심으면 **용량 측정이 오염된다.** 둘 다 조용히 틀리는 종류라 규칙을 고정한다.\n */\n\n/** 커널에 심을 관측 상태 — 구조가 아니라 \"무엇이 어디에 얼마나\". */\nexport interface ObservedSeed {\n locations: unknown[]\n items: unknown[]\n equipment: unknown[]\n}\n\nexport type WarmStartPlan =\n | { action: 'hydrate'; seed: ObservedSeed; itemCount: number; equipmentCount: number }\n /** 벤치 트윈 — 새 시작에서 용량을 재는 게 목적이라 현재 상태를 심으면 측정이 오염된다. */\n | { action: 'skip'; reason: 'bench' }\n /** 심을 상태가 없다 — 처음 만든 트윈이거나 저널·체크포인트가 비었다. 정상이다. */\n | { action: 'skip'; reason: 'no-state' }\n /** 커널이 관측 주입을 지원하지 않는다 — 구조만으로 시작하므로 보유량은 0 으로 읽힌다(알려야 한다). */\n | { action: 'skip'; reason: 'unsupported' }\n\n/**\n * 무엇을 할지 정한다.\n *\n * **오더는 의도적으로 심지 않는다.** 커널의 관측 주입은 오더에 requested/fulfilled/lines 를 요구하는데\n * 스냅샷의 오더에는 `progress` 밖에 없다. progress 에서 역산하면 없는 숫자를 지어내는 것이므로\n * 넘기지 않는다 — 재고·노드·무버만 복원되고 진행 중 오더는 비어서 시작하는 편이 정직하다.\n */\nexport function planWarmStart(\n state: { locations?: unknown[]; items?: unknown[]; equipment?: unknown[] } | null | undefined,\n purpose: string | undefined,\n canHydrate: boolean\n): WarmStartPlan {\n /* 벤치 판정이 먼저다 — 상태가 있든 없든 벤치에는 심지 않는다는 사실이 바뀌지 않는다. */\n if (purpose === 'bench') return { action: 'skip', reason: 'bench' }\n if (!state) return { action: 'skip', reason: 'no-state' }\n\n const locations = state.locations ?? []\n const items = state.items ?? []\n const equipment = state.equipment ?? []\n /* 셋 다 비었으면 심을 것이 없다 — 빈 주입으로 로그만 남기지 않는다. */\n if (locations.length === 0 && items.length === 0 && equipment.length === 0) return { action: 'skip', reason: 'no-state' }\n\n /* 지원 여부는 마지막에 본다 — 심을 게 있는데 못 심는 상황이라야 경고할 값어치가 있다. */\n if (!canHydrate) return { action: 'skip', reason: 'unsupported' }\n\n return { action: 'hydrate', seed: { locations, items, equipment }, itemCount: items.length, equipmentCount: equipment.length }\n}\n"]}
|
|
@@ -3,6 +3,8 @@ export * from './twin-instance/twin-instance.js';
|
|
|
3
3
|
export * from './twin-space/twin-space.js';
|
|
4
4
|
export * from './twin-space/twin-space-representation.js';
|
|
5
5
|
export * from './twin-space/twin-space-area.js';
|
|
6
|
+
export * from './twin-target/twin-target.js';
|
|
7
|
+
export * from './twin-structure/twin-structure.js';
|
|
6
8
|
export * from './twin-state/twin-state-subscription.js';
|
|
7
9
|
export * from './reference/reference-master.js';
|
|
8
10
|
export * from './reference/reference-registry.js';
|
|
@@ -11,10 +13,10 @@ export * from './reference/reference-live.js';
|
|
|
11
13
|
export * from './reference/twin-reference.js';
|
|
12
14
|
export * from './reference/template-registry.js';
|
|
13
15
|
export * from './twin-lifecycle/domain-catalog.js';
|
|
14
|
-
export declare const entities: (typeof import("./twin-event/twin-event.js").TwinEvent | typeof import("./twin-instance/twin-instance.js").TwinInstance | typeof import("./twin-space/twin-space.js").TwinSpace | typeof import("./twin-space/twin-space-representation.js").TwinSpaceRepresentation | typeof import("./twin-space/twin-space-area.js").TwinSpaceArea | typeof import("./twin-space/twin-area.js").TwinArea | typeof import("./reference/twin-reference.js").TwinReference)[];
|
|
16
|
+
export declare const entities: (typeof import("./twin-event/twin-event.js").TwinEvent | typeof import("./twin-instance/twin-instance.js").TwinInstance | typeof import("./twin-structure/twin-structure.js").TwinStructure | typeof import("./twin-space/twin-space.js").TwinSpace | typeof import("./twin-space/twin-space-representation.js").TwinSpaceRepresentation | typeof import("./twin-space/twin-space-area.js").TwinSpaceArea | typeof import("./twin-space/twin-area.js").TwinArea | typeof import("./twin-target/twin-target.js").TwinTarget | typeof import("./reference/twin-reference.js").TwinReference)[];
|
|
15
17
|
export declare const subscribers: any[];
|
|
16
18
|
export declare const schema: {
|
|
17
19
|
typeDefs: {};
|
|
18
|
-
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery)[];
|
|
20
|
+
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./twin-target/twin-target-resolver.js").TwinTargetResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery)[];
|
|
19
21
|
directives: {};
|
|
20
22
|
};
|
|
@@ -8,6 +8,8 @@ tslib_1.__exportStar(require("./twin-instance/twin-instance.js"), exports);
|
|
|
8
8
|
tslib_1.__exportStar(require("./twin-space/twin-space.js"), exports);
|
|
9
9
|
tslib_1.__exportStar(require("./twin-space/twin-space-representation.js"), exports);
|
|
10
10
|
tslib_1.__exportStar(require("./twin-space/twin-space-area.js"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./twin-target/twin-target.js"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./twin-structure/twin-structure.js"), exports);
|
|
11
13
|
tslib_1.__exportStar(require("./twin-state/twin-state-subscription.js"), exports);
|
|
12
14
|
tslib_1.__exportStar(require("./reference/reference-master.js"), exports);
|
|
13
15
|
tslib_1.__exportStar(require("./reference/reference-registry.js"), exports);
|
|
@@ -20,36 +22,41 @@ tslib_1.__exportStar(require("./twin-lifecycle/domain-catalog.js"), exports);
|
|
|
20
22
|
/* IMPORT ENTITIES AND RESOLVERS */
|
|
21
23
|
const index_js_1 = require("./twin-event/index.js");
|
|
22
24
|
const index_js_2 = require("./twin-instance/index.js");
|
|
23
|
-
const index_js_3 = require("./twin-
|
|
24
|
-
const index_js_4 = require("./
|
|
25
|
-
const index_js_5 = require("./twin-
|
|
26
|
-
const index_js_6 = require("./
|
|
27
|
-
const index_js_7 = require("./twin-
|
|
28
|
-
const index_js_8 = require("./twin-
|
|
29
|
-
const index_js_9 = require("./twin-
|
|
30
|
-
const index_js_10 = require("./twin-
|
|
31
|
-
const index_js_11 = require("./twin-
|
|
25
|
+
const index_js_3 = require("./twin-structure/index.js");
|
|
26
|
+
const index_js_4 = require("./twin-space/index.js");
|
|
27
|
+
const index_js_5 = require("./twin-target/index.js");
|
|
28
|
+
const index_js_6 = require("./reference/index.js");
|
|
29
|
+
const index_js_7 = require("./twin-state/index.js");
|
|
30
|
+
const index_js_8 = require("./twin-control/index.js");
|
|
31
|
+
const index_js_9 = require("./twin-lifecycle/index.js");
|
|
32
|
+
const index_js_10 = require("./twin-journal/index.js");
|
|
33
|
+
const index_js_11 = require("./twin-forecast/index.js");
|
|
34
|
+
const index_js_12 = require("./twin-attention/index.js");
|
|
35
|
+
const index_js_13 = require("./twin-metrics/index.js");
|
|
32
36
|
exports.entities = [
|
|
33
37
|
/* ENTITIES */
|
|
34
38
|
...index_js_1.entities,
|
|
35
39
|
...index_js_2.entities,
|
|
36
40
|
...index_js_3.entities,
|
|
37
|
-
...index_js_4.entities
|
|
41
|
+
...index_js_4.entities,
|
|
42
|
+
...index_js_5.entities,
|
|
43
|
+
...index_js_6.entities
|
|
38
44
|
];
|
|
39
45
|
exports.subscribers = [];
|
|
40
46
|
exports.schema = {
|
|
41
47
|
typeDefs: {},
|
|
42
48
|
resolverClasses: [
|
|
43
49
|
/* RESOLVER CLASSES */
|
|
44
|
-
...index_js_5.resolvers,
|
|
45
|
-
...index_js_6.resolvers,
|
|
46
50
|
...index_js_7.resolvers,
|
|
47
51
|
...index_js_8.resolvers,
|
|
48
52
|
...index_js_9.resolvers,
|
|
49
53
|
...index_js_10.resolvers,
|
|
50
54
|
...index_js_11.resolvers,
|
|
51
|
-
...
|
|
52
|
-
...
|
|
55
|
+
...index_js_12.resolvers,
|
|
56
|
+
...index_js_13.resolvers,
|
|
57
|
+
...index_js_4.resolvers,
|
|
58
|
+
...index_js_5.resolvers,
|
|
59
|
+
...index_js_6.resolvers
|
|
53
60
|
],
|
|
54
61
|
directives: {}
|
|
55
62
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,wEAA6C;AAC7C,wEAA6C;AAC7C,2EAAgD;AAChD,sEAAsE;AACtE,6EAAkD;AAElD,mCAAmC;AACnC,oDAAqE;AACrE,uDAA2E;AAC3E,oDAAsG;AACtG,mDAA6G;AAC7G,oDAAuE;AACvE,sDAA2E;AAC3E,wDAA+E;AAC/E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,uEAA4C;AAC5C,6EAAkD;AAClD,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,wEAA6C;AAC7C,wEAA6C;AAC7C,2EAAgD;AAChD,sEAAsE;AACtE,6EAAkD;AAElD,mCAAmC;AACnC,oDAAqE;AACrE,uDAA2E;AAC3E,wDAA6E;AAC7E,oDAAsG;AACtG,qDAAyG;AACzG,mDAA6G;AAC7G,oDAAuE;AACvE,sDAA2E;AAC3E,wDAA+E;AAC/E,uDAA2E;AAC3E,wDAA6E;AAC7E,yDAA+E;AAC/E,uDAA2E;AAE9D,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAiB;IACpB,GAAG,mBAAoB;IACvB,GAAG,mBAAqB;IACxB,GAAG,mBAAiB;IACpB,GAAG,mBAAkB;IACrB,GAAG,mBAAqB;CACzB,CAAA;AAEY,QAAA,WAAW,GAAG,EAAE,CAAA;AAEhB,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE,EAAE;IACZ,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAAkB;QACrB,GAAG,oBAAoB;QACvB,GAAG,oBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,qBAAqB;QACxB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,oBAAmB;QACtB,GAAG,oBAAsB;KAC1B;IACD,UAAU,EAAE,EAAE;CACf,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './twin-event/twin-event.js'\nexport * from './twin-instance/twin-instance.js'\nexport * from './twin-space/twin-space.js'\nexport * from './twin-space/twin-space-representation.js'\nexport * from './twin-space/twin-space-area.js'\nexport * from './twin-target/twin-target.js'\nexport * from './twin-structure/twin-structure.js'\nexport * from './twin-state/twin-state-subscription.js'\nexport * from './reference/reference-master.js'\nexport * from './reference/reference-registry.js'\nexport * from './reference/reference-adapter.js'\nexport * from './reference/reference-live.js'\nexport * from './reference/twin-reference.js'\nexport * from './reference/template-registry.js'\n/* 도메인 어휘 카탈로그 조합(커널 SSOT 노출) — 서버측 소비자(board-ai 도구 등)가 같은 형태를 쓰도록. */\nexport * from './twin-lifecycle/domain-catalog.js'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as TwinEventEntities } from './twin-event/index.js'\nimport { entities as TwinInstanceEntities } from './twin-instance/index.js'\nimport { entities as TwinStructureEntities } from './twin-structure/index.js'\nimport { entities as TwinSpaceEntities, resolvers as TwinSpaceResolvers } from './twin-space/index.js'\nimport { entities as TwinTargetEntities, resolvers as TwinTargetResolvers } from './twin-target/index.js'\nimport { entities as TwinReferenceEntities, resolvers as TwinReferenceResolvers } from './reference/index.js'\nimport { resolvers as TwinStateResolvers } from './twin-state/index.js'\nimport { resolvers as TwinControlResolvers } from './twin-control/index.js'\nimport { resolvers as TwinLifecycleResolvers } from './twin-lifecycle/index.js'\nimport { resolvers as TwinJournalResolvers } from './twin-journal/index.js'\nimport { resolvers as TwinForecastResolvers } from './twin-forecast/index.js'\nimport { resolvers as TwinAttentionResolvers } from './twin-attention/index.js'\nimport { resolvers as TwinMetricsResolvers } from './twin-metrics/index.js'\n\nexport const entities = [\n /* ENTITIES */\n ...TwinEventEntities,\n ...TwinInstanceEntities,\n ...TwinStructureEntities,\n ...TwinSpaceEntities,\n ...TwinTargetEntities,\n ...TwinReferenceEntities\n]\n\nexport const subscribers = []\n\nexport const schema = {\n typeDefs: {},\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...TwinStateResolvers,\n ...TwinControlResolvers,\n ...TwinLifecycleResolvers,\n ...TwinJournalResolvers,\n ...TwinForecastResolvers,\n ...TwinAttentionResolvers,\n ...TwinMetricsResolvers,\n ...TwinSpaceResolvers,\n ...TwinTargetResolvers,\n ...TwinReferenceResolvers\n ],\n directives: {}\n}\n"]}
|
|
@@ -26,7 +26,8 @@ async function startReferenceLiveFeed(domainId, adapterType, cfg, site, instance
|
|
|
26
26
|
if (!det?.board)
|
|
27
27
|
throw new Error(`instance "${instanceId}" not provisioned (no board) — ingest master before live start`);
|
|
28
28
|
// mirror 모드 기동 — 커널 tick 없이 projector 가 실 이벤트를 미러(startLive 가 realityMode='mirror' 설정).
|
|
29
|
-
|
|
29
|
+
// 시각 기준은 **공간**이 갖는다(테넌트 아님) — 교대의 HH:MM 을 어느 기준으로 읽나.
|
|
30
|
+
index_js_1.TwinEngine.startLive(instanceId, domainId, det.kind ?? 'wms', await index_js_1.TwinEngine.withSpaceTimeBase(det.board, domainId));
|
|
30
31
|
stopReferenceLiveFeed(instanceId); // 재기동 시 이전 피드 정리
|
|
31
32
|
const unsubscribe = adapter.openLiveFeed(cfg, site, (records) => {
|
|
32
33
|
if (!index_js_1.TwinEngine.instances[instanceId]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-live.js","sourceRoot":"","sources":["../../../server/service/reference/reference-live.ts"],"names":[],"mappings":";;AAkBA,
|
|
1
|
+
{"version":3,"file":"reference-live.js","sourceRoot":"","sources":["../../../server/service/reference/reference-live.ts"],"names":[],"mappings":";;AAkBA,wDA4BC;AAGD,sDAMC;AAGD,wDAEC;AA5DD,iEAA+F;AAC/F,oDAAgG;AAEhG;;;;;;GAMG;AAEH,iEAAiE;AACjE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAA;AAE3C;;;GAGG;AACI,KAAK,UAAU,sBAAsB,CAC1C,QAAgB,EAChB,WAAmB,EACnB,GAAqB,EACrB,IAAoB,EACpB,UAAkB;IAElB,MAAM,OAAO,GAAG,IAAA,iCAAU,EAAC,WAAW,CAAC,CAAA;IACvC,IAAI,CAAC,OAAO,EAAE,YAAY;QAAE,OAAO,KAAK,CAAA;IAExC,MAAM,GAAG,GAAG,MAAM,qBAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACzD,IAAI,CAAC,GAAG,EAAE,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,UAAU,gEAAgE,CAAC,CAAA;IAEzH,wFAAwF;IACxF,uDAAuD;IACvD,qBAAU,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,IAAI,KAAK,EAAE,MAAM,qBAAU,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IAEtH,qBAAqB,CAAC,UAAU,CAAC,CAAA,CAAC,iBAAiB;IACnD,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,OAAkB,EAAE,EAAE;QACzE,IAAI,CAAC,qBAAU,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,qBAAqB,CAAC,UAAU,CAAC,CAAA,CAAC,uBAAuB;YACzD,OAAM;QACR,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,iCAAsB,EAAC,OAA4B,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;QAC7G,IAAI,QAAQ,CAAC,MAAM;YAAE,qBAAU,CAAC,UAAU,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;IACF,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IAClC,OAAO,IAAI,CAAA;AACb,CAAC;AAED,8BAA8B;AAC9B,SAAgB,qBAAqB,CAAC,UAAkB;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,IAAI,CAAC;QAAC,KAAK,EAAE,CAAA;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;IACpC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACxB,OAAO,IAAI,CAAA;AACb,CAAC;AAED,oBAAoB;AACpB,SAAgB,sBAAsB;IACpC,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;AAC1B,CAAC;AAED,kCAAkC;AAClC,qBAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAA","sourcesContent":["import { getAdapter, type ConnectionConfig, type SiteDescriptor } from './reference-adapter.js'\nimport { TwinEngine, ingestCanonicalRecords, type CanonicalRecord } from '../../engine/index.js'\n\n/*\n * 커넥터 기반 라이브 피드 결선(reference → live, §0 프레임 ① realityMode='mirror').\n * 흐름: adapter.openLiveFeed(cfg, site, records) → ingestCanonicalRecords(정규→봉투) → TwinEngine.ingestLive(projector 미러).\n * 커넥터가 \"벤더 이벤트 → 정규 레코드\" 매핑을 소유(코드)한다 — preset 기반 startLiveFeed(operato-twin, http-get+jsonata)와\n * 대칭이되, 매핑 위치만 다르다. 실 시스템 전환은 connectionConfig.baseUrl 교체뿐(코드 경로 동일).\n * 생명주기: 인스턴스 정지(TwinEngine.stop)에 훅으로 묶여 피드도 함께 unsubscribe.\n */\n\n/** 활성 라이브 피드 — instanceId → unsubscribe(어댑터 openLiveFeed 반환). */\nconst feeds = new Map<string, () => void>()\n\n/**\n * 커넥터 라이브 기동 — 어댑터 openLiveFeed 를 인스턴스에 결선.\n * 반환: 라이브 결선 성공 여부(어댑터가 openLiveFeed 미지원이면 false → 호출측이 sim 폴백).\n */\nexport async function startReferenceLiveFeed(\n domainId: string,\n adapterType: string,\n cfg: ConnectionConfig,\n site: SiteDescriptor,\n instanceId: string\n): Promise<boolean> {\n const adapter = getAdapter(adapterType)\n if (!adapter?.openLiveFeed) return false\n\n const det = await TwinEngine.detail(domainId, instanceId)\n if (!det?.board) throw new Error(`instance \"${instanceId}\" not provisioned (no board) — ingest master before live start`)\n\n // mirror 모드 기동 — 커널 tick 없이 projector 가 실 이벤트를 미러(startLive 가 realityMode='mirror' 설정).\n // 시각 기준은 **공간**이 갖는다(테넌트 아님) — 교대의 HH:MM 을 어느 기준으로 읽나.\n TwinEngine.startLive(instanceId, domainId, det.kind ?? 'wms', await TwinEngine.withSpaceTimeBase(det.board, domainId))\n\n stopReferenceLiveFeed(instanceId) // 재기동 시 이전 피드 정리\n const unsubscribe = adapter.openLiveFeed(cfg, site, (records: unknown[]) => {\n if (!TwinEngine.instances[instanceId]) {\n stopReferenceLiveFeed(instanceId) // 인스턴스 사라지면 자가 정지(안전망)\n return\n }\n const { accepted } = ingestCanonicalRecords(records as CanonicalRecord[], domainId, new Date().toISOString())\n if (accepted.length) TwinEngine.ingestLive(instanceId, accepted)\n })\n feeds.set(instanceId, unsubscribe)\n return true\n}\n\n/** 라이브 피드 중지(unsubscribe). */\nexport function stopReferenceLiveFeed(instanceId: string): boolean {\n const unsub = feeds.get(instanceId)\n if (!unsub) return false\n try { unsub() } catch { /* noop */ }\n feeds.delete(instanceId)\n return true\n}\n\n/** 활성 피드 목록(관측). */\nexport function listReferenceLiveFeeds(): string[] {\n return [...feeds.keys()]\n}\n\n/* 인스턴스 정지 시 피드 자동 정리(생명주기 대칭). */\nTwinEngine.onStop(instanceId => stopReferenceLiveFeed(instanceId))\n"]}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
+
import type { EquipmentLevel, WorkCalendarEntry } from '@operato/twin-kernel';
|
|
1
2
|
export interface RefLocation {
|
|
2
3
|
id: string;
|
|
3
4
|
type: string;
|
|
4
5
|
name?: string;
|
|
5
|
-
role: '
|
|
6
|
+
role: 'location' | 'area' | 'both';
|
|
6
7
|
parentId?: string;
|
|
8
|
+
/** 저장 용량 — 이 자리에 동시에 놓일 수 있는 물품 수. */
|
|
7
9
|
capacity?: number;
|
|
10
|
+
/**
|
|
11
|
+
* 동시 처리 수 — 이 자리에서 한 번에 진행될 수 있는 작업 수. **저장 용량과 다른 축**이다
|
|
12
|
+
* (자재를 20개 쌓아 두는 절단 스테이션이 한 번에 한 대만 깎는다). 미지정=제약 없음.
|
|
13
|
+
*/
|
|
14
|
+
parallelism?: number;
|
|
15
|
+
/**
|
|
16
|
+
* **ISA-95 설비 계층 단계** — `EquipmentLevel` 열거값(`ProductionLine`·`StorageZone`·`WorkCenter`…).
|
|
17
|
+
*
|
|
18
|
+
* `type` 은 현장의 낱말(`paint-booth`)이고 이것은 **표준이 정한 역할**이다. 둘은 다른 축이라 함께 든다 —
|
|
19
|
+
* 연동·집계는 표준 축으로, 화면은 현장 낱말로. 미지정이면 그 자리의 표준 역할을 아직 모른다는 뜻이고,
|
|
20
|
+
* 짐작해 채우지 않는다.
|
|
21
|
+
*/
|
|
22
|
+
level?: EquipmentLevel;
|
|
8
23
|
footprint?: {
|
|
9
24
|
x: number;
|
|
10
25
|
y: number;
|
|
@@ -13,6 +28,54 @@ export interface RefLocation {
|
|
|
13
28
|
};
|
|
14
29
|
gs1Id?: string;
|
|
15
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* 설비 속성 — **ISA-95 `EquipmentSpecificationProperty`(`OpEquipmentSpecificationPropertyType`) 1:1.**
|
|
33
|
+
* 그 타입도 `ID` + `Value`(`ValueString`·`DataType`·`UnitOfMeasure`) 이므로 공정 모수와 같은 모양이다.
|
|
34
|
+
*
|
|
35
|
+
* 단위 코드는 **UN/CEFACT 권고 20 공통코드**(EPCIS `uom` 과 같은 코드계): 속도는 `MTS`(metre per
|
|
36
|
+
* second) 또는 `KMH`(kilometre per hour). 코드를 우리가 발명하지 않는다.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* 자원 속성 — **커널 `ResourceProperty` 와 같은 모양**이고, 그 모양은 ISA-95 가 세 자원에 똑같이
|
|
40
|
+
* 정의한 것이다(`EquipmentPropertyType` · `PersonPropertyType` · `PhysicalAssetPropertyType`).
|
|
41
|
+
*
|
|
42
|
+
* 그래서 자원마다 다른 속성 타입을 두지 않는다 — 예전에는 설비만 속성을 가졌고(RefEquipmentProperty),
|
|
43
|
+
* 인원·자산에 붙는 사실은 **실을 자리가 없어서 들어오지 못했다.**
|
|
44
|
+
*/
|
|
45
|
+
export interface RefResourceProperty {
|
|
46
|
+
id: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
value?: string;
|
|
49
|
+
/** 표준 `Value.DataType`. */
|
|
50
|
+
dataType?: string;
|
|
51
|
+
/** 표준 `Value.UnitOfMeasure` — UN/CEFACT 공통코드. 물리량에 단위가 없으면 소비처가 거절한다. */
|
|
52
|
+
uom?: string;
|
|
53
|
+
/** 하위 속성(재귀) — 표준 `<X>PropertyChild`. */
|
|
54
|
+
children?: RefResourceProperty[];
|
|
55
|
+
/** 이 개체 속성이 구체화하는 등급 속성 — 표준 `<X>ClassPropertyID`. */
|
|
56
|
+
classPropertyId?: string;
|
|
57
|
+
}
|
|
58
|
+
/** 옛 이름 — 설비 전용이던 시절의 별칭(소비처 호환). 신규 소싱은 `RefResourceProperty`. */
|
|
59
|
+
export type RefEquipmentProperty = RefResourceProperty;
|
|
60
|
+
/**
|
|
61
|
+
* 자원 **등급 정의** — 커널 `ResourceClassDef` 와 같은 모양이고, 그 모양은 ISA-95 가 세 자원에 똑같이
|
|
62
|
+
* 정의한 것이다(`PersonnelClassType`·`EquipmentClassType`·`PhysicalAssetClassType`).
|
|
63
|
+
*
|
|
64
|
+
* 왜 필요한가: 원 시스템은 등급 **체계**를 갖고 있다("6축 용접 자격자" 는 "용접 자격자" 이고, 그것은
|
|
65
|
+
* "생산직" 이다). 그 체계가 들어올 자리가 없으면 요구와 소속이 **문자열이 똑같을 때만** 맞아,
|
|
66
|
+
* 현장에서는 되는 배정이 모델에서 막힌다.
|
|
67
|
+
*/
|
|
68
|
+
export interface RefResourceClass {
|
|
69
|
+
id: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
/** 상위 등급들 — 표준 `<X>ClassBaseID`(복수 = 다중 상속). */
|
|
72
|
+
baseIds?: string[];
|
|
73
|
+
properties?: RefResourceProperty[];
|
|
74
|
+
testSpecificationIds?: string[];
|
|
75
|
+
/** 유효 기간(ISO) — 표준 `EffectiveStartDate`/`EffectiveEndDate`. 밖이면 자격이 성립하지 않는다. */
|
|
76
|
+
effectiveStart?: string;
|
|
77
|
+
effectiveEnd?: string;
|
|
78
|
+
}
|
|
16
79
|
export interface RefEquipment {
|
|
17
80
|
id: string;
|
|
18
81
|
kind: string;
|
|
@@ -20,16 +83,216 @@ export interface RefEquipment {
|
|
|
20
83
|
mtbfMs?: number;
|
|
21
84
|
mttrMs?: number;
|
|
22
85
|
gs1Id?: string;
|
|
86
|
+
/**
|
|
87
|
+
* 교대(가동시간) — 이 시간대에만 배정된다. `startHour <= endHour` 면 같은 날, 넘어가면 야간(22→6).
|
|
88
|
+
* 미지정=24시간 가용. 교대 밖 자원은 고장·계획정지와 **구별되는 이유**로 쉰다(상태에 드러난다).
|
|
89
|
+
*/
|
|
90
|
+
window?: {
|
|
91
|
+
startHour: number;
|
|
92
|
+
endHour: number;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 있으면 `window` 보다 이것을 본다.
|
|
96
|
+
*
|
|
97
|
+
* 축을 프레임워크 `@things-factory/work-shift` 의 `WorkShift`(`fromDate`·`fromTime`·`toDate`·`toTime`)와
|
|
98
|
+
* 맞춰 두었다 — 그 엔티티를 교대의 정본으로 결선할 때 직선 매핑이 되게. **아직 결선하지 않았다**:
|
|
99
|
+
* 그 엔티티에는 자원 연결과 비근무(휴일)가 없어 확장이 함께 필요하고, operato-twin 의 의존성 추가는
|
|
100
|
+
* 앱 아키텍처 결정이다(커버리지 §4-7 에 결선 지점으로 기록).
|
|
101
|
+
*/
|
|
102
|
+
workCalendar?: WorkCalendarEntry[];
|
|
103
|
+
/**
|
|
104
|
+
* 설비 속성(속도 등) — 없으면 **이동시간을 거리에서 계산할 수 없다**(속도 없이 거리만으로는 시간이
|
|
105
|
+
* 안 나온다). 그때는 커널 상수로 떨어지고 그 사실이 `specCoverage()` 에 남는다.
|
|
106
|
+
*/
|
|
107
|
+
properties?: RefResourceProperty[];
|
|
108
|
+
/** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(교정 등). */
|
|
109
|
+
testSpecificationIds?: string[];
|
|
110
|
+
/**
|
|
111
|
+
* 유효 기간 — **표준 `EffectiveStartDate` / `EffectiveEndDate`**(개체 타입 셋 모두에 있다).
|
|
112
|
+
*
|
|
113
|
+
* 없으면 **폐기한 것이 영구히 살아 있다**: 배정 대상에 남고, 가용 수에 들어가고, 화면에 뜬다.
|
|
114
|
+
* 도입 예정인 것은 반대로 오늘부터 있는 것처럼 보인다. 기간 밖이면 커널이 그 시각의 모델에서
|
|
115
|
+
* 빼고 **이유를 달아 남긴다**(`effectivity: 'not-yet' | 'expired'`) — 조용히 지우지 않는다.
|
|
116
|
+
*/
|
|
117
|
+
effectiveStart?: string;
|
|
118
|
+
effectiveEnd?: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 공정 모수 — 커널 `OperationParameter`(= ISA-95 `ParameterSpecification`/`ParameterType`) 와 **같은 모양**.
|
|
122
|
+
* 이름을 바꾸지 않는다: 인제스트 경계에서 어휘가 갈리면 그 자리에서 방언이 생긴다.
|
|
123
|
+
* ID 어휘의 정본은 커널 `OP_PARAM`(표준이 파라미터 ID 를 정하지 않으므로 우리가 한 곳에서 정한다).
|
|
124
|
+
*/
|
|
125
|
+
export interface RefOperationParameter {
|
|
126
|
+
id: string;
|
|
127
|
+
/** ISA-95 `Value.ValueString` — 표준이 문자열로 싣는다. 기간 모수는 ISO 8601(`PT8M`). */
|
|
128
|
+
value: string;
|
|
129
|
+
/** ISA-95 `Value.UnitOfMeasure`. 무차원(비율)이면 생략. */
|
|
130
|
+
uom?: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 공정의 자재 요구·산출 — **ISA-95 `OperationsSegment.MaterialSpecification`** 과 같은 모양.
|
|
134
|
+
* 인원·설비·자산 명세와 같은 자리에 있어야 4대 자원이 대칭이 된다(자재만 빠져 있었다).
|
|
135
|
+
*/
|
|
136
|
+
export interface RefOpMaterialSpecification {
|
|
137
|
+
id?: string;
|
|
138
|
+
/** 품목 등급으로 요구 — 표준 `MaterialClassID`. */
|
|
139
|
+
materialClass?: string;
|
|
140
|
+
/** 특정 품목으로 요구 — 표준 `MaterialDefinitionID`. */
|
|
141
|
+
materialDefinition?: string;
|
|
142
|
+
/** 표준 `MaterialUse` — 이 공정에서의 쓰임. */
|
|
143
|
+
use: 'consumed' | 'produced' | 'consumable';
|
|
144
|
+
/** 표준 `Quantity`. 단위 미지정이면 개수. */
|
|
145
|
+
quantity: number;
|
|
146
|
+
uom?: string;
|
|
147
|
+
}
|
|
148
|
+
/** 소요시간 변동 — **표준 밖 확장**(ISA-95 Duration 은 스칼라). 커널 `DurationVariability` 와 동형. */
|
|
149
|
+
export interface RefOperationVariability {
|
|
150
|
+
distribution: 'constant' | 'exponential' | 'uniform' | 'triangular';
|
|
151
|
+
min?: string;
|
|
152
|
+
max?: string;
|
|
153
|
+
mode?: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 오퍼레이션 명세 — ISA-95 `OperationsSegment`. 커널 `OperationDef` 와 필드 이름이 1:1이다.
|
|
157
|
+
*
|
|
158
|
+
* **이것이 없으면 시뮬레이션의 시간은 우리가 코드에 박아 둔 상수다.** 원 시스템(또는 가상 레퍼런스)이
|
|
159
|
+
* 공정별 소요·변동·수율을 말해 줘야 예측이 그 현장의 예측이 된다. 주지 않으면 커널 기본값으로
|
|
160
|
+
* 굴러가고, **기본값을 썼다는 사실은 커널 `specCoverage()` 가 밝힌다**(조용히 넘어가지 않는다).
|
|
161
|
+
*/
|
|
162
|
+
export interface RefOperation {
|
|
163
|
+
/** 작업 종류 키 — 커널 `FlowTask.kind` 와 같은 값이어야 명세가 그 작업에 붙는다. */
|
|
164
|
+
key: string;
|
|
165
|
+
label: string;
|
|
166
|
+
intent: 'transport' | 'process' | 'dwell';
|
|
167
|
+
locationType?: string;
|
|
168
|
+
resourceType?: string;
|
|
169
|
+
bizStep?: string;
|
|
170
|
+
/** ISA-95 `OperationsSegment.Duration` — ISO 8601 기간(`PT12M`). */
|
|
171
|
+
duration?: string;
|
|
172
|
+
variability?: RefOperationVariability;
|
|
173
|
+
parameters?: RefOperationParameter[];
|
|
174
|
+
/**
|
|
175
|
+
* 필요 인원 — ISA-95 `PersonnelSpecification`(`PersonnelClassID` + `Quantity`) 1:1.
|
|
176
|
+
* 없으면 사람 없이 도는 공정이다(설비만 잡는다).
|
|
177
|
+
*/
|
|
178
|
+
personnelSpecification?: {
|
|
179
|
+
personnelClass?: string;
|
|
180
|
+
quantity: number;
|
|
181
|
+
}[];
|
|
182
|
+
/** 필요 물리 자산 — ISA-95 `PhysicalAssetSpecification`(등급 + 수량). 빈 팔레트가 없으면 못 나간다. */
|
|
183
|
+
physicalAssetSpecification?: {
|
|
184
|
+
assetClass?: string;
|
|
185
|
+
quantity: number;
|
|
186
|
+
}[];
|
|
187
|
+
/**
|
|
188
|
+
* 필요·산출 자재 — ISA-95 `MaterialSpecification`. **4대 자원 중 자재만 이 자리가 없었다** —
|
|
189
|
+
* 그래서 부품이 떨어져 라인이 서는 상황이 예측에 나타나지 않았다.
|
|
190
|
+
* BOM 의 "몇 개" 는 여기다(품목 정의의 조립 구조에는 수량이 없다 — 1차 출처 확인).
|
|
191
|
+
*/
|
|
192
|
+
materialSpecification?: RefOpMaterialSpecification[];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 사람 — **ISA-95 `Person`.** 설비와 별개 자원이다(고장·설비효율이 아니라 등급·교대로 산다).
|
|
196
|
+
* `personnelClass` 가 ISA-95 `PersonnelClassID` — 공정은 특정인이 아니라 **등급으로** 인원을 요구한다.
|
|
197
|
+
*/
|
|
198
|
+
export interface RefPerson {
|
|
199
|
+
id: string;
|
|
200
|
+
name?: string;
|
|
201
|
+
/**
|
|
202
|
+
* 속한 등급들 — **표준 `Person.PersonnelClassID`, `maxOccurs="unbounded"`**.
|
|
203
|
+
* 복수인 것이 표준이고 그것이 자격 표현이다: 한 사람이 용접 자격과 지게차 자격을 함께 갖는다.
|
|
204
|
+
*/
|
|
205
|
+
personnelClassIds?: string[];
|
|
206
|
+
/** 교대 — 설비와 같은 모양. 미지정=24시간 가용. */
|
|
207
|
+
window?: {
|
|
208
|
+
startHour: number;
|
|
209
|
+
endHour: number;
|
|
210
|
+
};
|
|
211
|
+
/** 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 2교대·휴게·휴일을 함께 표현한다. */
|
|
212
|
+
workCalendar?: WorkCalendarEntry[];
|
|
213
|
+
/**
|
|
214
|
+
* 기본 위치 — 표준 `Person.OperationalLocation`. 없으면 "이 라인에 몇 명 있나" 를 물을 수 없다.
|
|
215
|
+
* 사람은 움직이므로 이 값은 **출발점**이고, 실제 위치는 관측(person.status)이 갱신한다.
|
|
216
|
+
*/
|
|
217
|
+
homeLocationId?: string;
|
|
218
|
+
/**
|
|
219
|
+
* 자원 속성 — 표준 `PersonProperty`. **자격증·숙련도가 여기 들어간다.**
|
|
220
|
+
*
|
|
221
|
+
* 표준에는 자격(Qualification)이라는 별도 요소가 없다(`B2MML-Personnel.xsd` 확인). 자격은
|
|
222
|
+
* ① 등급 복수 소속으로(`personnelClassIds: ['welder-certified','forklift-licensed']`) ②
|
|
223
|
+
* 증빙은 이 속성으로(자격증 번호·만료일), ③ 검증 시험은 표준 `TestSpecificationID`(아직 미구현).
|
|
224
|
+
*/
|
|
225
|
+
properties?: RefResourceProperty[];
|
|
226
|
+
/** 자격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(참조만; 시험 명세·결과 모델은 아직 없다). */
|
|
227
|
+
testSpecificationIds?: string[];
|
|
228
|
+
/** 유효 기간 — 사람에게는 **입사 전·퇴사 후**다(자격 만료는 등급 쪽 기간이다: 사람은 남고 자격만 끊긴다). */
|
|
229
|
+
effectiveStart?: string;
|
|
230
|
+
effectiveEnd?: string;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 물리 자산(반복사용) — **ISA-95 `PhysicalAsset`, GS1 `GRAI`.**
|
|
234
|
+
* 팔레트·랙·용기처럼 **나갔다 돌아오는** 것. 물류단위(SSCC)와 다르다: SSCC 는 그 팔레트에 실린 화물
|
|
235
|
+
* 한 덩어리이고, GRAI 는 팔레트 자체다(오늘은 이 화물, 내일은 저 화물을 싣는다).
|
|
236
|
+
*/
|
|
237
|
+
export interface RefAsset {
|
|
238
|
+
id: string;
|
|
239
|
+
name?: string;
|
|
240
|
+
/**
|
|
241
|
+
* 속한 등급들 — 표준 `PhysicalAssetClassID`(복수). 팔레트가 'euro-pallet' 이면서 'food-grade' 일 수 있다.
|
|
242
|
+
* 공정의 요구는 등급 하나 + 수량이다(표준 `PhysicalAssetSpecification`).
|
|
243
|
+
*/
|
|
244
|
+
assetClassIds?: string[];
|
|
245
|
+
/** 기본 위치. */
|
|
246
|
+
homeLocationId?: string;
|
|
247
|
+
/** 정식 GS1 정체성 — 반복사용 자산=GRAI. */
|
|
248
|
+
gs1Id?: string;
|
|
249
|
+
/** 자원 속성 — 표준 `PhysicalAssetProperty`(적재 한도·규격 등). */
|
|
250
|
+
properties?: RefResourceProperty[];
|
|
251
|
+
/** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */
|
|
252
|
+
testSpecificationIds?: string[];
|
|
253
|
+
/** 유효 기간 — 자산에서는 **폐기한 팔레트가 풀에서 빠지는 것**이다(회수 대상 수가 부풀지 않게). */
|
|
254
|
+
effectiveStart?: string;
|
|
255
|
+
effectiveEnd?: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 품목 정의 — **ISA-95 `MaterialDefinition`.**
|
|
259
|
+
*
|
|
260
|
+
* 없으면 로트만 관측으로 들어오고 **품목이 무엇인지는 아무 데도 없다.** 그래서 같은 로트를 100개로
|
|
261
|
+
* 받아도 "몇 kg 인가" 에 답할 수 없다(커널 `quantityIn` 이 환산을 거부하는 근거).
|
|
262
|
+
*
|
|
263
|
+
* **표준에 단위 환산 요소는 없다**(1차 출처 전수 확인) — 속성 주머니뿐이다. 그래서 환산 계수는
|
|
264
|
+
* 커널이 정한 속성 이름(`perBaseUnit`, `uom` 이 대상 단위)으로 `properties` 에 싣는다.
|
|
265
|
+
*/
|
|
266
|
+
export interface RefMaterialDefinition {
|
|
267
|
+
/** 표준 `ID` — GTIN(`urn:epc:idpat:sgtin:…`)을 쓴다(정체성의 정본이 EPCIS 다). */
|
|
268
|
+
id: string;
|
|
269
|
+
name?: string;
|
|
270
|
+
/** 속한 등급들 — 표준 `MaterialClassID`(복수). */
|
|
271
|
+
materialClassIds?: string[];
|
|
272
|
+
/** 품목 속성 — 표준 `MaterialDefinitionProperty`. 환산 계수가 여기 들어간다. */
|
|
273
|
+
properties?: RefResourceProperty[];
|
|
274
|
+
testSpecificationIds?: string[];
|
|
275
|
+
effectiveStart?: string;
|
|
276
|
+
effectiveEnd?: string;
|
|
23
277
|
}
|
|
24
278
|
export interface ReferenceMaster {
|
|
25
279
|
source: string;
|
|
26
280
|
system: 'wms' | 'yms' | 'mes';
|
|
27
281
|
siteName: string;
|
|
282
|
+
/**
|
|
283
|
+
* 현장(공간). **시각 기준(`timezone`)이 여기 있는 이유**: 한 테넌트가 Rosarito(태평양)와 한국
|
|
284
|
+
* 공장을 함께 가질 수 있다. 테넌트 단위(`Domain.timezone`)로 두면 **둘 중 하나는 반드시 틀린다.**
|
|
285
|
+
* 시각 기준은 **물리 현장의 성질**이므로 공간이 그것을 갖는다(공간 중심 원칙).
|
|
286
|
+
*
|
|
287
|
+
* `timezone` 은 IANA 이름(`'America/Tijuana'`) — 오프셋 숫자가 아니다. 숫자로 두면 일광절약시간을
|
|
288
|
+
* 표현할 수 없고, 나중에 절대 구간으로 옮길 때도 이름이 있어야 계산할 수 있다.
|
|
289
|
+
*/
|
|
28
290
|
space: {
|
|
29
291
|
spaceId?: string;
|
|
30
292
|
width: number;
|
|
31
293
|
depth: number;
|
|
32
294
|
unit?: string;
|
|
295
|
+
timezone?: string;
|
|
33
296
|
geo?: {
|
|
34
297
|
lat: number;
|
|
35
298
|
lon: number;
|
|
@@ -40,6 +303,35 @@ export interface ReferenceMaster {
|
|
|
40
303
|
};
|
|
41
304
|
locations: RefLocation[];
|
|
42
305
|
equipment?: RefEquipment[];
|
|
306
|
+
/** 공정 명세(소요·변동·수율) — 없으면 커널 기본값으로 굴러간다(그 사실은 specCoverage 가 보고). */
|
|
307
|
+
operations?: RefOperation[];
|
|
308
|
+
/**
|
|
309
|
+
* **생산 정의**(레시피·라우트·자재 바인딩) — 커널의 정의-구동 MES 모드 입구.
|
|
310
|
+
*
|
|
311
|
+
* 없으면 커널은 레거시(하드코딩) 흐름으로 돈다. 즉 **현장 레시피를 적어도 이 자리에 실리지 않으면
|
|
312
|
+
* 커널은 그것을 보지 못한다** — 선언과 실행이 갈라지는 지점이라 자리를 연다.
|
|
313
|
+
*/
|
|
314
|
+
mesSpec?: unknown;
|
|
315
|
+
/**
|
|
316
|
+
* 사람 — 선언하지 않으면 **인원 제약이 없는 트윈**이 된다(설비만 있으면 언제나 돌아가는 공장).
|
|
317
|
+
* 공정이 인원을 요구하는데 사람이 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.
|
|
318
|
+
*/
|
|
319
|
+
persons?: RefPerson[];
|
|
320
|
+
/**
|
|
321
|
+
* 물리 자산(반복사용) — 선언하지 않으면 자산 제약이 없는 트윈이다.
|
|
322
|
+
* 공정이 자산을 요구하는데 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.
|
|
323
|
+
*/
|
|
324
|
+
assets?: RefAsset[];
|
|
325
|
+
/**
|
|
326
|
+
* 등급 정의 — 표준 `PersonnelClass`·`EquipmentClass`·`PhysicalAssetClass`.
|
|
327
|
+
* 안 실어도 트윈은 돈다(소속 문자열 그대로 판정). 실으면 상속·유효기간이 살아난다.
|
|
328
|
+
*/
|
|
329
|
+
personnelClasses?: RefResourceClass[];
|
|
330
|
+
equipmentClasses?: RefResourceClass[];
|
|
331
|
+
assetClasses?: RefResourceClass[];
|
|
332
|
+
/** 품목 정의·등급 — 표준 `MaterialDefinition` / `MaterialClass`. 없으면 단위 환산을 할 수 없다. */
|
|
333
|
+
materialDefinitions?: RefMaterialDefinition[];
|
|
334
|
+
materialClasses?: RefResourceClass[];
|
|
43
335
|
}
|
|
44
336
|
export interface IngestedTwin {
|
|
45
337
|
board: any;
|
|
@@ -50,4 +342,18 @@ export interface IngestedTwin {
|
|
|
50
342
|
* 마스터 → 트윈 구조 매핑(순수). 노드타입을 카탈로그로 검증(모르는 타입은 warning, 버리진 않음 — 반영 우선).
|
|
51
343
|
* @param catalog DOMAIN_CATALOG (커널 SSOT). 없으면 검증 생략.
|
|
52
344
|
*/
|
|
345
|
+
/**
|
|
346
|
+
* IANA 시간대 이름 → **그 시각의** UTC 오프셋(분). 의존성 없이 `Intl` 로 푼다.
|
|
347
|
+
*
|
|
348
|
+
* `Intl.DateTimeFormat` 의 `longOffset` 은 `"GMT-07:00"` 처럼 나오고, 이것은 **일광절약시간을 반영한**
|
|
349
|
+
* 값이다. 그래서 8월의 `America/Tijuana` 는 −420(PDT), 1월은 −480(PST)이 된다.
|
|
350
|
+
*
|
|
351
|
+
* **한계를 밝힌다**: 여기서 나오는 것은 **한 시점의** 오프셋이다. 커널은 고정 오프셋으로 하루 중 분을
|
|
352
|
+
* 읽으므로, 보드를 만든 계절과 다른 계절을 길게 예측하면 한 시간 어긋난다. 표준에 충실한 끝 모습은
|
|
353
|
+
* 호스트가 절대 구간(`StartDateTime`/`FinishDateTime`)을 계산해 넣는 것이고, 그때 이 함수는 사라진다.
|
|
354
|
+
*
|
|
355
|
+
* 모르는 이름에는 **0 을 답하지 않는다**(`undefined`) — 0 은 "UTC 라고 선언했다" 는 뜻이고, 모르는 것을
|
|
356
|
+
* 그렇게 바꾸면 판정이 조용히 틀린다.
|
|
357
|
+
*/
|
|
358
|
+
export declare function utcOffsetOf(timezone?: string, at?: Date): number | undefined;
|
|
53
359
|
export declare function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTwin;
|