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 (`<idx>vm`, `<idx>am`,
`<idx>jm`, `<idx>sa`, `<idx>tr`, `<idx>mi`, `<idx>tm`,
`<idx>tn`, `<idx>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.
This commit is contained in:
2026-05-01 16:08:31 +02:00
parent 5376d23f8b
commit 549b69c234
2 changed files with 16 additions and 133 deletions

View File

@@ -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");

View File

@@ -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']);
}
}
}
};