Replace all class components with function components and move server-state
polling to @tanstack/react-query.
- add @tanstack/react-query; generate query helpers via the hey-api
@tanstack/react-query plugin (src/client/@tanstack)
- QueryClientProvider + one-time client.setConfig({ baseUrl: '' }) in index.tsx
- App polls statistics with useQuery(getStatisticsOptions, refetchInterval),
DataProcessingPlot uses getPreviewPlotOptions; manual setInterval polling gone
- memo() on presentational children; with TanStack structural sharing this
re-renders a child only when its own statistics slice changes
- PreviewImage stays imperative (binary JPEG -> object URL) using useEffect +
a ref for the object-URL lifecycle
- fix AzIntSettings correction checkboxes that mutated state in place (relied on
the poll re-render); they now use setState
- drop dead code uncovered during the port (unused upload/deactivate handlers
and imports in ImageFormatSettings, DetectorSettings, ROI)
Build (tsc + vite) passes; dev server transforms all entry modules.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
import {memo, ReactNode} from 'react';
|
|
|
|
import Paper from '@mui/material/Paper';
|
|
import {DataGrid, GridCellParams, GridColDef} from "@mui/x-data-grid";
|
|
import { fpga_status} from "../client";
|
|
|
|
function Circles(val: boolean) : ReactNode {
|
|
if (val)
|
|
return <div style={{color: "green", display: "inline"}} >●</div>;
|
|
else
|
|
return <div style={{color: "red", display: "inline"}}>○</div>;
|
|
}
|
|
|
|
function EthLinkStatus(count: number, status: number) : ReactNode {
|
|
if (!Number.isInteger(count) || !Number.isInteger(status) || (count < 1))
|
|
return <div>"ERR"</div>;
|
|
|
|
let arr: boolean[] = [];
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
if (status & (1 << i))
|
|
arr.push(true);
|
|
else
|
|
arr.push(false);
|
|
}
|
|
|
|
return <>
|
|
{ arr.map(val => Circles(val)) }
|
|
</>;
|
|
}
|
|
|
|
function CardIdle(val: boolean) : ReactNode {
|
|
return Circles(val);
|
|
}
|
|
|
|
function LinkSpeed(speed: number, width: number) : ReactNode {
|
|
if ((speed === 4) && (width === 8))
|
|
return <div style={{display: "inline"}} >Gen{speed}x{width}</div>;
|
|
else
|
|
return <div style={{color: "red", display: "inline"}} >Gen{speed}x{width}</div>;
|
|
}
|
|
|
|
function DataVolume(count_4kib: number) : string {
|
|
if (!Number.isInteger(count_4kib))
|
|
return "ERR";
|
|
|
|
let val = 4096 * count_4kib;
|
|
let val_MiB = val / (1024*1024);
|
|
let val_GiB = val / (1024*1024*1024);
|
|
|
|
if (val == 0)
|
|
return "0";
|
|
else if (val_GiB > 1)
|
|
return val_GiB.toFixed(1) + " GiB";
|
|
else
|
|
return val_MiB.toFixed(1) + " MiB";
|
|
}
|
|
|
|
|
|
type MyProps = {
|
|
s?: fpga_status;
|
|
};
|
|
|
|
const columns : GridColDef[] = [
|
|
{ field: 'pci_dev_id', type: 'string', headerName: 'PCIe ID' },
|
|
{ field: 'base_mac_addr', type: 'string', headerName: 'MAC address', width: 200 },
|
|
{ field: 'eth_link_status', type: 'string', headerName: 'Link status', align: `center`,
|
|
renderCell: (params : GridCellParams) => EthLinkStatus(params.row.eth_link_count,
|
|
params.row.eth_link_status)
|
|
},
|
|
{ field: 'idle', type: 'string', headerName: 'Idle', align: `center`,
|
|
renderCell: (params : GridCellParams) => CardIdle(params.row.idle)
|
|
},
|
|
{ field: 'power_usage_W', type: 'number', headerName: 'Power usage [W]', width: 150},
|
|
{ field: 'fpga_temp_C', type: 'number', headerName: 'FPGA temperature [C]', width: 200 },
|
|
{ field: 'hbm_temp_C', type: 'number', headerName: 'HBM temperature [C]', width: 200 },
|
|
{ field: 'packets_sls', type: 'number', headerName: 'Data (current data collection)', width: 150,
|
|
valueGetter: (params, row) => DataVolume(row.packets_sls)},
|
|
{ field: "fw_version", type: 'string', headerName: 'Firmware version', width: 150 },
|
|
{ field: "pcie_link", type: 'string', headerName: 'PCIe link', width: 100, align: `center`,
|
|
renderCell: (params : GridCellParams) => LinkSpeed(params.row.pcie_link_speed, params.row.pcie_link_width)}
|
|
];
|
|
|
|
function FpgaStatus({s}: MyProps) {
|
|
return <Paper style={{textAlign: 'center'}} sx={{ height: 500, width: '100%' }}>
|
|
{((s !== undefined) && (s.length > 0)) ?
|
|
<DataGrid
|
|
rows={s}
|
|
columns={columns}
|
|
getRowId={(row) => row.pci_dev_id}
|
|
autosizeOnMount={true}
|
|
initialState={{
|
|
pagination: { paginationModel: { pageSize: 8 } },
|
|
}}
|
|
/> : <div> No FPGA available</div>
|
|
}
|
|
</Paper>
|
|
}
|
|
|
|
export default memo(FpgaStatus);
|