kopia lustrzana https://github.com/cyoung/stratux
261 wiersze
8.0 KiB
JavaScript
261 wiersze
8.0 KiB
JavaScript
angular.module('appControllers').controller('WeatherCtrl', WeatherCtrl); // get the main module contollers set
|
|
WeatherCtrl.$inject = ['$rootScope', '$scope', '$state', '$http', '$interval']; // Inject my dependencies
|
|
|
|
// create our controller function with all necessary logic
|
|
function WeatherCtrl($rootScope, $scope, $state, $http, $interval) {
|
|
|
|
var CONF_WATCHLIST = "KBOS KATL KORD KLAX"; // we default to 4 major airports
|
|
var MAX_DATALIST = 10;
|
|
|
|
$scope.$parent.helppage = 'plates/weather-help.html';
|
|
$scope.data_list = [];
|
|
$scope.watch_list = [];
|
|
$scope.data_count = 0;
|
|
$scope.watch_count = 0;
|
|
|
|
function updateWatchList() {
|
|
$scope.watching = CONF_WATCHLIST;
|
|
// Simple GET request example (note: responce is asynchronous)
|
|
$http.get(URL_SETTINGS_GET).
|
|
then(function (response) {
|
|
settings = angular.fromJson(response.data);
|
|
$scope.watching = settings.WatchList.toUpperCase();
|
|
}, function (response) {
|
|
// nop
|
|
});
|
|
};
|
|
|
|
function inList(word, sentence) {
|
|
// since the watch list is just one long string, we cheat and see if the word in anywhere in the 'sentence'
|
|
if ((sentence) && (word)) {
|
|
return sentence.includes(word);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function parseFlightCondition(msg, body) {
|
|
if ((msg !== "METAR") && (msg !== "SPECI"))
|
|
return "";
|
|
|
|
// check the visibility: a value preceeding 'SM' which is either a fraction or a whole number
|
|
// we don't care what value of fraction since anything below 1SM is LIFR
|
|
|
|
// BTW: now I know why no one wants to parse METARs - ther can be spaces in the numbers ARGH
|
|
// test for special case of 'X X/X'
|
|
var exp = new RegExp("([0-9]) ([0-9])/([0-9])SM");
|
|
var match = exp.exec(body);
|
|
if ((match !== null) && (match.length === 4)) {
|
|
visability = parseInt(match[1]) + (parseInt(match[2]) / parseInt(match[3]));
|
|
} else {
|
|
exp = new RegExp("([0-9/]{1,5}?)SM");
|
|
match = exp.exec(body);
|
|
if (match === null)
|
|
return "";
|
|
// the only way we have 3 or more characters is if the '/' is present which means we need to do extra checking
|
|
if (match[1].length === 3)
|
|
return "LIFR";
|
|
// do we have a usable visability distance
|
|
var visability = parseInt(match[1]);
|
|
if (visability === 0)
|
|
return "";
|
|
}
|
|
|
|
// ceiling is at either the BKN or OVC layer
|
|
exp = new RegExp("BKN([0-9]{3})");
|
|
match = exp.exec(body);
|
|
if (match === null) {
|
|
exp = new RegExp("OVC([0-9]{3})");
|
|
match = exp.exec(body);
|
|
}
|
|
var ceiling = 999;
|
|
if (match !== null)
|
|
ceiling = parseInt(match[1]);
|
|
|
|
if ((visability > 5) && (ceiling > 30))
|
|
return "VFR";
|
|
if ((visability >= 3) && (ceiling >= 10))
|
|
return "MVFR";
|
|
if ((visability >= 1) && (ceiling >= 5))
|
|
return "IFR";
|
|
return "LIFR";
|
|
}
|
|
|
|
|
|
function deltaTimeString(epoc) {
|
|
var time = "";
|
|
var val;
|
|
var d = new Date(epoc);
|
|
val = d.getUTCDate() - 1; // we got here by subtrracting two dates so we have a delta, not a day of month
|
|
if (val > 0)
|
|
time += (val < 10 ? "0" + val : "" + val) + "d ";
|
|
val = d.getUTCHours();
|
|
if (val > 0) {
|
|
time += (val < 10 ? "0" + val : "" + val) + "h ";
|
|
} else {
|
|
if (time.length > 0)
|
|
time += "00h ";
|
|
}
|
|
val = d.getUTCMinutes();
|
|
time += (val < 10 ? "0" + val : "" + val) + "m ";
|
|
// ADS-B weather is only accurate to minutes
|
|
// val = d.getUTCSeconds();
|
|
// time += (val < 10 ? "0" + val : "" + val) + "s";
|
|
|
|
return time;
|
|
}
|
|
|
|
function parseShortDatetime(sdt) {
|
|
var d = new Date();
|
|
var s = String(sdt);
|
|
if (s.length < 7)
|
|
return 0;
|
|
d.setUTCDate(parseInt(s.substring(0, 2)));
|
|
d.setUTCHours(parseInt(s.substring(2, 4)));
|
|
if (s.length > 7) { // TAF datetime range
|
|
d.setUTCMinutes(0);
|
|
} else {
|
|
d.setUTCMinutes(parseInt(s.substring(4, 6)));
|
|
}
|
|
d.setUTCSeconds(0);
|
|
d.setUTCMilliseconds(0);
|
|
return d;
|
|
}
|
|
|
|
function setDataItem(obj, data_item) {
|
|
if (obj.Type === "TAF.AMD") {
|
|
data_item.type = "TAF";
|
|
data_item.update = true;
|
|
} else {
|
|
data_item.type = obj.Type;
|
|
data_item.update = false;
|
|
}
|
|
|
|
data_item.flight_condition = parseFlightCondition(obj.Type, obj.Data);
|
|
data_item.location = obj.Location;
|
|
s = obj.Time;
|
|
// data_item.time = s.substring(0, 2) + '-' + s.substring(2, 4) + ':' + s.substring(4, 6) + 'Z';
|
|
// we may not get an accurate base time on the stratux device so we use the device time as our base
|
|
// var dNow = new Date(obj.LocaltimeReceived);
|
|
var dNow = new Date();
|
|
var dThen = parseShortDatetime(obj.Time);
|
|
data_item.age = dThen.getTime();
|
|
var diff_ms = Math.abs(dThen - dNow);
|
|
|
|
// If time is more than two days away, don't attempt to display data age.
|
|
if (diff_ms > (1000*60*60*24*2)) {
|
|
data_item.time = "?";
|
|
} else if (dThen > dNow) {
|
|
data_item.time = deltaTimeString(dThen - dNow) + " from now";
|
|
} else {
|
|
data_item.time = deltaTimeString(dNow - dThen) + " old";
|
|
}
|
|
|
|
// data_item.received = utcTimeString(obj.LocaltimeReceived);
|
|
data_item.data = obj.Data;
|
|
}
|
|
|
|
function connect($scope) {
|
|
if (($scope === undefined) || ($scope === null))
|
|
return; // we are getting called once after clicking away from the status page
|
|
|
|
if (($scope.socket === undefined) || ($scope.socket === null)) {
|
|
socket = new WebSocket(URL_WEATHER_WS);
|
|
$scope.socket = socket; // store socket in scope for enter/exit usage
|
|
}
|
|
|
|
$scope.ConnectState = "Disconnected";
|
|
|
|
socket.onopen = function (msg) {
|
|
// $scope.ConnectStyle = "label-success";
|
|
$scope.ConnectState = "Connected";
|
|
};
|
|
|
|
socket.onclose = function (msg) {
|
|
// $scope.ConnectStyle = "label-danger";
|
|
$scope.ConnectState = "Disconnected";
|
|
$scope.$apply();
|
|
setTimeout(connect, 1000);
|
|
};
|
|
|
|
socket.onerror = function (msg) {
|
|
// $scope.ConnectStyle = "label-danger";
|
|
$scope.ConnectState = "Problem";
|
|
$scope.$apply();
|
|
};
|
|
|
|
socket.onmessage = function (msg) {
|
|
console.log('Received data_list update.');
|
|
|
|
$scope.raw_data = angular.toJson(msg.data, true);
|
|
var message = JSON.parse(msg.data);
|
|
// we need to use an array so AngularJS can perform sorting; it also means we need to loop to find an aircraft in the data_list set
|
|
var found = false;
|
|
if (inList(message.Location, $scope.watching)) {
|
|
for (var i = 0, len = $scope.watch_list.length; i < len; i++) {
|
|
if (($scope.watch_list[i].type === message.Type) && ($scope.watch_list[i].location === message.Location)) {
|
|
setDataItem(message, $scope.watch_list[i]);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
var new_data_item = {};
|
|
setDataItem(message, new_data_item);
|
|
$scope.watch_list.unshift(new_data_item); // add to start of array
|
|
}
|
|
}
|
|
// add to scrolling data_list
|
|
{
|
|
var new_data_item = {};
|
|
setDataItem(message, new_data_item);
|
|
$scope.data_list.unshift(new_data_item); // add to start of array
|
|
if ($scope.data_list.length > MAX_DATALIST)
|
|
$scope.data_list.pop(); // remove last from array
|
|
}
|
|
$scope.data_count = $scope.data_list.length;
|
|
$scope.watch_count = $scope.watch_list.length;
|
|
$scope.$apply();
|
|
};
|
|
}
|
|
|
|
// perform cleanup every 5 minutes
|
|
var clearStaleMessages = $interval(function () {
|
|
// remove stale data = anything more than 30 minutes old
|
|
var dirty = false;
|
|
var cutoff = Date.now() - (30 * 60 * 1000);
|
|
|
|
for (var i = len = $scope.watch_list.length; i > 0; i--) {
|
|
if ($scope.watch_list[i - 1].age < cutoff) {
|
|
$scope.watch_list.splice(i - 1, 1);
|
|
dirty = true;
|
|
}
|
|
}
|
|
if (dirty) {
|
|
$scope.raw_data = "";
|
|
$scope.$apply();
|
|
}
|
|
}, (5 * 60 * 1000), 0, false);
|
|
|
|
|
|
$state.get('weather').onEnter = function () {
|
|
// everything gets handled correctly by the controller
|
|
updateWatchList();
|
|
};
|
|
|
|
$state.get('weather').onExit = function () {
|
|
// disconnect from the socket
|
|
if (($scope.socket !== undefined) && ($scope.socket !== null)) {
|
|
$scope.socket.close();
|
|
$scope.socket = null;
|
|
}
|
|
// stop stale message cleanup
|
|
$interval.cancel(clearStaleMessages);
|
|
};
|
|
|
|
|
|
|
|
// Weather Controller tasks
|
|
updateWatchList();
|
|
connect($scope); // connect - opens a socket and listens for messages
|
|
}; |