mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
7c7f030aa8
Having counted the correct number into the buffer it seems a shame not to send them all out to the next node... (Also allow separator char to be specified as hex (eg 0x0a))
306 lines
13 KiB
JavaScript
306 lines
13 KiB
JavaScript
/**
|
|
* Copyright 2013,2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
**/
|
|
|
|
module.exports = function(RED) {
|
|
"use strict";
|
|
var settings = RED.settings;
|
|
var events = require("events");
|
|
var util = require("util");
|
|
var serialp = require("serialport");
|
|
var bufMaxSize = 32768; // Max serial buffer size, for inputs...
|
|
|
|
// TODO: 'serialPool' should be encapsulated in SerialPortNode
|
|
|
|
function SerialPortNode(n) {
|
|
RED.nodes.createNode(this,n);
|
|
this.serialport = n.serialport;
|
|
this.newline = n.newline;
|
|
this.addchar = n.addchar || "false";
|
|
this.serialbaud = parseInt(n.serialbaud) || 57600;
|
|
this.databits = parseInt(n.databits) || 8;
|
|
this.parity = n.parity || "none";
|
|
this.stopbits = parseInt(n.stopbits) || 1;
|
|
this.bin = n.bin || "false";
|
|
this.out = n.out || "char";
|
|
}
|
|
RED.nodes.registerType("serial-port",SerialPortNode);
|
|
|
|
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.serialport,
|
|
this.serialConfig.serialbaud,
|
|
this.serialConfig.databits,
|
|
this.serialConfig.parity,
|
|
this.serialConfig.stopbits,
|
|
this.serialConfig.newline);
|
|
node.addCh = "";
|
|
if (node.serialConfig.addchar == "true") {
|
|
node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0");
|
|
}
|
|
node.on("input",function(msg) {
|
|
var payload = msg.payload;
|
|
if (!Buffer.isBuffer(payload)) {
|
|
if (typeof payload === "object") {
|
|
payload = JSON.stringify(payload);
|
|
} else {
|
|
payload = new String(payload);
|
|
}
|
|
payload += node.addCh;
|
|
} else if (node.addCh !== "") {
|
|
payload = Buffer.concat([payload,new Buffer(node.addCh)]);
|
|
}
|
|
node.port.write(payload,function(err,res) {
|
|
if (err) {
|
|
node.error(err);
|
|
}
|
|
});
|
|
});
|
|
node.port.on('ready', function() {
|
|
node.status({fill:"green",shape:"dot",text:"connected"});
|
|
});
|
|
node.port.on('closed', function() {
|
|
node.status({fill:"red",shape:"ring",text:"not connected"});
|
|
});
|
|
} else {
|
|
this.error("missing serial config");
|
|
}
|
|
|
|
this.on("close", function(done) {
|
|
if (this.serialConfig) {
|
|
serialPool.close(this.serialConfig.serialport,done);
|
|
} else {
|
|
done();
|
|
}
|
|
});
|
|
}
|
|
RED.nodes.registerType("serial out",SerialOutNode);
|
|
|
|
|
|
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.tout = null;
|
|
var buf;
|
|
if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); }
|
|
else { buf = new Buffer(Number(node.serialConfig.newline)); }
|
|
var i = 0;
|
|
node.status({fill:"grey",shape:"dot",text:"unknown"});
|
|
node.port = serialPool.get(this.serialConfig.serialport,
|
|
this.serialConfig.serialbaud,
|
|
this.serialConfig.databits,
|
|
this.serialConfig.parity,
|
|
this.serialConfig.stopbits,
|
|
this.serialConfig.newline
|
|
);
|
|
|
|
if (node.serialConfig.newline.substr(0,2) == "0x") {
|
|
var splitc = new Buffer([parseInt(node.serialConfig.newline)]);
|
|
} else {
|
|
var splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"));
|
|
}
|
|
|
|
this.port.on('data', function(msg) {
|
|
// single char buffer
|
|
if ((node.serialConfig.newline == 0)||(node.serialConfig.newline == "")) {
|
|
if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); }
|
|
else { node.send({"payload": new Buffer([msg])}); }
|
|
}
|
|
else {
|
|
// do the timer thing
|
|
if (node.serialConfig.out === "time") {
|
|
if (node.tout) {
|
|
i += 1;
|
|
buf[i] = msg;
|
|
}
|
|
else {
|
|
node.tout = setTimeout(function () {
|
|
node.tout = null;
|
|
var m = new Buffer(i+1);
|
|
buf.copy(m,0,0,i+1);
|
|
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
|
|
node.send({"payload": m});
|
|
m = null;
|
|
}, node.serialConfig.newline);
|
|
i = 0;
|
|
buf[0] = msg;
|
|
}
|
|
}
|
|
// count bytes into a buffer...
|
|
else if (node.serialConfig.out === "count") {
|
|
buf[i] = msg;
|
|
i += 1;
|
|
if ( i >= parseInt(node.serialConfig.newline)) {
|
|
var m = new Buffer(i);
|
|
buf.copy(m,0,0,i);
|
|
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
|
|
node.send({"payload":m});
|
|
m = null;
|
|
i = 0;
|
|
}
|
|
}
|
|
// look to match char...
|
|
else if (node.serialConfig.out === "char") {
|
|
buf[i] = msg;
|
|
i += 1;
|
|
if ((msg === splitc[0]) || (i === bufMaxSize)) {
|
|
var m = new Buffer(i);
|
|
buf.copy(m,0,0,i);
|
|
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
|
|
node.send({"payload":m});
|
|
m = null;
|
|
i = 0;
|
|
}
|
|
}
|
|
else { console.log("Should never get here"); }
|
|
}
|
|
});
|
|
this.port.on('ready', function() {
|
|
node.status({fill:"green",shape:"dot",text:"connected"});
|
|
});
|
|
this.port.on('closed', function() {
|
|
node.status({fill:"red",shape:"ring",text:"not connected"});
|
|
});
|
|
} else {
|
|
this.error("missing serial config");
|
|
}
|
|
|
|
this.on("close", function(done) {
|
|
if (this.serialConfig) {
|
|
serialPool.close(this.serialConfig.serialport,done);
|
|
} else {
|
|
done();
|
|
}
|
|
});
|
|
}
|
|
RED.nodes.registerType("serial in",SerialInNode);
|
|
|
|
|
|
var serialPool = function() {
|
|
var connections = {};
|
|
return {
|
|
get:function(port,baud,databits,parity,stopbits,newline,callback) {
|
|
var id = port;
|
|
if (!connections[id]) {
|
|
connections[id] = function() {
|
|
var obj = {
|
|
_emitter: new events.EventEmitter(),
|
|
serial: null,
|
|
_closing: false,
|
|
tout: null,
|
|
on: function(a,b) { this._emitter.on(a,b); },
|
|
close: function(cb) { this.serial.close(cb); },
|
|
write: function(m,cb) { this.serial.write(m,cb); },
|
|
}
|
|
//newline = newline.replace("\\n","\n").replace("\\r","\r");
|
|
var setupSerial = function() {
|
|
//if (newline == "") {
|
|
obj.serial = new serialp.SerialPort(port,{
|
|
baudrate: baud,
|
|
databits: databits,
|
|
parity: parity,
|
|
stopbits: stopbits,
|
|
parser: serialp.parsers.raw
|
|
},true, function(err, results) { if (err) obj.serial.emit('error',err); });
|
|
//}
|
|
//else {
|
|
// obj.serial = new serialp.SerialPort(port,{
|
|
// baudrate: baud,
|
|
// databits: databits,
|
|
// parity: parity,
|
|
// stopbits: stopbits,
|
|
// parser: serialp.parsers.readline(newline)
|
|
// },true, function(err, results) { if (err) obj.serial.emit('error',err); });
|
|
//}
|
|
obj.serial.on('error', function(err) {
|
|
util.log("[serial] serial port "+port+" error "+err);
|
|
obj._emitter.emit('closed');
|
|
obj.tout = setTimeout(function() {
|
|
setupSerial();
|
|
}, settings.serialReconnectTime);
|
|
});
|
|
obj.serial.on('close', function() {
|
|
if (!obj._closing) {
|
|
util.log("[serial] serial port "+port+" closed unexpectedly");
|
|
obj._emitter.emit('closed');
|
|
obj.tout = setTimeout(function() {
|
|
setupSerial();
|
|
}, settings.serialReconnectTime);
|
|
}
|
|
});
|
|
obj.serial.on('open',function() {
|
|
util.log("[serial] serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits);
|
|
if (obj.tout) { clearTimeout(obj.tout); }
|
|
//obj.serial.flush();
|
|
obj._emitter.emit('ready');
|
|
});
|
|
obj.serial.on('data',function(d) {
|
|
//console.log(Buffer.isBuffer(d),d.length,d);
|
|
//if (typeof d !== "string") {
|
|
// //d = d.toString();
|
|
for (var z=0; z<d.length; z++) {
|
|
obj._emitter.emit('data',d[z]);
|
|
}
|
|
//}
|
|
//else {
|
|
// obj._emitter.emit('data',d);
|
|
//}
|
|
});
|
|
}
|
|
setupSerial();
|
|
return obj;
|
|
}();
|
|
}
|
|
return connections[id];
|
|
},
|
|
close: function(port,done) {
|
|
if (connections[port]) {
|
|
if (connections[port].tout != null) {
|
|
clearTimeout(connections[port].tout);
|
|
}
|
|
connections[port]._closing = true;
|
|
try {
|
|
connections[port].close(function() {
|
|
util.log("[serial] serial port closed");
|
|
done();
|
|
});
|
|
} catch(err) { };
|
|
delete connections[port];
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
}
|
|
}();
|
|
|
|
RED.httpAdmin.get("/serialports",function(req,res) {
|
|
serialp.list(function (err, ports) {
|
|
//console.log(JSON.stringify(ports));
|
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
res.write(JSON.stringify(ports));
|
|
res.end();
|
|
});
|
|
});
|
|
}
|