@enyo-energy/sunspec-sdk 0.0.9 → 0.0.10
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/sunspec-modbus-client.cjs +143 -123
- package/dist/cjs/sunspec-modbus-client.d.cts +37 -9
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/sunspec-modbus-client.d.ts +37 -9
- package/dist/sunspec-modbus-client.js +143 -123
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SunspecModbusClient = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Sunspec Modbus Client Implementation
|
|
6
|
+
*
|
|
7
|
+
* This module implements the Sunspec protocol for reading data from solar inverters,
|
|
8
|
+
* meters, and other energy devices over Modbus TCP/RTU.
|
|
9
|
+
*
|
|
10
|
+
* Sunspec NOT_IMPLEMENTED values reference:
|
|
11
|
+
* - int16: 0x8000 (-32768)
|
|
12
|
+
* - uint16: 0xFFFF (65535)
|
|
13
|
+
* - int32: 0x80000000 (-2147483648)
|
|
14
|
+
* - uint32: 0xFFFFFFFF (4294967295)
|
|
15
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, valid but zero)
|
|
16
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, valid but zero)
|
|
17
|
+
* - enum16/bitfield16: 0xFFFF
|
|
18
|
+
* - enum32/bitfield32: 0xFFFFFFFF
|
|
19
|
+
* - pad: 0x8000 (always returns this value)
|
|
20
|
+
* - string: all registers 0x0000 (NULL)
|
|
21
|
+
*/
|
|
4
22
|
const sunspec_interfaces_js_1 = require("./sunspec-interfaces.cjs");
|
|
5
23
|
const EnergyAppModbusConnectionHealth_js_1 = require("@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusConnectionHealth.js");
|
|
6
24
|
const EnergyAppModbusFaultTolerantReader_js_1 = require("@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js");
|
|
@@ -151,58 +169,65 @@ class SunspecModbusClient {
|
|
|
151
169
|
return this.discoveredModels.get(modelId);
|
|
152
170
|
}
|
|
153
171
|
/**
|
|
154
|
-
*
|
|
155
|
-
*/
|
|
156
|
-
async readRegisterWithScaleFactor(valueAddress, scaleFactorAddress, quantity = 1) {
|
|
157
|
-
if (!this.connected) {
|
|
158
|
-
throw new Error('Not connected to Modbus device');
|
|
159
|
-
}
|
|
160
|
-
// Read the raw value
|
|
161
|
-
if (!this.modbusClient) {
|
|
162
|
-
throw new Error('Modbus client not initialized');
|
|
163
|
-
}
|
|
164
|
-
const buffer = await this.modbusClient.readHoldingRegisters(valueAddress, quantity);
|
|
165
|
-
let value = buffer.readUInt16BE(0);
|
|
166
|
-
// Apply scale factor if provided
|
|
167
|
-
if (scaleFactorAddress) {
|
|
168
|
-
let scaleFactor = this.scaleFactors[`sf_${scaleFactorAddress}`];
|
|
169
|
-
if (scaleFactor === undefined) {
|
|
170
|
-
if (!this.modbusClient) {
|
|
171
|
-
throw new Error('Modbus client not initialized');
|
|
172
|
-
}
|
|
173
|
-
const sfBuffer = await this.modbusClient.readHoldingRegisters(scaleFactorAddress, 1);
|
|
174
|
-
// Scale factors are signed int16
|
|
175
|
-
scaleFactor = this.convertToSigned16(sfBuffer.readUInt16BE(0));
|
|
176
|
-
this.scaleFactors[`sf_${scaleFactorAddress}`] = scaleFactor;
|
|
177
|
-
}
|
|
178
|
-
// Apply scale factor: value * 10^scaleFactor
|
|
179
|
-
value = value * Math.pow(10, scaleFactor);
|
|
180
|
-
}
|
|
181
|
-
return value;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Convert unsigned 16-bit value to signed
|
|
185
|
-
*/
|
|
186
|
-
convertToSigned16(value) {
|
|
187
|
-
if (value > 32767) {
|
|
188
|
-
return value - 65536;
|
|
189
|
-
}
|
|
190
|
-
return value;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Check if a value is "unimplemented" according to Sunspec
|
|
172
|
+
* Check if a value is "unimplemented" according to Sunspec specification
|
|
194
173
|
* Returns true if the value represents an unimplemented/not applicable register
|
|
174
|
+
*
|
|
175
|
+
* Sunspec NOT_IMPLEMENTED values:
|
|
176
|
+
* - int16: 0x8000
|
|
177
|
+
* - uint16: 0xFFFF
|
|
178
|
+
* - int32: 0x80000000
|
|
179
|
+
* - uint32: 0xFFFFFFFF
|
|
180
|
+
* - int64: 0x8000000000000000
|
|
181
|
+
* - uint64: 0xFFFFFFFFFFFFFFFF
|
|
182
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
183
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
184
|
+
* - acc64: 0x0000000000000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
185
|
+
* - enum16: 0xFFFF
|
|
186
|
+
* - enum32: 0xFFFFFFFF
|
|
187
|
+
* - bitfield16: 0xFFFF
|
|
188
|
+
* - bitfield32: 0xFFFFFFFF
|
|
189
|
+
* - pad: 0x8000 (always returns this value)
|
|
190
|
+
* - ipaddr: 0x00000000 (NOT_CONFIGURED)
|
|
191
|
+
* - string: all registers 0x0000 (NULL)
|
|
195
192
|
*/
|
|
196
193
|
isUnimplementedValue(value, dataType = 'uint16') {
|
|
197
194
|
switch (dataType) {
|
|
198
195
|
case 'uint16':
|
|
199
|
-
|
|
196
|
+
case 'enum16':
|
|
197
|
+
case 'bitfield16':
|
|
198
|
+
return value === 0xFFFF;
|
|
200
199
|
case 'int16':
|
|
201
|
-
|
|
200
|
+
case 'pad':
|
|
201
|
+
return value === 0x8000;
|
|
202
202
|
case 'uint32':
|
|
203
|
+
case 'enum32':
|
|
204
|
+
case 'bitfield32':
|
|
203
205
|
return value === 0xFFFFFFFF;
|
|
204
206
|
case 'int32':
|
|
205
|
-
return value ===
|
|
207
|
+
return value === 0x80000000;
|
|
208
|
+
case 'uint64':
|
|
209
|
+
return value === 0xffffffffffffffffn;
|
|
210
|
+
case 'int64':
|
|
211
|
+
return value === 0x8000000000000000n;
|
|
212
|
+
case 'acc16':
|
|
213
|
+
// 0x0000 means NOT_ACCUMULATED (value is valid but zero)
|
|
214
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
215
|
+
return false;
|
|
216
|
+
case 'acc32':
|
|
217
|
+
// 0x00000000 means NOT_ACCUMULATED (value is valid but zero)
|
|
218
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
219
|
+
return false;
|
|
220
|
+
case 'acc64':
|
|
221
|
+
// 0x0000000000000000 means NOT_ACCUMULATED (value is valid but zero)
|
|
222
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
223
|
+
return false;
|
|
224
|
+
case 'ipaddr':
|
|
225
|
+
// 0x00000000 means NOT_CONFIGURED
|
|
226
|
+
return value === 0x00000000;
|
|
227
|
+
case 'string':
|
|
228
|
+
// For strings, all 0x0000 means unimplemented
|
|
229
|
+
// This would need special handling in the string reading logic
|
|
230
|
+
return value === 0;
|
|
206
231
|
default:
|
|
207
232
|
return false;
|
|
208
233
|
}
|
|
@@ -242,24 +267,15 @@ class SunspecModbusClient {
|
|
|
242
267
|
}
|
|
243
268
|
return this.cleanString(str);
|
|
244
269
|
case 'int16':
|
|
245
|
-
return
|
|
270
|
+
return buffer.readInt16BE(0);
|
|
246
271
|
case 'uint32':
|
|
247
272
|
case 'acc32':
|
|
248
|
-
|
|
249
|
-
return (buffer.readUInt16BE(0) << 16) | buffer.readUInt16BE(2);
|
|
273
|
+
return buffer.readUint32BE(0);
|
|
250
274
|
case 'int32':
|
|
251
|
-
|
|
252
|
-
return val > 0x7FFFFFFF ? val - 0x100000000 : val;
|
|
275
|
+
return buffer.readInt32BE(0);
|
|
253
276
|
case 'uint16':
|
|
254
277
|
default:
|
|
255
|
-
|
|
256
|
-
return buffer.readUInt16BE(0);
|
|
257
|
-
}
|
|
258
|
-
const values = [];
|
|
259
|
-
for (let i = 0; i < quantity; i++) {
|
|
260
|
-
values.push(buffer.readUInt16BE(i * 2));
|
|
261
|
-
}
|
|
262
|
-
return values;
|
|
278
|
+
return buffer.readUInt16BE(0);
|
|
263
279
|
}
|
|
264
280
|
}
|
|
265
281
|
catch (error) {
|
|
@@ -308,54 +324,54 @@ class SunspecModbusClient {
|
|
|
308
324
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
309
325
|
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
310
326
|
return raw;
|
|
311
|
-
})(), scaleFactors.A_SF),
|
|
312
|
-
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
313
|
-
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
314
|
-
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
327
|
+
})(), scaleFactors.A_SF, 'uint16', 'AC Current'),
|
|
328
|
+
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase A Current'),
|
|
329
|
+
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase B Current'),
|
|
330
|
+
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase C Current'),
|
|
315
331
|
// Voltage values - Offsets 7-12
|
|
316
|
-
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF),
|
|
317
|
-
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF),
|
|
318
|
-
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF),
|
|
332
|
+
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage AB'),
|
|
333
|
+
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage BC'),
|
|
334
|
+
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage CA'),
|
|
319
335
|
// Apply scale factor with unimplemented value checking
|
|
320
|
-
voltageAN: this.applyScaleFactor(voltageANRaw, scaleFactors.V_SF),
|
|
321
|
-
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF),
|
|
322
|
-
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF),
|
|
336
|
+
voltageAN: this.applyScaleFactor(voltageANRaw, scaleFactors.V_SF, 'uint16', 'Voltage AN'),
|
|
337
|
+
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF, 'uint16', 'Voltage BN'),
|
|
338
|
+
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF, 'uint16', 'Voltage CN'),
|
|
323
339
|
// Power values - Offsets 14, 18, 20, 22
|
|
324
340
|
acPower: this.applyScaleFactor(await (async () => {
|
|
325
341
|
const addr = baseAddr + 14;
|
|
326
342
|
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
327
343
|
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
328
344
|
return raw;
|
|
329
|
-
})(), scaleFactors.W_SF, 'int16'),
|
|
330
|
-
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
331
|
-
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
332
|
-
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
345
|
+
})(), scaleFactors.W_SF, 'int16', 'AC Power'),
|
|
346
|
+
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF, 'uint16', 'Apparent Power'),
|
|
347
|
+
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF, 'int16', 'Reactive Power'),
|
|
348
|
+
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF, 'int16', 'Power Factor'),
|
|
333
349
|
// Frequency - Offset 16
|
|
334
|
-
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
350
|
+
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF, 'uint16', 'Frequency'),
|
|
335
351
|
// DC values - Offsets 27, 28, 30
|
|
336
352
|
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
337
353
|
const addr = baseAddr + 27;
|
|
338
354
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
339
355
|
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
340
356
|
return raw;
|
|
341
|
-
})(), scaleFactors.DCA_SF),
|
|
357
|
+
})(), scaleFactors.DCA_SF, 'uint16', 'DC Current'),
|
|
342
358
|
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
343
359
|
const addr = baseAddr + 28;
|
|
344
360
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
345
361
|
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
346
362
|
return raw;
|
|
347
|
-
})(), scaleFactors.DCV_SF),
|
|
363
|
+
})(), scaleFactors.DCV_SF, 'uint16', 'DC Voltage'),
|
|
348
364
|
dcPower: this.applyScaleFactor(await (async () => {
|
|
349
365
|
const addr = baseAddr + 30;
|
|
350
366
|
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
351
367
|
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
352
368
|
return raw;
|
|
353
|
-
})(), scaleFactors.DCW_SF, 'int16'),
|
|
369
|
+
})(), scaleFactors.DCW_SF, 'int16', 'DC Power'),
|
|
354
370
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
355
|
-
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
356
|
-
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
357
|
-
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
358
|
-
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
371
|
+
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Cabinet Temperature'),
|
|
372
|
+
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Heat Sink Temperature'),
|
|
373
|
+
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Transformer Temperature'),
|
|
374
|
+
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Other Temperature'),
|
|
359
375
|
// Status values - Offsets 38, 39
|
|
360
376
|
operatingState: await this.readRegisterValue(baseAddr + 38, 1, 'uint16'),
|
|
361
377
|
vendorState: await this.readRegisterValue(baseAddr + 39, 1, 'uint16'),
|
|
@@ -371,7 +387,7 @@ class SunspecModbusClient {
|
|
|
371
387
|
const acEnergyAddr = baseAddr + 24;
|
|
372
388
|
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
373
389
|
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
374
|
-
data.acEnergy = !this.isUnimplementedValue(acEnergy, '
|
|
390
|
+
data.acEnergy = !this.isUnimplementedValue(acEnergy, 'acc32')
|
|
375
391
|
? acEnergy * Math.pow(10, scaleFactors.WH_SF) // Regular number with scale factor
|
|
376
392
|
: undefined;
|
|
377
393
|
// Log final calculated values
|
|
@@ -436,13 +452,13 @@ class SunspecModbusClient {
|
|
|
436
452
|
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
437
453
|
return {
|
|
438
454
|
blockNumber: 101,
|
|
439
|
-
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
440
|
-
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
441
|
-
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
442
|
-
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
443
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
444
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
445
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
455
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF, 'uint16', 'Single Phase Voltage'),
|
|
456
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF, 'uint16', 'Single Phase AC Current'),
|
|
457
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16', 'Single Phase AC Power'),
|
|
458
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF, 'uint16', 'Single Phase Frequency'),
|
|
459
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', 'Single Phase DC Current'),
|
|
460
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', 'Single Phase DC Voltage'),
|
|
461
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16', 'Single Phase DC Power'),
|
|
446
462
|
operatingState: stateRaw
|
|
447
463
|
};
|
|
448
464
|
}
|
|
@@ -476,12 +492,16 @@ class SunspecModbusClient {
|
|
|
476
492
|
* Apply scale factor to a value
|
|
477
493
|
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
478
494
|
*/
|
|
479
|
-
applyScaleFactor(value, scaleFactor, dataType = 'uint16') {
|
|
495
|
+
applyScaleFactor(value, scaleFactor, dataType = 'uint16', fieldName) {
|
|
480
496
|
// Check for unimplemented values
|
|
481
497
|
if (this.isUnimplementedValue(value, dataType)) {
|
|
482
498
|
return undefined;
|
|
483
499
|
}
|
|
484
|
-
|
|
500
|
+
const scaledValue = value * Math.pow(10, scaleFactor);
|
|
501
|
+
// Log the raw and scaled values in decimal format
|
|
502
|
+
const fieldPrefix = fieldName ? `${fieldName}: ` : '';
|
|
503
|
+
console.log(`Scale Factor Applied - ${fieldPrefix}raw=${value} (decimal), SF=${scaleFactor}, scaled=${scaledValue}`);
|
|
504
|
+
return scaledValue;
|
|
485
505
|
}
|
|
486
506
|
/**
|
|
487
507
|
* Read MPPT data from Model 160
|
|
@@ -536,24 +556,24 @@ class SunspecModbusClient {
|
|
|
536
556
|
// String ID - Offset 1 (8 registers for string)
|
|
537
557
|
stringId: await this.readRegisterValue(moduleAddr + 1, 8, 'string'),
|
|
538
558
|
// DC Current - Offset 9
|
|
539
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
559
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', `MPPT ${moduleId} DC Current`),
|
|
540
560
|
dcCurrentSF: scaleFactors.DCA_SF,
|
|
541
561
|
// DC Voltage - Offset 11
|
|
542
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
562
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', `MPPT ${moduleId} DC Voltage`),
|
|
543
563
|
dcVoltageSF: scaleFactors.DCV_SF,
|
|
544
564
|
// DC Power - Offset 13
|
|
545
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF),
|
|
565
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'uint16', `MPPT ${moduleId} DC Power`),
|
|
546
566
|
dcPowerSF: scaleFactors.DCW_SF,
|
|
547
567
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
548
568
|
// Only calculate if value is not unimplemented
|
|
549
|
-
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, '
|
|
569
|
+
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'acc32')
|
|
550
570
|
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF) // Regular number with scale factor
|
|
551
571
|
: undefined,
|
|
552
572
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
553
573
|
// Timestamp - Offset 18-19 (32-bit)
|
|
554
574
|
timestamp: await this.readRegisterValue(moduleAddr + 18, 2, 'uint32'),
|
|
555
575
|
// Temperature - Offset 20
|
|
556
|
-
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16'),
|
|
576
|
+
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16', `MPPT ${moduleId} Temperature`),
|
|
557
577
|
temperatureSF: scaleFactors.Tmp_SF,
|
|
558
578
|
// Operating State - Offset 22
|
|
559
579
|
operatingState: await this.readRegisterValue(moduleAddr + 22, 1, 'uint16'),
|
|
@@ -681,12 +701,12 @@ class SunspecModbusClient {
|
|
|
681
701
|
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
682
702
|
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
683
703
|
// Calculate final values with scale factors
|
|
684
|
-
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16');
|
|
685
|
-
const frequency = this.applyScaleFactor(freqRaw, freqSF);
|
|
686
|
-
const exportedEnergy = !this.isUnimplementedValue(exportRaw, '
|
|
704
|
+
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16', 'Meter Total Power');
|
|
705
|
+
const frequency = this.applyScaleFactor(freqRaw, freqSF, 'uint16', 'Meter Frequency');
|
|
706
|
+
const exportedEnergy = !this.isUnimplementedValue(exportRaw, 'acc32')
|
|
687
707
|
? exportRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
688
708
|
: undefined;
|
|
689
|
-
const importedEnergy = !this.isUnimplementedValue(importRaw, '
|
|
709
|
+
const importedEnergy = !this.isUnimplementedValue(importRaw, 'acc32')
|
|
690
710
|
? importRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
691
711
|
: undefined;
|
|
692
712
|
console.log('Meter Final Values:', {
|
|
@@ -812,40 +832,40 @@ class SunspecModbusClient {
|
|
|
812
832
|
blockAddress: model.address,
|
|
813
833
|
blockLength: model.length,
|
|
814
834
|
// Power settings - Offset 2
|
|
815
|
-
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF),
|
|
835
|
+
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF, 'uint16', 'Max Power'),
|
|
816
836
|
WMax_SF: scaleFactors.WMax_SF,
|
|
817
837
|
// Voltage settings - Offsets 3-6
|
|
818
|
-
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF),
|
|
838
|
+
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF, 'uint16', 'Voltage Reference'),
|
|
819
839
|
VRef_SF: scaleFactors.VRef_SF,
|
|
820
|
-
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF),
|
|
840
|
+
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF, 'int16', 'Voltage Reference Offset'),
|
|
821
841
|
VRefOfs_SF: scaleFactors.VRefOfs_SF,
|
|
822
|
-
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF),
|
|
823
|
-
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF),
|
|
842
|
+
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Max Voltage'),
|
|
843
|
+
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Min Voltage'),
|
|
824
844
|
VMinMax_SF: scaleFactors.VMinMax_SF,
|
|
825
845
|
// Apparent power settings - Offset 7
|
|
826
|
-
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF),
|
|
846
|
+
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF, 'uint16', 'Max Apparent Power'),
|
|
827
847
|
VAMax_SF: scaleFactors.VAMax_SF,
|
|
828
848
|
// Reactive power settings - Offsets 8-11
|
|
829
|
-
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
830
|
-
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
831
|
-
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
832
|
-
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
849
|
+
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q1'),
|
|
850
|
+
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q2'),
|
|
851
|
+
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q3'),
|
|
852
|
+
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q4'),
|
|
833
853
|
VArMax_SF: scaleFactors.VArMax_SF,
|
|
834
854
|
// Ramp rate settings - Offset 12
|
|
835
|
-
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF),
|
|
855
|
+
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF, 'uint16', 'Power Ramp Rate'),
|
|
836
856
|
WGra_SF: scaleFactors.WGra_SF,
|
|
837
857
|
// Power factor settings - Offsets 13-16
|
|
838
|
-
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
839
|
-
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
840
|
-
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
841
|
-
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
858
|
+
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q1'),
|
|
859
|
+
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q2'),
|
|
860
|
+
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q3'),
|
|
861
|
+
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q4'),
|
|
842
862
|
PFMin_SF: scaleFactors.PFMin_SF,
|
|
843
863
|
// Other settings - Offsets 17-21
|
|
844
864
|
VArAct: await this.readRegisterValue(baseAddr + 17, 1, 'uint16'),
|
|
845
865
|
ClcTotVA: await this.readRegisterValue(baseAddr + 18, 1, 'uint16'),
|
|
846
|
-
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF),
|
|
866
|
+
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF, 'uint16', 'Max Ramp Rate'),
|
|
847
867
|
MaxRmpRte_SF: scaleFactors.MaxRmpRte_SF,
|
|
848
|
-
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF),
|
|
868
|
+
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF, 'uint16', 'Nominal Frequency'),
|
|
849
869
|
ECPNomHz_SF: scaleFactors.ECPNomHz_SF,
|
|
850
870
|
ConnPh: await this.readRegisterValue(baseAddr + 21, 1, 'uint16')
|
|
851
871
|
};
|
|
@@ -882,23 +902,23 @@ class SunspecModbusClient {
|
|
|
882
902
|
Conn_RvrtTms: await this.readRegisterValue(baseAddr + 1, 1, 'uint16'),
|
|
883
903
|
Conn: await this.readRegisterValue(baseAddr + 2, 1, 'uint16'),
|
|
884
904
|
// Power limit control - Offsets 3-7
|
|
885
|
-
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF),
|
|
905
|
+
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF, 'uint16', 'Power Limit Percentage'),
|
|
886
906
|
WMaxLimPct_SF: scaleFactors.WMaxLimPct_SF,
|
|
887
907
|
WMaxLimPct_WinTms: await this.readRegisterValue(baseAddr + 4, 1, 'uint16'),
|
|
888
908
|
WMaxLimPct_RvrtTms: await this.readRegisterValue(baseAddr + 5, 1, 'uint16'),
|
|
889
909
|
WMaxLimPct_RmpTms: await this.readRegisterValue(baseAddr + 6, 1, 'uint16'),
|
|
890
910
|
WMaxLim_Ena: await this.readRegisterValue(baseAddr + 7, 1, 'uint16'),
|
|
891
911
|
// Power factor control - Offsets 8-12
|
|
892
|
-
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF),
|
|
912
|
+
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF, 'int16', 'Output Power Factor Set'),
|
|
893
913
|
OutPFSet_SF: scaleFactors.OutPFSet_SF,
|
|
894
914
|
OutPFSet_WinTms: await this.readRegisterValue(baseAddr + 9, 1, 'uint16'),
|
|
895
915
|
OutPFSet_RvrtTms: await this.readRegisterValue(baseAddr + 10, 1, 'uint16'),
|
|
896
916
|
OutPFSet_RmpTms: await this.readRegisterValue(baseAddr + 11, 1, 'uint16'),
|
|
897
917
|
OutPFSet_Ena: await this.readRegisterValue(baseAddr + 12, 1, 'uint16'),
|
|
898
918
|
// Reactive power control - Offsets 13-20
|
|
899
|
-
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
900
|
-
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
901
|
-
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
919
|
+
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Reactive Power at Max Power %'),
|
|
920
|
+
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Max Reactive Power %'),
|
|
921
|
+
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Available Reactive Power %'),
|
|
902
922
|
VArPct_SF: scaleFactors.VArPct_SF,
|
|
903
923
|
VArPct_WinTms: await this.readRegisterValue(baseAddr + 16, 1, 'uint16'),
|
|
904
924
|
VArPct_RvrtTms: await this.readRegisterValue(baseAddr + 17, 1, 'uint16'),
|
|
@@ -929,7 +949,7 @@ class SunspecModbusClient {
|
|
|
929
949
|
if (settings.WMax !== undefined && this.modbusClient) {
|
|
930
950
|
// Need to read scale factor first if not provided
|
|
931
951
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 22, 1);
|
|
932
|
-
const scaleFactor =
|
|
952
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
933
953
|
const scaledValue = Math.round(settings.WMax / Math.pow(10, scaleFactor));
|
|
934
954
|
// Writing registers needs to be implemented in EnergyAppModbusInstance
|
|
935
955
|
// For now, log the write operation
|
|
@@ -937,7 +957,7 @@ class SunspecModbusClient {
|
|
|
937
957
|
}
|
|
938
958
|
if (settings.VRef !== undefined && this.modbusClient) {
|
|
939
959
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 23, 1);
|
|
940
|
-
const scaleFactor =
|
|
960
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
941
961
|
const scaledValue = Math.round(settings.VRef / Math.pow(10, scaleFactor));
|
|
942
962
|
console.log(`Would write value ${scaledValue} to register ${baseAddr + 1}`);
|
|
943
963
|
}
|
|
@@ -969,7 +989,7 @@ class SunspecModbusClient {
|
|
|
969
989
|
// Power limit control
|
|
970
990
|
if (controls.WMaxLimPct !== undefined && this.modbusClient) {
|
|
971
991
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 21, 1);
|
|
972
|
-
const scaleFactor =
|
|
992
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
973
993
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
974
994
|
console.log(`Would write power limit ${scaledValue} to register ${baseAddr + 3}`);
|
|
975
995
|
}
|
|
@@ -979,7 +999,7 @@ class SunspecModbusClient {
|
|
|
979
999
|
// Power factor control
|
|
980
1000
|
if (controls.OutPFSet !== undefined && this.modbusClient) {
|
|
981
1001
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 22, 1);
|
|
982
|
-
const scaleFactor =
|
|
1002
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
983
1003
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
984
1004
|
console.log(`Would write power factor ${scaledValue} to register ${baseAddr + 8}`);
|
|
985
1005
|
}
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sunspec Modbus Client Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module implements the Sunspec protocol for reading data from solar inverters,
|
|
5
|
+
* meters, and other energy devices over Modbus TCP/RTU.
|
|
6
|
+
*
|
|
7
|
+
* Sunspec NOT_IMPLEMENTED values reference:
|
|
8
|
+
* - int16: 0x8000 (-32768)
|
|
9
|
+
* - uint16: 0xFFFF (65535)
|
|
10
|
+
* - int32: 0x80000000 (-2147483648)
|
|
11
|
+
* - uint32: 0xFFFFFFFF (4294967295)
|
|
12
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, valid but zero)
|
|
13
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, valid but zero)
|
|
14
|
+
* - enum16/bitfield16: 0xFFFF
|
|
15
|
+
* - enum32/bitfield32: 0xFFFFFFFF
|
|
16
|
+
* - pad: 0x8000 (always returns this value)
|
|
17
|
+
* - string: all registers 0x0000 (NULL)
|
|
18
|
+
*/
|
|
1
19
|
import { type SunspecInverterControls, type SunspecInverterData, type SunspecInverterSettings, type SunspecMeterData, type SunspecModel, type SunspecMPPTData } from "./sunspec-interfaces.cjs";
|
|
2
20
|
import { IConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/interfaces.js";
|
|
3
21
|
import { EnergyApp } from "@enyo-energy/energy-app-sdk";
|
|
@@ -37,16 +55,26 @@ export declare class SunspecModbusClient {
|
|
|
37
55
|
*/
|
|
38
56
|
findModel(modelId: number): SunspecModel | undefined;
|
|
39
57
|
/**
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
readRegisterWithScaleFactor(valueAddress: number, scaleFactorAddress?: number, quantity?: number): Promise<number>;
|
|
43
|
-
/**
|
|
44
|
-
* Convert unsigned 16-bit value to signed
|
|
45
|
-
*/
|
|
46
|
-
private convertToSigned16;
|
|
47
|
-
/**
|
|
48
|
-
* Check if a value is "unimplemented" according to Sunspec
|
|
58
|
+
* Check if a value is "unimplemented" according to Sunspec specification
|
|
49
59
|
* Returns true if the value represents an unimplemented/not applicable register
|
|
60
|
+
*
|
|
61
|
+
* Sunspec NOT_IMPLEMENTED values:
|
|
62
|
+
* - int16: 0x8000
|
|
63
|
+
* - uint16: 0xFFFF
|
|
64
|
+
* - int32: 0x80000000
|
|
65
|
+
* - uint32: 0xFFFFFFFF
|
|
66
|
+
* - int64: 0x8000000000000000
|
|
67
|
+
* - uint64: 0xFFFFFFFFFFFFFFFF
|
|
68
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
69
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
70
|
+
* - acc64: 0x0000000000000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
71
|
+
* - enum16: 0xFFFF
|
|
72
|
+
* - enum32: 0xFFFFFFFF
|
|
73
|
+
* - bitfield16: 0xFFFF
|
|
74
|
+
* - bitfield32: 0xFFFFFFFF
|
|
75
|
+
* - pad: 0x8000 (always returns this value)
|
|
76
|
+
* - ipaddr: 0x00000000 (NOT_CONFIGURED)
|
|
77
|
+
* - string: all registers 0x0000 (NULL)
|
|
50
78
|
*/
|
|
51
79
|
private isUnimplementedValue;
|
|
52
80
|
/**
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.10';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sunspec Modbus Client Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module implements the Sunspec protocol for reading data from solar inverters,
|
|
5
|
+
* meters, and other energy devices over Modbus TCP/RTU.
|
|
6
|
+
*
|
|
7
|
+
* Sunspec NOT_IMPLEMENTED values reference:
|
|
8
|
+
* - int16: 0x8000 (-32768)
|
|
9
|
+
* - uint16: 0xFFFF (65535)
|
|
10
|
+
* - int32: 0x80000000 (-2147483648)
|
|
11
|
+
* - uint32: 0xFFFFFFFF (4294967295)
|
|
12
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, valid but zero)
|
|
13
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, valid but zero)
|
|
14
|
+
* - enum16/bitfield16: 0xFFFF
|
|
15
|
+
* - enum32/bitfield32: 0xFFFFFFFF
|
|
16
|
+
* - pad: 0x8000 (always returns this value)
|
|
17
|
+
* - string: all registers 0x0000 (NULL)
|
|
18
|
+
*/
|
|
1
19
|
import { type SunspecInverterControls, type SunspecInverterData, type SunspecInverterSettings, type SunspecMeterData, type SunspecModel, type SunspecMPPTData } from "./sunspec-interfaces.js";
|
|
2
20
|
import { IConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/interfaces.js";
|
|
3
21
|
import { EnergyApp } from "@enyo-energy/energy-app-sdk";
|
|
@@ -37,16 +55,26 @@ export declare class SunspecModbusClient {
|
|
|
37
55
|
*/
|
|
38
56
|
findModel(modelId: number): SunspecModel | undefined;
|
|
39
57
|
/**
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
readRegisterWithScaleFactor(valueAddress: number, scaleFactorAddress?: number, quantity?: number): Promise<number>;
|
|
43
|
-
/**
|
|
44
|
-
* Convert unsigned 16-bit value to signed
|
|
45
|
-
*/
|
|
46
|
-
private convertToSigned16;
|
|
47
|
-
/**
|
|
48
|
-
* Check if a value is "unimplemented" according to Sunspec
|
|
58
|
+
* Check if a value is "unimplemented" according to Sunspec specification
|
|
49
59
|
* Returns true if the value represents an unimplemented/not applicable register
|
|
60
|
+
*
|
|
61
|
+
* Sunspec NOT_IMPLEMENTED values:
|
|
62
|
+
* - int16: 0x8000
|
|
63
|
+
* - uint16: 0xFFFF
|
|
64
|
+
* - int32: 0x80000000
|
|
65
|
+
* - uint32: 0xFFFFFFFF
|
|
66
|
+
* - int64: 0x8000000000000000
|
|
67
|
+
* - uint64: 0xFFFFFFFFFFFFFFFF
|
|
68
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
69
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
70
|
+
* - acc64: 0x0000000000000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
71
|
+
* - enum16: 0xFFFF
|
|
72
|
+
* - enum32: 0xFFFFFFFF
|
|
73
|
+
* - bitfield16: 0xFFFF
|
|
74
|
+
* - bitfield32: 0xFFFFFFFF
|
|
75
|
+
* - pad: 0x8000 (always returns this value)
|
|
76
|
+
* - ipaddr: 0x00000000 (NOT_CONFIGURED)
|
|
77
|
+
* - string: all registers 0x0000 (NULL)
|
|
50
78
|
*/
|
|
51
79
|
private isUnimplementedValue;
|
|
52
80
|
/**
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sunspec Modbus Client Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module implements the Sunspec protocol for reading data from solar inverters,
|
|
5
|
+
* meters, and other energy devices over Modbus TCP/RTU.
|
|
6
|
+
*
|
|
7
|
+
* Sunspec NOT_IMPLEMENTED values reference:
|
|
8
|
+
* - int16: 0x8000 (-32768)
|
|
9
|
+
* - uint16: 0xFFFF (65535)
|
|
10
|
+
* - int32: 0x80000000 (-2147483648)
|
|
11
|
+
* - uint32: 0xFFFFFFFF (4294967295)
|
|
12
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, valid but zero)
|
|
13
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, valid but zero)
|
|
14
|
+
* - enum16/bitfield16: 0xFFFF
|
|
15
|
+
* - enum32/bitfield32: 0xFFFFFFFF
|
|
16
|
+
* - pad: 0x8000 (always returns this value)
|
|
17
|
+
* - string: all registers 0x0000 (NULL)
|
|
18
|
+
*/
|
|
1
19
|
import { SunspecModelId } from "./sunspec-interfaces.js";
|
|
2
20
|
import { EnergyAppModbusConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusConnectionHealth.js";
|
|
3
21
|
import { EnergyAppModbusFaultTolerantReader } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/EnergyAppModbusFaultTolerantReader.js";
|
|
@@ -148,58 +166,65 @@ export class SunspecModbusClient {
|
|
|
148
166
|
return this.discoveredModels.get(modelId);
|
|
149
167
|
}
|
|
150
168
|
/**
|
|
151
|
-
*
|
|
152
|
-
*/
|
|
153
|
-
async readRegisterWithScaleFactor(valueAddress, scaleFactorAddress, quantity = 1) {
|
|
154
|
-
if (!this.connected) {
|
|
155
|
-
throw new Error('Not connected to Modbus device');
|
|
156
|
-
}
|
|
157
|
-
// Read the raw value
|
|
158
|
-
if (!this.modbusClient) {
|
|
159
|
-
throw new Error('Modbus client not initialized');
|
|
160
|
-
}
|
|
161
|
-
const buffer = await this.modbusClient.readHoldingRegisters(valueAddress, quantity);
|
|
162
|
-
let value = buffer.readUInt16BE(0);
|
|
163
|
-
// Apply scale factor if provided
|
|
164
|
-
if (scaleFactorAddress) {
|
|
165
|
-
let scaleFactor = this.scaleFactors[`sf_${scaleFactorAddress}`];
|
|
166
|
-
if (scaleFactor === undefined) {
|
|
167
|
-
if (!this.modbusClient) {
|
|
168
|
-
throw new Error('Modbus client not initialized');
|
|
169
|
-
}
|
|
170
|
-
const sfBuffer = await this.modbusClient.readHoldingRegisters(scaleFactorAddress, 1);
|
|
171
|
-
// Scale factors are signed int16
|
|
172
|
-
scaleFactor = this.convertToSigned16(sfBuffer.readUInt16BE(0));
|
|
173
|
-
this.scaleFactors[`sf_${scaleFactorAddress}`] = scaleFactor;
|
|
174
|
-
}
|
|
175
|
-
// Apply scale factor: value * 10^scaleFactor
|
|
176
|
-
value = value * Math.pow(10, scaleFactor);
|
|
177
|
-
}
|
|
178
|
-
return value;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Convert unsigned 16-bit value to signed
|
|
182
|
-
*/
|
|
183
|
-
convertToSigned16(value) {
|
|
184
|
-
if (value > 32767) {
|
|
185
|
-
return value - 65536;
|
|
186
|
-
}
|
|
187
|
-
return value;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Check if a value is "unimplemented" according to Sunspec
|
|
169
|
+
* Check if a value is "unimplemented" according to Sunspec specification
|
|
191
170
|
* Returns true if the value represents an unimplemented/not applicable register
|
|
171
|
+
*
|
|
172
|
+
* Sunspec NOT_IMPLEMENTED values:
|
|
173
|
+
* - int16: 0x8000
|
|
174
|
+
* - uint16: 0xFFFF
|
|
175
|
+
* - int32: 0x80000000
|
|
176
|
+
* - uint32: 0xFFFFFFFF
|
|
177
|
+
* - int64: 0x8000000000000000
|
|
178
|
+
* - uint64: 0xFFFFFFFFFFFFFFFF
|
|
179
|
+
* - acc16: 0x0000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
180
|
+
* - acc32: 0x00000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
181
|
+
* - acc64: 0x0000000000000000 (NOT_ACCUMULATED, not truly unimplemented)
|
|
182
|
+
* - enum16: 0xFFFF
|
|
183
|
+
* - enum32: 0xFFFFFFFF
|
|
184
|
+
* - bitfield16: 0xFFFF
|
|
185
|
+
* - bitfield32: 0xFFFFFFFF
|
|
186
|
+
* - pad: 0x8000 (always returns this value)
|
|
187
|
+
* - ipaddr: 0x00000000 (NOT_CONFIGURED)
|
|
188
|
+
* - string: all registers 0x0000 (NULL)
|
|
192
189
|
*/
|
|
193
190
|
isUnimplementedValue(value, dataType = 'uint16') {
|
|
194
191
|
switch (dataType) {
|
|
195
192
|
case 'uint16':
|
|
196
|
-
|
|
193
|
+
case 'enum16':
|
|
194
|
+
case 'bitfield16':
|
|
195
|
+
return value === 0xFFFF;
|
|
197
196
|
case 'int16':
|
|
198
|
-
|
|
197
|
+
case 'pad':
|
|
198
|
+
return value === 0x8000;
|
|
199
199
|
case 'uint32':
|
|
200
|
+
case 'enum32':
|
|
201
|
+
case 'bitfield32':
|
|
200
202
|
return value === 0xFFFFFFFF;
|
|
201
203
|
case 'int32':
|
|
202
|
-
return value ===
|
|
204
|
+
return value === 0x80000000;
|
|
205
|
+
case 'uint64':
|
|
206
|
+
return value === 0xffffffffffffffffn;
|
|
207
|
+
case 'int64':
|
|
208
|
+
return value === 0x8000000000000000n;
|
|
209
|
+
case 'acc16':
|
|
210
|
+
// 0x0000 means NOT_ACCUMULATED (value is valid but zero)
|
|
211
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
212
|
+
return false;
|
|
213
|
+
case 'acc32':
|
|
214
|
+
// 0x00000000 means NOT_ACCUMULATED (value is valid but zero)
|
|
215
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
216
|
+
return false;
|
|
217
|
+
case 'acc64':
|
|
218
|
+
// 0x0000000000000000 means NOT_ACCUMULATED (value is valid but zero)
|
|
219
|
+
// For accumulators, we typically don't treat 0 as unimplemented
|
|
220
|
+
return false;
|
|
221
|
+
case 'ipaddr':
|
|
222
|
+
// 0x00000000 means NOT_CONFIGURED
|
|
223
|
+
return value === 0x00000000;
|
|
224
|
+
case 'string':
|
|
225
|
+
// For strings, all 0x0000 means unimplemented
|
|
226
|
+
// This would need special handling in the string reading logic
|
|
227
|
+
return value === 0;
|
|
203
228
|
default:
|
|
204
229
|
return false;
|
|
205
230
|
}
|
|
@@ -239,24 +264,15 @@ export class SunspecModbusClient {
|
|
|
239
264
|
}
|
|
240
265
|
return this.cleanString(str);
|
|
241
266
|
case 'int16':
|
|
242
|
-
return
|
|
267
|
+
return buffer.readInt16BE(0);
|
|
243
268
|
case 'uint32':
|
|
244
269
|
case 'acc32':
|
|
245
|
-
|
|
246
|
-
return (buffer.readUInt16BE(0) << 16) | buffer.readUInt16BE(2);
|
|
270
|
+
return buffer.readUint32BE(0);
|
|
247
271
|
case 'int32':
|
|
248
|
-
|
|
249
|
-
return val > 0x7FFFFFFF ? val - 0x100000000 : val;
|
|
272
|
+
return buffer.readInt32BE(0);
|
|
250
273
|
case 'uint16':
|
|
251
274
|
default:
|
|
252
|
-
|
|
253
|
-
return buffer.readUInt16BE(0);
|
|
254
|
-
}
|
|
255
|
-
const values = [];
|
|
256
|
-
for (let i = 0; i < quantity; i++) {
|
|
257
|
-
values.push(buffer.readUInt16BE(i * 2));
|
|
258
|
-
}
|
|
259
|
-
return values;
|
|
275
|
+
return buffer.readUInt16BE(0);
|
|
260
276
|
}
|
|
261
277
|
}
|
|
262
278
|
catch (error) {
|
|
@@ -305,54 +321,54 @@ export class SunspecModbusClient {
|
|
|
305
321
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
306
322
|
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
307
323
|
return raw;
|
|
308
|
-
})(), scaleFactors.A_SF),
|
|
309
|
-
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
310
|
-
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
311
|
-
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
324
|
+
})(), scaleFactors.A_SF, 'uint16', 'AC Current'),
|
|
325
|
+
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase A Current'),
|
|
326
|
+
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase B Current'),
|
|
327
|
+
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF, 'uint16', 'Phase C Current'),
|
|
312
328
|
// Voltage values - Offsets 7-12
|
|
313
|
-
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF),
|
|
314
|
-
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF),
|
|
315
|
-
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF),
|
|
329
|
+
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage AB'),
|
|
330
|
+
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage BC'),
|
|
331
|
+
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF, 'uint16', 'Voltage CA'),
|
|
316
332
|
// Apply scale factor with unimplemented value checking
|
|
317
|
-
voltageAN: this.applyScaleFactor(voltageANRaw, scaleFactors.V_SF),
|
|
318
|
-
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF),
|
|
319
|
-
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF),
|
|
333
|
+
voltageAN: this.applyScaleFactor(voltageANRaw, scaleFactors.V_SF, 'uint16', 'Voltage AN'),
|
|
334
|
+
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF, 'uint16', 'Voltage BN'),
|
|
335
|
+
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF, 'uint16', 'Voltage CN'),
|
|
320
336
|
// Power values - Offsets 14, 18, 20, 22
|
|
321
337
|
acPower: this.applyScaleFactor(await (async () => {
|
|
322
338
|
const addr = baseAddr + 14;
|
|
323
339
|
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
324
340
|
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
325
341
|
return raw;
|
|
326
|
-
})(), scaleFactors.W_SF, 'int16'),
|
|
327
|
-
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
328
|
-
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
329
|
-
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
342
|
+
})(), scaleFactors.W_SF, 'int16', 'AC Power'),
|
|
343
|
+
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF, 'uint16', 'Apparent Power'),
|
|
344
|
+
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF, 'int16', 'Reactive Power'),
|
|
345
|
+
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF, 'int16', 'Power Factor'),
|
|
330
346
|
// Frequency - Offset 16
|
|
331
|
-
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
347
|
+
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF, 'uint16', 'Frequency'),
|
|
332
348
|
// DC values - Offsets 27, 28, 30
|
|
333
349
|
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
334
350
|
const addr = baseAddr + 27;
|
|
335
351
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
336
352
|
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
337
353
|
return raw;
|
|
338
|
-
})(), scaleFactors.DCA_SF),
|
|
354
|
+
})(), scaleFactors.DCA_SF, 'uint16', 'DC Current'),
|
|
339
355
|
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
340
356
|
const addr = baseAddr + 28;
|
|
341
357
|
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
342
358
|
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
343
359
|
return raw;
|
|
344
|
-
})(), scaleFactors.DCV_SF),
|
|
360
|
+
})(), scaleFactors.DCV_SF, 'uint16', 'DC Voltage'),
|
|
345
361
|
dcPower: this.applyScaleFactor(await (async () => {
|
|
346
362
|
const addr = baseAddr + 30;
|
|
347
363
|
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
348
364
|
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
349
365
|
return raw;
|
|
350
|
-
})(), scaleFactors.DCW_SF, 'int16'),
|
|
366
|
+
})(), scaleFactors.DCW_SF, 'int16', 'DC Power'),
|
|
351
367
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
352
|
-
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
353
|
-
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
354
|
-
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
355
|
-
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
368
|
+
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Cabinet Temperature'),
|
|
369
|
+
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Heat Sink Temperature'),
|
|
370
|
+
transformerTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 35, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Transformer Temperature'),
|
|
371
|
+
otherTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 36, 1, 'int16'), scaleFactors.Tmp_SF, 'int16', 'Other Temperature'),
|
|
356
372
|
// Status values - Offsets 38, 39
|
|
357
373
|
operatingState: await this.readRegisterValue(baseAddr + 38, 1, 'uint16'),
|
|
358
374
|
vendorState: await this.readRegisterValue(baseAddr + 39, 1, 'uint16'),
|
|
@@ -368,7 +384,7 @@ export class SunspecModbusClient {
|
|
|
368
384
|
const acEnergyAddr = baseAddr + 24;
|
|
369
385
|
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
370
386
|
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
371
|
-
data.acEnergy = !this.isUnimplementedValue(acEnergy, '
|
|
387
|
+
data.acEnergy = !this.isUnimplementedValue(acEnergy, 'acc32')
|
|
372
388
|
? acEnergy * Math.pow(10, scaleFactors.WH_SF) // Regular number with scale factor
|
|
373
389
|
: undefined;
|
|
374
390
|
// Log final calculated values
|
|
@@ -433,13 +449,13 @@ export class SunspecModbusClient {
|
|
|
433
449
|
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
434
450
|
return {
|
|
435
451
|
blockNumber: 101,
|
|
436
|
-
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
437
|
-
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
438
|
-
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
439
|
-
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
440
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
441
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
442
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
452
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF, 'uint16', 'Single Phase Voltage'),
|
|
453
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF, 'uint16', 'Single Phase AC Current'),
|
|
454
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16', 'Single Phase AC Power'),
|
|
455
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF, 'uint16', 'Single Phase Frequency'),
|
|
456
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', 'Single Phase DC Current'),
|
|
457
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', 'Single Phase DC Voltage'),
|
|
458
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16', 'Single Phase DC Power'),
|
|
443
459
|
operatingState: stateRaw
|
|
444
460
|
};
|
|
445
461
|
}
|
|
@@ -473,12 +489,16 @@ export class SunspecModbusClient {
|
|
|
473
489
|
* Apply scale factor to a value
|
|
474
490
|
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
475
491
|
*/
|
|
476
|
-
applyScaleFactor(value, scaleFactor, dataType = 'uint16') {
|
|
492
|
+
applyScaleFactor(value, scaleFactor, dataType = 'uint16', fieldName) {
|
|
477
493
|
// Check for unimplemented values
|
|
478
494
|
if (this.isUnimplementedValue(value, dataType)) {
|
|
479
495
|
return undefined;
|
|
480
496
|
}
|
|
481
|
-
|
|
497
|
+
const scaledValue = value * Math.pow(10, scaleFactor);
|
|
498
|
+
// Log the raw and scaled values in decimal format
|
|
499
|
+
const fieldPrefix = fieldName ? `${fieldName}: ` : '';
|
|
500
|
+
console.log(`Scale Factor Applied - ${fieldPrefix}raw=${value} (decimal), SF=${scaleFactor}, scaled=${scaledValue}`);
|
|
501
|
+
return scaledValue;
|
|
482
502
|
}
|
|
483
503
|
/**
|
|
484
504
|
* Read MPPT data from Model 160
|
|
@@ -533,24 +553,24 @@ export class SunspecModbusClient {
|
|
|
533
553
|
// String ID - Offset 1 (8 registers for string)
|
|
534
554
|
stringId: await this.readRegisterValue(moduleAddr + 1, 8, 'string'),
|
|
535
555
|
// DC Current - Offset 9
|
|
536
|
-
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
556
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF, 'uint16', `MPPT ${moduleId} DC Current`),
|
|
537
557
|
dcCurrentSF: scaleFactors.DCA_SF,
|
|
538
558
|
// DC Voltage - Offset 11
|
|
539
|
-
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
559
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF, 'uint16', `MPPT ${moduleId} DC Voltage`),
|
|
540
560
|
dcVoltageSF: scaleFactors.DCV_SF,
|
|
541
561
|
// DC Power - Offset 13
|
|
542
|
-
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF),
|
|
562
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'uint16', `MPPT ${moduleId} DC Power`),
|
|
543
563
|
dcPowerSF: scaleFactors.DCW_SF,
|
|
544
564
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
545
565
|
// Only calculate if value is not unimplemented
|
|
546
|
-
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, '
|
|
566
|
+
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'acc32')
|
|
547
567
|
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF) // Regular number with scale factor
|
|
548
568
|
: undefined,
|
|
549
569
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
550
570
|
// Timestamp - Offset 18-19 (32-bit)
|
|
551
571
|
timestamp: await this.readRegisterValue(moduleAddr + 18, 2, 'uint32'),
|
|
552
572
|
// Temperature - Offset 20
|
|
553
|
-
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16'),
|
|
573
|
+
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16', `MPPT ${moduleId} Temperature`),
|
|
554
574
|
temperatureSF: scaleFactors.Tmp_SF,
|
|
555
575
|
// Operating State - Offset 22
|
|
556
576
|
operatingState: await this.readRegisterValue(moduleAddr + 22, 1, 'uint16'),
|
|
@@ -678,12 +698,12 @@ export class SunspecModbusClient {
|
|
|
678
698
|
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
679
699
|
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
680
700
|
// Calculate final values with scale factors
|
|
681
|
-
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16');
|
|
682
|
-
const frequency = this.applyScaleFactor(freqRaw, freqSF);
|
|
683
|
-
const exportedEnergy = !this.isUnimplementedValue(exportRaw, '
|
|
701
|
+
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16', 'Meter Total Power');
|
|
702
|
+
const frequency = this.applyScaleFactor(freqRaw, freqSF, 'uint16', 'Meter Frequency');
|
|
703
|
+
const exportedEnergy = !this.isUnimplementedValue(exportRaw, 'acc32')
|
|
684
704
|
? exportRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
685
705
|
: undefined;
|
|
686
|
-
const importedEnergy = !this.isUnimplementedValue(importRaw, '
|
|
706
|
+
const importedEnergy = !this.isUnimplementedValue(importRaw, 'acc32')
|
|
687
707
|
? importRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
688
708
|
: undefined;
|
|
689
709
|
console.log('Meter Final Values:', {
|
|
@@ -809,40 +829,40 @@ export class SunspecModbusClient {
|
|
|
809
829
|
blockAddress: model.address,
|
|
810
830
|
blockLength: model.length,
|
|
811
831
|
// Power settings - Offset 2
|
|
812
|
-
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF),
|
|
832
|
+
WMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 2, 1, 'uint16'), scaleFactors.WMax_SF, 'uint16', 'Max Power'),
|
|
813
833
|
WMax_SF: scaleFactors.WMax_SF,
|
|
814
834
|
// Voltage settings - Offsets 3-6
|
|
815
|
-
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF),
|
|
835
|
+
VRef: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.VRef_SF, 'uint16', 'Voltage Reference'),
|
|
816
836
|
VRef_SF: scaleFactors.VRef_SF,
|
|
817
|
-
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF),
|
|
837
|
+
VRefOfs: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'int16'), scaleFactors.VRefOfs_SF, 'int16', 'Voltage Reference Offset'),
|
|
818
838
|
VRefOfs_SF: scaleFactors.VRefOfs_SF,
|
|
819
|
-
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF),
|
|
820
|
-
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF),
|
|
839
|
+
VMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Max Voltage'),
|
|
840
|
+
VMin: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 6, 1, 'uint16'), scaleFactors.VMinMax_SF, 'uint16', 'Min Voltage'),
|
|
821
841
|
VMinMax_SF: scaleFactors.VMinMax_SF,
|
|
822
842
|
// Apparent power settings - Offset 7
|
|
823
|
-
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF),
|
|
843
|
+
VAMax: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.VAMax_SF, 'uint16', 'Max Apparent Power'),
|
|
824
844
|
VAMax_SF: scaleFactors.VAMax_SF,
|
|
825
845
|
// Reactive power settings - Offsets 8-11
|
|
826
|
-
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
827
|
-
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
828
|
-
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
829
|
-
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF),
|
|
846
|
+
VArMaxQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q1'),
|
|
847
|
+
VArMaxQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q2'),
|
|
848
|
+
VArMaxQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 10, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q3'),
|
|
849
|
+
VArMaxQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 11, 1, 'int16'), scaleFactors.VArMax_SF, 'int16', 'Max Reactive Power Q4'),
|
|
830
850
|
VArMax_SF: scaleFactors.VArMax_SF,
|
|
831
851
|
// Ramp rate settings - Offset 12
|
|
832
|
-
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF),
|
|
852
|
+
WGra: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 12, 1, 'uint16'), scaleFactors.WGra_SF, 'uint16', 'Power Ramp Rate'),
|
|
833
853
|
WGra_SF: scaleFactors.WGra_SF,
|
|
834
854
|
// Power factor settings - Offsets 13-16
|
|
835
|
-
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
836
|
-
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
837
|
-
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
838
|
-
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF),
|
|
855
|
+
PFMinQ1: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q1'),
|
|
856
|
+
PFMinQ2: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q2'),
|
|
857
|
+
PFMinQ3: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q3'),
|
|
858
|
+
PFMinQ4: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'int16'), scaleFactors.PFMin_SF, 'int16', 'Min Power Factor Q4'),
|
|
839
859
|
PFMin_SF: scaleFactors.PFMin_SF,
|
|
840
860
|
// Other settings - Offsets 17-21
|
|
841
861
|
VArAct: await this.readRegisterValue(baseAddr + 17, 1, 'uint16'),
|
|
842
862
|
ClcTotVA: await this.readRegisterValue(baseAddr + 18, 1, 'uint16'),
|
|
843
|
-
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF),
|
|
863
|
+
MaxRmpRte: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 19, 1, 'uint16'), scaleFactors.MaxRmpRte_SF, 'uint16', 'Max Ramp Rate'),
|
|
844
864
|
MaxRmpRte_SF: scaleFactors.MaxRmpRte_SF,
|
|
845
|
-
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF),
|
|
865
|
+
ECPNomHz: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'uint16'), scaleFactors.ECPNomHz_SF, 'uint16', 'Nominal Frequency'),
|
|
846
866
|
ECPNomHz_SF: scaleFactors.ECPNomHz_SF,
|
|
847
867
|
ConnPh: await this.readRegisterValue(baseAddr + 21, 1, 'uint16')
|
|
848
868
|
};
|
|
@@ -879,23 +899,23 @@ export class SunspecModbusClient {
|
|
|
879
899
|
Conn_RvrtTms: await this.readRegisterValue(baseAddr + 1, 1, 'uint16'),
|
|
880
900
|
Conn: await this.readRegisterValue(baseAddr + 2, 1, 'uint16'),
|
|
881
901
|
// Power limit control - Offsets 3-7
|
|
882
|
-
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF),
|
|
902
|
+
WMaxLimPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.WMaxLimPct_SF, 'uint16', 'Power Limit Percentage'),
|
|
883
903
|
WMaxLimPct_SF: scaleFactors.WMaxLimPct_SF,
|
|
884
904
|
WMaxLimPct_WinTms: await this.readRegisterValue(baseAddr + 4, 1, 'uint16'),
|
|
885
905
|
WMaxLimPct_RvrtTms: await this.readRegisterValue(baseAddr + 5, 1, 'uint16'),
|
|
886
906
|
WMaxLimPct_RmpTms: await this.readRegisterValue(baseAddr + 6, 1, 'uint16'),
|
|
887
907
|
WMaxLim_Ena: await this.readRegisterValue(baseAddr + 7, 1, 'uint16'),
|
|
888
908
|
// Power factor control - Offsets 8-12
|
|
889
|
-
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF),
|
|
909
|
+
OutPFSet: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'int16'), scaleFactors.OutPFSet_SF, 'int16', 'Output Power Factor Set'),
|
|
890
910
|
OutPFSet_SF: scaleFactors.OutPFSet_SF,
|
|
891
911
|
OutPFSet_WinTms: await this.readRegisterValue(baseAddr + 9, 1, 'uint16'),
|
|
892
912
|
OutPFSet_RvrtTms: await this.readRegisterValue(baseAddr + 10, 1, 'uint16'),
|
|
893
913
|
OutPFSet_RmpTms: await this.readRegisterValue(baseAddr + 11, 1, 'uint16'),
|
|
894
914
|
OutPFSet_Ena: await this.readRegisterValue(baseAddr + 12, 1, 'uint16'),
|
|
895
915
|
// Reactive power control - Offsets 13-20
|
|
896
|
-
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
897
|
-
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
898
|
-
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF),
|
|
916
|
+
VArWMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 13, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Reactive Power at Max Power %'),
|
|
917
|
+
VArMaxPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 14, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Max Reactive Power %'),
|
|
918
|
+
VArAvalPct: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 15, 1, 'int16'), scaleFactors.VArPct_SF, 'int16', 'Available Reactive Power %'),
|
|
899
919
|
VArPct_SF: scaleFactors.VArPct_SF,
|
|
900
920
|
VArPct_WinTms: await this.readRegisterValue(baseAddr + 16, 1, 'uint16'),
|
|
901
921
|
VArPct_RvrtTms: await this.readRegisterValue(baseAddr + 17, 1, 'uint16'),
|
|
@@ -926,7 +946,7 @@ export class SunspecModbusClient {
|
|
|
926
946
|
if (settings.WMax !== undefined && this.modbusClient) {
|
|
927
947
|
// Need to read scale factor first if not provided
|
|
928
948
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 22, 1);
|
|
929
|
-
const scaleFactor =
|
|
949
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
930
950
|
const scaledValue = Math.round(settings.WMax / Math.pow(10, scaleFactor));
|
|
931
951
|
// Writing registers needs to be implemented in EnergyAppModbusInstance
|
|
932
952
|
// For now, log the write operation
|
|
@@ -934,7 +954,7 @@ export class SunspecModbusClient {
|
|
|
934
954
|
}
|
|
935
955
|
if (settings.VRef !== undefined && this.modbusClient) {
|
|
936
956
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 23, 1);
|
|
937
|
-
const scaleFactor =
|
|
957
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
938
958
|
const scaledValue = Math.round(settings.VRef / Math.pow(10, scaleFactor));
|
|
939
959
|
console.log(`Would write value ${scaledValue} to register ${baseAddr + 1}`);
|
|
940
960
|
}
|
|
@@ -966,7 +986,7 @@ export class SunspecModbusClient {
|
|
|
966
986
|
// Power limit control
|
|
967
987
|
if (controls.WMaxLimPct !== undefined && this.modbusClient) {
|
|
968
988
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 21, 1);
|
|
969
|
-
const scaleFactor =
|
|
989
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
970
990
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
971
991
|
console.log(`Would write power limit ${scaledValue} to register ${baseAddr + 3}`);
|
|
972
992
|
}
|
|
@@ -976,7 +996,7 @@ export class SunspecModbusClient {
|
|
|
976
996
|
// Power factor control
|
|
977
997
|
if (controls.OutPFSet !== undefined && this.modbusClient) {
|
|
978
998
|
const sfBuffer = await this.modbusClient.readHoldingRegisters(baseAddr + 22, 1);
|
|
979
|
-
const scaleFactor =
|
|
999
|
+
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
980
1000
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
981
1001
|
console.log(`Would write power factor ${scaledValue} to register ${baseAddr + 8}`);
|
|
982
1002
|
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED