UNPKG

node-red-contrib-tak-registration

Version:

A Node-RED node to register to TAK and to help wrap files as datapackages to send to TAK

141 lines (132 loc) 5.81 kB
const { XMLParser, XMLBuilder, XMLValidator} = require("fast-xml-parser"); const axios = require('axios').default; var Long = require('long').Long; var protobuf = require('protobufjs'); var path = require('path'); protobuf.util.Long = Long; protobuf.configure(); module.exports = function(RED) { "use strict"; const fastXmlOptions = { attributeNamePrefix: "", // attributesGroupName: "attr", ignoreAttributes: false, }; function TakIngestNode(n) { RED.nodes.createNode(this,n); var node = this; var global = this.context().global; const parser = new XMLParser(fastXmlOptions); var handleProtoBuf = function(msg) { protobuf.load(path.join(__dirname,"proto/takmessage.proto"), function (err, root) { if (err) { node.error(err); } var m = root.lookupType("atakmap.commoncommo.protobuf.v1.TakMessage"); var m3; try { var m2 = m.decode(msg.payload); m3 = m.toObject(m2, { longs:String, enums:String, bytes:String } ).cotEvent; } catch(e) { node.error("Protobuf decode "+e,msg); return; } // Add any unique ID/callsigns to some global variables just in case it's useful for later. if (m3.detail?.contact?.callsign && m3.uid) { var a = global.get("_takgatewaycs") || {}; var c = a[m3.detail.contact.callsign]; a[m3.detail.contact.callsign] = m3.uid; global.set("_takgatewaycs", a); var b = global.get("_takgatewayid") || {}; if (c) { delete b[c]; } b[m3.uid] = m3.detail.contact.callsign; global.set("_takgatewayid", b); } node.send({ topic: m3.type, payload: { cotEvent: m3 } }); }); } node.on("input",function(msg) { if (Buffer.isBuffer(msg.payload)) { // handle UDP packet if (msg.payload[0] === 191 && msg.payload[2] === 191) { var head = msg.payload[1]; msg.payload = msg.payload.slice(3); // type 1 = protobuf if (head === 1) { handleProtoBuf(msg); return; } // type 0 = plain text else if (head === 0) { msg.payload = msg.payload.toString(); } else { node.error("Unknown CoT packet type",msg); return; } } // ideally need to check if tcp and streaming protobuf... // else if (msg.payload[0] === 191 && !msg.hasOwnProperty("fromip")) { // var l = msg.payload[1] + msg.payload[2] * 256; // msg.payload = msg.payload.slice(3,l+3); // handleProtoBuf(msg); // return; // } else { node.error("Unknown buffer type",msg); return; } } else if (typeof(msg.payload) !== "string") { node.error("Input is not a string.",msg); return; } msg.payload = msg.payload.trim().replace(/>\s+</g, "><"); var p = msg.payload.indexOf("<event"); if (p >= 0) { msg.payload = msg.payload.substr(p); } if (msg.payload.indexOf("<event") !== 0) { if (msg.payload.trim().length > 0 && msg.payload !== '</event>') { // ignore blank lines node.error("Input is not an XML event string.",msg); } return; } msg.payload = parser.parse(msg.payload); // Add any unique ID/callsigns to some global variables just in case it's useful for later. if (msg.payload?.event?.detail?.contact?.callsign && msg.payload?.event?.uid) { var a = global.get("_takgatewaycs") || {}; var c = a[msg.payload.event.detail.contact.callsign]; a[msg.payload.event.detail.contact.callsign] = msg.payload.event.uid; global.set("_takgatewaycs", a); var b = global.get("_takgatewayid") || {}; if (c) { delete b[c]; } b[msg.payload.event.uid] = msg.payload.event.detail.contact.callsign; global.set("_takgatewayid", b); } if (msg.payload?.event?.type) { msg.topic = msg.payload?.event?.type; } if (msg.payload?.event?.detail?.fileshare) { msg.filename = msg.payload.event.detail.fileshare.filename; axios({ method: 'get', url: msg.payload.event.detail.fileshare.senderUrl, headers: { 'Accept': 'application/zip' }, responseType: 'arraybuffer' }) .then(function (response) { msg.datapackage = Buffer.from(response.data); node.send(msg); }) .catch(function (error) { node.error(error.message, error); node.send(msg); }) } else { node.send(msg); } }); node.on("close", function() { }); } RED.nodes.registerType("tak ingest", TakIngestNode); };