-
Hi, With serialport v9.x, I was able to create a custom class that extended What I'm trying to accomplish is for the main node.js program to be given a device path (to either a real device or a mock path - it shouldn't matter). If a real device is chosen, then it would be provided What is the proper way for me to create this emulator in serialport v10.x? The emulator should be able to capture data sent from the application (I assume in the 'data' event handler) and then send the appropriate response back to the application. Here is what I've attempted so far: const { SerialPortMock } = require("serialport");
const { SerialPort } = require("serialport");
const { ReadlineParser } = require("@serialport/parser-readline");
// Create a port for the emulated device
SerialPortMock.binding.createPort("/dev/tty_Mock", {
echo: true,
record: true,
lock: false,
});
const emulator = new SerialPortMock({
path: "/dev/tty_Mock",
baudRate: 115200,
lock: false,
});
// Setup the emulator to capture and respond to request from the interface
const emulatorParser = new ReadlineParser();
emulator.pipe(emulatorParser).on("data", (line) => {
console.log(`MOCK: ${line.toUpperCase()}`);
// The next line, if un-commented will trigger an infinite loop
// emulator.write("Response from Mock\n"); // <<< This line just triggers this same 'data' event handler for the emulator and not back to the interface
// Is there a way to emit data back?
});
// wait for port to open...
emulator.on("open", () => {
emulator.port.emitData("Emulator Opened!\n");
});
// Setup a serial port (only different required here when switching between a real device and the emulator should be the path)
const interface = new SerialPort({
path: "/dev/tty_Mock",
baudRate: 115200,
lock: false,
});
const parser = interface.pipe(new ReadlineParser());
parser.on("data", (data) => {
console.log(`INT: ${data}`);
});
interface.write("Command-FromInterface\n"); This fails with an error from serialport/stream: Any suggestions on the proper way to do this? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
I'm guessing that no response is a bad sign for me... |
Beta Was this translation helpful? Give feedback.
-
I've been able to create an emulator. I'm not sure it is the most elegant solution, but it works. The approach I've used might not be future-compatible as SerialPort moves on to newer versions (specifically changes made to MockBinding and MockPortBinding, so for reference, the below is using v10.4.0. My demo project is in JavaScript and not TypeScript, so I grabbed the transpiled js file from @serialport/binding-mock as my base code, and trimmed down and modified from there. In the MockBinding definition, the only change I made was to add a section at the end of the // If an emulator command handler (method) was provided, then use that instead
if (openOptions?.emulator instanceof Function) {
const emulatorBinding = new EmulatorMockPortBinding(port, openOptions);
emulatorBinding.commandCallback = openOptions.emulator; // Point to the callback provided
return emulatorBinding;
}
return new MockPortBinding(port, openOptions); // Otherwise, use the original MockPortBinding This EmulatorMockPortBinding class is an extension of MockPortBinding that overwrites the class EmulatorMockPortBinding extends MockPortBinding {
/**
* This method should be overridden to handle the commands and provide the
* response expected from the emulated device.
*
* @example
* async function emulatorCommandHandler(buffer) {
* const cmd = Buffer.from(buffer).toString();
* let response = "Unknown Command!\n"; // Default response
* // NOTE: Custom logic here to determine the proper response
* return response;
* }
* const spStream = new SerialPortStream({..., emulator: emulatorCommandHandler });
*
* @param {buffer} data
*/
commandCallback = async (buffer) => {
return buffer;
};
constructor(port, openOptions) {
super(port, openOptions);
}
async write(buffer) {
const response = await this.commandCallback(buffer);
super.emitData(response);
}
} To use this emulator, // Create a port and enable the echo and recording.
EmulatorMockBinding.createPort("/dev/tty_Emulator");
const cmdPattern = /(?<cmd>\w+)\b(?<args>.*)/;
async function emulatorCommandHandler(buffer) {
const cmdMatch = cmdPattern.exec(buffer.toString());
const supportedCommands = {
ECHO: (args) => args.trim(),
DATE: () => new Date().toDateString(),
TIME: () => new Date().toTimeString(),
};
if (cmdMatch) {
const cmd = cmdMatch.groups.cmd.toLocaleUpperCase();
if (cmd in supportedCommands) {
return `${supportedCommands[cmd](cmdMatch.groups.args)}\n`; // Append new line terminator char
} else {
return "ERROR: Unknown Command\n";
}
}
return "ERROR: Invalid Syntax\n";
}
const spStream = new SerialPortStream({
path: "/dev/tty_Emulator",
baudRate: 115200,
binding: EmulatorMockBinding,
emulator: emulatorCommandHandler,
});
// Setup listener to handle response from emulator
const parser = new ReadlineParser();
spStream.pipe(parser).on("data", (line) => {
console.log(`Rx: ${line.toUpperCase()}`);
});
// When the port opens, start sending commands to the emulator
spStream.on("open", () => {
// Test by sending data to the emulator
setTimeout(() => spStream.write("ECHO Hello World\n"), 0);
setTimeout(() => spStream.write("DATE\n"), 250);
setTimeout(() => spStream.write("TIME\n"), 500);
setTimeout(() => spStream.write("Bad Command\n"), 750);
}); I've attached the source code for reference: |
Beta Was this translation helpful? Give feedback.
I've been able to create an emulator. I'm not sure it is the most elegant solution, but it works. The approach I've used might not be future-compatible as SerialPort moves on to newer versions (specifically changes made to MockBinding and MockPortBinding, so for reference, the below is using v10.4.0.
My demo project is in JavaScript and not TypeScript, so I grabbed the transpiled js file from @serialport/binding-mock as my base code, and trimmed down and modified from there.
In the MockBinding definition, the only change I made was to add a section at the end of the
open
method that looked for anemulator
field to be defined in theopenOptions
:// If an emulator command handler (method) w…