@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +674 -0
  3. package/README.md +226 -0
  4. package/bin/ya-modbus-emulator.js +10 -0
  5. package/dist/behaviors/function-codes.d.ts +12 -0
  6. package/dist/behaviors/function-codes.d.ts.map +1 -0
  7. package/dist/behaviors/function-codes.js +139 -0
  8. package/dist/behaviors/function-codes.js.map +1 -0
  9. package/dist/behaviors/timing.d.ts +46 -0
  10. package/dist/behaviors/timing.d.ts.map +1 -0
  11. package/dist/behaviors/timing.js +90 -0
  12. package/dist/behaviors/timing.js.map +1 -0
  13. package/dist/cli.d.ts +10 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +124 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/device.d.ts +25 -0
  18. package/dist/device.d.ts.map +1 -0
  19. package/dist/device.js +93 -0
  20. package/dist/device.js.map +1 -0
  21. package/dist/emulator.d.ts +24 -0
  22. package/dist/emulator.d.ts.map +1 -0
  23. package/dist/emulator.js +116 -0
  24. package/dist/emulator.js.map +1 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +6 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/transports/base.d.ts +10 -0
  30. package/dist/transports/base.d.ts.map +1 -0
  31. package/dist/transports/base.js +6 -0
  32. package/dist/transports/base.js.map +1 -0
  33. package/dist/transports/memory.d.ts +16 -0
  34. package/dist/transports/memory.d.ts.map +1 -0
  35. package/dist/transports/memory.js +30 -0
  36. package/dist/transports/memory.js.map +1 -0
  37. package/dist/transports/rtu.d.ts +44 -0
  38. package/dist/transports/rtu.d.ts.map +1 -0
  39. package/dist/transports/rtu.js +121 -0
  40. package/dist/transports/rtu.js.map +1 -0
  41. package/dist/types/config.d.ts +54 -0
  42. package/dist/types/config.d.ts.map +1 -0
  43. package/dist/types/config.js +5 -0
  44. package/dist/types/config.js.map +1 -0
  45. package/dist/types/device.d.ts +27 -0
  46. package/dist/types/device.d.ts.map +1 -0
  47. package/dist/types/device.js +5 -0
  48. package/dist/types/device.js.map +1 -0
  49. package/dist/utils/config-loader.d.ts +21 -0
  50. package/dist/utils/config-loader.d.ts.map +1 -0
  51. package/dist/utils/config-loader.js +53 -0
  52. package/dist/utils/config-loader.js.map +1 -0
  53. package/package.json +51 -0
