mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
better dock resolving
This commit is contained in:
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<!-- site-metadata:inject -->
|
<!-- site-metadata:inject -->
|
||||||
<!-- analytics:inject -->
|
<!-- analytics:inject -->
|
||||||
<script type="module" crossorigin src="/assets/index-DtnnIgZU.js"></script>
|
<script type="module" crossorigin src="/assets/index-BNuqn1jZ.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D2qTDWX0.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D2qTDWX0.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
||||||
import { dockTelemetryEqual, selectDockTelemetry } from '../../context/telemetryViews.js';
|
import { dockTelemetryEqual, isDockedChargingState, resolveDocked, selectDockTelemetry } from '../../context/telemetryViews.js';
|
||||||
|
|
||||||
export function deriveDriveDockStateFromTelemetry(dockTelemetry) {
|
export function deriveDriveDockStateFromTelemetry(dockTelemetry) {
|
||||||
const oiLabel = dockTelemetry?.oiModeLabel || 'Unknown';
|
const oiLabel = dockTelemetry?.oiModeLabel || 'Unknown';
|
||||||
const oiNormalized = oiLabel.toLowerCase();
|
const oiNormalized = oiLabel.toLowerCase();
|
||||||
const chargingLabel = dockTelemetry?.chargingStateLabel || '';
|
const chargingLabel = dockTelemetry?.chargingStateLabel || '';
|
||||||
const docked = Boolean(dockTelemetry?.homeBase);
|
const docked = resolveDocked(dockTelemetry);
|
||||||
const charging = docked && chargingLabel.toLowerCase() !== 'not charging' && chargingLabel !== '';
|
const charging = isDockedChargingState(chargingLabel);
|
||||||
const driving = oiNormalized === 'full';
|
const driving = oiNormalized === 'full';
|
||||||
const dockedNotCharging = docked && !charging;
|
const dockedNotCharging = docked && !charging;
|
||||||
const dockingInProgress = !docked && !charging && oiNormalized === 'passive';
|
const dockingInProgress = !docked && !charging && oiNormalized === 'passive';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createElement, useMemo } from 'react';
|
|||||||
import { FaArrowDown, FaArrowUp, FaBatteryHalf, FaBolt, FaExclamationTriangle, FaMemory, FaThermometerHalf, FaWifi } from 'react-icons/fa';
|
import { FaArrowDown, FaArrowUp, FaBatteryHalf, FaBolt, FaExclamationTriangle, FaMemory, FaThermometerHalf, FaWifi } from 'react-icons/fa';
|
||||||
import { useSessionSelector } from '../../../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../../../context/SessionContext.jsx';
|
||||||
import { useTelemetrySelector } from '../../../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../../../context/TelemetryContext.jsx';
|
||||||
import { hostStatsEqual, selectHostStats, selectSpectatorTelemetry, spectatorTelemetryEqual } from '../../../../context/telemetryViews.js';
|
import { hostStatsEqual, resolveDocked, selectHostStats, selectSpectatorTelemetry, spectatorTelemetryEqual } from '../../../../context/telemetryViews.js';
|
||||||
import CornerPodToggle from './CornerPodToggle.jsx';
|
import CornerPodToggle from './CornerPodToggle.jsx';
|
||||||
import ExpansionToggle from './ExpansionToggle.jsx';
|
import ExpansionToggle from './ExpansionToggle.jsx';
|
||||||
import usePodVisibility from './usePodVisibility.js';
|
import usePodVisibility from './usePodVisibility.js';
|
||||||
@@ -101,7 +101,7 @@ export default function TopRightPod({ roverId }) {
|
|||||||
const memoryTone = memoryUsed >= 90 ? 'bg-red-400' : memoryUsed >= 75 ? 'bg-amber-400' : 'bg-violet-400';
|
const memoryTone = memoryUsed >= 90 ? 'bg-red-400' : memoryUsed >= 75 ? 'bg-amber-400' : 'bg-violet-400';
|
||||||
const download = finite(wifi.downloadMbps);
|
const download = finite(wifi.downloadMbps);
|
||||||
const upload = finite(wifi.uploadMbps);
|
const upload = finite(wifi.uploadMbps);
|
||||||
const docked = Boolean(electrical?.homeBase);
|
const docked = resolveDocked(electrical);
|
||||||
const warningMessage = urgentBattery ? 'BATTERY CRITICAL, DOCK NOW' : 'Battery low, please dock soon.';
|
const warningMessage = urgentBattery ? 'BATTERY CRITICAL, DOCK NOW' : 'Battery low, please dock soon.';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { FaChargingStation, FaChevronDown } from 'react-icons/fa';
|
|||||||
import { useControlActions, useControlSelector } from '../../../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../../../controls/index.js';
|
||||||
import { formatKeyLabel } from '../../../../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../../../controls/keymapUtils.js';
|
||||||
import { useTelemetrySelector } from '../../../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../../../context/TelemetryContext.jsx';
|
||||||
import { dockTelemetryEqual, selectDockTelemetry } from '../../../../context/telemetryViews.js';
|
import { dockTelemetryEqual, resolveDocked, selectDockTelemetry } from '../../../../context/telemetryViews.js';
|
||||||
import { useManualDockAssist } from '../../../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
import { useSettingsNamespace } from '../../../../settings/index.js';
|
import { useSettingsNamespace } from '../../../../settings/index.js';
|
||||||
import useCanControlRover from '../../../../hooks/useCanControlRover.js';
|
import useCanControlRover from '../../../../hooks/useCanControlRover.js';
|
||||||
@@ -256,7 +256,7 @@ export default function DockingHud({ roverId }) {
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [showUndockTransition, setShowUndockTransition] = useState(false);
|
const [showUndockTransition, setShowUndockTransition] = useState(false);
|
||||||
|
|
||||||
const docked = Boolean(dockTelemetry?.homeBase);
|
const docked = resolveDocked(dockTelemetry);
|
||||||
const oiMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase();
|
const oiMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase();
|
||||||
// The established UI contract treats exactly passive + undocked as the Roomba's
|
// The established UI contract treats exactly passive + undocked as the Roomba's
|
||||||
// autonomous docking attempt. Unknown telemetry must not fabricate that state.
|
// autonomous docking attempt. Unknown telemetry must not fabricate that state.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { FaBullhorn, FaCrosshairs, FaLightbulb } from 'react-icons/fa';
|
|||||||
import './mobileControls.css';
|
import './mobileControls.css';
|
||||||
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
||||||
import { dockTelemetryEqual, selectDockTelemetry } from '../../context/telemetryViews.js';
|
import { dockTelemetryEqual, resolveDocked, selectDockTelemetry } from '../../context/telemetryViews.js';
|
||||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
import useCanControlRover from '../../hooks/useCanControlRover.js';
|
import useCanControlRover from '../../hooks/useCanControlRover.js';
|
||||||
import HornControl from '../HornControl/index.jsx';
|
import HornControl from '../HornControl/index.jsx';
|
||||||
@@ -37,7 +37,7 @@ function AuxColumnContent() {
|
|||||||
// column. Dock and OI state are applied separately only where the hardware
|
// column. Dock and OI state are applied separately only where the hardware
|
||||||
// command itself depends on the Roomba being able to drive.
|
// command itself depends on the Roomba being able to drive.
|
||||||
const controlsDisabled = !roverId || !canControl;
|
const controlsDisabled = !roverId || !canControl;
|
||||||
const docked = Boolean(dockTelemetry?.homeBase);
|
const docked = resolveDocked(dockTelemetry);
|
||||||
const drivingMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase() === 'full';
|
const drivingMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase() === 'full';
|
||||||
const vacuumDisabled = controlsDisabled
|
const vacuumDisabled = controlsDisabled
|
||||||
|| docked
|
|| docked
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FaChargingStation } from 'react-icons/fa';
|
|||||||
import { useControlSelector } from '../../controls/index.js';
|
import { useControlSelector } from '../../controls/index.js';
|
||||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
||||||
import { dockTelemetryEqual, selectDockTelemetry } from '../../context/telemetryViews.js';
|
import { dockTelemetryEqual, resolveDocked, selectDockTelemetry } from '../../context/telemetryViews.js';
|
||||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
import useCanControlRover from '../../hooks/useCanControlRover.js';
|
import useCanControlRover from '../../hooks/useCanControlRover.js';
|
||||||
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
||||||
@@ -22,7 +22,7 @@ function MovementColumnContent({ layout }) {
|
|||||||
});
|
});
|
||||||
const batteryUrgent = Boolean(batteryState?.urgentActive);
|
const batteryUrgent = Boolean(batteryState?.urgentActive);
|
||||||
const batteryLow = Boolean(batteryState?.warnActive || batteryUrgent);
|
const batteryLow = Boolean(batteryState?.warnActive || batteryUrgent);
|
||||||
const docked = Boolean(dockTelemetry?.homeBase);
|
const docked = resolveDocked(dockTelemetry);
|
||||||
const drivingMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase() === 'full';
|
const drivingMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase() === 'full';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -37,6 +37,27 @@ const EMPTY_MAIN_BRUSH_AUDIO = Object.freeze({
|
|||||||
mainBrushOvercurrent: false,
|
mainBrushOvercurrent: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// These labels represent an active charging relationship reported by the
|
||||||
|
// Roomba. That is independently useful dock evidence: some already-docked
|
||||||
|
// rovers report charging before the home-base contact bit changes again.
|
||||||
|
const DOCKED_CHARGING_STATES = new Set([
|
||||||
|
'reconditioning charging',
|
||||||
|
'full charging',
|
||||||
|
'trickle charging',
|
||||||
|
'waiting',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function isDockedChargingState(chargingStateLabel) {
|
||||||
|
return DOCKED_CHARGING_STATES.has(String(chargingStateLabel || '').trim().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDocked(dockTelemetry) {
|
||||||
|
// Dock contact and active charging are complementary sensor evidence, not
|
||||||
|
// fallback state. Treating either as sufficient prevents UI controls from
|
||||||
|
// claiming a charging rover should be driven onto a dock it already occupies.
|
||||||
|
return Boolean(dockTelemetry?.homeBase) || isDockedChargingState(dockTelemetry?.chargingStateLabel);
|
||||||
|
}
|
||||||
|
|
||||||
function bucketNumber(value, step) {
|
function bucketNumber(value, step) {
|
||||||
// Visual widgets do not benefit from repainting for tiny analog jitter. The
|
// Visual widgets do not benefit from repainting for tiny analog jitter. The
|
||||||
// bucket step intentionally applies only to display selectors; raw telemetry
|
// bucket step intentionally applies only to display selectors; raw telemetry
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
import { useControlActions, useControlSelector } from '../../controls/index.js';
|
||||||
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
|
||||||
import { dockTelemetryEqual, selectDockTelemetry } from '../../context/telemetryViews.js';
|
import { dockTelemetryEqual, isDockedChargingState, resolveDocked, selectDockTelemetry } from '../../context/telemetryViews.js';
|
||||||
|
|
||||||
export function useManualDockAssist(options = {}) {
|
export function useManualDockAssist(options = {}) {
|
||||||
const { manageLifecycle = false } = options;
|
const { manageLifecycle = false } = options;
|
||||||
@@ -10,8 +10,10 @@ export function useManualDockAssist(options = {}) {
|
|||||||
const actions = useControlActions();
|
const actions = useControlActions();
|
||||||
const dockTelemetry = useTelemetrySelector(roverId, selectDockTelemetry, dockTelemetryEqual);
|
const dockTelemetry = useTelemetrySelector(roverId, selectDockTelemetry, dockTelemetryEqual);
|
||||||
const chargingLabel = dockTelemetry.chargingStateLabel || '';
|
const chargingLabel = dockTelemetry.chargingStateLabel || '';
|
||||||
const docked = Boolean(dockTelemetry.homeBase);
|
const docked = resolveDocked(dockTelemetry);
|
||||||
const charging = docked && chargingLabel.toLowerCase() !== 'not charging' && chargingLabel !== '';
|
// Use the same explicit charging labels that can establish dock presence.
|
||||||
|
// A non-empty fault or unknown label must not masquerade as active charging.
|
||||||
|
const charging = isDockedChargingState(chargingLabel);
|
||||||
const wasDockedRef = useRef(false);
|
const wasDockedRef = useRef(false);
|
||||||
|
|
||||||
const enterAssist = useCallback(() => {
|
const enterAssist = useCallback(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user