farmcontrol-ui/src/components/Dashboard/common/PrinterPositionPanel.jsx

501 lines
16 KiB
JavaScript

import { useContext, useState, useEffect } from 'react'
import {
Typography,
Spin,
Flex,
Space,
Collapse,
InputNumber,
Descriptions,
Button,
Divider
} from 'antd'
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
import { ApiServerContext } from '../context/ApiServerContext'
import styled from 'styled-components'
import PropTypes from 'prop-types'
import BoolDisplay from './BoolDisplay'
import merge from 'lodash/merge'
import { round } from '../utils/Utils'
const { Text } = Typography
const CustomCollapse = styled(Collapse)`
.ant-collapse-header {
padding: 0 !important;
}
.ant-collapse-content-box {
padding-left: 0 !important;
padding-right: 0 !important;
padding-bottom: 0 !important;
}
`
const PrinterPositionPanel = ({
id,
showControls = true,
showMoreInfo = true
}) => {
const { subscribeToObjectEvent, connected, sendObjectAction } =
useContext(ApiServerContext)
const [positionData, setPositionData] = useState({
speedFactor: 1.0, // eslint-disable-line
speed: 100,
extrudeFactor: 1.0, // eslint-disable-line
absoluteCoordinates: true, // eslint-disable-line
absoluteExtrude: false, // eslint-disable-line
homingOrigin: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
toolheadPosition: [0.0, 0.0, 0.0, 0.0],
gcodePosition: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
livePosition: [0.0, 0.0, 0.0, 0.0],
maxVelocity: 1000,
maxAcceleration: 1000,
squareCornerVelocity: 100,
minCruiseRatio: 0
})
useEffect(() => {
if (id && connected == true) {
const motionEventUnsubscribe = subscribeToObjectEvent(
id,
'printer',
'motion',
(event) => {
setPositionData((prev) => {
const merged = merge({}, prev, event.data)
return merged
})
}
)
return () => {
if (motionEventUnsubscribe) motionEventUnsubscribe()
}
}
}, [id, connected])
const [speedFactor, setSpeedFactor] = useState(positionData.speedFactor)
const [extrudeFactor, setExtrudeFactor] = useState(positionData.extrudeFactor)
const [maxVelocity, setMaxVelocity] = useState(positionData.maxVelocity)
const [maxAcceleration, setMaxAcceleration] = useState(
positionData.maxAcceleration
)
const [squareCornerVelocity, setSquareCornerVelocity] = useState(
positionData.squareCornerVelocity
)
const [minCruiseRatio, setMinCruiseRatio] = useState(
positionData.minCruiseRatio
)
useEffect(() => {
if (positionData.speedFactor !== undefined) {
setSpeedFactor(positionData.speedFactor)
}
if (positionData.extrudeFactor !== undefined) {
setExtrudeFactor(positionData.extrudeFactor)
}
if (positionData.maxVelocity !== undefined) {
setMaxVelocity(positionData.maxVelocity)
}
if (positionData.maxAcceleration !== undefined) {
setMaxAcceleration(positionData.maxAcceleration)
}
if (positionData.squareCornerVelocity !== undefined) {
setSquareCornerVelocity(positionData.squareCornerVelocity)
}
if (positionData.minCruiseRatio !== undefined) {
setMinCruiseRatio(positionData.minCruiseRatio)
}
}, [
positionData.speedFactor,
positionData.extrudeFactor,
positionData.maxVelocity,
positionData.maxAcceleration,
positionData.squareCornerVelocity,
positionData.minCruiseRatio
])
const handleSetSpeedFactor = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setSpeedFactor',
data: {
speedFactor: speedFactor * 100
}
})
}
}
const handleSetExtrudeFactor = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setExtrudeFactor',
data: {
extrudeFactor: extrudeFactor * 100
}
})
}
}
const handleSetMaxVelocity = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setMaxVelocity',
data: {
maxVelocity: maxVelocity
}
})
}
}
const handleSetMaxAcceleration = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setMaxAcceleration',
data: {
maxAcceleration: maxAcceleration
}
})
}
}
const handleSetSquareCornerVelocity = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setSquareCornerVelocity',
data: {
squareCornerVelocity: squareCornerVelocity
}
})
}
}
const handleSetMinCruiseRatio = () => {
if (id && connected == true) {
sendObjectAction(id, 'printer', {
type: 'setMinCruiseRatio',
data: {
minCruiseRatio: minCruiseRatio
}
})
}
}
const moreInfoItems = [
{
key: '1',
label: 'More Position Data',
children: (
<>
<Divider style={{ margin: '0 0 12px 0' }} />
<Flex vertical gap={'middle'}>
<Text>GCode Position:</Text>
<Descriptions
column={1}
size='small'
bordered
styles={{ label: { width: '40px' } }}
>
<Descriptions.Item label='X'>
{' '}
{round(positionData.gcodePosition[0], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='Y'>
{round(positionData.gcodePosition[1], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='Z'>
{round(positionData.gcodePosition[2], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='E'>
{round(positionData.gcodePosition[3], 2)}mm
</Descriptions.Item>
</Descriptions>
{showControls && (
<>
<Space direction='vertical' style={{ width: '100%' }}>
<Space direction='horizontal'>
<Text>Max Velocity:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(maxVelocity, 2)}
min={1}
max={10000}
step={5}
style={{ width: '125px' }}
suffix='mm/s'
onChange={(value) => setMaxVelocity(value)}
onPressEnter={handleSetMaxVelocity}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetMaxVelocity}
>
Set
</Button>
</Space.Compact>
</Space>
<Space direction='vertical' style={{ width: '100%' }}>
<Space direction='horizontal'>
<Text>Max Acceleration:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(maxAcceleration, 2)}
min={1}
max={10000}
step={5}
style={{ width: '125px' }}
suffix='mm/s²'
onChange={(value) => setMaxAcceleration(value)}
onPressEnter={handleSetMaxAcceleration}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetMaxAcceleration}
>
Set
</Button>
</Space.Compact>
</Space>
</Space>
<Space direction='vertical' style={{ width: '100%' }}>
<Space direction='horizontal'>
<Text>Sqr Corner Vel:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(squareCornerVelocity, 2) || 0}
min={0.1}
max={1000}
step={0.1}
suffix='mm/s'
style={{ width: '125px' }}
onChange={(value) => setSquareCornerVelocity(value)}
onPressEnter={handleSetSquareCornerVelocity}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetSquareCornerVelocity}
>
Set
</Button>
</Space.Compact>
</Space>
</Space>
<Space direction='vertical' style={{ width: '100%' }}>
<Space direction='horizontal'>
<Text>Min Cruise Ratio:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(minCruiseRatio * 100, 2) || 0}
min={0}
max={100}
step={1}
suffix='%'
style={{ width: '125px' }}
onChange={(value) => setMinCruiseRatio(value / 100)}
onPressEnter={handleSetMinCruiseRatio}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetMinCruiseRatio}
>
Set
</Button>
</Space.Compact>
</Space>
</Space>
</Space>
</>
)}
<Text>Homing Origin:</Text>
<Descriptions
column={1}
size='small'
bordered
style={{ flexGrow: 1 }}
styles={{ label: { width: '40px' } }}
>
<Descriptions.Item label='X' span={1}>
{round(positionData.homingOrigin[0], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='Y' span={1}>
{round(positionData.homingOrigin[1], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='Z' span={1}>
{round(positionData.homingOrigin[2], 2)}mm
</Descriptions.Item>
<Descriptions.Item label='E'>
{round(positionData.homingOrigin[3], 2)}mm
</Descriptions.Item>
</Descriptions>
</Flex>
</>
)
}
]
return (
<div style={{ minWidth: 190 }}>
{positionData ? (
<Flex vertical gap='middle'>
<Flex vertical gap={'middle'}>
<Descriptions
column={1}
size='small'
bordered
styles={{ label: { width: '40px' } }}
>
<Descriptions.Item label='X'>
<Space>
<Text>{round(positionData.livePosition[0], 2)}mm</Text>
<Text type='secondary'>
{round(positionData.toolheadPosition[0], 2)}mm
</Text>
</Space>
</Descriptions.Item>
<Descriptions.Item label='Y'>
<Space>
<Text>{round(positionData.livePosition[1], 2)}mm</Text>
<Text type='secondary'>
{round(positionData.toolheadPosition[1], 2)}mm
</Text>
</Space>
</Descriptions.Item>
<Descriptions.Item label='Z'>
<Space>
<Text>{round(positionData.livePosition[2], 2)}mm</Text>
<Text type='secondary'>
{round(positionData.toolheadPosition[2], 2)}mm
</Text>
</Space>
</Descriptions.Item>
<Descriptions.Item label='E'>
<Space>
<Text>{round(positionData.livePosition[3], 2)}mm</Text>
<Text type='secondary'>
{round(positionData.toolheadPosition[3], 2)}mm
</Text>
</Space>
</Descriptions.Item>
</Descriptions>
{showControls && (
<>
<Space direction='vertical' style={{ width: '100%' }}>
<Space direction='horizontal'>
<Text>Speed Factor:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(speedFactor, 2)}
min={0.1}
max={2}
step={0.1}
style={{ width: '125px' }}
suffix='%'
onChange={(value) => setSpeedFactor(value)}
onPressEnter={handleSetSpeedFactor}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetSpeedFactor}
>
Set
</Button>
</Space.Compact>
</Space>
<Space direction='horizontal'>
<Text>Extrude Factor:</Text>
<Space.Compact block size='small'>
<InputNumber
value={round(extrudeFactor, 2)}
min={0.1}
max={2}
step={0.1}
style={{ width: '125px' }}
suffix='%'
onChange={(value) => setExtrudeFactor(value)}
onPressEnter={handleSetExtrudeFactor}
size='small'
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetExtrudeFactor}
>
Set
</Button>
</Space.Compact>
</Space>
</Space>
</>
)}
<Descriptions
column={1}
size='small'
bordered
styles={{ label: { width: '125px' } }}
>
<Descriptions.Item label='Speed'>
{round(positionData.speed, 2)}mm/s
</Descriptions.Item>
<Descriptions.Item label='Abs Coor'>
{positionData ? (
<BoolDisplay
value={positionData.absoluteCoordinates}
yesNo={true}
/>
) : (
<Text>n/a</Text>
)}
</Descriptions.Item>
<Descriptions.Item label='Abs Extrude'>
{positionData ? (
<BoolDisplay
value={positionData.absoluteExtrude}
yesNo={true}
/>
) : (
<Text>n/a</Text>
)}
</Descriptions.Item>
</Descriptions>
</Flex>
{showMoreInfo && (
<CustomCollapse
ghost
size='small'
items={moreInfoItems}
expandIcon={({ isActive }) => (
<CaretRightOutlined rotate={isActive ? 90 : 0} />
)}
/>
)}
</Flex>
) : (
<Flex justify='centre'>
<Spin indicator={<LoadingOutlined spin />} size='large' />
</Flex>
)}
</div>
)
}
PrinterPositionPanel.propTypes = {
id: PropTypes.string.isRequired,
showControls: PropTypes.bool,
showMoreInfo: PropTypes.bool
}
export default PrinterPositionPanel