完善 working5 gmoccapy HAL 输入对标
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
buildTaskHalProgramMotionPlan,
|
||||
buildTaskHalSessionFromMachineFiles,
|
||||
} from "../runtime/linuxcnc-task-hal-runtime.js";
|
||||
import { gmoccapyHalModel, resolveGmoccapyHardwareButton } from "../runtime/gmoccapy-hal-model.js";
|
||||
import {
|
||||
createLinuxCncTaskPolicyStatus,
|
||||
gateLinuxCncTaskAction,
|
||||
@@ -230,6 +231,35 @@ const initialState = {
|
||||
flood: true,
|
||||
mist: false,
|
||||
},
|
||||
gmoccapyGui: {
|
||||
ignoreLimits: false,
|
||||
optionalBlocks: false,
|
||||
optionalStop: false,
|
||||
feedOverrideCounts: 0,
|
||||
rapidOverrideCounts: 0,
|
||||
spindleOverrideCounts: 0,
|
||||
jogVelocityCounts: 0,
|
||||
feedOverrideCountEnabled: false,
|
||||
rapidOverrideCountEnabled: false,
|
||||
spindleOverrideCountEnabled: false,
|
||||
jogVelocityCountEnabled: false,
|
||||
feedOverrideAnalogEnabled: false,
|
||||
rapidOverrideAnalogEnabled: false,
|
||||
spindleOverrideAnalogEnabled: false,
|
||||
jogVelocityAnalogEnabled: false,
|
||||
jogVelocity: 100,
|
||||
jogIncrementIndex: 0,
|
||||
jogIncrementLabel: "Continuous",
|
||||
jogIncrementOutput: 0,
|
||||
turtleJog: false,
|
||||
activeJogPin: null,
|
||||
warningConfirm: false,
|
||||
error: false,
|
||||
deletedMessageCount: 0,
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: null,
|
||||
},
|
||||
preview: {
|
||||
pathPoints: 64,
|
||||
selectedView: "iso",
|
||||
@@ -964,6 +994,34 @@ export function createSimulationStore(seed = {}) {
|
||||
case "REFRESH_KINEMATICS_FRAME":
|
||||
return refreshAsyncKinematicsFrame({ operatorMessage: "LinuxCNC kinematics frame refreshed" });
|
||||
break;
|
||||
case "GMOCAPY_HARDWARE_BUTTON":
|
||||
{
|
||||
if (action.value === false || action.risingEdge === false) {
|
||||
setState({ operatorMessage: "gmoccapy hardware button falling edge ignored" });
|
||||
break;
|
||||
}
|
||||
const button = resolveGmoccapyHardwareButton(action);
|
||||
if (!button) {
|
||||
setState({ operatorMessage: `gmoccapy hardware button unmapped: ${action.pin || `${action.location}:${action.index}`}` });
|
||||
break;
|
||||
}
|
||||
if (!button.webDispatch) {
|
||||
setState({ operatorMessage: `gmoccapy hardware button diagnostic-only: ${button.pin}` });
|
||||
break;
|
||||
}
|
||||
dispatch(button.webDispatch);
|
||||
}
|
||||
break;
|
||||
case "GMOCAPY_HAL_PIN":
|
||||
{
|
||||
const result = applyGmoccapyHalPinPatch(state, action, gmoccapyHalModel);
|
||||
if (!result.applied) {
|
||||
setState({ operatorMessage: result.operatorMessage });
|
||||
break;
|
||||
}
|
||||
setState(result.patch);
|
||||
}
|
||||
break;
|
||||
case "TOGGLE_POWER":
|
||||
{
|
||||
const gate = gateLinuxCncTaskAction(state, action);
|
||||
@@ -1585,6 +1643,12 @@ export function createSimulationStore(seed = {}) {
|
||||
200,
|
||||
),
|
||||
},
|
||||
gmoccapyGui: {
|
||||
...state.gmoccapyGui,
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: `${action.target} override manual step`,
|
||||
},
|
||||
operatorMessage: `${action.target} override adjusted`,
|
||||
});
|
||||
}
|
||||
@@ -1601,10 +1665,55 @@ export function createSimulationStore(seed = {}) {
|
||||
...state.spindle,
|
||||
override: clampPercent(state.spindle.override + action.delta, 0, 150),
|
||||
},
|
||||
gmoccapyGui: {
|
||||
...state.gmoccapyGui,
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: "spindle override manual step",
|
||||
},
|
||||
operatorMessage: "spindle override adjusted",
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "RESET_OVERRIDE":
|
||||
setState(resetOverridePatchForTarget(state, action.target));
|
||||
break;
|
||||
case "SET_BLOCK_DELETE":
|
||||
setState({
|
||||
gmoccapyGui: {
|
||||
...state.gmoccapyGui,
|
||||
optionalBlocks: Boolean(action.enabled),
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: "block delete set from Web optional blocks button",
|
||||
},
|
||||
operatorMessage: `block delete ${action.enabled ? "on" : "off"}`,
|
||||
});
|
||||
break;
|
||||
case "SET_OPTIONAL_STOP":
|
||||
setState({
|
||||
gmoccapyGui: {
|
||||
...state.gmoccapyGui,
|
||||
optionalStop: Boolean(action.enabled),
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: "optional stop set from Web control",
|
||||
},
|
||||
operatorMessage: `optional stop ${action.enabled ? "on" : "off"}`,
|
||||
});
|
||||
break;
|
||||
case "SET_IGNORE_LIMITS":
|
||||
setState({
|
||||
gmoccapyGui: {
|
||||
...state.gmoccapyGui,
|
||||
ignoreLimits: Boolean(action.enabled),
|
||||
lastHalPin: null,
|
||||
lastHalPinValue: null,
|
||||
lastHalPinEffect: "ignore limits set from Web checkbox",
|
||||
},
|
||||
operatorMessage: action.enabled ? "limit override requested" : "limit override clear",
|
||||
});
|
||||
break;
|
||||
case "TOGGLE_COOLANT":
|
||||
{
|
||||
const gate = gateLinuxCncTaskAction(state, action);
|
||||
@@ -3269,6 +3378,535 @@ function clampPercent(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
const GMOCAPY_OVERRIDE_TARGETS = {
|
||||
feed: {
|
||||
countPin: "gmoccapy.feed.feed-override.counts",
|
||||
countEnablePin: "gmoccapy.feed.feed-override.count-enable",
|
||||
analogEnablePin: "gmoccapy.feed.feed-override.analog-enable",
|
||||
directValuePin: "gmoccapy.feed.feed-override.direct-value",
|
||||
resetPin: "gmoccapy.feed.reset-feed-override",
|
||||
countStateKey: "feedOverrideCounts",
|
||||
countEnableStateKey: "feedOverrideCountEnabled",
|
||||
analogEnableStateKey: "feedOverrideAnalogEnabled",
|
||||
stateDomain: "feed",
|
||||
stateKey: "feedOverride",
|
||||
min: 0,
|
||||
max: 200,
|
||||
scale: 1,
|
||||
},
|
||||
rapid: {
|
||||
countPin: "gmoccapy.rapid.rapid-override.counts",
|
||||
countEnablePin: "gmoccapy.rapid.rapid-override.count-enable",
|
||||
analogEnablePin: "gmoccapy.rapid.rapid-override.analog-enable",
|
||||
directValuePin: "gmoccapy.rapid.rapid-override.direct-value",
|
||||
resetPin: "gmoccapy.rapid.reset-rapid-override",
|
||||
countStateKey: "rapidOverrideCounts",
|
||||
countEnableStateKey: "rapidOverrideCountEnabled",
|
||||
analogEnableStateKey: "rapidOverrideAnalogEnabled",
|
||||
stateDomain: "feed",
|
||||
stateKey: "rapidOverride",
|
||||
min: 0,
|
||||
max: 200,
|
||||
scale: 1,
|
||||
},
|
||||
spindle: {
|
||||
countPin: "gmoccapy.spindle.spindle-override.counts",
|
||||
countEnablePin: "gmoccapy.spindle.spindle-override.count-enable",
|
||||
analogEnablePin: "gmoccapy.spindle.spindle-override.analog-enable",
|
||||
directValuePin: "gmoccapy.spindle.spindle-override.direct-value",
|
||||
resetPin: "gmoccapy.spindle.reset-spindle-override",
|
||||
countStateKey: "spindleOverrideCounts",
|
||||
countEnableStateKey: "spindleOverrideCountEnabled",
|
||||
analogEnableStateKey: "spindleOverrideAnalogEnabled",
|
||||
stateDomain: "spindle",
|
||||
stateKey: "override",
|
||||
min: 0,
|
||||
max: 150,
|
||||
scale: 1,
|
||||
},
|
||||
jogVelocity: {
|
||||
countPin: "gmoccapy.jog.jog-velocity.counts",
|
||||
countEnablePin: "gmoccapy.jog.jog-velocity.count-enable",
|
||||
analogEnablePin: "gmoccapy.jog.jog-velocity.analog-enable",
|
||||
directValuePin: "gmoccapy.jog.jog-velocity.direct-value",
|
||||
countStateKey: "jogVelocityCounts",
|
||||
countEnableStateKey: "jogVelocityCountEnabled",
|
||||
analogEnableStateKey: "jogVelocityAnalogEnabled",
|
||||
stateDomain: "gmoccapyGui",
|
||||
stateKey: "jogVelocity",
|
||||
min: 0,
|
||||
max: 14040,
|
||||
scale: 140.4,
|
||||
},
|
||||
};
|
||||
|
||||
function applyGmoccapyHalPinPatch(state, action, model = gmoccapyHalModel) {
|
||||
const pin = normalizeGmoccapyHalPin(action.pin);
|
||||
const value = action.value;
|
||||
if (!pin) {
|
||||
return { applied: false, operatorMessage: "gmoccapy HAL pin blocked: missing pin" };
|
||||
}
|
||||
if (!isKnownGmoccapyHalPin(pin, model)) {
|
||||
return { applied: false, operatorMessage: `gmoccapy HAL pin unmapped: ${pin}` };
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.ignore-limits") {
|
||||
const enabled = Boolean(value);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
ignoreLimits: enabled,
|
||||
lastHalPinEffect: enabled
|
||||
? "chk_ignore_limits set active; command.override_limits() requested"
|
||||
: "chk_ignore_limits cleared",
|
||||
}),
|
||||
operatorMessage: enabled ? "gmoccapy HAL ignore-limits requested" : "gmoccapy HAL ignore-limits cleared",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.optional-stop") {
|
||||
const enabled = Boolean(value);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
optionalBlocks: enabled,
|
||||
lastHalPinEffect: "optional-stop pin drives tbtn_optional_blocks -> set_block_delete",
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL optional-stop -> block delete ${enabled ? "on" : "off"}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.blockdelete") {
|
||||
const enabled = Boolean(value);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
optionalStop: enabled,
|
||||
lastHalPinEffect: "blockdelete pin drives command.set_optional_stop",
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL blockdelete -> optional stop ${enabled ? "on" : "off"}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const jogAxisPin = resolveGmoccapyJogAxisPin(pin, model);
|
||||
if (jogAxisPin) {
|
||||
return applyGmoccapyJogAxisHalPatch(state, pin, value, jogAxisPin);
|
||||
}
|
||||
|
||||
const jogIncrement = resolveGmoccapyJogIncrementPin(pin, model);
|
||||
if (jogIncrement) {
|
||||
return applyGmoccapyJogIncrementHalPatch(state, pin, value, jogIncrement);
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.jog.turtle-jog") {
|
||||
const enabled = Boolean(value);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
turtleJog: enabled,
|
||||
lastHalPinEffect: `turtle jog ${enabled ? "enabled" : "disabled"} by level-driven pin`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL turtle jog ${enabled ? "on" : "off"}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.delete-message") {
|
||||
if (!value) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: "delete-message falling edge ignored",
|
||||
}),
|
||||
operatorMessage: "gmoccapy HAL delete-message falling edge ignored",
|
||||
},
|
||||
};
|
||||
}
|
||||
const hadError = state.gmoccapyGui?.error === true || /error|blocked|warning/i.test(state.operatorMessage || "");
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
error: false,
|
||||
deletedMessageCount: Number(state.gmoccapyGui?.deletedMessageCount || 0) + 1,
|
||||
lastHalPinEffect: hadError
|
||||
? "delete-message removed first alert and cleared gmoccapy.error"
|
||||
: "delete-message deleted last notification",
|
||||
}),
|
||||
operatorMessage: hadError
|
||||
? "gmoccapy HAL delete-message cleared alert"
|
||||
: "gmoccapy HAL delete-message deleted last message",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === "gmoccapy.warning-confirm") {
|
||||
const enabled = Boolean(value);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
warningConfirm: enabled,
|
||||
lastHalPinEffect: enabled
|
||||
? "warning-confirm level would accept active warning dialog"
|
||||
: "warning-confirm level cleared",
|
||||
}),
|
||||
operatorMessage: enabled
|
||||
? "gmoccapy HAL warning-confirm asserted"
|
||||
: "gmoccapy HAL warning-confirm cleared",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const overrideTarget = findOverrideTargetForPin(pin);
|
||||
if (overrideTarget) {
|
||||
return applyGmoccapyOverrideHalPatch(state, pin, value, overrideTarget);
|
||||
}
|
||||
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: "diagnostic-only gmoccapy HAL pin",
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL diagnostic-only: ${pin}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGmoccapyHalPin(pin) {
|
||||
const value = String(pin || "").trim();
|
||||
if (!value) return "";
|
||||
return value.startsWith("gmoccapy.") ? value : `gmoccapy.${value}`;
|
||||
}
|
||||
|
||||
function isKnownGmoccapyHalPin(pin, model = gmoccapyHalModel) {
|
||||
return model.nativePins.some((group) => group.pins.includes(pin));
|
||||
}
|
||||
|
||||
function halGuiPatch(state, pin, value, patch = {}) {
|
||||
return {
|
||||
...state.gmoccapyGui,
|
||||
...patch,
|
||||
lastHalPin: pin,
|
||||
lastHalPinValue: value,
|
||||
};
|
||||
}
|
||||
|
||||
function findOverrideTargetForPin(pin) {
|
||||
return Object.entries(GMOCAPY_OVERRIDE_TARGETS)
|
||||
.find(([, target]) => [
|
||||
target.countPin,
|
||||
target.countEnablePin,
|
||||
target.analogEnablePin,
|
||||
target.directValuePin,
|
||||
target.resetPin,
|
||||
].includes(pin)) || null;
|
||||
}
|
||||
|
||||
function resolveGmoccapyJogAxisPin(pin, model = gmoccapyHalModel) {
|
||||
for (const entry of model.halPinActions?.jogPins?.axes || []) {
|
||||
if (pin === entry.plus) return { axis: entry.axis, direction: 1 };
|
||||
if (pin === entry.minus) return { axis: entry.axis, direction: -1 };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveGmoccapyJogIncrementPin(pin, model = gmoccapyHalModel) {
|
||||
return (model.halPinActions?.jogPins?.increments || []).find((entry) => entry.pin === pin) || null;
|
||||
}
|
||||
|
||||
function applyGmoccapyJogAxisHalPatch(state, pin, value, jogAxisPin) {
|
||||
const axis = String(jogAxisPin.axis || "").toLowerCase();
|
||||
const direction = Number(jogAxisPin.direction || 1);
|
||||
const axisLabel = axis.toUpperCase();
|
||||
const sign = direction > 0 ? "+" : "-";
|
||||
if (!value) {
|
||||
const isActivePin = state.gmoccapyGui?.activeJogPin === pin;
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
runState: isActivePin && state.runState === "jogging" ? "idle" : state.runState,
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
activeJogPin: isActivePin ? null : state.gmoccapyGui?.activeJogPin || null,
|
||||
lastHalPinEffect: `jog ${axisLabel}${sign} released`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL jog ${axisLabel}${sign} released`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const gate = gateLinuxCncTaskAction(state, { type: "JOG", axis, direction });
|
||||
if (!gate.allowed) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: `jog ${axisLabel}${sign} ignored: ${gate.operatorMessage}`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL jog blocked: ${gate.operatorMessage}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const increment = Number(state.gmoccapyGui?.jogIncrementOutput ?? state.machine?.jogIncrement ?? 0);
|
||||
const continuous = Math.abs(increment) <= 0;
|
||||
const nextAxisPose = continuous
|
||||
? state.axisPose
|
||||
: {
|
||||
...state.axisPose,
|
||||
[axis]: Number(state.axisPose?.[axis] || 0) + direction * increment,
|
||||
};
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "manual",
|
||||
jogAxis: axis,
|
||||
jogIncrement: increment,
|
||||
},
|
||||
axisPose: nextAxisPose,
|
||||
runState: "jogging",
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
activeJogPin: pin,
|
||||
lastHalPinEffect: continuous
|
||||
? `jog ${axisLabel}${sign} continuous press`
|
||||
: `jog ${axisLabel}${sign} incremental ${increment}`,
|
||||
}),
|
||||
operatorMessage: continuous
|
||||
? `gmoccapy HAL jog ${axisLabel}${sign} continuous`
|
||||
: `gmoccapy HAL jog ${axisLabel}${sign} ${increment}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function applyGmoccapyJogIncrementHalPatch(state, pin, value, jogIncrement) {
|
||||
if (!value) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: `jog increment ${jogIncrement.index} falling edge ignored`,
|
||||
}),
|
||||
operatorMessage: "gmoccapy HAL jog increment falling edge ignored",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const distance = Number(jogIncrement.distance || 0);
|
||||
const label = String(jogIncrement.label || `increment ${jogIncrement.index}`);
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
machine: {
|
||||
...state.machine,
|
||||
jogIncrement: distance,
|
||||
},
|
||||
runState: state.machine.mode === "manual" && state.runState === "jogging" ? "idle" : state.runState,
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
jogIncrementIndex: Number(jogIncrement.index),
|
||||
jogIncrementLabel: label,
|
||||
jogIncrementOutput: distance,
|
||||
activeJogPin: null,
|
||||
lastHalPinEffect: `jog increment selected ${label}; gmoccapy.jog.jog-increment=${distance}`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL jog increment ${label}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function applyGmoccapyOverrideHalPatch(state, pin, value, [targetName, target]) {
|
||||
if (pin === target.countEnablePin || pin === target.analogEnablePin) {
|
||||
const enabled = Boolean(value);
|
||||
const key = pin === target.countEnablePin ? target.countEnableStateKey : target.analogEnableStateKey;
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
[key]: enabled,
|
||||
lastHalPinEffect: `${targetName} ${pin === target.countEnablePin ? "count" : "analog"} input ${enabled ? "enabled" : "disabled"}`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL ${targetName} ${pin === target.countEnablePin ? "counts" : "analog"} ${enabled ? "enabled" : "disabled"}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === target.resetPin) {
|
||||
if (!value) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: `${targetName} reset falling edge ignored`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL ${targetName} reset falling edge ignored`,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
applied: true,
|
||||
patch: resetOverridePatchForTarget(state, targetName, { pin, value }),
|
||||
};
|
||||
}
|
||||
|
||||
if (pin === target.directValuePin) {
|
||||
if (!state.gmoccapyGui?.[target.analogEnableStateKey]) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
lastHalPinEffect: `${targetName} direct-value ignored until analog-enable is true`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL ${targetName} direct-value ignored: analog disabled`,
|
||||
},
|
||||
};
|
||||
}
|
||||
const normalized = clampNumber(Number(value), 0, 1);
|
||||
const nextValue = target.min + (target.max - target.min) * normalized;
|
||||
return overrideValuePatch(state, targetName, target, nextValue, {
|
||||
pin,
|
||||
value,
|
||||
effect: `${targetName} direct-value ${normalized}`,
|
||||
operatorMessage: `gmoccapy HAL ${targetName} direct-value ${Math.round(nextValue)}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (pin === target.countPin) {
|
||||
const counts = Number(value);
|
||||
if (!Number.isFinite(counts)) {
|
||||
return { applied: false, operatorMessage: `gmoccapy HAL ${targetName} counts invalid: ${value}` };
|
||||
}
|
||||
if (!state.gmoccapyGui?.[target.countEnableStateKey]) {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: halGuiPatch(state, pin, value, {
|
||||
[target.countStateKey]: counts,
|
||||
lastHalPinEffect: `${targetName} counts synchronized while count-enable is false`,
|
||||
}),
|
||||
operatorMessage: `gmoccapy HAL ${targetName} counts synchronized`,
|
||||
},
|
||||
};
|
||||
}
|
||||
const previousCounts = Number(state.gmoccapyGui?.[target.countStateKey] || 0);
|
||||
const delta = (counts - previousCounts) * target.scale;
|
||||
const currentValue = currentOverrideValue(state, target);
|
||||
return overrideValuePatch(state, targetName, target, currentValue + delta, {
|
||||
pin,
|
||||
value,
|
||||
countPatch: { [target.countStateKey]: counts },
|
||||
effect: `${targetName} counts delta ${delta}`,
|
||||
operatorMessage: `gmoccapy HAL ${targetName} counts adjusted`,
|
||||
});
|
||||
}
|
||||
|
||||
return { applied: false, operatorMessage: `gmoccapy HAL override pin unmapped: ${pin}` };
|
||||
}
|
||||
|
||||
function overrideValuePatch(state, targetName, target, rawValue, {
|
||||
pin,
|
||||
value,
|
||||
countPatch = {},
|
||||
effect,
|
||||
operatorMessage,
|
||||
} = {}) {
|
||||
const nextValue = clampPercent(rawValue, target.min, target.max);
|
||||
const gmoccapyGui = halGuiPatch(state, pin, value, {
|
||||
...countPatch,
|
||||
lastHalPinEffect: effect,
|
||||
});
|
||||
if (target.stateDomain === "feed") {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
feed: {
|
||||
...state.feed,
|
||||
[target.stateKey]: Math.round(nextValue),
|
||||
},
|
||||
gmoccapyGui,
|
||||
operatorMessage,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (target.stateDomain === "spindle") {
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
spindle: {
|
||||
...state.spindle,
|
||||
[target.stateKey]: Math.round(nextValue),
|
||||
},
|
||||
gmoccapyGui,
|
||||
operatorMessage,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
applied: true,
|
||||
patch: {
|
||||
gmoccapyGui: {
|
||||
...gmoccapyGui,
|
||||
[target.stateKey]: Number(nextValue.toFixed(3)),
|
||||
},
|
||||
operatorMessage,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function currentOverrideValue(state, target) {
|
||||
if (target.stateDomain === "feed") return Number(state.feed?.[target.stateKey] || 0);
|
||||
if (target.stateDomain === "spindle") return Number(state.spindle?.[target.stateKey] || 0);
|
||||
return Number(state.gmoccapyGui?.[target.stateKey] || 0);
|
||||
}
|
||||
|
||||
function resetOverridePatchForTarget(state, targetName, hal = {}) {
|
||||
const target = GMOCAPY_OVERRIDE_TARGETS[targetName];
|
||||
if (!target || targetName === "jogVelocity") {
|
||||
return {
|
||||
gmoccapyGui: halGuiPatch(state, hal.pin || null, hal.value ?? null, {
|
||||
lastHalPinEffect: `reset unsupported for ${targetName}`,
|
||||
}),
|
||||
operatorMessage: `override reset unsupported: ${targetName}`,
|
||||
};
|
||||
}
|
||||
const effect = `${targetName} override reset to 100`;
|
||||
const gmoccapyGui = halGuiPatch(state, hal.pin || null, hal.value ?? null, {
|
||||
lastHalPinEffect: hal.pin ? `HAL ${effect}` : effect,
|
||||
});
|
||||
if (target.stateDomain === "spindle") {
|
||||
return {
|
||||
spindle: {
|
||||
...state.spindle,
|
||||
[target.stateKey]: 100,
|
||||
},
|
||||
gmoccapyGui,
|
||||
operatorMessage: hal.pin ? `gmoccapy HAL ${targetName} reset to 100` : `${targetName} override reset`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
feed: {
|
||||
...state.feed,
|
||||
[target.stateKey]: 100,
|
||||
},
|
||||
gmoccapyGui,
|
||||
operatorMessage: hal.pin ? `gmoccapy HAL ${targetName} reset to 100` : `${targetName} override reset`,
|
||||
};
|
||||
}
|
||||
|
||||
function clampNumber(value, min, max) {
|
||||
if (!Number.isFinite(value)) return min;
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function buildDroFromFrame(frame, runtimeFeedback = null) {
|
||||
const dtg = runtimeFeedback?.dtg || null;
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user