@operato/twin-kernel 0.7.16 → 0.7.17
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/contract.d.ts +20 -0
- package/dist/ems-kernel.js +9 -0
- package/dist/ems-profile.d.ts +10 -0
- package/dist/ems-profile.js +11 -1
- package/dist/flow-engine.d.ts +10 -1
- package/dist/flow-engine.js +20 -1
- package/dist-cjs/index.cjs +31 -1
- package/package.json +1 -1
package/dist/contract.d.ts
CHANGED
|
@@ -1450,6 +1450,26 @@ export interface ScenarioDef {
|
|
|
1450
1450
|
* 아무도 답할 수 없다).
|
|
1451
1451
|
*/
|
|
1452
1452
|
interventions?: ScenarioIntervention[];
|
|
1453
|
+
/**
|
|
1454
|
+
* **가정한 선언** — 「이렇게 선언돼 있었다면?」(예: 축전지에 피크 억제 임계를 넣으면).
|
|
1455
|
+
*
|
|
1456
|
+
* 개입(`interventions`)이 시각을 가진 **행동**이라면 이쪽은 시각이 없는 **모델의 변주**다. 둘을 한
|
|
1457
|
+
* 자리에 두지 않는 이유: 행동은 저널에 남을 사실이 되고, 변주는 「그런 현장이었다면」이라는 가정이다.
|
|
1458
|
+
*
|
|
1459
|
+
* 시나리오와 함께 다니므로 fork 가 자기완결이다 — 같은 선언 하나로 예측·백테스트·AI 가 같은 미래를
|
|
1460
|
+
* 재생한다. 다만 **실행 중 트윈에 이 시나리오를 걸면 그 트윈의 선언이 실제로 바뀐다**: 그것은
|
|
1461
|
+
* 「우리 현장에 그 자동화가 있다」는 주장이므로, 무엇을 덮어썼는지 소비처가 읽을 수 있어야 한다
|
|
1462
|
+
* (`declarationOverrides()`).
|
|
1463
|
+
*/
|
|
1464
|
+
overrides?: ScenarioOverride[];
|
|
1465
|
+
}
|
|
1466
|
+
/** 가정한 선언 하나 — 어느 자원의 어느 속성을 무엇으로. */
|
|
1467
|
+
export interface ScenarioOverride {
|
|
1468
|
+
resourceId: string;
|
|
1469
|
+
propertyId: string;
|
|
1470
|
+
value: string | number;
|
|
1471
|
+
/** 그 자원이 없었으면 false — 조용히 성공한 척하지 않는다. */
|
|
1472
|
+
applied?: boolean;
|
|
1453
1473
|
}
|
|
1454
1474
|
/** 가정한 개입 하나 — 시각 + 행동. */
|
|
1455
1475
|
export interface ScenarioIntervention {
|
package/dist/ems-kernel.js
CHANGED
|
@@ -554,6 +554,15 @@ export class EmsKernel extends FlowEngine {
|
|
|
554
554
|
const state = this.equipment.get(e.id);
|
|
555
555
|
if (!state)
|
|
556
556
|
continue;
|
|
557
|
+
/*
|
|
558
|
+
* 시뮬의 출발 SOC — 선언에서 한 번 심는다(계측이 들어오면 그것이 이긴다).
|
|
559
|
+
* 이것이 없던 동안, 정책을 선언해도 「SOC 를 모른다」에 걸려 배터리가 아무 일도 하지 못했다.
|
|
560
|
+
*/
|
|
561
|
+
if (!Number.isFinite(Number(state.soc))) {
|
|
562
|
+
const seed = this.numberProperty(e, EMS_PROPERTY.initialSoc);
|
|
563
|
+
if (seed !== undefined)
|
|
564
|
+
state.soc = seed;
|
|
565
|
+
}
|
|
557
566
|
const over = demandBeforeStorageKW - dispatched - above;
|
|
558
567
|
if (over <= 0) {
|
|
559
568
|
/* 임계 아래 — 쓸 이유가 없다. 낡은 방전값을 남기지 않는다(상태가 지난 틱을 말하게 된다). */
|
package/dist/ems-profile.d.ts
CHANGED
|
@@ -30,5 +30,15 @@ export declare const EMS_PROPERTY: {
|
|
|
30
30
|
readonly maxDischargeKW: "dispatch.maxDischargeKW";
|
|
31
31
|
/** 예비 SOC(%) — 이 아래로는 쓰지 않는다(비상 대비). 없으면 0 으로 본다. */
|
|
32
32
|
readonly reserveSoc: "dispatch.reserveSoc";
|
|
33
|
+
/**
|
|
34
|
+
* 시뮬레이션이 **출발할 때의 SOC(%)** — 씨앗값이다.
|
|
35
|
+
*
|
|
36
|
+
* 계측이 SOC 를 알려 주는 트윈에는 필요 없다(잰 값이 진실이다). 시뮬 트윈에는 알려 줄 것이 없어서
|
|
37
|
+
* 「SOC 를 모르면 방전하지 않는다」는 규율에 걸려 **배터리가 아무 일도 하지 못했다** — 정책을 선언해도
|
|
38
|
+
* 피크가 한 톨도 깎이지 않았다(실화면에서 그렇게 났다).
|
|
39
|
+
*
|
|
40
|
+
* 이것은 상태의 씨앗이지 계측이 아니다. 그래서 계측이 들어오는 순간 그것이 이깁니다.
|
|
41
|
+
*/
|
|
42
|
+
readonly initialSoc: "storage.initialSoc";
|
|
33
43
|
};
|
|
34
44
|
export declare const EMS_TYPES: TwinTypeInfo[];
|
package/dist/ems-profile.js
CHANGED
|
@@ -61,7 +61,17 @@ export const EMS_PROPERTY = {
|
|
|
61
61
|
/** 최대 방전율(kW) — 인버터가 낼 수 있는 한계. */
|
|
62
62
|
maxDischargeKW: 'dispatch.maxDischargeKW',
|
|
63
63
|
/** 예비 SOC(%) — 이 아래로는 쓰지 않는다(비상 대비). 없으면 0 으로 본다. */
|
|
64
|
-
reserveSoc: 'dispatch.reserveSoc'
|
|
64
|
+
reserveSoc: 'dispatch.reserveSoc',
|
|
65
|
+
/**
|
|
66
|
+
* 시뮬레이션이 **출발할 때의 SOC(%)** — 씨앗값이다.
|
|
67
|
+
*
|
|
68
|
+
* 계측이 SOC 를 알려 주는 트윈에는 필요 없다(잰 값이 진실이다). 시뮬 트윈에는 알려 줄 것이 없어서
|
|
69
|
+
* 「SOC 를 모르면 방전하지 않는다」는 규율에 걸려 **배터리가 아무 일도 하지 못했다** — 정책을 선언해도
|
|
70
|
+
* 피크가 한 톨도 깎이지 않았다(실화면에서 그렇게 났다).
|
|
71
|
+
*
|
|
72
|
+
* 이것은 상태의 씨앗이지 계측이 아니다. 그래서 계측이 들어오는 순간 그것이 이깁니다.
|
|
73
|
+
*/
|
|
74
|
+
initialSoc: 'storage.initialSoc'
|
|
65
75
|
};
|
|
66
76
|
export const EMS_TYPES = [
|
|
67
77
|
/* ── 자리: 전기적 구간 ─────────────────────────────────────────────────── */
|
package/dist/flow-engine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TestResult, ISOTime, MaterialQuantity, WorkCalendarEntry, EffectivePeriod, Effectivity, OffCalendarReason, ResourceProperty, ResourceClassDef, MaterialDefinition, Attention, TwinModelDef, CanonicalEnvelope, Command, CommandAck, EventHandler, EquipmentMotion, OeeMetrics, AssetState, GeneratorSpec, InterventionOutcome, OrderState, PersonState, ScenarioControl, StateSnapshot, TwinKernel, Unsubscribe, LocationState, ItemState, EquipmentState, OrderStatusDelta, TaskState, StructureShift } from './contract.ts';
|
|
1
|
+
import type { TestResult, ISOTime, MaterialQuantity, WorkCalendarEntry, EffectivePeriod, Effectivity, OffCalendarReason, ResourceProperty, ResourceClassDef, MaterialDefinition, Attention, TwinModelDef, CanonicalEnvelope, Command, CommandAck, EventHandler, EquipmentMotion, OeeMetrics, AssetState, GeneratorSpec, InterventionOutcome, OrderState, PersonState, ScenarioControl, ScenarioOverride, StateSnapshot, TwinKernel, Unsubscribe, LocationState, ItemState, EquipmentState, OrderStatusDelta, TaskState, StructureShift } 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';
|
|
@@ -329,6 +329,8 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
329
329
|
private interventions;
|
|
330
330
|
/** 개입이 어떻게 됐나 — 걸린 것·거절된 것. 조용히 사라지지 않게 소비처가 읽는다. */
|
|
331
331
|
private interventionLog;
|
|
332
|
+
/** 덮어쓴 선언 — 「이 값은 가정이다」를 소비처가 말할 수 있게. */
|
|
333
|
+
private overrideLog;
|
|
332
334
|
private generating;
|
|
333
335
|
private speed;
|
|
334
336
|
private eventSeq;
|
|
@@ -480,6 +482,13 @@ export declare abstract class FlowEngine implements TwinKernel {
|
|
|
480
482
|
* 아직 시각이 오지 않은 것은 여기 없다(일어나지 않은 일을 결과로 적지 않는다).
|
|
481
483
|
*/
|
|
482
484
|
interventionOutcomes(): InterventionOutcome[];
|
|
485
|
+
/**
|
|
486
|
+
* 이 커널이 **가정으로 덮어쓴 선언들** — 없으면 빈 배열.
|
|
487
|
+
*
|
|
488
|
+
* 비어 있지 않다는 것은 「이 트윈의 수는 선언된 현장이 아니라 가정한 현장의 것」이라는 뜻이다.
|
|
489
|
+
* 화면·AI 가 그 사실을 함께 말해야 한다.
|
|
490
|
+
*/
|
|
491
|
+
declarationOverrides(): ScenarioOverride[];
|
|
483
492
|
/**
|
|
484
493
|
* 이 커널의 **지금**(ms) — 시각으로 바뀌는 모든 판정의 단일 기준.
|
|
485
494
|
*
|
package/dist/flow-engine.js
CHANGED
|
@@ -290,6 +290,8 @@ export class FlowEngine {
|
|
|
290
290
|
interventions = [];
|
|
291
291
|
/** 개입이 어떻게 됐나 — 걸린 것·거절된 것. 조용히 사라지지 않게 소비처가 읽는다. */
|
|
292
292
|
interventionLog = [];
|
|
293
|
+
/** 덮어쓴 선언 — 「이 값은 가정이다」를 소비처가 말할 수 있게. */
|
|
294
|
+
overrideLog = [];
|
|
293
295
|
generating = false;
|
|
294
296
|
speed = 1;
|
|
295
297
|
eventSeq = 0;
|
|
@@ -875,6 +877,14 @@ export class FlowEngine {
|
|
|
875
877
|
/* 개입은 **시작할 때** 시각이 정해진다(경과 기준) — 여기서는 받아만 둔다. */
|
|
876
878
|
this.interventions = (def.interventions ?? []).map(spec => ({ spec, dueMs: 0, done: false }));
|
|
877
879
|
this.interventionLog = [];
|
|
880
|
+
/*
|
|
881
|
+
* 가정한 선언은 **지금 곧 걸린다** — 시각이 없는 변주이므로 미룰 것이 없다.
|
|
882
|
+
* 무엇을 덮어썼는지 남긴다: 말하지 않으면 소비처가 what-if 결과를 트윈의 현재 상태로 읽는다.
|
|
883
|
+
*/
|
|
884
|
+
this.overrideLog = (def.overrides ?? []).map(o => ({
|
|
885
|
+
...o,
|
|
886
|
+
applied: this.setResourceProperty(o.resourceId, o.propertyId, o.value)
|
|
887
|
+
}));
|
|
878
888
|
},
|
|
879
889
|
start: () => {
|
|
880
890
|
if (this.generating)
|
|
@@ -889,7 +899,7 @@ export class FlowEngine {
|
|
|
889
899
|
}
|
|
890
900
|
},
|
|
891
901
|
pause: () => { this.generating = false; },
|
|
892
|
-
reset: () => { this.generating = false; this.gens = []; this.interventions = []; this.interventionLog = []; },
|
|
902
|
+
reset: () => { this.generating = false; this.gens = []; this.interventions = []; this.interventionLog = []; this.overrideLog = []; },
|
|
893
903
|
setSpeed: (f) => { this.speed = f; }
|
|
894
904
|
};
|
|
895
905
|
tick(dtMs) {
|
|
@@ -1117,6 +1127,15 @@ export class FlowEngine {
|
|
|
1117
1127
|
interventionOutcomes() {
|
|
1118
1128
|
return this.interventionLog.map(o => ({ ...o }));
|
|
1119
1129
|
}
|
|
1130
|
+
/**
|
|
1131
|
+
* 이 커널이 **가정으로 덮어쓴 선언들** — 없으면 빈 배열.
|
|
1132
|
+
*
|
|
1133
|
+
* 비어 있지 않다는 것은 「이 트윈의 수는 선언된 현장이 아니라 가정한 현장의 것」이라는 뜻이다.
|
|
1134
|
+
* 화면·AI 가 그 사실을 함께 말해야 한다.
|
|
1135
|
+
*/
|
|
1136
|
+
declarationOverrides() {
|
|
1137
|
+
return this.overrideLog.map(o => ({ ...o }));
|
|
1138
|
+
}
|
|
1120
1139
|
// ── 보호 헬퍼 (도메인 hook 에서 사용) ──────────────────────────────────────
|
|
1121
1140
|
/**
|
|
1122
1141
|
* 이 커널의 **지금**(ms) — 시각으로 바뀌는 모든 판정의 단일 기준.
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -1725,7 +1725,17 @@ var EMS_PROPERTY = {
|
|
|
1725
1725
|
/** 최대 방전율(kW) — 인버터가 낼 수 있는 한계. */
|
|
1726
1726
|
maxDischargeKW: "dispatch.maxDischargeKW",
|
|
1727
1727
|
/** 예비 SOC(%) — 이 아래로는 쓰지 않는다(비상 대비). 없으면 0 으로 본다. */
|
|
1728
|
-
reserveSoc: "dispatch.reserveSoc"
|
|
1728
|
+
reserveSoc: "dispatch.reserveSoc",
|
|
1729
|
+
/**
|
|
1730
|
+
* 시뮬레이션이 **출발할 때의 SOC(%)** — 씨앗값이다.
|
|
1731
|
+
*
|
|
1732
|
+
* 계측이 SOC 를 알려 주는 트윈에는 필요 없다(잰 값이 진실이다). 시뮬 트윈에는 알려 줄 것이 없어서
|
|
1733
|
+
* 「SOC 를 모르면 방전하지 않는다」는 규율에 걸려 **배터리가 아무 일도 하지 못했다** — 정책을 선언해도
|
|
1734
|
+
* 피크가 한 톨도 깎이지 않았다(실화면에서 그렇게 났다).
|
|
1735
|
+
*
|
|
1736
|
+
* 이것은 상태의 씨앗이지 계측이 아니다. 그래서 계측이 들어오는 순간 그것이 이깁니다.
|
|
1737
|
+
*/
|
|
1738
|
+
initialSoc: "storage.initialSoc"
|
|
1729
1739
|
};
|
|
1730
1740
|
var EMS_TYPES = [
|
|
1731
1741
|
/* ── 자리: 전기적 구간 ─────────────────────────────────────────────────── */
|
|
@@ -2996,6 +3006,8 @@ var FlowEngine = class {
|
|
|
2996
3006
|
interventions = [];
|
|
2997
3007
|
/** 개입이 어떻게 됐나 — 걸린 것·거절된 것. 조용히 사라지지 않게 소비처가 읽는다. */
|
|
2998
3008
|
interventionLog = [];
|
|
3009
|
+
/** 덮어쓴 선언 — 「이 값은 가정이다」를 소비처가 말할 수 있게. */
|
|
3010
|
+
overrideLog = [];
|
|
2999
3011
|
generating = false;
|
|
3000
3012
|
speed = 1;
|
|
3001
3013
|
eventSeq = 0;
|
|
@@ -3523,6 +3535,10 @@ var FlowEngine = class {
|
|
|
3523
3535
|
this.gens = def.generators.map((spec) => ({ spec, nextMs: 0 }));
|
|
3524
3536
|
this.interventions = (def.interventions ?? []).map((spec) => ({ spec, dueMs: 0, done: false }));
|
|
3525
3537
|
this.interventionLog = [];
|
|
3538
|
+
this.overrideLog = (def.overrides ?? []).map((o) => ({
|
|
3539
|
+
...o,
|
|
3540
|
+
applied: this.setResourceProperty(o.resourceId, o.propertyId, o.value)
|
|
3541
|
+
}));
|
|
3526
3542
|
},
|
|
3527
3543
|
start: () => {
|
|
3528
3544
|
if (this.generating) return;
|
|
@@ -3541,6 +3557,7 @@ var FlowEngine = class {
|
|
|
3541
3557
|
this.gens = [];
|
|
3542
3558
|
this.interventions = [];
|
|
3543
3559
|
this.interventionLog = [];
|
|
3560
|
+
this.overrideLog = [];
|
|
3544
3561
|
},
|
|
3545
3562
|
setSpeed: (f) => {
|
|
3546
3563
|
this.speed = f;
|
|
@@ -3744,6 +3761,15 @@ var FlowEngine = class {
|
|
|
3744
3761
|
interventionOutcomes() {
|
|
3745
3762
|
return this.interventionLog.map((o) => ({ ...o }));
|
|
3746
3763
|
}
|
|
3764
|
+
/**
|
|
3765
|
+
* 이 커널이 **가정으로 덮어쓴 선언들** — 없으면 빈 배열.
|
|
3766
|
+
*
|
|
3767
|
+
* 비어 있지 않다는 것은 「이 트윈의 수는 선언된 현장이 아니라 가정한 현장의 것」이라는 뜻이다.
|
|
3768
|
+
* 화면·AI 가 그 사실을 함께 말해야 한다.
|
|
3769
|
+
*/
|
|
3770
|
+
declarationOverrides() {
|
|
3771
|
+
return this.overrideLog.map((o) => ({ ...o }));
|
|
3772
|
+
}
|
|
3747
3773
|
// ── 보호 헬퍼 (도메인 hook 에서 사용) ──────────────────────────────────────
|
|
3748
3774
|
/**
|
|
3749
3775
|
* 이 커널의 **지금**(ms) — 시각으로 바뀌는 모든 판정의 단일 기준.
|
|
@@ -6292,6 +6318,10 @@ var EmsKernel = class extends FlowEngine {
|
|
|
6292
6318
|
if (!(capacity > 0) || !(maxRate > 0)) continue;
|
|
6293
6319
|
const state = this.equipment.get(e.id);
|
|
6294
6320
|
if (!state) continue;
|
|
6321
|
+
if (!Number.isFinite(Number(state.soc))) {
|
|
6322
|
+
const seed = this.numberProperty(e, EMS_PROPERTY.initialSoc);
|
|
6323
|
+
if (seed !== void 0) state.soc = seed;
|
|
6324
|
+
}
|
|
6295
6325
|
const over = demandBeforeStorageKW - dispatched - above;
|
|
6296
6326
|
if (over <= 0) {
|
|
6297
6327
|
if (Number(state.dischargeKW) > 0) {
|
package/package.json
CHANGED