From ebb72c651785d085ea926ede6c9c3adb46849038 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Tue, 3 Jun 2025 13:44:21 -0700 Subject: [PATCH] Added HPG validation --- apps/dispatch-server/modules/mission.ts | 4 + .../modules/socketJWTmiddleware.ts | 1 - apps/dispatch-server/routes/mission.ts | 150 +++++++++++------- .../socket-events/connect-pilot.ts | 4 - .../app/_components/left/SituationBoard.tsx | 2 - .../app/_components/map/AircraftMarker.tsx | 81 ++++++---- .../map/_components/AircraftMarkerTabs.tsx | 24 ++- apps/dispatch/app/_querys/aircrafts.ts | 12 +- apps/dispatch/app/_querys/missions.ts | 10 +- .../app/_store/dispatch/connectionStore.ts | 1 - .../app/api/aircrafts/positionlog/route.ts | 33 ++++ apps/dispatch/app/api/position-log/route.ts | 3 - .../_components/pannel/MissionForm.tsx | 12 +- apps/dispatch/app/dispatch/page.tsx | 2 + 14 files changed, 223 insertions(+), 116 deletions(-) create mode 100644 apps/dispatch/app/api/aircrafts/positionlog/route.ts diff --git a/apps/dispatch-server/modules/mission.ts b/apps/dispatch-server/modules/mission.ts index 0e33eb58..70f12f72 100644 --- a/apps/dispatch-server/modules/mission.ts +++ b/apps/dispatch-server/modules/mission.ts @@ -49,6 +49,10 @@ export const sendAlert = async ( ...mission, Stations, }); + io.to(`desktop:${aircraft.userId}`).emit("mission-alert", { + missionId: mission.id, + }); + const user = await prisma.user.findUnique({ where: { id: aircraft.userId }, }); diff --git a/apps/dispatch-server/modules/socketJWTmiddleware.ts b/apps/dispatch-server/modules/socketJWTmiddleware.ts index 0670870f..6c556a58 100644 --- a/apps/dispatch-server/modules/socketJWTmiddleware.ts +++ b/apps/dispatch-server/modules/socketJWTmiddleware.ts @@ -18,7 +18,6 @@ export const jwtMiddleware = async (socket: Socket, next: (err?: ExtendedError) next(); } catch (err) { - console.error(err); next(new Error("Authentication error")); } }; diff --git a/apps/dispatch-server/routes/mission.ts b/apps/dispatch-server/routes/mission.ts index 00198812..52ce77c7 100644 --- a/apps/dispatch-server/routes/mission.ts +++ b/apps/dispatch-server/routes/mission.ts @@ -13,6 +13,7 @@ import { Router } from "express"; import { io } from "../index"; import { sendNtfyMission } from "modules/ntfy"; import { sendAlert } from "modules/mission"; +import { userInfo } from "os"; const router: Router = Router(); @@ -192,25 +193,90 @@ router.post("/:id/send-alert", async (req, res) => { } }); -router.post("/:id/send-sds", async (req, res) => { - const sdsMessage = req.body as MissionSdsLog; - const newMission = await prisma.mission.update({ - where: { - id: Number(req.params.id), - }, - data: { - missionLog: { - push: sdsMessage as any, - }, - }, - }); +router.post("/send-sds", async (req, res) => { + const { sdsMessage, missionId } = req.body as { + missionId?: number; + sdsMessage: MissionSdsLog; + }; io.to(`station:${sdsMessage.data.stationId}`).emit("sds-message", sdsMessage); - res.json({ - message: "SDS message sent", - mission: newMission, - }); - io.to("dispatchers").emit("update-mission", newMission); + if (missionId) { + const newMission = await prisma.mission.update({ + where: { + id: Number(missionId), + }, + data: { + missionLog: { + push: sdsMessage as any, + }, + }, + }); + + res.json({ + message: "SDS message sent", + mission: newMission, + }); + io.to("dispatchers").emit("update-mission", newMission); + } else { + res.json({ + message: "SDS message sent", + }); + } +}); + +router.post("/:id/hpg-validation-result", async (req, res) => { + try { + const missionId = req.params.id; + const result = req.body as { + state: HpgValidationState; + lat: number; + lng: number; + alertWhenValid?: boolean; + userId?: number; + }; + + if (!result) return; + + const newMission = await prisma.mission.update({ + where: { id: Number(missionId) }, + data: { + // save position of new mission + addressLat: result.state === "POSITION_AMANDED" ? result.lat : undefined, + addressLng: result.state === "POSITION_AMANDED" ? result.lng : undefined, + hpgLocationLat: result.lat, + hpgLocationLng: result.lng, + hpgValidationState: result.state, + }, + }); + io.to("dispatchers").emit("update-mission", newMission); + + const noActionRequired = result.state === "VALID"; + if (noActionRequired) { + io.to(`user:${result.userId}`).emit("notification", { + type: "hpg-validation", + status: "success", + message: `HPG Validierung erfolgreich`, + } as NotificationPayload); + + if (result.alertWhenValid) { + if (!req.user) return; + sendAlert(Number(missionId), {}, req.user); + } + } else { + io.to(`user:${result.userId}`).emit("notification", { + type: "hpg-validation", + status: "failed", + message: result.state, + } as NotificationPayload); + } + res.json({ + message: `HPG Validation result processed`, + }); + } catch (error) { + console.error("Error in HPG validation result:", error); + res.status(500).json({ error: "Failed to process HPG validation result" }); + return; + } }); router.post("/:id/validate-hpg", async (req, res) => { @@ -255,50 +321,14 @@ router.post("/:id/validate-hpg", async (req, res) => { res.json({ message: "HPG validierung gestartet", }); - - io.to(`desktop:${activeAircraftinMission}`).emit( - "hpg-validation", - { - hpgMissionType: mission?.hpgMissionString, - lat: mission?.addressLat, - lng: mission?.addressLng, - }, - async (result: { state: HpgValidationState; lat: number; lng: number }) => { - console.log("response from user:", result); - - const newMission = await prisma.mission.update({ - where: { id: Number(id) }, - data: { - // save position of new mission - addressLat: result.state === "POSITION_AMANDED" ? result.lat : mission.addressLat, - addressLng: result.state === "POSITION_AMANDED" ? result.lng : mission.addressLng, - hpgLocationLat: result.lat, - hpgLocationLng: result.lng, - hpgValidationState: result.state, - }, - }); - io.to("dispatchers").emit("update-mission", newMission); - - const noActionRequired = result.state === "VALID"; - if (noActionRequired) { - io.to(`user:${req.user?.id}`).emit("notification", { - type: "hpg-validation", - status: "success", - message: `HPG Validierung erfolgreich`, - } as NotificationPayload); - if (config?.alertWhenValid) { - if (!req.user) return; - sendAlert(Number(id), {}, req.user); - } - } else { - io.to(`user:${req.user?.id}`).emit("notification", { - type: "hpg-validation", - status: "failed", - message: `HPG Validation fehlgeschlagen`, - } as NotificationPayload); - } - }, + console.log( + `HPG Validation for ${user?.publicId} (${mission?.hpgSelectedMissionString}) started`, ); + io.to(`desktop:${activeAircraftinMission?.userId}`).emit("hpg-validation", { + missionId: parseInt(id), + userId: req.user?.id, + alertWhenValid: config?.alertWhenValid || false, + }); } catch (error) { console.error(error); res.json({ error: (error as Error).message || "Failed to validate HPG" }); diff --git a/apps/dispatch-server/socket-events/connect-pilot.ts b/apps/dispatch-server/socket-events/connect-pilot.ts index c5e743f0..cb077970 100644 --- a/apps/dispatch-server/socket-events/connect-pilot.ts +++ b/apps/dispatch-server/socket-events/connect-pilot.ts @@ -63,10 +63,6 @@ export const handleConnectPilot = userId: userId, loginTime: new Date().toISOString(), stationId: parseInt(stationId), - // TODO: remove this after testing - posLat: 51.45, - posLng: 9.77, - posH145active: true, }, }); socket.join("dispatchers"); // Join the dispatchers room diff --git a/apps/dispatch/app/_components/left/SituationBoard.tsx b/apps/dispatch/app/_components/left/SituationBoard.tsx index f65510a0..1d14859c 100644 --- a/apps/dispatch/app/_components/left/SituationBoard.tsx +++ b/apps/dispatch/app/_components/left/SituationBoard.tsx @@ -41,8 +41,6 @@ export const SituationBoard = () => { }); const { setOpenAircraftMarker, setOpenMissionMarker, setMap } = useMapStore((state) => state); - console.log("station", connectedAircrafts); - return (
diff --git a/apps/dispatch/app/_components/map/AircraftMarker.tsx b/apps/dispatch/app/_components/map/AircraftMarker.tsx index da68b817..ba347ad0 100644 --- a/apps/dispatch/app/_components/map/AircraftMarker.tsx +++ b/apps/dispatch/app/_components/map/AircraftMarker.tsx @@ -1,4 +1,4 @@ -import { Marker, useMap } from "react-leaflet"; +import { Marker, Polyline, useMap } from "react-leaflet"; import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet"; import { useMapStore } from "_store/mapStore"; import { Fragment, useCallback, useEffect, useRef, useState, useMemo } from "react"; @@ -13,7 +13,7 @@ import FMSStatusHistory, { } from "./_components/AircraftMarkerTabs"; import { ConnectedAircraft, Station } from "@repo/db"; import { useQuery } from "@tanstack/react-query"; -import { getConnectedAircraftsAPI } from "_querys/aircrafts"; +import { getConnectedAircraftPositionLogAPI, getConnectedAircraftsAPI } from "_querys/aircrafts"; import { getMissionsAPI } from "_querys/missions"; import { checkSimulatorConnected } from "_helpers/simulatorConnected"; @@ -263,9 +263,18 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station: const popupRef = useRef(null); const { openAircraftMarker, setOpenAircraftMarker } = useMapStore((store) => store); + const { data: positionLog } = useQuery({ + queryKey: ["positionlog", aircraft.id], + queryFn: () => + getConnectedAircraftPositionLogAPI({ + id: aircraft.id, + }), + refetchInterval: 10000, + }); useEffect(() => { const handleClick = () => { + console.log("Marker clicked", aircraft.id); const open = openAircraftMarker.some((m) => m.id === aircraft.id); if (open) { setOpenAircraftMarker({ @@ -289,7 +298,7 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station: return () => { marker?.off("click", handleClick); }; - }, [aircraft.id, openAircraftMarker, setOpenAircraftMarker]); + }, [aircraft.id, openAircraftMarker, setOpenAircraftMarker, markerRef.current]); const [anchor, setAnchor] = useState<"topleft" | "topright" | "bottomleft" | "bottomright">( "topleft", @@ -372,38 +381,46 @@ const AircraftMarker = ({ aircraft }: { aircraft: ConnectedAircraft & { Station:
`; }; + if (!aircraft.posLat || !aircraft.posLng) return null; return ( - { - - } + {openAircraftMarker.some((m) => m.id === aircraft.id) && !hideMarker && ( - -
- -
-
+ <> + +
+ +
+
+ [pos.lat, pos.lng]) || []} + /> + )}
); diff --git a/apps/dispatch/app/_components/map/_components/AircraftMarkerTabs.tsx b/apps/dispatch/app/_components/map/_components/AircraftMarkerTabs.tsx index 5ea062eb..ac3e7d45 100644 --- a/apps/dispatch/app/_components/map/_components/AircraftMarkerTabs.tsx +++ b/apps/dispatch/app/_components/map/_components/AircraftMarkerTabs.tsx @@ -29,6 +29,7 @@ import { Hash, ListCollapse, LocateFixed, + Lollipop, MapPin, Mountain, Navigation, @@ -252,6 +253,14 @@ const RettungsmittelTab = ({ ALT: {aircraft.posAlt} ft
+
+ + {" "} + + {aircraft.posH145active ? "H145 Aktiv" : "H145 Inaktiv"} + + +
); }; @@ -306,8 +315,14 @@ const SDSTab = ({ const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected"; const sendSdsMutation = useMutation({ - mutationFn: async ({ id, message }: { id: number; message: MissionSdsLog }) => { - await sendSdsMessageAPI(id, message); + mutationFn: async ({ + missionId, + sdsMessage, + }: { + missionId?: number; + sdsMessage: MissionSdsLog; + }) => { + await sendSdsMessageAPI({ missionId, sdsMessage }); queryClient.invalidateQueries({ queryKey: ["missions"], }); @@ -348,11 +363,10 @@ const SDSTab = ({