- minor big fixes
- default settings toggled -update camera zoom need to test
This commit is contained in:
@@ -5,6 +5,7 @@ import { useCameraZoom } from "../../hooks/useCameraZoom";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import Loading from "../UI/Loading";
|
import Loading from "../UI/Loading";
|
||||||
import ErrorState from "../UI/ErrorState";
|
import ErrorState from "../UI/ErrorState";
|
||||||
|
import { zoomMapping } from "../../utils/utils";
|
||||||
|
|
||||||
type SnapshotContainerProps = {
|
type SnapshotContainerProps = {
|
||||||
side: string;
|
side: string;
|
||||||
@@ -13,20 +14,14 @@ type SnapshotContainerProps = {
|
|||||||
onZoomLevelChange?: (level: number) => void;
|
onZoomLevelChange?: (level: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SnapshotContainer = ({
|
export const SnapshotContainer = ({ side, settingsPage, zoomLevel, onZoomLevelChange }: SnapshotContainerProps) => {
|
||||||
side,
|
|
||||||
settingsPage,
|
|
||||||
zoomLevel,
|
|
||||||
onZoomLevelChange,
|
|
||||||
}: SnapshotContainerProps) => {
|
|
||||||
const { canvasRef, isError, isPending } = useGetOverviewSnapshot(side);
|
const { canvasRef, isError, isPending } = useGetOverviewSnapshot(side);
|
||||||
const cameraControllerSide =
|
const cameraControllerSide = side === "CameraA" ? "CameraControllerA" : "CameraControllerB";
|
||||||
side === "CameraA" ? "CameraControllerA" : "CameraControllerB";
|
|
||||||
const { mutation } = useCameraZoom({ camera: cameraControllerSide });
|
const { mutation } = useCameraZoom({ camera: cameraControllerSide });
|
||||||
|
|
||||||
const handleZoomClick = () => {
|
const handleZoomClick = () => {
|
||||||
const baseLevel = zoomLevel ?? 1;
|
const baseLevel = zoomLevel ?? 1;
|
||||||
const newLevel = baseLevel >= 8 ? 1 : baseLevel * 2;
|
const newLevel = baseLevel >= 4 ? 1 : baseLevel * 2;
|
||||||
|
|
||||||
if (onZoomLevelChange) onZoomLevelChange(newLevel);
|
if (onZoomLevelChange) onZoomLevelChange(newLevel);
|
||||||
|
|
||||||
@@ -35,9 +30,11 @@ export const SnapshotContainer = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (zoomLevel) {
|
if (zoomLevel) {
|
||||||
|
const text = zoomMapping(zoomLevel);
|
||||||
const zoomInOptions: ZoomInOptions = {
|
const zoomInOptions: ZoomInOptions = {
|
||||||
camera: cameraControllerSide,
|
camera: cameraControllerSide,
|
||||||
multiplier: zoomLevel,
|
multiplier: zoomLevel,
|
||||||
|
multiplierText: text,
|
||||||
};
|
};
|
||||||
mutation.mutate(zoomInOptions);
|
mutation.mutate(zoomInOptions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
|
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
|
||||||
import CardHeader from "../UI/CardHeader";
|
import CardHeader from "../UI/CardHeader";
|
||||||
import { useCameraZoom } from "../../hooks/useCameraZoom";
|
import { useCameraZoom } from "../../hooks/useCameraZoom";
|
||||||
import { parseRTSPUrl } from "../../utils/utils";
|
import { parseRTSPUrl, zoomMapping } from "../../utils/utils";
|
||||||
|
|
||||||
type CameraSettingsProps = {
|
type CameraSettingsProps = {
|
||||||
initialData: CameraConfig;
|
initialData: CameraConfig;
|
||||||
@@ -25,7 +25,12 @@ const CameraSettingFields = ({
|
|||||||
const [showPwd, setShowPwd] = useState(false);
|
const [showPwd, setShowPwd] = useState(false);
|
||||||
const cameraControllerSide = initialData?.id === "CameraA" ? "CameraControllerA" : "CameraControllerB";
|
const cameraControllerSide = initialData?.id === "CameraA" ? "CameraControllerA" : "CameraControllerB";
|
||||||
const { mutation, query } = useCameraZoom({ camera: cameraControllerSide });
|
const { mutation, query } = useCameraZoom({ camera: cameraControllerSide });
|
||||||
const zoomOptions = [1, 2, 4, 8];
|
const zoomOptions = [1, 2, 4];
|
||||||
|
// const zoomTextOptions = [
|
||||||
|
// { label: "near", value: 4 },
|
||||||
|
// { label: "medium", value: 2 },
|
||||||
|
// { label: "far", value: 1 },
|
||||||
|
// ];
|
||||||
|
|
||||||
const parsed = parseRTSPUrl(initialData?.propURI?.value);
|
const parsed = parseRTSPUrl(initialData?.propURI?.value);
|
||||||
|
|
||||||
@@ -36,6 +41,7 @@ const CameraSettingFields = ({
|
|||||||
}, [query?.data, onZoomLevelChange]);
|
}, [query?.data, onZoomLevelChange]);
|
||||||
|
|
||||||
const getZoomLevel = (levelstring: string | undefined) => {
|
const getZoomLevel = (levelstring: string | undefined) => {
|
||||||
|
console.log(levelstring);
|
||||||
switch (levelstring) {
|
switch (levelstring) {
|
||||||
case "1x":
|
case "1x":
|
||||||
return 1;
|
return 1;
|
||||||
@@ -46,8 +52,6 @@ const CameraSettingFields = ({
|
|||||||
case "4x":
|
case "4x":
|
||||||
return 4;
|
return 4;
|
||||||
|
|
||||||
case "8x":
|
|
||||||
return 8;
|
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -80,11 +84,13 @@ const CameraSettingFields = ({
|
|||||||
|
|
||||||
const handleRadioButtonChange = async (levelNumber: number) => {
|
const handleRadioButtonChange = async (levelNumber: number) => {
|
||||||
if (!onZoomLevelChange || !zoomLevel) return;
|
if (!onZoomLevelChange || !zoomLevel) return;
|
||||||
|
const text = zoomMapping(levelNumber);
|
||||||
onZoomLevelChange(levelNumber);
|
onZoomLevelChange(levelNumber);
|
||||||
|
|
||||||
const zoomInOptions: ZoomInOptions = {
|
const zoomInOptions: ZoomInOptions = {
|
||||||
camera: cameraControllerSide,
|
camera: cameraControllerSide,
|
||||||
multiplier: levelNumber,
|
multiplier: levelNumber,
|
||||||
|
multiplierText: text,
|
||||||
};
|
};
|
||||||
|
|
||||||
mutation.mutate(zoomInOptions);
|
mutation.mutate(zoomInOptions);
|
||||||
@@ -169,7 +175,7 @@ const CameraSettingFields = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="my-3">
|
<div className="my-3">
|
||||||
<CardHeader title="Zoom settings" />
|
<CardHeader title="Zoom settings" />
|
||||||
<div className="mx-auto grid grid-cols-4 items-center">
|
<div className="mx-auto grid grid-cols-3 place-items-center">
|
||||||
{zoomOptions.map((zoom) => (
|
{zoomOptions.map((zoom) => (
|
||||||
<div key={zoom} className="my-3">
|
<div key={zoom} className="my-3">
|
||||||
<Field
|
<Field
|
||||||
@@ -187,7 +193,7 @@ const CameraSettingFields = ({
|
|||||||
peer-checked:border-2 peer-checked:border-blue-900
|
peer-checked:border-2 peer-checked:border-blue-900
|
||||||
peer-checked:text-blue-600 peer-checked:bg-gray-100"
|
peer-checked:text-blue-600 peer-checked:bg-gray-100"
|
||||||
>
|
>
|
||||||
x{zoom}
|
{zoomMapping(zoom)}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ const NumberPlate = ({ motion, vrm, size }: NumberPlateProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative ${options.plateWidth} ${
|
className={`relative ${options.plateWidth} ${options.borderWidth} border-black rounded-xl text-nowrap
|
||||||
options.borderWidth
|
|
||||||
} border-black rounded-xl text-nowrap
|
|
||||||
text-black px-6 py-2
|
text-black px-6 py-2
|
||||||
${motion ? "bg-yellow-400" : "bg-white"}`}
|
${motion ? "bg-yellow-400" : "bg-white"}`}
|
||||||
>
|
>
|
||||||
@@ -50,9 +48,7 @@ const NumberPlate = ({ motion, vrm, size }: NumberPlateProps) => {
|
|||||||
<div className="absolute inset-y-0 left-0 bg-blue-600 w-8 flex flex-col">
|
<div className="absolute inset-y-0 left-0 bg-blue-600 w-8 flex flex-col">
|
||||||
<GB />
|
<GB />
|
||||||
</div>
|
</div>
|
||||||
<p className={`pl-4 font-extrabold ${options.textSize} text-right`}>
|
<p className={`pl-4 font-extrabold ${options.textSize} text-right`}>{vrm && formatNumberPlate(vrm)}</p>
|
||||||
{vrm && formatNumberPlate(vrm)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,9 +58,7 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
|
|||||||
type="text"
|
type="text"
|
||||||
id="backoffice"
|
id="backoffice"
|
||||||
placeholder="https://www.backoffice.com"
|
placeholder="https://www.backoffice.com"
|
||||||
className={`p-1.5 border ${
|
className={`p-1.5 border border-gray-400 rounded-lg w-full md:w-60`}
|
||||||
errors.backOfficeURL && touched.backOfficeURL ? "border-red-500" : "border-gray-400 "
|
|
||||||
} rounded-lg w-full md:w-60`}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
@@ -70,9 +68,7 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
|
|||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
placeholder="Back office username"
|
placeholder="Back office username"
|
||||||
className={`p-1.5 border ${
|
className={`p-1.5 border border-gray-400 rounded-lg w-full md:w-60`}
|
||||||
errors.username && touched.username ? "border-red-500" : "border-gray-400 "
|
|
||||||
} rounded-lg w-full md:w-60`}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
@@ -159,7 +155,6 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
|
|||||||
id="timestampSource"
|
id="timestampSource"
|
||||||
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
||||||
>
|
>
|
||||||
<option value="">-- Select format --</option>
|
|
||||||
<option value={"UTC"}>UTC</option>
|
<option value={"UTC"}>UTC</option>
|
||||||
<option value={"local"}>Local</option>
|
<option value={"local"}>Local</option>
|
||||||
</Field>
|
</Field>
|
||||||
@@ -172,9 +167,8 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
|
|||||||
id="GPSFormat"
|
id="GPSFormat"
|
||||||
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
||||||
>
|
>
|
||||||
<option value="">-- Select format --</option>
|
|
||||||
<option value={"Decimal Degrees"}>Decimal degrees</option>
|
|
||||||
<option value={"Minutes"}>Minutes</option>
|
<option value={"Minutes"}>Minutes</option>
|
||||||
|
<option value={"Decimal Degrees"}>Decimal degrees</option>
|
||||||
</Field>
|
</Field>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -46,16 +46,6 @@ const SettingForms = () => {
|
|||||||
const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => {
|
const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => {
|
||||||
const errors: InitialValuesFormErrors = {};
|
const errors: InitialValuesFormErrors = {};
|
||||||
|
|
||||||
const url = values.backOfficeURL?.trim();
|
|
||||||
const username = values.username?.trim();
|
|
||||||
const password = values.password?.trim();
|
|
||||||
if (!url) {
|
|
||||||
errors.backOfficeURL = "Required";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!username) errors.username = "Required";
|
|
||||||
if (!password) errors.password = "Required";
|
|
||||||
|
|
||||||
const read = Number(values.readTimeoutSeconds);
|
const read = Number(values.readTimeoutSeconds);
|
||||||
if (!Number.isFinite(read)) {
|
if (!Number.isFinite(read)) {
|
||||||
errors.readTimeoutSeconds = "Must be a number";
|
errors.readTimeoutSeconds = "Must be a number";
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => {
|
|||||||
icon={faArrowLeft}
|
icon={faArrowLeft}
|
||||||
size="2xl"
|
size="2xl"
|
||||||
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
||||||
onClick={() => navigationDest(side)}
|
onClick={() => navigationDest("Rear")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { useMutation, useQuery, type QueryFunctionContext } from "@tanstack/react-query";
|
||||||
useMutation,
|
|
||||||
useQuery,
|
|
||||||
type QueryFunctionContext,
|
|
||||||
} from "@tanstack/react-query";
|
|
||||||
import { CAM_BASE } from "../utils/config";
|
import { CAM_BASE } from "../utils/config";
|
||||||
import type { zoomConfig, ZoomInOptions } from "../types/types";
|
import type { zoomConfig, ZoomInOptions } from "../types/types";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -10,7 +6,7 @@ import { useEffect } from "react";
|
|||||||
|
|
||||||
async function zoomIn(options: ZoomInOptions) {
|
async function zoomIn(options: ZoomInOptions) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${CAM_BASE}/Ip${options.camera}-command?magnification=${options.multiplier}x`,
|
`${CAM_BASE}/Ip${options.camera}-command?magnification=${options.multiplierText?.toLowerCase()}`,
|
||||||
{
|
{
|
||||||
signal: AbortSignal.timeout(500),
|
signal: AbortSignal.timeout(500),
|
||||||
}
|
}
|
||||||
@@ -22,11 +18,9 @@ async function zoomIn(options: ZoomInOptions) {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchZoomInConfig({
|
async function fetchZoomInConfig({ queryKey }: QueryFunctionContext<[string, zoomConfig]>) {
|
||||||
queryKey,
|
|
||||||
}: QueryFunctionContext<[string, zoomConfig]>) {
|
|
||||||
const [, { camera }] = queryKey;
|
const [, { camera }] = queryKey;
|
||||||
const response = await fetch(`${CAM_BASE}/Ip${camera}-inspect`, {
|
const response = await fetch(`${CAM_BASE}/Ip${camera}-command`, {
|
||||||
signal: AbortSignal.timeout(500),
|
signal: AbortSignal.timeout(500),
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@@ -374,6 +374,7 @@ export type ModemConfig = {
|
|||||||
export type ZoomInOptions = {
|
export type ZoomInOptions = {
|
||||||
camera: string;
|
camera: string;
|
||||||
multiplier: number;
|
multiplier: number;
|
||||||
|
multiplierText?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type zoomConfig = {
|
export type zoomConfig = {
|
||||||
@@ -407,5 +408,6 @@ export type NPEDSTATE = {
|
|||||||
|
|
||||||
export type NPEDACTION = {
|
export type NPEDACTION = {
|
||||||
type: string;
|
type: string;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
payload: any;
|
payload: any;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -158,3 +158,16 @@ export function getHotlistName(obj: HotlistMatches | undefined) {
|
|||||||
|
|
||||||
export const getNPEDCategory = (r?: SightingType | null) =>
|
export const getNPEDCategory = (r?: SightingType | null) =>
|
||||||
r?.metadata?.npedJSON?.["NPED CATEGORY"] as "A" | "B" | "C" | "D" | undefined;
|
r?.metadata?.npedJSON?.["NPED CATEGORY"] as "A" | "B" | "C" | "D" | undefined;
|
||||||
|
|
||||||
|
export const zoomMapping = (zoomLevel: number) => {
|
||||||
|
switch (zoomLevel) {
|
||||||
|
case 1:
|
||||||
|
return "Far";
|
||||||
|
case 2:
|
||||||
|
return "Medium";
|
||||||
|
case 4:
|
||||||
|
return "Close";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user