Compare commits

...
Author SHA1 Message Date
snacj a31a21e56e Fixed Merge issues 2025-09-23 11:35:16 +02:00
snacj d2d9055903 Change Max Current of Buffer Lift 2025-09-23 11:29:19 +02:00
snacj 524708a373 Implemented Buffer state for Winder 2025-09-23 11:29:19 +02:00
snacj ae6eaf1d08 Updated End Switch Logic 2025-09-23 11:29:19 +02:00
snacj 1f4f8f7788 Fixed Formatting 2025-09-23 11:29:19 +02:00
snacj c12e4052a1 Added Digital Input switch for end Switch (Top) at Buffer 2025-09-23 11:29:19 +02:00
snacj ec3e02fb32 Changed Formatting and Removed unused imports 2025-09-23 11:28:59 +02:00
snacj f82aadd1ed Changed Mode names for Buffer + Added Hold Mode 2025-09-23 11:28:10 +02:00
snacj ed2f2f82e8 Added Comment 2025-09-23 11:28:10 +02:00
snacj 774fb2d64b Update sync of puller speed and buffer input speed & Added description to helper function 2025-09-23 11:28:10 +02:00
snacj 2d88411630 Added settings page to buffer route 2025-09-23 11:28:10 +02:00
snacj 511b67df7d Added helper function to get Connected Machine reference. --Needs testing 2025-09-23 11:28:10 +02:00
snacj 510cb64b95 Added Puller frontend functions, hooks and Controlpage for Buffer 2025-09-23 11:28:10 +02:00
snacj 9cb667a55b Implemented Puller functions in Buffer Mod 2025-09-23 11:28:10 +02:00
snacj 9594e06403 Added pullerSpeed State Event 2025-09-23 11:28:10 +02:00
snacj ed8879c1ea Renamed Function 2025-09-23 11:28:10 +02:00
snacj 52ba340917 Added call to winder on buffering 2025-09-23 11:28:10 +02:00
snacj 36618d20c0 Fixed Formatting 2025-09-23 11:28:10 +02:00
snacj 1dfc66170c Refactored to clean Act function in Winder2 2025-09-23 11:28:10 +02:00
snacj 9d1587a91f Changed updating speed logic to use JerkSpeedController 2025-09-23 11:27:48 +02:00
snacj 52da8bae6b Implemented Buffer Business Logic 2025-09-23 11:26:42 +02:00
snacj 9b2e493091 Updated Act function to implement puller sync and buffer input speed 2025-09-23 11:24:47 +02:00
snacj 0b5c734472 Added Puller Speed Controller to Buffer 2025-09-23 11:24:06 +02:00
snacj b68d18dbfb Added start of business logic for buffer lift 2025-09-23 11:24:06 +02:00
snacj 7d92d9654e Added current_input_speed state to test the calculation of buffer_lift_speed 2025-09-23 11:20:46 +02:00
snacj b30cd36eb4 Changed name buffer_tower_controller -> buffer-lift_controller 2025-09-23 11:20:46 +02:00
13 changed files with 860 additions and 70 deletions

No files matched your search

