diff --git a/src/js/w-axis-view.js b/src/js/a-axis-view.js similarity index 66% rename from src/js/w-axis-view.js rename to src/js/a-axis-view.js index d4a4360..132ec4d 100644 --- a/src/js/w-axis-view.js +++ b/src/js/a-axis-view.js @@ -1,16 +1,16 @@ "use strict"; -// V09 W-axis page \u2014 mounts the existing WAxisSettings Svelte component +// V09 A-axis page — mounts the AAxisSettings Svelte component // inside the settings shell so it gets a real top-level rail entry // instead of being a soft-link anchor inside Display & Units. module.exports = { - template: "#w-axis-view-template", + template: "#a-axis-view-template", attached: function () { this.svelteComponent = SvelteComponents.createComponent( - "WAxisSettings", - document.getElementById("w-axis-mount") + "AAxisSettings", + document.getElementById("a-axis-mount") ); }, diff --git a/src/js/app.js b/src/js/app.js index b0acb98..f8d54f9 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -369,7 +369,7 @@ module.exports = new Vue({ ready: function() { window.onhashchange = () => this.parse_hash(); - // Embedded Svelte subviews (W axis settings, etc.) signal + // Embedded Svelte subviews (A axis settings, etc.) signal // unsaved changes via this event. The master Save button // highlights when modified is true. window.addEventListener("onefin:dirty", () => { @@ -391,7 +391,7 @@ module.exports = new Vue({ "admin-general", "admin-network", "motor", "tool", "io", "macros", "help", "cheat-sheet", - "w-axis", + "a-axis", ]; const initialHead = (location.hash || "").replace(/^#/, "").split(":")[0]; if (settingsFamily.indexOf(initialHead) === -1) { @@ -627,7 +627,7 @@ module.exports = new Vue({ "admin-general", "admin-network", "motor", "tool", "io", "macros", "help", "cheat-sheet", - "w-axis", + "a-axis", ]; if (head == "control") { @@ -690,7 +690,7 @@ module.exports = new Vue({ try { await api.put("config/save", this.config); // Notify any embedded Svelte subviews that own their - // own persistence (W axis -> aux.json, etc.) that + // own persistence (A axis -> aux.json, etc.) that // the user just hit the master Save button. They // listen for `onefin:save-all` and PUT their state. try { diff --git a/src/js/settings-shell-view.js b/src/js/settings-shell-view.js index 1c0bea4..299b9d6 100644 --- a/src/js/settings-shell-view.js +++ b/src/js/settings-shell-view.js @@ -24,7 +24,7 @@ module.exports = { "io-view": require("./io-view"), "macros-view": require("./macros"), "help-view": require("./help-view"), - "w-axis-view": require("./w-axis-view"), + "a-axis-view": require("./a-axis-view"), "cheat-sheet-view": { template: "#cheat-sheet-view-template", data: function () { @@ -58,10 +58,8 @@ module.exports = { { sub: "motor", motor: 2, href: "#motor:2", icon: "fa-arrows-up-down-left-right", label: "Motor 2" }, { sub: "motor", motor: 3, href: "#motor:3", icon: "fa-arrows-up-down-left-right", label: "Motor 3" }, // Auxiliary axis (auxcnc ESP32 - exposed to gplan as A). - // Mounts the WAxisSettings Svelte component on its own page. - // The route slug stays "#w-axis" for back-compat with any - // bookmarks; the visible label and tab content say "A Axis". - { sub: "w-axis", href: "#w-axis", icon: "fa-arrows-up-down", label: "A Axis" }, + // Mounts the AAxisSettings Svelte component on its own page. + { sub: "a-axis", href: "#a-axis", icon: "fa-arrows-up-down", label: "A Axis" }, { section: " " }, { sub: "help", href: "#help", icon: "fa-circle-question", label: "Help" }, ], @@ -139,7 +137,7 @@ module.exports = { // layout, which under tablet mode pulls the fixed header out // of view. if (location.hash !== item.href) location.hash = item.href; - this._w_axis_focus = (item.sub === "w-axis"); + this._a_axis_focus = (item.sub === "a-axis"); const reset = () => { // Force any inadvertent ancestor scroll back to 0 before // we move .settings-content explicitly. @@ -162,7 +160,7 @@ module.exports = { requestAnimationFrame(reset); }, 320); } else { - this._w_axis_focus = false; + this._a_axis_focus = false; if (location.hash !== item.href) location.hash = item.href; // Reset .app-body scroll so each route starts at the top. const body = document.querySelector(".app-body"); diff --git a/src/pug/templates/a-axis-view.pug b/src/pug/templates/a-axis-view.pug new file mode 100644 index 0000000..2e61cc4 --- /dev/null +++ b/src/pug/templates/a-axis-view.pug @@ -0,0 +1,4 @@ +script#a-axis-view-template(type="text/x-template") + #a-axis-page + h1 A Axis (auxcnc) + #a-axis-mount diff --git a/src/pug/templates/control-view.pug b/src/pug/templates/control-view.pug index 4a4d8b7..0e082e3 100644 --- a/src/pug/templates/control-view.pug +++ b/src/pug/templates/control-view.pug @@ -249,7 +249,7 @@ script#control-view-template(type="text/x-template") @click=`home('${axis}')`) .fa.fa-home - // Legacy W axis row - shown only when the auxcnc stepper is + // Legacy auxiliary-axis row - shown only when the auxcnc stepper is // *not* exposed as a virtual A axis. After v2 the standard // A row above renders this axis natively (with full offset // + set-position support); this row only appears on legacy diff --git a/src/pug/templates/settings-shell.pug b/src/pug/templates/settings-shell.pug index 091068a..a889b38 100644 --- a/src/pug/templates/settings-shell.pug +++ b/src/pug/templates/settings-shell.pug @@ -46,7 +46,7 @@ script#settings-shell-view-template(type="text/x-template") :index="index", :config="config", :template="template", :state="state") io-view(v-if="sub === 'io' && config_ready", :index="index", :config="config", :template="template", :state="state") - w-axis-view(v-if="sub === 'w-axis' && config_ready", + a-axis-view(v-if="sub === 'a-axis' && config_ready", :index="index", :config="config", :template="template", :state="state") macros-view(v-if="sub === 'macros' && config_ready", :index="index", :config="config", :template="template", :state="state") diff --git a/src/pug/templates/w-axis-view.pug b/src/pug/templates/w-axis-view.pug deleted file mode 100644 index 8b5728f..0000000 --- a/src/pug/templates/w-axis-view.pug +++ /dev/null @@ -1,4 +0,0 @@ -script#w-axis-view-template(type="text/x-template") - #w-axis-page - h1 W Axis (auxcnc) - #w-axis-mount diff --git a/src/py/bbctrl/AuxAxis.py b/src/py/bbctrl/AuxAxis.py index 1ba39e0..d0d16ba 100644 --- a/src/py/bbctrl/AuxAxis.py +++ b/src/py/bbctrl/AuxAxis.py @@ -40,8 +40,8 @@ DEFAULTS = { # 4th axis). gcode uses A for moves; the host ExternalAxis layer # forks A motion to the ESP transparently. 'axis_letter': 'a', - 'min_w': 0.0, # soft limit min (mm), exposed as 4tn - 'max_w': 100.0, # soft limit max (mm), exposed as 4tm + 'min_mm': 0.0, # soft limit min (mm), exposed as 4tn + 'max_mm': 100.0, # soft limit max (mm), exposed as 4tm # Per-axis kinematic limits used to populate the planner's config. # Units match the bbctrl/onefinity per-motor convention so the # values are directly comparable to motors 0-3: @@ -120,23 +120,61 @@ class AuxAxis(object): def _config_path(self): return self.ctrl.get_path(filename='aux.json') + # Legacy aux.json fields that have been renamed for clarity. + # Loaded values are migrated up on every load/save so existing + # installs keep working without operator intervention. + _LEGACY_FIELD_MAP = { + 'min_w': 'min_mm', + 'max_w': 'max_mm', + } + + def _migrate_legacy_fields(self, cfg): + """In-place rename of legacy keys in `cfg` (dict). Returns + True if anything was migrated, so callers can decide whether + to persist the upgraded form. + """ + migrated = False + for old, new in self._LEGACY_FIELD_MAP.items(): + if old in cfg: + if new not in cfg: + cfg[new] = cfg[old] + del cfg[old] + migrated = True + return migrated + def _load_config(self): path = self._config_path() if os.path.exists(path): try: with open(path) as f: user = json.load(f) + migrated = self._migrate_legacy_fields(user) # Be permissive; ignore unknown keys. for k, v in user.items(): if k in self._cfg: self._cfg[k] = v self.log.info('Loaded aux config from %s' % path) + if migrated: + # Persist the upgraded form so future restarts + # see the new field names directly. + try: + self.save_config(self._cfg) + self.log.info( + 'Migrated aux.json legacy fields ' + '(min_w/max_w -> min_mm/max_mm)') + except Exception: + self.log.warning( + 'Could not persist aux.json migration') except Exception: self.log.error('Failed to read aux.json: %s' % traceback.format_exc()) def save_config(self, cfg): merged = dict(DEFAULTS) + # Accept legacy keys from callers that may still send the + # old names (older UI bundles, hand-edited POSTs). + cfg = dict(cfg) + self._migrate_legacy_fields(cfg) for k, v in cfg.items(): if k in DEFAULTS: merged[k] = v @@ -317,8 +355,8 @@ class AuxAxis(object): raise AuxAxisError('Aux axis not connected') def _check_limits(self, target_mm): - lo = float(self._cfg['min_w']) - hi = float(self._cfg['max_w']) + lo = float(self._cfg['min_mm']) + hi = float(self._cfg['max_mm']) if hi <= lo: return # no limits if target_mm < lo - 1e-6 or target_mm > hi + 1e-6: diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index 53118f2..173c88a 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -216,6 +216,32 @@ class Config(object): defaults = json.load(f) config['selected-tool-settings'] = defaults['selected-tool-settings']; + # Auxiliary axis nomenclature: rename W -> A in macro names and + # filenames. The auxcnc-driven stepper has been integrated into + # gplan as A since the option-b migration; old configs may + # still carry W Down/W Up macro entries pointing at + # w_down.nc/w_up.nc which were renamed on disk to a_down.nc / + # a_up.nc. Migrate idempotently on every load so a stale + # in-memory copy can never reintroduce the old names. + macros = config.get('macros') if isinstance(config, dict) else None + if isinstance(macros, list): + renames = { + 'w_down.nc': 'a_down.nc', + 'w_up.nc': 'a_up.nc', + } + display_renames = { + 'W Down': 'A Down', + 'W Up': 'A Up', + } + for m in macros: + if not isinstance(m, dict): continue + fn = m.get('file_name') + if isinstance(fn, str) and fn in renames: + m['file_name'] = renames[fn] + nm = m.get('name') + if isinstance(nm, str) and nm in display_renames: + m['name'] = display_renames[nm] + config['version'] = self.version.split('b')[0] config['full_version'] = self.version diff --git a/src/py/bbctrl/ExternalAxis.py b/src/py/bbctrl/ExternalAxis.py index 4cca7b9..ac2c691 100644 --- a/src/py/bbctrl/ExternalAxis.py +++ b/src/py/bbctrl/ExternalAxis.py @@ -146,8 +146,8 @@ class ExternalAxis(object): """Return (min_mm, max_mm) in machine coords, or (None, None) if soft limits are disabled (max <= min).""" try: - lo = float(self.aux._cfg.get('min_w', 0.0)) - hi = float(self.aux._cfg.get('max_w', 0.0)) + lo = float(self.aux._cfg.get('min_mm', 0.0)) + hi = float(self.aux._cfg.get('max_mm', 0.0)) except Exception: return (None, None) if hi <= lo: @@ -230,8 +230,8 @@ class ExternalAxis(object): # Soft limits in machine units (mm). State.get_soft_limit_vector # returns these directly, no scaling. - st.set(i + 'tn', float(cfg.get('min_w', 0.0))) - st.set(i + 'tm', float(cfg.get('max_w', 0.0))) + st.set(i + 'tn', float(cfg.get('min_mm', 0.0))) + st.set(i + 'tm', float(cfg.get('max_mm', 0.0))) # home_position / home_travel are exposed as callbacks for # motors 0..3 (see State.__init__). Register the same lazy @@ -242,7 +242,7 @@ class ExternalAxis(object): i + 'home_position', lambda name: self.home_position_mm) st.set_callback( i + 'home_travel', - lambda name: float(self.aux._cfg.get('max_w', 0.0)) + lambda name: float(self.aux._cfg.get('max_mm', 0.0)) - self.home_position_mm) # Misc fields that other code paths might query. Defaults diff --git a/src/svelte-components/src/components/WAxisSettings.svelte b/src/svelte-components/src/components/AAxisSettings.svelte similarity index 95% rename from src/svelte-components/src/components/WAxisSettings.svelte rename to src/svelte-components/src/components/AAxisSettings.svelte index afed7fa..9c8c02f 100644 --- a/src/svelte-components/src/components/WAxisSettings.svelte +++ b/src/svelte-components/src/components/AAxisSettings.svelte @@ -6,9 +6,9 @@ // Mirrors the DEFAULTS in src/py/bbctrl/AuxAxis.py. The "enabled" // flag is read-only here; toggling the auxiliary A axis on/off // is done via aux.json on disk, so adding/removing the hardware - // doesn't have a surprise UI that bricks bring-up. Field names - // (min_w/max_w) keep the historical "w" prefix for back-compat - // with existing aux.json files - rename happens at display only. + // doesn't have a surprise UI that bricks bring-up. Legacy aux.json + // files using min_w/max_w are migrated up to min_mm/max_mm by + // AuxAxis._migrate_legacy_fields on load. type AuxConfig = { enabled: boolean; port: string; @@ -16,8 +16,8 @@ steps_per_mm: number; dir_sign: number; axis_letter: string; - min_w: number; - max_w: number; + min_mm: number; + max_mm: number; max_feed_mm_min: number; max_velocity_m_per_min: number; max_accel_km_per_min2: number; @@ -88,9 +88,9 @@ } -
Loading auxiliary A axis configuration...
+Loading A axis configuration...
{:else}