diff --git a/electron/src/machines/aquapath/aquapath1/Aquapath1ControlPage.tsx b/electron/src/machines/aquapath/aquapath1/Aquapath1ControlPage.tsx index 18cf02e6f..3d5c554e7 100644 --- a/electron/src/machines/aquapath/aquapath1/Aquapath1ControlPage.tsx +++ b/electron/src/machines/aquapath/aquapath1/Aquapath1ControlPage.tsx @@ -6,7 +6,9 @@ import { TimeSeriesValueNumeric } from "@/control/TimeSeriesValue"; import { useAquapath1 } from "./useAquapath"; import { SelectionGroup } from "@/control/SelectionGroup"; import { EditValue } from "@/control/EditValue"; +import { Badge } from "@/components/ui/badge"; import { Label } from "@/control/Label"; +import { HeatingZone } from "../../extruder/HeatingZone"; export function Aquapath1ControlPage() { const { @@ -16,8 +18,12 @@ export function Aquapath1ControlPage() { back_flow, front_temperature, back_temperature, - front_temp_reservoir, - back_temp_reservoir, + front_revolutions, + back_revolutions, + front_power, + back_power, + front_total_energy, + back_total_energy, setAquapathMode, setFrontTemperature, setBackTemperature, @@ -29,111 +35,55 @@ export function Aquapath1ControlPage() { const backTargetTemperature = state?.temperature_states?.back.target_temperature ?? 0; + const frontCurrentTemperature = + state?.temperature_states?.front.temperature ?? 0; + const backCurrentTemperature = + state?.temperature_states?.back.temperature ?? 0; + + const frontCoolingBoundary = + frontTargetTemperature + (state?.tolerance_states.front.cooling ?? 0); + const frontHeatingBoundary = + frontTargetTemperature - (state?.tolerance_states.front.heating ?? 0); + const backCoolingBoundary = + backTargetTemperature + (state?.tolerance_states.back.cooling ?? 0); + const backHeatingBoundary = + backTargetTemperature - (state?.tolerance_states.back.heating ?? 0); + const frontTargetFlow = state?.flow_states.front.should_flow ?? false; const backTargetFlow = state?.flow_states.back.should_flow ?? false; return ( - -
-
+ +
+
value.toFixed(1)} />
-
+
value.toFixed(1)} - /> -
-
- -
- - - -
- - - -
-
- value.toFixed(1)} /> -
+ +
+ + + {frontCurrentTemperature < frontHeatingBoundary && + (state?.flow_states.front.flow ?? 0) > 0 && ( + + Power: {front_power.current?.value} W
+ Total: {front_total_energy.current?.value} kWh +
+ )} + {frontCurrentTemperature > frontCoolingBoundary && + frontTargetFlow && + (state?.flow_states.front.flow ?? 0) && ( + Cooling + )} +
+ +
+ value.toFixed(1)} + /> +
+ +
+ +
+
+
+ + +
+
+ value.toFixed(1)} + />
-
+
value.toFixed(1)} /> -
+ +
+ + + {backCurrentTemperature < backHeatingBoundary && + backTargetFlow && + (state?.flow_states.front.flow ?? 0) && ( + + Power: {back_power.current?.value} W
+ Total: {back_total_energy.current?.value} kWh +
+ )} + {backCurrentTemperature > backCoolingBoundary && + frontTargetFlow && + (state?.flow_states.front.flow ?? 0) && ( + Cooling + )} +
+ +
+ value.toFixed(1)} + /> +
+ +
+
diff --git a/electron/src/machines/aquapath/aquapath1/Aquapath1Page.tsx b/electron/src/machines/aquapath/aquapath1/Aquapath1Page.tsx index 4ee39b7b6..19e3e7c76 100644 --- a/electron/src/machines/aquapath/aquapath1/Aquapath1Page.tsx +++ b/electron/src/machines/aquapath/aquapath1/Aquapath1Page.tsx @@ -20,6 +20,12 @@ export function Aquapath1Page() { icon: "lu:ChartSpline", activeLink: "graphs", }, + { + link: "settings", + title: "Config", + icon: "lu:Settings", + activeLink: "settings", + }, ]} /> ); diff --git a/electron/src/machines/aquapath/aquapath1/Aquapath1Settings.tsx b/electron/src/machines/aquapath/aquapath1/Aquapath1Settings.tsx new file mode 100644 index 000000000..66a5f6fe3 --- /dev/null +++ b/electron/src/machines/aquapath/aquapath1/Aquapath1Settings.tsx @@ -0,0 +1,121 @@ +import { Page } from "@/components/Page"; +import { ControlCard } from "@/control/ControlCard"; +import { ControlGrid } from "@/control/ControlGrid"; +import { EditValue } from "@/control/EditValue"; +import { Label } from "@/control/Label"; +import { useAquapath1 } from "./useAquapath"; +import React from "react"; + +export function Aquapath1SettingsPage() { + const { + state, + setFrontRevolutions, + setBackRevolutions, + setFrontHeatingTolerance, + setFrontCoolingTolerance, + setBackHeatingTolerance, + setBackCoolingTolerance, + } = useAquapath1(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/electron/src/machines/aquapath/aquapath1/aquapath1Namespace.ts b/electron/src/machines/aquapath/aquapath1/aquapath1Namespace.ts index 9229106f5..a75c01119 100644 --- a/electron/src/machines/aquapath/aquapath1/aquapath1Namespace.ts +++ b/electron/src/machines/aquapath/aquapath1/aquapath1Namespace.ts @@ -51,6 +51,23 @@ export const flowStatesSchema = z.object({ back: flowStateSchema, }); +export const fanStateSchema = z.object({ + revolutions: z.number(), + max_revolutions: z.number(), +}); +export const fanStatesSchema = z.object({ + back: fanStateSchema, + front: fanStateSchema, +}); + +export const toleranceStateSchema = z.object({ + heating: z.number(), + cooling: z.number(), +}); +export const toleranceStatesSchema = z.object({ + back: toleranceStateSchema, + front: toleranceStateSchema, +}); /** * Live values event schema (time-series data) */ @@ -61,6 +78,12 @@ export const liveValuesEventDataSchema = z.object({ back_temperature: z.number(), front_temp_reservoir: z.number(), back_temp_reservoir: z.number(), + front_revolutions: z.number(), + back_revolutions: z.number(), + back_power: z.number(), + front_power: z.number(), + front_total_energy: z.number(), + back_total_energy: z.number(), }); /** @@ -71,6 +94,8 @@ export const stateEventDataSchema = z.object({ mode_state: modeStateSchema, flow_states: flowStatesSchema, temperature_states: tempStatesSchema, + fan_states: fanStatesSchema, + tolerance_states: toleranceStatesSchema, }); // ========== Event Schemas with Wrappers ========== @@ -96,6 +121,15 @@ export type Aquapath1NamespaceStore = { front_temp_reservoir: TimeSeries; back_temp_reservoir: TimeSeries; + + front_revolutions: TimeSeries; + back_revolutions: TimeSeries; + + front_power: TimeSeries; + back_power: TimeSeries; + + front_total_energy: TimeSeries; + back_total_energy: TimeSeries; }; const { initialTimeSeries: front_temperature, insert: addTemperature1 } = @@ -108,6 +142,18 @@ const { initialTimeSeries: back_temp_reservoir, insert: addTempReserv2 } = createTimeSeries(); const { initialTimeSeries: front_flow, insert: addFlow1 } = createTimeSeries(); const { initialTimeSeries: back_flow, insert: addFlow2 } = createTimeSeries(); +const { initialTimeSeries: front_revolutions, insert: addFan1 } = + createTimeSeries(); +const { initialTimeSeries: back_revolutions, insert: addFan2 } = + createTimeSeries(); +const { initialTimeSeries: front_power, insert: addFrontPower } = + createTimeSeries(); +const { initialTimeSeries: back_power, insert: addBackPower } = + createTimeSeries(); +const { initialTimeSeries: front_total_energy, insert: addFrontEnergy } = + createTimeSeries(); +const { initialTimeSeries: back_total_energy, insert: addBackEnergy } = + createTimeSeries(); /** * Factory function to create a new Aquapath namespace store @@ -125,6 +171,12 @@ export const createAquapath1NamespaceStore = back_flow: back_flow, front_temp_reservoir: front_temp_reservoir, back_temp_reservoir: back_temp_reservoir, + front_revolutions: front_revolutions, + back_revolutions: back_revolutions, + back_power: back_power, + front_power: front_power, + front_total_energy: front_total_energy, + back_total_energy: back_total_energy, }; }); }; @@ -192,6 +244,30 @@ export function aquapath1MessageHandler( value: liveValuesEvent.data.back_temp_reservoir, timestamp: event.ts, }), + front_revolutions: addFan1(state.front_revolutions, { + value: liveValuesEvent.data.front_revolutions, + timestamp: event.ts, + }), + back_revolutions: addFan2(state.back_revolutions, { + value: liveValuesEvent.data.back_revolutions, + timestamp: event.ts, + }), + front_power: addFrontPower(state.front_power, { + value: liveValuesEvent.data.front_power, + timestamp: event.ts, + }), + back_power: addBackPower(state.back_power, { + value: liveValuesEvent.data.back_power, + timestamp: event.ts, + }), + front_total_energy: addFrontEnergy(state.front_total_energy, { + value: liveValuesEvent.data.front_total_energy, + timestamp: event.ts, + }), + back_total_energy: addBackEnergy(state.back_total_energy, { + value: liveValuesEvent.data.back_total_energy, + timestamp: event.ts, + }), })); } else { handleUnhandledEventError(eventName); diff --git a/electron/src/machines/aquapath/aquapath1/useAquapath.ts b/electron/src/machines/aquapath/aquapath1/useAquapath.ts index eff145fcc..16a1ac267 100644 --- a/electron/src/machines/aquapath/aquapath1/useAquapath.ts +++ b/electron/src/machines/aquapath/aquapath1/useAquapath.ts @@ -42,12 +42,18 @@ export function useAquapath1() { const { state, defaultState, - front_temperature: front_temperature, - back_temperature: back_temperature, - front_flow: front_flow, - back_flow: back_flow, - front_temp_reservoir: front_temp_reservoir, - back_temp_reservoir: back_temp_reservoir, + front_temperature, + back_temperature, + front_flow, + back_flow, + front_temp_reservoir, + back_temp_reservoir, + front_revolutions, + back_revolutions, + front_power, + back_power, + front_total_energy, + back_total_energy, } = useAquapath1Namespace(machineIdentification); // Single optimistic state for all state management @@ -125,6 +131,89 @@ export function useAquapath1() { ); }; + const setFrontRevolutions = (revolutions: number) => { + updateStateOptimistically( + (current) => { + current.data.fan_states.back.revolutions = revolutions; + }, + () => { + requestFrontRevolutions({ + machine_identification_unique: machineIdentification, + data: { SetFrontRevolutions: revolutions }, + }); + }, + ); + }; + + const setBackRevolutions = (revolutions: number) => { + updateStateOptimistically( + (current) => { + current.data.fan_states.back.revolutions = revolutions; + }, + () => + requestBackRevolutions({ + machine_identification_unique: machineIdentification, + data: { SetBackRevolutions: revolutions }, + }), + ); + }; + + const setFrontHeatingTolerance = (tolerance: number) => { + updateStateOptimistically( + (current) => { + current.data.tolerance_states.front.heating = tolerance; + }, + () => { + requestFrontHeatingTolerance({ + machine_identification_unique: machineIdentification, + data: { SetFrontHeatingTolerance: tolerance }, + }); + }, + ); + }; + + const setBackHeatingTolerance = (tolerance: number) => { + updateStateOptimistically( + (current) => { + current.data.tolerance_states.back.heating = tolerance; + }, + () => { + requestBackHeatingTolerance({ + machine_identification_unique: machineIdentification, + data: { SetBackHeatingTolerance: tolerance }, + }); + }, + ); + }; + + const setFrontCoolingTolerance = (tolerance: number) => { + updateStateOptimistically( + (current) => { + current.data.tolerance_states.front.cooling = tolerance; + }, + () => { + requestFrontCoolingTolerance({ + machine_identification_unique: machineIdentification, + data: { SetFrontCoolingTolerance: tolerance }, + }); + }, + ); + }; + + const setBackCoolingTolerance = (tolerance: number) => { + updateStateOptimistically( + (current) => { + current.data.tolerance_states.back.cooling = tolerance; + }, + () => { + requestBackCoolingTolerance({ + machine_identification_unique: machineIdentification, + data: { SetBackCoolingTolerance: tolerance }, + }); + }, + ); + }; + // Mutation hooks const { request: requestAquapathMode } = useMachineMutation( z.object({ SetAquaPathMode: z.enum(["Standby", "Auto"]) }), @@ -141,6 +230,25 @@ export function useAquapath1() { const { request: requestBackFlow } = useMachineMutation( z.object({ SetBackFlow: z.boolean() }), ); + const { request: requestFrontRevolutions } = useMachineMutation( + z.object({ SetFrontRevolutions: z.number() }), + ); + const { request: requestBackRevolutions } = useMachineMutation( + z.object({ SetBackRevolutions: z.number() }), + ); + const { request: requestFrontHeatingTolerance } = useMachineMutation( + z.object({ SetFrontHeatingTolerance: z.number() }), + ); + const { request: requestFrontCoolingTolerance } = useMachineMutation( + z.object({ SetFrontCoolingTolerance: z.number() }), + ); + const { request: requestBackHeatingTolerance } = useMachineMutation( + z.object({ SetBackHeatingTolerance: z.number() }), + ); + const { request: requestBackCoolingTolerance } = useMachineMutation( + z.object({ SetBackCoolingTolerance: z.number() }), + ); + // Helper function for optimistic updates using produce const updateStateOptimistically = ( producer: (current: StateEvent) => void, @@ -165,11 +273,23 @@ export function useAquapath1() { back_temperature, front_temp_reservoir, back_temp_reservoir, + front_revolutions, + back_revolutions, + front_power, + back_power, + front_total_energy, + back_total_energy, setAquapathMode, setFrontTemperature, setBackTemperature, setFrontFlow, setBackFlow, + setFrontRevolutions, + setBackRevolutions, + setFrontHeatingTolerance, + setBackHeatingTolerance, + setFrontCoolingTolerance, + setBackCoolingTolerance, }; } diff --git a/electron/src/routes/routes.tsx b/electron/src/routes/routes.tsx index c3e678b70..c3f449aa3 100644 --- a/electron/src/routes/routes.tsx +++ b/electron/src/routes/routes.tsx @@ -53,6 +53,7 @@ import { Mock1PresetsPage } from "@/machines/mock/mock1/Mock1PresetsPage"; import { Aquapath1ControlPage } from "@/machines/aquapath/aquapath1/Aquapath1ControlPage"; import { Aquapath1Page } from "@/machines/aquapath/aquapath1/Aquapath1Page"; import { Aquapath1GraphPage } from "@/machines/aquapath/aquapath1/Aquapath1Graph"; +import { Aquapath1SettingsPage } from "@/machines/aquapath/aquapath1/Aquapath1Settings"; import { TestMachinePage } from "@/machines/testmachine/TestMachinePage"; import { TestMachineControlPage } from "@/machines/testmachine/TestMachineControlPage"; @@ -315,6 +316,12 @@ export const aquapath1ControlRoute = createRoute({ component: () => , }); +export const aquapath1SettingsRoute = createRoute({ + getParentRoute: () => aquapath1SerialRoute, + path: "settings", + component: () => , +}); + export const buffer1SettingsRoute = createRoute({ getParentRoute: () => buffer1SerialRoute, path: "settings", @@ -445,6 +452,7 @@ export const rootTree = RootRoute.addChildren([ aquapath1SerialRoute.addChildren([ aquapath1ControlRoute, aquapath1GraphRoute, + aquapath1SettingsRoute, ]), winder2SerialRoute.addChildren([ diff --git a/machines/src/aquapath1/api.rs b/machines/src/aquapath1/api.rs index 51a2701d2..0625236a6 100644 --- a/machines/src/aquapath1/api.rs +++ b/machines/src/aquapath1/api.rs @@ -20,6 +20,12 @@ pub struct LiveValuesEvent { pub back_temperature: f64, pub front_temp_reservoir: f64, pub back_temp_reservoir: f64, + pub front_revolutions: f64, + pub back_revolutions: f64, + pub front_power: f64, + pub back_power: f64, + pub front_total_energy: f64, + pub back_total_energy: f64, } impl LiveValuesEvent { @@ -35,6 +41,8 @@ pub struct StateEvent { pub mode_state: ModeState, pub flow_states: FlowStates, pub temperature_states: TempStates, + pub fan_states: FanStates, + pub tolerance_states: ToleranceStates, } impl StateEvent { @@ -70,6 +78,28 @@ pub struct FlowState { pub should_flow: bool, } +#[derive(Serialize, Debug, Clone)] +pub struct FanState { + pub revolutions: f64, + pub max_revolutions: f64, +} +#[derive(Serialize, Debug, Clone)] +pub struct FanStates { + pub front: FanState, + pub back: FanState, +} + +#[derive(Serialize, Debug, Clone)] +pub struct ToleranceState { + pub heating: f64, + pub cooling: f64, +} +#[derive(Serialize, Debug, Clone)] +pub struct ToleranceStates { + pub front: ToleranceState, + pub back: ToleranceState, +} + pub enum AquaPathV1Events { LiveValues(Event), State(Event), @@ -85,6 +115,14 @@ enum Mutation { SetFrontFlow(bool), SetBackFlow(bool), + + SetFrontRevolutions(f64), + SetBackRevolutions(f64), + + SetFrontHeatingTolerance(f64), + SetBackHeatingTolerance(f64), + SetFrontCoolingTolerance(f64), + SetBackCoolingTolerance(f64), } #[derive(Debug, Clone)] @@ -145,6 +183,24 @@ impl MachineApi for AquaPathV1 { Mutation::SetFrontFlow(should_pump) => { self.set_should_pump(should_pump, super::AquaPathSideType::Front) } + Mutation::SetBackRevolutions(revolution) => { + self.set_max_revolutions(revolution, super::AquaPathSideType::Back) + } + Mutation::SetFrontRevolutions(revolutions) => { + self.set_max_revolutions(revolutions, super::AquaPathSideType::Front) + } + Mutation::SetBackHeatingTolerance(tolerance) => { + self.set_heating_tolerance(tolerance, super::AquaPathSideType::Back) + } + Mutation::SetFrontHeatingTolerance(tolerance) => { + self.set_heating_tolerance(tolerance, super::AquaPathSideType::Front) + } + Mutation::SetBackCoolingTolerance(tolerance) => { + self.set_cooling_tolerance(tolerance, super::AquaPathSideType::Back); + } + Mutation::SetFrontCoolingTolerance(tolerance) => { + self.set_cooling_tolerance(tolerance, super::AquaPathSideType::Front); + } } Ok(()) } diff --git a/machines/src/aquapath1/controller.rs b/machines/src/aquapath1/controller.rs index 2fc78e141..91fd6a89f 100644 --- a/machines/src/aquapath1/controller.rs +++ b/machines/src/aquapath1/controller.rs @@ -5,17 +5,17 @@ use ethercat_hal::io::encoder_input::EncoderInput; use ethercat_hal::io::{ analog_output::AnalogOutput, digital_output::DigitalOutput, temperature_input::TemperatureInput, }; -use std::time::{Duration, Instant}; +use std::time::Instant; +use units::AngularVelocity; +use units::angular_velocity::revolution_per_minute; use units::f64::ThermodynamicTemperature; -use units::thermodynamic_temperature::degree_celsius; +use units::thermodynamic_temperature::{degree_celsius, kelvin}; use units::volume_rate::liter_per_minute; #[derive(Debug)] pub struct Controller { pub pid: PidController, - temperature_pid_output: f64, window_start: Instant, - pwm_period: Duration, pub temperature: Temperature, pub target_temperature: ThermodynamicTemperature, @@ -27,10 +27,19 @@ pub struct Controller { pub cooling_controller: AnalogOutput, pub cooling_relais: DigitalOutput, + pub cooling_tolerance: ThermodynamicTemperature, + pub heating_tolerance: ThermodynamicTemperature, + + pub current_revolutions: AngularVelocity, + pub max_revolutions: AngularVelocity, + pub heating_relais_1: DigitalOutput, pub temperature_sensor_in: TemperatureInput, pub temperature_sensor_out: TemperatureInput, + pub power: f64, + pub total_energy: f64, + pub cooling_allowed: bool, pub heating_allowed: bool, @@ -48,7 +57,6 @@ impl Controller { kp: f64, ki: f64, kd: f64, - pwm_duration: Duration, temp: Temperature, target_tempetature: ThermodynamicTemperature, cooling_controller: AnalogOutput, @@ -56,6 +64,7 @@ impl Controller { heating_relais_1: DigitalOutput, temp_sensor_in: TemperatureInput, temp_sensor_out: TemperatureInput, + max_revolutions: AngularVelocity, flow: Flow, pump_relais: DigitalOutput, @@ -63,9 +72,7 @@ impl Controller { ) -> Self { Self { pid: PidController::new(kp, ki, kd), - temperature_pid_output: 0.0, window_start: Instant::now(), - pwm_period: pwm_duration, target_temperature: target_tempetature, current_temperature: ThermodynamicTemperature::new::(25.0), temp_reservoir: ThermodynamicTemperature::new::(25.0), @@ -74,13 +81,24 @@ impl Controller { temperature: temp, cooling_controller: cooling_controller, + + cooling_tolerance: ThermodynamicTemperature::new::(2.0), + heating_tolerance: ThermodynamicTemperature::new::(2.0), + + current_revolutions: AngularVelocity::new::(0.0), + max_revolutions: max_revolutions, + cooling_relais: cooling_relais, heating_relais_1: heating_relais_1, + cooling_allowed: false, heating_allowed: false, temperature_sensor_in: temp_sensor_in, temperature_sensor_out: temp_sensor_out, + power: 700.0, + total_energy: 0.0, + flow: flow, pump_relais: pump_relais, flow_sensor: flow_sensor, @@ -174,12 +192,12 @@ impl Controller { pub fn turn_cooling_on(&mut self) { self.cooling_relais.set(true); - self.cooling_controller.set(10.0); self.temperature.cooling = true; } pub fn turn_cooling_off(&mut self) { self.cooling_relais.set(false); + self.current_revolutions = AngularVelocity::new::(0.0); self.temperature.cooling = false; } @@ -191,6 +209,7 @@ impl Controller { pub fn turn_heating_off(&mut self) { self.heating_relais_1.set(false); self.temperature.heating = false; + self.power = 0.0; } pub fn set_should_pump(&mut self, should_pump: bool) { @@ -224,6 +243,35 @@ impl Controller { } } + pub fn get_current_revolutions(&self) -> AngularVelocity { + self.current_revolutions + } + + pub fn get_max_revolutions(&self) -> AngularVelocity { + self.max_revolutions + } + + pub fn set_max_revolutions(&mut self, revolutions: AngularVelocity) { + self.max_revolutions = revolutions; + } + + pub fn set_cooling_tolerance(&mut self, tolerance: ThermodynamicTemperature) { + self.cooling_tolerance = tolerance; + } + + pub fn set_heating_tolerance(&mut self, tolerance: ThermodynamicTemperature) { + self.heating_tolerance = tolerance; + } + + // no power/energy unit implemented + pub fn get_current_power(&self) -> f64 { + self.power + } + + pub fn get_total_energy(&self) -> f64 { + self.total_energy + } + pub fn update(&mut self, now: Instant) -> () { let current_flow = self.get_flow(); self.current_flow = current_flow; @@ -250,43 +298,50 @@ impl Controller { // Calculate PID error once let error = self.target_temperature.get::() - self.current_temperature.get::(); - let control = self.pid.update(error, now); - self.temperature_pid_output = control; - let elapsed = now.duration_since(self.window_start); - if elapsed >= self.pwm_period { - self.window_start = now; - } + let elapsed = now - self.window_start; + self.window_start = now; // Decide whether to heat or cool based on error - if error > 0.0 { + if error > self.heating_tolerance.get::() { // Need heating (current < target) if self.temperature.cooling { self.turn_cooling_off(); } if self.heating_allowed && current_flow > VolumeRate::new::(0.0) { - // Only start heating if pump is on - let duty = control.clamp(0.0, 1.0); - let on_time = self.pwm_period.mul_f64(duty); - let on = elapsed < on_time; - if on && !self.temperature.heating { - self.turn_heating_on(); - } else if !on && self.temperature.heating { - self.turn_heating_off(); - } + self.turn_heating_on(); + + self.total_energy += self.get_current_power() * elapsed.as_secs_f64() / 3600.0; } else { // Pump is off or heating not allowed - don't heat if self.temperature.heating { self.turn_heating_off(); } } - } else { + } else if error < -self.cooling_tolerance.get::() { // Need cooling (current > target) if self.temperature.heating { self.turn_heating_off(); } - if self.cooling_allowed && !self.temperature.cooling { - self.turn_cooling_on(); + if self.cooling_allowed && current_flow > VolumeRate::new::(0.0) { + if !self.temperature.cooling { + self.turn_cooling_on(); + } + + let max_revolutions = self.get_max_revolutions(); + let temp_offset = self.current_temperature - self.target_temperature; + + let target_revolutions = (temp_offset.get::() * 10.0) + .clamp(0.0, max_revolutions.get::()); + + self.cooling_controller + .set(target_revolutions as f32 / 10.0); + self.current_revolutions = + AngularVelocity::new::(target_revolutions); + } else { + if self.temperature.cooling { + self.turn_cooling_off(); + } } } } diff --git a/machines/src/aquapath1/mod.rs b/machines/src/aquapath1/mod.rs index 204367b92..2ca22a888 100644 --- a/machines/src/aquapath1/mod.rs +++ b/machines/src/aquapath1/mod.rs @@ -1,6 +1,8 @@ +use api::{ToleranceState, ToleranceStates}; use control_core::socketio::namespace::NamespaceCacheingLogic; use serde::{Deserialize, Serialize}; use std::time::Instant; +use units::angular_velocity::revolution_per_minute; use units::f64::*; use units::{thermodynamic_temperature::degree_celsius, volume_rate::liter_per_minute}; @@ -9,8 +11,8 @@ use crate::{ MACHINE_AQUAPATH_V1, VENDOR_QITECH, aquapath1::{ api::{ - AquaPathV1Events, AquaPathV1Namespace, FlowState, FlowStates, LiveValuesEvent, - ModeState, StateEvent, TempState, TempStates, + AquaPathV1Events, AquaPathV1Namespace, FanState, FanStates, FlowState, FlowStates, + LiveValuesEvent, ModeState, StateEvent, TempState, TempStates, }, controller::Controller, }, @@ -123,6 +125,18 @@ impl AquaPathV1 { back_flow: self.back_controller.current_flow.get::(), front_temp_reservoir: self.front_controller.temp_reservoir.get::(), back_temp_reservoir: self.back_controller.temp_reservoir.get::(), + front_revolutions: self + .front_controller + .current_revolutions + .get::(), + back_revolutions: self + .back_controller + .current_revolutions + .get::(), + front_power: self.front_controller.get_current_power(), + back_power: self.back_controller.get_current_power(), + front_total_energy: self.front_controller.get_total_energy(), + back_total_energy: self.back_controller.get_total_energy(), }; let event = live_values.build(); self.namespace.emit(AquaPathV1Events::LiveValues(event)); @@ -167,6 +181,50 @@ impl AquaPathV1 { should_flow: self.back_controller.should_pump, }, }, + fan_states: FanStates { + front: FanState { + revolutions: self + .front_controller + .max_revolutions + .get::(), + max_revolutions: self + .front_controller + .max_revolutions + .get::(), + }, + back: FanState { + revolutions: self + .back_controller + .max_revolutions + .get::(), + max_revolutions: self + .back_controller + .max_revolutions + .get::(), + }, + }, + tolerance_states: ToleranceStates { + front: ToleranceState { + heating: self + .front_controller + .heating_tolerance + .get::(), + cooling: self + .front_controller + .cooling_tolerance + .get::(), + }, + back: ToleranceState { + heating: self + .back_controller + .heating_tolerance + .get::(), + cooling: self + .back_controller + .cooling_tolerance + .get::(), + }, + }, }; let event = state.build(); @@ -269,3 +327,45 @@ impl AquaPathV1 { self.emit_state(); } } + +impl AquaPathV1 { + fn set_max_revolutions(&mut self, revolutions: f64, fan_type: AquaPathSideType) { + match fan_type { + AquaPathSideType::Back => self + .back_controller + .set_max_revolutions(AngularVelocity::new::(revolutions)), + AquaPathSideType::Front => self + .front_controller + .set_max_revolutions(AngularVelocity::new::(revolutions)), + } + self.emit_state(); + } +} + +impl AquaPathV1 { + fn set_heating_tolerance(&mut self, tolerance: f64, tolerance_type: AquaPathSideType) { + match tolerance_type { + AquaPathSideType::Back => self + .back_controller + .set_heating_tolerance(ThermodynamicTemperature::new::(tolerance)), + AquaPathSideType::Front => self + .front_controller + .set_heating_tolerance(ThermodynamicTemperature::new::(tolerance)), + } + + self.emit_state(); + } + + fn set_cooling_tolerance(&mut self, tolerance: f64, tolerance_type: AquaPathSideType) { + match tolerance_type { + AquaPathSideType::Back => self + .back_controller + .set_cooling_tolerance(ThermodynamicTemperature::new::(tolerance)), + AquaPathSideType::Front => self + .front_controller + .set_cooling_tolerance(ThermodynamicTemperature::new::(tolerance)), + } + + self.emit_state(); + } +} diff --git a/machines/src/aquapath1/new.rs b/machines/src/aquapath1/new.rs index f5ccfe8c1..d6f6c1f60 100644 --- a/machines/src/aquapath1/new.rs +++ b/machines/src/aquapath1/new.rs @@ -25,8 +25,12 @@ use ethercat_hal::{ temperature_input::TemperatureInput, }, }; -use std::time::{Duration, Instant}; -use units::thermodynamic_temperature::{ThermodynamicTemperature, degree_celsius}; +use std::time::Instant; +use units::{ + AngularVelocity, + angular_velocity::revolution_per_minute, + thermodynamic_temperature::{ThermodynamicTemperature, degree_celsius}, +}; impl MachineNewTrait for AquaPathV1 { fn new<'maindevice>(params: &MachineNewParams) -> Result { @@ -131,7 +135,6 @@ impl MachineNewTrait for AquaPathV1 { 0.10, 0.0, 0.015, - Duration::from_millis(500), Temperature::default(), ThermodynamicTemperature::new::(25.0), ao1, @@ -139,6 +142,7 @@ impl MachineNewTrait for AquaPathV1 { do2, t1, t2, + AngularVelocity::new::(100.0), Flow::default(), do1, enc1, @@ -148,7 +152,6 @@ impl MachineNewTrait for AquaPathV1 { 0.10, 0.0, 0.015, - Duration::from_millis(500), Temperature::default(), ThermodynamicTemperature::new::(25.0), ao2, @@ -156,6 +159,7 @@ impl MachineNewTrait for AquaPathV1 { do6, t3, t4, + AngularVelocity::new::(100.0), Flow::default(), do5, enc2,