@enyo-energy/sunspec-sdk 0.0.5 → 0.0.7
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-devices.cjs +21 -9
- package/dist/cjs/sunspec-devices.d.cts +3 -3
- package/dist/cjs/sunspec-modbus-client.cjs +238 -38
- package/dist/cjs/sunspec-modbus-client.d.cts +6 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/sunspec-devices.d.ts +3 -3
- package/dist/sunspec-devices.js +21 -9
- package/dist/sunspec-modbus-client.d.ts +6 -0
- package/dist/sunspec-modbus-client.js +238 -38
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -103,6 +103,15 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
103
103
|
const inverterData = await this.sunspecClient.readInverterData();
|
|
104
104
|
const mpptDataList = await this.sunspecClient.readAllMPPTData();
|
|
105
105
|
if (inverterData) {
|
|
106
|
+
// Log what we're sending
|
|
107
|
+
console.log('Preparing Inverter Message:', {
|
|
108
|
+
pvPowerW: inverterData.dcPower,
|
|
109
|
+
acPower: inverterData.acPower,
|
|
110
|
+
voltageL1: inverterData.voltageAN,
|
|
111
|
+
voltageL2: inverterData.voltageBN,
|
|
112
|
+
voltageL3: inverterData.voltageCN,
|
|
113
|
+
stringsCount: mpptDataList.length
|
|
114
|
+
});
|
|
106
115
|
const inverterMessage = {
|
|
107
116
|
id: (0, node_crypto_1.randomUUID)(),
|
|
108
117
|
message: enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.InverterValuesUpdateV1,
|
|
@@ -113,12 +122,12 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
113
122
|
timestampIso: timestamp.toISOString(),
|
|
114
123
|
resolution,
|
|
115
124
|
data: {
|
|
116
|
-
pvPowerW: inverterData.
|
|
117
|
-
activePowerLimitationW: inverterData.acPower || 0,
|
|
125
|
+
pvPowerW: inverterData.dcPower || 0, // Use DC power for PV power
|
|
126
|
+
activePowerLimitationW: inverterData.acPower || 0,
|
|
118
127
|
state: this.mapOperatingState(inverterData.operatingState),
|
|
119
128
|
voltageL1: inverterData.voltageAN || 0,
|
|
120
|
-
voltageL2: inverterData.voltageBN,
|
|
121
|
-
voltageL3: inverterData.voltageCN,
|
|
129
|
+
voltageL2: inverterData.voltageBN ?? undefined, // Use undefined if unimplemented phase
|
|
130
|
+
voltageL3: inverterData.voltageCN ?? undefined, // Use undefined if unimplemented phase
|
|
122
131
|
strings: this.mapMPPTToStrings(mpptDataList)
|
|
123
132
|
}
|
|
124
133
|
};
|
|
@@ -152,11 +161,14 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
152
161
|
mapMPPTToStrings(mpptDataList) {
|
|
153
162
|
const result = [];
|
|
154
163
|
mpptDataList.forEach((mppt, index) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
// Only include strings with valid data
|
|
165
|
+
if (mppt.dcVoltage !== undefined || mppt.dcPower !== undefined) {
|
|
166
|
+
result.push({
|
|
167
|
+
index: index + 1,
|
|
168
|
+
voltage: mppt.dcVoltage ?? undefined, // Use null if undefined
|
|
169
|
+
powerW: mppt.dcPower ?? 0 // Default to 0 if undefined
|
|
170
|
+
});
|
|
171
|
+
}
|
|
160
172
|
});
|
|
161
173
|
return result;
|
|
162
174
|
}
|
|
@@ -2,7 +2,7 @@ import { ApplianceManager, EnergyApp } from "@enyo-energy/energy-app-sdk";
|
|
|
2
2
|
import { EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
|
|
3
3
|
import { EnyoNetworkDevice } from "@enyo-energy/energy-app-sdk/dist/types/enyo-network-device.js";
|
|
4
4
|
import { SunspecModbusClient } from "./sunspec-modbus-client.cjs";
|
|
5
|
-
import { EnyoDataBusMessage } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
|
|
5
|
+
import { EnyoDataBusMessage, EnyoDataBusMessageResolution } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
|
|
6
6
|
/**
|
|
7
7
|
* Base abstract class for all Sunspec devices
|
|
8
8
|
*/
|
|
@@ -27,7 +27,7 @@ export declare abstract class BaseSunspecDevice {
|
|
|
27
27
|
/**
|
|
28
28
|
* Update device data and return data bus messages
|
|
29
29
|
*/
|
|
30
|
-
abstract readData(clockId: string, resolution:
|
|
30
|
+
abstract readData(clockId: string, resolution: EnyoDataBusMessageResolution): Promise<EnyoDataBusMessage[]>;
|
|
31
31
|
/**
|
|
32
32
|
* Check if the device is connected
|
|
33
33
|
*/
|
|
@@ -83,5 +83,5 @@ export declare class SunspecMeter extends BaseSunspecDevice {
|
|
|
83
83
|
/**
|
|
84
84
|
* Update meter data and return data bus messages
|
|
85
85
|
*/
|
|
86
|
-
readData(clockId: string, resolution:
|
|
86
|
+
readData(clockId: string, resolution: EnyoDataBusMessageResolution): Promise<EnyoDataBusMessage[]>;
|
|
87
87
|
}
|
|
@@ -189,6 +189,24 @@ class SunspecModbusClient {
|
|
|
189
189
|
}
|
|
190
190
|
return value;
|
|
191
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if a value is "unimplemented" according to Sunspec
|
|
194
|
+
* Returns true if the value represents an unimplemented/not applicable register
|
|
195
|
+
*/
|
|
196
|
+
isUnimplementedValue(value, dataType = 'uint16') {
|
|
197
|
+
switch (dataType) {
|
|
198
|
+
case 'uint16':
|
|
199
|
+
return value === 0xFFFF || value === 65535;
|
|
200
|
+
case 'int16':
|
|
201
|
+
return value === 0x7FFF || value === 32767;
|
|
202
|
+
case 'uint32':
|
|
203
|
+
return value === 0xFFFFFFFF;
|
|
204
|
+
case 'int32':
|
|
205
|
+
return value === 0x7FFFFFFF;
|
|
206
|
+
default:
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
192
210
|
/**
|
|
193
211
|
* Helper to clean string values by removing null characters
|
|
194
212
|
*/
|
|
@@ -261,19 +279,36 @@ class SunspecModbusClient {
|
|
|
261
279
|
console.error('No inverter model found');
|
|
262
280
|
return null;
|
|
263
281
|
}
|
|
282
|
+
console.warn('IMPORTANT: Working with single-phase inverter, but 3-phase is expected!');
|
|
264
283
|
return this.readSinglePhaseInverterData(singlePhaseModel);
|
|
265
284
|
}
|
|
285
|
+
console.log(`Found 3-phase inverter model 103 at address ${model.address} with length ${model.length}`);
|
|
266
286
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
267
287
|
try {
|
|
268
288
|
// Read all scale factors first using fault-tolerant reader
|
|
289
|
+
console.log(`Reading Inverter Data from Model 103 at base address: ${baseAddr}`);
|
|
269
290
|
const scaleFactors = await this.readInverterScaleFactors(baseAddr);
|
|
270
291
|
// Read values using fault-tolerant reader with proper data types
|
|
292
|
+
// Read raw voltage values to check for unimplemented phases
|
|
293
|
+
const voltageANRaw = await this.readRegisterValue(baseAddr + 10, 1, 'uint16');
|
|
294
|
+
const voltageBNRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
295
|
+
const voltageCNRaw = await this.readRegisterValue(baseAddr + 12, 1, 'uint16');
|
|
296
|
+
console.log('Inverter Raw Voltage Values:', {
|
|
297
|
+
voltageANRaw: `0x${voltageANRaw.toString(16).toUpperCase()} (${voltageANRaw})`,
|
|
298
|
+
voltageBNRaw: `0x${voltageBNRaw.toString(16).toUpperCase()} (${voltageBNRaw})`,
|
|
299
|
+
voltageCNRaw: `0x${voltageCNRaw.toString(16).toUpperCase()} (${voltageCNRaw})`
|
|
300
|
+
});
|
|
271
301
|
const data = {
|
|
272
302
|
blockNumber: 103,
|
|
273
303
|
blockAddress: model.address,
|
|
274
304
|
blockLength: model.length,
|
|
275
305
|
// AC Current values - Offsets 2-5
|
|
276
|
-
acCurrent: this.applyScaleFactor(await
|
|
306
|
+
acCurrent: this.applyScaleFactor(await (async () => {
|
|
307
|
+
const addr = baseAddr + 2;
|
|
308
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
309
|
+
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
310
|
+
return raw;
|
|
311
|
+
})(), scaleFactors.A_SF),
|
|
277
312
|
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
278
313
|
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
279
314
|
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
@@ -281,20 +316,41 @@ class SunspecModbusClient {
|
|
|
281
316
|
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF),
|
|
282
317
|
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF),
|
|
283
318
|
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF),
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
319
|
+
// 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),
|
|
287
323
|
// Power values - Offsets 14, 18, 20, 22
|
|
288
|
-
acPower: this.applyScaleFactor(await
|
|
324
|
+
acPower: this.applyScaleFactor(await (async () => {
|
|
325
|
+
const addr = baseAddr + 14;
|
|
326
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
327
|
+
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
328
|
+
return raw;
|
|
329
|
+
})(), scaleFactors.W_SF, 'int16'),
|
|
289
330
|
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
290
331
|
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
291
332
|
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
292
333
|
// Frequency - Offset 16
|
|
293
334
|
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
294
335
|
// DC values - Offsets 27, 28, 30
|
|
295
|
-
dcCurrent: this.applyScaleFactor(await
|
|
296
|
-
|
|
297
|
-
|
|
336
|
+
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
337
|
+
const addr = baseAddr + 27;
|
|
338
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
339
|
+
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
340
|
+
return raw;
|
|
341
|
+
})(), scaleFactors.DCA_SF),
|
|
342
|
+
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
343
|
+
const addr = baseAddr + 28;
|
|
344
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
345
|
+
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
346
|
+
return raw;
|
|
347
|
+
})(), scaleFactors.DCV_SF),
|
|
348
|
+
dcPower: this.applyScaleFactor(await (async () => {
|
|
349
|
+
const addr = baseAddr + 30;
|
|
350
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
351
|
+
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
352
|
+
return raw;
|
|
353
|
+
})(), scaleFactors.DCW_SF, 'int16'),
|
|
298
354
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
299
355
|
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
300
356
|
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
@@ -312,8 +368,21 @@ class SunspecModbusClient {
|
|
|
312
368
|
vendorEvents4: await this.readRegisterValue(baseAddr + 50, 2, 'uint32')
|
|
313
369
|
};
|
|
314
370
|
// Read AC Energy (32-bit accumulator) - Offset 24-25
|
|
315
|
-
const
|
|
371
|
+
const acEnergyAddr = baseAddr + 24;
|
|
372
|
+
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
373
|
+
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
316
374
|
data.acEnergy = BigInt(acEnergy) * BigInt(Math.pow(10, scaleFactors.WH_SF));
|
|
375
|
+
// Log final calculated values
|
|
376
|
+
console.log('Inverter Final Values:', {
|
|
377
|
+
acPower: data.acPower,
|
|
378
|
+
dcPower: data.dcPower,
|
|
379
|
+
voltageAN: data.voltageAN,
|
|
380
|
+
voltageBN: data.voltageBN,
|
|
381
|
+
voltageCN: data.voltageCN,
|
|
382
|
+
dcVoltage: data.dcVoltage,
|
|
383
|
+
dcCurrent: data.dcCurrent,
|
|
384
|
+
acCurrent: data.acCurrent
|
|
385
|
+
});
|
|
317
386
|
return data;
|
|
318
387
|
}
|
|
319
388
|
catch (error) {
|
|
@@ -329,15 +398,53 @@ class SunspecModbusClient {
|
|
|
329
398
|
// Implementation would be similar but simplified
|
|
330
399
|
const baseAddr = model.address + 2;
|
|
331
400
|
try {
|
|
332
|
-
|
|
401
|
+
console.log(`Reading Single-Phase Inverter Data from Model 101 at base address: ${baseAddr}`);
|
|
402
|
+
// Read scale factors for single phase model
|
|
403
|
+
const scaleFactors = {
|
|
404
|
+
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'),
|
|
405
|
+
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'),
|
|
406
|
+
W_SF: await this.readRegisterValue(baseAddr + 10, 1, 'int16'),
|
|
407
|
+
Hz_SF: await this.readRegisterValue(baseAddr + 12, 1, 'int16'),
|
|
408
|
+
DCA_SF: await this.readRegisterValue(baseAddr + 18, 1, 'int16'),
|
|
409
|
+
DCV_SF: await this.readRegisterValue(baseAddr + 19, 1, 'int16'),
|
|
410
|
+
DCW_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16')
|
|
411
|
+
};
|
|
412
|
+
console.log('Single-Phase Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
413
|
+
// Read raw values with logging
|
|
414
|
+
const acCurrentAddr = baseAddr + 2;
|
|
415
|
+
const acCurrentRaw = await this.readRegisterValue(acCurrentAddr, 1, 'uint16');
|
|
416
|
+
console.log(`AC Current: address=${acCurrentAddr}, raw=0x${acCurrentRaw.toString(16).toUpperCase()} (${acCurrentRaw})`);
|
|
417
|
+
const voltageAddr = baseAddr + 7;
|
|
418
|
+
const voltageRaw = await this.readRegisterValue(voltageAddr, 1, 'uint16');
|
|
419
|
+
console.log(`AC Voltage: address=${voltageAddr}, raw=0x${voltageRaw.toString(16).toUpperCase()} (${voltageRaw})`);
|
|
420
|
+
const acPowerAddr = baseAddr + 9;
|
|
421
|
+
const acPowerRaw = await this.readRegisterValue(acPowerAddr, 1, 'int16');
|
|
422
|
+
console.log(`AC Power: address=${acPowerAddr}, raw=0x${acPowerRaw.toString(16).toUpperCase()} (${acPowerRaw}), SF=${scaleFactors.W_SF}`);
|
|
423
|
+
const freqAddr = baseAddr + 11;
|
|
424
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
425
|
+
console.log(`Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw})`);
|
|
426
|
+
const dcCurrentAddr = baseAddr + 14;
|
|
427
|
+
const dcCurrentRaw = await this.readRegisterValue(dcCurrentAddr, 1, 'uint16');
|
|
428
|
+
console.log(`DC Current: address=${dcCurrentAddr}, raw=0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`);
|
|
429
|
+
const dcVoltageAddr = baseAddr + 15;
|
|
430
|
+
const dcVoltageRaw = await this.readRegisterValue(dcVoltageAddr, 1, 'uint16');
|
|
431
|
+
console.log(`DC Voltage: address=${dcVoltageAddr}, raw=0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`);
|
|
432
|
+
const dcPowerAddr = baseAddr + 20;
|
|
433
|
+
const dcPowerRaw = await this.readRegisterValue(dcPowerAddr, 1, 'int16');
|
|
434
|
+
console.log(`DC Power: address=${dcPowerAddr}, raw=0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw}), SF=${scaleFactors.DCW_SF}`);
|
|
435
|
+
const stateAddr = baseAddr + 24;
|
|
436
|
+
const stateRaw = await this.readRegisterValue(stateAddr, 1, 'uint16');
|
|
437
|
+
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
333
438
|
return {
|
|
334
439
|
blockNumber: 101,
|
|
335
|
-
voltageAN:
|
|
336
|
-
acCurrent:
|
|
337
|
-
acPower:
|
|
338
|
-
frequency:
|
|
339
|
-
|
|
340
|
-
|
|
440
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
441
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
442
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
443
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
444
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
445
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
446
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
447
|
+
operatingState: stateRaw
|
|
341
448
|
};
|
|
342
449
|
}
|
|
343
450
|
catch (error) {
|
|
@@ -349,7 +456,7 @@ class SunspecModbusClient {
|
|
|
349
456
|
* Read inverter scale factors
|
|
350
457
|
*/
|
|
351
458
|
async readInverterScaleFactors(baseAddr) {
|
|
352
|
-
|
|
459
|
+
const scaleFactors = {
|
|
353
460
|
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'), // Offset 6
|
|
354
461
|
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'), // Offset 13
|
|
355
462
|
W_SF: await this.readRegisterValue(baseAddr + 15, 1, 'int16'), // Offset 15
|
|
@@ -363,11 +470,18 @@ class SunspecModbusClient {
|
|
|
363
470
|
DCW_SF: await this.readRegisterValue(baseAddr + 31, 1, 'int16'), // Offset 31
|
|
364
471
|
Tmp_SF: await this.readRegisterValue(baseAddr + 36, 1, 'int16') // Offset 36
|
|
365
472
|
};
|
|
473
|
+
console.log('Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
474
|
+
return scaleFactors;
|
|
366
475
|
}
|
|
367
476
|
/**
|
|
368
477
|
* Apply scale factor to a value
|
|
478
|
+
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
369
479
|
*/
|
|
370
|
-
applyScaleFactor(value, scaleFactor) {
|
|
480
|
+
applyScaleFactor(value, scaleFactor, dataType = 'uint16') {
|
|
481
|
+
// Check for unimplemented values
|
|
482
|
+
if (this.isUnimplementedValue(value, dataType)) {
|
|
483
|
+
return undefined;
|
|
484
|
+
}
|
|
371
485
|
return value * Math.pow(10, scaleFactor);
|
|
372
486
|
}
|
|
373
487
|
/**
|
|
@@ -393,6 +507,28 @@ class SunspecModbusClient {
|
|
|
393
507
|
DCWH_SF: await this.readRegisterValue(moduleAddr + 17, 1, 'int16'),
|
|
394
508
|
Tmp_SF: await this.readRegisterValue(moduleAddr + 21, 1, 'int16')
|
|
395
509
|
};
|
|
510
|
+
console.log(`MPPT Module ${moduleId} Scale Factors:`, JSON.stringify(scaleFactors, null, 2));
|
|
511
|
+
// Read raw values first
|
|
512
|
+
const dcCurrentRaw = await this.readRegisterValue(moduleAddr + 9, 1, 'uint16');
|
|
513
|
+
const dcVoltageRaw = await this.readRegisterValue(moduleAddr + 11, 1, 'uint16');
|
|
514
|
+
const dcPowerRaw = await this.readRegisterValue(moduleAddr + 13, 1, 'uint16');
|
|
515
|
+
const dcEnergyRaw = await this.readRegisterValue(moduleAddr + 15, 2, 'acc32');
|
|
516
|
+
const temperatureRaw = await this.readRegisterValue(moduleAddr + 20, 1, 'int16');
|
|
517
|
+
console.log(`MPPT Module ${moduleId} Raw Values:`, {
|
|
518
|
+
dcCurrentRaw: `0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`,
|
|
519
|
+
dcVoltageRaw: `0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`,
|
|
520
|
+
dcPowerRaw: `0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw})`,
|
|
521
|
+
dcEnergyRaw: `0x${dcEnergyRaw.toString(16).toUpperCase()} (${dcEnergyRaw})`,
|
|
522
|
+
temperatureRaw: `0x${temperatureRaw.toString(16).toUpperCase()} (${temperatureRaw})`
|
|
523
|
+
});
|
|
524
|
+
// Check if this module is actually implemented/connected
|
|
525
|
+
// If all key values are unimplemented, this module doesn't exist
|
|
526
|
+
if (this.isUnimplementedValue(dcCurrentRaw, 'uint16') &&
|
|
527
|
+
this.isUnimplementedValue(dcVoltageRaw, 'uint16') &&
|
|
528
|
+
this.isUnimplementedValue(dcPowerRaw, 'uint16')) {
|
|
529
|
+
console.log(`MPPT module ${moduleId} appears to be unconnected (all values are 0xFFFF)`);
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
396
532
|
const data = {
|
|
397
533
|
blockNumber: 160,
|
|
398
534
|
blockAddress: model.address,
|
|
@@ -401,22 +537,24 @@ class SunspecModbusClient {
|
|
|
401
537
|
// String ID - Offset 1 (8 registers for string)
|
|
402
538
|
stringId: await this.readRegisterValue(moduleAddr + 1, 8, 'string'),
|
|
403
539
|
// DC Current - Offset 9
|
|
404
|
-
dcCurrent: this.applyScaleFactor(
|
|
540
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
405
541
|
dcCurrentSF: scaleFactors.DCA_SF,
|
|
406
542
|
// DC Voltage - Offset 11
|
|
407
|
-
dcVoltage: this.applyScaleFactor(
|
|
543
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
408
544
|
dcVoltageSF: scaleFactors.DCV_SF,
|
|
409
545
|
// DC Power - Offset 13
|
|
410
|
-
dcPower: this.applyScaleFactor(
|
|
546
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF),
|
|
411
547
|
dcPowerSF: scaleFactors.DCW_SF,
|
|
412
548
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
413
|
-
|
|
414
|
-
|
|
549
|
+
// Only calculate if value is not unimplemented
|
|
550
|
+
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'uint32')
|
|
551
|
+
? BigInt(dcEnergyRaw) * BigInt(Math.pow(10, scaleFactors.DCWH_SF))
|
|
552
|
+
: undefined,
|
|
415
553
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
416
554
|
// Timestamp - Offset 18-19 (32-bit)
|
|
417
555
|
timestamp: await this.readRegisterValue(moduleAddr + 18, 2, 'uint32'),
|
|
418
556
|
// Temperature - Offset 20
|
|
419
|
-
temperature: this.applyScaleFactor(
|
|
557
|
+
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16'),
|
|
420
558
|
temperatureSF: scaleFactors.Tmp_SF,
|
|
421
559
|
// Operating State - Offset 22
|
|
422
560
|
operatingState: await this.readRegisterValue(moduleAddr + 22, 1, 'uint16'),
|
|
@@ -439,9 +577,19 @@ class SunspecModbusClient {
|
|
|
439
577
|
const mpptData = [];
|
|
440
578
|
// Try to read up to 4 MPPT strings (typical maximum)
|
|
441
579
|
for (let i = 1; i <= 4; i++) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
580
|
+
try {
|
|
581
|
+
const data = await this.readMPPTData(i);
|
|
582
|
+
// Only include if we got valid data (not null) and it has actual values
|
|
583
|
+
if (data &&
|
|
584
|
+
(data.dcCurrent !== undefined ||
|
|
585
|
+
data.dcVoltage !== undefined ||
|
|
586
|
+
data.dcPower !== undefined)) {
|
|
587
|
+
mpptData.push(data);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
console.debug(`Could not read MPPT module ${i}: ${error}`);
|
|
592
|
+
// Continue to try other modules
|
|
445
593
|
}
|
|
446
594
|
}
|
|
447
595
|
return mpptData;
|
|
@@ -462,13 +610,47 @@ class SunspecModbusClient {
|
|
|
462
610
|
return null;
|
|
463
611
|
}
|
|
464
612
|
const baseAddr = model.address + 2;
|
|
613
|
+
console.log(`Reading Meter Data from Model ${model.id} at base address: ${baseAddr}`);
|
|
465
614
|
try {
|
|
466
|
-
//
|
|
467
|
-
//
|
|
615
|
+
// Model 201 (single phase) and 203 (3-phase) have different offsets
|
|
616
|
+
// Model 201: Power at offset 12, Frequency at offset 18
|
|
617
|
+
// Model 203: Power at offset 14, Frequency at offset 24
|
|
618
|
+
const isPowerMeter201 = model.id === 201;
|
|
619
|
+
const powerOffset = isPowerMeter201 ? 12 : 14; // Total Real Power offset
|
|
620
|
+
const powerSFOffset = isPowerMeter201 ? 15 : 17; // Power scale factor offset
|
|
621
|
+
const freqOffset = isPowerMeter201 ? 18 : 24; // Frequency offset
|
|
622
|
+
const freqSFOffset = isPowerMeter201 ? 21 : 27; // Frequency scale factor offset
|
|
623
|
+
const exportOffset = isPowerMeter201 ? 32 : 38; // Total Wh Exported offset
|
|
624
|
+
const importOffset = isPowerMeter201 ? 40 : 46; // Total Wh Imported offset
|
|
625
|
+
const energySFOffset = 4; // Energy scale factor is typically at offset 4
|
|
626
|
+
// Read scale factors
|
|
627
|
+
const powerSF = await this.readRegisterValue(baseAddr + powerSFOffset, 1, 'int16');
|
|
628
|
+
const freqSF = await this.readRegisterValue(baseAddr + freqSFOffset, 1, 'int16');
|
|
629
|
+
const energySF = await this.readRegisterValue(baseAddr + energySFOffset, 1, 'int16');
|
|
630
|
+
console.log(`Meter Scale Factors: Power_SF=${powerSF}, Freq_SF=${freqSF}, Energy_SF=${energySF}`);
|
|
631
|
+
// Read raw values
|
|
632
|
+
const powerAddr = baseAddr + powerOffset;
|
|
633
|
+
const powerRaw = await this.readRegisterValue(powerAddr, 1, 'int16');
|
|
634
|
+
console.log(`Meter Total Power: address=${powerAddr}, raw=0x${powerRaw.toString(16).toUpperCase()} (${powerRaw}), SF=${powerSF}`);
|
|
635
|
+
const freqAddr = baseAddr + freqOffset;
|
|
636
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
637
|
+
console.log(`Meter Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw}), SF=${freqSF}`);
|
|
638
|
+
const exportAddr = baseAddr + exportOffset;
|
|
639
|
+
const exportRaw = await this.readRegisterValue(exportAddr, 2, 'acc32');
|
|
640
|
+
console.log(`Meter Export Energy: address=${exportAddr}, raw=0x${exportRaw.toString(16).toUpperCase()} (${exportRaw}), SF=${energySF}`);
|
|
641
|
+
const importAddr = baseAddr + importOffset;
|
|
642
|
+
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
643
|
+
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
468
644
|
return {
|
|
469
645
|
blockNumber: model.id,
|
|
470
|
-
totalPower:
|
|
471
|
-
frequency:
|
|
646
|
+
totalPower: this.applyScaleFactor(powerRaw, powerSF, 'int16'),
|
|
647
|
+
frequency: this.applyScaleFactor(freqRaw, freqSF),
|
|
648
|
+
exportedEnergy: !this.isUnimplementedValue(exportRaw, 'uint32')
|
|
649
|
+
? BigInt(exportRaw) * BigInt(Math.pow(10, energySF))
|
|
650
|
+
: undefined,
|
|
651
|
+
importedEnergy: !this.isUnimplementedValue(importRaw, 'uint32')
|
|
652
|
+
? BigInt(importRaw) * BigInt(Math.pow(10, energySF))
|
|
653
|
+
: undefined
|
|
472
654
|
};
|
|
473
655
|
}
|
|
474
656
|
catch (error) {
|
|
@@ -486,15 +668,31 @@ class SunspecModbusClient {
|
|
|
486
668
|
return null;
|
|
487
669
|
}
|
|
488
670
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
671
|
+
console.log(`Reading Common Block - Model address: ${model.address}, Base address for data: ${baseAddr}`);
|
|
489
672
|
try {
|
|
490
673
|
// Read all strings using fault-tolerant reader with proper data type conversion
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
const
|
|
497
|
-
|
|
674
|
+
// Common block offsets are relative to the start of the data (after ID and Length)
|
|
675
|
+
const manufacturerAddr = baseAddr; // Offset 0-15 (16 registers) from data start
|
|
676
|
+
const modelAddr = baseAddr + 16; // Offset 16-31 (16 registers) from data start
|
|
677
|
+
const optionsAddr = baseAddr + 32; // Offset 32-39 (8 registers) from data start
|
|
678
|
+
const versionAddr = baseAddr + 40; // Offset 40-47 (8 registers) from data start
|
|
679
|
+
const serialAddr = baseAddr + 48; // Offset 48-63 (16 registers) from data start
|
|
680
|
+
const deviceAddrAddr = baseAddr + 64; // Offset 64 from data start
|
|
681
|
+
console.log(`Reading manufacturer from address ${manufacturerAddr} (16 registers)`);
|
|
682
|
+
const manufacturer = await this.readRegisterValue(manufacturerAddr, 16, 'string');
|
|
683
|
+
console.log(`Manufacturer raw value: "${manufacturer}"`);
|
|
684
|
+
console.log(`Reading model from address ${modelAddr} (16 registers)`);
|
|
685
|
+
const modelName = await this.readRegisterValue(modelAddr, 16, 'string');
|
|
686
|
+
console.log(`Model raw value: "${modelName}"`);
|
|
687
|
+
console.log(`Reading options from address ${optionsAddr} (8 registers)`);
|
|
688
|
+
const options = await this.readRegisterValue(optionsAddr, 8, 'string');
|
|
689
|
+
console.log(`Reading version from address ${versionAddr} (8 registers)`);
|
|
690
|
+
const version = await this.readRegisterValue(versionAddr, 8, 'string');
|
|
691
|
+
console.log(`Reading serial from address ${serialAddr} (16 registers)`);
|
|
692
|
+
const serialNumber = await this.readRegisterValue(serialAddr, 16, 'string');
|
|
693
|
+
console.log(`Reading device address from address ${deviceAddrAddr}`);
|
|
694
|
+
const deviceAddress = await this.readRegisterValue(deviceAddrAddr, 1, 'uint16');
|
|
695
|
+
const result = {
|
|
498
696
|
manufacturer: manufacturer,
|
|
499
697
|
model: modelName,
|
|
500
698
|
options: options,
|
|
@@ -502,6 +700,8 @@ class SunspecModbusClient {
|
|
|
502
700
|
serialNumber: serialNumber,
|
|
503
701
|
deviceAddress: deviceAddress
|
|
504
702
|
};
|
|
703
|
+
console.log('Common Block Data:', JSON.stringify(result, null, 2));
|
|
704
|
+
return result;
|
|
505
705
|
}
|
|
506
706
|
catch (error) {
|
|
507
707
|
console.error(`Error reading common block: ${error}`);
|
|
@@ -44,6 +44,11 @@ export declare class SunspecModbusClient {
|
|
|
44
44
|
* Convert unsigned 16-bit value to signed
|
|
45
45
|
*/
|
|
46
46
|
private convertToSigned16;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a value is "unimplemented" according to Sunspec
|
|
49
|
+
* Returns true if the value represents an unimplemented/not applicable register
|
|
50
|
+
*/
|
|
51
|
+
private isUnimplementedValue;
|
|
47
52
|
/**
|
|
48
53
|
* Helper to clean string values by removing null characters
|
|
49
54
|
*/
|
|
@@ -66,6 +71,7 @@ export declare class SunspecModbusClient {
|
|
|
66
71
|
private readInverterScaleFactors;
|
|
67
72
|
/**
|
|
68
73
|
* Apply scale factor to a value
|
|
74
|
+
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
69
75
|
*/
|
|
70
76
|
private applyScaleFactor;
|
|
71
77
|
/**
|
package/dist/cjs/version.cjs
CHANGED
package/dist/cjs/version.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import { ApplianceManager, EnergyApp } from "@enyo-energy/energy-app-sdk";
|
|
|
2
2
|
import { EnyoApplianceName } from "@enyo-energy/energy-app-sdk/dist/types/enyo-appliance.js";
|
|
3
3
|
import { EnyoNetworkDevice } from "@enyo-energy/energy-app-sdk/dist/types/enyo-network-device.js";
|
|
4
4
|
import { SunspecModbusClient } from "./sunspec-modbus-client.js";
|
|
5
|
-
import { EnyoDataBusMessage } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
|
|
5
|
+
import { EnyoDataBusMessage, EnyoDataBusMessageResolution } from "@enyo-energy/energy-app-sdk/dist/types/enyo-data-bus-value.js";
|
|
6
6
|
/**
|
|
7
7
|
* Base abstract class for all Sunspec devices
|
|
8
8
|
*/
|
|
@@ -27,7 +27,7 @@ export declare abstract class BaseSunspecDevice {
|
|
|
27
27
|
/**
|
|
28
28
|
* Update device data and return data bus messages
|
|
29
29
|
*/
|
|
30
|
-
abstract readData(clockId: string, resolution:
|
|
30
|
+
abstract readData(clockId: string, resolution: EnyoDataBusMessageResolution): Promise<EnyoDataBusMessage[]>;
|
|
31
31
|
/**
|
|
32
32
|
* Check if the device is connected
|
|
33
33
|
*/
|
|
@@ -83,5 +83,5 @@ export declare class SunspecMeter extends BaseSunspecDevice {
|
|
|
83
83
|
/**
|
|
84
84
|
* Update meter data and return data bus messages
|
|
85
85
|
*/
|
|
86
|
-
readData(clockId: string, resolution:
|
|
86
|
+
readData(clockId: string, resolution: EnyoDataBusMessageResolution): Promise<EnyoDataBusMessage[]>;
|
|
87
87
|
}
|
package/dist/sunspec-devices.js
CHANGED
|
@@ -99,6 +99,15 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
99
99
|
const inverterData = await this.sunspecClient.readInverterData();
|
|
100
100
|
const mpptDataList = await this.sunspecClient.readAllMPPTData();
|
|
101
101
|
if (inverterData) {
|
|
102
|
+
// Log what we're sending
|
|
103
|
+
console.log('Preparing Inverter Message:', {
|
|
104
|
+
pvPowerW: inverterData.dcPower,
|
|
105
|
+
acPower: inverterData.acPower,
|
|
106
|
+
voltageL1: inverterData.voltageAN,
|
|
107
|
+
voltageL2: inverterData.voltageBN,
|
|
108
|
+
voltageL3: inverterData.voltageCN,
|
|
109
|
+
stringsCount: mpptDataList.length
|
|
110
|
+
});
|
|
102
111
|
const inverterMessage = {
|
|
103
112
|
id: randomUUID(),
|
|
104
113
|
message: EnyoDataBusMessageEnum.InverterValuesUpdateV1,
|
|
@@ -109,12 +118,12 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
109
118
|
timestampIso: timestamp.toISOString(),
|
|
110
119
|
resolution,
|
|
111
120
|
data: {
|
|
112
|
-
pvPowerW: inverterData.
|
|
113
|
-
activePowerLimitationW: inverterData.acPower || 0,
|
|
121
|
+
pvPowerW: inverterData.dcPower || 0, // Use DC power for PV power
|
|
122
|
+
activePowerLimitationW: inverterData.acPower || 0,
|
|
114
123
|
state: this.mapOperatingState(inverterData.operatingState),
|
|
115
124
|
voltageL1: inverterData.voltageAN || 0,
|
|
116
|
-
voltageL2: inverterData.voltageBN,
|
|
117
|
-
voltageL3: inverterData.voltageCN,
|
|
125
|
+
voltageL2: inverterData.voltageBN ?? undefined, // Use undefined if unimplemented phase
|
|
126
|
+
voltageL3: inverterData.voltageCN ?? undefined, // Use undefined if unimplemented phase
|
|
118
127
|
strings: this.mapMPPTToStrings(mpptDataList)
|
|
119
128
|
}
|
|
120
129
|
};
|
|
@@ -148,11 +157,14 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
148
157
|
mapMPPTToStrings(mpptDataList) {
|
|
149
158
|
const result = [];
|
|
150
159
|
mpptDataList.forEach((mppt, index) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
// Only include strings with valid data
|
|
161
|
+
if (mppt.dcVoltage !== undefined || mppt.dcPower !== undefined) {
|
|
162
|
+
result.push({
|
|
163
|
+
index: index + 1,
|
|
164
|
+
voltage: mppt.dcVoltage ?? undefined, // Use null if undefined
|
|
165
|
+
powerW: mppt.dcPower ?? 0 // Default to 0 if undefined
|
|
166
|
+
});
|
|
167
|
+
}
|
|
156
168
|
});
|
|
157
169
|
return result;
|
|
158
170
|
}
|
|
@@ -44,6 +44,11 @@ export declare class SunspecModbusClient {
|
|
|
44
44
|
* Convert unsigned 16-bit value to signed
|
|
45
45
|
*/
|
|
46
46
|
private convertToSigned16;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a value is "unimplemented" according to Sunspec
|
|
49
|
+
* Returns true if the value represents an unimplemented/not applicable register
|
|
50
|
+
*/
|
|
51
|
+
private isUnimplementedValue;
|
|
47
52
|
/**
|
|
48
53
|
* Helper to clean string values by removing null characters
|
|
49
54
|
*/
|
|
@@ -66,6 +71,7 @@ export declare class SunspecModbusClient {
|
|
|
66
71
|
private readInverterScaleFactors;
|
|
67
72
|
/**
|
|
68
73
|
* Apply scale factor to a value
|
|
74
|
+
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
69
75
|
*/
|
|
70
76
|
private applyScaleFactor;
|
|
71
77
|
/**
|
|
@@ -186,6 +186,24 @@ export class SunspecModbusClient {
|
|
|
186
186
|
}
|
|
187
187
|
return value;
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Check if a value is "unimplemented" according to Sunspec
|
|
191
|
+
* Returns true if the value represents an unimplemented/not applicable register
|
|
192
|
+
*/
|
|
193
|
+
isUnimplementedValue(value, dataType = 'uint16') {
|
|
194
|
+
switch (dataType) {
|
|
195
|
+
case 'uint16':
|
|
196
|
+
return value === 0xFFFF || value === 65535;
|
|
197
|
+
case 'int16':
|
|
198
|
+
return value === 0x7FFF || value === 32767;
|
|
199
|
+
case 'uint32':
|
|
200
|
+
return value === 0xFFFFFFFF;
|
|
201
|
+
case 'int32':
|
|
202
|
+
return value === 0x7FFFFFFF;
|
|
203
|
+
default:
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
189
207
|
/**
|
|
190
208
|
* Helper to clean string values by removing null characters
|
|
191
209
|
*/
|
|
@@ -258,19 +276,36 @@ export class SunspecModbusClient {
|
|
|
258
276
|
console.error('No inverter model found');
|
|
259
277
|
return null;
|
|
260
278
|
}
|
|
279
|
+
console.warn('IMPORTANT: Working with single-phase inverter, but 3-phase is expected!');
|
|
261
280
|
return this.readSinglePhaseInverterData(singlePhaseModel);
|
|
262
281
|
}
|
|
282
|
+
console.log(`Found 3-phase inverter model 103 at address ${model.address} with length ${model.length}`);
|
|
263
283
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
264
284
|
try {
|
|
265
285
|
// Read all scale factors first using fault-tolerant reader
|
|
286
|
+
console.log(`Reading Inverter Data from Model 103 at base address: ${baseAddr}`);
|
|
266
287
|
const scaleFactors = await this.readInverterScaleFactors(baseAddr);
|
|
267
288
|
// Read values using fault-tolerant reader with proper data types
|
|
289
|
+
// Read raw voltage values to check for unimplemented phases
|
|
290
|
+
const voltageANRaw = await this.readRegisterValue(baseAddr + 10, 1, 'uint16');
|
|
291
|
+
const voltageBNRaw = await this.readRegisterValue(baseAddr + 11, 1, 'uint16');
|
|
292
|
+
const voltageCNRaw = await this.readRegisterValue(baseAddr + 12, 1, 'uint16');
|
|
293
|
+
console.log('Inverter Raw Voltage Values:', {
|
|
294
|
+
voltageANRaw: `0x${voltageANRaw.toString(16).toUpperCase()} (${voltageANRaw})`,
|
|
295
|
+
voltageBNRaw: `0x${voltageBNRaw.toString(16).toUpperCase()} (${voltageBNRaw})`,
|
|
296
|
+
voltageCNRaw: `0x${voltageCNRaw.toString(16).toUpperCase()} (${voltageCNRaw})`
|
|
297
|
+
});
|
|
268
298
|
const data = {
|
|
269
299
|
blockNumber: 103,
|
|
270
300
|
blockAddress: model.address,
|
|
271
301
|
blockLength: model.length,
|
|
272
302
|
// AC Current values - Offsets 2-5
|
|
273
|
-
acCurrent: this.applyScaleFactor(await
|
|
303
|
+
acCurrent: this.applyScaleFactor(await (async () => {
|
|
304
|
+
const addr = baseAddr + 2;
|
|
305
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
306
|
+
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
307
|
+
return raw;
|
|
308
|
+
})(), scaleFactors.A_SF),
|
|
274
309
|
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
275
310
|
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
276
311
|
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
@@ -278,20 +313,41 @@ export class SunspecModbusClient {
|
|
|
278
313
|
voltageAB: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 7, 1, 'uint16'), scaleFactors.V_SF),
|
|
279
314
|
voltageBC: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 8, 1, 'uint16'), scaleFactors.V_SF),
|
|
280
315
|
voltageCA: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 9, 1, 'uint16'), scaleFactors.V_SF),
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
316
|
+
// 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),
|
|
284
320
|
// Power values - Offsets 14, 18, 20, 22
|
|
285
|
-
acPower: this.applyScaleFactor(await
|
|
321
|
+
acPower: this.applyScaleFactor(await (async () => {
|
|
322
|
+
const addr = baseAddr + 14;
|
|
323
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
324
|
+
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
325
|
+
return raw;
|
|
326
|
+
})(), scaleFactors.W_SF, 'int16'),
|
|
286
327
|
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
287
328
|
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
288
329
|
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
289
330
|
// Frequency - Offset 16
|
|
290
331
|
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
291
332
|
// DC values - Offsets 27, 28, 30
|
|
292
|
-
dcCurrent: this.applyScaleFactor(await
|
|
293
|
-
|
|
294
|
-
|
|
333
|
+
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
334
|
+
const addr = baseAddr + 27;
|
|
335
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
336
|
+
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
337
|
+
return raw;
|
|
338
|
+
})(), scaleFactors.DCA_SF),
|
|
339
|
+
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
340
|
+
const addr = baseAddr + 28;
|
|
341
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
342
|
+
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
343
|
+
return raw;
|
|
344
|
+
})(), scaleFactors.DCV_SF),
|
|
345
|
+
dcPower: this.applyScaleFactor(await (async () => {
|
|
346
|
+
const addr = baseAddr + 30;
|
|
347
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
348
|
+
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
349
|
+
return raw;
|
|
350
|
+
})(), scaleFactors.DCW_SF, 'int16'),
|
|
295
351
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
296
352
|
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
297
353
|
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
@@ -309,8 +365,21 @@ export class SunspecModbusClient {
|
|
|
309
365
|
vendorEvents4: await this.readRegisterValue(baseAddr + 50, 2, 'uint32')
|
|
310
366
|
};
|
|
311
367
|
// Read AC Energy (32-bit accumulator) - Offset 24-25
|
|
312
|
-
const
|
|
368
|
+
const acEnergyAddr = baseAddr + 24;
|
|
369
|
+
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
370
|
+
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
313
371
|
data.acEnergy = BigInt(acEnergy) * BigInt(Math.pow(10, scaleFactors.WH_SF));
|
|
372
|
+
// Log final calculated values
|
|
373
|
+
console.log('Inverter Final Values:', {
|
|
374
|
+
acPower: data.acPower,
|
|
375
|
+
dcPower: data.dcPower,
|
|
376
|
+
voltageAN: data.voltageAN,
|
|
377
|
+
voltageBN: data.voltageBN,
|
|
378
|
+
voltageCN: data.voltageCN,
|
|
379
|
+
dcVoltage: data.dcVoltage,
|
|
380
|
+
dcCurrent: data.dcCurrent,
|
|
381
|
+
acCurrent: data.acCurrent
|
|
382
|
+
});
|
|
314
383
|
return data;
|
|
315
384
|
}
|
|
316
385
|
catch (error) {
|
|
@@ -326,15 +395,53 @@ export class SunspecModbusClient {
|
|
|
326
395
|
// Implementation would be similar but simplified
|
|
327
396
|
const baseAddr = model.address + 2;
|
|
328
397
|
try {
|
|
329
|
-
|
|
398
|
+
console.log(`Reading Single-Phase Inverter Data from Model 101 at base address: ${baseAddr}`);
|
|
399
|
+
// Read scale factors for single phase model
|
|
400
|
+
const scaleFactors = {
|
|
401
|
+
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'),
|
|
402
|
+
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'),
|
|
403
|
+
W_SF: await this.readRegisterValue(baseAddr + 10, 1, 'int16'),
|
|
404
|
+
Hz_SF: await this.readRegisterValue(baseAddr + 12, 1, 'int16'),
|
|
405
|
+
DCA_SF: await this.readRegisterValue(baseAddr + 18, 1, 'int16'),
|
|
406
|
+
DCV_SF: await this.readRegisterValue(baseAddr + 19, 1, 'int16'),
|
|
407
|
+
DCW_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16')
|
|
408
|
+
};
|
|
409
|
+
console.log('Single-Phase Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
410
|
+
// Read raw values with logging
|
|
411
|
+
const acCurrentAddr = baseAddr + 2;
|
|
412
|
+
const acCurrentRaw = await this.readRegisterValue(acCurrentAddr, 1, 'uint16');
|
|
413
|
+
console.log(`AC Current: address=${acCurrentAddr}, raw=0x${acCurrentRaw.toString(16).toUpperCase()} (${acCurrentRaw})`);
|
|
414
|
+
const voltageAddr = baseAddr + 7;
|
|
415
|
+
const voltageRaw = await this.readRegisterValue(voltageAddr, 1, 'uint16');
|
|
416
|
+
console.log(`AC Voltage: address=${voltageAddr}, raw=0x${voltageRaw.toString(16).toUpperCase()} (${voltageRaw})`);
|
|
417
|
+
const acPowerAddr = baseAddr + 9;
|
|
418
|
+
const acPowerRaw = await this.readRegisterValue(acPowerAddr, 1, 'int16');
|
|
419
|
+
console.log(`AC Power: address=${acPowerAddr}, raw=0x${acPowerRaw.toString(16).toUpperCase()} (${acPowerRaw}), SF=${scaleFactors.W_SF}`);
|
|
420
|
+
const freqAddr = baseAddr + 11;
|
|
421
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
422
|
+
console.log(`Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw})`);
|
|
423
|
+
const dcCurrentAddr = baseAddr + 14;
|
|
424
|
+
const dcCurrentRaw = await this.readRegisterValue(dcCurrentAddr, 1, 'uint16');
|
|
425
|
+
console.log(`DC Current: address=${dcCurrentAddr}, raw=0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`);
|
|
426
|
+
const dcVoltageAddr = baseAddr + 15;
|
|
427
|
+
const dcVoltageRaw = await this.readRegisterValue(dcVoltageAddr, 1, 'uint16');
|
|
428
|
+
console.log(`DC Voltage: address=${dcVoltageAddr}, raw=0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`);
|
|
429
|
+
const dcPowerAddr = baseAddr + 20;
|
|
430
|
+
const dcPowerRaw = await this.readRegisterValue(dcPowerAddr, 1, 'int16');
|
|
431
|
+
console.log(`DC Power: address=${dcPowerAddr}, raw=0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw}), SF=${scaleFactors.DCW_SF}`);
|
|
432
|
+
const stateAddr = baseAddr + 24;
|
|
433
|
+
const stateRaw = await this.readRegisterValue(stateAddr, 1, 'uint16');
|
|
434
|
+
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
330
435
|
return {
|
|
331
436
|
blockNumber: 101,
|
|
332
|
-
voltageAN:
|
|
333
|
-
acCurrent:
|
|
334
|
-
acPower:
|
|
335
|
-
frequency:
|
|
336
|
-
|
|
337
|
-
|
|
437
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
438
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
439
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
440
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
441
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
442
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
443
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
444
|
+
operatingState: stateRaw
|
|
338
445
|
};
|
|
339
446
|
}
|
|
340
447
|
catch (error) {
|
|
@@ -346,7 +453,7 @@ export class SunspecModbusClient {
|
|
|
346
453
|
* Read inverter scale factors
|
|
347
454
|
*/
|
|
348
455
|
async readInverterScaleFactors(baseAddr) {
|
|
349
|
-
|
|
456
|
+
const scaleFactors = {
|
|
350
457
|
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'), // Offset 6
|
|
351
458
|
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'), // Offset 13
|
|
352
459
|
W_SF: await this.readRegisterValue(baseAddr + 15, 1, 'int16'), // Offset 15
|
|
@@ -360,11 +467,18 @@ export class SunspecModbusClient {
|
|
|
360
467
|
DCW_SF: await this.readRegisterValue(baseAddr + 31, 1, 'int16'), // Offset 31
|
|
361
468
|
Tmp_SF: await this.readRegisterValue(baseAddr + 36, 1, 'int16') // Offset 36
|
|
362
469
|
};
|
|
470
|
+
console.log('Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
471
|
+
return scaleFactors;
|
|
363
472
|
}
|
|
364
473
|
/**
|
|
365
474
|
* Apply scale factor to a value
|
|
475
|
+
* Returns undefined if the value is unimplemented or scale factor is out of range
|
|
366
476
|
*/
|
|
367
|
-
applyScaleFactor(value, scaleFactor) {
|
|
477
|
+
applyScaleFactor(value, scaleFactor, dataType = 'uint16') {
|
|
478
|
+
// Check for unimplemented values
|
|
479
|
+
if (this.isUnimplementedValue(value, dataType)) {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
368
482
|
return value * Math.pow(10, scaleFactor);
|
|
369
483
|
}
|
|
370
484
|
/**
|
|
@@ -390,6 +504,28 @@ export class SunspecModbusClient {
|
|
|
390
504
|
DCWH_SF: await this.readRegisterValue(moduleAddr + 17, 1, 'int16'),
|
|
391
505
|
Tmp_SF: await this.readRegisterValue(moduleAddr + 21, 1, 'int16')
|
|
392
506
|
};
|
|
507
|
+
console.log(`MPPT Module ${moduleId} Scale Factors:`, JSON.stringify(scaleFactors, null, 2));
|
|
508
|
+
// Read raw values first
|
|
509
|
+
const dcCurrentRaw = await this.readRegisterValue(moduleAddr + 9, 1, 'uint16');
|
|
510
|
+
const dcVoltageRaw = await this.readRegisterValue(moduleAddr + 11, 1, 'uint16');
|
|
511
|
+
const dcPowerRaw = await this.readRegisterValue(moduleAddr + 13, 1, 'uint16');
|
|
512
|
+
const dcEnergyRaw = await this.readRegisterValue(moduleAddr + 15, 2, 'acc32');
|
|
513
|
+
const temperatureRaw = await this.readRegisterValue(moduleAddr + 20, 1, 'int16');
|
|
514
|
+
console.log(`MPPT Module ${moduleId} Raw Values:`, {
|
|
515
|
+
dcCurrentRaw: `0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`,
|
|
516
|
+
dcVoltageRaw: `0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`,
|
|
517
|
+
dcPowerRaw: `0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw})`,
|
|
518
|
+
dcEnergyRaw: `0x${dcEnergyRaw.toString(16).toUpperCase()} (${dcEnergyRaw})`,
|
|
519
|
+
temperatureRaw: `0x${temperatureRaw.toString(16).toUpperCase()} (${temperatureRaw})`
|
|
520
|
+
});
|
|
521
|
+
// Check if this module is actually implemented/connected
|
|
522
|
+
// If all key values are unimplemented, this module doesn't exist
|
|
523
|
+
if (this.isUnimplementedValue(dcCurrentRaw, 'uint16') &&
|
|
524
|
+
this.isUnimplementedValue(dcVoltageRaw, 'uint16') &&
|
|
525
|
+
this.isUnimplementedValue(dcPowerRaw, 'uint16')) {
|
|
526
|
+
console.log(`MPPT module ${moduleId} appears to be unconnected (all values are 0xFFFF)`);
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
393
529
|
const data = {
|
|
394
530
|
blockNumber: 160,
|
|
395
531
|
blockAddress: model.address,
|
|
@@ -398,22 +534,24 @@ export class SunspecModbusClient {
|
|
|
398
534
|
// String ID - Offset 1 (8 registers for string)
|
|
399
535
|
stringId: await this.readRegisterValue(moduleAddr + 1, 8, 'string'),
|
|
400
536
|
// DC Current - Offset 9
|
|
401
|
-
dcCurrent: this.applyScaleFactor(
|
|
537
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
402
538
|
dcCurrentSF: scaleFactors.DCA_SF,
|
|
403
539
|
// DC Voltage - Offset 11
|
|
404
|
-
dcVoltage: this.applyScaleFactor(
|
|
540
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
405
541
|
dcVoltageSF: scaleFactors.DCV_SF,
|
|
406
542
|
// DC Power - Offset 13
|
|
407
|
-
dcPower: this.applyScaleFactor(
|
|
543
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF),
|
|
408
544
|
dcPowerSF: scaleFactors.DCW_SF,
|
|
409
545
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
410
|
-
|
|
411
|
-
|
|
546
|
+
// Only calculate if value is not unimplemented
|
|
547
|
+
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'uint32')
|
|
548
|
+
? BigInt(dcEnergyRaw) * BigInt(Math.pow(10, scaleFactors.DCWH_SF))
|
|
549
|
+
: undefined,
|
|
412
550
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
413
551
|
// Timestamp - Offset 18-19 (32-bit)
|
|
414
552
|
timestamp: await this.readRegisterValue(moduleAddr + 18, 2, 'uint32'),
|
|
415
553
|
// Temperature - Offset 20
|
|
416
|
-
temperature: this.applyScaleFactor(
|
|
554
|
+
temperature: this.applyScaleFactor(temperatureRaw, scaleFactors.Tmp_SF, 'int16'),
|
|
417
555
|
temperatureSF: scaleFactors.Tmp_SF,
|
|
418
556
|
// Operating State - Offset 22
|
|
419
557
|
operatingState: await this.readRegisterValue(moduleAddr + 22, 1, 'uint16'),
|
|
@@ -436,9 +574,19 @@ export class SunspecModbusClient {
|
|
|
436
574
|
const mpptData = [];
|
|
437
575
|
// Try to read up to 4 MPPT strings (typical maximum)
|
|
438
576
|
for (let i = 1; i <= 4; i++) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
577
|
+
try {
|
|
578
|
+
const data = await this.readMPPTData(i);
|
|
579
|
+
// Only include if we got valid data (not null) and it has actual values
|
|
580
|
+
if (data &&
|
|
581
|
+
(data.dcCurrent !== undefined ||
|
|
582
|
+
data.dcVoltage !== undefined ||
|
|
583
|
+
data.dcPower !== undefined)) {
|
|
584
|
+
mpptData.push(data);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
console.debug(`Could not read MPPT module ${i}: ${error}`);
|
|
589
|
+
// Continue to try other modules
|
|
442
590
|
}
|
|
443
591
|
}
|
|
444
592
|
return mpptData;
|
|
@@ -459,13 +607,47 @@ export class SunspecModbusClient {
|
|
|
459
607
|
return null;
|
|
460
608
|
}
|
|
461
609
|
const baseAddr = model.address + 2;
|
|
610
|
+
console.log(`Reading Meter Data from Model ${model.id} at base address: ${baseAddr}`);
|
|
462
611
|
try {
|
|
463
|
-
//
|
|
464
|
-
//
|
|
612
|
+
// Model 201 (single phase) and 203 (3-phase) have different offsets
|
|
613
|
+
// Model 201: Power at offset 12, Frequency at offset 18
|
|
614
|
+
// Model 203: Power at offset 14, Frequency at offset 24
|
|
615
|
+
const isPowerMeter201 = model.id === 201;
|
|
616
|
+
const powerOffset = isPowerMeter201 ? 12 : 14; // Total Real Power offset
|
|
617
|
+
const powerSFOffset = isPowerMeter201 ? 15 : 17; // Power scale factor offset
|
|
618
|
+
const freqOffset = isPowerMeter201 ? 18 : 24; // Frequency offset
|
|
619
|
+
const freqSFOffset = isPowerMeter201 ? 21 : 27; // Frequency scale factor offset
|
|
620
|
+
const exportOffset = isPowerMeter201 ? 32 : 38; // Total Wh Exported offset
|
|
621
|
+
const importOffset = isPowerMeter201 ? 40 : 46; // Total Wh Imported offset
|
|
622
|
+
const energySFOffset = 4; // Energy scale factor is typically at offset 4
|
|
623
|
+
// Read scale factors
|
|
624
|
+
const powerSF = await this.readRegisterValue(baseAddr + powerSFOffset, 1, 'int16');
|
|
625
|
+
const freqSF = await this.readRegisterValue(baseAddr + freqSFOffset, 1, 'int16');
|
|
626
|
+
const energySF = await this.readRegisterValue(baseAddr + energySFOffset, 1, 'int16');
|
|
627
|
+
console.log(`Meter Scale Factors: Power_SF=${powerSF}, Freq_SF=${freqSF}, Energy_SF=${energySF}`);
|
|
628
|
+
// Read raw values
|
|
629
|
+
const powerAddr = baseAddr + powerOffset;
|
|
630
|
+
const powerRaw = await this.readRegisterValue(powerAddr, 1, 'int16');
|
|
631
|
+
console.log(`Meter Total Power: address=${powerAddr}, raw=0x${powerRaw.toString(16).toUpperCase()} (${powerRaw}), SF=${powerSF}`);
|
|
632
|
+
const freqAddr = baseAddr + freqOffset;
|
|
633
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
634
|
+
console.log(`Meter Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw}), SF=${freqSF}`);
|
|
635
|
+
const exportAddr = baseAddr + exportOffset;
|
|
636
|
+
const exportRaw = await this.readRegisterValue(exportAddr, 2, 'acc32');
|
|
637
|
+
console.log(`Meter Export Energy: address=${exportAddr}, raw=0x${exportRaw.toString(16).toUpperCase()} (${exportRaw}), SF=${energySF}`);
|
|
638
|
+
const importAddr = baseAddr + importOffset;
|
|
639
|
+
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
640
|
+
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
465
641
|
return {
|
|
466
642
|
blockNumber: model.id,
|
|
467
|
-
totalPower:
|
|
468
|
-
frequency:
|
|
643
|
+
totalPower: this.applyScaleFactor(powerRaw, powerSF, 'int16'),
|
|
644
|
+
frequency: this.applyScaleFactor(freqRaw, freqSF),
|
|
645
|
+
exportedEnergy: !this.isUnimplementedValue(exportRaw, 'uint32')
|
|
646
|
+
? BigInt(exportRaw) * BigInt(Math.pow(10, energySF))
|
|
647
|
+
: undefined,
|
|
648
|
+
importedEnergy: !this.isUnimplementedValue(importRaw, 'uint32')
|
|
649
|
+
? BigInt(importRaw) * BigInt(Math.pow(10, energySF))
|
|
650
|
+
: undefined
|
|
469
651
|
};
|
|
470
652
|
}
|
|
471
653
|
catch (error) {
|
|
@@ -483,15 +665,31 @@ export class SunspecModbusClient {
|
|
|
483
665
|
return null;
|
|
484
666
|
}
|
|
485
667
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
668
|
+
console.log(`Reading Common Block - Model address: ${model.address}, Base address for data: ${baseAddr}`);
|
|
486
669
|
try {
|
|
487
670
|
// Read all strings using fault-tolerant reader with proper data type conversion
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
|
|
671
|
+
// Common block offsets are relative to the start of the data (after ID and Length)
|
|
672
|
+
const manufacturerAddr = baseAddr; // Offset 0-15 (16 registers) from data start
|
|
673
|
+
const modelAddr = baseAddr + 16; // Offset 16-31 (16 registers) from data start
|
|
674
|
+
const optionsAddr = baseAddr + 32; // Offset 32-39 (8 registers) from data start
|
|
675
|
+
const versionAddr = baseAddr + 40; // Offset 40-47 (8 registers) from data start
|
|
676
|
+
const serialAddr = baseAddr + 48; // Offset 48-63 (16 registers) from data start
|
|
677
|
+
const deviceAddrAddr = baseAddr + 64; // Offset 64 from data start
|
|
678
|
+
console.log(`Reading manufacturer from address ${manufacturerAddr} (16 registers)`);
|
|
679
|
+
const manufacturer = await this.readRegisterValue(manufacturerAddr, 16, 'string');
|
|
680
|
+
console.log(`Manufacturer raw value: "${manufacturer}"`);
|
|
681
|
+
console.log(`Reading model from address ${modelAddr} (16 registers)`);
|
|
682
|
+
const modelName = await this.readRegisterValue(modelAddr, 16, 'string');
|
|
683
|
+
console.log(`Model raw value: "${modelName}"`);
|
|
684
|
+
console.log(`Reading options from address ${optionsAddr} (8 registers)`);
|
|
685
|
+
const options = await this.readRegisterValue(optionsAddr, 8, 'string');
|
|
686
|
+
console.log(`Reading version from address ${versionAddr} (8 registers)`);
|
|
687
|
+
const version = await this.readRegisterValue(versionAddr, 8, 'string');
|
|
688
|
+
console.log(`Reading serial from address ${serialAddr} (16 registers)`);
|
|
689
|
+
const serialNumber = await this.readRegisterValue(serialAddr, 16, 'string');
|
|
690
|
+
console.log(`Reading device address from address ${deviceAddrAddr}`);
|
|
691
|
+
const deviceAddress = await this.readRegisterValue(deviceAddrAddr, 1, 'uint16');
|
|
692
|
+
const result = {
|
|
495
693
|
manufacturer: manufacturer,
|
|
496
694
|
model: modelName,
|
|
497
695
|
options: options,
|
|
@@ -499,6 +697,8 @@ export class SunspecModbusClient {
|
|
|
499
697
|
serialNumber: serialNumber,
|
|
500
698
|
deviceAddress: deviceAddress
|
|
501
699
|
};
|
|
700
|
+
console.log('Common Block Data:', JSON.stringify(result, null, 2));
|
|
701
|
+
return result;
|
|
502
702
|
}
|
|
503
703
|
catch (error) {
|
|
504
704
|
console.error(`Error reading common block: ${error}`);
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED