Skip to content

Commit 28fa9f4

Browse files
authored
Architecture cleanup (#56)
# New functionality in 0.9.5 - Added **FIT-File support**: you an now automatically generate a FIT-file after a rowing session, which alows for a more detailed reporting than the tcx-format, and is commonly accepted by most platforms - **Introduction of the session manager**, which provides support for intervals, splits, rest intervals and spontanuous pauses in the session and also adds these to the FIT, tcx and RowingData recordings. Please note, setting predetermined intervals and splits in a user friendly way (via PM5 and webinterface) is still a ToDo that is intended for 1.0.0. - **Improvement of Magnetic rower support**: the new session manager makes sure that the session is nicely stopped, even when the flywheel has stopped quite abruptly before pause timeouts have time to kick in. This is the case on some magnetic rowers which have an extreme high drag, resulting in very short spin down times of their flywheel. # Bugfixes and robustness improvements in 0.9.5 - **Improvement of the architecture**: we cleaned up the old architecture and went to a more message bus structure where clients are responsible for listening to the datatransmissions they are interested in. See [the architecture description](Architecture.md) for a deep-dive of the implementation. Key benefit is that this is more maintainable as it allows serving data more easily to totally different clients (webGUI, recorders and BLE/ANT+) with totally different needs, making future enhancements easier. - **Improvement of Bluetooth stability**: we moved away from abandonware's BLE NoBle/BleNo implementation and moved to stoprocent's implementation, as that package is better maintained and works better with newer installs of BlueZ, which should fix some issues on Raspberry Pi Bookworm. Unfortunatly, none of the NoBle/BleNo descendents are immune to some specific BlueZ issues (see known issues). - **Performance improvement of the TS estimator**, further reducing CPU load, which significantly improves accuracy of the measurements and metrics as the Linux kernel has an easier job keeping the time accurate. - **Removed a lot of memory leaks**, although only being problematic in large simulations (i.e. over 3000K), we want to keep our code to behave nice - **Improved robustness of the stroke detection algorithm** - **Validation of the engine against a PM5 for over 3000KM**, where the deviation is a maximum of 0.1% # Known issues in 0.9.5 - **Bluetooth Heartrate can't be switched dynamically**: due to some underlying changes in the OS, BLE heartrate monitors can't be activated through the GUI without crashing the BLE metrics broadcast (see [the description of issue 69](#69)). As this is an issue in the OS, and all previous versions of OpenRowingMonitor are also affected by this issue. Version 0.9.5 has a workaround implemented that mitigates this at startup. So configuring the use of a BLE heartrate monitor in the config file should work.
1 parent d405fb3 commit 28fa9f4

File tree

118 files changed

+145174
-17217
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+145174
-17217
lines changed

app/WebServer.js

Lines changed: 128 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Creates the WebServer which serves the static assets and communicates with the clients
66
via WebSockets
@@ -12,10 +12,25 @@ import serveStatic from 'serve-static'
1212
import log from 'loglevel'
1313
import EventEmitter from 'events'
1414

15-
function createWebServer () {
15+
export function createWebServer (config) {
1616
const emitter = new EventEmitter()
1717
const port = process.env.PORT || 80
1818
const serve = serveStatic('./build', { index: ['index.html'] })
19+
let timer = setTimeout(timeBasedPresenter, config.webUpdateInterval)
20+
let lastKnownMetrics = {
21+
strokeState: 'WaitingForDrive',
22+
sessionStatus: 'WaitingForStart',
23+
totalMovingTime: 0,
24+
totalNumberOfStrokes: 0,
25+
totalLinearDistance: 0,
26+
cyclePace: Infinity,
27+
cyclePower: 0,
28+
driveLength: 0,
29+
driveDistance: 0,
30+
dragFactor: undefined
31+
}
32+
let heartRate
33+
let heartRateBatteryLevel
1934

2035
const server = http.createServer((req, res) => {
2136
serve(req, res, finalhandler(req, res))
@@ -30,7 +45,7 @@ function createWebServer () {
3045

3146
wss.on('connection', function connection (client) {
3247
log.debug('websocket client connected')
33-
emitter.emit('clientConnected', client)
48+
notifyClient(client, 'config', getConfig())
3449
client.on('message', function incoming (data) {
3550
try {
3651
const message = JSON.parse(data)
@@ -48,6 +63,100 @@ function createWebServer () {
4863
})
4964
})
5065

66+
// This function handles all incomming commands. As all commands are broadasted to all application parts,
67+
// we need to filter here what the webserver will react to and what it will ignore
68+
// The start...reset commands are handled by the RowingEngine and the result will be reported by the metrics update, so we ignore them here
69+
function handleCommand (commandName, data, client) {
70+
switch (commandName) {
71+
case ('updateIntervalSettings'):
72+
break
73+
case ('start'):
74+
break
75+
case ('startOrResume'):
76+
break
77+
case ('pause'):
78+
break
79+
case ('stop'):
80+
break
81+
case ('requestControl'):
82+
break
83+
case ('reset'):
84+
break
85+
case 'switchBlePeripheralMode':
86+
break
87+
case 'switchAntPeripheralMode':
88+
break
89+
case 'switchHrmMode':
90+
break
91+
case 'refreshPeripheralConfig':
92+
notifyClients('config', getConfig())
93+
break
94+
case 'authorizeStrava':
95+
notifyClient(client, 'authorizeStrava', data)
96+
break
97+
case 'uploadTraining':
98+
break
99+
case 'stravaAuthorizationCode':
100+
break
101+
case 'shutdown':
102+
break
103+
default:
104+
log.error(`WebServer: Recieved unknown command: ${commandName}`)
105+
}
106+
}
107+
108+
function presentRowingMetrics (metrics) {
109+
if (metrics.metricsContext === undefined) return
110+
switch (true) {
111+
case (metrics.metricsContext.isSessionStart):
112+
notifyClients('metrics', metrics)
113+
break
114+
case (metrics.metricsContext.isSessionStop):
115+
notifyClients('metrics', metrics)
116+
break
117+
case (metrics.metricsContext.isIntervalStart):
118+
notifyClients('metrics', metrics)
119+
break
120+
case (metrics.metricsContext.isPauseStart):
121+
notifyClients('metrics', metrics)
122+
break
123+
case (metrics.metricsContext.isPauseEnd):
124+
notifyClients('metrics', metrics)
125+
break
126+
case (metrics.metricsContext.isDriveStart):
127+
notifyClients('metrics', metrics)
128+
break
129+
case (metrics.metricsContext.isRecoveryStart):
130+
notifyClients('metrics', metrics)
131+
break
132+
}
133+
lastKnownMetrics = metrics
134+
}
135+
136+
// initiated when a new heart rate value is received from heart rate sensor
137+
async function presentHeartRate (value) {
138+
heartRate = value.heartrate
139+
heartRateBatteryLevel = value.batteryLevel
140+
}
141+
142+
// Make sure that the GUI is updated with the latest metrics even when no fresh data arrives
143+
function timeBasedPresenter () {
144+
notifyClients('metrics', lastKnownMetrics)
145+
}
146+
147+
function addHeartRateToMetrics (metrics) {
148+
if (heartRate !== undefined) {
149+
metrics.heartrate = heartRate
150+
} else {
151+
metrics.heartrate = undefined
152+
}
153+
if (heartRateBatteryLevel !== undefined) {
154+
metrics.heartRateBatteryLevel = heartRateBatteryLevel
155+
} else {
156+
metrics.heartRateBatteryLevel = undefined
157+
}
158+
}
159+
51160
function notifyClient (client, type, data) {
52161
const messageString = JSON.stringify({ type, data })
53162
if (wss.clients.has(client)) {
@@ -60,18 +169,31 @@ function createWebServer () {
60169
}
61170

62171
function notifyClients (type, data) {
172+
clearTimeout(timer)
173+
if (type === 'metrics') { addHeartRateToMetrics(data) }
63174
const messageString = JSON.stringify({ type, data })
64175
wss.clients.forEach(function each (client) {
65176
if (client.readyState === WebSocket.OPEN) {
66177
client.send(messageString)
67178
}
68179
})
180+
timer = setTimeout(timeBasedPresenter, config.webUpdateInterval)
181+
}
182+
183+
function getConfig () {
184+
return {
185+
blePeripheralMode: config.bluetoothMode,
186+
antPeripheralMode: config.antPlusMode,
187+
hrmPeripheralMode: config.heartRateMode,
188+
stravaUploadEnabled: !!config.stravaClientId && !!config.stravaClientSecret,
189+
shutdownEnabled: !!config.shutdownCommand
190+
}
69191
}
70192

71193
return Object.assign(emitter, {
72194
notifyClient,
73-
notifyClients
195+
presentRowingMetrics,
196+
presentHeartRate,
197+
handleCommand
74198
})
75199
}
76-
77-
export { createWebServer }

app/client/components/AppDialog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders a html dialog
66
*/

app/client/components/AppElement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Base Component for all other App Components
66
*/

app/client/components/BatteryIcon.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders a battery indicator
66
*/

app/client/components/DashboardActions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders the action buttons of the dashboard
66
*/

app/client/components/DashboardForceCurve.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders a metric of the dashboard
66
*/

app/client/components/DashboardMetric.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders a metric of the dashboard
66
*/

app/client/components/PerformanceDashboard.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders the dashboard
66
*/

app/client/components/SettingsDialog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Component that renders the action buttons of the dashboard
66
*/

app/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22
/*
3-
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
3+
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
44
55
Main Initialization Component of the Web Component App
66
*/

0 commit comments

Comments
 (0)