initial commit

This commit is contained in:
2022-06-28 13:02:50 +02:00
commit a4e6b655b7
16 changed files with 7870 additions and 0 deletions

223
server/MhdMapApp.js Normal file
View 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;
}
}