battery manager progress

This commit is contained in:
legop3
2025-11-20 13:48:26 -05:00
parent a4cc514b9a
commit d09c63ca7d
6 changed files with 132 additions and 14 deletions
+1
View File
@@ -20,4 +20,5 @@ require('./src/services/videoAuthService');
require('./src/services/videoSocketService');
require('./src/services/logStreamService');
require('./src/services/sessionService');
require('./src/services/batteryManager');
require('./src/services/httpServer');
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webui</title>
<script type="module" crossorigin src="/assets/index-DN5ZA6hs.js"></script>
<script type="module" crossorigin src="/assets/index-DBNvZqXA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DP0zs3Oi.css">
</head>
<body>
+112
View File
@@ -0,0 +1,112 @@
const logger = require('../globals/logger').child('batteryManager');
const { publishEvent } = require('./eventBus');
const { managerEvents, lockRover, rovers } = require('./roverManager');
const STATES = {
NORMAL: 'normal',
WARN: 'warn',
URGENT: 'urgent',
DOCKED: 'docked',
CHARGING: 'charging',
FULL: 'full',
LOCKED: 'locked',
};
const roverState = new Map(); // roverId -> state snapshot
function getState(roverId) {
if (!roverState.has(roverId)) {
roverState.set(roverId, {
lastPercent: null,
warned: false,
urgent: false,
docked: false,
batteryLocked: false,
});
}
return roverState.get(roverId);
}
function shouldLock(record) {
return record.lockReason == null || record.lockReason === 'battery';
}
function handleSensorEvent({ roverId, sensors, batteryState }) {
if (!roverId) return;
const record = rovers.get(roverId);
if (!record) return;
const state = getState(roverId);
const config = record.meta?.battery || {};
const lockable = shouldLock(record);
if (batteryState?.percent != null) {
state.lastPercent = batteryState.percent;
}
if (!state.warned && batteryState?.warnActive) {
state.warned = true;
publishEvent({
source: 'batteryManager',
type: 'battery.warn',
payload: { roverId, batteryState },
});
}
if (!state.urgent && batteryState?.urgentActive) {
state.urgent = true;
publishEvent({
source: 'batteryManager',
type: 'battery.urgent',
payload: { roverId, batteryState },
});
}
const chargingState = sensors?.chargingState?.code;
const chargingSources = sensors?.chargingSources;
const docked =
Boolean(chargingSources?.homeBase) ||
chargingState === 2 ||
chargingState === 3 ||
chargingState === 4;
if (docked && !state.docked) {
state.docked = true;
publishEvent({
source: 'batteryManager',
type: 'battery.docked',
payload: { roverId, batteryState },
});
} else if (!docked && state.docked) {
state.docked = false;
}
if (docked && lockable && !record.locked) {
logger.info('Auto-locking rover for charging', { roverId });
lockRover(roverId, true, { reason: 'battery' });
state.batteryLocked = true;
publishEvent({
source: 'batteryManager',
type: 'battery.locked',
payload: { roverId, batteryState },
});
}
const fullThreshold =
typeof config?.Full === 'number' ? config.Full : batteryState?.full ?? null;
const isFull =
(batteryState?.charge != null && fullThreshold != null && batteryState.charge >= fullThreshold) ||
(batteryState?.percent != null && batteryState.percent >= 0.99);
if (record.locked && record.lockReason === 'battery') {
if (isFull || !docked) {
logger.info('Unlocking rover after charge', { roverId, isFull, docked });
lockRover(roverId, false, { reason: 'battery' });
state.batteryLocked = false;
publishEvent({
source: 'batteryManager',
type: 'battery.unlocked',
payload: { roverId, batteryState },
});
}
}
}
managerEvents.on('sensor', handleSensorEvent);