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 = ({