Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d4da37c9 | ||
|
|
65fb1ab6ad | ||
|
|
924227ce14 |
No files matched your search
@@ -9,6 +9,7 @@ import { EditValue } from "@/control/EditValue";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Label } from "@/control/Label";
|
||||
import { HeatingZone } from "../../extruder/HeatingZone";
|
||||
import { StatusBadge } from "@/control/StatusBadge";
|
||||
|
||||
export function Aquapath1ControlPage() {
|
||||
const {
|
||||
@@ -51,21 +52,19 @@ export function Aquapath1ControlPage() {
|
||||
|
||||
const frontTargetFlow = state?.flow_states.front.should_flow ?? false;
|
||||
const backTargetFlow = state?.flow_states.back.should_flow ?? false;
|
||||
|
||||
console.log(state);
|
||||
return (
|
||||
<Page>
|
||||
<ControlGrid columns={2}>
|
||||
<ControlCard title="Reservoir 1">
|
||||
<div className="grid grid-rows-5 gap-4">
|
||||
<div className="flex flex-row">
|
||||
<TimeSeriesValueNumeric
|
||||
label="Flow"
|
||||
unit="l/min"
|
||||
timeseries={front_flow}
|
||||
renderValue={(value) => value.toFixed(1)}
|
||||
/>
|
||||
{ state?.front_flow === true ? (
|
||||
<StatusBadge variant="success">Flow: active</StatusBadge>
|
||||
) : (
|
||||
<StatusBadge variant="error">Flow: inactive</StatusBadge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row">
|
||||
<TimeSeriesValueNumeric
|
||||
label="Temperature"
|
||||
@@ -149,13 +148,13 @@ export function Aquapath1ControlPage() {
|
||||
<ControlCard title="Reservoir 2">
|
||||
<div className="grid grid-rows-4 gap-4">
|
||||
<div className="flex flex-row">
|
||||
<TimeSeriesValueNumeric
|
||||
label="Flow"
|
||||
unit="l/min"
|
||||
timeseries={back_flow}
|
||||
renderValue={(value) => value.toFixed(1)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
{ state?.back_flow === true ? (
|
||||
<StatusBadge variant="success">Flow: active</StatusBadge>
|
||||
) : (
|
||||
<StatusBadge variant="error">Flow: inactive</StatusBadge>
|
||||
)}
|
||||
</div></div>
|
||||
|
||||
<div className="flex flex-row">
|
||||
<TimeSeriesValueNumeric
|
||||
|
||||
@@ -14,12 +14,12 @@ export function Aquapath1Page() {
|
||||
title: "Control",
|
||||
icon: "lu:CirclePlay",
|
||||
},
|
||||
{
|
||||
link: "graph",
|
||||
title: "Graph",
|
||||
icon: "lu:ChartSpline",
|
||||
activeLink: "graphs",
|
||||
},
|
||||
//{
|
||||
// link: "graph",
|
||||
//title: "Graph",
|
||||
//icon: "lu:ChartSpline",
|
||||
//activeLink: "graphs",
|
||||
//},
|
||||
{
|
||||
link: "settings",
|
||||
title: "Config",
|
||||
|
||||
@@ -72,8 +72,6 @@ export const toleranceStatesSchema = z.object({
|
||||
* Live values event schema (time-series data)
|
||||
*/
|
||||
export const liveValuesEventDataSchema = z.object({
|
||||
front_flow: z.number(),
|
||||
back_flow: z.number(),
|
||||
front_temperature: z.number(),
|
||||
back_temperature: z.number(),
|
||||
front_temp_reservoir: z.number(),
|
||||
@@ -96,6 +94,8 @@ export const stateEventDataSchema = z.object({
|
||||
temperature_states: tempStatesSchema,
|
||||
fan_states: fanStatesSchema,
|
||||
tolerance_states: toleranceStatesSchema,
|
||||
front_flow: z.boolean(),
|
||||
back_flow: z.boolean(),
|
||||
});
|
||||
|
||||
// ========== Event Schemas with Wrappers ==========
|
||||
@@ -113,9 +113,6 @@ export type Aquapath1NamespaceStore = {
|
||||
state: StateEvent | null;
|
||||
defaultState: StateEvent | null;
|
||||
|
||||
front_flow: TimeSeries;
|
||||
back_flow: TimeSeries;
|
||||
|
||||
front_temperature: TimeSeries;
|
||||
back_temperature: TimeSeries;
|
||||
|
||||
@@ -217,7 +214,7 @@ export function aquapath1MessageHandler(
|
||||
// Live values events (time-series data)
|
||||
else if (eventName === "LiveValuesEvent") {
|
||||
const liveValuesEvent = liveValuesEventSchema.parse(event);
|
||||
|
||||
console.log(liveValuesEvent);
|
||||
updateStore((state) => ({
|
||||
...state,
|
||||
front_temperature: addTemperature1(state.front_temperature, {
|
||||
|
||||
@@ -416,12 +416,12 @@ export const aquapath1: MachineProperties = {
|
||||
},
|
||||
{
|
||||
role: 4,
|
||||
role_label: "EL5152",
|
||||
role_label: "EL1002",
|
||||
allowed_devices: [
|
||||
{
|
||||
vendor_id: 2,
|
||||
product_id: 0x14203052,
|
||||
revision: 0x140000,
|
||||
product_id: 0x3ea3052,
|
||||
revision: 0x120000,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -161,18 +161,12 @@ impl crate::coe::Configuration for EL3062_0030TxPdo {
|
||||
len += 1;
|
||||
device.sdo_write(0x1C13, len, 0x1A00u16).await?;
|
||||
}
|
||||
if let Some(_) = &self.ai_compact_channel1 {
|
||||
len += 1;
|
||||
device.sdo_write(0x1C13, len, 0x1A01u16).await?;
|
||||
}
|
||||
|
||||
if let Some(_) = &self.ai_standard_channel2 {
|
||||
len += 1;
|
||||
device.sdo_write(0x1C13, len, 0x1A02u16).await?;
|
||||
}
|
||||
if let Some(_) = &self.ai_compact_channel2 {
|
||||
len += 1;
|
||||
device.sdo_write(0x1C13, len, 0x1A03u16).await?;
|
||||
}
|
||||
|
||||
device.sdo_write(0x1C13, 0, len).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,8 +23,13 @@ impl MachineAct for AquaPathV1 {
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
self.front_controller.update(now_ts);
|
||||
self.back_controller.update(now_ts);
|
||||
if self.front_controller.update(now_ts) {
|
||||
self.emit_state();
|
||||
}
|
||||
|
||||
if self.back_controller.update(now_ts) {
|
||||
self.emit_state();
|
||||
}
|
||||
|
||||
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
|
||||
self.emit_live_values();
|
||||
|
||||
@@ -14,8 +14,6 @@ use tracing::instrument;
|
||||
|
||||
#[derive(Serialize, Debug, Clone, Default)]
|
||||
pub struct LiveValuesEvent {
|
||||
pub front_flow: f64,
|
||||
pub back_flow: f64,
|
||||
pub front_temperature: f64,
|
||||
pub back_temperature: f64,
|
||||
pub front_temp_reservoir: f64,
|
||||
@@ -43,6 +41,8 @@ pub struct StateEvent {
|
||||
pub temperature_states: TempStates,
|
||||
pub fan_states: FanStates,
|
||||
pub tolerance_states: ToleranceStates,
|
||||
pub front_flow: bool,
|
||||
pub back_flow: bool,
|
||||
}
|
||||
|
||||
impl StateEvent {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::aquapath1::VolumeRate;
|
||||
use crate::aquapath1::{Flow, Temperature};
|
||||
use control_core::controllers::pid::PidController;
|
||||
use ethercat_hal::io::encoder_input::EncoderInput;
|
||||
use ethercat_hal::io::analog_input::AnalogInput;
|
||||
use ethercat_hal::io::digital_input::DigitalInput;
|
||||
use ethercat_hal::io::{
|
||||
analog_output::AnalogOutput, digital_output::DigitalOutput, temperature_input::TemperatureInput,
|
||||
};
|
||||
@@ -44,9 +45,10 @@ pub struct Controller {
|
||||
pub heating_allowed: bool,
|
||||
|
||||
pub flow: Flow,
|
||||
pub is_flowing : bool,
|
||||
pump_relais: DigitalOutput,
|
||||
pub should_pump: bool,
|
||||
pub flow_sensor: EncoderInput,
|
||||
pub flow_sensor: AnalogInput,
|
||||
pub pump_allowed: bool,
|
||||
pub current_flow: VolumeRate,
|
||||
pub max_flow: VolumeRate,
|
||||
@@ -68,7 +70,7 @@ impl Controller {
|
||||
|
||||
flow: Flow,
|
||||
pump_relais: DigitalOutput,
|
||||
flow_sensor: EncoderInput,
|
||||
flow_sensor: AnalogInput,
|
||||
) -> Self {
|
||||
Self {
|
||||
pid: PidController::new(kp, ki, kd),
|
||||
@@ -93,6 +95,7 @@ impl Controller {
|
||||
|
||||
cooling_allowed: false,
|
||||
heating_allowed: false,
|
||||
is_flowing : false,
|
||||
temperature_sensor_in: temp_sensor_in,
|
||||
temperature_sensor_out: temp_sensor_out,
|
||||
|
||||
@@ -220,27 +223,15 @@ impl Controller {
|
||||
self.should_pump
|
||||
}
|
||||
|
||||
pub fn get_flow(&mut self) -> VolumeRate {
|
||||
let value = match self.flow_sensor.get_frequency_value() {
|
||||
pub fn get_flow(&mut self) -> bool {
|
||||
/* let value = match self.flow_sensor.get_value() {
|
||||
Ok(val) => val,
|
||||
Err(_e) => {
|
||||
return VolumeRate::new::<liter_per_minute>(0.0);
|
||||
false
|
||||
}
|
||||
};
|
||||
};*/
|
||||
|
||||
match value {
|
||||
Some(val) => {
|
||||
if val == 0 {
|
||||
return VolumeRate::new::<liter_per_minute>(0.0);
|
||||
}
|
||||
// Formula: f = 8.1*q - 3, so q = (f + 3) / 8.1
|
||||
let actual_flow = ((val / 100) as f32 + 3.0) / 8.1;
|
||||
VolumeRate::new::<liter_per_minute>(actual_flow.into())
|
||||
}
|
||||
None => {
|
||||
return VolumeRate::new::<liter_per_minute>(0.0);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_current_revolutions(&self) -> AngularVelocity {
|
||||
@@ -272,10 +263,10 @@ impl Controller {
|
||||
self.total_energy
|
||||
}
|
||||
|
||||
pub fn update(&mut self, now: Instant) -> () {
|
||||
pub fn update(&mut self, now: Instant) -> bool {
|
||||
let current_flow = self.get_flow();
|
||||
self.current_flow = current_flow;
|
||||
self.flow.flow = current_flow;
|
||||
let flow_changed = current_flow == self.is_flowing;
|
||||
self.is_flowing = current_flow;
|
||||
|
||||
let should_flow = self.get_should_pump();
|
||||
self.flow.should_pump = should_flow;
|
||||
@@ -308,7 +299,7 @@ impl Controller {
|
||||
if self.temperature.cooling {
|
||||
self.turn_cooling_off();
|
||||
}
|
||||
if self.heating_allowed && current_flow > VolumeRate::new::<liter_per_minute>(0.0) {
|
||||
if self.heating_allowed && self.is_flowing {
|
||||
self.turn_heating_on();
|
||||
|
||||
self.total_energy += self.get_current_power() * elapsed.as_secs_f64() / 3600.0;
|
||||
@@ -323,7 +314,7 @@ impl Controller {
|
||||
if self.temperature.heating {
|
||||
self.turn_heating_off();
|
||||
}
|
||||
if self.cooling_allowed && current_flow > VolumeRate::new::<liter_per_minute>(0.0) {
|
||||
if self.cooling_allowed && self.is_flowing {
|
||||
if !self.temperature.cooling {
|
||||
self.turn_cooling_on();
|
||||
}
|
||||
@@ -344,5 +335,6 @@ impl Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
flow_changed
|
||||
}
|
||||
}
|
||||
@@ -121,8 +121,6 @@ impl AquaPathV1 {
|
||||
.back_controller
|
||||
.current_temperature
|
||||
.get::<degree_celsius>(),
|
||||
front_flow: self.front_controller.current_flow.get::<liter_per_minute>(),
|
||||
back_flow: self.back_controller.current_flow.get::<liter_per_minute>(),
|
||||
front_temp_reservoir: self.front_controller.temp_reservoir.get::<degree_celsius>(),
|
||||
back_temp_reservoir: self.back_controller.temp_reservoir.get::<degree_celsius>(),
|
||||
front_revolutions: self
|
||||
@@ -228,6 +226,8 @@ impl AquaPathV1 {
|
||||
.get::<degree_celsius>(),
|
||||
},
|
||||
},
|
||||
front_flow: self.front_controller.is_flowing,
|
||||
back_flow: self.back_controller.is_flowing,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,13 @@ use anyhow::Error;
|
||||
use ethercat_hal::{
|
||||
coe::ConfigurableDevice,
|
||||
devices::{
|
||||
ek1100::{EK1100, EK1100_IDENTITY_A},
|
||||
el2008::{EL2008, EL2008_IDENTITY_A, EL2008_IDENTITY_B, EL2008Port},
|
||||
el3204::{EL3204, EL3204_IDENTITY_A, EL3204_IDENTITY_B, EL3204Port},
|
||||
el4002::{EL4002, EL4002_IDENTITY_A, EL4002Port},
|
||||
el5152::{
|
||||
ek1100::{EK1100, EK1100_IDENTITY_A}, el1002::{EL1002, EL1002_IDENTITY_A}, el2008::{EL2008, EL2008_IDENTITY_A, EL2008_IDENTITY_B, EL2008Port}, el3062_0030::{EL3062_0030, EL3062_0030_IDENTITY_A, EL3062_0030Configuration}, el3204::{EL3204, EL3204_IDENTITY_A, EL3204_IDENTITY_B, EL3204Port}, el4002::{EL4002, EL4002_IDENTITY_A, EL4002Port}, el5152::{
|
||||
EL5152, EL5152_IDENTITY_A, EL5152Configuration, EL5152Port,
|
||||
EL5152PredefinedPdoAssignment,
|
||||
},
|
||||
}
|
||||
},
|
||||
io::{
|
||||
analog_output::AnalogOutput, digital_output::DigitalOutput, encoder_input::EncoderInput,
|
||||
temperature_input::TemperatureInput,
|
||||
analog_input::AnalogInput, analog_output::AnalogOutput, digital_input::DigitalInput, digital_output::DigitalOutput, encoder_input::EncoderInput, temperature_input::TemperatureInput
|
||||
},
|
||||
};
|
||||
use std::time::Instant;
|
||||
@@ -83,49 +78,32 @@ impl MachineNewTrait for AquaPathV1 {
|
||||
.await?
|
||||
.0;
|
||||
|
||||
let el5152 =
|
||||
get_ethercat_device::<EL5152>(hardware, params, 4, [EL5152_IDENTITY_A].to_vec())
|
||||
let el3062_0030 =
|
||||
get_ethercat_device::<EL3062_0030>(hardware, params, 4, [EL3062_0030_IDENTITY_A].to_vec())
|
||||
.await?
|
||||
.0;
|
||||
|
||||
let config = EL5152Configuration {
|
||||
pdo_assignment: EL5152PredefinedPdoAssignment::Frequency,
|
||||
..Default::default()
|
||||
};
|
||||
let subdevice = get_subdevice_by_index(hardware.subdevices, 4)?;
|
||||
el5152
|
||||
.write()
|
||||
.await
|
||||
.write_config(&subdevice, &config)
|
||||
.await?;
|
||||
let el3062_0030_subdevice = get_subdevice_by_index(hardware.subdevices, 4)?;
|
||||
let mut guard = el3062_0030.write().await;
|
||||
let config = EL3062_0030Configuration::default();
|
||||
guard.write_config(el3062_0030_subdevice,&config).await;
|
||||
drop(guard);
|
||||
|
||||
let enc1 = EncoderInput::new(el5152.clone(), EL5152Port::ENC1);
|
||||
let ai1 = AnalogInput::new(el3062_0030.clone(),ethercat_hal::devices::el3062_0030::EL3062_0030Port::AI1);
|
||||
let ai2 = AnalogInput::new(el3062_0030.clone(), ethercat_hal::devices::el3062_0030::EL3062_0030Port::AI2);
|
||||
|
||||
let enc2 = EncoderInput::new(el5152.clone(), EL5152Port::ENC2);
|
||||
//after heating
|
||||
let t1 = TemperatureInput::new(el3204.clone(), EL3204Port::T1);
|
||||
//in reservoir
|
||||
let t2 = TemperatureInput::new(el3204.clone(), EL3204Port::T2);
|
||||
//after heating
|
||||
let t3 = TemperatureInput::new(el3204.clone(), EL3204Port::T3);
|
||||
//in reservoir
|
||||
let t1 = TemperatureInput::new(el3204.clone(), EL3204Port::T1);
|
||||
let t2 = TemperatureInput::new(el3204.clone(), EL3204Port::T2);
|
||||
let t3 = TemperatureInput::new(el3204.clone(), EL3204Port::T3);
|
||||
let t4 = TemperatureInput::new(el3204.clone(), EL3204Port::T4);
|
||||
//pump flow control
|
||||
//phys 1
|
||||
let do1 = DigitalOutput::new(el2008.clone(), EL2008Port::DO1);
|
||||
//phys 5
|
||||
let do2 = DigitalOutput::new(el2008.clone(), EL2008Port::DO2);
|
||||
//heating
|
||||
|
||||
//phys 6
|
||||
let do4 = DigitalOutput::new(el2008.clone(), EL2008Port::DO4);
|
||||
//phys 3
|
||||
let do5 = DigitalOutput::new(el2008.clone(), EL2008Port::DO5);
|
||||
//phys 7
|
||||
let do6 = DigitalOutput::new(el2008.clone(), EL2008Port::DO6);
|
||||
//cooling power cut
|
||||
|
||||
//phys 8
|
||||
|
||||
//pump flow control
|
||||
let do1 = DigitalOutput::new(el2008.clone(), EL2008Port::DO1);
|
||||
let do2 = DigitalOutput::new(el2008.clone(), EL2008Port::DO2);
|
||||
let do4 = DigitalOutput::new(el2008.clone(), EL2008Port::DO4);
|
||||
let do5 = DigitalOutput::new(el2008.clone(), EL2008Port::DO5);
|
||||
let do6 = DigitalOutput::new(el2008.clone(), EL2008Port::DO6);
|
||||
let do8 = DigitalOutput::new(el2008.clone(), EL2008Port::DO8);
|
||||
|
||||
let ao1 = AnalogOutput::new(el4002.clone(), EL4002Port::AO1);
|
||||
@@ -145,7 +123,7 @@ impl MachineNewTrait for AquaPathV1 {
|
||||
AngularVelocity::new::<revolution_per_minute>(100.0),
|
||||
Flow::default(),
|
||||
do1,
|
||||
enc1,
|
||||
ai1,
|
||||
);
|
||||
|
||||
let back_controller = Controller::new(
|
||||
@@ -162,7 +140,7 @@ impl MachineNewTrait for AquaPathV1 {
|
||||
AngularVelocity::new::<revolution_per_minute>(100.0),
|
||||
Flow::default(),
|
||||
do5,
|
||||
enc2,
|
||||
ai2,
|
||||
);
|
||||
let (sender, receiver) = smol::channel::unbounded();
|
||||
let mut water_cooling = Self {
|
||||
|
||||
@@ -433,6 +433,8 @@ pub async fn setup_loop(
|
||||
)
|
||||
.await?;
|
||||
|
||||
smol::Timer::after(Duration::from_millis(3000)).await;
|
||||
|
||||
let group_safe = match group_preop.into_safe_op(&maindevice).await {
|
||||
Ok(group_op) => {
|
||||
tracing::info!("Group in Safe-OP state");
|
||||
|
||||
Reference in new issue
Block a user