diff --git a/electron/src/machines/extruder/extruder2/Extruder2Settings.tsx b/electron/src/machines/extruder/extruder2/Extruder2Settings.tsx index 71f13ff9..0d9cfbc7 100644 --- a/electron/src/machines/extruder/extruder2/Extruder2Settings.tsx +++ b/electron/src/machines/extruder/extruder2/Extruder2Settings.tsx @@ -16,14 +16,19 @@ export function Extruder2SettingsPage() { resetInverter, setExtruderPressureLimit, setExtruderPressureLimitEnabled, + setInverterTargetPressure, setPressurePidKp, setPressurePidKi, setPressurePidKd, setTemperaturePidValue, setTemperatureTargetEnabled, + startPressurePidAutoTune, + stopPressurePidAutoTune, } = useExtruder2(); const [showAdvanced, setShowAdvanced] = useState(false); + const [tuneDelta, setTuneDelta] = useState(1.0); + const [frequencyStepHz, setFrequencyStepHz] = useState(5.0); return ( @@ -91,44 +96,144 @@ export function Extruder2SettingsPage() { {showAdvanced && ( <> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKp} - title="Pressure PID KP" - /> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKi} - title="Pressure PID KI" - /> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKd} - title="Pressure PID KD" - /> - - + + + + roundToDecimals(v, 2)} + onChange={setPressurePidKp} + title="Pressure PID KP" + /> + + + roundToDecimals(v, 2)} + onChange={setPressurePidKi} + title="Pressure PID KI" + /> + + + roundToDecimals(v, 2)} + onChange={setPressurePidKd} + title="Pressure PID KD" + /> + + + + + roundToDecimals(v, 1)} + onChange={setInverterTargetPressure} + /> + + + roundToDecimals(v, 1)} + onChange={setTuneDelta} + /> + + + roundToDecimals(v, 1)} + onChange={setFrequencyStepHz} + /> + + + + startPressurePidAutoTune(tuneDelta, frequencyStepHz)} + disabled={state?.pid_autotune_state.state === "running"} + className="inline-block w-fit rounded bg-blue-600 px-4 py-4 text-base text-white hover:bg-blue-700 disabled:opacity-50" + > + Start Auto-Tune + + + Stop + + + + + + + {(state?.pid_autotune_state.state ?? "not_started").replace(/_/g, " ")} + + + + + + {roundToDecimals(state?.pid_autotune_state.progress ?? 0, 1)}% + + + + {state?.pid_autotune_state.result && ( + + + + Kp: {roundToDecimals(state.pid_autotune_state.result.kp, 4)} + Ki: {roundToDecimals(state.pid_autotune_state.result.ki, 4)} + Kd: {roundToDecimals(state.pid_autotune_state.result.kd, 4)} + + { + const r = state.pid_autotune_state.result!; + setPressurePidKp(r.kp); + setPressurePidKi(r.ki); + setPressurePidKd(r.kd); + }} + className="inline-block w-fit rounded bg-green-600 px-4 py-4 text-base text-white hover:bg-green-700" + > + Apply to PID Settings + + + + )} + + @@ -297,6 +402,7 @@ export function Extruder2SettingsPage() { > )} + ); } diff --git a/electron/src/machines/extruder/extruder2/extruder2Namespace.ts b/electron/src/machines/extruder/extruder2/extruder2Namespace.ts index 9a46e54e..a50ec518 100644 --- a/electron/src/machines/extruder/extruder2/extruder2Namespace.ts +++ b/electron/src/machines/extruder/extruder2/extruder2Namespace.ts @@ -164,6 +164,26 @@ export const pidSettingsSchema = z.object({ }), }); +/** + * PID auto-tune result schema + */ +export const pidAutoTuneResultSchema = z.object({ + kp: z.number(), + ki: z.number(), + kd: z.number(), +}); + +/** + * PID auto-tune state schema + */ +export const pidAutoTuneStateSchema = z.object({ + state: z.string(), + progress: z.number(), + result: pidAutoTuneResultSchema.nullable(), +}); + +export type PidAutoTuneState = z.infer; + /** * Consolidated state event schema (state changes only) */ @@ -178,6 +198,7 @@ export const stateEventDataSchema = z.object({ extruder_settings_state: extruderSettingsStateSchema, inverter_status_state: inverterStatusStateSchema, pid_settings: pidSettingsSchema, + pid_autotune_state: pidAutoTuneStateSchema, }); // ========== Event Schemas with Wrappers ========== diff --git a/electron/src/machines/extruder/extruder2/useExtruder.ts b/electron/src/machines/extruder/extruder2/useExtruder.ts index beaaf5de..55802f9e 100644 --- a/electron/src/machines/extruder/extruder2/useExtruder.ts +++ b/electron/src/machines/extruder/extruder2/useExtruder.ts @@ -350,6 +350,25 @@ export function useExtruder2() { }); }; + const startPressurePidAutoTune = (tuneDelta: number, frequencyStepHz: number) => { + requestStartPressurePidAutoTune({ + machine_identification_unique: machineIdentification, + data: { + StartPressurePidAutoTune: { + tune_delta: tuneDelta, + frequency_step_hz: frequencyStepHz, + }, + }, + }); + }; + + const stopPressurePidAutoTune = () => { + requestStopPressurePidAutoTune({ + machine_identification_unique: machineIdentification, + data: { StopPressurePidAutoTune: true }, + }); + }; + // Mutation hooks const { request: requestInverterRotationDirection } = useMachineMutation( z.object({ SetInverterRotationDirection: z.boolean() }), @@ -424,6 +443,19 @@ export function useExtruder2() { z.object({ ResetInverter: z.boolean() }), ); + const { request: requestStartPressurePidAutoTune } = useMachineMutation( + z.object({ + StartPressurePidAutoTune: z.object({ + tune_delta: z.number(), + frequency_step_hz: z.number(), + }), + }), + ); + + const { request: requestStopPressurePidAutoTune } = useMachineMutation( + z.object({ StopPressurePidAutoTune: z.boolean() }), + ); + return { // Consolidated state state: stateOptimistic.value?.data, @@ -479,5 +511,7 @@ export function useExtruder2() { setTemperaturePidValue, setTemperatureTargetEnabled, resetInverter, + startPressurePidAutoTune, + stopPressurePidAutoTune, }; } diff --git a/electron/src/machines/extruder/extruder3/Extruder3Settings.tsx b/electron/src/machines/extruder/extruder3/Extruder3Settings.tsx index 045bcbc3..1d2fdf60 100644 --- a/electron/src/machines/extruder/extruder3/Extruder3Settings.tsx +++ b/electron/src/machines/extruder/extruder3/Extruder3Settings.tsx @@ -16,14 +16,19 @@ export function Extruder3SettingsPage() { resetInverter, setExtruderPressureLimit, setExtruderPressureLimitEnabled, + setInverterTargetPressure, setPressurePidKp, setPressurePidKi, setPressurePidKd, setTemperaturePidValue, setTemperatureTargetEnabled, + startPressurePidAutoTune, + stopPressurePidAutoTune, } = useExtruder3(); const [showAdvanced, setShowAdvanced] = useState(false); + const [tuneDelta, setTuneDelta] = useState(1.0); + const [frequencyStepHz, setFrequencyStepHz] = useState(5.0); return ( @@ -91,44 +96,144 @@ export function Extruder3SettingsPage() { {showAdvanced && ( <> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKp} - title="Pressure PID KP" - /> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKi} - title="Pressure PID KI" - /> - - - roundToDecimals(v, 2)} - onChange={setPressurePidKd} - title="Pressure PID KD" - /> - - + + + + roundToDecimals(v, 2)} + onChange={setPressurePidKp} + title="Pressure PID KP" + /> + + + roundToDecimals(v, 2)} + onChange={setPressurePidKi} + title="Pressure PID KI" + /> + + + roundToDecimals(v, 2)} + onChange={setPressurePidKd} + title="Pressure PID KD" + /> + + + + + roundToDecimals(v, 1)} + onChange={setInverterTargetPressure} + /> + + + roundToDecimals(v, 1)} + onChange={setTuneDelta} + /> + + + roundToDecimals(v, 1)} + onChange={setFrequencyStepHz} + /> + + + + startPressurePidAutoTune(tuneDelta, frequencyStepHz)} + disabled={state?.pid_autotune_state.state === "running"} + className="inline-block w-fit rounded bg-blue-600 px-4 py-4 text-base text-white hover:bg-blue-700 disabled:opacity-50" + > + Start Auto-Tune + + + Stop + + + + + + + {(state?.pid_autotune_state.state ?? "not_started").replace(/_/g, " ")} + + + + + + {roundToDecimals(state?.pid_autotune_state.progress ?? 0, 1)}% + + + + {state?.pid_autotune_state.result && ( + + + + Kp: {roundToDecimals(state.pid_autotune_state.result.kp, 4)} + Ki: {roundToDecimals(state.pid_autotune_state.result.ki, 4)} + Kd: {roundToDecimals(state.pid_autotune_state.result.kd, 4)} + + { + const r = state.pid_autotune_state.result!; + setPressurePidKp(r.kp); + setPressurePidKi(r.ki); + setPressurePidKd(r.kd); + }} + className="inline-block w-fit rounded bg-green-600 px-4 py-4 text-base text-white hover:bg-green-700" + > + Apply to PID Settings + + + + )} + + @@ -297,6 +402,7 @@ export function Extruder3SettingsPage() { > )} + ); } diff --git a/electron/src/machines/extruder/extruder3/extruder3Namespace.ts b/electron/src/machines/extruder/extruder3/extruder3Namespace.ts index b8fa2ea3..002be32a 100644 --- a/electron/src/machines/extruder/extruder3/extruder3Namespace.ts +++ b/electron/src/machines/extruder/extruder3/extruder3Namespace.ts @@ -164,6 +164,26 @@ export const pidSettingsSchema = z.object({ }), }); +/** + * PID auto-tune result schema + */ +export const pidAutoTuneResultSchema = z.object({ + kp: z.number(), + ki: z.number(), + kd: z.number(), +}); + +/** + * PID auto-tune state schema + */ +export const pidAutoTuneStateSchema = z.object({ + state: z.string(), + progress: z.number(), + result: pidAutoTuneResultSchema.nullable(), +}); + +export type PidAutoTuneState = z.infer; + /** * Consolidated state event schema (state changes only) */ @@ -178,6 +198,7 @@ export const stateEventDataSchema = z.object({ extruder_settings_state: extruderSettingsStateSchema, inverter_status_state: inverterStatusStateSchema, pid_settings: pidSettingsSchema, + pid_autotune_state: pidAutoTuneStateSchema, }); // ========== Event Schemas with Wrappers ========== diff --git a/electron/src/machines/extruder/extruder3/useExtruder.ts b/electron/src/machines/extruder/extruder3/useExtruder.ts index 7587b44e..209613d2 100644 --- a/electron/src/machines/extruder/extruder3/useExtruder.ts +++ b/electron/src/machines/extruder/extruder3/useExtruder.ts @@ -358,6 +358,25 @@ export function useExtruder3() { }); }; + const startPressurePidAutoTune = (tuneDelta: number, frequencyStepHz: number) => { + requestStartPressurePidAutoTune({ + machine_identification_unique: machineIdentification, + data: { + StartPressurePidAutoTune: { + tune_delta: tuneDelta, + frequency_step_hz: frequencyStepHz, + }, + }, + }); + }; + + const stopPressurePidAutoTune = () => { + requestStopPressurePidAutoTune({ + machine_identification_unique: machineIdentification, + data: { StopPressurePidAutoTune: true }, + }); + }; + // Mutation hooks const { request: requestInverterRotationDirection } = useMachineMutation( z.object({ SetInverterRotationDirection: z.boolean() }), @@ -432,6 +451,19 @@ export function useExtruder3() { z.object({ ResetInverter: z.boolean() }), ); + const { request: requestStartPressurePidAutoTune } = useMachineMutation( + z.object({ + StartPressurePidAutoTune: z.object({ + tune_delta: z.number(), + frequency_step_hz: z.number(), + }), + }), + ); + + const { request: requestStopPressurePidAutoTune } = useMachineMutation( + z.object({ StopPressurePidAutoTune: z.boolean() }), + ); + return { // Consolidated state state: stateOptimistic.value?.data, @@ -487,5 +519,7 @@ export function useExtruder3() { setPressurePidKd, setTemperaturePidValue, resetInverter, + startPressurePidAutoTune, + stopPressurePidAutoTune, }; }