From 549b69c234f28a93fb5e1d66110f366cf283f783 Mon Sep 17 00:00:00 2001 From: Henrik Muehe Date: Fri, 1 May 2026 16:08:31 +0200 Subject: [PATCH] motor-view: stop clobbering user edits with controller state The legacy Vue 1 motor settings page had nine `current_xxx` computed props mirroring controller state vars (`vm`, `am`, `jm`, `sa`, `tr`, `mi`, `tm`, `tn`, `an`) paired with watchers that copied the state value back into `config.motors[index]`, plus an `attached()` hook running the same sync on mount. The controller streams those vars continuously over the websocket. Whenever a user typed into a field, the next state tick reverted it to the controller's pre-edit value, so the form felt racy and edits disappeared before Save. The same path also nuked unsaved edits when navigating to another settings page and back. The watcher logic was added in 749d63e to handle the case where toggling rotary mode (PUT /api/rotary) rewrites motor 1+2 in config.json on the server. Move that fix to the right place: refetch config after the rotary PUT in app.js. The form now edits config directly, Save PUTs it, and incoming controller state never overwrites the user's in-progress edits. Also drop the unused `syncStateToConfig` method. --- src/js/app.js | 6 ++ src/js/motor-view.js | 143 +++---------------------------------------- 2 files changed, 16 insertions(+), 133 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index 31463cc..b0acb98 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -488,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"); diff --git a/src/js/motor-view.js b/src/js/motor-view.js index 5af9560..5564761 100644 --- a/src/js/motor-view.js +++ b/src/js/motor-view.js @@ -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 (`vm`, `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']); - } } } };