Add support for Addon Motor 5 in Gluetex machine, including state management and UI integration
This commit is contained in:
1 parent
a5da005f7b
commit
d1f07e8d01
10 files changed
+222
-1
No files matched your search
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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() {
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<Label label="Stepper 5">
|
||||
<SelectionGroup<StepperMode>
|
||||
value={state?.stepper_state?.stepper5_mode}
|
||||
disabled={isDisabled}
|
||||
loading={isLoading}
|
||||
onChange={setStepper5Mode}
|
||||
orientation="horizontal"
|
||||
className="grid grid-cols-2 gap-2"
|
||||
options={{
|
||||
Standby: {
|
||||
children: "Standby",
|
||||
icon: "lu:Power",
|
||||
isActiveClassName: "bg-green-600",
|
||||
},
|
||||
Run: {
|
||||
children: "Run",
|
||||
icon: "lu:Play",
|
||||
isActiveClassName: "bg-green-600",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
</ControlCard>
|
||||
|
||||
<ControlCard className="bg-red" title="Heating Mode">
|
||||
@@ -320,6 +345,17 @@ export function GluetexAddonsPage() {
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<Label label="Stepper 5 Ratio">
|
||||
<RatioInput
|
||||
master={state?.motor_ratios_state?.stepper5_master}
|
||||
slave={state?.motor_ratios_state?.stepper5_slave}
|
||||
title="Stepper 5 Motor Ratio"
|
||||
onRatioChange={(master, slave) => {
|
||||
setStepper5Master(master);
|
||||
setStepper5Slave(slave);
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
</ControlCard>
|
||||
|
||||
<ControlCard title="Addon Tension Arm">
|
||||
|
||||
@@ -200,7 +200,7 @@ export function GluetexOverviewPage() {
|
||||
{/* Addon Motors Status Grid */}
|
||||
<div>
|
||||
<h3 className="mb-2 text-lg font-semibold">Addon Motors</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="flex flex-col items-center gap-2 rounded-lg border p-3">
|
||||
<span className="text-sm font-medium">Stepper 3</span>
|
||||
<div
|
||||
@@ -225,6 +225,18 @@ export function GluetexOverviewPage() {
|
||||
{state?.stepper_state?.stepper4_mode || "Standby"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2 rounded-lg border p-3">
|
||||
<span className="text-sm font-medium">Stepper 5</span>
|
||||
<div
|
||||
className={cn(
|
||||
"h-8 w-8 rounded-full",
|
||||
getStepperColor(state?.stepper_state?.stepper5_mode),
|
||||
)}
|
||||
/>
|
||||
<span className="text-xs">
|
||||
{state?.stepper_state?.stepper5_mode || "Standby"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<typeof addonMotorStateSchema>,
|
||||
motor4: z.infer<typeof addonMotorStateSchema>,
|
||||
motor5: z.infer<typeof addonMotorStateSchema>,
|
||||
): 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<typeof addonMotorStateSchema>,
|
||||
): 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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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::<EL7031_0030>(
|
||||
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,
|
||||
|
||||
Reference in new issue
Block a user