完善 working5 gmoccapy HAL 输入对标
This commit is contained in:
@@ -2,6 +2,15 @@ import { getSourceReferencesForProfile } from "./source-reference-map.js";
|
||||
|
||||
const sourceReferenceObjects = getSourceReferencesForProfile("gmoccapy-xyzab");
|
||||
|
||||
const gmoccapyXyzabJogAxisPins = ["x", "y", "z", "a", "b"].flatMap((axis) => [
|
||||
`gmoccapy.jog.axis.jog-${axis}-plus`,
|
||||
`gmoccapy.jog.axis.jog-${axis}-minus`,
|
||||
]);
|
||||
|
||||
const gmoccapyXyzabJogIncrementPins = Array.from({ length: 6 }, (_, index) => (
|
||||
`gmoccapy.jog.jog-inc-${index}`
|
||||
));
|
||||
|
||||
export const gmoccapyXyzabPanelSchema = {
|
||||
id: "gmoccapy-xyzab-hal-component",
|
||||
profileId: "gmoccapy-xyzab",
|
||||
@@ -20,8 +29,8 @@ export const gmoccapyXyzabPanelSchema = {
|
||||
id: "tbtn-estop",
|
||||
type: "button",
|
||||
text: "ESTOP",
|
||||
halpin: "gmoccapy.h-button.button-0",
|
||||
halNet: "gmoccapy-h-button-0",
|
||||
halpin: "gmoccapy.v-button.button-0",
|
||||
halNet: "gmoccapy-v-button-0",
|
||||
target: "gmoccapy.command.STATE_ESTOP",
|
||||
nativeCommand: "STATE_ESTOP / STATE_ESTOP_RESET",
|
||||
webAction: { type: "ESTOP" },
|
||||
@@ -30,8 +39,8 @@ export const gmoccapyXyzabPanelSchema = {
|
||||
id: "tbtn-on",
|
||||
type: "button",
|
||||
text: "POWER",
|
||||
halpin: "gmoccapy.h-button.button-1",
|
||||
halNet: "gmoccapy-h-button-1",
|
||||
halpin: "gmoccapy.v-button.button-1",
|
||||
halNet: "gmoccapy-v-button-1",
|
||||
target: "gmoccapy.command.STATE_ON",
|
||||
nativeCommand: "STATE_ON / STATE_OFF",
|
||||
webAction: { type: "TOGGLE_POWER" },
|
||||
@@ -40,8 +49,8 @@ export const gmoccapyXyzabPanelSchema = {
|
||||
id: "rbt-manual",
|
||||
type: "button",
|
||||
text: "MANUAL",
|
||||
halpin: "gmoccapy.v-button.button-0",
|
||||
halNet: "gmoccapy-v-button-0",
|
||||
halpin: "gmoccapy.v-button.button-2",
|
||||
halNet: "gmoccapy-v-button-2",
|
||||
target: "gmoccapy.command.MODE_MANUAL",
|
||||
nativeCommand: "MODE_MANUAL",
|
||||
webAction: { type: "SET_MODE", mode: "manual" },
|
||||
@@ -50,8 +59,8 @@ export const gmoccapyXyzabPanelSchema = {
|
||||
id: "rbt-mdi",
|
||||
type: "button",
|
||||
text: "MDI",
|
||||
halpin: "gmoccapy.v-button.button-1",
|
||||
halNet: "gmoccapy-v-button-1",
|
||||
halpin: "gmoccapy.v-button.button-3",
|
||||
halNet: "gmoccapy-v-button-3",
|
||||
target: "gmoccapy.command.MODE_MDI",
|
||||
nativeCommand: "MODE_MDI",
|
||||
webAction: { type: "SET_MODE", mode: "mdi" },
|
||||
@@ -60,8 +69,8 @@ export const gmoccapyXyzabPanelSchema = {
|
||||
id: "rbt-auto",
|
||||
type: "button",
|
||||
text: "AUTO",
|
||||
halpin: "gmoccapy.v-button.button-2",
|
||||
halNet: "gmoccapy-v-button-2",
|
||||
halpin: "gmoccapy.v-button.button-4",
|
||||
halNet: "gmoccapy-v-button-4",
|
||||
target: "gmoccapy.command.MODE_AUTO",
|
||||
nativeCommand: "MODE_AUTO",
|
||||
webAction: { type: "SET_MODE", mode: "auto" },
|
||||
@@ -215,6 +224,26 @@ export const gmoccapyXyzabProfile = {
|
||||
{ signal: "spindle-at-speed", source: "near_speed.out", target: "spindle.0.at-speed" },
|
||||
{ signal: "spindle-abs", source: "abs_spindle_feedback.out", target: "gmoccapy.spindle_feedback_bar" },
|
||||
],
|
||||
toolChange: {
|
||||
strategy: "iocontrol-loopback",
|
||||
manualGmoccapyPinsConnected: false,
|
||||
postguiUnlinks: [
|
||||
"iocontrol.0.tool-change",
|
||||
"iocontrol.0.tool-changed",
|
||||
"iocontrol.0.tool-prep-number",
|
||||
],
|
||||
commentedManualGuiNets: [
|
||||
"gmoccapy.toolchange-change <= iocontrol.0.tool-change",
|
||||
"gmoccapy.toolchange-changed => iocontrol.0.tool-changed",
|
||||
"gmoccapy.toolchange-number <= iocontrol.0.tool-prep-number",
|
||||
],
|
||||
activeLoop: {
|
||||
signal: "tool-change-loop",
|
||||
source: "iocontrol.0.tool-change",
|
||||
target: "iocontrol.0.tool-changed",
|
||||
stage: "gmoccapy_postgui.hal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nativeRuntime: {
|
||||
@@ -269,11 +298,32 @@ export const gmoccapyXyzabProfile = {
|
||||
halPins: [
|
||||
"gmoccapy.h-button.button-0",
|
||||
"gmoccapy.v-button.button-0",
|
||||
"gmoccapy.jog.axis.jog-x-plus",
|
||||
"gmoccapy.jog.axis.jog-b-minus",
|
||||
...gmoccapyXyzabJogAxisPins,
|
||||
...gmoccapyXyzabJogIncrementPins,
|
||||
"gmoccapy.jog.jog-increment",
|
||||
"gmoccapy.jog.turtle-jog",
|
||||
"gmoccapy.feed.feed-override.counts",
|
||||
"gmoccapy.feed.feed-override.count-enable",
|
||||
"gmoccapy.feed.feed-override.analog-enable",
|
||||
"gmoccapy.feed.feed-override.direct-value",
|
||||
"gmoccapy.feed.reset-feed-override",
|
||||
"gmoccapy.spindle.spindle-override.counts",
|
||||
"gmoccapy.spindle.spindle-override.count-enable",
|
||||
"gmoccapy.spindle.spindle-override.analog-enable",
|
||||
"gmoccapy.spindle.spindle-override.direct-value",
|
||||
"gmoccapy.spindle.reset-spindle-override",
|
||||
"gmoccapy.rapid.rapid-override.counts",
|
||||
"gmoccapy.rapid.rapid-override.count-enable",
|
||||
"gmoccapy.rapid.rapid-override.analog-enable",
|
||||
"gmoccapy.rapid.rapid-override.direct-value",
|
||||
"gmoccapy.rapid.reset-rapid-override",
|
||||
"gmoccapy.jog.jog-velocity.counts",
|
||||
"gmoccapy.jog.jog-velocity.count-enable",
|
||||
"gmoccapy.jog.jog-velocity.analog-enable",
|
||||
"gmoccapy.jog.jog-velocity.direct-value",
|
||||
"gmoccapy.ignore-limits",
|
||||
"gmoccapy.optional-stop",
|
||||
"gmoccapy.blockdelete",
|
||||
"gmoccapy.tooloffset-x",
|
||||
"gmoccapy.tooloffset-z",
|
||||
"gmoccapy.toolchange-change",
|
||||
@@ -284,6 +334,8 @@ export const gmoccapyXyzabProfile = {
|
||||
"gmoccapy.spindle_feedback_bar",
|
||||
"gmoccapy.spindle_at_speed_led",
|
||||
"gmoccapy.error",
|
||||
"gmoccapy.delete-message",
|
||||
"gmoccapy.warning-confirm",
|
||||
],
|
||||
offsets: {
|
||||
toolOffsetX: 0,
|
||||
|
||||
@@ -20,6 +20,16 @@ export const gmoccapyCommunicationModel = {
|
||||
path: ["gmoccapy.* pins", "HAL shared memory", "halui/iocontrol/motmod/spindle_sim"],
|
||||
notes: "postgui HAL connects gmoccapy pins after halcomp.ready().",
|
||||
},
|
||||
hardwareButtons: {
|
||||
label: "HAL hardware buttons",
|
||||
path: ["external HAL bit", "gmoccapy.h-button/v-button pin", "_button_pin_changed", "visible sensitive GTK button", "linuxcnc.command()"],
|
||||
notes: "gmoccapy maps rising-edge hard-button pins to the current visible software button; insensitive or hidden targets are ignored.",
|
||||
},
|
||||
halPins: {
|
||||
label: "HAL input pins",
|
||||
path: ["external HAL value", "gmoccapy.* input pin", "hal_glib.GPin value_changed", "gmoccapy callback", "linuxcnc.command() or GTK widget state"],
|
||||
notes: "jog pins, jog increment pins, ignore-limits, optional-stop/blockdelete, override counts/direct-value, reset and message pins enter gmoccapy through HAL callbacks.",
|
||||
},
|
||||
remap: {
|
||||
label: "Interpreter REMAP",
|
||||
path: ["milltask", "RS274NGC", "Python remap prolog/ngc/epilog"],
|
||||
@@ -148,6 +158,34 @@ export const gmoccapyCommunicationModel = {
|
||||
nativeStatus: "iocontrol.0.coolant-flood/mist",
|
||||
gate: "blocked in estop/off",
|
||||
},
|
||||
{
|
||||
actionId: "hardware-button",
|
||||
webAction: "explicit data-gmoccapy-hal-pin button mapping",
|
||||
nativeCommand: "_button_pin_changed -> GtkButton emit clicked/pressed/toggled",
|
||||
nativeStatus: "same status as the target software button",
|
||||
gate: "rising-edge pin ignored when target button is hidden or insensitive",
|
||||
},
|
||||
{
|
||||
actionId: "hal-pin-input",
|
||||
webAction: "GMOCAPY_HAL_PIN",
|
||||
nativeCommand: "_ignore_limits/_optional_blocks/_blockdelete/_on_counts_changed/_on_analog_value_changed/_reset_override/_del_message_changed",
|
||||
nativeStatus: "ignore-limits, block delete, optional stop, feed/rapid/spindle/jog override widget state and operator message state",
|
||||
gate: "counts require count-enable, direct-value requires analog-enable, reset/delete-message pins are rising-edge only",
|
||||
},
|
||||
{
|
||||
actionId: "hal-jog-pin",
|
||||
webAction: "GMOCAPY_HAL_PIN jog.axis/jog-inc",
|
||||
nativeCommand: "_on_pin_jog_changed/_on_pin_incr_changed",
|
||||
nativeStatus: "manual jog command, active jog increment and gmoccapy.jog.jog-increment HAL OUT",
|
||||
gate: "axis pins use press/release level, task must be on and manual; increment pins are rising-edge only",
|
||||
},
|
||||
{
|
||||
actionId: "hal-warning-confirm",
|
||||
webAction: "GMOCAPY_HAL_PIN warning-confirm",
|
||||
nativeCommand: "dialogs.warning_dialog confirm_pin polling",
|
||||
nativeStatus: "active warning dialog response",
|
||||
gate: "level-polled while warning dialog is active",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -163,6 +201,8 @@ export function createGmoccapyCommunicationSummary(model = gmoccapyCommunication
|
||||
model.startupStages.findIndex((stage) => stage.id === "gmoccapy"),
|
||||
hasNativeNmlCommandPath: model.nativePaths.command.path.includes("NML emcCommand"),
|
||||
hasNativeHalPath: model.nativePaths.hal.path.includes("HAL shared memory"),
|
||||
hasNativeHardwareButtonPath: model.nativePaths.hardwareButtons.path.includes("_button_pin_changed"),
|
||||
hasNativeHalPinInputPath: model.nativePaths.halPins.path.includes("hal_glib.GPin value_changed"),
|
||||
hasWebStorePath: model.webPath.path.includes("store.dispatch"),
|
||||
semanticBoundary: model.semanticBoundary,
|
||||
};
|
||||
|
||||
@@ -68,7 +68,13 @@ export const gmoccapyHalModel = {
|
||||
"gmoccapy.jog.axis.jog-b-plus",
|
||||
"gmoccapy.jog.axis.jog-b-minus",
|
||||
"gmoccapy.jog.jog-inc-0",
|
||||
"gmoccapy.jog.jog-inc-1",
|
||||
"gmoccapy.jog.jog-inc-2",
|
||||
"gmoccapy.jog.jog-inc-3",
|
||||
"gmoccapy.jog.jog-inc-4",
|
||||
"gmoccapy.jog.jog-inc-5",
|
||||
"gmoccapy.jog.jog-increment",
|
||||
"gmoccapy.jog.turtle-jog",
|
||||
],
|
||||
webSource: "JOG dispatch and machine.jogIncrement",
|
||||
semanticBoundary: "native_jog_pin_reference_web_task_policy_gate",
|
||||
@@ -77,17 +83,38 @@ export const gmoccapyHalModel = {
|
||||
group: "override",
|
||||
pins: [
|
||||
"gmoccapy.feed.feed-override.counts",
|
||||
"gmoccapy.feed.feed-override.count-enable",
|
||||
"gmoccapy.feed.feed-override.analog-enable",
|
||||
"gmoccapy.feed.feed-override.direct-value",
|
||||
"gmoccapy.feed.reset-feed-override",
|
||||
"gmoccapy.spindle.spindle-override.counts",
|
||||
"gmoccapy.spindle.spindle-override.count-enable",
|
||||
"gmoccapy.spindle.spindle-override.analog-enable",
|
||||
"gmoccapy.spindle.spindle-override.direct-value",
|
||||
"gmoccapy.spindle.reset-spindle-override",
|
||||
"gmoccapy.jog.jog-velocity.counts",
|
||||
"gmoccapy.jog.jog-velocity.count-enable",
|
||||
"gmoccapy.jog.jog-velocity.analog-enable",
|
||||
"gmoccapy.jog.jog-velocity.direct-value",
|
||||
"gmoccapy.rapid.rapid-override.counts",
|
||||
"gmoccapy.rapid.rapid-override.count-enable",
|
||||
"gmoccapy.rapid.rapid-override.analog-enable",
|
||||
"gmoccapy.rapid.rapid-override.direct-value",
|
||||
"gmoccapy.rapid.reset-rapid-override",
|
||||
],
|
||||
webSource: "feed/spindle override state",
|
||||
semanticBoundary: "native_override_pin_reference_web_state_mapping",
|
||||
},
|
||||
{
|
||||
group: "operator-inputs",
|
||||
pins: [
|
||||
"gmoccapy.ignore-limits",
|
||||
"gmoccapy.optional-stop",
|
||||
"gmoccapy.blockdelete",
|
||||
],
|
||||
webSource: "gmoccapyGui ignoreLimits, optionalBlocks, optionalStop",
|
||||
semanticBoundary: "native_ignore_limits_optional_stop_blockdelete_pin_reference_web_state_mapping",
|
||||
},
|
||||
{
|
||||
group: "tool",
|
||||
pins: [
|
||||
@@ -98,7 +125,7 @@ export const gmoccapyHalModel = {
|
||||
"gmoccapy.toolchange-changed",
|
||||
"gmoccapy.toolchange-number",
|
||||
],
|
||||
webSource: "toolPreview and tool-change diagnostics",
|
||||
webSource: "toolPreview and disconnected manual tool-change diagnostics",
|
||||
semanticBoundary: "native_tool_pin_reference_simulated_tool_loop",
|
||||
},
|
||||
{
|
||||
@@ -257,12 +284,319 @@ export const gmoccapyHalModel = {
|
||||
role: "automatic simulated tool-change completion",
|
||||
},
|
||||
],
|
||||
hardwareButtons: {
|
||||
source: {
|
||||
path: "linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py",
|
||||
methods: ["_button_pin_changed", "_get_child_button", "_make_hal_pins"],
|
||||
},
|
||||
behavior: {
|
||||
edge: "rising-edge-only",
|
||||
hButtonContainer: "current ntb_button page visible children",
|
||||
vButtonContainer: "vbtb_main visible children",
|
||||
insensitiveTarget: "ignored",
|
||||
hiddenLabelsSkipped: true,
|
||||
semanticBoundary: "native_gmoccapy_hardware_button_pin_to_visible_sensitive_button",
|
||||
},
|
||||
verticalMain: [
|
||||
{
|
||||
index: 0,
|
||||
pin: "gmoccapy.v-button.button-0",
|
||||
nativeWidget: "tbtn_estop",
|
||||
webAction: "ESTOP",
|
||||
webDispatch: { type: "ESTOP" },
|
||||
webSelector: '[data-action="estop"]',
|
||||
gate: "always clickable safety action",
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
pin: "gmoccapy.v-button.button-1",
|
||||
nativeWidget: "tbtn_on",
|
||||
webAction: "TOGGLE_POWER",
|
||||
webDispatch: { type: "TOGGLE_POWER" },
|
||||
webSelector: '[data-action="power"]',
|
||||
gate: "blocked while STATE_ESTOP",
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
pin: "gmoccapy.v-button.button-2",
|
||||
nativeWidget: "rbt_manual",
|
||||
webAction: "SET_MODE manual",
|
||||
webDispatch: { type: "SET_MODE", mode: "manual" },
|
||||
webSelector: '[data-action="mode-manual"]',
|
||||
gate: "machine on and AUTO interpreter idle",
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
pin: "gmoccapy.v-button.button-3",
|
||||
nativeWidget: "rbt_mdi",
|
||||
webAction: "SET_MODE mdi",
|
||||
webDispatch: { type: "SET_MODE", mode: "mdi" },
|
||||
webSelector: '[data-action="mode-mdi"]',
|
||||
gate: "machine on and homed when NO_FORCE_HOMING=0",
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
pin: "gmoccapy.v-button.button-4",
|
||||
nativeWidget: "rbt_auto",
|
||||
webAction: "SET_MODE auto",
|
||||
webDispatch: { type: "SET_MODE", mode: "auto" },
|
||||
webSelector: '[data-action="mode-auto"]',
|
||||
gate: "machine on and homed when NO_FORCE_HOMING=0",
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
pin: "gmoccapy.v-button.button-5",
|
||||
nativeWidget: "tbtn_user_tabs",
|
||||
webAction: "SET_MODE manual",
|
||||
webDispatch: { type: "SET_MODE", mode: "jog" },
|
||||
webSelector: '[data-action="mode-jog"]',
|
||||
gate: "machine on and manual page available",
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
pin: "gmoccapy.v-button.button-6",
|
||||
nativeWidget: "tbtn_setup",
|
||||
webAction: "UI_CONTROL",
|
||||
webSelector: null,
|
||||
gate: "native setup page is diagnostic-only in the Web shell",
|
||||
},
|
||||
],
|
||||
bottomMain: [
|
||||
{
|
||||
index: 0,
|
||||
pin: "gmoccapy.h-button.button-0",
|
||||
nativeWidget: "btn_homing",
|
||||
webAction: "HOME",
|
||||
webDispatch: { type: "HOME" },
|
||||
webSelector: '[data-action="HOME"]',
|
||||
gate: "machine on and manual mode",
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
pin: "gmoccapy.h-button.button-1",
|
||||
nativeWidget: "btn_touch",
|
||||
webAction: "UI_CONTROL",
|
||||
webSelector: null,
|
||||
gate: "touch-off page not implemented in Web shell",
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
pin: "gmoccapy.h-button.button-3",
|
||||
nativeWidget: "btn_tool",
|
||||
webAction: "UI_CONTROL",
|
||||
webSelector: null,
|
||||
gate: "tool editor page not implemented in Web shell",
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
pin: "gmoccapy.h-button.button-6",
|
||||
nativeWidget: "tbtn_switch_mode",
|
||||
webAction: "SET_KINS_TYPE identity",
|
||||
webSelector: null,
|
||||
gate: "native joint/world switch page position is diagnostic-only in the Web shell",
|
||||
},
|
||||
],
|
||||
},
|
||||
halPinActions: {
|
||||
source: {
|
||||
path: "linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py",
|
||||
methods: [
|
||||
"_ignore_limits",
|
||||
"_optional_blocks",
|
||||
"_blockdelete",
|
||||
"_on_pin_jog_changed",
|
||||
"_on_pin_incr_changed",
|
||||
"_on_counts_changed",
|
||||
"_on_analog_enable_changed",
|
||||
"_on_analog_value_changed",
|
||||
"_reset_override",
|
||||
"_del_message_changed",
|
||||
],
|
||||
prefPath: "linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.pref",
|
||||
},
|
||||
jogPins: {
|
||||
axes: [
|
||||
{ axis: "x", plus: "gmoccapy.jog.axis.jog-x-plus", minus: "gmoccapy.jog.axis.jog-x-minus" },
|
||||
{ axis: "y", plus: "gmoccapy.jog.axis.jog-y-plus", minus: "gmoccapy.jog.axis.jog-y-minus" },
|
||||
{ axis: "z", plus: "gmoccapy.jog.axis.jog-z-plus", minus: "gmoccapy.jog.axis.jog-z-minus" },
|
||||
{ axis: "a", plus: "gmoccapy.jog.axis.jog-a-plus", minus: "gmoccapy.jog.axis.jog-a-minus" },
|
||||
{ axis: "b", plus: "gmoccapy.jog.axis.jog-b-plus", minus: "gmoccapy.jog.axis.jog-b-minus" },
|
||||
],
|
||||
increments: [
|
||||
{ index: 0, pin: "gmoccapy.jog.jog-inc-0", label: "Continuous", distance: 0 },
|
||||
{ index: 1, pin: "gmoccapy.jog.jog-inc-1", label: "1.000 mm", distance: 1 },
|
||||
{ index: 2, pin: "gmoccapy.jog.jog-inc-2", label: "0.100 mm", distance: 0.1 },
|
||||
{ index: 3, pin: "gmoccapy.jog.jog-inc-3", label: "0.010 mm", distance: 0.01 },
|
||||
{ index: 4, pin: "gmoccapy.jog.jog-inc-4", label: "0.001 mm", distance: 0.001 },
|
||||
{ index: 5, pin: "gmoccapy.jog.jog-inc-5", label: "1.2345 in", distance: 31.3563 },
|
||||
],
|
||||
outputPin: "gmoccapy.jog.jog-increment",
|
||||
turtlePin: "gmoccapy.jog.turtle-jog",
|
||||
nativeCallbacks: {
|
||||
axis: "_on_pin_jog_changed -> _on_btn_jog_pressed/_on_btn_jog_released",
|
||||
increment: "_on_pin_incr_changed -> _jog_increment_changed",
|
||||
turtle: "_on_pin_turtle_jog -> tbtn_turtle_jog.set_active",
|
||||
},
|
||||
},
|
||||
operatorPins: [
|
||||
{
|
||||
pin: "gmoccapy.ignore-limits",
|
||||
nativeCallback: "_ignore_limits -> chk_ignore_limits.set_active",
|
||||
nativeCommand: "on_chk_ignore_limits_toggled -> command.override_limits() when active",
|
||||
webDispatch: { type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.ignore-limits" },
|
||||
webState: "gmoccapyGui.ignoreLimits",
|
||||
},
|
||||
{
|
||||
pin: "gmoccapy.optional-stop",
|
||||
nativeCallback: "_optional_blocks",
|
||||
nativeCommand: "tbtn_optional_blocks -> command.set_block_delete(state)",
|
||||
webDispatch: { type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.optional-stop" },
|
||||
webState: "gmoccapyGui.optionalBlocks",
|
||||
},
|
||||
{
|
||||
pin: "gmoccapy.blockdelete",
|
||||
nativeCallback: "_blockdelete",
|
||||
nativeCommand: "command.set_optional_stop(state)",
|
||||
webDispatch: { type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.blockdelete" },
|
||||
webState: "gmoccapyGui.optionalStop",
|
||||
},
|
||||
],
|
||||
overridePins: [
|
||||
{
|
||||
target: "feed",
|
||||
counts: "gmoccapy.feed.feed-override.counts",
|
||||
countEnable: "gmoccapy.feed.feed-override.count-enable",
|
||||
analogEnable: "gmoccapy.feed.feed-override.analog-enable",
|
||||
directValue: "gmoccapy.feed.feed-override.direct-value",
|
||||
reset: "gmoccapy.feed.reset-feed-override",
|
||||
nativeWidget: "spc_feed",
|
||||
scalePref: "scale_feed_override=1",
|
||||
webState: "feed.feedOverride",
|
||||
},
|
||||
{
|
||||
target: "rapid",
|
||||
counts: "gmoccapy.rapid.rapid-override.counts",
|
||||
countEnable: "gmoccapy.rapid.rapid-override.count-enable",
|
||||
analogEnable: "gmoccapy.rapid.rapid-override.analog-enable",
|
||||
directValue: "gmoccapy.rapid.rapid-override.direct-value",
|
||||
reset: "gmoccapy.rapid.reset-rapid-override",
|
||||
nativeWidget: "spc_rapid",
|
||||
scalePref: "scale_rapid_override=1",
|
||||
webState: "feed.rapidOverride",
|
||||
},
|
||||
{
|
||||
target: "spindle",
|
||||
counts: "gmoccapy.spindle.spindle-override.counts",
|
||||
countEnable: "gmoccapy.spindle.spindle-override.count-enable",
|
||||
analogEnable: "gmoccapy.spindle.spindle-override.analog-enable",
|
||||
directValue: "gmoccapy.spindle.spindle-override.direct-value",
|
||||
reset: "gmoccapy.spindle.reset-spindle-override",
|
||||
nativeWidget: "spc_spindle",
|
||||
scalePref: "scale_spindle_override=1",
|
||||
webState: "spindle.override",
|
||||
},
|
||||
{
|
||||
target: "jogVelocity",
|
||||
counts: "gmoccapy.jog.jog-velocity.counts",
|
||||
countEnable: "gmoccapy.jog.jog-velocity.count-enable",
|
||||
analogEnable: "gmoccapy.jog.jog-velocity.analog-enable",
|
||||
directValue: "gmoccapy.jog.jog-velocity.direct-value",
|
||||
reset: null,
|
||||
nativeWidget: "spc_lin_jog_vel",
|
||||
scalePref: "scale_jog_vel=140.4",
|
||||
webState: "gmoccapyGui.jogVelocity",
|
||||
},
|
||||
],
|
||||
messagePins: [
|
||||
{
|
||||
pin: "gmoccapy.delete-message",
|
||||
nativeCallback: "_del_message_changed",
|
||||
nativeCommand: "pin true deletes first alert message when gmoccapy.error is set, otherwise del_last()",
|
||||
webState: "operatorMessage",
|
||||
},
|
||||
{
|
||||
pin: "gmoccapy.warning-confirm",
|
||||
nativeCallback: "dialogs.warning_dialog periodic confirm_pin poll",
|
||||
nativeCommand: "pin true responds OK to active warning dialog",
|
||||
webState: "gmoccapyGui.warningConfirm",
|
||||
},
|
||||
{
|
||||
pin: "gmoccapy.error",
|
||||
nativeCallback: "_show_error/_on_message_deleted",
|
||||
nativeCommand: "HAL OUT bit tracks alert messages",
|
||||
webState: "gmoccapyGui.error",
|
||||
},
|
||||
],
|
||||
behavior: {
|
||||
optionalStopPinCrossesToBlockDelete: true,
|
||||
blockdeletePinCrossesToOptionalStop: true,
|
||||
jogAxisPinsPressRelease: true,
|
||||
jogPinsUseTaskPolicyGate: true,
|
||||
jogIncrementPinsRisingEdgeOnly: true,
|
||||
jogIncrementZeroIsContinuous: true,
|
||||
turtleJogLevelDriven: true,
|
||||
countInputRequiresEnable: true,
|
||||
directValueRequiresAnalogEnable: true,
|
||||
resetPinsRisingEdgeOnly: true,
|
||||
deleteMessageRisingEdgeOnly: true,
|
||||
warningConfirmLevelPolled: true,
|
||||
directValueRange: "0..1",
|
||||
semanticBoundary: "native_gmoccapy_hal_pin_callbacks_to_web_store_actions",
|
||||
},
|
||||
},
|
||||
toolChange: {
|
||||
strategy: "iocontrol-loopback",
|
||||
manualGmoccapyPinsConnected: false,
|
||||
remapCodes: ["M6", "M61"],
|
||||
postguiUnlinks: [
|
||||
{
|
||||
pin: "iocontrol.0.tool-change",
|
||||
reason: "gmoccapy_postgui.hal removes the core_sim connection before applying the final loopback",
|
||||
},
|
||||
{
|
||||
pin: "iocontrol.0.tool-changed",
|
||||
reason: "gmoccapy_postgui.hal removes the core_sim connection before applying the final loopback",
|
||||
},
|
||||
{
|
||||
pin: "iocontrol.0.tool-prep-number",
|
||||
reason: "manual gmoccapy tool-change number net is not active in this simulation",
|
||||
},
|
||||
],
|
||||
commentedManualGuiNets: [
|
||||
{
|
||||
signal: "tool-change",
|
||||
source: "iocontrol.0.tool-change",
|
||||
target: "gmoccapy.toolchange-change",
|
||||
},
|
||||
{
|
||||
signal: "tool-changed",
|
||||
source: "gmoccapy.toolchange-changed",
|
||||
target: "iocontrol.0.tool-changed",
|
||||
},
|
||||
{
|
||||
signal: "tool-prep-number",
|
||||
source: "iocontrol.0.tool-prep-number",
|
||||
target: "gmoccapy.toolchange-number",
|
||||
},
|
||||
],
|
||||
activeLoop: {
|
||||
signal: "tool-change-loop",
|
||||
source: "iocontrol.0.tool-change",
|
||||
target: "iocontrol.0.tool-changed",
|
||||
stage: "POSTGUI_HALFILE",
|
||||
},
|
||||
semanticBoundary: "gmoccapy_xyzab_iocontrol_tool_change_loop_no_manual_gui_dialog",
|
||||
},
|
||||
semanticBoundary: "gmoccapy_hal_pin_postgui_reference_web_diagnostic_only",
|
||||
};
|
||||
|
||||
export function createGmoccapyHalSummary(model = gmoccapyHalModel) {
|
||||
const pinCount = model.nativePins.reduce((total, group) => total + group.pins.length, 0);
|
||||
const postguiFile = model.halFiles.find((file) => file.stage === "POSTGUI_HALFILE");
|
||||
const mappedHardwareButtonCount = [
|
||||
...(model.hardwareButtons?.verticalMain || []),
|
||||
...(model.hardwareButtons?.bottomMain || []),
|
||||
].filter((button) => button.webSelector).length;
|
||||
return {
|
||||
apiName: `${model.apiName}-summary`,
|
||||
profileId: model.profileId,
|
||||
@@ -275,6 +609,48 @@ export function createGmoccapyHalSummary(model = gmoccapyHalModel) {
|
||||
postguiRequiresHalcompReady: Boolean(postguiFile?.requiresHalcompReady) &&
|
||||
model.postguiNets.every((net) => net.requiresHalcompReady),
|
||||
hasToolChangeSimulationLoop: model.postguiNets.some((net) => net.simulationLoop),
|
||||
toolChangeStrategy: model.toolChange.strategy,
|
||||
manualToolChangeGuiConnected: model.toolChange.manualGmoccapyPinsConnected,
|
||||
postguiToolUnlinkCount: model.toolChange.postguiUnlinks.length,
|
||||
commentedManualToolChangeNetCount: model.toolChange.commentedManualGuiNets.length,
|
||||
hardwareButtonVerticalCount: model.hardwareButtons.verticalMain.length,
|
||||
hardwareButtonBottomMainCount: model.hardwareButtons.bottomMain.length,
|
||||
mappedHardwareButtonCount,
|
||||
hardwareButtonEdge: model.hardwareButtons.behavior.edge,
|
||||
hardwareButtonInsensitiveTarget: model.hardwareButtons.behavior.insensitiveTarget,
|
||||
operatorInputPinCount: model.halPinActions.operatorPins.length,
|
||||
overridePinTargetCount: model.halPinActions.overridePins.length,
|
||||
jogAxisPinCount: (model.halPinActions.jogPins?.axes || []).length * 2,
|
||||
jogIncrementPinCount: model.halPinActions.jogPins?.increments?.length || 0,
|
||||
messageInputPinCount: model.halPinActions.messagePins.length,
|
||||
optionalStopPinCrossesToBlockDelete: model.halPinActions.behavior.optionalStopPinCrossesToBlockDelete,
|
||||
blockdeletePinCrossesToOptionalStop: model.halPinActions.behavior.blockdeletePinCrossesToOptionalStop,
|
||||
jogAxisPinsPressRelease: model.halPinActions.behavior.jogAxisPinsPressRelease,
|
||||
jogPinsUseTaskPolicyGate: model.halPinActions.behavior.jogPinsUseTaskPolicyGate,
|
||||
jogIncrementPinsRisingEdgeOnly: model.halPinActions.behavior.jogIncrementPinsRisingEdgeOnly,
|
||||
jogIncrementZeroIsContinuous: model.halPinActions.behavior.jogIncrementZeroIsContinuous,
|
||||
turtleJogLevelDriven: model.halPinActions.behavior.turtleJogLevelDriven,
|
||||
countInputRequiresEnable: model.halPinActions.behavior.countInputRequiresEnable,
|
||||
directValueRequiresAnalogEnable: model.halPinActions.behavior.directValueRequiresAnalogEnable,
|
||||
resetPinsRisingEdgeOnly: model.halPinActions.behavior.resetPinsRisingEdgeOnly,
|
||||
deleteMessageRisingEdgeOnly: model.halPinActions.behavior.deleteMessageRisingEdgeOnly,
|
||||
warningConfirmLevelPolled: model.halPinActions.behavior.warningConfirmLevelPolled,
|
||||
semanticBoundary: model.semanticBoundary,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGmoccapyHardwareButton(input, model = gmoccapyHalModel) {
|
||||
const pin = typeof input === "string" ? input : input?.pin;
|
||||
const location = typeof input === "object" ? input?.location || input?.panel : null;
|
||||
const index = typeof input === "object" && input?.index !== undefined ? Number(input.index) : null;
|
||||
const allButtons = [
|
||||
...(model.hardwareButtons?.verticalMain || []).map((button) => ({ ...button, panel: "right", location: "v" })),
|
||||
...(model.hardwareButtons?.bottomMain || []).map((button) => ({ ...button, panel: "bottom", location: "h" })),
|
||||
];
|
||||
if (pin) return allButtons.find((button) => button.pin === pin) || null;
|
||||
if (!Number.isFinite(index)) return null;
|
||||
const normalizedLocation = location === "right" || location === "v" || location === "vertical" ? "v"
|
||||
: location === "bottom" || location === "h" || location === "horizontal" ? "h"
|
||||
: null;
|
||||
return allButtons.find((button) => button.location === normalizedLocation && button.index === index) || null;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -735,7 +735,7 @@ button[data-icon-name] span {
|
||||
grid-area: override;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(3, minmax(0, 1fr));
|
||||
grid-template-rows: repeat(4, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
padding: 6px;
|
||||
border-top: 2px solid var(--border);
|
||||
@@ -806,7 +806,7 @@ button[data-icon-name] span {
|
||||
|
||||
.stepper {
|
||||
display: grid;
|
||||
grid-template-columns: 30px minmax(54px, 1fr) 30px;
|
||||
grid-template-columns: 30px minmax(48px, 1fr) 30px 36px;
|
||||
gap: 2px;
|
||||
align-items: center;
|
||||
margin-top: 1px;
|
||||
@@ -827,6 +827,42 @@ button[data-icon-name] span {
|
||||
min-width: 0;
|
||||
min-height: 26px;
|
||||
padding: 2px 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.toggle-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 3px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.toggle-row button {
|
||||
min-width: 0;
|
||||
min-height: 24px;
|
||||
padding: 2px 3px;
|
||||
overflow: hidden;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toggle-row button[data-active="true"] {
|
||||
background: #ffd25f;
|
||||
border-color: #a76e00;
|
||||
}
|
||||
|
||||
.status-chip {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: 2px 4px;
|
||||
background: #ebe6dc;
|
||||
border: 1px solid #c6bfb3;
|
||||
color: #4f4a42;
|
||||
font: 10px/1.2 "Courier New", monospace;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.spindle-coolant-panel {
|
||||
|
||||
@@ -353,13 +353,13 @@ function renderSidebar(element, state, dispatch) {
|
||||
const manualGate = gateLinuxCncTaskAction(state, { type: "SET_MODE", mode: "manual" });
|
||||
const mdiGate = gateLinuxCncTaskAction(state, { type: "SET_MODE", mode: "mdi" });
|
||||
element.innerHTML = `
|
||||
${iconButtonHtml({ buttonId: "tbtn_estop", action: "estop", label: "E-STOP", title: "Estop the machine / F1", className: "sidebar-button estop", active: state.machine.estopActive })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_on", action: "power", label: "POWER", title: "Turn the machine on/off / F2", className: "sidebar-button power", active: state.machine.powerOn })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_estop", action: "estop", label: "E-STOP", title: "Estop the machine / F1", className: "sidebar-button estop", active: state.machine.estopActive, attrs: nativeButtonAttrs("v", 0) })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_on", action: "power", label: "POWER", title: "Turn the machine on/off / F2", className: "sidebar-button power", active: state.machine.powerOn, attrs: nativeButtonAttrs("v", 1) })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_setup", action: "reset", label: "RESET", title: "Reset estop and return machine off", className: "sidebar-button", active: false })}
|
||||
${iconButtonHtml({ buttonId: "rbt_auto", action: "mode-auto", label: "AUTO", title: autoGate.operatorMessage || "Enter auto mode", className: "sidebar-button", active: state.machine.mode === "auto", gate: autoGate })}
|
||||
${iconButtonHtml({ buttonId: "rbt_manual", action: "mode-manual", label: "MANUAL", title: manualGate.operatorMessage || "Enter manual mode", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_user_tabs", action: "mode-jog", label: "JOG", title: manualGate.operatorMessage || "Manual jog page", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate })}
|
||||
${iconButtonHtml({ buttonId: "rbt_mdi", action: "mode-mdi", label: "MDI", title: mdiGate.operatorMessage || "Enter MDI mode", className: "sidebar-button", active: state.machine.mode === "mdi", gate: mdiGate })}
|
||||
${iconButtonHtml({ buttonId: "rbt_auto", action: "mode-auto", label: "AUTO", title: autoGate.operatorMessage || "Enter auto mode", className: "sidebar-button", active: state.machine.mode === "auto", gate: autoGate, attrs: nativeButtonAttrs("v", 4) })}
|
||||
${iconButtonHtml({ buttonId: "rbt_manual", action: "mode-manual", label: "MANUAL", title: manualGate.operatorMessage || "Enter manual mode", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate, attrs: nativeButtonAttrs("v", 2) })}
|
||||
${iconButtonHtml({ buttonId: "tbtn_user_tabs", action: "mode-jog", label: "JOG", title: manualGate.operatorMessage || "Manual jog page", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate, attrs: nativeButtonAttrs("v", 5) })}
|
||||
${iconButtonHtml({ buttonId: "rbt_mdi", action: "mode-mdi", label: "MDI", title: mdiGate.operatorMessage || "Enter MDI mode", className: "sidebar-button", active: state.machine.mode === "mdi", gate: mdiGate, attrs: nativeButtonAttrs("v", 3) })}
|
||||
<button type="button" class="sidebar-button" data-action="kins-identity" data-active="${state.kinsType === "identity"}">IDENTITY</button>
|
||||
<button type="button" class="sidebar-button" data-action="kins-tcp" data-active="${state.kinsType.startsWith("tcp-")}"${tcpDisabled}>TCP</button>
|
||||
<time>13:30:31<br />20.06.2026</time>
|
||||
@@ -449,8 +449,14 @@ function renderInfoTabs(element, state) {
|
||||
<dt>HAL model:</dt><dd data-gmoccapy-hal="boundary">${gmoccapyHalModel.semanticBoundary}</dd>
|
||||
<dt>HAL files:</dt><dd data-gmoccapy-hal="files">${halSummary.halFileCount} files / POSTGUI ${halSummary.postguiRequiresHalcompReady ? "after halcomp.ready" : "pending"}</dd>
|
||||
<dt>HAL pins:</dt><dd data-gmoccapy-hal="pins">${halSummary.nativePinGroupCount} groups / ${halSummary.nativePinCount} pins</dd>
|
||||
<dt>Hard buttons:</dt><dd data-gmoccapy-hal="hard-buttons">${formatGmoccapyHardButtons(halSummary)}</dd>
|
||||
<dt>HAL jog:</dt><dd data-gmoccapy-hal="jog-inputs">${formatGmoccapyJogInputs(halSummary, state)}</dd>
|
||||
<dt>HAL inputs:</dt><dd data-gmoccapy-hal="operator-inputs">${formatGmoccapyHalInputs(halSummary, state)}</dd>
|
||||
<dt>HAL overrides:</dt><dd data-gmoccapy-hal="override-inputs">${formatGmoccapyOverrideInputs(halSummary, state)}</dd>
|
||||
<dt>HAL messages:</dt><dd data-gmoccapy-hal="message-inputs">${formatGmoccapyMessageInputs(halSummary, state)}</dd>
|
||||
<dt>postgui nets:</dt><dd data-gmoccapy-hal="postgui">${gmoccapyHalModel.postguiNets.map((net) => net.signal).join(" / ")}</dd>
|
||||
<dt>Tool loop:</dt><dd data-gmoccapy-hal="tool-loop">${halSummary.hasToolChangeSimulationLoop ? "simulated tool-change loop" : "pending"}</dd>
|
||||
<dt>Tool change:</dt><dd data-gmoccapy-hal="tool-change">${formatGmoccapyToolChange(halSummary)}</dd>
|
||||
<dt>Operator:</dt><dd data-operator-message>${escapeHtml(state.operatorMessage)}</dd>
|
||||
</dl>
|
||||
`;
|
||||
@@ -597,6 +603,15 @@ function renderOverride(element, state, dispatch) {
|
||||
</section>
|
||||
${overrideControl("Rapid Override", "rapid", state.feed.rapidOverride)}
|
||||
${overrideControl("Feed Rate", "feed", state.feed.feedOverride, `F ${state.feed.feedRate}`)}
|
||||
<section class="override-control" data-control="gmoccapy-hal-inputs">
|
||||
<h2>HAL Inputs</h2>
|
||||
<div class="toggle-row">
|
||||
<button type="button" data-action="ignore-limits" data-active="${state.gmoccapyGui.ignoreLimits}">Limits</button>
|
||||
<button type="button" data-action="block-delete" data-active="${state.gmoccapyGui.optionalBlocks}">/ Block</button>
|
||||
<button type="button" data-action="optional-stop" data-active="${state.gmoccapyGui.optionalStop}">M1</button>
|
||||
</div>
|
||||
<div class="status-chip" data-value="gmoccapy-hal-last">${escapeHtml(formatGmoccapyHalLastInput(state))}</div>
|
||||
</section>
|
||||
`;
|
||||
|
||||
for (const target of ["rapid", "feed"]) {
|
||||
@@ -606,7 +621,22 @@ function renderOverride(element, state, dispatch) {
|
||||
element.querySelector(`[data-action="${target}-override-up"]`).addEventListener("click", () => {
|
||||
dispatch({ type: "ADJUST_OVERRIDE", target, delta: 10 });
|
||||
});
|
||||
const reset = element.querySelector(`[data-action="${target}-override-reset"]`);
|
||||
if (reset) {
|
||||
reset.addEventListener("click", () => {
|
||||
dispatch({ type: "RESET_OVERRIDE", target });
|
||||
});
|
||||
}
|
||||
}
|
||||
element.querySelector('[data-action="ignore-limits"]').addEventListener("click", () => {
|
||||
dispatch({ type: "SET_IGNORE_LIMITS", enabled: !state.gmoccapyGui.ignoreLimits });
|
||||
});
|
||||
element.querySelector('[data-action="block-delete"]').addEventListener("click", () => {
|
||||
dispatch({ type: "SET_BLOCK_DELETE", enabled: !state.gmoccapyGui.optionalBlocks });
|
||||
});
|
||||
element.querySelector('[data-action="optional-stop"]').addEventListener("click", () => {
|
||||
dispatch({ type: "SET_OPTIONAL_STOP", enabled: !state.gmoccapyGui.optionalStop });
|
||||
});
|
||||
}
|
||||
|
||||
function overrideControl(label, target, value, prefix = "") {
|
||||
@@ -618,6 +648,7 @@ function overrideControl(label, target, value, prefix = "") {
|
||||
<button type="button" data-action="${target}-override-down">-</button>
|
||||
<div data-value="${target}-override">${value} %</div>
|
||||
<button type="button" data-action="${target}-override-up">+</button>
|
||||
<button type="button" data-action="${target}-override-reset">100</button>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
@@ -647,6 +678,7 @@ function renderSpindleCoolant(element, state, dispatch) {
|
||||
<button type="button" data-action="spindle-override-down">-</button>
|
||||
<div data-value="spindle-override">${state.spindle.override} %</div>
|
||||
<button type="button" data-action="spindle-override-up">+</button>
|
||||
<button type="button" data-action="spindle-override-reset">100</button>
|
||||
</div>
|
||||
<div class="spindle-range"><i style="width: ${(state.spindle.rpm / 6000) * 100}%"></i></div>
|
||||
</section>
|
||||
@@ -664,6 +696,9 @@ function renderSpindleCoolant(element, state, dispatch) {
|
||||
element.querySelector('[data-action="spindle-override-up"]').addEventListener("click", () => {
|
||||
dispatch({ type: "ADJUST_SPINDLE_OVERRIDE", delta: 10 });
|
||||
});
|
||||
element.querySelector('[data-action="spindle-override-reset"]').addEventListener("click", () => {
|
||||
dispatch({ type: "RESET_OVERRIDE", target: "spindle" });
|
||||
});
|
||||
element.querySelector('[data-action="spindle-forward"]').addEventListener("click", () => {
|
||||
dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "forward" });
|
||||
});
|
||||
@@ -716,7 +751,7 @@ function renderBottomControls(element, state, dispatch) {
|
||||
title: gate.allowed ? label : gate.operatorMessage || "blocked",
|
||||
active: bottomControlActive(state, action),
|
||||
disabled: Boolean(disabled),
|
||||
attrs: blockedAttrs,
|
||||
attrs: `${blockedAttrs}${bottomNativeButtonAttrs(action)}`,
|
||||
});
|
||||
})
|
||||
.join("")}
|
||||
@@ -785,6 +820,17 @@ function bottomIconButtonId(action) {
|
||||
}[action] || "btn_load";
|
||||
}
|
||||
|
||||
function bottomNativeButtonAttrs(action) {
|
||||
if (action === "HOME") return nativeButtonAttrs("h", 0);
|
||||
return "";
|
||||
}
|
||||
|
||||
function nativeButtonAttrs(location, index) {
|
||||
const prefix = location === "v" ? "v-button" : "h-button";
|
||||
const panel = location === "v" ? "right" : "bottom";
|
||||
return ` data-gmoccapy-hal-pin="gmoccapy.${prefix}.button-${index}" data-gmoccapy-native-panel="${panel}" data-gmoccapy-native-button-index="${index}"`;
|
||||
}
|
||||
|
||||
function bottomControlActive(state, action) {
|
||||
if (action === "PAUSE") return state.runState === "paused";
|
||||
if (action === "RUN" || action === "RESUME") return state.runState === "running";
|
||||
@@ -855,6 +901,79 @@ function formatGmoccapyActionMapping(actionId) {
|
||||
return `${mapping.nativeCommand} / ${mapping.gate}`;
|
||||
}
|
||||
|
||||
function formatGmoccapyToolChange(halSummary) {
|
||||
const manualPins = halSummary.manualToolChangeGuiConnected ? "manual GUI pins connected" : "manual GUI pins disconnected";
|
||||
return [
|
||||
halSummary.toolChangeStrategy,
|
||||
manualPins,
|
||||
`${halSummary.postguiToolUnlinkCount} postgui unlinkp`,
|
||||
`${halSummary.commentedManualToolChangeNetCount} commented manual nets`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatGmoccapyHalInputs(halSummary, state) {
|
||||
return [
|
||||
`${halSummary.operatorInputPinCount} operator pins`,
|
||||
halSummary.optionalStopPinCrossesToBlockDelete ? "optional-stop -> block delete" : "optional-stop direct",
|
||||
halSummary.blockdeletePinCrossesToOptionalStop ? "blockdelete -> optional stop" : "blockdelete direct",
|
||||
`ignore-limits ${state.gmoccapyGui.ignoreLimits ? "on" : "off"}`,
|
||||
`block ${state.gmoccapyGui.optionalBlocks ? "on" : "off"}`,
|
||||
`M1 ${state.gmoccapyGui.optionalStop ? "on" : "off"}`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatGmoccapyJogInputs(halSummary, state) {
|
||||
return [
|
||||
`${halSummary.jogAxisPinCount} axis jog pins`,
|
||||
`${halSummary.jogIncrementPinCount} jog-inc pins`,
|
||||
halSummary.jogAxisPinsPressRelease ? "press/release level" : "edge jog",
|
||||
halSummary.jogIncrementPinsRisingEdgeOnly ? "jog-inc rising-edge-only" : "jog-inc level",
|
||||
halSummary.jogIncrementZeroIsContinuous ? "jog-inc-0 continuous" : "jog-inc-0 incremental",
|
||||
halSummary.turtleJogLevelDriven ? "turtle level-driven" : "turtle pending",
|
||||
`active ${state.gmoccapyGui.activeJogPin || "none"}`,
|
||||
`increment ${state.gmoccapyGui.jogIncrementLabel}=${state.gmoccapyGui.jogIncrementOutput}`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatGmoccapyOverrideInputs(halSummary, state) {
|
||||
return [
|
||||
`${halSummary.overridePinTargetCount} override targets`,
|
||||
halSummary.countInputRequiresEnable ? "counts require enable" : "counts direct",
|
||||
halSummary.directValueRequiresAnalogEnable ? "direct-value requires analog-enable" : "direct-value direct",
|
||||
halSummary.resetPinsRisingEdgeOnly ? "reset rising-edge-only" : "reset level",
|
||||
`last ${formatGmoccapyHalLastInput(state)}`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatGmoccapyMessageInputs(halSummary, state) {
|
||||
return [
|
||||
`${halSummary.messageInputPinCount} message pins`,
|
||||
halSummary.deleteMessageRisingEdgeOnly ? "delete-message rising-edge-only" : "delete-message level",
|
||||
halSummary.warningConfirmLevelPolled ? "warning-confirm level-polled" : "warning-confirm edge",
|
||||
`error ${state.gmoccapyGui.error ? "on" : "off"}`,
|
||||
`deleted ${state.gmoccapyGui.deletedMessageCount}`,
|
||||
`confirm ${state.gmoccapyGui.warningConfirm ? "on" : "off"}`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatGmoccapyHalLastInput(state) {
|
||||
if (!state.gmoccapyGui?.lastHalPin && !state.gmoccapyGui?.lastHalPinEffect) return "none";
|
||||
return [
|
||||
state.gmoccapyGui.lastHalPin || "web-control",
|
||||
state.gmoccapyGui.lastHalPinEffect || "-",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function formatGmoccapyHardButtons(halSummary) {
|
||||
return [
|
||||
`${halSummary.hardwareButtonVerticalCount} right v-button pins`,
|
||||
`${halSummary.hardwareButtonBottomMainCount} bottom h-button main refs`,
|
||||
`${halSummary.mappedHardwareButtonCount} mapped Web buttons`,
|
||||
halSummary.hardwareButtonEdge,
|
||||
`insensitive ${halSummary.hardwareButtonInsensitiveTarget}`,
|
||||
].join(" / ");
|
||||
}
|
||||
|
||||
function formatNumber(value, digits = 3) {
|
||||
return Number(value).toFixed(digits);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user