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.
 
 
 

203 lines
4.8 KiB

const EventEmitter = require("events");
const createDriver = require("../drivers");
class Workout {
/**
* @param {import("../repositories/sqlite3")} repo
* @param {number} id
* @param {{
* id: string,
* name: string,
* driver: string,
* connect: string,
* maxLevel: number,
* }} bike
* @param {{
* id: number,
* name: string,
* cpm: number,
* level: number,
* warmupMin: number,
* warmupCpm: number,
* warmupLevel: number,
* cooldownCpm: number,
* cooldownLevel: number,
* }} program
* @param {Date} date
*/
constructor(repo, id, bike, program, date) {
this.repo = repo;
this.id = id;
this.bike = bike;
this.program = program;
this.date = date;
this.driver = null;
this.state = "disconnected";
this.offsetSeconds = 0;
this.cooldownMin = -1;
this.offsets = {};
this.events = new EventEmitter();
this.events.emit("state", this.state);
}
async connect() {
if (this.state !== "disconnected") {
return Promise.resolve();
}
this.driver = createDriver(this.bike.driver, this.bike.connect);
this.driver.events.on("workoutStatus", ws => this.handleworkoutStatus(ws));
return this.driver.connect().then((v) => {
this.state = this.driver.state;
this.events.emit("state", this.state);
return v;
});
}
start() {
if (this.state === "started") {
return Promise.resolve();
}
return this.driver.start().then((v) => {
this.state = this.driver.state;
this.events.emit("state", this.state);
return v;
});
}
pause() {
if (this.state !== "started") {
return Promise.resolve();
}
return this.driver.pause().then((v) => {
this.state = this.driver.state;
this.events.emit("state", this.state);
return v;
});
}
stop() {
if (this.state === "started") {
return Promise.resolve();
}
return this.driver.stop().then((v) => {
this.state = this.driver.state;
this.events.emit("state", this.state);
return v;
});
}
destroy() {
this.events.emit("destroy");
this.events.removeAllListeners();
if (this.driver != null) {
this.driver.destroy();
this.state = this.driver.state;
this.events.emit("state", this.state);
this.driver = null;
}
}
listMeasurements() {
return this.repo.listMeasurements(this.id);
}
handleworkoutStatus(ws) {
if (this.offsetSeconds > 0) {
const seconds = (ws.minutes * 60) + ws.seconds + this.offsetSeconds;
ws.minutes = Math.floor(seconds / 60);
ws.seconds = seconds % 60;
for (const key in this.offsets) {
if (!this.offsets.hasOwnProperty(key)) {
continue;
}
ws[key] += this.offsets[key];
}
}
// Steer the level
let targetLevel = this.program.level;
if (this.cooldownMin > -1 && ws.minutes >= this.cooldownMin) {
targetLevel = this.program.cooldownLevel;
} else if (this.program.warmupMin > ws.minutes) {
targetLevel = this.program.warmupLevel;
}
if (targetLevel !== ws.level && status === "started") {
this.driver.setLevel(targetLevel);
}
this.repo.insertMeasurement({...ws, workoutId: this.id});
this.events.emit("workoutStatus", {...ws});
}
setCooldownMin(min) {
this.cooldownMin = min;
this.events.emit("cooldownMin", min);
}
/**
* @param {import("../repositories/sqlite3")} repo
* @param {number} bikeId
* @param {number} programId
*/
static async create(repo, bikeId, programId) {
const bike = await repo.findBike(bikeId);
if (bike == null) {
throw new Error("bike not found");
}
const program = await repo.findProgram(programId);
if (program == null) {
throw new Error("program not found");
}
const date = new Date();
const id = await repo.insertWorkout({
bikeId: bike.id,
programId: program.id,
date: date,
cooldownMin: -1,
});
return new Workout(repo, id, bike, program, date);
}
/**
* @param {import("../repositories/sqlite3")} repo
* @param {number} id
*/
static async continue(repo, id) {
const data = await repo.findWorkout(id);
const bike = await repo.findBike(data.bikeId);
const program = await repo.findProgram(data.programId);
const workout = new Workout(repo, id, bike, program, new Date(data.date));
workout.cooldownMin = data.cooldownMin;
const list = await repo.listMeasurements(id);
if (list.length > 0) {
const last = list[list.length - 1];
workout.offsetSeconds = (last.minutes * 60 + last.seconds);
workout.offsets = {
distance: last.distance,
calories: last.calories,
}
}
return workout;
}
}
module.exports = Workout;