import m from 'mithril'; export interface Status { batteryVoltage: number; // in Volts motorCurrent: number; // in Amperes speed: number; // in meters per second tripDistance: number; // in meters tripTotalTime: number; // in seconds tripMovingTime: number; // in seconds tripAscendingElevation: number; // in meters tripMotorEnergy: number; // in Watt-hour temperature: number; // in Celcius degrees altitude: number; // in meters above sea level }; interface ApiStatus { v: number; // voltage (mV) c: number; // current (mA) s: number; // speed (mm/s) td: number; // trip distance (m) ttt: number; // trip total time (s) tmt: number; // trip moving time (s) tae: number; // trip ascending elevation (m) tme: number; // trip motor energy (Wh) temp: number; // temperature (tenth of °C) alt: number; // altitude (mm) } export class MonitorApi { private mockServer: boolean; private lastStatus: Status = null; private static api: MonitorApi = null; private lastFetchTime = 0; private lastFetchAltitude = -100000; private mockedTripDistance = 0; private mockedTripTotalTime = 0; private mockedTripMovingTime = 0; private mockedTripAscendingElevation = 0; private mockedTripMotorEnergy = 0; constructor() { this.mockServer = window.location.protocol == "file:"; } static get() { if(MonitorApi.api == null) MonitorApi.api = new MonitorApi(); return MonitorApi.api; } getStatus() { return this.lastStatus; } async fetchStatus(): Promise { let apiStatus: ApiStatus; if(this.mockServer) { await new Promise(resolve => setTimeout(resolve, 200)); let t = Date.now() / 1000.0; if(this.lastFetchTime == 0) this.lastFetchTime = t; let dt = t - this.lastFetchTime; this.lastFetchTime = t; let speed = (Math.cos(t*0.3)+1.0)*0.5 * 14; // in meters per second if(speed < 0.25) speed = 0; this.mockedTripDistance += speed * dt; this.mockedTripTotalTime += dt; if(speed > 0) this.mockedTripMovingTime += dt; let altitude = (Math.cos(t*0.0001)+1.0)*0.5 * 4500 - 200 + (Math.cos(t*0.03))*0.5 * 60; // in meters if(this.lastFetchAltitude == -100000) this.lastFetchAltitude = altitude; let altitudeChange = altitude - this.lastFetchAltitude; this.lastFetchAltitude = altitude; if(altitudeChange > 0) this.mockedTripAscendingElevation += altitudeChange; let voltage = (Math.cos(t*0.4)+1.0)*0.5 * 6 + 36; let current = (Math.cos(t*0.7)+1.0)*0.5 * 30; let newEnergy = voltage * current * dt / 3600; this.mockedTripMotorEnergy += newEnergy; apiStatus = { v: Math.round(voltage * 1000), c: Math.round(current * 1000), s: Math.round(speed * 1000), td: Math.round(this.mockedTripDistance), ttt: Math.round(this.mockedTripTotalTime), tmt: Math.round(this.mockedTripMovingTime), tae: Math.round(this.mockedTripAscendingElevation*10.0), tme: Math.round(this.mockedTripMotorEnergy*10.0), temp: Math.round((Math.cos(t*0.2)+1.0)*0.5 * 400 - 100), alt: Math.round(altitude * 1000) }; } else { apiStatus = await new Promise((resolve, error) => { let request = new XMLHttpRequest(); request.onreadystatechange = () => { if(request.readyState == 4) { if(request.status == 200) { resolve(JSON.parse(request.response)); } else { error(); } } }; request.open('GET', '/api/status', true); request.send(); }); } this.lastStatus = { batteryVoltage: apiStatus.v / 1000, motorCurrent: apiStatus.c / 1000, speed: apiStatus.s / 1000, tripDistance: apiStatus.td, tripTotalTime: apiStatus.ttt, tripMovingTime: apiStatus.tmt, tripAscendingElevation: apiStatus.tae / 10, tripMotorEnergy: apiStatus.tme / 10, temperature: apiStatus.temp / 10, altitude: apiStatus.alt / 1000 }; return this.lastStatus; } }