diff --git a/CHANGELOG.md b/CHANGELOG.md index d531b82..5de26d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.1.1 + +- Fixes #2: TAK2WM broken in 4.1.0 + ## 4.1.0 - Moved documentation to ReadTheDocs: [node-red-contrib-tak](https://node-red-contrib-tak.readthedocs.io) diff --git a/package.json b/package.json index 12e5f6f..2a75580 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-tak", - "version": "4.1.0", + "version": "4.1.1", "description": "Node-RED Nodes for encoding & decoding TAK Protocol and Cursor on Target messages from TAK Products.", "license": "Apache-2.0", "url": "https://github.com/snstac/node-red-contrib-tak", diff --git a/tak/wm.js b/tak/wm.js index 2eb623c..4f570a3 100644 --- a/tak/wm.js +++ b/tak/wm.js @@ -28,214 +28,224 @@ const makeTAK2WMNode = (RED) => { let node = this; node.on("input", (msg) => { - if (msg.payload === undefined) { - return; - } + node.status({ fill: "green", shape: "dot", text: "Receiving" }); - let pl = handlePayload(msg.payload)[0]; + // let value = RED.util.getMessageProperty(msg, node.property); + let payload = handlePayload(msg.payload); - if (pl === undefined) { + if (payload === undefined || payload === null) { + node.error({ message: "Undefined or null payload." }); return; } - let event; - let detail; - let cotType; - let uid; - let point; - let contact; - let status; - let group; - let track; - let takv; - let lastUpdate; - - if (pl.cotEvent === undefined) { - event = pl.event; - - detail = event.detail; - if (detail) { - contact = detail.contact; - if (contact) { - contact = contact._attributes; - } + let error = payload.error; - status = detail.status; - if (status) { - status = status._attributes; - } + if (typeof error !== "undefined" && error !== null) { + node.error(error); + } else { + let pl = payload.payload[0].payload; + + let event; + let detail; + let cotType; + let uid; + let point; + let contact; + let status; + let group; + let track; + let takv; + let lastUpdate; + + if (typeof pl.event !== "undefined" && pl.event !== null) { + event = pl.event; + + detail = event.detail; + if (detail) { + contact = detail.contact; + if (contact) { + contact = contact._attributes; + } - group = detail.__group; - if (group) { - group = group._attributes; - } + status = detail.status; + if (status) { + status = status._attributes; + } - track = detail.track; - if (track) { - track = track._attributes; - } + group = detail.__group; + if (group) { + group = group._attributes; + } - takv = detail.takv; - if (takv) { - takv = takv._attributes; - } - } + track = detail.track; + if (track) { + track = track._attributes; + } - cotType = event._attributes.type; - uid = event._attributes.uid; - point = event.point._attributes; + takv = detail.takv; + if (takv) { + takv = takv._attributes; + } + } - lastUpdate = new Date(event._attributes.time).toLocaleString(); - } else { - event = pl.cotEvent; + cotType = event._attributes.type; + uid = event._attributes.uid; + point = event.point._attributes; - detail = event.detail; - if (detail === undefined) { - return; - } + lastUpdate = new Date(event._attributes.time).toLocaleString(); + } else if (pl.cotEvent !== "undefined" && pl.cotEvent !== null) { + event = pl.cotEvent; - cotType = event.type; - uid = event.uid; - point = event; - contact = detail.contact; - status = detail.status; - group = detail.group; - lastUpdate = new Date(event.sendTime).toLocaleString(); - track = detail.track; - takv = detail.takv; - } + detail = event.detail; + if (detail === undefined) { + console.log("undefined detail") + return; + } - let icon; - let invalid = "9999999.0"; - let sidc = cotType2SIDC(cotType); - - /* Bug? - The "TAK Protocol Version 1" Contact Protobuf message[1] contains - two items:: - - message Contact { - string endpoint = 1; - string callsign = 2; - } - - WinTAK sends an additional Contact item:: - - string emailAddress - - This causes our parser, @vidterra/tak.js, to pass WinTAK Contact - messages as opaque XML. That's OK, because we can parse it directly, see below. - - It is unclear how other implementations of "TAK Protocol Version 1" - handle the inclusion of this extra Contact item from WinTAK. - - This is probably a bug in WinTAK for now, but should be added to future - revisions of "TAK Protocol Version 1". - - 1. Contact Protobuf message: https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/contact.proto - */ - if ( - takv && - takv.platform && - takv.platform.toLowerCase().includes("wintak") - ) { - let xmlDetail = cot.xml2js(detail.xmlDetail); - - if (xmlDetail._attributes) { - contact = xmlDetail._attributes; - } else if (xmlDetail.contact) { - contact = xmlDetail.contact._attributes; + cotType = event.type; + uid = event.uid; + point = event; + contact = detail.contact; + status = detail.status; + group = detail.group; + lastUpdate = new Date(event.sendTime).toLocaleString(); + track = detail.track; + takv = detail.takv; } - } - /* - Points on the Worldmap can only have one uniquite identifier, which is also - that Points display name. If possible, use a Callsign, otherwise use UID. + let icon; + let invalid = "9999999.0"; + let sidc = cotType2SIDC(cotType); + + /* Bug? + The "TAK Protocol Version 1" Contact Protobuf message[1] contains + two items:: + + message Contact { + string endpoint = 1; + string callsign = 2; + } + + WinTAK sends an additional Contact item:: + + string emailAddress + + This causes our parser, @vidterra/tak.js, to pass WinTAK Contact + messages as opaque XML. That's OK, because we can parse it directly, see below. + + It is unclear how other implementations of "TAK Protocol Version 1" + handle the inclusion of this extra Contact item from WinTAK. + + This is probably a bug in WinTAK for now, but should be added to future + revisions of "TAK Protocol Version 1". + + 1. Contact Protobuf message: https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/contact.proto */ - let callsign; - if (contact) { - callsign = contact.callsign; - } else { - callsign = uid; - } + if ( + takv && + takv.platform && + takv.platform.toLowerCase().includes("wintak") + ) { + let xmlDetail = cot.xml2js(detail.xmlDetail); - let battery; - if (status) { - battery = status.battery; - } + if (xmlDetail._attributes) { + contact = xmlDetail._attributes; + } else if (xmlDetail.contact) { + contact = xmlDetail.contact._attributes; + } + } - let iconColor; - if (group) { - iconColor = group.name.toLowerCase(); - } + /* + Points on the Worldmap can only have one uniquite identifier, which is also + that Points display name. If possible, use a Callsign, otherwise use UID. + */ + let callsign; + if (contact) { + callsign = contact.callsign; + } else { + callsign = uid; + } - if (callsign.includes("SFPD") || uid.includes("SFPD")) { - icon = ":cop:"; - } + let battery; + if (status) { + battery = status.battery; + } - if (callsign.includes("PulsePoint") || uid.includes("PulsePoint")) { - icon = ":rotating_light:"; - } + let iconColor; + if (group) { + iconColor = group.name.toLowerCase(); + } - let tooltip; - let remarks; - if (detail) { - remarks = detail.remarks; - } - if (remarks) { - remarks = remarks._text; - tooltip = remarks; - } + if (callsign.includes("SFPD") || uid.includes("SFPD")) { + icon = ":cop:"; + } - let speed; - let course; - if (track) { - if (track.speed && track.speed.toString() !== invalid) { - speed = track.speed; + if (callsign.includes("PulsePoint") || uid.includes("PulsePoint")) { + icon = ":rotating_light:"; } - if ( - track.course && - track.course.toString() !== invalid && - track.course !== 0.0 && - track.course !== 0 - ) { - course = track.course; + + let tooltip; + let remarks; + if (detail) { + remarks = detail.remarks; + } + if (remarks) { + remarks = remarks._text; + tooltip = remarks; } - } - /* - If COT Point CE is set and is not invalid, use that as Worldmap Point Accuracy. - */ - let accuracy; - let ce = point.ce; - if (ce.toString() !== invalid) { - accuracy = ce; - } + let speed; + let course; + if (track) { + if (track.speed && track.speed.toString() !== invalid) { + speed = track.speed; + } + if ( + track.course && + track.course.toString() !== invalid && + track.course !== 0.0 && + track.course !== 0 + ) { + course = track.course; + } + } - /* Serialize as a Worldmap compatible Payload. */ - let payload = { - name: iconColor ? uid : callsign, - callsign: iconColor ? callsign : undefined, - label: iconColor ? callsign : undefined, - hae: point.hae !== invalid ? point.hae : undefined, - lat: parseFloat(point.lat), - lon: parseFloat(point.lon), - cotType: cotType, - lastUpdate: lastUpdate, - icon: icon, - tooltip: tooltip, - track: parseFloat(course), - speed: parseFloat(speed), - accuracy: parseFloat(accuracy), - layer: cotType, - groupName: group ? group.name : undefined, - groupRole: group ? group.role : undefined, - SIDC: iconColor ? undefined : sidc, - iconColor: iconColor, - battery: battery ? `${battery}%` : undefined, - }; + /* + If COT Point CE is set and is not invalid, use that as Worldmap Point Accuracy. + */ + let accuracy; + let ce = point.ce; + if (ce.toString() !== invalid) { + accuracy = ce; + } - msg.payload = payload; - node.send(msg); + /* Serialize as a Worldmap compatible Payload. */ + let newPayload = { + name: iconColor ? uid : callsign, + callsign: iconColor ? callsign : undefined, + label: iconColor ? callsign : undefined, + hae: point.hae !== invalid ? point.hae : undefined, + lat: parseFloat(point.lat), + lon: parseFloat(point.lon), + cotType: cotType, + lastUpdate: lastUpdate, + icon: icon, + tooltip: tooltip, + track: parseFloat(course), + speed: parseFloat(speed), + accuracy: parseFloat(accuracy), + layer: cotType, + groupName: group ? group.name : undefined, + groupRole: group ? group.role : undefined, + SIDC: iconColor ? undefined : sidc, + iconColor: iconColor, + battery: battery ? `${battery}%` : undefined, + }; + + msg.payload = newPayload; + node.send(msg); + + }; }); } RED.nodes.registerType("tak2wm", tak2wm);