217 lines
11 KiB
JavaScript
217 lines
11 KiB
JavaScript
'use strict'
|
|
/*
|
|
Open Rowing Monitor, https://github.com/JaapvanEkris/openrowingmonitor
|
|
|
|
Merges the different config files and presents the configuration to the application
|
|
Checks the config for plausibilit, fixes the errors when needed
|
|
*/
|
|
import defaultConfig from '../../config/default.config.js'
|
|
import { deepMerge } from './Helper.js'
|
|
import log from 'loglevel'
|
|
|
|
async function getConfig () {
|
|
let customConfig
|
|
try {
|
|
customConfig = await import('../../config/config.js')
|
|
} catch (exception) {}
|
|
return customConfig !== undefined ? deepMerge(defaultConfig, customConfig.default) : defaultConfig
|
|
}
|
|
|
|
function checkConfig (configToCheck) {
|
|
checkRangeValue(configToCheck.loglevel, 'default', ['trace', 'debug', 'info', 'warn', 'error', 'silent'], true, 'error')
|
|
checkRangeValue(configToCheck.loglevel, 'RowingEngine', ['trace', 'debug', 'info', 'warn', 'error', 'silent'], true, 'error')
|
|
checkIntegerValue(configToCheck, 'gpioPin', 1, 27, false, false, null)
|
|
checkIntegerValue(configToCheck, 'gpioPriority', -7, 0, true, true, 0)
|
|
checkIntegerValue(configToCheck, 'gpioMinimumPulseLength', 1, null, false, true, 0)
|
|
checkIntegerValue(configToCheck, 'gpioPollingInterval', 1, 10, false, true, 10)
|
|
checkRangeValue(configToCheck, 'gpioPollingInterval', [1, 2, 5, 10], true, 10)
|
|
checkRangeValue(configToCheck, 'gpioTriggeredFlank', ['Up', 'Down', 'Both'], true, 'Up')
|
|
checkIntegerValue(configToCheck, 'appPriority', configToCheck.gpioPriority, 0, true, true, 0)
|
|
checkIntegerValue(configToCheck, 'webUpdateInterval', 50, 1000, false, true, 1000)
|
|
checkIntegerValue(configToCheck, 'peripheralUpdateInterval', 50, 1000, false, true, 1000)
|
|
checkRangeValue(configToCheck, 'bluetoothMode', ['OFF', 'PM5', 'FTMS', 'FTMSBIKE', 'CPS', 'CSC'], true, 'OFF')
|
|
checkRangeValue(configToCheck, 'antplusMode', ['OFF', 'FE'], true, 'OFF')
|
|
checkRangeValue(configToCheck, 'heartRateMode', ['OFF', 'ANT', 'BLE'], true, 'OFF')
|
|
checkIntegerValue(configToCheck, 'numOfPhasesForAveragingScreenData', 2, null, false, true, 4)
|
|
checkBooleanValue(configToCheck, 'createRowingDataFiles', true, true)
|
|
checkBooleanValue(configToCheck, 'createRawDataFiles', true, true)
|
|
checkBooleanValue(configToCheck, 'gzipRawDataFiles', true, false)
|
|
checkBooleanValue(configToCheck, 'createTcxFiles', true, true)
|
|
checkBooleanValue(configToCheck, 'gzipTcxFiles', true, false)
|
|
checkFloatValue(configToCheck.userSettings, 'restingHR', 30, 220, false, true, 40)
|
|
checkFloatValue(configToCheck.userSettings, 'maxHR', configToCheck.userSettings.restingHR, 220, false, true, 220)
|
|
if (configToCheck.createTcxFiles) {
|
|
checkFloatValue(configToCheck.userSettings, 'minPower', 1, 500, false, true, 50)
|
|
checkFloatValue(configToCheck.userSettings, 'maxPower', 100, 6000, false, true, 500)
|
|
checkFloatValue(configToCheck.userSettings, 'distanceCorrectionFactor', 0, 50, false, true, 5)
|
|
checkFloatValue(configToCheck.userSettings, 'weight', 25, 500, false, true, 80)
|
|
checkRangeValue(configToCheck.userSettings, 'sex', ['male', 'female'], true, 'male')
|
|
checkBooleanValue(configToCheck.userSettings, 'highlyTrained', true, false)
|
|
}
|
|
checkIntegerValue(configToCheck.rowerSettings, 'numOfImpulsesPerRevolution', 1, null, false, false, null)
|
|
checkIntegerValue(configToCheck.rowerSettings, 'flankLength', 3, null, false, false, null)
|
|
checkFloatValue(configToCheck.rowerSettings, 'sprocketRadius', 0, 20, false, true, 3)
|
|
checkFloatValue(configToCheck.rowerSettings, 'minimumTimeBetweenImpulses', 0, 3, false, false, null)
|
|
checkFloatValue(configToCheck.rowerSettings, 'maximumTimeBetweenImpulses', configToCheck.rowerSettings.minimumTimeBetweenImpulses, 3, false, false, null)
|
|
checkFloatValue(configToCheck.rowerSettings, 'smoothing', 1, null, false, true, 1)
|
|
checkFloatValue(configToCheck.rowerSettings, 'dragFactor', 1, null, false, false, null)
|
|
checkBooleanValue(configToCheck.rowerSettings, 'autoAdjustDragFactor', true, false)
|
|
checkIntegerValue(configToCheck.rowerSettings, 'dragFactorSmoothing', 1, null, false, true, 1)
|
|
if (configToCheck.rowerSettings.autoAdjustDragFactor) {
|
|
checkFloatValue(configToCheck.rowerSettings, 'minimumDragQuality', 0, 1, true, true, 0)
|
|
}
|
|
checkFloatValue(configToCheck.rowerSettings, 'flywheelInertia', 0, null, false, false, null)
|
|
checkFloatValue(configToCheck.rowerSettings, 'minumumForceBeforeStroke', 0, 500, true, true, 0)
|
|
checkFloatValue(configToCheck.rowerSettings, 'minumumRecoverySlope', 0, null, true, true, 0)
|
|
checkFloatValue(configToCheck.rowerSettings, 'minimumStrokeQuality', 0, 1, true, true, 0)
|
|
checkBooleanValue(configToCheck.rowerSettings, 'autoAdjustRecoverySlope', true, false)
|
|
if (!configToCheck.rowerSettings.autoAdjustDragFactor && configToCheck.rowerSettings.autoAdjustRecoverySlope) {
|
|
log.error('Configuration Error: rowerSettings.autoAdjustRecoverySlope can not be true when rowerSettings.autoAdjustDragFactor is false, ignoring request')
|
|
}
|
|
if (configToCheck.rowerSettings.autoAdjustDragFactor && configToCheck.rowerSettings.autoAdjustRecoverySlope) {
|
|
checkFloatValue(configToCheck.rowerSettings, 'autoAdjustRecoverySlopeMargin', 0, 1, false, true, 1)
|
|
}
|
|
checkFloatValue(configToCheck.rowerSettings, 'minimumDriveTime', 0, null, false, true, 0.001)
|
|
checkFloatValue(configToCheck.rowerSettings, 'minimumRecoveryTime', 0, null, false, true, 0.001)
|
|
checkFloatValue(configToCheck.rowerSettings, 'maximumStrokeTimeBeforePause', 3, 60, false, true, 6)
|
|
checkFloatValue(configToCheck.rowerSettings, 'magicConstant', 0, null, false, true, 2.8)
|
|
}
|
|
|
|
function checkIntegerValue (parameterSection, parameterName, minimumValue, maximumvalue, allowZero, allowRepair, defaultValue) {
|
|
// PLEASE NOTE: the parameterSection, parameterName seperation is needed to force a call by reference, which is needed for the repair action
|
|
let errors = 0
|
|
switch (true) {
|
|
case (parameterSection[parameterName] === undefined):
|
|
log.error(`Configuration Error: ${parameterName} isn't defined (at the right spot)`)
|
|
errors++
|
|
break
|
|
case (!Number.isInteger(parameterSection[parameterName])):
|
|
log.error(`Configuration Error: ${parameterName} should be an integer value, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (minimumValue != null && parameterSection[parameterName] < minimumValue):
|
|
log.error(`Configuration Error: ${parameterName} should be at least ${minimumValue}, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (maximumvalue != null && parameterSection[parameterName] > maximumvalue):
|
|
log.error(`Configuration Error: ${parameterName} can't be above ${maximumvalue}, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (!allowZero && parameterSection[parameterName] === 0):
|
|
log.error(`Configuration Error: ${parameterName} can't be zero`)
|
|
errors++
|
|
break
|
|
default:
|
|
// No error detected :)
|
|
}
|
|
if (errors > 0) {
|
|
// Errors were made
|
|
if (allowRepair) {
|
|
log.error(` resolved by setting ${parameterName} to ${defaultValue}`)
|
|
parameterSection[parameterName] = defaultValue
|
|
} else {
|
|
log.error(` as ${parameterName} is a fatal parameter, I'm exiting`)
|
|
process.exit(9)
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkFloatValue (parameterSection, parameterName, minimumValue, maximumvalue, allowZero, allowRepair, defaultValue) {
|
|
// PLEASE NOTE: the parameterSection, parameterName seperation is needed to force a call by reference, which is needed for the repair action
|
|
let errors = 0
|
|
switch (true) {
|
|
case (parameterSection[parameterName] === undefined):
|
|
log.error(`Configuration Error: ${parameterName} isn't defined (at the right spot)`)
|
|
errors++
|
|
break
|
|
case (!(typeof (parameterSection[parameterName]) === 'number')):
|
|
log.error(`Configuration Error: ${parameterName} should be a numerical value, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (minimumValue != null && parameterSection[parameterName] < minimumValue):
|
|
log.error(`Configuration Error: ${parameterName} should be at least ${minimumValue}, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (maximumvalue != null && parameterSection[parameterName] > maximumvalue):
|
|
log.error(`Configuration Error: ${parameterName} can't be above ${maximumvalue}, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
case (!allowZero && parameterSection[parameterName] === 0):
|
|
log.error(`Configuration Error: ${parameterName} can't be zero`)
|
|
errors++
|
|
break
|
|
default:
|
|
// No error detected :)
|
|
}
|
|
if (errors > 0) {
|
|
// Errors were made
|
|
if (allowRepair) {
|
|
log.error(` resolved by setting ${parameterName} to ${defaultValue}`)
|
|
parameterSection[parameterName] = defaultValue
|
|
} else {
|
|
log.error(` as ${parameterName} is a fatal parameter, I'm exiting`)
|
|
process.exit(9)
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkBooleanValue (parameterSection, parameterName, allowRepair, defaultValue) {
|
|
// PLEASE NOTE: the parameterSection, parameterName seperation is needed to force a call by reference, which is needed for the repair action
|
|
let errors = 0
|
|
switch (true) {
|
|
case (parameterSection[parameterName] === undefined):
|
|
log.error(`Configuration Error: ${parameterName} isn't defined (at the right spot)`)
|
|
errors++
|
|
break
|
|
case (!(parameterSection[parameterName] === true || parameterSection[parameterName] === false)):
|
|
log.error(`Configuration Error: ${parameterName} should be either false or true, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
default:
|
|
// No error detected :)
|
|
}
|
|
if (errors > 0) {
|
|
// Errors were made
|
|
if (allowRepair) {
|
|
log.error(` resolved by setting ${parameterName} to ${defaultValue}`)
|
|
parameterSection[parameterName] = defaultValue
|
|
} else {
|
|
log.error(` as ${parameterName} is a fatal parameter, I'm exiting`)
|
|
process.exit(9)
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkRangeValue (parameterSection, parameterName, range, allowRepair, defaultValue) {
|
|
// PLEASE NOTE: the parameterSection, parameterName seperation is needed to force a call by reference, which is needed for the repair action
|
|
let errors = 0
|
|
switch (true) {
|
|
case (parameterSection[parameterName] === undefined):
|
|
log.error(`Configuration Error: ${parameterName} isn't defined (at the right spot)`)
|
|
errors++
|
|
break
|
|
case (!range.includes(parameterSection[parameterName])):
|
|
log.error(`Configuration Error: ${parameterName} should be come from ${range}, encountered ${parameterSection[parameterName]}`)
|
|
errors++
|
|
break
|
|
default:
|
|
// No error detected :)
|
|
}
|
|
if (errors > 0) {
|
|
// Errors were made
|
|
if (allowRepair) {
|
|
log.error(` resolved by setting ${parameterName} to ${defaultValue}`)
|
|
parameterSection[parameterName] = defaultValue
|
|
} else {
|
|
log.error(` as ${parameterName} is a fatal parameter, I'm exiting`)
|
|
process.exit(9)
|
|
}
|
|
}
|
|
}
|
|
|
|
const config = await getConfig()
|
|
|
|
checkConfig(config)
|
|
|
|
export default config
|