openrowingmonitor/app/server.js

153 lines
5.3 KiB
JavaScript

'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
This start file is currently a mess, as this currently is the devlopment playground to plug
everything together while figuring out the physics and model of the application.
todo: refactor this as we progress
*/
import { fork } from 'child_process'
import log from 'loglevel'
// eslint-disable-next-line no-unused-vars
import fs from 'fs'
import config from './config.js'
import { createRowingEngine } from './engine/RowingEngine.js'
import { createRowingStatistics } from './engine/RowingStatistics.js'
import { createWebServer } from './WebServer.js'
import { createPeripheralManager } from './ble/PeripheralManager.js'
// eslint-disable-next-line no-unused-vars
import { replayRowingSession } from './tools/RowingRecorder.js'
// set the log levels
log.setLevel(config.loglevel.default)
for (const [loggerName, logLevel] of Object.entries(config.loglevel)) {
if (loggerName !== 'default') {
log.getLogger(loggerName).setLevel(logLevel)
}
}
log.info(`==== Open Rowing Monitor ${process.env.npm_package_version} ====\n`)
const peripheralManager = createPeripheralManager()
peripheralManager.on('control', (event) => {
if (event?.req?.name === 'requestControl') {
event.res = true
} else if (event?.req?.name === 'reset') {
log.debug('reset requested')
rowingStatistics.reset()
peripheralManager.notifyStatus({ name: 'reset' })
event.res = true
// todo: we could use these controls once we implement a concept of a rowing session
} else if (event?.req?.name === 'stop') {
log.debug('stop requested')
peripheralManager.notifyStatus({ name: 'stoppedOrPausedByUser' })
event.res = true
} else if (event?.req?.name === 'pause') {
log.debug('pause requested')
peripheralManager.notifyStatus({ name: 'stoppedOrPausedByUser' })
event.res = true
} else if (event?.req?.name === 'startOrResume') {
log.debug('startOrResume requested')
peripheralManager.notifyStatus({ name: 'startedOrResumedByUser' })
event.res = true
} else if (event?.req?.name === 'peripheralMode') {
webServer.notifyClients({ peripheralMode: event.req.peripheralMode })
event.res = true
} else {
log.info('unhandled Command', event.req)
}
})
const gpioTimerService = fork('./app/gpio/GpioTimerService.js')
gpioTimerService.on('message', (dataPoint) => {
rowingEngine.handleRotationImpulse(dataPoint)
// fs.appendFile('recordings/wrx700_2magnets_long.csv', `${dataPoint}\n`, (err) => { if (err) log.error(err) })
})
const rowingEngine = createRowingEngine()
const rowingStatistics = createRowingStatistics()
rowingEngine.notify(rowingStatistics)
rowingStatistics.on('strokeFinished', (data) => {
log.info(`stroke: ${data.strokesTotal}, dur: ${data.strokeTime}s, power: ${data.power}w` +
`, split: ${data.splitFormatted}, ratio: ${data.powerRatio}, dist: ${data.distanceTotal}m` +
`, cal: ${data.caloriesTotal}kcal, SPM: ${data.strokesPerMinute}, speed: ${data.speed}km/h` +
`, cal/hour: ${data.caloriesPerHour}kcal, cal/minute: ${data.caloriesPerMinute}kcal`)
const metrics = {
durationTotal: data.durationTotal,
durationTotalFormatted: data.durationTotalFormatted,
strokesTotal: data.strokesTotal,
distanceTotal: data.distanceTotal,
caloriesTotal: data.caloriesTotal,
caloriesPerMinute: data.caloriesPerMinute,
caloriesPerHour: data.caloriesPerHour,
power: data.power,
splitFormatted: data.splitFormatted,
split: data.split,
strokesPerMinute: data.strokesPerMinute,
speed: data.speed,
// todo: no real measurement of heartRate yet
heartRate: Math.max(data.caloriesPerMinute * 7, 50),
strokeState: data.strokeState
}
webServer.notifyClients(metrics)
peripheralManager.notifyMetrics('strokeFinished', metrics)
})
rowingStatistics.on('rowingPaused', (data) => {
const metrics = {
durationTotal: data.durationTotal,
durationTotalFormatted: data.durationTotalFormatted,
strokesTotal: data.strokesTotal,
distanceTotal: data.distanceTotal,
caloriesTotal: data.caloriesTotal,
caloriesPerMinute: 0,
caloriesPerHour: 0,
strokesPerMinute: 0,
power: 0,
splitFormatted: '∞',
split: Infinity,
speed: 0,
// todo: no real measurement of heartRate yet
heartRate: Math.max(data.caloriesPerMinute * 7, 50),
strokeState: 'RECOVERY'
}
webServer.notifyClients(metrics)
peripheralManager.notifyMetrics('rowingPaused', metrics)
})
rowingStatistics.on('strokeStateChanged', (metrics) => {
peripheralManager.notifyMetrics('strokeStateChanged', metrics)
})
rowingStatistics.on('durationUpdate', (data) => {
webServer.notifyClients({
durationTotalFormatted: data.durationTotalFormatted
})
})
const webServer = createWebServer()
webServer.on('messageReceived', (message) => {
if (message.command === 'reset') {
rowingStatistics.reset()
peripheralManager.notifyStatus({ name: 'reset' })
} if (message.command === 'switchPeripheralMode') {
peripheralManager.switchPeripheralMode()
} else {
log.warn(`invalid command received: ${message}`)
}
})
webServer.on('clientConnected', () => {
webServer.notifyClients({ peripheralMode: peripheralManager.getPeripheralMode() })
})
/*
replayRowingSession(rowingEngine.handleRotationImpulse, {
filename: 'recordings/wrx700_2magnets.csv',
realtime: true,
loop: true
})
*/