btnexus-node
Version:
Baseline libary for btNexus development.
297 lines (248 loc) • 8.61 kB
JavaScript
/**
* Ophion Cloud client node
*
* @author Marc Fiedler
* @copyright 2020 Blackout Technologies, all rights reserved, all rights reserved
*/
// Use Strict mode ECMA Script 5+
"use_strict";
// System imports
const os = require('os');
const fs = require('fs');
// 3rd party imports
const io = require('socket.io-client');
const uniqid = require('uniqid');
const ip = require('ip');
const findRoot = require('find-root');
const chalk = require('chalk');
// local imports
const Message = require('./message');
const BTRequest = require('./btRequest');
const ConnectHash = require('./connectHash');
module.exports = class Node {
constructor(btNexusConfig){
// load connect hash
const root = findRoot(process.cwd());
const ch = new ConnectHash();
if( ch.isValid() ){
this.settings = ch.data;
}else{
this.error(ch.error);
process.exit(-1);
}
this.defaultMaxListeners = 10;
// once per start setup
this.nodeId = uniqid();
// configuration
this.config = JSON.parse(fs.readFileSync(root+'/package.json', 'utf-8'));
this.btNexus = btNexusConfig;
// message callbacks for topics. Format: messageCallbacks["topic"] = callback
this.messageCallbacks = {};
this.groups = {};
// define the namespace of the topic
this.namespace = "ai.blackout";
this.hostName = os.hostname();
this.hostAddress = ip.address();
this.nodeNamespace = undefined;
this.connectionAlive = false;
this.protocol = "wss";
this.heartBeatRate = 50000;
this.heartBeatCheckRate = 30000;
this.shouldCheckHeartbeat = true;
if( this.btNexus == undefined ){
if( config.btNexus != undefined ){
this.btNexus = config.btNexus;
}else{
this.emit('error', 'Unable to find btNexus connect config');
}
}
// Remove in >= 2.3.x
// legacy dependency, will be removed in later releases
if( this.btNexus.interface != undefined ){
this.warning("Deprication warning: btNexus.interface will no longer be supported from version >= 2.3.x");
this.btNexus.token = this.btNexus.interface;
}
var accessRequest = {
applicationId: this.settings.id,
applicationType: this.config.type
}
this.api = new BTRequest(this.settings.host, this.settings.token);
this.api.post("applicationAccessRequest", accessRequest, (success, resp) => {
if( success ){
this.sessionId = resp.sessionId;
this.run();
}else{
this.error("Unable to register with btNexus instance: "+resp.error+" Code: "+resp.code);
}
});
}
run(){
// start connecting
this.connect();
}
connect(){
// in case of reconnect, clear intervals
if( this.heartbeatInterval != undefined ){
this.heartbeatInterval.cancel();
}
this.messageCallbacks = {};
this.groups = {};
var host = undefined;
if( this.btNexus.protocol != undefined ){
protocol = this.btNexus.protocol;
}
if( this.settings.host != undefined ){
host = this.settings.host;
}
if( this.btNexus.port != undefined ){
host += ':'+this.btNexus.port;
}
// load the WebSocket
var protocol = "wss";
if( host.indexOf("http://") > -1 ){
protocol = "ws";
}
host = host.replace('https://', '');
host = host.replace('http://', '');
host = host.replace('wss://', '');
host = host.replace('ws://');
// make connect
this.info("Connecting to: "+protocol+'://'+host);
this.ws = io(protocol+'://'+host, {
query: 'instance='+this.settings.hostId
});
var self = this;
this.ws.on('btnexus-registration', function(msg) {
if( msg.success === true ){
self.connectionAlive = true;
if( self.btNexus.onConnected != undefined ){
self.btNexus.onConnected();
}
}else if(msg.success === false ){
self.error("Registration with btNexus failed. Please verify your accessToken and sessionId");
process.exit(-1);
}else{
// after connection is established, register with service
var rMsg = new Message('register');
rMsg.data.sessionId = self.sessionId;
rMsg.data.id = msg.id;
rMsg.data.host = self.hostName;
rMsg.data.ip = self.hostAddress;
rMsg.data.node = self.config;
this.nodeId = msg.id;
self.ws.emit('btnexus-registration', rMsg.toString());
}
});
this.ws.on('pong', function(data) {
// ignore pong message
if( process.env.DEBUG_MEMORY != undefined ){
console.log(new Date()+" pong");
}
});
this.ws.on('error', function(error) {
if( self.btNexus.onError != undefined ){
self.btNexus.onError(error);
}else{
console.log('btNexus Error: '+error)
}
});
this.ws.on('disconnect', function(code, reason) {
self.connectionAlive = false;
// reconnect
if( self.btNexus.onClose != undefined ){
self.btNexus.onClose();
}else{
console.log("Connection to btNexus lost.");
}
});
}
publish(group, topic, payload){
var acMsg = new Message('publish');
acMsg.data.nodeId = this.nodeId;
if( topic.indexOf(".") == -1 ){
topic = this.namespace+"."+topic;
}
acMsg.data.topic = topic;
acMsg.data.payload = payload;
acMsg.data.group = group;
// send message over the wire
this.ws.emit('btnexus-publish', acMsg.toString());
}
subscribe(group, topic, callback){
if( this[group] != undefined ){
if( topic.indexOf(".") == -1 ){
topic = this.namespace+"."+topic;
}
if( this[group][topic] == undefined ){
this[group][topic] = callback;
this.ws.on(topic, (msg) => {
this[group][topic](msg);
});
}else{
this.warning("Multiple subscriptions to topic: "+topic+" in group "+group);
}
}else{
this.error("Unable to subscribe to topic "+topic+" of unknown group "+group);
}
}
publishError(group, error){
this.publish(group, 'error', {message: error});
}
publishWarning(group, warning){
this.publish(group, 'warning', {message: warning});
}
publishDebug(group, debug){
this.publish(group, 'debug', {message: debug});
}
join(group){
if( this[group] == undefined ){
this[group] = [];
// subscribe to a specific topic
var sub = new Message('join');
sub.data.group = group;
this.ws.emit('btnexus-join', sub.toString());
}else{
this.warning("Duplicate join to group: "+group);
}
}
leave(groupName, joinCallback){
// register message callbacks
this.groups[groupName] = undefined;
// subscribe to a specific topic
var sub = new Message('leave');
sub.data.groupName = groupName;
this.ws.emit('message', sub.toString());
// TODO NEEDS TO BE IMPLEMENTED
// delete message after use
sub = undefined;
}
close(){
// close connection to socket
if( this.ws != undefined ){
this.ws.close();
}
}
error(error){
console.log(
chalk.red("[")+
chalk.red("btNexus Node")+
chalk.red("]")+": "+
error
);
}
info(text){
console.log(
chalk.white("[")+
chalk.blue("btNexus Node")+
chalk.white("] ")+": "+text
)
}
warning(text){
console.log(
chalk.yellow("[")+
chalk.blue("btNexus Node")+
chalk.yellow("] ")+": "+
chalk.yellow(text)
)
}
}