From d1f07e8d01dfc14787dca936d25fbb9ea6d17dd2 Mon Sep 17 00:00:00 2001 From: Christian <80114847+TheBest6337@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:56:30 +0100 Subject: [PATCH] Add support for Addon Motor 5 in Gluetex machine, including state management and UI integration --- electron/src/machines/properties.ts | 21 +++++++ .../winder/gluetex/GluetexAddonsPage.tsx | 36 ++++++++++++ .../winder/gluetex/GluetexOverviewPage.tsx | 14 ++++- .../winder/gluetex/gluetexNamespace.ts | 24 ++++++++ .../src/machines/winder/gluetex/useGluetex.ts | 55 +++++++++++++++++++ machines/src/gluetex/act.rs | 1 + machines/src/gluetex/api.rs | 17 ++++++ machines/src/gluetex/emit.rs | 5 ++ machines/src/gluetex/mod.rs | 10 ++++ machines/src/gluetex/new.rs | 40 ++++++++++++++ 10 files changed, 222 insertions(+), 1 deletion(-) diff --git a/electron/src/machines/properties.ts b/electron/src/machines/properties.ts index 5b6af518..753cec79 100644 --- a/electron/src/machines/properties.ts +++ b/electron/src/machines/properties.ts @@ -866,6 +866,27 @@ export const gluetex: MachineProperties = { }, ], }, + { + role: 11, + role_label: "Addon Motor 5", + allowed_devices: [ + { + vendor_id: 2, + product_id: 0x1b773052, + revision: 0x1a0000, + }, + { + vendor_id: 2, + product_id: 0x1b773052, + revision: 0x190000, + }, + { + vendor_id: 2, + product_id: 0x1b773052, + revision: 0x10001e, + }, + ], + }, ], }; diff --git a/electron/src/machines/winder/gluetex/GluetexAddonsPage.tsx b/electron/src/machines/winder/gluetex/GluetexAddonsPage.tsx index 189fcfb4..1382902c 100644 --- a/electron/src/machines/winder/gluetex/GluetexAddonsPage.tsx +++ b/electron/src/machines/winder/gluetex/GluetexAddonsPage.tsx @@ -27,6 +27,7 @@ export function GluetexAddonsPage() { isDisabled, setStepper3Mode, setStepper4Mode, + setStepper5Mode, setHeatingMode, temperature1, temperature2, @@ -43,6 +44,8 @@ export function GluetexAddonsPage() { setStepper3Slave, setStepper4Master, setStepper4Slave, + setStepper5Master, + setStepper5Slave, } = useGluetex(); // Calculate max speed based on gear ratio (same as main puller) @@ -98,6 +101,28 @@ export function GluetexAddonsPage() { }} /> + @@ -320,6 +345,17 @@ export function GluetexAddonsPage() { }} /> + diff --git a/electron/src/machines/winder/gluetex/GluetexOverviewPage.tsx b/electron/src/machines/winder/gluetex/GluetexOverviewPage.tsx index 570b0c8b..188dcf9c 100644 --- a/electron/src/machines/winder/gluetex/GluetexOverviewPage.tsx +++ b/electron/src/machines/winder/gluetex/GluetexOverviewPage.tsx @@ -200,7 +200,7 @@ export function GluetexOverviewPage() { {/* Addon Motors Status Grid */}

Addon Motors

-
+
Stepper 3
+
+ Stepper 5 +
+ + {state?.stepper_state?.stepper5_mode || "Standby"} + +
diff --git a/electron/src/machines/winder/gluetex/gluetexNamespace.ts b/electron/src/machines/winder/gluetex/gluetexNamespace.ts index 576a61b3..49772c98 100644 --- a/electron/src/machines/winder/gluetex/gluetexNamespace.ts +++ b/electron/src/machines/winder/gluetex/gluetexNamespace.ts @@ -305,6 +305,7 @@ export const stateEventDataSchema = z.object({ connected_machine_state: connectedMachineStateSchema, addon_motor_3_state: addonMotorStateSchema, addon_motor_4_state: addonMotorStateSchema, + addon_motor_5_state: addonMotorStateSchema, slave_puller_state: slavePullerStateSchema, addon_tension_arm_state: tensionArmStateSchema, tension_arm_monitor_state: tensionArmMonitorStateSchema, @@ -331,11 +332,14 @@ export type MotorRatiosState = { stepper3_slave: number; stepper4_master: number; stepper4_slave: number; + stepper5_master: number; + stepper5_slave: number; }; export type StepperState = { stepper3_mode: StepperMode; stepper4_mode: StepperMode; + stepper5_mode: StepperMode; }; /** @@ -344,12 +348,15 @@ export type StepperState = { function getMotorRatiosFromBackend( motor3: z.infer, motor4: z.infer, + motor5: z.infer, ): MotorRatiosState { return { stepper3_master: motor3.master_ratio, stepper3_slave: motor3.slave_ratio, stepper4_master: motor4.master_ratio, stepper4_slave: motor4.slave_ratio, + stepper5_master: motor5.master_ratio, + stepper5_slave: motor5.slave_ratio, }; } @@ -371,6 +378,15 @@ function getStepper4ModeFromBackend( return motor4.enabled ? "Run" : "Standby"; } +/** + * Helper to determine stepper5 mode from backend enabled state + */ +function getStepper5ModeFromBackend( + motor5: z.infer, +): StepperMode { + return motor5.enabled ? "Run" : "Standby"; +} + /** * Helper to determine heating mode from backend enabled state */ @@ -516,10 +532,13 @@ const DEFAULT_ADDON_STATE = { stepper3_slave: 1.0, stepper4_master: 1.0, stepper4_slave: 1.0, + stepper5_master: 1.0, + stepper5_slave: 1.0, }, stepper_state: { stepper3_mode: "Standby" as StepperMode, stepper4_mode: "Standby" as StepperMode, + stepper5_mode: "Standby" as StepperMode, }, heating_state: { heating_mode: "Standby" as HeatingMode, @@ -607,6 +626,7 @@ export function gluetexMessageHandler( const motorRatiosState = getMotorRatiosFromBackend( stateEvent.data.addon_motor_3_state, stateEvent.data.addon_motor_4_state, + stateEvent.data.addon_motor_5_state, ); // Derive stepper modes from backend enabled state @@ -616,6 +636,9 @@ export function gluetexMessageHandler( const stepper4Mode = getStepper4ModeFromBackend( stateEvent.data.addon_motor_4_state, ); + const stepper5Mode = getStepper5ModeFromBackend( + stateEvent.data.addon_motor_5_state, + ); // Derive heating mode from backend enabled state const heatingMode = getHeatingModeFromBackend( @@ -631,6 +654,7 @@ export function gluetexMessageHandler( stepper_state: { stepper3_mode: stepper3Mode, stepper4_mode: stepper4Mode, + stepper5_mode: stepper5Mode, }, heating_state: { heating_mode: heatingMode, diff --git a/electron/src/machines/winder/gluetex/useGluetex.ts b/electron/src/machines/winder/gluetex/useGluetex.ts index c1dab6bd..57fba138 100644 --- a/electron/src/machines/winder/gluetex/useGluetex.ts +++ b/electron/src/machines/winder/gluetex/useGluetex.ts @@ -218,6 +218,17 @@ export function useGluetex() { z.object({ SetAddonMotor4SlaveRatio: z.number() }), ); + // Addon Motor 5 mutations + const { request: requestAddonMotor5SetEnabled } = useMachineMutation( + z.object({ SetAddonMotor5Enabled: z.boolean() }), + ); + const { request: requestAddonMotor5SetMasterRatio } = useMachineMutation( + z.object({ SetAddonMotor5MasterRatio: z.number() }), + ); + const { request: requestAddonMotor5SetSlaveRatio } = useMachineMutation( + z.object({ SetAddonMotor5SlaveRatio: z.number() }), + ); + // Slave Puller mutations const { request: requestSlavePullerSetEnabled } = useMachineMutation( z.object({ SetSlavePullerEnabled: z.boolean() }), @@ -818,6 +829,47 @@ export function useGluetex() { ); }; + const setStepper5Mode = (mode: StepperMode) => { + const enabled = mode === "Run"; + updateStateOptimistically( + (current) => { + current.data.stepper_state.stepper5_mode = mode; + }, + () => { + requestAddonMotor5SetEnabled({ + machine_identification_unique: machineIdentification, + data: { SetAddonMotor5Enabled: enabled }, + }); + }, + ); + }; + + const setStepper5Master = (value: number) => { + updateStateOptimistically( + (current) => { + current.data.motor_ratios_state.stepper5_master = value; + }, + () => + requestAddonMotor5SetMasterRatio({ + machine_identification_unique: machineIdentification, + data: { SetAddonMotor5MasterRatio: value }, + }), + ); + }; + + const setStepper5Slave = (value: number) => { + updateStateOptimistically( + (current) => { + current.data.motor_ratios_state.stepper5_slave = value; + }, + () => + requestAddonMotor5SetSlaveRatio({ + machine_identification_unique: machineIdentification, + data: { SetAddonMotor5SlaveRatio: value }, + }), + ); + }; + // ========== Slave Puller Functions ========== const setSlavePullerEnabled = (enabled: boolean) => { @@ -1194,6 +1246,7 @@ export function useGluetex() { // Addon action functions (local only) setStepper3Mode, setStepper4Mode, + setStepper5Mode, setHeatingMode, setTemperature1Min, setTemperature1Max, @@ -1203,5 +1256,7 @@ export function useGluetex() { setStepper3Slave, setStepper4Master, setStepper4Slave, + setStepper5Master, + setStepper5Slave, }; } diff --git a/machines/src/gluetex/act.rs b/machines/src/gluetex/act.rs index 3a7a2197..7d7686c3 100644 --- a/machines/src/gluetex/act.rs +++ b/machines/src/gluetex/act.rs @@ -21,6 +21,7 @@ impl MachineAct for Gluetex { // sync addon motor speeds self.sync_addon_motor_3_speed(now); self.sync_addon_motor_4_speed(now); + self.sync_addon_motor_5_speed(now); // sync the traverse speed self.sync_traverse_speed(); diff --git a/machines/src/gluetex/api.rs b/machines/src/gluetex/api.rs index 59c66574..d478a53a 100644 --- a/machines/src/gluetex/api.rs +++ b/machines/src/gluetex/api.rs @@ -139,10 +139,13 @@ pub enum Mutation { // Addon Motors SetAddonMotor3Enabled(bool), SetAddonMotor4Enabled(bool), + SetAddonMotor5Enabled(bool), SetAddonMotor3MasterRatio(f64), SetAddonMotor3SlaveRatio(f64), SetAddonMotor4MasterRatio(f64), SetAddonMotor4SlaveRatio(f64), + SetAddonMotor5MasterRatio(f64), + SetAddonMotor5SlaveRatio(f64), // Slave Puller SetSlavePullerEnabled(bool), @@ -235,6 +238,8 @@ pub struct StateEvent { pub addon_motor_3_state: AddonMotorState, /// addon motor 4 state pub addon_motor_4_state: AddonMotorState, + /// addon motor 5 state + pub addon_motor_5_state: AddonMotorState, /// slave puller state pub slave_puller_state: SlavePullerState, /// addon tension arm state @@ -563,6 +568,18 @@ impl MachineApi for Gluetex { self.addon_motor_4_controller.set_slave_ratio(ratio); self.emit_state(); } + Mutation::SetAddonMotor5Enabled(enabled) => { + self.addon_motor_5_controller.set_enabled(enabled); + self.emit_state(); + } + Mutation::SetAddonMotor5MasterRatio(ratio) => { + self.addon_motor_5_controller.set_master_ratio(ratio); + self.emit_state(); + } + Mutation::SetAddonMotor5SlaveRatio(ratio) => { + self.addon_motor_5_controller.set_slave_ratio(ratio); + self.emit_state(); + } Mutation::SetSlavePullerEnabled(enabled) => { self.slave_puller_user_enabled = enabled; // Re-apply the current mode to update the controller state diff --git a/machines/src/gluetex/emit.rs b/machines/src/gluetex/emit.rs index f9e73336..29b7ba5e 100644 --- a/machines/src/gluetex/emit.rs +++ b/machines/src/gluetex/emit.rs @@ -450,6 +450,11 @@ impl Gluetex { master_ratio: self.addon_motor_4_controller.get_master_ratio(), slave_ratio: self.addon_motor_4_controller.get_slave_ratio(), }, + addon_motor_5_state: api::AddonMotorState { + enabled: self.addon_motor_5_controller.is_enabled(), + master_ratio: self.addon_motor_5_controller.get_master_ratio(), + slave_ratio: self.addon_motor_5_controller.get_slave_ratio(), + }, slave_puller_state: api::SlavePullerState { enabled: self.slave_puller_user_enabled, forward: self.slave_puller_speed_controller.get_forward(), diff --git a/machines/src/gluetex/mod.rs b/machines/src/gluetex/mod.rs index 03afcb70..b2360bdc 100644 --- a/machines/src/gluetex/mod.rs +++ b/machines/src/gluetex/mod.rs @@ -144,6 +144,7 @@ pub struct Gluetex { // addon motors pub addon_motor_3: StepperVelocityEL70x1, pub addon_motor_4: StepperVelocityEL70x1, + pub addon_motor_5: StepperVelocityEL70x1, // controllers pub traverse_controller: TraverseController, @@ -152,6 +153,7 @@ pub struct Gluetex { // addon motor controllers pub addon_motor_3_controller: AddonMotorController, pub addon_motor_4_controller: AddonMotorController, + pub addon_motor_5_controller: AddonMotorController, // temperature controllers (PID-controlled heaters with temperature sensors) pub temperature_controller_1: TemperatureController, @@ -424,6 +426,14 @@ impl Gluetex { .sync_motor_speed(&mut self.addon_motor_4, puller_angular_velocity); } + /// Sync addon motor 5 speed based on puller angular velocity and ratio + /// called by `act` + pub fn sync_addon_motor_5_speed(&mut self, t: Instant) { + let puller_angular_velocity = self.puller_speed_controller.calc_angular_velocity(t); + self.addon_motor_5_controller + .sync_motor_speed(&mut self.addon_motor_5, puller_angular_velocity); + } + /// Sync slave puller speed based on master puller speed and slave tension arm /// called by `act` pub fn sync_slave_puller_speed(&mut self, t: Instant) { diff --git a/machines/src/gluetex/new.rs b/machines/src/gluetex/new.rs index b1111815..09bb7ace 100644 --- a/machines/src/gluetex/new.rs +++ b/machines/src/gluetex/new.rs @@ -333,6 +333,40 @@ impl MachineNewTrait for Gluetex { device.0 }; + // Role 11: Addon Motor 5 EL7031-0030 + let el7031_addon5 = { + let device = get_ethercat_device::( + hardware, + params, + 11, + vec![EL7031_0030_IDENTITY_A, EL7031_IDENTITY_A, EL7031_IDENTITY_B], + ) + .await?; + + let el7031_0030_config = EL7031_0030Configuration { + stm_features: el7031_0030::coe::StmFeatures { + operation_mode: EL70x1OperationMode::DirectVelocity, + speed_range: shared_config::el70x1::EL70x1SpeedRange::Steps1000, + ..Default::default() + }, + stm_motor: StmMotorConfiguration { + max_current: 1500, + ..Default::default() + }, + pdo_assignment: EL7031_0030PredefinedPdoAssignment::VelocityControlCompact, + ..Default::default() + }; + + device + .0 + .write() + .await + .write_config(&device.1, &el7031_0030_config) + .await?; + + device.0 + }; + // Digital outputs for SSR control (24V to external SSRs for 60W heaters) let heater_ssr_1 = DigitalOutput::new(el2008.clone(), EL2008Port::DO1); let heater_ssr_2 = DigitalOutput::new(el2008.clone(), EL2008Port::DO2); @@ -468,6 +502,10 @@ impl MachineNewTrait for Gluetex { el7031_addon4.clone(), EL7031_0030StepperPort::STM1, ), + addon_motor_5: StepperVelocityEL70x1::new( + el7031_addon5, + EL7031_0030StepperPort::STM1, + ), addon_tension_arm: TensionArm::new(AnalogInput::new( el7031_addon4, EL7031_0030AnalogInputPort::AI1, @@ -476,6 +514,8 @@ impl MachineNewTrait for Gluetex { super::addon_motor_controller::AddonMotorController::new(200), addon_motor_4_controller: super::addon_motor_controller::AddonMotorController::new(200), + addon_motor_5_controller: + super::addon_motor_controller::AddonMotorController::new(200), tension_arm: TensionArm::new(AnalogInput::new( el7031_0030, EL7031_0030AnalogInputPort::AI1,