initial commit
This commit is contained in:
223
server/MhdMapApp.js
Normal file
223
server/MhdMapApp.js
Normal file
@@ -0,0 +1,223 @@
|
||||
import BratislavaOpendata from "./BratislavaOpendata.js";
|
||||
import config from "../config.js";
|
||||
import {bearing, removeMilis} from "./math.js";
|
||||
import ImhdClient from "./ImhdClient.js";
|
||||
import Recorder from "./Recorder.js";
|
||||
import getDatabase from "./getDatabase.js";
|
||||
|
||||
export default class MhdMapApp {
|
||||
|
||||
constructor(websockets) {
|
||||
this.opendata = new BratislavaOpendata(config.openDataKey);
|
||||
this.websockets = websockets;
|
||||
|
||||
this.stops = null;
|
||||
this.oldVehicles = null;
|
||||
this.vehicles = null;
|
||||
this.lastUpdatedStatic = null;
|
||||
this.lastUpdatedDynamic = null;
|
||||
this.lastDelta = null;
|
||||
|
||||
this.imhd = new ImhdClient();
|
||||
this.recorder = new Recorder(getDatabase());
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.websockets.on('join', ws => {
|
||||
this.sendAction(ws, 'updateStops', this.stops);
|
||||
this.sendAction(ws, 'updateVehicles', this.vehicles);
|
||||
});
|
||||
this.websockets.on('message', this._onWsMessage.bind(this));
|
||||
|
||||
await this.recorder.start();
|
||||
|
||||
setInterval(() => {
|
||||
this.updateStatic();
|
||||
}, 1000 * 60 * 60 * 24);
|
||||
|
||||
await this.updateStatic();
|
||||
await this.updateDynamic();
|
||||
|
||||
setInterval(async () => {
|
||||
await this.updateDynamic();
|
||||
if (this.lastDelta.length > 0) {
|
||||
this.broadcastAction('updateVehicles', this.lastDelta);
|
||||
}
|
||||
}, 1000 * 5);
|
||||
|
||||
}
|
||||
|
||||
async updateStatic() {
|
||||
let tmp = await this.opendata.fetchAllStops();
|
||||
if (tmp) {
|
||||
this.stops = tmp;
|
||||
this.lastUpdatedStatic = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
async updateDynamic() {
|
||||
let tmp = await this.opendata.fetchAllVehicles();
|
||||
if (!tmp)
|
||||
return;
|
||||
|
||||
// TODO: We're in Slovakia.
|
||||
tmp = tmp.filter(el => el.gpsLongitude > 10);
|
||||
tmp = this._vehiclesToObj(tmp);
|
||||
|
||||
this.oldVehicles = this.vehicles;
|
||||
this.vehicles = tmp;
|
||||
this.lastUpdatedDynamic = new Date();
|
||||
if (this.oldVehicles) {
|
||||
this.lastDelta = this.calculateVehiclesDelta(this.oldVehicles, this.vehicles);
|
||||
this.vehicles = this.addRotationToVehicles(this.oldVehicles, this.vehicles);
|
||||
|
||||
for (let veh of Object.values(this.lastDelta)) {
|
||||
await this.recorder.putVehicle(veh);
|
||||
}
|
||||
} else {
|
||||
for (let veh of Object.values(this.vehicles)) {
|
||||
await this.recorder.putVehicle(veh);
|
||||
}
|
||||
}
|
||||
|
||||
this.vehicles = this.addImhdDataToVehicles(this.vehicles);
|
||||
|
||||
let down = 0;
|
||||
for (let vehId in this.vehicles) {
|
||||
if (await this.imhd.downloadVehicleIcon(vehId) === true) {
|
||||
if (down++ === 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calculateVehiclesDelta(beforeObj, afterObj) {
|
||||
let delta = {};
|
||||
|
||||
for (let vehId in beforeObj) {
|
||||
let beforeVeh = beforeObj[vehId];
|
||||
if (afterObj.hasOwnProperty(vehId)) {
|
||||
let afterVeh = afterObj[vehId];
|
||||
if (!this.isVehicleObjectEqual(beforeVeh, afterVeh)) {
|
||||
delta[vehId] = afterVeh;
|
||||
}
|
||||
} else {
|
||||
// removed vehicle
|
||||
beforeVeh.gpsLatitude = -1000;
|
||||
beforeVeh.gpsLongitude = -1000;
|
||||
delta[vehId] = beforeVeh;
|
||||
console.log('vehicle left', beforeVeh);
|
||||
}
|
||||
}
|
||||
|
||||
for (let vehId in afterObj) {
|
||||
if (!beforeObj.hasOwnProperty(vehId)) {
|
||||
// new vehicle
|
||||
delta[vehId] = afterObj[vehId];
|
||||
// console.log('new vehicle joined', afterObj[vehId]);
|
||||
}
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
_vehiclesToObj(arr) {
|
||||
let obj = {};
|
||||
|
||||
for (let vehicle of arr) {
|
||||
let id = this._vehicleToId(vehicle);
|
||||
vehicle.lastModified = removeMilis(new Date(vehicle.lastModified));
|
||||
|
||||
if (obj.hasOwnProperty(id)) {
|
||||
console.log('DUPLICATE vehicle?!', 'old:', obj[id], 'new:', vehicle);
|
||||
}
|
||||
obj[id] = vehicle;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
_vehicleToId(vehicle) {
|
||||
return vehicle.vehicleNumber;
|
||||
}
|
||||
|
||||
isVehicleObjectEqual(a, b, checkLastModified = false) {
|
||||
return (
|
||||
a.vehicleNumber === b.vehicleNumber
|
||||
&& a.lineNumber === b.lineNumber
|
||||
&& a.gpsLatitude === b.gpsLatitude
|
||||
&& a.gpsLongitude === b.gpsLongitude
|
||||
&& (!checkLastModified || (a.lastModified === b.lastModified))
|
||||
);
|
||||
}
|
||||
|
||||
isVehiclePositionEqual(a, b) {
|
||||
return a.gpsLatitude === b.gpsLatitude && a.gpsLongitude === b.gpsLongitude;
|
||||
}
|
||||
|
||||
addImhdDataToVehicles(vehicles) {
|
||||
for (let vehId in vehicles) {
|
||||
const data = this.imhd.getVehicleData(vehId);
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
let vehicle = vehicles[vehId];
|
||||
vehicle.img = data.img;
|
||||
vehicle.type = data.type;
|
||||
vehicle.imhdinfo = data.info;
|
||||
}
|
||||
return vehicles;
|
||||
}
|
||||
|
||||
addRotationToVehicles(before, after) {
|
||||
for (let vehId in after) {
|
||||
if (before.hasOwnProperty(vehId)) {
|
||||
after[vehId].bearing = bearing(before[vehId], after[vehId]);
|
||||
}
|
||||
}
|
||||
return after;
|
||||
}
|
||||
|
||||
broadcastAction(action, data) {
|
||||
this.websockets.broadcast(JSON.stringify({ action, data }));
|
||||
}
|
||||
|
||||
sendAction(ws, action, data) {
|
||||
ws.send(JSON.stringify({ action, data }));
|
||||
}
|
||||
|
||||
async _onWsMessage(ws, data) {
|
||||
// Note: before sending the response, it should be checked if this ws is still open
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.log('Malformed request: ', data, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
const msgid = data.id;
|
||||
|
||||
let res = await this.handleWsRequest(ws, data.action, data.msg);
|
||||
|
||||
if (ws.readyState !== 1) { // 1 = WebSocket.OPEN
|
||||
// closed while handling the request
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendAction(ws, 'response', {
|
||||
id: msgid,
|
||||
msg: res,
|
||||
});
|
||||
}
|
||||
|
||||
async handleWsRequest(ws, action, data) {
|
||||
if (action === 'requestVehicleTrace') {
|
||||
return await this.recorder.getVehicleTrace(data.vehicle, data.count || 20);
|
||||
} else if (action === 'requestLineTrace') {
|
||||
return await this.recorder.getSimpleLineTrace(data.line, data.count || 20);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user