@operato/twin-kernel 0.7.70 → 0.7.71

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.
@@ -2030,10 +2030,9 @@ export interface GenerationPeriodObservation {
2030
2030
  equipmentId: string;
2031
2031
  /** 선언된 시각 기준의 며칠째인가(§`localDayIndexAt`). */
2032
2032
  dayIndex: number;
2033
- /** 이 기간에서 처음 본 적산과 그 시각 — 평생 적산의 기준점이다. */
2034
- fromKWh: number;
2033
+ /** 이 기간에서 처음 관측한 시각 — 기간 시작과 다르면 그만큼 재지 않았다. */
2035
2034
  fromMs: number;
2036
- /** 이 기간에서 마지막으로 본 적산과 그 시각 — 하루 누적은 이것이 곧 총량이다. */
2035
+ /** 이 기간에서 마지막으로 본 적산과 그 시각 — 기간 누적은 이것이 곧 총량이다. */
2037
2036
  lastKWh: number;
2038
2037
  lastMs: number;
2039
2038
  }
@@ -430,19 +430,18 @@ export class EmsKernel extends FlowEngine {
430
430
  */
431
431
  closeGenerationPeriod(id, eq, at) {
432
432
  const obs = this.generationPeriodObs.get(id);
433
- const fresh = { dayIndex: at.dayIndex, fromKWh: at.kWh, fromMs: at.atMs, lastKWh: at.kWh, lastMs: at.atMs };
434
- /* 계기가 되돌아간 뒤의 기준점은 쓸 수 없다 — 차이가 음수가 되거나, 더 나쁘게 작아진 총량이 참인 척한다. */
433
+ const fresh = { dayIndex: at.dayIndex, fromMs: at.atMs, lastKWh: at.kWh, lastMs: at.atMs };
434
+ /* 계기가 되돌아간 뒤의 관측은 쓸 수 없다 — 되돌아간 시점 앞뒤가 한 기간에 섞인다. */
435
435
  if (at.faulted || !obs || at.kind === undefined || at.kind === 'unknown') {
436
436
  this.generationPeriodObs.set(id, fresh);
437
437
  return;
438
438
  }
439
439
  const declaredClose = at.periodAdvanced && !!at.priorSince && !!at.since;
440
- const dayClose = at.dayIndex > obs.dayIndex && (at.kind === 'daily' || at.kind === 'lifetime');
440
+ const dayClose = at.dayIndex > obs.dayIndex && at.kind === 'daily';
441
441
  let startMs;
442
442
  let endMs;
443
443
  let boundary;
444
- if (declaredClose && at.kind !== 'lifetime') {
445
- /* 평생 적산에 기준점이 나아가는 것은 뜻이 서지 않는다(기간이 하루가 아니다) — 자정 마감만 쓴다. */
444
+ if (declaredClose) {
446
445
  startMs = Date.parse(at.priorSince);
447
446
  endMs = Date.parse(at.since);
448
447
  boundary = 'declared';
@@ -454,10 +453,10 @@ export class EmsKernel extends FlowEngine {
454
453
  }
455
454
  if (startMs === undefined || endMs === undefined || boundary === undefined || !Number.isFinite(startMs) || !Number.isFinite(endMs)) {
456
455
  /*
457
- * 하루가 지났는데 마감할 근거가 없다 — 월·요금기간 종류인데 기준점이 오지 않는 경우다. 세어 둔다:
458
- * 세지 않으면 그 발전소의 발전량이 아무 표시 없이 영원히 나오지 않는다.
456
+ * 하루가 지났는데 마감할 근거가 없다 — 하루가 아닌 기간인데 원본이 기준점을 말하지 않는 경우다.
457
+ * 세어 둔다: 세지 않으면 그 발전소의 발전량이 아무 표시 없이 영원히 나오지 않는다.
459
458
  */
460
- if (at.dayIndex > obs.dayIndex && (at.kind === 'monthly' || at.kind === 'billing'))
459
+ if (at.dayIndex > obs.dayIndex)
461
460
  this.unclosedGenerationPeriods++;
462
461
  if (at.dayIndex !== obs.dayIndex)
463
462
  this.generationPeriodObs.set(id, fresh);
@@ -465,7 +464,19 @@ export class EmsKernel extends FlowEngine {
465
464
  this.generationPeriodObs.set(id, { ...obs, lastKWh: at.kWh, lastMs: at.atMs });
466
465
  return;
467
466
  }
468
- const total = at.kind === 'lifetime' ? obs.lastKWh - obs.fromKWh : obs.lastKWh;
467
+ /*
468
+ * ── 총량은 관측한 값 그대로다 — 뺄셈을 하지 않는다 (2026-08-28) ────────────
469
+ *
470
+ * 기간 누적은 그 기간에 쌓인 값이므로, 마지막으로 본 값이 곧 그 기간의 총량이다.
471
+ *
472
+ * 평생 누적을 뺄셈으로 하루 몫으로 바꾸는 갈래가 잠깐 있었는데 **지웠다.** 사용자 판단이다: 발전
473
+ * 원본이 주는 「평생 총량」은 진짜 평생치가 아니다(레지스터가 차면 되돌아가고, 설비를 교체하면
474
+ * 0부터 시작한다). 누적이 필요하면 기간 사실을 합해서 만드는 것이고, 원본에서 받을 값이 아니다.
475
+ *
476
+ * `'lifetime'` 은 **어휘에는 남는다** — 되돌아감을 기간 경계가 아니라 계기의 이상으로 판정하는
477
+ * 데 쓰인다(§위 `periodic`). 그 종류로 오는 값은 기간 사실을 만들지 않고 위에서 세어 둔다.
478
+ */
479
+ const total = obs.lastKWh;
469
480
  if (Number.isFinite(total) && total >= 0) {
470
481
  this.emitOp(ENERGY_EVENT.generationPeriod, {
471
482
  equipmentId: id,
@@ -1283,11 +1294,10 @@ export class EmsKernel extends FlowEngine {
1283
1294
  for (const o of e.generationPeriods ?? []) {
1284
1295
  if (!o?.equipmentId)
1285
1296
  continue;
1286
- if (![o.dayIndex, o.fromKWh, o.fromMs, o.lastKWh, o.lastMs].every(v => Number.isFinite(Number(v))))
1297
+ if (![o.dayIndex, o.fromMs, o.lastKWh, o.lastMs].every(v => Number.isFinite(Number(v))))
1287
1298
  continue;
1288
1299
  this.generationPeriodObs.set(o.equipmentId, {
1289
1300
  dayIndex: Number(o.dayIndex),
1290
- fromKWh: Number(o.fromKWh),
1291
1301
  fromMs: Number(o.fromMs),
1292
1302
  lastKWh: Number(o.lastKWh),
1293
1303
  lastMs: Number(o.lastMs)
@@ -7995,17 +7995,17 @@ var EmsKernel = class extends FlowEngine {
7995
7995
  */
7996
7996
  closeGenerationPeriod(id, eq, at) {
7997
7997
  const obs = this.generationPeriodObs.get(id);
7998
- const fresh = { dayIndex: at.dayIndex, fromKWh: at.kWh, fromMs: at.atMs, lastKWh: at.kWh, lastMs: at.atMs };
7998
+ const fresh = { dayIndex: at.dayIndex, fromMs: at.atMs, lastKWh: at.kWh, lastMs: at.atMs };
7999
7999
  if (at.faulted || !obs || at.kind === void 0 || at.kind === "unknown") {
8000
8000
  this.generationPeriodObs.set(id, fresh);
8001
8001
  return;
8002
8002
  }
8003
8003
  const declaredClose = at.periodAdvanced && !!at.priorSince && !!at.since;
8004
- const dayClose = at.dayIndex > obs.dayIndex && (at.kind === "daily" || at.kind === "lifetime");
8004
+ const dayClose = at.dayIndex > obs.dayIndex && at.kind === "daily";
8005
8005
  let startMs;
8006
8006
  let endMs;
8007
8007
  let boundary;
8008
- if (declaredClose && at.kind !== "lifetime") {
8008
+ if (declaredClose) {
8009
8009
  startMs = Date.parse(at.priorSince);
8010
8010
  endMs = Date.parse(at.since);
8011
8011
  boundary = "declared";
@@ -8015,12 +8015,12 @@ var EmsKernel = class extends FlowEngine {
8015
8015
  boundary = "offset";
8016
8016
  }
8017
8017
  if (startMs === void 0 || endMs === void 0 || boundary === void 0 || !Number.isFinite(startMs) || !Number.isFinite(endMs)) {
8018
- if (at.dayIndex > obs.dayIndex && (at.kind === "monthly" || at.kind === "billing")) this.unclosedGenerationPeriods++;
8018
+ if (at.dayIndex > obs.dayIndex) this.unclosedGenerationPeriods++;
8019
8019
  if (at.dayIndex !== obs.dayIndex) this.generationPeriodObs.set(id, fresh);
8020
8020
  else this.generationPeriodObs.set(id, { ...obs, lastKWh: at.kWh, lastMs: at.atMs });
8021
8021
  return;
8022
8022
  }
8023
- const total = at.kind === "lifetime" ? obs.lastKWh - obs.fromKWh : obs.lastKWh;
8023
+ const total = obs.lastKWh;
8024
8024
  if (Number.isFinite(total) && total >= 0) {
8025
8025
  this.emitOp(ENERGY_EVENT.generationPeriod, {
8026
8026
  equipmentId: id,
@@ -8601,10 +8601,9 @@ var EmsKernel = class extends FlowEngine {
8601
8601
  if (e.peakSince && Number.isFinite(e.peakSince.kW)) this.peak = { kW: e.peakSince.kW, windowStartMs: e.peakSince.windowStartMs };
8602
8602
  for (const o of e.generationPeriods ?? []) {
8603
8603
  if (!o?.equipmentId) continue;
8604
- if (![o.dayIndex, o.fromKWh, o.fromMs, o.lastKWh, o.lastMs].every((v) => Number.isFinite(Number(v)))) continue;
8604
+ if (![o.dayIndex, o.fromMs, o.lastKWh, o.lastMs].every((v) => Number.isFinite(Number(v)))) continue;
8605
8605
  this.generationPeriodObs.set(o.equipmentId, {
8606
8606
  dayIndex: Number(o.dayIndex),
8607
- fromKWh: Number(o.fromKWh),
8608
8607
  fromMs: Number(o.fromMs),
8609
8608
  lastKWh: Number(o.lastKWh),
8610
8609
  lastMs: Number(o.lastMs)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/twin-kernel",
3
- "version": "0.7.70",
3
+ "version": "0.7.71",
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": {