@ya-modbus/emulator 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 +23 -0
- package/LICENSE +674 -0
- package/README.md +226 -0
- package/bin/ya-modbus-emulator.js +10 -0
- package/dist/behaviors/function-codes.d.ts +12 -0
- package/dist/behaviors/function-codes.d.ts.map +1 -0
- package/dist/behaviors/function-codes.js +139 -0
- package/dist/behaviors/function-codes.js.map +1 -0
- package/dist/behaviors/timing.d.ts +46 -0
- package/dist/behaviors/timing.d.ts.map +1 -0
- package/dist/behaviors/timing.js +90 -0
- package/dist/behaviors/timing.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +124 -0
- package/dist/cli.js.map +1 -0
- package/dist/device.d.ts +25 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +93 -0
- package/dist/device.js.map +1 -0
- package/dist/emulator.d.ts +24 -0
- package/dist/emulator.d.ts.map +1 -0
- package/dist/emulator.js +116 -0
- package/dist/emulator.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/transports/base.d.ts +10 -0
- package/dist/transports/base.d.ts.map +1 -0
- package/dist/transports/base.js +6 -0
- package/dist/transports/base.js.map +1 -0
- package/dist/transports/memory.d.ts +16 -0
- package/dist/transports/memory.d.ts.map +1 -0
- package/dist/transports/memory.js +30 -0
- package/dist/transports/memory.js.map +1 -0
- package/dist/transports/rtu.d.ts +44 -0
- package/dist/transports/rtu.d.ts.map +1 -0
- package/dist/transports/rtu.js +121 -0
- package/dist/transports/rtu.js.map +1 -0
- package/dist/types/config.d.ts +54 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +5 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/device.d.ts +27 -0
- package/dist/types/device.d.ts.map +1 -0
- package/dist/types/device.js +5 -0
- package/dist/types/device.js.map +1 -0
- package/dist/utils/config-loader.d.ts +21 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +53 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RTU (serial) transport for Modbus emulator
|
|
3
|
+
*
|
|
4
|
+
* This implementation uses modbus-serial for protocol handling.
|
|
5
|
+
* Serial port communication will be added in future iterations.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseTransport } from './base.js';
|
|
8
|
+
export class RtuTransport extends BaseTransport {
|
|
9
|
+
requestHandler;
|
|
10
|
+
started = false;
|
|
11
|
+
constructor(_config) {
|
|
12
|
+
super();
|
|
13
|
+
// Config stored for future serial port implementation
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
this.started = true;
|
|
17
|
+
// Serial port initialization will be implemented when needed
|
|
18
|
+
// For now, this is a placeholder for the interface
|
|
19
|
+
return Promise.resolve();
|
|
20
|
+
}
|
|
21
|
+
stop() {
|
|
22
|
+
if (!this.started) {
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
this.started = false;
|
|
26
|
+
// Serial port cleanup will be implemented when needed
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
send(_slaveId, _response) {
|
|
30
|
+
if (!this.started) {
|
|
31
|
+
return Promise.reject(new Error('Transport not started'));
|
|
32
|
+
}
|
|
33
|
+
// Response sending will be implemented with serial port
|
|
34
|
+
return Promise.resolve();
|
|
35
|
+
}
|
|
36
|
+
onRequest(handler) {
|
|
37
|
+
this.requestHandler = handler;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handle complete RTU frame (for future serial port implementation)
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
async handleFrame(frame) {
|
|
44
|
+
// Minimum frame: slave_id + function_code + CRC (4 bytes)
|
|
45
|
+
if (frame.length < 4) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Verify CRC
|
|
49
|
+
if (!this.verifyCRC(frame)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Extract slave ID (safe after length check)
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
54
|
+
const slaveId = frame[0];
|
|
55
|
+
// Remove CRC to get request data
|
|
56
|
+
const request = frame.subarray(0, frame.length - 2);
|
|
57
|
+
if (!this.requestHandler) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
// Get response from handler
|
|
62
|
+
const response = await this.requestHandler(slaveId, request);
|
|
63
|
+
// Send response (null check required by exactOptionalPropertyTypes)
|
|
64
|
+
if (response !== undefined) {
|
|
65
|
+
await this.send(slaveId, response);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Don't send response if handler fails
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Calculate Modbus RTU CRC-16 (Modbus variant)
|
|
74
|
+
*
|
|
75
|
+
* Returns CRC as a 16-bit value that can be written with writeUInt16LE
|
|
76
|
+
*/
|
|
77
|
+
calculateCRC(buffer) {
|
|
78
|
+
let crc = 0xffff;
|
|
79
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
81
|
+
crc ^= buffer[i];
|
|
82
|
+
for (let j = 0; j < 8; j++) {
|
|
83
|
+
if (crc & 0x0001) {
|
|
84
|
+
crc = (crc >> 1) ^ 0xa001;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
crc = crc >> 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Swap bytes for little-endian representation
|
|
92
|
+
// CRC is stored low byte first in Modbus RTU
|
|
93
|
+
return ((crc & 0xff) << 8) | ((crc >> 8) & 0xff);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Verify CRC of RTU frame
|
|
97
|
+
*/
|
|
98
|
+
verifyCRC(frame) {
|
|
99
|
+
if (frame.length < 4) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// Get CRC from frame (last 2 bytes, little-endian)
|
|
103
|
+
const receivedCRC = frame.readUInt16LE(frame.length - 2);
|
|
104
|
+
// Calculate CRC of data (everything except last 2 bytes)
|
|
105
|
+
const data = frame.subarray(0, frame.length - 2);
|
|
106
|
+
const calculatedCRC = this.calculateCRC(data);
|
|
107
|
+
return receivedCRC === calculatedCRC;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Add CRC to buffer
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
addCRC(buffer) {
|
|
114
|
+
const crc = this.calculateCRC(buffer);
|
|
115
|
+
const result = Buffer.alloc(buffer.length + 2);
|
|
116
|
+
buffer.copy(result);
|
|
117
|
+
result.writeUInt16LE(crc, buffer.length);
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=rtu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtu.js","sourceRoot":"","sources":["../../src/transports/rtu.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types for the Modbus emulator
|
|
3
|
+
*/
|
|
4
|
+
export interface EmulatorConfig {
|
|
5
|
+
/** Transport type */
|
|
6
|
+
transport: 'tcp' | 'rtu' | 'memory';
|
|
7
|
+
/** Port number (TCP) or serial port path (RTU) */
|
|
8
|
+
port?: number | string;
|
|
9
|
+
/** Host address for TCP transport */
|
|
10
|
+
host?: string;
|
|
11
|
+
/** Maximum number of connections (TCP) */
|
|
12
|
+
maxConnections?: number;
|
|
13
|
+
/** Baud rate for RTU transport */
|
|
14
|
+
baudRate?: number;
|
|
15
|
+
/** Parity for RTU transport */
|
|
16
|
+
parity?: 'none' | 'even' | 'odd';
|
|
17
|
+
/** Stop bits for RTU transport */
|
|
18
|
+
stopBits?: 1 | 2;
|
|
19
|
+
/** Data bits for RTU transport */
|
|
20
|
+
dataBits?: 7 | 8;
|
|
21
|
+
}
|
|
22
|
+
export interface RegisterStorage {
|
|
23
|
+
/** Holding registers (read/write) */
|
|
24
|
+
holding?: Record<number, number>;
|
|
25
|
+
/** Input registers (read-only) */
|
|
26
|
+
input?: Record<number, number>;
|
|
27
|
+
/** Coils (read/write bits) */
|
|
28
|
+
coils?: Record<number, boolean>;
|
|
29
|
+
/** Discrete inputs (read-only bits) */
|
|
30
|
+
discreteInputs?: Record<number, boolean>;
|
|
31
|
+
}
|
|
32
|
+
export interface TimingBehavior {
|
|
33
|
+
/** Time device takes to notice incoming command (ms) */
|
|
34
|
+
commandDetectionDelay?: number | [min: number, max: number];
|
|
35
|
+
/** Internal polling interval (ms) - realistic: 1-100ms */
|
|
36
|
+
pollingInterval?: number;
|
|
37
|
+
/** Base processing time per command (ms) */
|
|
38
|
+
processingDelay?: number | [min: number, max: number];
|
|
39
|
+
/** Additional delay per register read/written (ms) */
|
|
40
|
+
perRegisterDelay?: number;
|
|
41
|
+
/** Baud rate (affects transmission time for RTU) */
|
|
42
|
+
baudRate?: number;
|
|
43
|
+
/** Auto-calculate transmission delay based on frame size */
|
|
44
|
+
autoCalculateTransmissionDelay?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface DeviceConfig {
|
|
47
|
+
/** Slave ID (1-247) */
|
|
48
|
+
slaveId: number;
|
|
49
|
+
/** Initial register values */
|
|
50
|
+
registers?: RegisterStorage;
|
|
51
|
+
/** Timing behavior configuration */
|
|
52
|
+
timing?: TimingBehavior;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,qBAAqB;IACrB,SAAS,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;IACnC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;IAChC,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAChB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,qBAAqB,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;IAC3D,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;IACrD,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,8BAA8B,CAAC,EAAE,OAAO,CAAA;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,oCAAoC;IACpC,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-related types
|
|
3
|
+
*/
|
|
4
|
+
import type { TimingSimulator } from '../behaviors/timing.js';
|
|
5
|
+
export interface EmulatedDevice {
|
|
6
|
+
/** Slave ID */
|
|
7
|
+
slaveId: number;
|
|
8
|
+
/** Get holding register value */
|
|
9
|
+
getHoldingRegister(address: number): number;
|
|
10
|
+
/** Set holding register value */
|
|
11
|
+
setHoldingRegister(address: number, value: number): void;
|
|
12
|
+
/** Get input register value */
|
|
13
|
+
getInputRegister(address: number): number;
|
|
14
|
+
/** Set input register value */
|
|
15
|
+
setInputRegister(address: number, value: number): void;
|
|
16
|
+
/** Get coil value */
|
|
17
|
+
getCoil(address: number): boolean;
|
|
18
|
+
/** Set coil value */
|
|
19
|
+
setCoil(address: number, value: boolean): void;
|
|
20
|
+
/** Get discrete input value */
|
|
21
|
+
getDiscreteInput(address: number): boolean;
|
|
22
|
+
/** Set discrete input value */
|
|
23
|
+
setDiscreteInput(address: number, value: boolean): void;
|
|
24
|
+
/** Get timing simulator */
|
|
25
|
+
getTimingSimulator(): TimingSimulator | undefined;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/types/device.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D,MAAM,WAAW,cAAc;IAC7B,eAAe;IACf,OAAO,EAAE,MAAM,CAAA;IACf,iCAAiC;IACjC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3C,iCAAiC;IACjC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACxD,+BAA+B;IAC/B,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IACzC,+BAA+B;IAC/B,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACtD,qBAAqB;IACrB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IACjC,qBAAqB;IACrB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9C,+BAA+B;IAC/B,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IAC1C,+BAA+B;IAC/B,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACvD,2BAA2B;IAC3B,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAAA;CAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/types/device.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file loader for YAML and JSON formats
|
|
3
|
+
*/
|
|
4
|
+
import type { DeviceConfig } from '../types/config.js';
|
|
5
|
+
export interface ConfigFile {
|
|
6
|
+
transport: {
|
|
7
|
+
type: 'tcp' | 'rtu' | 'memory';
|
|
8
|
+
port?: number | string;
|
|
9
|
+
host?: string;
|
|
10
|
+
baudRate?: number;
|
|
11
|
+
parity?: 'none' | 'even' | 'odd';
|
|
12
|
+
dataBits?: 7 | 8;
|
|
13
|
+
stopBits?: 1 | 2;
|
|
14
|
+
};
|
|
15
|
+
devices: DeviceConfig[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load configuration from YAML or JSON file
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadConfig(filePath: string): Promise<ConfigFile>;
|
|
21
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE;QACT,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;QAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;QACtB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;QAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;QAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;KACjB,CAAA;IACD,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBtE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file loader for YAML and JSON formats
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
6
|
+
/**
|
|
7
|
+
* Load configuration from YAML or JSON file
|
|
8
|
+
*/
|
|
9
|
+
export async function loadConfig(filePath) {
|
|
10
|
+
// Read file content
|
|
11
|
+
const content = await readFile(filePath, 'utf-8');
|
|
12
|
+
// Parse based on file extension
|
|
13
|
+
let config;
|
|
14
|
+
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
|
|
15
|
+
config = yaml.load(content);
|
|
16
|
+
}
|
|
17
|
+
else if (filePath.endsWith('.json')) {
|
|
18
|
+
config = JSON.parse(content);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
throw new Error('Unsupported config format. Use .yaml, .yml, or .json');
|
|
22
|
+
}
|
|
23
|
+
// Validate configuration
|
|
24
|
+
validateConfig(config);
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate configuration structure
|
|
29
|
+
*/
|
|
30
|
+
function validateConfig(config) {
|
|
31
|
+
if (!config || typeof config !== 'object') {
|
|
32
|
+
throw new Error('Invalid configuration format');
|
|
33
|
+
}
|
|
34
|
+
const cfg = config;
|
|
35
|
+
// Validate transport
|
|
36
|
+
if (!cfg['transport']) {
|
|
37
|
+
throw new Error('Missing transport configuration');
|
|
38
|
+
}
|
|
39
|
+
if (typeof cfg['transport'] !== 'object' || cfg['transport'] === null) {
|
|
40
|
+
throw new Error('Invalid transport configuration');
|
|
41
|
+
}
|
|
42
|
+
// Validate devices
|
|
43
|
+
if (!cfg['devices']) {
|
|
44
|
+
throw new Error('Missing devices configuration');
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(cfg['devices'])) {
|
|
47
|
+
throw new Error('Devices must be an array');
|
|
48
|
+
}
|
|
49
|
+
if (cfg['devices'].length === 0) {
|
|
50
|
+
throw new Error('At least one device must be configured');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,IAAI,MAAM,SAAS,CAAA;AAiB1B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,oBAAoB;IACpB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAEjD,gCAAgC;IAChC,IAAI,MAAe,CAAA;IAEnB,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7B,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACzE,CAAC;IAED,yBAAyB;IACzB,cAAc,CAAC,MAAM,CAAC,CAAA;IAEtB,OAAO,MAAoB,CAAA;AAC7B,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,MAAe;IACrC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,GAAG,GAAG,MAAiC,CAAA;IAE7C,qBAAqB;IACrB,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACpD,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACpD,CAAC;IAED,mBAAmB;IACnB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAClD,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IAC7C,CAAC;IAED,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ya-modbus/emulator",
|
|
3
|
+
"version": "0.4.1-refactor-scope-driver-packages.0",
|
|
4
|
+
"description": "Software Modbus device emulator for testing drivers without physical hardware",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"modbus",
|
|
7
|
+
"emulator",
|
|
8
|
+
"testing",
|
|
9
|
+
"simulation"
|
|
10
|
+
],
|
|
11
|
+
"author": "Geno Roupsky <geno@roupsky.name>",
|
|
12
|
+
"license": "GPL-3.0-or-later",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"bin": {
|
|
17
|
+
"ya-modbus-emulator": "./bin/ya-modbus-emulator.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"bin",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/groupsky/ya-modbus.git",
|
|
28
|
+
"directory": "packages/emulator"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/groupsky/ya-modbus/tree/main/packages/emulator#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/groupsky/ya-modbus/issues"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc --build",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"test": "jest"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"commander": "^12.1.0",
|
|
44
|
+
"js-yaml": "^4.1.0",
|
|
45
|
+
"modbus-serial": "^8.0.23"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/js-yaml": "^4.0.9"
|
|
49
|
+
},
|
|
50
|
+
"gitHead": "b55d909d67dfff46fb79951e68a2d47ac3fab267"
|
|
51
|
+
}
|