Session 022: Migrate Lts/Tools (72 submodules) to Qt6
- Copied complete Lts/Tools directory from qt5-before to qt6-after - Updated 32 CMakeLists.txt files (Qt5:: → Qt6::) - Tools subdirectory already included in parent Lts/CMakeLists.txt - All 72 tool submodules now present in qt6-migrated-legatus - Includes: Adler, ArAssets, ConfigX, DBAdministration, DBUpdate, DdsView, InfrastructureManagement, and 67 more tools - C++17 standard (RTI 7.7.0 compatibility maintained) - Tests deferred until final validation phase Migration progress: ✅ Lts/Application (68 files) ✅ Lts/DDS (verified Qt6-ready) ✅ Lts/Plugins (60 submodules, verified) ✅ Lts/Projects (5 submodules, verified) ✅ Lts/Proxies (13 submodules, verified) ✅ Lts/Services (47 submodules, verified) ✅ Lts/Tools (72 submodules, copied + CMake updated) ✅ Lts/Viewer (LtsFileDialog, LtsReportDisplay, SituationMap) 🟡 TetraIF (partial) 🟡 Z (partial: nglwidgets, QtSingleApplication, qsql_informix, WMS_Interface)
This commit is contained in:
1 parent
01dce5fe52
commit
d5c13033e2
1792 files changed
+982018
No files matched your search
+13
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.leaflet-draw.leaflet-control {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { useState } from "react"
|
||||
import './App.css';
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useGetExerciseIds } from "./websocket/getExerciseIds";
|
||||
import { ErrorPopup } from "./components/errorPopup";
|
||||
import MapBox from './components/map'
|
||||
import SideBar from "./components/sideBar";
|
||||
import Popup from "./components/popup";
|
||||
import { useGetPartyData } from "./websocket/getPartyData";
|
||||
|
||||
function App() {
|
||||
const [createPopupOpen, setCreatePopupOpen] = useState(false)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
useGetExerciseIds(dispatch)
|
||||
useGetPartyData(42, dispatch)
|
||||
return (
|
||||
<div>
|
||||
<ErrorPopup />
|
||||
<Popup createPopupState={createPopupOpen} setPopupState={setCreatePopupOpen} />
|
||||
<SideBar createPopupStateChanger={setCreatePopupOpen} />
|
||||
<MapBox />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
qt6-after/qt6-migrated-legatus/Lts/Tools/TNE-Simulator/frontend/src/assets/Rheinmetall_Logo_2021.svg
Executable
+63
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Rheinmetall_Group_4c_positiv"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="4656.32px"
|
||||
height="1268.483px" viewBox="0 0 4656.32 1268.483" enable-background="new 0 0 4656.32 1268.483" xml:space="preserve">
|
||||
<path id="RHEINMETALL_1_" fill="#3E3D40" d="M955.22,1044.451h-48.742c-9.427,0-15.275-5.268-15.275-17.631V696.106
|
||||
c0-44.62,16.423-66.955,65.189-66.955H1001c57.589,0,96.966,5.306,119.287,14.117c44.621,18.201,59.921,59.328,59.921,109.823
|
||||
c0,50.53-17.064,92.805-66.399,113.364l79.911,144.521c3.505,6.453,8.193,14.091,8.193,21.15c0,5.887-4.083,9.981-8.773,10.575
|
||||
c-5.294,1.184-46.446,2.925-58.773,2.925c-10.563,0-16.424-5.282-20.558-13.5l-76.949-150.963
|
||||
c-9.972,0.593-20.583,0.593-32.294,0.593h-32.317v145.064C972.249,1038.604,965.19,1044.451,955.22,1044.451z M1006.911,817.121
|
||||
c25.246,0,45.804-2.331,59.34-7.057c18.793-7.035,30.54-26.409,30.54-52.852c0-31.133-9.415-48.149-29.38-55.207
|
||||
c-11.747-4.135-29.96-5.306-52.875-5.306h-22.915c-14.104,0-19.373,6.464-19.373,19.396v101.025H1006.911z M1328.826,1044.451
|
||||
h-49.309c-9.416,0-15.277-5.268-15.277-17.631v-380.64c0-10.564,4.69-17.622,15.844-17.622h50.531
|
||||
c11.143,0,14.671,6.479,14.671,17.054v148.025h160.958V646.181c0-10.564,4.69-17.622,16.485-17.622h49.89
|
||||
c11.167,0,14.671,6.479,14.671,17.054v381.207c0,11.192-6.43,17.631-16.424,17.631h-48.766c-9.391,0-15.857-5.268-15.857-17.631
|
||||
V864.701h-160.958v162.119C1345.287,1038.013,1338.821,1044.451,1328.826,1044.451z M1746.508,1043.885
|
||||
c-41.128,0-62.857-16.448-62.857-62.856V693.775c0-44.066,19.978-64.624,62.857-64.624h176.197c11.191,0,16.473,3.541,16.473,15.869
|
||||
v41.116c0,9.97-5.281,13.513-16.473,13.513h-139.191c-12.339,0-18.806,6.464-18.806,21.74v72.25h143.337
|
||||
c10.574,0,16.423,3.516,16.423,15.832v41.152c0,9.983-5.849,14.079-16.423,14.079h-143.337v85.761
|
||||
c0,15.855,5.887,22.916,22.323,22.916h145.706c10.551,0,16.437,2.924,16.437,15.843v40.55c0,10.007-5.318,14.114-16.437,14.114
|
||||
H1746.508z M2091.141,1044.451h-49.321c-9.416,0-15.265-5.268-15.265-17.631v-380.64c0-10.564,4.689-17.622,15.857-17.622h50.505
|
||||
c11.168,0,14.696,6.479,14.696,17.054v381.207C2107.614,1038.013,2101.161,1044.451,2091.141,1044.451z M2271.101,1044.451h-47
|
||||
c-9.392,0-15.264-5.268-15.264-17.631V682.039c0-37.019,14.659-55.231,54.023-55.231h22.321c33.478,0,41.153,14.67,52.876,39.35
|
||||
c94.557,193.856,111.599,233.231,129.812,277.272c-1.764-53.442-1.764-104.567-1.764-293.141c0-12.918,5.885-21.73,17.645-21.73
|
||||
h44.065c11.748,0,15.832,8.231,15.832,19.979v353.64c0,38.759-21.717,45.817-45.792,45.817h-18.227
|
||||
c-34.082,0-44.064-11.76-60.513-45.226c-105.145-215.004-114.522-237.34-132.75-280.787c0.593,40.497,1.173,80.454,1.173,304.838
|
||||
C2287.538,1038.013,2281.084,1044.451,2271.101,1044.451z M2708.651,1044.451h-49.915c-9.415,0-14.091-6.438-14.091-17.631V686.136
|
||||
c0-30.553,7.602-59.329,57.564-59.329h17.634c41.103,0,44.04,10.563,63.993,61.081c40.535,103.95,52.307,135.142,64.029,170.939
|
||||
c12.906-36.98,22.335-65.782,62.871-172.111c16.435-43.448,22.31-59.909,66.966-59.909h19.953c47.619,0,50.543,23.481,50.543,59.329
|
||||
v338.93c0,14.117-5.861,19.385-16.473,19.385h-49.371c-9.366,0-15.239-5.268-15.239-18.792c0-211.462,0.592-258.453,2.356-316.597
|
||||
c-14.709,48.149-26.431,82.775-81.083,226.739c-9.391,24.642-15.831,34.626-36.39,34.626h-9.427
|
||||
c-19.386,0-28.172-9.391-36.414-32.292c-52.271-140.981-65.758-177.407-83.392-229.073c1.765,58.144,1.765,108.651,1.765,318.374
|
||||
C2724.532,1036.827,2720.422,1044.451,2708.651,1044.451z M3207.404,1043.885c-41.114,0-62.833-16.448-62.833-62.856V693.775
|
||||
c0-44.066,19.966-64.624,62.833-64.624h176.223c11.155,0,16.437,3.541,16.437,15.869v41.116c0,9.97-5.281,13.513-16.437,13.513
|
||||
h-139.228c-12.34,0-18.793,6.464-18.793,21.74v72.25h143.336c10.563,0,16.461,3.516,16.461,15.832v41.152
|
||||
c0,9.983-5.898,14.079-16.461,14.079h-143.336v85.761c0,15.855,5.849,22.916,22.321,22.916h145.681
|
||||
c10.551,0,16.475,2.924,16.475,15.843v40.55c0,10.007-5.331,14.114-16.475,14.114H3207.404z M3619.782,1044.451h-49.891
|
||||
c-9.438,0-15.867-5.847-15.867-17.631V699.649h-76.049c-11.131,0-16.462-3.542-16.462-13.513v-41.116
|
||||
c0-12.329,5.331-15.869,16.462-15.869h253.356c11.143,0,16.485,3.998,12.45,15.869l-14.451,41.116
|
||||
c-3.084,9.847-8.34,13.513-19.532,13.513h-73.556v327.171C3636.243,1038.604,3629.208,1044.451,3619.782,1044.451z
|
||||
M3799.841,1025.659c-4.678,14.118-8.194,19.967-19.978,19.967c-11.712,0-50.482-1.175-58.133-2.925
|
||||
c-6.465-1.765-8.206-7.06-8.206-11.748c0-5.294,1.741-12.351,4.097-18.793l115.141-334.847
|
||||
c11.735-34.044,20.534-50.505,62.859-50.505h18.792c39.364,0,45.236,15.289,56.418,48.754l112.783,335.414
|
||||
c2.345,6.453,4.107,12.92,4.107,19.384c0,4.69-2.344,9.984-7.688,12.342c-7.032,2.925-41.09,2.925-58.13,2.925
|
||||
c-11.725,0-15.869-5.849-20.545-19.967l-29.95-88.673h-142.757L3799.841,1025.659z M3953.196,868.257
|
||||
c-32.329-103.395-42.338-136.291-51.714-170.965c-9.416,34.082-19.952,68.138-52.876,170.965H3953.196z M4199.952,1043.885
|
||||
c-39.918,0-61.624-17.657-61.624-59.932V646.181c0-10.564,4.665-17.622,15.807-17.622h50.507c11.191,0,14.709,6.479,14.709,17.054
|
||||
v306.021c0,14.093,5.873,21.744,21.717,21.744h117.781c10.526,0,16.4,2.924,16.4,15.843v40.55c0,10.007-5.874,14.114-16.4,14.114
|
||||
H4199.952z M4481.024,1043.885c-39.969,0-61.688-17.657-61.688-59.932V646.181c0-10.564,4.678-17.622,15.845-17.622h50.555
|
||||
c11.144,0,14.661,6.479,14.661,17.054v306.021c0,14.093,5.886,21.744,21.691,21.744h117.783c10.587,0,16.448,2.924,16.448,15.843
|
||||
v40.55c0,10.007-5.861,14.114-16.448,14.114H4481.024z"/>
|
||||
<g id="Bildmarke_1_">
|
||||
<path fill="#007EC1" d="M880.132,1.214c0,0-798.58,355.603-798.604,355.627C16.053,385.876,0,429.485,0,460.211v375.901v420.176
|
||||
c0,8.18,8.279,14.894,17.004,11.115l414.327-184.39c24.802-12.105,57.551-40.313,57.551-96.531V544.477L835.007,390.22
|
||||
c35.896-16.966,61.266-64.08,61.266-126.321c0-0.123,0.013-210.587,0.013-251.395C896.286,2.474,889.228-2.463,880.132,1.214z"/>
|
||||
<path fill="#00406E" d="M17.004,1267.403l414.327-184.39c24.802-12.105,57.551-40.313,57.551-96.531V544.477
|
||||
c0,0-407.477,183.749-407.453,183.749h0.012C13.807,758.236,0.679,803.053,0,836.112v420.176
|
||||
C0,1264.468,8.279,1271.182,17.004,1267.403z"/>
|
||||
<path fill="#000824" d="M0,836.112c0.679-33.059,13.807-77.876,81.441-107.887h-0.012c-0.024,0,407.453-183.749,407.453-183.749
|
||||
v72.347L81.441,768.269C20.804,791.009,0.679,835.113,0,868.17V836.112z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
Executable
+16
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 24 24" xml:space="preserve">
|
||||
<g id="group">
|
||||
<path d="M24,15.9c0-2.8-1.5-5-3.7-6.1C21.3,8.8,22,7.5,22,6c0-2.8-2.2-5-5-5c-2.1,0-3.8,1.2-4.6,3c0,0,0,0,0,0c-0.1,0-0.3,0-0.4,0
|
||||
c-0.1,0-0.3,0-0.4,0c0,0,0,0,0,0C10.8,2.2,9.1,1,7,1C4.2,1,2,3.2,2,6c0,1.5,0.7,2.8,1.7,3.8C1.5,10.9,0,13.2,0,15.9V20h5v3h14v-3h5
|
||||
V15.9z M17,3c1.7,0,3,1.3,3,3c0,1.6-1.3,3-3,3c0-1.9-1.1-3.5-2.7-4.4c0,0,0,0,0,0C14.8,3.6,15.8,3,17,3z M13.4,4.2
|
||||
C13.4,4.2,13.4,4.2,13.4,4.2C13.4,4.2,13.4,4.2,13.4,4.2z M15,9c0,1.7-1.3,3-3,3s-3-1.3-3-3s1.3-3,3-3S15,7.3,15,9z M10.6,4.2
|
||||
C10.6,4.2,10.6,4.2,10.6,4.2C10.6,4.2,10.6,4.2,10.6,4.2z M7,3c1.2,0,2.2,0.6,2.7,1.6C8.1,5.5,7,7.1,7,9C5.3,9,4,7.7,4,6S5.3,3,7,3
|
||||
z M5.1,18H2v-2.1C2,13.1,4.1,11,7,11v0c0,0,0,0,0,0c0.1,0,0.2,0,0.3,0c0,0,0,0,0,0c0.3,0.7,0.8,1.3,1.3,1.8
|
||||
C6.7,13.8,5.4,15.7,5.1,18z M17,21H7v-2.1c0-2.8,2.2-4.9,5-4.9c2.9,0,5,2.1,5,4.9V21z M22,18h-3.1c-0.3-2.3-1.7-4.2-3.7-5.2
|
||||
c0.6-0.5,1-1.1,1.3-1.8c0.1,0,0.2,0,0.4,0v0c2.9,0,5,2.1,5,4.9V18z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
Executable
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 12H18M12 6V18" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
Executable
+41
@@ -0,0 +1,41 @@
|
||||
import { useEffect, useCallback } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import '../styles/errorpopup.css'
|
||||
|
||||
import { exerciseError } from '../store/exerciseSlice'
|
||||
import { participantError } from '../store/participantSlice';
|
||||
import { partyDataError } from '../store/partySlice';
|
||||
|
||||
export function ErrorPopup() {
|
||||
const message = useSelector((state) => state.participants.errorMessage || state.exercise.errorMessage || state.party.errorMessage)
|
||||
const isError = useSelector((state) => state.participants.isError || state.exercise.isError || state.party.isError)
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const resetStates = useCallback(() => {
|
||||
dispatch(exerciseError(''));
|
||||
dispatch(participantError(''));
|
||||
dispatch(partyDataError(''));
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
let timer;
|
||||
if (isError) {
|
||||
timer = setTimeout(() => {
|
||||
resetStates();
|
||||
}, 5000); // Setzt den Timer auf 5 Sekunden
|
||||
}
|
||||
return () => clearTimeout(timer); // Bereinigt den Timer, wenn die Komponente unmountet wird oder isError sich ändert
|
||||
}, [isError, dispatch, resetStates]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isError &&
|
||||
<div className="popup-error">
|
||||
<button className="popup-error-button" onClick={() => resetStates()}>X</button>
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import React, { useState } from "react";
|
||||
import { MapContainer, TileLayer, FeatureGroup, Marker, Tooltip, Polyline } from 'react-leaflet';
|
||||
import { EditControl } from "react-leaflet-draw";
|
||||
import { connect, useDispatch } from "react-redux";
|
||||
import L from 'leaflet';
|
||||
|
||||
import MapEvents from '../helper/mapEvent.js';
|
||||
|
||||
|
||||
import '../styles/map.css';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import 'leaflet-draw/dist/leaflet.draw.css';
|
||||
|
||||
import icon from 'leaflet/dist/images/marker-icon.png';
|
||||
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
|
||||
import { setWalkingPath } from "../store/participantSlice.js";
|
||||
import { completedDrawing } from "./participantList.js";
|
||||
import { addLayers, deleteLayers } from "../store/layerSlice.js";
|
||||
|
||||
let DefaultIcon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow,
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12.5, 41]
|
||||
});
|
||||
|
||||
L.Marker.prototype.options.icon = DefaultIcon;
|
||||
|
||||
function MapBox({ mapPosition, participants, currentParticipant, layers }) {
|
||||
const [position, setPosition] = useState({ lat: 0, lng: 0 });
|
||||
const [clickPosition, setClickPosition] = useState({ lat: 0, lng: 0 });
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<p className="map-position-center">Center : {position.lat.toFixed(3)} | {position.lng.toFixed(3)}</p>
|
||||
<p className="map-position-click">Clicked : {clickPosition.lat.toFixed(3)} | {clickPosition.lng.toFixed(3)}</p>
|
||||
|
||||
<MapContainer
|
||||
key={mapPosition.toString()}
|
||||
center={mapPosition}
|
||||
zoom={14.5}
|
||||
scrollWheelZoom={true}
|
||||
attributionControl={false}
|
||||
>
|
||||
<TileLayer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
<FeatureGroup>
|
||||
<EditControl key={currentParticipant}
|
||||
onCreated={(e) => {
|
||||
if (e.layerType === 'polyline') {
|
||||
localStorage.setItem(currentParticipant, true)
|
||||
dispatch(deleteLayers(currentParticipant))
|
||||
dispatch(setWalkingPath({ equipmentNumber: currentParticipant, path: JSON.parse(JSON.stringify(e.layer.getLatLngs())) }))
|
||||
completedDrawing()
|
||||
|
||||
var newObject = {
|
||||
layers: [],
|
||||
participant: currentParticipant
|
||||
}
|
||||
newObject.layers.push(JSON.parse(JSON.stringify(e.layer.getLatLngs())))
|
||||
|
||||
dispatch(addLayers(newObject))
|
||||
e.layer.remove()
|
||||
}
|
||||
}}
|
||||
position='topright'
|
||||
edit={{
|
||||
edit: false,
|
||||
remove: false,
|
||||
}}
|
||||
draw={{
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false,
|
||||
polygon: false
|
||||
}}
|
||||
/>
|
||||
</FeatureGroup>
|
||||
{/* Display the participants on the map */}
|
||||
{participants.map((participant, i) => (
|
||||
<Marker key={i} position={[participant.currentPosition[0], participant.currentPosition[1]]}>
|
||||
<Tooltip>{participant.equipmentNumber}</Tooltip>
|
||||
</Marker>
|
||||
))}
|
||||
{layers.map((layer, i) => (
|
||||
<Polyline key={i} positions={layer.layers} />
|
||||
))}
|
||||
<MapEvents setPosition={setPosition} mapPosition={mapPosition} setClickPosition={setClickPosition} />
|
||||
</MapContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
mapPosition: state.map,
|
||||
participants: state.participants.data,
|
||||
currentParticipant: state.participants.currentParticipantWalkPathEq,
|
||||
layers: state.layer
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(MapBox);
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import "../styles/participantList.css"
|
||||
import logo from "../assets/group-svgrepo-com.svg"
|
||||
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { changeMapPosition } from '../store/mapSlice';
|
||||
import { useSetAbcProtection, select, setCurrentWalkParticipant } from '../store/participantSlice';
|
||||
import { useCalculation } from '../helper/walkingPathCaclulation';
|
||||
import { sleep } from '../helper/sleep';
|
||||
import { changeHoldingPathValue } from './sideBar';
|
||||
|
||||
let isFinishedDrawing = false;
|
||||
export function completedDrawing() {
|
||||
isFinishedDrawing = true;
|
||||
}
|
||||
export function startDrawing() {
|
||||
isFinishedDrawing = false;
|
||||
}
|
||||
export default function ParticipantList(participant) {
|
||||
const [buttonState, setButtonState] = useState(false);
|
||||
const calculate = useCalculation()
|
||||
const dispatch = useDispatch()
|
||||
// Handels value of checkbox input
|
||||
const useHandleChange = (e) => {
|
||||
let checkStatus = participant.participant.abcProtection
|
||||
dispatch(useSetAbcProtection({ equipmentNumber: participant.participant.equipmentNumber, newAbcProtection: !checkStatus }))
|
||||
};
|
||||
|
||||
// Opens Collapsible
|
||||
function buttonPressed() {
|
||||
setButtonState(!buttonState);
|
||||
}
|
||||
|
||||
async function drawingPath() {
|
||||
dispatch(setCurrentWalkParticipant({ currentParticipantWalkPathEq: participant.participant.equipmentNumber }))
|
||||
let drawButton = document.getElementsByClassName("leaflet-draw-draw-polyline")[0];
|
||||
|
||||
await sleep(10)
|
||||
|
||||
drawButton.click()
|
||||
}
|
||||
|
||||
function patrouille() {
|
||||
localStorage.setItem(participant.participant.equipmentNumber.toString(), false)
|
||||
calculate(participant.participant.path, participant.participant.equipmentNumber, participant.participant, "Patrouille")
|
||||
startDrawing()
|
||||
}
|
||||
|
||||
function oneTimeUsePath() {
|
||||
localStorage.setItem(participant.participant.equipmentNumber.toString(), false)
|
||||
calculate(participant.participant.path, participant.participant.equipmentNumber, participant.participant, "OTUP")
|
||||
startDrawing()
|
||||
}
|
||||
|
||||
function hold() {
|
||||
localStorage.setItem(participant.participant.equipmentNumber.toString(), false)
|
||||
calculate(participant.participant.path, participant.participant.equipmentNumber, participant.participant, "Hold")
|
||||
startDrawing()
|
||||
changeHoldingPathValue(true)
|
||||
}
|
||||
return (
|
||||
<div className='collapsible-div'>
|
||||
|
||||
<button className={buttonState ? 'collapsible-active' : 'collapsible'} onClick={buttonPressed}>
|
||||
<img src={logo} alt="Group" className='group-icon' />
|
||||
<div className='participant-text'>
|
||||
<p style={{ color: participant.participant.party }} > {participant.participant.equipmentNumber} | {participant.participant.type}</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{buttonState &&
|
||||
<div className="content">
|
||||
{/* Table of Participants Information */}
|
||||
<table className='participants-table'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>Group:</h4>
|
||||
</td>
|
||||
<td>{participant.participant.party}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>Typ:</h4>
|
||||
</td>
|
||||
<td>{participant.participant.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>Equip. No.:</h4>
|
||||
</td>
|
||||
<td>{participant.participant.equipmentNumber}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>Position:</h4>
|
||||
</td>
|
||||
<td>{participant.participant.currentPosition[0].toFixed(3)} | {participant.participant.currentPosition[1].toFixed(3)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>ABC Protection:</h4>
|
||||
</td>
|
||||
<td><input type="checkbox" value={participant.participant.abcProtection} onChange={useHandleChange}></input></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>Status:</h4>
|
||||
</td>
|
||||
<td>{participant.participant.status}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/* Buttons for the Participants */}
|
||||
<div className='collapsible-button-div'>
|
||||
|
||||
<button onClick={() => dispatch(changeMapPosition(participant.participant.currentPosition))} className="collapsible-button">GO TO</button>
|
||||
<button className='collapsible-button' onClick={() => dispatch(select({ participant: participant.participant }))}>SET POS</button>
|
||||
<button className='collapsible-button' onClick={() => drawingPath()}>EMULATE RUN</button>
|
||||
{isFinishedDrawing && (
|
||||
<div className='choose-popup'>
|
||||
<button className='collapsible-button' onClick={() => patrouille()}>Patrouille</button>
|
||||
<button className='collapsible-button' onClick={() => oneTimeUsePath()}>One time use path</button>
|
||||
<button className='collapsible-button' onClick={() => hold()}>Hold Path</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { useCheckIfParticipantIsValid } from '../websocket/checkIfParticipantIsValid'
|
||||
import "../styles/popup.css"
|
||||
import { useGetPartyData } from '../websocket/getPartyData';
|
||||
import { clearPartyData } from '../store/partySlice';
|
||||
import { setCurrentExerciseId } from '../store/exerciseSlice';
|
||||
|
||||
export default function Popup({ createPopupState, setPopupState }) {
|
||||
const [equipmentNumber, setEquipmentNumber] = useState('');
|
||||
const [selectedExerciseId, setSelectedExerciseId] = useState('');
|
||||
const [isExerciseIdSelected, setIsExerciseIdSelected] = useState(false);
|
||||
const [partyId, setPartyId] = useState(2);
|
||||
const partyList = useSelector((state) => state.party.partyData);
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const excerciseIds = useSelector((state) => state.exercise.exerciseIds);
|
||||
// Add participant to the store
|
||||
function useHandleClick() {
|
||||
useCheckIfParticipantIsValid(equipmentNumber, selectedExerciseId, dispatch, partyList, partyId);
|
||||
dispatch(clearPartyData([]))
|
||||
setIsExerciseIdSelected(false)
|
||||
}
|
||||
function useHandleExerciseIdSubmit() {
|
||||
useGetPartyData(selectedExerciseId, dispatch)
|
||||
setIsExerciseIdSelected(!isExerciseIdSelected)
|
||||
setPartyId(2)
|
||||
dispatch(setCurrentExerciseId(selectedExerciseId))
|
||||
setPopupState(false)
|
||||
}
|
||||
function handlePartyNameChange(event) {
|
||||
setPartyId(event.target.value)
|
||||
}
|
||||
// Handle input change
|
||||
const handleInputChange = (event) => {
|
||||
setEquipmentNumber(event.target.value);
|
||||
};
|
||||
// Handle exerciseId change
|
||||
const handleExerciseIdChange = (event) => {
|
||||
setSelectedExerciseId(event.target.value);
|
||||
};
|
||||
// Close popup when ESC key is pressed
|
||||
const handleKeyDown = useCallback((event) => {
|
||||
if (event.keyCode === 27) {
|
||||
setPopupState(false);
|
||||
}
|
||||
}, [setPopupState]);
|
||||
// Add event listener when popup is open
|
||||
useEffect(() => {
|
||||
if (createPopupState) {
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}
|
||||
}, [createPopupState, handleKeyDown]);
|
||||
return (
|
||||
<>
|
||||
{createPopupState && (
|
||||
<div className='popup-blur'>
|
||||
<div className="popup">
|
||||
<p className="popup-text">SELECT EXERCISE ID</p>
|
||||
</div>
|
||||
<div className='select-party'>
|
||||
<form className='select-party-form'>
|
||||
<select value={selectedExerciseId} onChange={handleExerciseIdChange} className='select-party-form-select'>
|
||||
{excerciseIds.map((exerciseId) => (
|
||||
<option key={exerciseId} value={exerciseId}>{exerciseId}</option>
|
||||
))}
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<div className="select-party-text">ID :</div>
|
||||
<div className="select-party-dropdown"></div>
|
||||
</div>
|
||||
<div className="popup-buttons">
|
||||
<button className="popup-cancel" onClick={() => setPopupState(false)}>Cancel</button>
|
||||
<button onClick={useHandleExerciseIdSubmit} className="popup-submit">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isExerciseIdSelected && (
|
||||
<div className='popup-blur'>
|
||||
|
||||
<div className="popup">
|
||||
<p className="popup-text">ADD A PARTICIPANT</p>
|
||||
</div>
|
||||
|
||||
<div className="select-party">
|
||||
|
||||
<form className='select-party-form'>
|
||||
<select id="group" name="group" className='select-party-form-select' onChange={handlePartyNameChange}>
|
||||
{partyList.map((item, index) => (
|
||||
<option key={index} value={item.id} style={{ color: item.color }}>{item.party}</option>
|
||||
))}
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<div className="select-party-text">Group :</div>
|
||||
<div className="select-party-dropdown"></div>
|
||||
</div>
|
||||
|
||||
<div className='select-number'>
|
||||
|
||||
<input className='select-number-inputfield' type="number" value={equipmentNumber} onChange={handleInputChange}></input>
|
||||
<div className="select-number-text">Number :</div>
|
||||
<div className="select-number-inputfield-div"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="popup-buttons">
|
||||
|
||||
<button className="popup-cancel" onClick={() => setIsExerciseIdSelected(false)}>Cancel</button>
|
||||
<button className="popup-submit" onClick={useHandleClick}>Submit</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
import React, { useState } from 'react';
|
||||
import "../styles/sideBar.css";
|
||||
import logo from "../assets/Rheinmetall_Logo_2021.svg";
|
||||
import ParticipantList from './participantList';
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getAllParticipants } from "../websocket/getAllParticipants";
|
||||
import { setWalkingPath } from '../store/participantSlice';
|
||||
import { generateRandomPath } from '../helper/generateRandomPath';
|
||||
import { useCalculation } from '../helper/walkingPathCaclulation';
|
||||
import { addLayers } from '../store/layerSlice';
|
||||
import { sleep } from '../helper/sleep';
|
||||
import { getStateFromLocalStorage, saveStateToLocalStorage } from '../helper/localStorageUsage';
|
||||
let isHoldingPaths = false;
|
||||
export function changeHoldingPathValue(value) {
|
||||
isHoldingPaths = value;
|
||||
}
|
||||
export default function SideBar({ createPopupStateChanger }) {
|
||||
const [hasParticipants, setHasParticipants] = useState(false)
|
||||
const [startedStressTest, setStartedStressTest] = useState(false)
|
||||
const participantList = useSelector((state) => state.participants.data);
|
||||
const partyList = useSelector(state => state.party.partyData)
|
||||
const excerciseId = useSelector(state => state.exercise.currentExerciseId) ? 0 : 42
|
||||
const calculate = useCalculation()
|
||||
const dispatch = useDispatch();
|
||||
|
||||
function useStartStressTest() {
|
||||
const lowerRight = [53.019, 8.995]
|
||||
const topLeft = [53.128, 8.730]
|
||||
|
||||
for (var participant of participantList) {
|
||||
var newObject = {
|
||||
layers: [],
|
||||
participant: participant
|
||||
}
|
||||
|
||||
var path = generateRandomPath(lowerRight, topLeft, participant.currentPosition);
|
||||
newObject.layers.push(path)
|
||||
|
||||
localStorage.setItem(participant.equipmentNumber.toString(), false)
|
||||
|
||||
dispatch(setWalkingPath({ equipmentNumber: participant.equipmentNumber, path: path }))
|
||||
dispatch(addLayers(newObject))
|
||||
calculate(path, participant.equipmentNumber, participant, "OTUP")
|
||||
}
|
||||
|
||||
|
||||
setHasParticipants(false)
|
||||
setStartedStressTest(true)
|
||||
}
|
||||
|
||||
async function useGetAllParticipants() {
|
||||
getAllParticipants(excerciseId || 42, dispatch, partyList)
|
||||
|
||||
await sleep(500)
|
||||
|
||||
setHasParticipants(true)
|
||||
}
|
||||
|
||||
function startAllPaths() {
|
||||
var pathObjectArray = [];
|
||||
for (var singleParticipant of participantList) {
|
||||
var pathObject = {
|
||||
equipmentNumber: singleParticipant.equipmentNumber,
|
||||
path: singleParticipant.path
|
||||
}
|
||||
|
||||
if (singleParticipant.path && singleParticipant.path.length > 0) {
|
||||
localStorage.setItem(singleParticipant.equipmentNumber.toString(), false)
|
||||
calculate(singleParticipant.path, singleParticipant.equipmentNumber, singleParticipant, "OTUP")
|
||||
pathObjectArray.push(pathObject)
|
||||
}
|
||||
}
|
||||
saveStateToLocalStorage("paths", pathObjectArray)
|
||||
getStateFromLocalStorage("paths")
|
||||
changeHoldingPathValue(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
|
||||
<div className="sidebar-header">
|
||||
<img src={logo} alt="Rheinmetall Logo" className='logo' />
|
||||
</div>
|
||||
|
||||
<div className="participant-list">
|
||||
<p className="participant-list-text">Participant list</p>
|
||||
<div className="addButton-div" onClick={() => createPopupStateChanger(true)}>
|
||||
<div className="addButton"><p className="addButton-text">+</p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-content">
|
||||
{
|
||||
participantList.map((participant, i) => {
|
||||
return <ParticipantList key={i} participant={participant} participantList={participantList} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className='sidebar-footer'>
|
||||
<button onClick={useGetAllParticipants}>Get all Participants</button>
|
||||
{hasParticipants &&
|
||||
<button onClick={useStartStressTest}>Start Stress Test</button>
|
||||
}
|
||||
{startedStressTest &&
|
||||
<div className='sidebar-startStop-container'>
|
||||
<button>Start</button>
|
||||
<button>Pause</button>
|
||||
<button>Stop</button>
|
||||
</div>
|
||||
}
|
||||
{isHoldingPaths > 0 && (
|
||||
<div className='startingpath-popup'>
|
||||
<button className='collapsible-button' onClick={() => startAllPaths()}>Start all Paths</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
export const participantData = [
|
||||
{
|
||||
equipmentNumber: 1054,
|
||||
currentPosition: [53.0580229, 8.8972232],
|
||||
abcProtection: true,
|
||||
gear: [
|
||||
{
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}, {
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}
|
||||
],
|
||||
party: "Blue",
|
||||
symbol: "--",
|
||||
type: "Soldat/G",
|
||||
status: "Wounded",
|
||||
selected: false,
|
||||
walkingPath: []
|
||||
},
|
||||
{
|
||||
equipmentNumber: 1024,
|
||||
currentPosition: [53.0510229, 8.8902232],
|
||||
abcProtection: true,
|
||||
gear: [
|
||||
{
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}, {
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}
|
||||
],
|
||||
party: "Blue",
|
||||
symbol: "--",
|
||||
type: "Soldat/G",
|
||||
status: "Wounded",
|
||||
selected: false,
|
||||
walkingPath: []
|
||||
},
|
||||
{
|
||||
equipmentNumber: 1014,
|
||||
currentPosition: [53.0520229, 8.8932232],
|
||||
abcProtection: true,
|
||||
gear: [
|
||||
{
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}, {
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}
|
||||
],
|
||||
party: "Red",
|
||||
symbol: "--",
|
||||
type: "Soldat/G",
|
||||
status: "Wounded",
|
||||
selected: false,
|
||||
walkingPath: [],
|
||||
walkMode: ""
|
||||
},
|
||||
{
|
||||
equipmentNumber: 1088,
|
||||
currentPosition: [53.0550229, 8.8942232],
|
||||
abcProtection: true,
|
||||
gear: [
|
||||
{
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}, {
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}
|
||||
],
|
||||
party: "Pink",
|
||||
symbol: "--",
|
||||
type: "Soldat/G",
|
||||
status: "Wounded",
|
||||
selected: false,
|
||||
walkingPath: [],
|
||||
walkMode: ""
|
||||
|
||||
},
|
||||
{
|
||||
equipmentNumber: 1034,
|
||||
currentPosition: [53.0540229, 8.8972232],
|
||||
abcProtection: true,
|
||||
gear: [
|
||||
{
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}, {
|
||||
weapon: "AK-74",
|
||||
munition: "7.62x39mm AP"
|
||||
}
|
||||
],
|
||||
party: "Orange",
|
||||
symbol: "--",
|
||||
type: "Soldat/G",
|
||||
status: "Wounded",
|
||||
selected: false,
|
||||
walkingPath: [],
|
||||
walkMode: ""
|
||||
|
||||
},
|
||||
]
|
||||
|
||||
export const partyData = [
|
||||
{
|
||||
id:2,
|
||||
party:"RED",
|
||||
color:"#eb0016"
|
||||
},
|
||||
{
|
||||
id:3,
|
||||
party:"BLUE",
|
||||
color:"#1721a6"
|
||||
},
|
||||
{
|
||||
id:4,
|
||||
party:"GREEN",
|
||||
color:"#00ff00"
|
||||
}
|
||||
]
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
export function generateRandomPath(lowerRight, topLeft,currentPosition) {
|
||||
const path = [
|
||||
{
|
||||
lat: currentPosition[0],
|
||||
lng: currentPosition[1],
|
||||
},
|
||||
{
|
||||
lat: Math.random() * (topLeft[0] - lowerRight[0]) + lowerRight[0],
|
||||
lng: Math.random() * (topLeft[1] - lowerRight[1]) + lowerRight[1],
|
||||
},
|
||||
|
||||
{
|
||||
lat: Math.random() * (topLeft[0] - lowerRight[0]) + lowerRight[0],
|
||||
lng: Math.random() * (topLeft[1] - lowerRight[1]) + lowerRight[1],
|
||||
},
|
||||
]
|
||||
return path;
|
||||
}
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
export function saveStateToLocalStorage(item, pathObject) {
|
||||
localStorage.setItem(
|
||||
item,
|
||||
JSON.stringify(pathObject)
|
||||
);
|
||||
}
|
||||
|
||||
export function getStateFromLocalStorage(item) {
|
||||
let data = localStorage.getItem(item);
|
||||
if (data !== undefined) {
|
||||
console.log(data);
|
||||
}
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useMap } from 'react-leaflet';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { useSetPosition as SetParticipantPosition, deselect } from '../store/participantSlice';
|
||||
|
||||
|
||||
|
||||
export default function MapEvents({ setPosition, mapPosition, setClickPosition }) {
|
||||
const dispatch = useDispatch()
|
||||
const map = useMap();
|
||||
const currentParticipant = useSelector((state) => state.participants.currentParticipant)
|
||||
|
||||
// Set the positions
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
const onMove = () => {
|
||||
setPosition(map.getCenter());
|
||||
};
|
||||
const onClick = (e) => {
|
||||
if (currentParticipant === "") {
|
||||
return
|
||||
}
|
||||
|
||||
setClickPosition(e.latlng);
|
||||
|
||||
dispatch(SetParticipantPosition({ equipmentNumber: currentParticipant.equipmentNumber, newPosition: [e.latlng.lat, e.latlng.lng], exerciseId: currentParticipant.exerciseId }))
|
||||
dispatch(deselect({ currentParticipant: "" }))
|
||||
|
||||
};
|
||||
|
||||
map.on('move', onMove);
|
||||
map.on('click', onClick);
|
||||
|
||||
return () => {
|
||||
map.off('move', onMove);
|
||||
map.off('click', onClick);
|
||||
};
|
||||
}
|
||||
}, [dispatch, map, mapPosition, setClickPosition, setPosition, currentParticipant]);
|
||||
|
||||
return null;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
qt6-after/qt6-migrated-legatus/Lts/Tools/TNE-Simulator/frontend/src/helper/walkingPathCaclulation.js
Executable
+63
@@ -0,0 +1,63 @@
|
||||
import { useSetPosition as setPosition, walkingState } from "../store/participantSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sleep } from "./sleep";
|
||||
|
||||
export function useCalculation() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return async function (latLngs, equipmentNumber, participant, walkMode) {
|
||||
var speed = 0.00003;
|
||||
var currentPosition = participant.currentPosition;
|
||||
|
||||
if (latLngs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (localStorage.getItem(equipmentNumber.toString()) === "false") {
|
||||
if (walkMode === "Patrouille") {
|
||||
for (var i = 0; i < latLngs.length; i++) {
|
||||
currentPosition = await moveToTarget(latLngs[i], currentPosition, speed, dispatch, equipmentNumber);
|
||||
}
|
||||
// Rückwärts durch den Pfad
|
||||
for (i = latLngs.length - 1; i >= 0; i--) {
|
||||
currentPosition = await moveToTarget(latLngs[i], currentPosition, speed, dispatch, equipmentNumber);
|
||||
}
|
||||
} else if (walkMode === "OTUP") {
|
||||
for (i = 0; i < latLngs.length; i++) {
|
||||
currentPosition = await moveToTarget(latLngs[i], currentPosition, speed, dispatch, equipmentNumber);
|
||||
}
|
||||
break;
|
||||
} else if (walkMode === "Hold") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dispatch(walkingState({ equipmentNumber: equipmentNumber, isWalking: false, walkingMode: " " }))
|
||||
}
|
||||
}
|
||||
|
||||
async function moveToTarget(target, currentPosition, speed, dispatch, equipmentNumber) {
|
||||
var targetLat = target.lat;
|
||||
var targetLng = target.lng;
|
||||
|
||||
while (localStorage.getItem(equipmentNumber.toString()) === "false") {
|
||||
var diffLat = targetLat - currentPosition[0];
|
||||
var diffLng = targetLng - currentPosition[1];
|
||||
|
||||
var distance = Math.sqrt(diffLat * diffLat + diffLng * diffLng);
|
||||
|
||||
if (distance < speed) {
|
||||
currentPosition = [targetLat, targetLng];
|
||||
break;
|
||||
}
|
||||
// Berechne den Anteil der Bewegung in Richtung des Ziels
|
||||
var ratio = speed / distance;
|
||||
var moveLat = diffLat * ratio;
|
||||
var moveLng = diffLng * ratio;
|
||||
// Aktualisiere die aktuelle Position
|
||||
currentPosition = [currentPosition[0] + moveLat, currentPosition[1] + moveLng];
|
||||
dispatch(setPosition({ equipmentNumber: equipmentNumber, newPosition: currentPosition, exerciseId: 42 }));
|
||||
// Warte eine Sekunde bevor die Position erneut aktualisiert wird
|
||||
await sleep(300);
|
||||
}
|
||||
return currentPosition;
|
||||
}
|
||||
Whitespace-only changes.
+42
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
import participantReducer from './store/participantSlice'
|
||||
import mapReducer from './store/mapSlice'
|
||||
import partyReducer from './store/partySlice'
|
||||
import exerciseReducer from './store/exerciseSlice'
|
||||
import layerReducer from './store/layerSlice'
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
}
|
||||
]);
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
participants: participantReducer,
|
||||
map: mapReducer,
|
||||
party: partyReducer,
|
||||
exercise: exerciseReducer,
|
||||
layer: layerReducer,
|
||||
}
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<RouterProvider router={router} />
|
||||
</Provider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
reportWebVitals();
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
const excerciseId = createSlice({
|
||||
name: "exercise",
|
||||
initialState: {exerciseIds : [], currentExerciseId: 0, isError : false, errorMessage : "" },
|
||||
reducers: {
|
||||
setExcerciseId: (state, action) => {
|
||||
for(var excerciseId in action.payload) {
|
||||
state.exerciseIds[excerciseId] = action.payload[excerciseId]
|
||||
}
|
||||
},
|
||||
setCurrentExerciseId: (state, action) => {
|
||||
state.currentExerciseId = action.payload
|
||||
},
|
||||
exerciseError: (state, action) => {
|
||||
state.isError = action.payload.isError;
|
||||
state.errorMessage = action.payload.errorMessage;
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
export const { setExcerciseId, setCurrentExerciseId, exerciseError } = excerciseId.actions
|
||||
|
||||
export default excerciseId.reducer
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const layerSlice = createSlice({
|
||||
name: "layer",
|
||||
initialState: [],
|
||||
reducers: {
|
||||
addLayers: (state, action) => {
|
||||
state.push(action.payload)
|
||||
},
|
||||
deleteLayers: (state, action) => {
|
||||
for(var layerIndex in state) {
|
||||
if (state[layerIndex].participant === action.payload) {
|
||||
state.splice(layerIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const { addLayers, deleteLayers } = layerSlice.actions
|
||||
|
||||
export default layerSlice.reducer
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const mapSlice = createSlice({
|
||||
name: "map",
|
||||
initialState: [53.0580229, 8.8972232],
|
||||
reducers: {
|
||||
changeMapPosition: (state, action) => {
|
||||
state[0] = action.payload[0]
|
||||
state[1] = action.payload[1]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const { changeMapPosition } = mapSlice.actions
|
||||
|
||||
export default mapSlice.reducer
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { original } from 'immer'
|
||||
import { useSendPositionChange } from "../websocket/sendPositionChange";
|
||||
import { useChangeAbcProtection } from "../websocket/changeAbcProtection"
|
||||
import { useDispatch as dispatch } from "react-redux";
|
||||
|
||||
const participantSlice = createSlice({
|
||||
name: "participants",
|
||||
initialState: { data: [], currentParticipant: "", currentParticipantWalkPathEq: 0, isError: false, errorMessage: "" },
|
||||
reducers: {
|
||||
useSetPosition: (state, action) => {
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.equipmentNumber) {
|
||||
state.data[i].currentPosition = action.payload.newPosition
|
||||
}
|
||||
}
|
||||
useSendPositionChange(action.payload.equipmentNumber, action.payload.newPosition, action.payload.exerciseId ,dispatch)
|
||||
},
|
||||
useSetAbcProtection: (state, action) => {
|
||||
var exerciseId = 0;
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.equipmentNumber) {
|
||||
state.data[i].abcProtection = action.payload.newAbcProtection
|
||||
exerciseId = state.data[i].exerciseId;
|
||||
}
|
||||
}
|
||||
useChangeAbcProtection(action.payload.equipmentNumber, action.payload.newAbcProtection, exerciseId, dispatch)
|
||||
},
|
||||
select: (state, action) => {
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.participant.equipmentNumber) {
|
||||
state.currentParticipant = action.payload.participant;
|
||||
}
|
||||
}
|
||||
},
|
||||
deselect: (state, action) => {
|
||||
state.currentParticipant = "";
|
||||
},
|
||||
addParticipant: (state, action) => {
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.equipmentNumber) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
state.data.push(action.payload)
|
||||
},
|
||||
setCurrentWalkParticipant: (state, action) => {
|
||||
state.currentParticipantWalkPathEq = action.payload.currentParticipantWalkPathEq
|
||||
},
|
||||
setWalkingPath: (state, action) => {
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.equipmentNumber) {
|
||||
state.data[i].path = action.payload.path
|
||||
}
|
||||
}
|
||||
},
|
||||
walkingState: (state, action) => {
|
||||
for (var i = 0; i < original(state.data).length; i++) {
|
||||
if (original(state.data)[i].equipmentNumber === action.payload.equipmentNumber) {
|
||||
state.data[i].isWalking = action.payload.isWalking
|
||||
state.data[i].walkingMode = action.payload.walkingMode
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
participantError: (state, action) => {
|
||||
state.isError = action.payload.isError;
|
||||
state.errorMessage = action.payload.errorMessage;
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const {
|
||||
useSetPosition,
|
||||
useSetAbcProtection,
|
||||
select,
|
||||
deselect,
|
||||
addParticipant,
|
||||
setCurrentWalkParticipant,
|
||||
setWalkingPath,
|
||||
walkingState,
|
||||
participantError,
|
||||
} = participantSlice.actions
|
||||
|
||||
export default participantSlice.reducer
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const partySlice = createSlice({
|
||||
name: "party",
|
||||
initialState: { partyData: [], isError: false, errorMessage: "" },
|
||||
reducers: {
|
||||
setPartyData: (state, action) => {
|
||||
var found = false;
|
||||
|
||||
for(var partyObject of state.partyData) {
|
||||
if (partyObject["party"] === action.payload["party"]) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
state.partyData.push(action.payload)
|
||||
}
|
||||
},
|
||||
clearPartyData: (state, action) => {
|
||||
state = action.payload
|
||||
},
|
||||
partyDataError: (state, action) => {
|
||||
state.isError = action.payload.isError;
|
||||
state.errorMessage = action.payload.errorMessage;
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
export const { setPartyData, clearPartyData, partyDataError } = partySlice.actions
|
||||
|
||||
export default partySlice.reducer
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
.popup-error {
|
||||
position: absolute;
|
||||
width: 40%;
|
||||
height: 20px;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
background-color: white;
|
||||
color: red;
|
||||
z-index: 3;
|
||||
border-radius: 25px;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.popup-error-button {
|
||||
position: absolute;
|
||||
left: 95%;
|
||||
border-radius: 25px;
|
||||
border: 1px solid #007EC1;
|
||||
background-color: #007EC1;
|
||||
color: #ffffff;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.popup-error-button:hover {
|
||||
border: 1px solid #0073b1;
|
||||
background-color: #0073b1;
|
||||
color: #ffffff;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.popup-error-button:disabled,
|
||||
.popup-error-button[disabled]{
|
||||
border: 1px solid #007EC1;
|
||||
background-color: #007EC1;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
.leaflet-container {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
height: 100vh;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-control,
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
||||
.map-position-center {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 15px;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.map-position-click {
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 15px;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1100px) {
|
||||
.leaflet-container {
|
||||
width: 60%;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
/* Media queries for responsive design */
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.leaflet-container {
|
||||
width: 60%;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.leaflet-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.map-position-center {
|
||||
font-size: 10px;
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.map-position-click {
|
||||
font-size: 10px;
|
||||
top: 53%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.leaflet-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.map-position-center {
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.map-position-click {
|
||||
top: 53%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 320px) {
|
||||
.leaflet-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.map-position-center {
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.map-position-click {
|
||||
top: 53%;
|
||||
right: 5px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
.collapsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #F4F4F4;
|
||||
color: #AAAAAA;
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
border-radius: 25px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
line-height: 19px;
|
||||
color: #AAAAAA;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.active,
|
||||
.collapsible:hover {
|
||||
background-color: #e4e4e4;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
background-color: #F4F4F4;
|
||||
padding: 0 18px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 0px 0px 25px 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.collapsible-active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #F4F4F4;
|
||||
color: #AAAAAA;
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
border-radius: 25px 25px 0px 0px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
line-height: 19px;
|
||||
color: #AAAAAA;
|
||||
flex-grow: 1;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.group-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.participant-text {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
color: #050505;
|
||||
}
|
||||
|
||||
.participant-text-selected {
|
||||
display: flex;
|
||||
align-self: flex-end;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: green;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.participant-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
td {
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.collapsible-button-div {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
border-radius: 25px;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.collapsible-button {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
border-radius: 25px;
|
||||
background: #007EC1;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: white;
|
||||
font-size: 8px;
|
||||
font-family: 'Cascadia';
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.collapsible-button:hover {
|
||||
background-color: #005a8c;
|
||||
}
|
||||
|
||||
.choose-popup {
|
||||
position: fixed;
|
||||
width: 10%;
|
||||
height: 8%;
|
||||
top: 10%;
|
||||
left: 50%;
|
||||
background-color: white;
|
||||
color: red;
|
||||
z-index: 3;
|
||||
border-radius: 25px;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.startingpath-popup {
|
||||
position: fixed;
|
||||
width: 10%;
|
||||
height: auto;
|
||||
top: 90%;
|
||||
left: 50%;
|
||||
background-color: white;
|
||||
color: red;
|
||||
z-index: 3;
|
||||
border-radius: 25px;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
.popup {
|
||||
position: fixed;
|
||||
width: 20%;
|
||||
height: 25%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background-color: white;
|
||||
z-index: 3;
|
||||
border-radius: 25px;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.popup-blur {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
|
||||
.popup-text {
|
||||
position: absolute;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.select-party {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
width: 12%;
|
||||
height: 3%;
|
||||
background: #D9D9D9;
|
||||
opacity: 0.5;
|
||||
border-radius: 25px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.select-party-dropdown {
|
||||
width: 35%;
|
||||
height: 100%;
|
||||
background: #D9D9D9;
|
||||
opacity: 0.4;
|
||||
box-shadow: 2px 0px 5px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 25px;
|
||||
z-index: 4;
|
||||
border: none;
|
||||
appearance: none;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.select-party-text {
|
||||
position: absolute;
|
||||
width: 42px;
|
||||
height: 15px;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #000000;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.select-number-inputfield {
|
||||
position: absolute;
|
||||
width: 55%;
|
||||
height: 80%;
|
||||
margin-top: 1%;
|
||||
left: 40%;
|
||||
background-color: #D9D9D9;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.select-number-inputfield:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.select-party-form-select {
|
||||
position: absolute;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
left: 40%;
|
||||
border: none;
|
||||
background-color: #D9D9D9;
|
||||
border-radius: 25px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.select-number {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
width: 12%;
|
||||
height: 3%;
|
||||
background: #D9D9D9;
|
||||
opacity: 0.5;
|
||||
border-radius: 25px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.select-number-inputfield-div {
|
||||
width: 35%;
|
||||
height: 100%;
|
||||
background: #D9D9D9;
|
||||
opacity: 0.5;
|
||||
box-shadow: 2px 0px 5px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
text-align: right;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.select-number-text {
|
||||
position: absolute;
|
||||
width: 53px;
|
||||
height: 15px;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #000000;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
position: absolute;
|
||||
width: 12%;
|
||||
height: 32px;
|
||||
left: 50%;
|
||||
top: 55%;
|
||||
transform: translate(-50%, 0%);
|
||||
background: white;
|
||||
border-radius: 25px;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.popup-buttons button {
|
||||
width: 45%;
|
||||
height: 25px;
|
||||
border-radius: 25px;
|
||||
background: #007EC1;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-family: 'Cascadia';
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popup-cancel {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.popup-submit {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.popup-buttons button:hover,
|
||||
.popup-buttons button:focus {
|
||||
background-color: #005a8c;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
/* Media queries for responsive design */
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.popup {
|
||||
width: 40%;
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
.select-party,
|
||||
.select-number {
|
||||
width: 20%;
|
||||
height: 4%;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
width: 20%;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.popup {
|
||||
width: 40%;
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
.select-party,
|
||||
.select-number {
|
||||
width: 25%;
|
||||
height: 5%;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
width: 25%;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.popup {
|
||||
width: 50%;
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
.select-party,
|
||||
.select-number {
|
||||
margin-top: 5px;
|
||||
width: 35%;
|
||||
height: 3%;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
width: 35%;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 320px) {
|
||||
.popup {
|
||||
width: 50%;
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
.select-party,
|
||||
.select-number {
|
||||
width: 40%;
|
||||
height: 3%;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
width: 40%;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.popup-text {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.select-party-text,
|
||||
.select-number-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.popup-buttons button {
|
||||
font-size: 10px;
|
||||
width: 30%;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 600px) {
|
||||
.popup {
|
||||
width: 50%;
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
.select-party,
|
||||
.select-number {
|
||||
width: 40%;
|
||||
height: 3%;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
width: 40%;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.popup-text {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.select-party-text,
|
||||
.select-number-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.popup-buttons button {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
height: 100vh;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.participant-list {
|
||||
width: 80%;
|
||||
height: 36px;
|
||||
background: #F4F4F4;
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.participant-list-text {
|
||||
font-weight: 600;
|
||||
font-size: 90%;
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
line-height: 19px;
|
||||
color: #777777;
|
||||
flex-grow: 1;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.addButton-div {
|
||||
width: 15%;
|
||||
height: 36px;
|
||||
background: #007EC1;
|
||||
box-shadow: -2px 1px 7.6px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.addButton {
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sidebar-content {
|
||||
width: 80%;
|
||||
height: 87%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-top: 20px;
|
||||
margin-left: 0.5cm;
|
||||
margin-right: 0.5cm;
|
||||
width: 150px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1100px) {
|
||||
.sidebar {
|
||||
width: 40%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.participant-list,
|
||||
.sidebar-content {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.sidebar {
|
||||
width: 40%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.participant-list,
|
||||
.sidebar-content {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.sidebar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.participant-list,
|
||||
.sidebar-content {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.addButton-div {
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.sidebar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.participant-list,
|
||||
.sidebar-content {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.addButton-div {
|
||||
width: 15%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 320px) {
|
||||
.sidebar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.participant-list,
|
||||
.sidebar-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.addButton-div {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.addButton {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
height: 3%;
|
||||
align-content: space-between;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sidebar-footer button {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
height: 100%;
|
||||
border-radius: 25px;
|
||||
background: #007EC1;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-family: 'Cascadia';
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sidebar-footer button:hover,
|
||||
.sidebar-footer button:focus {
|
||||
background-color: #005a8c;
|
||||
}
|
||||
|
||||
|
||||
.sidebar-startStop-container {
|
||||
position: fixed;
|
||||
width: 30%;
|
||||
height: 7.5%;
|
||||
background-color: #FFFFFF;
|
||||
z-index: 5;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-startStop-container button {
|
||||
width: 30%;
|
||||
height: 50%;
|
||||
}
|
||||
qt6-after/qt6-migrated-legatus/Lts/Tools/TNE-Simulator/frontend/src/websocket/changeAbcProtection.js
Executable
+28
@@ -0,0 +1,28 @@
|
||||
import { participantError } from "../store/participantSlice";
|
||||
/**
|
||||
* Verwendet einen WebSocket, um die ABC-Schutzinformationen eines Teilnehmers zu ändern.
|
||||
*
|
||||
* Diese Funktion stellt eine Verbindung zu einem WebSocket-Server her und sendet eine Anfrage,
|
||||
* um die ABC-Schutzinformationen eines bestimmten Teilnehmers zu ändern. Nach dem Senden der Anfrage
|
||||
* wird die Verbindung sofort geschlossen. Im Fehlerfall wird ein Fehlerzustand über Redux dispatcht.
|
||||
*
|
||||
* @param {string} equipmentNumber - Die Ausrüstungsnummer des Teilnehmers, dessen ABC-Schutzinformationen geändert werden sollen.
|
||||
* @param {boolean} abcProtection - Der neue Zustand des ABC-Schutzes für den Teilnehmer (true für aktiviert, false für deaktiviert).
|
||||
* @param {string} exerciseId - Die ID der Übung, zu der der Teilnehmer gehört.
|
||||
* @param {function} dispatch - Die Redux Dispatch-Funktion, um den Fehlerzustand zu aktualisieren, falls ein Fehler auftritt.
|
||||
*/
|
||||
export function useChangeAbcProtection(equipmentNumber, abcProtection, exerciseId, dispatch) {
|
||||
const socket = new WebSocket('ws://localhost:6969/participantHandler');
|
||||
socket.onopen = function open() {
|
||||
var req = JSON.stringify({
|
||||
type: 'changeAbcProtection',
|
||||
excerciseId: parseInt(exerciseId),
|
||||
participantData: { "participantId": parseInt(equipmentNumber), "abcProtection": abcProtection }
|
||||
})
|
||||
socket.send(req);
|
||||
socket.close()
|
||||
};
|
||||
socket.onerror = () => {
|
||||
dispatch(participantError({ isError: true, errorMessage: "ERROR: There was an error with the participant websocket!" }))
|
||||
};
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { addParticipant, participantError } from "../store/participantSlice";
|
||||
/**
|
||||
* Überprüft, ob ein Teilnehmer für eine bestimmte Übung gültig ist, indem eine WebSocket-Verbindung verwendet wird.
|
||||
*
|
||||
* Diese Funktion initiiert eine WebSocket-Verbindung zu einem Server und sendet eine Anfrage, um zu überprüfen,
|
||||
* ob ein Teilnehmer für eine bestimmte Übung gültig ist. Die Anfrage enthält die Übungs-ID und die Teilnehmer-ID.
|
||||
* Bei einem Fehler in der WebSocket-Verbindung wird ein Fehlerzustand über die `participantError`-Aktion im Redux-Store
|
||||
* dispatcht. Nach dem Öffnen der Verbindung wird die Anfrage gesendet. Bei Erhalt einer Nachricht vom Server (die Antwort
|
||||
* auf die Gültigkeitsprüfung) wird die Antwort verarbeitet. Die Informationen über die
|
||||
* Zugehörigkeit des Teilnehmers beinhaltet die PartyId, die dann weiter verwendet werden können.
|
||||
*
|
||||
* @param {string} equipmentNumber - Die ID des Teilnehmers, dessen Gültigkeit überprüft werden soll.
|
||||
* @param {string} exerciseId - Die ID der Übung, für die die Gültigkeit des Teilnehmers überprüft werden soll.
|
||||
* @param {function} dispatch - Die Redux Dispatch-Funktion, die verwendet wird, um Aktionen im Redux-Store zu dispatchen.
|
||||
* @param {Array<Object>} partyList - Eine Liste von Parteien, die in der Überprüfung verwendet wird, um Details zum Teilnehmer zu ermitteln.
|
||||
* @param {string} _partyId - Eine vorläufige Partei-ID, die in der Funktion verwendet wird (der genaue Verwendungszweck ist aus dem Auszug nicht ersichtlich).
|
||||
*/
|
||||
export function useCheckIfParticipantIsValid(equipmentNumber, exerciseId, dispatch, partyList, _partyId) {
|
||||
const socket = new WebSocket('ws://localhost:6969/participantHandler');
|
||||
socket.onerror = (error) => {
|
||||
dispatch(participantError({ isError: true, errorMessage: "ERROR: There was an error with the participant websocket!" }))
|
||||
};
|
||||
|
||||
socket.onopen = function open() {
|
||||
var req = JSON.stringify({
|
||||
type: 'checkIfParticipantIsValid',
|
||||
excerciseId: parseInt(exerciseId),
|
||||
participantData: { "participantId": parseInt(equipmentNumber) }
|
||||
})
|
||||
socket.send(req);
|
||||
};
|
||||
socket.onmessage = function incoming(event) {
|
||||
const participant = JSON.parse(event.data);
|
||||
var partyName = ""
|
||||
var partyId = ""
|
||||
for(var party of partyList) {
|
||||
if(party.id === participant.partyId) {
|
||||
partyName = party.party;
|
||||
partyId = party.id
|
||||
}
|
||||
}
|
||||
if (participant.valid && partyId === parseInt(_partyId)) {
|
||||
dispatch(addParticipant({ equipmentNumber: participant.equipmentNumber, currentPosition: [53.0580229, 8.8972232], abcProtection: false, type: participant.tacticalIdentifier, party: partyName, exerciseId: exerciseId, isWalking: false, walkingMode: "", path: []}));
|
||||
} else {
|
||||
dispatch(participantError({isError: true, errorMessage: "Participant is not valid! Group or Equipment Number could be wrong!"}))
|
||||
}
|
||||
socket.close();
|
||||
};
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
|
||||
import { addParticipant, participantError } from "../store/participantSlice";
|
||||
|
||||
let req = ""
|
||||
export function getAllParticipants(exerciseId, dispatch, partyList) {
|
||||
var socket = new WebSocket('ws://localhost:6969/participantHandler');
|
||||
|
||||
socket.onerror = (error) => {
|
||||
dispatch(participantError({ isError: true, errorMessage: "ERROR: There was an error with the participant websocket!" }))
|
||||
};
|
||||
|
||||
socket.onopen = function open() {
|
||||
req = JSON.stringify(
|
||||
{type: "getAllParticipants", excerciseId: 42}
|
||||
)
|
||||
socket.send(req);
|
||||
};
|
||||
socket.onmessage = function incoming(event) {
|
||||
const participants = JSON.parse(event.data);
|
||||
|
||||
var blueCount = 0;
|
||||
var redCount = 0;
|
||||
|
||||
socket.onmessage = () => {}
|
||||
|
||||
for(var participant of participants) {
|
||||
var partyName = ""
|
||||
for (var party of partyList) {
|
||||
if (party.id === participant.partyId) {
|
||||
partyName = party.party;
|
||||
}
|
||||
}
|
||||
if (participant.valid) {
|
||||
if(participant.partyId === 2 ) {
|
||||
dispatch(addParticipant({ equipmentNumber: participant.equipmentNumber, currentPosition: [53.044 + 0.0005 * blueCount , 8.814], abcProtection: false, type: participant.tacticalIdentifier, party: partyName, exerciseId: exerciseId, isWalking: false, walkingMode: "", path: [] }));
|
||||
req = JSON.stringify({
|
||||
type: 'changePosition',
|
||||
excerciseId: parseInt(exerciseId),
|
||||
participantData: { "participantId": parseInt(participant.equipmentNumber), "positions": [{"lat": 53.044 + 0.0005 * blueCount, "lng": 8.814}] }
|
||||
})
|
||||
socket.send(req);
|
||||
blueCount++;
|
||||
} else if(participant.partyId === 3) {
|
||||
dispatch(addParticipant({ equipmentNumber: participant.equipmentNumber, currentPosition: [53.044 + 0.0005 * redCount , 8.959], abcProtection: false, type: participant.tacticalIdentifier, party: partyName, exerciseId: exerciseId, isWalking: false, walkingMode: "", path: [] }));
|
||||
req = JSON.stringify({
|
||||
type: 'changePosition',
|
||||
excerciseId: parseInt(exerciseId),
|
||||
participantData: { "participantId": parseInt(participant.equipmentNumber), "positions": [{"lat": 53.044 + 0.0005 * redCount, "lng": 8.959}] }
|
||||
})
|
||||
socket.send(req);
|
||||
redCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
import { setExcerciseId, exerciseError } from "../store/exerciseSlice";
|
||||
|
||||
/**
|
||||
* Verwendet einen WebSocket, um eine Liste von Übungs-IDs vom Server zu erhalten.
|
||||
*
|
||||
* Diese Funktion stellt eine Verbindung zu einem WebSocket-Server her und wartet auf eine Nachricht,
|
||||
* die eine Liste von Übungs-IDs enthält. Sobald die Nachricht empfangen wird, wird die Liste der IDs
|
||||
* in einem Redux-Store gespeichert, indem die `setExcerciseId`-Aktion dispatched wird. Nach dem Empfang
|
||||
* der Daten wird die WebSocket-Verbindung geschlossen. Im Falle eines Fehlers beim Empfangen der Daten
|
||||
* wird ein Fehlerzustand über die `exerciseError`-Aktion im Redux-Store gesetzt.
|
||||
*
|
||||
* @param {function} dispatch - Die Redux Dispatch-Funktion, die verwendet wird, um Aktionen im Redux-Store zu dispatchen.
|
||||
*/
|
||||
export function useGetExerciseIds(dispatch) {
|
||||
|
||||
const socket = new WebSocket('ws://localhost:6969/exerciseIds');
|
||||
const exerciseIds = [];
|
||||
|
||||
socket.onmessage = function incoming(event) {
|
||||
for (var exerciseId of JSON.parse(event.data)) {
|
||||
exerciseIds.push(exerciseId);
|
||||
}
|
||||
dispatch(setExcerciseId(exerciseIds))
|
||||
socket.close();
|
||||
};
|
||||
socket.onerror = () => {
|
||||
dispatch(exerciseError({ isError: true, errorMessage: "ERROR: There was an error with the exerciseIds websocket!" }))
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
import { partyDataError, setPartyData } from '../store/partySlice.js';
|
||||
/**
|
||||
* Stellt eine WebSocket-Verbindung her, um Parteidaten basierend auf einer Übungs-ID zu erhalten.
|
||||
*
|
||||
* Diese Funktion initiiert eine WebSocket-Verbindung zu einem vorgegebenen Server und sendet eine Anfrage
|
||||
* mit der Übungs-ID, um spezifische Partydaten zu erhalten. Sobald die Verbindung geöffnet ist, wird die
|
||||
* Übungs-ID an den Server gesendet. Bei Empfang der Daten (über die `onmessage`-Ereignisbehandlung) werden
|
||||
* die empfangenen Parteidaten durch Dispatchen der `setPartyData`-Aktion im Redux-Store gespeichert.
|
||||
* Im Falle eines Verbindungsfehlers wird ein Fehlerzustand durch Dispatchen der `partyDataError`-Aktion
|
||||
* im Redux-Store gesetzt. Nach erfolgreichem Empfang der Daten wird die WebSocket-Verbindung geschlossen.
|
||||
*
|
||||
* @param {string} exerciseId - Die ID der Übung, für die Parteidaten abgerufen werden sollen.
|
||||
* @param {function} dispatch - Die Redux Dispatch-Funktion, die verwendet wird, um Aktionen im Redux-Store zu dispatchen.
|
||||
*/
|
||||
export function useGetPartyData(exerciseId, dispatch) {
|
||||
const socket = new WebSocket('ws://localhost:6969/partyData');
|
||||
socket.onopen = function open() {
|
||||
socket.send(JSON.stringify({
|
||||
excerciseId: parseInt(exerciseId),
|
||||
}));
|
||||
};
|
||||
socket.onmessage = function incoming(event) {
|
||||
for (var party of JSON.parse(event.data)) {
|
||||
dispatch(setPartyData(party))
|
||||
}
|
||||
socket.close();
|
||||
};
|
||||
socket.onerror = (error) => {
|
||||
dispatch(partyDataError({ isError: true, errorMessage: "ERROR: There was an error with the partyData websocket!" }))
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Verwendet einen WebSocket, um eine Positionsänderung eines Teilnehmers zu senden.
|
||||
*
|
||||
* Diese Funktion stellt eine Verbindung zu einem WebSocket-Server her und sendet eine Anfrage,
|
||||
* um die Position eines Teilnehmers zu aktualisieren. Die Anfrage enthält die Übungs-ID, die Ausrüstungsnummer
|
||||
* des Teilnehmers und die neue geographische Position. Nach dem Senden der Anfrage wird die Verbindung
|
||||
* sofort geschlossen. Im Fehlerfall wird ein Fehlerzustand über Redux dispatcht, um die Anwendung
|
||||
* über das Problem zu informieren.
|
||||
*
|
||||
* @param {string} equipmentNumber - Die Ausrüstungsnummer des Teilnehmers, dessen Position geändert werden soll.
|
||||
* @param {Array<number>} geoPosition - Ein Array mit zwei Elementen, wobei das erste Element die Breitengrad- und das zweite Element die Längengradkoordinate ist.
|
||||
* @param {string} exerciseId - Die ID der Übung, zu der der Teilnehmer gehört.
|
||||
* @param {function} dispatch - Die Redux Dispatch-Funktion, um den Fehlerzustand zu aktualisieren, falls ein Fehler auftritt.
|
||||
*/
|
||||
export function useSendPositionChange(equipmentNumber, geoPosition, exerciseId) {
|
||||
const socket = new WebSocket('ws://localhost:6969/participantHandler');
|
||||
socket.onopen = function open() {
|
||||
var req = JSON.stringify({
|
||||
type: 'changePosition',
|
||||
excerciseId: parseInt(exerciseId),
|
||||
participantData: { "participantId": parseInt(equipmentNumber), "positions": [{"lat" : geoPosition[0], "lng": geoPosition[1]}] }
|
||||
})
|
||||
socket.send(req);
|
||||
socket.close()
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user