feat: sync axis task state parity work

This commit is contained in:
wangdequan
2026-07-07 18:45:26 -04:00
parent 16484afce6
commit 8ef67f94c2
562 changed files with 175971 additions and 248 deletions

View File

@@ -43,6 +43,7 @@ import { createLinuxCncParityMatrix } from "../runtime/linuxcnc-parity-matrix.js
import { gmoccapyHalModel, resolveGmoccapyHardwareButton } from "../runtime/gmoccapy-hal-model.js";
import {
createLinuxCncTaskPolicyStatus,
deriveLinuxCncTaskState,
gateLinuxCncTaskAction,
normalizeLinuxCncTaskMode,
} from "./linuxcnc-task-policy.js";
@@ -166,15 +167,22 @@ const initialState = {
machine: {
powerOn: false,
estopActive: false,
motionEnabled: false,
taskState: "estop-reset",
mode: "manual",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
resumeInhibit: false,
manualPanel: "manual",
allHomed: false,
homed: [false, false, false, false, false],
noForceHoming: false,
homing: false,
homeState: "unhomed",
selectedJoint: 0,
jogAxis: "x",
jogIncrement: 1,
@@ -410,6 +418,7 @@ export function createSimulationStore(seed = {}) {
: seed.programAxisPreviewPath;
state = {
...state,
machine: normalizeMachineForLinuxCncTask(state.machine, state.runState),
programAxisPreviewPath: initialAxisPreviewPath,
preview: {
...state.preview,
@@ -1309,13 +1318,18 @@ export function createSimulationStore(seed = {}) {
...state.machine,
powerOn: false,
estopActive: true,
motionEnabled: false,
taskState: "estop",
manualPanel: "manual",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "estopped",
taskHalPauseLock: null,
kinsType: "identity",
rtcpState: "off",
feed: {
@@ -1330,8 +1344,29 @@ export function createSimulationStore(seed = {}) {
spindle: {
...stoppedSpindleState(state.spindle),
},
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
operatorMessage: "emergency stop active",
});
if (state.taskHalRuntime?.loaded) {
runTaskHalCommandSequence([
{ type: "EMC_TASK_SET_STATE", state: "ESTOP" },
], {
operatorMessage: "task/HAL emergency stop active",
preserveMachine: {
...state.machine,
powerOn: false,
estopActive: true,
motionEnabled: false,
taskState: "estop",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
}).catch(() => {});
}
break;
case "RESET":
setState({
@@ -1339,14 +1374,19 @@ export function createSimulationStore(seed = {}) {
...state.machine,
powerOn: false,
estopActive: false,
motionEnabled: false,
taskState: "estop-reset",
manualPanel: "manual",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
resetCount: state.machine.resetCount + 1,
},
runState: "idle",
taskHalPauseLock: null,
kinsType: "identity",
rtcpState: "off",
coolant: {
@@ -1357,8 +1397,29 @@ export function createSimulationStore(seed = {}) {
spindle: {
...stoppedSpindleState(state.spindle),
},
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
operatorMessage: "estop reset; machine off",
});
if (state.taskHalRuntime?.loaded) {
runTaskHalCommandSequence([
{ type: "EMC_TASK_SET_STATE", state: "ESTOP_RESET" },
], {
operatorMessage: "task/HAL estop reset; machine off",
preserveMachine: {
...state.machine,
powerOn: false,
estopActive: false,
motionEnabled: false,
taskState: "estop-reset",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
}).catch(() => {});
}
break;
case "SET_MODE":
{
@@ -1375,27 +1436,46 @@ export function createSimulationStore(seed = {}) {
? "manual"
: null;
if (state.taskHalRuntime?.loaded) {
const manualModePatch = mode === "manual"
? {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
}
: {};
const requestedMachine = {
...state.machine,
mode,
manualPanel,
...manualModePatch,
};
setState({
machine: requestedMachine,
operatorMessage: `task/HAL mode ${mode} requested`,
});
runTaskHalCommandSequence([
{ type: "EMC_TASK_SET_MODE", mode: mode.toUpperCase() },
], {
operatorMessage: `task/HAL mode ${mode}`,
preserveMachine: {
...state.machine,
mode,
manualPanel,
},
preserveMachine: requestedMachine,
}).catch(() => {});
break;
}
setState({
machine: {
...state.machine,
mode,
manualPanel,
interpState: mode === "manual" ? "idle" : state.machine.interpState,
interpResumeState: mode === "manual" ? "idle" : state.machine.interpResumeState,
taskPaused: mode === "manual" ? false : state.machine.taskPaused,
},
machine: {
...state.machine,
mode,
manualPanel,
interpState: mode === "manual" ? "idle" : state.machine.interpState,
interpResumeState: mode === "manual" ? "idle" : state.machine.interpResumeState,
taskPaused: mode === "manual" ? false : state.machine.taskPaused,
motionPaused: mode === "manual" ? false : state.machine.motionPaused,
singleStepping: mode === "manual" ? false : state.machine.singleStepping,
motionStepping: mode === "manual" ? false : state.machine.motionStepping,
},
runState: mode === "manual" && state.runState === "running" ? "stopped" : state.runState,
operatorMessage: `mode ${mode}`,
});
@@ -1473,6 +1553,9 @@ export function createSimulationStore(seed = {}) {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
axisPose: nextAxisPose,
runState: "jogging",
@@ -1496,6 +1579,9 @@ export function createSimulationStore(seed = {}) {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
operatorMessage: `task/HAL jog ${axis.toUpperCase()} ${direction > 0 ? "+" : "-"}${increment}`,
}).catch(() => {});
@@ -1580,6 +1666,9 @@ export function createSimulationStore(seed = {}) {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
axisPose: initialAxisPose,
runState: "idle",
@@ -1615,13 +1704,15 @@ export function createSimulationStore(seed = {}) {
...state.machine,
mode: "auto",
manualPanel: null,
interpState: "reading",
interpResumeState: "reading",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "running",
taskHalPauseLock: null,
operatorMessage: "task/HAL program run requested",
operatorMessage: "task/HAL program run requested; waiting for status",
});
runValidatedTaskHalProgramRun().catch(() => {});
break;
@@ -1644,6 +1735,9 @@ export function createSimulationStore(seed = {}) {
interpState: playback.complete ? "idle" : "reading",
interpResumeState: playback.complete ? "idle" : "reading",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: playback.complete ? "complete" : "running",
taskHalPauseLock: null,
@@ -1687,6 +1781,9 @@ export function createSimulationStore(seed = {}) {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
})
.then(() => {
@@ -1705,6 +1802,9 @@ export function createSimulationStore(seed = {}) {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "stopped",
taskHalPauseLock: null,
@@ -1764,6 +1864,8 @@ export function createSimulationStore(seed = {}) {
interpState: "paused",
taskPaused: true,
motionPaused: true,
singleStepping: false,
motionStepping: false,
};
const pauseLock = createTaskHalPauseLock(state, "task-hal-plan-pause");
setState({
@@ -1803,6 +1905,8 @@ export function createSimulationStore(seed = {}) {
interpState: "paused",
taskPaused: true,
motionPaused: true,
singleStepping: false,
motionStepping: false,
},
runState: "paused",
taskHalPauseLock: pauseLock,
@@ -1853,10 +1957,12 @@ export function createSimulationStore(seed = {}) {
interpResumeState: resumeState,
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
};
setState({
machine: resumedMachine,
runState: resumeState === "reading" ? "running" : "idle",
runState: resumeState === "reading" || resumeState === "waiting" ? "running" : "idle",
taskHalPauseLock: null,
operatorMessage: "task/HAL resume requested",
});
@@ -1885,8 +1991,10 @@ export function createSimulationStore(seed = {}) {
interpResumeState: resumeState,
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: resumeState === "reading" ? "running" : "idle",
runState: resumeState === "reading" || resumeState === "waiting" ? "running" : "idle",
taskHalPauseLock: null,
operatorMessage: "program resumed",
});
@@ -1921,6 +2029,8 @@ export function createSimulationStore(seed = {}) {
interpState: "paused",
taskPaused: true,
motionPaused: true,
singleStepping: true,
motionStepping: true,
};
setState({
machine: steppedMachine,
@@ -1965,6 +2075,8 @@ export function createSimulationStore(seed = {}) {
interpState: "paused",
taskPaused: true,
motionPaused: true,
singleStepping: true,
motionStepping: true,
},
runState: "stepping",
taskHalPauseLock: null,
@@ -2002,6 +2114,9 @@ export function createSimulationStore(seed = {}) {
interpState: playback.complete ? "idle" : "reading",
interpResumeState: playback.complete ? "idle" : "reading",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "running",
...playbackPatch,
@@ -2022,16 +2137,24 @@ export function createSimulationStore(seed = {}) {
break;
}
const homeAxisPose = homeAxisPoseForState(state);
const homedFalse = createHomedArrayForState(state, false);
const homedTrue = createHomedArrayForState(state, true);
if (state.taskHalRuntime?.loaded) {
setState({
machine: {
...state.machine,
mode: "manual",
manualPanel: "manual",
allHomed: true,
allHomed: false,
homed: homedFalse,
homing: true,
homeState: "homing",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "idle",
axisPose: homeAxisPose,
@@ -2044,9 +2167,11 @@ export function createSimulationStore(seed = {}) {
operatorMessage: "task/HAL machine homed",
preserveAxisPose: homeAxisPose,
preserveMachine: {
...state.machine,
manualPanel: "manual",
allHomed: true,
homed: homedTrue,
homing: false,
homeState: "homed",
},
}).catch(() => {});
break;
@@ -2057,9 +2182,15 @@ export function createSimulationStore(seed = {}) {
mode: "manual",
manualPanel: "manual",
allHomed: true,
homed: homedTrue,
homing: false,
homeState: "homed",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "idle",
axisPose: homeAxisPose,
@@ -2073,6 +2204,9 @@ export function createSimulationStore(seed = {}) {
machine: {
...state.machine,
allHomed: false,
homed: createHomedArrayForState(state, false),
homing: false,
homeState: "unhomed",
},
operatorMessage: "machine unhomed",
});
@@ -2695,11 +2829,13 @@ export function createSimulationStore(seed = {}) {
...state.machine,
mode: "auto",
manualPanel: null,
interpState: "reading",
interpResumeState: "reading",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: "running",
taskHalPauseLock: null,
operatorMessage: "RUN ready: motion plan source ready; preparing task/HAL session",
});
@@ -3806,7 +3942,13 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
: rawTaskState;
const taskMode = statusPaused
? programControlModeForMachine(state.machine)
: normalizeLinuxCncTaskMode(preserveMachine?.mode || ui.taskMode || task.mode || state.machine.mode);
: normalizeLinuxCncTaskMode(
preserveMachine?.mode ||
(preserveMachine?.allHomed && state.machine.mode !== "manual" ? state.machine.mode : null) ||
ui.taskMode ||
task.mode ||
state.machine.mode,
);
const manualPanel = taskMode === "manual"
? (preserveMachine?.manualPanel || state.machine.manualPanel || "manual")
: null;
@@ -3815,12 +3957,40 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
: Object.hasOwn(preserveMachine || {}, "interpState")
? normalizeTaskHalInterpState(preserveMachine.interpState)
: statusInterpState;
const allHomed = Boolean(preserveMachine?.allHomed ?? state.machine.allHomed);
const allHomed = Boolean(
task.allHomed === true ||
ui.allHomed === true ||
preserveMachine?.allHomed ||
state.machine.allHomed,
);
const homed = normalizeHomedArrayForState(
state,
ui.homed || task.homed || preserveMachine?.homed || state.machine.homed,
allHomed,
);
const homing = Object.hasOwn(task, "homing")
? task.homing === true
: Object.hasOwn(ui, "homing")
? ui.homing === true
: Boolean(preserveMachine?.homing);
const homeState = normalizeTaskHalHomeState(
ui.homeState ||
task.homeState ||
preserveMachine?.homeState ||
state.machine.homeState ||
(allHomed ? "homed" : "unhomed"),
);
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
const motionPaused = statusMotionPaused || preserveMachine?.motionPaused === true;
const taskPaused = statusTaskPaused || preserveMachine?.taskPaused === true;
const paused = interpState === "paused" || motionPaused || taskPaused;
const singleStepping = ui.singleStepping === true ||
task.singleStepping === true ||
preserveMachine?.singleStepping === true;
const motionStepping = ui.motionStepping === true ||
motion.stepping === true ||
preserveMachine?.motionStepping === true;
const interpResumeState = normalizeTaskHalInterpState(
ui.interpResumeState ||
task.interpResumeState ||
@@ -3955,6 +4125,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
...state.machine,
powerOn: taskState === "on",
estopActive: taskState === "estop",
motionEnabled: taskState === "on",
taskState,
mode: taskMode,
manualPanel,
@@ -3962,7 +4133,12 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
interpResumeState,
taskPaused: paused,
motionPaused,
singleStepping,
motionStepping,
allHomed,
homed,
homing,
homeState,
noForceHoming: Boolean(state.machine.noForceHoming),
},
runState,
@@ -4035,17 +4211,26 @@ function programControlModeForMachine(machine = {}) {
}
function createPowerToggleMachinePatch(machine = {}, turningOn = false) {
const poweredOffHomed = Array.isArray(machine.homed)
? machine.homed.map(() => false)
: [false, false, false, false, false];
return {
...machine,
powerOn: turningOn,
estopActive: false,
taskState: turningOn ? "on" : "off",
motionEnabled: turningOn,
taskState: turningOn ? "on" : "estop-reset",
manualPanel: "manual",
allHomed: turningOn ? Boolean(machine.allHomed) : false,
homed: turningOn ? normalizeHomedArrayForMachine(machine, Boolean(machine.allHomed)) : poweredOffHomed,
homing: false,
homeState: turningOn ? (machine.allHomed ? "homed" : machine.homeState || "unhomed") : "unhomed",
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
};
}
@@ -4094,6 +4279,9 @@ function createStoppedProgramStatePatch(state, {
interpState: "idle",
interpResumeState: "idle",
taskPaused: false,
motionPaused: false,
singleStepping: false,
motionStepping: false,
},
runState: reason === "aborted" ? "stopped" : reason,
taskHalPauseLock: null,
@@ -4108,6 +4296,7 @@ function createStoppedProgramStatePatch(state, {
...state.feed,
currentVelocity: 0,
},
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
operatorMessage,
};
}
@@ -4615,7 +4804,6 @@ function normalizeTaskHalTaskState(value) {
const state = String(value || "").toLowerCase().replaceAll("_", "-");
if (state === "on") return "on";
if (state === "estop") return "estop";
if (state === "off") return "off";
return "estop-reset";
}
@@ -4627,6 +4815,41 @@ function normalizeTaskHalInterpState(value) {
return "idle";
}
function normalizeTaskHalHomeState(value) {
const state = String(value || "").toLowerCase().replaceAll("_", "-");
if (state === "homing") return "homing";
if (state === "homed") return "homed";
if (state === "no-force-homing") return "no-force-homing";
return "unhomed";
}
function jointCountForState(state = {}) {
return Math.max(
Number(state.profile?.jointConfig?.length || 0),
Number(state.profile?.joints?.length || 0),
Array.isArray(state.machine?.homed) ? state.machine.homed.length : 0,
5,
);
}
function createHomedArrayForState(state = {}, value = false) {
return Array.from({ length: jointCountForState(state) }, () => Boolean(value));
}
function normalizeHomedArrayForState(state = {}, source = [], allHomed = false) {
const count = jointCountForState(state);
if (allHomed) return Array.from({ length: count }, () => true);
if (!Array.isArray(source)) return Array.from({ length: count }, () => false);
return Array.from({ length: count }, (_, index) => Boolean(source[index]));
}
function normalizeHomedArrayForMachine(machine = {}, allHomed = false, fallbackCount = 5) {
const source = Array.isArray(machine.homed) ? machine.homed : [];
const count = Math.max(source.length, fallbackCount);
if (allHomed) return Array.from({ length: count }, () => true);
return Array.from({ length: count }, (_, index) => Boolean(source[index]));
}
function kinsTypeFromSwitchkinsTypeValue(state, value) {
const numeric = Number(value);
if (!Number.isFinite(numeric) || numeric === 0) return "identity";
@@ -4639,33 +4862,37 @@ function canMoveMachine(state) {
}
function normalizeMachineForLinuxCncTask(machine = {}, runState = "idle") {
const taskState = machine.estopActive
? "estop"
: machine.taskState === "on" || machine.powerOn
? "on"
: machine.taskState === "off"
? "off"
: "estop-reset";
const mode = normalizeLinuxCncTaskMode(machine.mode);
const interpState = machine.interpState
const baseMachine = { ...initialState.machine, ...machine };
const taskState = deriveLinuxCncTaskState(baseMachine);
const mode = normalizeLinuxCncTaskMode(baseMachine.mode);
const interpState = baseMachine.interpState
|| (runState === "running" ? "reading" : runState === "paused" || runState === "stepping" ? "paused" : "idle");
const manualPanel = mode === "manual"
? (machine.manualPanel === "jog" ? "jog" : "manual")
? (baseMachine.manualPanel === "jog" ? "jog" : "manual")
: null;
const homed = normalizeHomedArrayForMachine(baseMachine, Boolean(baseMachine.allHomed));
const allHomed = Boolean(baseMachine.allHomed || homed.every(Boolean));
return {
...machine,
...baseMachine,
taskState,
mode,
manualPanel,
interpState,
interpResumeState: machine.interpResumeState || (interpState === "paused" ? "reading" : interpState),
taskPaused: Boolean(machine.taskPaused || interpState === "paused"),
motionPaused: Boolean(machine.motionPaused || machine.taskPaused || interpState === "paused"),
interpResumeState: baseMachine.interpResumeState || (interpState === "paused" ? "reading" : interpState),
taskPaused: Boolean(baseMachine.taskPaused || interpState === "paused"),
motionPaused: Boolean(baseMachine.motionPaused || baseMachine.taskPaused || interpState === "paused"),
singleStepping: Boolean(baseMachine.singleStepping),
motionStepping: Boolean(baseMachine.motionStepping),
resumeInhibit: Boolean(baseMachine.resumeInhibit),
motionEnabled: taskState === "on",
powerOn: taskState === "on",
estopActive: taskState === "estop",
allHomed: Boolean(machine.allHomed),
noForceHoming: Boolean(machine.noForceHoming),
allHomed,
homed,
noForceHoming: Boolean(baseMachine.noForceHoming),
homing: Boolean(baseMachine.homing),
homeState: allHomed ? "homed" : baseMachine.homeState || "unhomed",
};
}