161 lines
4.9 KiB
JavaScript
161 lines
4.9 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 readline from 'readline'
|
|
// import fs from 'fs'
|
|
import { fork } from 'child_process'
|
|
import WebSocket from 'ws'
|
|
import finalhandler from 'finalhandler'
|
|
import http from 'http'
|
|
import serveStatic from 'serve-static'
|
|
import log from 'loglevel'
|
|
import { createRowingMachinePeripheral } from './ble/RowingMachinePeripheral.js'
|
|
import { createRowingEngine } from './engine/RowingEngine.js'
|
|
import { createRowingStatistics } from './engine/RowingStatistics.js'
|
|
// import { recordRowingSession } from './tools/RowingRecorder.js'
|
|
|
|
// sets the global log level
|
|
log.setLevel(log.levels.INFO)
|
|
// recordRowingSession('recordings/wrx700_2magnets.csv')
|
|
const peripheral = createRowingMachinePeripheral({
|
|
simulateIndoorBike: true
|
|
})
|
|
|
|
peripheral.on('controlPoint', (event) => {
|
|
if (event?.req?.name === 'requestControl') {
|
|
event.res = true
|
|
} else if (event?.req?.name === 'reset') {
|
|
log.debug('reset requested')
|
|
rowingStatistics.reset()
|
|
peripheral.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')
|
|
peripheral.notifyStatus({ name: 'stoppedOrPausedByUser' })
|
|
event.res = true
|
|
} else if (event?.req?.name === 'pause') {
|
|
log.debug('pause requested')
|
|
peripheral.notifyStatus({ name: 'stoppedOrPausedByUser' })
|
|
event.res = true
|
|
} else if (event?.req?.name === 'startOrResume') {
|
|
log.debug('startOrResume requested')
|
|
peripheral.notifyStatus({ name: 'startedOrResumedByUser' })
|
|
event.res = true
|
|
} else {
|
|
log.info('unhandled Command', event.req)
|
|
}
|
|
})
|
|
|
|
const gpioTimerService = fork('./app/gpio/GpioTimerService.js')
|
|
gpioTimerService.on('message', (dataPoint) => {
|
|
rowingEngine.handleRotationImpulse(dataPoint)
|
|
})
|
|
|
|
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`)
|
|
|
|
const metrics = {
|
|
strokesTotal: data.strokesTotal,
|
|
distanceTotal: data.distanceTotal,
|
|
caloriesTotal: data.caloriesTotal,
|
|
power: data.power,
|
|
splitFormatted: data.splitFormatted,
|
|
split: data.split,
|
|
strokesPerMinute: data.strokesPerMinute,
|
|
speed: data.speed
|
|
}
|
|
|
|
notifyWebClients(metrics)
|
|
peripheral.notifyData(metrics)
|
|
})
|
|
|
|
rowingStatistics.on('durationUpdate', (data) => {
|
|
notifyWebClients({
|
|
durationTotal: data.durationTotal
|
|
})
|
|
})
|
|
|
|
const port = process.env.PORT || 80
|
|
const serve = serveStatic('./app/client', { index: ['index.html'] })
|
|
|
|
const server = http.createServer((req, res) => {
|
|
serve(req, res, finalhandler(req, res))
|
|
})
|
|
|
|
server.listen(port)
|
|
|
|
const wss = new WebSocket.Server({ server })
|
|
|
|
wss.on('connection', function connection (ws) {
|
|
log.debug('websocket client connected')
|
|
ws.on('message', function incoming (data) {
|
|
try {
|
|
const message = JSON.parse(data)
|
|
if (message && message.command === 'reset') {
|
|
rowingStatistics.reset()
|
|
peripheral.notifyStatus({ name: 'reset' })
|
|
} else {
|
|
log.info(`invalid command received: ${data}`)
|
|
}
|
|
} catch (err) {
|
|
log.error(err)
|
|
}
|
|
})
|
|
ws.on('close', function () {
|
|
log.debug('websocket client disconnected')
|
|
})
|
|
})
|
|
|
|
function notifyWebClients (message) {
|
|
const messageString = JSON.stringify(message)
|
|
wss.clients.forEach(function each (client) {
|
|
if (client.readyState === WebSocket.OPEN) {
|
|
client.send(messageString)
|
|
}
|
|
})
|
|
}
|
|
|
|
/*
|
|
const readInterface = readline.createInterface({
|
|
input: fs.createReadStream('recordings/wrx700_2magnets.csv')
|
|
})
|
|
|
|
readInterface.on('line', function (line) {
|
|
rowingEngine.handleRotationImpulse(parseFloat(line))
|
|
})
|
|
*/
|
|
|
|
// for temporary simulation of usage
|
|
/*
|
|
setInterval(simulateRowing, 2000)
|
|
let simStroke = 0
|
|
let simDistance = 0.0
|
|
let simCalories = 0.0
|
|
function simulateRowing () {
|
|
const metrics = {
|
|
strokesTotal: simStroke++,
|
|
distanceTotal: Math.round(simDistance += 10.1),
|
|
caloriesTotal: Math.round(simCalories += 0.3),
|
|
power: Math.round(80 + 20 * (Math.random() - 0.5)),
|
|
splitFormatted: '02:30',
|
|
split: Math.round(80 + 20 * (Math.random() - 0.5)),
|
|
strokesPerMinute: Math.round(10 + 20 * (Math.random() - 0.5)),
|
|
speed: Math.round((15 + 20 * (Math.random() - 0.5)).toFixed(2))
|
|
}
|
|
peripheral.notifyData(metrics)
|
|
notifyWebClients(metrics)
|
|
}
|
|
*/
|