@yachteye/signalk-engineroom-plugin
Version:
Get EngineRoom data from the source (database or other) and add it to the SignalK graph.
187 lines (186 loc) • 9.81 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const _1 = require(".");
const node_net_1 = __importDefault(require("node:net"));
const utils = require('@signalk/nmea0183-utilities');
const cloudwatch_1 = require("./cloudwatch");
class TcpData {
constructor(app, settings, hostName, port, definitions) {
this.server = null;
/** The data we have received, keyed by the correct SignalK path. */
this.data = {};
app.debug(`[TcpData] starting for ${hostName}:${port}`);
this.app = app;
this.dataDefinitions = definitions;
this.server = node_net_1.default.createServer({ keepAlive: true }, (socket) => {
const socketAddress = JSON.stringify(socket.address());
(0, cloudwatch_1.log)('info', 'TCP client connected', { address: socketAddress });
// app.debug('[TcpData] Client connected to socket: ' + socketAddress);
socket.on('data', (data) => {
if (data.length > 0) {
this.addData(data.toString());
}
});
socket.on('end', function () {
(0, cloudwatch_1.log)('warn', 'TCP client disconnected', { address: socketAddress });
});
socket.on('error', function (err) {
app.error(`[TcpData] Socket error: ${err.message} (${socketAddress})`);
(0, cloudwatch_1.log)('error', 'TCP socket error', { error: err.message, address: socketAddress });
});
});
this.server.on('listening', () => {
app.debug(`[TcpData] tcp server listening on ${hostName}:${port} .`);
});
this.server.on('close', () => {
app.debug('[TcpData] tcp server closes');
});
this.server.on('drop', (data) => {
app.error(`[TcpData] tcp server drop event: ${JSON.stringify(data)}`);
(0, cloudwatch_1.log)('error', 'TCP connection dropped, max connections reached', { data: JSON.stringify(data) });
});
this.server.on('error', (e) => {
app.error(`[TcpData] tcp server error: ${e.message}`);
(0, cloudwatch_1.log)('error', 'TCP server error', { error: e.message });
});
this.server.listen(port, hostName);
}
stop() {
if (this.server) {
this.server.close((err) => {
this.server = null;
this.app.debug(`[TcpData] tcp server closed, error:${err === null || err === void 0 ? void 0 : err.message} `);
});
}
this.data = {};
}
/**
* Get the data that has been received over TCP, with fixed vales added.
* @returns Dictionary with data, keyed by the SignalK path.
*/
getData() {
// this.app.debug(`[TcpData] getData()`);
// Add any fixed values we have defined.
for (const dataDef of this.dataDefinitions) {
if (dataDef.type === _1.IDataType.Fixed) {
this.data[dataDef.path] = dataDef.fixed === undefined ? 0 : dataDef.fixed;
}
}
return this.data;
}
/**
* Add the data to the internal collection.
* @param data Data to process, e.g. '{Type:2,Unix:1739802466,Key:Power Genset 3 (Genset CNT),Value:0.0,Unit:kW}'.
*/
addData(data) {
this.app.debug(`[TcpData] addData() data: ${data}`);
const nowSeconds = Date.now() / 1000;
const parts = data.split(',');
if (parts.length === 5) {
const timestampSeconds = parseInt(parts[1].replace('Unix:', '').trim(), 10);
const keyPart = parts[2];
const key = keyPart.replace('Key:', '').trim();
const value = parts[3].replace('Value:', '').trim();
const unitPart = parts[4];
if (nowSeconds - timestampSeconds > 5) {
this.app.debug(`[TcpData] addData(): data seems outdated (${nowSeconds - timestampSeconds}s), timestampSeconds=${timestampSeconds}, now=${nowSeconds} for ${keyPart}`);
}
switch (key) {
case 'Power POD (Total)':
this.data['yachteye.pod.wattage'] = Number.parseFloat(value) * 1000 * 10;
this.assertUnitString(unitPart, 'Unit:kW}', data);
break;
case 'Power PTI/O PS':
this.data['yachteye.pti_pto.ps.wattage'] = Math.round(Number.parseFloat(value) * 1000 * 10);
break;
case 'Power PTI/O SB':
this.data['yachteye.pti_pto.sb.wattage'] = Math.round(Number.parseFloat(value) * 1000 * 10);
break;
case 'Power Multidrive AFT POD':
this.data['yachteye.pod.aft.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Multidrive FWD POD':
this.data['yachteye.pod.fwd.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Input Multidrive AFT':
this.data['yachteye.multiDriveConverter.aft.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Input Multidrive FWD':
this.data['yachteye.multiDriveConverter.fwd.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Genset 1 (Genset SB OUT)':
this.data['yachteye.genset.1.wattage'] = Number.parseFloat(value) * 1000 * 10;
this.assertUnitString(unitPart, 'Unit:kW}', data);
break;
case 'Power Genset 2 (Genset SB IN)':
this.data['yachteye.genset.2.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Genset 3 (Genset CNT)':
this.data['yachteye.genset.3.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Genset 4 (Genset PS IN)':
this.data['yachteye.genset.4.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Genset 5 (Genset PS OUT)':
this.data['yachteye.genset.5.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Input MSB 400V AFT':
this.data['yachteye.msb400v.aft.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Power Input MSB 400V FWD':
this.data['yachteye.msb400v.fwd.wattage'] = Number.parseFloat(value) * 1000 * 10;
break;
case 'Total Remaining Fuel':
this.data['yachteye.fuel.remaining'] = Number.parseFloat(value);
this.assertUnitString(unitPart, 'Unit:m3}', data);
break;
case 'Shaft speed Main Engine PS':
this.data['yachteye.shaft.port.rpm'] = Number.parseFloat(value);
break;
case 'Shaft speed Main Engine SB':
this.data['yachteye.shaft.starboard.rpm'] = Number.parseFloat(value);
break;
case 'Pitch propeller Main Engine PS':
this.data['yachteye.propellor.port.pitch'] = Number.parseFloat(value) / 100.0;
break;
case 'Pitch propeller Main Engine SB':
this.data['yachteye.propellor.starboard.pitch'] = Number.parseFloat(value) / 100.0;
break;
case 'Shaft speed POD':
this.data['yachteye.pod.shaft.rpm'] = Number.parseFloat(value);
break;
case 'Pitch propeller POD':
this.data['yachteye.pod.propellor.pitch'] = Number.parseFloat(value) / 100.0;
break;
case 'Rotation angle POD':
this.data['yachteye.pod.rotation-angle'] = Number.parseFloat(value);
break;
case 'Water temperature':
// Re-enabled this on 30 Jan 2026
this.assertUnitString(unitPart, 'Unit:°C}', data);
this.data['environment.water.temperature'] = utils.transform(Number.parseFloat(value), 'C', 'K');
break;
default:
this.app.debug(`[TcpData] addData() unexpected key: ${data}`);
break;
}
if (this.data.hasOwnProperty('yachteye.msb400v.aft.wattage') && typeof this.data['yachteye.msb400v.aft.wattage'] === 'number' && this.data.hasOwnProperty('yachteye.msb400v.fwd.wattage') && typeof this.data['yachteye.msb400v.fwd.wattage'] === 'number') {
this.data['yachteye.msb400v.combined.wattage'] = this.data['yachteye.msb400v.fwd.wattage'] + this.data['yachteye.msb400v.aft.wattage'];
this.app.debug(`[TcpData] addData() msb400v.combined.wattage: ${this.data['yachteye.msb400v.combined.wattage']}`);
}
}
else {
this.app.error(`[TcpData] addData() unexpected data format (unexpected number of parts: ${parts.length}): ${data}`);
(0, cloudwatch_1.log)('error', 'TCP unexpected data format', { raw: data, parts: parts.length });
}
}
assertUnitString(input, check, data) {
if (input !== check) {
this.app.error(`[TcpData] assertUnitString() unexpected unit-string: ${input} in ${data}`);
}
}
}
exports.default = TcpData;