2021-02-16 05:10:20 +01:00
|
|
|
import { h, Fragment } from 'preact';
|
2021-02-07 22:46:05 +01:00
|
|
|
import ActivityIndicator from '../components/ActivityIndicator';
|
|
|
|
import Button from '../components/Button';
|
|
|
|
import Heading from '../components/Heading';
|
|
|
|
import Link from '../components/Link';
|
2022-11-24 03:03:20 +01:00
|
|
|
import { useWs } from '../api/ws';
|
2022-02-26 20:11:00 +01:00
|
|
|
import useSWR from 'swr';
|
2022-11-13 19:48:14 +01:00
|
|
|
import axios from 'axios';
|
2021-02-07 22:46:05 +01:00
|
|
|
import { Table, Tbody, Thead, Tr, Th, Td } from '../components/Table';
|
2022-12-07 14:36:56 +01:00
|
|
|
import { useState } from 'preact/hooks';
|
2022-11-13 19:48:14 +01:00
|
|
|
import Dialog from '../components/Dialog';
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2021-02-09 20:35:33 +01:00
|
|
|
const emptyObject = Object.freeze({});
|
|
|
|
|
2022-11-20 15:56:48 +01:00
|
|
|
export default function System() {
|
2022-11-13 19:48:14 +01:00
|
|
|
const [state, setState] = useState({ showFfprobe: false, ffprobe: '' });
|
2022-02-26 20:11:00 +01:00
|
|
|
const { data: config } = useSWR('config');
|
2021-01-26 16:04:03 +01:00
|
|
|
|
2021-02-16 05:10:20 +01:00
|
|
|
const {
|
2021-02-18 05:53:57 +01:00
|
|
|
value: { payload: stats },
|
2022-11-24 03:03:20 +01:00
|
|
|
} = useWs('stats');
|
2022-02-26 20:11:00 +01:00
|
|
|
const { data: initialStats } = useSWR('stats');
|
2021-01-09 18:26:46 +01:00
|
|
|
|
2022-11-29 02:24:20 +01:00
|
|
|
const {
|
|
|
|
cpu_usages,
|
|
|
|
gpu_usages,
|
|
|
|
detectors,
|
|
|
|
service = {},
|
|
|
|
detection_fps: _,
|
|
|
|
...cameras
|
|
|
|
} = stats || initialStats || emptyObject;
|
2021-01-18 19:49:00 +01:00
|
|
|
|
2021-02-09 20:35:33 +01:00
|
|
|
const detectorNames = Object.keys(detectors || emptyObject);
|
2022-11-29 02:24:20 +01:00
|
|
|
const gpuNames = Object.keys(gpu_usages || emptyObject);
|
2021-02-09 20:35:33 +01:00
|
|
|
const cameraNames = Object.keys(cameras || emptyObject);
|
2021-01-18 19:49:00 +01:00
|
|
|
|
2022-11-13 19:48:14 +01:00
|
|
|
const onHandleFfprobe = async (camera, e) => {
|
|
|
|
if (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
|
|
|
|
setState({ ...state, showFfprobe: true });
|
|
|
|
let paths = '';
|
|
|
|
config.cameras[camera].ffmpeg.inputs.forEach((input) => {
|
|
|
|
if (paths) {
|
|
|
|
paths += ',';
|
|
|
|
paths += input.path;
|
|
|
|
} else {
|
|
|
|
paths = input.path;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const response = await axios.get('ffprobe', {
|
|
|
|
params: {
|
|
|
|
paths,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (response.status === 200) {
|
2022-11-29 02:24:20 +01:00
|
|
|
setState({ ...state, showFfprobe: true, ffprobe: JSON.stringify(response.data, null, 2) });
|
2022-11-13 19:48:14 +01:00
|
|
|
} else {
|
2022-11-29 02:24:20 +01:00
|
|
|
setState({ ...state, showFfprobe: true, ffprobe: 'There was an error getting the ffprobe output.' });
|
2022-11-13 19:48:14 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const onCopyFfprobe = async () => {
|
|
|
|
await window.navigator.clipboard.writeText(JSON.stringify(state.ffprobe, null, 2));
|
|
|
|
setState({ ...state, ffprobe: '', showFfprobe: false });
|
|
|
|
};
|
|
|
|
|
2022-11-29 02:24:20 +01:00
|
|
|
const onHandleVainfo = async (e) => {
|
|
|
|
if (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
|
|
|
|
const response = await axios.get('vainfo');
|
|
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
setState({ ...state, showVainfo: true, vainfo: JSON.stringify(response.data, null, 2) });
|
|
|
|
} else {
|
|
|
|
setState({ ...state, showVainfo: true, vainfo: 'There was an error getting the vainfo output.' });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const onCopyVainfo = async () => {
|
|
|
|
await window.navigator.clipboard.writeText(JSON.stringify(state.vaifp, null, 2));
|
|
|
|
setState({ ...state, vainfo: '', showVainfo: false });
|
|
|
|
};
|
|
|
|
|
2021-02-16 05:10:20 +01:00
|
|
|
return (
|
2022-02-27 15:04:12 +01:00
|
|
|
<div className="space-y-4 p-2 px-4">
|
2021-01-18 19:49:00 +01:00
|
|
|
<Heading>
|
2022-11-20 15:56:48 +01:00
|
|
|
System <span className="text-sm">{service.version}</span>
|
2021-01-18 19:49:00 +01:00
|
|
|
</Heading>
|
2022-11-29 02:24:20 +01:00
|
|
|
|
2022-11-13 19:48:14 +01:00
|
|
|
{state.showFfprobe && (
|
|
|
|
<Dialog>
|
|
|
|
<div className="p-4">
|
|
|
|
<Heading size="lg">Ffprobe Output</Heading>
|
|
|
|
{state.ffprobe != '' ? <p className="mb-2">{state.ffprobe}</p> : <ActivityIndicator />}
|
|
|
|
</div>
|
|
|
|
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
|
|
|
<Button className="ml-2" onClick={() => onCopyFfprobe()} type="text">
|
|
|
|
Copy
|
|
|
|
</Button>
|
|
|
|
<Button
|
|
|
|
className="ml-2"
|
|
|
|
onClick={() => setState({ ...state, ffprobe: '', showFfprobe: false })}
|
|
|
|
type="text"
|
|
|
|
>
|
|
|
|
Close
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
)}
|
2021-01-27 01:18:45 +01:00
|
|
|
|
2022-11-29 02:24:20 +01:00
|
|
|
{state.showVainfo && (
|
|
|
|
<Dialog>
|
|
|
|
<div className="p-4">
|
|
|
|
<Heading size="lg">Vainfo Output</Heading>
|
2022-12-07 05:13:48 +01:00
|
|
|
{state.vainfo != '' ? <p className="mb-2 max-h-96 overflow-scroll">{state.vainfo}</p> : <ActivityIndicator />}
|
2022-11-29 02:24:20 +01:00
|
|
|
</div>
|
|
|
|
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
|
|
|
<Button className="ml-2" onClick={() => onCopyVainfo()} type="text">
|
|
|
|
Copy
|
|
|
|
</Button>
|
|
|
|
<Button className="ml-2" onClick={() => setState({ ...state, vainfo: '', showVainfo: false })} type="text">
|
|
|
|
Close
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
)}
|
|
|
|
|
2021-02-16 05:10:20 +01:00
|
|
|
{!detectors ? (
|
|
|
|
<div>
|
|
|
|
<ActivityIndicator />
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<Fragment>
|
2022-11-13 19:48:14 +01:00
|
|
|
<Heading size="lg">Detectors</Heading>
|
|
|
|
<div data-testid="detectors" className="grid grid-cols-1 3xl:grid-cols-3 md:grid-cols-2 gap-4">
|
|
|
|
{detectorNames.map((detector) => (
|
|
|
|
<div key={detector} className="dark:bg-gray-800 shadow-md hover:shadow-lg rounded-lg transition-shadow">
|
|
|
|
<div className="text-lg flex justify-between p-4">{detector}</div>
|
|
|
|
<div className="p-2">
|
|
|
|
<Table className="w-full">
|
|
|
|
<Thead>
|
|
|
|
<Tr>
|
|
|
|
<Th>P-ID</Th>
|
|
|
|
<Th>Detection Start</Th>
|
|
|
|
<Th>Inference Speed</Th>
|
|
|
|
</Tr>
|
|
|
|
</Thead>
|
|
|
|
<Tbody>
|
|
|
|
<Tr>
|
|
|
|
<Td>{detectors[detector]['pid']}</Td>
|
|
|
|
<Td>{detectors[detector]['detection_start']}</Td>
|
|
|
|
<Td>{detectors[detector]['inference_speed']}</Td>
|
|
|
|
</Tr>
|
|
|
|
</Tbody>
|
|
|
|
</Table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
2021-02-16 05:10:20 +01:00
|
|
|
</div>
|
2021-02-06 00:56:04 +01:00
|
|
|
|
2022-11-29 02:24:20 +01:00
|
|
|
<div className="text-lg flex justify-between p-4">
|
|
|
|
<Heading size="lg">GPUs</Heading>
|
|
|
|
<Button onClick={(e) => onHandleVainfo(e)}>vainfo</Button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{!gpu_usages ? (
|
|
|
|
<div className="p-4">
|
|
|
|
<Link href={'https://docs.frigate.video/configuration/hardware_acceleration'}>
|
|
|
|
Hardware acceleration has not been setup, see the docs to setup hardware acceleration.
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div data-testid="gpus" className="grid grid-cols-1 3xl:grid-cols-3 md:grid-cols-2 gap-4">
|
|
|
|
{gpuNames.map((gpu) => (
|
|
|
|
<div key={gpu} className="dark:bg-gray-800 shadow-md hover:shadow-lg rounded-lg transition-shadow">
|
|
|
|
<div className="text-lg flex justify-between p-4">{gpu}</div>
|
|
|
|
<div className="p-2">
|
|
|
|
{gpu_usages[gpu]['gpu'] == -1 ? (
|
|
|
|
<div className="p-4">
|
|
|
|
There was an error getting usage stats. Either your GPU does not support this or frigate does
|
|
|
|
not have proper access.
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<Table className="w-full">
|
|
|
|
<Thead>
|
|
|
|
<Tr>
|
|
|
|
<Th>Gpu %</Th>
|
|
|
|
<Th>Memory %</Th>
|
|
|
|
</Tr>
|
|
|
|
</Thead>
|
|
|
|
<Tbody>
|
|
|
|
<Tr>
|
|
|
|
<Td>{gpu_usages[gpu]['gpu']}</Td>
|
|
|
|
<Td>{gpu_usages[gpu]['mem']}</Td>
|
|
|
|
</Tr>
|
|
|
|
</Tbody>
|
|
|
|
</Table>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
2022-11-13 19:48:14 +01:00
|
|
|
<Heading size="lg">Cameras</Heading>
|
|
|
|
<div data-testid="cameras" className="grid grid-cols-1 3xl:grid-cols-3 md:grid-cols-2 gap-4">
|
|
|
|
{cameraNames.map((camera) => (
|
|
|
|
<div key={camera} className="dark:bg-gray-800 shadow-md hover:shadow-lg rounded-lg transition-shadow">
|
2022-12-07 14:37:45 +01:00
|
|
|
<div className="capitalize text-lg flex justify-between p-4">
|
2022-11-13 19:48:14 +01:00
|
|
|
<Link href={`/cameras/${camera}`}>{camera.replaceAll('_', ' ')}</Link>
|
|
|
|
<Button onClick={(e) => onHandleFfprobe(camera, e)}>ffprobe</Button>
|
|
|
|
</div>
|
|
|
|
<div className="p-2">
|
|
|
|
<Table className="w-full">
|
|
|
|
<Thead>
|
|
|
|
<Tr>
|
|
|
|
<Th>Process</Th>
|
|
|
|
<Th>P-ID</Th>
|
|
|
|
<Th>fps</Th>
|
|
|
|
<Th>Cpu %</Th>
|
|
|
|
<Th>Memory %</Th>
|
|
|
|
</Tr>
|
|
|
|
</Thead>
|
|
|
|
<Tbody>
|
|
|
|
<Tr key="capture" index="0">
|
|
|
|
<Td>Capture</Td>
|
|
|
|
<Td>{cameras[camera]['capture_pid']}</Td>
|
|
|
|
<Td>{cameras[camera]['process_fps']}</Td>
|
|
|
|
<Td>{cpu_usages[cameras[camera]['capture_pid']]['cpu']}%</Td>
|
|
|
|
<Td>{cpu_usages[cameras[camera]['capture_pid']]['mem']}%</Td>
|
|
|
|
</Tr>
|
|
|
|
<Tr key="detect" index="1">
|
|
|
|
<Td>Detect</Td>
|
|
|
|
<Td>{cameras[camera]['pid']}</Td>
|
|
|
|
<Td>
|
|
|
|
{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)
|
|
|
|
</Td>
|
|
|
|
<Td>{cpu_usages[cameras[camera]['pid']]['cpu']}%</Td>
|
2022-11-19 14:14:54 +01:00
|
|
|
<Td>{cpu_usages[cameras[camera]['pid']]['mem']}%</Td>
|
2022-11-13 19:48:14 +01:00
|
|
|
</Tr>
|
|
|
|
<Tr key="ffmpeg" index="2">
|
|
|
|
<Td>ffmpeg</Td>
|
|
|
|
<Td>{cameras[camera]['ffmpeg_pid']}</Td>
|
|
|
|
<Td>{cameras[camera]['camera_fps']}</Td>
|
|
|
|
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['cpu']}%</Td>
|
2022-11-19 14:14:54 +01:00
|
|
|
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['mem']}%</Td>
|
2022-11-13 19:48:14 +01:00
|
|
|
</Tr>
|
|
|
|
</Tbody>
|
|
|
|
</Table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
2021-02-16 05:10:20 +01:00
|
|
|
</div>
|
|
|
|
|
2022-11-20 15:56:48 +01:00
|
|
|
<p>System stats update automatically every {config.mqtt.stats_interval} seconds.</p>
|
2021-02-16 05:10:20 +01:00
|
|
|
</Fragment>
|
|
|
|
)}
|
2021-01-18 19:49:00 +01:00
|
|
|
</div>
|
|
|
|
);
|
2021-01-09 18:26:46 +01:00
|
|
|
}
|