@enyo-energy/sunspec-sdk 0.0.31 → 0.0.33
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/cjs/connection-retry-manager.cjs +63 -80
- package/dist/cjs/connection-retry-manager.d.cts +32 -27
- package/dist/cjs/sunspec-devices.cjs +201 -33
- package/dist/cjs/sunspec-devices.d.cts +56 -6
- package/dist/cjs/sunspec-interfaces.cjs +109 -5
- package/dist/cjs/sunspec-interfaces.d.cts +191 -5
- package/dist/cjs/sunspec-modbus-client.cjs +599 -290
- package/dist/cjs/sunspec-modbus-client.d.cts +43 -27
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/connection-retry-manager.d.ts +32 -27
- package/dist/connection-retry-manager.js +63 -80
- package/dist/sunspec-devices.d.ts +56 -6
- package/dist/sunspec-devices.js +201 -33
- package/dist/sunspec-interfaces.d.ts +191 -5
- package/dist/sunspec-interfaces.js +108 -4
- package/dist/sunspec-modbus-client.d.ts +43 -27
- package/dist/sunspec-modbus-client.js +600 -291
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -20,7 +20,6 @@ exports.SunspecModbusClient = void 0;
|
|
|
20
20
|
* - string: all registers 0x0000 (NULL)
|
|
21
21
|
*/
|
|
22
22
|
const sunspec_interfaces_js_1 = require("./sunspec-interfaces.cjs");
|
|
23
|
-
const connection_retry_manager_js_1 = require("./connection-retry-manager.cjs");
|
|
24
23
|
const EnergyAppModbusConnectionHealth_js_1 = require("@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusConnectionHealth.js");
|
|
25
24
|
const EnergyAppModbusFaultTolerantReader_js_1 = require("@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js");
|
|
26
25
|
const EnergyAppModbusDataTypeConverter_js_1 = require("@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusDataTypeConverter.js");
|
|
@@ -33,13 +32,11 @@ class SunspecModbusClient {
|
|
|
33
32
|
faultTolerantReader = null;
|
|
34
33
|
modbusDataTypeConverter;
|
|
35
34
|
connectionParams = null;
|
|
36
|
-
retryManager;
|
|
37
35
|
autoReconnectEnabled = true;
|
|
38
|
-
constructor(energyApp
|
|
36
|
+
constructor(energyApp) {
|
|
39
37
|
this.energyApp = energyApp;
|
|
40
38
|
this.connectionHealth = new EnergyAppModbusConnectionHealth_js_1.EnergyAppModbusConnectionHealth();
|
|
41
39
|
this.modbusDataTypeConverter = new EnergyAppModbusDataTypeConverter_js_1.EnergyAppModbusDataTypeConverter();
|
|
42
|
-
this.retryManager = new connection_retry_manager_js_1.ConnectionRetryManager(retryConfig);
|
|
43
40
|
}
|
|
44
41
|
/**
|
|
45
42
|
* Connect to Modbus device
|
|
@@ -66,15 +63,12 @@ class SunspecModbusClient {
|
|
|
66
63
|
}
|
|
67
64
|
this.connected = true;
|
|
68
65
|
this.connectionHealth.recordSuccess();
|
|
69
|
-
this.retryManager.reset();
|
|
70
66
|
console.log(`Connected to Sunspec device at ${host}:${port} unit ${unitId}`);
|
|
71
67
|
}
|
|
72
68
|
/**
|
|
73
69
|
* Disconnect from Modbus device
|
|
74
70
|
*/
|
|
75
71
|
async disconnect() {
|
|
76
|
-
// Cancel any pending retry attempts
|
|
77
|
-
this.retryManager.cancelPendingRetry();
|
|
78
72
|
if (this.modbusClient && this.connected) {
|
|
79
73
|
await this.modbusClient.disconnect();
|
|
80
74
|
this.modbusClient = null;
|
|
@@ -153,61 +147,12 @@ class SunspecModbusClient {
|
|
|
153
147
|
return false;
|
|
154
148
|
}
|
|
155
149
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Check connection health and trigger automatic reconnection if unhealthy
|
|
158
|
-
* Returns true if connection is healthy or was successfully restored
|
|
159
|
-
*/
|
|
160
|
-
async ensureHealthyConnection() {
|
|
161
|
-
// If already healthy, return immediately
|
|
162
|
-
if (this.isHealthy()) {
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
// If no connection params, we can't reconnect
|
|
166
|
-
if (!this.connectionParams) {
|
|
167
|
-
console.error('Connection unhealthy and no connection parameters stored for reconnection.');
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
// If auto-reconnect is disabled, just report the status
|
|
171
|
-
if (!this.autoReconnectEnabled) {
|
|
172
|
-
console.log('Connection unhealthy but auto-reconnect is disabled.');
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
// If a retry is already in progress, don't start another
|
|
176
|
-
if (this.retryManager.isRetryInProgress()) {
|
|
177
|
-
console.log('Retry already in progress...');
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
console.log('Connection unhealthy, initiating reconnection with exponential backoff...');
|
|
181
|
-
// Attempt reconnection with exponential backoff
|
|
182
|
-
let success = false;
|
|
183
|
-
while (this.retryManager.shouldRetry() && !success) {
|
|
184
|
-
success = await this.retryManager.scheduleRetry(() => this.reconnect());
|
|
185
|
-
if (success) {
|
|
186
|
-
// Re-discover models after successful reconnection
|
|
187
|
-
try {
|
|
188
|
-
await this.discoverModels();
|
|
189
|
-
console.log('Models re-discovered after reconnection.');
|
|
190
|
-
}
|
|
191
|
-
catch (e) {
|
|
192
|
-
console.warn('Failed to re-discover models after reconnection:', e);
|
|
193
|
-
}
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (!success) {
|
|
198
|
-
console.error('Failed to restore healthy connection after all retry attempts.');
|
|
199
|
-
}
|
|
200
|
-
return success;
|
|
201
|
-
}
|
|
202
150
|
/**
|
|
203
151
|
* Enable or disable automatic reconnection
|
|
204
152
|
*/
|
|
205
153
|
setAutoReconnect(enabled) {
|
|
206
154
|
this.autoReconnectEnabled = enabled;
|
|
207
155
|
console.log(`Auto-reconnect ${enabled ? 'enabled' : 'disabled'}`);
|
|
208
|
-
if (!enabled) {
|
|
209
|
-
this.retryManager.cancelPendingRetry();
|
|
210
|
-
}
|
|
211
156
|
}
|
|
212
157
|
/**
|
|
213
158
|
* Check if auto-reconnect is enabled
|
|
@@ -215,12 +160,6 @@ class SunspecModbusClient {
|
|
|
215
160
|
isAutoReconnectEnabled() {
|
|
216
161
|
return this.autoReconnectEnabled;
|
|
217
162
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Get the retry manager for advanced configuration
|
|
220
|
-
*/
|
|
221
|
-
getRetryManager() {
|
|
222
|
-
return this.retryManager;
|
|
223
|
-
}
|
|
224
163
|
/**
|
|
225
164
|
* Detect the base address and addressing mode (0-based or 1-based) for SunSpec
|
|
226
165
|
*/
|
|
@@ -295,7 +234,7 @@ class SunspecModbusClient {
|
|
|
295
234
|
}
|
|
296
235
|
}
|
|
297
236
|
const addressesChecked = customBaseAddress !== undefined
|
|
298
|
-
? `${customBaseAddress}, ${customBaseAddress + 1}
|
|
237
|
+
? `${customBaseAddress}, ${customBaseAddress + 1}`
|
|
299
238
|
: '40000 or 40001';
|
|
300
239
|
throw new Error(`Device is not SunSpec compliant - "SunS" identifier not found at addresses ${addressesChecked}`);
|
|
301
240
|
}
|
|
@@ -500,7 +439,7 @@ class SunspecModbusClient {
|
|
|
500
439
|
}
|
|
501
440
|
}
|
|
502
441
|
/**
|
|
503
|
-
* Read inverter data from Model 103 (Three Phase)
|
|
442
|
+
* Read inverter data from Model 101 (Single Phase) / Model 103 (Three Phase)
|
|
504
443
|
*/
|
|
505
444
|
async readInverterData() {
|
|
506
445
|
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Inverter3Phase);
|
|
@@ -525,64 +464,44 @@ class SunspecModbusClient {
|
|
|
525
464
|
const voltageANRaw = await this.readRegisterValue(baseAddr + 10, 1, 'uint16');
|
|
526
465
|
const voltageBNRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
527
466
|
const voltageCNRaw = await this.readRegisterValue(baseAddr + 12, 1, 'uint16');
|
|
467
|
+
// Read raw values for fields that need IIFEs removed
|
|
468
|
+
const acCurrentRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
469
|
+
const acPowerRaw = await this.readRegisterValue(baseAddr + 14, 1, 'int16');
|
|
470
|
+
const dcCurrentRaw = await this.readRegisterValue(baseAddr + 27, 1, 'uint16');
|
|
471
|
+
const dcVoltageRaw = await this.readRegisterValue(baseAddr + 28, 1, 'uint16');
|
|
472
|
+
const dcPowerRaw = await this.readRegisterValue(baseAddr + 30, 1, 'int16');
|
|
528
473
|
const data = {
|
|
529
474
|
blockNumber: 103,
|
|
530
475
|
blockAddress: model.address,
|
|
531
476
|
blockLength: model.length,
|
|
532
477
|
// AC Current values - Offsets 2-5
|
|
533
|
-
acCurrent: this.applyScaleFactor(
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
return raw;
|
|
538
|
-
})(), scaleFactors.A_SF, 'uint16', 'AC Current'),
|
|
539
|
-
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase A Current'),
|
|
540
|
-
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase B Current'),
|
|
541
|
-
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase C Current'),
|
|
478
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF, 'uint16', 'AC Current', 2, 103),
|
|
479
|
+
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase A Current', 3, 103),
|
|
480
|
+
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase B Current', 4, 103),
|
|
481
|
+
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase C Current', 5, 103),
|
|
542
482
|
// Voltage values - Offsets 7-12
|
|
543
|
-
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage AB'),
|
|
544
|
-
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage BC'),
|
|
545
|
-
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage CA'),
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF, 'uint16', 'Voltage CN'),
|
|
483
|
+
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage AB', 7, 103),
|
|
484
|
+
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage BC', 8, 103),
|
|
485
|
+
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage CA', 9, 103),
|
|
486
|
+
voltageAN: this.applyScaleFactor(voltageANRaw, scaleFactors.V_SF, 'uint16', 'Voltage AN', 10, 103),
|
|
487
|
+
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF, 'uint16', 'Voltage BN', 11, 103),
|
|
488
|
+
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF, 'uint16', 'Voltage CN', 12, 103),
|
|
550
489
|
// Power values - Offsets 14, 18, 20, 22
|
|
551
|
-
acPower: this.applyScaleFactor(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
return raw;
|
|
556
|
-
})(), scaleFactors.W_SF, 'int16', 'AC Power'),
|
|
557
|
-
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF, 'uint16', 'Apparent Power'),
|
|
558
|
-
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF, 'int16', 'Reactive Power'),
|
|
559
|
-
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF, 'int16', 'Power Factor'),
|
|
490
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16', 'AC Power', 14, 103),
|
|
491
|
+
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF, 'uint16', 'Apparent Power', 18, 103),
|
|
492
|
+
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF, 'int16', 'Reactive Power', 20, 103),
|
|
493
|
+
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF, 'int16', 'Power Factor', 22, 103),
|
|
560
494
|
// Frequency - Offset 16
|
|
561
|
-
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF, 'uint16', 'Frequency'),
|
|
495
|
+
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF, 'uint16', 'Frequency', 16, 103),
|
|
562
496
|
// DC values - Offsets 27, 28, 30
|
|
563
|
-
dcCurrent: this.applyScaleFactor(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
567
|
-
return raw;
|
|
568
|
-
})(), scaleFactors.DCA_SF, 'uint16', 'DC Current'),
|
|
569
|
-
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
570
|
-
const addr = baseAddr + 28;
|
|
571
|
-
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
572
|
-
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
573
|
-
return raw;
|
|
574
|
-
})(), scaleFactors.DCV_SF, 'uint16', 'DC Voltage'),
|
|
575
|
-
dcPower: this.applyScaleFactor(await (async () => {
|
|
576
|
-
const addr = baseAddr + 30;
|
|
577
|
-
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
578
|
-
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
579
|
-
return raw;
|
|
580
|
-
})(), scaleFactors.DCW_SF, 'int16', 'DC Power'),
|
|
497
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', 'DC Current', 27, 103),
|
|
498
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', 'DC Voltage', 28, 103),
|
|
499
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16', 'DC Power', 30, 103),
|
|
581
500
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
582
|
-
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Cabinet Temperature'),
|
|
583
|
-
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Heat Sink Temperature'),
|
|
584
|
-
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Transformer Temperature'),
|
|
585
|
-
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Other Temperature'),
|
|
501
|
+
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Cabinet Temperature', 32, 103),
|
|
502
|
+
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Heat Sink Temperature', 34, 103),
|
|
503
|
+
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Transformer Temperature', 35, 103),
|
|
504
|
+
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Other Temperature', 36, 103),
|
|
586
505
|
// Status values - Offsets 38, 39
|
|
587
506
|
operatingState: await this.readRegisterValue(baseAddr + 38, 1, 'uint16'),
|
|
588
507
|
vendorState: await this.readRegisterValue(baseAddr + 39, 1, 'uint16'),
|
|
@@ -594,11 +513,20 @@ class SunspecModbusClient {
|
|
|
594
513
|
vendorEvents3: await this.readRegisterValue(baseAddr + 48, 2, 'uint32'),
|
|
595
514
|
vendorEvents4: await this.readRegisterValue(baseAddr + 50, 2, 'uint32')
|
|
596
515
|
};
|
|
516
|
+
// Log non-scaled fields
|
|
517
|
+
this.logRegisterRead(103, 38, 'Operating State', data.operatingState, 'enum16');
|
|
518
|
+
this.logRegisterRead(103, 39, 'Vendor State', data.vendorState, 'uint16');
|
|
519
|
+
this.logRegisterRead(103, 40, 'Events', data.events, 'bitfield32');
|
|
520
|
+
this.logRegisterRead(103, 42, 'Events2', data.events2, 'bitfield32');
|
|
521
|
+
this.logRegisterRead(103, 44, 'Vendor Events 1', data.vendorEvents1, 'bitfield32');
|
|
522
|
+
this.logRegisterRead(103, 46, 'Vendor Events 2', data.vendorEvents2, 'bitfield32');
|
|
523
|
+
this.logRegisterRead(103, 48, 'Vendor Events 3', data.vendorEvents3, 'bitfield32');
|
|
524
|
+
this.logRegisterRead(103, 50, 'Vendor Events 4', data.vendorEvents4, 'bitfield32');
|
|
597
525
|
// Read AC Energy (32-bit accumulator) - Offset 24-25
|
|
598
|
-
const
|
|
599
|
-
|
|
526
|
+
const acEnergy = await this.readRegisterValue(baseAddr + 24, 2, 'uint32');
|
|
527
|
+
this.logRegisterRead(103, 24, 'AC Energy', acEnergy, 'acc32');
|
|
600
528
|
data.acEnergy = !this.isUnimplementedValue(acEnergy, 'acc32')
|
|
601
|
-
? acEnergy * Math.pow(10, scaleFactors.WH_SF)
|
|
529
|
+
? acEnergy * Math.pow(10, scaleFactors.WH_SF)
|
|
602
530
|
: undefined;
|
|
603
531
|
return data;
|
|
604
532
|
}
|
|
@@ -626,41 +554,32 @@ class SunspecModbusClient {
|
|
|
626
554
|
DCV_SF: await this.readRegisterValue(baseAddr + 19, 1, 'int16'),
|
|
627
555
|
DCW_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16')
|
|
628
556
|
};
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
const
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
const dcVoltageAddr = baseAddr + 15;
|
|
647
|
-
const dcVoltageRaw = await this.readRegisterValue(dcVoltageAddr, 1, 'uint16');
|
|
648
|
-
console.log(`DC Voltage: address=${dcVoltageAddr}, raw=0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`);
|
|
649
|
-
const dcPowerAddr = baseAddr + 20;
|
|
650
|
-
const dcPowerRaw = await this.readRegisterValue(dcPowerAddr, 1, 'int16');
|
|
651
|
-
console.log(`DC Power: address=${dcPowerAddr}, raw=0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw}), SF=${scaleFactors.DCW_SF}`);
|
|
652
|
-
const stateAddr = baseAddr + 24;
|
|
653
|
-
const stateRaw = await this.readRegisterValue(stateAddr, 1, 'uint16');
|
|
654
|
-
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
557
|
+
this.logRegisterRead(101, 6, 'A_SF', scaleFactors.A_SF, 'int16');
|
|
558
|
+
this.logRegisterRead(101, 13, 'V_SF', scaleFactors.V_SF, 'int16');
|
|
559
|
+
this.logRegisterRead(101, 10, 'W_SF', scaleFactors.W_SF, 'int16');
|
|
560
|
+
this.logRegisterRead(101, 12, 'Hz_SF', scaleFactors.Hz_SF, 'int16');
|
|
561
|
+
this.logRegisterRead(101, 18, 'DCA_SF', scaleFactors.DCA_SF, 'int16');
|
|
562
|
+
this.logRegisterRead(101, 19, 'DCV_SF', scaleFactors.DCV_SF, 'int16');
|
|
563
|
+
this.logRegisterRead(101, 21, 'DCW_SF', scaleFactors.DCW_SF, 'int16');
|
|
564
|
+
// Read raw values
|
|
565
|
+
const acCurrentRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
566
|
+
const voltageRaw = await this.readRegisterValue(baseAddr + 7, 1, 'uint16');
|
|
567
|
+
const acPowerRaw = await this.readRegisterValue(baseAddr + 9, 1, 'int16');
|
|
568
|
+
const freqRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
569
|
+
const dcCurrentRaw = await this.readRegisterValue(baseAddr + 14, 1, 'uint16');
|
|
570
|
+
const dcVoltageRaw = await this.readRegisterValue(baseAddr + 15, 1, 'uint16');
|
|
571
|
+
const dcPowerRaw = await this.readRegisterValue(baseAddr + 20, 1, 'int16');
|
|
572
|
+
const stateRaw = await this.readRegisterValue(baseAddr + 24, 1, 'uint16');
|
|
573
|
+
this.logRegisterRead(101, 24, 'Operating State', stateRaw, 'enum16');
|
|
655
574
|
return {
|
|
656
575
|
blockNumber: 101,
|
|
657
|
-
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF, 'uint16', '
|
|
658
|
-
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF, 'uint16', '
|
|
659
|
-
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16', '
|
|
660
|
-
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF, 'uint16', '
|
|
661
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', '
|
|
662
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', '
|
|
663
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16', '
|
|
576
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF, 'uint16', 'Voltage AN', 7, 101),
|
|
577
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF, 'uint16', 'AC Current', 2, 101),
|
|
578
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16', 'AC Power', 9, 101),
|
|
579
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF, 'uint16', 'Frequency', 11, 101),
|
|
580
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', 'DC Current', 14, 101),
|
|
581
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', 'DC Voltage', 15, 101),
|
|
582
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16', 'DC Power', 20, 101),
|
|
664
583
|
operatingState: stateRaw
|
|
665
584
|
};
|
|
666
585
|
}
|
|
@@ -687,27 +606,57 @@ class SunspecModbusClient {
|
|
|
687
606
|
DCW_SF: await this.readRegisterValue(baseAddr + 31, 1, 'int16'), // Offset 31
|
|
688
607
|
Tmp_SF: await this.readRegisterValue(baseAddr + 36, 1, 'int16') // Offset 36
|
|
689
608
|
};
|
|
690
|
-
|
|
609
|
+
this.logRegisterRead(103, 6, 'A_SF', scaleFactors.A_SF, 'int16');
|
|
610
|
+
this.logRegisterRead(103, 13, 'V_SF', scaleFactors.V_SF, 'int16');
|
|
611
|
+
this.logRegisterRead(103, 15, 'W_SF', scaleFactors.W_SF, 'int16');
|
|
612
|
+
this.logRegisterRead(103, 17, 'Hz_SF', scaleFactors.Hz_SF, 'int16');
|
|
613
|
+
this.logRegisterRead(103, 19, 'VA_SF', scaleFactors.VA_SF, 'int16');
|
|
614
|
+
this.logRegisterRead(103, 21, 'VAr_SF', scaleFactors.VAr_SF, 'int16');
|
|
615
|
+
this.logRegisterRead(103, 23, 'PF_SF', scaleFactors.PF_SF, 'int16');
|
|
616
|
+
this.logRegisterRead(103, 26, 'WH_SF', scaleFactors.WH_SF, 'int16');
|
|
617
|
+
this.logRegisterRead(103, 28, 'DCA_SF', scaleFactors.DCA_SF, 'int16');
|
|
618
|
+
this.logRegisterRead(103, 29, 'DCV_SF', scaleFactors.DCV_SF, 'int16');
|
|
619
|
+
this.logRegisterRead(103, 31, 'DCW_SF', scaleFactors.DCW_SF, 'int16');
|
|
620
|
+
this.logRegisterRead(103, 36, 'Tmp_SF', scaleFactors.Tmp_SF, 'int16');
|
|
691
621
|
return scaleFactors;
|
|
692
622
|
}
|
|
693
623
|
/**
|
|
694
624
|
* Apply scale factor to a value
|
|
695
625
|
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
696
626
|
*/
|
|
697
|
-
applyScaleFactor(value, scaleFactor, dataType = 'uint16', fieldName) {
|
|
627
|
+
applyScaleFactor(value, scaleFactor, dataType = 'uint16', fieldName, offset, modelId) {
|
|
698
628
|
// Check for unimplemented values
|
|
699
629
|
if (this.isUnimplementedValue(value, dataType)) {
|
|
700
630
|
return undefined;
|
|
701
631
|
}
|
|
702
632
|
const scaledValue = value * Math.pow(10, scaleFactor);
|
|
703
|
-
// Log the raw and scaled values
|
|
704
|
-
|
|
705
|
-
|
|
633
|
+
// Log the raw and scaled values
|
|
634
|
+
if (offset !== undefined && modelId !== undefined && fieldName) {
|
|
635
|
+
const hex = value >= 0 ? value.toString(16).toUpperCase() : (value >>> 0).toString(16).toUpperCase();
|
|
636
|
+
console.log(`[Model ${modelId}] offset ${offset}: ${fieldName} raw=${value} (0x${hex}), SF=${scaleFactor}, scaled=${scaledValue}`);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
const fieldPrefix = fieldName ? `${fieldName}: ` : '';
|
|
640
|
+
console.log(`Scale Factor Applied - ${fieldPrefix}raw=${value} (decimal), SF=${scaleFactor}, scaled=${scaledValue}`);
|
|
641
|
+
}
|
|
706
642
|
return scaledValue;
|
|
707
643
|
}
|
|
644
|
+
logRegisterRead(modelId, offset, fieldName, rawValue, dataType) {
|
|
645
|
+
const typeInfo = dataType ? ` (${dataType})` : '';
|
|
646
|
+
if (rawValue === undefined) {
|
|
647
|
+
console.log(`[Model ${modelId}] offset ${offset}: ${fieldName} = -${typeInfo}`);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (typeof rawValue === 'number') {
|
|
651
|
+
const hex = rawValue >= 0 ? rawValue.toString(16).toUpperCase() : (rawValue >>> 0).toString(16).toUpperCase();
|
|
652
|
+
console.log(`[Model ${modelId}] offset ${offset}: ${fieldName} = ${rawValue} (0x${hex})${typeInfo}`);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
console.log(`[Model ${modelId}] offset ${offset}: ${fieldName} = "${rawValue}"${typeInfo}`);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
708
658
|
/**
|
|
709
|
-
* Read
|
|
710
|
-
* Returns the scale factors for DC Current, DC Voltage, DC Power, and DC Energy
|
|
659
|
+
* Read scale factors from Model 160 (MPPT)
|
|
711
660
|
*
|
|
712
661
|
* MPPT Model 160 Scale Factor Register Offsets (relative to module start):
|
|
713
662
|
* - DCA_SF (Current Scale Factor): Offset 2
|
|
@@ -738,10 +687,10 @@ class SunspecModbusClient {
|
|
|
738
687
|
DCW_SF, // Power Scale Factor
|
|
739
688
|
DCWH_SF, // Energy Scale Factor
|
|
740
689
|
};
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
690
|
+
this.logRegisterRead(160, 2, 'DCA_SF', DCA_SF, 'int16');
|
|
691
|
+
this.logRegisterRead(160, 3, 'DCV_SF', DCV_SF, 'int16');
|
|
692
|
+
this.logRegisterRead(160, 4, 'DCW_SF', DCW_SF, 'int16');
|
|
693
|
+
this.logRegisterRead(160, 5, 'DCWH_SF', DCWH_SF, 'int16');
|
|
745
694
|
return scaleFactors;
|
|
746
695
|
}
|
|
747
696
|
catch (error) {
|
|
@@ -771,7 +720,6 @@ class SunspecModbusClient {
|
|
|
771
720
|
console.error(`Failed to read scale factors for MPPT module ${moduleId}`);
|
|
772
721
|
return null;
|
|
773
722
|
}
|
|
774
|
-
console.log(`MPPT Module ${moduleId} Scale Factors:`, JSON.stringify(scaleFactors, null, 2));
|
|
775
723
|
const id = await this.readRegisterValue(moduleAddr, 1, 'uint16');
|
|
776
724
|
const idString = await this.readRegisterValue(moduleAddr + 1, 8, 'string');
|
|
777
725
|
const dcCurrentRaw = await this.readRegisterValue(moduleAddr + 9, 1, 'uint16');
|
|
@@ -780,6 +728,8 @@ class SunspecModbusClient {
|
|
|
780
728
|
const dcEnergyRaw = await this.readRegisterValue(moduleAddr + 12, 2, 'uint32');
|
|
781
729
|
const temperatureRaw = await this.readRegisterValue(moduleAddr + 16, 1, 'int16');
|
|
782
730
|
const dcst = await this.readRegisterValue(moduleAddr + 17, 1, 'uint16');
|
|
731
|
+
this.logRegisterRead(160, 0, `MPPT ${moduleId} ID`, id, 'uint16');
|
|
732
|
+
this.logRegisterRead(160, 1, `MPPT ${moduleId} String ID`, idString, 'string');
|
|
783
733
|
// Map the DC module state to human-readable name
|
|
784
734
|
// Check if this module is actually implemented/connected
|
|
785
735
|
// If all key values are unimplemented, this module doesn't exist
|
|
@@ -793,24 +743,28 @@ class SunspecModbusClient {
|
|
|
793
743
|
// Note: There appears to be a temperature scale factor in the original model,
|
|
794
744
|
// but it's not in the current register map. We'll apply a default scale factor.
|
|
795
745
|
const temperatureScaleFactor = -1; // Common default for temperature readings
|
|
746
|
+
this.logRegisterRead(160, 12, `MPPT ${moduleId} DC Energy`, dcEnergyRaw, 'acc32');
|
|
747
|
+
const timestampRaw = await this.readRegisterValue(moduleAddr + 14, 2, 'uint32');
|
|
748
|
+
this.logRegisterRead(160, 14, `MPPT ${moduleId} Timestamp`, timestampRaw, 'uint32');
|
|
749
|
+
this.logRegisterRead(160, 17, `MPPT ${moduleId} Operating State`, dcst, 'enum16');
|
|
796
750
|
const data = {
|
|
797
751
|
blockNumber: 160,
|
|
798
752
|
blockAddress: model.address,
|
|
799
753
|
blockLength: model.length,
|
|
800
754
|
id: id,
|
|
801
755
|
stringId: idString,
|
|
802
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', `MPPT ${moduleId} DC Current
|
|
756
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', `MPPT ${moduleId} DC Current`, 9, 160),
|
|
803
757
|
dcCurrentSF: scaleFactors.DCA_SF,
|
|
804
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', `MPPT ${moduleId} DC Voltage
|
|
758
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', `MPPT ${moduleId} DC Voltage`, 10, 160),
|
|
805
759
|
dcVoltageSF: scaleFactors.DCV_SF,
|
|
806
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'uint16', `MPPT ${moduleId} DC Power
|
|
760
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'uint16', `MPPT ${moduleId} DC Power`, 11, 160),
|
|
807
761
|
dcPowerSF: scaleFactors.DCW_SF,
|
|
808
762
|
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'acc32')
|
|
809
|
-
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF)
|
|
763
|
+
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF)
|
|
810
764
|
: undefined,
|
|
811
765
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
812
|
-
timestamp:
|
|
813
|
-
temperature: this.applyScaleFactor(temperatureRaw, temperatureScaleFactor, 'int16', `MPPT ${moduleId} Temperature
|
|
766
|
+
timestamp: timestampRaw,
|
|
767
|
+
temperature: this.applyScaleFactor(temperatureRaw, temperatureScaleFactor, 'int16', `MPPT ${moduleId} Temperature`, 16, 160),
|
|
814
768
|
temperatureSF: temperatureScaleFactor,
|
|
815
769
|
operatingState: dcst,
|
|
816
770
|
};
|
|
@@ -822,7 +776,7 @@ class SunspecModbusClient {
|
|
|
822
776
|
}
|
|
823
777
|
}
|
|
824
778
|
/**
|
|
825
|
-
* Read all
|
|
779
|
+
* Read all MPPT strings from Model 160 (Multiple MPPT)
|
|
826
780
|
*/
|
|
827
781
|
async readAllMPPTData() {
|
|
828
782
|
const mpptData = [];
|
|
@@ -904,7 +858,239 @@ class SunspecModbusClient {
|
|
|
904
858
|
}
|
|
905
859
|
}
|
|
906
860
|
/**
|
|
907
|
-
*
|
|
861
|
+
* Map battery type to human-readable name (Model 802)
|
|
862
|
+
*/
|
|
863
|
+
mapBatteryType(typ) {
|
|
864
|
+
switch (typ) {
|
|
865
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.NOT_APPLICABLE_UNKNOWN:
|
|
866
|
+
return "NOT_APPLICABLE_UNKNOWN";
|
|
867
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.LEAD_ACID:
|
|
868
|
+
return "LEAD_ACID";
|
|
869
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.NICKEL_METAL_HYDRIDE:
|
|
870
|
+
return "NICKEL_METAL_HYDRIDE";
|
|
871
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.NICKEL_CADMIUM:
|
|
872
|
+
return "NICKEL_CADMIUM";
|
|
873
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.LITHIUM_ION:
|
|
874
|
+
return "LITHIUM_ION";
|
|
875
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.CARBON_ZINC:
|
|
876
|
+
return "CARBON_ZINC";
|
|
877
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.ZINC_CHLORIDE:
|
|
878
|
+
return "ZINC_CHLORIDE";
|
|
879
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.ALKALINE:
|
|
880
|
+
return "ALKALINE";
|
|
881
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.RECHARGEABLE_ALKALINE:
|
|
882
|
+
return "RECHARGEABLE_ALKALINE";
|
|
883
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.SODIUM_SULFUR:
|
|
884
|
+
return "SODIUM_SULFUR";
|
|
885
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.FLOW:
|
|
886
|
+
return "FLOW";
|
|
887
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.SUPER_CAPACITOR:
|
|
888
|
+
return "SUPER_CAPACITOR";
|
|
889
|
+
case sunspec_interfaces_js_1.SunspecBatteryType.OTHER:
|
|
890
|
+
return "OTHER";
|
|
891
|
+
default:
|
|
892
|
+
if (this.isUnimplementedValue(typ, 'enum16')) {
|
|
893
|
+
return "NOT_IMPLEMENTED";
|
|
894
|
+
}
|
|
895
|
+
return `UNKNOWN(${typ})`;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Map battery bank state to human-readable name (Model 802)
|
|
900
|
+
*/
|
|
901
|
+
mapBatteryBankState(state) {
|
|
902
|
+
switch (state) {
|
|
903
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.DISCONNECTED:
|
|
904
|
+
return "DISCONNECTED";
|
|
905
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.INITIALIZING:
|
|
906
|
+
return "INITIALIZING";
|
|
907
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.CONNECTED:
|
|
908
|
+
return "CONNECTED";
|
|
909
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.STANDBY:
|
|
910
|
+
return "STANDBY";
|
|
911
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.SOC_PROTECTION:
|
|
912
|
+
return "SOC_PROTECTION";
|
|
913
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.SUSPENDING:
|
|
914
|
+
return "SUSPENDING";
|
|
915
|
+
case sunspec_interfaces_js_1.SunspecBatteryBankState.FAULT:
|
|
916
|
+
return "FAULT";
|
|
917
|
+
default:
|
|
918
|
+
if (this.isUnimplementedValue(state, 'enum16')) {
|
|
919
|
+
return "NOT_IMPLEMENTED";
|
|
920
|
+
}
|
|
921
|
+
return `UNKNOWN(${state})`;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Read Model 802 scale factors (offsets 52-63)
|
|
926
|
+
*/
|
|
927
|
+
async readBatteryBaseScaleFactors(baseAddr) {
|
|
928
|
+
const scaleFactors = {
|
|
929
|
+
AHRtg_SF: await this.readRegisterValue(baseAddr + 52, 1, 'int16'),
|
|
930
|
+
WHRtg_SF: await this.readRegisterValue(baseAddr + 53, 1, 'int16'),
|
|
931
|
+
WChaDisChaMax_SF: await this.readRegisterValue(baseAddr + 54, 1, 'int16'),
|
|
932
|
+
DisChaRte_SF: await this.readRegisterValue(baseAddr + 55, 1, 'int16'),
|
|
933
|
+
SoC_SF: await this.readRegisterValue(baseAddr + 56, 1, 'int16'),
|
|
934
|
+
DoD_SF: await this.readRegisterValue(baseAddr + 57, 1, 'int16'),
|
|
935
|
+
SoH_SF: await this.readRegisterValue(baseAddr + 58, 1, 'int16'),
|
|
936
|
+
V_SF: await this.readRegisterValue(baseAddr + 59, 1, 'int16'),
|
|
937
|
+
CellV_SF: await this.readRegisterValue(baseAddr + 60, 1, 'int16'),
|
|
938
|
+
A_SF: await this.readRegisterValue(baseAddr + 61, 1, 'int16'),
|
|
939
|
+
AMax_SF: await this.readRegisterValue(baseAddr + 62, 1, 'int16'),
|
|
940
|
+
W_SF: await this.readRegisterValue(baseAddr + 63, 1, 'int16'),
|
|
941
|
+
};
|
|
942
|
+
this.logRegisterRead(802, 52, 'AHRtg_SF', scaleFactors.AHRtg_SF, 'int16');
|
|
943
|
+
this.logRegisterRead(802, 53, 'WHRtg_SF', scaleFactors.WHRtg_SF, 'int16');
|
|
944
|
+
this.logRegisterRead(802, 54, 'WChaDisChaMax_SF', scaleFactors.WChaDisChaMax_SF, 'int16');
|
|
945
|
+
this.logRegisterRead(802, 55, 'DisChaRte_SF', scaleFactors.DisChaRte_SF, 'int16');
|
|
946
|
+
this.logRegisterRead(802, 56, 'SoC_SF', scaleFactors.SoC_SF, 'int16');
|
|
947
|
+
this.logRegisterRead(802, 57, 'DoD_SF', scaleFactors.DoD_SF, 'int16');
|
|
948
|
+
this.logRegisterRead(802, 58, 'SoH_SF', scaleFactors.SoH_SF, 'int16');
|
|
949
|
+
this.logRegisterRead(802, 59, 'V_SF', scaleFactors.V_SF, 'int16');
|
|
950
|
+
this.logRegisterRead(802, 60, 'CellV_SF', scaleFactors.CellV_SF, 'int16');
|
|
951
|
+
this.logRegisterRead(802, 61, 'A_SF', scaleFactors.A_SF, 'int16');
|
|
952
|
+
this.logRegisterRead(802, 62, 'AMax_SF', scaleFactors.AMax_SF, 'int16');
|
|
953
|
+
this.logRegisterRead(802, 63, 'W_SF', scaleFactors.W_SF, 'int16');
|
|
954
|
+
return scaleFactors;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Read battery base data from Model 802 (Battery Base)
|
|
958
|
+
*/
|
|
959
|
+
async readBatteryBaseData() {
|
|
960
|
+
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.BatteryBase);
|
|
961
|
+
if (!model) {
|
|
962
|
+
console.log('Battery Base model 802 not found');
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
const baseAddr = model.address;
|
|
966
|
+
console.log(`Reading Battery Base Data from Model 802 at base address: ${baseAddr}`);
|
|
967
|
+
try {
|
|
968
|
+
// Read scale factors first (offsets 52-63)
|
|
969
|
+
const sf = await this.readBatteryBaseScaleFactors(baseAddr);
|
|
970
|
+
// Read raw values
|
|
971
|
+
const ahRtgRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
972
|
+
const whRtgRaw = await this.readRegisterValue(baseAddr + 3, 1, 'uint16');
|
|
973
|
+
const wChaRteMaxRaw = await this.readRegisterValue(baseAddr + 4, 1, 'uint16');
|
|
974
|
+
const wDisChaRteMaxRaw = await this.readRegisterValue(baseAddr + 5, 1, 'uint16');
|
|
975
|
+
const disChaRteRaw = await this.readRegisterValue(baseAddr + 6, 1, 'uint16');
|
|
976
|
+
const soCMaxRaw = await this.readRegisterValue(baseAddr + 7, 1, 'uint16');
|
|
977
|
+
const soCMinRaw = await this.readRegisterValue(baseAddr + 8, 1, 'uint16');
|
|
978
|
+
const soCRsvMaxRaw = await this.readRegisterValue(baseAddr + 9, 1, 'uint16');
|
|
979
|
+
const soCRsvMinRaw = await this.readRegisterValue(baseAddr + 10, 1, 'uint16');
|
|
980
|
+
const soCRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
981
|
+
const doDRaw = await this.readRegisterValue(baseAddr + 12, 1, 'uint16');
|
|
982
|
+
const soHRaw = await this.readRegisterValue(baseAddr + 13, 1, 'uint16');
|
|
983
|
+
const nCycRaw = await this.readRegisterValue(baseAddr + 14, 2, 'uint32');
|
|
984
|
+
const chaStRaw = await this.readRegisterValue(baseAddr + 16, 1, 'uint16');
|
|
985
|
+
const locRemCtlRaw = await this.readRegisterValue(baseAddr + 17, 1, 'uint16');
|
|
986
|
+
const typRaw = await this.readRegisterValue(baseAddr + 21, 1, 'uint16');
|
|
987
|
+
const stateRaw = await this.readRegisterValue(baseAddr + 22, 1, 'uint16');
|
|
988
|
+
const evt1Raw = await this.readRegisterValue(baseAddr + 26, 2, 'uint32');
|
|
989
|
+
const evt2Raw = await this.readRegisterValue(baseAddr + 28, 2, 'uint32');
|
|
990
|
+
const evtVnd1Raw = await this.readRegisterValue(baseAddr + 30, 2, 'uint32');
|
|
991
|
+
const evtVnd2Raw = await this.readRegisterValue(baseAddr + 32, 2, 'uint32');
|
|
992
|
+
const vRaw = await this.readRegisterValue(baseAddr + 34, 1, 'uint16');
|
|
993
|
+
const vMaxRaw = await this.readRegisterValue(baseAddr + 35, 1, 'uint16');
|
|
994
|
+
const vMinRaw = await this.readRegisterValue(baseAddr + 36, 1, 'uint16');
|
|
995
|
+
const cellVMaxRaw = await this.readRegisterValue(baseAddr + 37, 1, 'uint16');
|
|
996
|
+
const cellVMaxStrRaw = await this.readRegisterValue(baseAddr + 38, 1, 'uint16');
|
|
997
|
+
const cellVMaxModRaw = await this.readRegisterValue(baseAddr + 39, 1, 'uint16');
|
|
998
|
+
const cellVMinRaw = await this.readRegisterValue(baseAddr + 40, 1, 'uint16');
|
|
999
|
+
const cellVMinStrRaw = await this.readRegisterValue(baseAddr + 41, 1, 'uint16');
|
|
1000
|
+
const cellVMinModRaw = await this.readRegisterValue(baseAddr + 42, 1, 'uint16');
|
|
1001
|
+
const cellVAvgRaw = await this.readRegisterValue(baseAddr + 43, 1, 'uint16');
|
|
1002
|
+
const aRaw = await this.readRegisterValue(baseAddr + 44, 1, 'int16');
|
|
1003
|
+
const aChaMaxRaw = await this.readRegisterValue(baseAddr + 45, 1, 'uint16');
|
|
1004
|
+
const aDisChaMaxRaw = await this.readRegisterValue(baseAddr + 46, 1, 'uint16');
|
|
1005
|
+
const wRaw = await this.readRegisterValue(baseAddr + 47, 1, 'int16');
|
|
1006
|
+
const reqInvStateRaw = await this.readRegisterValue(baseAddr + 48, 1, 'uint16');
|
|
1007
|
+
const reqWRaw = await this.readRegisterValue(baseAddr + 49, 1, 'int16');
|
|
1008
|
+
const setOpRaw = await this.readRegisterValue(baseAddr + 50, 1, 'uint16');
|
|
1009
|
+
const setInvStateRaw = await this.readRegisterValue(baseAddr + 51, 1, 'uint16');
|
|
1010
|
+
// Map enum fields
|
|
1011
|
+
const chaStName = this.mapBatteryChargeState(chaStRaw);
|
|
1012
|
+
const typName = this.mapBatteryType(typRaw);
|
|
1013
|
+
const stateName = this.mapBatteryBankState(stateRaw);
|
|
1014
|
+
// Log non-scaled fields
|
|
1015
|
+
this.logRegisterRead(802, 14, 'nCyc', nCycRaw, 'uint32');
|
|
1016
|
+
this.logRegisterRead(802, 16, 'chaSt', chaStRaw, 'enum16');
|
|
1017
|
+
this.logRegisterRead(802, 17, 'locRemCtl', locRemCtlRaw, 'enum16');
|
|
1018
|
+
this.logRegisterRead(802, 21, 'typ', typRaw, 'enum16');
|
|
1019
|
+
this.logRegisterRead(802, 22, 'state', stateRaw, 'enum16');
|
|
1020
|
+
this.logRegisterRead(802, 26, 'evt1', evt1Raw, 'bitfield32');
|
|
1021
|
+
this.logRegisterRead(802, 28, 'evt2', evt2Raw, 'bitfield32');
|
|
1022
|
+
this.logRegisterRead(802, 30, 'evtVnd1', evtVnd1Raw, 'bitfield32');
|
|
1023
|
+
this.logRegisterRead(802, 32, 'evtVnd2', evtVnd2Raw, 'bitfield32');
|
|
1024
|
+
this.logRegisterRead(802, 38, 'cellVMaxStr', cellVMaxStrRaw, 'uint16');
|
|
1025
|
+
this.logRegisterRead(802, 39, 'cellVMaxMod', cellVMaxModRaw, 'uint16');
|
|
1026
|
+
this.logRegisterRead(802, 41, 'cellVMinStr', cellVMinStrRaw, 'uint16');
|
|
1027
|
+
this.logRegisterRead(802, 42, 'cellVMinMod', cellVMinModRaw, 'uint16');
|
|
1028
|
+
this.logRegisterRead(802, 48, 'reqInvState', reqInvStateRaw, 'enum16');
|
|
1029
|
+
this.logRegisterRead(802, 50, 'setOp', setOpRaw, 'enum16');
|
|
1030
|
+
this.logRegisterRead(802, 51, 'setInvState', setInvStateRaw, 'enum16');
|
|
1031
|
+
const data = {
|
|
1032
|
+
blockNumber: 802,
|
|
1033
|
+
blockAddress: model.address,
|
|
1034
|
+
blockLength: model.length,
|
|
1035
|
+
// Nameplate
|
|
1036
|
+
ahRtg: this.applyScaleFactor(ahRtgRaw, sf.AHRtg_SF, 'uint16', 'AH Rating', 2, 802),
|
|
1037
|
+
whRtg: this.applyScaleFactor(whRtgRaw, sf.WHRtg_SF, 'uint16', 'WH Rating', 3, 802),
|
|
1038
|
+
wChaRteMax: this.applyScaleFactor(wChaRteMaxRaw, sf.WChaDisChaMax_SF, 'uint16', 'Max Charge Rate', 4, 802),
|
|
1039
|
+
wDisChaRteMax: this.applyScaleFactor(wDisChaRteMaxRaw, sf.WChaDisChaMax_SF, 'uint16', 'Max Discharge Rate', 5, 802),
|
|
1040
|
+
// SoC/Health
|
|
1041
|
+
disChaRte: this.applyScaleFactor(disChaRteRaw, sf.DisChaRte_SF, 'uint16', 'Self Discharge Rate', 6, 802),
|
|
1042
|
+
soCMax: this.applyScaleFactor(soCMaxRaw, sf.SoC_SF, 'uint16', 'SoC Max', 7, 802),
|
|
1043
|
+
soCMin: this.applyScaleFactor(soCMinRaw, sf.SoC_SF, 'uint16', 'SoC Min', 8, 802),
|
|
1044
|
+
soCRsvMax: this.applyScaleFactor(soCRsvMaxRaw, sf.SoC_SF, 'uint16', 'SoC Reserve Max', 9, 802),
|
|
1045
|
+
soCRsvMin: this.applyScaleFactor(soCRsvMinRaw, sf.SoC_SF, 'uint16', 'SoC Reserve Min', 10, 802),
|
|
1046
|
+
soC: this.applyScaleFactor(soCRaw, sf.SoC_SF, 'uint16', 'State of Charge', 11, 802),
|
|
1047
|
+
doD: this.applyScaleFactor(doDRaw, sf.DoD_SF, 'uint16', 'Depth of Discharge', 12, 802),
|
|
1048
|
+
soH: this.applyScaleFactor(soHRaw, sf.SoH_SF, 'uint16', 'State of Health', 13, 802),
|
|
1049
|
+
// Status
|
|
1050
|
+
nCyc: !this.isUnimplementedValue(nCycRaw, 'uint32') ? nCycRaw : undefined,
|
|
1051
|
+
chaSt: !this.isUnimplementedValue(chaStRaw, 'enum16') ? chaStRaw : undefined,
|
|
1052
|
+
chaStName,
|
|
1053
|
+
locRemCtl: !this.isUnimplementedValue(locRemCtlRaw, 'enum16') ? locRemCtlRaw : undefined,
|
|
1054
|
+
typ: !this.isUnimplementedValue(typRaw, 'enum16') ? typRaw : undefined,
|
|
1055
|
+
typName,
|
|
1056
|
+
state: !this.isUnimplementedValue(stateRaw, 'enum16') ? stateRaw : undefined,
|
|
1057
|
+
stateName,
|
|
1058
|
+
// Events
|
|
1059
|
+
evt1: !this.isUnimplementedValue(evt1Raw, 'bitfield32') ? evt1Raw : undefined,
|
|
1060
|
+
evt2: !this.isUnimplementedValue(evt2Raw, 'bitfield32') ? evt2Raw : undefined,
|
|
1061
|
+
evtVnd1: !this.isUnimplementedValue(evtVnd1Raw, 'bitfield32') ? evtVnd1Raw : undefined,
|
|
1062
|
+
evtVnd2: !this.isUnimplementedValue(evtVnd2Raw, 'bitfield32') ? evtVnd2Raw : undefined,
|
|
1063
|
+
// Voltage
|
|
1064
|
+
v: this.applyScaleFactor(vRaw, sf.V_SF, 'uint16', 'Battery Voltage', 34, 802),
|
|
1065
|
+
vMax: this.applyScaleFactor(vMaxRaw, sf.V_SF, 'uint16', 'Max Battery Voltage', 35, 802),
|
|
1066
|
+
vMin: this.applyScaleFactor(vMinRaw, sf.V_SF, 'uint16', 'Min Battery Voltage', 36, 802),
|
|
1067
|
+
cellVMax: this.applyScaleFactor(cellVMaxRaw, sf.CellV_SF, 'uint16', 'Max Cell Voltage', 37, 802),
|
|
1068
|
+
cellVMaxStr: !this.isUnimplementedValue(cellVMaxStrRaw, 'uint16') ? cellVMaxStrRaw : undefined,
|
|
1069
|
+
cellVMaxMod: !this.isUnimplementedValue(cellVMaxModRaw, 'uint16') ? cellVMaxModRaw : undefined,
|
|
1070
|
+
cellVMin: this.applyScaleFactor(cellVMinRaw, sf.CellV_SF, 'uint16', 'Min Cell Voltage', 40, 802),
|
|
1071
|
+
cellVMinStr: !this.isUnimplementedValue(cellVMinStrRaw, 'uint16') ? cellVMinStrRaw : undefined,
|
|
1072
|
+
cellVMinMod: !this.isUnimplementedValue(cellVMinModRaw, 'uint16') ? cellVMinModRaw : undefined,
|
|
1073
|
+
cellVAvg: this.applyScaleFactor(cellVAvgRaw, sf.CellV_SF, 'uint16', 'Avg Cell Voltage', 43, 802),
|
|
1074
|
+
// Current
|
|
1075
|
+
a: this.applyScaleFactor(aRaw, sf.A_SF, 'int16', 'Battery Current', 44, 802),
|
|
1076
|
+
aChaMax: this.applyScaleFactor(aChaMaxRaw, sf.AMax_SF, 'uint16', 'Max Charge Current', 45, 802),
|
|
1077
|
+
aDisChaMax: this.applyScaleFactor(aDisChaMaxRaw, sf.AMax_SF, 'uint16', 'Max Discharge Current', 46, 802),
|
|
1078
|
+
// Power
|
|
1079
|
+
w: this.applyScaleFactor(wRaw, sf.W_SF, 'int16', 'Battery Power', 47, 802),
|
|
1080
|
+
reqInvState: !this.isUnimplementedValue(reqInvStateRaw, 'enum16') ? reqInvStateRaw : undefined,
|
|
1081
|
+
reqW: this.applyScaleFactor(reqWRaw, sf.W_SF, 'int16', 'Requested Power', 49, 802),
|
|
1082
|
+
setOp: !this.isUnimplementedValue(setOpRaw, 'enum16') ? setOpRaw : undefined,
|
|
1083
|
+
setInvState: !this.isUnimplementedValue(setInvStateRaw, 'enum16') ? setInvStateRaw : undefined,
|
|
1084
|
+
};
|
|
1085
|
+
return data;
|
|
1086
|
+
}
|
|
1087
|
+
catch (error) {
|
|
1088
|
+
console.error(`Error reading battery base data: ${error}`);
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Read battery data from Model 124 (Basic Storage) with fallback to Model 802 / Model 803
|
|
908
1094
|
*/
|
|
909
1095
|
async readBatteryData() {
|
|
910
1096
|
// Try Model 124 first (Basic Storage Controls)
|
|
@@ -937,7 +1123,14 @@ class SunspecModbusClient {
|
|
|
937
1123
|
InBatV_SF: await this.readRegisterValue(baseAddr + 24, 1, 'int16'),
|
|
938
1124
|
InOutWRte_SF: await this.readRegisterValue(baseAddr + 25, 1, 'int16')
|
|
939
1125
|
};
|
|
940
|
-
|
|
1126
|
+
this.logRegisterRead(124, 18, 'WChaMax_SF', scaleFactors.WChaMax_SF, 'int16');
|
|
1127
|
+
this.logRegisterRead(124, 19, 'WChaDisChaGra_SF', scaleFactors.WChaDisChaGra_SF, 'int16');
|
|
1128
|
+
this.logRegisterRead(124, 20, 'VAChaMax_SF', scaleFactors.VAChaMax_SF, 'int16');
|
|
1129
|
+
this.logRegisterRead(124, 21, 'MinRsvPct_SF', scaleFactors.MinRsvPct_SF, 'int16');
|
|
1130
|
+
this.logRegisterRead(124, 22, 'ChaState_SF', scaleFactors.ChaState_SF, 'int16');
|
|
1131
|
+
this.logRegisterRead(124, 23, 'StorAval_SF', scaleFactors.StorAval_SF, 'int16');
|
|
1132
|
+
this.logRegisterRead(124, 24, 'InBatV_SF', scaleFactors.InBatV_SF, 'int16');
|
|
1133
|
+
this.logRegisterRead(124, 25, 'InOutWRte_SF', scaleFactors.InOutWRte_SF, 'int16');
|
|
941
1134
|
// Read raw values
|
|
942
1135
|
const wChaMaxRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
943
1136
|
const wChaGraRaw = await this.readRegisterValue(baseAddr + 3, 1, 'uint16');
|
|
@@ -955,43 +1148,42 @@ class SunspecModbusClient {
|
|
|
955
1148
|
const inOutWRteRvrtTmsRaw = await this.readRegisterValue(baseAddr + 15, 1, 'uint16');
|
|
956
1149
|
const inOutWRteRmpTmsRaw = await this.readRegisterValue(baseAddr + 16, 1, 'uint16');
|
|
957
1150
|
const chaGriSetRaw = await this.readRegisterValue(baseAddr + 17, 1, 'uint16');
|
|
958
|
-
// Map charge state
|
|
1151
|
+
// Map charge state and log non-scaled fields
|
|
959
1152
|
const chaStName = this.mapBatteryChargeState(chaStRaw);
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
}
|
|
1153
|
+
this.logRegisterRead(124, 5, 'storCtlMod', storCtlModRaw, 'bitfield16');
|
|
1154
|
+
this.logRegisterRead(124, 11, 'chaSt', chaStRaw, 'enum16');
|
|
1155
|
+
this.logRegisterRead(124, 14, 'inOutWRteWinTms', inOutWRteWinTmsRaw, 'uint16');
|
|
1156
|
+
this.logRegisterRead(124, 15, 'inOutWRteRvrtTms', inOutWRteRvrtTmsRaw, 'uint16');
|
|
1157
|
+
this.logRegisterRead(124, 16, 'inOutWRteRmpTms', inOutWRteRmpTmsRaw, 'uint16');
|
|
1158
|
+
this.logRegisterRead(124, 17, 'chaGriSet', chaGriSetRaw, 'enum16');
|
|
967
1159
|
// Calculate actual values with scale factors
|
|
968
1160
|
const data = {
|
|
969
1161
|
blockNumber: 124,
|
|
970
1162
|
blockAddress: model.address,
|
|
971
1163
|
blockLength: model.length,
|
|
972
1164
|
// Control settings
|
|
973
|
-
wChaMax: this.applyScaleFactor(wChaMaxRaw, scaleFactors.WChaMax_SF, 'uint16', 'Max Charge Power'),
|
|
1165
|
+
wChaMax: this.applyScaleFactor(wChaMaxRaw, scaleFactors.WChaMax_SF, 'uint16', 'Max Charge Power', 2, 124),
|
|
974
1166
|
wChaMaxSF: scaleFactors.WChaMax_SF,
|
|
975
|
-
wChaGra: this.applyScaleFactor(wChaGraRaw, scaleFactors.WChaDisChaGra_SF, 'uint16', 'Charge Rate Gradient'),
|
|
976
|
-
wDisChaGra: this.applyScaleFactor(wDisChaGraRaw, scaleFactors.WChaDisChaGra_SF, 'uint16', 'Discharge Rate Gradient'),
|
|
1167
|
+
wChaGra: this.applyScaleFactor(wChaGraRaw, scaleFactors.WChaDisChaGra_SF, 'uint16', 'Charge Rate Gradient', 3, 124),
|
|
1168
|
+
wDisChaGra: this.applyScaleFactor(wDisChaGraRaw, scaleFactors.WChaDisChaGra_SF, 'uint16', 'Discharge Rate Gradient', 4, 124),
|
|
977
1169
|
wChaDisChaGraSF: scaleFactors.WChaDisChaGra_SF,
|
|
978
1170
|
storCtlMod: !this.isUnimplementedValue(storCtlModRaw, 'bitfield16') ? storCtlModRaw : undefined,
|
|
979
|
-
vaChaMax: this.applyScaleFactor(vaChaMaxRaw, scaleFactors.VAChaMax_SF, 'uint16', 'Max Charging VA'),
|
|
1171
|
+
vaChaMax: this.applyScaleFactor(vaChaMaxRaw, scaleFactors.VAChaMax_SF, 'uint16', 'Max Charging VA', 6, 124),
|
|
980
1172
|
vaChaMaxSF: scaleFactors.VAChaMax_SF,
|
|
981
|
-
minRsvPct: this.applyScaleFactor(minRsvPctRaw, scaleFactors.MinRsvPct_SF, 'uint16', 'Min Reserve Percent'),
|
|
1173
|
+
minRsvPct: this.applyScaleFactor(minRsvPctRaw, scaleFactors.MinRsvPct_SF, 'uint16', 'Min Reserve Percent', 7, 124),
|
|
982
1174
|
minRsvPctSF: scaleFactors.MinRsvPct_SF,
|
|
983
1175
|
// Status values
|
|
984
|
-
chaState: this.applyScaleFactor(chaStateRaw, scaleFactors.ChaState_SF, 'uint16', 'State of Charge'),
|
|
1176
|
+
chaState: this.applyScaleFactor(chaStateRaw, scaleFactors.ChaState_SF, 'uint16', 'State of Charge', 8, 124),
|
|
985
1177
|
chaStateSF: scaleFactors.ChaState_SF,
|
|
986
|
-
storAval: this.applyScaleFactor(storAvalRaw, scaleFactors.StorAval_SF, 'uint16', 'Available Storage'),
|
|
1178
|
+
storAval: this.applyScaleFactor(storAvalRaw, scaleFactors.StorAval_SF, 'uint16', 'Available Storage', 9, 124),
|
|
987
1179
|
storAvalSF: scaleFactors.StorAval_SF,
|
|
988
|
-
inBatV: this.applyScaleFactor(inBatVRaw, scaleFactors.InBatV_SF, 'uint16', 'Battery Voltage'),
|
|
1180
|
+
inBatV: this.applyScaleFactor(inBatVRaw, scaleFactors.InBatV_SF, 'uint16', 'Battery Voltage', 10, 124),
|
|
989
1181
|
inBatVSF: scaleFactors.InBatV_SF,
|
|
990
1182
|
chaSt: !this.isUnimplementedValue(chaStRaw, 'enum16') ? chaStRaw : undefined,
|
|
991
1183
|
chaStName: chaStName,
|
|
992
1184
|
// Rate control
|
|
993
|
-
outWRte: this.applyScaleFactor(outWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Discharge Rate'),
|
|
994
|
-
inWRte: this.applyScaleFactor(inWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Charge Rate'),
|
|
1185
|
+
outWRte: this.applyScaleFactor(outWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Discharge Rate', 12, 124),
|
|
1186
|
+
inWRte: this.applyScaleFactor(inWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Charge Rate', 13, 124),
|
|
995
1187
|
inOutWRteSF: scaleFactors.InOutWRte_SF,
|
|
996
1188
|
// Timing parameters
|
|
997
1189
|
inOutWRteWinTms: !this.isUnimplementedValue(inOutWRteWinTmsRaw, 'uint16') ? inOutWRteWinTmsRaw : undefined,
|
|
@@ -1016,8 +1208,43 @@ class SunspecModbusClient {
|
|
|
1016
1208
|
}
|
|
1017
1209
|
return data;
|
|
1018
1210
|
}
|
|
1211
|
+
else if (model.id === 802) {
|
|
1212
|
+
// Model 802: Battery Base
|
|
1213
|
+
console.log('Using Model 802 (Battery Base)');
|
|
1214
|
+
const baseData = await this.readBatteryBaseData();
|
|
1215
|
+
if (!baseData) {
|
|
1216
|
+
return null;
|
|
1217
|
+
}
|
|
1218
|
+
// Derive charge/discharge power from w field
|
|
1219
|
+
let chargePower;
|
|
1220
|
+
let dischargePower;
|
|
1221
|
+
if (baseData.w !== undefined) {
|
|
1222
|
+
if (baseData.w >= 0) {
|
|
1223
|
+
chargePower = baseData.w;
|
|
1224
|
+
dischargePower = 0;
|
|
1225
|
+
}
|
|
1226
|
+
else {
|
|
1227
|
+
chargePower = 0;
|
|
1228
|
+
dischargePower = Math.abs(baseData.w);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
blockNumber: 802,
|
|
1233
|
+
blockAddress: model.address,
|
|
1234
|
+
blockLength: model.length,
|
|
1235
|
+
soc: baseData.soC,
|
|
1236
|
+
soh: baseData.soH,
|
|
1237
|
+
voltage: baseData.v,
|
|
1238
|
+
current: baseData.a,
|
|
1239
|
+
chaSt: baseData.chaSt,
|
|
1240
|
+
chaStName: baseData.chaStName,
|
|
1241
|
+
status: baseData.state,
|
|
1242
|
+
chargePower,
|
|
1243
|
+
dischargePower,
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1019
1246
|
else {
|
|
1020
|
-
// Handle other battery models (
|
|
1247
|
+
// Handle other battery models (803) if needed
|
|
1021
1248
|
console.log(`Battery Model ${model.id} reading not yet implemented`);
|
|
1022
1249
|
return {
|
|
1023
1250
|
blockNumber: model.id,
|
|
@@ -1131,7 +1358,7 @@ class SunspecModbusClient {
|
|
|
1131
1358
|
return this.writeBatteryControls({ chaGriSet });
|
|
1132
1359
|
}
|
|
1133
1360
|
/**
|
|
1134
|
-
* Read
|
|
1361
|
+
* Read battery control settings from Model 124 (Basic Storage Controls)
|
|
1135
1362
|
*/
|
|
1136
1363
|
async readBatteryControls() {
|
|
1137
1364
|
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Battery);
|
|
@@ -1148,6 +1375,9 @@ class SunspecModbusClient {
|
|
|
1148
1375
|
MinRsvPct_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16'),
|
|
1149
1376
|
InOutWRte_SF: await this.readRegisterValue(baseAddr + 25, 1, 'int16')
|
|
1150
1377
|
};
|
|
1378
|
+
this.logRegisterRead(124, 18, 'WChaMax_SF', scaleFactors.WChaMax_SF, 'int16');
|
|
1379
|
+
this.logRegisterRead(124, 21, 'MinRsvPct_SF', scaleFactors.MinRsvPct_SF, 'int16');
|
|
1380
|
+
this.logRegisterRead(124, 25, 'InOutWRte_SF', scaleFactors.InOutWRte_SF, 'int16');
|
|
1151
1381
|
// Read raw values
|
|
1152
1382
|
const wChaMaxRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
1153
1383
|
const storCtlModRaw = await this.readRegisterValue(baseAddr + 5, 1, 'uint16');
|
|
@@ -1155,14 +1385,16 @@ class SunspecModbusClient {
|
|
|
1155
1385
|
const outWRteRaw = await this.readRegisterValue(baseAddr + 12, 1, 'int16');
|
|
1156
1386
|
const inWRteRaw = await this.readRegisterValue(baseAddr + 13, 1, 'int16');
|
|
1157
1387
|
const chaGriSetRaw = await this.readRegisterValue(baseAddr + 17, 1, 'uint16');
|
|
1388
|
+
this.logRegisterRead(124, 5, 'storCtlMod', storCtlModRaw, 'bitfield16');
|
|
1389
|
+
this.logRegisterRead(124, 17, 'chaGriSet', chaGriSetRaw, 'enum16');
|
|
1158
1390
|
// Apply scale factors and return control settings
|
|
1159
1391
|
return {
|
|
1160
1392
|
storCtlMod: !this.isUnimplementedValue(storCtlModRaw, 'bitfield16') ? storCtlModRaw : undefined,
|
|
1161
1393
|
chaGriSet: !this.isUnimplementedValue(chaGriSetRaw, 'enum16') ? chaGriSetRaw : undefined,
|
|
1162
|
-
wChaMax: this.applyScaleFactor(wChaMaxRaw, scaleFactors.WChaMax_SF, 'uint16'),
|
|
1163
|
-
inWRte: this.applyScaleFactor(inWRteRaw, scaleFactors.InOutWRte_SF, 'int16'),
|
|
1164
|
-
outWRte: this.applyScaleFactor(outWRteRaw, scaleFactors.InOutWRte_SF, 'int16'),
|
|
1165
|
-
minRsvPct: this.applyScaleFactor(minRsvPctRaw, scaleFactors.MinRsvPct_SF, 'uint16')
|
|
1394
|
+
wChaMax: this.applyScaleFactor(wChaMaxRaw, scaleFactors.WChaMax_SF, 'uint16', 'Max Charge Power', 2, 124),
|
|
1395
|
+
inWRte: this.applyScaleFactor(inWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Charge Rate', 13, 124),
|
|
1396
|
+
outWRte: this.applyScaleFactor(outWRteRaw, scaleFactors.InOutWRte_SF, 'int16', 'Discharge Rate', 12, 124),
|
|
1397
|
+
minRsvPct: this.applyScaleFactor(minRsvPctRaw, scaleFactors.MinRsvPct_SF, 'uint16', 'Min Reserve Percent', 7, 124)
|
|
1166
1398
|
};
|
|
1167
1399
|
}
|
|
1168
1400
|
catch (error) {
|
|
@@ -1171,7 +1403,7 @@ class SunspecModbusClient {
|
|
|
1171
1403
|
}
|
|
1172
1404
|
}
|
|
1173
1405
|
/**
|
|
1174
|
-
* Read meter data (Model 203
|
|
1406
|
+
* Read meter data from Model 201 (Single Phase) / Model 203 (Three Phase) / Model 204 (Split Phase)
|
|
1175
1407
|
*/
|
|
1176
1408
|
async readMeterData() {
|
|
1177
1409
|
let model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Meter3Phase);
|
|
@@ -1233,25 +1465,21 @@ class SunspecModbusClient {
|
|
|
1233
1465
|
const powerSF = await this.readRegisterValue(baseAddr + powerSFOffset, 1, 'int16');
|
|
1234
1466
|
const freqSF = await this.readRegisterValue(baseAddr + freqSFOffset, 1, 'int16');
|
|
1235
1467
|
const energySF = await this.readRegisterValue(baseAddr + energySFOffset, 1, 'int16');
|
|
1236
|
-
|
|
1468
|
+
this.logRegisterRead(model.id, powerSFOffset, 'W_SF', powerSF, 'int16');
|
|
1469
|
+
this.logRegisterRead(model.id, freqSFOffset, 'Hz_SF', freqSF, 'int16');
|
|
1470
|
+
this.logRegisterRead(model.id, energySFOffset, 'TotWh_SF', energySF, 'int16');
|
|
1237
1471
|
// Read raw values
|
|
1238
|
-
const
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
const exportAddr = baseAddr + exportOffset;
|
|
1245
|
-
const exportRaw = await this.readRegisterValue(exportAddr, 2, 'uint32');
|
|
1246
|
-
console.log(`Meter Export Energy: address=${exportAddr}, raw=0x${exportRaw.toString(16).toUpperCase()} (${exportRaw}), SF=${energySF}`);
|
|
1247
|
-
const importAddr = baseAddr + importOffset;
|
|
1248
|
-
const importRaw = await this.readRegisterValue(importAddr, 2, 'uint32');
|
|
1249
|
-
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
1472
|
+
const powerRaw = await this.readRegisterValue(baseAddr + powerOffset, 1, 'int16');
|
|
1473
|
+
const freqRaw = await this.readRegisterValue(baseAddr + freqOffset, 1, 'uint16');
|
|
1474
|
+
const exportRaw = await this.readRegisterValue(baseAddr + exportOffset, 2, 'uint32');
|
|
1475
|
+
const importRaw = await this.readRegisterValue(baseAddr + importOffset, 2, 'uint32');
|
|
1476
|
+
this.logRegisterRead(model.id, exportOffset, 'TotWhExp', exportRaw, 'acc32');
|
|
1477
|
+
this.logRegisterRead(model.id, importOffset, 'TotWhImp', importRaw, 'acc32');
|
|
1250
1478
|
// Calculate final values with scale factors
|
|
1251
|
-
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16', '
|
|
1252
|
-
const frequency = this.applyScaleFactor(freqRaw, freqSF, 'uint16', '
|
|
1253
|
-
const exportedEnergy = this.applyScaleFactor(exportRaw, energySF, "acc32");
|
|
1254
|
-
const importedEnergy = this.applyScaleFactor(importRaw, energySF, "acc32");
|
|
1479
|
+
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16', 'Total Power', powerOffset, model.id);
|
|
1480
|
+
const frequency = this.applyScaleFactor(freqRaw, freqSF, 'uint16', 'Frequency', freqOffset, model.id);
|
|
1481
|
+
const exportedEnergy = this.applyScaleFactor(exportRaw, energySF, "acc32", 'Exported Energy', exportOffset, model.id);
|
|
1482
|
+
const importedEnergy = this.applyScaleFactor(importRaw, energySF, "acc32", 'Imported Energy', importOffset, model.id);
|
|
1255
1483
|
return {
|
|
1256
1484
|
blockNumber: model.id,
|
|
1257
1485
|
totalPower,
|
|
@@ -1285,30 +1513,26 @@ class SunspecModbusClient {
|
|
|
1285
1513
|
const versionAddr = baseAddr + 40; // Offset 40-47 (8 registers) from data start
|
|
1286
1514
|
const serialAddr = baseAddr + 48; // Offset 48-63 (16 registers) from data start
|
|
1287
1515
|
const deviceAddrAddr = baseAddr + 64; // Offset 64 from data start
|
|
1288
|
-
console.log(`Reading manufacturer from address ${manufacturerAddr} (16 registers)`);
|
|
1289
1516
|
const manufacturer = await this.readRegisterValue(manufacturerAddr, 16, 'string');
|
|
1290
|
-
|
|
1291
|
-
console.log(`Reading model from address ${modelAddr} (16 registers)`);
|
|
1517
|
+
this.logRegisterRead(1, 0, 'Manufacturer', manufacturer, 'string');
|
|
1292
1518
|
const modelName = await this.readRegisterValue(modelAddr, 16, 'string');
|
|
1293
|
-
|
|
1294
|
-
console.log(`Reading options from address ${optionsAddr} (8 registers)`);
|
|
1519
|
+
this.logRegisterRead(1, 16, 'Model', modelName, 'string');
|
|
1295
1520
|
const options = await this.readRegisterValue(optionsAddr, 8, 'string');
|
|
1296
|
-
|
|
1521
|
+
this.logRegisterRead(1, 32, 'Options', options, 'string');
|
|
1297
1522
|
const version = await this.readRegisterValue(versionAddr, 8, 'string');
|
|
1298
|
-
|
|
1523
|
+
this.logRegisterRead(1, 40, 'Version', version, 'string');
|
|
1299
1524
|
const serialNumber = await this.readRegisterValue(serialAddr, 16, 'string');
|
|
1300
|
-
|
|
1525
|
+
this.logRegisterRead(1, 48, 'Serial Number', serialNumber, 'string');
|
|
1301
1526
|
const deviceAddress = await this.readRegisterValue(deviceAddrAddr, 1, 'uint16');
|
|
1302
|
-
|
|
1303
|
-
|
|
1527
|
+
this.logRegisterRead(1, 64, 'Device Address', deviceAddress, 'uint16');
|
|
1528
|
+
return {
|
|
1529
|
+
manufacturer,
|
|
1304
1530
|
model: modelName,
|
|
1305
|
-
options
|
|
1306
|
-
version
|
|
1307
|
-
serialNumber
|
|
1308
|
-
deviceAddress
|
|
1531
|
+
options,
|
|
1532
|
+
version,
|
|
1533
|
+
serialNumber,
|
|
1534
|
+
deviceAddress
|
|
1309
1535
|
};
|
|
1310
|
-
console.log('Common Block Data:', JSON.stringify(result, null, 2));
|
|
1311
|
-
return result;
|
|
1312
1536
|
}
|
|
1313
1537
|
catch (error) {
|
|
1314
1538
|
console.error(`Error reading common block: ${error}`);
|
|
@@ -1341,7 +1565,7 @@ class SunspecModbusClient {
|
|
|
1341
1565
|
return this.connectionHealth;
|
|
1342
1566
|
}
|
|
1343
1567
|
/**
|
|
1344
|
-
* Read
|
|
1568
|
+
* Read inverter settings from Model 121 (Inverter Settings)
|
|
1345
1569
|
*/
|
|
1346
1570
|
async readInverterSettings() {
|
|
1347
1571
|
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Settings);
|
|
@@ -1364,47 +1588,64 @@ class SunspecModbusClient {
|
|
|
1364
1588
|
MaxRmpRte_SF: await this.readRegisterValue(baseAddr + 30, 1, 'int16'),
|
|
1365
1589
|
ECPNomHz_SF: await this.readRegisterValue(baseAddr + 31, 1, 'int16')
|
|
1366
1590
|
};
|
|
1591
|
+
this.logRegisterRead(121, 22, 'WMax_SF', scaleFactors.WMax_SF, 'int16');
|
|
1592
|
+
this.logRegisterRead(121, 23, 'VRef_SF', scaleFactors.VRef_SF, 'int16');
|
|
1593
|
+
this.logRegisterRead(121, 24, 'VRefOfs_SF', scaleFactors.VRefOfs_SF, 'int16');
|
|
1594
|
+
this.logRegisterRead(121, 25, 'VMinMax_SF', scaleFactors.VMinMax_SF, 'int16');
|
|
1595
|
+
this.logRegisterRead(121, 26, 'VAMax_SF', scaleFactors.VAMax_SF, 'int16');
|
|
1596
|
+
this.logRegisterRead(121, 27, 'VArMax_SF', scaleFactors.VArMax_SF, 'int16');
|
|
1597
|
+
this.logRegisterRead(121, 28, 'WGra_SF', scaleFactors.WGra_SF, 'int16');
|
|
1598
|
+
this.logRegisterRead(121, 29, 'PFMin_SF', scaleFactors.PFMin_SF, 'int16');
|
|
1599
|
+
this.logRegisterRead(121, 30, 'MaxRmpRte_SF', scaleFactors.MaxRmpRte_SF, 'int16');
|
|
1600
|
+
this.logRegisterRead(121, 31, 'ECPNomHz_SF', scaleFactors.ECPNomHz_SF, 'int16');
|
|
1601
|
+
// Read non-scaled fields first for logging
|
|
1602
|
+
const vArActRaw = await this.readRegisterValue(baseAddr + 17, 1, 'uint16');
|
|
1603
|
+
const clcTotVARaw = await this.readRegisterValue(baseAddr + 18, 1, 'uint16');
|
|
1604
|
+
const connPhRaw = await this.readRegisterValue(baseAddr + 21, 1, 'uint16');
|
|
1605
|
+
this.logRegisterRead(121, 17, 'VArAct', vArActRaw, 'enum16');
|
|
1606
|
+
this.logRegisterRead(121, 18, 'ClcTotVA', clcTotVARaw, 'enum16');
|
|
1607
|
+
this.logRegisterRead(121, 21, 'ConnPh', connPhRaw, 'enum16');
|
|
1367
1608
|
return {
|
|
1368
1609
|
blockNumber: 121,
|
|
1369
1610
|
blockAddress: model.address,
|
|
1370
1611
|
blockLength: model.length,
|
|
1371
1612
|
// Power settings - Offset 2
|
|
1372
|
-
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF, 'uint16', 'Max Power'),
|
|
1613
|
+
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF, 'uint16', 'Max Power', 2, 121),
|
|
1373
1614
|
WMax_SF: scaleFactors.WMax_SF,
|
|
1374
1615
|
// Voltage settings - Offsets 3-6
|
|
1375
|
-
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF, 'uint16', 'Voltage Reference'),
|
|
1616
|
+
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF, 'uint16', 'Voltage Reference', 3, 121),
|
|
1376
1617
|
VRef_SF: scaleFactors.VRef_SF,
|
|
1377
|
-
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF, 'int16', 'Voltage Reference Offset'),
|
|
1618
|
+
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF, 'int16', 'Voltage Reference Offset', 4, 121),
|
|
1378
1619
|
VRefOfs_SF: scaleFactors.VRefOfs_SF,
|
|
1379
|
-
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Max Voltage'),
|
|
1380
|
-
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Min Voltage'),
|
|
1620
|
+
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Max Voltage', 5, 121),
|
|
1621
|
+
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Min Voltage', 6, 121),
|
|
1381
1622
|
VMinMax_SF: scaleFactors.VMinMax_SF,
|
|
1382
1623
|
// Apparent power settings - Offset 7
|
|
1383
|
-
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF, 'uint16', 'Max Apparent Power'),
|
|
1624
|
+
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF, 'uint16', 'Max Apparent Power', 7, 121),
|
|
1384
1625
|
VAMax_SF: scaleFactors.VAMax_SF,
|
|
1385
1626
|
// Reactive power settings - Offsets 8-11
|
|
1386
|
-
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q1'),
|
|
1387
|
-
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q2'),
|
|
1388
|
-
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q3'),
|
|
1389
|
-
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q4'),
|
|
1627
|
+
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q1', 8, 121),
|
|
1628
|
+
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q2', 9, 121),
|
|
1629
|
+
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q3', 10, 121),
|
|
1630
|
+
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q4', 11, 121),
|
|
1390
1631
|
VArMax_SF: scaleFactors.VArMax_SF,
|
|
1391
1632
|
// Ramp rate settings - Offset 12
|
|
1392
|
-
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF, 'uint16', 'Power Ramp Rate'),
|
|
1633
|
+
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF, 'uint16', 'Power Ramp Rate', 12, 121),
|
|
1393
1634
|
WGra_SF: scaleFactors.WGra_SF,
|
|
1394
1635
|
// Power factor settings - Offsets 13-16
|
|
1395
|
-
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q1'),
|
|
1396
|
-
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q2'),
|
|
1397
|
-
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q3'),
|
|
1398
|
-
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q4'),
|
|
1636
|
+
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q1', 13, 121),
|
|
1637
|
+
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q2', 14, 121),
|
|
1638
|
+
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q3', 15, 121),
|
|
1639
|
+
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q4', 16, 121),
|
|
1399
1640
|
PFMin_SF: scaleFactors.PFMin_SF,
|
|
1400
1641
|
// Other settings - Offsets 17-21
|
|
1401
|
-
VArAct:
|
|
1402
|
-
ClcTotVA:
|
|
1403
|
-
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF, 'uint16', 'Max Ramp Rate'),
|
|
1642
|
+
VArAct: vArActRaw,
|
|
1643
|
+
ClcTotVA: clcTotVARaw,
|
|
1644
|
+
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF, 'uint16', 'Max Ramp Rate', 19, 121),
|
|
1404
1645
|
MaxRmpRte_SF: scaleFactors.MaxRmpRte_SF,
|
|
1405
|
-
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF, 'uint16', 'Nominal Frequency'),
|
|
1646
|
+
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF, 'uint16', 'Nominal Frequency', 20, 121),
|
|
1406
1647
|
ECPNomHz_SF: scaleFactors.ECPNomHz_SF,
|
|
1407
|
-
ConnPh:
|
|
1648
|
+
ConnPh: connPhRaw
|
|
1408
1649
|
};
|
|
1409
1650
|
}
|
|
1410
1651
|
catch (error) {
|
|
@@ -1413,7 +1654,7 @@ class SunspecModbusClient {
|
|
|
1413
1654
|
}
|
|
1414
1655
|
}
|
|
1415
1656
|
/**
|
|
1416
|
-
* Read
|
|
1657
|
+
* Read inverter controls from Model 123 (Immediate Inverter Controls)
|
|
1417
1658
|
*/
|
|
1418
1659
|
async readInverterControls() {
|
|
1419
1660
|
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Controls);
|
|
@@ -1429,38 +1670,74 @@ class SunspecModbusClient {
|
|
|
1429
1670
|
OutPFSet_SF: await this.readRegisterValue(baseAddr + 22, 1, 'int16'),
|
|
1430
1671
|
VArPct_SF: await this.readRegisterValue(baseAddr + 23, 1, 'int16')
|
|
1431
1672
|
};
|
|
1673
|
+
this.logRegisterRead(123, 21, 'WMaxLimPct_SF', scaleFactors.WMaxLimPct_SF, 'int16');
|
|
1674
|
+
this.logRegisterRead(123, 22, 'OutPFSet_SF', scaleFactors.OutPFSet_SF, 'int16');
|
|
1675
|
+
this.logRegisterRead(123, 23, 'VArPct_SF', scaleFactors.VArPct_SF, 'int16');
|
|
1676
|
+
// Read non-scaled fields
|
|
1677
|
+
const connWinTmsRaw = await this.readRegisterValue(baseAddr, 1, 'uint16');
|
|
1678
|
+
const connRvrtTmsRaw = await this.readRegisterValue(baseAddr + 1, 1, 'uint16');
|
|
1679
|
+
const connRaw = await this.readRegisterValue(baseAddr + 2, 1, 'uint16');
|
|
1680
|
+
const wMaxLimPctWinTmsRaw = await this.readRegisterValue(baseAddr + 4, 1, 'uint16');
|
|
1681
|
+
const wMaxLimPctRvrtTmsRaw = await this.readRegisterValue(baseAddr + 5, 1, 'uint16');
|
|
1682
|
+
const wMaxLimPctRmpTmsRaw = await this.readRegisterValue(baseAddr + 6, 1, 'uint16');
|
|
1683
|
+
const wMaxLimEnaRaw = await this.readRegisterValue(baseAddr + 7, 1, 'uint16');
|
|
1684
|
+
const outPFSetWinTmsRaw = await this.readRegisterValue(baseAddr + 9, 1, 'uint16');
|
|
1685
|
+
const outPFSetRvrtTmsRaw = await this.readRegisterValue(baseAddr + 10, 1, 'uint16');
|
|
1686
|
+
const outPFSetRmpTmsRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
1687
|
+
const outPFSetEnaRaw = await this.readRegisterValue(baseAddr + 12, 1, 'uint16');
|
|
1688
|
+
const vArPctWinTmsRaw = await this.readRegisterValue(baseAddr + 16, 1, 'uint16');
|
|
1689
|
+
const vArPctRvrtTmsRaw = await this.readRegisterValue(baseAddr + 17, 1, 'uint16');
|
|
1690
|
+
const vArPctRmpTmsRaw = await this.readRegisterValue(baseAddr + 18, 1, 'uint16');
|
|
1691
|
+
const vArPctModRaw = await this.readRegisterValue(baseAddr + 19, 1, 'uint16');
|
|
1692
|
+
const vArPctEnaRaw = await this.readRegisterValue(baseAddr + 20, 1, 'uint16');
|
|
1693
|
+
this.logRegisterRead(123, 0, 'Conn_WinTms', connWinTmsRaw, 'uint16');
|
|
1694
|
+
this.logRegisterRead(123, 1, 'Conn_RvrtTms', connRvrtTmsRaw, 'uint16');
|
|
1695
|
+
this.logRegisterRead(123, 2, 'Conn', connRaw, 'enum16');
|
|
1696
|
+
this.logRegisterRead(123, 4, 'WMaxLimPct_WinTms', wMaxLimPctWinTmsRaw, 'uint16');
|
|
1697
|
+
this.logRegisterRead(123, 5, 'WMaxLimPct_RvrtTms', wMaxLimPctRvrtTmsRaw, 'uint16');
|
|
1698
|
+
this.logRegisterRead(123, 6, 'WMaxLimPct_RmpTms', wMaxLimPctRmpTmsRaw, 'uint16');
|
|
1699
|
+
this.logRegisterRead(123, 7, 'WMaxLim_Ena', wMaxLimEnaRaw, 'enum16');
|
|
1700
|
+
this.logRegisterRead(123, 9, 'OutPFSet_WinTms', outPFSetWinTmsRaw, 'uint16');
|
|
1701
|
+
this.logRegisterRead(123, 10, 'OutPFSet_RvrtTms', outPFSetRvrtTmsRaw, 'uint16');
|
|
1702
|
+
this.logRegisterRead(123, 11, 'OutPFSet_RmpTms', outPFSetRmpTmsRaw, 'uint16');
|
|
1703
|
+
this.logRegisterRead(123, 12, 'OutPFSet_Ena', outPFSetEnaRaw, 'enum16');
|
|
1704
|
+
this.logRegisterRead(123, 16, 'VArPct_WinTms', vArPctWinTmsRaw, 'uint16');
|
|
1705
|
+
this.logRegisterRead(123, 17, 'VArPct_RvrtTms', vArPctRvrtTmsRaw, 'uint16');
|
|
1706
|
+
this.logRegisterRead(123, 18, 'VArPct_RmpTms', vArPctRmpTmsRaw, 'uint16');
|
|
1707
|
+
this.logRegisterRead(123, 19, 'VArPct_Mod', vArPctModRaw, 'enum16');
|
|
1708
|
+
this.logRegisterRead(123, 20, 'VArPct_Ena', vArPctEnaRaw, 'enum16');
|
|
1432
1709
|
const controls = {
|
|
1433
1710
|
blockNumber: 123,
|
|
1434
1711
|
blockAddress: model.address,
|
|
1435
1712
|
blockLength: model.length,
|
|
1436
1713
|
// Connection control - Offsets 0-2
|
|
1437
|
-
Conn_WinTms:
|
|
1438
|
-
Conn_RvrtTms:
|
|
1439
|
-
Conn:
|
|
1714
|
+
Conn_WinTms: connWinTmsRaw,
|
|
1715
|
+
Conn_RvrtTms: connRvrtTmsRaw,
|
|
1716
|
+
Conn: connRaw,
|
|
1440
1717
|
// Power limit control - Offsets 3-7
|
|
1441
|
-
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF, 'uint16', 'Power Limit Percentage'),
|
|
1718
|
+
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF, 'uint16', 'Power Limit Percentage', 3, 123),
|
|
1442
1719
|
WMaxLimPct_SF: scaleFactors.WMaxLimPct_SF,
|
|
1443
|
-
WMaxLimPct_WinTms:
|
|
1444
|
-
WMaxLimPct_RvrtTms:
|
|
1445
|
-
WMaxLimPct_RmpTms:
|
|
1446
|
-
WMaxLim_Ena:
|
|
1720
|
+
WMaxLimPct_WinTms: wMaxLimPctWinTmsRaw,
|
|
1721
|
+
WMaxLimPct_RvrtTms: wMaxLimPctRvrtTmsRaw,
|
|
1722
|
+
WMaxLimPct_RmpTms: wMaxLimPctRmpTmsRaw,
|
|
1723
|
+
WMaxLim_Ena: wMaxLimEnaRaw,
|
|
1447
1724
|
// Power factor control - Offsets 8-12
|
|
1448
|
-
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF, 'int16', 'Output Power Factor Set'),
|
|
1725
|
+
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF, 'int16', 'Output Power Factor Set', 8, 123),
|
|
1449
1726
|
OutPFSet_SF: scaleFactors.OutPFSet_SF,
|
|
1450
|
-
OutPFSet_WinTms:
|
|
1451
|
-
OutPFSet_RvrtTms:
|
|
1452
|
-
OutPFSet_RmpTms:
|
|
1453
|
-
OutPFSet_Ena:
|
|
1727
|
+
OutPFSet_WinTms: outPFSetWinTmsRaw,
|
|
1728
|
+
OutPFSet_RvrtTms: outPFSetRvrtTmsRaw,
|
|
1729
|
+
OutPFSet_RmpTms: outPFSetRmpTmsRaw,
|
|
1730
|
+
OutPFSet_Ena: outPFSetEnaRaw,
|
|
1454
1731
|
// Reactive power control - Offsets 13-20
|
|
1455
|
-
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Reactive Power at Max Power %'),
|
|
1456
|
-
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Max Reactive Power %'),
|
|
1457
|
-
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Available Reactive Power %'),
|
|
1732
|
+
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Reactive Power at Max Power %', 13, 123),
|
|
1733
|
+
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Max Reactive Power %', 14, 123),
|
|
1734
|
+
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Available Reactive Power %', 15, 123),
|
|
1458
1735
|
VArPct_SF: scaleFactors.VArPct_SF,
|
|
1459
|
-
VArPct_WinTms:
|
|
1460
|
-
VArPct_RvrtTms:
|
|
1461
|
-
VArPct_RmpTms:
|
|
1462
|
-
VArPct_Mod:
|
|
1463
|
-
VArPct_Ena:
|
|
1736
|
+
VArPct_WinTms: vArPctWinTmsRaw,
|
|
1737
|
+
VArPct_RvrtTms: vArPctRvrtTmsRaw,
|
|
1738
|
+
VArPct_RmpTms: vArPctRmpTmsRaw,
|
|
1739
|
+
VArPct_Mod: vArPctModRaw,
|
|
1740
|
+
VArPct_Ena: vArPctEnaRaw
|
|
1464
1741
|
};
|
|
1465
1742
|
return controls;
|
|
1466
1743
|
}
|
|
@@ -1507,7 +1784,7 @@ class SunspecModbusClient {
|
|
|
1507
1784
|
}
|
|
1508
1785
|
}
|
|
1509
1786
|
/**
|
|
1510
|
-
* Write
|
|
1787
|
+
* Write inverter controls to Model 123 (Immediate Inverter Controls)
|
|
1511
1788
|
*/
|
|
1512
1789
|
async writeInverterControls(controls) {
|
|
1513
1790
|
const model = this.findModel(sunspec_interfaces_js_1.SunspecModelId.Controls);
|
|
@@ -1517,32 +1794,35 @@ class SunspecModbusClient {
|
|
|
1517
1794
|
}
|
|
1518
1795
|
const baseAddr = model.address;
|
|
1519
1796
|
try {
|
|
1520
|
-
// Connection control
|
|
1521
|
-
if (controls.Conn !== undefined
|
|
1522
|
-
|
|
1523
|
-
console.log(`
|
|
1797
|
+
// Connection control (Register 2)
|
|
1798
|
+
if (controls.Conn !== undefined) {
|
|
1799
|
+
await this.writeRegisterValue(baseAddr + 2, controls.Conn, 'uint16');
|
|
1800
|
+
console.log(`Set connection control to ${controls.Conn}`);
|
|
1524
1801
|
}
|
|
1525
|
-
// Power limit control
|
|
1526
|
-
if (controls.WMaxLimPct !== undefined
|
|
1527
|
-
const
|
|
1528
|
-
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
1802
|
+
// Power limit control (Register 3) - needs scale factor
|
|
1803
|
+
if (controls.WMaxLimPct !== undefined) {
|
|
1804
|
+
const scaleFactor = await this.readRegisterValue(baseAddr + 21, 1, 'int16');
|
|
1529
1805
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
1530
|
-
|
|
1806
|
+
await this.writeRegisterValue(baseAddr + 3, scaledValue, 'uint16');
|
|
1807
|
+
console.log(`Set power limit to ${controls.WMaxLimPct}% (scaled: ${scaledValue})`);
|
|
1531
1808
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1809
|
+
// Throttle enable/disable (Register 7)
|
|
1810
|
+
if (controls.WMaxLim_Ena !== undefined) {
|
|
1811
|
+
await this.writeRegisterValue(baseAddr + 7, controls.WMaxLim_Ena, 'uint16');
|
|
1812
|
+
console.log(`Set throttle enable to ${controls.WMaxLim_Ena}`);
|
|
1534
1813
|
}
|
|
1535
|
-
// Power factor control
|
|
1536
|
-
if (controls.OutPFSet !== undefined
|
|
1537
|
-
const
|
|
1538
|
-
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
1814
|
+
// Power factor control (Register 8) - needs scale factor
|
|
1815
|
+
if (controls.OutPFSet !== undefined) {
|
|
1816
|
+
const scaleFactor = await this.readRegisterValue(baseAddr + 22, 1, 'int16');
|
|
1539
1817
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
1540
|
-
|
|
1818
|
+
await this.writeRegisterValue(baseAddr + 8, scaledValue, 'int16');
|
|
1819
|
+
console.log(`Set power factor to ${controls.OutPFSet} (scaled: ${scaledValue})`);
|
|
1541
1820
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1821
|
+
// Power factor enable/disable (Register 12)
|
|
1822
|
+
if (controls.OutPFSet_Ena !== undefined) {
|
|
1823
|
+
await this.writeRegisterValue(baseAddr + 12, controls.OutPFSet_Ena, 'uint16');
|
|
1824
|
+
console.log(`Set PF enable to ${controls.OutPFSet_Ena}`);
|
|
1544
1825
|
}
|
|
1545
|
-
// Add more control writes as needed
|
|
1546
1826
|
console.log('Inverter controls written successfully');
|
|
1547
1827
|
return true;
|
|
1548
1828
|
}
|
|
@@ -1551,5 +1831,34 @@ class SunspecModbusClient {
|
|
|
1551
1831
|
return false;
|
|
1552
1832
|
}
|
|
1553
1833
|
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Set the inverter feed-in power limit using Model 123 (Immediate Inverter Controls)
|
|
1836
|
+
*
|
|
1837
|
+
* When limitW is a number, reads WMax from Model 121, computes percentage,
|
|
1838
|
+
* writes WMaxLimPct and enables WMaxLim_Ena.
|
|
1839
|
+
* When limitW is null, disables the limit by setting WMaxLim_Ena = DISABLED.
|
|
1840
|
+
*
|
|
1841
|
+
* @param limitW - Power limit in Watts, or null to remove the limit
|
|
1842
|
+
* @returns true if successful, false otherwise
|
|
1843
|
+
*/
|
|
1844
|
+
async setFeedInLimit(limitW) {
|
|
1845
|
+
if (limitW === null) {
|
|
1846
|
+
// Remove limit: disable WMaxLim_Ena
|
|
1847
|
+
console.log('Removing feed-in limit (disabling WMaxLim_Ena)');
|
|
1848
|
+
return this.writeInverterControls({ WMaxLim_Ena: sunspec_interfaces_js_1.SunspecEnableControl.DISABLED });
|
|
1849
|
+
}
|
|
1850
|
+
// Read WMax from Model 121 to compute percentage
|
|
1851
|
+
const settings = await this.readInverterSettings();
|
|
1852
|
+
if (!settings || !settings.WMax) {
|
|
1853
|
+
console.error('Cannot set feed-in limit: unable to read WMax from Model 121');
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
const pct = (limitW / settings.WMax) * 100;
|
|
1857
|
+
console.log(`Setting feed-in limit to ${limitW}W (${pct.toFixed(2)}% of WMax ${settings.WMax}W)`);
|
|
1858
|
+
return this.writeInverterControls({
|
|
1859
|
+
WMaxLimPct: pct,
|
|
1860
|
+
WMaxLim_Ena: sunspec_interfaces_js_1.SunspecEnableControl.ENABLED
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1554
1863
|
}
|
|
1555
1864
|
exports.SunspecModbusClient = SunspecModbusClient;
|