Compare commits

...
Author SHA1 Message Date
93094049+kraemr@users.noreply.github.com b92cae0ac1 lasergraph fix 2025-06-23 14:22:55 +02:00
93094049+kraemr@users.noreply.github.com 40e4a95268 fixed errors after merge and added graphs to pages 2025-06-23 13:15:02 +02:00
93094049+kraemr@users.noreply.github.com da5bfc5ed0 Merge branch '377-inverter-state-machine-and-el6021-incorrectly-handle-requestresponses' into extruder_feature_testing 2025-06-23 13:02:17 +02:00
93094049+kraemr@users.noreply.github.com 745a3c95f1 refactor 2025-06-23 13:02:14 +02:00
93094049+kraemr@users.noreply.github.com e34ed6929a Merge branch 'master' into 175-fine-tune-pressure-pid-controller 2025-06-23 12:55:40 +02:00
93094049+kraemr@users.noreply.github.com a121f99be4 Revert "Revert "frontend changes for api""
This reverts commit fbcae62e25.
2025-06-23 12:47:52 +02:00
93094049+kraemr@users.noreply.github.com 7b15161ada Revert "Revert "added comments to el6021 init""
This reverts commit c455b8b2b0.
2025-06-23 12:46:05 +02:00
93094049+kraemr@users.noreply.github.com c455b8b2b0 Revert "added comments to el6021 init"
This reverts commit a15d708138.
2025-06-23 12:45:23 +02:00
93094049+kraemr@users.noreply.github.com dbfb4410a1 Revert "Revert "bakcend api""
This reverts commit 0b3479bc16.
2025-06-23 12:44:58 +02:00
93094049+kraemr@users.noreply.github.com 004f5d5d5c Merge branch 'master' of github.com:qitechgmbh/control 2025-06-23 12:29:25 +02:00
93094049+kraemr@users.noreply.github.com f4609bfb3c Merge branch 'master' into 377-inverter-state-machine-and-el6021-incorrectly-handle-requestresponses 2025-06-23 12:04:27 +02:00
93094049+kraemr@users.noreply.github.com afa4663176 cleanup of EL6021 and inverter 2025-06-23 12:02:21 +02:00
93094049+kraemr@users.noreply.github.com 9b806ee8b3 Merge branch 'inverter-fix' into 377-inverter-state-machine-and-el6021-incorrectly-handle-requestresponses 2025-06-23 11:54:57 +02:00
93094049+kraemr@users.noreply.github.com 4975d2f6a7 fixed el6021 and inverter state machine 2025-06-23 11:54:30 +02:00
93094049+kraemr@users.noreply.github.com 1a675db872 Merge branch 'master' of github.com:qitechgmbh/control 2025-06-23 11:47:00 +02:00
93094049+kraemr@users.noreply.github.com 9f13765db6 Merge branch 'master' of github.com:qitechgmbh/control 2025-06-17 14:13:56 +02:00
93094049+kraemr@users.noreply.github.com 465f8e9f07 added revision 0x10000 el6021 to frontend 2025-06-17 14:13:46 +02:00
17 changed files with 488 additions and 38 deletions

No files matched your search

