Compare commits
17
Commits
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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -226,6 +226,11 @@ export const extruder2: MachinePreset = {
|
||||
product_id: 394604626,
|
||||
revision: 0x160000,
|
||||
},
|
||||
{
|
||||
vendor_id: 2,
|
||||
product_id: 394604626,
|
||||
revision: 0x100000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user