@ya-modbus/driver-sdk 0.4.1-refactor-scope-driver-packages.0
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/CHANGELOG.md +27 -0
- package/LICENSE +674 -0
- package/README.md +357 -0
- package/dist/codec.d.ts +90 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +177 -0
- package/dist/codec.js.map +1 -0
- package/dist/errors.d.ts +64 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/validators.d.ts +75 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +85 -0
- package/dist/validators.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# @ya-modbus/driver-sdk
|
|
2
|
+
|
|
3
|
+
Runtime SDK for ya-modbus device drivers with transformation utilities and helpers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides reusable utilities for developing Modbus device drivers:
|
|
8
|
+
|
|
9
|
+
- **Codec functions**: Read/write scaled integer values from/to register buffers
|
|
10
|
+
- **Validators**: Type-safe configuration validation with TypeScript type narrowing
|
|
11
|
+
- **Error formatting**: Consistent validation error messages
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @ya-modbus/driver-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## API Reference
|
|
20
|
+
|
|
21
|
+
### Codec Functions
|
|
22
|
+
|
|
23
|
+
Utilities for reading and writing scaled integer values from Modbus register buffers.
|
|
24
|
+
|
|
25
|
+
#### `readScaledUInt16BE(buffer, offset, scale)`
|
|
26
|
+
|
|
27
|
+
Read and scale an unsigned 16-bit integer from a buffer.
|
|
28
|
+
|
|
29
|
+
**Parameters:**
|
|
30
|
+
|
|
31
|
+
- `buffer: Buffer` - Buffer containing register data
|
|
32
|
+
- `offset: number` - Byte offset to start reading from
|
|
33
|
+
- `scale: number` - Scale factor (e.g., 10 for ×10 values)
|
|
34
|
+
|
|
35
|
+
**Returns:** `number` - Scaled floating-point value
|
|
36
|
+
|
|
37
|
+
**Throws:** Error if scale is not a finite positive number
|
|
38
|
+
|
|
39
|
+
**Example:**
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { readScaledUInt16BE } from '@ya-modbus/driver-sdk'
|
|
43
|
+
|
|
44
|
+
// Device stores temperature as integer ×10 (235 = 23.5°C)
|
|
45
|
+
const buffer = await transport.readInputRegisters(0, 1)
|
|
46
|
+
const temperature = readScaledUInt16BE(buffer, 0, 10)
|
|
47
|
+
// temperature = 23.5
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### `readScaledInt16BE(buffer, offset, scale)`
|
|
51
|
+
|
|
52
|
+
Read and scale a signed 16-bit integer from a buffer.
|
|
53
|
+
|
|
54
|
+
**Parameters:**
|
|
55
|
+
|
|
56
|
+
- `buffer: Buffer` - Buffer containing register data
|
|
57
|
+
- `offset: number` - Byte offset to start reading from
|
|
58
|
+
- `scale: number` - Scale factor (e.g., 10 for ×10 values)
|
|
59
|
+
|
|
60
|
+
**Returns:** `number` - Scaled floating-point value
|
|
61
|
+
|
|
62
|
+
**Throws:** Error if scale is not a finite positive number
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { readScaledInt16BE } from '@ya-modbus/driver-sdk'
|
|
68
|
+
|
|
69
|
+
// Device stores correction offset as signed integer ×10 (-50 = -5.0°C)
|
|
70
|
+
const buffer = await transport.readHoldingRegisters(0x103, 1)
|
|
71
|
+
const correction = readScaledInt16BE(buffer, 0, 10)
|
|
72
|
+
// correction = -5.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### `readScaledUInt32BE(buffer, offset, scale)`
|
|
76
|
+
|
|
77
|
+
Read and scale an unsigned 32-bit integer from a buffer (2 consecutive registers).
|
|
78
|
+
|
|
79
|
+
**Parameters:**
|
|
80
|
+
|
|
81
|
+
- `buffer: Buffer` - Buffer containing register data
|
|
82
|
+
- `offset: number` - Byte offset to start reading from
|
|
83
|
+
- `scale: number` - Scale factor (e.g., 100 for ×100 values)
|
|
84
|
+
|
|
85
|
+
**Returns:** `number` - Scaled floating-point value
|
|
86
|
+
|
|
87
|
+
**Throws:** Error if scale is not a finite positive number
|
|
88
|
+
|
|
89
|
+
**Example:**
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { readScaledUInt32BE } from '@ya-modbus/driver-sdk'
|
|
93
|
+
|
|
94
|
+
// Device stores total energy as 32-bit integer ×100 (1000000 = 10000.00 kWh)
|
|
95
|
+
const buffer = await transport.readHoldingRegisters(0x0007, 2)
|
|
96
|
+
const totalEnergy = readScaledUInt32BE(buffer, 0, 100)
|
|
97
|
+
// totalEnergy = 10000.0
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `writeScaledUInt16BE(value, scale)`
|
|
101
|
+
|
|
102
|
+
Encode and scale a value to an unsigned 16-bit integer buffer.
|
|
103
|
+
|
|
104
|
+
**Parameters:**
|
|
105
|
+
|
|
106
|
+
- `value: number` - Value to encode
|
|
107
|
+
- `scale: number` - Scale factor (e.g., 10 for ×10 values)
|
|
108
|
+
|
|
109
|
+
**Returns:** `Buffer` - 2-byte buffer containing the scaled value
|
|
110
|
+
|
|
111
|
+
**Throws:** Error if value is not finite, scale is invalid, or scaled value exceeds uint16 range
|
|
112
|
+
|
|
113
|
+
**Example:**
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { writeScaledUInt16BE } from '@ya-modbus/driver-sdk'
|
|
117
|
+
|
|
118
|
+
// Write humidity correction of 5.5% (stored as 55)
|
|
119
|
+
const buffer = writeScaledUInt16BE(5.5, 10)
|
|
120
|
+
await transport.writeMultipleRegisters(0x104, buffer)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### `writeScaledInt16BE(value, scale)`
|
|
124
|
+
|
|
125
|
+
Encode and scale a value to a signed 16-bit integer buffer.
|
|
126
|
+
|
|
127
|
+
**Parameters:**
|
|
128
|
+
|
|
129
|
+
- `value: number` - Value to encode
|
|
130
|
+
- `scale: number` - Scale factor (e.g., 10 for ×10 values)
|
|
131
|
+
|
|
132
|
+
**Returns:** `Buffer` - 2-byte buffer containing the scaled value
|
|
133
|
+
|
|
134
|
+
**Throws:** Error if value is not finite, scale is invalid, or scaled value exceeds int16 range
|
|
135
|
+
|
|
136
|
+
**Example:**
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { writeScaledInt16BE } from '@ya-modbus/driver-sdk'
|
|
140
|
+
|
|
141
|
+
// Write temperature correction of -3.5°C (stored as -35)
|
|
142
|
+
const buffer = writeScaledInt16BE(-3.5, 10)
|
|
143
|
+
await transport.writeMultipleRegisters(0x103, buffer)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Validation Functions
|
|
147
|
+
|
|
148
|
+
Type-safe validators for configuration values with proper TypeScript type narrowing.
|
|
149
|
+
|
|
150
|
+
#### `createEnumValidator(values)`
|
|
151
|
+
|
|
152
|
+
Create a type-safe enum validator function.
|
|
153
|
+
|
|
154
|
+
**Parameters:**
|
|
155
|
+
|
|
156
|
+
- `values: readonly T[]` - Readonly array of valid enum values
|
|
157
|
+
|
|
158
|
+
**Returns:** `(value: unknown) => value is T[number]` - Type guard function
|
|
159
|
+
|
|
160
|
+
**Example:**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { createEnumValidator, formatEnumError } from '@ya-modbus/driver-sdk'
|
|
164
|
+
|
|
165
|
+
const VALID_BAUD_RATES = [9600, 14400, 19200] as const
|
|
166
|
+
type ValidBaudRate = (typeof VALID_BAUD_RATES)[number]
|
|
167
|
+
|
|
168
|
+
const isValidBaudRate = createEnumValidator(VALID_BAUD_RATES)
|
|
169
|
+
|
|
170
|
+
if (!isValidBaudRate(value)) {
|
|
171
|
+
throw new Error(formatEnumError('baud rate', VALID_BAUD_RATES))
|
|
172
|
+
}
|
|
173
|
+
// value is now typed as ValidBaudRate (9600 | 14400 | 19200)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `createRangeValidator(min, max)`
|
|
177
|
+
|
|
178
|
+
Create a numeric range validator function.
|
|
179
|
+
|
|
180
|
+
**Parameters:**
|
|
181
|
+
|
|
182
|
+
- `min: number` - Minimum valid value (inclusive)
|
|
183
|
+
- `max: number` - Maximum valid value (inclusive)
|
|
184
|
+
|
|
185
|
+
**Returns:** `(value: unknown) => value is number` - Validator function
|
|
186
|
+
|
|
187
|
+
**Example:**
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { createRangeValidator, formatRangeError } from '@ya-modbus/driver-sdk'
|
|
191
|
+
|
|
192
|
+
const isValidAddress = createRangeValidator(1, 247)
|
|
193
|
+
|
|
194
|
+
if (!isValidAddress(value)) {
|
|
195
|
+
throw new Error(formatRangeError('device address', 1, 247))
|
|
196
|
+
}
|
|
197
|
+
// value is a finite number between 1 and 247
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `isValidInteger(value)`
|
|
201
|
+
|
|
202
|
+
Validate that a value is a finite integer.
|
|
203
|
+
|
|
204
|
+
**Parameters:**
|
|
205
|
+
|
|
206
|
+
- `value: unknown` - Value to validate
|
|
207
|
+
|
|
208
|
+
**Returns:** `value is number` - True if value is a finite integer
|
|
209
|
+
|
|
210
|
+
**Example:**
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { isValidInteger } from '@ya-modbus/driver-sdk'
|
|
214
|
+
|
|
215
|
+
if (!isValidInteger(value)) {
|
|
216
|
+
throw new Error('Device address must be an integer')
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Error Formatting
|
|
221
|
+
|
|
222
|
+
Utilities for consistent validation error messages.
|
|
223
|
+
|
|
224
|
+
#### `formatRangeError(name, min, max)`
|
|
225
|
+
|
|
226
|
+
Format a range validation error message.
|
|
227
|
+
|
|
228
|
+
**Parameters:**
|
|
229
|
+
|
|
230
|
+
- `name: string` - Field name for the error message
|
|
231
|
+
- `min: number` - Minimum valid value
|
|
232
|
+
- `max: number` - Maximum valid value
|
|
233
|
+
|
|
234
|
+
**Returns:** `string` - Formatted error message
|
|
235
|
+
|
|
236
|
+
**Example:**
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { formatRangeError } from '@ya-modbus/driver-sdk'
|
|
240
|
+
|
|
241
|
+
formatRangeError('device address', 1, 247)
|
|
242
|
+
// => 'Invalid device address: must be between 1 and 247'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### `formatEnumError(name, values)`
|
|
246
|
+
|
|
247
|
+
Format an enum validation error message.
|
|
248
|
+
|
|
249
|
+
**Parameters:**
|
|
250
|
+
|
|
251
|
+
- `name: string` - Field name for the error message
|
|
252
|
+
- `values: readonly unknown[]` - Valid enum values
|
|
253
|
+
|
|
254
|
+
**Returns:** `string` - Formatted error message
|
|
255
|
+
|
|
256
|
+
**Example:**
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { formatEnumError } from '@ya-modbus/driver-sdk'
|
|
260
|
+
|
|
261
|
+
formatEnumError('baud rate', [9600, 14400, 19200])
|
|
262
|
+
// => 'Invalid baud rate: must be one of 9600, 14400, 19200'
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Edge Case Handling
|
|
266
|
+
|
|
267
|
+
All codec functions include comprehensive edge case validation:
|
|
268
|
+
|
|
269
|
+
- **Division by zero**: Scale must be greater than 0
|
|
270
|
+
- **Non-finite values**: NaN and Infinity are rejected
|
|
271
|
+
- **Integer overflow**: Values exceeding uint16/int16 ranges throw errors
|
|
272
|
+
- **Negative scale**: Scale must be positive
|
|
273
|
+
|
|
274
|
+
**Example:**
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// Throws: Invalid scale: must be greater than 0
|
|
278
|
+
readScaledUInt16BE(buffer, 0, 0)
|
|
279
|
+
|
|
280
|
+
// Throws: Invalid value: must be a finite number
|
|
281
|
+
writeScaledUInt16BE(NaN, 10)
|
|
282
|
+
|
|
283
|
+
// Throws: Invalid scaled value: 65536 is outside uint16 range (0 to 65535)
|
|
284
|
+
writeScaledUInt16BE(6553.6, 10)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## TypeScript Support
|
|
288
|
+
|
|
289
|
+
All functions include full TypeScript type definitions with type narrowing support:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const isValidBaudRate = createEnumValidator([9600, 14400, 19200] as const)
|
|
293
|
+
|
|
294
|
+
let value: unknown = getUserInput()
|
|
295
|
+
|
|
296
|
+
if (isValidBaudRate(value)) {
|
|
297
|
+
// TypeScript knows: value is 9600 | 14400 | 19200
|
|
298
|
+
const encoded = encodeBaudRate(value)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Usage in Drivers
|
|
303
|
+
|
|
304
|
+
Typical driver implementation pattern:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import type { DeviceDriver } from '@ya-modbus/driver-types'
|
|
308
|
+
import {
|
|
309
|
+
readScaledUInt16BE,
|
|
310
|
+
readScaledInt16BE,
|
|
311
|
+
writeScaledUInt16BE,
|
|
312
|
+
createEnumValidator,
|
|
313
|
+
formatEnumError,
|
|
314
|
+
} from '@ya-modbus/driver-sdk'
|
|
315
|
+
|
|
316
|
+
const VALID_BAUD_RATES = [9600, 14400, 19200] as const
|
|
317
|
+
const isValidBaudRate = createEnumValidator(VALID_BAUD_RATES)
|
|
318
|
+
|
|
319
|
+
export const createDriver = async (config): Promise<DeviceDriver> => {
|
|
320
|
+
// Validate configuration
|
|
321
|
+
if (!isValidBaudRate(config.baudRate)) {
|
|
322
|
+
throw new Error(formatEnumError('baud rate', VALID_BAUD_RATES))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
name: 'my-device',
|
|
327
|
+
|
|
328
|
+
async readDataPoint(id: string) {
|
|
329
|
+
if (id === 'temperature') {
|
|
330
|
+
const buffer = await transport.readInputRegisters(0, 1)
|
|
331
|
+
return readScaledUInt16BE(buffer, 0, 10)
|
|
332
|
+
}
|
|
333
|
+
if (id === 'correction') {
|
|
334
|
+
const buffer = await transport.readHoldingRegisters(0x103, 1)
|
|
335
|
+
return readScaledInt16BE(buffer, 0, 10)
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
async writeDataPoint(id: string, value: number) {
|
|
340
|
+
if (id === 'correction') {
|
|
341
|
+
const buffer = writeScaledInt16BE(value, 10)
|
|
342
|
+
await transport.writeMultipleRegisters(0x103, buffer)
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## See Also
|
|
350
|
+
|
|
351
|
+
- [Driver Development Guide](../../docs/DRIVER-DEVELOPMENT.md)
|
|
352
|
+
- [Driver Types](../driver-types/README.md)
|
|
353
|
+
- [Example Drivers](../driver-xymd1/)
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
GPL-3.0-or-later
|
package/dist/codec.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer encoding/decoding utilities for Modbus register values
|
|
3
|
+
*
|
|
4
|
+
* These utilities handle common patterns in Modbus drivers like reading
|
|
5
|
+
* scaled integers (×10, ×100, ×1000) from register buffers.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Read and scale an unsigned 16-bit integer from a buffer
|
|
9
|
+
*
|
|
10
|
+
* @param buffer - Buffer containing the register data
|
|
11
|
+
* @param offset - Byte offset to start reading from
|
|
12
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
13
|
+
* @returns Scaled floating-point value
|
|
14
|
+
* @throws Error if scale is not a finite positive number
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Device stores temperature as integer ×10 (235 = 23.5°C)
|
|
19
|
+
* const buffer = await transport.readInputRegisters(0, 1)
|
|
20
|
+
* const temperature = readScaledUInt16BE(buffer, 0, 10)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function readScaledUInt16BE(buffer: Buffer, offset: number, scale: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* Read and scale a signed 16-bit integer from a buffer
|
|
26
|
+
*
|
|
27
|
+
* @param buffer - Buffer containing the register data
|
|
28
|
+
* @param offset - Byte offset to start reading from
|
|
29
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
30
|
+
* @returns Scaled floating-point value
|
|
31
|
+
* @throws Error if scale is not a finite positive number
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // Device stores correction offset as signed integer ×10 (-50 = -5.0°C)
|
|
36
|
+
* const buffer = await transport.readHoldingRegisters(0x103, 1)
|
|
37
|
+
* const correction = readScaledInt16BE(buffer, 0, 10)
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function readScaledInt16BE(buffer: Buffer, offset: number, scale: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Read and scale an unsigned 32-bit integer from a buffer
|
|
43
|
+
*
|
|
44
|
+
* @param buffer - Buffer containing the register data (2 consecutive registers)
|
|
45
|
+
* @param offset - Byte offset to start reading from
|
|
46
|
+
* @param scale - Scale factor (e.g., 100 for ×100 values)
|
|
47
|
+
* @returns Scaled floating-point value
|
|
48
|
+
* @throws Error if scale is not a finite positive number
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Device stores total energy as 32-bit integer ×100 (1000000 = 10000.00 kWh)
|
|
53
|
+
* const buffer = await transport.readHoldingRegisters(0x0007, 2)
|
|
54
|
+
* const totalEnergy = readScaledUInt32BE(buffer, 0, 100)
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function readScaledUInt32BE(buffer: Buffer, offset: number, scale: number): number;
|
|
58
|
+
/**
|
|
59
|
+
* Encode and scale a value to an unsigned 16-bit integer buffer
|
|
60
|
+
*
|
|
61
|
+
* @param value - Value to encode
|
|
62
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
63
|
+
* @returns 2-byte buffer containing the scaled value
|
|
64
|
+
* @throws Error if value is not finite, scale is invalid, or scaled value exceeds uint16 range
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Write humidity correction of 5.5% (stored as 55)
|
|
69
|
+
* const buffer = writeScaledUInt16BE(5.5, 10)
|
|
70
|
+
* await transport.writeMultipleRegisters(0x104, buffer)
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function writeScaledUInt16BE(value: number, scale: number): Buffer;
|
|
74
|
+
/**
|
|
75
|
+
* Encode and scale a value to a signed 16-bit integer buffer
|
|
76
|
+
*
|
|
77
|
+
* @param value - Value to encode
|
|
78
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
79
|
+
* @returns 2-byte buffer containing the scaled value
|
|
80
|
+
* @throws Error if value is not finite, scale is invalid, or scaled value exceeds int16 range
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // Write temperature correction of -3.5°C (stored as -35)
|
|
85
|
+
* const buffer = writeScaledInt16BE(-3.5, 10)
|
|
86
|
+
* await transport.writeMultipleRegisters(0x103, buffer)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function writeScaledInt16BE(value: number, scale: number): Buffer;
|
|
90
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsEH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKvF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUxE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE"}
|
package/dist/codec.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer encoding/decoding utilities for Modbus register values
|
|
3
|
+
*
|
|
4
|
+
* These utilities handle common patterns in Modbus drivers like reading
|
|
5
|
+
* scaled integers (×10, ×100, ×1000) from register buffers.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validate that a scale parameter is valid
|
|
9
|
+
*
|
|
10
|
+
* @param scale - Scale factor to validate
|
|
11
|
+
* @throws Error if scale is not finite or is not positive
|
|
12
|
+
*/
|
|
13
|
+
function validateScale(scale) {
|
|
14
|
+
if (!Number.isFinite(scale)) {
|
|
15
|
+
throw new Error('Invalid scale: must be a finite number');
|
|
16
|
+
}
|
|
17
|
+
if (scale <= 0) {
|
|
18
|
+
throw new Error('Invalid scale: must be greater than 0');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate that a value for writing is valid
|
|
23
|
+
*
|
|
24
|
+
* @param value - Value to validate
|
|
25
|
+
* @throws Error if value is not finite
|
|
26
|
+
*/
|
|
27
|
+
function validateWriteValue(value) {
|
|
28
|
+
if (!Number.isFinite(value)) {
|
|
29
|
+
throw new Error('Invalid value: must be a finite number');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate that a scaled value fits within the target integer range
|
|
34
|
+
*
|
|
35
|
+
* @param scaledValue - Scaled value to validate
|
|
36
|
+
* @param min - Minimum allowed value (inclusive)
|
|
37
|
+
* @param max - Maximum allowed value (inclusive)
|
|
38
|
+
* @param typeName - Name of the target type for error messages
|
|
39
|
+
* @throws Error if scaled value is outside the valid range
|
|
40
|
+
*/
|
|
41
|
+
function validateRange(scaledValue, min, max, typeName) {
|
|
42
|
+
if (scaledValue < min || scaledValue > max) {
|
|
43
|
+
throw new Error(`Invalid scaled value: ${scaledValue} is outside ${typeName} range (${min} to ${max})`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate that buffer has sufficient bytes for reading at the given offset
|
|
48
|
+
*
|
|
49
|
+
* @param buffer - Buffer to validate
|
|
50
|
+
* @param offset - Byte offset to start reading from
|
|
51
|
+
* @param bytesNeeded - Number of bytes required
|
|
52
|
+
* @param typeName - Name of the type being read for error messages
|
|
53
|
+
* @throws Error if buffer doesn't have enough bytes
|
|
54
|
+
*/
|
|
55
|
+
function validateBufferBounds(buffer, offset, bytesNeeded, typeName) {
|
|
56
|
+
const available = buffer.length - offset;
|
|
57
|
+
if (available < bytesNeeded) {
|
|
58
|
+
throw new Error(`Insufficient buffer size for ${typeName}: need ${bytesNeeded} bytes at offset ${offset}, ` +
|
|
59
|
+
`but only ${available} bytes available (buffer length: ${buffer.length})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read and scale an unsigned 16-bit integer from a buffer
|
|
64
|
+
*
|
|
65
|
+
* @param buffer - Buffer containing the register data
|
|
66
|
+
* @param offset - Byte offset to start reading from
|
|
67
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
68
|
+
* @returns Scaled floating-point value
|
|
69
|
+
* @throws Error if scale is not a finite positive number
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // Device stores temperature as integer ×10 (235 = 23.5°C)
|
|
74
|
+
* const buffer = await transport.readInputRegisters(0, 1)
|
|
75
|
+
* const temperature = readScaledUInt16BE(buffer, 0, 10)
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function readScaledUInt16BE(buffer, offset, scale) {
|
|
79
|
+
validateScale(scale);
|
|
80
|
+
validateBufferBounds(buffer, offset, 2, 'uint16');
|
|
81
|
+
const rawValue = buffer.readUInt16BE(offset);
|
|
82
|
+
return rawValue / scale;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Read and scale a signed 16-bit integer from a buffer
|
|
86
|
+
*
|
|
87
|
+
* @param buffer - Buffer containing the register data
|
|
88
|
+
* @param offset - Byte offset to start reading from
|
|
89
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
90
|
+
* @returns Scaled floating-point value
|
|
91
|
+
* @throws Error if scale is not a finite positive number
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* // Device stores correction offset as signed integer ×10 (-50 = -5.0°C)
|
|
96
|
+
* const buffer = await transport.readHoldingRegisters(0x103, 1)
|
|
97
|
+
* const correction = readScaledInt16BE(buffer, 0, 10)
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export function readScaledInt16BE(buffer, offset, scale) {
|
|
101
|
+
validateScale(scale);
|
|
102
|
+
validateBufferBounds(buffer, offset, 2, 'int16');
|
|
103
|
+
const rawValue = buffer.readInt16BE(offset);
|
|
104
|
+
return rawValue / scale;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read and scale an unsigned 32-bit integer from a buffer
|
|
108
|
+
*
|
|
109
|
+
* @param buffer - Buffer containing the register data (2 consecutive registers)
|
|
110
|
+
* @param offset - Byte offset to start reading from
|
|
111
|
+
* @param scale - Scale factor (e.g., 100 for ×100 values)
|
|
112
|
+
* @returns Scaled floating-point value
|
|
113
|
+
* @throws Error if scale is not a finite positive number
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* // Device stores total energy as 32-bit integer ×100 (1000000 = 10000.00 kWh)
|
|
118
|
+
* const buffer = await transport.readHoldingRegisters(0x0007, 2)
|
|
119
|
+
* const totalEnergy = readScaledUInt32BE(buffer, 0, 100)
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function readScaledUInt32BE(buffer, offset, scale) {
|
|
123
|
+
validateScale(scale);
|
|
124
|
+
validateBufferBounds(buffer, offset, 4, 'uint32');
|
|
125
|
+
const rawValue = buffer.readUInt32BE(offset);
|
|
126
|
+
return rawValue / scale;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Encode and scale a value to an unsigned 16-bit integer buffer
|
|
130
|
+
*
|
|
131
|
+
* @param value - Value to encode
|
|
132
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
133
|
+
* @returns 2-byte buffer containing the scaled value
|
|
134
|
+
* @throws Error if value is not finite, scale is invalid, or scaled value exceeds uint16 range
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // Write humidity correction of 5.5% (stored as 55)
|
|
139
|
+
* const buffer = writeScaledUInt16BE(5.5, 10)
|
|
140
|
+
* await transport.writeMultipleRegisters(0x104, buffer)
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function writeScaledUInt16BE(value, scale) {
|
|
144
|
+
validateWriteValue(value);
|
|
145
|
+
validateScale(scale);
|
|
146
|
+
const scaledValue = Math.trunc(value * scale);
|
|
147
|
+
validateRange(scaledValue, 0, 0xffff, 'uint16');
|
|
148
|
+
const buffer = Buffer.allocUnsafe(2);
|
|
149
|
+
buffer.writeUInt16BE(scaledValue, 0);
|
|
150
|
+
return buffer;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Encode and scale a value to a signed 16-bit integer buffer
|
|
154
|
+
*
|
|
155
|
+
* @param value - Value to encode
|
|
156
|
+
* @param scale - Scale factor (e.g., 10 for ×10 values)
|
|
157
|
+
* @returns 2-byte buffer containing the scaled value
|
|
158
|
+
* @throws Error if value is not finite, scale is invalid, or scaled value exceeds int16 range
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* // Write temperature correction of -3.5°C (stored as -35)
|
|
163
|
+
* const buffer = writeScaledInt16BE(-3.5, 10)
|
|
164
|
+
* await transport.writeMultipleRegisters(0x103, buffer)
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export function writeScaledInt16BE(value, scale) {
|
|
168
|
+
validateWriteValue(value);
|
|
169
|
+
validateScale(scale);
|
|
170
|
+
// Use Math.trunc for predictable rounding toward zero (avoids floating-point precision issues)
|
|
171
|
+
const scaledValue = Math.trunc(value * scale);
|
|
172
|
+
validateRange(scaledValue, -0x8000, 0x7fff, 'int16');
|
|
173
|
+
const buffer = Buffer.allocUnsafe(2);
|
|
174
|
+
buffer.writeInt16BE(scaledValue, 0);
|
|
175
|
+
return buffer;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error formatting utilities for consistent validation error messages
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a range validation error message
|
|
6
|
+
*
|
|
7
|
+
* @param name - Field name for the error message
|
|
8
|
+
* @param min - Minimum valid value
|
|
9
|
+
* @param max - Maximum valid value
|
|
10
|
+
* @returns Formatted error message
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* formatRangeError('device address', 1, 247)
|
|
15
|
+
* // => 'Invalid device address: must be between 1 and 247'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatRangeError(name: string, min: number, max: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* Format a range validation error message with the actual value
|
|
21
|
+
*
|
|
22
|
+
* @param name - Field name for the error message
|
|
23
|
+
* @param value - Actual value that was rejected
|
|
24
|
+
* @param min - Minimum valid value
|
|
25
|
+
* @param max - Maximum valid value
|
|
26
|
+
* @returns Formatted error message
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* formatRangeError('device address', 256, 1, 247)
|
|
31
|
+
* // => 'Invalid device address: received 256, must be between 1 and 247'
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatRangeError(name: string, value: unknown, min: number, max: number): string;
|
|
35
|
+
/**
|
|
36
|
+
* Format an enum validation error message
|
|
37
|
+
*
|
|
38
|
+
* @param name - Field name for the error message
|
|
39
|
+
* @param values - Valid enum values
|
|
40
|
+
* @returns Formatted error message
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* formatEnumError('baud rate', [9600, 14400, 19200])
|
|
45
|
+
* // => 'Invalid baud rate: must be one of 9600, 14400, 19200'
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function formatEnumError(name: string, values: readonly unknown[]): string;
|
|
49
|
+
/**
|
|
50
|
+
* Format an enum validation error message with the actual value
|
|
51
|
+
*
|
|
52
|
+
* @param name - Field name for the error message
|
|
53
|
+
* @param value - Actual value that was rejected
|
|
54
|
+
* @param values - Valid enum values
|
|
55
|
+
* @returns Formatted error message
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* formatEnumError('baud rate', 115200, [9600, 14400, 19200])
|
|
60
|
+
* // => 'Invalid baud rate: received 115200, must be one of 9600, 14400, 19200'
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function formatEnumError(name: string, value: unknown, values: readonly unknown[]): string;
|
|
64
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;AAChF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;AAahG;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAAA;AACjF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAAA"}
|