@tak-ps/node-cot
Version:
Lightweight JavaScript library for parsing and manipulating TAK messages
401 lines • 11.7 kB
JavaScript
import { v4 as randomUUID } from 'uuid';
import Err from '@openaddresses/batch-error';
import Sensor from './sensor.js';
import Util from './utils/util.js';
import JSONCoT, { Detail } from './types/types.js';
/**
* Convert to and from an XML CoT message
* @class
*
* @param cot A string/buffer containing the XML representation or the xml-js object tree
*
* @prop raw Raw XML-JS representation of CoT
*/
export default class CoT {
raw;
// Key/Value JSON Records - not currently support by TPC Clients
// but used for styling/dynamic overrides and hopefully eventually
// merged into the CoT spec
metadata;
// Does the CoT belong to a folder - defaults to "/"
path;
constructor(cot, opts = {}) {
this.raw = cot;
this.metadata = {};
this.path = '/';
if (!this.raw.event._attributes.uid) {
this.raw.event._attributes.uid = Util.cot_uuid();
}
if (!this.raw.event.detail)
this.raw.event.detail = {};
if (this.raw.event.detail.archive && Object.keys(this.raw.event.detail.archive).length === 0) {
this.raw.event.detail.archive = { _attributes: {} };
}
if (opts.creator) {
this.creator({
uid: opts.creator instanceof CoT ? opts.creator.uid() : opts.creator.uid,
type: opts.creator instanceof CoT ? opts.creator.type() : opts.creator.type,
callsign: opts.creator instanceof CoT ? opts.creator.callsign() : opts.creator.callsign,
time: opts.creator instanceof CoT ? new Date() : opts.creator.time
});
}
if (process.env.DEBUG_COTS)
console.log(JSON.stringify(this.raw));
}
/**
* Returns or sets the UID of the CoT
*/
uid(uid) {
if (uid)
this.raw.event._attributes.uid = uid;
return this.raw.event._attributes.uid;
}
/**
* Returns or sets the Callsign of the CoT
*/
type(type) {
if (type) {
this.raw.event._attributes.type = type;
}
return this.raw.event._attributes.type;
}
/**
* Returns or sets the Archived State of the CoT
*
* @param callsign - Optional Archive state to set
*/
archived(archived) {
const detail = this.detail();
if (archived === true) {
detail.archive = { _attributes: {} };
}
else if (archived === false) {
delete detail.archive;
}
return detail.archive !== undefined;
}
/**
* Returns or sets the Callsign of the CoT
*
* @param callsign - Optional Callsign to set
*/
callsign(callsign) {
const detail = this.detail();
if (callsign && !detail.contact) {
detail.contact = { _attributes: { callsign } };
}
else if (callsign && detail.contact) {
detail.contact._attributes.callsign = callsign;
}
if (detail.contact && detail.contact._attributes && typeof detail.contact._attributes.callsign === 'string') {
return detail.contact._attributes.callsign;
}
else {
return 'UNKNOWN';
}
}
/**
* Return Detail Object of CoT or create one if it doesn't yet exist and pass a reference
*/
detail() {
if (!this.raw.event.detail)
this.raw.event.detail = {};
return this.raw.event.detail;
}
/**
* Add a given Dest tag to a CoT
*/
addDest(dest) {
const detail = this.detail();
if (!detail.marti)
detail.marti = {};
let destArr = [];
if (detail.marti.dest && !Array.isArray(detail.marti.dest)) {
destArr = [detail.marti.dest];
}
else if (detail.marti.dest && Array.isArray(detail.marti.dest)) {
destArr = detail.marti.dest;
}
destArr.push({ _attributes: dest });
detail.marti.dest = destArr;
return this;
}
addVideo(video, connection) {
const detail = this.detail();
if (detail.__video)
throw new Err(400, null, 'A video stream already exists on this CoT');
if (!video.url)
throw new Err(400, null, 'A Video URL must be provided');
if (!video.uid && connection && connection.uid) {
video.uid = connection.uid;
}
else if (video.uid && connection && !connection.uid) {
connection.uid = video.uid;
}
else if (!video.uid) {
video.uid = randomUUID();
}
detail.__video = {
_attributes: video
};
if (connection) {
detail.__video.ConnectionEntry = {
_attributes: connection
};
}
else {
detail.__video.ConnectionEntry = {
_attributes: {
uid: video.uid,
networkTimeout: 12000,
path: '',
protocol: 'raw',
bufferTime: -1,
address: video.url,
port: -1,
roverPort: -1,
rtspReliable: 0,
ignoreEmbeddedKLV: false,
alias: this.callsign()
}
};
}
return this;
}
position(position) {
if (position) {
this.raw.event.point._attributes.lon = position[0];
this.raw.event.point._attributes.lat = position[1];
}
return [
Number(this.raw.event.point._attributes.lon),
Number(this.raw.event.point._attributes.lat)
];
}
sensor(sensor) {
const detail = this.detail();
if (sensor) {
detail.sensor = {
_attributes: sensor
};
}
if (!detail.sensor || !detail.sensor._attributes) {
return null;
}
return new Sensor(this.position(), detail.sensor._attributes).to_geojson();
}
;
creator(creator) {
const detail = this.detail();
if (creator) {
this.addLink({
uid: creator.uid,
production_time: creator.time ? new Date(creator.time).toISOString() : new Date().toISOString(),
type: creator.type,
parent_callsign: creator.callsign,
relation: 'p-p'
});
detail.creator = {
_attributes: {
...creator,
time: creator.time ? new Date(creator.time).toISOString() : new Date().toISOString(),
}
};
}
if (detail.creator) {
return detail.creator._attributes;
}
else {
return;
}
}
addLink(link) {
const detail = this.detail();
let linkArr = [];
if (detail.link && !Array.isArray(detail.link)) {
linkArr = [detail.link];
}
else if (detail.link && Array.isArray(detail.link)) {
linkArr = detail.link;
}
linkArr.push({ _attributes: link });
detail.link = linkArr;
return this;
}
is_stale() {
return new Date(this.raw.event._attributes.stale) < new Date();
}
/**
* Determines if the CoT message represents a Tasking Message
*
* @return {boolean}
*/
is_tasking() {
return !!this.raw.event._attributes.type.match(/^t-/);
}
/**
* Determines if the CoT message represents a Chat Message
*
* @return {boolean}
*/
is_chat() {
return !!(this.raw.event.detail && this.raw.event.detail.__chat);
}
/**
* Determines if the CoT message represents a Friendly Element
*
* @return {boolean}
*/
is_friend() {
return !!this.raw.event._attributes.type.match(/^a-f-/);
}
/**
* Determines if the CoT message represents a Hostile Element
*
* @return {boolean}
*/
is_hostile() {
return !!this.raw.event._attributes.type.match(/^a-h-/);
}
/**
* Determines if the CoT message represents a Unknown Element
*
* @return {boolean}
*/
is_unknown() {
return !!this.raw.event._attributes.type.match(/^a-u-/);
}
/**
* Determines if the CoT message represents a Pending Element
*
* @return {boolean}
*/
is_pending() {
return !!this.raw.event._attributes.type.match(/^a-p-/);
}
/**
* Determines if the CoT message represents an Assumed Element
*
* @return {boolean}
*/
is_assumed() {
return !!this.raw.event._attributes.type.match(/^a-a-/);
}
/**
* Determines if the CoT message represents a Neutral Element
*
* @return {boolean}
*/
is_neutral() {
return !!this.raw.event._attributes.type.match(/^a-n-/);
}
/**
* Determines if the CoT message represents a Suspect Element
*
* @return {boolean}
*/
is_suspect() {
return !!this.raw.event._attributes.type.match(/^a-s-/);
}
/**
* Determines if the CoT message represents a Joker Element
*
* @return {boolean}
*/
is_joker() {
return !!this.raw.event._attributes.type.match(/^a-j-/);
}
/**
* Determines if the CoT message represents a Faker Element
*
* @return {boolean}
*/
is_faker() {
return !!this.raw.event._attributes.type.match(/^a-k-/);
}
/**
* Determines if the CoT message represents an Element
*
* @return {boolean}
*/
is_atom() {
return !!this.raw.event._attributes.type.match(/^a-/);
}
/**
* Determines if the CoT message represents an Airborne Element
*
* @return {boolean}
*/
is_airborne() {
return !!this.raw.event._attributes.type.match(/^a-.-A/);
}
/**
* Determines if the CoT message represents a Ground Element
*
* @return {boolean}
*/
is_ground() {
return !!this.raw.event._attributes.type.match(/^a-.-G/);
}
/**
* Determines if the CoT message represents an Installation
*
* @return {boolean}
*/
is_installation() {
return !!this.raw.event._attributes.type.match(/^a-.-G-I/);
}
/**
* Determines if the CoT message represents a Vehicle
*
* @return {boolean}
*/
is_vehicle() {
return !!this.raw.event._attributes.type.match(/^a-.-G-E-V/);
}
/**
* Determines if the CoT message represents Equipment
*
* @return {boolean}
*/
is_equipment() {
return !!this.raw.event._attributes.type.match(/^a-.-G-E/);
}
/**
* Determines if the CoT message represents a Surface Element
*
* @return {boolean}
*/
is_surface() {
return !!this.raw.event._attributes.type.match(/^a-.-S/);
}
/**
* Determines if the CoT message represents a Subsurface Element
*
* @return {boolean}
*/
is_subsurface() {
return !!this.raw.event._attributes.type.match(/^a-.-U/);
}
/**
* Determines if the CoT message represents a UAV Element
*
* @return {boolean}
*/
is_uav() {
return !!this.raw.event._attributes.type.match(/^a-f-A-M-F-Q-r/);
}
/**
* Return a CoT Message
*/
static ping() {
return new CoT({
event: {
_attributes: Util.cot_event_attr('t-x-c-t', 'h-g-i-g-o'),
detail: {},
point: Util.cot_point()
}
});
}
}
//# sourceMappingURL=cot.js.map