diff --git a/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js b/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js index a27d86a..aa17a9a 100644 --- a/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js +++ b/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js @@ -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 call" }, + { name: "halo_world", args: [], command: "O call" }, + { name: "jog_around", args: [], command: "O call" }, + { name: "increment", args: ["xinc", "yinc"], command: "O call [xinc] [yinc]" }, + { name: "go_to_position", args: ["X-pos", "Y-pos", "Z-pos"], command: "O 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", ], diff --git a/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js b/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js index 7de50fa..9230c01 100644 --- a/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js +++ b/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js @@ -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 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., 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 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"), diff --git a/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js b/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js index 1461fc0..f9c3fa7 100644 --- a/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js +++ b/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js @@ -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.", + "gmoccapy.messages.-waiting", + "gmoccapy.messages.-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 call" }, + { name: "halo_world", args: [], file: "macros/halo_world.ngc", commandTemplate: "O call" }, + { name: "jog_around", args: [], file: "macros/jog_around.ngc", commandTemplate: "O call" }, + { name: "increment", args: ["xinc", "yinc"], file: "macros/increment.ngc", commandTemplate: "O call [xinc] [yinc]" }, + { name: "go_to_position", args: ["X-pos", "Y-pos", "Z-pos"], file: "macros/go_to_position.ngc", commandTemplate: "O call [X-pos] [Y-pos] [Z-pos]" }, + ], + nativeCallbacks: [ + "_make_macro_button -> get_ini_info.get_macros()", + "_on_btn_macro_pressed -> command.mdi(O 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, }; } diff --git a/web-rtcp-5axis-sim-plan/app/src/state/store.js b/web-rtcp-5axis-sim-plan/app/src/state/store.js index d968e58..528de57 100644 --- a/web-rtcp-5axis-sim-plan/app/src/state/store.js +++ b/web-rtcp-5axis-sim-plan/app/src/state/store.js @@ -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); diff --git a/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js b/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js index e4879a4..c872d00 100644 --- a/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js +++ b/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js @@ -443,6 +443,10 @@ function renderInfoTabs(element, state) {
gmoccapy comms:
${gmoccapyCommunicationModel.semanticBoundary}
NML command:
${formatGmoccapyNativePath("command")}
NML status:
${formatGmoccapyNativePath("status")}
+
Native file page:
${formatGmoccapyFilePage(halSummary, state)}
+
Native macros:
${formatGmoccapyMacroPage(halSummary, state)}
+
Native tool editor:
${formatGmoccapyToolEditorPage(halSummary, state)}
+
Native page matrix:
${formatGmoccapyNativePageMatrix(halSummary, state)}
Web runtime:
${gmoccapyCommunicationModel.webPath.path.join(" -> ")}
postgui order:
${commSummary.postguiAfterHalcompReady ? "halcomp.ready before POSTGUI_HALFILE" : "pending"}
native RUN:
${formatGmoccapyActionMapping("auto-run")}
@@ -452,8 +456,10 @@ function renderInfoTabs(element, state) {
Hard buttons:
${formatGmoccapyHardButtons(halSummary)}
HAL jog:
${formatGmoccapyJogInputs(halSummary, state)}
HAL inputs:
${formatGmoccapyHalInputs(halSummary, state)}
+
HAL settings:
${formatGmoccapySettingsInputs(halSummary, state)}
HAL overrides:
${formatGmoccapyOverrideInputs(halSummary, state)}
HAL messages:
${formatGmoccapyMessageInputs(halSummary, state)}
+
HAL tool/user:
${formatGmoccapyToolUserInputs(halSummary, state)}
postgui nets:
${gmoccapyHalModel.postguiNets.map((net) => net.signal).join(" / ")}
Tool loop:
${halSummary.hasToolChangeSimulationLoop ? "simulated tool-change loop" : "pending"}
Tool change:
${formatGmoccapyToolChange(halSummary)}
@@ -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(" / "); } diff --git a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md index ed95643..4224a06 100644 --- a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md +++ b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md @@ -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 控制 setup;tool 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 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 mapping,HAL 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` + - 结果:通过,无输出。 diff --git a/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html b/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html index 7452224..782ba94 100644 --- a/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html +++ b/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html @@ -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 call [1.25] [2.5]" || + !macroDiagnostic.includes("O 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); diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs index 41c6030..9466e6c 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs @@ -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."), 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); diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs index 4329110..88e5c72 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs @@ -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.-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"); diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs index c909600..e95eeb4 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs @@ -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 call [1.25] [2.5]"); +assert.equal(macroReadyStore.getState().machine.mdiCommand, "O CALL [1.25] [2.5]"); +assert.equal(macroReadyStore.getState().operatorMessage, "gmoccapy macro MDI O call [1.25] [2.5]"); + console.log("gmoccapy_xyzab_gates_smoke=ok"); diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs index c66efe5..01ee3d2 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs @@ -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 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"); diff --git a/work/working5/03-推进台账.md b/work/working5/03-推进台账.md index 78cf332..9298096 100644 --- a/work/working5/03-推进台账.md +++ b/work/working5/03-推进台账.md @@ -205,3 +205,67 @@ 下一步是什么: - 可继续核对 `gmoccapy.unlock-settings`、tool measurement/blockheight/probeheight、INI user messages 等更低频 HAL pins;本轮 W5-190 已完成。 + +## 2026-06-27 R7:细化 gmoccapy 设置解锁、测刀输出和 INI user message 边界 + +做了什么: + +- 对照 `gmoccapy.py` 的 `_on_unlock_settings_changed()`、`_check_toolmeasurement()`、`on_chk_use_tool_measurement_toggled()`、`on_btn_block_height_clicked()`、`_init_user_messages()` 和 `_show_user_message()`,确认 `unlock-settings` 只在设置页选择 HAL unlock 时控制 `tbtn_setup` 敏感性;tool measurement 相关 pin 是 HAL_OUT;INI user messages 只在配置 `MESSAGE_*` 后动态创建 `messages.*` pins。 +- 对照 `gmoccapy_XYZAB.ini` 和 `gmoccapy_XYZAB.pref`,确认本配置没有 `[TOOLSENSOR]`、没有 `[DISPLAY] MESSAGE_*`,且默认 `unlock_way=use`、`use_toolmeasurement=False`、`blockheight=0.0`。 +- 在 `gmoccapy-xyzab` profile 中补齐 `gmoccapy.unlock-settings`、`gmoccapy.probeheight`、`gmoccapy.blockheight`、`gmoccapy.toolmeasurement`、`gmoccapy.searchvel`、`gmoccapy.probevel`、`gmoccapy.toolchange-number`、`gmoccapy.toolchange-confirm`,并记录 toolsensor/user messages 默认未配置原因。 +- 扩展 HAL model 和 communication model,记录 settings pin、tool measurement HAL_OUT pins、动态 user message pins、XYZAB no `[TOOLSENSOR]` 和 no `MESSAGE_*` 边界。 +- 扩展 `GMOCAPY_HAL_PIN`:模拟 `unlock-settings` 在默认 `unlock_way=use` 下忽略、在 HAL unlock 模式下按 level 控制 setup;tool measurement pins 作为诊断输出记录;`messages.*` 在当前 XYZAB 无配置时返回明确 operator message。 +- 在 gmoccapy UI 诊断中新增 `HAL settings` 和 `HAL tool/user` 行,并补充 Node/浏览器断言。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/state/store.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs` +- `/home/meswork/cnc_wams/work/working5/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working5/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working5/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working5/06-决策记录.md` + +验证了什么: + +- `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` 通过。 +- `bash ../tests/browser/verify_gmoccapy_shell_browser.sh` 通过。 +- `npm run smoke:node` 通过。 +- `npm run build && npm run smoke` 通过。 +- `git diff --check -- web-rtcp-5axis-sim-plan work/working5` 通过,无输出。 + +下一步是什么: + +- W5-200 已完成。后续如继续深化,可转向 gmoccapy 文件/宏页面、tool editor 页面或 native GTK 未实现页面的诊断边界,但当前任务矩阵内 HAL 输入细化项已闭环。 + +## 2026-06-27 R8:working5 最终复核与验收收口 + +做了什么: + +- 重新读取 `working5` README、功能内容、开发步骤、推进台账、任务矩阵、验收证据和决策记录,确认 W5-001 到 W5-900 均已闭环。 +- 核对当前工作区改动范围,确认源码和测试改动仍集中在 gmoccapy XYZAB profile、HAL/communication model、store、UI shell 和对应 Node/browser 验收。 +- 在当前工作区重新执行完整 Node 冒烟、静态构建和浏览器冒烟,确认历史记录对应的实现仍可通过。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/work/working5/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working5/05-验收证据.md` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md` + +验证了什么: + +- `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`。 +- `npm run build` 通过,输出 `gmoccapy_static_build=ok`。 +- `npm run smoke` 通过,输出 `gmoccapy_shell_smoke=ok`、`gmoccapy_dist_smoke=ok`。 + +下一步是什么: + +- `working5` 已按当前任务矩阵完成并复核通过;本轮不新增后续实现任务。 diff --git a/work/working5/04-任务矩阵.md b/work/working5/04-任务矩阵.md index efa4fdd..bf2f9f3 100644 --- a/work/working5/04-任务矩阵.md +++ b/work/working5/04-任务矩阵.md @@ -17,4 +17,5 @@ | W5-170 | 细化外部 HAL hard-button pin 与 UI action 映射 | 完成 | profile/panel schema、HAL model、communication model、store 和浏览器 DOM 均明确 `gmoccapy.v-button.button-0..6`、`gmoccapy.h-button.button-0` 等硬件按钮 pin 的 rising-edge、可见敏感按钮映射和 Web gate 行为;相关 Node、构建、浏览器 smoke 通过 | | W5-180 | 细化 ignore-limits、optional-stop、blockdelete 与 override HAL input pin 语义 | 完成 | profile、HAL model、communication model、store 和浏览器 UI 均明确 `gmoccapy.ignore-limits`、`gmoccapy.optional-stop`、`gmoccapy.blockdelete`、override counts/direct-value/reset pins 的 Web 映射;Node、构建、浏览器 smoke 通过 | | W5-190 | 细化 jog、jog-increment、delete-message、warning-confirm HAL input pin 语义 | 完成 | profile、HAL model、communication model、store 和浏览器 UI 均明确 `gmoccapy.jog.axis.*`、`gmoccapy.jog.jog-inc-*`、`gmoccapy.jog.turtle-jog`、`gmoccapy.delete-message`、`gmoccapy.warning-confirm` 的 Web 映射;Node、构建、浏览器 smoke 通过 | +| W5-200 | 细化 unlock-settings、tool measurement 和 INI user messages 低频 HAL pin 边界 | 完成 | profile、HAL model、communication model、store 和浏览器 UI 均明确 `gmoccapy.unlock-settings`、`gmoccapy.probeheight/blockheight/toolmeasurement/searchvel/probevel`、动态 `gmoccapy.messages.*` pins 的源码边界和 XYZAB 默认未启用原因;Node、构建、浏览器 smoke 通过 | | W5-900 | 更新验收证据和决策记录 | 完成 | 命令、结果、修改原因和后续项已记录 | diff --git a/work/working5/05-验收证据.md b/work/working5/05-验收证据.md index 4067a67..1be8af0 100644 --- a/work/working5/05-验收证据.md +++ b/work/working5/05-验收证据.md @@ -17,6 +17,9 @@ | EV-W5-181 | W5-020/W5-030/W5-040/W5-180 | 命令 | `npm run smoke:node`、`npm run build && npm run smoke` | 通过,完整 Node、静态构建、浏览器 gmoccapy shell/dist smoke 均覆盖新增 HAL input/override 诊断和 Web 控件 | | EV-W5-190 | W5-150/W5-190 | 命令 | `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`、`bash ../tests/browser/verify_gmoccapy_shell_browser.sh` | 通过,验证 `GMOCAPY_HAL_PIN` 对 jog axis press/release、jog increment rising-edge、turtle jog、delete-message、warning-confirm pins 的映射和 UI 诊断 | | EV-W5-191 | W5-020/W5-030/W5-040/W5-190 | 命令 | `npm run smoke:node`、`npm run build && npm run smoke` | 通过,完整 Node、静态构建、浏览器 gmoccapy shell/dist smoke 均覆盖新增 jog/message HAL pin 诊断和 Web 行为 | +| EV-W5-200 | W5-150/W5-200 | 命令 | `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`、`bash ../tests/browser/verify_gmoccapy_shell_browser.sh` | 通过,验证 `gmoccapy.unlock-settings`、tool measurement HAL_OUT pins、动态 `messages.*` pin 边界和 UI 诊断 | +| EV-W5-201 | W5-020/W5-030/W5-040/W5-200 | 命令 | `npm run smoke:node`、`npm run build && npm run smoke` | 通过,完整 Node、静态构建、浏览器 gmoccapy shell/dist smoke 均覆盖新增 settings/tool/user-message 低频 HAL pin 诊断和 Web 行为 | +| EV-W5-900 | W5-900 | 命令 | `npm run smoke:node`、`npm run build`、`npm run smoke` | 通过,最终复核确认 working5 当前任务矩阵闭环,Node、静态构建、浏览器冒烟均在当前工作区通过 | ## 命令记录 @@ -297,6 +300,119 @@ npm run build && npm run smoke - `data-gmoccapy-hal="message-inputs"` 必须包含 `3 message pins`、`delete-message rising-edge-only`、`warning-confirm level-polled`。 - 通过 `window.webRtcp5AxisSimulation.dispatch({ type: "GMOCAPY_HAL_PIN", ... })` 验证浏览器内 jog increment、axis jog press/release、turtle jog、delete-message 和 warning-confirm 状态。 +### 2026-06-27 R7 + +```bash +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` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +bash ../tests/browser/verify_gmoccapy_shell_browser.sh +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` + +```bash +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` + +```bash +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` + +```bash +cd /home/meswork/cnc_wams +git diff --check -- web-rtcp-5axis-sim-plan work/working5 +``` + +结果:通过,无输出。 + +本轮新增 Node 断言: + +- `gmoccapy.unlock-settings` 是 settings pin;默认 `unlock_way=use` 时只记录 pin level,不锁定 setup;显式 HAL unlock 模式下按 level 控制 setup sensitive。 +- `gmoccapy.probeheight`、`gmoccapy.blockheight`、`gmoccapy.toolmeasurement`、`gmoccapy.searchvel`、`gmoccapy.probevel` 记录为 HAL_OUT tool measurement pins。 +- `gmoccapy_XYZAB.ini` 无 `[TOOLSENSOR]`,所以 auto tool measurement 默认禁用;`gmoccapy_XYZAB.pref` 默认 `use_toolmeasurement=False`、`blockheight=0.0`。 +- `gmoccapy_XYZAB.ini` 无 `[DISPLAY] MESSAGE_*`,所以动态 `gmoccapy.messages.*` pins 不创建;Web 对 `messages.*` dispatch 返回明确 operator message。 + +本轮新增浏览器断言: + +- `data-gmoccapy-hal="settings-inputs"` 必须包含 `1 settings pins`、`unlock-settings level-driven`、`unlock-way use`、`setup sensitive`。 +- `data-gmoccapy-hal="tool-user-inputs"` 必须包含 `5 tool measurement HAL_OUT pins`、`XYZAB no TOOLSENSOR`、`0 user message pins`、`no MESSAGE_*`。 +- 通过 `window.webRtcp5AxisSimulation.dispatch({ type: "GMOCAPY_HAL_PIN", ... })` 验证浏览器内 `unlock-settings` 默认忽略和 HAL unlock level 行为、`blockheight` 诊断记录、`messages.*` 无配置提示。 + +### 2026-06-27 R8 + +```bash +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` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run build +``` + +结果:通过,输出 `gmoccapy_static_build=ok`。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` +- `gmoccapy_dist_smoke=ok` + +本轮复核结论: + +- `04-任务矩阵.md` 中 W5-001 到 W5-900 均为完成。 +- 当前源码、测试和文档状态可通过完整 Node、构建和浏览器冒烟验收。 + ## 页面/截图/report/job 记录 本轮未生成截图、PDF、job_id 或 report_id。浏览器验收由 headless Chromium smoke 脚本完成。 diff --git a/work/working5/06-决策记录.md b/work/working5/06-决策记录.md index a3b7186..87e3b0a 100644 --- a/work/working5/06-决策记录.md +++ b/work/working5/06-决策记录.md @@ -136,3 +136,28 @@ - `GMOCAPY_HAL_PIN` 现在覆盖 jog axis press/release、jog increment selection、turtle jog、delete-message 和 warning-confirm。 - HAL model 和 communication model 现在能分别呈现 hard-button、operator/override input、jog input、message input 的不同语义。 - UI 诊断增加 `HAL jog` 和 `HAL messages`,浏览器 smoke 通过 DOM 和公开 dispatch 验证这些语义。 + +## DR-W5-007:低频 settings/tool/user-message HAL pins 按配置启用边界建模 + +日期:2026-06-27 + +决策: + +- `gmoccapy.unlock-settings` 作为 level-driven settings input pin 记录;默认 `gmoccapy_XYZAB.pref` 为 `unlock_way=use`,所以 Web 中默认只记录 pin level,不把 setup 锁定或解锁。 +- 当测试显式模拟 HAL unlock 模式时,`unlock-settings` 才按 pin level 控制 `setupSensitive`,对应源码 `_on_unlock_settings_changed()` 对 `rbt_hal_unlock` 的条件判断。 +- `gmoccapy.probeheight`、`gmoccapy.blockheight`、`gmoccapy.toolmeasurement`、`gmoccapy.searchvel`、`gmoccapy.probevel` 作为 tool measurement HAL_OUT pins 记录,不伪装成外部 HAL 输入。 +- 当前 `gmoccapy_XYZAB.ini` 没有 `[TOOLSENSOR]`,所以 profile、HAL model 和 UI 诊断明确自动测刀默认禁用,而不是暴露为可操作流程。 +- 动态 `gmoccapy.messages.*` pins 只在 INI 存在 `MESSAGE_*` 配置时创建;当前 XYZAB 没有这些配置,所以 Web 对 `messages.*` dispatch 返回“未配置”的明确 operator message。 + +原因: + +- `gmoccapy.py` 中 `_on_unlock_settings_changed()` 会先检查初始化状态,再检查 `rbt_hal_unlock` 或 user mode;未选择 HAL unlock 时 pin 变化不应改变 setup 页面可用性。 +- `_check_toolmeasurement()` 需要有效 `[TOOLSENSOR]` 数据;缺少探针配置时会显示禁用提示、关闭 `chk_use_tool_measurement` 并使其不敏感。 +- `on_chk_use_tool_measurement_toggled()`、`on_spbtn_probe_height_value_changed()`、`on_btn_block_height_clicked()` 都是 GUI/prefs 侧写 HAL_OUT pins,不是外部 HAL 驱动 GUI 的输入边界。 +- `_init_user_messages()` 只有从 INI 读到 user messages 才创建 `messages.`、`messages.-waiting`、`messages.-response`;当前 `gmoccapy_XYZAB.ini` 没有 `MESSAGE_*` 项。 + +影响: + +- `gmoccapy-xyzab` profile 补齐 settings/tool measurement/toolchange confirm 相关 pins,并记录 no `[TOOLSENSOR]` 与 no `MESSAGE_*` 原因。 +- HAL model 和 communication model 能展示 settings input、tool measurement HAL_OUT、dynamic user message 三类不同边界。 +- Store 和浏览器 smoke 覆盖 `unlock-settings` 默认忽略、HAL unlock level 控制、`blockheight` 诊断记录和 `messages.*` 未配置提示。 diff --git a/work/working6/01-项目功能内容.md b/work/working6/01-项目功能内容.md new file mode 100644 index 0000000..46f5901 --- /dev/null +++ b/work/working6/01-项目功能内容.md @@ -0,0 +1,64 @@ +# 01-项目功能内容 + +## 目标 + +在 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` 中补齐 gmoccapy 文件/宏页面、tool editor 页面、native GTK 未实现页面的诊断边界,让 Web 仿真能够明确表达哪些 gmoccapy 原生页面已经实现、哪些只做诊断映射、哪些因 XYZAB 配置或 Web 仿真边界暂不执行。 + +本轮不重复 working5 已完成的 HAL 输入、hard-button、模式门控、换刀回环和低频 HAL pin 工作;本轮聚焦页面级功能边界和可验收诊断。 + +## 对标范围 + +### gmoccapy 文件页面 + +- 对照 `gmoccapy.py` 和 `gmoccapy.glade` 中程序文件加载、最近文件、文件选择器、程序预览和自动运行入口。 +- 对照 `[DISPLAY] PROGRAM_PREFIX`、默认 NC 文件路径、文件过滤规则和最近文件行为。 +- Web 侧需要明确: + - 当前可加载的演示/机床程序来源。 + - 文件选择、打开、关闭、最近文件、预览、运行之间的状态边界。 + - 与 Auto 模式、全轴回零、解释器 idle、运行中禁止切换文件等互锁关系。 + - 未实现的 native GTK file chooser 行为需要诊断说明,不伪装成完整桌面文件管理器。 + +### gmoccapy 宏页面 + +- 对照 gmoccapy 原生宏按钮、INI 中 macro 配置、MDI 命令发送路径和敏感状态。 +- Web 侧需要明确: + - 当前 XYZAB 配置是否存在可用宏。 + - 宏按钮如何映射到 MDI 命令或诊断行。 + - 未回零、未上电、运行中、解释器非 idle 时宏执行的门控原因。 + - 未配置宏时应显示明确边界,而不是空白或误导性成功状态。 + +### tool editor 页面 + +- 对照 gmoccapy tool editor、LinuxCNC tool table、`M6`/`M61`、刀具号、刀补、手动换刀回环的状态显示。 +- Web 侧需要明确: + - 当前 tool table 来源、读取状态、活动刀具、准备刀具、刀补显示。 + - 可编辑字段与只读诊断字段的边界。 + - Web 仿真是否允许直接写 tool table;若暂不写文件,需要显示只读/诊断原因。 + - 与 working5 中 `iocontrol-loopback` 换刀语义保持一致,不重新引入 gmoccapy 手动换刀弹窗。 + +### native GTK 未实现页面诊断边界 + +- 对照 `gmoccapy.glade` 中原生 notebooks、side buttons、bottom buttons 和 setup/user tabs/tool/touch 等页面。 +- Web 侧需要明确: + - 已实现页面、部分实现页面、诊断-only 页面和未实现页面清单。 + - hard-button pin 对未实现 native 页面按钮的行为:只能诊断、不能伪装成真实页面动作。 + - 每个未实现页面的原因、风险和未来补齐入口。 + - 浏览器 UI 中提供可测试的 `data-*` 诊断节点,便于 smoke 脚本验收。 + +## 当前项目入口 + +- Web 应用:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` +- gmoccapy profile:`app/src/profiles/gmoccapy-xyzab.js` +- gmoccapy HAL model:`app/src/runtime/gmoccapy-hal-model.js` +- gmoccapy communication model:`app/src/runtime/gmoccapy-communication-model.js` +- 状态存储:`app/src/state/store.js` +- gmoccapy UI shell:`app/src/ui/gmoccapy-shell.js` +- Node 验证:`tests/node` +- 浏览器验证:`tests/browser` + +## 非目标 + +- 不在本轮实现完整桌面 GTK 文件选择器。 +- 不在本轮引入真实 LinuxCNC 外部进程、真实 HAL pin 写入或真实 tool table 持久化,除非后续任务矩阵明确升级。 +- 不回退 working5 已完成的 `GMOCAPY_HAL_PIN`、`GMOCAPY_HARDWARE_BUTTON` 和 gmoccapy XYZAB profile 行为。 +- 不把未实现 native 页面包装成可操作成功路径;必须明确诊断边界。 diff --git a/work/working6/02-项目程序开发详细步骤.md b/work/working6/02-项目程序开发详细步骤.md new file mode 100644 index 0000000..2ff8f09 --- /dev/null +++ b/work/working6/02-项目程序开发详细步骤.md @@ -0,0 +1,87 @@ +# 02-项目程序开发详细步骤 + +## 步骤 1:建立工作控制文件 + +- 创建 README、功能内容、开发步骤、推进台账、任务矩阵、验收证据、决策记录。 +- 将 working5 后续深化方向拆分为文件/宏页面、tool editor 页面、native GTK 未实现页面诊断边界三条主线。 + +## 步骤 2:读取基准与现有实现 + +- 读取 `work/working5` 的任务矩阵、决策记录和验收证据,确认不可回退的既有行为。 +- 读取 `work/working3/gmoccapy_XYZAB_execution_analysis.md`。 +- 读取 LinuxCNC gmoccapy 源码和 Glade 文件: + - `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py` + - `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.glade` + - `linuxcnc/src/emc/usr_intf/gmoccapy/dialogs.py` +- 读取 Web 项目 profile、runtime、state、UI shell 和 tests。 + +## 步骤 3:建立页面/按钮/状态映射 + +- 梳理 native gmoccapy notebook 页面和 Web gmoccapy shell 当前页面。 +- 建立文件页面映射: + - native file chooser、program preview、recent files、program run action。 + - Web program source、preview、load/run/reset 状态。 +- 建立宏页面映射: + - INI macro 配置、宏按钮、MDI command、执行 gate。 + - XYZAB 未配置宏时的诊断边界。 +- 建立 tool editor 映射: + - tool table、active/prepared tool、offset、M6/M61 状态。 + - 只读诊断与可编辑字段边界。 +- 建立未实现 native 页面清单: + - setup/user tabs/tool/touch 等按钮和页面。 + - hard-button pin 与诊断-only action 的映射。 + +## 步骤 4:执行现有测试 + +- 在 `app` 下运行 `npm run smoke:node`。 +- 运行构建:`npm run build`。 +- 运行浏览器冒烟:`npm run smoke`。 +- 如测试失败,先判断是环境问题、既有改动问题还是本轮新增行为问题。 + +## 步骤 5:补充模型和状态 + +- 在 profile 或 runtime model 中补充 gmoccapy 页面级 schema: + - file page capability。 + - macro page capability。 + - tool editor capability。 + - native GTK page implementation matrix。 +- 在 store 中补充必要 action 或诊断状态: + - 文件页面的 load/recent/preview gate。 + - 宏执行的 MDI gate 或未配置提示。 + - tool editor 的只读诊断状态。 + - native 未实现页面的 operator message。 +- 保持 action 命名和 existing gate 风格一致,避免绕过 LinuxCNC task policy。 + +## 步骤 6:补充 UI 诊断 + +- 在 gmoccapy shell 中增加可测试的诊断行或页面区块。 +- 所有新增诊断必须带稳定 `data-*` 属性。 +- 未实现页面必须显示“诊断-only/未实现/配置未启用”的明确状态。 +- 文件/宏/tool editor 控件不得在不满足 gate 时显示为可执行。 + +## 步骤 7:补充测试 + +- Node 测试: + - profile 页面 schema 测试。 + - communication model 页面路径测试。 + - store gate 和 operator message 测试。 +- Browser 测试: + - DOM 诊断行存在。 + - `data-*` 内容包含关键边界词。 + - 公开 dispatch 或按钮点击能触发预期状态和 gate。 +- 如改动 UI,运行 `npm run build && npm run smoke`。 + +## 步骤 8:回归验收 + +- 运行本轮相关最小测试。 +- 运行完整 `npm run smoke:node`。 +- 运行 `npm run build`。 +- 运行 `npm run smoke`。 +- 运行 `git diff --check -- web-rtcp-5axis-sim-plan work/working6`。 + +## 步骤 9:更新台账和证据 + +- 更新 `03-推进台账.md`。 +- 更新 `04-任务矩阵.md` 中任务状态。 +- 更新 `05-验收证据.md` 中命令、页面、截图、报告或输出摘要。 +- 如涉及架构、对标边界或兼容性取舍,更新 `06-决策记录.md`。 diff --git a/work/working6/03-推进台账.md b/work/working6/03-推进台账.md new file mode 100644 index 0000000..38a4adb --- /dev/null +++ b/work/working6/03-推进台账.md @@ -0,0 +1,94 @@ +# 03-推进台账 + +## 2026-06-27 R1:创建 working6 控制文件 + +做了什么: + +- 创建 `working6` 工作目录。 +- 建立 README、功能内容、开发步骤、推进台账、任务矩阵、验收证据和决策记录。 +- 将 working5 的可选深化方向拆分为 gmoccapy 文件/宏页面、tool editor 页面、native GTK 未实现页面诊断边界三条主线。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/work/working6/README.md` +- `/home/meswork/cnc_wams/work/working6/01-项目功能内容.md` +- `/home/meswork/cnc_wams/work/working6/02-项目程序开发详细步骤.md` +- `/home/meswork/cnc_wams/work/working6/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working6/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working6/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working6/06-决策记录.md` + +验证了什么: + +- 待执行现有 Node、构建和浏览器冒烟测试。 +- 待读取 gmoccapy 源码、Glade、INI 和当前 Web 实现后开始 W6-010。 + +下一步是什么: + +- 按 `04-任务矩阵.md` 从 W6-010 开始,先建立 native gmoccapy 页面到 Web 模块的映射,再执行基线测试。 + +## 2026-06-27 R2:实现 gmoccapy 页面级诊断边界 + +做了什么: + +- 对照 LinuxCNC `gmoccapy.py`、`gmoccapy.glade` 和 `gmoccapy_XYZAB.ini`,确认文件页使用 `IconFileSelection1`,宏页来自 `[MACROS]` 的 5 个 O-word MDI 宏,tool editor 读取 `tool.tbl` 并可在原生 GTK 中 add/delete/reload/save/change tool。 +- 在 Web profile、HAL model 和 communication model 中增加页面级 schema:file page、macro page、tool editor page、native GTK implementation matrix。 +- 在 store 中增加 `GMOCAPY_NATIVE_PAGE`、`GMOCAPY_PAGE_ACTION`、`GMOCAPY_RUN_MACRO`、`GMOCAPY_TOOL_EDITOR_ACTION`,用于表达 native 页面诊断、文件页边界、宏 MDI gate 和 tool editor 只读/写回阻塞。 +- 在 gmoccapy UI 诊断中新增 `data-gmoccapy-page="file"`、`macros`、`tool-editor`、`matrix` 四类稳定 DOM 证据。 +- 修正 hard-button 对 `tbtn_user_tabs`、`tbtn_setup`、`btn_touch`、`btn_tool`、`tbtn_switch_mode` 的 Web 行为:进入结构化 diagnostic-only 页面诊断,不再伪装为普通 Web Jog 或已实现工具页。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/profiles/gmoccapy-xyzab.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-communication-model.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/runtime/gmoccapy-hal-model.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/state/store.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_communication_model.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_hal_model.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_profile.mjs` +- `/home/meswork/cnc_wams/work/working6/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working6/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working6/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working6/06-决策记录.md` + +验证了什么: + +- 基线和回归 `npm run smoke:node` 均通过。 +- 基线和回归 `npm run build` 均通过。 +- 基线和回归 `npm run smoke` 均通过。 +- 新增最小测试 `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` 通过。 +- `bash ../tests/browser/verify_gmoccapy_shell_browser.sh` 通过。 + +下一步是什么: + +- 当前 W6-010 到 W6-160、W6-900 已完成。后续如继续深化,可新增 working7 或扩展 W6 任务,转向真实文件浏览器交互、宏参数输入 UI、tool table 安全写回等可执行能力。 + +## 2026-06-27 R3:接续复核 working6 完成状态 + +做了什么: + +- 接续上一轮后,重新读取 `work/working6` 的 README、任务矩阵、推进台账、验收证据、功能内容、开发步骤和决策记录。 +- 复核实际代码差异,确认 profile、HAL model、communication model、store、gmoccapy UI shell 和 Node/browser tests 已覆盖 W6 文件页、宏页、tool editor 页和 native GTK 页面诊断边界。 +- 复核 hard-button 对 `setup`、`user-tabs`、`touch-off`、`tool-editor`、`switchkins-mode` 的行为已进入 `GMOCAPY_NATIVE_PAGE` 结构化诊断,不再伪装为已实现页面。 +- 执行最终完整回归和空白检查。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/work/working6/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working6/05-验收证据.md` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md` + +验证了什么: + +- `npm run smoke:node` 通过,包含 `gmoccapy_xyzab_profile_smoke=ok`、`gmoccapy_communication_model_smoke=ok`、`gmoccapy_hal_model_smoke=ok`、`gmoccapy_xyzab_gates_smoke=ok`。 +- `npm run build` 通过,输出 `gmoccapy_static_build=ok`。 +- `npm run smoke` 通过,输出 `gmoccapy_shell_smoke=ok`、`gmoccapy_dist_smoke=ok`。 +- `git diff --check -- web-rtcp-5axis-sim-plan work/working6` 通过,无输出。 + +下一步是什么: + +- `working6` 当前任务闭环。后续若继续深化,建议新建 `working7`,聚焦真实文件浏览交互、宏参数输入 UI 或 tool table 安全写回的设计与实现。 diff --git a/work/working6/04-任务矩阵.md b/work/working6/04-任务矩阵.md new file mode 100644 index 0000000..a05c617 --- /dev/null +++ b/work/working6/04-任务矩阵.md @@ -0,0 +1,17 @@ +# 04-任务矩阵 + +| 任务编号 | 任务 | 状态 | 验收标准 | +| --- | --- | --- | --- | +| W6-001 | 创建 working6 工作控制文件 | 完成 | README 和 01-06 文件存在,内容可指导后续推进 | +| W6-010 | 建立 gmoccapy native 页面到 Web 模块映射 | 完成 | 文件/宏/tool editor/native 未实现页面已映射到 profile、communication model、store、UI shell 和 tests | +| W6-020 | 执行当前 Node 冒烟基线 | 完成 | `npm run smoke:node` 通过 | +| W6-030 | 执行当前构建基线 | 完成 | `npm run build` 通过 | +| W6-040 | 执行当前浏览器冒烟基线 | 完成 | `npm run smoke` 通过 | +| W6-100 | 细化 gmoccapy 文件页面诊断边界 | 完成 | profile/model/store/UI 明确文件选择、最近文件、预览、加载、运行 gate 和 native file chooser 边界;Node/browser 测试通过 | +| W6-110 | 细化 gmoccapy 宏页面诊断边界 | 完成 | 明确 XYZAB 5 个宏配置、宏按钮到 MDI 的 gate、运行中/未上电拦截;Node/browser 测试通过 | +| W6-120 | 细化 tool editor 页面诊断边界 | 完成 | 明确 tool table 来源、active/prepared tool、offset、只读/可编辑边界、与 iocontrol-loopback 换刀语义一致;Node/browser 测试通过 | +| W6-130 | 建立 native GTK 未实现页面 implementation matrix | 完成 | 已实现、部分实现、diagnostic-only、native-only 页面清单可在 model/UI 中稳定读取;浏览器 DOM 有 `data-*` 诊断 | +| W6-140 | 细化 hard-button 对未实现 native 页面按钮的行为 | 完成 | `tbtn_setup`、user tabs、tool/touch 等未实现按钮只触发 `GMOCAPY_NATIVE_PAGE` 诊断,不伪装成已实现页面;store/browser 测试通过 | +| W6-150 | 汇总 gmoccapy 页面级通信模型 | 完成 | communication model 展示 file/macro/tool/native page 的命令、状态、错误/诊断路径;Node 测试通过 | +| W6-160 | 增加浏览器 UI 诊断验收 | 完成 | gmoccapy shell smoke 验证 file/macro/tool/native diagnostic rows、关键 `data-*` 属性和 gate 文案 | +| W6-900 | 更新验收证据和决策记录 | 完成 | 命令、结果、修改原因、边界判断和后续项已记录 | diff --git a/work/working6/05-验收证据.md b/work/working6/05-验收证据.md new file mode 100644 index 0000000..ecf8ea4 --- /dev/null +++ b/work/working6/05-验收证据.md @@ -0,0 +1,186 @@ +# 05-验收证据 + +## 证据索引 + +| 证据编号 | 任务编号 | 类型 | 命令/页面/文件 | 结果 | +| --- | --- | --- | --- | --- | +| EV-W6-001 | W6-001 | 文件 | `/home/meswork/cnc_wams/work/working6` | 已创建 README 和 01-06 工作控制文件 | +| EV-W6-020 | W6-020/W6-030/W6-040 | 命令 | `npm run smoke:node`、`npm run build`、`npm run smoke` | 基线通过,确认实现前项目当前态可运行 | +| EV-W6-100 | W6-010/W6-100/W6-110/W6-120/W6-130/W6-140/W6-150 | 命令 | `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` | 通过,验证页面级模型、profile、communication model、store gate 和 diagnostic-only hard-button 行为 | +| EV-W6-160 | W6-160 | 页面/命令 | `bash ../tests/browser/verify_gmoccapy_shell_browser.sh` | 通过,验证 `data-gmoccapy-page="file/macros/tool-editor/matrix"` 诊断行和公开 dispatch 页面行为 | +| EV-W6-900 | W6-020/W6-030/W6-040/W6-900 | 命令 | `npm run smoke:node`、`npm run build`、`npm run smoke` | 回归通过,完整 Node、静态构建、浏览器 shell/dist smoke 均通过 | +| EV-W6-901 | W6-900 | 命令 | `npm run smoke:node`、`npm run build`、`npm run smoke`、`git diff --check -- web-rtcp-5axis-sim-plan work/working6` | 接续复核通过,确认 working6 当前任务闭环且无空白错误 | + +## 命令记录 + +### 2026-06-27 R1 + +```bash +test -d /home/meswork/cnc_wams/work/working6 && find /home/meswork/cnc_wams/work/working6 -maxdepth 2 -type f | sort || true +``` + +结果:创建前无可见文件。 + +```bash +mkdir -p /home/meswork/cnc_wams/work/working6 +``` + +结果:通过,创建工作目录。 + +本轮创建文件: + +- `/home/meswork/cnc_wams/work/working6/README.md` +- `/home/meswork/cnc_wams/work/working6/01-项目功能内容.md` +- `/home/meswork/cnc_wams/work/working6/02-项目程序开发详细步骤.md` +- `/home/meswork/cnc_wams/work/working6/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working6/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working6/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working6/06-决策记录.md` + +### 2026-06-27 R2 + +基线命令: + +```bash +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` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run build +``` + +结果:通过,输出 `gmoccapy_static_build=ok`。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` +- `gmoccapy_dist_smoke=ok` + +新增最小验收: + +```bash +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 +``` + +结果:通过,输出: + +- `gmoccapy_hal_model_smoke=ok` +- `gmoccapy_communication_model_smoke=ok` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +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 +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +bash ../tests/browser/verify_gmoccapy_shell_browser.sh +``` + +结果:通过,输出 `gmoccapy_shell_smoke=ok`。 + +回归命令: + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke:node +``` + +结果:通过。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run build +``` + +结果:通过,输出 `gmoccapy_static_build=ok`。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` +- `gmoccapy_dist_smoke=ok` + +本轮新增断言摘要: + +- `gmoccapy_XYZAB.ini` 的 5 个 `[MACROS]` 被识别为 MDI O-word 宏,不再误判为未配置。 +- `IconFileSelection1` 文件页以 Web file input/staged source 诊断表达,不伪装成 native GTK chooser。 +- `tooledit1` tool editor 读取 `tool.tbl` 的 17 把刀,Web 写回被明确阻塞。 +- native GTK 页面矩阵包含 implemented、partial、diagnostic-only、native-only 四类。 +- `tbtn_setup`、`tbtn_user_tabs`、`btn_touch`、`btn_tool`、`tbtn_switch_mode` hard-button 进入 `GMOCAPY_NATIVE_PAGE` 诊断,不伪装为已实现页面。 + +### 2026-06-27 R3 + +接续复核命令: + +```bash +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` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run build +``` + +结果:通过,输出 `gmoccapy_static_build=ok`。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` +- `gmoccapy_dist_smoke=ok` + +```bash +cd /home/meswork/cnc_wams +git diff --check -- web-rtcp-5axis-sim-plan work/working6 +``` + +结果:通过,无输出。 + +复核结论: + +- W6 任务矩阵已全部完成,无需新增代码补丁。 +- profile/model/store/UI/tests 的实际差异与 W6 文档目标一致。 +- 本轮未生成截图、PDF、job_id 或 report_id。 + +## 页面/截图/report/job 记录 + +本轮未生成截图、PDF、job_id 或 report_id。浏览器页面证据由 headless Chromium smoke 脚本验证,关键 DOM 为 `data-gmoccapy-page="file"`、`data-gmoccapy-page="macros"`、`data-gmoccapy-page="tool-editor"`、`data-gmoccapy-page="matrix"`。 diff --git a/work/working6/06-决策记录.md b/work/working6/06-决策记录.md new file mode 100644 index 0000000..e11e98a --- /dev/null +++ b/work/working6/06-决策记录.md @@ -0,0 +1,127 @@ +# 06-决策记录 + +## DR-W6-001:working6 作为 working5 后续深化,不作为未完成补丁 + +日期:2026-06-27 + +决策: + +- `working6` 不是修补 working5 未完成项,而是承接 working5 台账中“可选深化方向”。 +- 本轮主线聚焦 gmoccapy 文件/宏页面、tool editor 页面、native GTK 未实现页面诊断边界。 +- working5 中已完成的 HAL 输入、hard-button、状态机门控、换刀回环和低频 HAL pin 行为不得在 working6 中回退。 + +原因: + +- `working5` 任务矩阵 W5-001 到 W5-900 已全部完成,并有 EV-W5-900 最终复核证据。 +- 文件/宏/tool editor/native GTK 页面属于页面级能力和诊断边界深化,风险面和验收口径不同于 working5 的 HAL pin 细化。 + +影响: + +- `working6` 需要独立任务矩阵、验收证据和决策记录。 +- 后续提交或验收应区分 W5 已闭环行为与 W6 新增页面级诊断行为。 + +## DR-W6-002:未实现 native GTK 页面必须诊断化,不伪装成已实现 + +日期:2026-06-27 + +决策: + +- 对 gmoccapy 原生存在但 Web 尚未完整实现的页面,先建立 implementation matrix。 +- Web UI 可以显示 diagnostic-only 状态、源码对应关系、未实现原因和未来补齐入口。 +- 未实现页面按钮和 hard-button pin 不得触发假的成功状态。 + +原因: + +- gmoccapy 原生 GTK 页面和 Web shell 的布局、运行环境、文件系统权限、HAL 交互能力不同。 +- 若直接把未实现页面显示为可操作,会误导验收,掩盖仿真边界。 + +影响: + +- UI 和 tests 必须验证 diagnostic-only/unsupported/not configured 等边界文案或结构化状态。 +- store 中未实现 action 应返回明确 operator message。 + +## DR-W6-003:文件/宏/tool editor 先按 XYZAB 配置边界建模 + +日期:2026-06-27 + +决策: + +- 文件页面、宏页面和 tool editor 页面优先按 `gmoccapy_XYZAB.ini` 当前配置和 Web 仿真能力建模。 +- 配置未启用的能力显示为 no config 或 diagnostic-only。 +- 真实文件写入、外部 LinuxCNC 进程联动和持久化 tool table 修改不作为初始实现默认目标。 + +原因: + +- 当前 Web 项目是 LinuxCNC/gmoccapy 行为仿真,不是完整桌面 gmoccapy 进程。 +- XYZAB 配置的宏、tool sensor、user message 等能力存在明确启用边界。 +- 先把边界表达清楚,后续才能安全决定哪些功能升级为可执行。 + +影响: + +- W6-100、W6-110、W6-120 的验收重点是“能力、gate、边界和诊断是否正确”,不是盲目补齐所有 GTK 行为。 +- 若后续要启用真实写文件或外部进程,需要新增决策记录和验收任务。 + +## DR-W6-004:XYZAB 宏页按已配置 5 个 `[MACROS]` 建模 + +日期:2026-06-27 + +决策: + +- `gmoccapy_XYZAB.ini` 的 `[MACROS]` 中 5 个宏必须建模为已配置能力: + - `i_am_lost` + - `halo_world` + - `jog_around` + - `increment xinc yinc` + - `go_to_position X-pos Y-pos Z-pos` +- Web 通过 `GMOCAPY_RUN_MACRO` 模拟 `_on_btn_macro_pressed()`,生成 `O call [args]` MDI 命令。 +- 宏执行必须复用 `RUN_MDI` gate;未上电、未回零、非 MDI-capable 或解释器忙时拦截。 + +原因: + +- 初始文档把“未配置宏时显示边界”列为需要处理的通用场景,但实际 XYZAB INI 明确配置了 5 个宏。 +- gmoccapy 原生 `_make_macro_button()` 从 INI 读取宏,`_on_btn_macro_pressed()` 将宏转为 O-word MDI 调用。 + +影响: + +- profile、HAL model、communication model、store、Node/browser tests 均断言 XYZAB 宏 count 为 5。 +- Web 不创建 native entry dialog;参数由 dispatch/test 传入,后续如需 UI 参数输入需要单独任务。 + +## DR-W6-005:tool editor 本轮只做诊断和 gate,不写 `tool.tbl` + +日期:2026-06-27 + +决策: + +- Web 记录 `tooledit1`、`tool.tbl`、17 把刀、可见 XYZAB 轴和 M6/M61 工具动作边界。 +- `GMOCAPY_TOOL_EDITOR_ACTION` 对 `save`、`add`、`delete`、`touch-off` 等写操作返回 diagnostic-only/operator message,不修改本地 `tool.tbl`。 +- 工具换刀继续沿用 working5 的 `iocontrol-loopback`,不重新启用手动 gmoccapy toolchange GUI pins。 + +原因: + +- 原生 gmoccapy tool editor 可以直接写配置目录中的 `tool.tbl`,但浏览器 Web 仿真没有等价的 LinuxCNC 配置写回权限和进程同步语义。 +- 未经单独设计直接写文件会混淆“诊断仿真”和“真实机床配置修改”的边界。 + +影响: + +- 本轮验收关注 tool table 来源、数量、只读边界和换刀语义一致性。 +- 后续若要实现真实写回,需要新增持久化设计、备份策略、并发保护和验收任务。 + +## DR-W6-006:hard-button 对 native-only/diagnostic-only 页面使用结构化页面诊断 + +日期:2026-06-27 + +决策: + +- `tbtn_user_tabs`、`tbtn_setup`、`btn_touch`、`btn_tool`、`tbtn_switch_mode` 不再作为普通 Web action 或模式切换伪装执行。 +- 这些按钮通过 `GMOCAPY_NATIVE_PAGE` 更新 `gmoccapyGui.activeNativePage`、`nativePageMode` 和 operator message。 +- UI 通过 `data-gmoccapy-page="matrix"` 展示 native GTK 页面实现矩阵。 + +原因: + +- gmoccapy 原生 `_button_pin_changed()` 会触发当前可见且敏感的 GTK 按钮,但 Web shell 尚未实现完整 setup/user tabs/touch/tool/switchkins 页面。 +- 直接把这些按钮映射成其他 Web action 会误表达 native 行为。 + +影响: + +- hard-button 模型仍保留 pin 到 native widget 的源码映射。 +- Web 验收明确区分 implemented、partial、diagnostic-only、native-only 页面,避免后续重复争论“未实现页面是否可点击成功”。 diff --git a/work/working6/README.md b/work/working6/README.md new file mode 100644 index 0000000..caf12c4 --- /dev/null +++ b/work/working6/README.md @@ -0,0 +1,33 @@ +# working6 README 索引 + +本目录用于推进 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` 中 gmoccapy 文件/宏页面、tool editor 页面、native GTK 未实现页面诊断边界的实现、验证和验收。 + +前置工作: + +- `/home/meswork/cnc_wams/work/working5` +- `/home/meswork/cnc_wams/work/working3/gmoccapy_XYZAB_execution_analysis.md` + +对标基准: + +- `/home/meswork/cnc_wams/linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini` +- `/home/meswork/cnc_wams/linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py` +- `/home/meswork/cnc_wams/linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.glade` +- `/home/meswork/cnc_wams/linuxcnc/src/emc/usr_intf/gmoccapy/dialogs.py` +- `/home/meswork/cnc_wams/linuxcnc/configs/sim/gmoccapy/*.hal` + +工作文件: + +1. [01-项目功能内容.md](01-项目功能内容.md) +2. [02-项目程序开发详细步骤.md](02-项目程序开发详细步骤.md) +3. [03-推进台账.md](03-推进台账.md) +4. [04-任务矩阵.md](04-任务矩阵.md) +5. [05-验收证据.md](05-验收证据.md) +6. [06-决策记录.md](06-决策记录.md) + +推进规则: + +- 先按 `04-任务矩阵.md` 选择任务,避免重复做同一项。 +- 每轮结束更新 `03-推进台账.md`,记录做了什么、改了哪些文件、验证了什么、下一步是什么。 +- 所有命令、页面、report、job_id、PDF、截图、测试输出摘要写入 `05-验收证据.md`。 +- 影响架构、兼容性、验收口径或 LinuxCNC/gmoccapy 对标边界的判断写入 `06-决策记录.md`。 +- 本轮是 working5 的后续深化,不回退 working5 已完成的 HAL 输入和按钮门控行为。