Compare commits
6 Commits
19e6cc6c93
...
549b69c234
| Author | SHA1 | Date | |
|---|---|---|---|
| 549b69c234 | |||
|
|
5376d23f8b | ||
|
|
ecf3191fcc | ||
|
|
3baa67360c | ||
|
|
b7bd7a1c9c | ||
|
|
6fe2e79bff |
@@ -369,6 +369,13 @@ module.exports = new Vue({
|
||||
ready: function() {
|
||||
window.onhashchange = () => this.parse_hash();
|
||||
|
||||
// Embedded Svelte subviews (W axis settings, etc.) signal
|
||||
// unsaved changes via this event. The master Save button
|
||||
// highlights when modified is true.
|
||||
window.addEventListener("onefin:dirty", () => {
|
||||
this.modified = true;
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Resolve the initial route before the websocket connects so
|
||||
@@ -481,6 +488,12 @@ module.exports = new Vue({
|
||||
toggle_rotary: async function(isActive) {
|
||||
try {
|
||||
await api.put("rotary", {status: isActive});
|
||||
// The /api/rotary endpoint rewrites motors[1]/[2]
|
||||
// in config.json on the server. Refetch so the UI
|
||||
// reflects the new motor config (otherwise the
|
||||
// motor settings page keeps showing pre-toggle
|
||||
// values until the next page reload).
|
||||
await this.update();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("Error occured");
|
||||
@@ -676,6 +689,13 @@ 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
|
||||
// the user just hit the master Save button. They
|
||||
// listen for `onefin:save-all` and PUT their state.
|
||||
try {
|
||||
window.dispatchEvent(new CustomEvent("onefin:save-all"));
|
||||
} catch (_e) {}
|
||||
this.modified = false;
|
||||
} catch (error) {
|
||||
console.error("Save failed:", error);
|
||||
|
||||
@@ -255,6 +255,21 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
// Home every enabled axis at once (legacy Onefinity behavior).
|
||||
// The XYZ home is fired first via the standard /api/home endpoint,
|
||||
// then the W axis is homed if it is enabled. The two cycles run
|
||||
// in parallel — W is on the auxcnc ESP and doesn't share motors
|
||||
// with the AVR — so the user sees one click homing everything.
|
||||
home_all: function () {
|
||||
this.ask_home = false;
|
||||
api.put("home");
|
||||
if (this.w && this.w.enabled) {
|
||||
api.put("aux/home").catch(function (err) {
|
||||
console.error("W home failed:", err);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
aux_jog: function (delta_mm) {
|
||||
api.put("aux/jog", { mm: delta_mm }).catch(function (err) {
|
||||
console.error("W jog failed:", err);
|
||||
|
||||
@@ -87,100 +87,16 @@ module.exports = {
|
||||
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
||||
},
|
||||
|
||||
current_axis: function() {
|
||||
return this.state[this.index + 'an'];
|
||||
},
|
||||
|
||||
current_max_velocity: function() {
|
||||
return this.state[this.index + 'vm'];
|
||||
},
|
||||
|
||||
current_max_soft_limit: function() {
|
||||
return this.state[this.index + 'tm'];
|
||||
},
|
||||
|
||||
current_min_soft_limit: function() {
|
||||
return this.state[this.index + 'tn'];
|
||||
},
|
||||
current_max_accel: function() {
|
||||
return this.state[this.index + 'am'];
|
||||
},
|
||||
current_max_jerk: function() {
|
||||
return this.state[this.index + 'jm'];
|
||||
},
|
||||
current_step_angle: function() {
|
||||
return this.state[this.index + 'sa'];
|
||||
},
|
||||
current_travel_per_rev: function() {
|
||||
return this.state[this.index + 'tr'];
|
||||
},
|
||||
current_microsteps: function() {
|
||||
return this.state[this.index + 'mi'];
|
||||
}
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
// Sync all state values with motor config when component is ready
|
||||
// This ensures UI shows correct values when component is first loaded
|
||||
console.log("Syncing state to motor config for motor index ",this.index);
|
||||
this.syncStateToConfig();
|
||||
},
|
||||
|
||||
watch: {
|
||||
current_axis(new_value) {
|
||||
const motor_axes = ["X", "Y", "Z", "A", "B", "C"]
|
||||
if(motor_axes[new_value] != this.motor['axis']){
|
||||
this.motor['axis'] = motor_axes[new_value];
|
||||
}
|
||||
},
|
||||
|
||||
current_max_velocity(new_value) {
|
||||
if(new_value != this.motor['max-velocity']) {
|
||||
this.motor['max-velocity'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_max_soft_limit(new_value) {
|
||||
if(new_value != this.motor['max-soft-limit']) {
|
||||
this.motor['max-soft-limit'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_min_soft_limit(new_value) {
|
||||
if(new_value != this.motor['min-soft-limit']) {
|
||||
this.motor['min-soft-limit'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_max_accel(new_value) {
|
||||
if(new_value != this.motor['max-accel']) {
|
||||
this.motor['max-accel'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_max_jerk(new_value) {
|
||||
if(new_value != this.motor['max-jerk']) {
|
||||
this.motor['max-jerk'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_step_angle(new_value) {
|
||||
if(new_value != this.motor['step-angle']) {
|
||||
this.motor['step-angle'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_travel_per_rev(new_value) {
|
||||
if(new_value != this.motor['travel-per-rev']) {
|
||||
this.motor['travel-per-rev'] = new_value;
|
||||
}
|
||||
},
|
||||
|
||||
current_microsteps(new_value) {
|
||||
if(new_value != this.motor['microsteps']) {
|
||||
this.motor['microsteps'] = new_value;
|
||||
}
|
||||
}
|
||||
// NOTE: do not add `current_xxx` computed props that mirror
|
||||
// controller state vars (`<idx>vm`, `<idx>am`, …) and pair
|
||||
// them with watchers that copy state -> motor config. The
|
||||
// controller streams those vars continuously over the WS;
|
||||
// any watcher that writes them back into
|
||||
// `config.motors[index]` will clobber whatever the user is
|
||||
// typing into the form between websocket ticks. The form
|
||||
// edits config directly; Save (app.js) PUTs it to the
|
||||
// server. The server-side rotary toggle is handled by
|
||||
// refetching config after the PUT, not by watching state.
|
||||
},
|
||||
|
||||
events: {
|
||||
@@ -210,45 +126,6 @@ module.exports = {
|
||||
}
|
||||
|
||||
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||
},
|
||||
|
||||
syncStateToConfig: function() {
|
||||
// Force sync all state values to motor config
|
||||
// This ensures the UI reflects the current state even if changes happened while component was unmounted
|
||||
|
||||
if(this.state == undefined) {
|
||||
console.log("State is undefined");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state[this.index + 'an'] != this.motor['axis']) {
|
||||
const motor_axes = ["X", "Y", "Z", "A", "B", "C"];
|
||||
this.$set('motor["axis"]', motor_axes[this.state[this.index + 'an']]);
|
||||
}
|
||||
if (this.state[this.index + 'vm'] != this.motor['max-velocity']) {
|
||||
this.$set('motor["max-velocity"]', this.state[this.index + 'vm']);
|
||||
}
|
||||
if (this.state[this.index + 'tm'] != this.motor['max-soft-limit']) {
|
||||
this.$set('motor["max-soft-limit"]', this.state[this.index + 'tm']);
|
||||
}
|
||||
if (this.state[this.index + 'tn'] != this.motor['min-soft-limit']) {
|
||||
this.$set('motor["min-soft-limit"]', this.state[this.index + 'tn']);
|
||||
}
|
||||
if (this.state[this.index + 'am'] != this.motor['max-accel']) {
|
||||
this.$set('motor["max-accel"]', this.state[this.index + 'am']);
|
||||
}
|
||||
if (this.state[this.index + 'jm'] != this.motor['max-jerk']) {
|
||||
this.$set('motor["max-jerk"]', this.state[this.index + 'jm']);
|
||||
}
|
||||
if (this.state[this.index + 'sa'] != this.motor['step-angle']) {
|
||||
this.$set('motor["step-angle"]', this.state[this.index + 'sa']);
|
||||
}
|
||||
if (this.state[this.index + 'tr'] != this.motor['travel-per-rev']) {
|
||||
this.$set('motor["travel-per-rev"]', this.state[this.index + 'tr']);
|
||||
}
|
||||
if (this.state[this.index + 'mi'] != this.motor['microsteps']) {
|
||||
this.$set('motor["microsteps"]', this.state[this.index + 'mi']);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,11 +76,11 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
// Row 2
|
||||
button.jbtn(@click="jog_fn(-1, 0, 0, 0)") X−
|
||||
button.jbtn.ghost(@click="showMoveToZeroDialog('xy')")
|
||||
button.jbtn(@click="showMoveToZeroDialog('xy')")
|
||||
span.lbl XY
|
||||
span Origin
|
||||
button.jbtn(@click="jog_fn(1, 0, 0, 0)") X+
|
||||
button.jbtn.ghost(@click="showMoveToZeroDialog('z')")
|
||||
button.jbtn(@click="showMoveToZeroDialog('z')")
|
||||
span.lbl Z
|
||||
span Origin
|
||||
|
||||
@@ -213,7 +213,14 @@ script#control-view-template(type="text/x-template")
|
||||
div Offset
|
||||
div State
|
||||
div Toolpath
|
||||
div(style="text-align:right") Actions
|
||||
.actions-cell
|
||||
// Master Home All. Each row's Actions cell has a per-axis
|
||||
// home button; this header-level button homes every
|
||||
// enabled axis (legacy Onefinity behavior). Auto-includes
|
||||
// the W axis when it is enabled.
|
||||
button.icon-btn(:disabled="!is_idle",
|
||||
title="Home all axes.", @click="home_all()")
|
||||
.fa.fa-house-chimney
|
||||
|
||||
// Per-axis rows — keep unit-value + bindings from axis-vars
|
||||
each axis in 'xyzabc'
|
||||
|
||||
@@ -41,12 +41,17 @@ DEFAULTS = {
|
||||
'home_dir': '-', # which direction is "toward limit" (host's view)
|
||||
'home_position_mm': 0.0, # mm value to assign at home
|
||||
# ESP-side homing rates (steps/sec). Pushed via HOMECFG on connect.
|
||||
'home_fast_sps': 4000,
|
||||
'home_slow_sps': 400,
|
||||
'home_backoff_steps': 200,
|
||||
# Speeds tuned for a typical 25 steps/mm aux drive (so 1 step =
|
||||
# 0.04 mm). With the limit-aware ESP firmware these values give
|
||||
# a brisk seek (100 mm/s), enough backoff to clear the switch
|
||||
# hysteresis (16 mm), and a slow re-engage (10 mm/s) that's
|
||||
# accurate without being painfully slow on a longer axis.
|
||||
'home_fast_sps': 2500, # ≈ 100 mm/s @ 25 steps/mm
|
||||
'home_slow_sps': 250, # ≈ 10 mm/s
|
||||
'home_backoff_steps': 400, # ≈ 16 mm
|
||||
'home_maxtravel_steps': 200000,
|
||||
'step_max_sps': 4000,
|
||||
'step_accel_sps2': 16000,
|
||||
'step_max_sps': 4000, # ≈ 160 mm/s normal-move cap
|
||||
'step_accel_sps2': 12000,
|
||||
'step_start_sps': 200,
|
||||
'limit_low': True,
|
||||
}
|
||||
|
||||
@@ -1410,6 +1410,15 @@ tt.save
|
||||
letter-spacing 0.1em
|
||||
color $muted-2
|
||||
|
||||
// Master Home All sits in the header's Actions cell. Make it
|
||||
// visually subordinate to the per-axis home buttons in the rows
|
||||
// below — same family, smaller scale.
|
||||
.icon-btn
|
||||
width 44px
|
||||
height 44px
|
||||
font-size 0.95rem
|
||||
border-radius 9px
|
||||
|
||||
.control-page .dro-row
|
||||
border-bottom 1px solid $line-soft
|
||||
flex 1
|
||||
|
||||
@@ -31,10 +31,19 @@
|
||||
let cfg: AuxConfig | null = null;
|
||||
let status: { enabled: boolean; present: boolean; homed: boolean; pos_mm: number } | null = null;
|
||||
let busy = false;
|
||||
let saveMessage = "";
|
||||
|
||||
// Listen for the global "save-all" event the Vue root dispatches
|
||||
// when the user clicks the master Save button. We persist our
|
||||
// current cfg the same way the in-form button used to. This way
|
||||
// the user only ever needs one Save button.
|
||||
function onGlobalSave() {
|
||||
save().catch(e => console.error("aux save failed:", e));
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await refresh();
|
||||
window.addEventListener("onefin:save-all", onGlobalSave);
|
||||
return () => window.removeEventListener("onefin:save-all", onGlobalSave);
|
||||
});
|
||||
|
||||
async function refresh() {
|
||||
@@ -49,19 +58,28 @@
|
||||
async function save() {
|
||||
if (!cfg) return;
|
||||
busy = true;
|
||||
saveMessage = "";
|
||||
try {
|
||||
await api.PUT("aux/config/save", cfg);
|
||||
saveMessage = "Saved.";
|
||||
await refresh();
|
||||
setTimeout(() => (saveMessage = ""), 3000);
|
||||
} catch (e) {
|
||||
console.error("Failed to save aux config:", e);
|
||||
saveMessage = "Save failed - see console.";
|
||||
throw e;
|
||||
} finally {
|
||||
busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the root config as modified whenever a W axis field is
|
||||
// edited, so the master Save button highlights and the user knows
|
||||
// there are unsaved changes.
|
||||
function markDirty() {
|
||||
try {
|
||||
const root = (window as any).$root || (window as any).Vue?.root;
|
||||
if (root && "modified" in root) root.modified = true;
|
||||
} catch (_e) {}
|
||||
// Also dispatch a generic event the Vue root listens for.
|
||||
window.dispatchEvent(new CustomEvent("onefin:dirty"));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-axis-settings">
|
||||
@@ -85,7 +103,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-form pure-form-aligned" on:input={markDirty} on:change={markDirty}>
|
||||
<fieldset>
|
||||
<div class="pure-control-group" title="Enable the auxcnc W axis. Edit aux.json to toggle.">
|
||||
<label for="enabled">enabled</label>
|
||||
@@ -206,25 +224,13 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="actions">
|
||||
<Button
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={save}
|
||||
disabled={busy}
|
||||
>
|
||||
<Label>Save W Axis Settings</Label>
|
||||
</Button>
|
||||
{#if saveMessage}
|
||||
<span class="save-msg">{saveMessage}</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="tip">
|
||||
Changes are written to aux.json. Homing rates and the
|
||||
limit polarity are pushed to the ESP immediately; any
|
||||
running motion is unaffected. Re-home the W axis after
|
||||
changing direction, sign, or step settings.
|
||||
Changes are written to aux.json when you click the
|
||||
master <strong>Save</strong> button at the bottom of the
|
||||
settings rail. Homing rates and the limit polarity are
|
||||
pushed to the ESP immediately; any running motion is
|
||||
unaffected. Re-home the W axis after changing direction,
|
||||
sign, or step settings.
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user