@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.
- package/CHANGELOG.md +32 -0
- package/LICENSE +674 -0
- package/README.md +612 -0
- package/dist/bin/ya-modbus.d.ts +9 -0
- package/dist/bin/ya-modbus.d.ts.map +1 -0
- package/dist/bin/ya-modbus.js +10 -0
- package/dist/bin/ya-modbus.js.map +1 -0
- package/dist/src/commands/discover.d.ts +25 -0
- package/dist/src/commands/discover.d.ts.map +1 -0
- package/dist/src/commands/discover.js +160 -0
- package/dist/src/commands/discover.js.map +1 -0
- package/dist/src/commands/list-devices.d.ts +21 -0
- package/dist/src/commands/list-devices.d.ts.map +1 -0
- package/dist/src/commands/list-devices.js +75 -0
- package/dist/src/commands/list-devices.js.map +1 -0
- package/dist/src/commands/read.d.ts +25 -0
- package/dist/src/commands/read.d.ts.map +1 -0
- package/dist/src/commands/read.js +76 -0
- package/dist/src/commands/read.js.map +1 -0
- package/dist/src/commands/show-defaults.d.ts +21 -0
- package/dist/src/commands/show-defaults.d.ts.map +1 -0
- package/dist/src/commands/show-defaults.js +48 -0
- package/dist/src/commands/show-defaults.js.map +1 -0
- package/dist/src/commands/write.d.ts +26 -0
- package/dist/src/commands/write.d.ts.map +1 -0
- package/dist/src/commands/write.js +61 -0
- package/dist/src/commands/write.js.map +1 -0
- package/dist/src/discovery/constants.d.ts +37 -0
- package/dist/src/discovery/constants.d.ts.map +1 -0
- package/dist/src/discovery/constants.js +45 -0
- package/dist/src/discovery/constants.js.map +1 -0
- package/dist/src/discovery/device-identifier.d.ts +52 -0
- package/dist/src/discovery/device-identifier.d.ts.map +1 -0
- package/dist/src/discovery/device-identifier.js +193 -0
- package/dist/src/discovery/device-identifier.js.map +1 -0
- package/dist/src/discovery/parameter-generator-utils.d.ts +29 -0
- package/dist/src/discovery/parameter-generator-utils.d.ts.map +1 -0
- package/dist/src/discovery/parameter-generator-utils.js +59 -0
- package/dist/src/discovery/parameter-generator-utils.js.map +1 -0
- package/dist/src/discovery/parameter-generator.d.ts +97 -0
- package/dist/src/discovery/parameter-generator.d.ts.map +1 -0
- package/dist/src/discovery/parameter-generator.js +184 -0
- package/dist/src/discovery/parameter-generator.js.map +1 -0
- package/dist/src/discovery/progress.d.ts +24 -0
- package/dist/src/discovery/progress.d.ts.map +1 -0
- package/dist/src/discovery/progress.js +57 -0
- package/dist/src/discovery/progress.js.map +1 -0
- package/dist/src/discovery/scanner.d.ts +46 -0
- package/dist/src/discovery/scanner.d.ts.map +1 -0
- package/dist/src/discovery/scanner.js +143 -0
- package/dist/src/discovery/scanner.js.map +1 -0
- package/dist/src/formatters/discovery-results.d.ts +10 -0
- package/dist/src/formatters/discovery-results.d.ts.map +1 -0
- package/dist/src/formatters/discovery-results.js +57 -0
- package/dist/src/formatters/discovery-results.js.map +1 -0
- package/dist/src/formatters/json.d.ts +30 -0
- package/dist/src/formatters/json.d.ts.map +1 -0
- package/dist/src/formatters/json.js +33 -0
- package/dist/src/formatters/json.js.map +1 -0
- package/dist/src/formatters/performance.d.ts +19 -0
- package/dist/src/formatters/performance.d.ts.map +1 -0
- package/dist/src/formatters/performance.js +24 -0
- package/dist/src/formatters/performance.js.map +1 -0
- package/dist/src/formatters/table.d.ts +10 -0
- package/dist/src/formatters/table.d.ts.map +1 -0
- package/dist/src/formatters/table.js +91 -0
- package/dist/src/formatters/table.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +154 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/utils/commands.d.ts +191 -0
- package/dist/src/utils/commands.d.ts.map +1 -0
- package/dist/src/utils/commands.js +400 -0
- package/dist/src/utils/commands.js.map +1 -0
- package/dist/src/utils/object-utils.d.ts +20 -0
- package/dist/src/utils/object-utils.d.ts.map +1 -0
- package/dist/src/utils/object-utils.js +28 -0
- package/dist/src/utils/object-utils.js.map +1 -0
- package/dist/src/utils/validation.d.ts +70 -0
- package/dist/src/utils/validation.d.ts.map +1 -0
- package/dist/src/utils/validation.js +158 -0
- package/dist/src/utils/validation.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- 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"}
|