mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 18:10:47 -04:00
139 lines
4.0 KiB
JavaScript
139 lines
4.0 KiB
JavaScript
// rover Manager math utils
|
|
// Purpose: Centralizes numeric clamping and battery display calculations used by rover-manager orchestration.
|
|
// Scope: Keeps runtime behavior unchanged by extracting pure utility functions from the main service module.
|
|
const { DEFAULT_PRIVATE_SAFETY } = require('./constants');
|
|
|
|
function clampInt(value, min, max, fallback) {
|
|
const num = Number.parseInt(value, 10);
|
|
if (!Number.isFinite(num)) return fallback;
|
|
return Math.max(min, Math.min(max, num));
|
|
}
|
|
|
|
function normalizePrivateSafety(raw = {}) {
|
|
const source = raw && typeof raw === 'object' ? raw : {};
|
|
return {
|
|
speedLimitEnabled: Boolean(source.speedLimitEnabled),
|
|
speedLimitMaxWheelSpeed: clampInt(
|
|
source.speedLimitMaxWheelSpeed,
|
|
1,
|
|
500,
|
|
DEFAULT_PRIVATE_SAFETY.speedLimitMaxWheelSpeed,
|
|
),
|
|
hardOvercurrentEnabled: Boolean(source.hardOvercurrentEnabled),
|
|
overcurrentStopMs: clampInt(
|
|
source.overcurrentStopMs,
|
|
100,
|
|
5000,
|
|
DEFAULT_PRIVATE_SAFETY.overcurrentStopMs,
|
|
),
|
|
hardBumpEnabled: Boolean(source.hardBumpEnabled),
|
|
bumpBackoffSpeed: clampInt(
|
|
source.bumpBackoffSpeed,
|
|
1,
|
|
500,
|
|
DEFAULT_PRIVATE_SAFETY.bumpBackoffSpeed,
|
|
),
|
|
bumpBackoffMs: clampInt(
|
|
source.bumpBackoffMs,
|
|
100,
|
|
5000,
|
|
DEFAULT_PRIVATE_SAFETY.bumpBackoffMs,
|
|
),
|
|
cliffEnabled: Boolean(source.cliffEnabled),
|
|
cliffBackoffSpeed: clampInt(
|
|
source.cliffBackoffSpeed,
|
|
1,
|
|
500,
|
|
DEFAULT_PRIVATE_SAFETY.cliffBackoffSpeed,
|
|
),
|
|
cliffBackoffMs: clampInt(
|
|
source.cliffBackoffMs,
|
|
100,
|
|
5000,
|
|
DEFAULT_PRIVATE_SAFETY.cliffBackoffMs,
|
|
),
|
|
triggerCooldownMs: clampInt(
|
|
source.triggerCooldownMs,
|
|
100,
|
|
10000,
|
|
DEFAULT_PRIVATE_SAFETY.triggerCooldownMs,
|
|
),
|
|
};
|
|
}
|
|
|
|
function computeBatteryDisplayPercent({ charge, full, warn, urgent, percent, capacity }) {
|
|
if (
|
|
charge != null &&
|
|
full != null &&
|
|
warn != null &&
|
|
urgent != null &&
|
|
full > warn &&
|
|
warn > urgent
|
|
) {
|
|
if (charge <= urgent) return 0;
|
|
if (charge <= warn) {
|
|
const t = (charge - urgent) / (warn - urgent);
|
|
return Math.round(Math.max(0, Math.min(1, t)) * 10);
|
|
}
|
|
if (charge >= full) return 100;
|
|
const t = (charge - warn) / (full - warn);
|
|
return Math.round((0.1 + Math.max(0, Math.min(1, t)) * 0.9) * 100);
|
|
}
|
|
if (percent != null && Number.isFinite(percent)) {
|
|
return Math.round(Math.max(0, Math.min(1, percent)) * 100);
|
|
}
|
|
if (charge != null && capacity != null && capacity > 0) {
|
|
const fallback = charge / capacity;
|
|
return Math.round(Math.max(0, Math.min(1, fallback)) * 100);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function computeBatteryState(record, sensors) {
|
|
if (!record) return null;
|
|
if (!sensors) return record.batteryState;
|
|
const config = record.meta?.battery || null;
|
|
const charge = sensors?.batteryChargeMah ?? null;
|
|
const capacity = sensors?.batteryCapacityMah ?? null;
|
|
const full = typeof config?.Full === 'number' ? config.Full : null;
|
|
const warn = typeof config?.Warn === 'number' ? config.Warn : null;
|
|
const urgent = typeof config?.Urgent === 'number' ? config.Urgent : null;
|
|
let percent = null;
|
|
if (charge != null && warn != null && full != null && full > warn) {
|
|
const span = full - warn;
|
|
percent = (charge - warn) / span;
|
|
} else if (charge != null && capacity != null && capacity > 0) {
|
|
percent = charge / capacity;
|
|
}
|
|
if (percent != null) {
|
|
percent = Math.max(0, Math.min(1, percent));
|
|
}
|
|
const percentDisplay = computeBatteryDisplayPercent({
|
|
charge,
|
|
full,
|
|
warn,
|
|
urgent,
|
|
percent,
|
|
capacity,
|
|
});
|
|
return {
|
|
charge,
|
|
capacity,
|
|
full,
|
|
warn,
|
|
urgent,
|
|
percent,
|
|
percentDisplay,
|
|
warnActive: Boolean(warn != null && charge != null && charge <= warn),
|
|
urgentActive: Boolean(urgent != null && charge != null && charge <= urgent),
|
|
updatedAt: Date.now(),
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
clampInt,
|
|
normalizePrivateSafety,
|
|
computeBatteryDisplayPercent,
|
|
computeBatteryState,
|
|
};
|