@operato/twin-kernel 0.7.28 → 0.7.30
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/flow-engine.d.ts +45 -3
- package/dist/flow-engine.js +66 -2
- package/dist-cjs/index.cjs +53 -2
- package/package.json +1 -1
package/dist/flow-engine.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { TestResult, ISOTime, MaterialQuantity, WorkCalendarEntry, Effectiv
|
|
|
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
|
-
import type { OperationDef } from './domain-definition.ts';
|
|
5
|
+
import type { OperationDef, IsoDuration } from './domain-definition.ts';
|
|
6
6
|
import { type CapacityAnalysis } from './capacity.ts';
|
|
7
7
|
import { type CommittedDemand, type OperationsCapabilityReport } from './operations-capability.ts';
|
|
8
8
|
export interface FlowLocation {
|
|
@@ -237,6 +237,18 @@ export declare const ATTENTION_DEFAULTS: {
|
|
|
237
237
|
readonly scrapPct: 15;
|
|
238
238
|
readonly scrapMinSamples: 10;
|
|
239
239
|
};
|
|
240
|
+
/**
|
|
241
|
+
* 공정에 선언할 수 있는 것의 **어휘** — 지금은 소요시간 하나다(`declareDurations` 가 읽는다).
|
|
242
|
+
*
|
|
243
|
+
* 자리·설비의 속성처럼 「속성 id」로 부른다: 선언을 받는 문·화면·AI 가 같은 이름을 써야 하고, 그 이름이
|
|
244
|
+
* 두 곳에 적히면 한쪽만 바뀌는 순간 값이 조용히 버려진다.
|
|
245
|
+
*
|
|
246
|
+
* 이 값이 얹히는 대상은 **자리·설비가 아니라 공정 종류**다(`FlowTask.kind` = `OperationDef.key`).
|
|
247
|
+
*/
|
|
248
|
+
export declare const OPERATION_PROPERTY: {
|
|
249
|
+
/** 그 현장에서 이 공정이 걸리는 시간 — 실측이 없을 때 상수를 대신한다. */
|
|
250
|
+
readonly duration: "operation.duration";
|
|
251
|
+
};
|
|
240
252
|
/** 판정에 쓸 기준 — 대상별 선언이 있으면 그것, 없으면 기본값(그 사실을 함께 든다). */
|
|
241
253
|
export interface AttentionThresholds {
|
|
242
254
|
congestionPctOf?: (locationId: string) => number | undefined;
|
|
@@ -347,6 +359,13 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
347
359
|
* 도메인 정의에서 실어 온다(`loadOperations`). 없으면 커널 기본값을 쓰고 그 사실을 `specCoverage()` 가 밝힌다.
|
|
348
360
|
*/
|
|
349
361
|
protected operationSpecs: Map<string, OperationDef>;
|
|
362
|
+
/**
|
|
363
|
+
* 현장이 선언한 소요시간(작업 종류 → ms) — `declareDurations` 로 들어온다.
|
|
364
|
+
*
|
|
365
|
+
* 명세 행과 **따로** 두는 이유: 행이 없는 종류에도 시간을 줄 수 있어야 하고(창고·야드 트윈에는 행이
|
|
366
|
+
* 없다), 행을 지어 만들면 지어낸 `intent` 가 능력 계산까지 오염시킨다.
|
|
367
|
+
*/
|
|
368
|
+
protected localDurations: Map<string, number>;
|
|
350
369
|
/** 관측 구동(P0) — 이벤트를 접는 투영기와 그 사실. tick 과 섞이지 않게 명시적으로 들고 있다. */
|
|
351
370
|
private observer?;
|
|
352
371
|
/** 관측분이 아직 커널 상태로 옮겨지지 않았다 — 스냅샷·fork 직전에 한 번만 옮긴다. */
|
|
@@ -673,6 +692,27 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
673
692
|
*/
|
|
674
693
|
protected declaredOperations(): readonly OperationDef[];
|
|
675
694
|
loadOperations(ops?: OperationDef[]): void;
|
|
695
|
+
/**
|
|
696
|
+
* **현장이 정한 소요시간** — 종류별로 시간만 받는다(2026-08-18).
|
|
697
|
+
*
|
|
698
|
+
* ── 왜 명세 행과 따로 받나 ──────────────────────────────────────────────────
|
|
699
|
+
* 창고·야드 트윈에는 오퍼레이션 명세 행이 **아예 없다**(실측: 35개 트윈 중 WMS·YMS 전부 0개). 그런데
|
|
700
|
+
* 그 트윈들도 `putaway`·`pick`·`spot`·`dwell` 을 돌리고, 시간은 커널 상수에서 온다 — 그 현장이 실제로
|
|
701
|
+
* 몇 분 걸리는지 말할 문이 없었다.
|
|
702
|
+
*
|
|
703
|
+
* 명세 행을 지어 만들 수는 없다: `OperationDef` 는 `label`·`intent` 를 요구하고(ISA-95
|
|
704
|
+
* `OperationsSegment`), 그 둘은 **지어내면 다른 답까지 오염시킨다**(`capacity()`·능력 보고가 의도별로
|
|
705
|
+
* 자원을 센다). 「몇 분 걸리나」를 말하려고 「무슨 종류의 작업인가」를 발명하지 않는다.
|
|
706
|
+
*
|
|
707
|
+
* ── 순서: 실측 > 현장 선언 > 원천 명세 > 상수 ──────────────────────────────
|
|
708
|
+
* 이력에서 배운 값이 가장 강하고(그 현장이 실제로 그랬다), 그다음이 **이 선언**이다 — 원천 명세보다
|
|
709
|
+
* 이긴다(원천 사본의 값이 낡았을 때 고칠 자리가 여기다). 무엇을 썼는지는 `specCoverage()` 가 밝힌다.
|
|
710
|
+
*
|
|
711
|
+
* ── 조용히 버리지 않는다 ────────────────────────────────────────────────────
|
|
712
|
+
* 읽을 수 없는 값은 던진다. 받아 두고 무시하면 화면은 「넣었습니다」라고 말하고 시뮬은 상수로 도는데,
|
|
713
|
+
* 그 어긋남을 아무도 볼 수 없다(이 시스템에서 가장 비싼 종류의 침묵이다).
|
|
714
|
+
*/
|
|
715
|
+
declareDurations(durations: Record<string, IsoDuration> | undefined | null): void;
|
|
676
716
|
/**
|
|
677
717
|
* 라우트(공정 순서) — 수율을 거슬러 올릴 때 필요하다. 기본은 모른다(선언 순서를 쓴다).
|
|
678
718
|
* 생산 정의를 가진 커널이 override 해서 자기 라우트를 답한다.
|
|
@@ -727,10 +767,12 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
727
767
|
*/
|
|
728
768
|
protected committedDemands(): CommittedDemand[];
|
|
729
769
|
/**
|
|
730
|
-
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ②
|
|
770
|
+
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ② 현장 선언(`declareDurations`) →
|
|
771
|
+
* ③ 명세(ISA-95 Duration + 변동) → ④ 도메인 상수.**
|
|
731
772
|
*
|
|
732
773
|
* 이 순서인 이유: 실측에서 배운 값이 선언값을 이기고, 선언값이 우리가 코드에 고정한 상수를 이긴다.
|
|
733
|
-
*
|
|
774
|
+
* 선언이 둘로 갈리는 이유는 권위다 — **현장이 정한 값**이 원천이 그려 준 명세를 이긴다(ADR-0034).
|
|
775
|
+
* 무엇을 썼는지는 `specCoverage()` 로 드러낸다 — 상수를 쓴 것이 조용히 넘어가지 않게.
|
|
734
776
|
* "얼마"만 소비하고 "경로"는 씬이 소유한다(좌표-free 유지).
|
|
735
777
|
*/
|
|
736
778
|
protected durationOf(ctx: DurationContext, fallbackMs: number): number;
|
package/dist/flow-engine.js
CHANGED
|
@@ -67,6 +67,18 @@ export const ATTENTION_PROPERTY = {
|
|
|
67
67
|
};
|
|
68
68
|
/** 선언이 없을 때 쓰는 기본값 — **드러내 둔다**(코드 안에 숨은 상수가 아니라 계약의 일부다). */
|
|
69
69
|
export const ATTENTION_DEFAULTS = { congestionPct: 90, scrapPct: 15, scrapMinSamples: 10 };
|
|
70
|
+
/**
|
|
71
|
+
* 공정에 선언할 수 있는 것의 **어휘** — 지금은 소요시간 하나다(`declareDurations` 가 읽는다).
|
|
72
|
+
*
|
|
73
|
+
* 자리·설비의 속성처럼 「속성 id」로 부른다: 선언을 받는 문·화면·AI 가 같은 이름을 써야 하고, 그 이름이
|
|
74
|
+
* 두 곳에 적히면 한쪽만 바뀌는 순간 값이 조용히 버려진다.
|
|
75
|
+
*
|
|
76
|
+
* 이 값이 얹히는 대상은 **자리·설비가 아니라 공정 종류**다(`FlowTask.kind` = `OperationDef.key`).
|
|
77
|
+
*/
|
|
78
|
+
export const OPERATION_PROPERTY = {
|
|
79
|
+
/** 그 현장에서 이 공정이 걸리는 시간 — 실측이 없을 때 상수를 대신한다. */
|
|
80
|
+
duration: 'operation.duration'
|
|
81
|
+
};
|
|
70
82
|
export function deriveAttentions(view, acked,
|
|
71
83
|
/** 지금(ISO) — 납기 판정에 필요하다. **주지 않으면 지연을 판정하지 않는다**(모르면 판단하지 않는다). */
|
|
72
84
|
nowIso,
|
|
@@ -311,6 +323,13 @@ export class FlowEngine {
|
|
|
311
323
|
* 도메인 정의에서 실어 온다(`loadOperations`). 없으면 커널 기본값을 쓰고 그 사실을 `specCoverage()` 가 밝힌다.
|
|
312
324
|
*/
|
|
313
325
|
operationSpecs = new Map();
|
|
326
|
+
/**
|
|
327
|
+
* 현장이 선언한 소요시간(작업 종류 → ms) — `declareDurations` 로 들어온다.
|
|
328
|
+
*
|
|
329
|
+
* 명세 행과 **따로** 두는 이유: 행이 없는 종류에도 시간을 줄 수 있어야 하고(창고·야드 트윈에는 행이
|
|
330
|
+
* 없다), 행을 지어 만들면 지어낸 `intent` 가 능력 계산까지 오염시킨다.
|
|
331
|
+
*/
|
|
332
|
+
localDurations = new Map();
|
|
314
333
|
/** 관측 구동(P0) — 이벤트를 접는 투영기와 그 사실. tick 과 섞이지 않게 명시적으로 들고 있다. */
|
|
315
334
|
observer;
|
|
316
335
|
/** 관측분이 아직 커널 상태로 옮겨지지 않았다 — 스냅샷·fork 직전에 한 번만 옮긴다. */
|
|
@@ -1425,6 +1444,39 @@ export class FlowEngine {
|
|
|
1425
1444
|
if (o?.key)
|
|
1426
1445
|
this.operationSpecs.set(o.key, o);
|
|
1427
1446
|
}
|
|
1447
|
+
/**
|
|
1448
|
+
* **현장이 정한 소요시간** — 종류별로 시간만 받는다(2026-08-18).
|
|
1449
|
+
*
|
|
1450
|
+
* ── 왜 명세 행과 따로 받나 ──────────────────────────────────────────────────
|
|
1451
|
+
* 창고·야드 트윈에는 오퍼레이션 명세 행이 **아예 없다**(실측: 35개 트윈 중 WMS·YMS 전부 0개). 그런데
|
|
1452
|
+
* 그 트윈들도 `putaway`·`pick`·`spot`·`dwell` 을 돌리고, 시간은 커널 상수에서 온다 — 그 현장이 실제로
|
|
1453
|
+
* 몇 분 걸리는지 말할 문이 없었다.
|
|
1454
|
+
*
|
|
1455
|
+
* 명세 행을 지어 만들 수는 없다: `OperationDef` 는 `label`·`intent` 를 요구하고(ISA-95
|
|
1456
|
+
* `OperationsSegment`), 그 둘은 **지어내면 다른 답까지 오염시킨다**(`capacity()`·능력 보고가 의도별로
|
|
1457
|
+
* 자원을 센다). 「몇 분 걸리나」를 말하려고 「무슨 종류의 작업인가」를 발명하지 않는다.
|
|
1458
|
+
*
|
|
1459
|
+
* ── 순서: 실측 > 현장 선언 > 원천 명세 > 상수 ──────────────────────────────
|
|
1460
|
+
* 이력에서 배운 값이 가장 강하고(그 현장이 실제로 그랬다), 그다음이 **이 선언**이다 — 원천 명세보다
|
|
1461
|
+
* 이긴다(원천 사본의 값이 낡았을 때 고칠 자리가 여기다). 무엇을 썼는지는 `specCoverage()` 가 밝힌다.
|
|
1462
|
+
*
|
|
1463
|
+
* ── 조용히 버리지 않는다 ────────────────────────────────────────────────────
|
|
1464
|
+
* 읽을 수 없는 값은 던진다. 받아 두고 무시하면 화면은 「넣었습니다」라고 말하고 시뮬은 상수로 도는데,
|
|
1465
|
+
* 그 어긋남을 아무도 볼 수 없다(이 시스템에서 가장 비싼 종류의 침묵이다).
|
|
1466
|
+
*/
|
|
1467
|
+
declareDurations(durations) {
|
|
1468
|
+
for (const [kind, text] of Object.entries(durations ?? {})) {
|
|
1469
|
+
const key = String(kind ?? '').trim();
|
|
1470
|
+
if (!key)
|
|
1471
|
+
throw new Error('declareDurations: an operation kind is empty — say which operation the duration belongs to');
|
|
1472
|
+
const ms = parseIsoDuration(text);
|
|
1473
|
+
if (ms === undefined)
|
|
1474
|
+
throw new Error(`declareDurations: "${text}" is not an ISO 8601 duration (operation "${key}") — the value would be dropped without a trace`);
|
|
1475
|
+
if (ms <= 0)
|
|
1476
|
+
throw new Error(`declareDurations: operation "${key}" cannot take ${ms}ms — a task with no duration never finishes`);
|
|
1477
|
+
this.localDurations.set(key, ms);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1428
1480
|
/**
|
|
1429
1481
|
* 라우트(공정 순서) — 수율을 거슬러 올릴 때 필요하다. 기본은 모른다(선언 순서를 쓴다).
|
|
1430
1482
|
* 생산 정의를 가진 커널이 override 해서 자기 라우트를 답한다.
|
|
@@ -1528,10 +1580,12 @@ export class FlowEngine {
|
|
|
1528
1580
|
return out;
|
|
1529
1581
|
}
|
|
1530
1582
|
/**
|
|
1531
|
-
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ②
|
|
1583
|
+
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ② 현장 선언(`declareDurations`) →
|
|
1584
|
+
* ③ 명세(ISA-95 Duration + 변동) → ④ 도메인 상수.**
|
|
1532
1585
|
*
|
|
1533
1586
|
* 이 순서인 이유: 실측에서 배운 값이 선언값을 이기고, 선언값이 우리가 코드에 고정한 상수를 이긴다.
|
|
1534
|
-
*
|
|
1587
|
+
* 선언이 둘로 갈리는 이유는 권위다 — **현장이 정한 값**이 원천이 그려 준 명세를 이긴다(ADR-0034).
|
|
1588
|
+
* 무엇을 썼는지는 `specCoverage()` 로 드러낸다 — 상수를 쓴 것이 조용히 넘어가지 않게.
|
|
1535
1589
|
* "얼마"만 소비하고 "경로"는 씬이 소유한다(좌표-free 유지).
|
|
1536
1590
|
*/
|
|
1537
1591
|
durationOf(ctx, fallbackMs) {
|
|
@@ -1548,6 +1602,16 @@ export class FlowEngine {
|
|
|
1548
1602
|
return this.sampleSpread(estimated.meanMs, estimated.spread);
|
|
1549
1603
|
}
|
|
1550
1604
|
const spec = this.operationSpecs.get(ctx.kind);
|
|
1605
|
+
/*
|
|
1606
|
+
* **현장이 정한 시간이 원천 명세를 이긴다** — 원천 사본의 값이 낡았을 때 고칠 자리가 그것뿐이다
|
|
1607
|
+
* (ADR-0034: 우리가 정한 값이 원천이 그려 준 값을 이긴다). 변동은 명세 행이 말한 것을 그대로 쓴다 —
|
|
1608
|
+
* 평균만 우리가 고쳤다고 퍼짐을 0 으로 만들면 줄이 사라진다.
|
|
1609
|
+
*/
|
|
1610
|
+
const local = this.localDurations.get(ctx.kind);
|
|
1611
|
+
if (local !== undefined) {
|
|
1612
|
+
this.noteSpecUse(ctx.kind, 'declared');
|
|
1613
|
+
return this.applyVariability(local, spec?.variability);
|
|
1614
|
+
}
|
|
1551
1615
|
const declared = parseIsoDuration(spec?.duration);
|
|
1552
1616
|
if (declared === undefined) {
|
|
1553
1617
|
this.noteSpecUse(ctx.kind, 'default');
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -55,6 +55,7 @@ __export(index_exports, {
|
|
|
55
55
|
MES_PRODUCT_GTINS: () => MES_PRODUCT_GTINS,
|
|
56
56
|
MES_TYPES: () => MES_TYPES,
|
|
57
57
|
MesKernel: () => MesKernel,
|
|
58
|
+
OPERATION_PROPERTY: () => OPERATION_PROPERTY,
|
|
58
59
|
OP_EVENT: () => OP_EVENT,
|
|
59
60
|
OP_PARAM: () => OP_PARAM,
|
|
60
61
|
ObservedReducer: () => ObservedReducer,
|
|
@@ -2963,6 +2964,10 @@ var ATTENTION_PROPERTY = {
|
|
|
2963
2964
|
scrapMinSamples: "attention.scrapMinSamples"
|
|
2964
2965
|
};
|
|
2965
2966
|
var ATTENTION_DEFAULTS = { congestionPct: 90, scrapPct: 15, scrapMinSamples: 10 };
|
|
2967
|
+
var OPERATION_PROPERTY = {
|
|
2968
|
+
/** 그 현장에서 이 공정이 걸리는 시간 — 실측이 없을 때 상수를 대신한다. */
|
|
2969
|
+
duration: "operation.duration"
|
|
2970
|
+
};
|
|
2966
2971
|
function deriveAttentions(view, acked, nowIso, thresholds) {
|
|
2967
2972
|
const out = [];
|
|
2968
2973
|
for (const m of view.equipment) {
|
|
@@ -3163,6 +3168,13 @@ var FlowEngine = class {
|
|
|
3163
3168
|
* 도메인 정의에서 실어 온다(`loadOperations`). 없으면 커널 기본값을 쓰고 그 사실을 `specCoverage()` 가 밝힌다.
|
|
3164
3169
|
*/
|
|
3165
3170
|
operationSpecs = /* @__PURE__ */ new Map();
|
|
3171
|
+
/**
|
|
3172
|
+
* 현장이 선언한 소요시간(작업 종류 → ms) — `declareDurations` 로 들어온다.
|
|
3173
|
+
*
|
|
3174
|
+
* 명세 행과 **따로** 두는 이유: 행이 없는 종류에도 시간을 줄 수 있어야 하고(창고·야드 트윈에는 행이
|
|
3175
|
+
* 없다), 행을 지어 만들면 지어낸 `intent` 가 능력 계산까지 오염시킨다.
|
|
3176
|
+
*/
|
|
3177
|
+
localDurations = /* @__PURE__ */ new Map();
|
|
3166
3178
|
/** 관측 구동(P0) — 이벤트를 접는 투영기와 그 사실. tick 과 섞이지 않게 명시적으로 들고 있다. */
|
|
3167
3179
|
observer;
|
|
3168
3180
|
/** 관측분이 아직 커널 상태로 옮겨지지 않았다 — 스냅샷·fork 직전에 한 번만 옮긴다. */
|
|
@@ -4170,6 +4182,37 @@ var FlowEngine = class {
|
|
|
4170
4182
|
loadOperations(ops = []) {
|
|
4171
4183
|
for (const o of ops) if (o?.key) this.operationSpecs.set(o.key, o);
|
|
4172
4184
|
}
|
|
4185
|
+
/**
|
|
4186
|
+
* **현장이 정한 소요시간** — 종류별로 시간만 받는다(2026-08-18).
|
|
4187
|
+
*
|
|
4188
|
+
* ── 왜 명세 행과 따로 받나 ──────────────────────────────────────────────────
|
|
4189
|
+
* 창고·야드 트윈에는 오퍼레이션 명세 행이 **아예 없다**(실측: 35개 트윈 중 WMS·YMS 전부 0개). 그런데
|
|
4190
|
+
* 그 트윈들도 `putaway`·`pick`·`spot`·`dwell` 을 돌리고, 시간은 커널 상수에서 온다 — 그 현장이 실제로
|
|
4191
|
+
* 몇 분 걸리는지 말할 문이 없었다.
|
|
4192
|
+
*
|
|
4193
|
+
* 명세 행을 지어 만들 수는 없다: `OperationDef` 는 `label`·`intent` 를 요구하고(ISA-95
|
|
4194
|
+
* `OperationsSegment`), 그 둘은 **지어내면 다른 답까지 오염시킨다**(`capacity()`·능력 보고가 의도별로
|
|
4195
|
+
* 자원을 센다). 「몇 분 걸리나」를 말하려고 「무슨 종류의 작업인가」를 발명하지 않는다.
|
|
4196
|
+
*
|
|
4197
|
+
* ── 순서: 실측 > 현장 선언 > 원천 명세 > 상수 ──────────────────────────────
|
|
4198
|
+
* 이력에서 배운 값이 가장 강하고(그 현장이 실제로 그랬다), 그다음이 **이 선언**이다 — 원천 명세보다
|
|
4199
|
+
* 이긴다(원천 사본의 값이 낡았을 때 고칠 자리가 여기다). 무엇을 썼는지는 `specCoverage()` 가 밝힌다.
|
|
4200
|
+
*
|
|
4201
|
+
* ── 조용히 버리지 않는다 ────────────────────────────────────────────────────
|
|
4202
|
+
* 읽을 수 없는 값은 던진다. 받아 두고 무시하면 화면은 「넣었습니다」라고 말하고 시뮬은 상수로 도는데,
|
|
4203
|
+
* 그 어긋남을 아무도 볼 수 없다(이 시스템에서 가장 비싼 종류의 침묵이다).
|
|
4204
|
+
*/
|
|
4205
|
+
declareDurations(durations) {
|
|
4206
|
+
for (const [kind, text] of Object.entries(durations ?? {})) {
|
|
4207
|
+
const key = String(kind ?? "").trim();
|
|
4208
|
+
if (!key) throw new Error("declareDurations: an operation kind is empty \u2014 say which operation the duration belongs to");
|
|
4209
|
+
const ms2 = parseIsoDuration(text);
|
|
4210
|
+
if (ms2 === void 0)
|
|
4211
|
+
throw new Error(`declareDurations: "${text}" is not an ISO 8601 duration (operation "${key}") \u2014 the value would be dropped without a trace`);
|
|
4212
|
+
if (ms2 <= 0) throw new Error(`declareDurations: operation "${key}" cannot take ${ms2}ms \u2014 a task with no duration never finishes`);
|
|
4213
|
+
this.localDurations.set(key, ms2);
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4173
4216
|
/**
|
|
4174
4217
|
* 라우트(공정 순서) — 수율을 거슬러 올릴 때 필요하다. 기본은 모른다(선언 순서를 쓴다).
|
|
4175
4218
|
* 생산 정의를 가진 커널이 override 해서 자기 라우트를 답한다.
|
|
@@ -4271,10 +4314,12 @@ var FlowEngine = class {
|
|
|
4271
4314
|
return out;
|
|
4272
4315
|
}
|
|
4273
4316
|
/**
|
|
4274
|
-
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ②
|
|
4317
|
+
* task 소요 산출 — 우선순위: **① 추정기(이력 보정) → ② 현장 선언(`declareDurations`) →
|
|
4318
|
+
* ③ 명세(ISA-95 Duration + 변동) → ④ 도메인 상수.**
|
|
4275
4319
|
*
|
|
4276
4320
|
* 이 순서인 이유: 실측에서 배운 값이 선언값을 이기고, 선언값이 우리가 코드에 고정한 상수를 이긴다.
|
|
4277
|
-
*
|
|
4321
|
+
* 선언이 둘로 갈리는 이유는 권위다 — **현장이 정한 값**이 원천이 그려 준 명세를 이긴다(ADR-0034).
|
|
4322
|
+
* 무엇을 썼는지는 `specCoverage()` 로 드러낸다 — 상수를 쓴 것이 조용히 넘어가지 않게.
|
|
4278
4323
|
* "얼마"만 소비하고 "경로"는 씬이 소유한다(좌표-free 유지).
|
|
4279
4324
|
*/
|
|
4280
4325
|
durationOf(ctx, fallbackMs) {
|
|
@@ -4288,6 +4333,11 @@ var FlowEngine = class {
|
|
|
4288
4333
|
return this.sampleSpread(estimated.meanMs, estimated.spread);
|
|
4289
4334
|
}
|
|
4290
4335
|
const spec = this.operationSpecs.get(ctx.kind);
|
|
4336
|
+
const local = this.localDurations.get(ctx.kind);
|
|
4337
|
+
if (local !== void 0) {
|
|
4338
|
+
this.noteSpecUse(ctx.kind, "declared");
|
|
4339
|
+
return this.applyVariability(local, spec?.variability);
|
|
4340
|
+
}
|
|
4291
4341
|
const declared = parseIsoDuration(spec?.duration);
|
|
4292
4342
|
if (declared === void 0) {
|
|
4293
4343
|
this.noteSpecUse(ctx.kind, "default");
|
|
@@ -7276,6 +7326,7 @@ function retiredVocabularyIn(line) {
|
|
|
7276
7326
|
MES_PRODUCT_GTINS,
|
|
7277
7327
|
MES_TYPES,
|
|
7278
7328
|
MesKernel,
|
|
7329
|
+
OPERATION_PROPERTY,
|
|
7279
7330
|
OP_EVENT,
|
|
7280
7331
|
OP_PARAM,
|
|
7281
7332
|
ObservedReducer,
|
package/package.json
CHANGED