Refactor Load and Unload Filament Stock components to utilize WizardView for improved user experience, integrate API server context for real-time temperature and filament sensor updates, and streamline form validation logic.
This commit is contained in:
parent
657d6a5c6e
commit
ecf73c13c0
@ -1,27 +1,14 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Typography,
|
||||
Flex,
|
||||
Steps,
|
||||
Divider,
|
||||
Descriptions,
|
||||
Alert
|
||||
} from 'antd'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Form, Flex, Descriptions, Alert } from 'antd'
|
||||
import PropTypes from 'prop-types'
|
||||
import { PrintServerContext } from '../../context/PrintServerContext'
|
||||
|
||||
import FilamentStockSelect from '../../common/FilamentStockSelect'
|
||||
import PrinterSelect from '../../common/PrinterSelect'
|
||||
import FilamentStockDisplay from '../../common/FilamentStockDisplay'
|
||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import PrinterState from '../../common/StateDisplay'
|
||||
|
||||
const { Title } = Typography
|
||||
import ObjectSelect from '../../common/ObjectSelect'
|
||||
import ObjectDisplay from '../../common/ObjectDisplay'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
|
||||
const LoadFilamentStock = ({
|
||||
onOk,
|
||||
@ -29,7 +16,8 @@ const LoadFilamentStock = ({
|
||||
printer = null,
|
||||
filamentStockLoaded = false
|
||||
}) => {
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
const { connected, subscribeToObjectEvent, sendObjectAction } =
|
||||
useContext(ApiServerContext)
|
||||
|
||||
LoadFilamentStock.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
@ -38,8 +26,6 @@ const LoadFilamentStock = ({
|
||||
filamentStockLoaded: PropTypes.bool
|
||||
}
|
||||
|
||||
const { printServer } = useContext(PrintServerContext)
|
||||
|
||||
const initialLoadFilamentStockForm = {
|
||||
printer: printer,
|
||||
filamentStock: null
|
||||
@ -47,8 +33,7 @@ const LoadFilamentStock = ({
|
||||
|
||||
const [loadFilamentStockLoading, setLoadFilamentStockLoading] =
|
||||
useState(false)
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [nextEnabled, setNextEnabled] = useState(false)
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
||||
const [targetTemperature, setTargetTemperature] = useState(0)
|
||||
const [filamentSensorDetected, setFilamentSensorDetected] =
|
||||
@ -62,77 +47,69 @@ const LoadFilamentStock = ({
|
||||
loadFilamentStockForm
|
||||
)
|
||||
|
||||
// Add websocket temperature monitoring
|
||||
useEffect(() => {
|
||||
if (loadFilamentStockFormValues.printer) {
|
||||
const params = {
|
||||
printerId: loadFilamentStockFormValues.printer._id,
|
||||
objects: {
|
||||
extruder: null,
|
||||
'filament_switch_sensor fsensor': null
|
||||
if (printer?._id && connected) {
|
||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
||||
printer._id,
|
||||
'printer',
|
||||
'temperature',
|
||||
(event) => {
|
||||
if (event.data?.extruder?.current) {
|
||||
setCurrentTemperature(event.data.extruder.current)
|
||||
}
|
||||
if (event.data?.extruder?.target) {
|
||||
setTargetTemperature(event.data.extruder.target)
|
||||
}
|
||||
}
|
||||
|
||||
const notifyStatusUpdate = (statusUpdate) => {
|
||||
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||
setCurrentTemperature(statusUpdate.extruder.temperature)
|
||||
}
|
||||
if (statusUpdate?.extruder?.target !== undefined) {
|
||||
setTargetTemperature(statusUpdate.extruder.target)
|
||||
}
|
||||
if (
|
||||
statusUpdate?.['filament_switch_sensor fsensor']
|
||||
?.filament_detected !== undefined
|
||||
) {
|
||||
setFilamentSensorDetected(
|
||||
Boolean(
|
||||
statusUpdate['filament_switch_sensor fsensor'].filament_detected
|
||||
)
|
||||
const filamentStockEventUnsubscribe = subscribeToObjectEvent(
|
||||
printer._id,
|
||||
'printer',
|
||||
'filamentSensor',
|
||||
(event) => {
|
||||
console.log('filamentSensor', event.data)
|
||||
setFilamentSensorDetected(event.data.detected)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
printServer.emit('printer.objects.subscribe', params)
|
||||
printServer.emit('printer.objects.query', params)
|
||||
printServer.on('notify_status_update', notifyStatusUpdate)
|
||||
|
||||
return () => {
|
||||
printServer.off('notify_status_update', notifyStatusUpdate)
|
||||
printServer.emit('printer.objects.unsubscribe', params)
|
||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
||||
if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe()
|
||||
}
|
||||
}
|
||||
}, [printServer, loadFilamentStockFormValues.printer])
|
||||
}, [printer?._id, connected])
|
||||
|
||||
useEffect(() => {
|
||||
// Validate form fields
|
||||
loadFilamentStockForm
|
||||
.validateFields({
|
||||
validateOnly: true
|
||||
})
|
||||
.then(() => setNextEnabled(filamentSensorDetected))
|
||||
.catch(() => setNextEnabled(false))
|
||||
.then(() => {
|
||||
const hasPrinter = Boolean(loadFilamentStockFormValues.printer)
|
||||
const hasFilamentStock = Boolean(
|
||||
loadFilamentStockFormValues.filamentStock
|
||||
)
|
||||
|
||||
// Step 0 (Preheat): needs printer + filament detected + (temp reached or no temp set)
|
||||
const preheatReady =
|
||||
hasPrinter &&
|
||||
filamentSensorDetected &&
|
||||
(targetTemperature === 0 || currentTemperature >= targetTemperature)
|
||||
|
||||
// Step 1+ (Required/Summary): needs filamentStock selected
|
||||
// Form is valid if preheat is ready (for step 0) OR filamentStock is selected (for step 1+)
|
||||
// This allows progression: step 0 can proceed when preheat is ready,
|
||||
// and step 1+ can proceed when filamentStock is selected
|
||||
setFormValid(preheatReady || (hasPrinter && hasFilamentStock))
|
||||
})
|
||||
.catch(() => setFormValid(false))
|
||||
}, [
|
||||
loadFilamentStockForm,
|
||||
loadFilamentStockFormUpdateValues,
|
||||
filamentSensorDetected
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
filamentSensorDetected == true &&
|
||||
currentTemperature >= targetTemperature
|
||||
) {
|
||||
setNextEnabled(filamentSensorDetected)
|
||||
if (currentStep == 0) {
|
||||
setCurrentStep(1)
|
||||
}
|
||||
} else if (filamentSensorDetected == false) {
|
||||
setCurrentStep(0)
|
||||
}
|
||||
}, [
|
||||
loadFilamentStockFormValues,
|
||||
filamentSensorDetected,
|
||||
targetTemperature,
|
||||
currentTemperature,
|
||||
currentStep
|
||||
targetTemperature
|
||||
])
|
||||
|
||||
const summaryItems = [
|
||||
@ -140,8 +117,9 @@ const LoadFilamentStock = ({
|
||||
key: 'filamentStock',
|
||||
label: 'Stock',
|
||||
children: loadFilamentStockFormValues.filamentStock ? (
|
||||
<FilamentStockDisplay
|
||||
filamentStock={loadFilamentStockFormValues.filamentStock}
|
||||
<ObjectDisplay
|
||||
objectType='filamentStock'
|
||||
object={loadFilamentStockFormValues.filamentStock}
|
||||
/>
|
||||
) : (
|
||||
'n/a'
|
||||
@ -151,9 +129,12 @@ const LoadFilamentStock = ({
|
||||
key: 'printer',
|
||||
label: 'Printer',
|
||||
children: loadFilamentStockFormValues.printer ? (
|
||||
<PrinterState printer={loadFilamentStockFormValues.printer} />
|
||||
<ObjectDisplay
|
||||
objectType='printer'
|
||||
object={loadFilamentStockFormValues.printer}
|
||||
/>
|
||||
) : (
|
||||
'n/a>'
|
||||
'n/a'
|
||||
)
|
||||
}
|
||||
]
|
||||
@ -169,10 +150,16 @@ const LoadFilamentStock = ({
|
||||
|
||||
try {
|
||||
// Set the extruder temperature
|
||||
await printServer.emit('printer.filamentstock.load', {
|
||||
printerId: loadFilamentStockFormValues.printer._id,
|
||||
filamentStockId: loadFilamentStockFormValues.filamentStock._id
|
||||
})
|
||||
await sendObjectAction(
|
||||
loadFilamentStockFormValues.printer._id,
|
||||
'printer',
|
||||
{
|
||||
type: 'loadFilamentStock',
|
||||
data: {
|
||||
filamentStock: loadFilamentStockFormValues.filamentStock
|
||||
}
|
||||
}
|
||||
)
|
||||
onOk()
|
||||
} finally {
|
||||
setLoadFilamentStockLoading(false)
|
||||
@ -196,7 +183,7 @@ const LoadFilamentStock = ({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<PrinterSelect checkable={false} />
|
||||
<ObjectSelect type='printer' checkable={false} />
|
||||
</Form.Item>
|
||||
{targetTemperature == 0 ? (
|
||||
<Alert
|
||||
@ -226,10 +213,9 @@ const LoadFilamentStock = ({
|
||||
|
||||
{loadFilamentStockFormValues.printer ? (
|
||||
<PrinterTemperaturePanel
|
||||
showHeatedBed={false}
|
||||
showBed={false}
|
||||
showMoreInfo={false}
|
||||
printerId={loadFilamentStockFormValues.printer._id}
|
||||
shouldUnsubscribe={false}
|
||||
id={loadFilamentStockFormValues.printer._id}
|
||||
/>
|
||||
) : null}
|
||||
</Flex>
|
||||
@ -250,7 +236,7 @@ const LoadFilamentStock = ({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<FilamentStockSelect />
|
||||
<ObjectSelect type='filamentStock' />
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
@ -267,26 +253,8 @@ const LoadFilamentStock = ({
|
||||
]
|
||||
|
||||
return (
|
||||
<Flex gap={'middle'}>
|
||||
{!isMobile && (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />}
|
||||
|
||||
<Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'>
|
||||
<Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
|
||||
Load Filament Stock
|
||||
</Title>
|
||||
<Form
|
||||
name='basic'
|
||||
name='loadFilamentStock'
|
||||
autoComplete='off'
|
||||
form={loadFilamentStockForm}
|
||||
onFinish={handleLoadFilamentStock}
|
||||
@ -298,44 +266,15 @@ const LoadFilamentStock = ({
|
||||
}
|
||||
initialValues={initialLoadFilamentStockForm}
|
||||
>
|
||||
<div style={{ minHeight: '260px' }}>{steps[currentStep].content}</div>
|
||||
|
||||
<Flex justify={'end'}>
|
||||
<Button
|
||||
style={{
|
||||
margin: '0 8px'
|
||||
}}
|
||||
onClick={() => setCurrentStep(currentStep - 1)}
|
||||
disabled={!(currentStep > 0)}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
{currentStep < steps.length - 1 && (
|
||||
<Button
|
||||
type='primary'
|
||||
disabled={!nextEnabled}
|
||||
onClick={() => {
|
||||
setCurrentStep(currentStep + 1)
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
{currentStep === steps.length - 1 && (
|
||||
<Button
|
||||
type='primary'
|
||||
<WizardView
|
||||
title='Load Filament Stock'
|
||||
steps={steps}
|
||||
onSubmit={() => loadFilamentStockForm.submit()}
|
||||
formValid={formValid}
|
||||
loading={loadFilamentStockLoading}
|
||||
onClick={() => {
|
||||
loadFilamentStockForm.submit()
|
||||
}}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
submitText='Done'
|
||||
/>
|
||||
</Form>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { Form, Button, Typography, Flex, Steps, Divider, Alert } from 'antd'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { Form, Flex, Alert } from 'antd'
|
||||
import PropTypes from 'prop-types'
|
||||
import { PrintServerContext } from '../../context/PrintServerContext'
|
||||
|
||||
import PrinterSelect from '../../common/PrinterSelect'
|
||||
import ObjectSelect from '../../common/ObjectSelect'
|
||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
const { connected, subscribeToObjectEvent, sendObjectAction } =
|
||||
useContext(ApiServerContext)
|
||||
UnloadFilamentStock.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool.isRequired,
|
||||
printer: PropTypes.object
|
||||
}
|
||||
|
||||
const { printServer } = useContext(PrintServerContext)
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
|
||||
const initialUnloadFilamentStockForm = {
|
||||
printer: printer
|
||||
}
|
||||
|
||||
const [unloadFilamentStockLoading, setUnloadFilamentStockLoading] =
|
||||
useState(false)
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [nextEnabled, setNextEnabled] = useState(false)
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
||||
const [targetTemperature, setTargetTemperature] = useState(0)
|
||||
const [filamentSensorDetected, setFilamentSensorDetected] = useState(true)
|
||||
@ -36,46 +32,35 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
const [unloadFilamentStockFormValues, setUnloadFilamentStockFormValues] =
|
||||
useState(initialUnloadFilamentStockForm)
|
||||
|
||||
// Add websocket temperature monitoring
|
||||
useEffect(() => {
|
||||
if (unloadFilamentStockFormValues.printer) {
|
||||
const params = {
|
||||
printerId: unloadFilamentStockFormValues.printer._id,
|
||||
objects: {
|
||||
extruder: null,
|
||||
'filament_switch_sensor fsensor': null
|
||||
if (printer?._id && connected) {
|
||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
||||
printer._id,
|
||||
'printer',
|
||||
'temperature',
|
||||
(event) => {
|
||||
if (event.data?.extruder?.current) {
|
||||
setCurrentTemperature(event.data.extruder.current)
|
||||
}
|
||||
if (event.data?.extruder?.target) {
|
||||
setTargetTemperature(event.data.extruder.target)
|
||||
}
|
||||
}
|
||||
|
||||
const notifyStatusUpdate = (statusUpdate) => {
|
||||
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||
setCurrentTemperature(statusUpdate.extruder.temperature)
|
||||
}
|
||||
if (statusUpdate?.extruder?.target !== undefined) {
|
||||
setTargetTemperature(statusUpdate.extruder.target)
|
||||
}
|
||||
if (
|
||||
statusUpdate?.['filament_switch_sensor fsensor']
|
||||
?.filament_detected !== undefined
|
||||
) {
|
||||
setFilamentSensorDetected(
|
||||
Boolean(
|
||||
statusUpdate['filament_switch_sensor fsensor'].filament_detected
|
||||
)
|
||||
const filamentStockEventUnsubscribe = subscribeToObjectEvent(
|
||||
printer._id,
|
||||
'printer',
|
||||
'filamentSensor',
|
||||
(event) => {
|
||||
setFilamentSensorDetected(event.data.detected)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
printServer.emit('printer.objects.subscribe', params)
|
||||
printServer.emit('printer.objects.query', params)
|
||||
printServer.on('notify_status_update', notifyStatusUpdate)
|
||||
|
||||
return () => {
|
||||
printServer.off('notify_status_update', notifyStatusUpdate)
|
||||
printServer.emit('printer.objects.unsubscribe', params)
|
||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
||||
if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe()
|
||||
}
|
||||
}
|
||||
}, [printServer, unloadFilamentStockFormValues.printer])
|
||||
}, [printer?._id, connected])
|
||||
|
||||
useEffect(() => {
|
||||
if (reset) {
|
||||
@ -90,14 +75,14 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
})
|
||||
.then(() => {
|
||||
// Only enable next if we have a printer selected, we're not loading, and we've reached target temperature
|
||||
setNextEnabled(
|
||||
setFormValid(
|
||||
Boolean(unloadFilamentStockFormValues.printer) &&
|
||||
!unloadFilamentStockLoading &&
|
||||
currentTemperature + 1 > targetTemperature &&
|
||||
targetTemperature != 0
|
||||
)
|
||||
})
|
||||
.catch(() => setNextEnabled(false))
|
||||
.catch(() => setFormValid(false))
|
||||
}, [
|
||||
unloadFilamentStockForm,
|
||||
unloadFilamentStockFormValues,
|
||||
@ -109,10 +94,13 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
const handleUnloadFilamentStock = async () => {
|
||||
setUnloadFilamentStockLoading(true)
|
||||
// Send G-code to retract the filament
|
||||
await printServer.emit('printer.gcode.script', {
|
||||
printerId: unloadFilamentStockFormValues.printer._id,
|
||||
script: `_CLIENT_LINEAR_MOVE E=-200 F=1000`
|
||||
})
|
||||
await sendObjectAction(
|
||||
unloadFilamentStockFormValues.printer._id,
|
||||
'printer',
|
||||
{
|
||||
type: 'unloadFilamentStock'
|
||||
}
|
||||
)
|
||||
//setUnloadFilamentStockLoading(false)
|
||||
}
|
||||
|
||||
@ -140,7 +128,7 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<PrinterSelect checkable={false} />
|
||||
<ObjectSelect type='printer' checkable={false} />
|
||||
</Form.Item>
|
||||
|
||||
{unloadFilamentStockLoading == false ? (
|
||||
@ -182,11 +170,11 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{unloadFilamentStockFormValues.printer ? (
|
||||
{unloadFilamentStockFormValues.printer?._id ? (
|
||||
<PrinterTemperaturePanel
|
||||
showHeatedBed={false}
|
||||
showBed={false}
|
||||
showMoreInfo={false}
|
||||
printerId={unloadFilamentStockFormValues.printer._id}
|
||||
id={unloadFilamentStockFormValues.printer._id}
|
||||
/>
|
||||
) : null}
|
||||
</Flex>
|
||||
@ -195,24 +183,6 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
]
|
||||
|
||||
return (
|
||||
<Flex gap={'middle'}>
|
||||
{!isMobile && (
|
||||
<div style={{ minWidth: '160px' }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
direction='vertical'
|
||||
style={{ width: 'fit-content' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />}
|
||||
|
||||
<Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'>
|
||||
<Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
|
||||
Unload Filament Stock
|
||||
</Title>
|
||||
<Form
|
||||
name='unloadFilamentStock'
|
||||
autoComplete='off'
|
||||
@ -226,34 +196,15 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||
}
|
||||
initialValues={initialUnloadFilamentStockForm}
|
||||
>
|
||||
<div style={{ minHeight: '260px' }}>{steps[currentStep].content}</div>
|
||||
|
||||
<Flex justify={'end'}>
|
||||
<Button
|
||||
style={{
|
||||
margin: '0 8px'
|
||||
}}
|
||||
onClick={() => setCurrentStep(currentStep - 1)}
|
||||
disabled={!(currentStep > 0)}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
{currentStep === steps.length - 1 && (
|
||||
<Button
|
||||
type='primary'
|
||||
<WizardView
|
||||
title='Unload Filament Stock'
|
||||
steps={steps}
|
||||
onSubmit={() => unloadFilamentStockForm.submit()}
|
||||
formValid={formValid}
|
||||
loading={unloadFilamentStockLoading}
|
||||
disabled={!nextEnabled}
|
||||
onClick={() => {
|
||||
unloadFilamentStockForm.submit()
|
||||
}}
|
||||
>
|
||||
{unloadFilamentStockLoading ? 'Unloading...' : 'Unload'}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
submitText={unloadFilamentStockLoading ? 'Unloading...' : 'Unload'}
|
||||
/>
|
||||
</Form>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user