@ya-modbus/cli 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.
Files changed (85) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +674 -0
  3. package/README.md +612 -0
  4. package/dist/bin/ya-modbus.d.ts +9 -0
  5. package/dist/bin/ya-modbus.d.ts.map +1 -0
  6. package/dist/bin/ya-modbus.js +10 -0
  7. package/dist/bin/ya-modbus.js.map +1 -0
  8. package/dist/src/commands/discover.d.ts +25 -0
  9. package/dist/src/commands/discover.d.ts.map +1 -0
  10. package/dist/src/commands/discover.js +160 -0
  11. package/dist/src/commands/discover.js.map +1 -0
  12. package/dist/src/commands/list-devices.d.ts +21 -0
  13. package/dist/src/commands/list-devices.d.ts.map +1 -0
  14. package/dist/src/commands/list-devices.js +75 -0
  15. package/dist/src/commands/list-devices.js.map +1 -0
  16. package/dist/src/commands/read.d.ts +25 -0
  17. package/dist/src/commands/read.d.ts.map +1 -0
  18. package/dist/src/commands/read.js +76 -0
  19. package/dist/src/commands/read.js.map +1 -0
  20. package/dist/src/commands/show-defaults.d.ts +21 -0
  21. package/dist/src/commands/show-defaults.d.ts.map +1 -0
  22. package/dist/src/commands/show-defaults.js +48 -0
  23. package/dist/src/commands/show-defaults.js.map +1 -0
  24. package/dist/src/commands/write.d.ts +26 -0
  25. package/dist/src/commands/write.d.ts.map +1 -0
  26. package/dist/src/commands/write.js +61 -0
  27. package/dist/src/commands/write.js.map +1 -0
  28. package/dist/src/discovery/constants.d.ts +37 -0
  29. package/dist/src/discovery/constants.d.ts.map +1 -0
  30. package/dist/src/discovery/constants.js +45 -0
  31. package/dist/src/discovery/constants.js.map +1 -0
  32. package/dist/src/discovery/device-identifier.d.ts +52 -0
  33. package/dist/src/discovery/device-identifier.d.ts.map +1 -0
  34. package/dist/src/discovery/device-identifier.js +193 -0
  35. package/dist/src/discovery/device-identifier.js.map +1 -0
  36. package/dist/src/discovery/parameter-generator-utils.d.ts +29 -0
  37. package/dist/src/discovery/parameter-generator-utils.d.ts.map +1 -0
  38. package/dist/src/discovery/parameter-generator-utils.js +59 -0
  39. package/dist/src/discovery/parameter-generator-utils.js.map +1 -0
  40. package/dist/src/discovery/parameter-generator.d.ts +97 -0
  41. package/dist/src/discovery/parameter-generator.d.ts.map +1 -0
  42. package/dist/src/discovery/parameter-generator.js +184 -0
  43. package/dist/src/discovery/parameter-generator.js.map +1 -0
  44. package/dist/src/discovery/progress.d.ts +24 -0
  45. package/dist/src/discovery/progress.d.ts.map +1 -0
  46. package/dist/src/discovery/progress.js +57 -0
  47. package/dist/src/discovery/progress.js.map +1 -0
  48. package/dist/src/discovery/scanner.d.ts +46 -0
  49. package/dist/src/discovery/scanner.d.ts.map +1 -0
  50. package/dist/src/discovery/scanner.js +143 -0
  51. package/dist/src/discovery/scanner.js.map +1 -0
  52. package/dist/src/formatters/discovery-results.d.ts +10 -0
  53. package/dist/src/formatters/discovery-results.d.ts.map +1 -0
  54. package/dist/src/formatters/discovery-results.js +57 -0
  55. package/dist/src/formatters/discovery-results.js.map +1 -0
  56. package/dist/src/formatters/json.d.ts +30 -0
  57. package/dist/src/formatters/json.d.ts.map +1 -0
  58. package/dist/src/formatters/json.js +33 -0
  59. package/dist/src/formatters/json.js.map +1 -0
  60. package/dist/src/formatters/performance.d.ts +19 -0
  61. package/dist/src/formatters/performance.d.ts.map +1 -0
  62. package/dist/src/formatters/performance.js +24 -0
  63. package/dist/src/formatters/performance.js.map +1 -0
  64. package/dist/src/formatters/table.d.ts +10 -0
  65. package/dist/src/formatters/table.d.ts.map +1 -0
  66. package/dist/src/formatters/table.js +91 -0
  67. package/dist/src/formatters/table.js.map +1 -0
  68. package/dist/src/index.d.ts +5 -0
  69. package/dist/src/index.d.ts.map +1 -0
  70. package/dist/src/index.js +154 -0
  71. package/dist/src/index.js.map +1 -0
  72. package/dist/src/utils/commands.d.ts +191 -0
  73. package/dist/src/utils/commands.d.ts.map +1 -0
  74. package/dist/src/utils/commands.js +400 -0
  75. package/dist/src/utils/commands.js.map +1 -0
  76. package/dist/src/utils/object-utils.d.ts +20 -0
  77. package/dist/src/utils/object-utils.d.ts.map +1 -0
  78. package/dist/src/utils/object-utils.js +28 -0
  79. package/dist/src/utils/object-utils.js.map +1 -0
  80. package/dist/src/utils/validation.d.ts +70 -0
  81. package/dist/src/utils/validation.d.ts.map +1 -0
  82. package/dist/src/utils/validation.js +158 -0
  83. package/dist/src/utils/validation.js.map +1 -0
  84. package/dist/tsconfig.tsbuildinfo +1 -0
  85. package/package.json +63 -0
