Skip to content

Commit

Permalink
node-red-node-serialport port selection node addition (#1035)
Browse files Browse the repository at this point in the history
* port select
  • Loading branch information
yhur authored Nov 23, 2023
1 parent 81501df commit 2ddb603
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 89 deletions.
42 changes: 40 additions & 2 deletions io/serialport/25-serial.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
</script>

<script type="text/html" data-template-name="serial-port">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-config-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-row">
<label for="node-config-input-serialport"><i class="fa fa-random"></i> <span data-i18n="serial.label.serialport"></span></label>
<input type="text" id="node-config-input-serialport" style="width:66%;" data-i18n="[placeholder]serial.placeholder.serialport">
Expand Down Expand Up @@ -230,7 +234,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},
Expand All @@ -252,7 +256,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;
Expand Down Expand Up @@ -361,3 +365,37 @@
}
});
</script>

<script type="text/html" data-template-name="serial control">
<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('serial control',{
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>
239 changes: 153 additions & 86 deletions io/serialport/25-serial.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,101 +25,115 @@ module.exports = function(RED) {
this.dsr = n.dsr || "none";
this.bin = n.bin || "false";
this.out = n.out || "char";
this.enable = n.enable || true;
this.waitfor = n.waitfor || "";
this.responsetimeout = n.responsetimeout || 10000;
}
RED.nodes.registerType("serial-port",SerialPortNode);

this.changePort = (serialPort) => {
serialPool.close(this.serialport,() => {});
this.serialport = serialPort.serialport || this.serialport;
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;
}

};
RED.nodes.registerType("serial-port",SerialPortNode);

// receives msgs and sends them to the serial port
function SerialOutNode(n) {
RED.nodes.createNode(this,n);
this.serial = n.serial;
this.serialConfig = RED.nodes.getNode(this.serial);
this.serialConfig = RED.nodes.getNode(n.serial);

if (this.serialConfig) {
var node = this;
node.port = serialPool.get(this.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);
}
});
}
}
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"});
});
}
else {
if (!this.serialConfig) {
this.error(RED._("serial.errors.missing-conf"), {});
return;
}

this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
}
else {
done();
this.serial = n.serial;
var node = this;
node.port = serialPool.get(this.serialConfig);
var serialConfig = this.serialConfig;

this.serialConfig.on('start', function() {
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);
}
});
}
}
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;
this.serialConfig = RED.nodes.getNode(this.serial);
var node = this;

if (this.serialConfig) {
var node = this;
this.serialConfig.on('start', function() {
setCallback(node, node.serialConfig);
});

this.on("close", function(done) {
serialPool.close(this.serialConfig.serialport,done);
});

function setCallback(node) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"});
node.port = serialPool.get(this.serialConfig);
node.port = serialPool.get(node.serialConfig);

this.port.on('data', function(msgout) {
node.port.on('data', function(msgout) {
node.send(msgout);
});
this.port.on('ready', function() {
node.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
});
this.port.on('closed', function() {
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();
}
});
};
setCallback(node)
}
RED.nodes.registerType("serial in",SerialInNode);

Expand Down Expand Up @@ -162,28 +176,36 @@ module.exports = function(RED) {
});
});

// Serial In
this.port.on('data', function(msgout, sender) {
// serial request will only process incoming data pertaining to its own request (i.e. when it's at the head of the queue)
if (sender !== node) { return; }
node.status({fill:"green",shape:"dot",text:"node-red:common.status.ok"});
msgout.status = "OK";
node.send(msgout);
});
this.port.on('timeout', function(msgout, sender) {
if (sender !== node) { return; }
msgout.status = "ERR_TIMEOUT";
node.status({fill:"red",shape:"ring",text:"serial.status.timeout"});
node.send(msgout);
let serialConfig = this.serialConfig;
serialConfig.on('start', function() {
node.port = serialPool.get(serialConfig);
setCallback(node);
});

// Common part
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"});
});
// Serial In
function setCallback(node) {
node.port.on('data', function (msgout, sender) {
// serial request will only process incoming data pertaining to its own request (i.e. when it's at the head of the queue)
if (sender !== node) { return; }
node.status({ fill: "green", shape: "dot", text: "node-red:common.status.ok" });
msgout.status = "OK";
node.send(msgout);
});
node.port.on('timeout', function (msgout, sender) {
if (sender !== node) { return; }
msgout.status = "ERR_TIMEOUT";
node.status({ fill: "red", shape: "ring", text: "serial.status.timeout" });
node.send(msgout);
});

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" });
});
};
setCallback(node);
}
else {
this.error(RED._("serial.errors.missing-conf"), {});
Expand All @@ -200,6 +222,50 @@ module.exports = function(RED) {
}
RED.nodes.registerType("serial request", SerialRequestNode);


// Serial Control Node
function PortSelectNode(n) {
const configProps = [
"serialport", "serialbaud", "databits", "parity", "stopbits",
"dtr", "rts", "cts", "dsr", "bin", "out"
]
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 (configProps.some((p) =>{return msg.payload.hasOwnProperty(p)})) {
msg.payload.enable = msg.payload.hasOwnProperty('enable') ? msg.payload.enable : true;
node.serialConfig.changePort(msg.payload);
}
if (msg.payload.hasOwnProperty("enable")) {
// if any of config parameters or enable property is passed, do this control
node.serialConfig.enable = msg.payload.enable;
if (msg.payload.enable === true) {
node.serialConfig.emit('start');
} else {
serialPool.close(node.serialConfig.serialport,() => {
RED.log.info("[serialconfig:"+node.serialConfig.id+"] " + RED._("serial.stopped",{port:node.serialConfig.serialport}));
});
}
}
let currentConfig = {};
configProps.map((p) => {
currentConfig[p] = node.serialConfig[p];
});
currentConfig.enable = node.serialConfig.enable;
node.send({payload: currentConfig});
});
}
RED.nodes.registerType("serial control", PortSelectNode);

var serialPool = (function() {
var connections = {};
return {
Expand Down Expand Up @@ -334,6 +400,7 @@ module.exports = function(RED) {
},
}
//newline = newline.replace("\\n","\n").replace("\\r","\r");
obj._emitter.setMaxListeners(50);
var olderr = "";
var setupSerial = function() {
obj.serial = new SerialPort({
Expand Down
Loading

0 comments on commit 2ddb603

Please sign in to comment.