Skip to content

Commit

Permalink
bi-directional setup for REM/njsPC
Browse files Browse the repository at this point in the history
  • Loading branch information
tagyoureit committed Apr 5, 2021
1 parent 8e7f51e commit 8b04dcd
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 35 deletions.
10 changes: 10 additions & 0 deletions config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,15 @@ class Config {
});
}
}
public setInterface(obj: any){
let interfaces: any = this._cfg.web.interfaces;
for (var i in interfaces) {
if (interfaces[i].uuid === obj.uuid) {
interfaces[i] = obj;
break;
}
}
this.update();
}
}
export const config: Config = new Config();
2 changes: 1 addition & 1 deletion controller/nixie/chemistry/ChemController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export class NixieChemController extends NixieEquipment {
// We are not processing Homegrown at this point.
// Check each piece of equipment to make sure it is doing its thing.
this.calculateSaturationIndex();
await this.processAlarms(schem);
this.processAlarms(schem);
if (this.chem.ph.enabled) await this.ph.checkDosing(this.chem, schem.ph);
if (this.chem.orp.enabled) await this.orp.checkDosing(this.chem, schem.orp);
}
Expand Down
19 changes: 3 additions & 16 deletions defaultConfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"controller": {
"comms": {
"enabled": true,
"enabled": true,
"rs485Port": "/dev/ttyUSB0",
"mockPort": false,
"netConnect": false,
Expand Down Expand Up @@ -45,7 +45,7 @@
"enabled": false
},
"ssdp": {
"enabled": false
"enabled": true
}
},
"services": {},
Expand Down Expand Up @@ -145,7 +145,6 @@
"changesOnly": true
}
},

