From ac99acd01fc479cc83a37a030f5c0900d7ede0d3 Mon Sep 17 00:00:00 2001 From: NoobishSVK Date: Tue, 16 Jan 2024 22:11:04 +0100 Subject: [PATCH] windows update / librdsparser update --- datahandler.js | 201 +++++++++++++++++++++++++++++++------------------ index.js | 2 +- librds.so | Bin 26400 -> 0 bytes 3 files changed, 127 insertions(+), 76 deletions(-) delete mode 100644 librds.so diff --git a/datahandler.js b/datahandler.js index abb9924..d76ec04 100644 --- a/datahandler.js +++ b/datahandler.js @@ -1,89 +1,129 @@ const koffi = require('koffi'); const path = require('path'); -const lib = koffi.load(path.join(__dirname, "librds.so")); +//const lib = koffi.load(path.join(__dirname, "librds.so")); +//const lib = koffi.load(path.join(__dirname, "librds.dll")); +const os = require('os'); +const win32 = (os.platform() == "win32"); +const unicode_type = (win32 ? 'int16_t' : 'int32_t'); +const lib = koffi.load(path.join(__dirname, "librdsparser." + (win32 ? "dll" : "so"))); -koffi.proto('void callback_pi(uint16_t pi, void *user_data)'); -koffi.proto('void callback_pty(uint8_t pty, void *user_data)'); -koffi.proto('void callback_tp(bool tp, void *user_data)'); -koffi.proto('void callback_ta(bool ta, void *user_data)'); -koffi.proto('void callback_ms(bool ms, void *user_data)'); -koffi.proto('void callback_af(uint8_t af, void *user_data)'); -koffi.proto('void callback_ecc(uint8_t ecc, void *user_data)'); -koffi.proto('void callback_ps(const char *ps, const uint8_t *errors, void *user_data)'); -koffi.proto('void callback_rt(const char *rt, const uint8_t *errors, int flag, void *user_data)'); +koffi.proto('void callback_pi(void *rds, void *user_data)'); +koffi.proto('void callback_pty(void *rds, void *user_data)'); +koffi.proto('void callback_tp(void *rds, void *user_data)'); +koffi.proto('void callback_ta(void *rds, void *user_data)'); +koffi.proto('void callback_ms(void *rds, void *user_data)'); +koffi.proto('void callback_ecc(void *rds, void *user_data)'); +koffi.proto('void callback_af(void *rds, uint32_t af, void *user_data)'); +koffi.proto('void callback_ps(void *rds, void *user_data)'); +koffi.proto('void callback_rt(void *rds, int flag, void *user_data)'); +koffi.proto('void callback_ptyn(void *rds, void *user_data)'); -const librds = { - new: lib.func('void* librds_new()'), - clear: lib.func('void librds_clear(void *context)'), - free: lib.func('void librds_free(void *context)'), - parse: lib.func('bool librds_parse(void *context, const char *input)'), - get_ps: lib.func('char* librds_get_ps(void *context)'), - get_rt: lib.func('char* librds_get_rt(void *context, int flag)'), - register_pi: lib.func('void librds_register_pi(void *context, callback_pi *cb)'), - register_pty: lib.func('void librds_register_pty(void *context, callback_pty *cb)'), - register_tp: lib.func('void librds_register_tp(void *context, callback_tp *cb)'), - register_ta: lib.func('void librds_register_ta(void *context, callback_ta *cb)'), - register_ms: lib.func('void librds_register_ms(void *context, callback_ms *cb)'), - register_af: lib.func('void librds_register_af(void *context, callback_af *cb)'), - register_ecc: lib.func('void librds_register_ecc(void *context, callback_ecc *cb)'), - register_ps: lib.func('void librds_register_ps(void *context, callback_ps *cb1)'), - register_rt: lib.func('void librds_register_rt(void *context, callback_rt *cb)') +const rdsparser = { + new: lib.func('void* rdsparser_new()'), + clear: lib.func('void* rdsparser_clear()'), + free: lib.func('void rdsparser_free(void *rds)'), + parse_string: lib.func('bool rdsparser_parse_string(void *rds, const char *input)'), + get_pi: lib.func('int32_t rdsparser_get_pi(void *rds)'), + get_pty: lib.func('int8_t rdsparser_get_pty(void *rds)'), + get_tp: lib.func('int8_t rdsparser_get_tp(void *rds)'), + get_ta: lib.func('int8_t rdsparser_get_ta(void *rds)'), + get_ms: lib.func('int8_t rdsparser_get_ms(void *rds)'), + get_ecc: lib.func('int8_t rdsparser_get_ecc(void *rds)'), + get_ps: lib.func('void* rdsparser_get_ps(void *rds)'), + get_rt: lib.func('void* rdsparser_get_rt(void *rds, int flag)'), + get_ptyn: lib.func('void* rdsparser_get_ptyn(void *rds)'), + register_pi: lib.func('void rdsparser_register_pi(void *rds, callback_pi *cb)'), + register_pty: lib.func('void rdsparser_register_pty(void *rds, callback_pty *cb)'), + register_tp: lib.func('void rdsparser_register_tp(void *rds, callback_tp *cb)'), + register_ta: lib.func('void rdsparser_register_ta(void *rds, callback_ta *cb)'), + register_ms: lib.func('void rdsparser_register_ms(void *rds, callback_ms *cb)'), + register_ecc: lib.func('void rdsparser_register_ecc(void *rds, callback_ecc *cb)'), + register_af: lib.func('void rdsparser_register_af(void *rds, callback_af *cb)'), + register_ps: lib.func('void rdsparser_register_ps(void *rds, callback_ps *cb)'), + register_rt: lib.func('void rdsparser_register_rt(void *rds, callback_rt *cb)'), + register_ptyn: lib.func('void rdsparser_register_ptyn(void *rds, callback_ptyn *cb)'), + string_get_content: lib.func(unicode_type + '* rdsparser_string_get_content(void *string)'), + string_get_length: lib.func('uint8_t rdsparser_string_get_length(void *string)') } +const decode_unicode = function(string) +{ + let content = rdsparser.string_get_content(string); + let length = rdsparser.string_get_length(string); + let array = koffi.decode(content, koffi.array(unicode_type, length)); + return Buffer.from(array, 'utf-8').toString(); +}; + const callbacks = { - pi: koffi.register((value) => { - console.log('PI: ' + value.toString(16).toUpperCase()); - }, 'callback_pi *'), + pi: koffi.register(rds => ( + value = rdsparser.get_pi(rds), + console.log('PI: ' + value.toString(16).toUpperCase()) + ), 'callback_pi *'), - pty: koffi.register((value) => { - dataToSend.pty = value; - }, 'callback_pty *'), + pty: koffi.register(rds => ( + value = rdsparser.get_pty(rds), + dataToSend.pty = value + ), 'callback_pty *'), - tp: koffi.register((value) => { - dataToSend.tp = value; - }, 'callback_tp *'), + tp: koffi.register(rds => ( + value = rdsparser.get_tp(rds), + dataToSend.tp = value + ), 'callback_tp *'), - ta: koffi.register((value) => { - console.log('TA: ' + value); - }, 'callback_ta *'), + ta: koffi.register(rds => ( + value = rdsparser.get_ta(rds), + console.log('TA: ' + value) + ), 'callback_ta *'), - ms: koffi.register((value) => { - console.log('MS: ' + value); - }, 'callback_ms *'), + ms: koffi.register(rds => ( + value = rdsparser.get_ms(rds), + console.log('MS: ' + value) + ), 'callback_ms *'), - af: koffi.register((value) => { - dataToSend.af.push(87500 + value * 100); - }, 'callback_af *'), + af: koffi.register((rds, value) => ( + dataToSend.af.push(value) + ), 'callback_af *'), - ecc: koffi.register((value) => { - console.log('ECC: ' + value.toString(16).toUpperCase()); - }, 'callback_ecc *'), + ecc: koffi.register(rds => ( + value = rdsparser.get_ecc(rds), + console.log('ECC: ' + value.toString(16).toUpperCase()) + ), 'callback_ecc *'), - ps: koffi.register((value) => { - dataToSend.ps = value; - }, 'callback_ps *'), + ps: koffi.register(rds => ( + value = decode_unicode(rdsparser.get_ps(rds)), + dataToSend.ps = value + ), 'callback_ps *'), + + rt: koffi.register((rds, flag) => { + const value = decode_unicode(rdsparser.get_rt(rds, flag)); - rt: koffi.register((value, errors, flag) => { if (flag === 0) { dataToSend.rt0 = value; } + if (flag === 1) { dataToSend.rt1 = value; } }, 'callback_rt *'), + + ptyn: koffi.register((rds, flag) => ( + value = decode_unicode(rdsparser.get_ptyn(rds)), + console.log('PTYN: ' + value) +), 'callback_ptyn *') }; -let rds = librds.new() -librds.register_pi(rds, callbacks.pi) -librds.register_pty(rds, callbacks.pty) -librds.register_tp(rds, callbacks.tp) -librds.register_ta(rds, callbacks.ta) -librds.register_ms(rds, callbacks.ms) -librds.register_af(rds, callbacks.af) -librds.register_ecc(rds, callbacks.ecc) -librds.register_ps(rds, callbacks.ps) -librds.register_rt(rds, callbacks.rt) +let rds = rdsparser.new() +rdsparser.register_pi(rds, callbacks.pi); +rdsparser.register_pty(rds, callbacks.pty); +rdsparser.register_tp(rds, callbacks.tp); +rdsparser.register_ta(rds, callbacks.ta); +rdsparser.register_ms(rds, callbacks.ms); +rdsparser.register_ecc(rds, callbacks.ecc); +rdsparser.register_af(rds, callbacks.af); +rdsparser.register_ps(rds, callbacks.ps); +rdsparser.register_rt(rds, callbacks.rt); +rdsparser.register_ptyn(rds, callbacks.ptyn); const updateInterval = 75; const clientUpdateIntervals = new Map(); // Store update intervals for each client @@ -120,11 +160,11 @@ const initialData = { const resetToDefault = dataToSend => Object.assign(dataToSend, initialData); var rdsBuffer = []; -function handleBuffer() { +function handleBuffer() { for (let group of rdsBuffer) { - librds.parse(rds, group); + rdsparser.parse_string(rds, group); } } @@ -133,19 +173,22 @@ function handleData(ws, receivedData) { let lastUpdateTime = clientUpdateIntervals.get(ws) || 0; const currentTime = Date.now(); let modifiedData, parsedValue; + const receivedLines = receivedData.split('\n'); + + for (const receivedLine of receivedLines) { switch (true) { - case receivedData.startsWith('P'): - modifiedData = receivedData.substring(1, 5); + case receivedLine.startsWith('P'): + modifiedData = receivedLine.substring(1, 5); dataToSend.pi = modifiedData; break; - case receivedData.startsWith('T'): + case receivedLine.startsWith('T'): rdsBuffer = []; resetToDefault(dataToSend); dataToSend.af.length = 0; - librds.clear(rds); - modifiedData = receivedData.substring(1); + rdsparser.clear(rds); + modifiedData = receivedLine.substring(1); parsedValue = parseFloat(modifiedData); if (!isNaN(parsedValue)) { @@ -154,8 +197,8 @@ function handleData(ws, receivedData) { } break; - case receivedData.startsWith('Sm'): - modifiedData = receivedData.substring(2); + case receivedLine.startsWith('Sm'): + modifiedData = receivedLine.substring(2); parsedValue = parseFloat(modifiedData); dataToSend.st = false; @@ -173,9 +216,8 @@ function handleData(ws, receivedData) { } break; - case receivedData.startsWith('R'): - modifiedData = dataToSend.pi.toUpperCase() + receivedData.slice(1, -1).toUpperCase(); - + case receivedLine.startsWith('R'): + modifiedData = receivedLine.slice(1, -1).toUpperCase(); // Ensure modifiedData is exactly 18 characters long if (modifiedData.length < 18) { modifiedData = modifiedData.padEnd(18, '0'); // Add zeroes at the end @@ -188,21 +230,30 @@ function handleData(ws, receivedData) { rdsBuffer.shift(); } rdsBuffer.push(modifiedData); + if (rdsBuffer.length > 1) { handleBuffer(); } break; - } + } // Send the updated data to the client const dataToSendJSON = JSON.stringify(dataToSend); if (currentTime - lastUpdateTime >= updateInterval) { clientUpdateIntervals.set(ws, currentTime); // Update the last update time for this client ws.send(dataToSendJSON); - } + } } +/*setInterval(function () { + // some code + if (rdsBuffer.length > 50) { + handleBuffer(); + //console.log("handling buffer"); + } + //console.log(rdsBuffer.length); +}, 150);*/ module.exports = { handleData diff --git a/index.js b/index.js index 5c2372b..30923a3 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ const axios = require('axios'); const dataHandler = require('./datahandler'); /* Server settings */ -const webServerHost = '192.168.1.39'; // IP of the web server +const webServerHost = '192.168.1.14'; // IP of the web server const webServerPort = 8080; // web server port const xdrdServerHost = '192.168.1.15'; // xdrd server iP diff --git a/librds.so b/librds.so deleted file mode 100644 index aaef0e15de541070f6a115098554339c81cb729f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26400 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 J5K5!#2LOJwz`g(g