Validate AXIS task state flow
This commit is contained in:
@@ -212,15 +212,22 @@ export function buildTaskHalProgramMotionPlan({
|
||||
}
|
||||
|
||||
export function normalizeTaskHalStatus(status = {}) {
|
||||
const motion = status.motionStatus?.motion || {};
|
||||
const axis = status.motionStatus?.axis || {};
|
||||
rejectLegacyTaskHalStatusFields(status);
|
||||
const emcStatus = requireStandaloneEmcStatus(status);
|
||||
const emcTask = emcStatus.task;
|
||||
const emcMotion = emcStatus.motion;
|
||||
const emcTraj = emcMotion.traj || {};
|
||||
const task = normalizeStatusTask(emcTask);
|
||||
const axis = normalizeStatusAxis(emcMotion);
|
||||
const motion = normalizeStatusMotion(emcMotion, emcTraj, task);
|
||||
const commandQueueDepth = firstDefined(emcMotion.commandQueueDepth, emcTraj.queue, 0);
|
||||
const statusCycle = firstDefined(emcMotion.cycle, emcTraj.cycle, Number(task.cycle || 0) * 10, 0);
|
||||
const halPins = status.halSnapshot?.pins || {};
|
||||
const motionProgramLine = Number(motion.programLine || 0);
|
||||
const halProgramLine = Number(halPins["motion.program-line"]?.value || 0);
|
||||
const motionQueueDepth = Number(
|
||||
status.motionStatus?.commandQueueDepth ??
|
||||
commandQueueDepth ??
|
||||
motion.commandQueueDepth ??
|
||||
status.motionStatus?.queueDepth ??
|
||||
motion.queueDepth ??
|
||||
0,
|
||||
);
|
||||
@@ -229,45 +236,49 @@ export function normalizeTaskHalStatus(status = {}) {
|
||||
: halProgramLine > 0
|
||||
? halProgramLine
|
||||
: 1;
|
||||
const taskRuntimeReady = true;
|
||||
const taskMotionReady = emcMotion.valid === true;
|
||||
const halProgramLineReady = Boolean(halPins["motion.program-line"]);
|
||||
const halRuntimeReady = Boolean(status.halSnapshot?.halRuntimeReady ?? status.halSnapshot?.ready ?? true);
|
||||
return {
|
||||
...status,
|
||||
semanticBoundary: SEMANTIC_BOUNDARY,
|
||||
summary: {
|
||||
taskRuntimeReady: status.taskRuntimeReady === true,
|
||||
motionRuntimeReady: status.motionStatus?.motionHalSyncReady === true || status.taskCommandsDriveMotionRuntime === true,
|
||||
halRuntimeReady: Boolean(status.halSnapshot?.halRuntimeReady ?? status.halSnapshot?.ready ?? true),
|
||||
halSyncReady: status.taskCommandsDriveMotionRuntime === true && Boolean(halPins["motion.program-line"]),
|
||||
taskHalComparisonReady: status.taskRuntimeReady === true && status.taskCommandsDriveMotionRuntime === true,
|
||||
taskRuntimeReady,
|
||||
motionRuntimeReady: taskMotionReady,
|
||||
halRuntimeReady,
|
||||
halSyncReady: taskMotionReady && halProgramLineReady,
|
||||
taskHalComparisonReady: taskRuntimeReady && taskMotionReady,
|
||||
switchkinsRemapHalSync: Boolean(halPins["motion.switchkins-type"]),
|
||||
nativeTaskReady: status.taskRuntimeReady === true,
|
||||
nativeHalSyncReady: status.taskCommandsDriveMotionRuntime === true && Boolean(halPins["motion.program-line"]),
|
||||
nativeTaskReady: taskRuntimeReady,
|
||||
nativeHalSyncReady: taskMotionReady && halProgramLineReady,
|
||||
fullLinuxCncProgramExecutionReady: false,
|
||||
hardwareDrive: false,
|
||||
hostRealtimeKernel: false,
|
||||
},
|
||||
ui: {
|
||||
taskState: String(status.task?.state || "ESTOP").toLowerCase(),
|
||||
taskMode: String(status.task?.mode || "MANUAL").toLowerCase(),
|
||||
interpState: String(status.task?.interpState || "IDLE").toLowerCase(),
|
||||
interpResumeState: String(status.task?.interpResumeState || status.task?.interpState || "IDLE").toLowerCase(),
|
||||
execState: String(status.task?.execState || "DONE").toLowerCase(),
|
||||
taskPaused: status.task?.taskPaused === true,
|
||||
singleStepping: status.task?.singleStepping === true,
|
||||
taskCycle: Number(status.task?.taskCycle ?? status.taskCycle ?? 0),
|
||||
programOpen: status.task?.programOpen === true,
|
||||
programFile: status.task?.file || status.task?.openProgram || null,
|
||||
programStartLine: Number(status.task?.programStartLine || 0),
|
||||
planId: Number(status.task?.planId || 0),
|
||||
motionPlanLoaded: status.task?.motionPlanLoaded === true,
|
||||
errorText: status.task?.errorText || null,
|
||||
allHomed: status.task?.allHomed === true,
|
||||
homed: normalizeTaskHalHomed(status.task?.homed, status.task?.allHomed === true),
|
||||
noForceHoming: status.task?.noForceHoming === true,
|
||||
homing: status.task?.homing === true,
|
||||
homeState: String(status.task?.homeState || "UNHOMED").toLowerCase(),
|
||||
motionEnabled: String(status.task?.state || "").toUpperCase() === "ON" || motion.enabled === true,
|
||||
resumeInhibit: status.task?.resumeInhibit === true,
|
||||
servoCycle: Number(status.motionStatus?.cycle ?? status.task?.servoCycle ?? 0),
|
||||
taskState: String(task.state || "ESTOP").toLowerCase(),
|
||||
taskMode: String(task.mode || "MANUAL").toLowerCase(),
|
||||
interpState: String(task.interpState || "IDLE").toLowerCase(),
|
||||
interpResumeState: String(task.interpResumeState || task.interpState || "IDLE").toLowerCase(),
|
||||
execState: String(task.execState || "DONE").toLowerCase(),
|
||||
taskPaused: task.taskPaused === true,
|
||||
singleStepping: task.singleStepping === true,
|
||||
taskCycle: Number(task.taskCycle ?? 0),
|
||||
programOpen: task.programOpen === true,
|
||||
programFile: task.file || task.openProgram || null,
|
||||
programStartLine: Number(task.programStartLine || 0),
|
||||
planId: Number(task.planId || 0),
|
||||
motionPlanLoaded: task.motionPlanLoaded === true,
|
||||
errorText: task.errorText || null,
|
||||
allHomed: task.allHomed === true,
|
||||
homed: normalizeTaskHalHomed(task.homed, task.allHomed === true),
|
||||
noForceHoming: task.noForceHoming === true,
|
||||
homing: task.homing === true,
|
||||
homeState: String(task.homeState || "UNHOMED").toLowerCase(),
|
||||
motionEnabled: String(task.state || "").toUpperCase() === "ON" || motion.enabled === true,
|
||||
resumeInhibit: task.resumeInhibit === true,
|
||||
servoCycle: Number(statusCycle ?? task.servoCycle ?? 0),
|
||||
motionQueueDepth,
|
||||
motionPaused: motion.paused === true,
|
||||
motionStepping: motion.stepping === true,
|
||||
@@ -279,25 +290,92 @@ export function normalizeTaskHalStatus(status = {}) {
|
||||
activeLine,
|
||||
motionProgramLine,
|
||||
halProgramLine,
|
||||
activeLineSource: motionProgramLine > 0 ? "motion-status" : halProgramLine > 0 ? "hal-pin" : "fallback",
|
||||
activeLineSource: motionProgramLine > 0 ? "emc-status" : halProgramLine > 0 ? "hal-pin" : "fallback",
|
||||
activeLineHalSynced: motionProgramLine > 0 && halProgramLine > 0
|
||||
? motionProgramLine === halProgramLine
|
||||
: false,
|
||||
switchkinsType: Number(motion.switchkinsType ?? halPins["motion.switchkins-type"]?.value ?? 0),
|
||||
axisPose: {
|
||||
x: Number(axis.x ?? halPins["axis.0.pos-cmd"]?.value ?? 0),
|
||||
y: Number(axis.y ?? halPins["axis.1.pos-cmd"]?.value ?? 0),
|
||||
z: Number(axis.z ?? halPins["axis.2.pos-cmd"]?.value ?? 0),
|
||||
a: Number(axis.a ?? halPins["axis.3.pos-cmd"]?.value ?? 0),
|
||||
b: Number(axis.b ?? halPins["axis.4.pos-cmd"]?.value ?? 0),
|
||||
c: Number(axis.c ?? halPins["axis.5.pos-cmd"]?.value ?? 0),
|
||||
x: Number(axis.x ?? 0),
|
||||
y: Number(axis.y ?? 0),
|
||||
z: Number(axis.z ?? 0),
|
||||
a: Number(axis.a ?? 0),
|
||||
b: Number(axis.b ?? 0),
|
||||
c: Number(axis.c ?? 0),
|
||||
},
|
||||
axisPoseFrame: isJogMotion(motion) ? "task-local" : "work",
|
||||
currentVelocity: Number(motion.currentVel || motion.currentVelocity || 0) * 60,
|
||||
currentVelocity: Number(motion.currentVel ?? motion.currentVelocity ?? 0) * 60,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function rejectLegacyTaskHalStatusFields(status = {}) {
|
||||
const legacyFields = ["taskTopLevelStatus", "rcsStatus", "task", "servoCycle", "motionStatus"];
|
||||
const present = legacyFields.filter((field) => Object.hasOwn(status, field));
|
||||
if (present.length > 0) {
|
||||
throw new Error(`Task/HAL status contains removed legacy fields: ${present.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function requireStandaloneEmcStatus(status = {}) {
|
||||
const emcStatus = status.emcStatus;
|
||||
if (!emcStatus || typeof emcStatus !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus");
|
||||
}
|
||||
if (!emcStatus.task || typeof emcStatus.task !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus.task");
|
||||
}
|
||||
if (!emcStatus.motion || typeof emcStatus.motion !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus.motion");
|
||||
}
|
||||
return emcStatus;
|
||||
}
|
||||
|
||||
function normalizeStatusTask(emcTask = {}) {
|
||||
return {
|
||||
...emcTask,
|
||||
taskCycle: firstDefined(emcTask.taskCycle, emcTask.cycle, 0),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStatusMotion(emcMotion = {}, emcTraj = {}, task = {}) {
|
||||
return {
|
||||
...emcMotion,
|
||||
enabled: firstDefined(emcMotion.enabled, emcTraj.enabled),
|
||||
inPosition: firstDefined(emcMotion.inPosition, emcTraj.inpos),
|
||||
queueDepth: firstDefined(emcMotion.queueDepth, emcTraj.queue),
|
||||
commandQueueDepth: firstDefined(emcMotion.commandQueueDepth, emcTraj.queue),
|
||||
activeDepth: firstDefined(emcMotion.activeDepth, emcTraj.activeQueue),
|
||||
queueFull: firstDefined(emcMotion.queueFull, emcTraj.queueFull),
|
||||
id: firstDefined(emcTraj.id, emcMotion.motionId),
|
||||
motionId: firstDefined(emcMotion.motionId, emcTraj.id),
|
||||
paused: firstDefined(emcTraj.paused, emcMotion.paused),
|
||||
stepping: firstDefined(emcTraj.singleStepping, emcMotion.stepping),
|
||||
currentVel: firstDefined(emcTraj.currentVel, emcMotion.currentVel),
|
||||
programLine: firstDefined(emcMotion.programLine, task.motionLine),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStatusAxis(emcMotion = {}) {
|
||||
const axisByName = emcMotion.axisByName || {};
|
||||
const actualPosition = emcMotion.traj?.actualPosition || {};
|
||||
const position = emcMotion.traj?.position || {};
|
||||
const axisArray = Array.isArray(emcMotion.axis) ? emcMotion.axis : [];
|
||||
const byLetter = new Map(axisArray.map((axis) => [String(axis.letter || "").toLowerCase(), axis]));
|
||||
return {
|
||||
x: firstDefined(axisByName.x, actualPosition.x, byLetter.get("x")?.posFb, position.x),
|
||||
y: firstDefined(axisByName.y, actualPosition.y, byLetter.get("y")?.posFb, position.y),
|
||||
z: firstDefined(axisByName.z, actualPosition.z, byLetter.get("z")?.posFb, position.z),
|
||||
a: firstDefined(axisByName.a, actualPosition.a, byLetter.get("a")?.posFb, position.a),
|
||||
b: firstDefined(axisByName.b, actualPosition.b, byLetter.get("b")?.posFb, position.b),
|
||||
c: firstDefined(axisByName.c, actualPosition.c, byLetter.get("c")?.posFb, position.c),
|
||||
};
|
||||
}
|
||||
|
||||
function firstDefined(...values) {
|
||||
return values.find((value) => value !== undefined && value !== null);
|
||||
}
|
||||
|
||||
function normalizeTaskHalHomed(value, allHomed = false) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(Boolean);
|
||||
|
||||
@@ -80,28 +80,28 @@ export function createLinuxCncTaskPolicyStatus(state) {
|
||||
const machineFileOpened = profileId === "gmoccapy-xyzab"
|
||||
? Boolean(machineFileStaging.selectedGcodeSourceRel)
|
||||
: Boolean(machineFileStaging.selectedGcodeSourceRel || state.activeProgram);
|
||||
const taskProgramOpen = Boolean(state.taskHalStatus?.ui?.programOpen || state.taskHalStatus?.task?.programOpen);
|
||||
const taskMotionPlanLoaded = Boolean(state.taskHalStatus?.ui?.motionPlanLoaded || state.taskHalStatus?.task?.motionPlanLoaded);
|
||||
const taskProgramOpen = Boolean(state.taskHalStatus?.ui?.programOpen || state.taskHalStatus?.emcStatus?.task?.programOpen);
|
||||
const taskMotionPlanLoaded = Boolean(state.taskHalStatus?.ui?.motionPlanLoaded || state.taskHalStatus?.emcStatus?.task?.motionPlanLoaded);
|
||||
const interpIdle = interpState === "idle";
|
||||
const motionPaused = state.machine?.motionPaused === true ||
|
||||
state.taskHalStatus?.ui?.motionPaused === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.paused === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.paused === true;
|
||||
const taskPaused = state.machine?.taskPaused === true ||
|
||||
state.taskHalStatus?.ui?.taskPaused === true ||
|
||||
state.taskHalStatus?.task?.taskPaused === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.taskPaused === true;
|
||||
const motionEnabled = taskState === "on" ||
|
||||
state.machine?.motionEnabled === true ||
|
||||
state.taskHalStatus?.ui?.motionEnabled === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.enabled === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.enabled === true;
|
||||
const singleStepping = state.machine?.singleStepping === true ||
|
||||
state.taskHalStatus?.ui?.singleStepping === true ||
|
||||
state.taskHalStatus?.task?.singleStepping === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.singleStepping === true;
|
||||
const motionStepping = state.machine?.motionStepping === true ||
|
||||
state.taskHalStatus?.ui?.motionStepping === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.stepping === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.stepping === true;
|
||||
const resumeInhibit = state.machine?.resumeInhibit === true ||
|
||||
state.taskHalStatus?.ui?.resumeInhibit === true ||
|
||||
state.taskHalStatus?.task?.resumeInhibit === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.resumeInhibit === true;
|
||||
const feedbackPaused = state.programRuntimeFeedback?.paused === true;
|
||||
const paused = motionPaused || taskPaused || feedbackPaused || interpState === "paused" || state.runState === "paused";
|
||||
const running = taskMode === "auto" &&
|
||||
|
||||
@@ -1625,7 +1625,11 @@ export function createSimulationStore(seed = {}) {
|
||||
], {
|
||||
operatorMessage: `task/HAL MDI ${command}`,
|
||||
preserveAxisPose: mdiPatch.axisPose,
|
||||
preserveMachine: mdiPatch.machine,
|
||||
preserveMachine: {
|
||||
...mdiPatch.machine,
|
||||
kinsType: mdiPatch.kinsType,
|
||||
rtcpState: mdiPatch.rtcpState,
|
||||
},
|
||||
}).catch(() => {});
|
||||
break;
|
||||
}
|
||||
@@ -3459,7 +3463,7 @@ function createLinuxCncProcessMonitor(state = {}) {
|
||||
const uiExecution = state.programUiExecution || createProgramUiExecution(state);
|
||||
const status = state.taskHalStatus || {};
|
||||
const ui = status.ui || {};
|
||||
const motion = status.motionStatus?.motion || {};
|
||||
const motion = taskHalMotion(status);
|
||||
const halPins = status.halSnapshot?.pins || {};
|
||||
const toolRuntime = state.toolRuntimeState || {};
|
||||
const toolDb = state.toolDbSimulation || {};
|
||||
@@ -3487,7 +3491,7 @@ function createLinuxCncProcessMonitor(state = {}) {
|
||||
? state.programLines?.[sourceLine - Number(state.programStartLine || 1)] || ""
|
||||
: "");
|
||||
const currentVelocity = Number(feedback.currentVelocityMmPerMin ?? state.feed?.currentVelocity ?? ui.currentVelocity ?? 0);
|
||||
const requestedVelocity = Number(feedback.requestedVelocityMmPerMin ?? motion.requestedVel * 60 ?? currentVelocity);
|
||||
const requestedVelocity = Number(feedback.requestedVelocityMmPerMin ?? (Number(motion.requestedVel || 0) * 60) ?? currentVelocity);
|
||||
const feedRate = Number(state.feed?.feedRate || currentVelocity || 0);
|
||||
const spindleCommandRpm = Number(state.spindle?.rpm || 0);
|
||||
const spindleActualRpm = state.spindle?.enabled
|
||||
@@ -3929,11 +3933,13 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
preserveMachine = null,
|
||||
preserveAxisPose = null,
|
||||
} = {}) {
|
||||
rejectLegacyTaskHalStatusFields(status);
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
const statusInterpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const statusMotionPaused = ui.motionPaused === true || motion.paused === true;
|
||||
const statusMotionPaused = ui.motionPaused === true || motion.paused === true || traj.paused === true;
|
||||
const statusTaskPaused = ui.taskPaused === true || task.taskPaused === true;
|
||||
const statusPaused = statusInterpState === "paused" || statusMotionPaused || statusTaskPaused;
|
||||
const rawTaskState = normalizeTaskHalTaskState(ui.taskState || task.state);
|
||||
@@ -3981,7 +3987,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
(allHomed ? "homed" : "unhomed"),
|
||||
);
|
||||
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine, preserveMachine?.kinsType);
|
||||
const motionPaused = statusMotionPaused || preserveMachine?.motionPaused === true;
|
||||
const taskPaused = statusTaskPaused || preserveMachine?.taskPaused === true;
|
||||
const paused = interpState === "paused" || motionPaused || taskPaused;
|
||||
@@ -3990,6 +3996,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
preserveMachine?.singleStepping === true;
|
||||
const motionStepping = ui.motionStepping === true ||
|
||||
motion.stepping === true ||
|
||||
traj.singleStepping === true ||
|
||||
preserveMachine?.motionStepping === true;
|
||||
const interpResumeState = normalizeTaskHalInterpState(
|
||||
ui.interpResumeState ||
|
||||
@@ -4166,8 +4173,9 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
|
||||
function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
const interpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const taskMode = normalizeLinuxCncTaskMode(ui.taskMode || task.mode || state.machine?.mode);
|
||||
const aborted = motion.aborted === true;
|
||||
@@ -4175,6 +4183,7 @@ function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
const openedProgramLineCount = Number(task.openedSourceLineCount || task.openedLineCount || 1);
|
||||
const complete = interpState === "idle"
|
||||
@@ -4190,12 +4199,14 @@ function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
|
||||
function isTaskHalStatusPaused(status = {}) {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
return normalizeTaskHalInterpState(ui.interpState || task.interpState) === "paused" ||
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
}
|
||||
|
||||
@@ -4301,7 +4312,10 @@ function createStoppedProgramStatePatch(state, {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveTaskHalKinsType(state, status, activeLine) {
|
||||
function resolveTaskHalKinsType(state, status, activeLine, preserveKinsType = null) {
|
||||
if (preserveKinsType) {
|
||||
return preserveKinsType;
|
||||
}
|
||||
const ui = status?.ui || {};
|
||||
const numeric = Number(ui.switchkinsType);
|
||||
if (Number.isFinite(numeric) && numeric !== 0) {
|
||||
@@ -4381,7 +4395,7 @@ function addAxisDelta(axisPose, delta, profile) {
|
||||
}
|
||||
|
||||
function isJogStatus(status) {
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const motion = taskHalMotion(status);
|
||||
return Number(motion.motionType) === 3 || Number(motion.teleopMode) === 1 || motion.teleopMode === true;
|
||||
}
|
||||
|
||||
@@ -4394,17 +4408,21 @@ function wouldResetNonZeroPoseToLocalZero(currentPose = {}, nextPose = {}) {
|
||||
|
||||
function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
const ui = status?.ui || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
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 interpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const paused = interpState === "paused" ||
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
status?.task?.taskPaused === true;
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
const currentVelocity = paused ? 0 : Number(ui.currentVelocity || 0);
|
||||
const requestedVelocity = paused ? 0 : Number(motion.requestedVel || 0) * 60;
|
||||
const motionInPosition = motion.inPosition === true || traj.inpos === true;
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-program-runtime-feedback",
|
||||
sourceMode: "linuxcnc-task-motion-hal-wasm",
|
||||
@@ -4414,7 +4432,7 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
line: activeLine,
|
||||
motionProgramLine,
|
||||
halProgramLine,
|
||||
activeLineSource: ui.activeLineSource || (motionProgramLine > 0 ? "motion-status" : halProgramLine > 0 ? "hal-pin" : "fallback"),
|
||||
activeLineSource: ui.activeLineSource || (motionProgramLine > 0 ? "emc-status" : halProgramLine > 0 ? "hal-pin" : "fallback"),
|
||||
activeLineHalSynced: motionProgramLine > 0 && halProgramLine > 0 && motionProgramLine === halProgramLine,
|
||||
type: Number(motion.motionType || 0) === 3 ? "JOG" : "TASK_MOTION",
|
||||
paused,
|
||||
@@ -4422,16 +4440,32 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: currentVelocity,
|
||||
requestedVelocityMmPerMin: requestedVelocity,
|
||||
distanceToGo: motion.inPosition === true ? 0 : 1,
|
||||
distanceToGo: motionInPosition ? 0 : 1,
|
||||
dtg: { x: 0, y: 0, z: 0 },
|
||||
queueDepth: Number(ui.motionQueueDepth || status?.motionStatus?.commandQueueDepth || 0),
|
||||
activeDepth: motion.inPosition === true ? 0 : 1,
|
||||
cycle: Number(ui.servoCycle || status?.servoCycle || 0),
|
||||
taskCycle: Number(ui.taskCycle || status?.task?.cycle || 0),
|
||||
queueDepth: Number(ui.motionQueueDepth || motion.commandQueueDepth || traj.queue || 0),
|
||||
activeDepth: motionInPosition ? 0 : 1,
|
||||
cycle: Number(ui.servoCycle || 0),
|
||||
taskCycle: Number(ui.taskCycle || task.cycle || 0),
|
||||
halChangedPinCount: Number(ui.halChangedPinCount || 0),
|
||||
};
|
||||
}
|
||||
|
||||
function taskHalTask(status = {}) {
|
||||
return status?.emcStatus?.task || {};
|
||||
}
|
||||
|
||||
function taskHalMotion(status = {}) {
|
||||
return status?.emcStatus?.motion || {};
|
||||
}
|
||||
|
||||
function rejectLegacyTaskHalStatusFields(status = {}) {
|
||||
const legacyFields = ["taskTopLevelStatus", "rcsStatus", "task", "servoCycle", "motionStatus"];
|
||||
const present = legacyFields.filter((field) => Object.hasOwn(status, field));
|
||||
if (present.length > 0) {
|
||||
throw new Error(`Task/HAL status contains removed legacy fields: ${present.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function buildTaskHalProgramMotionPlanFromPreviewPath({
|
||||
programPath = null,
|
||||
path = null,
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -9,6 +9,7 @@
|
||||
"smoke": "npm run smoke:node",
|
||||
"smoke:node": "node ../tests/node/verify_xyzbc_trt_web_app.mjs",
|
||||
"smoke:browser": "bash ../tests/browser/verify_xyzbc_trt_browser.sh",
|
||||
"smoke:buttons": "bash ../tests/browser/verify_xyzbc_trt_all_buttons.sh",
|
||||
"evidence:web": "node ../tools/collect-web-xyzbc-trt-evidence.mjs",
|
||||
"evidence:compare": "node ../tools/compare-xyzbc-trt-evidence.mjs"
|
||||
},
|
||||
|
||||
@@ -212,15 +212,22 @@ export function buildTaskHalProgramMotionPlan({
|
||||
}
|
||||
|
||||
export function normalizeTaskHalStatus(status = {}) {
|
||||
const motion = status.motionStatus?.motion || {};
|
||||
const axis = status.motionStatus?.axis || {};
|
||||
rejectLegacyTaskHalStatusFields(status);
|
||||
const emcStatus = requireStandaloneEmcStatus(status);
|
||||
const emcTask = emcStatus.task;
|
||||
const emcMotion = emcStatus.motion;
|
||||
const emcTraj = emcMotion.traj || {};
|
||||
const task = normalizeStatusTask(emcTask);
|
||||
const axis = normalizeStatusAxis(emcMotion);
|
||||
const motion = normalizeStatusMotion(emcMotion, emcTraj, task);
|
||||
const commandQueueDepth = firstDefined(emcMotion.commandQueueDepth, emcTraj.queue, 0);
|
||||
const statusCycle = firstDefined(emcMotion.cycle, emcTraj.cycle, Number(task.cycle || 0) * 10, 0);
|
||||
const halPins = status.halSnapshot?.pins || {};
|
||||
const motionProgramLine = Number(motion.programLine || 0);
|
||||
const halProgramLine = Number(halPins["motion.program-line"]?.value || 0);
|
||||
const motionQueueDepth = Number(
|
||||
status.motionStatus?.commandQueueDepth ??
|
||||
commandQueueDepth ??
|
||||
motion.commandQueueDepth ??
|
||||
status.motionStatus?.queueDepth ??
|
||||
motion.queueDepth ??
|
||||
0,
|
||||
);
|
||||
@@ -229,45 +236,49 @@ export function normalizeTaskHalStatus(status = {}) {
|
||||
: halProgramLine > 0
|
||||
? halProgramLine
|
||||
: 1;
|
||||
const taskRuntimeReady = true;
|
||||
const taskMotionReady = emcMotion.valid === true;
|
||||
const halProgramLineReady = Boolean(halPins["motion.program-line"]);
|
||||
const halRuntimeReady = Boolean(status.halSnapshot?.halRuntimeReady ?? status.halSnapshot?.ready ?? true);
|
||||
return {
|
||||
...status,
|
||||
semanticBoundary: SEMANTIC_BOUNDARY,
|
||||
summary: {
|
||||
taskRuntimeReady: status.taskRuntimeReady === true,
|
||||
motionRuntimeReady: status.motionStatus?.motionHalSyncReady === true || status.taskCommandsDriveMotionRuntime === true,
|
||||
halRuntimeReady: Boolean(status.halSnapshot?.halRuntimeReady ?? status.halSnapshot?.ready ?? true),
|
||||
halSyncReady: status.taskCommandsDriveMotionRuntime === true && Boolean(halPins["motion.program-line"]),
|
||||
taskHalComparisonReady: status.taskRuntimeReady === true && status.taskCommandsDriveMotionRuntime === true,
|
||||
taskRuntimeReady,
|
||||
motionRuntimeReady: taskMotionReady,
|
||||
halRuntimeReady,
|
||||
halSyncReady: taskMotionReady && halProgramLineReady,
|
||||
taskHalComparisonReady: taskRuntimeReady && taskMotionReady,
|
||||
switchkinsRemapHalSync: Boolean(halPins["motion.switchkins-type"]),
|
||||
nativeTaskReady: status.taskRuntimeReady === true,
|
||||
nativeHalSyncReady: status.taskCommandsDriveMotionRuntime === true && Boolean(halPins["motion.program-line"]),
|
||||
nativeTaskReady: taskRuntimeReady,
|
||||
nativeHalSyncReady: taskMotionReady && halProgramLineReady,
|
||||
fullLinuxCncProgramExecutionReady: false,
|
||||
hardwareDrive: false,
|
||||
hostRealtimeKernel: false,
|
||||
},
|
||||
ui: {
|
||||
taskState: String(status.task?.state || "ESTOP").toLowerCase(),
|
||||
taskMode: String(status.task?.mode || "MANUAL").toLowerCase(),
|
||||
interpState: String(status.task?.interpState || "IDLE").toLowerCase(),
|
||||
interpResumeState: String(status.task?.interpResumeState || status.task?.interpState || "IDLE").toLowerCase(),
|
||||
execState: String(status.task?.execState || "DONE").toLowerCase(),
|
||||
taskPaused: status.task?.taskPaused === true,
|
||||
singleStepping: status.task?.singleStepping === true,
|
||||
taskCycle: Number(status.task?.taskCycle ?? status.taskCycle ?? 0),
|
||||
programOpen: status.task?.programOpen === true,
|
||||
programFile: status.task?.file || status.task?.openProgram || null,
|
||||
programStartLine: Number(status.task?.programStartLine || 0),
|
||||
planId: Number(status.task?.planId || 0),
|
||||
motionPlanLoaded: status.task?.motionPlanLoaded === true,
|
||||
errorText: status.task?.errorText || null,
|
||||
allHomed: status.task?.allHomed === true,
|
||||
homed: normalizeTaskHalHomed(status.task?.homed, status.task?.allHomed === true),
|
||||
noForceHoming: status.task?.noForceHoming === true,
|
||||
homing: status.task?.homing === true,
|
||||
homeState: String(status.task?.homeState || "UNHOMED").toLowerCase(),
|
||||
motionEnabled: String(status.task?.state || "").toUpperCase() === "ON" || motion.enabled === true,
|
||||
resumeInhibit: status.task?.resumeInhibit === true,
|
||||
servoCycle: Number(status.motionStatus?.cycle ?? status.task?.servoCycle ?? 0),
|
||||
taskState: String(task.state || "ESTOP").toLowerCase(),
|
||||
taskMode: String(task.mode || "MANUAL").toLowerCase(),
|
||||
interpState: String(task.interpState || "IDLE").toLowerCase(),
|
||||
interpResumeState: String(task.interpResumeState || task.interpState || "IDLE").toLowerCase(),
|
||||
execState: String(task.execState || "DONE").toLowerCase(),
|
||||
taskPaused: task.taskPaused === true,
|
||||
singleStepping: task.singleStepping === true,
|
||||
taskCycle: Number(task.taskCycle ?? 0),
|
||||
programOpen: task.programOpen === true,
|
||||
programFile: task.file || task.openProgram || null,
|
||||
programStartLine: Number(task.programStartLine || 0),
|
||||
planId: Number(task.planId || 0),
|
||||
motionPlanLoaded: task.motionPlanLoaded === true,
|
||||
errorText: task.errorText || null,
|
||||
allHomed: task.allHomed === true,
|
||||
homed: normalizeTaskHalHomed(task.homed, task.allHomed === true),
|
||||
noForceHoming: task.noForceHoming === true,
|
||||
homing: task.homing === true,
|
||||
homeState: String(task.homeState || "UNHOMED").toLowerCase(),
|
||||
motionEnabled: String(task.state || "").toUpperCase() === "ON" || motion.enabled === true,
|
||||
resumeInhibit: task.resumeInhibit === true,
|
||||
servoCycle: Number(statusCycle ?? task.servoCycle ?? 0),
|
||||
motionQueueDepth,
|
||||
motionPaused: motion.paused === true,
|
||||
motionStepping: motion.stepping === true,
|
||||
@@ -279,25 +290,92 @@ export function normalizeTaskHalStatus(status = {}) {
|
||||
activeLine,
|
||||
motionProgramLine,
|
||||
halProgramLine,
|
||||
activeLineSource: motionProgramLine > 0 ? "motion-status" : halProgramLine > 0 ? "hal-pin" : "fallback",
|
||||
activeLineSource: motionProgramLine > 0 ? "emc-status" : halProgramLine > 0 ? "hal-pin" : "fallback",
|
||||
activeLineHalSynced: motionProgramLine > 0 && halProgramLine > 0
|
||||
? motionProgramLine === halProgramLine
|
||||
: false,
|
||||
switchkinsType: Number(motion.switchkinsType ?? halPins["motion.switchkins-type"]?.value ?? 0),
|
||||
axisPose: {
|
||||
x: Number(axis.x ?? halPins["axis.0.pos-cmd"]?.value ?? 0),
|
||||
y: Number(axis.y ?? halPins["axis.1.pos-cmd"]?.value ?? 0),
|
||||
z: Number(axis.z ?? halPins["axis.2.pos-cmd"]?.value ?? 0),
|
||||
a: Number(axis.a ?? halPins["axis.3.pos-cmd"]?.value ?? 0),
|
||||
b: Number(axis.b ?? halPins["axis.4.pos-cmd"]?.value ?? 0),
|
||||
c: Number(axis.c ?? halPins["axis.5.pos-cmd"]?.value ?? 0),
|
||||
x: Number(axis.x ?? 0),
|
||||
y: Number(axis.y ?? 0),
|
||||
z: Number(axis.z ?? 0),
|
||||
a: Number(axis.a ?? 0),
|
||||
b: Number(axis.b ?? 0),
|
||||
c: Number(axis.c ?? 0),
|
||||
},
|
||||
axisPoseFrame: isJogMotion(motion) ? "task-local" : "work",
|
||||
currentVelocity: Number(motion.currentVel || motion.currentVelocity || 0) * 60,
|
||||
currentVelocity: Number(motion.currentVel ?? motion.currentVelocity ?? 0) * 60,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function rejectLegacyTaskHalStatusFields(status = {}) {
|
||||
const legacyFields = ["taskTopLevelStatus", "rcsStatus", "task", "servoCycle", "motionStatus"];
|
||||
const present = legacyFields.filter((field) => Object.hasOwn(status, field));
|
||||
if (present.length > 0) {
|
||||
throw new Error(`Task/HAL status contains removed legacy fields: ${present.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function requireStandaloneEmcStatus(status = {}) {
|
||||
const emcStatus = status.emcStatus;
|
||||
if (!emcStatus || typeof emcStatus !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus");
|
||||
}
|
||||
if (!emcStatus.task || typeof emcStatus.task !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus.task");
|
||||
}
|
||||
if (!emcStatus.motion || typeof emcStatus.motion !== "object") {
|
||||
throw new Error("Task/HAL status requires StandaloneEmcStatus emcStatus.motion");
|
||||
}
|
||||
return emcStatus;
|
||||
}
|
||||
|
||||
function normalizeStatusTask(emcTask = {}) {
|
||||
return {
|
||||
...emcTask,
|
||||
taskCycle: firstDefined(emcTask.taskCycle, emcTask.cycle, 0),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStatusMotion(emcMotion = {}, emcTraj = {}, task = {}) {
|
||||
return {
|
||||
...emcMotion,
|
||||
enabled: firstDefined(emcMotion.enabled, emcTraj.enabled),
|
||||
inPosition: firstDefined(emcMotion.inPosition, emcTraj.inpos),
|
||||
queueDepth: firstDefined(emcMotion.queueDepth, emcTraj.queue),
|
||||
commandQueueDepth: firstDefined(emcMotion.commandQueueDepth, emcTraj.queue),
|
||||
activeDepth: firstDefined(emcMotion.activeDepth, emcTraj.activeQueue),
|
||||
queueFull: firstDefined(emcMotion.queueFull, emcTraj.queueFull),
|
||||
id: firstDefined(emcTraj.id, emcMotion.motionId),
|
||||
motionId: firstDefined(emcMotion.motionId, emcTraj.id),
|
||||
paused: firstDefined(emcTraj.paused, emcMotion.paused),
|
||||
stepping: firstDefined(emcTraj.singleStepping, emcMotion.stepping),
|
||||
currentVel: firstDefined(emcTraj.currentVel, emcMotion.currentVel),
|
||||
programLine: firstDefined(emcMotion.programLine, task.motionLine),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStatusAxis(emcMotion = {}) {
|
||||
const axisByName = emcMotion.axisByName || {};
|
||||
const actualPosition = emcMotion.traj?.actualPosition || {};
|
||||
const position = emcMotion.traj?.position || {};
|
||||
const axisArray = Array.isArray(emcMotion.axis) ? emcMotion.axis : [];
|
||||
const byLetter = new Map(axisArray.map((axis) => [String(axis.letter || "").toLowerCase(), axis]));
|
||||
return {
|
||||
x: firstDefined(axisByName.x, actualPosition.x, byLetter.get("x")?.posFb, position.x),
|
||||
y: firstDefined(axisByName.y, actualPosition.y, byLetter.get("y")?.posFb, position.y),
|
||||
z: firstDefined(axisByName.z, actualPosition.z, byLetter.get("z")?.posFb, position.z),
|
||||
a: firstDefined(axisByName.a, actualPosition.a, byLetter.get("a")?.posFb, position.a),
|
||||
b: firstDefined(axisByName.b, actualPosition.b, byLetter.get("b")?.posFb, position.b),
|
||||
c: firstDefined(axisByName.c, actualPosition.c, byLetter.get("c")?.posFb, position.c),
|
||||
};
|
||||
}
|
||||
|
||||
function firstDefined(...values) {
|
||||
return values.find((value) => value !== undefined && value !== null);
|
||||
}
|
||||
|
||||
function normalizeTaskHalHomed(value, allHomed = false) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(Boolean);
|
||||
|
||||
@@ -80,28 +80,28 @@ export function createLinuxCncTaskPolicyStatus(state) {
|
||||
const machineFileOpened = profileId === "gmoccapy-xyzab"
|
||||
? Boolean(machineFileStaging.selectedGcodeSourceRel)
|
||||
: Boolean(machineFileStaging.selectedGcodeSourceRel || state.activeProgram);
|
||||
const taskProgramOpen = Boolean(state.taskHalStatus?.ui?.programOpen || state.taskHalStatus?.task?.programOpen);
|
||||
const taskMotionPlanLoaded = Boolean(state.taskHalStatus?.ui?.motionPlanLoaded || state.taskHalStatus?.task?.motionPlanLoaded);
|
||||
const taskProgramOpen = Boolean(state.taskHalStatus?.ui?.programOpen || state.taskHalStatus?.emcStatus?.task?.programOpen);
|
||||
const taskMotionPlanLoaded = Boolean(state.taskHalStatus?.ui?.motionPlanLoaded || state.taskHalStatus?.emcStatus?.task?.motionPlanLoaded);
|
||||
const interpIdle = interpState === "idle";
|
||||
const motionPaused = state.machine?.motionPaused === true ||
|
||||
state.taskHalStatus?.ui?.motionPaused === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.paused === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.paused === true;
|
||||
const taskPaused = state.machine?.taskPaused === true ||
|
||||
state.taskHalStatus?.ui?.taskPaused === true ||
|
||||
state.taskHalStatus?.task?.taskPaused === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.taskPaused === true;
|
||||
const motionEnabled = taskState === "on" ||
|
||||
state.machine?.motionEnabled === true ||
|
||||
state.taskHalStatus?.ui?.motionEnabled === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.enabled === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.enabled === true;
|
||||
const singleStepping = state.machine?.singleStepping === true ||
|
||||
state.taskHalStatus?.ui?.singleStepping === true ||
|
||||
state.taskHalStatus?.task?.singleStepping === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.singleStepping === true;
|
||||
const motionStepping = state.machine?.motionStepping === true ||
|
||||
state.taskHalStatus?.ui?.motionStepping === true ||
|
||||
state.taskHalStatus?.motionStatus?.motion?.stepping === true;
|
||||
state.taskHalStatus?.emcStatus?.motion?.stepping === true;
|
||||
const resumeInhibit = state.machine?.resumeInhibit === true ||
|
||||
state.taskHalStatus?.ui?.resumeInhibit === true ||
|
||||
state.taskHalStatus?.task?.resumeInhibit === true;
|
||||
state.taskHalStatus?.emcStatus?.task?.resumeInhibit === true;
|
||||
const feedbackPaused = state.programRuntimeFeedback?.paused === true;
|
||||
const paused = motionPaused || taskPaused || feedbackPaused || interpState === "paused" || state.runState === "paused";
|
||||
const running = taskMode === "auto" &&
|
||||
|
||||
@@ -1625,7 +1625,11 @@ export function createSimulationStore(seed = {}) {
|
||||
], {
|
||||
operatorMessage: `task/HAL MDI ${command}`,
|
||||
preserveAxisPose: mdiPatch.axisPose,
|
||||
preserveMachine: mdiPatch.machine,
|
||||
preserveMachine: {
|
||||
...mdiPatch.machine,
|
||||
kinsType: mdiPatch.kinsType,
|
||||
rtcpState: mdiPatch.rtcpState,
|
||||
},
|
||||
}).catch(() => {});
|
||||
break;
|
||||
}
|
||||
@@ -3459,7 +3463,7 @@ function createLinuxCncProcessMonitor(state = {}) {
|
||||
const uiExecution = state.programUiExecution || createProgramUiExecution(state);
|
||||
const status = state.taskHalStatus || {};
|
||||
const ui = status.ui || {};
|
||||
const motion = status.motionStatus?.motion || {};
|
||||
const motion = taskHalMotion(status);
|
||||
const halPins = status.halSnapshot?.pins || {};
|
||||
const toolRuntime = state.toolRuntimeState || {};
|
||||
const toolDb = state.toolDbSimulation || {};
|
||||
@@ -3487,7 +3491,7 @@ function createLinuxCncProcessMonitor(state = {}) {
|
||||
? state.programLines?.[sourceLine - Number(state.programStartLine || 1)] || ""
|
||||
: "");
|
||||
const currentVelocity = Number(feedback.currentVelocityMmPerMin ?? state.feed?.currentVelocity ?? ui.currentVelocity ?? 0);
|
||||
const requestedVelocity = Number(feedback.requestedVelocityMmPerMin ?? motion.requestedVel * 60 ?? currentVelocity);
|
||||
const requestedVelocity = Number(feedback.requestedVelocityMmPerMin ?? (Number(motion.requestedVel || 0) * 60) ?? currentVelocity);
|
||||
const feedRate = Number(state.feed?.feedRate || currentVelocity || 0);
|
||||
const spindleCommandRpm = Number(state.spindle?.rpm || 0);
|
||||
const spindleActualRpm = state.spindle?.enabled
|
||||
@@ -3929,11 +3933,13 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
preserveMachine = null,
|
||||
preserveAxisPose = null,
|
||||
} = {}) {
|
||||
rejectLegacyTaskHalStatusFields(status);
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
const statusInterpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const statusMotionPaused = ui.motionPaused === true || motion.paused === true;
|
||||
const statusMotionPaused = ui.motionPaused === true || motion.paused === true || traj.paused === true;
|
||||
const statusTaskPaused = ui.taskPaused === true || task.taskPaused === true;
|
||||
const statusPaused = statusInterpState === "paused" || statusMotionPaused || statusTaskPaused;
|
||||
const rawTaskState = normalizeTaskHalTaskState(ui.taskState || task.state);
|
||||
@@ -3981,7 +3987,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
(allHomed ? "homed" : "unhomed"),
|
||||
);
|
||||
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine, preserveMachine?.kinsType);
|
||||
const motionPaused = statusMotionPaused || preserveMachine?.motionPaused === true;
|
||||
const taskPaused = statusTaskPaused || preserveMachine?.taskPaused === true;
|
||||
const paused = interpState === "paused" || motionPaused || taskPaused;
|
||||
@@ -3990,6 +3996,7 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
preserveMachine?.singleStepping === true;
|
||||
const motionStepping = ui.motionStepping === true ||
|
||||
motion.stepping === true ||
|
||||
traj.singleStepping === true ||
|
||||
preserveMachine?.motionStepping === true;
|
||||
const interpResumeState = normalizeTaskHalInterpState(
|
||||
ui.interpResumeState ||
|
||||
@@ -4166,8 +4173,9 @@ function applyTaskHalStatusPatch(state, status, operatorMessage, {
|
||||
|
||||
function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
const interpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const taskMode = normalizeLinuxCncTaskMode(ui.taskMode || task.mode || state.machine?.mode);
|
||||
const aborted = motion.aborted === true;
|
||||
@@ -4175,6 +4183,7 @@ function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
const openedProgramLineCount = Number(task.openedSourceLineCount || task.openedLineCount || 1);
|
||||
const complete = interpState === "idle"
|
||||
@@ -4190,12 +4199,14 @@ function shouldContinueTaskHalStatusLoop(state = {}, status = {}) {
|
||||
|
||||
function isTaskHalStatusPaused(status = {}) {
|
||||
const ui = status?.ui || {};
|
||||
const task = status?.task || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
return normalizeTaskHalInterpState(ui.interpState || task.interpState) === "paused" ||
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
}
|
||||
|
||||
@@ -4301,7 +4312,10 @@ function createStoppedProgramStatePatch(state, {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveTaskHalKinsType(state, status, activeLine) {
|
||||
function resolveTaskHalKinsType(state, status, activeLine, preserveKinsType = null) {
|
||||
if (preserveKinsType) {
|
||||
return preserveKinsType;
|
||||
}
|
||||
const ui = status?.ui || {};
|
||||
const numeric = Number(ui.switchkinsType);
|
||||
if (Number.isFinite(numeric) && numeric !== 0) {
|
||||
@@ -4381,7 +4395,7 @@ function addAxisDelta(axisPose, delta, profile) {
|
||||
}
|
||||
|
||||
function isJogStatus(status) {
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const motion = taskHalMotion(status);
|
||||
return Number(motion.motionType) === 3 || Number(motion.teleopMode) === 1 || motion.teleopMode === true;
|
||||
}
|
||||
|
||||
@@ -4394,17 +4408,21 @@ function wouldResetNonZeroPoseToLocalZero(currentPose = {}, nextPose = {}) {
|
||||
|
||||
function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
const ui = status?.ui || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
const task = taskHalTask(status);
|
||||
const motion = taskHalMotion(status);
|
||||
const traj = motion.traj || {};
|
||||
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 interpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const paused = interpState === "paused" ||
|
||||
ui.motionPaused === true ||
|
||||
ui.taskPaused === true ||
|
||||
motion.paused === true ||
|
||||
status?.task?.taskPaused === true;
|
||||
traj.paused === true ||
|
||||
task.taskPaused === true;
|
||||
const currentVelocity = paused ? 0 : Number(ui.currentVelocity || 0);
|
||||
const requestedVelocity = paused ? 0 : Number(motion.requestedVel || 0) * 60;
|
||||
const motionInPosition = motion.inPosition === true || traj.inpos === true;
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-program-runtime-feedback",
|
||||
sourceMode: "linuxcnc-task-motion-hal-wasm",
|
||||
@@ -4414,7 +4432,7 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
line: activeLine,
|
||||
motionProgramLine,
|
||||
halProgramLine,
|
||||
activeLineSource: ui.activeLineSource || (motionProgramLine > 0 ? "motion-status" : halProgramLine > 0 ? "hal-pin" : "fallback"),
|
||||
activeLineSource: ui.activeLineSource || (motionProgramLine > 0 ? "emc-status" : halProgramLine > 0 ? "hal-pin" : "fallback"),
|
||||
activeLineHalSynced: motionProgramLine > 0 && halProgramLine > 0 && motionProgramLine === halProgramLine,
|
||||
type: Number(motion.motionType || 0) === 3 ? "JOG" : "TASK_MOTION",
|
||||
paused,
|
||||
@@ -4422,16 +4440,32 @@ function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
axisPose,
|
||||
currentVelocityMmPerMin: currentVelocity,
|
||||
requestedVelocityMmPerMin: requestedVelocity,
|
||||
distanceToGo: motion.inPosition === true ? 0 : 1,
|
||||
distanceToGo: motionInPosition ? 0 : 1,
|
||||
dtg: { x: 0, y: 0, z: 0 },
|
||||
queueDepth: Number(ui.motionQueueDepth || status?.motionStatus?.commandQueueDepth || 0),
|
||||
activeDepth: motion.inPosition === true ? 0 : 1,
|
||||
cycle: Number(ui.servoCycle || status?.servoCycle || 0),
|
||||
taskCycle: Number(ui.taskCycle || status?.task?.cycle || 0),
|
||||
queueDepth: Number(ui.motionQueueDepth || motion.commandQueueDepth || traj.queue || 0),
|
||||
activeDepth: motionInPosition ? 0 : 1,
|
||||
cycle: Number(ui.servoCycle || 0),
|
||||
taskCycle: Number(ui.taskCycle || task.cycle || 0),
|
||||
halChangedPinCount: Number(ui.halChangedPinCount || 0),
|
||||
};
|
||||
}
|
||||
|
||||
function taskHalTask(status = {}) {
|
||||
return status?.emcStatus?.task || {};
|
||||
}
|
||||
|
||||
function taskHalMotion(status = {}) {
|
||||
return status?.emcStatus?.motion || {};
|
||||
}
|
||||
|
||||
function rejectLegacyTaskHalStatusFields(status = {}) {
|
||||
const legacyFields = ["taskTopLevelStatus", "rcsStatus", "task", "servoCycle", "motionStatus"];
|
||||
const present = legacyFields.filter((field) => Object.hasOwn(status, field));
|
||||
if (present.length > 0) {
|
||||
throw new Error(`Task/HAL status contains removed legacy fields: ${present.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function buildTaskHalProgramMotionPlanFromPreviewPath({
|
||||
programPath = null,
|
||||
path = null,
|
||||
|
||||
Reference in New Issue
Block a user