Files
Mav-Mobile-UI/src/utils/utils.ts

218 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import switchSound from "../assets/sounds/ui/switch.mp3";
import popup from "../assets/sounds/ui/popup_open.mp3";
import notification from "../assets/sounds/ui/notification.mp3";
import beep from "../assets/sounds/ui/Beep.wav";
import warning from "../assets/sounds/ui/Warning.wav";
import ding from "../assets/sounds/ui/Ding.wav";
import shutter from "../assets/sounds/ui/shutter.mp3";
import attention from "../assets/sounds/ui/Attention.wav";
import type { HotlistMatches, SightingType } from "../types/types";
export function getSoundFileURL(name: string) {
const sounds: Record<string, string> = {
switch: switchSound,
popup: popup,
notification: notification,
beep: beep,
warning: warning,
ding: ding,
shutter: shutter,
attention: attention,
};
return sounds[name] ?? null;
}
export const showSoundURL = (url: URL | string | undefined) => {
console.log(url);
};
const randomChars = () => {
const uppercaseAsciiStart = 65;
const letterIndex = Math.floor(Math.random() * 26);
const letter = String.fromCharCode(uppercaseAsciiStart + letterIndex);
return letter;
};
export function cleanArray(str: string) {
const toArr = str?.split(",");
const cleaned = toArr?.map((el: string) => {
const test = el.replace(/[^0-9a-z]/gi, "");
return test;
});
return cleaned;
}
export function parseRTSPUrl(url: string) {
const regex = /rtsp:\/\/([^:]+):([^@]+)@([^:/]+):?(\d+)?(\/.*)?/;
const match = url?.match(regex);
if (!match) {
return null;
}
return {
username: match[1],
password: match[2],
ip: match[3],
port: match[4] || "554", // default RTSP port
path: match[5] || "/",
};
}
const generateNumberPlate = () => {
const numberPlateLetters = new Array(4);
const characters: string[] = [];
for (let index = 0; index <= numberPlateLetters.length; index++) {
const letter = randomChars();
characters.push(letter);
}
const number = Math.floor(Math.random() * 99);
characters.splice(2, 0, number + " ");
const numberPlate = characters.join("");
return numberPlate;
};
export const generateNumberPlateList = (numberofSightings = 5) => {
const numberPlates = [];
for (let i = 0; i <= numberofSightings; i++) {
numberPlates.push(generateNumberPlate());
}
return numberPlates;
};
export const formatNumberPlate = (plate: string) => {
const splittedPlate = plate?.split("");
splittedPlate?.splice(4, 0, " ");
const formattedPlate = splittedPlate?.join("");
return formattedPlate;
};
export const BLANK_IMG = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
export function capitalize(s?: string) {
return s ? s.charAt(0).toUpperCase() + s.slice(1) : "";
}
export function formatAge(tsMillis: number) {
const ms = Date.now() - tsMillis;
const seconds = 5 * Math.floor(ms / 5000); // quantize to 5s like the original
const d = Math.floor(seconds / (3600 * 24));
const h = Math.floor((seconds % (3600 * 24)) / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
if (d > 0) return `${d}d ago`;
if (h > 0) return `${h}h ago`;
if (m > 0) return `${m}m ago`;
return `${s}s ago`;
}
export function drawRects(
canvas: HTMLCanvasElement,
imageEl: HTMLImageElement,
rects: [number, number, number, number][],
color: string
) {
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Ensure canvas size matches displayed image size
const w = imageEl.clientWidth || imageEl.naturalWidth;
const h = imageEl.clientHeight || imageEl.naturalHeight;
if (canvas.width !== w) canvas.width = w;
if (canvas.height !== h) canvas.height = h;
ctx.imageSmoothingEnabled = false;
ctx.lineWidth = 1;
ctx.strokeStyle = color;
rects.forEach((r) => {
const [x, y, rw, rh] = r;
ctx.beginPath();
ctx.rect(Math.round(x * w), Math.round(y * h), Math.round(rw * w), Math.round(rh * h));
ctx.stroke();
});
}
export const checkIsHotListHit = (sigthing: SightingType | null) => {
if (!sigthing) return;
if (sigthing?.metadata?.hotlistMatches) {
const isHotListHit = Object.values(sigthing?.metadata?.hotlistMatches).includes(true);
return isHotListHit;
}
};
export function getHotlistName(obj: HotlistMatches | undefined) {
if (!obj) return;
const hotlistNames = Object.entries(obj)
.filter(([, value]) => value === true)
.map(([key]) => key);
return hotlistNames;
}
export const getNPEDCategory = (r?: SightingType | null) =>
r?.metadata?.npedJSON?.["NPED CATEGORY"] as "A" | "B" | "C" | "D" | undefined;
export const getNPEDReason = (r?: SightingType | null) => r?.metadata?.npedJSON;
export const zoomMapping = (zoomLevel: number | undefined) => {
switch (zoomLevel) {
case 1:
return "Near";
case 2:
return "Mid";
case 4:
return "Far";
default:
break;
}
};
export const reverseZoomMapping = (magnification: string) => {
switch (magnification) {
case "near":
return 1;
case "mid":
return 2;
case "far":
return 4;
default:
break;
}
};
export const ValidateIPaddress = (value: string | undefined) => {
if (!value) return;
const regex =
/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
if (!regex.test(value)) {
return "Invalid IP address format";
}
};
export function isUtcOutOfSync(
data: {
utctime: number;
localdate: string;
localtime: string;
},
maxDriftMs = 60_000
) {
const utc = new Date(data.utctime);
const [d, m, y] = data.localdate.split("/").map(Number);
const [hh, mm, ss] = data.localtime.split(":").map(Number);
// Construct local Date in devices local timezone
const local = new Date(y, m - 1, d, hh, mm, ss);
const driftMs = Math.abs(utc.getTime() - local.getTime() + utc.getTimezoneOffset() * 60_000);
return { driftMs, outOfSync: driftMs > maxDriftMs };
}