@operato/twin-kernel 0.7.73 → 0.7.75
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 +12 -1
- package/dist/capability.js +1 -1
- package/dist/contract.d.ts +44 -0
- package/dist/ems-kernel.d.ts +32 -0
- package/dist/ems-kernel.js +110 -0
- package/dist/energy-ingest.d.ts +5 -0
- package/dist/energy-ingest.js +49 -2
- package/dist-cjs/index.cjs +128 -3
- package/package.json +1 -1
package/dist/capability.d.ts
CHANGED
|
@@ -47,8 +47,19 @@ export interface CurtailableState {
|
|
|
47
47
|
curtailable: boolean;
|
|
48
48
|
minKW?: number;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* 전기 계측 — 전압(V)·전류(A). 전기를 만들거나 쓰는 어떤 설비에서나 같은 모양이다.
|
|
52
|
+
*
|
|
53
|
+
* 발전·저장·개폐 어느 능력에나 붙을 수 있어 따로 둔다. 상별 값은 배열이다(단상이면 원소 하나).
|
|
54
|
+
*/
|
|
55
|
+
export interface ElectricalMeasurementState {
|
|
56
|
+
dcVoltage?: number;
|
|
57
|
+
dcCurrent?: number;
|
|
58
|
+
acVoltage?: number[];
|
|
59
|
+
acCurrent?: number[];
|
|
60
|
+
}
|
|
50
61
|
/** 발전 — 만든다. 역송(`exportKW`)은 소비의 음수가 아니라 별개 사실이다. */
|
|
51
|
-
export interface GeneratingState {
|
|
62
|
+
export interface GeneratingState extends ElectricalMeasurementState {
|
|
52
63
|
generatedKW?: number;
|
|
53
64
|
exportKW?: number;
|
|
54
65
|
generatedKWh?: number;
|
package/dist/capability.js
CHANGED
|
@@ -86,7 +86,7 @@ export const CAPABILITIES = {
|
|
|
86
86
|
* 계량 능력(`metered`)이 `kW` 와 `kWh` 를 함께 선언한 것과 같은 짝이다. 적산은 발전 설비의 보편적
|
|
87
87
|
* 사실이고(태양광·열병합·디젤), 그것이 없으면 성능비·발전시간 같은 성과를 아무도 셀 수 없다.
|
|
88
88
|
*/
|
|
89
|
-
stateFields: ['generatedKW', 'exportKW', 'generatedKWh', 'generatedKWhResetAt', 'generatedKWhSince', 'generatedKWhBasis', 'generatedKWhAccumulation', 'generatedKWhLastPeriod', 'generatedKWhLastPeriodEnd', 'generatedKWhLastPeriodObservedFrom', 'generatedKWhLastPeriodObservedTo'], models: ['GeneratingState'], results: ['generated']
|
|
89
|
+
stateFields: ['generatedKW', 'exportKW', 'generatedKWh', 'generatedKWhResetAt', 'generatedKWhSince', 'generatedKWhBasis', 'generatedKWhAccumulation', 'generatedKWhLastPeriod', 'generatedKWhLastPeriodEnd', 'generatedKWhLastPeriodObservedFrom', 'generatedKWhLastPeriodObservedTo', 'dcVoltage', 'dcCurrent', 'acVoltage', 'acCurrent'], models: ['GeneratingState'], results: ['generated']
|
|
90
90
|
},
|
|
91
91
|
/*
|
|
92
92
|
* ── 왜 `storing` 이 아니라 `energyStoring` 인가 (2026-08-15) ────────────────
|
package/dist/contract.d.ts
CHANGED
|
@@ -1444,6 +1444,14 @@ export interface EquipmentState extends EffectivePeriod {
|
|
|
1444
1444
|
*/
|
|
1445
1445
|
generatedKWhLastPeriodObservedFrom?: ISOTime;
|
|
1446
1446
|
generatedKWhLastPeriodObservedTo?: ISOTime;
|
|
1447
|
+
/**
|
|
1448
|
+
* 전기 계측 — 전압(V)·전류(A). 출력이 0 일 때 **왜 0 인지**를 이 값들이 말한다
|
|
1449
|
+
* (§`EnergyEquipmentData.dcVoltage`).
|
|
1450
|
+
*/
|
|
1451
|
+
dcVoltage?: number;
|
|
1452
|
+
dcCurrent?: number;
|
|
1453
|
+
acVoltage?: number[];
|
|
1454
|
+
acCurrent?: number[];
|
|
1447
1455
|
/** storing — 충전율(%)·충전·방전(kW). 충전과 방전을 나눈다(손실·수명 판단이 그 둘을 구별한다). */
|
|
1448
1456
|
soc?: number;
|
|
1449
1457
|
chargeKW?: number;
|
|
@@ -2027,6 +2035,18 @@ export interface EnergyState {
|
|
|
2027
2035
|
* 틀렸다는 뜻이고, 사람이 찾아내지 않아도 드러나야 한다.
|
|
2028
2036
|
*/
|
|
2029
2037
|
lastBill?: EnergyBillData;
|
|
2038
|
+
/**
|
|
2039
|
+
* 이 청구 주기의 **최고 수요** — 기본요금이 이 값으로 다시 정해진다.
|
|
2040
|
+
*
|
|
2041
|
+
* `periodStart` 는 마지막 청구서의 끝이다(그 뒤가 아직 청구되지 않은 구간). 그것이 바뀌면 처음부터
|
|
2042
|
+
* 다시 센다 — 지난 주기의 최고가 이번 주기를 정하지 않는다.
|
|
2043
|
+
*/
|
|
2044
|
+
demandHigh?: {
|
|
2045
|
+
kW: number;
|
|
2046
|
+
atTo: ISOTime;
|
|
2047
|
+
periodStart?: ISOTime;
|
|
2048
|
+
since: ISOTime;
|
|
2049
|
+
};
|
|
2030
2050
|
/**
|
|
2031
2051
|
* 설비마다 **지금 기간의 발전 관측** — 기간 발전량을 확정할 때 쓰는 기준점.
|
|
2032
2052
|
*
|
|
@@ -2520,6 +2540,30 @@ export interface EnergyEquipmentData {
|
|
|
2520
2540
|
curtailable?: boolean;
|
|
2521
2541
|
minKW?: number;
|
|
2522
2542
|
position?: 'open' | 'closed' | 'intermediate' | 'bad';
|
|
2543
|
+
/**
|
|
2544
|
+
* **전기 계측** — 전압(V)·전류(A). 전기를 만들거나 쓰는 어떤 설비에서나 같은 모양이다
|
|
2545
|
+
* (IEC 61850 의 계측 항목).
|
|
2546
|
+
*
|
|
2547
|
+
* ── 왜 kW 만으로 부족한가 (2026-08-29) ─────────────────────────────────────
|
|
2548
|
+
* 출력이 0 일 때 **왜 0 인지**를 kW 는 말하지 못한다. 전압과 전류가 있으면 값이 답한다.
|
|
2549
|
+
*
|
|
2550
|
+
* 전압 있음 · 전류 0 회로가 끊겼다 (실측: 직류 621.9V · 0A)
|
|
2551
|
+
* 전압 0 · 전류 0 원천이 없다 (밤·차단)
|
|
2552
|
+
* 전압 낮음 · 전류 높음 부하가 무겁거나 계통이 약하다
|
|
2553
|
+
*
|
|
2554
|
+
* ── 직류와 교류를 가른다 ────────────────────────────────────────────────────
|
|
2555
|
+
* 변환기(인버터·정류기)를 낀 설비는 양쪽이 다른 값이고, 어느 쪽이 죽었는지가 곧 어디가 고장인지다.
|
|
2556
|
+
* 한 이름에 담으면 그 구별이 사라진다.
|
|
2557
|
+
*
|
|
2558
|
+
* ── 상별로 받는다 ──────────────────────────────────────────────────────────
|
|
2559
|
+
* 상 불평형은 그 자체로 사실이고, 합치면 되돌릴 수 없다. 단상이면 원소 하나짜리 배열이다.
|
|
2560
|
+
*
|
|
2561
|
+
* **역률은 이 자리에 없다.** 뜻을 확인하지 못한 값을 받으면 화면이 그것을 역률이라고 말한다.
|
|
2562
|
+
*/
|
|
2563
|
+
dcVoltage?: number;
|
|
2564
|
+
dcCurrent?: number;
|
|
2565
|
+
acVoltage?: number[];
|
|
2566
|
+
acCurrent?: number[];
|
|
2523
2567
|
}
|
|
2524
2568
|
/**
|
|
2525
2569
|
* 발전 적산으로 담기는 값 — **발전 설비 하나가 지금까지 만든 양.**
|
package/dist/ems-kernel.d.ts
CHANGED
|
@@ -199,6 +199,13 @@ export declare class EmsKernel extends FlowEngine {
|
|
|
199
199
|
private unclosedGenerationPeriods;
|
|
200
200
|
/** 마지막으로 받은 청구서 — 공급자가 확정한 금액. 우리 계산과 견주는 기준이다. */
|
|
201
201
|
private lastBill?;
|
|
202
|
+
/**
|
|
203
|
+
* 이 청구 주기의 **최고 수요** — 기본요금이 이 값으로 다시 정해진다.
|
|
204
|
+
*
|
|
205
|
+
* `since` 는 이 주기에서 처음 잰 구간의 끝이다. `periodStart` 는 마지막 청구서의 끝이고,
|
|
206
|
+
* 그것이 바뀌면 처음부터 다시 센다.
|
|
207
|
+
*/
|
|
208
|
+
private demandHigh?;
|
|
202
209
|
private applyEquipmentEnergy;
|
|
203
210
|
/** 우리 모델이 모르는 설비가 상태를 보내 온 횟수 — 조용히 버리지 않는다. */
|
|
204
211
|
private unknownEquipmentReports;
|
|
@@ -242,6 +249,31 @@ export declare class EmsKernel extends FlowEngine {
|
|
|
242
249
|
* 어려우므로 높게 부르지 않는다(경보가 소음이 되면 사람이 경보 자체를 읽지 않는다).
|
|
243
250
|
*/
|
|
244
251
|
protected collectAttentions(): Attention[];
|
|
252
|
+
/**
|
|
253
|
+
* **이 주기의 최고가 요금적용전력을 넘었다** — 다음 주기의 기본요금이 오른다.
|
|
254
|
+
*
|
|
255
|
+
* ── 왜 계약 초과와 다른 신호인가 (2026-08-29) ────────────────────────────
|
|
256
|
+
* 계약 초과는 약정 위반이고 지금 조치할 일이다(줄이면 된다). 이쪽은 **이미 일어난 일**이다 —
|
|
257
|
+
* 넘은 순간 다음 주기의 기준이 정해지고, 지금 줄여도 되돌아가지 않는다. 사람이 할 일이 다르므로
|
|
258
|
+
* 신호도 다르다.
|
|
259
|
+
*
|
|
260
|
+
* ── 한 번만 울린다 ──────────────────────────────────────────────────────────
|
|
261
|
+
* 넘으면 그 주기 내내 참이다. 구간마다 다시 울리면 소음이 되고, 소음이 되면 아무도 안 본다.
|
|
262
|
+
* 그래서 신호의 id 를 주기로 묶고 「처음 넘은 시각」이 아니라 **그 주기의 최고**를 싣는다.
|
|
263
|
+
*
|
|
264
|
+
* ── 늦게 안다 ───────────────────────────────────────────────────────────────
|
|
265
|
+
* 최고 수요는 지난 구간이 마감되어야 온다(지금 원본은 시간마다). 그래서 넘은 것을 최대 한 시간
|
|
266
|
+
* 뒤에 안다. 그 사실을 `observedTo` 로 함께 내어 화면이 「언제까지의 값인가」를 말할 수 있게 한다.
|
|
267
|
+
*/
|
|
268
|
+
protected billingDemandAttention(): Attention | undefined;
|
|
269
|
+
/**
|
|
270
|
+
* 선언된 **요금적용전력** — 기본요금이 걸리는 기준(§`EMS_PROPERTY.billingDemandKW`).
|
|
271
|
+
*
|
|
272
|
+
* 청구서가 그 값을 실어 오면 그쪽이 먼저다(§`billingDemandKW()`). 이것은 청구가 오지 않는 현장의 몫이다.
|
|
273
|
+
*/
|
|
274
|
+
private declaredBillingDemandKW;
|
|
275
|
+
/** 지금 쓰는 요금적용전력 — **청구서가 먼저다.** 매달 바뀌는 값이라 선언은 곧 낡는다. */
|
|
276
|
+
private billingDemandKW;
|
|
245
277
|
/** 계약을 선언한 수전 자리 — 주의가 가리킬 대상. 없으면 대상을 지어내지 않는다. */
|
|
246
278
|
private incomingLocationId;
|
|
247
279
|
/** 감축 가능으로 **선언된** 설비 — 커널이 능력을 짐작하지 않는다(타입이 선언한다). */
|
package/dist/ems-kernel.js
CHANGED
|
@@ -315,6 +315,26 @@ export class EmsKernel extends FlowEngine {
|
|
|
315
315
|
point.lastPeriodTo = d.to;
|
|
316
316
|
if (d?.basis)
|
|
317
317
|
point.lastPeriodBasis = d.basis;
|
|
318
|
+
/*
|
|
319
|
+
* ── 이 청구 주기의 최고 수요 (2026-08-29) ─────────────────────────────────
|
|
320
|
+
*
|
|
321
|
+
* 기본요금은 이 값으로 다시 정해진다. 그래서 **넘은 순간과 얼마나 넘었는지**를 기억한다.
|
|
322
|
+
*
|
|
323
|
+
* 주기의 시작은 마지막 청구서의 끝이다(그 뒤가 아직 청구되지 않은 구간이다). 청구서가 없으면
|
|
324
|
+
* 관측을 시작한 때부터다 — 그 사실은 `since` 가 말한다.
|
|
325
|
+
*
|
|
326
|
+
* 구간마다 새로 세지 않는다: 한 번 넘으면 그 주기 내내 참이고, 매 구간 다시 알리면 소음이 된다.
|
|
327
|
+
*/
|
|
328
|
+
const maxKW = Number(d?.maxKW);
|
|
329
|
+
if (Number.isFinite(maxKW) && maxKW >= 0 && d?.to) {
|
|
330
|
+
const periodStart = this.lastBill?.to;
|
|
331
|
+
/* 청구 주기가 바뀌었으면 처음부터 다시 센다 — 지난 주기의 최고가 이번 주기를 정하지 않는다. */
|
|
332
|
+
if (this.demandHigh && periodStart && this.demandHigh.periodStart !== periodStart)
|
|
333
|
+
this.demandHigh = undefined;
|
|
334
|
+
if (!this.demandHigh || maxKW > this.demandHigh.kW) {
|
|
335
|
+
this.demandHigh = { kW: maxKW, atTo: String(d.to), periodStart, since: this.demandHigh?.since ?? String(d.to) };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
318
338
|
this.revision++;
|
|
319
339
|
}
|
|
320
340
|
/**
|
|
@@ -582,6 +602,13 @@ export class EmsKernel extends FlowEngine {
|
|
|
582
602
|
unclosedGenerationPeriods = 0;
|
|
583
603
|
/** 마지막으로 받은 청구서 — 공급자가 확정한 금액. 우리 계산과 견주는 기준이다. */
|
|
584
604
|
lastBill;
|
|
605
|
+
/**
|
|
606
|
+
* 이 청구 주기의 **최고 수요** — 기본요금이 이 값으로 다시 정해진다.
|
|
607
|
+
*
|
|
608
|
+
* `since` 는 이 주기에서 처음 잰 구간의 끝이다. `periodStart` 는 마지막 청구서의 끝이고,
|
|
609
|
+
* 그것이 바뀌면 처음부터 다시 센다.
|
|
610
|
+
*/
|
|
611
|
+
demandHigh;
|
|
585
612
|
applyEquipmentEnergy(envelope) {
|
|
586
613
|
const d = envelope.data;
|
|
587
614
|
const id = String(d?.equipmentId ?? '').trim();
|
|
@@ -603,6 +630,11 @@ export class EmsKernel extends FlowEngine {
|
|
|
603
630
|
put('curtailable');
|
|
604
631
|
put('minKW');
|
|
605
632
|
put('position');
|
|
633
|
+
/* 전기 계측 — 「출력이 0인데 왜 0인가」에 값으로 답하는 축(§`EnergyEquipmentData.dcVoltage`). */
|
|
634
|
+
put('dcVoltage');
|
|
635
|
+
put('dcCurrent');
|
|
636
|
+
put('acVoltage');
|
|
637
|
+
put('acCurrent');
|
|
606
638
|
/* **값과 함께 시각을 남긴다** — 없으면 화면이 멈춘 값을 지금 값으로 그린다(계량 지점과 같은 규율). */
|
|
607
639
|
if (d?.at)
|
|
608
640
|
eq.measuredAt = d.at;
|
|
@@ -940,6 +972,9 @@ export class EmsKernel extends FlowEngine {
|
|
|
940
972
|
*/
|
|
941
973
|
collectAttentions() {
|
|
942
974
|
const out = super.collectAttentions();
|
|
975
|
+
const billingOver = this.billingDemandAttention();
|
|
976
|
+
if (billingOver)
|
|
977
|
+
out.push(billingOver);
|
|
943
978
|
const w = this.open;
|
|
944
979
|
if (!w || w.contractKW === undefined || w.meanKW === undefined)
|
|
945
980
|
return out;
|
|
@@ -983,6 +1018,77 @@ export class EmsKernel extends FlowEngine {
|
|
|
983
1018
|
});
|
|
984
1019
|
return out;
|
|
985
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* **이 주기의 최고가 요금적용전력을 넘었다** — 다음 주기의 기본요금이 오른다.
|
|
1023
|
+
*
|
|
1024
|
+
* ── 왜 계약 초과와 다른 신호인가 (2026-08-29) ────────────────────────────
|
|
1025
|
+
* 계약 초과는 약정 위반이고 지금 조치할 일이다(줄이면 된다). 이쪽은 **이미 일어난 일**이다 —
|
|
1026
|
+
* 넘은 순간 다음 주기의 기준이 정해지고, 지금 줄여도 되돌아가지 않는다. 사람이 할 일이 다르므로
|
|
1027
|
+
* 신호도 다르다.
|
|
1028
|
+
*
|
|
1029
|
+
* ── 한 번만 울린다 ──────────────────────────────────────────────────────────
|
|
1030
|
+
* 넘으면 그 주기 내내 참이다. 구간마다 다시 울리면 소음이 되고, 소음이 되면 아무도 안 본다.
|
|
1031
|
+
* 그래서 신호의 id 를 주기로 묶고 「처음 넘은 시각」이 아니라 **그 주기의 최고**를 싣는다.
|
|
1032
|
+
*
|
|
1033
|
+
* ── 늦게 안다 ───────────────────────────────────────────────────────────────
|
|
1034
|
+
* 최고 수요는 지난 구간이 마감되어야 온다(지금 원본은 시간마다). 그래서 넘은 것을 최대 한 시간
|
|
1035
|
+
* 뒤에 안다. 그 사실을 `observedTo` 로 함께 내어 화면이 「언제까지의 값인가」를 말할 수 있게 한다.
|
|
1036
|
+
*/
|
|
1037
|
+
billingDemandAttention() {
|
|
1038
|
+
const high = this.demandHigh;
|
|
1039
|
+
const basis = this.billingDemandKW();
|
|
1040
|
+
if (!high || basis === undefined || high.kW <= basis)
|
|
1041
|
+
return undefined;
|
|
1042
|
+
const over = high.kW - basis;
|
|
1043
|
+
const ratio = over / basis;
|
|
1044
|
+
return {
|
|
1045
|
+
id: `billing-demand-exceeded:${high.periodStart ?? high.since}`,
|
|
1046
|
+
kind: 'billing-demand-exceeded',
|
|
1047
|
+
severity: ratio > 0.15 ? 'high' : 'medium',
|
|
1048
|
+
anchor: { locationId: this.incomingLocationId() },
|
|
1049
|
+
params: {
|
|
1050
|
+
peakKW: Math.round(high.kW),
|
|
1051
|
+
billingDemandKW: Math.round(basis),
|
|
1052
|
+
overKW: Math.round(over),
|
|
1053
|
+
/* 값의 출처 — 청구서가 말한 기준인지 현장의 선언인지. 선언은 곧 낡는다. */
|
|
1054
|
+
basisFrom: Number.isFinite(Number(this.lastBill?.billingDemandKW)) ? 'bill' : 'declaration',
|
|
1055
|
+
observedTo: high.atTo,
|
|
1056
|
+
periodSince: high.since
|
|
1057
|
+
},
|
|
1058
|
+
/*
|
|
1059
|
+
* 조치를 걸지 않는다. **이미 일어난 일이라 되돌릴 수 없다** — 지금 줄여도 이 주기의 최고는
|
|
1060
|
+
* 그대로다. 조치를 붙이면 화면이 「누르면 해결된다」고 거짓말한다.
|
|
1061
|
+
*/
|
|
1062
|
+
recommendedActions: []
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* 선언된 **요금적용전력** — 기본요금이 걸리는 기준(§`EMS_PROPERTY.billingDemandKW`).
|
|
1067
|
+
*
|
|
1068
|
+
* 청구서가 그 값을 실어 오면 그쪽이 먼저다(§`billingDemandKW()`). 이것은 청구가 오지 않는 현장의 몫이다.
|
|
1069
|
+
*/
|
|
1070
|
+
declaredBillingDemandKW() {
|
|
1071
|
+
let max;
|
|
1072
|
+
for (const loc of this.boardDef?.locations ?? []) {
|
|
1073
|
+
for (const p of loc.properties ?? []) {
|
|
1074
|
+
if (p.id !== EMS_PROPERTY.billingDemandKW)
|
|
1075
|
+
continue;
|
|
1076
|
+
const v = Number(p.value);
|
|
1077
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
1078
|
+
continue;
|
|
1079
|
+
if (max === undefined || v > max)
|
|
1080
|
+
max = v;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return max;
|
|
1084
|
+
}
|
|
1085
|
+
/** 지금 쓰는 요금적용전력 — **청구서가 먼저다.** 매달 바뀌는 값이라 선언은 곧 낡는다. */
|
|
1086
|
+
billingDemandKW() {
|
|
1087
|
+
const billed = Number(this.lastBill?.billingDemandKW);
|
|
1088
|
+
if (Number.isFinite(billed) && billed > 0)
|
|
1089
|
+
return billed;
|
|
1090
|
+
return this.declaredBillingDemandKW();
|
|
1091
|
+
}
|
|
986
1092
|
/** 계약을 선언한 수전 자리 — 주의가 가리킬 대상. 없으면 대상을 지어내지 않는다. */
|
|
987
1093
|
incomingLocationId() {
|
|
988
1094
|
for (const loc of this.boardDef?.locations ?? []) {
|
|
@@ -1376,6 +1482,9 @@ export class EmsKernel extends FlowEngine {
|
|
|
1376
1482
|
/* 청구서도 이어받는다 — 재기동마다 사라지면 우리 계산과 견줄 기준이 없어진다. */
|
|
1377
1483
|
if (e.lastBill?.from && e.lastBill?.to)
|
|
1378
1484
|
this.lastBill = { ...e.lastBill };
|
|
1485
|
+
/* 이 주기의 최고도 이어받는다 — 재기동마다 0 으로 돌아가면 이미 넘은 것을 못 넘었다고 말한다. */
|
|
1486
|
+
if (Number.isFinite(Number(e.demandHigh?.kW)) && e.demandHigh?.since)
|
|
1487
|
+
this.demandHigh = { ...e.demandHigh };
|
|
1379
1488
|
}
|
|
1380
1489
|
getSnapshot() {
|
|
1381
1490
|
const snap = super.getSnapshot();
|
|
@@ -1401,6 +1510,7 @@ export class EmsKernel extends FlowEngine {
|
|
|
1401
1510
|
: {}),
|
|
1402
1511
|
...(this.unclosedGenerationPeriods ? { unclosedGenerationPeriods: this.unclosedGenerationPeriods } : {}),
|
|
1403
1512
|
...(this.lastBill ? { lastBill: { ...this.lastBill } } : {}),
|
|
1513
|
+
...(this.demandHigh ? { demandHigh: { ...this.demandHigh } } : {}),
|
|
1404
1514
|
...(contractKW !== undefined ? { contractKW } : {}),
|
|
1405
1515
|
/* 물류 흐름 요청을 받은 적이 있나 — 있으면 이 트윈에 엉뚱한 명령이 오고 있다는 사실이다. */
|
|
1406
1516
|
...(this.flowRequests.size
|
package/dist/energy-ingest.d.ts
CHANGED
|
@@ -49,6 +49,11 @@ export interface EnergyEquipmentRecord {
|
|
|
49
49
|
curtailable?: boolean;
|
|
50
50
|
minKW?: number;
|
|
51
51
|
position?: string;
|
|
52
|
+
/** 전기 계측 — 전압(V)·전류(A). 상별 값은 배열로(단상이면 원소 하나). */
|
|
53
|
+
dcVoltage?: number;
|
|
54
|
+
dcCurrent?: number;
|
|
55
|
+
acVoltage?: number[];
|
|
56
|
+
acCurrent?: number[];
|
|
52
57
|
}
|
|
53
58
|
/** 이 레코드가 설비 에너지 상태인가 — 라우팅 판정을 한 곳에 둔다. */
|
|
54
59
|
export declare function isEnergyEquipmentRecord(record: unknown): boolean;
|
package/dist/energy-ingest.js
CHANGED
|
@@ -180,6 +180,49 @@ export function ingestEnergyEquipmentRecords(records, opts) {
|
|
|
180
180
|
else
|
|
181
181
|
curtailable = r.curtailable;
|
|
182
182
|
}
|
|
183
|
+
/*
|
|
184
|
+
* ── 전기 계측 (2026-08-29) ─────────────────────────────────────────────────
|
|
185
|
+
*
|
|
186
|
+
* 음수는 받지 않는다. 전압·전류의 크기는 음수가 되지 않고, 방향은 다른 축이 말한다
|
|
187
|
+
* (`chargeKW`·`dischargeKW`·`exportKW`). 음수를 받아 두면 화면이 「-380V」를 그린다.
|
|
188
|
+
*
|
|
189
|
+
* 상별 값은 **배열 그대로** 받는다. 합치거나 평균 내지 않는다 — 상 불평형은 그 자체로 사실이고,
|
|
190
|
+
* 한 수로 접으면 되돌릴 수 없다.
|
|
191
|
+
*/
|
|
192
|
+
const dcVoltage = num(r?.dcVoltage, 'dcVoltage', 0);
|
|
193
|
+
const dcCurrent = num(r?.dcCurrent, 'dcCurrent', 0);
|
|
194
|
+
const phases = (raw, name) => {
|
|
195
|
+
if (raw === undefined || raw === null)
|
|
196
|
+
return undefined;
|
|
197
|
+
if (!Array.isArray(raw)) {
|
|
198
|
+
errors.push(`${name} 가 배열이 아니다 — 상별 값이라 배열로 받는다(단상이면 원소 하나): ${JSON.stringify(raw)}`);
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
if (!raw.length) {
|
|
202
|
+
errors.push(`${name} 가 빈 배열이다 — 잰 상이 없으면 그 칸을 보내지 않는다`);
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const out = [];
|
|
206
|
+
for (const [i, v] of raw.entries()) {
|
|
207
|
+
const n = Number(v);
|
|
208
|
+
if (!Number.isFinite(n)) {
|
|
209
|
+
errors.push(`${name}[${i}] 를 수로 읽을 수 없다: ${JSON.stringify(v)}`);
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (n < 0) {
|
|
213
|
+
errors.push(`${name}[${i}] 가 음수다(${n}) — 크기는 음수가 되지 않는다(방향은 다른 축이 말한다)`);
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
out.push(n);
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
};
|
|
220
|
+
const acVoltage = phases(r?.acVoltage, 'acVoltage');
|
|
221
|
+
const acCurrent = phases(r?.acCurrent, 'acCurrent');
|
|
222
|
+
/* 상 수가 다르면 짝이 맞지 않는다 — 어느 상의 전류인지 알 수 없는 값을 받지 않는다. */
|
|
223
|
+
if (acVoltage && acCurrent && acVoltage.length !== acCurrent.length) {
|
|
224
|
+
errors.push(`acVoltage(${acVoltage.length}상)와 acCurrent(${acCurrent.length}상)의 상 수가 다르다`);
|
|
225
|
+
}
|
|
183
226
|
let position;
|
|
184
227
|
if (r?.position !== undefined && r?.position !== null && r?.position !== '') {
|
|
185
228
|
const p = String(r.position);
|
|
@@ -188,7 +231,7 @@ export function ingestEnergyEquipmentRecords(records, opts) {
|
|
|
188
231
|
else
|
|
189
232
|
position = p;
|
|
190
233
|
}
|
|
191
|
-
const values = { generatedKW, exportKW, soc, chargeKW, dischargeKW, curtailable, minKW, position };
|
|
234
|
+
const values = { generatedKW, exportKW, soc, chargeKW, dischargeKW, curtailable, minKW, position, dcVoltage, dcCurrent, acVoltage, acCurrent };
|
|
192
235
|
if (!errors.length && Object.values(values).every(v => v === undefined)) {
|
|
193
236
|
errors.push('바꿀 상태가 하나도 없다 — 값 없는 보고는 사실로 적을 것이 없다');
|
|
194
237
|
}
|
|
@@ -207,7 +250,11 @@ export function ingestEnergyEquipmentRecords(records, opts) {
|
|
|
207
250
|
...(dischargeKW !== undefined ? { dischargeKW } : {}),
|
|
208
251
|
...(curtailable !== undefined ? { curtailable } : {}),
|
|
209
252
|
...(minKW !== undefined ? { minKW } : {}),
|
|
210
|
-
...(position !== undefined ? { position } : {})
|
|
253
|
+
...(position !== undefined ? { position } : {}),
|
|
254
|
+
...(dcVoltage !== undefined ? { dcVoltage } : {}),
|
|
255
|
+
...(dcCurrent !== undefined ? { dcCurrent } : {}),
|
|
256
|
+
...(acVoltage !== undefined ? { acVoltage } : {}),
|
|
257
|
+
...(acCurrent !== undefined ? { acCurrent } : {})
|
|
211
258
|
};
|
|
212
259
|
accepted.push({
|
|
213
260
|
eventId: `${opts.tenantId}-energy-eq-${++seq}`,
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -2757,7 +2757,7 @@ var CAPABILITIES = {
|
|
|
2757
2757
|
* 계량 능력(`metered`)이 `kW` 와 `kWh` 를 함께 선언한 것과 같은 짝이다. 적산은 발전 설비의 보편적
|
|
2758
2758
|
* 사실이고(태양광·열병합·디젤), 그것이 없으면 성능비·발전시간 같은 성과를 아무도 셀 수 없다.
|
|
2759
2759
|
*/
|
|
2760
|
-
stateFields: ["generatedKW", "exportKW", "generatedKWh", "generatedKWhResetAt", "generatedKWhSince", "generatedKWhBasis", "generatedKWhAccumulation", "generatedKWhLastPeriod", "generatedKWhLastPeriodEnd", "generatedKWhLastPeriodObservedFrom", "generatedKWhLastPeriodObservedTo"],
|
|
2760
|
+
stateFields: ["generatedKW", "exportKW", "generatedKWh", "generatedKWhResetAt", "generatedKWhSince", "generatedKWhBasis", "generatedKWhAccumulation", "generatedKWhLastPeriod", "generatedKWhLastPeriodEnd", "generatedKWhLastPeriodObservedFrom", "generatedKWhLastPeriodObservedTo", "dcVoltage", "dcCurrent", "acVoltage", "acCurrent"],
|
|
2761
2761
|
models: ["GeneratingState"],
|
|
2762
2762
|
results: ["generated"]
|
|
2763
2763
|
},
|
|
@@ -7980,6 +7980,14 @@ var EmsKernel = class extends FlowEngine {
|
|
|
7980
7980
|
if (d?.from) point.lastPeriodFrom = d.from;
|
|
7981
7981
|
if (d?.to) point.lastPeriodTo = d.to;
|
|
7982
7982
|
if (d?.basis) point.lastPeriodBasis = d.basis;
|
|
7983
|
+
const maxKW = Number(d?.maxKW);
|
|
7984
|
+
if (Number.isFinite(maxKW) && maxKW >= 0 && d?.to) {
|
|
7985
|
+
const periodStart = this.lastBill?.to;
|
|
7986
|
+
if (this.demandHigh && periodStart && this.demandHigh.periodStart !== periodStart) this.demandHigh = void 0;
|
|
7987
|
+
if (!this.demandHigh || maxKW > this.demandHigh.kW) {
|
|
7988
|
+
this.demandHigh = { kW: maxKW, atTo: String(d.to), periodStart, since: this.demandHigh?.since ?? String(d.to) };
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7983
7991
|
this.revision++;
|
|
7984
7992
|
}
|
|
7985
7993
|
/**
|
|
@@ -8158,6 +8166,13 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8158
8166
|
unclosedGenerationPeriods = 0;
|
|
8159
8167
|
/** 마지막으로 받은 청구서 — 공급자가 확정한 금액. 우리 계산과 견주는 기준이다. */
|
|
8160
8168
|
lastBill;
|
|
8169
|
+
/**
|
|
8170
|
+
* 이 청구 주기의 **최고 수요** — 기본요금이 이 값으로 다시 정해진다.
|
|
8171
|
+
*
|
|
8172
|
+
* `since` 는 이 주기에서 처음 잰 구간의 끝이다. `periodStart` 는 마지막 청구서의 끝이고,
|
|
8173
|
+
* 그것이 바뀌면 처음부터 다시 센다.
|
|
8174
|
+
*/
|
|
8175
|
+
demandHigh;
|
|
8161
8176
|
applyEquipmentEnergy(envelope) {
|
|
8162
8177
|
const d = envelope.data;
|
|
8163
8178
|
const id = String(d?.equipmentId ?? "").trim();
|
|
@@ -8178,6 +8193,10 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8178
8193
|
put("curtailable");
|
|
8179
8194
|
put("minKW");
|
|
8180
8195
|
put("position");
|
|
8196
|
+
put("dcVoltage");
|
|
8197
|
+
put("dcCurrent");
|
|
8198
|
+
put("acVoltage");
|
|
8199
|
+
put("acCurrent");
|
|
8181
8200
|
if (d?.at) eq.measuredAt = d.at;
|
|
8182
8201
|
const heardAt = Date.parse(String(d?.at ?? envelope.eventTime ?? ""));
|
|
8183
8202
|
if (Number.isFinite(heardAt)) this.noteObserved(heardAt);
|
|
@@ -8393,6 +8412,8 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8393
8412
|
*/
|
|
8394
8413
|
collectAttentions() {
|
|
8395
8414
|
const out = super.collectAttentions();
|
|
8415
|
+
const billingOver = this.billingDemandAttention();
|
|
8416
|
+
if (billingOver) out.push(billingOver);
|
|
8396
8417
|
const w = this.open;
|
|
8397
8418
|
if (!w || w.contractKW === void 0 || w.meanKW === void 0) return out;
|
|
8398
8419
|
const projected = w.meanKW;
|
|
@@ -8431,6 +8452,72 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8431
8452
|
});
|
|
8432
8453
|
return out;
|
|
8433
8454
|
}
|
|
8455
|
+
/**
|
|
8456
|
+
* **이 주기의 최고가 요금적용전력을 넘었다** — 다음 주기의 기본요금이 오른다.
|
|
8457
|
+
*
|
|
8458
|
+
* ── 왜 계약 초과와 다른 신호인가 (2026-08-29) ────────────────────────────
|
|
8459
|
+
* 계약 초과는 약정 위반이고 지금 조치할 일이다(줄이면 된다). 이쪽은 **이미 일어난 일**이다 —
|
|
8460
|
+
* 넘은 순간 다음 주기의 기준이 정해지고, 지금 줄여도 되돌아가지 않는다. 사람이 할 일이 다르므로
|
|
8461
|
+
* 신호도 다르다.
|
|
8462
|
+
*
|
|
8463
|
+
* ── 한 번만 울린다 ──────────────────────────────────────────────────────────
|
|
8464
|
+
* 넘으면 그 주기 내내 참이다. 구간마다 다시 울리면 소음이 되고, 소음이 되면 아무도 안 본다.
|
|
8465
|
+
* 그래서 신호의 id 를 주기로 묶고 「처음 넘은 시각」이 아니라 **그 주기의 최고**를 싣는다.
|
|
8466
|
+
*
|
|
8467
|
+
* ── 늦게 안다 ───────────────────────────────────────────────────────────────
|
|
8468
|
+
* 최고 수요는 지난 구간이 마감되어야 온다(지금 원본은 시간마다). 그래서 넘은 것을 최대 한 시간
|
|
8469
|
+
* 뒤에 안다. 그 사실을 `observedTo` 로 함께 내어 화면이 「언제까지의 값인가」를 말할 수 있게 한다.
|
|
8470
|
+
*/
|
|
8471
|
+
billingDemandAttention() {
|
|
8472
|
+
const high = this.demandHigh;
|
|
8473
|
+
const basis = this.billingDemandKW();
|
|
8474
|
+
if (!high || basis === void 0 || high.kW <= basis) return void 0;
|
|
8475
|
+
const over = high.kW - basis;
|
|
8476
|
+
const ratio = over / basis;
|
|
8477
|
+
return {
|
|
8478
|
+
id: `billing-demand-exceeded:${high.periodStart ?? high.since}`,
|
|
8479
|
+
kind: "billing-demand-exceeded",
|
|
8480
|
+
severity: ratio > 0.15 ? "high" : "medium",
|
|
8481
|
+
anchor: { locationId: this.incomingLocationId() },
|
|
8482
|
+
params: {
|
|
8483
|
+
peakKW: Math.round(high.kW),
|
|
8484
|
+
billingDemandKW: Math.round(basis),
|
|
8485
|
+
overKW: Math.round(over),
|
|
8486
|
+
/* 값의 출처 — 청구서가 말한 기준인지 현장의 선언인지. 선언은 곧 낡는다. */
|
|
8487
|
+
basisFrom: Number.isFinite(Number(this.lastBill?.billingDemandKW)) ? "bill" : "declaration",
|
|
8488
|
+
observedTo: high.atTo,
|
|
8489
|
+
periodSince: high.since
|
|
8490
|
+
},
|
|
8491
|
+
/*
|
|
8492
|
+
* 조치를 걸지 않는다. **이미 일어난 일이라 되돌릴 수 없다** — 지금 줄여도 이 주기의 최고는
|
|
8493
|
+
* 그대로다. 조치를 붙이면 화면이 「누르면 해결된다」고 거짓말한다.
|
|
8494
|
+
*/
|
|
8495
|
+
recommendedActions: []
|
|
8496
|
+
};
|
|
8497
|
+
}
|
|
8498
|
+
/**
|
|
8499
|
+
* 선언된 **요금적용전력** — 기본요금이 걸리는 기준(§`EMS_PROPERTY.billingDemandKW`).
|
|
8500
|
+
*
|
|
8501
|
+
* 청구서가 그 값을 실어 오면 그쪽이 먼저다(§`billingDemandKW()`). 이것은 청구가 오지 않는 현장의 몫이다.
|
|
8502
|
+
*/
|
|
8503
|
+
declaredBillingDemandKW() {
|
|
8504
|
+
let max;
|
|
8505
|
+
for (const loc of this.boardDef?.locations ?? []) {
|
|
8506
|
+
for (const p of loc.properties ?? []) {
|
|
8507
|
+
if (p.id !== EMS_PROPERTY.billingDemandKW) continue;
|
|
8508
|
+
const v = Number(p.value);
|
|
8509
|
+
if (!Number.isFinite(v) || v <= 0) continue;
|
|
8510
|
+
if (max === void 0 || v > max) max = v;
|
|
8511
|
+
}
|
|
8512
|
+
}
|
|
8513
|
+
return max;
|
|
8514
|
+
}
|
|
8515
|
+
/** 지금 쓰는 요금적용전력 — **청구서가 먼저다.** 매달 바뀌는 값이라 선언은 곧 낡는다. */
|
|
8516
|
+
billingDemandKW() {
|
|
8517
|
+
const billed = Number(this.lastBill?.billingDemandKW);
|
|
8518
|
+
if (Number.isFinite(billed) && billed > 0) return billed;
|
|
8519
|
+
return this.declaredBillingDemandKW();
|
|
8520
|
+
}
|
|
8434
8521
|
/** 계약을 선언한 수전 자리 — 주의가 가리킬 대상. 없으면 대상을 지어내지 않는다. */
|
|
8435
8522
|
incomingLocationId() {
|
|
8436
8523
|
for (const loc of this.boardDef?.locations ?? []) {
|
|
@@ -8711,6 +8798,7 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8711
8798
|
}
|
|
8712
8799
|
if (Number.isFinite(e.unclosedGenerationPeriods)) this.unclosedGenerationPeriods = Number(e.unclosedGenerationPeriods);
|
|
8713
8800
|
if (e.lastBill?.from && e.lastBill?.to) this.lastBill = { ...e.lastBill };
|
|
8801
|
+
if (Number.isFinite(Number(e.demandHigh?.kW)) && e.demandHigh?.since) this.demandHigh = { ...e.demandHigh };
|
|
8714
8802
|
}
|
|
8715
8803
|
getSnapshot() {
|
|
8716
8804
|
const snap = super.getSnapshot();
|
|
@@ -8732,6 +8820,7 @@ var EmsKernel = class extends FlowEngine {
|
|
|
8732
8820
|
} : {},
|
|
8733
8821
|
...this.unclosedGenerationPeriods ? { unclosedGenerationPeriods: this.unclosedGenerationPeriods } : {},
|
|
8734
8822
|
...this.lastBill ? { lastBill: { ...this.lastBill } } : {},
|
|
8823
|
+
...this.demandHigh ? { demandHigh: { ...this.demandHigh } } : {},
|
|
8735
8824
|
...contractKW !== void 0 ? { contractKW } : {},
|
|
8736
8825
|
/* 물류 흐름 요청을 받은 적이 있나 — 있으면 이 트윈에 엉뚱한 명령이 오고 있다는 사실이다. */
|
|
8737
8826
|
...this.flowRequests.size ? { flowRequests: [...this.flowRequests.entries()].map(([hook, count]) => ({ hook, count })) } : {}
|
|
@@ -9630,13 +9719,45 @@ function ingestEnergyEquipmentRecords(records, opts) {
|
|
|
9630
9719
|
if (typeof r.curtailable !== "boolean") errors.push(`curtailable \uAC00 \uCC38/\uAC70\uC9D3\uC774 \uC544\uB2C8\uB2E4: ${JSON.stringify(r.curtailable)}`);
|
|
9631
9720
|
else curtailable = r.curtailable;
|
|
9632
9721
|
}
|
|
9722
|
+
const dcVoltage = num(r?.dcVoltage, "dcVoltage", 0);
|
|
9723
|
+
const dcCurrent = num(r?.dcCurrent, "dcCurrent", 0);
|
|
9724
|
+
const phases = (raw, name) => {
|
|
9725
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
9726
|
+
if (!Array.isArray(raw)) {
|
|
9727
|
+
errors.push(`${name} \uAC00 \uBC30\uC5F4\uC774 \uC544\uB2C8\uB2E4 \u2014 \uC0C1\uBCC4 \uAC12\uC774\uB77C \uBC30\uC5F4\uB85C \uBC1B\uB294\uB2E4(\uB2E8\uC0C1\uC774\uBA74 \uC6D0\uC18C \uD558\uB098): ${JSON.stringify(raw)}`);
|
|
9728
|
+
return void 0;
|
|
9729
|
+
}
|
|
9730
|
+
if (!raw.length) {
|
|
9731
|
+
errors.push(`${name} \uAC00 \uBE48 \uBC30\uC5F4\uC774\uB2E4 \u2014 \uC7B0 \uC0C1\uC774 \uC5C6\uC73C\uBA74 \uADF8 \uCE78\uC744 \uBCF4\uB0B4\uC9C0 \uC54A\uB294\uB2E4`);
|
|
9732
|
+
return void 0;
|
|
9733
|
+
}
|
|
9734
|
+
const out = [];
|
|
9735
|
+
for (const [i, v] of raw.entries()) {
|
|
9736
|
+
const n = Number(v);
|
|
9737
|
+
if (!Number.isFinite(n)) {
|
|
9738
|
+
errors.push(`${name}[${i}] \uB97C \uC218\uB85C \uC77D\uC744 \uC218 \uC5C6\uB2E4: ${JSON.stringify(v)}`);
|
|
9739
|
+
return void 0;
|
|
9740
|
+
}
|
|
9741
|
+
if (n < 0) {
|
|
9742
|
+
errors.push(`${name}[${i}] \uAC00 \uC74C\uC218\uB2E4(${n}) \u2014 \uD06C\uAE30\uB294 \uC74C\uC218\uAC00 \uB418\uC9C0 \uC54A\uB294\uB2E4(\uBC29\uD5A5\uC740 \uB2E4\uB978 \uCD95\uC774 \uB9D0\uD55C\uB2E4)`);
|
|
9743
|
+
return void 0;
|
|
9744
|
+
}
|
|
9745
|
+
out.push(n);
|
|
9746
|
+
}
|
|
9747
|
+
return out;
|
|
9748
|
+
};
|
|
9749
|
+
const acVoltage = phases(r?.acVoltage, "acVoltage");
|
|
9750
|
+
const acCurrent = phases(r?.acCurrent, "acCurrent");
|
|
9751
|
+
if (acVoltage && acCurrent && acVoltage.length !== acCurrent.length) {
|
|
9752
|
+
errors.push(`acVoltage(${acVoltage.length}\uC0C1)\uC640 acCurrent(${acCurrent.length}\uC0C1)\uC758 \uC0C1 \uC218\uAC00 \uB2E4\uB974\uB2E4`);
|
|
9753
|
+
}
|
|
9633
9754
|
let position;
|
|
9634
9755
|
if (r?.position !== void 0 && r?.position !== null && r?.position !== "") {
|
|
9635
9756
|
const p = String(r.position);
|
|
9636
9757
|
if (!POSITIONS.has(p)) errors.push(`position \uC774 \uD45C\uC900 \uAC12\uC774 \uC544\uB2C8\uB2E4(open\xB7closed\xB7intermediate\xB7bad): ${JSON.stringify(r.position)}`);
|
|
9637
9758
|
else position = p;
|
|
9638
9759
|
}
|
|
9639
|
-
const values = { generatedKW, exportKW, soc, chargeKW, dischargeKW, curtailable, minKW, position };
|
|
9760
|
+
const values = { generatedKW, exportKW, soc, chargeKW, dischargeKW, curtailable, minKW, position, dcVoltage, dcCurrent, acVoltage, acCurrent };
|
|
9640
9761
|
if (!errors.length && Object.values(values).every((v) => v === void 0)) {
|
|
9641
9762
|
errors.push("\uBC14\uAFC0 \uC0C1\uD0DC\uAC00 \uD558\uB098\uB3C4 \uC5C6\uB2E4 \u2014 \uAC12 \uC5C6\uB294 \uBCF4\uACE0\uB294 \uC0AC\uC2E4\uB85C \uC801\uC744 \uAC83\uC774 \uC5C6\uB2E4");
|
|
9642
9763
|
}
|
|
@@ -9655,7 +9776,11 @@ function ingestEnergyEquipmentRecords(records, opts) {
|
|
|
9655
9776
|
...dischargeKW !== void 0 ? { dischargeKW } : {},
|
|
9656
9777
|
...curtailable !== void 0 ? { curtailable } : {},
|
|
9657
9778
|
...minKW !== void 0 ? { minKW } : {},
|
|
9658
|
-
...position !== void 0 ? { position } : {}
|
|
9779
|
+
...position !== void 0 ? { position } : {},
|
|
9780
|
+
...dcVoltage !== void 0 ? { dcVoltage } : {},
|
|
9781
|
+
...dcCurrent !== void 0 ? { dcCurrent } : {},
|
|
9782
|
+
...acVoltage !== void 0 ? { acVoltage } : {},
|
|
9783
|
+
...acCurrent !== void 0 ? { acCurrent } : {}
|
|
9659
9784
|
};
|
|
9660
9785
|
accepted.push({
|
|
9661
9786
|
eventId: `${opts.tenantId}-energy-eq-${++seq}`,
|
package/package.json
CHANGED