@@ -0,0 +1,184 @@
1
+ import { MAX_SLAVE_ID, MIN_SLAVE_ID } from './constants.js';
2
+ import { getParameterArrays } from './parameter-generator-utils.js';
3
+ /**
4
+ * Clamp slave ID to valid Modbus range [1, 247]
5
+ */
6
+ function clampSlaveId(id) {
7
+ return Math.max(MIN_SLAVE_ID, Math.min(MAX_SLAVE_ID, id));
8
+ }
9
+ /**
10
+ * Generate ordered list of slave IDs to test
11
+ *
12
+ * Priority order:
13
+ * 1. Default address (if provided)
14
+ * 2. Address 1 (most common)
15
+ * 3. Address 2
16
+ * 4. Remaining addresses sequentially (3, 4, 5, ...)
17
+ */
18
+ function* generateSlaveIds(range, defaultAddress) {
19
+ const [minId, maxId] = range;
20
+ const start = clampSlaveId(minId);
21
+ const end = clampSlaveId(maxId);
22
+ const yielded = new Set();
23
+ // Helper to yield if not already yielded and in range
24
+ function* yieldIfValid(id) {
25
+ const clamped = clampSlaveId(id);
26
+ if (clamped >= start && clamped <= end && !yielded.has(clamped)) {
27
+ yielded.add(clamped);
28
+ yield clamped;
29
+ }
30
+ }
31
+ // 1. Default address first
32
+ if (defaultAddress !== undefined) {
33
+ yield* yieldIfValid(defaultAddress);
34
+ }
35
+ // 2. Common addresses (1, 2)
36
+ yield* yieldIfValid(1);
37
+ yield* yieldIfValid(2);
38
+ // 3. Remaining addresses sequentially
39
+ for (let id = start; id <= end; id++) {
40
+ if (!yielded.has(id)) {
41
+ yielded.add(id);
42
+ yield id;
43
+ }
44
+ }
45
+ }
46
+ /**
47
+ * Prioritize default configuration values in parameter arrays
48
+ *
49
+ * Moves default values to the front of arrays while preserving uniqueness
50
+ */
51
+ function prioritizeDefaults(array, defaultValue) {
52
+ if (defaultValue === undefined || !array.includes(defaultValue)) {
53
+ return array;
54
+ }
55
+ // Move default to front, remove duplicates
56
+ return [defaultValue, ...array.filter((v) => v !== defaultValue)];
57
+ }
58
+ /**
59
+ * Generate parameter combinations grouped by serial configuration
60
+ *
61
+ * This is a memory-efficient alternative to materializing all combinations.
62
+ * Instead of creating all ~1500+ combinations at once, this generator yields
63
+ * groups of ~247 combinations at a time (one per serial config).
64
+ *
65
+ * Each group shares the same serial parameters (baud, parity, data bits, stop bits)
66
+ * but has different slave IDs. This matches how the scanner works - it creates
67
+ * one connection per serial config and tests all slave IDs with that connection.
68
+ *
69
+ * @param options - Generation options
70
+ * @yields Parameter groups to test
71
+ *
72
+ * @example Iterate over groups without materializing all combinations
73
+ * ```typescript
74
+ * for (const group of generateParameterGroups(options)) {
75
+ * // Only this group's ~247 combinations are in memory
76
+ * const { serialParams, combinations } = group
77
+ * // ... use combinations ...
78
+ * }
79
+ * ```
80
+ */
81
+ export function* generateParameterGroups(options) {
82
+ const { strategy, defaultConfig, supportedConfig } = options;
83
+ // Get parameter arrays
84
+ let { baudRates, parities, dataBits, stopBits } = getParameterArrays(strategy, supportedConfig);
85
+ const { addressRange } = getParameterArrays(strategy, supportedConfig);
86
+ // Prioritize default values to test them first
87
+ if (defaultConfig) {
88
+ baudRates = prioritizeDefaults(baudRates, defaultConfig.baudRate);
89
+ parities = prioritizeDefaults(parities, defaultConfig.parity);
90
+ dataBits = prioritizeDefaults(dataBits, defaultConfig.dataBits);
91
+ stopBits = prioritizeDefaults(stopBits, defaultConfig.stopBits);
92
+ }
93
+ // Generate slave IDs once (reused for each serial config)
94
+ const slaveIds = Array.from(generateSlaveIds(addressRange, defaultConfig?.defaultAddress));
95
+ // Generate groups: iterate serial params (baud × parity × data × stop)
96
+ // For each serial config, create a group with all slave IDs
97
+ for (const baudRate of baudRates) {
98
+ for (const parity of parities) {
99
+ for (const dataBits_ of dataBits) {
100
+ for (const stopBits_ of stopBits) {
101
+ // Build combinations for this serial config
102
+ const combinations = slaveIds.map((slaveId) => ({
103
+ slaveId,
104
+ baudRate,
105
+ parity,
106
+ dataBits: dataBits_,
107
+ stopBits: stopBits_,
108
+ }));
109
+ yield {
110
+ serialParams: {
111
+ baudRate,
112
+ parity,
113
+ dataBits: dataBits_,
114
+ stopBits: stopBits_,
115
+ },
116
+ combinations,
117
+ };
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * Generate all parameter combinations for Modbus device discovery
125
+ *
126
+ * Produces combinations in priority order:
127
+ * 1. Default configuration (if provided) tested first
128
+ * 2. Common slave addresses (1, 2) before others
129
+ * 3. Common baud rates before exotic ones
130
+ *
131
+ * @param options - Generation options
132
+ * @yields Parameter combinations to test
133
+ *
134
+ * @example Quick discovery with driver config
135
+ * ```typescript
136
+ * const combinations = generateParameterCombinations({
137
+ * strategy: 'quick',
138
+ * defaultConfig: { baudRate: 9600, parity: 'even', ... },
139
+ * supportedConfig: { validBaudRates: [9600, 19200], ... }
140
+ * })
141
+ * ```
142
+ *
143
+ * @example Thorough discovery without driver
144
+ * ```typescript
145
+ * const combinations = generateParameterCombinations({
146
+ * strategy: 'thorough'
147
+ * })
148
+ * ```
149
+ */
150
+ export function* generateParameterCombinations(options) {
151
+ const { strategy, defaultConfig, supportedConfig } = options;
152
+ // Get parameter arrays
153
+ let { baudRates, parities, dataBits, stopBits } = getParameterArrays(strategy, supportedConfig);
154
+ const { addressRange } = getParameterArrays(strategy, supportedConfig);
155
+ // Prioritize default values to test them first
156
+ if (defaultConfig) {
157
+ baudRates = prioritizeDefaults(baudRates, defaultConfig.baudRate);
158
+ parities = prioritizeDefaults(parities, defaultConfig.parity);
159
+ dataBits = prioritizeDefaults(dataBits, defaultConfig.dataBits);
160
+ stopBits = prioritizeDefaults(stopBits, defaultConfig.stopBits);
161
+ }
162
+ // Generate combinations in priority order:
163
+ // - Default config combination first
164
+ // - Then iterate: slave IDs (prioritized) × baud rates (prioritized) × parity × data × stop
165
+ const slaveIds = Array.from(generateSlaveIds(addressRange, defaultConfig?.defaultAddress));
166
+ for (const slaveId of slaveIds) {
167
+ for (const baudRate of baudRates) {
168
+ for (const parity of parities) {
169
+ for (const dataBits_ of dataBits) {
170
+ for (const stopBits_ of stopBits) {
171
+ yield {
172
+ slaveId,
173
+ baudRate,
174
+ parity,
175
+ dataBits: dataBits_,
176
+ stopBits: stopBits_,
177
+ };
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ //# sourceMappingURL=parameter-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parameter-generator.js","sourceRoot":"","sources":["../../../src/discovery/parameter-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Simple progress tracker for discovery operations
3
+ */
4
+ export declare class ProgressTracker {
5
+ private readonly totalCombinations;
6
+ private startTime;
7
+ private lastUpdateTime;
8
+ private readonly updateIntervalMs;
9
+ constructor(totalCombinations: number);
10
+ /**
11
+ * Update progress and return formatted status string
12
+ * Only returns a new string if enough time has passed since last update
13
+ *
14
+ * @param current - Current combination index
15
+ * @param devicesFound - Number of devices found so far
16
+ * @returns Progress string or null if no update needed
17
+ */
18
+ update(current: number, devicesFound: number): string | null;
19
+ /**
20
+ * Format duration in seconds to human-readable string
21
+ */
22
+ private formatDuration;
23
+ }
24
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../../src/discovery/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,eAAe;IAKd,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAJ9C,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;gBAEnB,iBAAiB,EAAE,MAAM;IAItD;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAyB5D;;OAEG;IACH,OAAO,CAAC,cAAc;CAgBvB"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Simple progress tracker for discovery operations
3
+ */
4
+ export class ProgressTracker {
5
+ totalCombinations;
6
+ startTime;
7
+ lastUpdateTime = 0;
8
+ updateIntervalMs = 1000; // Update every second
9
+ constructor(totalCombinations) {
10
+ this.totalCombinations = totalCombinations;
11
+ this.startTime = Date.now();
12
+ }
13
+ /**
14
+ * Update progress and return formatted status string
15
+ * Only returns a new string if enough time has passed since last update
16
+ *
17
+ * @param current - Current combination index
18
+ * @param devicesFound - Number of devices found so far
19
+ * @returns Progress string or null if no update needed
20
+ */
21
+ update(current, devicesFound) {
22
+ const now = Date.now();
23
+ // Throttle updates to avoid excessive console output
24
+ if (now - this.lastUpdateTime < this.updateIntervalMs && current < this.totalCombinations) {
25
+ return null;
26
+ }
27
+ this.lastUpdateTime = now;
28
+ const percentage = Math.round((current / this.totalCombinations) * 100);
29
+ const elapsed = Math.floor((now - this.startTime) / 1000);
30
+ // Calculate ETA
31
+ let eta = '';
32
+ if (current > 0 && current < this.totalCombinations && elapsed > 0) {
33
+ const rate = current / elapsed;
34
+ const remaining = this.totalCombinations - current;
35
+ const etaSeconds = Math.round(remaining / rate);
36
+ eta = ` | ETA: ${this.formatDuration(etaSeconds)}`;
37
+ }
38
+ return `Progress: ${percentage}% (${current}/${this.totalCombinations}) | Devices found: ${devicesFound} | Elapsed: ${this.formatDuration(elapsed)}${eta}`;
39
+ }
40
+ /**
41
+ * Format duration in seconds to human-readable string
42
+ */
43
+ formatDuration(seconds) {
44
+ if (seconds < 60) {
45
+ return `${seconds}s`;
46
+ }
47
+ const minutes = Math.floor(seconds / 60);
48
+ const remainingSeconds = seconds % 60;
49
+ if (minutes < 60) {
50
+ return `${minutes}m ${remainingSeconds}s`;
51
+ }
52
+ const hours = Math.floor(minutes / 60);
53
+ const remainingMinutes = minutes % 60;
54
+ return `${hours}h ${remainingMinutes}m`;
55
+ }
56
+ }
57
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../../src/discovery/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,eAAe;IAKG;IAJrB,SAAS,CAAQ;IACjB,cAAc,GAAW,CAAC,CAAA;IACjB,gBAAgB,GAAW,IAAI,CAAA,CAAC,sBAAsB;IAEvE,YAA6B,iBAAyB;QAAzB,sBAAiB,GAAjB,iBAAiB,CAAQ;QACpD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC7B,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,OAAe,EAAE,YAAoB;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,qDAAqD;QACrD,IAAI,GAAG,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,gBAAgB,IAAI,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC1F,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,GAAG,CAAA;QAEzB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC,CAAA;QACvE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAA;QAEzD,gBAAgB;QAChB,IAAI,GAAG,GAAG,EAAE,CAAA;QACZ,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,IAAI,CAAC,iBAAiB,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,OAAO,GAAG,OAAO,CAAA;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAA;YAClD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;YAC/C,GAAG,GAAG,WAAW,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAA;QACpD,CAAC;QAED,OAAO,aAAa,UAAU,MAAM,OAAO,IAAI,IAAI,CAAC,iBAAiB,sBAAsB,YAAY,eAAe,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,GAAG,EAAE,CAAA;IAC5J,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,OAAe;QACpC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;YACjB,OAAO,GAAG,OAAO,GAAG,CAAA;QACtB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAA;QACxC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAA;QAErC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;YACjB,OAAO,GAAG,OAAO,KAAK,gBAAgB,GAAG,CAAA;QAC3C,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAA;QACtC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAA;QACrC,OAAO,GAAG,KAAK,KAAK,gBAAgB,GAAG,CAAA;IACzC,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ import { type DeviceIdentificationResult } from './device-identifier.js';
2
+ import { type GeneratorOptions, type ParameterCombination } from './parameter-generator.js';
3
+ /**
4
+ * Discovered device with full configuration
5
+ */
6
+ export interface DiscoveredDevice extends ParameterCombination {
7
+ /** Device identification result */
8
+ identification: DeviceIdentificationResult;
9
+ }
10
+ /**
11
+ * Scanner options
12
+ */
13
+ export interface ScanOptions {
14
+ /** Serial port path (e.g., /dev/ttyUSB0) */
15
+ port: string;
16
+ /** Response timeout in milliseconds */
17
+ timeout: number;
18
+ /** Delay between attempts in milliseconds */
19
+ delayMs: number;
20
+ /** Maximum number of devices to find (0 for unlimited, default: 1) */
21
+ maxDevices?: number;
22
+ /** Verbose progress - show current parameters being tested */
23
+ verbose?: boolean;
24
+ /** Progress callback (current index, total combinations, devices found) */
25
+ onProgress?: (current: number, total: number, devicesFound: number) => void;
26
+ /** Device found callback */
27
+ onDeviceFound?: (device: DiscoveredDevice) => void;
28
+ /** Verbose progress callback - called for each test attempt */
29
+ onTestAttempt?: (params: ParameterCombination, result: 'testing' | 'found' | 'not-found') => void;
30
+ }
31
+ /**
32
+ * Scan for Modbus devices using given parameter combinations
33
+ *
34
+ * **Performance Optimization**: Uses grouped generator to avoid materializing
35
+ * all combinations. Groups by serial parameters to reuse connections,
36
+ * reducing overhead from ~600ms to ~100ms per test.
37
+ *
38
+ * **Memory Efficiency**: Only materializes one group (~247 combinations) at a time
39
+ * instead of all ~1500+ combinations.
40
+ *
41
+ * @param generatorOptions - Options for parameter combination generation
42
+ * @param scanOptions - Scanning options
43
+ * @returns Array of discovered devices
44
+ */
45
+ export declare function scanForDevices(generatorOptions: GeneratorOptions, scanOptions: ScanOptions): Promise<DiscoveredDevice[]>;
46
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../../src/discovery/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AAExF,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAA;AAEjC;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,mCAAmC;IACnC,cAAc,EAAE,0BAA0B,CAAA;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;IAEZ,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAA;IAEf,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAA;IAEf,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IAE3E,4BAA4B;IAC5B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAElD,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,KAAK,IAAI,CAAA;CAClG;AAsCD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAClC,gBAAgB,EAAE,gBAAgB,EAClC,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAsH7B"}
@@ -0,0 +1,143 @@
1
+ import ModbusRTU from 'modbus-serial';
2
+ import { identifyDevice } from './device-identifier.js';
3
+ import { countParameterCombinations } from './parameter-generator-utils.js';
4
+ import { generateParameterGroups, } from './parameter-generator.js';
5
+ /**
6
+ * Apply inter-test delay to prevent bus contention
7
+ *
8
+ * When a device is found, waits full delayMs for bus recovery.
9
+ * When no device found or error, waits remainder (delayMs - timeout) since
10
+ * timeout already consumed time waiting for response.
11
+ *
12
+ * @param delayMs - Configured delay between tests
13
+ * @param timeout - Request timeout in milliseconds
14
+ * @param deviceFound - Whether a device was found (true) or not found/error (false)
15
+ * @param shouldContinue - Whether scanning will continue after this delay
16
+ */
17
+ async function applyInterTestDelay(delayMs, timeout, deviceFound, shouldContinue) {
18
+ // Skip delay if not continuing (reached maxDevices)
19
+ if (!shouldContinue) {
20
+ return;
21
+ }
22
+ if (deviceFound) {
23
+ // Device found - wait full delay for bus recovery
24
+ if (delayMs > 0) {
25
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
26
+ }
27
+ }
28
+ else {
29
+ // No device or error - timeout already consumed time, wait remainder
30
+ if (delayMs > timeout) {
31
+ await new Promise((resolve) => setTimeout(resolve, delayMs - timeout));
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * Scan for Modbus devices using given parameter combinations
37
+ *
38
+ * **Performance Optimization**: Uses grouped generator to avoid materializing
39
+ * all combinations. Groups by serial parameters to reuse connections,
40
+ * reducing overhead from ~600ms to ~100ms per test.
41
+ *
42
+ * **Memory Efficiency**: Only materializes one group (~247 combinations) at a time
43
+ * instead of all ~1500+ combinations.
44
+ *
45
+ * @param generatorOptions - Options for parameter combination generation
46
+ * @param scanOptions - Scanning options
47
+ * @returns Array of discovered devices
48
+ */
49
+ export async function scanForDevices(generatorOptions, scanOptions) {
50
+ const { port, timeout, delayMs, maxDevices = 1, verbose = false, onProgress, onDeviceFound, onTestAttempt, } = scanOptions;
51
+ // Calculate total combinations efficiently (no materialization)
52
+ const total = countParameterCombinations(generatorOptions);
53
+ const discovered = [];
54
+ let currentIndex = 0;
55
+ // Test each serial parameter group
56
+ // Generator yields groups without materializing all combinations
57
+ for (const group of generateParameterGroups(generatorOptions)) {
58
+ const { serialParams, combinations: groupCombinations } = group;
59
+ // Stop if we've found enough devices
60
+ if (maxDevices > 0 && discovered.length >= maxDevices) {
61
+ break;
62
+ }
63
+ // Get serial params from group
64
+ const { baudRate, parity, dataBits, stopBits } = serialParams;
65
+ try {
66
+ // Create and connect once for this serial parameter set
67
+ const client = new ModbusRTU();
68
+ await client.connectRTUBuffered(port, {
69
+ baudRate,
70
+ parity,
71
+ dataBits,
72
+ stopBits,
73
+ });
74
+ // Set timeout once for this connection
75
+ client.setTimeout(timeout);
76
+ try {
77
+ // Test all slave IDs with this serial configuration
78
+ for (const combination of groupCombinations) {
79
+ // Stop if we've found enough devices
80
+ if (maxDevices > 0 && discovered.length >= maxDevices) {
81
+ break;
82
+ }
83
+ const { slaveId } = combination;
84
+ try {
85
+ // Notify verbose progress
86
+ onTestAttempt?.(combination, 'testing');
87
+ // Set slave ID (reusing same connection)
88
+ client.setID(slaveId);
89
+ // Try to identify device
90
+ const identification = await identifyDevice(client);
91
+ // If device found, add to results
92
+ if (identification.present) {
93
+ const device = {
94
+ ...combination,
95
+ identification,
96
+ };
97
+ discovered.push(device);
98
+ onTestAttempt?.(combination, 'found');
99
+ onDeviceFound?.(device);
100
+ // Apply delay for bus recovery before next test
101
+ const reachedLimit = maxDevices > 0 && discovered.length >= maxDevices;
102
+ await applyInterTestDelay(delayMs, timeout, true, !reachedLimit);
103
+ }
104
+ else {
105
+ onTestAttempt?.(combination, 'not-found');
106
+ // Apply delay before next test (timeout already consumed time)
107
+ await applyInterTestDelay(delayMs, timeout, false, true);
108
+ }
109
+ }
110
+ catch {
111
+ // Device identification error - skip this slave ID
112
+ onTestAttempt?.(combination, 'not-found');
113
+ // Apply delay before next test (error/timeout already consumed time)
114
+ await applyInterTestDelay(delayMs, timeout, false, true);
115
+ }
116
+ // Update progress
117
+ currentIndex++;
118
+ onProgress?.(currentIndex, total, discovered.length);
119
+ }
120
+ }
121
+ finally {
122
+ // Close connection after testing all slave IDs
123
+ // Client is always defined here since we only reach this point after successful connection
124
+ const clientToClose = client;
125
+ await new Promise((resolve) => clientToClose.close(resolve));
126
+ }
127
+ }
128
+ catch (error) {
129
+ // Connection error for this serial parameter set - skip entire group
130
+ // This can happen if port is busy, doesn't exist, or serial params are invalid
131
+ // Log connection error in verbose mode to help debug hardware issues
132
+ if (verbose) {
133
+ const errorMessage = error instanceof Error ? error.message : String(error);
134
+ console.warn(`Connection failed on ${port} for ${baudRate}/${parity}/${dataBits}/${stopBits}: ${errorMessage}`);
135
+ }
136
+ // Still update progress for skipped combinations
137
+ currentIndex += groupCombinations.length;
138
+ onProgress?.(currentIndex, total, discovered.length);
139
+ }
140
+ }
141
+ return discovered;
142
+ }
143
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../../src/discovery/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { DiscoveredDevice } from '../discovery/scanner.js';
2
+ /**
3
+ * Format discovered devices as a table
4
+ */
5
+ export declare function formatDiscoveryTable(devices: DiscoveredDevice[]): string;
6
+ /**
7
+ * Format discovered devices as JSON
8
+ */
9
+ export declare function formatDiscoveryJSON(devices: DiscoveredDevice[]): string;
10
+ //# sourceMappingURL=discovery-results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-results.d.ts","sourceRoot":"","sources":["../../../src/formatters/discovery-results.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAqCxE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAkBvE"}
@@ -0,0 +1,57 @@
1
+ import Table from 'cli-table3';
2
+ /**
3
+ * Format discovered devices as a table
4
+ */
5
+ export function formatDiscoveryTable(devices) {
6
+ if (devices.length === 0) {
7
+ return 'No devices found.';
8
+ }
9
+ const table = new Table({
10
+ head: ['Slave ID', 'Baud Rate', 'Parity', 'Data/Stop', 'Response', 'Vendor', 'Model'],
11
+ style: {
12
+ head: ['cyan'],
13
+ },
14
+ });
15
+ // Sort by slave ID
16
+ const sorted = [...devices].sort((a, b) => a.slaveId - b.slaveId);
17
+ for (const device of sorted) {
18
+ const { slaveId, baudRate, parity, dataBits, stopBits, identification } = device;
19
+ const parityChar = parity === 'none' ? 'N' : parity === 'even' ? 'E' : 'O';
20
+ const dataStop = `${dataBits}${parityChar}${stopBits}`;
21
+ const vendor = identification.vendorName ?? '-';
22
+ const model = identification.modelName ?? identification.productCode ?? '-';
23
+ const responseTime = `${Math.round(identification.responseTimeMs)}ms`;
24
+ table.push([
25
+ slaveId.toString(),
26
+ baudRate.toString(),
27
+ parityChar,
28
+ dataStop,
29
+ responseTime,
30
+ vendor,
31
+ model,
32
+ ]);
33
+ }
34
+ return table.toString();
35
+ }
36
+ /**
37
+ * Format discovered devices as JSON
38
+ */
39
+ export function formatDiscoveryJSON(devices) {
40
+ const formatted = devices.map((device) => ({
41
+ slaveId: device.slaveId,
42
+ baudRate: device.baudRate,
43
+ parity: device.parity,
44
+ dataBits: device.dataBits,
45
+ stopBits: device.stopBits,
46
+ responseTimeMs: device.identification.responseTimeMs,
47
+ identification: {
48
+ vendorName: device.identification.vendorName,
49
+ productCode: device.identification.productCode,
50
+ modelName: device.identification.modelName,
51
+ revision: device.identification.revision,
52
+ supportsFC43: device.identification.supportsFC43,
53
+ },
54
+ }));
55
+ return JSON.stringify(formatted, null, 2);
56
+ }
57
+ //# sourceMappingURL=discovery-results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-results.js","sourceRoot":"","sources":["../../../src/formatters/discovery-results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAA;AAI9B;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA2B;IAC9D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,mBAAmB,CAAA;IAC5B,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC;QACrF,KAAK,EAAE;YACL,IAAI,EAAE,CAAC,MAAM,CAAC;SACf;KACF,CAAC,CAAA;IAEF,mBAAmB;IACnB,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAA;IAEjE,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAAA;QAEhF,MAAM,UAAU,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAC1E,MAAM,QAAQ,GAAG,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,EAAE,CAAA;QAEtD,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,IAAI,GAAG,CAAA;QAC/C,MAAM,KAAK,GAAG,cAAc,CAAC,SAAS,IAAI,cAAc,CAAC,WAAW,IAAI,GAAG,CAAA;QAC3E,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAA;QAErE,KAAK,CAAC,IAAI,CAAC;YACT,OAAO,CAAC,QAAQ,EAAE;YAClB,QAAQ,CAAC,QAAQ,EAAE;YACnB,UAAU;YACV,QAAQ;YACR,YAAY;YACZ,MAAM;YACN,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAA;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA2B;IAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,cAAc;QACpD,cAAc,EAAE;YACd,UAAU,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU;YAC5C,WAAW,EAAE,MAAM,CAAC,cAAc,CAAC,WAAW;YAC9C,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,SAAS;YAC1C,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,QAAQ;YACxC,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,YAAY;SACjD;KACF,CAAC,CAAC,CAAA;IAEH,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AAC3C,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { DataPoint } from '@ya-modbus/driver-types';
2
+ /**
3
+ * Metadata for JSON output
4
+ */
5
+ export interface JSONMetadata {
6
+ /** Driver package name */
7
+ driver?: string | undefined;
8
+ /** Connection details */
9
+ connection?: {
10
+ port?: string | undefined;
11
+ host?: string | undefined;
12
+ slaveId?: number | undefined;
13
+ } | undefined;
14
+ /** Performance metrics */
15
+ performance?: {
16
+ responseTimeMs: number;
17
+ operations: number;
18
+ errors: number;
19
+ } | undefined;
20
+ }
21
+ /**
22
+ * Format data point results as JSON
23
+ *
24
+ * @param dataPoints - Data point definitions
25
+ * @param values - Data point values (keyed by ID)
26
+ * @param metadata - Additional metadata to include
27
+ * @returns Formatted JSON string with 2-space indentation
28
+ */
29
+ export declare function formatJSON(dataPoints: ReadonlyArray<DataPoint>, values: Record<string, unknown>, metadata: JSONMetadata): string;
30
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/formatters/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,yBAAyB;IACzB,UAAU,CAAC,EACP;QACE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACzB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACzB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC7B,GACD,SAAS,CAAA;IACb,0BAA0B;IAC1B,WAAW,CAAC,EACR;QACE,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,MAAM,CAAA;KACf,GACD,SAAS,CAAA;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,EACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,YAAY,GACrB,MAAM,CAyBR"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Format data point results as JSON
3
+ *
4
+ * @param dataPoints - Data point definitions
5
+ * @param values - Data point values (keyed by ID)
6
+ * @param metadata - Additional metadata to include
7
+ * @returns Formatted JSON string with 2-space indentation
8
+ */
9
+ export function formatJSON(dataPoints, values, metadata) {
10
+ // Build data points array with values
11
+ const dataPointsWithValues = dataPoints
12
+ .filter((dp) => dp.id in values)
13
+ .map((dp) => ({
14
+ id: dp.id,
15
+ name: dp.name,
16
+ value: values[dp.id],
17
+ type: dp.type,
18
+ unit: dp.unit,
19
+ access: dp.access,
20
+ pollType: dp.pollType,
21
+ }));
22
+ // Build output object
23
+ const output = {
24
+ timestamp: new Date().toISOString(),
25
+ ...(metadata.driver && { driver: metadata.driver }),
26
+ ...(metadata.connection && { connection: metadata.connection }),
27
+ dataPoints: dataPointsWithValues,
28
+ ...(metadata.performance && { performance: metadata.performance }),
29
+ };
30
+ // Return pretty-printed JSON with 2-space indentation
31
+ return JSON.stringify(output, null, 2);
32
+ }
33
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/formatters/json.ts"],"names":[],"mappings":"AA0BA;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CACxB,UAAoC,EACpC,MAA+B,EAC/B,QAAsB;IAEtB,sCAAsC;IACtC,MAAM,oBAAoB,GAAG,UAAU;SACpC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC;SAC/B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACZ,EAAE,EAAE,EAAE,CAAC,EAAE;QACT,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QACpB,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,MAAM,EAAE,EAAE,CAAC,MAAM;QACjB,QAAQ,EAAE,EAAE,CAAC,QAAQ;KACtB,CAAC,CAAC,CAAA;IAEL,sBAAsB;IACtB,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QACnD,GAAG,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC/D,UAAU,EAAE,oBAAoB;QAChC,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;KACnE,CAAA;IAED,sDAAsD;IACtD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACxC,CAAC"}