@@ -5,15 +5,33 @@ import React from "react";
import { useBuffer1 } from "./useBuffer1";
import { ControlCard } from "@/control/ControlCard";
import { SelectionGroup } from "@/control/SelectionGroup";
import { EditValue } from "@/control/EditValue";
import { TimeSeriesValueNumeric } from "@/control/TimeSeriesValue";
import { roundToDecimals } from "@/lib/decimal";
import { Label } from "@/control/Label";
export function Buffer1ControlPage() {
const { state, setBufferMode } = useBuffer1();
const {
state,
defaultState,
setBufferMode,
setCurrentInputSpeed,
pullerSpeed,
setPullerRegulationMode,
setPullerTargetSpeed,
isLoading,
isDisabled,
} = useBuffer1();
const current_input_speed =
state?.current_input_speed_state.current_input_speed ?? 0.0;
const default_speed = 0.0;
return (
<Page>
<ControlGrid>
<ControlCard className="bg-red" title="Mode">
<SelectionGroup<"Standby" | "FillingBuffer" | "EmptyingBuffer">
<SelectionGroup<"Standby" | "Hold" | "Filling" | "Emptying">
value={state?.mode_state.mode}
orientation="vertical"
className="grid h-full grid-cols-2 gap-2"
@@ -24,13 +42,19 @@ export function Buffer1ControlPage() {
isActiveClassName: "bg-green-600",
className: "h-full",
},
FillingBuffer: {
children: "FillingBuffer",
Hold: {
children: "Hold",
icon: "lu:Pause",
isActiveClassName: "bg-green-600",
className: "h-full",
},
Filling: {
children: "Filling",
icon: "lu:Flame",
isActiveClassName: "bg-green-600",
className: "h-full",
},
EmptyingBuffer: {
Emptying: {
children: "EmptyingBuffer",
icon: "lu:ArrowBigLeftDash",
isActiveClassName: "bg-green-600",
@@ -40,6 +64,60 @@ export function Buffer1ControlPage() {
onChange={setBufferMode}
/>
</ControlCard>
<ControlCard title="Set Current Input Speed - (For Debugging)">
<EditValue
title="Input Speed"
unit="m/min"
value={current_input_speed}
defaultValue={default_speed}
min={0.0}
max={60.0}
step={0.1}
renderValue={(value) => roundToDecimals(value, 1)}
onChange={setCurrentInputSpeed}
/>
</ControlCard>
<ControlCard className="bg-red" title="Puller">
<TimeSeriesValueNumeric
label="Speed"
unit="m/min"
timeseries={pullerSpeed}
renderValue={(value) => roundToDecimals(value, 1)}
/>
<Label label="Regulation">
<SelectionGroup
value={state?.puller_state?.regulation}
options={{
Speed: {
children: "Speed",
icon: "lu:Gauge",
},
Diameter: {
children: "Diameter",
icon: "lu:Sun",
disabled: true,
},
}}
onChange={setPullerRegulationMode}
disabled={isDisabled}
loading={isLoading}
/>
</Label>
<Label label="Target Speed">
<EditValue
value={state?.puller_state?.target_speed}
unit="m/min"
title="Target Speed"
defaultValue={defaultState?.puller_state?.target_speed}
min={0}
max={75}
step={0.1}
renderValue={(value) => roundToDecimals(value, 1)}
onChange={setPullerTargetSpeed}
/>
</Label>
</ControlCard>
</ControlGrid>
</Page>
);
@@ -16,23 +16,42 @@ import {
ThrottledStoreUpdater,
} from "../../../client/socketioStore";
import { MachineIdentificationUnique } from "@/machines/types";
import {
createTimeSeries,
TimeSeries,
TimeSeriesValue,
} from "@/lib/timeseries";
// ========== Event Schema Definitions ==========
/**
* Machine operation mode enum
*/
export const modeSchema = z.enum([
"Standby",
"FillingBuffer",
"EmptyingBuffer",
]);
export const modeSchema = z.enum(["Standby", "Hold", "Filling", "Emptying"]);
export type Mode = z.infer<typeof modeSchema>;
/**
* Consolidated live values event schema (30FPS data)
*/
export const liveValuesEventDataSchema = z.object({});
export const liveValuesEventDataSchema = z.object({
puller_speed: z.number(),
});
/**
* Puller regulation type enum
*/
export const pullerRegulationSchema = z.enum(["Speed", "Diameter"]);
export type PullerRegulation = z.infer<typeof pullerRegulationSchema>;
/**
* Puller state event schmema
*/
export const pullerStateSchema = z.object({
regulation: pullerRegulationSchema,
target_speed: z.number(),
target_diameter: z.number(),
forward: z.boolean(),
});
/**
* Mode state event schema
@@ -41,6 +60,10 @@ export const modeStateSchema = z.object({
mode: modeSchema,
});
export const currentInputSpeedSchema = z.object({
current_input_speed: z.number(),
});
/**
* Connected machine state scheme
*/
@@ -65,7 +88,9 @@ export const connectedMachineStateSchema = z.object({
export const stateEventDataSchema = z.object({
mode_state: modeStateSchema,
puller_state: pullerStateSchema,
connected_machine_state: connectedMachineStateSchema,
current_input_speed_state: currentInputSpeedSchema,
});
// ========== Event Schemas with Wrappers ==========
@@ -80,8 +105,21 @@ export type StateEvent = z.infer<typeof stateEventSchema>;
export type Buffer1NamespaceStore = {
// State events (latest only)
state: StateEvent | null;
defaultState: StateEvent | null;
// Time series data for live values
pullerSpeed: TimeSeries;
};
// Constants for time durations
const TWENTY_MILLISECOND = 20;
const ONE_SECOND = 1000;
const FIVE_SECOND = 5 * ONE_SECOND;
const ONE_HOUR = 60 * 60 * ONE_SECOND;
const { initialTimeSeries: pullerSpeed, insert: addPullerSpeed } =
createTimeSeries(TWENTY_MILLISECOND, ONE_SECOND, FIVE_SECOND, ONE_HOUR);
/**
* Creates a message handler for Buffer1 namespace events with validation and appropriate caching strategies
* @param store The store to update when messages are received
@@ -111,11 +149,28 @@ export function buffer1MessageHandler(
state: stateEvent,
}));
} else if (eventName === "LiveValuesEvent") {
// Parse and validate the live values event
const liveValuesEvent = liveValuesEventSchema.parse(event);
const timestamp = event.ts;
updateStore((state) => ({
...state,
}));
// Extract values and add to time series
const { puller_speed } = liveValuesEvent.data;
const timestamp = liveValuesEvent.ts;
updateStore((state) => {
const newState = { ...state };
// Add puller speed
const pullerSpeedValue: TimeSeriesValue = {
value: puller_speed,
timestamp,
};
newState.pullerSpeed = addPullerSpeed(
state.pullerSpeed,
pullerSpeedValue,
);
return newState;
});
} else {
handleUnhandledEventError(eventName);
}
@@ -134,7 +189,12 @@ export const createBuffer1NamespaceStore =
(): StoreApi<Buffer1NamespaceStore> =>
create<Buffer1NamespaceStore>(() => {
return {
// State event from server
state: null,
defaultState: null,
// Time series data for live values
pullerSpeed,
};
});
@@ -8,7 +8,13 @@ import {
import { buffer1SerialRoute } from "@/routes/routes";
import { z } from "zod";
import { useEffect, useMemo } from "react";
import { StateEvent, Mode, useBuffer1Namespace } from "./buffer1Namespace";
import {
StateEvent,
Mode,
useBuffer1Namespace,
pullerRegulationSchema,
PullerRegulation,
} from "./buffer1Namespace";
import { useStateOptimistic } from "@/lib/useStateOptimistic";
import { produce } from "immer";
import { useMachines } from "@/client/useMachines";
@@ -45,7 +51,9 @@ export function useBuffer1() {
const machine_identification_unique = machineIdentification;
// Get consolidated state and live values from namespace
const { state } = useBuffer1Namespace(machineIdentification);
const { state, defaultState, pullerSpeed } = useBuffer1Namespace(
machineIdentification,
);
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
@@ -122,10 +130,76 @@ export function useBuffer1() {
);
};
const setCurrentInputSpeed = (current_input_speed: number) => {
updateStateOptimistically(
(current) => {
current.data.current_input_speed_state.current_input_speed =
current_input_speed;
},
() =>
requestSetCurrentInputSpeed({
machine_identification_unique,
data: { SetCurrentInputSpeed: current_input_speed },
}),
);
};
const setPullerTargetSpeed = (targetSpeed: number) => {
updateStateOptimistically(
(current) => {
current.data.puller_state.target_speed = targetSpeed;
},
() =>
requestPullerSetTargetSpeed({
machine_identification_unique: machineIdentification,
data: { SetPullerTargetSpeed: targetSpeed },
}),
);
};
const setPullerTargetDiameter = (targetDiameter: number) => {
updateStateOptimistically(
(current) => {
current.data.puller_state.target_diameter = targetDiameter;
},
() =>
requestPullerSetTargetDiameter({
machine_identification_unique: machineIdentification,
data: { SetPullerTargetDiameter: targetDiameter },
}),
);
};
const setPullerRegulationMode = (regulationMode: PullerRegulation) => {
updateStateOptimistically(
(current) => {
current.data.puller_state.regulation = regulationMode;
},
() =>
requestPullerSetRegulationMode({
machine_identification_unique: machineIdentification,
data: { SetPullerRegulationMode: regulationMode },
}),
);
};
const setPullerForward = (forward: boolean) => {
updateStateOptimistically(
(current) => {
current.data.puller_state.forward = forward;
},
() =>
requestPullerSetForward({
machine_identification_unique: machineIdentification,
data: { SetPullerForward: forward },
}),
);
};
// Mutation hooks
const { request: requestBufferMode } = useMachineMutation(
z.object({
SetBufferMode: z.enum(["Standby", "FillingBuffer", "EmptyingBuffer"]),
SetBufferMode: z.enum(["Standby", "Hold", "Filling", "Emptying"]),
}),
);
@@ -141,6 +215,31 @@ export function useBuffer1() {
}),
);
const { request: requestSetCurrentInputSpeed } = useMachineMutation(
z.object({
SetCurrentInputSpeed: z.number(),
}),
);
const { request: requestPullerSetTargetSpeed } = useMachineMutation(
z.object({ SetPullerTargetSpeed: z.number() }),
);
const { request: requestPullerSetTargetDiameter } = useMachineMutation(
z.object({ SetPullerTargetDiameter: z.number() }),
);
const { request: requestPullerSetRegulationMode } = useMachineMutation(
z.object({
SetPullerRegulationMode: pullerRegulationSchema,
}),
);
const { request: requestPullerSetForward } = useMachineMutation(
z.object({ SetPullerForward: z.boolean() }),
);
// Calculate loading states
const isLoading = stateOptimistic.isOptimistic;
const isDisabled = !stateOptimistic.isInitialized;
// General Helper functions
const machines = useMachines();
// Filter machines for the correct type
@@ -176,11 +275,24 @@ export function useBuffer1() {
filteredMachines,
selectedMachine,
// Default state for initial values
defaultState: defaultState?.data,
// Individual live values (TimeSeries)
pullerSpeed,
// Loading states
isLoading,
isDisabled,
// Action functions (verb-first)
setBufferMode,
setConnectedMachine,
disconnectMachine,
setCurrentInputSpeed,
setPullerTargetSpeed,
setPullerTargetDiameter,
setPullerRegulationMode,
setPullerForward,
};
}
+10 -7
View File
@@ -188,18 +188,18 @@ export const buffer1ControlRoute = createRoute({
component: () => <Buffer1ControlPage />,
});
export const mock1PresetsRoute = createRoute({
getParentRoute: () => mock1SerialRoute,
path: "presets",
component: () => <Mock1PresetsPage />,
});
export const buffer1SettingsRoute = createRoute({
getParentRoute: () => buffer1SerialRoute,
path: "settings",
component: () => <Buffer1SettingsPage />,
});
export const mock1PresetsRoute = createRoute({
getParentRoute: () => mock1SerialRoute,
path: "presets",
component: () => <Mock1PresetsPage />,
});
export const setupRoute = createRoute({
getParentRoute: () => sidebarRoute,
path: "setup",
@@ -315,7 +315,10 @@ export const rootTree = RootRoute.addChildren([
mock1PresetsRoute,
]),
buffer1SerialRoute.addChildren([buffer1ControlRoute]),
buffer1SerialRoute.addChildren([
buffer1ControlRoute,
buffer1SettingsRoute,
]),
]),
]),
]);
+6
View File
@@ -4,6 +4,12 @@ use super::BufferV1;
use control_core::machines::new::MachineAct;
impl MachineAct for BufferV1 {
fn act(&mut self, now: Instant) {
// sync the lift speed
self.sync_lift_speed(now);
// sync the puller speed
self.sync_puller_speed(now);
// if last measurement is older than 1 second, emit a new measurement
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
// Emit live values at 30 FPS
+46 -3
View File
@@ -1,5 +1,7 @@
use std::{sync::Arc, time::Duration};
use crate::machines::buffer1::puller_speed_controller::PullerRegulationMode;
use super::{BufferV1, BufferV1Mode};
use control_core::{
machines::{api::MachineApi, identification::MachineIdentificationUnique},
@@ -18,7 +20,10 @@ use socketioxide::extract::SocketRef;
use tracing::instrument;
#[derive(Serialize, Debug, Clone, Default)]
pub struct LiveValuesEvent {}
pub struct LiveValuesEvent {
/// puller speed in m/min
pub puller_speed: f64,
}
impl LiveValuesEvent {
pub fn build(&self) -> Event<Self> {
@@ -30,8 +35,12 @@ impl LiveValuesEvent {
pub struct StateEvent {
/// mode state
pub mode_state: ModeState,
/// puller state
pub puller_state: PullerState,
/// connected machine state
pub connected_machine_state: ConnectedMachineState,
/// CurrentInputSpeed
pub current_input_speed_state: CurrentInputSpeedState,
}
impl StateEvent {
@@ -53,8 +62,9 @@ pub struct ModeState {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Mode {
Standby,
FillingBuffer,
EmptyingBuffer,
Hold,
Filling,
Emptying,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
@@ -64,6 +74,23 @@ pub struct ConnectedMachineState {
pub is_available: bool,
}
#[derive(Serialize, Debug, Clone)]
pub struct PullerState {
/// regulation type
pub regulation: PullerRegulationMode,
/// target speed in m/min
pub target_speed: f64,
/// target diameter in mm
pub target_diameter: f64,
/// forward rotation direction
pub forward: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct CurrentInputSpeedState {
pub current_input_speed: f64,
}
#[derive(Deserialize, Serialize)]
enum Mutation {
// Mode
@@ -74,6 +101,16 @@ enum Mutation {
// Disconnect Machine
DisconnectMachine(MachineIdentificationUnique),
// Set current input speed
SetCurrentInputSpeed(f64),
// Puller
/// on = speed, off = stop
SetPullerRegulationMode(PullerRegulationMode),
SetPullerTargetSpeed(f64),
SetPullerTargetDiameter(f64),
SetPullerForward(bool),
}
#[derive(Debug)]
@@ -128,6 +165,12 @@ impl MachineApi for BufferV1 {
Mutation::DisconnectMachine(machine_identification_unique) => {
self.disconnect_winder(machine_identification_unique);
}
// Puller Mutations
Mutation::SetCurrentInputSpeed(speed) => self.set_current_input_speed(speed),
Mutation::SetPullerRegulationMode(regulation) => self.puller_set_regulation(regulation),
Mutation::SetPullerTargetSpeed(value) => self.puller_set_target_speed(value),
Mutation::SetPullerTargetDiameter(_) => todo!(),
Mutation::SetPullerForward(value) => self.puller_set_forward(value),
}
Ok(())
}
@@ -1,27 +1,117 @@
use std::time::Instant;
use control_core::{
controllers::second_degree_motion::linear_jerk_speed_controller::LinearJerkSpeedController,
converters::linear_step_converter::LinearStepConverter,
uom_extensions::{
acceleration::meter_per_minute_per_second, jerk::meter_per_minute_per_second_squared,
velocity::meter_per_minute,
},
};
use ethercat_hal::io::stepper_velocity_el70x1::StepperVelocityEL70x1;
use uom::{
ConstZero,
si::{
f64::{Acceleration, Jerk, Velocity},
velocity::millimeter_per_second,
},
};
#[derive(Debug)]
pub struct BufferTowerController {
/// Whether the speed controller is enabled or not
enabled: bool,
/// Stepper driver. Controls buffer stepper motor
pub stepper_driver: StepperVelocityEL70x1,
// Step Converter
pub converter: LinearStepConverter,
/// Linear acceleration controller to dampen speed change
acceleration_controller: LinearJerkSpeedController,
/// Forward rotation direction. If false, applies negative sign to speed
pub forward: bool,
/// Fixed constants
spool_amount: u8,
/// Variables
current_input_speed: Velocity,
target_output_speed: Velocity,
lift_speed: Velocity,
}
impl BufferTowerController {
pub const fn new(driver: StepperVelocityEL70x1) -> Self {
let acceleration = Acceleration::new::<meter_per_minute_per_second>(5.0);
let jerk = Jerk::new::<meter_per_minute_per_second_squared>(10.0);
let speed = Velocity::new::<meter_per_minute>(50.0);
Self {
enabled: false,
stepper_driver: driver,
}
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
if enabled {
self.stepper_driver.set_enabled(true);
let _ = self.stepper_driver.set_speed(10.0);
} else {
self.stepper_driver.set_enabled(false);
let _ = self.stepper_driver.set_speed(0.0);
spool_amount: 13,
converter,
forward: true,
acceleration_controller: LinearJerkSpeedController::new_simple(
Some(speed),
acceleration,
jerk,
),
current_input_speed: Velocity::ZERO,
target_output_speed: Velocity::ZERO,
lift_speed: Velocity::ZERO,
}
}
}
impl BufferTowerontroller {
/// Calculate the speed of the buffer lift from current input speed
///
/// Formula: input_speed / ( 2 * spool_amount )
pub fn calculate_buffer_lift_speed(&mut self) -> Velocity {
self.lift_speed = Velocity::new::<millimeter_per_second>(
(self.current_input_speed.get::<millimeter_per_second>()
- self.target_output_speed.get::<millimeter_per_second>())
/ (2.0 * self.spool_amount as f64),
);
self.lift_speed
}
pub fn update_speed(&mut self, t: Instant) -> Velocity {
let speed = match self.enabled {
true => self.calculate_buffer_lift_speed(),
false => Velocity::ZERO,
};
let speed = if self.forward { speed } else { -speed };
self.acceleration_controller.update(speed, t)
}
}
impl BufferTowerController {
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
self.stepper_driver.set_enabled(enabled);
}
pub fn set_forward(&mut self, forward: bool) {
self.forward = forward;
}
pub fn set_current_input_speed(&mut self, speed: f64) {
self.current_input_speed = Velocity::new::<meter_per_minute>(speed);
}
pub fn set_target_output_speed(&mut self, speed: f64) {
self.target_output_speed = Velocity::new::<meter_per_minute>(speed);
}
pub fn get_current_input_speed(&self) -> Velocity {
self.current_input_speed
}
pub fn get_target_output_speed(&self) -> Velocity {
self.target_output_speed
}
pub fn get_lift_speed(&self) -> Velocity {
self.lift_speed
}
}
+221 -20
View File
@@ -2,34 +2,58 @@ pub mod act;
pub mod api;
pub mod buffer_tower_controller;
pub mod new;
pub mod puller_speed_controller;
use api::{Buffer1Namespace, BufferV1Events, LiveValuesEvent, ModeState, StateEvent};
use buffer_tower_controller::BufferTowerController;
use control_core::{
converters::linear_step_converter::LinearStepConverter,
machines::{
ConnectedMachine, ConnectedMachineData, downcast_machine,
identification::{MachineIdentification, MachineIdentificationUnique},
manager::MachineManager,
},
socketio::namespace::NamespaceCacheingLogic,
uom_extensions::velocity::meter_per_minute,
};
use control_core_derive::Machine;
use ethercat_hal::io::{
digital_input::DigitalInput, stepper_velocity_el70x1::StepperVelocityEL70x1,
};
use futures::executor::block_on;
use puller_speed_controller::PullerRegulationMode;
use serde::{Deserialize, Serialize};
use smol::lock::{Mutex, RwLock};
use std::{
sync::{Arc, Weak},
time::Instant,
};
use uom::si::{
f64::{Length, Velocity},
length::millimeter,
};
use crate::machines::{
MACHINE_BUFFER_V1, VENDOR_QITECH, buffer1::api::ConnectedMachineState, winder2::Winder2,
MACHINE_BUFFER_V1, VENDOR_QITECH,
buffer1::{
api::{ConnectedMachineState, CurrentInputSpeedState, PullerState},
puller_speed_controller::PullerSpeedController,
},
winder2::{BufferState, Winder2, Winder2Mode},
};
#[derive(Debug, Machine)]
pub struct BufferV1 {
// drivers
pub lift: StepperVelocityEL70x1,
pub lift_end_stop: DigitalInput,
pub puller: StepperVelocityEL70x1,
// controllers
pub buffer_tower_controller: BufferTowerController,
pub puller_speed_controller: PullerSpeedController,
pub lift_step_converter: LinearStepConverter,
// socketio
namespace: Buffer1Namespace,
@@ -61,7 +85,20 @@ impl BufferV1 {
impl BufferV1 {
pub fn emit_live_values(&mut self) {
let live_values = LiveValuesEvent {};
// Calculate puller speed from current motor steps
let steps_per_second = self.puller.get_speed();
let angular_velocity = self
.puller_speed_controller
.converter
.steps_to_angular_velocity(steps_per_second as f64);
let puller_speed = self
.puller_speed_controller
.angular_velocity_to_speed(angular_velocity);
// live values to be emittet
let live_values = LiveValuesEvent {
puller_speed: puller_speed.get::<meter_per_minute>(),
};
let event = live_values.build();
self.namespace.emit(BufferV1Events::LiveValues(event));
@@ -86,6 +123,24 @@ impl BufferV1 {
})
.unwrap_or(false),
},
current_input_speed_state: CurrentInputSpeedState {
current_input_speed: self
.buffer_lift_controller
.get_current_input_speed()
.get::<meter_per_minute>(),
},
puller_state: PullerState {
regulation: self.puller_speed_controller.regulation_mode.clone(),
target_speed: self
.puller_speed_controller
.target_speed
.get::<meter_per_minute>(),
target_diameter: self
.puller_speed_controller
.target_diameter
.get::<millimeter>(),
forward: self.puller_speed_controller.forward,
},
};
let event = state.build();
@@ -94,45 +149,85 @@ impl BufferV1 {
}
impl BufferV1 {
// To be implemented
fn fill_buffer(&mut self) {
todo!();
// stop the winder until the buffer is ful
self.update_winder2_mode(Winder2Mode::Hold);
self.update_winder2_buffer_state(BufferState::Buffering);
}
fn empty_buffer(&mut self) {
todo!();
// Set the winder2 to a mode where its faster than before to empty the buffer slowly
self.update_winder2_mode(Winder2Mode::Pull);
self.update_winder2_buffer_state(BufferState::Emptying);
}
fn update_winder2_mode(&mut self, mode: Winder2Mode) {
self.get_winder(|winder2| {
if winder2.mode != mode {
winder2.mode = mode;
}
});
}
fn update_winder2_buffer_state(&mut self, state: BufferState) {
self.get_winder(|winder2| {
if winder2.buffer_state != state {
winder2.set_buffer_state(state);
}
});
}
// Turn off motor and do nothing
fn switch_to_standby(&mut self) {
match self.mode {
BufferV1Mode::Standby => (),
BufferV1Mode::FillingBuffer => {}
BufferV1Mode::EmptyingBuffer => {}
BufferV1Mode::Hold => {}
BufferV1Mode::Filling => {}
BufferV1Mode::Emptying => {}
};
self.mode = BufferV1Mode::Standby;
self.buffer_tower_controller.set_enabled(false);
self.buffer_lift_controller.set_enabled(false);
let _ = self.buffer_lift_controller.stepper_driver.set_speed(0.0);
self.update_winder2_buffer_state(BufferState::Hold);
}
// hold motor
fn switch_to_hold(&mut self) {
match self.mode {
BufferV1Mode::Standby => {}
BufferV1Mode::Hold => (),
BufferV1Mode::Filling => {}
BufferV1Mode::Emptying => {}
};
self.mode = BufferV1Mode::Hold;
self.buffer_lift_controller.set_enabled(false);
let _ = self.buffer_lift_controller.stepper_driver.set_speed(0.0);
self.update_winder2_buffer_state(BufferState::Hold);
}
// Turn on motor and fill buffer
fn switch_to_filling(&mut self) {
match self.mode {
BufferV1Mode::Standby => self.fill_buffer(),
BufferV1Mode::FillingBuffer => (),
BufferV1Mode::EmptyingBuffer => {}
BufferV1Mode::Hold => {}
BufferV1Mode::Filling => (),
BufferV1Mode::Emptying => {}
};
self.mode = BufferV1Mode::FillingBuffer;
self.buffer_tower_controller.set_enabled(true);
self.mode = BufferV1Mode::Filling;
self.buffer_lift_controller.set_forward(true);
self.buffer_lift_controller.set_enabled(true);
}
// Turn on motor reverse and empty buffer
fn switch_to_emptying(&mut self) {
match self.mode {
BufferV1Mode::Standby => self.empty_buffer(),
BufferV1Mode::FillingBuffer => {}
BufferV1Mode::EmptyingBuffer => (),
BufferV1Mode::Hold => {}
BufferV1Mode::Filling => {}
BufferV1Mode::Emptying => (),
};
self.mode = BufferV1Mode::EmptyingBuffer;
self.mode = BufferV1Mode::Emptying;
self.buffer_lift_controller.set_forward(false);
}
fn switch_mode(&mut self, mode: BufferV1Mode) {
@@ -142,8 +237,9 @@ impl BufferV1 {
match mode {
BufferV1Mode::Standby => self.switch_to_standby(),
BufferV1Mode::FillingBuffer => self.switch_to_filling(),
BufferV1Mode::EmptyingBuffer => self.switch_to_emptying(),
BufferV1Mode::Hold => self.switch_to_hold(),
BufferV1Mode::Filling => self.switch_to_filling(),
BufferV1Mode::Emptying => self.switch_to_emptying(),
}
}
}
@@ -153,9 +249,91 @@ impl BufferV1 {
self.switch_mode(mode);
self.emit_state();
}
fn set_current_input_speed(&mut self, speed: f64) {
// speed comes as a f64 represents m/min
self.buffer_lift_controller.set_current_input_speed(speed);
self.emit_state();
}
}
/// Connecting/Disconnecting machine
// Implement Puller
impl BufferV1 {
/// called by `act`
pub fn sync_puller_speed(&mut self, t: Instant) {
let angular_velocity = self.puller_speed_controller.calc_angular_velocity(t);
let steps_per_second = self
.puller_speed_controller
.converter
.angular_velocity_to_steps(angular_velocity);
let _ = self.puller.set_speed(steps_per_second);
// sync puller speed to lift input speed
let linear_velocity = self
.puller_speed_controller
.converter
.angular_velocity_to_velocity(angular_velocity);
self.buffer_lift_controller
.set_current_input_speed(linear_velocity.get::<meter_per_minute>());
}
pub fn puller_set_regulation(&mut self, puller_regulation_mode: PullerRegulationMode) {
self.puller_speed_controller
.set_regulation_mode(puller_regulation_mode);
self.emit_state();
}
/// Set target speed in m/min
pub fn puller_set_target_speed(&mut self, target_speed: f64) {
// Convert m/min to velocity
let target_speed = Velocity::new::<meter_per_minute>(target_speed);
self.puller_speed_controller.set_target_speed(target_speed);
self.emit_state();
}
/// Set target diameter in mm
pub fn puller_set_target_diameter(&mut self, target_diameter: f64) {
// Convert mm to length
let target_diameter = Length::new::<millimeter>(target_diameter);
self.puller_speed_controller
.set_target_diameter(target_diameter);
self.emit_state();
}
/// Set forward direction
pub fn puller_set_forward(&mut self, forward: bool) {
self.puller_speed_controller.set_forward(forward);
self.emit_state();
}
}
// Implement Lift
impl BufferV1 {
pub fn sync_lift_speed(&mut self, t: Instant) {
let linear_velocity = self.buffer_lift_controller.update_speed(t);
if self.can_move() {
let steps_per_second = self.lift_step_converter.velocity_to_steps(linear_velocity);
let _ = self.lift.set_speed(steps_per_second);
} else {
let _ = self.lift.set_speed(0.0);
}
}
fn can_move(&mut self) -> bool {
match self.lift_end_stop.get_value() {
Ok(reached) => {
if reached {
false
} else {
true
}
}
Err(_) => false,
}
}
}
// Connecting/Disconnecting machine
impl BufferV1 {
/// set connected winder
pub fn set_connected_winder(