Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node-red-node-serialport port selection node addition #1035

Merged
merged 21 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
234 changes: 148 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,() => {});
yhur marked this conversation as resolved.
Show resolved Hide resolved
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);

if (this.serialConfig) {
var node = this;
node.port = serialPool.get(this.serialConfig);
this.serialConfig = RED.nodes.getNode(n.serial);

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,45 @@ 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}));
});
}
}
node.send({payload: node.serialConfig});
yhur marked this conversation as resolved.
Show resolved Hide resolved
});
}
RED.nodes.registerType("serial control", PortSelectNode);

var serialPool = (function() {
var connections = {};
return {
Expand Down Expand Up @@ -334,6 +395,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