[CCSDK-28] populated the seed code for dgbuilder
[ccsdk/distribution.git] / dgbuilder / core_nodes / io / 25-serial.js
1 /**
2 * Copyright 2013,2014 IBM Corp.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 **/
16
17 module.exports = function(RED) {
18     "use strict";
19     var settings = RED.settings;
20     var events = require("events");
21     var util = require("util");
22     var serialp = require("serialport");
23     var bufMaxSize = 32768;  // Max serial buffer size, for inputs...
24
25     // TODO: 'serialPool' should be encapsulated in SerialPortNode
26
27     function SerialPortNode(n) {
28         RED.nodes.createNode(this,n);
29         this.serialport = n.serialport;
30         this.newline = n.newline;
31         this.addchar = n.addchar || "false";
32         this.serialbaud = parseInt(n.serialbaud) || 57600;
33         this.databits = parseInt(n.databits) || 8;
34         this.parity = n.parity || "none";
35         this.stopbits = parseInt(n.stopbits) || 1;
36         this.bin = n.bin || "false";
37         this.out = n.out || "char";
38     }
39     RED.nodes.registerType("serial-port",SerialPortNode);
40
41     function SerialOutNode(n) {
42         RED.nodes.createNode(this,n);
43         this.serial = n.serial;
44         this.serialConfig = RED.nodes.getNode(this.serial);
45
46         if (this.serialConfig) {
47             var node = this;
48             node.port = serialPool.get(this.serialConfig.serialport,
49                 this.serialConfig.serialbaud,
50                 this.serialConfig.databits,
51                 this.serialConfig.parity,
52                 this.serialConfig.stopbits,
53                 this.serialConfig.newline);
54             node.addCh = "";
55             if (node.serialConfig.addchar == "true") {
56                 node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0");
57             }
58             node.on("input",function(msg) {
59                 var payload = msg.payload;
60                 if (!Buffer.isBuffer(payload)) {
61                     if (typeof payload === "object") {
62                         payload = JSON.stringify(payload);
63                     } else {
64                         payload = payload.toString();
65                     }
66                     payload += node.addCh;
67                 } else if (node.addCh !== "") {
68                     payload = Buffer.concat([payload,new Buffer(node.addCh)]);
69                 }
70                 node.port.write(payload,function(err,res) {
71                     if (err) {
72                         node.error(err);
73                     }
74                 });
75             });
76             node.port.on('ready', function() {
77                 node.status({fill:"green",shape:"dot",text:"connected"});
78             });
79             node.port.on('closed', function() {
80                 node.status({fill:"red",shape:"ring",text:"not connected"});
81             });
82         } else {
83             this.error("missing serial config");
84         }
85
86         this.on("close", function(done) {
87             if (this.serialConfig) {
88                 serialPool.close(this.serialConfig.serialport,done);
89             } else {
90                 done();
91             }
92         });
93     }
94     RED.nodes.registerType("serial out",SerialOutNode);
95
96
97     function SerialInNode(n) {
98         RED.nodes.createNode(this,n);
99         this.serial = n.serial;
100         this.serialConfig = RED.nodes.getNode(this.serial);
101
102         if (this.serialConfig) {
103             var node = this;
104             node.tout = null;
105             var buf;
106             if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); }
107             else { buf = new Buffer(Number(node.serialConfig.newline)); }
108             var i = 0;
109             node.status({fill:"grey",shape:"dot",text:"unknown"});
110             node.port = serialPool.get(this.serialConfig.serialport,
111                 this.serialConfig.serialbaud,
112                 this.serialConfig.databits,
113                 this.serialConfig.parity,
114                 this.serialConfig.stopbits,
115                 this.serialConfig.newline
116             );
117
118             var splitc;
119             if (node.serialConfig.newline.substr(0,2) == "0x") {
120                 splitc = new Buffer([parseInt(node.serialConfig.newline)]);
121             } else {
122                 splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"));
123             }
124
125             this.port.on('data', function(msg) {
126                 // single char buffer
127                 if ((node.serialConfig.newline === 0)||(node.serialConfig.newline === "")) {
128                     if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); }
129                     else { node.send({"payload": new Buffer([msg])}); }
130                 }
131                 else {
132                     // do the timer thing
133                     if (node.serialConfig.out === "time")  {
134                         if (node.tout) {
135                             i += 1;
136                             buf[i] = msg;
137                         }
138                         else {
139                             node.tout = setTimeout(function () {
140                                 node.tout = null;
141                                 var m = new Buffer(i+1);
142                                 buf.copy(m,0,0,i+1);
143                                 if (node.serialConfig.bin !== "bin") { m = m.toString(); }
144                                 node.send({"payload": m});
145                                 m = null;
146                             }, node.serialConfig.newline);
147                             i = 0;
148                             buf[0] = msg;
149                         }
150                     }
151                     // count bytes into a buffer...
152                     else if (node.serialConfig.out === "count") {
153                         buf[i] = msg;
154                         i += 1;
155                         if ( i >= parseInt(node.serialConfig.newline)) {
156                             var m = new Buffer(i);
157                             buf.copy(m,0,0,i);
158                             if (node.serialConfig.bin !== "bin") { m = m.toString(); }
159                             node.send({"payload":m});
160                             m = null;
161                             i = 0;
162                         }
163                     }
164                     // look to match char...
165                     else if (node.serialConfig.out === "char") {
166                         buf[i] = msg;
167                         i += 1;
168                         if ((msg === splitc[0]) || (i === bufMaxSize)) {
169                             var m = new Buffer(i);
170                             buf.copy(m,0,0,i);
171                             if (node.serialConfig.bin !== "bin") { m = m.toString(); }
172                             node.send({"payload":m});
173                             m = null;
174                             i = 0;
175                         }
176                     }
177                     else { console.log("Should never get here"); }
178                 }
179             });
180             this.port.on('ready', function() {
181                 node.status({fill:"green",shape:"dot",text:"connected"});
182             });
183             this.port.on('closed', function() {
184                 node.status({fill:"red",shape:"ring",text:"not connected"});
185             });
186         } else {
187             this.error("missing serial config");
188         }
189
190         this.on("close", function(done) {
191             if (this.serialConfig) {
192                 serialPool.close(this.serialConfig.serialport,done);
193             } else {
194                 done();
195             }
196         });
197     }
198     RED.nodes.registerType("serial in",SerialInNode);
199
200
201     var serialPool = function() {
202         var connections = {};
203         return {
204             get:function(port,baud,databits,parity,stopbits,newline,callback) {
205                 var id = port;
206                 if (!connections[id]) {
207                     connections[id] = function() {
208                         var obj = {
209                             _emitter: new events.EventEmitter(),
210                             serial: null,
211                             _closing: false,
212                             tout: null,
213                             on: function(a,b) { this._emitter.on(a,b); },
214                             close: function(cb) { this.serial.close(cb); },
215                             write: function(m,cb) { this.serial.write(m,cb); },
216                         }
217                         //newline = newline.replace("\\n","\n").replace("\\r","\r");
218                         var setupSerial = function() {
219                             //if (newline == "") {
220                                 obj.serial = new serialp.SerialPort(port,{
221                                     baudrate: baud,
222                                     databits: databits,
223                                     parity: parity,
224                                     stopbits: stopbits,
225                                     parser: serialp.parsers.raw
226                                 },true, function(err, results) { if (err) { obj.serial.emit('error',err); } });
227                             //}
228                             //else {
229                             //    obj.serial = new serialp.SerialPort(port,{
230                             //        baudrate: baud,
231                             //        databits: databits,
232                             //        parity: parity,
233                             //        stopbits: stopbits,
234                             //        parser: serialp.parsers.readline(newline)
235                             //    },true, function(err, results) { if (err) obj.serial.emit('error',err); });
236                             //}
237                             obj.serial.on('error', function(err) {
238                                 util.log("[serial] serial port "+port+" error "+err);
239                                 obj._emitter.emit('closed');
240                                 obj.tout = setTimeout(function() {
241                                     setupSerial();
242                                 }, settings.serialReconnectTime);
243                             });
244                             obj.serial.on('close', function() {
245                                 if (!obj._closing) {
246                                     util.log("[serial] serial port "+port+" closed unexpectedly");
247                                     obj._emitter.emit('closed');
248                                     obj.tout = setTimeout(function() {
249                                         setupSerial();
250                                     }, settings.serialReconnectTime);
251                                 }
252                             });
253                             obj.serial.on('open',function() {
254                                 util.log("[serial] serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits);
255                                 if (obj.tout) { clearTimeout(obj.tout); }
256                                 //obj.serial.flush();
257                                 obj._emitter.emit('ready');
258                             });
259                             obj.serial.on('data',function(d) {
260                                 //console.log(Buffer.isBuffer(d),d.length,d);
261                                 //if (typeof d !== "string") {
262                                 //    //d = d.toString();
263                                     for (var z=0; z<d.length; z++) {
264                                         obj._emitter.emit('data',d[z]);
265                                     }
266                                 //}
267                                 //else {
268                                 //    obj._emitter.emit('data',d);
269                                 //}
270                             });
271                             obj.serial.on("disconnect",function() {
272                                 util.log("[serial] serial port "+port+" gone away");
273                             });
274                         }
275                         setupSerial();
276                         return obj;
277                     }();
278                 }
279                 return connections[id];
280             },
281             close: function(port,done) {
282                 if (connections[port]) {
283                     if (connections[port].tout != null) {
284                         clearTimeout(connections[port].tout);
285                     }
286                     connections[port]._closing = true;
287                     try {
288                         connections[port].close(function() {
289                             util.log("[serial] serial port closed");
290                             done();
291                         });
292                     }
293                     catch(err) { }
294                     delete connections[port];
295                 } else {
296                     done();
297                 }
298             }
299         }
300     }();
301
302     RED.httpAdmin.get("/serialports",function(req,res) {
303         serialp.list(function (err, ports) {
304             //console.log(JSON.stringify(ports));
305             res.writeHead(200, {'Content-Type': 'text/plain'});
306             res.write(JSON.stringify(ports));
307             res.end();
308         });
309     });
310 }