@enyo-energy/sunspec-sdk 0.0.50 → 0.0.52

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.
@@ -19,22 +19,38 @@
19
19
  import { type SunspecInverterControls, type SunspecInverterData, type SunspecInverterSettings, type SunspecMeterData, type SunspecModel, type SunspecMPPTData, type SunspecBatteryData, type SunspecBatteryBaseData, type SunspecBatteryControls, SunspecStorageMode } from "./sunspec-interfaces.js";
20
20
  import { EnergyAppModbusDataType, IConnectionHealth } from "@enyo-energy/energy-app-sdk/dist/implementations/modbus/interfaces.js";
21
21
  import { EnergyApp } from "@enyo-energy/energy-app-sdk";
22
+ /**
23
+ * Get (or create) the singleton SunspecModbusClient for this network device.
24
+ * Increments the refcount; pair with `releaseSunspecClient` on teardown.
25
+ */
26
+ export declare function getOrCreateSunspecClient(energyApp: EnergyApp, host: string, port?: number): SunspecModbusClient;
27
+ /**
28
+ * Release a refcount on the network-device client. When the last consumer releases,
29
+ * the client is fully disconnected (all units closed) and removed from the registry.
30
+ *
31
+ * Releasing more times than acquired is a programming error and is logged; the count
32
+ * is clamped at zero so repeat releases are no-ops.
33
+ */
34
+ export declare function releaseSunspecClient(host: string, port?: number): Promise<void>;
22
35
  export declare class SunspecModbusClient {
23
36
  private energyApp;
24
- private modbusClient;
25
- private discoveredModels;
26
- private connected;
37
+ private modbusInstances;
38
+ private faultTolerantReaders;
39
+ private discoveredModelsByUnit;
27
40
  private connectionHealth;
28
- private faultTolerantReader;
29
41
  private modbusDataTypeConverter;
30
42
  private connectionParams;
43
+ private knownUnits;
31
44
  private autoReconnectEnabled;
32
45
  private openCount;
33
46
  private closeCount;
34
- private currentlyOpen;
47
+ private operationChain;
35
48
  constructor(energyApp: EnergyApp);
36
49
  /**
37
- * Connect to Modbus device
50
+ * Connect to a Modbus device unit. Multiple unit IDs on the same network device share this
51
+ * client; calling connect() with different unit IDs adds each as a separately-managed unit.
52
+ * The first call locks in the host/port for this client; later calls for a different host
53
+ * are rejected.
38
54
  * @param host Primary host (hostname) to connect to
39
55
  * @param port Modbus port (default 502)
40
56
  * @param unitId Modbus unit ID (default 1)
@@ -42,33 +58,75 @@ export declare class SunspecModbusClient {
42
58
  */
43
59
  connect(host: string, port?: number, unitId?: number, secondaryHost?: string): Promise<void>;
44
60
  /**
45
- * Disconnect from Modbus device.
61
+ * Disconnect from all units of this network device.
46
62
  *
47
- * Note: connection parameters are preserved so reconnect() can be called afterwards.
48
- * They will be overwritten by the next connect() call anyway.
63
+ * Note: connection parameters and the set of known units are preserved so reconnect() can
64
+ * be called afterwards. They will be overwritten by the next connect() call anyway.
49
65
  */
50
66
  disconnect(): Promise<void>;
51
67
  /**
52
- * Reconnect using stored connection parameters
53
- * First tries primaryHost (hostname), then falls back to secondaryHost (ipAddress) if available
54
- * Returns true if reconnection was successful, false otherwise
68
+ * Disconnect a single unit on this network device. Other units on the same device stay open.
69
+ */
70
+ disconnectUnit(unitId: number): Promise<void>;
71
+ /**
72
+ * Reconnect every previously-known unit on this network device using stored connection
73
+ * parameters. First tries primaryHost (hostname), then falls back to secondaryHost
74
+ * (ipAddress) if available. Returns true only if every known unit reconnected successfully.
55
75
  */
56
76
  reconnect(): Promise<boolean>;
57
77
  /**
58
- * Attempt to establish a connection to a specific host
59
- * Returns true if successful, false otherwise
78
+ * Reconnect a single previously-known unit on this network device. Other units on the
79
+ * same client stay open. Useful when one device's polling loop detects a dropped
80
+ * connection and only needs to recover its own unit, not thrash siblings.
81
+ *
82
+ * Returns true if the unit reconnected successfully (on primary or secondary host).
83
+ */
84
+ reconnectUnit(unitId: number): Promise<boolean>;
85
+ /**
86
+ * Attempt to (re)open every requested unit on a specific host. Caller must hold the
87
+ * connection lock. Closes any pre-existing per-unit instances first. Returns true only
88
+ * if every unit succeeded.
60
89
  */
61
90
  private attemptConnection;
91
+ /**
92
+ * Open a new Modbus instance for one unit ID and wire up its fault-tolerant reader.
93
+ * On any failure, the just-opened instance (if any) is closed so we never leak. Caller must
94
+ * hold the connection lock and ensure the unit is not already open.
95
+ */
96
+ private _openUnit;
97
+ /**
98
+ * Close the Modbus instance for a single unit. Idempotent. Caller must hold the
99
+ * connection lock.
100
+ */
101
+ private _closeUnit;
102
+ /**
103
+ * Run `fn` with exclusive access to connection-state transitions. Subsequent calls queue
104
+ * behind any in-flight one. A rejected `fn` does not poison the chain for later callers.
105
+ */
106
+ private withConnectionLock;
62
107
  private recordOpen;
63
108
  private recordClose;
64
109
  /**
65
- * Get cumulative open/close counts for this client. Useful for spotting connection leaks.
110
+ * Get cumulative open/close counts for this client (across all unit IDs). Useful for
111
+ * spotting connection leaks.
66
112
  */
67
113
  getConnectionStats(): {
68
114
  opens: number;
69
115
  closes: number;
70
- currentlyOpen: number;
116
+ openUnits: number;
71
117
  };
118
+ /**
119
+ * Get the EnergyAppModbusInstance for a unit, throwing if it isn't open.
120
+ */
121
+ private getInstance;
122
+ /**
123
+ * Get the fault-tolerant reader for a unit, throwing if it isn't open.
124
+ */
125
+ private getReader;
126
+ /**
127
+ * Get (or create) the discovered-models map for a unit.
128
+ */
129
+ private getModelsMap;
72
130
  /**
73
131
  * Enable or disable automatic reconnection
74
132
  */
@@ -80,20 +138,20 @@ export declare class SunspecModbusClient {
80
138
  /**
81
139
  * Detect the base address and addressing mode (0-based or 1-based) for SunSpec
82
140
  */
83
- detectSunspecBaseAddress(customBaseAddress?: number): Promise<{
141
+ detectSunspecBaseAddress(unitId: number, customBaseAddress?: number): Promise<{
84
142
  baseAddress: number;
85
143
  isZeroBased: boolean;
86
144
  nextAddress: number;
87
145
  }>;
88
146
  /**
89
- * Discover all available Sunspec models
147
+ * Discover all available Sunspec models for a unit
90
148
  * Automatically detects base address (40000 or 40001) and scans from there
91
149
  */
92
- discoverModels(customBaseAddress?: number): Promise<Map<number, SunspecModel>>;
150
+ discoverModels(unitId: number, customBaseAddress?: number): Promise<Map<number, SunspecModel>>;
93
151
  /**
94
- * Find a specific model by ID
152
+ * Find a specific model by ID for a given unit
95
153
  */
96
- findModel(modelId: number): SunspecModel | undefined;
154
+ findModel(unitId: number, modelId: number): SunspecModel | undefined;
97
155
  /**
98
156
  * Check if a value is "unimplemented" according to Sunspec specification
99
157
  * Returns true if the value represents an unimplemented/not applicable register
@@ -137,15 +195,15 @@ export declare class SunspecModbusClient {
137
195
  /**
138
196
  * Helper to read register value(s) using the fault-tolerant reader with data type conversion
139
197
  */
140
- readRegisterValue(address: number, quantity: number | undefined, dataType: EnergyAppModbusDataType): Promise<number | string | number[]>;
198
+ readRegisterValue(unitId: number, address: number, quantity: number | undefined, dataType: EnergyAppModbusDataType): Promise<number | string | number[]>;
141
199
  /**
142
200
  * Helper to write register value(s)
143
201
  */
144
- writeRegisterValue(address: number, value: number | number[], dataType?: EnergyAppModbusDataType): Promise<boolean>;
202
+ writeRegisterValue(unitId: number, address: number, value: number | number[], dataType?: EnergyAppModbusDataType): Promise<boolean>;
145
203
  /**
146
204
  * Read inverter data from Model 101 (Single Phase) / Model 103 (Three Phase)
147
205
  */
148
- readInverterData(): Promise<SunspecInverterData | null>;
206
+ readInverterData(unitId: number): Promise<SunspecInverterData | null>;
149
207
  /**
150
208
  * Read single phase inverter data (Model 101)
151
209
  */
@@ -175,7 +233,7 @@ export declare class SunspecModbusClient {
175
233
  * Extract MPPT scale factors from a pre-read model buffer
176
234
  */
177
235
  private extractMPPTScaleFactors;
178
- readMPPTScaleFactors(): Promise<{
236
+ readMPPTScaleFactors(unitId: number): Promise<{
179
237
  DCA_SF: number;
180
238
  DCV_SF: number;
181
239
  DCW_SF: number;
@@ -188,11 +246,11 @@ export declare class SunspecModbusClient {
188
246
  /**
189
247
  * Read MPPT data from Model 160
190
248
  */
191
- readMPPTData(moduleId?: number): Promise<SunspecMPPTData | null>;
249
+ readMPPTData(unitId: number, moduleId?: number): Promise<SunspecMPPTData | null>;
192
250
  /**
193
251
  * Read all MPPT strings from Model 160 (Multiple MPPT)
194
252
  */
195
- readAllMPPTData(): Promise<SunspecMPPTData[]>;
253
+ readAllMPPTData(unitId: number): Promise<SunspecMPPTData[]>;
196
254
  /**
197
255
  * Map battery charge state to human-readable name
198
256
  * @param state The numeric charge state value
@@ -214,67 +272,67 @@ export declare class SunspecModbusClient {
214
272
  /**
215
273
  * Read battery base data from Model 802 (Battery Base)
216
274
  */
217
- readBatteryBaseData(): Promise<SunspecBatteryBaseData | null>;
275
+ readBatteryBaseData(unitId: number): Promise<SunspecBatteryBaseData | null>;
218
276
  /**
219
277
  * Read battery data from Model 124 (Basic Storage) with fallback to Model 802 / Model 803
220
278
  */
221
- readBatteryData(): Promise<SunspecBatteryData | null>;
279
+ readBatteryData(unitId: number): Promise<SunspecBatteryData | null>;
222
280
  /**
223
281
  * Write battery control settings to Model 124
224
282
  */
225
- writeBatteryControls(controls: Partial<SunspecBatteryControls>): Promise<boolean>;
283
+ writeBatteryControls(unitId: number, controls: Partial<SunspecBatteryControls>): Promise<boolean>;
226
284
  /**
227
285
  * Set battery storage mode (simplified interface)
228
286
  */
229
- setStorageMode(mode: SunspecStorageMode): Promise<boolean>;
287
+ setStorageMode(unitId: number, mode: SunspecStorageMode): Promise<boolean>;
230
288
  /**
231
289
  * Enable or disable grid charging
232
290
  */
233
- enableGridCharging(enable: boolean): Promise<boolean>;
291
+ enableGridCharging(unitId: number, enable: boolean): Promise<boolean>;
234
292
  /**
235
293
  * Read battery control settings from Model 124 (Basic Storage Controls)
236
294
  */
237
- readBatteryControls(): Promise<SunspecBatteryControls | null>;
295
+ readBatteryControls(unitId: number): Promise<SunspecBatteryControls | null>;
238
296
  /**
239
297
  * Read meter data from Model 201 (Single Phase) / Model 203 (Three Phase) / Model 204 (Split Phase)
240
298
  */
241
- readMeterData(): Promise<SunspecMeterData | null>;
299
+ readMeterData(unitId: number): Promise<SunspecMeterData | null>;
242
300
  /**
243
301
  * Read common block data (Model 1)
244
302
  */
245
- readCommonBlock(): Promise<any>;
303
+ readCommonBlock(unitId: number): Promise<any>;
246
304
  /**
247
305
  * Get serial number from device
248
306
  */
249
- getSerialNumber(): Promise<string | undefined>;
307
+ getSerialNumber(unitId: number): Promise<string | undefined>;
250
308
  /**
251
- * Check if connected
309
+ * Check if a specific unit is connected on this network device
252
310
  */
253
- isConnected(): boolean;
311
+ isConnected(unitId: number): boolean;
254
312
  /**
255
- * Check if connection is healthy
313
+ * Check if a specific unit's connection is healthy
256
314
  */
257
- isHealthy(): boolean;
315
+ isHealthy(unitId: number): boolean;
258
316
  /**
259
- * Get connection health details
317
+ * Get connection health details (shared across all units on this network device)
260
318
  */
261
319
  getConnectionHealth(): IConnectionHealth;
262
320
  /**
263
321
  * Read inverter settings from Model 121 (Inverter Settings)
264
322
  */
265
- readInverterSettings(): Promise<SunspecInverterSettings | null>;
323
+ readInverterSettings(unitId: number): Promise<SunspecInverterSettings | null>;
266
324
  /**
267
325
  * Read inverter controls from Model 123 (Immediate Inverter Controls)
268
326
  */
269
- readInverterControls(): Promise<SunspecInverterControls | null>;
327
+ readInverterControls(unitId: number): Promise<SunspecInverterControls | null>;
270
328
  /**
271
329
  * Write Block 121 - Inverter Basic Settings
272
330
  */
273
- writeInverterSettings(settings: Partial<SunspecInverterSettings>): Promise<boolean>;
331
+ writeInverterSettings(unitId: number, settings: Partial<SunspecInverterSettings>): Promise<boolean>;
274
332
  /**
275
333
  * Write inverter controls to Model 123 (Immediate Inverter Controls)
276
334
  */
277
- writeInverterControls(controls: Partial<SunspecInverterControls>): Promise<boolean>;
335
+ writeInverterControls(unitId: number, controls: Partial<SunspecInverterControls>): Promise<boolean>;
278
336
  /**
279
337
  * Set the inverter feed-in power limit using Model 123 (Immediate Inverter Controls)
280
338
  *
@@ -285,5 +343,5 @@ export declare class SunspecModbusClient {
285
343
  * @param limitW - Power limit in Watts, or null to remove the limit
286
344
  * @returns true if successful, false otherwise
287
345
  */
288
- setFeedInLimit(limitW: number | null): Promise<boolean>;
346
+ setFeedInLimit(unitId: number, limitW: number | null): Promise<boolean>;
289
347
  }