"rem": {
"name": "Relay Equipment Manager",
"type": "rem",
Expand All @@ -167,18 +166,6 @@
"reconnection": true,
"reconnectionDelayMax": 20000
}
},
"rulesManager": {
"name": "Rules Manager",
"type": "http",
"enabled": false,
"fileName": "rulesManager.json",
"globals": {},
"options": {
"host": "localhost",
"port": 4200
},
"uuid": "65ee2581-e652-427e-99c6-17c6aa02a6e3"
}
}
},
Expand Down Expand Up @@ -251,7 +238,7 @@
"enabled": true,
"level": "info",
"captureForReplay": false,
"logToFile": false
"logToFile": false
}
},
"appVersion": "0.0.1"
Expand Down
92 changes: 76 additions & 16 deletions web/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ export class WebServer {
this._servers[s] = undefined;
} catch (err) { console.log(`Error stopping server ${s}: ${err.message}`); }
}
} catch (err) {`Error stopping servers`}

} catch (err) { `Error stopping servers` }
}
private getInterface() {
const networkInterfaces = os.networkInterfaces();
Expand Down Expand Up @@ -171,6 +170,12 @@ export class WebServer {
public findServer(name: string): ProtoServer { return this._servers.find(elem => elem.name === name); }
public findServersByType(type: string) { return this._servers.filter(elem => elem.type === type); }
public findServerByGuid(uuid: string) { return this._servers.find(elem => elem.uuid === uuid); }
public async updateServerInterface(obj: any) {
config.setInterface(obj);
let srv = this.findServerByGuid(obj.uuid);
if (typeof srv !== 'undefined') await srv.stopAsync();
if (obj.enabled) srv.init(obj);
}
}
class ProtoServer {
constructor(name: string, type: string) { this.name = name; this.type = type; }
Expand All @@ -182,6 +187,7 @@ class ProtoServer {
public get isConnected() { return this.isRunning; }
public emitToClients(evt: string, ...data: any) { }
public emitToChannel(channel: string, evt: string, ...data: any) { }
public init(obj: any) { };
public async stopAsync() { }
protected _dev: boolean = process.env.NODE_ENV !== 'production';
// todo: how do we know if the client is using IPv4/IPv6?
Expand Down Expand Up @@ -374,28 +380,33 @@ export class HttpServer extends ProtoServer {
this.isRunning = true;
}
}
public addListenerOnce(event: any, f: (data: any) => void) {
for (let i = 0; i < this._sockets.length; i++) {
this._sockets[i].once(event, f);
}
}
}
export class HttpsServer extends HttpServer {
public server: https.Server;

public init(cfg) {
// const auth = require('http-auth');
this.uuid = cfg.uuid;
if (!cfg.enabled) return;
try {
this.app = express();
// Enable Authentication (if configured)
/* if (cfg.authentication === 'basic') {
let basic = auth.basic({
realm: "nodejs-poolController.",
file: path.join(process.cwd(), cfg.authFile)
})
this.app.use(function(req, res, next) {
(auth.connect(basic))(req, res, next);
});
} */
if (cfg.sslKeyFile === '' || cfg.sslCertFile === '' || !fs.existsSync(path.join(process.cwd(), cfg.sslKeyFile)) || !fs.existsSync(path.join(process.cwd(), cfg.sslCertFile))) {
logger.warn(`HTTPS not enabled because key or crt file is missing.`);
/* if (cfg.authentication === 'basic') {
let basic = auth.basic({
realm: "nodejs-poolController.",
file: path.join(process.cwd(), cfg.authFile)
})
this.app.use(function(req, res, next) {
(auth.connect(basic))(req, res, next);
});
} */
if (cfg.sslKeyFile === '' || cfg.sslCertFile === '' || !fs.existsSync(path.join(process.cwd(), cfg.sslKeyFile)) || !fs.existsSync(path.join(process.cwd(), cfg.sslCertFile))) {
logger.warn(`HTTPS not enabled because key or crt file is missing.`);
return;
}
let opts = {
Expand Down Expand Up @@ -834,6 +845,55 @@ export class REMInterfaceServer extends ProtoServer {
this.uuid = cfg.uuid;
if (cfg.enabled) {
this.initSockets();
setTimeout(async () => {
try {
await this.initConnection();
}
catch (err) {
logger.error(`Error establishing bi-directional Nixie/REM connection: ${err}`)
}
}, 5000);
}
}
private async initConnection() {
try {
// find HTTP server
return new Promise(async (resolve, reject) => {
// First, send the connection info for njsPC and see if a connection exists.
let url = '/config/checkconnection/';
// can & should extend for https/username-password/ssl
let data: any = { type: "njspc", isActive: true, id: null, name: "njsPC - automatic", protocol: "http:", ipAddress: webApp.ip(), port: config.getSection('web').servers.http.port || 4200, userName: "", password: "", sslKeyFile: "", sslCertFile: "" }
let result = await this.putApiService(url, data, 5000);
// If the result code is > 200 we have an issue. (-1 is for timeout)
if (result.status.code > 200 || result.status.code < 0) return reject(new Error(`initConnection: ${result.error.message}`));

// The passed connection has been setup/verified; now test for emit
// if this fails, it could be because the remote connection is disabled. We will not
// automatically re-enable it
url = '/config/checkemit'
data = { eventName: "checkemit", property: "result", value: 'success', connectionId: result.obj.id }
// wait for REM server to finish resetting
setTimeout(async () => {
try {
let _tmr = setTimeout(() => { return reject(new Error(`initConnection: No socket response received. Check REM→njsPC communications.`)) }, 2000);
let srv: HttpServer = webApp.findServer('http') as HttpServer;
srv.addListenerOnce('/checkemit', (data: any) => {
// if we receive the emit, data will work both ways.
// console.log(data);
clearTimeout(_tmr);
logger.info(`REM bi-directional communications established.`)
return resolve();
});
result = await this.putApiService(url, data, 1000);
// If the result code is > 200 we have an issue.
if (result.status.code > 200) return reject(new Error(`initConnection: ${result.error.message}`));
}
catch (err) {reject(new Error(`initConnection setTimeout: ${result.error.message}`));}
}, 3000);
});
}
catch (err) {
logger.error(`Error with REM Interface Server initConnection: ${err}`)
}
}
public async stopAsync() {
Expand All @@ -850,7 +910,7 @@ export class REMInterfaceServer extends ProtoServer {
private _sockets: socketio.Socket[] = [];
private async sendClientRequest(method: string, url: string, data?: any, timeout:number = 10000): Promise<InterfaceServerResponse> {
try {

let ret = new InterfaceServerResponse();
let opts = extend(true, { headers: {} }, this.cfg.options);
if ((typeof opts.hostname === 'undefined' || !opts.hostname) && (typeof opts.host === 'undefined' || !opts.host || opts.host === '*')) {
Expand Down Expand Up @@ -898,8 +958,8 @@ export class REMInterfaceServer extends ProtoServer {
});
req.on('abort', () => { logger.warn('Request Aborted'); reject(new Error('Request Aborted.')); });
req.end(sbody);
logger.verbose(`REM server request returned. ${opts.method} ${opts.path} ${sbody}`);
}).catch((err) => { logger.error(`Error Sending REM Request: ${opts.method} ${url} ${err.message}`); ret.error = err; });
logger.verbose(`REM server request returned. ${opts.method} ${opts.path} ${sbody}`);
if (ret.status.code > 200) {
// We have an http error so let's parse it up.
try {
Expand Down
38 changes: 36 additions & 2 deletions web/services/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { utils } from "../../../controller/Constants";
import { state } from "../../../controller/State";
import { stopPacketCaptureAsync, startPacketCapture } from '../../../app';
import { conn } from "../../../controller/comms/Comms";
import { webApp } from "../../Server";

export class ConfigRoute {
public static initRoutes(app: express.Application) {
Expand Down Expand Up @@ -218,6 +219,7 @@ export class ConfigRoute {
flowSensorTypes: sys.board.valueMaps.flowSensorTypes.toArray(),
acidTypes: sys.board.valueMaps.acidTypes.toArray(),
remServers: await sys.ncp.getREMServers(),
interfaces: config.getSection('web.interfaces.rem'),
dosingStatus: sys.board.valueMaps.chemControllerDosingStatus.toArray(),
siCalcTypes: sys.board.valueMaps.siCalcTypes.toArray(),
alarms,
Expand Down Expand Up @@ -259,6 +261,26 @@ export class ConfigRoute {
}
return res.status(200).send(opts);
});
app.get('/app/all/', (req, res) => {
let opts = config.getSection();
return res.status(200).send(opts);
});
app.get('/app/options/interfaces', (req, res) => {
// todo: move bytevaluemaps out to a proper location; add additional definitions
let opts = {
interfaces: config.getSection('web.interfaces'),
types: [
{name: 'rem', desc: 'Relay Equipment Manager'},
{name: 'mqtt', desc: 'MQTT'}
],
protocols: [
{ val: 0, name: 'http://', desc: 'http://' },
{ val: 1, name: 'https://', desc: 'https://' },
{ val: 2, name: 'mqtt://', desc: 'mqtt://' },
]
}
return res.status(200).send(opts);
});
app.get('/config/options/tempSensors', (req, res) => {
let opts = {
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
Expand All @@ -276,6 +298,13 @@ export class ConfigRoute {
});
/******* END OF CONFIGURATION PICK LISTS/REFERENCES AND VALIDATION ***********/
/******* ENDPOINTS FOR MODIFYING THE OUTDOOR CONTROL PANEL SETTINGS **********/
app.put('/config/rem', async (req, res, next)=>{
try {
config.setSection('web.interfaces.rem', req.body);

}
catch (err) {next(err);}
})
app.put('/config/tempSensors', async (req, res, next) => {
try {
await sys.board.system.setTempSensorsAsync(req.body);
Expand Down Expand Up @@ -741,6 +770,13 @@ export class ConfigRoute {
sys.board.reloadConfig();
return res.status(200).send('OK');
});
app.put('/app/interface', async (req, res, next) => {
try{
await webApp.updateServerInterface(req.body);
return res.status(200).send('OK');
}
catch (err) {next(err);}
});
app.get('/app/config/startPacketCapture', (req, res) => {
startPacketCapture(true);
return res.status(200).send('OK');
Expand All @@ -759,7 +795,5 @@ export class ConfigRoute {
app.get('/app/config/:section', (req, res) => {
return res.status(200).send(config.getSection(req.params.section));
});


}
}

0 comments on commit 8b04dcd

Please sign in to comment.