@@ -2,11 +2,7 @@ use super::Actor;
use crate::modbus::{
ModbusFunctionCode, ModbusRequest, ModbusResponse, calculate_modbus_rtu_timeout,
};
use axum::http::request;
use bitvec::{
order::{Lsb0, Msb0},
slice::BitSlice,
};
use bitvec::{order::Lsb0, slice::BitSlice};
use ethercat_hal::io::serial_interface::{SerialEncoding, SerialInterface};
use std::{
collections::HashMap,
@@ -432,7 +428,7 @@ impl MitsubishiInverterRS485Actor {
}
Err(_) => {
self.last_message_size = 22;
self.state = State::WaitingForReceiveAccept;
// self.state = State::WaitingForReceiveAccept;
Err(anyhow::anyhow!("error"))
}
}
@@ -474,8 +470,8 @@ impl MitsubishiInverterRS485Actor {
None => return,
};
let modbus_request: Vec<u8> = request.request.clone().into();
let res = (self.serial_interface.write_message)(modbus_request.clone()).await;
let res = (self.serial_interface.write_message)(modbus_request.clone()).await;
match res {
Ok(_) => {
self.next_response_type = request.expected_response_type;
@@ -569,14 +565,16 @@ impl MitsubishiInverterRS485Actor {
};
}
fn handle_response(&mut self, resp: ModbusResponse) {
async fn handle_no_response(&mut self) {}
async fn handle_response(&mut self, resp: ModbusResponse) {
match self.next_response_type {
ResponseType::ReadFrequency => (),
ResponseType::WriteFrequency => (),
ResponseType::ReadMotorFrequency => self.handle_motor_frequency(resp),
ResponseType::InverterStatus => self.handle_read_inverter_status(resp),
ResponseType::InverterControl => (),
ResponseType::NoResponse => (),
ResponseType::NoResponse => self.handle_no_response().await,
}
}
}
@@ -623,18 +621,23 @@ impl Actor for MitsubishiInverterRS485Actor {
}
self.add_request(MitsubishiControlRequests::ReadMotorFrequency.into());
// inverterstatus has less priority than readmotor
self.add_request(MitsubishiControlRequests::ReadInverterStatus.into());
self.last_ts = now_ts;
match self.state {
State::WaitingForResponse => {
let ret = self.read_modbus_response().await;
match ret {
Ok(ret) => {
self.handle_response(ret);
self.handle_response(ret).await;
}
Err(_) => {
if let MitsubishiControlRequests::ResetInverter =
self.last_control_request_type
{
self.state = State::ReadyToSend;
}
}
Err(_) => (), // Do nothing for now
}
self.next_response_type = ResponseType::NoResponse;
@@ -645,11 +648,16 @@ impl Actor for MitsubishiInverterRS485Actor {
self.set_ignored_times_modbus_requests();
}
State::WaitingForReceiveAccept => {
_ = (self.serial_interface.read_finished)().await;
self.state = State::ReadyToSend;
}
State::WaitingForRequestAccept => {
self.state = State::WaitingForResponse;
let res = (self.serial_interface.write_finished)().await;
if res == true {
self.state = State::WaitingForResponse;
}
}
_ => (),
}
self.response = None;
+19
View File
@@ -34,6 +34,25 @@ impl PidController {
}
}
pub fn configure(&mut self, ki: f64, kp: f64, kd: f64) {
self.reset();
self.kp = kp;
self.ki = ki;
self.kd = kd;
}
pub fn get_kp(&self) -> f64 {
self.kp
}
pub fn get_ki(&self) -> f64 {
self.ki
}
pub fn get_kd(&self) -> f64 {
self.kd
}
pub fn update(&mut self, error: f64, t: Instant) -> f64 {
let signal = match self.last {
// First update
@@ -120,9 +120,11 @@ export function Extruder2ControlPage() {
<Label label="Target Pressure">
<EditValue
value={targetBar}
defaultValue={200}
defaultValue={0}
unit="bar"
title="Target Pressure"
min={0.0}
max={150.0}
renderValue={(value) => roundToDecimals(value, 0)}
onChange={screwSetTargetPressure}
/>
@@ -18,7 +18,7 @@ export function Extruder2Page() {
link: "graphs",
title: "Graphs",
icon: "lu:ChartSpline",
activeLink: "graph",
activeLink: "graphs",
},
{
link: "settings",
@@ -1,11 +1,11 @@
import React, { useEffect, useState } from "react";
import { Page } from "@/components/Page";
import { ControlCard } from "@/control/ControlCard";
import { Label } from "@/control/Label";
import { SelectionGroupBoolean } from "@/control/SelectionGroup";
import React from "react";
import { useExtruder2 } from "./useExtruder";
import { EditValue } from "@/control/EditValue";
import { roundToDecimals } from "@/lib/decimal";
import { useExtruder2 } from "./useExtruder";
import { TouchButton } from "@/components/touch/TouchButton";
export function Extruder2SettingsPage() {
@@ -17,7 +17,38 @@ export function Extruder2SettingsPage() {
extruderSetPressureLimitIsEnabled,
pressureLimitState,
pressureLimitEnabledState,
pressurePidSettings,
temperaturePidSettings,
setPressurePid,
setTemperaturePid,
} = useExtruder2();
const [showAdvanced, setShowAdvanced] = useState(false);
const [pid1, setPid1] = useState<{
kp: number;
ki: number;
kd: number;
} | null>(null);
const [pid2, setPid2] = useState<{
kp: number;
ki: number;
kd: number;
} | null>(null);
useEffect(() => {
if (!pid1 && temperaturePidSettings) {
setPid1(temperaturePidSettings);
}
if (!pid2 && pressurePidSettings) {
setPid2(pressurePidSettings);
}
}, [temperaturePidSettings, pressurePidSettings, pid1, pid2]);
if (!pid1 || !pid2) {
return <div>Loading PID settings...</div>;
}
return (
<Page>
<ControlCard className="bg-red" title="Inverter Settings">
@@ -62,7 +93,115 @@ export function Extruder2SettingsPage() {
onChange={extruderSetPressureLimitIsEnabled}
/>
</Label>
<Label label="Show Advanced PID Settings">
<SelectionGroupBoolean
value={showAdvanced}
optionTrue={{ children: "Show" }}
optionFalse={{ children: "Hide" }}
onChange={setShowAdvanced}
/>
</Label>
</ControlCard>
{showAdvanced && (
<>
<ControlCard title="Temperature PID Settings">
<Label label="Kp">
<EditValue
value={pid1.kp}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid1({ ...pid1, kp: v })}
title="Temperature PID KP"
/>
</Label>
<Label label="Ki">
<EditValue
value={pid1.ki}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid1({ ...pid1, ki: v })}
title="Temperature PID KI"
/>
</Label>
<Label label="Kd">
<EditValue
value={pid1.kd}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid1({ ...pid1, kd: v })}
title="Temperature PID KD"
/>
</Label>
<div className="mt-2 flex justify-end">
<button
className="rounded bg-blue-600 px-4 py-2 text-white"
onClick={() => setTemperaturePid(pid1)}
>
Save Temperature PID
</button>
</div>
</ControlCard>
<ControlCard title="Pressure PID Settings Group 2">
<Label label="Kp">
<EditValue
value={pid2.kp}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid2({ ...pid2, kp: v })}
title="Pressure PID KP"
/>
</Label>
<Label label="Ki">
<EditValue
value={pid2.ki}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid2({ ...pid2, ki: v })}
title="Pressure PID KI"
/>
</Label>
<Label label="Kd">
<EditValue
value={pid2.kd}
defaultValue={0}
min={0}
max={100}
step={0.01}
renderValue={(v) => roundToDecimals(v, 2)}
onChange={(v) => setPid2({ ...pid2, kd: v })}
title="Pressure PID KD"
/>
</Label>
<div className="mt-2 flex justify-end">
<button
className="rounded bg-blue-600 px-4 py-2 text-white"
onClick={() => setPressurePid(pid2)}
>
Save Pressure PID
</button>
</div>
</ControlCard>
</>
)}
</Page>
);
}
@@ -32,6 +32,8 @@ export type Extruder2NamespaceStore = {
extruderSettingsState: ExtruderSettingsStateEvent | null;
pressurePidSettings: PidSettingsEvent | null;
temperaturePidSettings: PidSettingsEvent | null;
// Metric Events (cached for 1 hour )
rpm: TimeSeries;
bar: TimeSeries;
@@ -110,6 +112,20 @@ export function extruder2MessageHandler(
};
try {
if (eventName == "TemperaturePidSettingsEvent") {
updateStore((state) => ({
...state,
temperaturePidSettings: pidSettingsEventSchema.parse(event),
}));
}
if (eventName == "PressurePidSettingsEvent") {
updateStore((state) => ({
...state,
pressurePidSettings: pidSettingsEventSchema.parse(event),
}));
}
if (eventName == "ExtruderSettingsStateEvent") {
updateStore((state) => ({
...state,
@@ -118,7 +134,6 @@ export function extruder2MessageHandler(
}
if (eventName == "InverterStatusEvent") {
console.log(eventName);
updateStore((state) => ({
...state,
inverterState: event as InverterStatusEvent,
@@ -292,6 +307,9 @@ export const createExtruder2NamespaceStore =
motorBarState: null,
extruderSettingsState: null,
pressurePidSettings: null,
temperaturePidSettings: null,
rpm,
bar,
@@ -403,6 +421,12 @@ export const extruderSettingsStateEventDataSchema = z.object({
pressure_limit_enabled: z.boolean(),
});
export const pidSettingsEventDataSchema = z.object({
ki: z.number(),
kd: z.number(),
kp: z.number(),
});
// Event Schemas
export const heatingTargetEventSchema = eventSchema(
heatingTargetTemperatureDataSchema,
@@ -430,6 +454,8 @@ export const extruderSettingsStateEventSchema = eventSchema(
extruderSettingsStateEventDataSchema,
);
export const pidSettingsEventSchema = eventSchema(pidSettingsEventDataSchema);
export const heatingPowerEventDataSchema = z.object({
wattage: z.number(),
});
@@ -460,10 +486,12 @@ export type Heating = z.infer<typeof heatingStateDataSchema>;
export type MotorPressure = z.infer<typeof motorBarStateEventDataSchema>;
export type MotorRpm = z.infer<typeof motorScrewStateEventDataSchema>;
export type Mode = z.infer<typeof modeSchema>;
export type InverterStatus = z.infer<typeof inverterStatusEventDataSchema>;
export type PidSettings = z.infer<typeof pidSettingsEventDataSchema>;
export type PidSettingsEvent = z.infer<typeof pidSettingsEventSchema>;
export type ExtruderSettingsStateEvent = z.infer<
typeof extruderSettingsStateEventSchema
>;
@@ -6,8 +6,9 @@ import { extruder2Route } from "@/routes/routes";
import { z } from "zod";
import {
Heating,
InverterStatus,
Mode,
PidSettings,
InverterStatus,
useExtruder2Namespace,
} from "./extruder2Namespace";
import { useEffect, useMemo } from "react";
@@ -47,6 +48,8 @@ export function useExtruder2() {
const heating = useHeatingTemperature(machineIdentification);
const heatingPower = useHeatingPower(machineIdentification);
const settings = useSettings(machineIdentification);
const pidSettings = usePidSettings(machineIdentification);
return {
...inverter,
...mode,
@@ -54,6 +57,7 @@ export function useExtruder2() {
...heating,
...settings,
...heatingPower,
...pidSettings,
};
}
@@ -342,6 +346,86 @@ export function useMotor(
};
}
export function usePidSettings(
machine_identification_unique: MachineIdentificationUnique,
): {
temperaturePidSettings: PidSettings | undefined;
pressurePidSettings: PidSettings | undefined;
setPressurePid: (settings: PidSettings) => void;
setTemperaturePid: (settings: PidSettings) => void;
} {
const { pressurePidSettings, temperaturePidSettings } = useExtruder2Namespace(
machine_identification_unique,
);
const temperaturePidSettingsState = useStateOptimistic<PidSettings>();
const pressurePidSettingsState = useStateOptimistic<PidSettings>();
const SetTemperaturePidSchema = z.object({
SetTemperaturePidSettings: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
}),
});
const SetPressurePidSchema = z.object({
SetPressurePidSettings: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
}),
});
const { request: TemperaturePidSettingsRequest } = useMachineMutation(
SetTemperaturePidSchema,
);
const { request: PressurePidSettingsRequest } =
useMachineMutation(SetPressurePidSchema);
const setPressurePid = async (value: PidSettings) => {
pressurePidSettingsState.setOptimistic(value);
PressurePidSettingsRequest({
machine_identification_unique,
data: { SetPressurePidSettings: value },
})
.then((response) => {
if (!response.success) pressurePidSettingsState.resetToReal();
})
.catch(() => pressurePidSettingsState.resetToReal());
};
const setTemperaturePid = async (value: PidSettings) => {
temperaturePidSettingsState.setOptimistic(value);
TemperaturePidSettingsRequest({
machine_identification_unique,
data: { SetTemperaturePidSettings: value },
})
.then((response) => {
if (!response.success) temperaturePidSettingsState.resetToReal();
})
.catch(() => temperaturePidSettingsState.resetToReal());
};
useEffect(() => {
if (pressurePidSettings?.data) {
pressurePidSettingsState.setReal(pressurePidSettings?.data);
}
if (temperaturePidSettings?.data) {
temperaturePidSettingsState.setReal(temperaturePidSettings?.data);
}
});
return {
pressurePidSettings: pressurePidSettingsState.value,
temperaturePidSettings: temperaturePidSettingsState.value,
setTemperaturePid,
setPressurePid,
};
}
export function useHeatingTemperature(
machine_identification_unique: MachineIdentificationUnique,
): {
@@ -492,6 +576,7 @@ export function useHeatingTemperature(
heatingSetFrontTemp,
heatingSetBackTemp,
heatingSetMiddleTemp,
nozzleHeatingTarget: nozzleHeatingTargetState.value,
frontHeatingTarget: frontHeatingTargetState.value,
backHeatingTarget: backHeatingTargetState.value,
@@ -59,7 +59,7 @@ export function Laser1GraphsPage() {
<AutoSyncedBigGraph
syncHook={syncHook}
newData={laserDiameter}
newData={{ newData: laserDiameter }}
unit="mm"
renderValue={(value) => value.toFixed(3)}
config={config}
+5
View File
@@ -226,6 +226,11 @@ export const extruder2: MachinePreset = {
product_id: 394604626,
revision: 0x160000,
},
{
vendor_id: 2,
product_id: 394604626,
revision: 0x100000,
},
],
},
{
+9
View File
@@ -10,7 +10,9 @@ import { Winder2ControlPage } from "@/machines/winder/winder2/Winder2ControlPage
import { Winder2ManualPage } from "@/machines/winder/winder2/Winder2Manual";
import { Winder2SettingPage } from "@/machines/winder/winder2/Winder2Settings";
import { Winder2GraphsPage } from "@/machines/winder/winder2/Winder2Graphs";
import { Extruder2Page } from "@/machines/extruder/extruder2/Extruder2Page";
import { Extruder2GraphsPage } from "@/machines/extruder/extruder2/Extruder2Graph";
import { Extruder2ControlPage } from "@/machines/extruder/extruder2/Extruder2ControlPage";
import { Extruder2SettingsPage } from "@/machines/extruder/extruder2/Extruder2Settings";
import { ExtruderV2ManualPage } from "@/machines/extruder/extruder2/Extruder2Manual";
@@ -73,6 +75,12 @@ export const extruder2ManualRoute = createRoute({
component: () => <ExtruderV2ManualPage />,
});
export const extruder2GraphRoute = createRoute({
getParentRoute: () => extruder2Route,
path: "graphs",
component: () => <Extruder2GraphsPage />,
});
export const winder2SerialRoute = createRoute({
getParentRoute: () => machinesRoute,
path: "winder2/$serial",
@@ -244,6 +252,7 @@ export const rootTree = RootRoute.addChildren([
extruder2ControlRoute,
extruder2SettingsRoute,
extruder2ManualRoute,
extruder2GraphRoute,
]),
mock1SerialRoute.addChildren([
+46 -11
View File
@@ -372,8 +372,8 @@ pub struct EL6021 {
is_used: bool,
pub output_ts: u64,
pub input_ts: u64,
pub has_messages_last_toggle: bool,
pub initialized: bool,
pub has_messages_last_toggle: bool,
}
impl EthercatDeviceProcessing for EL6021 {}
@@ -422,15 +422,15 @@ pub enum EL6021Port {
impl SerialInterfaceDevice<EL6021Port> for EL6021 {
fn serial_interface_has_messages(&mut self, _port: EL6021Port) -> bool {
if let Some(tx_pdo) = &mut self.txpdo.com_tx_pdo_map_22_byte {
let ret = tx_pdo.status.receive_request != self.has_messages_last_toggle;
self.has_messages_last_toggle = tx_pdo.status.receive_request;
tx_pdo.status.receive_request = !tx_pdo.status.receive_request;
return ret;
// Only check if the bit has changed, don't update our state yet
return tx_pdo.status.receive_request != self.has_messages_last_toggle;
}
return false;
}
fn serial_interface_read_message(&mut self, _port: EL6021Port) -> Option<Vec<u8>> {
//self.debug("serial_interface_read_message".to_string());
if !self.serial_interface_has_messages(_port) {
return None;
}
@@ -442,9 +442,10 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
if received_data.is_empty() {
return None;
}
if let Some(rx_pdo) = &mut self.rxpdo.com_rx_pdo_map_22_byte {
rx_pdo.control.received_acepted = !rx_pdo.control.received_acepted;
}
// Update our stored state of the toggle bit AFTER reading the data
self.has_messages_last_toggle = tx_pdo.status.receive_request;
return Some(received_data);
} else {
return None;
@@ -456,6 +457,8 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
_port: EL6021Port,
message: Vec<u8>,
) -> Result<(), Error> {
//self.debug("serial_interface_write_message".to_string());
if let Some(rx_pdo) = &mut self.rxpdo.com_rx_pdo_map_22_byte {
// perhaps the 22 could be a constant
if message.len() > 22 {
@@ -470,7 +473,6 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
rx_pdo.length = message.len() as u8;
rx_pdo.data = data_buffer;
rx_pdo.control.transmit_request = !rx_pdo.control.transmit_request;
// println!("{:?}", rx_pdo);
return Ok(());
} else {
return Err(anyhow::anyhow!("Error: RxPdo is not available"));
@@ -485,6 +487,7 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
fn get_serial_encoding(&self, _port: EL6021Port) -> Option<SerialEncoding> {
return Some(self.configuration.data_frame);
}
/// For el6021 this returns false for as long as the Initialization takes
/// When its finished it returns true
/// Every step of the init has to be done in an EtherCatCycle
@@ -492,13 +495,12 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
match port {
EL6021Port::SI1 => {
let rxpdo_opt = &mut self.rxpdo.com_rx_pdo_map_22_byte;
let txpdo_opt = &mut self.txpdo.com_tx_pdo_map_22_byte;
let rxpdo = match rxpdo_opt {
Some(rxpdo_opt) => rxpdo_opt,
None => return false,
};
let txpdo_opt = &mut self.txpdo.com_tx_pdo_map_22_byte;
let txpdo = match txpdo_opt {
Some(txpdo_opt) => txpdo_opt,
None => return false,
@@ -559,6 +561,39 @@ impl SerialInterfaceDevice<EL6021Port> for EL6021 {
}
}
}
fn serial_interface_write_finished(&mut self, port: EL6021Port) -> bool {
let txpdo_opt = &mut self.txpdo.com_tx_pdo_map_22_byte;
let txpdo = match txpdo_opt {
Some(txpdo_opt) => txpdo_opt,
None => return false,
};
let rxpdo_opt = &mut self.rxpdo.com_rx_pdo_map_22_byte;
let rxpdo = match rxpdo_opt {
Some(rxpdo_opt) => rxpdo_opt,
None => return false,
};
// request is successfully sent
return rxpdo.control.transmit_request == txpdo.status.transmit_accepted;
}
fn serial_interface_read_finished(&mut self, _port: EL6021Port) -> bool {
let txpdo_opt = &mut self.txpdo.com_tx_pdo_map_22_byte;
let txpdo = match txpdo_opt {
Some(txpdo_opt) => txpdo_opt,
None => return false,
};
let rxpdo_opt = &mut self.rxpdo.com_rx_pdo_map_22_byte;
let rxpdo = match rxpdo_opt {
Some(rxpdo_opt) => rxpdo_opt,
None => return false,
};
rxpdo.control.received_acepted = !rxpdo.control.received_acepted;
// response successfully read
return rxpdo.control.received_acepted == txpdo.status.receive_request;
}
}
#[cfg(test)]
+34
View File
@@ -17,6 +17,9 @@ pub struct SerialInterface {
Box<dyn Fn() -> Pin<Box<dyn Future<Output = Option<SerialEncoding>> + Send>> + Send + Sync>,
pub initialize: Box<dyn Fn() -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync>,
pub write_finished: Box<dyn Fn() -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync>,
pub read_finished: Box<dyn Fn() -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync>,
}
impl fmt::Debug for SerialInterface {
@@ -120,6 +123,33 @@ impl SerialInterface {
device.serial_interface_initialize(port_clone)
})
});
port2 = port.clone();
device2 = device.clone();
let write_finished = Box::new(move || -> Pin<Box<dyn Future<Output = bool> + Send>> {
let device2 = device2.clone();
let port_clone = port2.clone();
Box::pin(async move {
let mut device = device2.write().await;
device.serial_interface_write_finished(port_clone)
})
});
port2 = port.clone();
device2 = device.clone();
let read_finished = Box::new(move || -> Pin<Box<dyn Future<Output = bool> + Send>> {
let device2 = device2.clone();
let port_clone = port2.clone();
Box::pin(async move {
let mut device = device2.write().await;
device.serial_interface_read_finished(port_clone)
})
});
SerialInterface {
has_message,
write_message,
@@ -127,6 +157,8 @@ impl SerialInterface {
get_baudrate,
get_serial_encoding,
initialize,
write_finished,
read_finished,
}
}
}
@@ -145,6 +177,8 @@ where
fn get_serial_encoding(&self, port: PORTS) -> Option<SerialEncoding>;
fn get_baudrate(&self, port: PORTS) -> Option<u32>;
fn serial_interface_initialize(&mut self, port: PORTS) -> bool;
fn serial_interface_write_finished(&mut self, port: PORTS) -> bool;
fn serial_interface_read_finished(&mut self, port: PORTS) -> bool;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+3
View File
@@ -61,6 +61,9 @@ impl Actor for ExtruderV2 {
self.emit_rotation_state();
self.emit_inverter_status();
self.emit_pressure_pid_settings();
self.emit_temperature_pid_settings();
self.emit_bar();
self.emit_rpm();
self.emit_extruder_settings();
+43
View File
@@ -168,6 +168,34 @@ impl ExtruderSettingsStateEvent {
}
}
pub enum PidType {
Temperature,
Pressure,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct PidSettings {
pub ki: f64,
pub kp: f64,
pub kd: f64,
}
#[derive(Serialize, Debug, Clone)]
pub struct PidSettingsEvent {
pub ki: f64,
pub kp: f64,
pub kd: f64,
}
impl PidSettingsEvent {
pub fn build(&self, pid_type: PidType) -> Event<Self> {
match pid_type {
PidType::Temperature => Event::new("TemperaturePidSettingsEvent", self.clone()),
PidType::Pressure => Event::new("PressurePidSettingsEvent", self.clone()),
}
}
}
#[derive(Serialize, Debug, Clone)]
pub struct HeatingPowerEvent {
pub wattage: f64,
@@ -194,6 +222,7 @@ pub enum ExtruderV2Events {
ExtruderSettingsStateEvent(Event<ExtruderSettingsStateEvent>),
HeatingPowerEvent(Event<HeatingPowerEvent>),
InverterStatusEvent(Event<InverterStatusEvent>),
PidSettingsEvent(Event<PidSettingsEvent>),
}
#[derive(Deserialize, Serialize)]
@@ -217,6 +246,10 @@ enum Mutation {
ExtruderSetPressureLimit(f64),
ExtruderSetPressureLimitIsEnabled(bool),
// Pid Configure
SetTemperaturePidSettings(PidSettings),
SetPressurePidSettings(PidSettings),
// Reset
InverterReset(bool),
}
@@ -255,6 +288,7 @@ impl CacheableEvents<ExtruderV2Events> for ExtruderV2Events {
ExtruderV2Events::ExtruderSettingsStateEvent(event) => event.into(),
ExtruderV2Events::HeatingPowerEvent(event) => event.into(),
ExtruderV2Events::InverterStatusEvent(event) => event.into(),
ExtruderV2Events::PidSettingsEvent(event) => event.into(),
}
}
@@ -272,6 +306,7 @@ impl CacheableEvents<ExtruderV2Events> for ExtruderV2Events {
ExtruderV2Events::ScrewStateEvent(_) => cache_one_hour,
ExtruderV2Events::HeatingPowerEvent(_) => cache_one_hour,
ExtruderV2Events::InverterStatusEvent(_) => cache_one,
ExtruderV2Events::PidSettingsEvent(_) => cache_one,
}
}
}
@@ -306,6 +341,14 @@ impl MachineApi for ExtruderV2 {
Mutation::ExtruderSetPressureLimitIsEnabled(enabled) => {
self.set_nozzle_pressure_limit_is_enabled(enabled);
}
Mutation::SetPressurePidSettings(settings) => {
self.configure_pressure_pid(settings);
}
Mutation::SetTemperaturePidSettings(settings) => {
self.configure_temperature_pid(settings);
}
}
Ok(())
}
+41 -1
View File
@@ -1,4 +1,6 @@
use api::{ExtruderSettingsStateEvent, ExtruderV2Events, ExtruderV2Namespace};
use api::{
ExtruderSettingsStateEvent, ExtruderV2Events, ExtruderV2Namespace, PidSettings, PidType,
};
use control_core::{
actors::mitsubishi_inverter_rs485::MitsubishiControlRequests, machines::Machine,
socketio::namespace::NamespaceCacheingLogic,
@@ -384,3 +386,41 @@ impl ExtruderV2 {
.emit(ExtruderV2Events::PressureStateEvent(event));
}
}
impl ExtruderV2 {
fn emit_pressure_pid_settings(&mut self) {
let kd = self.screw_speed_controller.pid.get_kd();
let kp = self.screw_speed_controller.pid.get_kp();
let ki = self.screw_speed_controller.pid.get_ki();
let event = api::PidSettingsEvent { ki, kp, kd }.build(PidType::Pressure);
self.namespace
.emit(ExtruderV2Events::PidSettingsEvent(event));
}
fn emit_temperature_pid_settings(&mut self) {
let kd = self.temperature_controller_front.pid.get_kd();
let kp = self.temperature_controller_front.pid.get_kp();
let ki = self.temperature_controller_front.pid.get_ki();
let event = api::PidSettingsEvent { ki, kp, kd }.build(PidType::Temperature);
self.namespace
.emit(ExtruderV2Events::PidSettingsEvent(event));
}
fn configure_pressure_pid(&mut self, settings: PidSettings) {
self.screw_speed_controller
.pid
.configure(settings.ki, settings.kp, settings.kd);
}
fn configure_temperature_pid(&mut self, settings: PidSettings) {
self.temperature_controller_back
.pid
.configure(settings.ki, settings.kp, settings.kd);
self.temperature_controller_middle
.pid
.configure(settings.ki, settings.kp, settings.kd);
self.temperature_controller_front
.pid
.configure(settings.ki, settings.kp, settings.kd);
}
}
@@ -24,7 +24,7 @@ const MAX_FREQ: f64 = 60.0;
#[derive(Debug)]
pub struct ScrewSpeedController {
pid: PidController,
pub pid: PidController,
pub target_pressure: Pressure,
pub target_rpm: AngularVelocity,
pub inverter: MitsubishiInverterRS485Actor,
@@ -203,6 +203,7 @@ impl ScrewSpeedController {
pub async fn update(&mut self, now: Instant, is_extruding: bool) {
self.pressure_sensor.act(now).await;
self.inverter.act(now).await;
let measured_pressure = self.get_pressure();
if !self.uses_rpm && !is_extruding && self.motor_on {
@@ -237,11 +238,10 @@ impl ScrewSpeedController {
.update(error.get::<bar>(), now)
.clamp(MIN_FREQ, MAX_FREQ);
// println!("error:{} freq:{}", error.get::<bar>(), freq);
let frequency = Frequency::new::<hertz>(freq);
self.last_update = now;
self.inverter.set_frequency_target(frequency);
}
}
@@ -15,7 +15,7 @@ use uom::si::{
#[derive(Debug)]
pub struct TemperatureController {
pid: PidController,
pub pid: PidController,
temperature_sensor: TemperatureInputGetter,
relais: DigitalOutputSetter,
pub heating: Heating,