Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions app/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import { createSessionManager } from './engine/SessionManager.js'
import { createWebServer } from './WebServer.js'
import { createPeripheralManager } from './peripherals/PeripheralManager.js'
import { createRecordingManager } from './recorders/recordingManager.js'
/* eslint-disable-next-line no-unused-vars -- replayRowingSession shouldn't be used in a production environments */
import { replayRowingSession } from './recorders/RowingReplayer.js'
import { parseSimulationArgs } from './tools/SimulationArgs.js'

const simulationArgs = parseSimulationArgs(process.argv.slice(2))

const exec = promisify(child_process.exec)

Expand Down Expand Up @@ -147,12 +149,13 @@ async function shutdownApp () {
peripheralManager.handleCommand('shutdown')
}

/* Uncomment the following lines to simulate a session
setTimeout(function() {
replayRowingSession(handleRotationImpulse, {
filename: 'recordings/Concept2_RowErg_Session_2000meters.csv', // Concept 2, 2000 meter session
realtime: true,
loop: true
})
}, 30000)
*/
if (simulationArgs.simulate) {
log.info(`Simulation mode enabled, replaying '${simulationArgs.simulateFile}' after ${simulationArgs.simulateDelay}ms delay (realtime: ${simulationArgs.realtime}, loop: ${simulationArgs.loop})`)
setTimeout(() => {
replayRowingSession(handleRotationImpulse, {
filename: simulationArgs.simulateFile,
realtime: simulationArgs.realtime,
loop: simulationArgs.loop
})
}, simulationArgs.simulateDelay)
}
41 changes: 41 additions & 0 deletions app/tools/SimulationArgs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor

Parses command-line arguments for session simulation.
*/

import { parseArgs } from 'util'

const simulationArgOptions = /** @type {const} */ ({
simulate: { type: 'boolean', default: false },
simulateFile: { type: 'string', default: 'recordings/Concept2_RowErg_Session_2000meters.csv' },
simulateDelay: { type: 'string', default: '30000' },
simulateOnce: { type: 'boolean', default: false },
simulateFast: { type: 'boolean', default: false }
})

/**
* @param {string[]} argv
*/
function parseSimulationArgs (argv) {
const { values } = parseArgs({
options: simulationArgOptions,
args: argv,
strict: false
})

const parsedDelay = parseInt(String(values.simulateDelay ?? '30000'), 10)

return {
simulate: !!values.simulate,
simulateFile: String(values.simulateFile ?? 'recordings/Concept2_RowErg_Session_2000meters.csv'),
simulateDelay: Number.isFinite(parsedDelay) && parsedDelay >= 0 ? parsedDelay : 30000,
realtime: !values.simulateFast,
loop: !values.simulateOnce
}
}

export {
parseSimulationArgs
}
88 changes: 88 additions & 0 deletions app/tools/SimulationArgs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor

Tests for SimulationArgs parsing
*/
import { test } from 'uvu'
import * as assert from 'uvu/assert'
import { parseSimulationArgs } from './SimulationArgs.js'

test('defaults when no args are passed', () => {
const result = parseSimulationArgs([])
assert.is(result.simulate, false)
assert.is(result.simulateFile, 'recordings/Concept2_RowErg_Session_2000meters.csv')
assert.is(result.simulateDelay, 30000)
assert.is(result.realtime, true)
assert.is(result.loop, true)
})

test('--simulate enables simulation', () => {
const result = parseSimulationArgs(['--simulate'])
assert.is(result.simulate, true)
assert.is(result.simulateFile, 'recordings/Concept2_RowErg_Session_2000meters.csv')
assert.is(result.simulateDelay, 30000)
assert.is(result.realtime, true)
assert.is(result.loop, true)
})

test('--simulateFile overrides default file', () => {
const result = parseSimulationArgs(['--simulate', '--simulateFile', 'recordings/WRX700_2magnets.csv'])
assert.is(result.simulate, true)
assert.is(result.simulateFile, 'recordings/WRX700_2magnets.csv')
})

test('--simulateDelay overrides default delay', () => {
const result = parseSimulationArgs(['--simulate', '--simulateDelay', '5000'])
assert.is(result.simulateDelay, 5000)
})

test('--simulateOnce disables looping', () => {
const result = parseSimulationArgs(['--simulate', '--simulateOnce'])
assert.is(result.loop, false)
assert.is(result.realtime, true)
})

test('--simulateFast disables realtime', () => {
const result = parseSimulationArgs(['--simulate', '--simulateFast'])
assert.is(result.realtime, false)
assert.is(result.loop, true)
})

test('all flags combined', () => {
const result = parseSimulationArgs([
'--simulate',
'--simulateFile', 'recordings/RX800.csv',
'--simulateDelay', '1000',
'--simulateOnce',
'--simulateFast'
])
assert.is(result.simulate, true)
assert.is(result.simulateFile, 'recordings/RX800.csv')
assert.is(result.simulateDelay, 1000)
assert.is(result.realtime, false)
assert.is(result.loop, false)
})

test('non-numeric delay falls back to default', () => {
const result = parseSimulationArgs(['--simulate', '--simulateDelay', 'abc'])
assert.is(result.simulateDelay, 30000)
})

test('empty delay falls back to default', () => {
const result = parseSimulationArgs(['--simulate', '--simulateDelay', ''])
assert.is(result.simulateDelay, 30000)
})

test('negative delay falls back to default', () => {
const result = parseSimulationArgs(['--simulate', '--simulateDelay', '-5000'])
assert.is(result.simulateDelay, 30000)
})

test('unknown flags are ignored', () => {
const result = parseSimulationArgs(['--simulate', '--unknownFlag'])
assert.is(result.simulate, true)
assert.is(result.simulateDelay, 30000)
})

test.run()
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"scripts": {
"lint": "eslint ./app ./config && markdownlint-cli2 '**/*.md' '#node_modules'",
"start": "node app/server.js",
"simulate": "node app/server.js --simulate",
"dev": "vite",
"build": "vite build",
"build:watch": "vite build --watch",
Expand Down
Loading