@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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 이동시간 추정기 — **거리 × 속도.** 커널의 `TRAVEL_MS` 상수를 대체하는 유일한 정당한 길.
|
|
3
|
+
*
|
|
4
|
+
* 커널은 좌표를 모른다(설계: 좌표-free — "얼마 걸리나" 만 소비하고 "어떤 경로로" 는 씬 소관).
|
|
5
|
+
* 그래서 거리 기반 계산은 **호스트가 추정기로 주입**한다(`DurationEstimator` 시임).
|
|
6
|
+
*
|
|
7
|
+
* 좌표는 이미 있다: 마스터의 `RefLocation.footprint` → `masterToTwin` 이 `board.layout`(노드 중심)으로
|
|
8
|
+
* 낸다. 없던 것은 **속도**다 — 속도 없이 거리만으로는 시간이 나오지 않는다. 그래서 설비 속성
|
|
9
|
+
* (ISA-95 `EquipmentSpecificationProperty`)으로 받는다.
|
|
10
|
+
*
|
|
11
|
+
* 정직 규율: **셋 중 하나라도 모르면 답하지 않는다**(undefined → 커널이 명세·상수로 떨어진다).
|
|
12
|
+
* ① 두 노드의 좌표 ② 그 작업을 할 자원 종류의 속도 ③ 좌표의 단위가 미터인지
|
|
13
|
+
* 그리고 같은 노드 안의 작업(처리·체류)은 이동이 아니므로 다루지 않는다 — 거리 0 을 "0초" 로
|
|
14
|
+
* 만들면 공정 시간이 사라진다.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { MeasuredEstimate } from './measured-estimator.js'
|
|
18
|
+
|
|
19
|
+
/** UN/CEFACT 권고 20 공통코드 → m/s 변환. 모르는 코드는 변환하지 않는다(발명 금지). */
|
|
20
|
+
const SPEED_TO_MPS: Record<string, number> = {
|
|
21
|
+
MTS: 1, // metre per second
|
|
22
|
+
KMH: 1000 / 3600, // kilometre per hour
|
|
23
|
+
'2M': 0.3048, // foot per second
|
|
24
|
+
HM: 0.44704 // mile per hour
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 우리가 읽는 canonical 설비 속성 ID — 표준이 속성 ID 어휘를 정하지 않으므로 한 곳에서 정한다. */
|
|
28
|
+
export const EQUIPMENT_PROPERTY = { speed: 'speed' } as const
|
|
29
|
+
|
|
30
|
+
/** 추정기 문맥 — 커널 `DurationContext` 와 같은 모양(커널 타입에 의존하지 않기 위해 구조로 맞춘다). */
|
|
31
|
+
export interface DurationCtx {
|
|
32
|
+
kind: string
|
|
33
|
+
fromNode: string
|
|
34
|
+
toNode: string
|
|
35
|
+
resourceKind?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 추정기 — 숫자(평균만) 또는 추정치 객체(퍼짐까지). 커널 `DurationEstimator` 와 같은 모양.
|
|
40
|
+
* 거리 계산은 숫자를 내고, 실측은 관측된 퍼짐을 함께 낸다.
|
|
41
|
+
*/
|
|
42
|
+
export interface DurationEstimatorLike {
|
|
43
|
+
estimate(ctx: DurationCtx): number | MeasuredEstimate | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TravelEstimatorInput {
|
|
47
|
+
/** 노드 중심 좌표(masterToTwin 산출 `board.layout`). */
|
|
48
|
+
layout?: Record<string, { x: number; y: number }>
|
|
49
|
+
/** 무버(자원) — kind 와 속성. `board.equipment`. */
|
|
50
|
+
equipment?: { id: string; kind: string; properties?: { id: string; value: string; uom?: string }[] }[]
|
|
51
|
+
/** 좌표 단위(공간 `content.unit`). 미터가 아니면 계산하지 않는다(환산을 추측하지 않는다). */
|
|
52
|
+
unit?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface TravelEstimatorResult {
|
|
56
|
+
estimator?: { estimate(ctx: DurationCtx): number | undefined }
|
|
57
|
+
/** 왜 못 만들었나(만들었으면 비어 있음) — 조용히 없는 상태로 두지 않는다. */
|
|
58
|
+
reasons: string[]
|
|
59
|
+
/** 속도를 읽어낸 자원 종류들 — 무엇이 계산 대상인지 드러낸다. */
|
|
60
|
+
speedsByKind: Record<string, number>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 자원 종류별 속도(m/s) — 같은 종류에 값이 여럿이면 첫 유효값. 단위 없는 속도는 받지 않는다. */
|
|
64
|
+
function readSpeeds(equipment: TravelEstimatorInput['equipment'], reasons: string[]): Record<string, number> {
|
|
65
|
+
const out: Record<string, number> = {}
|
|
66
|
+
for (const m of equipment ?? []) {
|
|
67
|
+
if (out[m.kind] !== undefined) continue
|
|
68
|
+
const p = m.properties?.find(x => x.id === EQUIPMENT_PROPERTY.speed)
|
|
69
|
+
if (!p) continue
|
|
70
|
+
const n = Number(p.value)
|
|
71
|
+
if (!Number.isFinite(n) || n <= 0) { reasons.push(`equipment "${m.id}": speed "${p.value}" is not a positive number`); continue }
|
|
72
|
+
/* 단위가 없으면 m/s 라고 **가정하지 않는다** — 3 이 초속 3m 인지 시속 3km 인지 뜻이 완전히 다르다. */
|
|
73
|
+
if (!p.uom) { reasons.push(`equipment "${m.id}": speed has no unit of measure (expected UN/CEFACT code such as MTS or KMH)`); continue }
|
|
74
|
+
const factor = SPEED_TO_MPS[p.uom]
|
|
75
|
+
if (factor === undefined) { reasons.push(`equipment "${m.id}": unknown speed unit "${p.uom}"`); continue }
|
|
76
|
+
out[m.kind] = n * factor
|
|
77
|
+
}
|
|
78
|
+
return out
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 이동시간 추정기를 만든다 — 만들 수 없으면 **이유와 함께** 비운다.
|
|
83
|
+
* `estimator` 가 undefined 면 커널은 명세·상수로 굴러가고, `reasons` 가 왜 그런지 말한다.
|
|
84
|
+
*/
|
|
85
|
+
export function buildTravelEstimator(input: TravelEstimatorInput): TravelEstimatorResult {
|
|
86
|
+
const reasons: string[] = []
|
|
87
|
+
const layout = input.layout
|
|
88
|
+
const unit = input.unit ?? 'm'
|
|
89
|
+
if (unit !== 'm') reasons.push(`space unit is "${unit}", not metres — travel time is not computed (no assumed conversion)`)
|
|
90
|
+
if (!layout || !Object.keys(layout).length) reasons.push('no location coordinates (board.layout empty) — travel time cannot be derived from distance')
|
|
91
|
+
const speedsByKind = readSpeeds(input.equipment, reasons)
|
|
92
|
+
if (!Object.keys(speedsByKind).length) reasons.push('no resource declares a speed property — distance alone cannot produce a duration')
|
|
93
|
+
if (reasons.length) return { reasons, speedsByKind }
|
|
94
|
+
|
|
95
|
+
const lay = layout as Record<string, { x: number; y: number }>
|
|
96
|
+
return {
|
|
97
|
+
reasons,
|
|
98
|
+
speedsByKind,
|
|
99
|
+
estimator: {
|
|
100
|
+
estimate(ctx) {
|
|
101
|
+
/* 같은 자리에서 벌어지는 일(처리·체류)은 이동이 아니다 — 다루지 않고 넘긴다. */
|
|
102
|
+
if (!ctx.fromNode || !ctx.toNode || ctx.fromNode === ctx.toNode) return undefined
|
|
103
|
+
const a = lay[ctx.fromNode]
|
|
104
|
+
const b = lay[ctx.toNode]
|
|
105
|
+
if (!a || !b) return undefined // 좌표를 모르는 노드(관측으로 알게 된 자리 등)
|
|
106
|
+
const speed = ctx.resourceKind ? speedsByKind[ctx.resourceKind] : undefined
|
|
107
|
+
if (!speed) return undefined // 자원 종류를 모르거나 그 종류의 속도가 없다
|
|
108
|
+
const dist = Math.hypot(b.x - a.x, b.y - a.y)
|
|
109
|
+
if (!Number.isFinite(dist) || dist <= 0) return undefined
|
|
110
|
+
return Math.round((dist / speed) * 1000)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 추정기 사슬 — 앞선 것이 답하면 그 값, 아니면 다음. **실측 → 계산 → (커널이) 명세 → 상수** 순서를
|
|
118
|
+
* 이 사슬과 커널 `durationOf` 가 함께 만든다. 앞에 둘수록 강한 근거다.
|
|
119
|
+
*/
|
|
120
|
+
export function chainEstimators(estimators: (DurationEstimatorLike | undefined)[]): DurationEstimatorLike | undefined {
|
|
121
|
+
const live = estimators.filter(Boolean) as DurationEstimatorLike[]
|
|
122
|
+
if (!live.length) return undefined
|
|
123
|
+
if (live.length === 1) return live[0]
|
|
124
|
+
return {
|
|
125
|
+
estimate(ctx) {
|
|
126
|
+
for (const e of live) {
|
|
127
|
+
const v = e.estimate(ctx)
|
|
128
|
+
if (v !== undefined) return v
|
|
129
|
+
}
|
|
130
|
+
return undefined
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|