Rename W axis -> A axis everywhere (with migration)
Auxiliary axis is the auxcnc-driven stepper exposed to gplan as A,
not W. Half the stack already used A (gcode, DRO row, soft limits,
homing); the other half (settings tab, macros, internal field
names) still said W which was confusing.
Renames:
- aux.json fields: min_w/max_w -> min_mm/max_mm
- svelte component: WAxisSettings -> AAxisSettings
- settings tab slug: #w-axis -> #a-axis
- js view module: w-axis-view.js -> a-axis-view.js
- pug template: w-axis-view.pug -> a-axis-view.pug
- macros: w_down.nc/w_up.nc -> a_down.nc/a_up.nc
'W Down'/'W Up' -> 'A Down'/'A Up'
- css class & ids: .w-axis-settings -> .a-axis-settings,
min_w/max_w form ids match field names
- internal js identifiers and comments
Migration:
- AuxAxis._migrate_legacy_fields() promotes min_w/max_w in aux.json
on every load and persists the upgraded form, so existing
installs come out clean on first restart.
- Config._upgrade() renames macro file_name and display name in
every config.json load, so a stale in-memory copy can't
reintroduce the W names. Ships with a save right after.
The auxcnc ESP wire protocol verbs (WPOS/HOMED) are unchanged - they
are wire-format identifiers, not user-facing labels.
This commit is contained in:
@@ -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")
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
4
src/pug/templates/a-axis-view.pug
Normal file
4
src/pug/templates/a-axis-view.pug
Normal file
@@ -0,0 +1,4 @@
|
||||
script#a-axis-view-template(type="text/x-template")
|
||||
#a-axis-page
|
||||
h1 A Axis (auxcnc)
|
||||
#a-axis-mount
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
script#w-axis-view-template(type="text/x-template")
|
||||
#w-axis-page
|
||||
h1 W Axis (auxcnc)
|
||||
#w-axis-mount
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-axis-settings">
|
||||
<div class="a-axis-settings">
|
||||
{#if !cfg}
|
||||
<p class="tip">Loading auxiliary A axis configuration...</p>
|
||||
<p class="tip">Loading A axis configuration...</p>
|
||||
{:else}
|
||||
<div class="status">
|
||||
{#if status}
|
||||
@@ -154,14 +154,14 @@
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group" title="Soft-limit minimum in mm.">
|
||||
<label for="min_w">soft min</label>
|
||||
<input id="min_w" type="number" bind:value={cfg.min_w} step="any" />
|
||||
<label for="min_mm">soft min</label>
|
||||
<input id="min_mm" type="number" bind:value={cfg.min_mm} step="any" />
|
||||
<label for="" class="units">mm</label>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group" title="Soft-limit maximum in mm.">
|
||||
<label for="max_w">soft max</label>
|
||||
<input id="max_w" type="number" bind:value={cfg.max_w} step="any" />
|
||||
<label for="max_mm">soft max</label>
|
||||
<input id="max_mm" type="number" bind:value={cfg.max_mm} step="any" />
|
||||
<label for="" class="units">mm</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -273,7 +273,7 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.w-axis-settings {
|
||||
.a-axis-settings {
|
||||
.status {
|
||||
margin-bottom: 1em;
|
||||
font-size: 90%;
|
||||
@@ -2,10 +2,10 @@
|
||||
import configTemplate from "../../../resources/config-template.json";
|
||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
|
||||
// WAxisSettings is mounted directly by the V09 settings shell at
|
||||
// #w-axis instead of being embedded here — see
|
||||
// src/pug/templates/w-axis-view.pug.
|
||||
// import WAxisSettings from "./WAxisSettings.svelte";
|
||||
// AAxisSettings is mounted directly by the V09 settings shell at
|
||||
// #a-axis instead of being embedded here — see
|
||||
// src/pug/templates/a-axis-view.pug.
|
||||
// import AAxisSettings from "./AAxisSettings.svelte";
|
||||
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
</fieldset>
|
||||
|
||||
<!-- W Axis (auxcnc) is now its own routed page in the V09
|
||||
settings shell (#w-axis). Keep the SettingsView free of
|
||||
settings shell (#a-axis). Keep the SettingsView free of
|
||||
that section so we don't render it twice. -->
|
||||
|
||||
<h2 id="sec-path-accuracy" data-sec="gcode">Path Accuracy</h2>
|
||||
|
||||
@@ -6,7 +6,7 @@ matchAll.shim();
|
||||
import AdminNetworkView from "$components/AdminNetworkView.svelte";
|
||||
import SettingsView from "$components/SettingsView.svelte";
|
||||
import HelpView from "$components/HelpView.svelte";
|
||||
import WAxisSettings from "$components/WAxisSettings.svelte";
|
||||
import AAxisSettings from "$components/AAxisSettings.svelte";
|
||||
import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte";
|
||||
import { handleConfigUpdate, setDisplayUnits } from "$lib/ConfigStore";
|
||||
import { handleControllerStateUpdate } from "$lib/ControllerState";
|
||||
@@ -23,8 +23,8 @@ export function createComponent(component: string, target: HTMLElement, props: R
|
||||
case "HelpView":
|
||||
return new HelpView({ target, props });
|
||||
|
||||
case "WAxisSettings":
|
||||
return new WAxisSettings({ target, props });
|
||||
case "AAxisSettings":
|
||||
return new AAxisSettings({ target, props });
|
||||
|
||||
case "DialogHost":
|
||||
return new DialogHost({ target, props });
|
||||
|
||||
Reference in New Issue
Block a user