node-red-contrib-vscp-tcp
Version:

356 lines (286 loc) • 10.6 kB
JavaScript
///////////////////////////////////////////////////////////////////////////
// vscp-tcp-out
//
// VSCP tcp/ip output node.
//
// This file is part of the VSCP (http://www.vscp.org)
//
// The MIT License (MIT)
//
// Copyright © 2000-2020 Ake Hedman, Grodans Paradis AB
// <info@grodansparadis.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Debug:
// export NODE_DEBUG=node-red-vscp-tcp-out
const path = require('path');
// const hlp = require(path.join(__dirname, '/lib/dateTimeHelper.js'));
// https://nodejs.org/api/util.html
// export NODE_DEBUG=node-red-vscp-tcp* for all de for debug events from this module
const util = require('util');
const debuglog = util.debuglog('node-red-vscp-tcp-out');
const debuglogmsg = util.debuglog('node-red-vscp-tcp-out-msg');
const vscp_class = require('node-vscp-class');
const vscp_type = require('node-vscp-type');
const vscp = require('node-vscp');
const vscp_tcp_client = require('node-vscp-tcp');
module.exports = function(RED) {
'use strict';
///////////////////////////////////////////////////////////////////////////
// vscpTcpOutputNode
//
// @param {*} config - configuration
function vscpTcpOutputNode(config) {
// Create the tcp/ip input node
RED.nodes.createNode(this, config);
const node = this;
var flowContext = this.context().flow;
// Retrieve the config node
this.username = this.credentials.username || "admin";
this.password = this.credentials.password || "secret";
this.tls = parseInt(config.tls) || 0;
this.nodeid = config.id; // Id for node from node-red
// The channel id is saved in flow context if a key to
// save it under is entered.
this.keyctx = config.keyctx;
debuglog("keyctx: ",this.keyctx, typeof this.keyctx);
// Retrieve the config node
this.host = RED.nodes.getNode(config.host);
debuglog("Tcp/ip host node: " +
this.host.name + " " +
this.host.host + " " +
this.host.port + " " +
this.host.timeout + " " +
this.host.interface );
this.hostname = this.host.name;
this.host = this.host.host || 'localhost';
this.port = this.host.port || 9598;
this.timeout = this.host.timeout || 10000;
this.interface = this.host.interface || '';
this.keepalive = parseInt(this.host.keepalive) || 0;
this.chid = 0;
// Config node state
this.connected = false;
this.connecting = false;
this.options = {};
this.connecting = true;
this.status({fill:"yellow",shape:"dot",text:"node-red:common.status.connecting..."});
this.log('Connecting to VSCP server ' +
this.hostname + ' - ' +
this.host + ' on port: ' + ":" +
this.port);
this.vscpclient = new vscp_tcp_client();
///////////////////////////////////////////////////////////////////////////
// node-red: input
//
// Send Event
//
node.on('input', function(msg, send, done) {
var obj = {};
obj.event = msg.payload;
debuglog("Send event [input]");
doSendEvent(obj);
if (done) {
done();
}
});
///////////////////////////////////////////////////////////////////////////
// callback: connect
//
node.vscpclient.on('connect', function() {
debuglog("---------------- CONNECT -------------------");
node.connected = true;
node.connecting = false;
node.status({fill:"yellow",shape:"dot",text:"connecting..."});
});
///////////////////////////////////////////////////////////////////////////
// callback: disconnect
//
node.vscpclient.on('disconnect', function() {
debuglog("---------------- DISCONNECT -------------------");
node.connected = false;
node.connecting = false;
node.status({fill:"red",shape:"dot",text:"disconnected"});
});
///////////////////////////////////////////////////////////////////////////
// callback: timeout
//
node.vscpclient.on('timeout', function() {
debuglog("---------------- TIMEOUT -------------------");
node.connected = false;
node.connecting = false;
node.status({fill:"red",shape:"dot",text:"timeout"});
});
///////////////////////////////////////////////////////////////////////////
// callback: error
//
node.vscpclient.on('error', function(err) {
debuglog("---------------- ERROR -------------------");
if ('string' === typeof node.keyctx ) {
flowContext.set(node.keyctx+".error", err );
}
node.status({fill:"red",shape:"dot",text:"error "+err.message});
node.error(err, err.message);
});
///////////////////////////////////////////////////////////////////////////
// node-red: close
//
this.on('close', function(removed, done) {
debuglog("---------------- node-red CLOSE -------------------");
this.vscpclient.disconnect({
"onSuccess": function() {
debuglog("Disconnected ----------------->");
node.log('Disconnect from VSCP server');
done();
}
});
// No reconnects during close down
clearInterval(timeoutHandle);
node.status({fill:"red",shape:"dot",text:"Connection error: "+err.message});
if (removed) {
// This node has been deleted
debuglog("Node deleted");
} else {
// This node is being restarted
debuglog("Node restarted");
}
// const closeAsync = async () => {
// await this.vscpclient.disconnect({
// "onSuccess": function(removed, done) {
// debuglog("----------------->");
// done();
// }
// });
// node.log('Disconnect from VSCP server');
// }
// closeAsync().catch(err => {
// debuglog("Catching disconnect error");
// debuglog(err);
// node.error("Catch on close",err, err.message);
// })
//done();
});
///////////////////////////////////////////////////////////////////////////
// doConnect
//
function doConnect(client,options) {
debuglog("Options :",options);
let response = {}; // Command response object
const doAsync = async () => {
// Connect to VSCP server/device
debuglog("Connect", options);
response = await client.connect(
{
host: options.host,
port: options.port,
timeout: options.timeout
});
debuglog("connect response: "+response.result);
if ( 'success' !== response.result ) {
throw new Error("Failed to connect to VSCP host.");
}
debuglog("User login: ");
// Log on to server (step 1 user name)
// The response object is returned and logged
response = await client.sendCommand(
{
command: "user",
argument: options.username
});
debuglog("user response: "+response.result);
// Log on to server (step 2 password)
response = await client.sendCommand(
{
command: "pass",
argument: options.password
});
debuglog("pass response: "+response.result);
// Send no operation command (does nothing)
response = await client.sendCommand(
{
command: "noop"
})
debuglog("noop response: "+response.result);
node.status({fill:"green",shape:"dot",text:"connected"});
}
doAsync().catch(err => {
node.error(err.message);
debuglog(err.message);
node.status({fill:"red",shape:"dot",text:"Connection error: "+err.message});
})
}
function conn() {
node.connected = false;
node.connecting = true;
// Try to connect to the remote host
doConnect(node.vscpclient,
{
host: node.host,
port: node.port,
timeout: node.timeout,
username: node.username,
password: node.password,
keyctx: node.keyctx,
filter: node.filter
});
}
conn();
var timeoutHandle = setInterval( () => {
debuglog("Link timeout: connected=" +
node.connected +
" connecting=" +
node.connecting +
" keepalive=" +
node.keepalive );
if ( !node.connected && !node.connecting ) {
node.log("Reconnecting!");
node.status({fill:"black",shape:"dot",text:"Reconnecting "});
conn();
}
}, node.keepalive || 5000 );
///////////////////////////////////////////////////////////////////////////
// doSendEvent
//
function doSendEvent(ev) {
debuglog("Send event [func]",ev);
debuglog(ev,node.keyctx);
// Get channel id
if ('string' === typeof node.keyctx ) {
node.chid = flowContext.get(node.keyctx + ".chid");
debuglog("Getting keyctx", node.chid, typeof node.chid );
ev.event.vscpObId = node.chid;
debuglog(ev);
debuglog("ev.event.vscpObId=",ev.event.vscpObId);
}
else {
ev.event.vscpObId = 0;
debuglog("ev.event.vscpObId=",ev.event.vscpObId);
}
node.vscpclient.sendEvent(ev);
debuglog("ev.event sent");
}
}
RED.nodes.registerType('vscp-tcp-out', vscpTcpOutputNode, {
credentials: {
username: {type:"text"},
password: {type:"password"}
}
});
};