@operato/twin-kernel 0.2.3 → 0.4.0
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 +46 -5
- package/dist/capability.js +2 -2
- package/dist/capacity.d.ts +99 -0
- package/dist/capacity.js +172 -0
- package/dist/contract.d.ts +836 -45
- package/dist/contract.js +519 -7
- package/dist/divergence.d.ts +1 -1
- package/dist/divergence.js +8 -5
- package/dist/domain-catalog.d.ts +4 -4
- package/dist/domain-catalog.js +4 -4
- package/dist/domain-definition.d.ts +50 -5
- package/dist/domain-definition.js +6 -6
- package/dist/epcis.d.ts +12 -0
- package/dist/epcis.js +12 -0
- package/dist/event-journal.d.ts +31 -1
- package/dist/event-journal.js +27 -1
- package/dist/flow-engine.d.ts +323 -36
- package/dist/flow-engine.js +868 -164
- package/dist/forecast.d.ts +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/job-response.d.ts +59 -0
- package/dist/job-response.js +56 -0
- package/dist/kernel.d.ts +5 -0
- package/dist/kernel.js +31 -11
- package/dist/mes-kernel.d.ts +28 -0
- package/dist/mes-kernel.js +83 -29
- package/dist/mes-profile.d.ts +2 -2
- package/dist/mes-profile.js +11 -11
- package/dist/observed-reducer.d.ts +68 -7
- package/dist/observed-reducer.js +248 -41
- package/dist/task-fold.d.ts +2 -2
- package/dist/task-fold.js +4 -4
- package/dist/vocabulary.d.ts +19 -0
- package/dist/vocabulary.js +66 -0
- package/dist/wms-profile.d.ts +2 -2
- package/dist/wms-profile.js +6 -6
- package/dist/yms-kernel.js +36 -15
- package/dist/yms-profile.d.ts +2 -2
- package/dist/yms-profile.js +6 -6
- package/dist-cjs/index.cjs +1618 -270
- package/package.json +1 -1
package/dist/flow-engine.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { Attention, BoardDef, CanonicalEnvelope, Command, CommandAck, EventHandler,
|
|
1
|
+
import type { ISOTime, MaterialQuantity, WorkCalendarEntry, EffectivePeriod, Effectivity, OffCalendarReason, ResourceProperty, ResourceClassDef, MaterialDefinition, Attention, BoardDef, CanonicalEnvelope, Command, CommandAck, EventHandler, EquipmentMotion, OeeMetrics, AssetState, GeneratorSpec, OrderState, PersonState, ScenarioControl, StateSnapshot, TwinKernel, Unsubscribe, LocationState, ItemState, EquipmentState, OrderStatusDelta, TaskState } from './contract.ts';
|
|
2
2
|
import type { EpcisEvent, BizTransactionElement } from './epcis.ts';
|
|
3
3
|
import type { AllocationPolicy, SlotView } from './allocation-policy.ts';
|
|
4
4
|
import type { DurationEstimator, DurationContext } from './duration-estimator.ts';
|
|
5
5
|
import type { OperationDef } from './domain-definition.ts';
|
|
6
|
-
|
|
6
|
+
import { type CapacityAnalysis } from './capacity.ts';
|
|
7
|
+
export interface FlowLocation {
|
|
7
8
|
id: string;
|
|
8
9
|
type: string;
|
|
9
10
|
capacity: number;
|
|
@@ -24,6 +25,10 @@ export interface FlowItem {
|
|
|
24
25
|
epc: string;
|
|
25
26
|
location: string;
|
|
26
27
|
disposition: string;
|
|
28
|
+
/** 무슨 품목인가 — 표준 `MaterialLot.MaterialDefinitionID`. 보통은 `gtin` 이 곧 정의 id 라 비어 있다. */
|
|
29
|
+
definitionId?: string;
|
|
30
|
+
/** 로트의 부분(표준 `MaterialSubLot.ID`) — 비직렬 로트가 자리마다 갈릴 때. */
|
|
31
|
+
subLotId?: string;
|
|
27
32
|
gtin?: string;
|
|
28
33
|
qty?: number;
|
|
29
34
|
/** 수량 단위(UN/CEFACT). 개수면 없다 — 없는 것을 'EA' 로 꾸미지 않는다. */
|
|
@@ -35,38 +40,62 @@ export interface FlowItem {
|
|
|
35
40
|
expiry?: number;
|
|
36
41
|
/** 개체·로트 마스터데이터 원문 — 생겨날 때 정해지고 뒤 이벤트가 지우지 않는다. */
|
|
37
42
|
ilmd?: Record<string, unknown>;
|
|
43
|
+
/** 선언된 모든 수량 — 표준 `MaterialLot.Quantity`(복수). 관측에서 온 물품이 여러 단위를 들 수 있다. */
|
|
44
|
+
quantities?: MaterialQuantity[];
|
|
38
45
|
}
|
|
39
46
|
/** 물리 자산 — 반복사용(팔레트·랙·용기). 설비도 물품도 아니다(GRAI vs SSCC 구분은 계약 주석 참조). */
|
|
40
|
-
export interface FlowAsset {
|
|
47
|
+
export interface FlowAsset extends EffectivePeriod {
|
|
41
48
|
id: string;
|
|
42
|
-
|
|
49
|
+
/** 속한 등급들 — 표준 `PhysicalAssetClassID`(복수). */
|
|
50
|
+
assetClassIds?: string[];
|
|
43
51
|
location?: string;
|
|
44
52
|
status: string;
|
|
45
53
|
taskId: string | null;
|
|
46
54
|
carrying?: string;
|
|
55
|
+
/** 자원 속성 — 표준 `PhysicalAssetProperty`. */
|
|
56
|
+
properties?: ResourceProperty[];
|
|
57
|
+
/** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */
|
|
58
|
+
testSpecificationIds?: string[];
|
|
47
59
|
}
|
|
48
60
|
/** 사람 — 설비와 별개 자원(고장·OEE 가 아니라 등급·교대로 산다). */
|
|
49
|
-
export interface FlowPerson {
|
|
61
|
+
export interface FlowPerson extends EffectivePeriod {
|
|
50
62
|
id: string;
|
|
51
|
-
|
|
63
|
+
/** 속한 등급들 — 표준 `PersonnelClassID`(복수). 자격이 여럿인 사람을 표현한다. */
|
|
64
|
+
personnelClassIds?: string[];
|
|
52
65
|
status: string;
|
|
53
66
|
taskId: string | null;
|
|
54
67
|
window?: {
|
|
55
68
|
startHour: number;
|
|
56
69
|
endHour: number;
|
|
57
70
|
};
|
|
71
|
+
/** 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 있으면 `window` 보다 이것을 본다. */
|
|
72
|
+
workCalendar?: WorkCalendarEntry[];
|
|
73
|
+
/** 지금 어디에 있나 — 표준 `Person.OperationalLocation`. 커널은 사람을 움직이지 않는다(마스터·관측이 말한다). */
|
|
74
|
+
location?: string;
|
|
75
|
+
/** 자원 속성 — 표준 `PersonProperty`. */
|
|
76
|
+
properties?: ResourceProperty[];
|
|
77
|
+
/** 자격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */
|
|
78
|
+
testSpecificationIds?: string[];
|
|
58
79
|
}
|
|
59
|
-
export interface
|
|
80
|
+
export interface FlowEquipment extends EffectivePeriod {
|
|
60
81
|
id: string;
|
|
61
82
|
kind: string;
|
|
62
83
|
location: string;
|
|
63
84
|
status: string;
|
|
64
85
|
taskId: string | null;
|
|
86
|
+
/** 붙박인 자리 — 소속. `location` 은 지금 위치. 고정 설비는 둘이 항상 같다(EquipmentState.homeLocation 참조). */
|
|
87
|
+
homeLocation?: string;
|
|
88
|
+
/** 자원 속성 — 표준 `EquipmentProperty`(§ResourceProperty). */
|
|
89
|
+
properties?: ResourceProperty[];
|
|
90
|
+
/** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */
|
|
91
|
+
testSpecificationIds?: string[];
|
|
65
92
|
/** 교대(가동시간) — 지정 시 이 시간대에만 배정된다. 미지정=24시간 가용. */
|
|
66
93
|
window?: {
|
|
67
94
|
startHour: number;
|
|
68
95
|
endHour: number;
|
|
69
96
|
};
|
|
97
|
+
/** 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 있으면 `window` 보다 이것을 본다. */
|
|
98
|
+
workCalendar?: WorkCalendarEntry[];
|
|
70
99
|
runMs: number;
|
|
71
100
|
setupMs: number;
|
|
72
101
|
downMs: number;
|
|
@@ -85,12 +114,29 @@ export interface FlowTask {
|
|
|
85
114
|
id: string;
|
|
86
115
|
kind: string;
|
|
87
116
|
status: 'created' | 'in-progress' | 'completed';
|
|
117
|
+
/**
|
|
118
|
+
* **실제로 들어가고 나온 자재** — ISA-95 `JobResponse.MaterialActual`.
|
|
119
|
+
*
|
|
120
|
+
* 명세(계획)가 아니라 **일어난 일**이다. 투입 인원·설비가 이미 작업 델타에 타고 있으니 자재도 같은
|
|
121
|
+
* 채널에 실어야 실적을 **한 곳에서** 읽는다(EPCIS 이벤트에서 되짚으려면 작업과 잇는 끈이 없다).
|
|
122
|
+
*/
|
|
123
|
+
materialActual?: {
|
|
124
|
+
definitionId: string;
|
|
125
|
+
use: 'consumed' | 'produced';
|
|
126
|
+
quantity: number;
|
|
127
|
+
uom?: string;
|
|
128
|
+
}[];
|
|
88
129
|
itemEpc: string;
|
|
89
130
|
fromNode: string;
|
|
90
131
|
toNode: string;
|
|
91
132
|
resource: string | null;
|
|
92
133
|
/** 착수 시각(절대 sim-clock) — 보간 앵커(모션과 같은 규율). */
|
|
93
134
|
startedAtSimMs?: number;
|
|
135
|
+
/** 우선순위 — 표준 `JobOrder.Priority`(작은 값이 급하다). 배정 순서를 정한다. */
|
|
136
|
+
priority?: number;
|
|
137
|
+
/** 예정 창 — 표준 `JobOrder.StartTime`/`EndTime`. 실제 착수와 다른 축. */
|
|
138
|
+
startTime?: string;
|
|
139
|
+
endTime?: string;
|
|
94
140
|
/** 이 작업에 투입된 사람들 — 설비와 별개 축(설비 1대 + 작업자 2명이 동시에 잡힌다). */
|
|
95
141
|
personnel?: string[];
|
|
96
142
|
/** 이 작업에 투입된 물리 자산(팔레트 등). */
|
|
@@ -106,9 +152,9 @@ export interface FlowTask {
|
|
|
106
152
|
appliedSetupMs?: number;
|
|
107
153
|
/**
|
|
108
154
|
* 작업 의도(씬 3 직교의도 정렬 · ISA-95 이동 vs 변환). 기본 'transport'.
|
|
109
|
-
* transport:
|
|
110
|
-
* process:
|
|
111
|
-
* dwell:
|
|
155
|
+
* transport: eq 가 item 을 fromNode→toNode 운반(완료 시 자원 위치 이동 + 이동 모션).
|
|
156
|
+
* process: 자리에서 item 변환(자원=정지 설비 — 위치 불변, 모션 없음). 가공을 거리-0 운반으로 위장 안 함.
|
|
157
|
+
* dwell: 자리에서 시간만 소비(무자원 체류·큐어링 — 즉시 진행, 자원 불요).
|
|
112
158
|
*/
|
|
113
159
|
intent?: 'transport' | 'process' | 'dwell';
|
|
114
160
|
}
|
|
@@ -132,25 +178,31 @@ export interface FlowOrder {
|
|
|
132
178
|
held?: boolean;
|
|
133
179
|
dockDoor?: string;
|
|
134
180
|
windowStartMs?: number;
|
|
181
|
+
/** 우선순위 — 표준 `OperationsRequest.Priority`(작은 값이 급하다). 할당 순서를 정한다. */
|
|
182
|
+
priority?: number;
|
|
183
|
+
/** 예정 창 — 표준 `StartTime`/`EndTime`. 납기가 있어야 "늦었나" 를 물을 수 있다. */
|
|
184
|
+
startTime?: string;
|
|
185
|
+
endTime?: string;
|
|
135
186
|
}
|
|
136
187
|
interface Rng {
|
|
137
188
|
(): number;
|
|
138
189
|
state: number;
|
|
139
190
|
}
|
|
140
191
|
/**
|
|
141
|
-
* 주목신호 계산(순수) — State 스냅샷(
|
|
192
|
+
* 주목신호 계산(순수) — State 스냅샷(equipment/locations/orders)에서 attentions 파생.
|
|
142
193
|
* FlowEngine.computeAttentions(sim) 와 live projector 미러가 **공유**하는 계산 층(face2-inbound-live §1.1):
|
|
143
194
|
* sim/live 가 같은 임계값·규칙을 쓴다. good/scrap 미제공(관측 상태에 없음)이면 scrap-high 는 자연 스킵(metric 층 갭).
|
|
144
195
|
*/
|
|
145
196
|
export declare function deriveAttentions(view: {
|
|
146
|
-
|
|
197
|
+
equipment: {
|
|
147
198
|
id: string;
|
|
148
199
|
status?: string;
|
|
149
200
|
location?: string;
|
|
150
201
|
goodCount?: number;
|
|
151
202
|
scrapCount?: number;
|
|
203
|
+
offShift?: boolean;
|
|
152
204
|
}[];
|
|
153
|
-
|
|
205
|
+
locations: {
|
|
154
206
|
id: string;
|
|
155
207
|
capacity?: number;
|
|
156
208
|
occupancy?: number;
|
|
@@ -158,8 +210,23 @@ export declare function deriveAttentions(view: {
|
|
|
158
210
|
orders: {
|
|
159
211
|
id: string;
|
|
160
212
|
held?: boolean;
|
|
213
|
+
status?: string;
|
|
214
|
+
endTime?: ISOTime;
|
|
215
|
+
priority?: number;
|
|
216
|
+
}[];
|
|
217
|
+
/**
|
|
218
|
+
* 작업 — 대기 행렬을 재는 데 쓴다. **주지 않으면 재지 않는다**(모르면 판단하지 않는다).
|
|
219
|
+
* 두 구동(시뮬·미러)이 **똑같이 넣어야** 한다 — 한쪽만 넣으면 같은 공장이 한쪽에서만 막힌
|
|
220
|
+
* 것처럼 보인다.
|
|
221
|
+
*/
|
|
222
|
+
tasks?: {
|
|
223
|
+
id: string;
|
|
224
|
+
kind: string;
|
|
225
|
+
status: string;
|
|
161
226
|
}[];
|
|
162
|
-
}, acked?: ReadonlySet<string
|
|
227
|
+
}, acked?: ReadonlySet<string>,
|
|
228
|
+
/** 지금(ISO) — 납기 판정에 필요하다. **주지 않으면 지연을 판정하지 않는다**(모르면 판단하지 않는다). */
|
|
229
|
+
nowIso?: ISOTime): Attention[];
|
|
163
230
|
/** OEE 계측 카운터 — sim 은 tick 으로 누적, live 는 실 텔레메트리 또는 이벤트 누적기가 채운다(face2-inbound-live §1.1). */
|
|
164
231
|
export interface OeeCounters {
|
|
165
232
|
runMs: number;
|
|
@@ -178,9 +245,21 @@ export interface OeeCounters {
|
|
|
178
245
|
export declare function computeOee(c: OeeCounters, nowMs: number): OeeMetrics;
|
|
179
246
|
export declare abstract class FlowEngine implements TwinKernel {
|
|
180
247
|
tenantId: string;
|
|
181
|
-
|
|
248
|
+
locations: Map<string, FlowLocation>;
|
|
182
249
|
items: Map<string, FlowItem>;
|
|
183
|
-
|
|
250
|
+
equipment: Map<string, FlowEquipment>;
|
|
251
|
+
/** 등급 정의(표준 `<X>Class`) — 상속·유효기간 판정의 재료. 선언 안 하면 비어 있고, 소속 그대로 판정한다. */
|
|
252
|
+
protected classDefs: {
|
|
253
|
+
personnel?: ResourceClassDef[];
|
|
254
|
+
equipment?: ResourceClassDef[];
|
|
255
|
+
asset?: ResourceClassDef[];
|
|
256
|
+
material?: ResourceClassDef[];
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* 품목 정의 색인(표준 `MaterialDefinition`) — 단위 환산의 **유일한 근거**.
|
|
260
|
+
* 비어 있으면 환산하지 않는다(계수를 모르는데 값을 만들지 않는다).
|
|
261
|
+
*/
|
|
262
|
+
protected materialDefs: Map<string, MaterialDefinition>;
|
|
184
263
|
/** 사람 — 선언하지 않으면 빈 맵(인원 제약 없는 트윈, 기존 거동). */
|
|
185
264
|
persons: Map<string, FlowPerson>;
|
|
186
265
|
/** 물리 자산 — 선언하지 않으면 빈 맵(자산 제약 없는 트윈, 기존 거동). */
|
|
@@ -233,34 +312,45 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
233
312
|
protected abstract onTaskComplete(task: FlowTask): void;
|
|
234
313
|
loadBoard(def: BoardDef): void;
|
|
235
314
|
/**
|
|
236
|
-
* what-if 구성 변주 — fork(또는 실행 중) 엔진에
|
|
315
|
+
* what-if 구성 변주 — fork(또는 실행 중) 엔진에 설비 추가. loadBoard 설비 삽입과 동일 규약.
|
|
237
316
|
* 기본은 mtbf 미지정(고장 없는 신뢰 자원) → sampleExp(rng) 무소비라 baseline fork 와 깨끗이 비교 가능.
|
|
238
317
|
*/
|
|
239
|
-
|
|
318
|
+
addEquipment(m: {
|
|
240
319
|
id: string;
|
|
241
320
|
kind: string;
|
|
242
|
-
|
|
321
|
+
homeLocation: string;
|
|
243
322
|
mtbfMs?: number;
|
|
244
323
|
mttrMs?: number;
|
|
245
324
|
}): void;
|
|
246
325
|
/**
|
|
247
|
-
* 관측 상태 주입(라이브 예측용, kernel-unification P1) — 외부 관측 스냅샷(
|
|
326
|
+
* 관측 상태 주입(라이브 예측용, kernel-unification P1) — 외부 관측 스냅샷(재고·설비·자리)과
|
|
248
327
|
* 저널 오더(원값+라인)로 이 커널의 맵을 채운다. tick 으로 만든 게 아니라 "현재 관측된 현실"을 심어
|
|
249
328
|
* 이후 fork/tick 으로 예측한다. 라이브 런타임은 여전히 projector 미러 — 이 커널은 예측용 임시본.
|
|
250
329
|
* 오더는 남은 데맨드(라인별 requested-fulfilled)를 'created' 로 복원(현재 재고에서 재계획).
|
|
251
330
|
* 진행 중 개별 task 의 내부 상태는 관측만으론 복원 불가 → 재계획에 맡김(정직한 한계).
|
|
252
331
|
*/
|
|
332
|
+
/**
|
|
333
|
+
* **이어서 센다** — 재기동 뒤 저널을 이어 쓸 때.
|
|
334
|
+
*
|
|
335
|
+
* 커널은 리비전을 0부터 센다. 새로 시작하는 우주라면 맞지만, **이어지는 현실**(sim-world)에서는
|
|
336
|
+
* 그 번호가 이미 저널에 있는 행과 겹친다 — 같은 트윈에 revision 7이 두 개 생기고, 재생 순서가
|
|
337
|
+
* 뒤섞이며, 시간여행이 엉뚱한 시점을 답한다. 조용히 깨지는 종류다(오류가 안 난다).
|
|
338
|
+
*
|
|
339
|
+
* 그래서 이어 쓰기 전에 마지막 번호를 알려 준다. **뒤로는 못 간다** — 뒤로 가는 것은 곧 겹치는
|
|
340
|
+
* 것이고, 그것을 허용하면 이 함수가 막으려던 일이 이 함수를 통해 일어난다.
|
|
341
|
+
*/
|
|
342
|
+
resumeRevision(from: number): void;
|
|
253
343
|
hydrateObserved(snap: {
|
|
254
|
-
|
|
344
|
+
locations: LocationState[];
|
|
255
345
|
items: ItemState[];
|
|
256
|
-
|
|
346
|
+
equipment: EquipmentState[];
|
|
257
347
|
persons?: PersonState[];
|
|
258
348
|
assets?: AssetState[];
|
|
259
349
|
tasks?: TaskState[];
|
|
260
350
|
orders?: OrderState[];
|
|
261
351
|
}, orders?: OrderStatusDelta[]): void;
|
|
262
|
-
/** what-if 구성 변주 —
|
|
263
|
-
|
|
352
|
+
/** what-if 구성 변주 — 자리 용량 변경(fork 대상). 존재하면 true. */
|
|
353
|
+
setLocationCapacity(locationId: string, capacity: number): boolean;
|
|
264
354
|
/**
|
|
265
355
|
* forecast 몬테카를로 — fork 의 RNG 만 재시드(시나리오·상태·gens·in-flight 는 보존).
|
|
266
356
|
* "현재 조건 지속"을 유지한 채 **미래 확률만** 변주(도착·고장 타이밍 등) → run 마다 다른 표본.
|
|
@@ -274,6 +364,15 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
274
364
|
* State 델타를 유발한다(command → 행위 → 관측 폐루프).
|
|
275
365
|
*/
|
|
276
366
|
private _acked;
|
|
367
|
+
/**
|
|
368
|
+
* 완료 시점에 **주체가 사라져 접은 작업 수** — 조용한 누락이 되지 않게 센다.
|
|
369
|
+
*
|
|
370
|
+
* 물품이 포장·출하·소비로 없어지는 것은 정상이지만, 그 물품을 향한 작업이 남아 있었다는 것은
|
|
371
|
+
* 상류에 어긋남이 있다는 신호다. 0 이 아니면 그 사실을 소비처가 볼 수 있어야 한다.
|
|
372
|
+
*/
|
|
373
|
+
protected abandonedTasks: number;
|
|
374
|
+
/** 주목 신호가 **처음 성립한 시각**(id → ISO). 조건이 사라지면 지운다 — 재발은 새 시작이다. */
|
|
375
|
+
private _attentionSince;
|
|
277
376
|
dispatch(cmd: Command): CommandAck;
|
|
278
377
|
/** 도메인 커맨드 처리(order.release 등). 기본은 거절 — 도메인이 override. */
|
|
279
378
|
protected handleCommand(cmd: Command): CommandAck;
|
|
@@ -288,7 +387,39 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
288
387
|
* rng 는 fork 의 시나리오 load 시 재시드(드레인 예측은 생성 없어 rng 무관·결정적).
|
|
289
388
|
*/
|
|
290
389
|
fork(tenantId?: string): this;
|
|
390
|
+
/**
|
|
391
|
+
* 이 커널의 **지금**(ms) — 시각으로 바뀌는 모든 판정의 단일 기준.
|
|
392
|
+
*
|
|
393
|
+
* ── 관측 모드에서 시계가 멈춰 있었다 ────────────────────────────────────
|
|
394
|
+
* `apply()` 는 `clockMs` 를 밀지 않는다(관측은 시간을 굴리지 않는다). 그래서 라이브 트윈의 "지금" 이
|
|
395
|
+
* **BASE_EPOCH(2026-01-01)에 얼어 있었다.** 실 이벤트는 실제 시각을 달고 오므로 결과가 이렇게 된다:
|
|
396
|
+
* · **납기 초과를 영원히 보고하지 않는다** — 실 납기가 항상 미래로 보인다
|
|
397
|
+
* · 폐기·도입예정 판정이 틀린다(§EffectivePeriod)
|
|
398
|
+
* · 교대 판정이 틀린다(§offCalendarAt)
|
|
399
|
+
* 셋 다 "시각으로만 바뀌는 사실" 이라 같은 결함이었다. 그래서 기준을 **한 곳**으로 모은다.
|
|
400
|
+
*
|
|
401
|
+
* 관측 중이면 **마지막으로 들은 발생 시각**을 쓴다(미러의 `lastObservedMs` 와 같은 기준 — 규칙 한 벌).
|
|
402
|
+
* 아직 아무것도 못 들었으면 시뮬 기준으로 떨어진다(그때는 판정할 사실도 없다).
|
|
403
|
+
*/
|
|
404
|
+
protected nowMs(): number;
|
|
291
405
|
protected now(): string;
|
|
406
|
+
/**
|
|
407
|
+
* 자극이 선언한 **약속**을 오더 필드로 — 표준 `OperationsRequest.Priority`·`StartTime`·`EndTime`.
|
|
408
|
+
*
|
|
409
|
+
* **base 에 한 곳만 둔다.** 오더를 만드는 곳이 도메인마다 있어(WMS 판매오더·MES 작업지시·YMS
|
|
410
|
+
* 어포인트먼트) 각자 계산하면 시간축이나 규약이 갈린다 — 실제로 한 번 갈렸다(납기를 `clockMs` 로
|
|
411
|
+
* 재고 `now()` 와 비교해 갓 만든 오더가 56년 늦은 것으로 판정됐다).
|
|
412
|
+
*
|
|
413
|
+
* 선언이 없으면 **빈 객체**를 준다 — 없는 약속을 만들지 않는다.
|
|
414
|
+
*/
|
|
415
|
+
protected promiseOf(spec: {
|
|
416
|
+
promisedLeadMinutes?: number;
|
|
417
|
+
priority?: number;
|
|
418
|
+
}): {
|
|
419
|
+
priority?: number;
|
|
420
|
+
startTime?: string;
|
|
421
|
+
endTime?: string;
|
|
422
|
+
};
|
|
292
423
|
protected randInt(min: number, max: number): number;
|
|
293
424
|
/**
|
|
294
425
|
* 관측 구동(P0 스파이크) — **이벤트로 커널을 굴린다.**
|
|
@@ -318,6 +449,28 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
318
449
|
* 같은 key 를 다시 실으면 덮어쓴다(정의가 권위).
|
|
319
450
|
*/
|
|
320
451
|
loadOperations(ops?: OperationDef[]): void;
|
|
452
|
+
/**
|
|
453
|
+
* 라우트(공정 순서) — 수율을 거슬러 올릴 때 필요하다. 기본은 모른다(선언 순서를 쓴다).
|
|
454
|
+
* 생산 정의를 가진 커널이 override 해서 자기 라우트를 답한다.
|
|
455
|
+
*/
|
|
456
|
+
protected routeKeys(): string[] | undefined;
|
|
457
|
+
/**
|
|
458
|
+
* **이 공장이 하루 몇 대를 낼 수 있는가** — 굴려 보지 않고 답한다.
|
|
459
|
+
*
|
|
460
|
+
* 커널이 직접 답하는 이유: 필요한 사실이 전부 여기 있다(공정 명세·설비와 신뢰도·인원·물리자산·
|
|
461
|
+
* 자리·근무 달력·시각 기준). 밖에서 모으면 그 값을 옮겨 적게 되고, 한쪽만 바뀌는 순간 "충분하다"
|
|
462
|
+
* 가 조용히 거짓이 된다. 계산 자체는 순수 함수(`analyzeCapacity`)에 맡긴다.
|
|
463
|
+
*
|
|
464
|
+
* 달력은 **자원이 선언한 것**을 쓴다. 자원마다 다른 달력을 쓰는 현장이면 대표를 고를 수 없으므로
|
|
465
|
+
* 그 사실을 결과에 실어 보낸다(`mixedCalendars`) — 조용히 하나를 골라 계산하면 천장이 틀린 채로
|
|
466
|
+
* 그럴듯해 보인다.
|
|
467
|
+
*/
|
|
468
|
+
capacity(opts: {
|
|
469
|
+
unitsPerDay: number;
|
|
470
|
+
sampleWeekStartMs: number;
|
|
471
|
+
}): CapacityAnalysis & {
|
|
472
|
+
mixedCalendars: boolean;
|
|
473
|
+
};
|
|
321
474
|
/**
|
|
322
475
|
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ② 명세(ISA-95 Duration + 변동) → ③ 도메인 상수.**
|
|
323
476
|
*
|
|
@@ -361,17 +514,26 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
361
514
|
declaredDurations: number;
|
|
362
515
|
defaultDurations: number;
|
|
363
516
|
};
|
|
364
|
-
/**
|
|
517
|
+
/**
|
|
518
|
+
* `skuMix` 에서 weight 로 gtin 선택(rng) — 도착·오더 자극의 품목 결정.
|
|
519
|
+
*
|
|
520
|
+
* **빈 목록이면 고르지 않는다**(`undefined`). 예전에는 `mix[mix.length - 1].gtin` 으로 떨어져
|
|
521
|
+
* `mix[-1]` 이 undefined 가 되고 거기서 던졌다 — 그 예외가 서버까지 올라가 **정확도 추세 전체를
|
|
522
|
+
* 죽였다**(품목 구성을 선언하지 않은 자극 하나가 예측 전체를 껐다).
|
|
523
|
+
*
|
|
524
|
+
* 없는 품목을 지어내지 않는다: 무엇을 만들지 모르면 **만들지 않는 것**이 맞고, 부르는 쪽이
|
|
525
|
+
* 그 사실을 알고 건너뛴다.
|
|
526
|
+
*/
|
|
365
527
|
protected pickGtin(mix: {
|
|
366
528
|
gtin: string;
|
|
367
529
|
weight: number;
|
|
368
|
-
}[]): string;
|
|
369
|
-
protected
|
|
530
|
+
}[]): string | undefined;
|
|
531
|
+
protected locationByType(type: string): FlowLocation | undefined;
|
|
370
532
|
/**
|
|
371
533
|
* process 변화 대수(EPCIS TransformationEvent · ISA-95 Material Consumed/Produced · 씬 Processable.transform).
|
|
372
534
|
* inputs 소비 → outputs 생산. 입출력 arity 가 곧 대수:
|
|
373
535
|
* merge N→1(조립) · split 1→N(분해) · transform 1→1(타입변경) · loss N→0(소실) · gain 0→N(부산물·생성).
|
|
374
|
-
* 아이템 상태(소비/생산)와
|
|
536
|
+
* 아이템 상태(소비/생산)와 자리 점유를 갱신하고 계보(TransformationEvent)를 방출한다.
|
|
375
537
|
* 도메인은 이 원시만 호출 — onTaskComplete 의 소비/생산/EPCIS 손코딩을 대체.
|
|
376
538
|
*/
|
|
377
539
|
protected transform(inputs: string[], outputs: {
|
|
@@ -429,7 +591,7 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
429
591
|
}): void;
|
|
430
592
|
/**
|
|
431
593
|
* containment 분해(EPCIS AggregationEvent DELETE) — 부모(용기)에서 자식들을 풀어냄.
|
|
432
|
-
* materialize 지정 시 자식이 독립 아이템으로 등장(materialize:
|
|
594
|
+
* materialize 지정 시 자식이 독립 아이템으로 등장(materialize: 자리 배치 + occupancy + ObjectEvent ADD).
|
|
433
595
|
* 미지정 시 이벤트만.
|
|
434
596
|
*/
|
|
435
597
|
protected disaggregate(parent: string, children: string[], opts: {
|
|
@@ -442,10 +604,10 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
442
604
|
}): void;
|
|
443
605
|
/** 품질 보고(OEE Quality) — 도메인이 완료 시 자원별 양품/불량 1건 계상(예: 용접 수율). */
|
|
444
606
|
protected recordOutput(moverId: string | null, good: boolean): void;
|
|
445
|
-
/**
|
|
607
|
+
/** 설비 OEE(스냅샷 파생) — Availability×Performance×Quality. planned = 설비 존재 sim 시간(clockMs). */
|
|
446
608
|
private oeeOf;
|
|
447
|
-
/** 정책에 넘길 특정 타입
|
|
448
|
-
protected slotViews(
|
|
609
|
+
/** 정책에 넘길 특정 타입 자리의 관측 뷰 — 예약(그 자리로 향하는 in-flight task) 포함. */
|
|
610
|
+
protected slotViews(locationType: string): SlotView[];
|
|
449
611
|
protected emit(event: EpcisEvent): void;
|
|
450
612
|
protected emitOp(eventType: string, data: unknown): void;
|
|
451
613
|
/**
|
|
@@ -461,7 +623,7 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
461
623
|
protected emitPerson(p: FlowPerson): void;
|
|
462
624
|
/** 설비 상태 전이 — `taskId` 를 함께 싣는다(사람·자산 델타와 같은 규칙). 없으면 미러가 "이 설비가
|
|
463
625
|
* 무슨 일을 하는 중인가" 를 알 수 없어 작업↔자원 연결이 한쪽에서만 성립한다. */
|
|
464
|
-
protected
|
|
626
|
+
protected emitEquipment(m: FlowEquipment, motion?: EquipmentMotion): void;
|
|
465
627
|
/** 오더 델타 — **라인까지 싣는다.** 라인이 빠지면 미러가 남은 데맨드를 라인별로 재계획할 수 없다. */
|
|
466
628
|
protected emitOrder(o: FlowOrder): void;
|
|
467
629
|
/**
|
|
@@ -510,6 +672,76 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
510
672
|
* 여기서는 고르기만 하고 잡지 않는다: 설비까지 확보된 뒤 `assignCrew` 가 확정한다
|
|
511
673
|
* (반쯤 잡고 실패하면 사람이 아무 일도 못 하면서 묶인다).
|
|
512
674
|
*/
|
|
675
|
+
/**
|
|
676
|
+
* 필요 자재를 확보한다 — **ISA-95 `OperationsSegment.MaterialSpecification`**(`use: 'consumed'`).
|
|
677
|
+
*
|
|
678
|
+
* ── 4대 자원 중 자재만 게이트가 없었다 ────────────────────────────────────
|
|
679
|
+
* 인원·자산·설비는 모자라면 기다리는데 **자재는 없어도 작업이 시작됐다.** 그래서 부품이 떨어져
|
|
680
|
+
* 라인이 서는 상황이 예측에 아예 나타나지 않았다 — 트레일러 조립 공장에서 그것은 가장 흔한 정지다.
|
|
681
|
+
*
|
|
682
|
+
* 인원·자산과 **같은 규칙**이다: 등급(또는 품목)으로 요구하고, **부분 확보 없이 전량 아니면 대기.**
|
|
683
|
+
* 자재는 **작업이 일어나는 자리에 있어야** 한다(다른 창고의 부품은 지금 쓸 수 없다).
|
|
684
|
+
*
|
|
685
|
+
* 소비 시점은 **작업 시작**이다 — 자재는 들어가는 순간 없어진다(완료 시점에 빼면 그 사이 다른
|
|
686
|
+
* 작업이 같은 부품을 또 잡는다). 산출(`produced`)은 여기서 다루지 않는다(완료 시점의 일이다).
|
|
687
|
+
*/
|
|
688
|
+
private claimMaterials;
|
|
689
|
+
/** 이 물품이 명세를 만족하나 — 품목 지목이 우선, 없으면 등급(상속을 타고 닫는다). */
|
|
690
|
+
private materialMatches;
|
|
691
|
+
/**
|
|
692
|
+
* 산출 자재를 만든다 — ISA-95 `MaterialSpecification`(`use: 'produced'`).
|
|
693
|
+
*
|
|
694
|
+
* **소비의 짝이다**: 자재는 작업이 시작될 때 들어가고 끝날 때 나온다. 산출이 없으면 조립 공정이
|
|
695
|
+
* 부품만 먹고 아무것도 내놓지 않아, 다음 공정이 영원히 재료를 기다린다(라인 전체가 한 칸에서 멈춘다).
|
|
696
|
+
*
|
|
697
|
+
* 산출물의 자리는 **작업이 끝난 자리**(`toNode`)다. 식별자는 품목 식별자에 작업 id 를 붙여 만든다 —
|
|
698
|
+
* **EPC 처럼 보이게 꾸미지 않는다**(상류가 준 직렬번호와 구별돼야 한다). 상류가 진짜 식별자를
|
|
699
|
+
* 말해 주면 그것이 들어올 자리는 관측 경로다.
|
|
700
|
+
*
|
|
701
|
+
* 같은 품목이 그 자리에 이미 있으면 **수량을 더한다** — 새 줄을 만들면 같은 자리의 같은 로트가
|
|
702
|
+
* 둘로 갈려 재고가 부푼다(§MaterialSubLot 에서 겪은 것과 반대 방향의 같은 오류).
|
|
703
|
+
*/
|
|
704
|
+
private produceMaterials;
|
|
705
|
+
/**
|
|
706
|
+
* 이 작업이 **딛고 선 것**이 아직 있나 — 없으면 무엇이 없는지 답한다.
|
|
707
|
+
*
|
|
708
|
+
* 작업은 혼자 서지 못한다: 옮길 **물품**과, (있다면) 그것을 시킨 **오더** 위에 선다. 진행 중에
|
|
709
|
+
* 둘 중 하나가 사라질 수 있다 — 물품은 포장·출하·소비로, 오더는 이미 이행돼 씨앗이 심지 않아서.
|
|
710
|
+
*
|
|
711
|
+
* 그때 도메인 훅은 없는 것을 딛으려다 던진다(`order.gtin` · `item.location`). 그 예외 하나가
|
|
712
|
+
* **예측 전체를 죽였다** — 사용자에게는 기능이 통째로 사라진 것으로 보였다. 그래서 완료 **전에**
|
|
713
|
+
* 여기서 묻고, 없으면 그 작업만 접는다.
|
|
714
|
+
*/
|
|
715
|
+
protected missingContextOf(t: FlowTask): 'item' | 'order' | 'domain' | undefined;
|
|
716
|
+
/**
|
|
717
|
+
* 이 도메인이 이 작업을 **끝맺을 수 있나** — 코어가 모르는 조건은 도메인이 답한다.
|
|
718
|
+
*
|
|
719
|
+
* 코어는 물품과 오더까지만 안다. 그런데 도메인은 더 필요할 수 있다 — MES 는 완료 시점에 **오더와
|
|
720
|
+
* 제품 정의**를 딛고 서고, 그 중 하나만 없어도 던진다. 코어가 그 조건을 추측하면 도메인마다 다른
|
|
721
|
+
* 가정을 코어에 박게 되므로(방언), **묻는다.**
|
|
722
|
+
*
|
|
723
|
+
* 기본은 `true` — 대부분의 작업은 코어가 확인한 것으로 충분하다.
|
|
724
|
+
*/
|
|
725
|
+
protected canComplete(_t: FlowTask): boolean;
|
|
726
|
+
/**
|
|
727
|
+
* 작업이 가리키는 물품을 찾는다 — **참조와 키가 다를 수 있다.**
|
|
728
|
+
*
|
|
729
|
+
* 물품 맵의 키는 `itemKeyOf`(직렬 물품은 `epc`, 로트의 부분은 `subLotId`)인데, 작업은 로트 식별자
|
|
730
|
+
* (`itemEpc`)로 가리킨다. 비직렬 로트에서는 둘이 **다르다** — 그래서 그냥 `get` 하면 못 찾는다.
|
|
731
|
+
* 실제로 그 회귀를 냈다: 예측(씨앗) 경로에서 `item.location = …` 이 `undefined` 위에서 던져
|
|
732
|
+
* **정확도 추세 전체가 서버 오류로 죽었다.**
|
|
733
|
+
*
|
|
734
|
+
* **부분이 여럿이면 풀지 않는다** — 어느 부분을 가리키는지 알 수 없고, 아무거나 고르면 그 뒤
|
|
735
|
+
* 이동·처분이 엉뚱한 자리에 적힌다. 그때는 참조가 부족한 것이고, 부르는 쪽이 그 사실을 말해야 한다.
|
|
736
|
+
*/
|
|
737
|
+
protected itemByRef(ref: string): FlowItem | undefined;
|
|
738
|
+
/**
|
|
739
|
+
* 실제 자재 이동을 작업에 적어 둔다 — ISA-95 `JobResponse.MaterialActual`.
|
|
740
|
+
* 같은 품목·같은 쓰임은 **한 줄로 합친다**(줄을 늘리면 실적을 세는 쪽이 중복을 걷어내야 한다).
|
|
741
|
+
*/
|
|
742
|
+
private recordMaterialActual;
|
|
743
|
+
/** 확보한 자재를 **작업 시작 시점에 소비**한다 — 수량이 0 이 되면 물품 자체가 사라진다. */
|
|
744
|
+
private consumeMaterials;
|
|
513
745
|
private claimPersonnel;
|
|
514
746
|
/**
|
|
515
747
|
* 필요 설비를 고른다 — **인원·자산과 같은 규칙**(등급으로 요구, 부분 확보 없이 전량 아니면 대기).
|
|
@@ -529,7 +761,62 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
529
761
|
*/
|
|
530
762
|
private stationFull;
|
|
531
763
|
/** 교대 밖인가 — 자원이 지금 일하지 않는 이유 중 고장·계획정지와 구별되는 세 번째. */
|
|
532
|
-
protected offShift(m:
|
|
764
|
+
protected offShift(m: FlowEquipment): boolean;
|
|
765
|
+
/** 왜 쉬는가 — 휴일·휴게(non-working)인지 주말·교대 사이(off-hours)인지(§OffCalendarReason). */
|
|
766
|
+
protected offReason(r: {
|
|
767
|
+
window?: {
|
|
768
|
+
startHour: number;
|
|
769
|
+
endHour: number;
|
|
770
|
+
};
|
|
771
|
+
workCalendar?: WorkCalendarEntry[];
|
|
772
|
+
}): {
|
|
773
|
+
offShiftReason?: OffCalendarReason;
|
|
774
|
+
};
|
|
775
|
+
/**
|
|
776
|
+
* 유효 기간 밖인가 — **네 번째 이유**(§Effectivity). 설비·사람·자산이 같은 규칙을 쓴다.
|
|
777
|
+
*
|
|
778
|
+
* 시뮬 시각을 ISO 로 풀어 계약의 `effectivityAt` 에 넘긴다. **판정은 커널에 두지 않는다** —
|
|
779
|
+
* 호스트(라이브 관측)도 같은 판정을 해야 하고, 규칙이 두 벌이면 갈라진다.
|
|
780
|
+
*/
|
|
781
|
+
protected effectivityOf(r: EffectivePeriod): Effectivity | undefined;
|
|
782
|
+
/**
|
|
783
|
+
* 지금 어느 교대인가 — 계약의 `activeShiftOf` 에 위임한다(미러도 같은 함수를 쓴다).
|
|
784
|
+
* 선언이 없으면 `undefined`: 교대를 나눠 놓지 않은 현장에 이름을 지어내지 않는다.
|
|
785
|
+
*/
|
|
786
|
+
protected shiftOf(r: {
|
|
787
|
+
workCalendar?: WorkCalendarEntry[];
|
|
788
|
+
}): string | undefined;
|
|
789
|
+
/** 유효 기간 밖이라 이 시각의 모델에 참여하지 않는가 — 배정 게이트가 쓰는 형태. */
|
|
790
|
+
protected outOfEffect(r: EffectivePeriod): boolean;
|
|
791
|
+
/**
|
|
792
|
+
* 스냅샷·델타에 실을 조각 — **선언한 기간과 판정을 함께** 낸다.
|
|
793
|
+
*
|
|
794
|
+
* 판정만 내면 화면이 "왜" 를 말할 수 없고(언제 폐기됐나), 기간만 내면 소비처마다 다시 판정해 갈라진다.
|
|
795
|
+
* 선언이 없으면 아무것도 붙이지 않는다(대부분의 자원이 그렇다 — 필드를 늘리지 않는다).
|
|
796
|
+
*/
|
|
797
|
+
protected effectivePart(r: EffectivePeriod): Partial<EffectivePeriod> & {
|
|
798
|
+
effectivity?: Effectivity;
|
|
799
|
+
};
|
|
800
|
+
/**
|
|
801
|
+
* 지금 근무 시간 밖인가 — **캘린더가 있으면 그것으로, 없으면 옛 `window` 로** 판정한다.
|
|
802
|
+
*
|
|
803
|
+
* `window`(시 단위 하나)는 캘린더의 특수한 경우다. 둘을 한 함수로 모아 두면 인원·설비가 같은 규칙을
|
|
804
|
+
* 쓴다(예전에는 같은 식이 두 곳에 복사돼 있었다 — 한쪽만 고치면 조용히 갈라진다).
|
|
805
|
+
*/
|
|
806
|
+
protected offCalendar(r: {
|
|
807
|
+
window?: {
|
|
808
|
+
startHour: number;
|
|
809
|
+
endHour: number;
|
|
810
|
+
};
|
|
811
|
+
workCalendar?: WorkCalendarEntry[];
|
|
812
|
+
}): boolean;
|
|
813
|
+
/**
|
|
814
|
+
* 시뮬 시각의 분(0..1439) — 캘린더 판정의 기준.
|
|
815
|
+
*
|
|
816
|
+
* **보드가 선언한 기준으로 읽는다**(`utcOffsetMinutes`). 예전에는 UTC 로 읽어서 Rosarito(UTC−7)의
|
|
817
|
+
* 06시 교대가 7시간 틀렸다 — 시각대만 적고 기준을 안 적으면 반드시 이렇게 된다.
|
|
818
|
+
*/
|
|
819
|
+
protected minuteOfDay(): number;
|
|
533
820
|
/** 시뮬 시각의 시(0..23) — 운영시간·시간대 배율의 기준. */
|
|
534
821
|
protected hourOfDay(): number;
|
|
535
822
|
/**
|
|
@@ -541,9 +828,9 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
541
828
|
/** 지수분포 표본(고장/수리 간격) — mean 을 평균으로 하는 무기억 프로세스. */
|
|
542
829
|
private sampleExp;
|
|
543
830
|
/**
|
|
544
|
-
* 확률적 설비 고장(MTBF/MTTR) — mtbf 지정
|
|
831
|
+
* 확률적 설비 고장(MTBF/MTTR) — mtbf 지정 설비만 참여(미지정=고장 없음, rng 무소비 → byte-identical).
|
|
545
832
|
* up: nextFailure 도래 시 down(수리까지 repairUntil). down: downMs 누적, repair 도래 시 up(다음 고장 예약).
|
|
546
|
-
* down 중
|
|
833
|
+
* down 중 설비는 배정 불가 + 진행중 task 동결(processTasks 가 skip) → OEE Availability 손실.
|
|
547
834
|
*/
|
|
548
835
|
private processFailures;
|
|
549
836
|
/**
|