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