signalk-provider-vor1415
Version:
This provider takes the VOR2014/2015 JSON stream and transforms it into Signal K vessel information
274 lines (220 loc) • 6.2 kB
JavaScript
var debug = require('debug')('provider-vor1415')
, Readable = require('stream').Readable
, util = require('util')
, _ = require('lodash')
, request = require('request-promise')
;
function Race(url, opts) {
if(!(this instanceof Race)) {
return new Race(opts);
}
if(!url) {
throw new Error("Please provide JSON URL");
}
this.url = url;
this.options = opts || {};
if(!this.options.interval) {
this.options.interval = (30 * 60) * 1000; // 30 minutes
}
if(!this.options.stream) {
this.options.stream = {};
}
this.options.stream.objectMode = true;
Readable.call(this, this.options.stream);
this._lastDownload = -1;
this._lastData = null;
this._codes = {};
this._vessels = {};
setInterval(this.__check.bind(this), 1000);
}
util.inherits(Race, Readable);
module.exports = Race;
Race.prototype._read = function(size) {};
Race.prototype.__check = function() {
debug("Checking when we last downloaded a VOR update");
if(this._lastData !== null && this._lastDownload > 0 && (Date.now() - this._lastDownload) < this.options.interval) {
debug("Data is (quite) recent, idling.\n");
return;
}
debug("Data is not recent, getting ready to download a new copy.\n");
this.__fetch();
};
Race.prototype.__fetch = function() {
if(this._lastData === null) {
this._lastData = {};
}
this._lastDownload = Date.now();
request
.get(this.url)
.then(this.__handleData.bind(this))
.catch(this.__handleError.bind(this))
;
};
Race.prototype.__handleData = function(data) {
if(_.isString(data)) {
data = JSON.parse(data);
}
if(!_.isEqual(this._lastData, data)) {
this._lastData = data;
this.__reduce();
}
this._lastDownload = Date.now();
};
Race.prototype.__handleError = function(err) {
debug("ERROR");
debug(err);
};
Race.prototype.__reduce = function() {
if(!this.__isValid()) {
return;
}
var data = this._lastData;
var fields = data.data.fields;
var tracks = data.data.trackslatest;
var self = this;
_.each(data.codes, function(vessel) {
self._codes[vessel.code.toUpperCase()] = {
color: ('#' + vessel.color).toUpperCase(),
name: vessel.name,
uuid: 'VOR0' + vessel.code
};
});
_.each(tracks, function(track) {
var obj = {};
for(var i in fields) {
obj[fields[i]] = track[i];
}
obj.vessel = self._codes[obj.code.toUpperCase()];
obj.uuid = obj.vessel.uuid;
self._vessels[obj.code] = obj;
})
this.__generateDeltas();
};
Race.prototype.__generateUpdate = function(path, vessel, key, type) {
type = type || 'string';
var val;
switch(type) {
case 'float':
val = parseFloat(vessel[key]);
break;
default:
val = String(vessel[key]);
break;
}
if(typeof vessel[key] === "undefined") {
if(type === 'float') {
val = 0.0;
} else {
val = "";
}
}
if(val === null || typeof val === 'undefined' || val === "null" || val === "NULL") {
if(type === 'float') {
val = 0.0;
} else {
val = "";
}
}
return {
path: path,
value: val
};
};
Race.prototype.__generateDeltas = function() {
// return debug(JSON.stringify(this._vessels, null, 2));
/*
"TBRU": {
"code": "TBRU",
"reportdate": "2014-12-11 12:55:00",
"timeoffix": "2014-12-11 12:55:00",
"status": "RAC",
"latitude": "24.38333",
"longitude": "58.14517",
"dtf": "00321.50",
"dtlc": "+00000",
"legstanding": "1",
"twentyfourhourrun": "515.0",
"legprogress": 94,
"dul": "00000.00",
"boatheadingtrue": "315",
"smg": "04.3",
"seatemperature": 27.545454545455,
"truwindspeedavg": "3",
"speedthrowater": "6",
"truewindspeedmax": "4",
"truewinddirection": "42",
"latestspeedthrowater": "4",
"vessel": {
"color": "#AED13F",
"name": "Team Brunel",
"uuid": "VOR-TBRU"
},
"uuid": "VOR-TBRU"
}
*/
var self = this;
var legLength = parseFloat(this._lastData.leglength);
_.each(this._vessels, function(vessel) {
var delta = {
context: 'vessels.' + vessel.uuid,
updates: []
};
delta.updates.push({
path: 'name',
value: vessel.vessel.name
});
delta.updates.push({
path: 'uuid',
value: vessel.uuid
});
delta.updates.push({
path: 'navigation.state',
value: self.__getStatus(vessel.status)
});
if((legLength - parseFloat(vessel.dtf)) !== null) {
delta.updates.push({
path: 'navigation.logTrip',
value: legLength - parseFloat(vessel.dtf)
});
}
delta.updates.push(self.__generateUpdate('navigation.position.longitude', vessel, 'longitude', 'float'));
delta.updates.push(self.__generateUpdate('navigation.position.latitude', vessel, 'longitude', 'float'));
delta.updates.push(self.__generateUpdate('navigation.headingTrue', vessel, 'boatheadingtrue', 'float'));
delta.updates.push(self.__generateUpdate('navigation.speedThroughWater', vessel, 'speedthrowater', 'float'));
delta.updates.push(self.__generateUpdate('environment.wind.directionTrue', vessel, 'truewinddirection', 'float'));
delta.updates.push(self.__generateUpdate('environment.wind.speedTrue', vessel, 'truewindspeedavg', 'float'));
delta.updates.push(self.__generateUpdate('environment.waterTemp', vessel, 'seatemperature', 'float'));
self.push(delta);
});
};
Race.prototype.__getStatus = function(status) {
status = status.toUpperCase();
if(status === "RAC") {
return "Under way sailing";
} else if(status === "SUS") {
return "Aground";
} else {
return "Not defined";
}
}
Race.prototype.__isValid = function() {
if(this._lastData === null) {
return false;
}
if(!_.isObject(this._lastData.codes)) {
return false;
}
if(!_.isObject(this._lastData.data)) {
return false;
}
if(!_.isObject(this._lastData.data.fields)) {
return false;
}
if(!_.isArray(this._lastData.data.fields)) {
return false;
}
if(!_.isArray(this._lastData.data.trackslatest)) {
return false;
}
return true;
};