docs: record pause wasm implementation plan
This commit is contained in:
@@ -79,6 +79,9 @@ export function buildVismachModelState(state = {}) {
|
||||
}
|
||||
|
||||
function resolveAxisPose(state) {
|
||||
if (state.runState === "paused" || state.machine?.interpState === "paused" || state.machine?.taskPaused === true) {
|
||||
return state.axisPose || {};
|
||||
}
|
||||
if (state.programRuntimeFeedback?.axisPose) return state.programRuntimeFeedback.axisPose;
|
||||
if (state.taskHalStatus?.ui?.axisPose) return state.taskHalStatus.ui.axisPose;
|
||||
return state.axisPose || {};
|
||||
|
||||
@@ -99,7 +99,7 @@ export function createLinuxCncTaskPolicyStatus(state) {
|
||||
canRunAutoStrict: taskState === "on" && taskMode === "auto" && (allHomed || noForceHoming) &&
|
||||
interpIdle && iniLoaded && machineFileStaged && machineFileOpened && taskHalRuntimeReady,
|
||||
canExecuteMdi: taskState === "on" && taskMode === "mdi" && (allHomed || noForceHoming),
|
||||
canPause: taskState === "on" && (taskMode === "auto" || taskMode === "mdi"),
|
||||
canPause: taskState === "on" && taskMode === "auto" && (interpState === "reading" || interpState === "waiting"),
|
||||
canResume: taskState === "on" && (taskMode === "auto" || taskMode === "mdi") && interpState === "paused",
|
||||
canAbort: true,
|
||||
canSpindle: taskState === "on",
|
||||
@@ -116,9 +116,6 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
|
||||
switch (type) {
|
||||
case "TOGGLE_POWER":
|
||||
if (status.taskState === "estop") {
|
||||
return block(status, "power on blocked: reset estop first");
|
||||
}
|
||||
return allow(status);
|
||||
case "SET_MODE":
|
||||
return gateMode(status, requestedMode);
|
||||
@@ -164,10 +161,7 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
case "PAUSE":
|
||||
if (status.taskState !== "on") return block(status, "pause blocked: machine must be on");
|
||||
{
|
||||
const taskIsRunning = status.runState === "running" || status.runState === "stepping";
|
||||
const interpIsRunning = status.interpState === "reading" || status.interpState === "waiting";
|
||||
const interpCanPauseResume = status.interpState !== "idle" || taskIsRunning;
|
||||
const pauseReady = action.source === "pauseresume" ? interpCanPauseResume : (interpIsRunning || taskIsRunning);
|
||||
const requiredModeMessage = action.source === "pauseresume"
|
||||
? "pause blocked: task mode must be auto or MDI"
|
||||
: "pause blocked: task mode must be auto";
|
||||
@@ -176,18 +170,18 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
: "pause blocked: interpreter is not running";
|
||||
|
||||
if (action.source === "pauseresume") {
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") {
|
||||
return block(status, requiredModeMessage);
|
||||
}
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") return block(status, requiredModeMessage);
|
||||
if (status.interpState === "idle") return block(status, notRunningMessage);
|
||||
} else if (status.taskMode !== "auto") {
|
||||
return block(status, requiredModeMessage);
|
||||
}
|
||||
if (!pauseReady) return block(status, notRunningMessage);
|
||||
if (action.source !== "pauseresume" && status.taskMode !== "auto") return block(status, requiredModeMessage);
|
||||
if (action.source !== "pauseresume" && !interpIsRunning) return block(status, notRunningMessage);
|
||||
return allow(status);
|
||||
}
|
||||
case "RESUME":
|
||||
if (status.taskState !== "on") return block(status, "resume blocked: machine must be on");
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") {
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi" && status.runState !== "paused") {
|
||||
return block(status, "resume blocked: task mode must be auto or MDI");
|
||||
}
|
||||
if (status.interpState !== "paused") return block(status, "resume blocked: interpreter is not paused");
|
||||
|
||||
@@ -230,6 +230,7 @@ const initialState = {
|
||||
taskHalExecutionPending: false,
|
||||
taskHalExecutionSequence: 0,
|
||||
taskHalStatusLoop: createTaskHalStatusLoopState(),
|
||||
taskHalPauseLock: null,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
interpreterExecutionPending: false,
|
||||
@@ -1147,6 +1148,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
taskHalFallbackReason: action.error,
|
||||
taskHalExecutionPending: false,
|
||||
taskHalPauseLock: null,
|
||||
taskHalStatusLoop: {
|
||||
...state.taskHalStatusLoop,
|
||||
active: false,
|
||||
@@ -1269,50 +1271,34 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({ operatorMessage: gate.operatorMessage });
|
||||
break;
|
||||
}
|
||||
const turningOff = state.machine.taskState === "on" || state.machine.powerOn;
|
||||
const turningOn = state.machine.taskState === "estop-reset";
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
const nextMachine = createPowerToggleMachinePatch(state.machine, turningOn);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: !turningOff,
|
||||
estopActive: false,
|
||||
taskState: turningOff ? "estop-reset" : "on",
|
||||
manualPanel: "manual",
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: turningOff ? "powered-off" : "idle",
|
||||
kinsType: turningOff ? "identity" : state.kinsType,
|
||||
rtcpState: turningOff ? "off" : state.rtcpState,
|
||||
feed: turningOff ? { ...state.feed, currentVelocity: 0 } : state.feed,
|
||||
coolant: turningOff ? { ...state.coolant, flood: false, mist: false } : state.coolant,
|
||||
spindle: turningOff ? stoppedSpindleState(state.spindle) : state.spindle,
|
||||
operatorMessage: turningOff ? "task/HAL machine power off" : "task/HAL machine power on",
|
||||
machine: nextMachine,
|
||||
runState: turningOn ? "idle" : "powered-off",
|
||||
kinsType: turningOn ? state.kinsType : "identity",
|
||||
rtcpState: turningOn ? state.rtcpState : "off",
|
||||
feed: turningOn ? state.feed : { ...state.feed, currentVelocity: 0 },
|
||||
coolant: turningOn ? state.coolant : { ...state.coolant, flood: false, mist: false },
|
||||
spindle: turningOn ? state.spindle : stoppedSpindleState(state.spindle),
|
||||
operatorMessage: turningOn ? "task/HAL machine power on" : "task/HAL machine power off",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: turningOff ? "ESTOP_RESET" : "ON" },
|
||||
], { operatorMessage: turningOff ? "task/HAL machine power off" : "task/HAL machine power on" }).catch(() => {});
|
||||
{ type: "EMC_TASK_SET_STATE", state: turningOn ? "ON" : "OFF" },
|
||||
], { operatorMessage: turningOn ? "task/HAL machine power on" : "task/HAL machine power off" }).catch(() => {});
|
||||
break;
|
||||
}
|
||||
const nextMachine = createPowerToggleMachinePatch(state.machine, turningOn);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: !turningOff,
|
||||
estopActive: false,
|
||||
taskState: turningOff ? "estop-reset" : "on",
|
||||
manualPanel: "manual",
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: turningOff ? "powered-off" : "idle",
|
||||
kinsType: turningOff ? "identity" : state.kinsType,
|
||||
rtcpState: turningOff ? "off" : state.rtcpState,
|
||||
feed: turningOff ? { ...state.feed, currentVelocity: 0 } : state.feed,
|
||||
coolant: turningOff ? { ...state.coolant, flood: false, mist: false } : state.coolant,
|
||||
spindle: turningOff ? stoppedSpindleState(state.spindle) : state.spindle,
|
||||
operatorMessage: turningOff ? "machine power off" : "machine power on",
|
||||
machine: nextMachine,
|
||||
runState: turningOn ? "idle" : "powered-off",
|
||||
kinsType: turningOn ? state.kinsType : "identity",
|
||||
rtcpState: turningOn ? state.rtcpState : "off",
|
||||
feed: turningOn ? state.feed : { ...state.feed, currentVelocity: 0 },
|
||||
coolant: turningOn ? state.coolant : { ...state.coolant, flood: false, mist: false },
|
||||
spindle: turningOn ? state.spindle : stoppedSpindleState(state.spindle),
|
||||
operatorMessage: turningOn ? "machine power on" : "machine power off",
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -1633,6 +1619,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "running",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "task/HAL program run requested",
|
||||
});
|
||||
runValidatedTaskHalProgramRun().catch(() => {});
|
||||
@@ -1658,6 +1645,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: playback.complete ? "complete" : "running",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programLineExecution: createProgramLineExecutionPatch(state.programLineExecution, playback.runtimeFeedback, {
|
||||
status: playback.complete ? "done" : "running",
|
||||
@@ -1718,6 +1706,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "stopped",
|
||||
taskHalPauseLock: null,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
@@ -1766,19 +1755,24 @@ export function createSimulationStore(seed = {}) {
|
||||
stopTaskHalStatusLoop("paused", { operatorMessage: "task/HAL pause requested" });
|
||||
const pausedMachine = {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpResumeState: state.machine.interpState === "paused"
|
||||
? state.machine.interpResumeState
|
||||
: state.machine.interpState || "reading",
|
||||
interpState: "paused",
|
||||
taskPaused: true,
|
||||
};
|
||||
const pauseLock = createTaskHalPauseLock(state, "task-hal-plan-pause");
|
||||
setState({
|
||||
machine: pausedMachine,
|
||||
runState: "paused",
|
||||
taskHalPauseLock: pauseLock,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
},
|
||||
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
|
||||
operatorMessage: "task/HAL pause requested",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
@@ -1789,9 +1783,12 @@ export function createSimulationStore(seed = {}) {
|
||||
}).catch(() => {});
|
||||
break;
|
||||
}
|
||||
const pauseLock = createTaskHalPauseLock(state, "fixture-plan-pause");
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpResumeState: state.machine.interpState === "paused"
|
||||
? state.machine.interpResumeState
|
||||
: state.machine.interpState,
|
||||
@@ -1799,6 +1796,12 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: true,
|
||||
},
|
||||
runState: "paused",
|
||||
taskHalPauseLock: pauseLock,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
},
|
||||
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
|
||||
operatorMessage: "program paused",
|
||||
});
|
||||
}
|
||||
@@ -1806,17 +1809,16 @@ export function createSimulationStore(seed = {}) {
|
||||
case "PAUSE_RESUME":
|
||||
{
|
||||
const interpState = state.machine?.interpState || state.linuxCncTaskPolicy?.interpState || "idle";
|
||||
const taskMode = state.machine?.mode || state.linuxCncTaskPolicy?.taskMode || "manual";
|
||||
const taskIsRunning = state.runState === "running" || state.runState === "stepping";
|
||||
if (state.machine?.taskPaused === true || interpState === "paused" || state.runState === "paused") {
|
||||
if (taskMode === "auto" || taskMode === "mdi") {
|
||||
dispatch({ type: "RESUME" });
|
||||
break;
|
||||
}
|
||||
setState({ operatorMessage: "resume blocked: task mode must be auto or MDI" });
|
||||
const taskMode = normalizeLinuxCncTaskMode(state.machine?.mode);
|
||||
if (taskMode !== "auto" && taskMode !== "mdi") {
|
||||
setState({ operatorMessage: "pause blocked: task mode must be auto or MDI" });
|
||||
break;
|
||||
}
|
||||
if ((taskMode === "auto" || taskMode === "mdi") && (interpState !== "idle" || taskIsRunning)) {
|
||||
if (state.machine?.taskPaused === true || interpState === "paused" || state.runState === "paused") {
|
||||
dispatch({ type: "RESUME", source: "pauseresume" });
|
||||
break;
|
||||
}
|
||||
if (interpState !== "idle") {
|
||||
dispatch({ type: "PAUSE", source: "pauseresume" });
|
||||
break;
|
||||
}
|
||||
@@ -1836,6 +1838,8 @@ export function createSimulationStore(seed = {}) {
|
||||
: state.machine.interpResumeState || "reading";
|
||||
const resumedMachine = {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpState: resumeState,
|
||||
interpResumeState: resumeState,
|
||||
taskPaused: false,
|
||||
@@ -1843,6 +1847,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: resumedMachine,
|
||||
runState: resumeState === "reading" ? "running" : "idle",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "task/HAL resume requested",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
@@ -1865,11 +1870,14 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpState: resumeState,
|
||||
interpResumeState: resumeState,
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: resumeState === "reading" ? "running" : "idle",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "program resumed",
|
||||
});
|
||||
}
|
||||
@@ -1906,6 +1914,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: steppedMachine,
|
||||
runState: "stepping",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programElapsedSeconds: playback.timing.elapsedSeconds,
|
||||
programRemainingSeconds: playback.timing.remainingSeconds,
|
||||
@@ -1946,6 +1955,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: true,
|
||||
},
|
||||
runState: "stepping",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programElapsedSeconds: playback.timing.elapsedSeconds,
|
||||
programRemainingSeconds: playback.timing.remainingSeconds,
|
||||
@@ -2602,49 +2612,17 @@ export function createSimulationStore(seed = {}) {
|
||||
}
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
await initializeTaskHalSession({ openProgram: true });
|
||||
await runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: "ON" },
|
||||
{ type: "EMC_JOINT_HOME", joint: -1 },
|
||||
{ type: "EMC_TASK_SET_MODE", mode: "AUTO" },
|
||||
], {
|
||||
taskCycles: 3,
|
||||
operatorMessage: "RUN ready: power on, homed, auto mode",
|
||||
allowFixtureSession: false,
|
||||
preserveMachine: {
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
},
|
||||
setState({
|
||||
operatorMessage: state.machine.powerOn && state.machine.allHomed
|
||||
? "RUN ready: program opened; press Run"
|
||||
: "RUN setup ready: reset ESTOP, power on, Home All, then Run",
|
||||
});
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
if (tcpKinsType) {
|
||||
dispatch({ type: "SET_KINS_TYPE", kinsType: tcpKinsType });
|
||||
}
|
||||
return state;
|
||||
}
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "idle",
|
||||
kinsType: tcpKinsType || "identity",
|
||||
rtcpState: tcpKinsType ? "on" : "off",
|
||||
operatorMessage: tcpKinsType
|
||||
? "RUN ready: power on, homed, auto mode"
|
||||
: "RUN ready: power on, homed, auto mode; TCP unavailable for this reference profile",
|
||||
operatorMessage: state.machine.powerOn && state.machine.allHomed
|
||||
? "RUN ready: program opened; press Run"
|
||||
: "RUN setup ready: reset ESTOP, power on, Home All, then Run",
|
||||
});
|
||||
return state;
|
||||
};
|
||||
@@ -2657,6 +2635,19 @@ export function createSimulationStore(seed = {}) {
|
||||
await waitForStatePredicate((nextState) => nextState.machineFileStaging?.selectedGcodeSourceRel === sourceRel);
|
||||
}
|
||||
}
|
||||
const runGateState = {
|
||||
...state,
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
},
|
||||
};
|
||||
const runGate = gateLinuxCncTaskAction(runGateState, { type: "RUN" });
|
||||
if (!runGate.allowed) {
|
||||
setState({ operatorMessage: runGate.operatorMessage });
|
||||
return state;
|
||||
}
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
if (
|
||||
@@ -2679,17 +2670,14 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
interpState: "reading",
|
||||
interpResumeState: "reading",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "running",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "RUN ready: motion plan source ready; preparing task/HAL session",
|
||||
});
|
||||
const expectedProgramPath = expectedTaskHalProgramPathForState(state);
|
||||
@@ -2733,7 +2721,6 @@ export function createSimulationStore(seed = {}) {
|
||||
});
|
||||
const status = await runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: "ON" },
|
||||
{ type: "EMC_JOINT_HOME", joint: -1 },
|
||||
{ type: "EMC_TASK_SET_MODE", mode: "AUTO" },
|
||||
{ type: "EMC_TASK_PLAN_RUN", line: 0 },
|
||||
], {
|
||||
@@ -2755,8 +2742,15 @@ export function createSimulationStore(seed = {}) {
|
||||
return state;
|
||||
}
|
||||
const policy = createLinuxCncTaskPolicyStatus(state);
|
||||
if (policy.taskMode !== "auto" || !policy.allHomed || policy.taskState !== "on") {
|
||||
await runReadySequence();
|
||||
if (policy.taskMode !== "auto") {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
},
|
||||
operatorMessage: "mode auto",
|
||||
});
|
||||
}
|
||||
dispatch({ type: "RUN" });
|
||||
return state;
|
||||
@@ -3778,27 +3772,38 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const pauseLock = state.taskHalPauseLock?.active === true ? state.taskHalPauseLock : null;
|
||||
const rawTaskState = normalizeTaskHalTaskState(ui.taskState || task.state);
|
||||
const taskState = preserveMachine?.powerOn && rawTaskState === "estop-reset"
|
||||
? "on"
|
||||
: rawTaskState;
|
||||
const taskMode = normalizeLinuxCncTaskMode(preserveMachine?.mode || ui.taskMode || task.mode || state.machine.mode);
|
||||
const taskMode = pauseLock
|
||||
? programControlModeForMachine(state.machine)
|
||||
: normalizeLinuxCncTaskMode(preserveMachine?.mode || ui.taskMode || task.mode || state.machine.mode);
|
||||
const manualPanel = taskMode === "manual"
|
||||
? (preserveMachine?.manualPanel || state.machine.manualPanel || "manual")
|
||||
: null;
|
||||
const interpState = Object.hasOwn(preserveMachine || {}, "interpState")
|
||||
? normalizeTaskHalInterpState(preserveMachine.interpState)
|
||||
: normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const interpState = pauseLock
|
||||
? "paused"
|
||||
: Object.hasOwn(preserveMachine || {}, "interpState")
|
||||
? normalizeTaskHalInterpState(preserveMachine.interpState)
|
||||
: normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const allHomed = Boolean(preserveMachine?.allHomed ?? state.machine.allHomed);
|
||||
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
|
||||
const axisPose = preserveAxisPose
|
||||
? clampAxisPoseToProfile({ ...state.axisPose, ...preserveAxisPose }, state.profile)
|
||||
: resolveTaskHalAxisPose(state, status);
|
||||
const currentVelocity = Number.isFinite(ui.currentVelocity)
|
||||
? Math.max(ui.currentVelocity, 0)
|
||||
: state.feed.currentVelocity;
|
||||
const paused = interpState === "paused" || motion.paused === true;
|
||||
const paused = Boolean(pauseLock) || interpState === "paused" || motion.paused === true;
|
||||
const axisPose = pauseLock?.axisPose
|
||||
? clampAxisPoseToProfile(pauseLock.axisPose, state.profile)
|
||||
: paused
|
||||
? clampAxisPoseToProfile(state.axisPose, state.profile)
|
||||
: preserveAxisPose
|
||||
? clampAxisPoseToProfile({ ...state.axisPose, ...preserveAxisPose }, state.profile)
|
||||
: resolveTaskHalAxisPose(state, status);
|
||||
const currentVelocity = paused
|
||||
? 0
|
||||
: Number.isFinite(ui.currentVelocity)
|
||||
? Math.max(ui.currentVelocity, 0)
|
||||
: state.feed.currentVelocity;
|
||||
const aborted = motion.aborted === true;
|
||||
const openedProgramLineCount = Number(task.openedSourceLineCount || task.openedLineCount || 1);
|
||||
const programComplete = interpState === "idle" && Number(task.nextProgramLine || 0) >= openedProgramLineCount;
|
||||
@@ -3819,7 +3824,13 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const shouldTrackProgramPlayback = runState === "running"
|
||||
|| runState === "mdi"
|
||||
|| (runState === "complete" && (state.runState === "running" || state.runState === "mdi" || state.runState === "complete"));
|
||||
const taskHalSampleIndex = shouldTrackProgramPlayback
|
||||
const taskHalSampleIndex = pauseLock
|
||||
? clampNumber(
|
||||
pauseLock.sampleIndex,
|
||||
0,
|
||||
Math.max(Number(state.programAxisPreviewPath?.samples?.length || state.programAxisPreviewPath?.sampleCount || 1) - 1, 0),
|
||||
)
|
||||
: shouldTrackProgramPlayback
|
||||
? resolveRuntimeSampleIndexForTaskHalPose(state, axisPose, activeLine, runtimeFeedback.sampleIndex)
|
||||
: clampNumber(
|
||||
state.programExecutionSampleIndex || 0,
|
||||
@@ -3829,7 +3840,17 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const idleRuntimeFeedback = {
|
||||
...runtimeFeedback,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
motionIndex: Number(state.programExecutionMotionIndex || runtimeFeedback.motionIndex || 0),
|
||||
motionIndex: pauseLock
|
||||
? Number(pauseLock.motionIndex || 0)
|
||||
: Number(state.programExecutionMotionIndex || runtimeFeedback.motionIndex || 0),
|
||||
...(paused ? {
|
||||
axisPose,
|
||||
tcp: {
|
||||
x: Number(state.tcpPose?.x ?? axisPose.x ?? 0),
|
||||
y: Number(state.tcpPose?.y ?? axisPose.y ?? 0),
|
||||
z: Number(state.tcpPose?.z ?? axisPose.z ?? 0),
|
||||
},
|
||||
} : {}),
|
||||
};
|
||||
const taskHalPlaybackPatch = shouldTrackProgramPlayback
|
||||
? applyProgramPlaybackUiPatch(state, {
|
||||
@@ -3837,11 +3858,15 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
axisPose,
|
||||
kinsType,
|
||||
rtcpState: rtcpStateFromKinsType(kinsType),
|
||||
motionIndex: runtimeFeedback.motionIndex,
|
||||
motionIndex: pauseLock ? Number(pauseLock.motionIndex || 0) : runtimeFeedback.motionIndex,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
runtimeFeedback: {
|
||||
...runtimeFeedback,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
motionIndex: pauseLock ? Number(pauseLock.motionIndex || 0) : runtimeFeedback.motionIndex,
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: paused ? 0 : runtimeFeedback.currentVelocityMmPerMin,
|
||||
requestedVelocityMmPerMin: paused ? 0 : runtimeFeedback.requestedVelocityMmPerMin,
|
||||
},
|
||||
preferRuntimeAxisPose: true,
|
||||
})
|
||||
@@ -3879,6 +3904,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
return {
|
||||
taskHalStatus: status,
|
||||
taskHalExecutionPending: false,
|
||||
taskHalPauseLock: pauseLock ? { ...pauseLock, lastStatusAt: new Date().toISOString() } : state.taskHalPauseLock,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
...taskHalPlaybackPatch,
|
||||
@@ -3939,6 +3965,57 @@ function isTaskHalRunPausedByOperator(state = {}) {
|
||||
|| state.machine?.taskPaused === true;
|
||||
}
|
||||
|
||||
function programControlModeForMachine(machine = {}) {
|
||||
return machine.mode === "mdi" ? "mdi" : "auto";
|
||||
}
|
||||
|
||||
function createPowerToggleMachinePatch(machine = {}, turningOn = false) {
|
||||
return {
|
||||
...machine,
|
||||
powerOn: turningOn,
|
||||
estopActive: false,
|
||||
taskState: turningOn ? "on" : "off",
|
||||
manualPanel: "manual",
|
||||
allHomed: turningOn ? Boolean(machine.allHomed) : false,
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
};
|
||||
}
|
||||
|
||||
function createTaskHalPauseLock(state = {}, source = "pause") {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-task-hal-pause-lock",
|
||||
active: true,
|
||||
source,
|
||||
createdAt: new Date().toISOString(),
|
||||
runState: "paused",
|
||||
interpState: "paused",
|
||||
sampleIndex: Number(state.programExecutionSampleIndex || 0),
|
||||
motionIndex: Number(state.programExecutionMotionIndex || 0),
|
||||
activeLine: Number(state.activeLine || state.programStartLine || 1),
|
||||
axisPose: clampAxisPoseToProfile(state.axisPose || initialAxisPose, state.profile),
|
||||
tcpPose: {
|
||||
x: Number(state.tcpPose?.x || 0),
|
||||
y: Number(state.tcpPose?.y || 0),
|
||||
z: Number(state.tcpPose?.z || 0),
|
||||
},
|
||||
semanticBoundary: "linuxcnc_axis_task_pauseresume_freezes_task_hal_status_until_resume",
|
||||
};
|
||||
}
|
||||
|
||||
function zeroProgramRuntimeVelocity(feedback) {
|
||||
if (!feedback) return feedback;
|
||||
return {
|
||||
...feedback,
|
||||
currentVelocityMmPerMin: 0,
|
||||
requestedVelocityMmPerMin: 0,
|
||||
distanceToGo: 0,
|
||||
dtg: feedback.dtg ? { ...feedback.dtg, x: 0, y: 0, z: 0 } : feedback.dtg,
|
||||
activeDepth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function createStoppedProgramStatePatch(state, {
|
||||
reason = "stopped",
|
||||
operatorMessage = "program stopped",
|
||||
@@ -3951,6 +4028,7 @@ function createStoppedProgramStatePatch(state, {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: reason === "aborted" ? "stopped" : reason,
|
||||
taskHalPauseLock: null,
|
||||
taskHalStatusLoop: {
|
||||
...state.taskHalStatusLoop,
|
||||
active: false,
|
||||
@@ -4058,6 +4136,10 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const halProgramLine = Number(status?.halSnapshot?.pins?.["motion.program-line"]?.value || 0);
|
||||
const motionProgramLine = Number(motion.programLine || 0);
|
||||
const interpState = normalizeTaskHalInterpState(ui.interpState || status?.task?.interpState);
|
||||
const paused = interpState === "paused" || motion.paused === true;
|
||||
const currentVelocity = paused ? 0 : Number(ui.currentVelocity || 0);
|
||||
const requestedVelocity = paused ? 0 : Number(motion.requestedVel || 0) * 60;
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-program-runtime-feedback",
|
||||
sourceMode: "linuxcnc-task-motion-hal-wasm",
|
||||
@@ -4072,8 +4154,8 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
type: Number(motion.motionType || 0) === 3 ? "JOG" : "TASK_MOTION",
|
||||
timeSeconds: Number(ui.taskCycle || 0) * 0.01,
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: Number(ui.currentVelocity || 0),
|
||||
requestedVelocityMmPerMin: Number(motion.requestedVel || 0) * 60,
|
||||
currentVelocityMmPerMin: currentVelocity,
|
||||
requestedVelocityMmPerMin: requestedVelocity,
|
||||
distanceToGo: motion.inPosition === true ? 0 : 1,
|
||||
dtg: { x: 0, y: 0, z: 0 },
|
||||
queueDepth: Number(ui.motionQueueDepth || status?.motionStatus?.commandQueueDepth || 0),
|
||||
|
||||
@@ -10,7 +10,7 @@ export const AXIS_BUTTON_PARITY = [
|
||||
{ id: "menu-estop", action: "estop", sourceSymbol: "commands.estop_clicked", sourceLines: "axis.py:2223-2229", expected: "toggle ESTOP/ESTOP_RESET" },
|
||||
{ id: "menu-power", action: "power", sourceSymbol: "commands.onoff_clicked", sourceLines: "axis.py:2231-2241", expected: "toggle machine ON/OFF after estop reset" },
|
||||
{ id: "menu-home-all", action: "home-all", sourceSymbol: "commands.home_all_joints", sourceLines: "axis.py:2586-2596", expected: "home all joints in manual mode" },
|
||||
{ id: "menu-run-ready", action: "run-ready", sourceSymbol: "AXIS run preconditions", sourceLines: "axis.py:2308-2320", expected: "power on, home, auto mode and TCP kins ready" },
|
||||
{ id: "menu-run-ready", action: "run-ready", sourceSymbol: "AXIS run preconditions", sourceLines: "axis.py:2379-2391", expected: "open/stage the selected program without substituting power, Home All, or Run" },
|
||||
{ id: "menu-run", action: "run", sourceSymbol: "commands.task_run", sourceLines: "axis.py:2308-2320", expected: "AUTO_RUN from current start line" },
|
||||
{ id: "menu-pause", action: "pause", sourceSymbol: "commands.task_pause", sourceLines: "axis.py:2329-2333", expected: "AUTO_PAUSE when running" },
|
||||
{ id: "menu-resume", action: "resume", sourceSymbol: "commands.task_resume", sourceLines: "axis.py:2344-2351", expected: "AUTO_RESUME when paused" },
|
||||
|
||||
@@ -79,6 +79,9 @@ export function buildVismachModelState(state = {}) {
|
||||
}
|
||||
|
||||
function resolveAxisPose(state) {
|
||||
if (state.runState === "paused" || state.machine?.interpState === "paused" || state.machine?.taskPaused === true) {
|
||||
return state.axisPose || {};
|
||||
}
|
||||
if (state.programRuntimeFeedback?.axisPose) return state.programRuntimeFeedback.axisPose;
|
||||
if (state.taskHalStatus?.ui?.axisPose) return state.taskHalStatus.ui.axisPose;
|
||||
return state.axisPose || {};
|
||||
|
||||
@@ -99,7 +99,7 @@ export function createLinuxCncTaskPolicyStatus(state) {
|
||||
canRunAutoStrict: taskState === "on" && taskMode === "auto" && (allHomed || noForceHoming) &&
|
||||
interpIdle && iniLoaded && machineFileStaged && machineFileOpened && taskHalRuntimeReady,
|
||||
canExecuteMdi: taskState === "on" && taskMode === "mdi" && (allHomed || noForceHoming),
|
||||
canPause: taskState === "on" && (taskMode === "auto" || taskMode === "mdi"),
|
||||
canPause: taskState === "on" && taskMode === "auto" && (interpState === "reading" || interpState === "waiting"),
|
||||
canResume: taskState === "on" && (taskMode === "auto" || taskMode === "mdi") && interpState === "paused",
|
||||
canAbort: true,
|
||||
canSpindle: taskState === "on",
|
||||
@@ -116,9 +116,6 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
|
||||
switch (type) {
|
||||
case "TOGGLE_POWER":
|
||||
if (status.taskState === "estop") {
|
||||
return block(status, "power on blocked: reset estop first");
|
||||
}
|
||||
return allow(status);
|
||||
case "SET_MODE":
|
||||
return gateMode(status, requestedMode);
|
||||
@@ -164,10 +161,7 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
case "PAUSE":
|
||||
if (status.taskState !== "on") return block(status, "pause blocked: machine must be on");
|
||||
{
|
||||
const taskIsRunning = status.runState === "running" || status.runState === "stepping";
|
||||
const interpIsRunning = status.interpState === "reading" || status.interpState === "waiting";
|
||||
const interpCanPauseResume = status.interpState !== "idle" || taskIsRunning;
|
||||
const pauseReady = action.source === "pauseresume" ? interpCanPauseResume : (interpIsRunning || taskIsRunning);
|
||||
const requiredModeMessage = action.source === "pauseresume"
|
||||
? "pause blocked: task mode must be auto or MDI"
|
||||
: "pause blocked: task mode must be auto";
|
||||
@@ -176,18 +170,18 @@ export function gateLinuxCncTaskAction(state, action) {
|
||||
: "pause blocked: interpreter is not running";
|
||||
|
||||
if (action.source === "pauseresume") {
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") {
|
||||
return block(status, requiredModeMessage);
|
||||
}
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") return block(status, requiredModeMessage);
|
||||
if (status.interpState === "idle") return block(status, notRunningMessage);
|
||||
} else if (status.taskMode !== "auto") {
|
||||
return block(status, requiredModeMessage);
|
||||
}
|
||||
if (!pauseReady) return block(status, notRunningMessage);
|
||||
if (action.source !== "pauseresume" && status.taskMode !== "auto") return block(status, requiredModeMessage);
|
||||
if (action.source !== "pauseresume" && !interpIsRunning) return block(status, notRunningMessage);
|
||||
return allow(status);
|
||||
}
|
||||
case "RESUME":
|
||||
if (status.taskState !== "on") return block(status, "resume blocked: machine must be on");
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi") {
|
||||
if (status.taskMode !== "auto" && status.taskMode !== "mdi" && status.runState !== "paused") {
|
||||
return block(status, "resume blocked: task mode must be auto or MDI");
|
||||
}
|
||||
if (status.interpState !== "paused") return block(status, "resume blocked: interpreter is not paused");
|
||||
|
||||
@@ -230,6 +230,7 @@ const initialState = {
|
||||
taskHalExecutionPending: false,
|
||||
taskHalExecutionSequence: 0,
|
||||
taskHalStatusLoop: createTaskHalStatusLoopState(),
|
||||
taskHalPauseLock: null,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
interpreterExecutionPending: false,
|
||||
@@ -1147,6 +1148,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
taskHalFallbackReason: action.error,
|
||||
taskHalExecutionPending: false,
|
||||
taskHalPauseLock: null,
|
||||
taskHalStatusLoop: {
|
||||
...state.taskHalStatusLoop,
|
||||
active: false,
|
||||
@@ -1269,50 +1271,34 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({ operatorMessage: gate.operatorMessage });
|
||||
break;
|
||||
}
|
||||
const turningOff = state.machine.taskState === "on" || state.machine.powerOn;
|
||||
const turningOn = state.machine.taskState === "estop-reset";
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
const nextMachine = createPowerToggleMachinePatch(state.machine, turningOn);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: !turningOff,
|
||||
estopActive: false,
|
||||
taskState: turningOff ? "estop-reset" : "on",
|
||||
manualPanel: "manual",
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: turningOff ? "powered-off" : "idle",
|
||||
kinsType: turningOff ? "identity" : state.kinsType,
|
||||
rtcpState: turningOff ? "off" : state.rtcpState,
|
||||
feed: turningOff ? { ...state.feed, currentVelocity: 0 } : state.feed,
|
||||
coolant: turningOff ? { ...state.coolant, flood: false, mist: false } : state.coolant,
|
||||
spindle: turningOff ? stoppedSpindleState(state.spindle) : state.spindle,
|
||||
operatorMessage: turningOff ? "task/HAL machine power off" : "task/HAL machine power on",
|
||||
machine: nextMachine,
|
||||
runState: turningOn ? "idle" : "powered-off",
|
||||
kinsType: turningOn ? state.kinsType : "identity",
|
||||
rtcpState: turningOn ? state.rtcpState : "off",
|
||||
feed: turningOn ? state.feed : { ...state.feed, currentVelocity: 0 },
|
||||
coolant: turningOn ? state.coolant : { ...state.coolant, flood: false, mist: false },
|
||||
spindle: turningOn ? state.spindle : stoppedSpindleState(state.spindle),
|
||||
operatorMessage: turningOn ? "task/HAL machine power on" : "task/HAL machine power off",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: turningOff ? "ESTOP_RESET" : "ON" },
|
||||
], { operatorMessage: turningOff ? "task/HAL machine power off" : "task/HAL machine power on" }).catch(() => {});
|
||||
{ type: "EMC_TASK_SET_STATE", state: turningOn ? "ON" : "OFF" },
|
||||
], { operatorMessage: turningOn ? "task/HAL machine power on" : "task/HAL machine power off" }).catch(() => {});
|
||||
break;
|
||||
}
|
||||
const nextMachine = createPowerToggleMachinePatch(state.machine, turningOn);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: !turningOff,
|
||||
estopActive: false,
|
||||
taskState: turningOff ? "estop-reset" : "on",
|
||||
manualPanel: "manual",
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: turningOff ? "powered-off" : "idle",
|
||||
kinsType: turningOff ? "identity" : state.kinsType,
|
||||
rtcpState: turningOff ? "off" : state.rtcpState,
|
||||
feed: turningOff ? { ...state.feed, currentVelocity: 0 } : state.feed,
|
||||
coolant: turningOff ? { ...state.coolant, flood: false, mist: false } : state.coolant,
|
||||
spindle: turningOff ? stoppedSpindleState(state.spindle) : state.spindle,
|
||||
operatorMessage: turningOff ? "machine power off" : "machine power on",
|
||||
machine: nextMachine,
|
||||
runState: turningOn ? "idle" : "powered-off",
|
||||
kinsType: turningOn ? state.kinsType : "identity",
|
||||
rtcpState: turningOn ? state.rtcpState : "off",
|
||||
feed: turningOn ? state.feed : { ...state.feed, currentVelocity: 0 },
|
||||
coolant: turningOn ? state.coolant : { ...state.coolant, flood: false, mist: false },
|
||||
spindle: turningOn ? state.spindle : stoppedSpindleState(state.spindle),
|
||||
operatorMessage: turningOn ? "machine power on" : "machine power off",
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -1633,6 +1619,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "running",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "task/HAL program run requested",
|
||||
});
|
||||
runValidatedTaskHalProgramRun().catch(() => {});
|
||||
@@ -1658,6 +1645,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: playback.complete ? "complete" : "running",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programLineExecution: createProgramLineExecutionPatch(state.programLineExecution, playback.runtimeFeedback, {
|
||||
status: playback.complete ? "done" : "running",
|
||||
@@ -1718,6 +1706,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "stopped",
|
||||
taskHalPauseLock: null,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
@@ -1766,19 +1755,24 @@ export function createSimulationStore(seed = {}) {
|
||||
stopTaskHalStatusLoop("paused", { operatorMessage: "task/HAL pause requested" });
|
||||
const pausedMachine = {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpResumeState: state.machine.interpState === "paused"
|
||||
? state.machine.interpResumeState
|
||||
: state.machine.interpState || "reading",
|
||||
interpState: "paused",
|
||||
taskPaused: true,
|
||||
};
|
||||
const pauseLock = createTaskHalPauseLock(state, "task-hal-plan-pause");
|
||||
setState({
|
||||
machine: pausedMachine,
|
||||
runState: "paused",
|
||||
taskHalPauseLock: pauseLock,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
},
|
||||
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
|
||||
operatorMessage: "task/HAL pause requested",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
@@ -1789,9 +1783,12 @@ export function createSimulationStore(seed = {}) {
|
||||
}).catch(() => {});
|
||||
break;
|
||||
}
|
||||
const pauseLock = createTaskHalPauseLock(state, "fixture-plan-pause");
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpResumeState: state.machine.interpState === "paused"
|
||||
? state.machine.interpResumeState
|
||||
: state.machine.interpState,
|
||||
@@ -1799,6 +1796,12 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: true,
|
||||
},
|
||||
runState: "paused",
|
||||
taskHalPauseLock: pauseLock,
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
},
|
||||
programRuntimeFeedback: zeroProgramRuntimeVelocity(state.programRuntimeFeedback),
|
||||
operatorMessage: "program paused",
|
||||
});
|
||||
}
|
||||
@@ -1806,17 +1809,16 @@ export function createSimulationStore(seed = {}) {
|
||||
case "PAUSE_RESUME":
|
||||
{
|
||||
const interpState = state.machine?.interpState || state.linuxCncTaskPolicy?.interpState || "idle";
|
||||
const taskMode = state.machine?.mode || state.linuxCncTaskPolicy?.taskMode || "manual";
|
||||
const taskIsRunning = state.runState === "running" || state.runState === "stepping";
|
||||
if (state.machine?.taskPaused === true || interpState === "paused" || state.runState === "paused") {
|
||||
if (taskMode === "auto" || taskMode === "mdi") {
|
||||
dispatch({ type: "RESUME" });
|
||||
break;
|
||||
}
|
||||
setState({ operatorMessage: "resume blocked: task mode must be auto or MDI" });
|
||||
const taskMode = normalizeLinuxCncTaskMode(state.machine?.mode);
|
||||
if (taskMode !== "auto" && taskMode !== "mdi") {
|
||||
setState({ operatorMessage: "pause blocked: task mode must be auto or MDI" });
|
||||
break;
|
||||
}
|
||||
if ((taskMode === "auto" || taskMode === "mdi") && (interpState !== "idle" || taskIsRunning)) {
|
||||
if (state.machine?.taskPaused === true || interpState === "paused" || state.runState === "paused") {
|
||||
dispatch({ type: "RESUME", source: "pauseresume" });
|
||||
break;
|
||||
}
|
||||
if (interpState !== "idle") {
|
||||
dispatch({ type: "PAUSE", source: "pauseresume" });
|
||||
break;
|
||||
}
|
||||
@@ -1836,6 +1838,8 @@ export function createSimulationStore(seed = {}) {
|
||||
: state.machine.interpResumeState || "reading";
|
||||
const resumedMachine = {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpState: resumeState,
|
||||
interpResumeState: resumeState,
|
||||
taskPaused: false,
|
||||
@@ -1843,6 +1847,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: resumedMachine,
|
||||
runState: resumeState === "reading" ? "running" : "idle",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "task/HAL resume requested",
|
||||
});
|
||||
runTaskHalCommandSequence([
|
||||
@@ -1865,11 +1870,14 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: programControlModeForMachine(state.machine),
|
||||
manualPanel: null,
|
||||
interpState: resumeState,
|
||||
interpResumeState: resumeState,
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: resumeState === "reading" ? "running" : "idle",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "program resumed",
|
||||
});
|
||||
}
|
||||
@@ -1906,6 +1914,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: steppedMachine,
|
||||
runState: "stepping",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programElapsedSeconds: playback.timing.elapsedSeconds,
|
||||
programRemainingSeconds: playback.timing.remainingSeconds,
|
||||
@@ -1946,6 +1955,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPaused: true,
|
||||
},
|
||||
runState: "stepping",
|
||||
taskHalPauseLock: null,
|
||||
...playbackPatch,
|
||||
programElapsedSeconds: playback.timing.elapsedSeconds,
|
||||
programRemainingSeconds: playback.timing.remainingSeconds,
|
||||
@@ -2602,49 +2612,17 @@ export function createSimulationStore(seed = {}) {
|
||||
}
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
await initializeTaskHalSession({ openProgram: true });
|
||||
await runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: "ON" },
|
||||
{ type: "EMC_JOINT_HOME", joint: -1 },
|
||||
{ type: "EMC_TASK_SET_MODE", mode: "AUTO" },
|
||||
], {
|
||||
taskCycles: 3,
|
||||
operatorMessage: "RUN ready: power on, homed, auto mode",
|
||||
allowFixtureSession: false,
|
||||
preserveMachine: {
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
},
|
||||
setState({
|
||||
operatorMessage: state.machine.powerOn && state.machine.allHomed
|
||||
? "RUN ready: program opened; press Run"
|
||||
: "RUN setup ready: reset ESTOP, power on, Home All, then Run",
|
||||
});
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
if (tcpKinsType) {
|
||||
dispatch({ type: "SET_KINS_TYPE", kinsType: tcpKinsType });
|
||||
}
|
||||
return state;
|
||||
}
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "idle",
|
||||
kinsType: tcpKinsType || "identity",
|
||||
rtcpState: tcpKinsType ? "on" : "off",
|
||||
operatorMessage: tcpKinsType
|
||||
? "RUN ready: power on, homed, auto mode"
|
||||
: "RUN ready: power on, homed, auto mode; TCP unavailable for this reference profile",
|
||||
operatorMessage: state.machine.powerOn && state.machine.allHomed
|
||||
? "RUN ready: program opened; press Run"
|
||||
: "RUN setup ready: reset ESTOP, power on, Home All, then Run",
|
||||
});
|
||||
return state;
|
||||
};
|
||||
@@ -2657,6 +2635,19 @@ export function createSimulationStore(seed = {}) {
|
||||
await waitForStatePredicate((nextState) => nextState.machineFileStaging?.selectedGcodeSourceRel === sourceRel);
|
||||
}
|
||||
}
|
||||
const runGateState = {
|
||||
...state,
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
},
|
||||
};
|
||||
const runGate = gateLinuxCncTaskAction(runGateState, { type: "RUN" });
|
||||
if (!runGate.allowed) {
|
||||
setState({ operatorMessage: runGate.operatorMessage });
|
||||
return state;
|
||||
}
|
||||
const tcpKinsType = tcpKinsTypeForProfile(state.profile);
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
if (
|
||||
@@ -2679,17 +2670,14 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: true,
|
||||
estopActive: false,
|
||||
taskState: "on",
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
allHomed: true,
|
||||
interpState: "reading",
|
||||
interpResumeState: "reading",
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: "running",
|
||||
taskHalPauseLock: null,
|
||||
operatorMessage: "RUN ready: motion plan source ready; preparing task/HAL session",
|
||||
});
|
||||
const expectedProgramPath = expectedTaskHalProgramPathForState(state);
|
||||
@@ -2733,7 +2721,6 @@ export function createSimulationStore(seed = {}) {
|
||||
});
|
||||
const status = await runTaskHalCommandSequence([
|
||||
{ type: "EMC_TASK_SET_STATE", state: "ON" },
|
||||
{ type: "EMC_JOINT_HOME", joint: -1 },
|
||||
{ type: "EMC_TASK_SET_MODE", mode: "AUTO" },
|
||||
{ type: "EMC_TASK_PLAN_RUN", line: 0 },
|
||||
], {
|
||||
@@ -2755,8 +2742,15 @@ export function createSimulationStore(seed = {}) {
|
||||
return state;
|
||||
}
|
||||
const policy = createLinuxCncTaskPolicyStatus(state);
|
||||
if (policy.taskMode !== "auto" || !policy.allHomed || policy.taskState !== "on") {
|
||||
await runReadySequence();
|
||||
if (policy.taskMode !== "auto") {
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
manualPanel: null,
|
||||
},
|
||||
operatorMessage: "mode auto",
|
||||
});
|
||||
}
|
||||
dispatch({ type: "RUN" });
|
||||
return state;
|
||||
@@ -3778,27 +3772,38 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const pauseLock = state.taskHalPauseLock?.active === true ? state.taskHalPauseLock : null;
|
||||
const rawTaskState = normalizeTaskHalTaskState(ui.taskState || task.state);
|
||||
const taskState = preserveMachine?.powerOn && rawTaskState === "estop-reset"
|
||||
? "on"
|
||||
: rawTaskState;
|
||||
const taskMode = normalizeLinuxCncTaskMode(preserveMachine?.mode || ui.taskMode || task.mode || state.machine.mode);
|
||||
const taskMode = pauseLock
|
||||
? programControlModeForMachine(state.machine)
|
||||
: normalizeLinuxCncTaskMode(preserveMachine?.mode || ui.taskMode || task.mode || state.machine.mode);
|
||||
const manualPanel = taskMode === "manual"
|
||||
? (preserveMachine?.manualPanel || state.machine.manualPanel || "manual")
|
||||
: null;
|
||||
const interpState = Object.hasOwn(preserveMachine || {}, "interpState")
|
||||
? normalizeTaskHalInterpState(preserveMachine.interpState)
|
||||
: normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const interpState = pauseLock
|
||||
? "paused"
|
||||
: Object.hasOwn(preserveMachine || {}, "interpState")
|
||||
? normalizeTaskHalInterpState(preserveMachine.interpState)
|
||||
: normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const allHomed = Boolean(preserveMachine?.allHomed ?? state.machine.allHomed);
|
||||
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
|
||||
const axisPose = preserveAxisPose
|
||||
? clampAxisPoseToProfile({ ...state.axisPose, ...preserveAxisPose }, state.profile)
|
||||
: resolveTaskHalAxisPose(state, status);
|
||||
const currentVelocity = Number.isFinite(ui.currentVelocity)
|
||||
? Math.max(ui.currentVelocity, 0)
|
||||
: state.feed.currentVelocity;
|
||||
const paused = interpState === "paused" || motion.paused === true;
|
||||
const paused = Boolean(pauseLock) || interpState === "paused" || motion.paused === true;
|
||||
const axisPose = pauseLock?.axisPose
|
||||
? clampAxisPoseToProfile(pauseLock.axisPose, state.profile)
|
||||
: paused
|
||||
? clampAxisPoseToProfile(state.axisPose, state.profile)
|
||||
: preserveAxisPose
|
||||
? clampAxisPoseToProfile({ ...state.axisPose, ...preserveAxisPose }, state.profile)
|
||||
: resolveTaskHalAxisPose(state, status);
|
||||
const currentVelocity = paused
|
||||
? 0
|
||||
: Number.isFinite(ui.currentVelocity)
|
||||
? Math.max(ui.currentVelocity, 0)
|
||||
: state.feed.currentVelocity;
|
||||
const aborted = motion.aborted === true;
|
||||
const openedProgramLineCount = Number(task.openedSourceLineCount || task.openedLineCount || 1);
|
||||
const programComplete = interpState === "idle" && Number(task.nextProgramLine || 0) >= openedProgramLineCount;
|
||||
@@ -3819,7 +3824,13 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const shouldTrackProgramPlayback = runState === "running"
|
||||
|| runState === "mdi"
|
||||
|| (runState === "complete" && (state.runState === "running" || state.runState === "mdi" || state.runState === "complete"));
|
||||
const taskHalSampleIndex = shouldTrackProgramPlayback
|
||||
const taskHalSampleIndex = pauseLock
|
||||
? clampNumber(
|
||||
pauseLock.sampleIndex,
|
||||
0,
|
||||
Math.max(Number(state.programAxisPreviewPath?.samples?.length || state.programAxisPreviewPath?.sampleCount || 1) - 1, 0),
|
||||
)
|
||||
: shouldTrackProgramPlayback
|
||||
? resolveRuntimeSampleIndexForTaskHalPose(state, axisPose, activeLine, runtimeFeedback.sampleIndex)
|
||||
: clampNumber(
|
||||
state.programExecutionSampleIndex || 0,
|
||||
@@ -3829,7 +3840,17 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
const idleRuntimeFeedback = {
|
||||
...runtimeFeedback,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
motionIndex: Number(state.programExecutionMotionIndex || runtimeFeedback.motionIndex || 0),
|
||||
motionIndex: pauseLock
|
||||
? Number(pauseLock.motionIndex || 0)
|
||||
: Number(state.programExecutionMotionIndex || runtimeFeedback.motionIndex || 0),
|
||||
...(paused ? {
|
||||
axisPose,
|
||||
tcp: {
|
||||
x: Number(state.tcpPose?.x ?? axisPose.x ?? 0),
|
||||
y: Number(state.tcpPose?.y ?? axisPose.y ?? 0),
|
||||
z: Number(state.tcpPose?.z ?? axisPose.z ?? 0),
|
||||
},
|
||||
} : {}),
|
||||
};
|
||||
const taskHalPlaybackPatch = shouldTrackProgramPlayback
|
||||
? applyProgramPlaybackUiPatch(state, {
|
||||
@@ -3837,11 +3858,15 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
axisPose,
|
||||
kinsType,
|
||||
rtcpState: rtcpStateFromKinsType(kinsType),
|
||||
motionIndex: runtimeFeedback.motionIndex,
|
||||
motionIndex: pauseLock ? Number(pauseLock.motionIndex || 0) : runtimeFeedback.motionIndex,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
runtimeFeedback: {
|
||||
...runtimeFeedback,
|
||||
sampleIndex: taskHalSampleIndex,
|
||||
motionIndex: pauseLock ? Number(pauseLock.motionIndex || 0) : runtimeFeedback.motionIndex,
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: paused ? 0 : runtimeFeedback.currentVelocityMmPerMin,
|
||||
requestedVelocityMmPerMin: paused ? 0 : runtimeFeedback.requestedVelocityMmPerMin,
|
||||
},
|
||||
preferRuntimeAxisPose: true,
|
||||
})
|
||||
@@ -3879,6 +3904,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
return {
|
||||
taskHalStatus: status,
|
||||
taskHalExecutionPending: false,
|
||||
taskHalPauseLock: pauseLock ? { ...pauseLock, lastStatusAt: new Date().toISOString() } : state.taskHalPauseLock,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
...taskHalPlaybackPatch,
|
||||
@@ -3939,6 +3965,57 @@ function isTaskHalRunPausedByOperator(state = {}) {
|
||||
|| state.machine?.taskPaused === true;
|
||||
}
|
||||
|
||||
function programControlModeForMachine(machine = {}) {
|
||||
return machine.mode === "mdi" ? "mdi" : "auto";
|
||||
}
|
||||
|
||||
function createPowerToggleMachinePatch(machine = {}, turningOn = false) {
|
||||
return {
|
||||
...machine,
|
||||
powerOn: turningOn,
|
||||
estopActive: false,
|
||||
taskState: turningOn ? "on" : "off",
|
||||
manualPanel: "manual",
|
||||
allHomed: turningOn ? Boolean(machine.allHomed) : false,
|
||||
interpState: "idle",
|
||||
interpResumeState: "idle",
|
||||
taskPaused: false,
|
||||
};
|
||||
}
|
||||
|
||||
function createTaskHalPauseLock(state = {}, source = "pause") {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-task-hal-pause-lock",
|
||||
active: true,
|
||||
source,
|
||||
createdAt: new Date().toISOString(),
|
||||
runState: "paused",
|
||||
interpState: "paused",
|
||||
sampleIndex: Number(state.programExecutionSampleIndex || 0),
|
||||
motionIndex: Number(state.programExecutionMotionIndex || 0),
|
||||
activeLine: Number(state.activeLine || state.programStartLine || 1),
|
||||
axisPose: clampAxisPoseToProfile(state.axisPose || initialAxisPose, state.profile),
|
||||
tcpPose: {
|
||||
x: Number(state.tcpPose?.x || 0),
|
||||
y: Number(state.tcpPose?.y || 0),
|
||||
z: Number(state.tcpPose?.z || 0),
|
||||
},
|
||||
semanticBoundary: "linuxcnc_axis_task_pauseresume_freezes_task_hal_status_until_resume",
|
||||
};
|
||||
}
|
||||
|
||||
function zeroProgramRuntimeVelocity(feedback) {
|
||||
if (!feedback) return feedback;
|
||||
return {
|
||||
...feedback,
|
||||
currentVelocityMmPerMin: 0,
|
||||
requestedVelocityMmPerMin: 0,
|
||||
distanceToGo: 0,
|
||||
dtg: feedback.dtg ? { ...feedback.dtg, x: 0, y: 0, z: 0 } : feedback.dtg,
|
||||
activeDepth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function createStoppedProgramStatePatch(state, {
|
||||
reason = "stopped",
|
||||
operatorMessage = "program stopped",
|
||||
@@ -3951,6 +4028,7 @@ function createStoppedProgramStatePatch(state, {
|
||||
taskPaused: false,
|
||||
},
|
||||
runState: reason === "aborted" ? "stopped" : reason,
|
||||
taskHalPauseLock: null,
|
||||
taskHalStatusLoop: {
|
||||
...state.taskHalStatusLoop,
|
||||
active: false,
|
||||
@@ -4058,6 +4136,10 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const halProgramLine = Number(status?.halSnapshot?.pins?.["motion.program-line"]?.value || 0);
|
||||
const motionProgramLine = Number(motion.programLine || 0);
|
||||
const interpState = normalizeTaskHalInterpState(ui.interpState || status?.task?.interpState);
|
||||
const paused = interpState === "paused" || motion.paused === true;
|
||||
const currentVelocity = paused ? 0 : Number(ui.currentVelocity || 0);
|
||||
const requestedVelocity = paused ? 0 : Number(motion.requestedVel || 0) * 60;
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-program-runtime-feedback",
|
||||
sourceMode: "linuxcnc-task-motion-hal-wasm",
|
||||
@@ -4072,8 +4154,8 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
type: Number(motion.motionType || 0) === 3 ? "JOG" : "TASK_MOTION",
|
||||
timeSeconds: Number(ui.taskCycle || 0) * 0.01,
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: Number(ui.currentVelocity || 0),
|
||||
requestedVelocityMmPerMin: Number(motion.requestedVel || 0) * 60,
|
||||
currentVelocityMmPerMin: currentVelocity,
|
||||
requestedVelocityMmPerMin: requestedVelocity,
|
||||
distanceToGo: motion.inPosition === true ? 0 : 1,
|
||||
dtg: { x: 0, y: 0, z: 0 },
|
||||
queueDepth: Number(ui.motionQueueDepth || status?.motionStatus?.commandQueueDepth || 0),
|
||||
|
||||
@@ -10,7 +10,7 @@ export const AXIS_BUTTON_PARITY = [
|
||||
{ id: "menu-estop", action: "estop", sourceSymbol: "commands.estop_clicked", sourceLines: "axis.py:2223-2229", expected: "toggle ESTOP/ESTOP_RESET" },
|
||||
{ id: "menu-power", action: "power", sourceSymbol: "commands.onoff_clicked", sourceLines: "axis.py:2231-2241", expected: "toggle machine ON/OFF after estop reset" },
|
||||
{ id: "menu-home-all", action: "home-all", sourceSymbol: "commands.home_all_joints", sourceLines: "axis.py:2586-2596", expected: "home all joints in manual mode" },
|
||||
{ id: "menu-run-ready", action: "run-ready", sourceSymbol: "AXIS run preconditions", sourceLines: "axis.py:2308-2320", expected: "power on, home, auto mode and TCP kins ready" },
|
||||
{ id: "menu-run-ready", action: "run-ready", sourceSymbol: "AXIS run preconditions", sourceLines: "axis.py:2379-2391", expected: "open/stage the selected program without substituting power, Home All, or Run" },
|
||||
{ id: "menu-run", action: "run", sourceSymbol: "commands.task_run", sourceLines: "axis.py:2308-2320", expected: "AUTO_RUN from current start line" },
|
||||
{ id: "menu-pause", action: "pause", sourceSymbol: "commands.task_pause", sourceLines: "axis.py:2329-2333", expected: "AUTO_PAUSE when running" },
|
||||
{ id: "menu-resume", action: "resume", sourceSymbol: "commands.task_resume", sourceLines: "axis.py:2344-2351", expected: "AUTO_RESUME when paused" },
|
||||
|
||||
Reference in New Issue
Block a user