port select

This commit is contained in:
Yoonseok Hur 2023-11-06 21:48:11 +09:00
parent 4c3e7bff91
commit 98948c8296
3 changed files with 166 additions and 79 deletions

View File

@ -230,7 +230,7 @@
RED.nodes.registerType('serial-port',{
category: 'config',
defaults: {
//name: {value:""},
name: {value:""},
serialport: {value:"",required:true},
serialbaud: {value:"57600",required:true,validate:RED.validators.number()},
databits: {value:8,required:true},
@ -252,7 +252,7 @@
this.databits = this.databits || 8;
this.parity = this.parity || this._("serial.label.none");
this.stopbits = this.stopbits || 1;
return this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits;
return this.name||(this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits);
},
oneditprepare: function() {
var previous = null;
@ -361,3 +361,37 @@
}
});
</script>
<script type="text/html" data-template-name="port select">
<div class="form-row node-input-serial">
<label for="node-input-serial"><i class="fa fa-random"></i> <span data-i18n="serial.label.serialport"></span></label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-inputoutput-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('port select',{
category: 'network',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:1,
outputs:1,
icon: "serial.png",
align: "left",
label: function() {
var serialNode = RED.nodes.node(this.serial);
return this.name||(serialNode?serialNode.label().split(":")[0]:this._("serial.label.serial"));
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
outputLabels: function() { return RED.nodes.node(this.serial).bin === "bin" ? "buffer" : "string"; }
});
</script>

View File

@ -27,42 +27,102 @@ module.exports = function(RED) {
this.out = n.out || "char";
this.waitfor = n.waitfor || "";
this.responsetimeout = n.responsetimeout || 10000;
}
RED.nodes.registerType("serial-port",SerialPortNode);
this.newPort = (serialPort) => {
serialPool.close(this.serialport,() => {});
this.serialport = serialPort.port;
this.serialbaud = parseInt(serialPort.serialbaud) || this.serialbaud;
this.databits = parseInt(serialPort.databits) || this.databits;
this.parity = serialPort.parity || this.parity;
this.stopbits = parseInt(serialPort.stopbits) || this.stopbits;
this.dtr = serialPort.dtr || this.dtr;
this.rts = serialPort.rts || this.rts;
this.cts = serialPort.cts || this.cts;
this.dsr = serialPort.dsr || this.dsr;
this.bin = serialPort.bin || this.bin;
this.out = serialPort.out || this.out;
this.waitfor = serialPort.waitfor || this.waitfor;
this.responsetimeout = n.responsetimeout || 10000;
return serialPool.get(this);
}
};
RED.nodes.registerType("serial-port",SerialPortNode);
// receives msgs and sends them to the serial port
function SerialOutNode(n) {
RED.nodes.createNode(this,n);
this.serialConfig = RED.nodes.getNode(n.serial);
if (!this.serialConfig) {
this.error(RED._("serial.errors.missing-conf"), {});
return;
}
this.serial = n.serial;
this.serialConfig = RED.nodes.getNode(this.serial);
var node = this;
node.port = serialPool.get(this.serialConfig);
var serialConfig = this.serialConfig;
if (this.serialConfig) {
var node = this;
node.port = serialPool.get(this.serialConfig);
this.serialConfig.on('newport', function(newPort) {
node.port = serialPool.get(serialConfig);
});
node.on("input",function(msg) {
if (msg.hasOwnProperty("baudrate")) {
var baud = parseInt(msg.baudrate);
if (isNaN(baud)) {
node.error(RED._("serial.errors.badbaudrate"),msg);
} else {
node.port.update({baudRate: baud},function(err,res) {
if (err) {
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
node.error(errmsg,msg);
}
});
}
node.on("input",function(msg) {
if (msg.hasOwnProperty("baudrate")) {
var baud = parseInt(msg.baudrate);
if (isNaN(baud)) {
node.error(RED._("serial.errors.badbaudrate"),msg);
} else {
node.port.update({baudRate: baud},function(err,res) {
if (err) {
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
node.error(errmsg,msg);
}
});
}
if (!msg.hasOwnProperty("payload")) { return; } // do nothing unless we have a payload
var payload = node.port.encodePayload(msg.payload);
node.port.write(payload,function(err,res) {
if (err) {
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
node.error(errmsg,msg);
}
});
}
if (!msg.hasOwnProperty("payload")) { return; } // do nothing unless we have a payload
var payload = node.port.encodePayload(msg.payload);
node.port.write(payload,function(err,res) {
if (err) {
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
node.error(errmsg,msg);
}
});
});
node.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
});
node.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
});
}
RED.nodes.registerType("serial out",SerialOutNode);
// receives data from the serial port and emits msgs
function SerialInNode(n) {
RED.nodes.createNode(this,n);
this.serialConfig = RED.nodes.getNode(n.serial);
if (!this.serialConfig) {
this.error(RED._("serial.errors.missing-conf"), {});
return;
}
this.serial = n.serial;
var node = this;
this.serialConfig.on('newport', function(newPort) {
setCallback(node, node.serialConfig);
});
function setCallback(node) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"});
node.port = serialPool.get(node.serialConfig);
node.port.on('data', function(msgout) {
node.send(msgout);
});
node.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
@ -70,56 +130,8 @@ module.exports = function(RED) {
node.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
});
}
else {
this.error(RED._("serial.errors.missing-conf"), {});
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
}
else {
done();
}
});
}
RED.nodes.registerType("serial out",SerialOutNode);
// receives data from the serial port and emits msgs
function SerialInNode(n) {
RED.nodes.createNode(this,n);
this.serial = n.serial;
this.serialConfig = RED.nodes.getNode(this.serial);
if (this.serialConfig) {
var node = this;
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"});
node.port = serialPool.get(this.serialConfig);
this.port.on('data', function(msgout) {
node.send(msgout);
});
this.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
});
this.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
});
}
else {
this.error(RED._("serial.errors.missing-conf"), {});
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
}
else {
done();
}
});
};
setCallback(node)
}
RED.nodes.registerType("serial in",SerialInNode);
@ -200,6 +212,32 @@ module.exports = function(RED) {
}
RED.nodes.registerType("serial request", SerialRequestNode);
// Port Select Node
function PortSelectNode(n) {
RED.nodes.createNode(this,n);
this.serialConfig = RED.nodes.getNode(n.serial);
if (!this.serialConfig) {
this.error(RED._("serial.errors.missing-conf"), {});
return;
}
this.serial = n.serial;
var node = this;
node.port = serialPool.get(this.serialConfig);
node.on("input",function(msg) {
if (msg.payload.hasOwnProperty("port")) {
node.serialConfig.newPort(msg.payload);
node.serialConfig.emit('newport',msg.payload);
node.send(msg);
} else {
node.send({payload: {port: node.serialConfig.serialport}});
}
});
}
RED.nodes.registerType("port select", PortSelectNode);
var serialPool = (function() {
var connections = {};
return {

View File

@ -55,3 +55,18 @@ This node behaves as a tightly coupled combination of serial in and serial out n
Send the request message in `msg.payload` as you would do with a serial out node. The message will be forwarded to the serial port following a strict FIFO (First In, First Out) queue, waiting for a single response before transmitting the next request. Once a response is received (with the same logic of a serial in node), or after a timeout occurs, a message is produced on the output, with msg.payload containing the received response (or missing in case if timeout), msg.status containing relevant info, and all other fields preserved.
For consistency with the serial in node, msg.port is also set to the name of the port selected.
### Port Select
Provides the capability to change the serial ports on the run time programatically.
When you start the node-red, the flow(program) picks up the pre-programmed serial ports and open them. But when a device re-connects after disconnecting for any reason, it may be possible the port number change, and the end user of the flow can't change the port. With this `port selection node`, it's possible to let the user change the port while running the program by sending a message like this.
```json
{
"port": "/dev/tty.usbmodem1234561",
"serialbaud": 115200,
"databits": 8,
"parity": "none",
"stopbits": 1
}
```