package/dist/cli.js ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI interface for Modbus emulator
4
+ */
5
+ import { Command } from 'commander';
6
+ import { ModbusEmulator } from './emulator.js';
7
+ import { loadConfig } from './utils/config-loader.js';
8
+ const program = new Command();
9
+ program
10
+ .name('ya-modbus-emulator')
11
+ .description('Modbus device emulator for testing drivers without physical hardware')
12
+ .version('0.1.0');
13
+ program
14
+ .option('-c, --config <file>', 'Configuration file (YAML or JSON)')
15
+ .option('-t, --transport <type>', 'Transport type: tcp|rtu|memory')
16
+ .option('-p, --port <port>', 'TCP port number or serial port path')
17
+ .option('-H, --host <host>', 'TCP host address (default: 0.0.0.0)')
18
+ .option('-b, --baud-rate <rate>', 'Serial baud rate (default: 9600)', parseInt)
19
+ .option('--parity <type>', 'Serial parity: none|even|odd (default: none)')
20
+ .option('-s, --slave-id <id>', 'Slave ID (required if no config file)', parseInt)
21
+ .option('-v, --verbose', 'Enable verbose logging')
22
+ .option('-q, --quiet', 'Suppress all output except errors')
23
+ .option('--log-requests', 'Log all Modbus requests/responses');
24
+ program.parse();
25
+ // Type assertion needed for TypeScript strict mode with noUncheckedIndexedAccess
26
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
27
+ const options = program.opts();
28
+ /**
29
+ * Main CLI entry point
30
+ */
31
+ async function main() {
32
+ try {
33
+ // Load configuration
34
+ let config;
35
+ let devices = [];
36
+ if (options.config !== undefined) {
37
+ // Load from config file
38
+ const fileConfig = await loadConfig(options.config);
39
+ // Build config object only with defined properties (exactOptionalPropertyTypes)
40
+ config = {
41
+ transport: fileConfig.transport.type,
42
+ ...(fileConfig.transport.port !== undefined && { port: fileConfig.transport.port }),
43
+ ...(fileConfig.transport.host !== undefined && { host: fileConfig.transport.host }),
44
+ ...(fileConfig.transport.baudRate !== undefined && {
45
+ baudRate: fileConfig.transport.baudRate,
46
+ }),
47
+ ...(fileConfig.transport.parity !== undefined && { parity: fileConfig.transport.parity }),
48
+ ...(fileConfig.transport.dataBits !== undefined && {
49
+ dataBits: fileConfig.transport.dataBits,
50
+ }),
51
+ ...(fileConfig.transport.stopBits !== undefined && {
52
+ stopBits: fileConfig.transport.stopBits,
53
+ }),
54
+ };
55
+ devices = fileConfig.devices;
56
+ }
57
+ else {
58
+ // Build from CLI args
59
+ if (options.transport === undefined) {
60
+ console.error('Error: --transport is required when not using --config');
61
+ process.exit(1);
62
+ }
63
+ if (options.slaveId === undefined) {
64
+ console.error('Error: --slave-id is required when not using --config');
65
+ process.exit(1);
66
+ }
67
+ config = {
68
+ transport: options.transport,
69
+ ...(options.port !== undefined && { port: options.port }),
70
+ ...(options.host !== undefined && { host: options.host }),
71
+ ...(options.baudRate !== undefined && { baudRate: options.baudRate }),
72
+ ...(options.parity !== undefined && {
73
+ parity: options.parity,
74
+ }),
75
+ };
76
+ devices = [{ slaveId: options.slaveId }];
77
+ }
78
+ // Create emulator
79
+ const emulator = new ModbusEmulator(config);
80
+ // Add devices
81
+ for (const deviceConfig of devices) {
82
+ emulator.addDevice(deviceConfig);
83
+ }
84
+ // Start emulator
85
+ await emulator.start();
86
+ if (options.quiet !== true) {
87
+ console.log('[INFO] Starting Modbus emulator...');
88
+ console.log(`[INFO] Transport: ${config.transport.toUpperCase()} on ${config.port ?? 'memory'}`);
89
+ console.log(`[INFO] Devices: ${devices.length}`);
90
+ devices.forEach((device) => {
91
+ console.log(`[INFO] - Slave ID ${device.slaveId}`);
92
+ });
93
+ console.log('[INFO] Emulator started successfully');
94
+ console.log('[INFO] Press Ctrl+C to stop');
95
+ }
96
+ // Handle shutdown
97
+ const shutdown = async () => {
98
+ if (options.quiet !== true) {
99
+ console.log('\n[INFO] Shutting down...');
100
+ }
101
+ await emulator.stop();
102
+ if (options.quiet !== true) {
103
+ console.log('[INFO] Emulator stopped');
104
+ }
105
+ process.exit(0);
106
+ };
107
+ process.on('SIGINT', () => {
108
+ void shutdown();
109
+ });
110
+ process.on('SIGTERM', () => {
111
+ void shutdown();
112
+ });
113
+ }
114
+ catch (error) {
115
+ console.error('[ERROR]', error instanceof Error ? error.message : String(error));
116
+ process.exit(1);
117
+ }
118
+ }
119
+ main().catch((error) => {
120
+ console.error('[ERROR]', error instanceof Error ? error.message : String(error));
121
+ process.exit(1);
122
+ });
123
+ export { main };
124
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * EmulatedDevice class implementation
3
+ */
4
+ import { TimingSimulator } from './behaviors/timing.js';
5
+ import type { DeviceConfig } from './types/config.js';
6
+ import type { EmulatedDevice as IEmulatedDevice } from './types/device.js';
7
+ export declare class EmulatedDevice implements IEmulatedDevice {
8
+ readonly slaveId: number;
9
+ private holdingRegisters;
10
+ private inputRegisters;
11
+ private coils;
12
+ private discreteInputs;
13
+ private timingSimulator?;
14
+ constructor(config: DeviceConfig);
15
+ getHoldingRegister(address: number): number;
16
+ setHoldingRegister(address: number, value: number): void;
17
+ getInputRegister(address: number): number;
18
+ setInputRegister(address: number, value: number): void;
19
+ getCoil(address: number): boolean;
20
+ setCoil(address: number, value: boolean): void;
21
+ getDiscreteInput(address: number): boolean;
22
+ setDiscreteInput(address: number, value: boolean): void;
23
+ getTimingSimulator(): TimingSimulator | undefined;
24
+ }
25
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAoB1E,qBAAa,cAAe,YAAW,eAAe;IACpD,SAAgB,OAAO,EAAE,MAAM,CAAA;IAC/B,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,MAAM,EAAE,YAAY;IAwChC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI3C,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAMxD,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIzC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAMtD,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9C,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI1C,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAIvD,kBAAkB,IAAI,eAAe,GAAG,SAAS;CAGlD"}
package/dist/device.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * EmulatedDevice class implementation
3
+ */
4
+ import { TimingSimulator } from './behaviors/timing.js';
5
+ // Modbus register address and value ranges
6
+ const MIN_REGISTER_ADDRESS = 0;
7
+ const MAX_REGISTER_ADDRESS = 65535;
8
+ const MIN_REGISTER_VALUE = 0;
9
+ const MAX_REGISTER_VALUE = 65535;
10
+ function validateRegisterAddress(address) {
11
+ if (address < MIN_REGISTER_ADDRESS || address > MAX_REGISTER_ADDRESS) {
12
+ throw new Error(`Register address ${address} out of range (0-65535)`);
13
+ }
14
+ }
15
+ function validateRegisterValue(value, address) {
16
+ if (value < MIN_REGISTER_VALUE || value > MAX_REGISTER_VALUE) {
17
+ throw new Error(`Register value ${value} at address ${address} out of range (0-65535)`);
18
+ }
19
+ }
20
+ export class EmulatedDevice {
21
+ slaveId;
22
+ holdingRegisters = new Map();
23
+ inputRegisters = new Map();
24
+ coils = new Map();
25
+ discreteInputs = new Map();
26
+ timingSimulator;
27
+ constructor(config) {
28
+ this.slaveId = config.slaveId;
29
+ // Initialize timing behavior
30
+ if (config.timing) {
31
+ this.timingSimulator = new TimingSimulator(config.timing);
32
+ }
33
+ // Initialize registers from config
34
+ if (config.registers?.holding) {
35
+ for (const [address, value] of Object.entries(config.registers.holding)) {
36
+ const addr = Number(address);
37
+ validateRegisterAddress(addr);
38
+ validateRegisterValue(value, addr);
39
+ this.holdingRegisters.set(addr, value);
40
+ }
41
+ }
42
+ if (config.registers?.input) {
43
+ for (const [address, value] of Object.entries(config.registers.input)) {
44
+ const addr = Number(address);
45
+ validateRegisterAddress(addr);
46
+ validateRegisterValue(value, addr);
47
+ this.inputRegisters.set(addr, value);
48
+ }
49
+ }
50
+ if (config.registers?.coils) {
51
+ for (const [address, value] of Object.entries(config.registers.coils)) {
52
+ this.coils.set(Number(address), value);
53
+ }
54
+ }
55
+ if (config.registers?.discreteInputs) {
56
+ for (const [address, value] of Object.entries(config.registers.discreteInputs)) {
57
+ this.discreteInputs.set(Number(address), value);
58
+ }
59
+ }
60
+ }
61
+ getHoldingRegister(address) {
62
+ return this.holdingRegisters.get(address) ?? 0;
63
+ }
64
+ setHoldingRegister(address, value) {
65
+ validateRegisterAddress(address);
66
+ validateRegisterValue(value, address);
67
+ this.holdingRegisters.set(address, value);
68
+ }
69
+ getInputRegister(address) {
70
+ return this.inputRegisters.get(address) ?? 0;
71
+ }
72
+ setInputRegister(address, value) {
73
+ validateRegisterAddress(address);
74
+ validateRegisterValue(value, address);
75
+ this.inputRegisters.set(address, value);
76
+ }
77
+ getCoil(address) {
78
+ return this.coils.get(address) ?? false;
79
+ }
80
+ setCoil(address, value) {
81
+ this.coils.set(address, value);
82
+ }
83
+ getDiscreteInput(address) {
84
+ return this.discreteInputs.get(address) ?? false;
85
+ }
86
+ setDiscreteInput(address, value) {
87
+ this.discreteInputs.set(address, value);
88
+ }
89
+ getTimingSimulator() {
90
+ return this.timingSimulator;
91
+ }
92
+ }
93
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Main ModbusEmulator class
3
+ */
4
+ import { EmulatedDevice } from './device.js';
5
+ import type { BaseTransport } from './transports/base.js';
6
+ import type { EmulatorConfig, DeviceConfig } from './types/config.js';
7
+ export declare class ModbusEmulator {
8
+ private devices;
9
+ private transport;
10
+ private started;
11
+ constructor(config: EmulatorConfig);
12
+ start(): Promise<void>;
13
+ stop(): Promise<void>;
14
+ private handleRequest;
15
+ /**
16
+ * Parse register count from Modbus request
17
+ */
18
+ private parseRegisterCount;
19
+ getTransport(): BaseTransport;
20
+ addDevice(config: DeviceConfig): EmulatedDevice;
21
+ removeDevice(slaveId: number): void;
22
+ getDevice(slaveId: number): EmulatedDevice | undefined;
23
+ }
24
+ //# sourceMappingURL=emulator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../src/emulator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGzD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErE,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,OAAO,CAAQ;gBAEX,MAAM,EAAE,cAAc;IA+B5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAQb,aAAa;IA2B3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAyB1B,YAAY,IAAI,aAAa;IAI7B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc;IAU/C,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOnC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;CAGvD"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Main ModbusEmulator class
3
+ */
4
+ import { EmulatedDevice } from './device.js';
5
+ import { MemoryTransport } from './transports/memory.js';
6
+ import { RtuTransport } from './transports/rtu.js';
7
+ export class ModbusEmulator {
8
+ devices = new Map();
9
+ transport;
10
+ started = false;
11
+ constructor(config) {
12
+ // Create transport based on config
13
+ if (config.transport === 'memory') {
14
+ this.transport = new MemoryTransport();
15
+ }
16
+ else if (config.transport === 'rtu') {
17
+ if (!config.port || typeof config.port !== 'string') {
18
+ throw new Error('RTU transport requires port (serial port path)');
19
+ }
20
+ // Build RTU config with only defined properties (exactOptionalPropertyTypes)
21
+ const rtuConfig = {
22
+ port: config.port,
23
+ ...(config.baudRate !== undefined && { baudRate: config.baudRate }),
24
+ ...(config.parity !== undefined && { parity: config.parity }),
25
+ ...(config.dataBits !== undefined && { dataBits: config.dataBits }),
26
+ ...(config.stopBits !== undefined && { stopBits: config.stopBits }),
27
+ };
28
+ this.transport = new RtuTransport(rtuConfig);
29
+ }
30
+ else {
31
+ throw new Error(`Unsupported transport: ${config.transport}`);
32
+ }
33
+ // Set up request handler
34
+ this.transport.onRequest(this.handleRequest.bind(this));
35
+ }
36
+ async start() {
37
+ if (this.started) {
38
+ throw new Error('Emulator already started');
39
+ }
40
+ await this.transport.start();
41
+ this.started = true;
42
+ }
43
+ async stop() {
44
+ if (!this.started) {
45
+ return;
46
+ }
47
+ await this.transport.stop();
48
+ this.started = false;
49
+ }
50
+ async handleRequest(slaveId, request) {
51
+ // Get the device for this slave ID
52
+ const device = this.devices.get(slaveId);
53
+ if (!device) {
54
+ // No device found - return exception
55
+ const response = Buffer.alloc(3);
56
+ response[0] = slaveId;
57
+ const functionCode = request[1];
58
+ response[1] = functionCode !== undefined ? functionCode | 0x80 : 0x80;
59
+ response[2] = 0x0b; // Gateway Target Device Failed to Respond
60
+ return response;
61
+ }
62
+ // Apply timing delay if configured
63
+ const timingSimulator = device.getTimingSimulator();
64
+ if (timingSimulator !== undefined) {
65
+ // Parse register count from request
66
+ const registerCount = this.parseRegisterCount(request);
67
+ await timingSimulator.delay(request, registerCount);
68
+ }
69
+ // Import dynamically to avoid circular dependency issues
70
+ const { handleModbusRequest } = await import('./behaviors/function-codes.js');
71
+ return handleModbusRequest(device, request);
72
+ }
73
+ /**
74
+ * Parse register count from Modbus request
75
+ */
76
+ parseRegisterCount(request) {
77
+ if (request.length < 6) {
78
+ return 0;
79
+ }
80
+ const functionCode = request[1];
81
+ // For read operations, register count is in bytes 4-5
82
+ if (functionCode === 0x03 || functionCode === 0x04) {
83
+ return request.readUInt16BE(4);
84
+ }
85
+ // For write single register, count is 1
86
+ if (functionCode === 0x06) {
87
+ return 1;
88
+ }
89
+ // For write multiple registers, count is in bytes 4-5
90
+ if (functionCode === 0x10) {
91
+ return request.readUInt16BE(4);
92
+ }
93
+ return 0;
94
+ }
95
+ getTransport() {
96
+ return this.transport;
97
+ }
98
+ addDevice(config) {
99
+ if (this.devices.has(config.slaveId)) {
100
+ throw new Error(`Device with slave ID ${config.slaveId} already exists`);
101
+ }
102
+ const device = new EmulatedDevice(config);
103
+ this.devices.set(config.slaveId, device);
104
+ return device;
105
+ }
106
+ removeDevice(slaveId) {
107
+ if (!this.devices.has(slaveId)) {
108
+ throw new Error(`Device with slave ID ${slaveId} not found`);
109
+ }
110
+ this.devices.delete(slaveId);
111
+ }
112
+ getDevice(slaveId) {
113
+ return this.devices.get(slaveId);
114
+ }
115
+ }
116
+ //# sourceMappingURL=emulator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emulator.js","sourceRoot":"","sources":["../src/emulator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @ya-modbus/emulator - Software Modbus device emulator
3
+ */
4
+ export { ModbusEmulator } from './emulator.js';
5
+ export { EmulatedDevice } from './device.js';
6
+ export type { EmulatorConfig, DeviceConfig, RegisterStorage } from './types/config.js';
7
+ export type { EmulatedDevice as IEmulatedDevice } from './types/device.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACtF,YAAY,EAAE,cAAc,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @ya-modbus/emulator - Software Modbus device emulator
3
+ */
4
+ export { ModbusEmulator } from './emulator.js';
5
+ export { EmulatedDevice } from './device.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Base transport abstract class
3
+ */
4
+ export declare abstract class BaseTransport {
5
+ abstract start(): Promise<void>;
6
+ abstract stop(): Promise<void>;
7
+ abstract send(slaveId: number, response: Buffer): Promise<void>;
8
+ abstract onRequest(handler: (slaveId: number, request: Buffer) => Promise<Buffer>): void;
9
+ }
10
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/transports/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8BAAsB,aAAa;IACjC,QAAQ,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;CACzF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Base transport abstract class
3
+ */
4
+ export class BaseTransport {
5
+ }
6
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/transports/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,OAAgB,aAAa;CAKlC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * In-memory transport for testing
3
+ */
4
+ import { BaseTransport } from './base.js';
5
+ export declare class MemoryTransport extends BaseTransport {
6
+ private requestHandler?;
7
+ start(): Promise<void>;
8
+ stop(): Promise<void>;
9
+ send(_slaveId: number, _response: Buffer): Promise<void>;
10
+ onRequest(handler: (slaveId: number, request: Buffer) => Promise<Buffer>): void;
11
+ /**
12
+ * Send a request and get response (for testing)
13
+ */
14
+ sendRequest(slaveId: number, request: Buffer): Promise<Buffer>;
15
+ }
16
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/transports/memory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAEzC,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,cAAc,CAAC,CAAuD;IAExE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9D,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;IAI/E;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAMrE"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * In-memory transport for testing
3
+ */
4
+ import { BaseTransport } from './base.js';
5
+ export class MemoryTransport extends BaseTransport {
6
+ requestHandler;
7
+ async start() {
8
+ // Nothing to do for memory transport
9
+ }
10
+ async stop() {
11
+ // Nothing to do for memory transport
12
+ }
13
+ async send(_slaveId, _response) {
14
+ // In memory transport, send is not used directly
15
+ // Responses are returned from sendRequest
16
+ }
17
+ onRequest(handler) {
18
+ this.requestHandler = handler;
19
+ }
20
+ /**
21
+ * Send a request and get response (for testing)
22
+ */
23
+ async sendRequest(slaveId, request) {
24
+ if (!this.requestHandler) {
25
+ throw new Error('No request handler set');
26
+ }
27
+ return this.requestHandler(slaveId, request);
28
+ }
29
+ }
30
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/transports/memory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,OAAO,eAAgB,SAAQ,aAAa;IACxC,cAAc,CAAwD;IAE9E,KAAK,CAAC,KAAK;QACT,qCAAqC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,qCAAqC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,SAAiB;QAC5C,iDAAiD;QACjD,0CAA0C;IAC5C,CAAC;IAED,SAAS,CAAC,OAA8D;QACtE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAA;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,OAAe;QAChD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;CACF"}
@@ -0,0 +1,44 @@
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 interface RtuTransportConfig {
9
+ port: string;
10
+ baudRate?: number;
11
+ parity?: 'none' | 'even' | 'odd';
12
+ dataBits?: 7 | 8;
13
+ stopBits?: 1 | 2;
14
+ }
15
+ export declare class RtuTransport extends BaseTransport {
16
+ private requestHandler?;
17
+ private started;
18
+ constructor(_config: RtuTransportConfig);
19
+ start(): Promise<void>;
20
+ stop(): Promise<void>;
21
+ send(_slaveId: number, _response: Buffer): Promise<void>;
22
+ onRequest(handler: (slaveId: number, request: Buffer) => Promise<Buffer>): void;
23
+ /**
24
+ * Handle complete RTU frame (for future serial port implementation)
25
+ * @internal
26
+ */
27
+ handleFrame(frame: Buffer): Promise<void>;
28
+ /**
29
+ * Calculate Modbus RTU CRC-16 (Modbus variant)
30
+ *
31
+ * Returns CRC as a 16-bit value that can be written with writeUInt16LE
32
+ */
33
+ private calculateCRC;
34
+ /**
35
+ * Verify CRC of RTU frame
36
+ */
37
+ private verifyCRC;
38
+ /**
39
+ * Add CRC to buffer
40
+ * @internal
41
+ */
42
+ addCRC(buffer: Buffer): Buffer;
43
+ }
44
+ //# sourceMappingURL=rtu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtu.d.ts","sourceRoot":"","sources":["../../src/transports/rtu.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CACjB;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,OAAO,CAAC,cAAc,CAAC,CAAuD;IAC9E,OAAO,CAAC,OAAO,CAAQ;gBAEX,OAAO,EAAE,kBAAkB;IAKvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxD,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;IAI/E;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC/C;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAqBpB;;OAEG;IACH,OAAO,CAAC,SAAS;IAejB;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAO/B"}