Monitoring system for electric vehicles (log various sensors, such as consumed power, solar production, speed, slope, apparent wind, etc.)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

132 lines
3.8 KiB

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<Status> {
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<ApiStatus>((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;
}
}