Add RTCP simulation QA updates
This commit is contained in:
@@ -82,6 +82,7 @@ const initialState = {
|
||||
},
|
||||
sourceMode: "fixture-ui-only",
|
||||
frameSourceMode: "fixture-ui-only",
|
||||
desiredFrameSourceMode: "fixture-ui-only",
|
||||
machine: {
|
||||
powerOn: false,
|
||||
estopActive: false,
|
||||
@@ -142,6 +143,7 @@ const initialState = {
|
||||
taskHalExecutionPending: false,
|
||||
taskHalExecutionSequence: 0,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
interpreterExecutionPending: false,
|
||||
interpreterExecutionSequence: 0,
|
||||
machineFileExecution: null,
|
||||
@@ -230,7 +232,7 @@ export function createSimulationStore(seed = {}) {
|
||||
activeLine: seed.activeLine || initialState.activeLine,
|
||||
kinsType: seedKinsType,
|
||||
rtcpEnabled: seedRtcpState === "on" || seedKinsType === "tcp-xyzac",
|
||||
sourceMode: seed.sourceMode || seed.frameSourceMode || initialState.sourceMode,
|
||||
sourceMode: seed.desiredFrameSourceMode || seed.sourceMode || seed.frameSourceMode || initialState.sourceMode,
|
||||
});
|
||||
let state = {
|
||||
...initialState,
|
||||
@@ -268,6 +270,7 @@ export function createSimulationStore(seed = {}) {
|
||||
...next,
|
||||
sourceMode: frame.sourceMode,
|
||||
frameSourceMode: frame.sourceMode,
|
||||
desiredFrameSourceMode: next.desiredFrameSourceMode || frame.sourceMode,
|
||||
axisPose: frame.axisPose,
|
||||
jointPose: frame.jointPose,
|
||||
tcpPose: frame.tcpPose,
|
||||
@@ -321,8 +324,7 @@ export function createSimulationStore(seed = {}) {
|
||||
kinematicsExecutionContext: runtime?.executionContext || (runtime?.loaded ? "direct" : "none"),
|
||||
linuxCncBoundaryAdapter: adapter,
|
||||
linuxCncBoundaryReadiness: createLinuxCncBoundaryReadiness(adapter),
|
||||
sourceMode: runtime?.loaded ? "source-derived-kinematics-wasm" : "fixture-ui-only",
|
||||
frameSourceMode: runtime?.loaded ? "source-derived-kinematics-wasm" : "fixture-ui-only",
|
||||
desiredFrameSourceMode: runtime?.loaded ? "source-derived-kinematics-wasm" : "fixture-ui-only",
|
||||
operatorMessage: runtime?.loaded
|
||||
? `LinuxCNC kinematics ${runtime.moduleId} ready`
|
||||
: "LinuxCNC kinematics runtime missing",
|
||||
@@ -803,6 +805,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
taskHalFallbackReason: action.error,
|
||||
taskHalExecutionPending: false,
|
||||
pendingJogCommand: null,
|
||||
operatorMessage: `task/HAL fallback: ${action.error}`,
|
||||
});
|
||||
break;
|
||||
@@ -833,6 +836,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
sourceMode: action.sourceMode,
|
||||
frameSourceMode: action.sourceMode,
|
||||
desiredFrameSourceMode: action.sourceMode,
|
||||
operatorMessage: `frame source ${action.sourceMode}`,
|
||||
});
|
||||
break;
|
||||
@@ -971,6 +975,13 @@ export function createSimulationStore(seed = {}) {
|
||||
const direction = Number(action.direction || 1);
|
||||
const increment = Number(action.increment || state.machine.jogIncrement);
|
||||
if (state.taskHalRuntime?.loaded) {
|
||||
const pendingJogCommand = {
|
||||
axis,
|
||||
direction,
|
||||
increment,
|
||||
basePose: { ...state.axisPose },
|
||||
createdAtLine: state.activeLine,
|
||||
};
|
||||
runTaskHalCommandSequence([
|
||||
{
|
||||
type: "EMC_JOG_INCR",
|
||||
@@ -978,7 +989,10 @@ export function createSimulationStore(seed = {}) {
|
||||
distance: direction * increment,
|
||||
velocity: Number(action.velocity || 60),
|
||||
},
|
||||
], { operatorMessage: `task/HAL jog ${axis.toUpperCase()} ${direction > 0 ? "+" : "-"}${increment}` }).catch(() => {});
|
||||
], {
|
||||
pendingJogCommand,
|
||||
operatorMessage: `task/HAL jog ${axis.toUpperCase()} ${direction > 0 ? "+" : "-"}${increment}`,
|
||||
}).catch(() => {});
|
||||
break;
|
||||
}
|
||||
setState({
|
||||
@@ -1371,6 +1385,9 @@ export function createSimulationStore(seed = {}) {
|
||||
};
|
||||
|
||||
const refreshAsyncKinematicsFrame = async ({ operatorMessage = state.operatorMessage } = {}) => {
|
||||
if (state.desiredFrameSourceMode !== "source-derived-kinematics-wasm") {
|
||||
return state.rtcpFrame;
|
||||
}
|
||||
if (!state.kinematicsRuntime?.loaded || !isAsyncKinematicsRuntime(state.kinematicsRuntime)) {
|
||||
return state.rtcpFrame;
|
||||
}
|
||||
@@ -1381,44 +1398,58 @@ export function createSimulationStore(seed = {}) {
|
||||
asyncFrameRefreshSequence: sequence,
|
||||
};
|
||||
notify();
|
||||
await switchKinematicsRuntimeForState(state);
|
||||
const frameSource = await state.kinematicsRuntime.frameForJoints(
|
||||
jointsFromAxisPose(state.axisPose, state.profile),
|
||||
{ jointCount: state.kinematicsRuntime.jointCount || 5 },
|
||||
);
|
||||
if (state.asyncFrameRefreshSequence !== sequence) {
|
||||
try {
|
||||
await switchKinematicsRuntimeForState(state);
|
||||
const frameSource = await state.kinematicsRuntime.frameForJoints(
|
||||
jointsFromAxisPose(state.axisPose, state.profile),
|
||||
{ jointCount: state.kinematicsRuntime.jointCount || 5 },
|
||||
);
|
||||
if (state.asyncFrameRefreshSequence !== sequence) {
|
||||
return state.rtcpFrame;
|
||||
}
|
||||
const frame = buildRtcpFrame({
|
||||
axisPose: state.axisPose,
|
||||
activeLine: state.activeLine,
|
||||
kinsType: state.kinsType,
|
||||
rtcpEnabled: state.rtcpState === "on" || state.kinsType.startsWith("tcp-"),
|
||||
sourceMode: "source-derived-kinematics-wasm",
|
||||
profile: state.profile,
|
||||
linuxCncKinematicsResult: frameSource,
|
||||
});
|
||||
const nextState = {
|
||||
...state,
|
||||
sourceMode: "source-derived-kinematics-wasm",
|
||||
frameSourceMode: "source-derived-kinematics-wasm",
|
||||
desiredFrameSourceMode: "source-derived-kinematics-wasm",
|
||||
axisPose: frame.axisPose,
|
||||
jointPose: frame.jointPose,
|
||||
tcpPose: frame.tcpPose,
|
||||
toolAxisVector: frame.toolAxisVector,
|
||||
rtcpState: frame.rtcpState,
|
||||
rtcpFrame: frame,
|
||||
lastKinematicsResult: frameSource,
|
||||
dro: buildDroFromFrame(frame, state.programRuntimeFeedback),
|
||||
asyncFrameRefreshPending: false,
|
||||
operatorMessage,
|
||||
};
|
||||
state = {
|
||||
...nextState,
|
||||
fullExecutionBoundary: createFullLinuxCncExecutionBoundary(nextState),
|
||||
};
|
||||
notify();
|
||||
return frame;
|
||||
} catch (error) {
|
||||
if (state.asyncFrameRefreshSequence !== sequence) {
|
||||
return state.rtcpFrame;
|
||||
}
|
||||
state = {
|
||||
...state,
|
||||
asyncFrameRefreshPending: false,
|
||||
operatorMessage: `LinuxCNC kinematics refresh failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
};
|
||||
notify();
|
||||
return state.rtcpFrame;
|
||||
}
|
||||
const frame = buildRtcpFrame({
|
||||
axisPose: state.axisPose,
|
||||
activeLine: state.activeLine,
|
||||
kinsType: state.kinsType,
|
||||
rtcpEnabled: state.rtcpState === "on" || state.kinsType.startsWith("tcp-"),
|
||||
sourceMode: "source-derived-kinematics-wasm",
|
||||
profile: state.profile,
|
||||
linuxCncKinematicsResult: frameSource,
|
||||
});
|
||||
const nextState = {
|
||||
...state,
|
||||
sourceMode: frame.sourceMode,
|
||||
frameSourceMode: frame.sourceMode,
|
||||
axisPose: frame.axisPose,
|
||||
jointPose: frame.jointPose,
|
||||
tcpPose: frame.tcpPose,
|
||||
toolAxisVector: frame.toolAxisVector,
|
||||
rtcpState: frame.rtcpState,
|
||||
rtcpFrame: frame,
|
||||
lastKinematicsResult: frameSource,
|
||||
dro: buildDroFromFrame(frame, state.programRuntimeFeedback),
|
||||
asyncFrameRefreshPending: false,
|
||||
operatorMessage,
|
||||
};
|
||||
state = {
|
||||
...nextState,
|
||||
fullExecutionBoundary: createFullLinuxCncExecutionBoundary(nextState),
|
||||
};
|
||||
notify();
|
||||
return frame;
|
||||
};
|
||||
|
||||
const saveSession = async (options = {}) => {
|
||||
@@ -1543,6 +1574,7 @@ export function createSimulationStore(seed = {}) {
|
||||
taskPeriodNs = 10000000,
|
||||
servoPeriodNs = 1000000,
|
||||
operatorMessage = "task/HAL command complete",
|
||||
pendingJogCommand = null,
|
||||
} = {}) => {
|
||||
if (!state.taskHalRuntime?.loaded) {
|
||||
throw new Error("LinuxCNC task/HAL runtime not attached");
|
||||
@@ -1551,6 +1583,7 @@ export function createSimulationStore(seed = {}) {
|
||||
setState({
|
||||
taskHalExecutionPending: true,
|
||||
taskHalExecutionSequence: sequence,
|
||||
pendingJogCommand,
|
||||
operatorMessage: "LinuxCNC task/HAL command running",
|
||||
});
|
||||
try {
|
||||
@@ -1614,13 +1647,21 @@ export function createSimulationStore(seed = {}) {
|
||||
};
|
||||
|
||||
const scheduleAsyncKinematicsRefresh = () => {
|
||||
if (state.asyncFrameRefreshPending) return null;
|
||||
if (state.desiredFrameSourceMode !== "source-derived-kinematics-wasm") return null;
|
||||
if (!state.kinematicsRuntime?.loaded || !isAsyncKinematicsRuntime(state.kinematicsRuntime)) return null;
|
||||
if (state.frameSourceMode !== "source-derived-kinematics-wasm") return null;
|
||||
const frame = state.rtcpFrame;
|
||||
if (
|
||||
frame?.sourceMode === "source-derived-kinematics-wasm" &&
|
||||
frame.readiness?.linuxCncKinematicsReady === true &&
|
||||
frame.activeLine === state.activeLine
|
||||
frame.activeLine === state.activeLine &&
|
||||
frame.kinsType === state.kinsType &&
|
||||
frame.axisPose?.x === state.axisPose.x &&
|
||||
frame.axisPose?.y === state.axisPose.y &&
|
||||
frame.axisPose?.z === state.axisPose.z &&
|
||||
frame.axisPose?.a === state.axisPose.a &&
|
||||
frame.axisPose?.b === state.axisPose.b &&
|
||||
frame.axisPose?.c === state.axisPose.c
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -1645,10 +1686,9 @@ export function createSimulationStore(seed = {}) {
|
||||
}
|
||||
|
||||
function buildFrameForState(state, patch = {}) {
|
||||
const requestedSourceMode = state.frameSourceMode || state.sourceMode;
|
||||
const requestedSourceMode = state.desiredFrameSourceMode || state.frameSourceMode || state.sourceMode;
|
||||
let linuxCncKinematicsResult = patch.lastKinematicsResult || null;
|
||||
let sourceMode = requestedSourceMode;
|
||||
let operatorMessage = state.operatorMessage;
|
||||
|
||||
if (requestedSourceMode === "source-derived-kinematics-wasm") {
|
||||
if (state.kinematicsRuntime?.loaded && !isAsyncKinematicsRuntime(state.kinematicsRuntime)) {
|
||||
@@ -1657,10 +1697,12 @@ function buildFrameForState(state, patch = {}) {
|
||||
jointsFromAxisPose(state.axisPose, state.profile),
|
||||
{ jointCount: state.kinematicsRuntime.jointCount || 5 },
|
||||
);
|
||||
} else if (state.kinematicsRuntime?.loaded && isAsyncKinematicsRuntime(state.kinematicsRuntime)) {
|
||||
sourceMode = "fixture-ui-only";
|
||||
linuxCncKinematicsResult = null;
|
||||
} else {
|
||||
sourceMode = "fixture-ui-only";
|
||||
linuxCncKinematicsResult = null;
|
||||
operatorMessage = "LinuxCNC kinematics runtime missing; using fixture frame";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1674,10 +1716,6 @@ function buildFrameForState(state, patch = {}) {
|
||||
linuxCncKinematicsResult,
|
||||
});
|
||||
|
||||
if (operatorMessage !== state.operatorMessage) {
|
||||
state.operatorMessage = operatorMessage;
|
||||
}
|
||||
|
||||
return {
|
||||
frame,
|
||||
lastKinematicsResult: linuxCncKinematicsResult,
|
||||
@@ -1743,11 +1781,8 @@ function applyTaskHalStatusPatch(state, status, operatorMessage) {
|
||||
const taskMode = normalizeLinuxCncTaskMode(ui.taskMode || task.mode || state.machine.mode);
|
||||
const interpState = normalizeTaskHalInterpState(ui.interpState || task.interpState);
|
||||
const activeLine = state.programStartLine + Math.max(Number(ui.activeLine || 1) - 1, 0);
|
||||
const kinsType = kinsTypeFromSwitchkinsTypeValue(state, ui.switchkinsType);
|
||||
const axisPose = clampAxisPoseToProfile({
|
||||
...state.axisPose,
|
||||
...ui.axisPose,
|
||||
}, state.profile);
|
||||
const kinsType = resolveTaskHalKinsType(state, status, activeLine);
|
||||
const axisPose = resolveTaskHalAxisPose(state, status);
|
||||
const currentVelocity = Number.isFinite(ui.currentVelocity) && ui.currentVelocity > 0
|
||||
? ui.currentVelocity
|
||||
: state.feed.currentVelocity;
|
||||
@@ -1772,11 +1807,14 @@ function applyTaskHalStatusPatch(state, status, operatorMessage) {
|
||||
taskHalStatus: status,
|
||||
taskHalExecutionPending: false,
|
||||
taskHalFallbackReason: null,
|
||||
pendingJogCommand: null,
|
||||
activeLine,
|
||||
axisPose,
|
||||
kinsType,
|
||||
rtcpState: rtcpStateFromKinsType(kinsType),
|
||||
programExecutionSourceMode: "linuxcnc-task-motion-hal-wasm",
|
||||
programExecutionSourceMode: state.programExecution
|
||||
? state.programExecutionSourceMode
|
||||
: "linuxcnc-task-motion-hal-wasm",
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: taskState === "on",
|
||||
@@ -1797,6 +1835,93 @@ function applyTaskHalStatusPatch(state, status, operatorMessage) {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveTaskHalKinsType(state, status, activeLine) {
|
||||
const ui = status?.ui || {};
|
||||
const numeric = Number(ui.switchkinsType);
|
||||
if (Number.isFinite(numeric) && numeric !== 0) {
|
||||
return kinsTypeFromSwitchkinsTypeValue(state, numeric);
|
||||
}
|
||||
|
||||
const programKinsType = kinsTypeFromProgramActiveLine(state, activeLine);
|
||||
if (programKinsType) {
|
||||
return programKinsType;
|
||||
}
|
||||
|
||||
return kinsTypeFromSwitchkinsTypeValue(state, ui.switchkinsType);
|
||||
}
|
||||
|
||||
function kinsTypeFromProgramActiveLine(state, activeLine) {
|
||||
const motion = programMotionAtOrBeforeLine(state, activeLine)
|
||||
|| state.programExecution?.motion?.[clampMotionIndex(state, state.programExecutionMotionIndex)];
|
||||
return kinsTypeFromProgramMotion(state, motion);
|
||||
}
|
||||
|
||||
function programMotionAtOrBeforeLine(state, activeLine) {
|
||||
const motion = state.programExecution?.motion;
|
||||
if (!Array.isArray(motion) || motion.length === 0) return null;
|
||||
const line = Number(activeLine);
|
||||
if (!Number.isFinite(line)) return null;
|
||||
let candidate = null;
|
||||
for (const item of motion) {
|
||||
const itemLine = Number(item?.line);
|
||||
if (!Number.isFinite(itemLine)) continue;
|
||||
if (itemLine > line) break;
|
||||
candidate = item;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
function resolveTaskHalAxisPose(state, status) {
|
||||
const ui = status?.ui || {};
|
||||
const axisPose = ui.axisPose;
|
||||
if (!axisPose || typeof axisPose !== "object") {
|
||||
return state.axisPose;
|
||||
}
|
||||
|
||||
if (ui.axisPoseFrame === "work") {
|
||||
return clampAxisPoseToProfile({ ...state.axisPose, ...axisPose }, state.profile);
|
||||
}
|
||||
|
||||
if (ui.axisPoseDelta && typeof ui.axisPoseDelta === "object") {
|
||||
return addAxisDelta(state.axisPose, ui.axisPoseDelta, state.profile);
|
||||
}
|
||||
|
||||
if (state.pendingJogCommand && isJogStatus(status)) {
|
||||
const { axis, direction, increment, basePose } = state.pendingJogCommand;
|
||||
return clampAxisPoseToProfile({
|
||||
...basePose,
|
||||
[axis]: Number(basePose?.[axis] || 0) + direction * increment,
|
||||
}, state.profile);
|
||||
}
|
||||
|
||||
if (!ui.axisPoseFrame && wouldResetNonZeroPoseToLocalZero(state.axisPose, axisPose)) {
|
||||
return state.axisPose;
|
||||
}
|
||||
|
||||
return clampAxisPoseToProfile({ ...state.axisPose, ...axisPose }, state.profile);
|
||||
}
|
||||
|
||||
function addAxisDelta(axisPose, delta, profile) {
|
||||
const next = { ...axisPose };
|
||||
for (const axis of ["x", "y", "z", "a", "b", "c"]) {
|
||||
if (!Number.isFinite(Number(delta[axis]))) continue;
|
||||
next[axis] = Number(next[axis] || 0) + Number(delta[axis]);
|
||||
}
|
||||
return clampAxisPoseToProfile(next, profile);
|
||||
}
|
||||
|
||||
function isJogStatus(status) {
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
return Number(motion.motionType) === 3 || Number(motion.teleopMode) === 1 || motion.teleopMode === true;
|
||||
}
|
||||
|
||||
function wouldResetNonZeroPoseToLocalZero(currentPose = {}, nextPose = {}) {
|
||||
const axes = ["x", "y", "z", "a", "b", "c"];
|
||||
const currentHasNonZero = axes.some((axis) => Math.abs(Number(currentPose[axis] || 0)) > 0.001);
|
||||
const nextIsNearZero = axes.every((axis) => Math.abs(Number(nextPose[axis] || 0)) <= 0.001);
|
||||
return currentHasNonZero && nextIsNearZero;
|
||||
}
|
||||
|
||||
function createTaskHalRuntimeFeedback(state, status, axisPose, activeLine) {
|
||||
const ui = status?.ui || {};
|
||||
const motion = status?.motionStatus?.motion || {};
|
||||
|
||||
Reference in New Issue
Block a user