完善 working6 gmoccapy 页面诊断边界

This commit is contained in:
2026-06-27 00:01:01 -04:00
parent f964cedf66
commit 0e1e9d61a8
22 changed files with 2336 additions and 18 deletions

View File

@@ -302,6 +302,7 @@ export const gmoccapyXyzabProfile = {
...gmoccapyXyzabJogIncrementPins,
"gmoccapy.jog.jog-increment",
"gmoccapy.jog.turtle-jog",
"gmoccapy.unlock-settings",
"gmoccapy.feed.feed-override.counts",
"gmoccapy.feed.feed-override.count-enable",
"gmoccapy.feed.feed-override.analog-enable",
@@ -326,8 +327,15 @@ export const gmoccapyXyzabProfile = {
"gmoccapy.blockdelete",
"gmoccapy.tooloffset-x",
"gmoccapy.tooloffset-z",
"gmoccapy.probeheight",
"gmoccapy.blockheight",
"gmoccapy.toolmeasurement",
"gmoccapy.searchvel",
"gmoccapy.probevel",
"gmoccapy.toolchange-change",
"gmoccapy.toolchange-changed",
"gmoccapy.toolchange-number",
"gmoccapy.toolchange-confirm",
"gmoccapy.program.length",
"gmoccapy.program.current-line",
"gmoccapy.program.progress",
@@ -341,6 +349,21 @@ export const gmoccapyXyzabProfile = {
toolOffsetX: 0,
toolOffsetZ: 0,
},
toolsensor: {
configured: false,
useToolMeasurementPref: false,
blockHeightPref: 0,
probeHeightPref: null,
searchVelocityPref: null,
probeVelocityPref: null,
reason: "gmoccapy_XYZAB.ini has no [TOOLSENSOR] section; gmoccapy disables automatic tool measurement controls by default",
},
userMessages: {
configured: false,
count: 0,
pins: [],
reason: "gmoccapy_XYZAB.ini has no [DISPLAY] MESSAGE_* entries, so gmoccapy does not create dynamic messages.* HAL pins",
},
sourceReferences: sourceReferenceObjects.map((reference) => reference.path),
sourceReferenceObjects,
panelSchema: gmoccapyXyzabPanelSchema,
@@ -373,6 +396,39 @@ export const gmoccapyXyzabProfile = {
"increment xinc yinc",
"go_to_position X-pos Y-pos Z-pos",
],
nativePages: {
filePage: {
configured: true,
programPrefix: "../../nc_files/",
nativeWidget: "IconFileSelection1",
webImplementation: "partial",
boundary: "native GtkIconFileSelection is represented by Web file input and staged G-code source selector",
},
macroPage: {
configured: true,
count: 5,
subroutinePath: "./macros",
webImplementation: "partial",
macros: [
{ name: "i_am_lost", args: [], command: "O<i_am_lost> call" },
{ name: "halo_world", args: [], command: "O<halo_world> call" },
{ name: "jog_around", args: [], command: "O<jog_around> call" },
{ name: "increment", args: ["xinc", "yinc"], command: "O<increment> call [xinc] [yinc]" },
{ name: "go_to_position", args: ["X-pos", "Y-pos", "Z-pos"], command: "O<go_to_position> call [X-pos] [Y-pos] [Z-pos]" },
],
boundary: "macros enter LinuxCNC through MDI O-word calls and use the same MDI gate as RUN_MDI",
},
toolEditor: {
configured: true,
toolTablePath: "linuxcnc/configs/sim/gmoccapy/tool.tbl",
toolCount: 17,
visibleAxes: ["X", "Y", "Z", "A", "B"],
webImplementation: "diagnostic-only",
writebackEnabled: false,
boundary: "Web shows tool table and tool-change diagnostics but does not write tool.tbl",
},
implementationMatrixBoundary: "native GTK pages are classified as implemented, partial, diagnostic-only, or native-only",
},
samplePrograms: [
"linuxcnc/nc_files/3D_Chips.ngc",
],

View File

@@ -28,13 +28,33 @@ export const gmoccapyCommunicationModel = {
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.",
notes: "jog pins, jog increment pins, settings unlock, ignore-limits, optional-stop/blockdelete, override counts/direct-value, reset and message pins enter gmoccapy through HAL callbacks; tool measurement pins are HAL_OUT diagnostics in XYZAB.",
},
remap: {
label: "Interpreter REMAP",
path: ["milltask", "RS274NGC", "Python remap prolog/ngc/epilog"],
notes: "M6/M61 remap executes on the task/interpreter side, not inside the GUI event loop.",
},
filePage: {
label: "Native file page",
path: ["btn_load", "_show_iconview_tab(True)", "IconFileSelection1", "hal_action_open.load_file(path)", "GStat file-loaded"],
notes: "gmoccapy uses Gtk/IconFileSelection and PROGRAM_PREFIX; the Web shell maps this to OPEN_FILE plus staged LinuxCNC G-code source diagnostics.",
},
macroPage: {
label: "Native macro buttons",
path: ["[MACROS]", "_make_macro_button", "_on_btn_macro_pressed", "command.mdi(O<name> call [args])", "interp idle/run sensitivity"],
notes: "The XYZAB config defines five macros; Web macro dispatch must use MDI gates instead of bypassing task state.",
},
toolEditorPage: {
label: "Native tool editor",
path: ["btn_tool", "_show_tooledit_tab(True)", "tooledit1.reload()", "tool.tbl", "M61 Q? or T? M6"],
notes: "Native gmoccapy can edit and save tool.tbl; Web keeps writeback diagnostic-only while preserving iocontrol-loopback tool-change semantics.",
},
nativePages: {
label: "Native GTK page matrix",
path: ["gmoccapy.glade notebooks", "tbtn_setup/user tabs/tool/touch/load-file", "button callback", "Web implementation matrix", "operator diagnostic"],
notes: "Unimplemented native GTK pages are reported as diagnostic-only or native-only and must not be presented as completed Web actions.",
},
},
webPath: {
label: "Web simulation",
@@ -172,6 +192,13 @@ export const gmoccapyCommunicationModel = {
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-settings-unlock",
webAction: "GMOCAPY_HAL_PIN unlock-settings",
nativeCommand: "_on_unlock_settings_changed",
nativeStatus: "tbtn_setup sensitivity when rbt_hal_unlock is active",
gate: "level-driven pin only gates setup access when unlock_way/rbt_hal_unlock selects HAL unlock",
},
{
actionId: "hal-jog-pin",
webAction: "GMOCAPY_HAL_PIN jog.axis/jog-inc",
@@ -179,6 +206,20 @@ export const gmoccapyCommunicationModel = {
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-tool-measurement",
webAction: "GMOCAPY_HAL_PIN tool measurement diagnostics",
nativeCommand: "_check_toolmeasurement/on_chk_use_tool_measurement_toggled/on_btn_block_height_clicked",
nativeStatus: "probeheight, blockheight, toolmeasurement, searchvel and probevel HAL_OUT pins",
gate: "XYZAB has no [TOOLSENSOR], so auto tool measurement controls are disabled and Web records these pins as diagnostics",
},
{
actionId: "hal-user-message",
webAction: "GMOCAPY_HAL_PIN messages.* diagnostics",
nativeCommand: "_init_user_messages/_show_user_message",
nativeStatus: "dynamic messages.<pinname>, waiting and response pins when MESSAGE_* entries exist",
gate: "gmoccapy_XYZAB.ini defines no MESSAGE_* entries, so no dynamic user message pins are created",
},
{
actionId: "hal-warning-confirm",
webAction: "GMOCAPY_HAL_PIN warning-confirm",
@@ -186,6 +227,34 @@ export const gmoccapyCommunicationModel = {
nativeStatus: "active warning dialog response",
gate: "level-polled while warning dialog is active",
},
{
actionId: "file-page",
webAction: "LOAD_PROGRAM / LOAD_LINUXCNC_GCODE_SOURCE / GMOCAPY_PAGE_ACTION file-load",
nativeCommand: "on_btn_load_clicked -> IconFileSelection1 -> hal_action_open.load_file",
nativeStatus: "file-loaded signal updates gmoccapy.program.length/current-line/progress",
gate: "blocked while interpreter is running; native GTK file chooser is represented as Web file/staged-source diagnostics",
},
{
actionId: "macro-page",
webAction: "GMOCAPY_RUN_MACRO",
nativeCommand: "_on_btn_macro_pressed -> command.mdi(O<macro> call [args])",
nativeStatus: "macro buttons disabled during interpreter run and re-enabled at interp idle",
gate: "same as RUN_MDI: machine on, MDI-capable, homed when NO_FORCE_HOMING=0, interpreter idle",
},
{
actionId: "tool-editor-page",
webAction: "GMOCAPY_TOOL_EDITOR_ACTION",
nativeCommand: "_init_tooleditor/_show_tooledit_tab/on_btn_selected_tool_clicked",
nativeStatus: "tool.tbl rows, selected tool, active tool, tool offsets and M6/M61 commands",
gate: "Web writeback is disabled; tool-change actions remain diagnostic and preserve iocontrol-loopback",
},
{
actionId: "native-page-diagnostic",
webAction: "GMOCAPY_NATIVE_PAGE",
nativeCommand: "tbtn_setup/tbtn_user_tabs/btn_touch/btn_tool/tbtn_switch_mode native page callbacks",
nativeStatus: "implementation matrix and operator diagnostic message",
gate: "diagnostic-only pages do not emit fake GTK page transitions or native file/tool writes",
},
],
};
@@ -197,6 +266,8 @@ export function createGmoccapyCommunicationSummary(model = gmoccapyCommunication
nativePathCount: Object.keys(model.nativePaths).length,
startupStageCount: model.startupStages.length,
actionCount: model.actionMappings.length,
pagePathCount: ["filePage", "macroPage", "toolEditorPage", "nativePages"]
.filter((key) => Boolean(model.nativePaths[key])).length,
postguiAfterHalcompReady: model.startupStages.findIndex((stage) => stage.id === "postgui") >
model.startupStages.findIndex((stage) => stage.id === "gmoccapy"),
hasNativeNmlCommandPath: model.nativePaths.command.path.includes("NML emcCommand"),

View File

@@ -115,17 +115,31 @@ export const gmoccapyHalModel = {
webSource: "gmoccapyGui ignoreLimits, optionalBlocks, optionalStop",
semanticBoundary: "native_ignore_limits_optional_stop_blockdelete_pin_reference_web_state_mapping",
},
{
group: "settings",
pins: [
"gmoccapy.unlock-settings",
],
webSource: "gmoccapyGui settingsUnlockPin and setupSensitive",
semanticBoundary: "native_settings_unlock_pin_reference_web_diagnostic_state",
},
{
group: "tool",
pins: [
"gmoccapy.tooloffset-x",
"gmoccapy.tooloffset-z",
"gmoccapy.tool-diameter",
"gmoccapy.probeheight",
"gmoccapy.blockheight",
"gmoccapy.toolmeasurement",
"gmoccapy.searchvel",
"gmoccapy.probevel",
"gmoccapy.toolchange-change",
"gmoccapy.toolchange-changed",
"gmoccapy.toolchange-number",
"gmoccapy.toolchange-confirm",
],
webSource: "toolPreview and disconnected manual tool-change diagnostics",
webSource: "toolPreview, disconnected manual tool-change diagnostics, and disabled XYZAB tool measurement outputs",
semanticBoundary: "native_tool_pin_reference_simulated_tool_loop",
},
{
@@ -347,16 +361,17 @@ export const gmoccapyHalModel = {
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",
webAction: "GMOCAPY_NATIVE_PAGE user-tabs",
webDispatch: { type: "GMOCAPY_NATIVE_PAGE", pageId: "user-tabs", nativeWidget: "tbtn_user_tabs" },
webSelector: null,
gate: "native user tabs page is diagnostic-only in the Web shell",
},
{
index: 6,
pin: "gmoccapy.v-button.button-6",
nativeWidget: "tbtn_setup",
webAction: "UI_CONTROL",
webAction: "GMOCAPY_NATIVE_PAGE setup",
webDispatch: { type: "GMOCAPY_NATIVE_PAGE", pageId: "setup", nativeWidget: "tbtn_setup" },
webSelector: null,
gate: "native setup page is diagnostic-only in the Web shell",
},
@@ -375,7 +390,8 @@ export const gmoccapyHalModel = {
index: 1,
pin: "gmoccapy.h-button.button-1",
nativeWidget: "btn_touch",
webAction: "UI_CONTROL",
webAction: "GMOCAPY_NATIVE_PAGE touch-off",
webDispatch: { type: "GMOCAPY_NATIVE_PAGE", pageId: "touch-off", nativeWidget: "btn_touch" },
webSelector: null,
gate: "touch-off page not implemented in Web shell",
},
@@ -383,7 +399,8 @@ export const gmoccapyHalModel = {
index: 3,
pin: "gmoccapy.h-button.button-3",
nativeWidget: "btn_tool",
webAction: "UI_CONTROL",
webAction: "GMOCAPY_NATIVE_PAGE tool-editor",
webDispatch: { type: "GMOCAPY_NATIVE_PAGE", pageId: "tool-editor", nativeWidget: "btn_tool" },
webSelector: null,
gate: "tool editor page not implemented in Web shell",
},
@@ -391,7 +408,8 @@ export const gmoccapyHalModel = {
index: 6,
pin: "gmoccapy.h-button.button-6",
nativeWidget: "tbtn_switch_mode",
webAction: "SET_KINS_TYPE identity",
webAction: "GMOCAPY_NATIVE_PAGE switchkins-mode",
webDispatch: { type: "GMOCAPY_NATIVE_PAGE", pageId: "switchkins-mode", nativeWidget: "tbtn_switch_mode" },
webSelector: null,
gate: "native joint/world switch page position is diagnostic-only in the Web shell",
},
@@ -411,6 +429,13 @@ export const gmoccapyHalModel = {
"_on_analog_value_changed",
"_reset_override",
"_del_message_changed",
"_on_unlock_settings_changed",
"_check_toolmeasurement",
"on_chk_use_tool_measurement_toggled",
"on_btn_block_height_clicked",
"_on_blockheight_value_changed",
"_init_user_messages",
"_show_user_message",
],
prefPath: "linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.pref",
},
@@ -461,6 +486,16 @@ export const gmoccapyHalModel = {
webState: "gmoccapyGui.optionalStop",
},
],
settingsPins: [
{
pin: "gmoccapy.unlock-settings",
nativeCallback: "_on_unlock_settings_changed",
nativeCommand: "rbt_hal_unlock active gates tbtn_setup sensitivity from pin level",
prefDefault: "unlock_way=use",
webDispatch: { type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.unlock-settings" },
webState: "gmoccapyGui.settingsUnlockPin",
},
],
overridePins: [
{
target: "feed",
@@ -527,9 +562,64 @@ export const gmoccapyHalModel = {
webState: "gmoccapyGui.error",
},
],
toolMeasurementPins: {
toolsensorConfigured: false,
useToolMeasurementPref: false,
prefDefaults: {
useToolMeasurement: "use_toolmeasurement=False",
blockHeight: "blockheight=0.0",
},
disabledReason: "gmoccapy_XYZAB.ini has no [TOOLSENSOR] data, so _check_toolmeasurement disables the controls",
pins: [
{
pin: "gmoccapy.probeheight",
direction: "HAL_OUT",
nativeCallback: "on_spbtn_probe_height_value_changed / on_chk_use_tool_measurement_toggled / on_btn_block_height_clicked",
webState: "gmoccapyGui.probeHeight",
},
{
pin: "gmoccapy.blockheight",
direction: "HAL_OUT",
nativeCallback: "on_btn_block_height_clicked / _on_blockheight_value_changed",
webState: "gmoccapyGui.blockHeight",
},
{
pin: "gmoccapy.toolmeasurement",
direction: "HAL_OUT",
nativeCallback: "on_chk_use_tool_measurement_toggled",
webState: "gmoccapyGui.toolMeasurement",
},
{
pin: "gmoccapy.searchvel",
direction: "HAL_OUT",
nativeCallback: "on_spbtn_search_vel_value_changed / on_chk_use_tool_measurement_toggled",
webState: "gmoccapyGui.searchVelocity",
},
{
pin: "gmoccapy.probevel",
direction: "HAL_OUT",
nativeCallback: "on_spbtn_probe_vel_value_changed / on_chk_use_tool_measurement_toggled",
webState: "gmoccapyGui.probeVelocity",
},
],
},
userMessages: {
configured: false,
configuredMessageCount: 0,
pins: [],
source: "get_ini_info.get_user_messages()",
disabledReason: "gmoccapy_XYZAB.ini has no MESSAGE_* entries, so _init_user_messages() returns without creating messages.* pins",
dynamicPinPatterns: [
"gmoccapy.messages.<pinname>",
"gmoccapy.messages.<pinname>-waiting",
"gmoccapy.messages.<pinname>-response",
],
supportedTypes: ["status", "okdialog", "yesnodialog"],
},
behavior: {
optionalStopPinCrossesToBlockDelete: true,
blockdeletePinCrossesToOptionalStop: true,
unlockSettingsLevelDriven: true,
jogAxisPinsPressRelease: true,
jogPinsUseTaskPolicyGate: true,
jogIncrementPinsRisingEdgeOnly: true,
@@ -540,10 +630,124 @@ export const gmoccapyHalModel = {
resetPinsRisingEdgeOnly: true,
deleteMessageRisingEdgeOnly: true,
warningConfirmLevelPolled: true,
toolMeasurementPinsAreHalOut: true,
toolMeasurementDisabledWithoutToolsensor: true,
userMessagesDynamicIniOnly: true,
directValueRange: "0..1",
semanticBoundary: "native_gmoccapy_hal_pin_callbacks_to_web_store_actions",
},
},
nativePages: {
source: {
path: "linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py",
gladePath: "linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.glade",
methods: [
"_init_IconFileSelection",
"_init_file_to_load",
"on_btn_load_clicked",
"on_IconFileSelection1_selected",
"on_hal_status_file_loaded",
"_make_macro_button",
"_on_btn_macro_pressed",
"_init_tooleditor",
"_show_tooledit_tab",
"on_btn_tool_clicked",
"on_btn_selected_tool_clicked",
"on_btn_select_tool_by_no_clicked",
"on_tbtn_setup_toggled",
"on_tbtn_user_tabs_toggled",
"on_btn_touch_clicked",
],
},
filePage: {
pageId: "file-load",
nativeNotebookPage: "_BB_LOAD_FILE / ntb_preview page 3",
nativeWidget: "IconFileSelection1",
programPrefix: "../../nc_files/",
defaultOpenPreference: "open_file",
fileExtensions: [
"*.ngc",
"*.png",
"*.gif",
"*.jpg",
"*.py",
],
nativeCallbacks: [
"on_btn_load_clicked -> _show_iconview_tab(True)",
"on_IconFileSelection1_selected -> hal_action_open.load_file(path)",
"on_hal_status_file_loaded -> gmoccapy.program.length",
"on_hal_status_line_changed -> gmoccapy.program.current-line/progress",
],
webSurface: "OPEN_FILE input, staged LinuxCNC G-code source selector and program preview",
webAction: "GMOCAPY_PAGE_ACTION file-load diagnostics",
implementation: "partial",
boundary: "native_GtkIconFileSelection_not_embedded_in_browser",
},
macroPage: {
pageId: "mdi-macros",
nativeContainer: "hbtb_MDI",
iniSection: "[MACROS]",
macroLimit: 14,
macros: [
{ name: "i_am_lost", args: [], file: "macros/i_am_lost.ngc", commandTemplate: "O<i_am_lost> call" },
{ name: "halo_world", args: [], file: "macros/halo_world.ngc", commandTemplate: "O<halo_world> call" },
{ name: "jog_around", args: [], file: "macros/jog_around.ngc", commandTemplate: "O<jog_around> call" },
{ name: "increment", args: ["xinc", "yinc"], file: "macros/increment.ngc", commandTemplate: "O<increment> call [xinc] [yinc]" },
{ name: "go_to_position", args: ["X-pos", "Y-pos", "Z-pos"], file: "macros/go_to_position.ngc", commandTemplate: "O<go_to_position> call [X-pos] [Y-pos] [Z-pos]" },
],
nativeCallbacks: [
"_make_macro_button -> get_ini_info.get_macros()",
"_on_btn_macro_pressed -> command.mdi(O<name> call [args])",
"on_hal_status_interp_idle enables macro buttons",
"on_hal_status_interp_run disables macro buttons",
],
webAction: "GMOCAPY_RUN_MACRO",
implementation: "partial",
boundary: "macro_buttons_map_to_MDI_command_without_native_entry_dialogs",
},
toolEditorPage: {
pageId: "tool-editor",
nativeNotebookPage: "ntb_preview page 2 / _BB_TOOL",
nativeWidget: "tooledit1",
toolTablePath: "linuxcnc/configs/sim/gmoccapy/tool.tbl",
visibleAxes: ["X", "Y", "Z", "A", "B"],
toolCount: 17,
nativeCallbacks: [
"_init_tooleditor -> tooledit1.set_filename(tool.tbl)",
"_show_tooledit_tab(True) -> tooledit1.reload()",
"on_btn_selected_tool_clicked -> M61 Q? or T? M6",
"on_btn_save_tool_changes_clicked -> tooledit1.save(None)",
"on_btn_reload_tooltable_clicked -> tooledit1.reload(None)",
],
webAction: "GMOCAPY_TOOL_EDITOR_ACTION diagnostics",
editableInWeb: false,
implementation: "diagnostic-only",
boundary: "tool_table_writeback_not_promoted_browser_keeps_iocontrol_loopback",
},
implementationMatrix: [
{ pageId: "main", nativeWidget: "ntb_main page 0", implementation: "implemented", webSurface: "DRO/G-code/preview/main controls", hardButton: null },
{ pageId: "manual-home", nativeWidget: "btn_homing / _BB_HOME", implementation: "partial", webSurface: "HOME action", hardButton: "gmoccapy.h-button.button-0" },
{ pageId: "mdi-macros", nativeWidget: "hbtb_MDI macro buttons", implementation: "partial", webSurface: "MDI macro diagnostics and dispatch", hardButton: null },
{ pageId: "auto-run", nativeWidget: "_BB_AUTO buttons", implementation: "partial", webSurface: "RUN/STOP/PAUSE/STEP controls", hardButton: null },
{ pageId: "file-load", nativeWidget: "IconFileSelection1 / _BB_LOAD_FILE", implementation: "partial", webSurface: "OPEN_FILE input and staged source selector", hardButton: null },
{ pageId: "tool-editor", nativeWidget: "btn_tool / tooledit1", implementation: "diagnostic-only", webSurface: "tool table summary and action diagnostics", hardButton: "gmoccapy.h-button.button-3" },
{ pageId: "touch-off", nativeWidget: "btn_touch / offsetpage1", implementation: "diagnostic-only", webSurface: "touch-off boundary diagnostics", hardButton: "gmoccapy.h-button.button-1" },
{ pageId: "setup", nativeWidget: "tbtn_setup / ntb_setup", implementation: "diagnostic-only", webSurface: "settings/unlock diagnostics", hardButton: "gmoccapy.v-button.button-6" },
{ pageId: "user-tabs", nativeWidget: "tbtn_user_tabs / ntb_user_tabs", implementation: "diagnostic-only", webSurface: "user tab boundary diagnostics", hardButton: "gmoccapy.v-button.button-5" },
{ pageId: "switchkins-mode", nativeWidget: "tbtn_switch_mode", implementation: "diagnostic-only", webSurface: "identity-only XYZAB switchkins diagnostic", hardButton: "gmoccapy.h-button.button-6" },
{ pageId: "edit", nativeWidget: "_BB_EDIT / gcode_view editable", implementation: "native-only", webSurface: "not promoted", hardButton: null },
{ pageId: "offset-editor", nativeWidget: "offsetpage1 edit offsets", implementation: "native-only", webSurface: "not promoted", hardButton: null },
],
behavior: {
unimplementedPagesDiagnosticOnly: true,
hardButtonsDoNotFakeNativePages: true,
fileChooserNotNativeGtk: true,
macroButtonsUseMdiGate: true,
toolEditorWritebackDisabled: true,
toolEditorKeepsIocontrolLoopback: true,
semanticBoundary: "native_gmoccapy_page_matrix_web_diagnostic_boundary",
},
},
toolChange: {
strategy: "iocontrol-loopback",
manualGmoccapyPinsConnected: false,
@@ -619,12 +823,19 @@ export function createGmoccapyHalSummary(model = gmoccapyHalModel) {
hardwareButtonEdge: model.hardwareButtons.behavior.edge,
hardwareButtonInsensitiveTarget: model.hardwareButtons.behavior.insensitiveTarget,
operatorInputPinCount: model.halPinActions.operatorPins.length,
settingsInputPinCount: model.halPinActions.settingsPins.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,
toolMeasurementPinCount: model.halPinActions.toolMeasurementPins.pins.length,
toolMeasurementEnabled: model.halPinActions.toolMeasurementPins.useToolMeasurementPref,
toolsensorConfigured: model.halPinActions.toolMeasurementPins.toolsensorConfigured,
userMessagePinCount: model.halPinActions.userMessages.pins.length,
userMessagesConfigured: model.halPinActions.userMessages.configured,
optionalStopPinCrossesToBlockDelete: model.halPinActions.behavior.optionalStopPinCrossesToBlockDelete,
blockdeletePinCrossesToOptionalStop: model.halPinActions.behavior.blockdeletePinCrossesToOptionalStop,
unlockSettingsLevelDriven: model.halPinActions.behavior.unlockSettingsLevelDriven,
jogAxisPinsPressRelease: model.halPinActions.behavior.jogAxisPinsPressRelease,
jogPinsUseTaskPolicyGate: model.halPinActions.behavior.jogPinsUseTaskPolicyGate,
jogIncrementPinsRisingEdgeOnly: model.halPinActions.behavior.jogIncrementPinsRisingEdgeOnly,
@@ -635,6 +846,27 @@ export function createGmoccapyHalSummary(model = gmoccapyHalModel) {
resetPinsRisingEdgeOnly: model.halPinActions.behavior.resetPinsRisingEdgeOnly,
deleteMessageRisingEdgeOnly: model.halPinActions.behavior.deleteMessageRisingEdgeOnly,
warningConfirmLevelPolled: model.halPinActions.behavior.warningConfirmLevelPolled,
toolMeasurementPinsAreHalOut: model.halPinActions.behavior.toolMeasurementPinsAreHalOut,
toolMeasurementDisabledWithoutToolsensor: model.halPinActions.behavior.toolMeasurementDisabledWithoutToolsensor,
userMessagesDynamicIniOnly: model.halPinActions.behavior.userMessagesDynamicIniOnly,
nativePageCount: model.nativePages.implementationMatrix.length,
implementedNativePageCount: model.nativePages.implementationMatrix
.filter((page) => page.implementation === "implemented").length,
partialNativePageCount: model.nativePages.implementationMatrix
.filter((page) => page.implementation === "partial").length,
diagnosticOnlyNativePageCount: model.nativePages.implementationMatrix
.filter((page) => page.implementation === "diagnostic-only").length,
nativeOnlyPageCount: model.nativePages.implementationMatrix
.filter((page) => page.implementation === "native-only").length,
filePageImplementation: model.nativePages.filePage.implementation,
filePageBoundary: model.nativePages.filePage.boundary,
macroCount: model.nativePages.macroPage.macros.length,
macroLimit: model.nativePages.macroPage.macroLimit,
macroButtonsUseMdiGate: model.nativePages.behavior.macroButtonsUseMdiGate,
toolEditorImplementation: model.nativePages.toolEditorPage.implementation,
toolEditorWritebackDisabled: model.nativePages.behavior.toolEditorWritebackDisabled,
hardButtonsDoNotFakeNativePages: model.nativePages.behavior.hardButtonsDoNotFakeNativePages,
unimplementedPagesDiagnosticOnly: model.nativePages.behavior.unimplementedPagesDiagnosticOnly,
semanticBoundary: model.semanticBoundary,
};
}

View File

@@ -253,9 +253,31 @@ const initialState = {
jogIncrementOutput: 0,
turtleJog: false,
activeJogPin: null,
settingsUnlockMode: "use",
settingsUnlockPin: false,
setupSensitive: true,
toolsensorConfigured: false,
toolMeasurement: false,
probeHeight: 0,
blockHeight: 0,
searchVelocity: 0,
probeVelocity: 0,
userMessagesConfigured: false,
userMessagePins: [],
warningConfirm: false,
error: false,
deletedMessageCount: 0,
activeNativePage: "main",
nativePageMode: "implemented",
filePageStatus: "main",
filePageLastAction: null,
macroPageConfigured: true,
macroButtonsEnabled: true,
macroLastCommand: null,
macroLastName: null,
toolEditorStatus: "closed",
toolEditorWritebackEnabled: false,
toolEditorLastAction: null,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: null,
@@ -1022,6 +1044,34 @@ export function createSimulationStore(seed = {}) {
setState(result.patch);
}
break;
case "GMOCAPY_NATIVE_PAGE":
{
const patch = applyGmoccapyNativePagePatch(state, action, gmoccapyHalModel);
setState(patch);
}
break;
case "GMOCAPY_PAGE_ACTION":
{
const patch = applyGmoccapyPageActionPatch(state, action, gmoccapyHalModel);
setState(patch);
}
break;
case "GMOCAPY_RUN_MACRO":
{
const result = applyGmoccapyMacroPatch(state, action, gmoccapyHalModel);
if (!result.applied) {
setState({ operatorMessage: result.operatorMessage });
break;
}
setState(result.patch);
}
break;
case "GMOCAPY_TOOL_EDITOR_ACTION":
{
const patch = applyGmoccapyToolEditorPatch(state, action, gmoccapyHalModel);
setState(patch);
}
break;
case "TOGGLE_POWER":
{
const gate = gateLinuxCncTaskAction(state, action);
@@ -3494,6 +3544,36 @@ function applyGmoccapyHalPinPatch(state, action, model = gmoccapyHalModel) {
};
}
if (pin === "gmoccapy.unlock-settings") {
const enabled = Boolean(value);
const halUnlockActive = action.halUnlockMode === true || state.gmoccapyGui?.settingsUnlockMode === "hal";
if (!halUnlockActive) {
return {
applied: true,
patch: {
gmoccapyGui: halGuiPatch(state, pin, value, {
settingsUnlockPin: enabled,
setupSensitive: true,
lastHalPinEffect: "unlock-settings ignored because unlock_way is not hal",
}),
operatorMessage: "gmoccapy HAL unlock-settings ignored: unlock_way is use",
},
};
}
return {
applied: true,
patch: {
gmoccapyGui: halGuiPatch(state, pin, value, {
settingsUnlockMode: "hal",
settingsUnlockPin: enabled,
setupSensitive: enabled,
lastHalPinEffect: `unlock-settings ${enabled ? "enabled" : "disabled"} setup page sensitivity`,
}),
operatorMessage: `gmoccapy HAL unlock-settings ${enabled ? "enabled setup" : "disabled setup"}`,
},
};
}
const jogAxisPin = resolveGmoccapyJogAxisPin(pin, model);
if (jogAxisPin) {
return applyGmoccapyJogAxisHalPatch(state, pin, value, jogAxisPin);
@@ -3566,6 +3646,18 @@ function applyGmoccapyHalPinPatch(state, action, model = gmoccapyHalModel) {
};
}
const toolMeasurementPin = resolveGmoccapyToolMeasurementPin(pin, model);
if (toolMeasurementPin) {
return applyGmoccapyToolMeasurementHalPatch(state, pin, value, toolMeasurementPin);
}
if (pin.startsWith("gmoccapy.messages.")) {
return {
applied: false,
operatorMessage: "gmoccapy HAL user message pin unmapped: gmoccapy_XYZAB.ini defines no MESSAGE_* entries",
};
}
const overrideTarget = findOverrideTargetForPin(pin);
if (overrideTarget) {
return applyGmoccapyOverrideHalPatch(state, pin, value, overrideTarget);
@@ -3589,7 +3681,8 @@ function normalizeGmoccapyHalPin(pin) {
}
function isKnownGmoccapyHalPin(pin, model = gmoccapyHalModel) {
return model.nativePins.some((group) => group.pins.includes(pin));
if (model.nativePins.some((group) => group.pins.includes(pin))) return true;
return pin.startsWith("gmoccapy.messages.");
}
function halGuiPatch(state, pin, value, patch = {}) {
@@ -3601,6 +3694,149 @@ function halGuiPatch(state, pin, value, patch = {}) {
};
}
function applyGmoccapyNativePagePatch(state, action, model = gmoccapyHalModel) {
const pageId = String(action.pageId || action.page || "").trim();
const page = model.nativePages?.implementationMatrix?.find((entry) => entry.pageId === pageId);
if (!page) {
return {
operatorMessage: `gmoccapy native page unmapped: ${pageId || "unknown"}`,
};
}
return {
gmoccapyGui: {
...state.gmoccapyGui,
activeNativePage: page.pageId,
nativePageMode: page.implementation,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: `${page.nativeWidget} ${page.implementation}`,
},
operatorMessage: page.implementation === "diagnostic-only" || page.implementation === "native-only"
? `gmoccapy native page diagnostic-only: ${page.pageId}`
: `gmoccapy native page ${page.pageId}`,
};
}
function applyGmoccapyPageActionPatch(state, action, model = gmoccapyHalModel) {
const pageId = String(action.pageId || action.page || "").trim();
const actionId = String(action.actionId || action.action || "open").trim();
if (pageId === "file-load") {
const running = state.machine?.interpState === "reading" || state.runState === "running";
const status = running ? "blocked-running" : actionId;
return {
gmoccapyGui: {
...state.gmoccapyGui,
activeNativePage: "file-load",
nativePageMode: model.nativePages.filePage.implementation,
filePageStatus: status,
filePageLastAction: actionId,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: running
? "file load blocked while interpreter is running"
: "IconFileSelection native page represented by Web file/staged-source controls",
},
operatorMessage: running
? "gmoccapy file page blocked: interpreter running"
: `gmoccapy file page ${actionId}: native Gtk chooser diagnostic`,
};
}
return {
operatorMessage: `gmoccapy page action unmapped: ${pageId || "unknown"}`,
};
}
function applyGmoccapyMacroPatch(state, action, model = gmoccapyHalModel) {
const macroName = String(action.name || action.macro || "").trim();
const macro = model.nativePages?.macroPage?.macros?.find((entry) => entry.name === macroName);
if (!macro) {
return {
applied: false,
operatorMessage: `gmoccapy macro unmapped: ${macroName || "unknown"}`,
};
}
const gate = gateLinuxCncTaskAction(state, { type: "RUN_MDI" });
if (!gate.allowed) {
return {
applied: true,
patch: {
gmoccapyGui: {
...state.gmoccapyGui,
macroButtonsEnabled: false,
macroLastName: macro.name,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: `macro ${macro.name} blocked: ${gate.operatorMessage}`,
},
operatorMessage: `gmoccapy macro blocked: ${gate.operatorMessage}`,
},
};
}
const command = buildGmoccapyMacroCommand(macro, action.args || action.parameters || {});
const mdiResult = executeMdiCommand(state, command);
return {
applied: true,
patch: {
...mdiResult.patch,
gmoccapyGui: {
...state.gmoccapyGui,
activeNativePage: "mdi-macros",
nativePageMode: "partial",
macroButtonsEnabled: false,
macroLastName: macro.name,
macroLastCommand: command,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: `macro ${macro.name} dispatched as MDI O-word call`,
},
operatorMessage: `gmoccapy macro MDI ${command}`,
},
};
}
function buildGmoccapyMacroCommand(macro, args = {}) {
const values = Array.isArray(args)
? args
: macro.args.map((name) => args[name] ?? args[String(name).toLowerCase()] ?? 0);
return [
`O<${macro.name}> call`,
...macro.args.map((name, index) => `[${values[index] ?? 0}]`),
].join(" ");
}
function applyGmoccapyToolEditorPatch(state, action, model = gmoccapyHalModel) {
const actionId = String(action.actionId || action.action || "open").trim();
const writableAction = ["save", "add", "delete", "touch-off"].includes(actionId);
if (writableAction && model.nativePages?.toolEditorPage?.editableInWeb === false) {
return {
gmoccapyGui: {
...state.gmoccapyGui,
activeNativePage: "tool-editor",
nativePageMode: "diagnostic-only",
toolEditorStatus: "writeback-blocked",
toolEditorLastAction: actionId,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: "tool editor writeback disabled in browser",
},
operatorMessage: `gmoccapy tool editor diagnostic-only: ${actionId} does not write tool.tbl`,
};
}
return {
gmoccapyGui: {
...state.gmoccapyGui,
activeNativePage: "tool-editor",
nativePageMode: "diagnostic-only",
toolEditorStatus: actionId,
toolEditorLastAction: actionId,
lastHalPin: null,
lastHalPinValue: null,
lastHalPinEffect: "tooledit1 native page represented by diagnostics",
},
operatorMessage: `gmoccapy tool editor ${actionId}: ${state.profile?.toolTable?.toolCount || 0} tools diagnostic-only`,
};
}
function findOverrideTargetForPin(pin) {
return Object.entries(GMOCAPY_OVERRIDE_TARGETS)
.find(([, target]) => [
@@ -3612,6 +3848,10 @@ function findOverrideTargetForPin(pin) {
].includes(pin)) || null;
}
function resolveGmoccapyToolMeasurementPin(pin, model = gmoccapyHalModel) {
return model.halPinActions?.toolMeasurementPins?.pins?.find((entry) => entry.pin === 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 };
@@ -3724,6 +3964,27 @@ function applyGmoccapyJogIncrementHalPatch(state, pin, value, jogIncrement) {
};
}
function applyGmoccapyToolMeasurementHalPatch(state, pin, value, toolMeasurementPin) {
const nextValue = toolMeasurementPin.pin === "gmoccapy.toolmeasurement"
? Boolean(value)
: Number(value || 0);
const patch = {
lastHalPinEffect: "tool measurement HAL OUT pin recorded as diagnostic-only in Web",
};
if (toolMeasurementPin.pin === "gmoccapy.probeheight") patch.probeHeight = nextValue;
if (toolMeasurementPin.pin === "gmoccapy.blockheight") patch.blockHeight = nextValue;
if (toolMeasurementPin.pin === "gmoccapy.toolmeasurement") patch.toolMeasurement = nextValue;
if (toolMeasurementPin.pin === "gmoccapy.searchvel") patch.searchVelocity = nextValue;
if (toolMeasurementPin.pin === "gmoccapy.probevel") patch.probeVelocity = nextValue;
return {
applied: true,
patch: {
gmoccapyGui: halGuiPatch(state, pin, value, patch),
operatorMessage: "gmoccapy HAL tool measurement output recorded; XYZAB has no [TOOLSENSOR]",
},
};
}
function applyGmoccapyOverrideHalPatch(state, pin, value, [targetName, target]) {
if (pin === target.countEnablePin || pin === target.analogEnablePin) {
const enabled = Boolean(value);

View File

@@ -443,6 +443,10 @@ function renderInfoTabs(element, state) {
<dt>gmoccapy comms:</dt><dd data-gmoccapy-comm="boundary">${gmoccapyCommunicationModel.semanticBoundary}</dd>
<dt>NML command:</dt><dd data-gmoccapy-comm="native-command">${formatGmoccapyNativePath("command")}</dd>
<dt>NML status:</dt><dd data-gmoccapy-comm="native-status">${formatGmoccapyNativePath("status")}</dd>
<dt>Native file page:</dt><dd data-gmoccapy-page="file">${formatGmoccapyFilePage(halSummary, state)}</dd>
<dt>Native macros:</dt><dd data-gmoccapy-page="macros">${formatGmoccapyMacroPage(halSummary, state)}</dd>
<dt>Native tool editor:</dt><dd data-gmoccapy-page="tool-editor">${formatGmoccapyToolEditorPage(halSummary, state)}</dd>
<dt>Native page matrix:</dt><dd data-gmoccapy-page="matrix">${formatGmoccapyNativePageMatrix(halSummary, state)}</dd>
<dt>Web runtime:</dt><dd data-gmoccapy-comm="web-path">${gmoccapyCommunicationModel.webPath.path.join(" -> ")}</dd>
<dt>postgui order:</dt><dd data-gmoccapy-comm="postgui">${commSummary.postguiAfterHalcompReady ? "halcomp.ready before POSTGUI_HALFILE" : "pending"}</dd>
<dt>native RUN:</dt><dd data-gmoccapy-comm="auto-run">${formatGmoccapyActionMapping("auto-run")}</dd>
@@ -452,8 +456,10 @@ function renderInfoTabs(element, state) {
<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 settings:</dt><dd data-gmoccapy-hal="settings-inputs">${formatGmoccapySettingsInputs(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>HAL tool/user:</dt><dd data-gmoccapy-hal="tool-user-inputs">${formatGmoccapyToolUserInputs(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>
@@ -922,6 +928,16 @@ function formatGmoccapyHalInputs(halSummary, state) {
].join(" / ");
}
function formatGmoccapySettingsInputs(halSummary, state) {
return [
`${halSummary.settingsInputPinCount} settings pins`,
halSummary.unlockSettingsLevelDriven ? "unlock-settings level-driven" : "unlock-settings pending",
`unlock-way ${state.gmoccapyGui.settingsUnlockMode}`,
`pin ${state.gmoccapyGui.settingsUnlockPin ? "high" : "low"}`,
`setup ${state.gmoccapyGui.setupSensitive ? "sensitive" : "locked"}`,
].join(" / ");
}
function formatGmoccapyJogInputs(halSummary, state) {
return [
`${halSummary.jogAxisPinCount} axis jog pins`,
@@ -945,6 +961,19 @@ function formatGmoccapyOverrideInputs(halSummary, state) {
].join(" / ");
}
function formatGmoccapyToolUserInputs(halSummary, state) {
return [
`${halSummary.toolMeasurementPinCount} tool measurement HAL_OUT pins`,
halSummary.toolMeasurementPinsAreHalOut ? "tool pins HAL_OUT" : "tool pins input",
halSummary.toolMeasurementDisabledWithoutToolsensor ? "XYZAB no TOOLSENSOR" : "TOOLSENSOR configured",
`toolmeasurement ${state.gmoccapyGui.toolMeasurement ? "on" : "off"}`,
`blockheight ${state.gmoccapyGui.blockHeight}`,
`${halSummary.userMessagePinCount} user message pins`,
halSummary.userMessagesDynamicIniOnly ? "messages dynamic INI-only" : "messages static",
halSummary.userMessagesConfigured ? "MESSAGE_* configured" : "no MESSAGE_*",
].join(" / ");
}
function formatGmoccapyMessageInputs(halSummary, state) {
return [
`${halSummary.messageInputPinCount} message pins`,
@@ -956,6 +985,50 @@ function formatGmoccapyMessageInputs(halSummary, state) {
].join(" / ");
}
function formatGmoccapyFilePage(halSummary, state) {
return [
`file page ${halSummary.filePageImplementation}`,
"PROGRAM_PREFIX ../../nc_files/",
"native IconFileSelection",
"Web OPEN_FILE/staged-source",
halSummary.filePageBoundary,
`status ${state.gmoccapyGui.filePageStatus}`,
].join(" / ");
}
function formatGmoccapyMacroPage(halSummary, state) {
return [
`${halSummary.macroCount} XYZAB macros`,
`macro limit ${halSummary.macroLimit}`,
halSummary.macroButtonsUseMdiGate ? "MDI gate" : "no MDI gate",
"O-word call",
`last ${state.gmoccapyGui.macroLastCommand || "none"}`,
].join(" / ");
}
function formatGmoccapyToolEditorPage(halSummary, state) {
return [
`tool editor ${halSummary.toolEditorImplementation}`,
`${state.profile.toolTable?.toolCount || 0} tools`,
"tool.tbl",
halSummary.toolEditorWritebackDisabled ? "writeback disabled" : "writeback enabled",
"iocontrol-loopback preserved",
`status ${state.gmoccapyGui.toolEditorStatus}`,
].join(" / ");
}
function formatGmoccapyNativePageMatrix(halSummary, state) {
return [
`${halSummary.nativePageCount} native pages`,
`${halSummary.implementedNativePageCount} implemented`,
`${halSummary.partialNativePageCount} partial`,
`${halSummary.diagnosticOnlyNativePageCount} diagnostic-only`,
`${halSummary.nativeOnlyPageCount} native-only`,
halSummary.hardButtonsDoNotFakeNativePages ? "hard-buttons diagnostic-only" : "hard-buttons may dispatch",
`active ${state.gmoccapyGui.activeNativePage}`,
].join(" / ");
}
function formatGmoccapyHalLastInput(state) {
if (!state.gmoccapyGui?.lastHalPin && !state.gmoccapyGui?.lastHalPinEffect) return "none";
return [
@@ -971,6 +1044,7 @@ function formatGmoccapyHardButtons(halSummary) {
`${halSummary.mappedHardwareButtonCount} mapped Web buttons`,
halSummary.hardwareButtonEdge,
`insensitive ${halSummary.hardwareButtonInsensitiveTarget}`,
`${halSummary.diagnosticOnlyNativePageCount} diagnostic-only native pages`,
].join(" / ");
}

View File

@@ -5747,3 +5747,486 @@ Outcome:
- `917cec1 完善 working5 gmoccapy HAL 输入对标` 已同步到云仓库。
- `ea50bf7 记录云仓库同步认证阻塞` 已同步到云仓库。
- 因本日志是在推送成功后追加,还需要另行提交并推送本成功日志,保证日志文件也同步到云端。
## 2026-06-27 working5 接续执行日志W5-200 低频 gmoccapy HAL pin 边界
用户请求:
- 接续上一轮,继续完成 `/home/meswork/cnc_wams/work/working5` 的工作。
执行过程:
- 恢复工作现场:
- 命令:`pwd && rg --files work/working5 | sed -n '1,200p'`
- 结果:当前目录为 `/home/meswork/cnc_wams``work/working5` 下 01-06 和 README 文件存在。
- 命令:`git status --short`
- 结果:初始工作树干净。
- 命令:`tail -n 120 web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 结果:确认上一轮 W5-190 已完成,并已同步到云仓库。
- 读取工作控制文件:
- 命令:`sed -n '1,240p' work/working5/04-任务矩阵.md`
- 结果W5-001 到 W5-190、W5-900 均为完成。
- 命令:`sed -n '1,260p' work/working5/03-推进台账.md`
- 结果R6 下一步指向 `gmoccapy.unlock-settings`、tool measurement/blockheight/probeheight、INI user messages 等低频 HAL pins。
- 命令:`sed -n '1,220p' work/working5/01-项目功能内容.md``sed -n '1,220p' work/working5/02-项目程序开发详细步骤.md`
- 结果:确认仍以 LinuxCNC `gmoccapy_XYZAB.ini` 五轴仿真配置为行为基准。
- 定位源码和现有实现:
- 命令:`rg --files work/working5``rg -n "GMOCAPY_HAL_PIN|gmoccapyGui|delete-message|warning-confirm|jog\\.axis|unlock|block|probe|message" web-rtcp-5axis-sim-plan/app/src web-rtcp-5axis-sim-plan/tests -S`
- 结果:确认上一轮已有 `GMOCAPY_HAL_PIN`,覆盖 operator、override、jog 和 message pins。
- 命令:`find /home/meswork/cnc_wams -path '*/gmoccapy.py' -o -path '*/gmoccapy_XYZAB.ini' -o -path '*/gmoccapy_XYZAB.pref' -o -path '*/gmoccapy_postgui.hal'`
- 结果:确认 LinuxCNC 源码副本在 `/home/meswork/cnc_wams/linuxcnc`,包括 `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py``linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini``linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.pref`
- 命令:`rg -n "unlock-settings|toolmeasurement|blockheight|probeheight|searchvel|probevel|MESSAGE_|newpin|halcomp|hal_glib" linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py -S`
- 结果:定位 `_on_unlock_settings_changed()``_check_toolmeasurement()``on_chk_use_tool_measurement_toggled()``on_btn_block_height_clicked()``_init_user_messages()``_show_user_message()``_make_hal_pins()` 中相关 pins。
- 命令:`sed -n '1,260p' linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini``sed -n '1,260p' linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.pref`
- 结果:确认 XYZAB INI 没有 `[TOOLSENSOR]`,没有 `[DISPLAY] MESSAGE_*`pref 默认 `unlock_way=use``use_toolmeasurement=False``blockheight=0.0`
- 源码语义结论:
- `gmoccapy.unlock-settings` 是 HAL input bit但只有设置页选择 HAL unlock 时才控制 `tbtn_setup` 敏感性;默认 `unlock_way=use` 下不应锁定/解锁 setup。
- `gmoccapy.probeheight``gmoccapy.blockheight``gmoccapy.toolmeasurement``gmoccapy.searchvel``gmoccapy.probevel` 是 tool measurement HAL_OUT pins由 GUI/prefs 写出;当前 XYZAB 因无 `[TOOLSENSOR]` 默认禁用自动测刀。
- `gmoccapy.messages.*` 是 INI user messages 的动态 pin只有存在 `MESSAGE_*` 配置时才创建;当前 XYZAB 无配置。
- 修改实现:
- 修改 `web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js`
- 补齐 `gmoccapy.unlock-settings``gmoccapy.probeheight``gmoccapy.blockheight``gmoccapy.toolmeasurement``gmoccapy.searchvel``gmoccapy.probevel``gmoccapy.toolchange-number``gmoccapy.toolchange-confirm`
- 新增 `toolsensor``userMessages` 配置摘要,记录 no `[TOOLSENSOR]` 与 no `MESSAGE_*` 原因。
- 修改 `web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js`
- 新增 `settings` pin group。
- 扩展 `tool` pin group纳入 tool measurement HAL_OUT pins 和 `toolchange-confirm`
- 新增 `settingsPins``toolMeasurementPins``userMessages`、相关 behavior flags 和 summary 字段。
- 修改 `web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js`
- 扩展 HAL path notes。
- 新增 `hal-settings-unlock``hal-tool-measurement``hal-user-message` action mapping。
- 修改 `web-rtcp-5axis-sim-plan/app/src/state/store.js`
-`gmoccapyGui` 中新增 `settingsUnlockMode``settingsUnlockPin``setupSensitive``toolsensorConfigured``toolMeasurement``probeHeight``blockHeight``searchVelocity``probeVelocity``userMessagesConfigured``userMessagePins`
- 扩展 `GMOCAPY_HAL_PIN`:默认 `unlock_way=use``unlock-settings` 只记录 pin level显式 HAL unlock 模式下按 level 控制 setuptool measurement HAL_OUT pins 只作为诊断记录;当前 XYZAB 对 `gmoccapy.messages.*` 返回无 `MESSAGE_*` 配置提示。
- 修改 `web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js`
- 新增 `HAL settings``HAL tool/user` 诊断行。
- 修改 Node/browser tests
- `verify_gmoccapy_hal_model.mjs`
- `verify_gmoccapy_communication_model.mjs`
- `verify_gmoccapy_xyzab_profile.mjs`
- `verify_gmoccapy_xyzab_gates.mjs`
- `gmoccapy_shell_smoke.html`
- 修改 working5 文档:
- `work/working5/03-推进台账.md`
- `work/working5/04-任务矩阵.md`
- `work/working5/05-验收证据.md`
- `work/working5/06-决策记录.md`
- 验证过程:
- 命令:`node --check web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js && node --check web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js && node --check web-rtcp-5axis-sim-plan/app/src/state/store.js && node --check web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js`
- 结果:通过,无语法错误。
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && node ../tests/node/verify_gmoccapy_hal_model.mjs && node ../tests/node/verify_gmoccapy_communication_model.mjs && node ../tests/node/verify_gmoccapy_xyzab_profile.mjs && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs`
- 结果:通过,输出 `gmoccapy_hal_model_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_xyzab_profile_smoke=ok``gmoccapy_xyzab_gates_smoke=ok`
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && bash ../tests/browser/verify_gmoccapy_shell_browser.sh`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok`
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke:node`
- 结果:通过,关键输出包含 `gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok``linear_unit_conversion_smoke=ok`
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run build && npm run smoke`
- 结果:通过,输出 `gmoccapy_static_build=ok``gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working5`
- 结果:通过,无输出。
- 工作树状态:
- 命令:`git status --short --branch`
- 结果:当前 `master...origin/master`,存在本轮修改文件,尚未 commit。
- 命令:`git diff --stat -- web-rtcp-5axis-sim-plan work/working5`
- 结果14 个文件变更,包含 app/profile/runtime/state/ui、Node/browser tests 和 working5 文档。
结论:
- W5-200 已完成。
- Web 项目现在能按 gmoccapy 源码和 XYZAB 配置明确表达 `unlock-settings`、tool measurement HAL_OUT pins、动态 INI user message pins 的启用边界。
- 当前 XYZAB 的 no `[TOOLSENSOR]`、no `MESSAGE_*`、默认 `unlock_way=use` 均已写入 profile、HAL model、communication model、store、UI 诊断和测试证据。
- 没有执行 commit 或 push。
## 2026-06-27 working5 接续复核日志:任务矩阵闭环与完整验收重跑
用户请求:
- 接续上一轮,继续完成 `/home/meswork/cnc_wams/work/working5` 的工作。
执行过程:
- 恢复工作现场:
- 命令:`pwd && ls -la /home/meswork/cnc_wams/work/working5`
- 结果:当前工作区为 `/home/meswork/cnc_wams``working5` 目录下 README 和 01-06 工作控制文件均存在。
- 命令:`git -C /home/meswork/cnc_wams/work/working5 status --short --branch`
- 结果:当前分支为 `master...origin/master`;存在上一轮 app、tests、working5 文档和 `gpdlog.md` 修改。
- 命令:`rg --files /home/meswork/cnc_wams/work/working5 | sed 's#^/home/meswork/cnc_wams/work/working5/##' | head -200`
- 结果:确认 `04-任务矩阵.md``06-决策记录.md``03-推进台账.md``README.md``01-项目功能内容.md``05-验收证据.md``02-项目程序开发详细步骤.md` 存在。
- 读取 working5 控制文档:
- 命令:`sed -n '1,240p' /home/meswork/cnc_wams/work/working5/03-推进台账.md`
- 结果:确认 R1-R7 已记录R7 明确 W5-200 已完成,且 `npm run smoke:node``npm run build && npm run smoke``git diff --check -- web-rtcp-5axis-sim-plan work/working5` 均通过。
- 命令:`sed -n '1,240p' /home/meswork/cnc_wams/work/working5/04-任务矩阵.md`
- 结果:确认 W5-001 到 W5-200、W5-900 全部为“完成”。
- 命令:`sed -n '1,260p' /home/meswork/cnc_wams/work/working5/05-验收证据.md`
- 结果:确认 EV-W5-200、EV-W5-201 已覆盖 settings/tool/user-message 低频 HAL pin 诊断、完整 Node、静态构建和浏览器 smoke。
- 命令:`sed -n '1,260p' /home/meswork/cnc_wams/work/working5/06-决策记录.md`
- 结果:确认 DR-W5-007 已记录 `unlock-settings`、tool measurement HAL_OUT、动态 user messages 的配置启用边界。
- 检查约束和项目脚本:
- 命令:`sed -n '1,120p' /home/meswork/cnc_wams/AGENTS.md`
- 结果:确认本工作区要求每次 GPT/Codex 执行完成后,将完整过程日志用中文追加到 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 命令:`sed -n '1,220p' /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/package.json`
- 结果:确认 `smoke:node``build``smoke` 脚本定义。
- 命令:`git -C /home/meswork/cnc_wams diff --stat -- web-rtcp-5axis-sim-plan work/working5`
- 结果:确认当前主要变更仍集中在 gmoccapy profile/runtime/state/ui、Node/browser tests、working5 文档和过程日志。
- 重新执行完整验收:
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke:node`
- 结果:通过,关键输出包含 `linuxcnc_kinematics_runtime_smoke=ok``linuxcnc_interpreter_runtime_smoke=ok``linuxcnc_ini_runtime_smoke=ok``linuxcnc_task_hal_runtime_smoke=ok``real_linuxcnc_5axis_program_cases_smoke=ok``gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok``linear_unit_conversion_smoke=ok`
- 命令:`cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run build && npm run smoke`
- 结果:通过,输出 `gmoccapy_static_build=ok``gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working5`
- 结果:通过,无输出。
- 本次修改:
- 未修改 app 源码、测试或 working5 控制文档。
- 仅按 AGENTS.md 要求追加本中文过程日志。
结论:
- `/home/meswork/cnc_wams/work/working5` 的任务矩阵已闭环。
- 本次接续复核重新跑通完整 Node smoke、静态构建、浏览器 smoke 和 diff whitespace 检查。
- 当前未执行 commit 或 push。
## 2026-06-27 working5 最终完成日志:低频 HAL pin 收口后的当前态复核
用户请求:
- 完成 `/home/meswork/cnc_wams/work/working5` 的工作。
执行过程:
- 读取工作区规则:
- 命令:`pwd && rg --files -g 'AGENTS.md' -g '!node_modules' -g '!dist' -g '!build'`
- 结果:当前目录为 `/home/meswork/cnc_wams`,找到 `AGENTS.md``wasm-port/AGENTS.md`
- 命令:`sed -n '1,220p' AGENTS.md`
- 结果:确认每次 GPT/Codex 执行完成后,需要将中文完整过程日志追加到 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 检查 `working5` 文件和工作树状态:
- 命令:`find /home/meswork/cnc_wams/work/working5 -maxdepth 3 -type f | sort`
- 结果:确认 `README.md``01-项目功能内容.md``02-项目程序开发详细步骤.md``03-推进台账.md``04-任务矩阵.md``05-验收证据.md``06-决策记录.md` 均存在。
- 命令:`git status --short`
- 结果:工作树中已有 gmoccapy profile/runtime/state/ui、Node/browser tests、working5 文档和 `gpdlog.md` 修改;按规则不回滚既有改动。
- 读取 `working5` 控制文档:
- 命令:`sed -n '1,240p' work/working5/README.md`
- 结果:确认本目录用于推进 `web-rtcp-5axis-sim-plan` 与 LinuxCNC `gmoccapy_XYZAB.ini` 五轴仿真流程一致性验证。
- 命令:`sed -n '1,260p' work/working5/01-项目功能内容.md`
- 结果确认目标覆盖启动配置、task/status 互锁、HAL 仿真 IO、G-code/五轴运动和 gmoccapy UI 等范围。
- 命令:`sed -n '1,320p' work/working5/02-项目程序开发详细步骤.md`
- 结果:确认应按读取基准、执行测试、差异分析、修改实现、回归验收、更新台账的流程推进。
- 命令:`sed -n '1,320p' work/working5/03-推进台账.md`
- 结果:确认 R1-R7 已记录W5-200 已完成,完整 Node、构建、浏览器 smoke 和 diff check 均已有历史证据。
- 命令:`sed -n '1,360p' work/working5/04-任务矩阵.md`
- 结果:确认 W5-001 到 W5-200、W5-900 均为“完成”。
- 命令:`sed -n '1,420p' work/working5/05-验收证据.md`
- 结果:确认 EV-W5-001 到 EV-W5-201 已覆盖各任务验收命令和断言摘要。
- 命令:`sed -n '1,360p' work/working5/06-决策记录.md`
- 结果:确认 DR-W5-001 到 DR-W5-007 已记录模式门控、换刀 HAL、hard-button、HAL input、jog/message、settings/tool/user-message 等关键取舍。
- 检查项目脚本和新增实现覆盖点:
- 命令:`sed -n '1,220p' package.json`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:确认 `smoke:node``build``smoke` 脚本可用于完整验收。
- 命令:`rg -n "GMOCAPY_HAL_PIN|unlock-settings|tool-user-inputs|settings-inputs|messages\\.\\*|toolmeasurement|probeheight|blockheight" app/src tests -S`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan`
- 结果:确认低频 HAL pin 实现和断言分布在 `gmoccapy-xyzab.js``gmoccapy-hal-model.js``gmoccapy-communication-model.js``store.js``gmoccapy-shell.js` 以及 Node/browser 测试中。
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working5`
- 结果:通过,无空白错误。
- 执行当前态完整验收:
- 命令:`npm run smoke:node`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出包含 `linuxcnc_kinematics_runtime_smoke=ok``linuxcnc_interpreter_runtime_smoke=ok``linuxcnc_ini_runtime_smoke=ok``linuxcnc_task_hal_runtime_smoke=ok``real_linuxcnc_5axis_program_cases_smoke=ok``gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok``linear_unit_conversion_smoke=ok`
- 命令:`npm run build`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_static_build=ok`
- 命令:`npm run smoke`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 更新 `working5` 收口文档:
- 修改 `work/working5/03-推进台账.md`:新增 R8“working5 最终复核与验收收口”,记录当前态复核、变更文件和三项主验收命令结果。
- 修改 `work/working5/05-验收证据.md`:新增 EV-W5-900 和 R8 命令记录,明确 `npm run smoke:node``npm run build``npm run smoke` 当前通过。
- 未修改 app 源码和测试实现;本轮主要是核对、重跑验收和补齐收口证据。
- 最后检查:
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working5`
- 结果:通过,无输出。
- 命令:`git status --short`
- 结果:仍有 gmoccapy profile/runtime/state/ui、测试、working5 文档和 `gpdlog.md` 修改,未执行 commit。
- 命令:`git diff --stat -- web-rtcp-5axis-sim-plan work/working5`
- 结果:变更集中在 15 个文件,包含 app/profile/runtime/state/ui、Node/browser tests、working5 文档和过程日志。
结论:
- `/home/meswork/cnc_wams/work/working5` 已按当前任务矩阵完成并复核通过。
- 当前工作区完整 Node 冒烟、静态构建、浏览器冒烟和 diff 空白检查均通过。
- 本轮未执行 commit 或 push。
## 2026-06-27 working5 剩余工作查询日志
用户请求:
- 询问 `/home/meswork/cnc_wams/work/working5` 还有哪些工作要做。
执行过程:
- 读取任务矩阵:
- 命令:`sed -n '1,220p' work/working5/04-任务矩阵.md`
- 结果:确认 W5-001、W5-010、W5-020、W5-030、W5-040、W5-100、W5-110、W5-120、W5-130、W5-140、W5-150、W5-160、W5-170、W5-180、W5-190、W5-200、W5-900 状态均为“完成”。
- 读取推进台账末尾:
- 命令:`tail -n 90 work/working5/03-推进台账.md`
- 结果:确认 R8 写明 `working5` 已按当前任务矩阵完成并复核通过,本轮不新增后续实现任务。
- 搜索待办和后续关键词:
- 命令:`rg -n "下一步|后续|待|未完成|完成|W5-900|EV-W5-900" work/working5`
- 结果:确认验收证据中 EV-W5-900 记录最终复核通过;发现 `06-决策记录.md` 中 DR-W5-004 保留早期“未完成 optional-stop、blockdelete、ignore-limits”的历史影响描述但对应工作已在 W5-180 完成,不是当前剩余任务。
结论:
- 当前 `working5` 任务矩阵内没有剩余待办。
- 可选深化方向仅是台账 R7 提到的 gmoccapy 文件/宏页面、tool editor 页面或 native GTK 未实现页面诊断边界;这些不属于当前 `working5` 已定义任务矩阵的未完成项。
## 2026-06-27 working6 控制文档创建日志
用户请求:
- 为“gmoccapy 文件/宏页面、tool editor 页面、native GTK 未实现页面诊断边界”这些 working5 可选深化方向创建 7 个后续工作文档,写入 `/home/meswork/cnc_wams/work/working6`,便于后续按文档开展工作。
执行过程:
- 检查目标目录和工作区规则:
- 命令:`test -d /home/meswork/cnc_wams/work/working6 && find /home/meswork/cnc_wams/work/working6 -maxdepth 2 -type f | sort || true`
- 结果:创建前 `working6` 无可见文件。
- 命令:`sed -n '1,160p' AGENTS.md`
- 结果:确认本工作区要求每次执行完成后将中文过程日志追加到 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 命令:`find /home/meswork/cnc_wams/work/working5 -maxdepth 1 -type f | sort`
- 结果:确认 working5 的 README 和 01-06 文档可作为风格参照。
- 创建目录:
- 命令:`mkdir -p /home/meswork/cnc_wams/work/working6`
- 结果:通过。
- 创建 7 个文档:
- 新增 `/home/meswork/cnc_wams/work/working6/README.md`,作为工作索引,列出前置工作、对标基准、工作文件和推进规则。
- 新增 `/home/meswork/cnc_wams/work/working6/01-项目功能内容.md`,定义目标、对标范围、当前项目入口和非目标。
- 新增 `/home/meswork/cnc_wams/work/working6/02-项目程序开发详细步骤.md`,按读取基准、建立映射、跑基线、补模型/状态/UI/测试、回归验收和更新文档拆分步骤。
- 新增 `/home/meswork/cnc_wams/work/working6/03-推进台账.md`,记录 R1 创建控制文件、修改文件、待验证项和下一步。
- 新增 `/home/meswork/cnc_wams/work/working6/04-任务矩阵.md`,创建 W6-001 到 W6-900 任务W6-001 标为完成,其余标为待开始。
- 新增 `/home/meswork/cnc_wams/work/working6/05-验收证据.md`,记录 EV-W6-001 和本轮命令/文件证据。
- 新增 `/home/meswork/cnc_wams/work/working6/06-决策记录.md`,记录 working6 作为 working5 后续深化、未实现 native GTK 页面诊断化、文件/宏/tool editor 先按 XYZAB 配置边界建模等决策。
- 验证创建结果:
- 命令:`find /home/meswork/cnc_wams/work/working6 -maxdepth 1 -type f -printf '%f\\n' | sort`
- 结果:确认 `README.md``01-项目功能内容.md``06-决策记录.md` 共 7 个文件存在。
- 命令:`sed -n '1,220p' work/working6/04-任务矩阵.md`
- 结果:确认任务矩阵包含 W6-001、W6-010、W6-020、W6-030、W6-040、W6-100、W6-110、W6-120、W6-130、W6-140、W6-150、W6-160、W6-900。
- 命令:`git diff --check -- work/working6`
- 结果:通过,无空白错误。
- 命令:`git status --short -- work/working6`
- 结果:显示 `?? work/working6/`,说明新增文档尚未提交。
结论:
- `/home/meswork/cnc_wams/work/working6` 的 7 个控制文档已创建完成。
- working6 后续可从 W6-010 开始,先建立 gmoccapy native 页面到 Web 模块的映射,再执行 Node/构建/浏览器基线。
- 本轮未执行 app 源码修改、测试运行、commit 或 push。
## 2026-06-26 23:47 EDT working6 gmoccapy 页面诊断边界实现日志
用户请求:
- 对标 `linuxcnc` 源程序中的 `gmoccapy` 五轴示例功能和实现方式,完善 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` 项目,并将完善后的文档格式参考和记录写入 `/home/meswork/cnc_wams/work/working6`
执行过程:
- 读取并确认后续工作文档:
- 命令:`find /home/meswork/cnc_wams/work/working6 -maxdepth 1 -type f | sort`
- 结果:确认 `README.md``01-项目功能内容.md``06-决策记录.md` 已存在。
- 命令:`sed -n '1,240p' work/working6/04-任务矩阵.md`
- 结果:确认 W6-010 到 W6-900 为本轮待推进任务。
- 对标 LinuxCNC `gmoccapy` 五轴源程序和配置:
- 读取 `linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini`,确认 `PROGRAM_PREFIX = ../../nc_files/``SUBROUTINE_PATH = ./macros``TOOL_TABLE = tool.tbl`,以及 5 个宏:`i_am_lost``halo_world``jog_around``increment xinc yinc``go_to_position X-pos Y-pos Z-pos`
- 读取 `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py`确认文件页、宏按钮页、tool editor 页和 native GTK 页面切换的关键实现:`_init_IconFileSelection()``_init_file_to_load()``on_btn_load_clicked()``on_IconFileSelection1_selected()``_make_macro_button()``_on_btn_macro_pressed()``_init_tooleditor()``_show_tooledit_tab()``on_btn_tool_clicked()`
- 执行基线验证:
- 命令:`npm run smoke:node`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,包含 `gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok` 等。
- 命令:`npm run build`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_static_build=ok`
- 命令:`npm run smoke`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 修改 `app/src/runtime/gmoccapy-hal-model.js`
- 新增 `nativePages` 结构覆盖文件页、宏页、tool editor 页和 native GTK 页面实现矩阵。
- 文件页记录 `IconFileSelection1``PROGRAM_PREFIX`、文件扩展名、回调链和 Web 侧 partial 边界。
- 宏页记录 5 个 `gmoccapy_XYZAB.ini` 宏、参数、O-word MDI 命令模板和 `RUN_MDI` 门控边界。
- tool editor 页记录 `tool.tbl`、17 个工具、可见轴 `X/Y/Z/A/B`、诊断-only 实现和不写回 `tool.tbl` 的边界。
- native GTK 实现矩阵区分 implemented、partial、diagnostic-only、native-only。
-`tbtn_user_tabs``tbtn_setup``btn_touch``btn_tool``tbtn_switch_mode` 等 hard button 从伪 DOM 映射调整为结构化 native page 诊断分发。
- 扩展 `createGmoccapyHalSummary()`,输出 native page 数量、partial 数量、diagnostic-only 数量、macro 数量、tool editor 写回边界等摘要。
- 修改 `app/src/profiles/gmoccapy-xyzab.js`
- 在 XYZAB profile 中加入 `nativePages.filePage``nativePages.macroPage``nativePages.toolEditor` 和诊断边界说明。
- 明确文件页 `PROGRAM_PREFIX`、宏页 `SUBROUTINE_PATH`、tool editor 的 `tool.tbl` 路径、17 个工具和禁止浏览器写回。
- 修改 `app/src/runtime/gmoccapy-communication-model.js`
- 新增 `filePage``macroPage``toolEditorPage``nativePages` 通信路径。
- 新增 `file-page``macro-page``tool-editor-page``native-page-diagnostic` 动作映射。
- 摘要新增 `pagePathCount`
- 修改 `app/src/state/store.js`
- 扩展 `gmoccapyGui` 初始状态,增加 active native page、file page、macro page、tool editor 的诊断状态字段。
- 新增 `GMOCAPY_NATIVE_PAGE``GMOCAPY_PAGE_ACTION``GMOCAPY_RUN_MACRO``GMOCAPY_TOOL_EDITOR_ACTION` 分发处理。
- 宏执行通过 `gateLinuxCncTaskAction(... RUN_MDI)` 门控后构造 `O<name> call [args]` 并复用既有 MDI 执行路径。
- tool editor 的保存、增加、删除、touch-off 等写操作被诊断性阻断,保持不写 `tool.tbl` 和不破坏 iocontrol-loopback。
- 修改 `app/src/ui/gmoccapy-shell.js`
- 在信息/诊断 tab 中增加 `data-gmoccapy-page="file"``macros``tool-editor``matrix` 行。
- 新增文件页、宏页、tool editor 页、native page matrix 格式化输出。
- hard button 诊断输出补充 diagnostic-only native page 数量。
- 修改测试:
- `tests/node/verify_gmoccapy_hal_model.mjs`:覆盖 hard button native page 分发、nativePages 结构、5 个宏、17 个工具、实现矩阵和摘要字段。
- `tests/node/verify_gmoccapy_communication_model.mjs`:覆盖新增页面通信路径、动作映射和 `pagePathCount`
- `tests/node/verify_gmoccapy_xyzab_profile.mjs`:覆盖 profile 中 native page、宏、tool editor 配置。
- `tests/node/verify_gmoccapy_xyzab_gates.mjs`:覆盖 native page 诊断、文件页动作、tool editor 写回阻断、宏门控和 MDI 命令生成。
- `tests/browser/gmoccapy_shell_smoke.html`:覆盖新增诊断 DOM 行、native page 分发、文件页动作、tool editor 写回阻断和宏执行状态。
- 执行语法和局部验证:
- 命令:`node --check src/runtime/gmoccapy-hal-model.js && node --check src/runtime/gmoccapy-communication-model.js && node --check src/state/store.js && node --check src/ui/gmoccapy-shell.js`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过。
- 命令:`node ../tests/node/verify_gmoccapy_hal_model.mjs && node ../tests/node/verify_gmoccapy_communication_model.mjs`
- 结果:通过,输出 `gmoccapy_hal_model_smoke=ok``gmoccapy_communication_model_smoke=ok`
- 命令:`node ../tests/node/verify_gmoccapy_xyzab_profile.mjs && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs`
- 结果:通过,输出 `gmoccapy_xyzab_profile_smoke=ok``gmoccapy_xyzab_gates_smoke=ok`
- 命令:`bash ../tests/browser/verify_gmoccapy_shell_browser.sh`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok`
- 执行完整验收:
- 命令:`npm run smoke:node`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过。
- 命令:`npm run build`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_static_build=ok`
- 命令:`npm run smoke`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 更新 working6 文档:
- 修改 `work/working6/04-任务矩阵.md`:将 W6-001、W6-010、W6-020、W6-030、W6-040、W6-100、W6-110、W6-120、W6-130、W6-140、W6-150、W6-160、W6-900 全部标记为完成。
- 修改 `work/working6/03-推进台账.md`:新增 R2“实现 gmoccapy 页面级诊断边界”,记录对标内容、修改文件、验证结果和下一步。
- 修改 `work/working6/05-验收证据.md`:新增 EV-W6-020、EV-W6-100、EV-W6-160、EV-W6-900 和 R2 命令证据。
- 修改 `work/working6/06-决策记录.md`:新增 DR-W6-004、DR-W6-005、DR-W6-006记录宏页按 O-word MDI 建模、tool editor 只做诊断不写回、native-only 页面用结构化诊断边界表达。
- 最终检查:
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working6`
- 结果:通过,无输出。
- 命令:`node ../tests/node/verify_gmoccapy_hal_model.mjs && node ../tests/node/verify_gmoccapy_communication_model.mjs && node ../tests/node/verify_gmoccapy_xyzab_profile.mjs && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_hal_model_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_xyzab_profile_smoke=ok``gmoccapy_xyzab_gates_smoke=ok`
- 命令:`git status --short`
- 结果:显示本轮修改集中在 gmoccapy profile/runtime/state/ui、Node/browser tests、working6 文档和 `gpdlog.md`;历史 working5 文档修改仍存在,未回退;未执行 commit 或 push。
结论:
- `working6` 中定义的 gmoccapy 文件页、宏页、tool editor 页和 native GTK 未实现页面诊断边界任务已完成。
- Web 项目已具备对 `gmoccapy_XYZAB.ini` 宏配置、文件页路径、tool editor 表和 native page 边界的结构化表达、状态分发、UI 诊断和测试覆盖。
- 完整 Node 冒烟、静态构建、浏览器冒烟和 diff 空白检查均已通过。
## 2026-06-27 R3 接续 working6 最终复核过程日志
用户请求:
- 接续上一轮,继续完成 `/home/meswork/cnc_wams/work/working6` 的工作。
执行过程:
- 读取当前工作目录和 `work/working6` 文件清单,确认 `README.md``01-项目功能内容.md``02-项目程序开发详细步骤.md``03-推进台账.md``04-任务矩阵.md``05-验收证据.md``06-决策记录.md` 均存在。
- 执行 `git status --short`,确认当前未提交改动集中在 `web-rtcp-5axis-sim-plan` 的 gmoccapy profile/runtime/state/ui/tests、`work/working6` 文档、`gpdlog.md`,并存在历史 `work/working5` 文档改动;未回退任何既有改动。
- 读取 `work/working6/README.md`,确认本轮目标为 gmoccapy 文件页、宏页、tool editor 页和 native GTK 未实现页面诊断边界。
- 读取 `work/working6/04-任务矩阵.md`,确认 W6-001、W6-010、W6-020、W6-030、W6-040、W6-100、W6-110、W6-120、W6-130、W6-140、W6-150、W6-160、W6-900 均已标记完成。
- 读取 `work/working6/03-推进台账.md``work/working6/05-验收证据.md`,确认上一轮 R2 已记录实现内容和通过的基线、最小验收、回归命令。
- 读取 `work/working6/01-项目功能内容.md``02-项目程序开发详细步骤.md``06-决策记录.md`,确认 W6 的非目标、开发步骤和决策边界:不实现完整 GTK 文件选择器、不写真实 `tool.tbl`、未实现 native GTK 页面必须 diagnostic-only。
- 执行 `git diff -- web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js`,复核 profile 已新增 `nativePages.filePage``nativePages.macroPage``nativePages.toolEditor`、toolsensor/userMessages 诊断边界和相关 HAL pin。
- 执行 `git diff -- web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js`,复核 communication model 已新增 file/macro/tool/native page 路径和 action mappingHAL model 已新增 native page matrix、settings/tool measurement/user messages 诊断和 hard-button native page 分发。
- 执行 `git diff -- web-rtcp-5axis-sim-plan/app/src/state/store.js`,复核 store 已新增 `GMOCAPY_NATIVE_PAGE``GMOCAPY_PAGE_ACTION``GMOCAPY_RUN_MACRO``GMOCAPY_TOOL_EDITOR_ACTION`,并确认宏执行复用 `RUN_MDI` gate、tool editor 写操作被诊断性阻断。
- 执行 `git diff -- web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js`,复核 UI 已新增 `data-gmoccapy-page="file"``macros``tool-editor``matrix` 诊断行,以及 settings/tool/user HAL 诊断行。
- 执行测试差异复核:
- `git diff -- web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs`
- `git diff -- web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs`
- `git diff -- web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html`
确认 Node/browser 测试已覆盖页面模型、宏 count、tool table count、native page matrix、DOM 诊断、hard-button diagnostic-only 分发、宏 MDI 命令和 tool editor 写回阻断。
- 执行完整 Node 冒烟:
- 命令:`npm run smoke:node`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,关键输出包含 `gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok``linear_unit_conversion_smoke=ok`
- 执行静态构建:
- 命令:`npm run build`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_static_build=ok`
- 执行浏览器冒烟:
- 命令:`npm run smoke`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 执行空白检查:
- 命令:`git diff --check -- web-rtcp-5axis-sim-plan work/working6`
- 工作目录:`/home/meswork/cnc_wams`
- 结果:通过,无输出。
- 读取 workspace 级 `AGENTS.md`,确认需要在每次 GPT/Codex 执行结束后将中文过程日志追加到 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 修改 `work/working6/03-推进台账.md`,追加 R3“接续复核 working6 完成状态”,记录本轮复核范围、验证命令和下一步建议。
- 修改 `work/working6/05-验收证据.md`,追加 EV-W6-901 和 R3 命令记录,记录 `npm run smoke:node``npm run build``npm run smoke``git diff --check -- web-rtcp-5axis-sim-plan work/working6` 均通过。
- 修改本文件 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`,追加本轮中文过程日志。
结论:
- `working6` 当前任务已闭环,不需要新增功能代码补丁。
- gmoccapy 文件页、宏页、tool editor 页、native GTK 未实现页面诊断边界在 profile、model、store、UI 和测试中均有结构化表达与验收覆盖。
- 本轮最终回归中Node 冒烟、静态构建、浏览器冒烟和 `git diff --check` 均已通过。
## 2026-06-27 R4 提交到云仓库过程日志
用户请求:
- 将当前工作提交到云仓库。
执行过程:
- 查看仓库根目录、当前分支和远端:
- 命令:`git rev-parse --show-toplevel && git branch --show-current && git remote -v`
- 结果:仓库根目录为 `/home/meswork/cnc_wams`,当前分支为 `master`,远端 `origin``http://154.8.160.151:3000/wangdequan/cnc_wams`
- 查看工作树:
- 命令:`git status --short`
- 结果:存在 gmoccapy profile/runtime/state/ui/tests 修改、`gpdlog.md` 修改、`work/working5` 文档修改和新增 `work/working6/`
- 查看改动统计:
- 命令:`git diff --stat`
- 结果:确认主要改动集中在 gmoccapy 页面级诊断边界、测试、working5/working6 文档和过程日志。
- 查看最近提交:
- 命令:`git log -5 --oneline --decorate`
- 结果:当前 `HEAD``origin/master``origin/HEAD` 均在 `f964ced`
- 执行最终 Node 冒烟:
- 命令:`npm run smoke:node`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,关键输出包含 `gmoccapy_xyzab_profile_smoke=ok``gmoccapy_communication_model_smoke=ok``gmoccapy_hal_model_smoke=ok``gmoccapy_xyzab_gates_smoke=ok``linear_unit_conversion_smoke=ok`
- 执行静态构建:
- 命令:`npm run build`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_static_build=ok`
- 执行浏览器冒烟:
- 命令:`npm run smoke`
- 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app`
- 结果:通过,输出 `gmoccapy_shell_smoke=ok``gmoccapy_dist_smoke=ok`
- 执行空白检查:
- 命令:`git diff --check`
- 工作目录:`/home/meswork/cnc_wams`
- 结果:通过,无输出。
- 同步远端状态:
- 命令:`git fetch origin master`
- 结果:成功从 `origin/master` 获取。
- 命令:`git rev-list --left-right --count HEAD...origin/master`
- 结果:`0 0`,本地与远端无分叉。
- 追加本过程日志到 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`
- 下一步准备执行:
- `git add -A`
- `git commit -m "完善 working6 gmoccapy 页面诊断边界"`
- `git push origin master`
阶段结论:
- 提交前验证全部通过。
- 本地 `master` 与远端 `origin/master` 无分叉,可以直接提交并推送。
后续提交与推送结果:
- 执行提交:
- 命令:`git add -A && git commit -m "完善 working6 gmoccapy 页面诊断边界"`
- 结果:成功创建本地提交 `ef3247b 完善 working6 gmoccapy 页面诊断边界`,包含 22 个文件变更。
- 尝试推送:
- 命令:`git push origin master`
- 结果:失败,错误为 `fatal: could not read Username for 'http://154.8.160.151:3000': No such device or address`
- 失败原因判断:
- 当前远端使用 HTTP 地址 `http://154.8.160.151:3000/wangdequan/cnc_wams`
- 当前非交互执行环境没有可用的 Git credential helper、凭据文件、token 环境变量或 SSH 远端。
- 上一次云仓库同步成功记录显示,当时通过 Git 终端提示输入用户名和密码完成认证;本次执行环境无法读取交互式用户名。
- 当前仓库状态:
- 本地 `master` 已比 `origin/master` 领先 1 个提交。
- 云端推送未完成,阻塞点为 HTTP 认证。
最终结论:
- 本地提交已完成。
- 云仓库推送尚未完成;需要配置 Git HTTP 凭据、改用可用 token/credential helper或切换到已配置 SSH key 的远程地址后再执行 `git push origin master`
补充最终状态:
- 为了把推送失败原因纳入同一笔提交,执行了:
- `git add web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md && git commit --amend --no-edit`
- amend 后本地提交哈希发生变化,最终提交以 `git log -1 --oneline` 为准,提交主题仍为 `完善 working6 gmoccapy 页面诊断边界`
- 最终检查:
- 命令:`git status --short && git rev-list --left-right --count HEAD...origin/master && git log -1 --oneline --decorate`
- 结果:工作树干净,本地 `master` 领先 `origin/master` 1 个提交,远端未领先本地。
- 命令:`git diff --check HEAD~1..HEAD`
- 结果:通过,无输出。

View File

@@ -310,6 +310,41 @@
if (!doc.querySelector('[data-gmoccapy-comm="web-path"]')?.textContent.includes("store.dispatch")) {
throw new Error("missing gmoccapy Web runtime mapping");
}
const filePageDiagnostic = doc.querySelector('[data-gmoccapy-page="file"]')?.textContent || "";
if (
!filePageDiagnostic.includes("file page partial") ||
!filePageDiagnostic.includes("PROGRAM_PREFIX ../../nc_files/") ||
!filePageDiagnostic.includes("native IconFileSelection") ||
!filePageDiagnostic.includes("Web OPEN_FILE/staged-source")
) {
throw new Error(`missing gmoccapy file page diagnostic: ${filePageDiagnostic}`);
}
const macroPageDiagnostic = doc.querySelector('[data-gmoccapy-page="macros"]')?.textContent || "";
if (
!macroPageDiagnostic.includes("5 XYZAB macros") ||
!macroPageDiagnostic.includes("MDI gate") ||
!macroPageDiagnostic.includes("O-word call")
) {
throw new Error(`missing gmoccapy macro page diagnostic: ${macroPageDiagnostic}`);
}
const toolEditorDiagnostic = doc.querySelector('[data-gmoccapy-page="tool-editor"]')?.textContent || "";
if (
!toolEditorDiagnostic.includes("tool editor diagnostic-only") ||
!toolEditorDiagnostic.includes("17 tools") ||
!toolEditorDiagnostic.includes("writeback disabled") ||
!toolEditorDiagnostic.includes("iocontrol-loopback preserved")
) {
throw new Error(`missing gmoccapy tool editor diagnostic: ${toolEditorDiagnostic}`);
}
const nativePageMatrixDiagnostic = doc.querySelector('[data-gmoccapy-page="matrix"]')?.textContent || "";
if (
!nativePageMatrixDiagnostic.includes("12 native pages") ||
!nativePageMatrixDiagnostic.includes("5 diagnostic-only") ||
!nativePageMatrixDiagnostic.includes("2 native-only") ||
!nativePageMatrixDiagnostic.includes("hard-buttons diagnostic-only")
) {
throw new Error(`missing gmoccapy native page matrix diagnostic: ${nativePageMatrixDiagnostic}`);
}
if (!doc.querySelector('[data-gmoccapy-hal="postgui"]')?.textContent.includes("tool-change-loop")) {
throw new Error("missing gmoccapy HAL postgui diagnostic");
}
@@ -349,6 +384,24 @@
) {
throw new Error(`missing gmoccapy HAL message diagnostic: ${messageInputDiagnostic}`);
}
const settingsInputDiagnostic = doc.querySelector('[data-gmoccapy-hal="settings-inputs"]')?.textContent || "";
if (
!settingsInputDiagnostic.includes("1 settings pins") ||
!settingsInputDiagnostic.includes("unlock-settings level-driven") ||
!settingsInputDiagnostic.includes("unlock-way use") ||
!settingsInputDiagnostic.includes("setup sensitive")
) {
throw new Error(`missing gmoccapy HAL settings diagnostic: ${settingsInputDiagnostic}`);
}
const toolUserDiagnostic = doc.querySelector('[data-gmoccapy-hal="tool-user-inputs"]')?.textContent || "";
if (
!toolUserDiagnostic.includes("5 tool measurement HAL_OUT pins") ||
!toolUserDiagnostic.includes("XYZAB no TOOLSENSOR") ||
!toolUserDiagnostic.includes("0 user message pins") ||
!toolUserDiagnostic.includes("no MESSAGE_*")
) {
throw new Error(`missing gmoccapy HAL tool/user diagnostic: ${toolUserDiagnostic}`);
}
const hardButtonDiagnostic = doc.querySelector('[data-gmoccapy-hal="hard-buttons"]')?.textContent || "";
if (
!hardButtonDiagnostic.includes("7 right v-button pins") ||
@@ -1090,14 +1143,107 @@
if (halJogGuiState.deletedMessageCount !== 1 || halJogGuiState.warningConfirm !== true) {
throw new Error(`gmoccapy HAL message pins did not update state: ${JSON.stringify(halJogGuiState)}`);
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HAL_PIN",
pin: "gmoccapy.unlock-settings",
value: false,
});
await wait(25);
if (win.webRtcp5AxisSimulation.getState().gmoccapyGui.setupSensitive !== true) {
throw new Error("gmoccapy HAL unlock-settings should be ignored while unlock_way is use");
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HAL_PIN",
pin: "gmoccapy.unlock-settings",
value: false,
halUnlockMode: true,
});
await wait(25);
if (win.webRtcp5AxisSimulation.getState().gmoccapyGui.setupSensitive !== false) {
throw new Error("gmoccapy HAL unlock-settings low did not lock setup in HAL unlock mode");
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HAL_PIN",
pin: "gmoccapy.unlock-settings",
value: true,
});
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HAL_PIN",
pin: "gmoccapy.blockheight",
value: 12.5,
});
await wait(50);
const toolUserState = win.webRtcp5AxisSimulation.getState().gmoccapyGui;
if (toolUserState.setupSensitive !== true || toolUserState.blockHeight !== 12.5) {
throw new Error(`gmoccapy HAL settings/tool pins did not update state: ${JSON.stringify(toolUserState)}`);
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HAL_PIN",
pin: "gmoccapy.messages.test",
value: true,
});
await wait(25);
if (!win.webRtcp5AxisSimulation.getState().operatorMessage.includes("no MESSAGE_*")) {
throw new Error("gmoccapy HAL user message pin should report no MESSAGE_* entries");
}
const updatedJogDiagnostic = doc.querySelector('[data-gmoccapy-hal="jog-inputs"]')?.textContent || "";
const updatedMessageDiagnostic = doc.querySelector('[data-gmoccapy-hal="message-inputs"]')?.textContent || "";
const updatedSettingsDiagnostic = doc.querySelector('[data-gmoccapy-hal="settings-inputs"]')?.textContent || "";
const updatedToolUserDiagnostic = doc.querySelector('[data-gmoccapy-hal="tool-user-inputs"]')?.textContent || "";
if (!updatedJogDiagnostic.includes("turtle level-driven") || !updatedJogDiagnostic.includes("increment 0.100 mm=0.1")) {
throw new Error(`gmoccapy HAL jog diagnostic did not refresh: ${updatedJogDiagnostic}`);
}
if (!updatedMessageDiagnostic.includes("deleted 1") || !updatedMessageDiagnostic.includes("confirm on")) {
throw new Error(`gmoccapy HAL message diagnostic did not refresh: ${updatedMessageDiagnostic}`);
}
if (!updatedSettingsDiagnostic.includes("unlock-way hal") || !updatedSettingsDiagnostic.includes("pin high")) {
throw new Error(`gmoccapy HAL settings diagnostic did not refresh: ${updatedSettingsDiagnostic}`);
}
if (!updatedToolUserDiagnostic.includes("blockheight 12.5") || !updatedToolUserDiagnostic.includes("no MESSAGE_*")) {
throw new Error(`gmoccapy HAL tool/user diagnostic did not refresh: ${updatedToolUserDiagnostic}`);
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_HARDWARE_BUTTON",
pin: "gmoccapy.v-button.button-6",
value: true,
});
await wait(25);
if (
win.webRtcp5AxisSimulation.getState().gmoccapyGui.activeNativePage !== "setup" ||
!win.webRtcp5AxisSimulation.getState().operatorMessage.includes("diagnostic-only: setup")
) {
throw new Error("gmoccapy native setup page diagnostic did not update from hard-button");
}
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_PAGE_ACTION",
pageId: "file-load",
actionId: "open",
});
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_TOOL_EDITOR_ACTION",
actionId: "save",
});
await wait(50);
const nativePageState = win.webRtcp5AxisSimulation.getState().gmoccapyGui;
if (nativePageState.filePageStatus !== "open" || nativePageState.toolEditorStatus !== "writeback-blocked") {
throw new Error(`gmoccapy native page actions did not update state: ${JSON.stringify(nativePageState)}`);
}
win.webRtcp5AxisSimulation.dispatch({ type: "TOGGLE_POWER" });
win.webRtcp5AxisSimulation.dispatch({ type: "HOME" });
win.webRtcp5AxisSimulation.dispatch({ type: "SET_MODE", mode: "mdi" });
win.webRtcp5AxisSimulation.dispatch({
type: "GMOCAPY_RUN_MACRO",
name: "increment",
args: { xinc: 1.25, yinc: 2.5 },
});
await wait(50);
const macroDiagnostic = doc.querySelector('[data-gmoccapy-page="macros"]')?.textContent || "";
if (
win.webRtcp5AxisSimulation.getState().gmoccapyGui.macroLastCommand !== "O<increment> call [1.25] [2.5]" ||
!macroDiagnostic.includes("O<increment> call [1.25] [2.5]")
) {
throw new Error(`gmoccapy macro dispatch did not update diagnostics: ${macroDiagnostic}`);
}
doc.querySelector('[data-action="toggle-flood"]').click();
doc.querySelector('[data-action="toggle-mist"]').click();
await wait(50);

View File

@@ -20,6 +20,10 @@ assert.equal(model.nativePaths.hal.path.includes("HAL shared memory"), true);
assert.equal(model.nativePaths.hardwareButtons.path.includes("_button_pin_changed"), true);
assert.equal(model.nativePaths.halPins.path.includes("hal_glib.GPin value_changed"), true);
assert.equal(model.nativePaths.remap.path.includes("Python remap prolog/ngc/epilog"), true);
assert.equal(model.nativePaths.filePage.path.includes("IconFileSelection1"), true);
assert.equal(model.nativePaths.macroPage.path.includes("_on_btn_macro_pressed"), true);
assert.equal(model.nativePaths.toolEditorPage.path.includes("tooledit1.reload()"), true);
assert.equal(model.nativePaths.nativePages.path.includes("Web implementation matrix"), true);
assert.deepEqual(model.webPath.path, ["button", "store.dispatch", "linuxcnc-task-policy", "runtime/status snapshot", "UI/Three.js"]);
assert.equal(model.startupStages.length, 9);
@@ -46,8 +50,15 @@ for (const actionId of [
"coolant",
"hardware-button",
"hal-pin-input",
"hal-settings-unlock",
"hal-jog-pin",
"hal-tool-measurement",
"hal-user-message",
"hal-warning-confirm",
"file-page",
"macro-page",
"tool-editor-page",
"native-page-diagnostic",
]) {
assert.equal(actionIds.includes(actionId), true, `${actionId} missing`);
}
@@ -80,6 +91,11 @@ assert.equal(halPinMapping.gate.includes("counts require count-enable"), true);
assert.equal(halPinMapping.gate.includes("direct-value requires analog-enable"), true);
assert.equal(halPinMapping.gate.includes("delete-message pins are rising-edge only"), true);
const settingsUnlockMapping = model.actionMappings.find((mapping) => mapping.actionId === "hal-settings-unlock");
assert.equal(settingsUnlockMapping.nativeCommand, "_on_unlock_settings_changed");
assert.equal(settingsUnlockMapping.gate.includes("unlock_way"), true);
assert.equal(settingsUnlockMapping.gate.includes("HAL unlock"), true);
const halJogMapping = model.actionMappings.find((mapping) => mapping.actionId === "hal-jog-pin");
assert.equal(halJogMapping.webAction, "GMOCAPY_HAL_PIN jog.axis/jog-inc");
assert.equal(halJogMapping.nativeCommand.includes("_on_pin_jog_changed"), true);
@@ -87,16 +103,44 @@ assert.equal(halJogMapping.nativeCommand.includes("_on_pin_incr_changed"), true)
assert.equal(halJogMapping.gate.includes("press/release level"), true);
assert.equal(halJogMapping.gate.includes("increment pins are rising-edge only"), true);
const toolMeasurementMapping = model.actionMappings.find((mapping) => mapping.actionId === "hal-tool-measurement");
assert.equal(toolMeasurementMapping.nativeCommand.includes("_check_toolmeasurement"), true);
assert.equal(toolMeasurementMapping.nativeStatus.includes("probeheight"), true);
assert.equal(toolMeasurementMapping.gate.includes("no [TOOLSENSOR]"), true);
const userMessageMapping = model.actionMappings.find((mapping) => mapping.actionId === "hal-user-message");
assert.equal(userMessageMapping.nativeCommand.includes("_init_user_messages"), true);
assert.equal(userMessageMapping.nativeStatus.includes("messages.<pinname>"), true);
assert.equal(userMessageMapping.gate.includes("no MESSAGE_*"), true);
const warningConfirmMapping = model.actionMappings.find((mapping) => mapping.actionId === "hal-warning-confirm");
assert.equal(warningConfirmMapping.nativeCommand.includes("confirm_pin"), true);
assert.equal(warningConfirmMapping.gate, "level-polled while warning dialog is active");
const filePageMapping = model.actionMappings.find((mapping) => mapping.actionId === "file-page");
assert.equal(filePageMapping.nativeCommand.includes("IconFileSelection1"), true);
assert.equal(filePageMapping.gate.includes("native GTK file chooser"), true);
const macroPageMapping = model.actionMappings.find((mapping) => mapping.actionId === "macro-page");
assert.equal(macroPageMapping.webAction, "GMOCAPY_RUN_MACRO");
assert.equal(macroPageMapping.nativeCommand.includes("command.mdi"), true);
assert.equal(macroPageMapping.gate.includes("same as RUN_MDI"), true);
const toolEditorPageMapping = model.actionMappings.find((mapping) => mapping.actionId === "tool-editor-page");
assert.equal(toolEditorPageMapping.nativeCommand.includes("_show_tooledit_tab"), true);
assert.equal(toolEditorPageMapping.gate.includes("writeback is disabled"), true);
const nativePageMapping = model.actionMappings.find((mapping) => mapping.actionId === "native-page-diagnostic");
assert.equal(nativePageMapping.webAction, "GMOCAPY_NATIVE_PAGE");
assert.equal(nativePageMapping.gate.includes("do not emit fake GTK"), true);
const summary = createGmoccapyCommunicationSummary(model);
assert.equal(summary.apiName, "web-rtcp-5axis-gmoccapy-communication-model-summary");
assert.equal(summary.profileId, "gmoccapy-xyzab");
assert.equal(summary.nativePathCount, 6);
assert.equal(summary.nativePathCount, 10);
assert.equal(summary.startupStageCount, 9);
assert.equal(summary.actionCount, 19);
assert.equal(summary.actionCount, 26);
assert.equal(summary.pagePathCount, 4);
assert.equal(summary.postguiAfterHalcompReady, true);
assert.equal(summary.hasNativeNmlCommandPath, true);
assert.equal(summary.hasNativeHalPath, true);

View File

@@ -25,6 +25,7 @@ for (const groupName of [
"jog",
"override",
"operator-inputs",
"settings",
"tool",
"program",
"message",
@@ -44,7 +45,11 @@ assert.equal(groups.get("override").pins.includes("gmoccapy.spindle.reset-spindl
assert.equal(groups.get("operator-inputs").pins.includes("gmoccapy.ignore-limits"), true);
assert.equal(groups.get("operator-inputs").pins.includes("gmoccapy.optional-stop"), true);
assert.equal(groups.get("operator-inputs").pins.includes("gmoccapy.blockdelete"), true);
assert.equal(groups.get("settings").pins.includes("gmoccapy.unlock-settings"), true);
assert.equal(groups.get("tool").pins.includes("gmoccapy.toolchange-changed"), true);
assert.equal(groups.get("tool").pins.includes("gmoccapy.probeheight"), true);
assert.equal(groups.get("tool").pins.includes("gmoccapy.blockheight"), true);
assert.equal(groups.get("tool").pins.includes("gmoccapy.toolmeasurement"), true);
assert.equal(groups.get("program").pins.includes("gmoccapy.program.current-line"), true);
assert.equal(groups.get("message").pins.includes("gmoccapy.error"), true);
assert.equal(groups.get("spindle-postgui").pins.includes("gmoccapy.spindle_at_speed_led"), true);
@@ -90,14 +95,32 @@ assert.equal(model.hardwareButtons.bottomMain.find((button) => button.index ===
assert.equal(model.hardwareButtons.bottomMain.find((button) => button.index === 0).webDispatch.type, "HOME");
assert.equal(resolveGmoccapyHardwareButton("gmoccapy.v-button.button-4", model).webDispatch.mode, "auto");
assert.equal(resolveGmoccapyHardwareButton({ location: "bottom", index: 0 }, model).webDispatch.type, "HOME");
assert.equal(resolveGmoccapyHardwareButton({ location: "right", index: 6 }, model).webDispatch, undefined);
assert.equal(resolveGmoccapyHardwareButton({ location: "right", index: 6 }, model).webDispatch.pageId, "setup");
assert.equal(resolveGmoccapyHardwareButton("gmoccapy.h-button.button-9", model), null);
assert.equal(resolveGmoccapyHardwareButton({ location: "right", index: 5 }, model).webDispatch.type, "GMOCAPY_NATIVE_PAGE");
assert.equal(resolveGmoccapyHardwareButton({ location: "right", index: 5 }, model).webDispatch.pageId, "user-tabs");
assert.equal(resolveGmoccapyHardwareButton({ location: "bottom", index: 3 }, model).webDispatch.pageId, "tool-editor");
assert.equal(model.nativePages.filePage.nativeWidget, "IconFileSelection1");
assert.equal(model.nativePages.filePage.boundary, "native_GtkIconFileSelection_not_embedded_in_browser");
assert.equal(model.nativePages.macroPage.macros.length, 5);
assert.equal(model.nativePages.macroPage.macros.find((macro) => macro.name === "increment").args.length, 2);
assert.equal(model.nativePages.macroPage.boundary.includes("MDI"), true);
assert.equal(model.nativePages.toolEditorPage.editableInWeb, false);
assert.equal(model.nativePages.toolEditorPage.toolCount, 17);
assert.equal(model.nativePages.implementationMatrix.some((page) => page.pageId === "setup" && page.implementation === "diagnostic-only"), true);
assert.equal(model.nativePages.implementationMatrix.some((page) => page.pageId === "edit" && page.implementation === "native-only"), true);
assert.equal(model.nativePages.behavior.hardButtonsDoNotFakeNativePages, true);
assert.equal(model.nativePages.behavior.toolEditorKeepsIocontrolLoopback, true);
assert.equal(model.halPinActions.source.methods.includes("_optional_blocks"), true);
assert.equal(model.halPinActions.source.methods.includes("_blockdelete"), true);
assert.equal(model.halPinActions.source.methods.includes("_on_pin_jog_changed"), true);
assert.equal(model.halPinActions.source.methods.includes("_on_pin_incr_changed"), true);
assert.equal(model.halPinActions.source.methods.includes("_del_message_changed"), true);
assert.equal(model.halPinActions.source.methods.includes("_on_unlock_settings_changed"), true);
assert.equal(model.halPinActions.source.methods.includes("_check_toolmeasurement"), true);
assert.equal(model.halPinActions.source.methods.includes("_init_user_messages"), true);
assert.equal(model.halPinActions.jogPins.axes.length, 5);
assert.equal(model.halPinActions.jogPins.axes.find((entry) => entry.axis === "b").minus, "gmoccapy.jog.axis.jog-b-minus");
assert.equal(model.halPinActions.jogPins.increments.length, 6);
@@ -107,11 +130,13 @@ assert.equal(model.halPinActions.jogPins.increments[5].distance, 31.3563);
assert.equal(model.halPinActions.jogPins.turtlePin, "gmoccapy.jog.turtle-jog");
assert.equal(model.halPinActions.operatorPins.find((pin) => pin.pin === "gmoccapy.optional-stop").webState, "gmoccapyGui.optionalBlocks");
assert.equal(model.halPinActions.operatorPins.find((pin) => pin.pin === "gmoccapy.blockdelete").webState, "gmoccapyGui.optionalStop");
assert.equal(model.halPinActions.settingsPins.find((pin) => pin.pin === "gmoccapy.unlock-settings").prefDefault, "unlock_way=use");
assert.equal(model.halPinActions.overridePins.find((pin) => pin.target === "feed").countEnable, "gmoccapy.feed.feed-override.count-enable");
assert.equal(model.halPinActions.overridePins.find((pin) => pin.target === "spindle").reset, "gmoccapy.spindle.reset-spindle-override");
assert.equal(model.halPinActions.overridePins.find((pin) => pin.target === "jogVelocity").scalePref, "scale_jog_vel=140.4");
assert.equal(model.halPinActions.behavior.optionalStopPinCrossesToBlockDelete, true);
assert.equal(model.halPinActions.behavior.blockdeletePinCrossesToOptionalStop, true);
assert.equal(model.halPinActions.behavior.unlockSettingsLevelDriven, true);
assert.equal(model.halPinActions.behavior.jogAxisPinsPressRelease, true);
assert.equal(model.halPinActions.behavior.jogPinsUseTaskPolicyGate, true);
assert.equal(model.halPinActions.behavior.jogIncrementPinsRisingEdgeOnly, true);
@@ -124,6 +149,14 @@ assert.equal(model.halPinActions.messagePins.find((pin) => pin.pin === "gmoccapy
assert.equal(model.halPinActions.messagePins.find((pin) => pin.pin === "gmoccapy.warning-confirm").nativeCallback, "dialogs.warning_dialog periodic confirm_pin poll");
assert.equal(model.halPinActions.behavior.deleteMessageRisingEdgeOnly, true);
assert.equal(model.halPinActions.behavior.warningConfirmLevelPolled, true);
assert.equal(model.halPinActions.toolMeasurementPins.toolsensorConfigured, false);
assert.equal(model.halPinActions.toolMeasurementPins.pins.length, 5);
assert.equal(model.halPinActions.toolMeasurementPins.pins.find((pin) => pin.pin === "gmoccapy.toolmeasurement").direction, "HAL_OUT");
assert.equal(model.halPinActions.userMessages.configured, false);
assert.equal(model.halPinActions.userMessages.dynamicPinPatterns.includes("gmoccapy.messages.<pinname>-response"), true);
assert.equal(model.halPinActions.behavior.toolMeasurementPinsAreHalOut, true);
assert.equal(model.halPinActions.behavior.toolMeasurementDisabledWithoutToolsensor, true);
assert.equal(model.halPinActions.behavior.userMessagesDynamicIniOnly, true);
assert.equal(model.toolChange.strategy, "iocontrol-loopback");
assert.equal(model.toolChange.manualGmoccapyPinsConnected, false);
@@ -139,8 +172,8 @@ const summary = createGmoccapyHalSummary(model);
assert.equal(summary.apiName, "web-rtcp-5axis-gmoccapy-hal-model-summary");
assert.equal(summary.profileId, "gmoccapy-xyzab");
assert.equal(summary.halFileCount, 4);
assert.equal(summary.nativePinGroupCount, 8);
assert.equal(summary.nativePinCount >= 50, true);
assert.equal(summary.nativePinGroupCount, 9);
assert.equal(summary.nativePinCount >= 58, true);
assert.equal(summary.coreNetCount, 10);
assert.equal(summary.spindleNetCount, 4);
assert.equal(summary.postguiNetCount, 5);
@@ -152,16 +185,23 @@ assert.equal(summary.postguiToolUnlinkCount, 3);
assert.equal(summary.commentedManualToolChangeNetCount, 3);
assert.equal(summary.hardwareButtonVerticalCount, 7);
assert.equal(summary.hardwareButtonBottomMainCount, 4);
assert.equal(summary.mappedHardwareButtonCount, 7);
assert.equal(summary.mappedHardwareButtonCount, 6);
assert.equal(summary.hardwareButtonEdge, "rising-edge-only");
assert.equal(summary.hardwareButtonInsensitiveTarget, "ignored");
assert.equal(summary.operatorInputPinCount, 3);
assert.equal(summary.settingsInputPinCount, 1);
assert.equal(summary.overridePinTargetCount, 4);
assert.equal(summary.jogAxisPinCount, 10);
assert.equal(summary.jogIncrementPinCount, 6);
assert.equal(summary.messageInputPinCount, 3);
assert.equal(summary.toolMeasurementPinCount, 5);
assert.equal(summary.toolMeasurementEnabled, false);
assert.equal(summary.toolsensorConfigured, false);
assert.equal(summary.userMessagePinCount, 0);
assert.equal(summary.userMessagesConfigured, false);
assert.equal(summary.optionalStopPinCrossesToBlockDelete, true);
assert.equal(summary.blockdeletePinCrossesToOptionalStop, true);
assert.equal(summary.unlockSettingsLevelDriven, true);
assert.equal(summary.jogAxisPinsPressRelease, true);
assert.equal(summary.jogPinsUseTaskPolicyGate, true);
assert.equal(summary.jogIncrementPinsRisingEdgeOnly, true);
@@ -172,6 +212,21 @@ assert.equal(summary.directValueRequiresAnalogEnable, true);
assert.equal(summary.resetPinsRisingEdgeOnly, true);
assert.equal(summary.deleteMessageRisingEdgeOnly, true);
assert.equal(summary.warningConfirmLevelPolled, true);
assert.equal(summary.toolMeasurementPinsAreHalOut, true);
assert.equal(summary.toolMeasurementDisabledWithoutToolsensor, true);
assert.equal(summary.userMessagesDynamicIniOnly, true);
assert.equal(summary.nativePageCount, 12);
assert.equal(summary.implementedNativePageCount, 1);
assert.equal(summary.partialNativePageCount, 4);
assert.equal(summary.diagnosticOnlyNativePageCount, 5);
assert.equal(summary.nativeOnlyPageCount, 2);
assert.equal(summary.filePageImplementation, "partial");
assert.equal(summary.macroCount, 5);
assert.equal(summary.macroButtonsUseMdiGate, true);
assert.equal(summary.toolEditorImplementation, "diagnostic-only");
assert.equal(summary.toolEditorWritebackDisabled, true);
assert.equal(summary.hardButtonsDoNotFakeNativePages, true);
assert.equal(summary.unimplementedPagesDiagnosticOnly, true);
assert.equal(summary.semanticBoundary, model.semanticBoundary);
console.log("gmoccapy_hal_model_smoke=ok");

View File

@@ -215,7 +215,8 @@ assert.equal(hardButtonStore.getState().machine.allHomed, true);
hardButtonStore.dispatch({ type: "GMOCAPY_HARDWARE_BUTTON", pin: "gmoccapy.v-button.button-4", value: true });
assert.equal(hardButtonStore.getState().machine.mode, "auto");
hardButtonStore.dispatch({ type: "GMOCAPY_HARDWARE_BUTTON", pin: "gmoccapy.v-button.button-6", value: true });
assert.equal(hardButtonStore.getState().operatorMessage, "gmoccapy hardware button diagnostic-only: gmoccapy.v-button.button-6");
assert.equal(hardButtonStore.getState().gmoccapyGui.activeNativePage, "setup");
assert.equal(hardButtonStore.getState().operatorMessage, "gmoccapy native page diagnostic-only: setup");
hardButtonStore.dispatch({ type: "GMOCAPY_HARDWARE_BUTTON", pin: "gmoccapy.h-button.button-9", value: true });
assert.equal(hardButtonStore.getState().operatorMessage, "gmoccapy hardware button unmapped: gmoccapy.h-button.button-9");
@@ -231,6 +232,25 @@ assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL optional-stop
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.blockdelete", value: true });
assert.equal(halPinStore.getState().gmoccapyGui.optionalStop, true);
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL blockdelete -> optional stop on");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.unlock-settings", value: false });
assert.equal(halPinStore.getState().gmoccapyGui.settingsUnlockPin, false);
assert.equal(halPinStore.getState().gmoccapyGui.setupSensitive, true);
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL unlock-settings ignored: unlock_way is use");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.unlock-settings", value: false, halUnlockMode: true });
assert.equal(halPinStore.getState().gmoccapyGui.settingsUnlockMode, "hal");
assert.equal(halPinStore.getState().gmoccapyGui.setupSensitive, false);
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL unlock-settings disabled setup");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.unlock-settings", value: true });
assert.equal(halPinStore.getState().gmoccapyGui.settingsUnlockPin, true);
assert.equal(halPinStore.getState().gmoccapyGui.setupSensitive, true);
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL unlock-settings enabled setup");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.blockheight", value: 12.5 });
assert.equal(halPinStore.getState().gmoccapyGui.blockHeight, 12.5);
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL tool measurement output recorded; XYZAB has no [TOOLSENSOR]");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.toolmeasurement", value: true });
assert.equal(halPinStore.getState().gmoccapyGui.toolMeasurement, true);
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.messages.test", value: true });
assert.equal(halPinStore.getState().operatorMessage, "gmoccapy HAL user message pin unmapped: gmoccapy_XYZAB.ini defines no MESSAGE_* entries");
halPinStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.feed.feed-override.counts", value: 12 });
assert.equal(halPinStore.getState().feed.feedOverride, 100);
@@ -312,4 +332,41 @@ halMessageStore.dispatch({ type: "GMOCAPY_HAL_PIN", pin: "gmoccapy.warning-confi
assert.equal(halMessageStore.getState().gmoccapyGui.warningConfirm, false);
assert.equal(halMessageStore.getState().operatorMessage, "gmoccapy HAL warning-confirm cleared");
const nativePageStore = createSimulationStore();
nativePageStore.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" });
nativePageStore.dispatch({ type: "GMOCAPY_HARDWARE_BUTTON", pin: "gmoccapy.v-button.button-6", value: true });
assert.equal(nativePageStore.getState().gmoccapyGui.activeNativePage, "setup");
assert.equal(nativePageStore.getState().gmoccapyGui.nativePageMode, "diagnostic-only");
assert.equal(nativePageStore.getState().operatorMessage, "gmoccapy native page diagnostic-only: setup");
nativePageStore.dispatch({ type: "GMOCAPY_HARDWARE_BUTTON", pin: "gmoccapy.h-button.button-3", value: true });
assert.equal(nativePageStore.getState().gmoccapyGui.activeNativePage, "tool-editor");
assert.equal(nativePageStore.getState().operatorMessage, "gmoccapy native page diagnostic-only: tool-editor");
nativePageStore.dispatch({ type: "GMOCAPY_PAGE_ACTION", pageId: "file-load", actionId: "open" });
assert.equal(nativePageStore.getState().gmoccapyGui.filePageStatus, "open");
assert.equal(nativePageStore.getState().operatorMessage.includes("native Gtk chooser diagnostic"), true);
nativePageStore.dispatch({ type: "GMOCAPY_TOOL_EDITOR_ACTION", actionId: "save" });
assert.equal(nativePageStore.getState().gmoccapyGui.toolEditorStatus, "writeback-blocked");
assert.equal(nativePageStore.getState().operatorMessage.includes("does not write tool.tbl"), true);
const macroBlockedStore = createSimulationStore();
macroBlockedStore.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" });
macroBlockedStore.dispatch({ type: "GMOCAPY_RUN_MACRO", name: "halo_world" });
assert.equal(macroBlockedStore.getState().gmoccapyGui.macroLastName, "halo_world");
assert.equal(macroBlockedStore.getState().operatorMessage.includes("gmoccapy macro blocked"), true);
const macroReadyStore = createSimulationStore();
macroReadyStore.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" });
macroReadyStore.dispatch({ type: "TOGGLE_POWER" });
macroReadyStore.dispatch({ type: "HOME" });
macroReadyStore.dispatch({ type: "SET_MODE", mode: "mdi" });
macroReadyStore.dispatch({
type: "GMOCAPY_RUN_MACRO",
name: "increment",
args: { xinc: 1.25, yinc: 2.5 },
});
assert.equal(macroReadyStore.getState().gmoccapyGui.activeNativePage, "mdi-macros");
assert.equal(macroReadyStore.getState().gmoccapyGui.macroLastCommand, "O<increment> call [1.25] [2.5]");
assert.equal(macroReadyStore.getState().machine.mdiCommand, "O<INCREMENT> CALL [1.25] [2.5]");
assert.equal(macroReadyStore.getState().operatorMessage, "gmoccapy macro MDI O<increment> call [1.25] [2.5]");
console.log("gmoccapy_xyzab_gates_smoke=ok");

View File

@@ -62,12 +62,37 @@ assert.equal(profile.halPins.includes("gmoccapy.jog.turtle-jog"), true);
assert.equal(profile.halPins.includes("gmoccapy.ignore-limits"), true);
assert.equal(profile.halPins.includes("gmoccapy.optional-stop"), true);
assert.equal(profile.halPins.includes("gmoccapy.blockdelete"), true);
assert.equal(profile.halPins.includes("gmoccapy.unlock-settings"), true);
assert.equal(profile.halPins.includes("gmoccapy.feed.feed-override.count-enable"), true);
assert.equal(profile.halPins.includes("gmoccapy.feed.reset-feed-override"), true);
assert.equal(profile.halPins.includes("gmoccapy.spindle.reset-spindle-override"), true);
assert.equal(profile.halPins.includes("gmoccapy.jog.jog-velocity.direct-value"), true);
assert.equal(profile.halPins.includes("gmoccapy.delete-message"), true);
assert.equal(profile.halPins.includes("gmoccapy.warning-confirm"), true);
assert.equal(profile.halPins.includes("gmoccapy.probeheight"), true);
assert.equal(profile.halPins.includes("gmoccapy.blockheight"), true);
assert.equal(profile.halPins.includes("gmoccapy.toolmeasurement"), true);
assert.equal(profile.halPins.includes("gmoccapy.searchvel"), true);
assert.equal(profile.halPins.includes("gmoccapy.probevel"), true);
assert.equal(profile.halPins.includes("gmoccapy.toolchange-confirm"), true);
assert.equal(profile.toolsensor.configured, false);
assert.equal(profile.toolsensor.useToolMeasurementPref, false);
assert.equal(profile.toolsensor.blockHeightPref, 0);
assert.equal(profile.toolsensor.reason.includes("no [TOOLSENSOR]"), true);
assert.equal(profile.userMessages.configured, false);
assert.equal(profile.userMessages.count, 0);
assert.equal(profile.userMessages.reason.includes("no [DISPLAY] MESSAGE_*"), true);
assert.equal(profile.nativePages.filePage.configured, true);
assert.equal(profile.nativePages.filePage.programPrefix, "../../nc_files/");
assert.equal(profile.nativePages.filePage.webImplementation, "partial");
assert.equal(profile.nativePages.macroPage.configured, true);
assert.equal(profile.nativePages.macroPage.count, 5);
assert.equal(profile.nativePages.macroPage.macros.find((macro) => macro.name === "increment").args.length, 2);
assert.equal(profile.nativePages.macroPage.macros.find((macro) => macro.name === "go_to_position").command, "O<go_to_position> call [X-pos] [Y-pos] [Z-pos]");
assert.equal(profile.nativePages.toolEditor.configured, true);
assert.equal(profile.nativePages.toolEditor.writebackEnabled, false);
assert.equal(profile.nativePages.toolEditor.toolCount, 17);
assert.equal(profile.nativePages.implementationMatrixBoundary.includes("diagnostic-only"), true);
assert.equal(profile.toolTable.toolCount, 17);
assert.equal(profile.macros.includes("go_to_position X-pos Y-pos Z-pos"), true);
assert.equal(profile.panelSchema.id, "gmoccapy-xyzab-hal-component");