@operato/twin-kernel 0.6.14 → 0.7.1
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/capability.d.ts +29 -1
- package/dist/capability.js +23 -1
- package/dist/capacity.d.ts +5 -5
- package/dist/capacity.js +7 -7
- package/dist/contract.d.ts +176 -32
- package/dist/contract.js +66 -7
- package/dist/counterfactual.d.ts +1 -1
- package/dist/counterfactual.js +2 -2
- package/dist/domain-catalog.d.ts +83 -17
- package/dist/domain-catalog.js +99 -14
- package/dist/domain-definition.d.ts +17 -1
- package/dist/ems-kernel.d.ts +83 -0
- package/dist/ems-kernel.js +307 -0
- package/dist/ems-profile.d.ts +16 -0
- package/dist/ems-profile.js +133 -0
- package/dist/energy-attribution.d.ts +208 -0
- package/dist/energy-attribution.js +229 -0
- package/dist/energy-ingest.d.ts +39 -0
- package/dist/energy-ingest.js +91 -0
- package/dist/epcis.d.ts +3 -3
- package/dist/epcis.js +2 -2
- package/dist/event-journal.d.ts +2 -2
- package/dist/event-journal.js +1 -1
- package/dist/flow-engine.d.ts +20 -20
- package/dist/flow-engine.js +40 -40
- package/dist/forecast.js +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/kernel.d.ts +3 -3
- package/dist/kernel.js +5 -5
- package/dist/mes-kernel.d.ts +2 -2
- package/dist/mes-kernel.js +2 -2
- package/dist/mes-profile.js +25 -1
- package/dist/observed-reducer.d.ts +25 -4
- package/dist/observed-reducer.js +30 -9
- package/dist/operations-capability.d.ts +8 -8
- package/dist/operations-capability.js +2 -2
- package/dist/task-fold.d.ts +1 -1
- package/dist/task-fold.js +1 -1
- package/dist/twin-observer.js +1 -1
- package/dist/wms-profile.d.ts +0 -11
- package/dist/wms-profile.js +17 -2
- package/dist/yms-profile.js +16 -1
- package/dist-cjs/index.cjs +814 -56
- package/package.json +1 -1
package/dist/capability.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CapabilityKey = 'operable' | 'storable' | 'mobile' | 'processable' | 'trackable';
|
|
1
|
+
export type CapabilityKey = 'operable' | 'storable' | 'mobile' | 'processable' | 'trackable' | 'metered' | 'curtailable' | 'generating' | 'storing';
|
|
2
2
|
/** 이동 관측 — Mobile 이 노출. */
|
|
3
3
|
export interface Motion {
|
|
4
4
|
fromNode: string;
|
|
@@ -30,6 +30,34 @@ export interface TrackableState {
|
|
|
30
30
|
progress?: number;
|
|
31
31
|
held: boolean;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* 계량 — 이 자원은 **자기 소비를 잰다.**
|
|
35
|
+
*
|
|
36
|
+
* `kW` 는 그 계량 주기의 평균이고 순시값이 아니다(순시는 전력 품질의 영역이라 범위 밖).
|
|
37
|
+
* `kWh` 는 계기 누적값 — 차분은 소비처가 한다(계기 교체·리셋을 우리가 지어내지 않는다).
|
|
38
|
+
* 값이 오지 않는 계량기는 `0` 이 아니라 **모르는 것**이므로 필드가 없다(선택 필드인 이유).
|
|
39
|
+
*/
|
|
40
|
+
export interface MeteredState {
|
|
41
|
+
kW?: number;
|
|
42
|
+
kWh?: number;
|
|
43
|
+
powerFactor?: number;
|
|
44
|
+
}
|
|
45
|
+
/** 감축 가능 — 줄일 수 있다. `minKW` 는 최소 유지(그 아래로 내리면 공정이 죽는다). */
|
|
46
|
+
export interface CurtailableState {
|
|
47
|
+
curtailable: boolean;
|
|
48
|
+
minKW?: number;
|
|
49
|
+
}
|
|
50
|
+
/** 발전 — 만든다. 역송(`exportKW`)은 소비의 음수가 아니라 별개 사실이다. */
|
|
51
|
+
export interface GeneratingState {
|
|
52
|
+
generatedKW?: number;
|
|
53
|
+
exportKW?: number;
|
|
54
|
+
}
|
|
55
|
+
/** 저장 — 담고 낸다. 충전과 방전을 나눈다(손실·수명 판단이 둘을 구별해야 한다). */
|
|
56
|
+
export interface StoringState {
|
|
57
|
+
soc?: number;
|
|
58
|
+
chargeKW?: number;
|
|
59
|
+
dischargeKW?: number;
|
|
60
|
+
}
|
|
33
61
|
/** 능력 관측 계약 메타(발견·검증). ops 없음(원칙 ①). */
|
|
34
62
|
export interface CapabilitySpec {
|
|
35
63
|
key: CapabilityKey;
|
package/dist/capability.js
CHANGED
|
@@ -36,9 +36,31 @@ export const CAPABILITIES = {
|
|
|
36
36
|
trackable: {
|
|
37
37
|
key: 'trackable', label: 'twin.capability.trackable', semantics: '오더/아이템 생애 추적 — 생애단계(도메인 라벨, 무방언)·진행·보류.',
|
|
38
38
|
stateFields: ['lifecycle', 'progress', 'held'], results: ['lifecycleChanged']
|
|
39
|
+
},
|
|
40
|
+
/*
|
|
41
|
+
* ── 에너지 능력 넷 ──────────────────────────────────────────────────────
|
|
42
|
+
* 상태 필드는 기존 능력과 **직교**한다(원칙 ②) — 어느 필드도 두 능력에 겹치지 않는다.
|
|
43
|
+
* 계량은 `operable`(가동 상태)과도 직교한다: 도는 것과 재는 것은 다른 사실이고, 멈춘 설비도
|
|
44
|
+
* 대기전력을 쓴다.
|
|
45
|
+
*/
|
|
46
|
+
metered: {
|
|
47
|
+
key: 'metered', label: 'twin.capability.metered', semantics: '자기 소비를 잰다 — 유효전력·누적량·역률. 값이 없으면 모르는 것이다(0 이 아니다).',
|
|
48
|
+
stateFields: ['kW', 'kWh', 'powerFactor'], models: ['MeteredState'], results: ['measured']
|
|
49
|
+
},
|
|
50
|
+
curtailable: {
|
|
51
|
+
key: 'curtailable', label: 'twin.capability.curtailable', semantics: '줄일 수 있다 — 최소 유지 아래로는 내리지 않는다. 판정만 하고 집행은 사람과 그 시스템의 몫이다.',
|
|
52
|
+
stateFields: ['curtailable', 'minKW'], models: ['CurtailableState'], invariants: ['minKW >= 0'], results: ['drSuggested']
|
|
53
|
+
},
|
|
54
|
+
generating: {
|
|
55
|
+
key: 'generating', label: 'twin.capability.generating', semantics: '전기를 만든다 — 역송은 소비의 음수가 아니라 별개 사실이다.',
|
|
56
|
+
stateFields: ['generatedKW', 'exportKW'], models: ['GeneratingState'], results: ['generated']
|
|
57
|
+
},
|
|
58
|
+
storing: {
|
|
59
|
+
key: 'storing', label: 'twin.capability.storing', semantics: '담고 낸다 — 충전과 방전을 나눈다(손실·수명 판단이 둘을 구별해야 한다).',
|
|
60
|
+
stateFields: ['soc', 'chargeKW', 'dischargeKW'], models: ['StoringState'], invariants: ['0 <= soc <= 100'], results: ['stored', 'discharged']
|
|
39
61
|
}
|
|
40
62
|
};
|
|
41
|
-
export const CAPABILITY_KEYS = ['operable', 'storable', 'mobile', 'processable', 'trackable'];
|
|
63
|
+
export const CAPABILITY_KEYS = ['operable', 'storable', 'mobile', 'processable', 'trackable', 'metered', 'curtailable', 'generating', 'storing'];
|
|
42
64
|
/** 능력 집합이 관측 노출하는 상태 필드 합집합(직교이므로 단순 병합). */
|
|
43
65
|
export function stateFieldsOf(caps) {
|
|
44
66
|
const out = new Set();
|
package/dist/capacity.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface CapacityAnalysis {
|
|
|
39
39
|
axis?: CapacityAxis;
|
|
40
40
|
className?: string;
|
|
41
41
|
};
|
|
42
|
-
/** 이 공장의
|
|
42
|
+
/** 이 공장의 상한(하루). */
|
|
43
43
|
maxUnitsPerDay: number;
|
|
44
44
|
ok: boolean;
|
|
45
45
|
}
|
|
@@ -67,8 +67,8 @@ export interface CapacityInput {
|
|
|
67
67
|
* 가용 시간을 샘플링할 기준 주의 시작(월요일 00:00, ms).
|
|
68
68
|
*
|
|
69
69
|
* **공휴일이 없는 평상주를 골라야 한다** — 공휴일은 연간 가용량을 따로 깎지, 이 공장의 평상시
|
|
70
|
-
*
|
|
71
|
-
* 들어 있을 때
|
|
70
|
+
* 상한을 정하지 않는다. 기본값을 두지 않는 이유: 커널이 임의의 주를 고르면 그 주에 공휴일이
|
|
71
|
+
* 들어 있을 때 상한이 조용히 낮아진다.
|
|
72
72
|
*/
|
|
73
73
|
sampleWeekStartMs: number;
|
|
74
74
|
utcOffsetMinutes?: number;
|
|
@@ -81,7 +81,7 @@ export declare function isoDurationHours(iso: string | undefined): number;
|
|
|
81
81
|
* 근무 캘린더에서 **가용 시간과 조업일**을 읽는다 — 1분 간격 샘플링.
|
|
82
82
|
*
|
|
83
83
|
* 교대·휴게·비근무 규칙을 여기 다시 적지 않는다. 규칙은 `inWorkCalendarAt` 한 곳에만 있고, 이 함수는
|
|
84
|
-
* 그것에 묻기만 한다. 두 벌이 되면 달력을 고칠 때 한쪽만 고쳐져
|
|
84
|
+
* 그것에 묻기만 한다. 두 벌이 되면 달력을 고칠 때 한쪽만 고쳐져 어긋난다.
|
|
85
85
|
*
|
|
86
86
|
* 캘린더가 없으면 **종일 가동**으로 본다(7일 × 24h) — 제약이 없는 것이 아니라 **선언되지 않은** 것이고,
|
|
87
87
|
* 선언이 없으면 커널은 멈출 이유를 모른다.
|
|
@@ -94,6 +94,6 @@ export declare function workingTimeOfWeek(calendar: readonly WorkCalendarEntry[]
|
|
|
94
94
|
* 이 공장이 선언된 물량을 낼 수 있는가 — 공정마다, 자원 축마다.
|
|
95
95
|
*
|
|
96
96
|
* 순수 함수다. 커널 상태를 읽지 않고 넘겨받은 것만 본다 — 그래야 "이 설비를 두 대 더 놓으면?" 을
|
|
97
|
-
*
|
|
97
|
+
* 실행해 보지 않고 물을 수 있다(what-if 의 가장 싼 형태).
|
|
98
98
|
*/
|
|
99
99
|
export declare function analyzeCapacity(input: CapacityInput): CapacityAnalysis;
|
package/dist/capacity.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* 용량 분석 —
|
|
2
|
+
* 용량 분석 — **실행해 보기 전에 "이 공장이 그 물량을 낼 수 있는가" 에 답한다.**
|
|
3
3
|
*
|
|
4
4
|
* ── 왜 커널인가 ──────────────────────────────────────────────────────────────
|
|
5
5
|
* 이 계산을 처음에는 레퍼런스 마스터 옆에서 손으로 했다(Rosarito). 그러다 두 가지를 알았다.
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* 로봇 6대가 논다" 를 구조적으로 못 본다. 네 축을 다 보는 자리는 커널뿐이다.
|
|
14
14
|
*
|
|
15
15
|
* ── 이것이 시뮬레이션과 다른 점 ─────────────────────────────────────────────
|
|
16
|
-
* 시뮬레이션은
|
|
17
|
-
* 상한**이다 — 모든 것이 계획대로 흘렀을 때의
|
|
16
|
+
* 시뮬레이션은 **실행해 봐야** 답이 나오고, 변동·고장·줄서기가 섞인 결과를 준다. 이 계산은 **정상상태
|
|
17
|
+
* 상한**이다 — 모든 것이 계획대로 흘렀을 때의 상한. 둘은 서로를 대체하지 않는다:
|
|
18
18
|
* - 상한이 수요보다 낮으면 **시뮬레이션을 돌릴 필요가 없다.** 무슨 짓을 해도 못 낸다.
|
|
19
19
|
* - 상한이 충분한데 시뮬레이션이 못 내면 그것은 **흐름의 문제**다(줄서기·배치·변동).
|
|
20
20
|
*
|
|
@@ -42,7 +42,7 @@ function paramOf(op, id) {
|
|
|
42
42
|
* 근무 캘린더에서 **가용 시간과 조업일**을 읽는다 — 1분 간격 샘플링.
|
|
43
43
|
*
|
|
44
44
|
* 교대·휴게·비근무 규칙을 여기 다시 적지 않는다. 규칙은 `inWorkCalendarAt` 한 곳에만 있고, 이 함수는
|
|
45
|
-
* 그것에 묻기만 한다. 두 벌이 되면 달력을 고칠 때 한쪽만 고쳐져
|
|
45
|
+
* 그것에 묻기만 한다. 두 벌이 되면 달력을 고칠 때 한쪽만 고쳐져 어긋난다.
|
|
46
46
|
*
|
|
47
47
|
* 캘린더가 없으면 **종일 가동**으로 본다(7일 × 24h) — 제약이 없는 것이 아니라 **선언되지 않은** 것이고,
|
|
48
48
|
* 선언이 없으면 커널은 멈출 이유를 모른다.
|
|
@@ -83,7 +83,7 @@ function availabilityOf(records) {
|
|
|
83
83
|
* 이 공장이 선언된 물량을 낼 수 있는가 — 공정마다, 자원 축마다.
|
|
84
84
|
*
|
|
85
85
|
* 순수 함수다. 커널 상태를 읽지 않고 넘겨받은 것만 본다 — 그래야 "이 설비를 두 대 더 놓으면?" 을
|
|
86
|
-
*
|
|
86
|
+
* 실행해 보지 않고 물을 수 있다(what-if 의 가장 싼 형태).
|
|
87
87
|
*/
|
|
88
88
|
export function analyzeCapacity(input) {
|
|
89
89
|
const { hoursPerWeek, daysPerWeek } = workingTimeOfWeek(input.calendar, input.sampleWeekStartMs, input.utcOffsetMinutes);
|
|
@@ -149,13 +149,13 @@ export function analyzeCapacity(input) {
|
|
|
149
149
|
add('asset', s.assetClass, s.quantity, assetsByClass.get(s.assetClass) ?? 0, 1);
|
|
150
150
|
if (op.locationType)
|
|
151
151
|
add('location', op.locationType, 1, slotsByLocationType.get(op.locationType) ?? 0, 1);
|
|
152
|
-
/* 공정의 능력 = 요구들 중 가장 낮은 것. 전량 확보 규칙이라 하나만 모자라도 그만큼만
|
|
152
|
+
/* 공정의 능력 = 요구들 중 가장 낮은 것. 전량 확보 규칙이라 하나만 모자라도 그만큼만 처리된다. */
|
|
153
153
|
const constraint = requirements.length ? requirements.reduce((a, b) => (a.perHour <= b.perHour ? a : b)) : undefined;
|
|
154
154
|
const perHour = constraint ? constraint.perHour : Infinity;
|
|
155
155
|
reversed.push({ operation: op.key, cycleHours, perHour, requiredPerHour, ok: perHour >= requiredPerHour, constraint, requirements });
|
|
156
156
|
}
|
|
157
157
|
const operations = reversed.reverse();
|
|
158
|
-
/* 병목 = 여유가 가장 적은 공정. 라인 전체의
|
|
158
|
+
/* 병목 = 여유가 가장 적은 공정. 라인 전체의 상한은 그 공정이 정한다. */
|
|
159
159
|
const tightest = operations.length
|
|
160
160
|
? operations.reduce((a, b) => (a.perHour / a.requiredPerHour <= b.perHour / b.requiredPerHour ? a : b))
|
|
161
161
|
: undefined;
|
package/dist/contract.d.ts
CHANGED
|
@@ -82,7 +82,7 @@ export interface Hierarchy {
|
|
|
82
82
|
* `deep` 이면 아래 자리들의 설비까지("이 라인의 설비").
|
|
83
83
|
*
|
|
84
84
|
* 지금 그 자리에 **와 있는** 설비(`location` 기준)와 다른 질문이다 — 지게차는 어디에나 와 있을 수
|
|
85
|
-
* 있지만 어디에도 붙박이지 않는다. 둘을 한 함수로
|
|
85
|
+
* 있지만 어디에도 붙박이지 않는다. 둘을 한 함수로 합치면 소속과 현재 위치가 섞인다.
|
|
86
86
|
*/
|
|
87
87
|
equipmentOf(locationId: string, opts?: {
|
|
88
88
|
deep?: boolean;
|
|
@@ -103,7 +103,18 @@ export declare function hierarchyOf(s: {
|
|
|
103
103
|
id: string;
|
|
104
104
|
homeLocation?: string;
|
|
105
105
|
}[];
|
|
106
|
-
}
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* 자리가 단(`level`)을 적지 않았을 때 **타입으로 알아내는 해석기** — 카탈로그가 주입한다.
|
|
109
|
+
*
|
|
110
|
+
* 왜 주입인가: 단은 타입의 성질이라 카탈로그가 선언하는데(`TwinTypeInfo.level`), 이 파일은
|
|
111
|
+
* 카탈로그보다 아래에 있다(카탈로그가 이 파일을 읽는다). 여기서 카탈로그를 부르면 순환이 된다.
|
|
112
|
+
*
|
|
113
|
+
* 왜 필요한가: 실측(2026-08-14) 로케이션 82개 중 `level` 을 적은 것이 **0개**였다. 모델이 적어 줄
|
|
114
|
+
* 때까지 기다리면 `ancestorOfLevel` 은 계속 `undefined` 를 답한다 — 선언은 있고 답은 없는 상태다.
|
|
115
|
+
* 자리가 적었으면 그것이 권위이고(현장이 우리보다 자기 계층을 잘 안다), 없으면 타입이 답한다.
|
|
116
|
+
*/
|
|
117
|
+
levelOfType?: (type: string) => EquipmentLevel | undefined): Hierarchy;
|
|
107
118
|
/**
|
|
108
119
|
* 자원 속성 — **ISA-95 가 세 자원에 똑같이 정의한 한 모양.**
|
|
109
120
|
*
|
|
@@ -121,7 +132,8 @@ export declare function hierarchyOf(s: {
|
|
|
121
132
|
* 추정기까지 `any` 로 전달됐다. 자원에 붙는 사실이 계약에 자리가 없으면 소비처마다 다르게 읽는다.
|
|
122
133
|
*/
|
|
123
134
|
export interface ResourceProperty {
|
|
124
|
-
/** 속성 식별자 — 표준 `ID`. 어휘는 표준이 정하지 않으므로 우리가 한 곳에서 정한다
|
|
135
|
+
/** 속성 식별자 — 표준 `ID`. 어휘는 표준이 정하지 않으므로 우리가 한 곳에서 정한다 — 그 자리는 읽는 쪽에 있다
|
|
136
|
+
* (headless-twin `EQUIPMENT_PROPERTY`·`PROPERTY_EFFECTS`). 커널은 속성을 **나르기만** 한다. */
|
|
125
137
|
id: string;
|
|
126
138
|
description?: string;
|
|
127
139
|
/** 표준 `Value.ValueString`. 값의 표기는 문자열이고, 뜻은 `dataType`·`uom` 이 정한다. */
|
|
@@ -166,7 +178,7 @@ export type TestSpecificationRefs = string[];
|
|
|
166
178
|
* ── 결과는 여기 담지 않는다 ───────────────────────────────────────────────────
|
|
167
179
|
* 명세는 **선언**이고 시험 결과는 **관측**이다(누가 언제 통과했나). 결과의 집은 저널이고, 축이
|
|
168
180
|
* 필요해지면 일정·실적과 같은 방식(`source: 'state'|'journal'`)으로 따로 낸다. 한 축에 둘을 담으면
|
|
169
|
-
* 선언과 관측을
|
|
181
|
+
* 선언과 관측을 구분하는 이 트윈의 뼈대가 무너진다.
|
|
170
182
|
*/
|
|
171
183
|
/**
|
|
172
184
|
* 시험 결과 — **"이 개체가 그 시험을 통과했다"** 는 기록.
|
|
@@ -222,7 +234,7 @@ export declare function meetsTests(required: readonly string[], results: readonl
|
|
|
222
234
|
* ── 왜 계약이 이것을 소유해야 하나 ────────────────────────────────────────────
|
|
223
235
|
* 커널은 배정할 때 이미 이 판정을 한다(교대·고장·보류·유효기간·시험 만료). 그런데 그 규칙이 **엔진 안의
|
|
224
236
|
* 필터 조건으로만** 있어서, 화면은 같은 판정을 자기 코드로 다시 만들었다(`reasonOf`). 규칙이 두 벌이면
|
|
225
|
-
* 반드시
|
|
237
|
+
* 반드시 어긋난다 — 배정은 막는데 화면은 "가용" 이라 말하는 순간이 온다. 그 어긋남은 조용하다.
|
|
226
238
|
*
|
|
227
239
|
* 그래서 **이유까지 계약이 낸다.** 화면·예측·AI 가 같은 낱말로 말하고, 새 이유가 생기면(시험 만료가
|
|
228
240
|
* 그랬다) 한 곳만 늘어난다.
|
|
@@ -328,7 +340,7 @@ export interface ResourceClassDef extends EffectivePeriod {
|
|
|
328
340
|
* 등급 소속을 **상속을 타고 닫는다** — "이 개체가 이 등급으로 통하는가".
|
|
329
341
|
*
|
|
330
342
|
* 순환은 방문 집합으로 끊는다(잘못된 마스터가 무한 루프를 만들지 않게). 등급 정의가 없으면 소속
|
|
331
|
-
* 그대로만 본다 — 정의를 요구하지 않는다(정의를 싣지 않은 트윈이 그대로
|
|
343
|
+
* 그대로만 본다 — 정의를 요구하지 않는다(정의를 싣지 않은 트윈이 그대로 동작해야 한다).
|
|
332
344
|
*
|
|
333
345
|
* `at` 를 주면 **유효 기간 밖의 등급은 제외**한다. 안 주면 기간을 보지 않는다(모르면 판단하지 않는다).
|
|
334
346
|
*/
|
|
@@ -338,7 +350,7 @@ export declare function classClosure(directIds: readonly string[] | undefined, d
|
|
|
338
350
|
* `NumericType` 제한). 즉 표준은 **숫자라는 것만 정하고 방향은 정하지 않는다.**
|
|
339
351
|
*
|
|
340
352
|
* **그래서 방향은 우리가 정한다: 작은 값이 급하다(1 = 가장 급함).** 흔한 관행이고, 무엇보다
|
|
341
|
-
* 한쪽으로
|
|
353
|
+
* 한쪽으로 고정해 두지 않으면 소비처마다 반대로 읽는다. 우리가 정한 규약이라는 사실을 여기 밝힌다.
|
|
342
354
|
*
|
|
343
355
|
* 미지정은 **0 이 아니라 "우선순위 없음"** 이다 — 선언한 것들 뒤에 선다(0 으로 채우면 미지정이
|
|
344
356
|
* 가장 급한 것이 된다).
|
|
@@ -386,8 +398,8 @@ export interface MaterialQuantity {
|
|
|
386
398
|
/**
|
|
387
399
|
* 로트의 부분 식별자를 **한 규칙으로** 만든다 — 표준 `MaterialSubLot.ID`.
|
|
388
400
|
*
|
|
389
|
-
* 시뮬(생산)과 미러(관측)가 각자 만들면 같은 부분이 다른 이름을 갖고, 두 구동이
|
|
390
|
-
* (적합성 하네스가 실제로 잡았다). 부분을
|
|
401
|
+
* 시뮬(생산)과 미러(관측)가 각자 만들면 같은 부분이 다른 이름을 갖고, 두 구동이 어긋난다
|
|
402
|
+
* (적합성 하네스가 실제로 잡았다). 부분을 구분하는 것은 **자리**다.
|
|
391
403
|
*/
|
|
392
404
|
/**
|
|
393
405
|
* 물품을 구별하는 키 — 직렬 물품은 `epc`, 로트의 부분은 `subLotId`(표준 `MaterialSubLot.ID`).
|
|
@@ -422,7 +434,7 @@ export declare function quantityIn(item: {
|
|
|
422
434
|
*
|
|
423
435
|
* ── 표준에는 환산 요소가 없다 ─────────────────────────────────────────────
|
|
424
436
|
* 전수 대조 결과 `MaterialDefinitionType` 에 단위 환산을 담을 **전용 요소가 없다.** 표준이 주는 것은
|
|
425
|
-
* **속성 주머니**(`MaterialDefinitionProperty`)뿐이다 — 설비 속도(`
|
|
437
|
+
* **속성 주머니**(`MaterialDefinitionProperty`)뿐이다 — 설비 속도(`speed`)와 같은
|
|
426
438
|
* 상황이다. 그래서 자리는 표준 것을 쓰고 **이름은 우리가 정하고 밝힌다**(§MATERIAL_PROPERTY).
|
|
427
439
|
*/
|
|
428
440
|
export interface MaterialDefinition extends EffectivePeriod {
|
|
@@ -521,7 +533,7 @@ export interface WorkCalendarEntry {
|
|
|
521
533
|
*
|
|
522
534
|
* ── 표준에 되풀이 표기가 없다 (1차 출처 확인) ────────────────────────────
|
|
523
535
|
* `WorkCalendarDefinitionEntryType.RecurrenceTime`·`DurationRule` 은 **`CodeType`**, 즉 **불투명한
|
|
524
|
-
* 코드**다 — 표준은 자리만 두고 문법을 정하지 않는다. 설비 속도(`
|
|
536
|
+
* 코드**다 — 표준은 자리만 두고 문법을 정하지 않는다. 설비 속도(`speed`)·
|
|
525
537
|
* 단위 환산(`MATERIAL_PROPERTY.perBaseUnit`)과 같은 상황이라, **우리가 정하고 밝힌다.**
|
|
526
538
|
*
|
|
527
539
|
* ── 없을 때 무엇을 못 했나 ───────────────────────────────────────────────
|
|
@@ -593,7 +605,7 @@ export declare function minuteOfDayAt(ms: number, utcOffsetMinutes?: number): nu
|
|
|
593
605
|
* 옛 판정에 머물렀다. 시각으로만 바뀌는 사실은 실어 보낼 수 없고 **각자 계산해야** 한다.
|
|
594
606
|
*/
|
|
595
607
|
/**
|
|
596
|
-
* **왜 안 하고 있나** — 쉬는 이유를
|
|
608
|
+
* **왜 안 하고 있나** — 쉬는 이유를 구분한다.
|
|
597
609
|
*
|
|
598
610
|
* `offShift` 만으로는 화면이 "교대 밖" 이라고밖에 못 말한다. 그런데 사용자가 알고 싶은 것은
|
|
599
611
|
* **휴일이라 오늘 통째로 서는 것인지**, 잠깐 휴게인지, 그냥 교대 시간이 아닌지다 — 셋은 기다릴
|
|
@@ -660,7 +672,7 @@ export interface LocationState {
|
|
|
660
672
|
*
|
|
661
673
|
* 저장 용량과 다른 축이다: 절단 스테이션은 자재를 20개 쌓아 둘 수 있어도(`capacity`) 한 번에
|
|
662
674
|
* 한 대만 깎는다(`parallelism: 1`). 예전에는 한 필드가 둘을 겸해서 그 현장을 표현할 방법이 없었고,
|
|
663
|
-
* 자원만
|
|
675
|
+
* 자원만 대기 중이면 같은 자리에서 작업이 무제한 동시에 진행됐다 — 대기가 생기지 않아 병목이
|
|
664
676
|
* 사라지고 예측이 낙관 쪽으로 치우쳤다.
|
|
665
677
|
*
|
|
666
678
|
* 미지정 = 제약 없음(기존 거동). 0 은 "처리하지 않는 자리"가 아니라 **선언 오류**로 보고 무시한다
|
|
@@ -740,7 +752,7 @@ export interface ItemState {
|
|
|
740
752
|
* 오더의 skuMix·할당이 이 값으로 매칭하므로 뜻을 바꾸지 않는다.
|
|
741
753
|
*/
|
|
742
754
|
gtin?: string;
|
|
743
|
-
/** 품번 키(CompanyPrefix.ItemRef) — 식별자에서 파생. 로트가 달라도 같은 품번으로
|
|
755
|
+
/** 품번 키(CompanyPrefix.ItemRef) — 식별자에서 파생. 로트가 달라도 같은 품번으로 모으는 축. */
|
|
744
756
|
gtinKey?: string;
|
|
745
757
|
/** 로트·배치 번호 — LGTIN 이면 식별자에서 파생, 직렬 개체면 ilmd 에서 온다(ilmd 는 미지원). */
|
|
746
758
|
lot?: string;
|
|
@@ -811,7 +823,7 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
811
823
|
/**
|
|
812
824
|
* **어디에 속하나** — 붙박인 자리(마스터의 `homeLocationId`). `location` 과 다른 질문이다.
|
|
813
825
|
*
|
|
814
|
-
* 이 필드가 **고정 설비와 이동 설비를
|
|
826
|
+
* 이 필드가 **고정 설비와 이동 설비를 구분한다** — 새 타입 플래그 없이:
|
|
815
827
|
* - 도장기·용접로봇은 평생 그 자리에 있다 → `location === homeLocation` 가 항상 성립.
|
|
816
828
|
* - 지게차·호슬러는 돌아다닌다 → 둘이 갈린다. 그래도 소속은 `homeLocation` 하나로 안정적이다.
|
|
817
829
|
*
|
|
@@ -827,7 +839,7 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
827
839
|
held?: boolean;
|
|
828
840
|
/**
|
|
829
841
|
* 교대 밖이라 지금 일하지 않는다 — 고장(down)·계획정지(held)와 **다른 이유**다.
|
|
830
|
-
* 셋을
|
|
842
|
+
* 셋을 합치면 "왜 안 움직이나" 에 답할 수 없다(고쳐야 하나·풀어야 하나·기다려야 하나).
|
|
831
843
|
*/
|
|
832
844
|
offShift?: boolean;
|
|
833
845
|
/**
|
|
@@ -850,7 +862,7 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
850
862
|
/** 이 자원을 어떻게 알게 됐는가 — LocationState.origin 과 같은 뜻(성장 정책을 한 규칙으로 선언). */
|
|
851
863
|
origin?: 'master' | 'observed';
|
|
852
864
|
/**
|
|
853
|
-
* 자원 속성 — 표준 `EquipmentProperty`. 속도(`
|
|
865
|
+
* 자원 속성 — 표준 `EquipmentProperty`. 속도(`speed`)처럼 **소비처가 읽는 사실**이
|
|
854
866
|
* 여기 실린다. 지금까지 계약 밖으로 흘러 호스트까지 `any` 로 전달됐다(§ResourceProperty).
|
|
855
867
|
*/
|
|
856
868
|
properties?: ResourceProperty[];
|
|
@@ -869,7 +881,7 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
869
881
|
* 상태에 실어 보내는 이유: 이 판정은 **시각과 등급 정의를 함께 알아야** 나온다(유효기간·교대·자격
|
|
870
882
|
* 만료). 소비처는 그 둘을 갖고 있지 않다 — 화면은 시뮬 시각을 모르고, 호스트는 등급 상속을 타고
|
|
871
883
|
* 필수 시험을 모으는 규칙을 모른다. 그래서 예전에는 소비처가 플래그를 보고 짐작했고, 자격이 만료된
|
|
872
|
-
* 사람이 `대기` 로 보였다.
|
|
884
|
+
* 사람이 `대기` 로 보였다. 대기 중인 것은 맞지만 **쓸 수 있는 것은 아니다** — "대기 7명" 이 조용히
|
|
873
885
|
* 틀린 숫자가 됐다.
|
|
874
886
|
*
|
|
875
887
|
* 배정이 쓰는 판정과 **같은 함수의 결과**다(규칙 한 벌). 사유의 순서도 계약이 정한다.
|
|
@@ -879,7 +891,7 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
879
891
|
/**
|
|
880
892
|
* 사람 — **ISA-95 `Person`.** 설비와 다른 자원 종류다.
|
|
881
893
|
*
|
|
882
|
-
* 왜
|
|
894
|
+
* 왜 설비로 합치지 않는가: 사람은 고장 나지 않고(MTBF), 설비종합효율로 평가하지 않으며,
|
|
883
895
|
* **등급(자격)과 교대로 산다.** 같은 그릇에 담으면 설비의 어휘(고장·수리·OEE)가 사람에게 붙고
|
|
884
896
|
* 사람의 어휘(등급·교대·투입 인원)가 설비에 붙는다 — 둘 다 거짓이 된다.
|
|
885
897
|
*
|
|
@@ -941,7 +953,7 @@ export interface PersonState extends EffectivePeriod {
|
|
|
941
953
|
* 상태에 실어 보내는 이유: 이 판정은 **시각과 등급 정의를 함께 알아야** 나온다(유효기간·교대·자격
|
|
942
954
|
* 만료). 소비처는 그 둘을 갖고 있지 않다 — 화면은 시뮬 시각을 모르고, 호스트는 등급 상속을 타고
|
|
943
955
|
* 필수 시험을 모으는 규칙을 모른다. 그래서 예전에는 소비처가 플래그를 보고 짐작했고, 자격이 만료된
|
|
944
|
-
* 사람이 `대기` 로 보였다.
|
|
956
|
+
* 사람이 `대기` 로 보였다. 대기 중인 것은 맞지만 **쓸 수 있는 것은 아니다** — "대기 7명" 이 조용히
|
|
945
957
|
* 틀린 숫자가 됐다.
|
|
946
958
|
*
|
|
947
959
|
* 배정이 쓰는 판정과 **같은 함수의 결과**다(규칙 한 벌). 사유의 순서도 계약이 정한다.
|
|
@@ -953,7 +965,7 @@ export interface PersonState extends EffectivePeriod {
|
|
|
953
965
|
*
|
|
954
966
|
* 왜 물품(Material)과 따로 두는가: **SSCC 와 GRAI 는 다른 것**이다. SSCC 는 *물류단위*(그 팔레트에
|
|
955
967
|
* 실린 화물 한 덩어리)이고 GRAI 는 *돌아오는 팔레트 자체*다. 같은 GRAI 팔레트가 오늘은 SSCC 999 를,
|
|
956
|
-
* 내일은 다른 SSCC 를 싣는다. 둘을
|
|
968
|
+
* 내일은 다른 SSCC 를 싣는다. 둘을 합치면 **팔레트 회수·풀링을 표현할 수 없고**(팔레트가 화물과 함께
|
|
957
969
|
* 사라진다), EPCIS 조립 이벤트의 `parentID`(=물류단위)도 뜻이 흐려진다.
|
|
958
970
|
*
|
|
959
971
|
* 그리고 빈 팔레트 부족은 현장의 실제 제약이다 — 자산이 없어 작업이 못 나가는 일이 사람 부족만큼 잦다.
|
|
@@ -1011,7 +1023,7 @@ export interface TaskState {
|
|
|
1011
1023
|
orderId?: string;
|
|
1012
1024
|
progress?: number;
|
|
1013
1025
|
/**
|
|
1014
|
-
* 남은 시간·총 소요(ms) — 진행 중인 작업을 이어서
|
|
1026
|
+
* 남은 시간·총 소요(ms) — 진행 중인 작업을 이어서 실행하는 데 필요(씨앗의 충실도).
|
|
1015
1027
|
* `remainingMs` 는 **마지막 전이 시점의 값**이다. 델타는 매 tick 오지 않으므로(설계) 미러가 든 값은
|
|
1016
1028
|
* 그때의 것이고, 지금 값은 `startedAtSimMs` 로 보간한다 — 모션(EquipmentMotion)과 같은 규율.
|
|
1017
1029
|
*/
|
|
@@ -1092,7 +1104,7 @@ export interface OrderState {
|
|
|
1092
1104
|
* 보이고, 그 오더에 딸린 진행 중 작업이 완료될 때 계보(`TransformationEvent`)가 **입력 없이**
|
|
1093
1105
|
* 나간다 — "무엇이 무엇으로 바뀌었나" 의 절반이 사라진다. 실제로 그렇게 되고 있었다.
|
|
1094
1106
|
*
|
|
1095
|
-
* 씨앗이 잃은 것은 예측도 모른다 — 진행 중 자재를 안 잡은 것으로 놓고 미래를
|
|
1107
|
+
* 씨앗이 잃은 것은 예측도 모른다 — 진행 중 자재를 안 잡은 것으로 놓고 미래를 실행하면 답이
|
|
1096
1108
|
* 낙관 쪽으로 치우친다(`hydrateObserved` 가 상태를 지키는 이유와 같다).
|
|
1097
1109
|
*/
|
|
1098
1110
|
allocated?: string[];
|
|
@@ -1157,6 +1169,60 @@ export interface Attention {
|
|
|
1157
1169
|
args?: unknown;
|
|
1158
1170
|
};
|
|
1159
1171
|
}
|
|
1172
|
+
/** 계량 지점의 마지막 관측 — 값이 없으면 **모르는 것이다**(0 이 아니다). */
|
|
1173
|
+
export interface MeterPointState {
|
|
1174
|
+
id: string;
|
|
1175
|
+
kW?: number;
|
|
1176
|
+
/** 누적 전력량(원천이 준 적산값) — 우리가 적분한 값이 아니다. */
|
|
1177
|
+
kWh?: number;
|
|
1178
|
+
powerFactor?: number;
|
|
1179
|
+
/** 마지막 표본의 시각 — 계측이 끊긴 것을 소비처가 알 수 있게. */
|
|
1180
|
+
atMs?: number;
|
|
1181
|
+
/** 이 구간에 받은 표본 수 — 「못 쟀다」와 「0이었다」를 구별한다. */
|
|
1182
|
+
samplesInWindow: number;
|
|
1183
|
+
}
|
|
1184
|
+
export interface DemandWindowState {
|
|
1185
|
+
startMs: number;
|
|
1186
|
+
endMs: number;
|
|
1187
|
+
/** 그 구간의 최대 순간부하 — 표본이 없으면 `undefined`(0 이 아니다). */
|
|
1188
|
+
maxKW?: number;
|
|
1189
|
+
/** 표본 평균 부하 — 요금 산정의 평균 수요에 대응한다. */
|
|
1190
|
+
meanKW?: number;
|
|
1191
|
+
/** **kW 를 실은** 표본 수 — 부하 판정의 근거 수다(받은 표본 전체가 아니다). */
|
|
1192
|
+
samples: number;
|
|
1193
|
+
/** 계약전력 대비 — 계약을 모르면 `undefined`(짐작하지 않는다). */
|
|
1194
|
+
contractKW?: number;
|
|
1195
|
+
overContract?: boolean;
|
|
1196
|
+
}
|
|
1197
|
+
export interface EnergyState {
|
|
1198
|
+
points: MeterPointState[];
|
|
1199
|
+
/** 지금 열려 있는 구간 — 아직 마감되지 않았다(예측은 `projectedKW`). */
|
|
1200
|
+
open?: DemandWindowState & {
|
|
1201
|
+
projectedKW?: number;
|
|
1202
|
+
projectionBasis?: 'mean-so-far';
|
|
1203
|
+
};
|
|
1204
|
+
/**
|
|
1205
|
+
* 마감된 구간들 — **최근 것만 들고 있다**(상태 크기가 시간에 비례하지 않게).
|
|
1206
|
+
* 자른 사실을 `closedTotal` 로 함께 낸다 — 조용히 자르지 않는다.
|
|
1207
|
+
*/
|
|
1208
|
+
closed: DemandWindowState[];
|
|
1209
|
+
closedTotal: number;
|
|
1210
|
+
/** 관측 시작 이후 최대 수요 — 월 경계는 여기서 정하지 않는다(위 주석). */
|
|
1211
|
+
peakSince?: {
|
|
1212
|
+
kW: number;
|
|
1213
|
+
windowStartMs: number;
|
|
1214
|
+
};
|
|
1215
|
+
/** 현장이 선언한 계약전력(자리 속성) — 없으면 계약 대비 판정을 하지 않는다. */
|
|
1216
|
+
contractKW?: number;
|
|
1217
|
+
/**
|
|
1218
|
+
* 이 커널이 받은 **물류 흐름 요청** — 에너지에는 없는 것들이다(도착·오더·배정·작업 완료).
|
|
1219
|
+
* 비어 있지 않으면 배선 오류다: EMS 트윈에 물류 명령이 오고 있다. 조용히 넘기지 않는다.
|
|
1220
|
+
*/
|
|
1221
|
+
flowRequests?: {
|
|
1222
|
+
hook: string;
|
|
1223
|
+
count: number;
|
|
1224
|
+
}[];
|
|
1225
|
+
}
|
|
1160
1226
|
/**
|
|
1161
1227
|
* 조치방향 — code=안정 조치 키(언어 중립). command 있으면 원클릭 실행, 없으면 권고.
|
|
1162
1228
|
* 라벨·힌트(사람 언어)는 표현계층이 code 로 렌더(커널은 문장 미보유). command 보유 조치는 code=command 문자열,
|
|
@@ -1202,9 +1268,16 @@ export interface StateSnapshot {
|
|
|
1202
1268
|
* 확인(ack)해 둔 주목 신호 id — **상태에서 파생되지 않는 유일한 축.**
|
|
1203
1269
|
*
|
|
1204
1270
|
* 신호 자체는 상태에서 다시 계산되지만 "사람이 봤다" 는 계산으로 되살릴 수 없다. 스냅샷으로
|
|
1205
|
-
* 왕복시켜야
|
|
1271
|
+
* 왕복시켜야 재기동·재계산에서 확인 상태가 유지된다.
|
|
1206
1272
|
*/
|
|
1207
1273
|
acked?: string[];
|
|
1274
|
+
/**
|
|
1275
|
+
* 에너지 — **에너지 트윈만 채운다**(계량 지점·수요 구간·피크). 다른 종류에서는 없다.
|
|
1276
|
+
*
|
|
1277
|
+
* 없는 것과 빈 것을 구별한다: 필드가 아예 없으면 그 트윈은 에너지를 재지 않는 것이고,
|
|
1278
|
+
* `points: []` 는 「아직 표본이 없다」다.
|
|
1279
|
+
*/
|
|
1280
|
+
energy?: EnergyState;
|
|
1208
1281
|
}
|
|
1209
1282
|
export interface Command<T = unknown> {
|
|
1210
1283
|
commandId: string;
|
|
@@ -1323,10 +1396,80 @@ export declare const OP_EVENT: {
|
|
|
1323
1396
|
*
|
|
1324
1397
|
* 다른 파생 상태는 상태에서 다시 계산된다(주목 신호 자체가 그렇다). 그런데 "누가 이것을 봤다" 는
|
|
1325
1398
|
* 계산으로 되살릴 수 없다. 저널에 남기지 않으면 재기동하면 확인해 둔 신호가 다시 빨개지고,
|
|
1326
|
-
* 과거를
|
|
1399
|
+
* 과거를 다시 계산해도 그때 무엇을 확인했는지 알 수 없다 — 저널이 현실을 불완전하게 담는 자리였다.
|
|
1327
1400
|
*/
|
|
1328
1401
|
readonly attentionAck: "attention.acked";
|
|
1329
1402
|
};
|
|
1403
|
+
/**
|
|
1404
|
+
* 에너지 트윈의 사건 — **물(物)의 계보가 아니라 스칼라의 시계열.**
|
|
1405
|
+
*
|
|
1406
|
+
* ── 왜 EPCIS 어휘를 쓰지 않나 ──────────────────────────────────────────────
|
|
1407
|
+
* EPCIS 는 "무엇이 어디서 무엇에 일어났나" 를 물건 단위로 말한다. 에너지에는 옮겨 다니는 물건이
|
|
1408
|
+
* 없다 — 계량 지점에서 수치가 변할 뿐이다. 억지로 ObjectEvent 로 감싸면 화면이 "전력 한 개가
|
|
1409
|
+
* 이동했다" 로 읽는다. 그래서 자기 `eventType` 을 갖되 **봉투는 같은 것**을 쓴다
|
|
1410
|
+
* (`CanonicalEnvelope { eventType, data }`) — 저널·리플레이·시간여행을 그대로 얻는다.
|
|
1411
|
+
*
|
|
1412
|
+
* ── 왜 지금 사건만 선언하나 ────────────────────────────────────────────────
|
|
1413
|
+
* 상태 필드(계량 지점의 kW·구간의 계약전력)와 능력(`metered`·`curtailable`)은 **채우는 쪽이
|
|
1414
|
+
* 생길 때** 함께 선언한다. 선언에만 자리를 두고 아무도 옮기지 않으면 그것이 이 프로젝트가 하네스로
|
|
1415
|
+
* 막아 온 바로 그 결함이다(`declaration-in-state`). 사건은 그 검사의 대상이 아니고, 커넥터·커널이
|
|
1416
|
+
* 무엇을 주고받을지 먼저 합의해야 하는 값이라 여기가 그 자리다.
|
|
1417
|
+
*
|
|
1418
|
+
* 설계 근거: `design/profiles/ems.md` §4.
|
|
1419
|
+
*/
|
|
1420
|
+
export declare const ENERGY_EVENT: {
|
|
1421
|
+
/** 계량 도착 — 그 시점의 유효전력·누적량. 15분 수요 구간에 누적된다. */
|
|
1422
|
+
readonly measured: "energy.measured";
|
|
1423
|
+
/** 수요 구간 마감 — 그 구간의 최대 수요가 확정된다(요금의 단위). */
|
|
1424
|
+
readonly demandWindow: "energy.demand.window";
|
|
1425
|
+
/**
|
|
1426
|
+
* 피크 경신 — 월 최대 수요가 갱신됐다.
|
|
1427
|
+
*
|
|
1428
|
+
* 구간 마감에서 파생되지만 **사실로 남긴다**: 요금의 근거이고, 나중에 "언제 무엇 때문에 올랐나" 를
|
|
1429
|
+
* 물을 때 파생으로는 답할 수 없다(그 순간의 부하 구성이 사라진다).
|
|
1430
|
+
*/
|
|
1431
|
+
readonly peak: "energy.peak";
|
|
1432
|
+
/** 요금 구간 전환 — 경부하·중간부하·최대부하. 달력이 아니라 사건이다(계절제·특례로 바뀐다). */
|
|
1433
|
+
readonly tariffShift: "energy.tariff.shift";
|
|
1434
|
+
/** 발전(PV 등) — 역송을 포함한다(음의 소비가 아니라 별개 사실이다). */
|
|
1435
|
+
readonly generated: "energy.generated";
|
|
1436
|
+
/** 저장(ESS 충전). */
|
|
1437
|
+
readonly stored: "energy.stored";
|
|
1438
|
+
/** 방전(ESS). 충전과 나눈다 — 손실·수명 판단이 둘을 구별해야 한다. */
|
|
1439
|
+
readonly discharged: "energy.discharged";
|
|
1440
|
+
/**
|
|
1441
|
+
* 수요 제어 제안 — **제안이지 명령이 아니다.**
|
|
1442
|
+
*
|
|
1443
|
+
* 이 트윈은 차단·투입을 하지 않는다(안전 계통은 범위 밖: `ems.md` §1). 이대로면 계약을 넘는다는
|
|
1444
|
+
* 판단과 무엇을 줄이면 되는지를 낼 뿐이고, 집행은 사람과 그 시스템의 몫이다. 이름을 `suggested`
|
|
1445
|
+
* 로 둔 이유가 그것이다 — 저널만 보고도 "우리가 끈 것이 아니다" 를 알 수 있어야 한다.
|
|
1446
|
+
*/
|
|
1447
|
+
readonly drSuggested: "energy.dr.suggested";
|
|
1448
|
+
};
|
|
1449
|
+
export type EnergyEventType = (typeof ENERGY_EVENT)[keyof typeof ENERGY_EVENT];
|
|
1450
|
+
/** 계량 도착의 실린 값 — 계량 지점 하나의 한 시점. */
|
|
1451
|
+
export interface EnergyMeasuredData {
|
|
1452
|
+
/** 계량 지점(설비 id). */
|
|
1453
|
+
meterId: string;
|
|
1454
|
+
/** 유효전력(kW). 순시가 아니라 그 계량 주기의 평균이다. */
|
|
1455
|
+
kW: number;
|
|
1456
|
+
/** 누적 전력량(kWh) — 계기 누적값. 차분은 소비처가 한다(계기 교체·리셋을 우리가 지어내지 않는다). */
|
|
1457
|
+
kWh?: number;
|
|
1458
|
+
/** 역률 — 없으면 모르는 것이다(1 로 채우지 않는다). */
|
|
1459
|
+
powerFactor?: number;
|
|
1460
|
+
at: ISOTime;
|
|
1461
|
+
}
|
|
1462
|
+
/** 수요 구간 마감 — 계약 대비 판정의 단위. */
|
|
1463
|
+
export interface EnergyDemandWindowData {
|
|
1464
|
+
/** 배전 구간(로케이션 id) 또는 현장 전체. */
|
|
1465
|
+
feederId?: string;
|
|
1466
|
+
windowStart: ISOTime;
|
|
1467
|
+
windowEnd: ISOTime;
|
|
1468
|
+
/** 그 구간의 최대 수요(kW). */
|
|
1469
|
+
peakKW: number;
|
|
1470
|
+
/** 계약전력(kW) — 선언에서 온다. 없으면 판정하지 않는다(넘었는지 말할 수 없다). */
|
|
1471
|
+
contractKW?: number;
|
|
1472
|
+
}
|
|
1330
1473
|
/**
|
|
1331
1474
|
* 주목 신호 확인 델타 — 확인한 신호의 id 와 시각.
|
|
1332
1475
|
*
|
|
@@ -1391,7 +1534,7 @@ export interface TaskStatusDelta {
|
|
|
1391
1534
|
*/
|
|
1392
1535
|
intent?: 'transport' | 'process' | 'dwell';
|
|
1393
1536
|
/**
|
|
1394
|
-
* 진척(0~1)과 남은 시간(ms) — **진행 중인 작업을 이어서
|
|
1537
|
+
* 진척(0~1)과 남은 시간(ms) — **진행 중인 작업을 이어서 실행하려면 필요하다.**
|
|
1395
1538
|
*
|
|
1396
1539
|
* 없으면 미러 상태를 씨앗으로 한 예측이 "진행 중인 일이 하나도 없는 현장" 에서 출발한다.
|
|
1397
1540
|
* 완료·생성 시점에는 의미가 없어 in-progress 에만 실린다.
|
|
@@ -1526,6 +1669,7 @@ export interface TwinModelDef {
|
|
|
1526
1669
|
capacity: number;
|
|
1527
1670
|
parallelism?: number;
|
|
1528
1671
|
parentId?: string;
|
|
1672
|
+
properties?: ResourceProperty[];
|
|
1529
1673
|
}[];
|
|
1530
1674
|
/**
|
|
1531
1675
|
* 설비(설비). mtbfMs/mttrMs 지정 시 확률적 고장 모델 참여(OEE Availability 손실). 미지정=고장 없음.
|
|
@@ -1586,7 +1730,7 @@ export interface TwinModelDef {
|
|
|
1586
1730
|
/**
|
|
1587
1731
|
* **시험 명세** — 표준 `TestSpecification`. 자원의 `testSpecificationIds` 가 가리키는 대상.
|
|
1588
1732
|
*
|
|
1589
|
-
* 선언하지 않아도 트윈은
|
|
1733
|
+
* 선언하지 않아도 트윈은 동작한다 — 그때 그 참조는 **가리킬 것이 없다**(화면이 끊어진 참조로 센다).
|
|
1590
1734
|
* 커널은 이 값으로 자격을 판정하지 않는다: 판정에는 **결과**가 필요하고 결과는 아직 모델에 없다.
|
|
1591
1735
|
* 없는 것으로 판정하면 자격자가 전부 사라져 라인이 영구히 굶는다.
|
|
1592
1736
|
*/
|
|
@@ -1594,13 +1738,13 @@ export interface TwinModelDef {
|
|
|
1594
1738
|
/**
|
|
1595
1739
|
* **등급 정의** — 표준 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass`.
|
|
1596
1740
|
*
|
|
1597
|
-
* 선언하지 않아도 트윈은
|
|
1741
|
+
* 선언하지 않아도 트윈은 동작한다(소속 문자열 그대로 판정). 선언하면 **상속과 유효기간**이 살아난다 —
|
|
1598
1742
|
* "생산직 2명" 요구를 "용접 자격자" 가 만족하고, 만료된 자격은 배정되지 않는다.
|
|
1599
1743
|
*/
|
|
1600
1744
|
/**
|
|
1601
1745
|
* **품목 정의**(표준 `MaterialDefinition`)와 **품목 등급**(`MaterialClass`).
|
|
1602
1746
|
*
|
|
1603
|
-
* 선언하지 않아도 트윈은
|
|
1747
|
+
* 선언하지 않아도 트윈은 동작한다 — 그때는 단위 환산을 할 수 없고, `quantityIn` 이 **없다고 답한다**
|
|
1604
1748
|
* (계수를 모르는데 값을 만들면 그 뒤 모든 계산이 거짓 위에 선다).
|
|
1605
1749
|
*/
|
|
1606
1750
|
materialDefinitions?: MaterialDefinition[];
|
|
@@ -1677,10 +1821,10 @@ export interface ProductionSpec {
|
|
|
1677
1821
|
recipeKey?: string;
|
|
1678
1822
|
}
|
|
1679
1823
|
/**
|
|
1680
|
-
* 공장을
|
|
1824
|
+
* 공장을 전환한 결과 — **몇 개가 늘고 몇 개가 사라졌나.**
|
|
1681
1825
|
*
|
|
1682
1826
|
* 돌려주지 않으면 조용히 사라진다. 자리 하나가 없어진 것을 사용자가 화면의 수를 세어 눈치채기를
|
|
1683
|
-
* 기대할 수는 없다(특히 자리가 수천인 현장에서). 그래서
|
|
1827
|
+
* 기대할 수는 없다(특히 자리가 수천인 현장에서). 그래서 구조 전환은 **말없이 성공하지 않는다.**
|
|
1684
1828
|
*/
|
|
1685
1829
|
export interface StructureShift {
|
|
1686
1830
|
locationsAdded: number;
|
|
@@ -1692,7 +1836,7 @@ export interface StructureShift {
|
|
|
1692
1836
|
export interface TwinKernel {
|
|
1693
1837
|
loadTwinModel(def: TwinModelDef): void;
|
|
1694
1838
|
/**
|
|
1695
|
-
* 돌면서 공장을
|
|
1839
|
+
* 돌면서 공장을 전환한다 — 관측 구동(미러)에서만. 시뮬레이션은 거절한다(멈추고 다시 세운다).
|
|
1696
1840
|
* 관측으로 알게 된 자리는 새 선언에 없어도 남고, 선언에서 사라진 자원은 버리되 수를 돌려준다.
|
|
1697
1841
|
*/
|
|
1698
1842
|
adoptStructure(def: TwinModelDef): StructureShift;
|