@operato/twin-kernel 0.7.8 → 0.7.9

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.
@@ -171,6 +171,20 @@ export declare function energyIntensity(input: IntensityInput): IntensityResult;
171
171
  export interface WindowedEnergy {
172
172
  /** 그 범위의 전력량 — 셀 수 있는 구간이 하나도 없으면 `undefined`(0 이 아니다). */
173
173
  kWh?: number;
174
+ /**
175
+ * 그 범위의 **최대수요** — 구간 최대부하 중 가장 큰 값. 요금이 걸리는 수다.
176
+ *
177
+ * ── 왜 여기서 내나 (2026-08-17) ──────────────────────────────────────────
178
+ * 「이 범위」를 정하는 규칙(걸친 구간은 세지 않는다)이 이미 여기 있다. 최대수요를 소비처에서 따로
179
+ * 세면 그 경계 규칙이 두 벌이 되고, 전력량과 최대수요가 **서로 다른 구간 집합**을 말하게 된다 —
180
+ * 같은 창을 두고 두 수가 어긋나면 어느 쪽도 못 믿는다.
181
+ *
182
+ * 평균부하(`meanKW`)로 대신하지 않는다. 15분 평균 400kW 인 구간의 최대가 900kW 일 수 있고,
183
+ * 계약을 넘겼는지는 **최대**가 답한다. 구간이 최대를 싣고 오지 않으면 이 값은 없다(짐작하지 않는다).
184
+ */
185
+ peakKW?: number;
186
+ /** 그 최대수요가 난 구간의 시작 — 「언제였나」를 물으면 답할 수 있어야 한다. */
187
+ peakStartMs?: number;
174
188
  /**
175
189
  * 어떻게 얻었나 — **파생임을 숨기지 않는다.**
176
190
  * · `mean-kw` — 구간 평균부하 × 구간 길이. 표본의 평균이므로 **추정**이다.
@@ -202,6 +216,7 @@ export declare function energyOfWindows(windows: readonly {
202
216
  startMs: number;
203
217
  endMs: number;
204
218
  meanKW?: number;
219
+ maxKW?: number;
205
220
  }[], range?: {
206
221
  startMs: number;
207
222
  endMs: number;
@@ -203,9 +203,20 @@ export function energyOfWindows(windows, range) {
203
203
  let skipped = 0;
204
204
  let first;
205
205
  let last;
206
+ let peakKW;
207
+ let peakStartMs;
206
208
  for (const w of windows ?? []) {
207
209
  if (!inRange(w))
208
210
  continue;
211
+ /*
212
+ * 최대수요는 전력량과 **따로 센다.** 평균을 못 읽어 전력량에서 빠진 구간도 최대는 실려 올 수
213
+ * 있고(그 반대도 있다), 하나가 없다고 다른 하나를 버리면 있는 사실을 잃는다.
214
+ */
215
+ const max = Number(w.maxKW);
216
+ if (Number.isFinite(max) && (peakKW === undefined || max > peakKW)) {
217
+ peakKW = max;
218
+ peakStartMs = Number(w.startMs);
219
+ }
209
220
  const mean = Number(w.meanKW);
210
221
  const hours = (Number(w.endMs) - Number(w.startMs)) / 3_600_000;
211
222
  if (!Number.isFinite(mean) || !Number.isFinite(hours) || hours <= 0) {
@@ -221,6 +232,7 @@ export function energyOfWindows(windows, range) {
221
232
  }
222
233
  return {
223
234
  ...(counted > 0 ? { kWh } : {}),
235
+ ...(peakKW !== undefined ? { peakKW, peakStartMs } : {}),
224
236
  basis: 'mean-kw',
225
237
  counted,
226
238
  skipped,
@@ -6339,8 +6339,15 @@ function energyOfWindows(windows, range) {
6339
6339
  let skipped = 0;
6340
6340
  let first;
6341
6341
  let last;
6342
+ let peakKW;
6343
+ let peakStartMs;
6342
6344
  for (const w of windows ?? []) {
6343
6345
  if (!inRange(w)) continue;
6346
+ const max = Number(w.maxKW);
6347
+ if (Number.isFinite(max) && (peakKW === void 0 || max > peakKW)) {
6348
+ peakKW = max;
6349
+ peakStartMs = Number(w.startMs);
6350
+ }
6344
6351
  const mean = Number(w.meanKW);
6345
6352
  const hours = (Number(w.endMs) - Number(w.startMs)) / 36e5;
6346
6353
  if (!Number.isFinite(mean) || !Number.isFinite(hours) || hours <= 0) {
@@ -6354,6 +6361,7 @@ function energyOfWindows(windows, range) {
6354
6361
  }
6355
6362
  return {
6356
6363
  ...counted > 0 ? { kWh } : {},
6364
+ ...peakKW !== void 0 ? { peakKW, peakStartMs } : {},
6357
6365
  basis: "mean-kw",
6358
6366
  counted,
6359
6367
  skipped,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/twin-kernel",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "type": "module",
5
5
  "description": "Twin Domain Kernel — framework-agnostic, zero-dep (domain + sim + 3-channel contract). WMS/YMS/MES, EPCIS 2.0 · ISA-95.",
6
6
  "publishConfig": {