@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/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
@@ -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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"}
@@ -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"}