diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0a1a6f2..448d180 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,8 @@
+
+
+
-
-
-
+
\ No newline at end of file
diff --git a/src/main/java/vassistent/bootstrap/ApplicationContext.java b/src/main/java/vassistent/bootstrap/ApplicationContext.java
index 30ae471..3adaabb 100644
--- a/src/main/java/vassistent/bootstrap/ApplicationContext.java
+++ b/src/main/java/vassistent/bootstrap/ApplicationContext.java
@@ -10,7 +10,7 @@ public class ApplicationContext {
private DataPersistenceService persistenceService;
private StatisticsService statisticsService;
private EvaluationService evaluationService;
- private UnrealWebSocketService unrealService;
+ private AnimationFileService unrealService;
private MqttClientService mqttService;
private BinaryEventService binaryEventService;
private ProcessManagerService processManagerService;
@@ -31,7 +31,7 @@ public class ApplicationContext {
this.evaluationService = evaluationService;
}
- public void setUnrealService(UnrealWebSocketService unrealService) {
+ public void setAnimationFileService(AnimationFileService unrealService) {
this.unrealService = unrealService;
}
@@ -59,7 +59,7 @@ public class ApplicationContext {
return evaluationService;
}
- public UnrealWebSocketService getUnrealService() {
+ public AnimationFileService getAnimationFileService() {
return unrealService;
}
diff --git a/src/main/java/vassistent/bootstrap/ApplicationInitializer.java b/src/main/java/vassistent/bootstrap/ApplicationInitializer.java
index 4e05120..ac51d81 100644
--- a/src/main/java/vassistent/bootstrap/ApplicationInitializer.java
+++ b/src/main/java/vassistent/bootstrap/ApplicationInitializer.java
@@ -1,14 +1,11 @@
package vassistent.bootstrap;
import com.formdev.flatlaf.FlatDarkLaf;
-import vassistent.controller.DashboardController;
import vassistent.model.AppState;
import vassistent.service.*;
import vassistent.util.ConfigLoader;
-import vassistent.util.Logger;
import javax.swing.*;
-import java.io.InputStream;
import java.util.Properties;
public class ApplicationInitializer {
@@ -26,8 +23,8 @@ public class ApplicationInitializer {
context.setAppState(new AppState());
// ===== Infrastructure Services =====
- context.setUnrealService(
- new UnrealWebSocketService("ws://localhost:8888/avatar")
+ context.setAnimationFileService(
+ new AnimationFileService()
);
context.setMqttService(new MqttClientService(context.getAppState()));
@@ -42,7 +39,7 @@ public class ApplicationInitializer {
new EvaluationService(
context.getStatisticsService(),
context.getAppState(),
- context.getUnrealService()
+ context.getAnimationFileService()
)
);
diff --git a/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java b/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java
index dd4aa08..b396546 100644
--- a/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java
+++ b/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java
@@ -1,25 +1,58 @@
package vassistent.bootstrap;
+import vassistent.service.MqttClientService;
+import vassistent.service.ProcessManagerService;
+import vassistent.util.Logger;
+
public class ApplicationShutdownManager {
private final ApplicationContext context;
public ApplicationShutdownManager(ApplicationContext context) {
this.context = context;
+
+ Runtime.getRuntime().addShutdownHook(
+ new Thread(this::shutdown)
+ );
}
public void shutdown() {
- if (context.getMqttService() != null) {
- context.getMqttService().disconnect();
- }
+ Logger.info("SHUTDOWN", "Shutdown Sequencing gestartet");
- if (context.getUnrealService() != null) {
- context.getUnrealService().disconnect();
- }
+ disconnectMqtt();
+ stopExternalProcesses();
+ }
- if (context.getProcessManagerService() != null) {
- context.getProcessManagerService().shutdown();
+ private void disconnectMqtt() {
+
+ try {
+ MqttClientService mqtt = context.getMqttService();
+
+ if (mqtt != null) {
+ mqtt.disconnect();
+ }
+
+ Logger.info("SHUTDOWN", "MQTT getrennt");
+ } catch (Exception e) {
+ Logger.error("SHUTDOWN", "MQTT Shutdown Fehler", e);
+ }
+ }
+
+ private void stopExternalProcesses() {
+
+ try {
+
+ ProcessManagerService pm = context.getProcessManagerService();
+
+ if (pm != null) {
+ pm.shutdown();
+ }
+
+ Logger.info("SHUTDOWN", "Externe Prozesse beendet");
+
+ } catch (Exception e) {
+ Logger.error("SHUWTDOWN", "Process Shutdown Fehler", e);
}
}
}
diff --git a/src/main/java/vassistent/controller/AppWindowController.java b/src/main/java/vassistent/controller/AppWindowController.java
index 553e56b..d49d2a2 100644
--- a/src/main/java/vassistent/controller/AppWindowController.java
+++ b/src/main/java/vassistent/controller/AppWindowController.java
@@ -5,8 +5,11 @@ import vassistent.bootstrap.ApplicationShutdownManager;
import vassistent.model.AppState;
import vassistent.ui.AppWindow;
import vassistent.ui.DashboardView;
+import vassistent.ui.PixelStreamingView;
import vassistent.util.Logger;
+import javax.swing.*;
+import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -28,6 +31,7 @@ public class AppWindowController {
window = new AppWindow();
DashboardView dashboardView = window.getDashboardView();
+ PixelStreamingView pixelStreamingView = window.getStreamingView();
DashboardController dashboardController =
new DashboardController(
@@ -36,8 +40,16 @@ public class AppWindowController {
context.getAppState()
);
- window.getRefreshDashboardButton()
- .addActionListener(e -> dashboardController.loadChartData());
+ StreamingController previewController = new StreamingController(pixelStreamingView);
+
+ dashboardView.getReloadPixelStreamingViewButton()
+ .addActionListener(e ->
+ previewController.reloadStream("http://141.75.215.233")
+ );
+
+ dashboardView.getOpenFullscreenButton()
+ .addActionListener(e ->
+ window.getStreamingView().requestFocus());
window.addWindowListener(new WindowAdapter() {
@Override
@@ -66,17 +78,21 @@ public class AppWindowController {
AppState state = context.getAppState();
- state.addListener(appState -> {
+ state.addListener(this::onStateChanged);
- /*Logger.debug("Controller",
- "AppState Update erhalten: " +
- appState.getProblemLevel());*/
+ Logger.info("Controller",
+ "AppState Observer registriert");
+ }
- if (window == null) {
- Logger.warn("Controller",
- "Window ist null, UI Update übersprungen");
- return;
- }
+ private void onStateChanged(AppState appState) {
+
+ if (window == null) {
+ Logger.warn("Controller",
+ "Window ist null, UI Update übersprungen");
+ return;
+ }
+
+ SwingUtilities.invokeLater(() -> {
window.updateProblemLevel(
appState.getProblemLevel().name()
@@ -87,11 +103,8 @@ public class AppWindowController {
);
Logger.debug("Controller",
- "ProblemLevel UI aktualisiert → " +
- appState.getProblemLevel());
+ "ProblemLevel UI aktualisiert → "
+ + appState.getProblemLevel());
});
-
- Logger.info("Controller",
- "AppState Observer registriert");
}
}
diff --git a/src/main/java/vassistent/controller/DashboardController.java b/src/main/java/vassistent/controller/DashboardController.java
index 4564509..a060bf6 100644
--- a/src/main/java/vassistent/controller/DashboardController.java
+++ b/src/main/java/vassistent/controller/DashboardController.java
@@ -5,6 +5,7 @@ import vassistent.model.RatioPoint;
import vassistent.service.StatisticsService;
import vassistent.ui.DashboardView;
+import javax.swing.*;
import java.util.List;
public class DashboardController {
@@ -21,14 +22,23 @@ public class DashboardController {
this.statisticsService = statisticsService;
this.dashboardView = dashboardView;
- appState.addListener(state -> {
+ appState.addListener(this::onStateChanged);
+ }
- if (state.getDataVersion() != lastDataVersion) {
- lastDataVersion = state.getDataVersion();
+ private void onStateChanged(AppState state) {
- List points = statisticsService.getLastNAverages(20);
- dashboardView.updateChart(points);
- }
+ List points =
+ statisticsService.getLastNAverages(20);
+
+ if (points.isEmpty())
+ return;
+
+ double ratio = points.get(points.size() - 1).getRatio();
+
+ SwingUtilities.invokeLater(() -> {
+
+ dashboardView.updateChart(points);
+ dashboardView.updateProblemLevel(ratio);
});
}
diff --git a/src/main/java/vassistent/model/AppState.java b/src/main/java/vassistent/model/AppState.java
index 1f71ee1..98fdde7 100644
--- a/src/main/java/vassistent/model/AppState.java
+++ b/src/main/java/vassistent/model/AppState.java
@@ -27,6 +27,7 @@ public class AppState {
public void setProblemLevel(ProblemLevel problemLevel) {
this.problemLevel = problemLevel;
+ dataVersion++;
notifyListeners();
}
@@ -40,13 +41,4 @@ public class AppState {
notifyListeners();
}
}
-
- public long getDataVersion() {
- return dataVersion;
- }
-
- public void incrementDataVersion() {
- dataVersion++;
- notifyListeners();
- }
}
diff --git a/src/main/java/vassistent/service/AnimationFileService.java b/src/main/java/vassistent/service/AnimationFileService.java
new file mode 100644
index 0000000..7cafd32
--- /dev/null
+++ b/src/main/java/vassistent/service/AnimationFileService.java
@@ -0,0 +1,50 @@
+package vassistent.service;
+
+import vassistent.model.ProblemLevel;
+import vassistent.util.Logger;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.atomic.AtomicBoolean;
+import jakarta.websocket.*;
+
+public class AnimationFileService {
+ private static final String PATH = "C:\\Privat\\Dokumente\\Niklas_Aumueller\\TH\\MSY\\Semester_2\\animation.json";
+
+ public void wirteAnimationState(ProblemLevel level) {
+
+ String animation = mapLevelToAnimation(level);
+
+ File file = new File(PATH);
+
+ try {
+
+ String json = """
+ {
+ "animation": "%s"
+ }
+ """.formatted(animation);
+
+ try (FileWriter writer = new FileWriter(file, false)) {
+ writer.write(json);
+ }
+
+ Logger.info("ANIMATION FILE", "Animation json geschrieben");
+
+ } catch (IOException e) {
+ Logger.error("ANIMATION FILE", "Fehler beim Schreiben der Animation Datei", e);
+ }
+ }
+
+ private String mapLevelToAnimation(ProblemLevel level) {
+
+ return switch (level) {
+ case NONE -> "no";
+ case WARNING -> "warning";
+ case HIGH -> "high";
+ case DISASTER -> "disaster";
+ };
+ }
+}
diff --git a/src/main/java/vassistent/service/EvaluationService.java b/src/main/java/vassistent/service/EvaluationService.java
index ab918b0..6622965 100644
--- a/src/main/java/vassistent/service/EvaluationService.java
+++ b/src/main/java/vassistent/service/EvaluationService.java
@@ -7,28 +7,26 @@ import vassistent.model.ProblemLevel;
public class EvaluationService {
private final StatisticsService statisticsService;
private final AppState appState;
- private final UnrealWebSocketService unrealService;
+ private final AnimationFileService animationFileService;
public EvaluationService(
StatisticsService statisticsService,
AppState appState,
- UnrealWebSocketService unrealService
+ AnimationFileService animationFileService
) {
this.statisticsService = statisticsService;
this.appState = appState;
- this.unrealService = unrealService;
+ this.animationFileService = animationFileService;
}
public void evaluate() {
- double ratio = statisticsService.getRatio(10);
+ double ratio = statisticsService.getRatio(20);
ProblemLevel level = calculateLevel(ratio);
appState.setProblemLevel(level);
- unrealService.speak(level.name());
-
- appState.incrementDataVersion();
+ animationFileService.wirteAnimationState(level);
}
private ProblemLevel calculateLevel(Double ratio) {
diff --git a/src/main/java/vassistent/service/MqttClientService.java b/src/main/java/vassistent/service/MqttClientService.java
index 5ebc64e..945a336 100644
--- a/src/main/java/vassistent/service/MqttClientService.java
+++ b/src/main/java/vassistent/service/MqttClientService.java
@@ -23,7 +23,6 @@ public class MqttClientService implements MqttCallback {
public MqttClientService(AppState appState) {
this.appState = appState;
-
try {
client = new MqttClient(
BROKER_URL,
@@ -107,6 +106,7 @@ public class MqttClientService implements MqttCallback {
Consumer listener = topicListeners.get(topic);
if (listener != null) {
listener.accept(payload);
+ Logger.debug("MQTT", "Payload accepted");
} else {
Logger.warn(
"MQTT",
diff --git a/src/main/java/vassistent/service/PixelStreamingService.java b/src/main/java/vassistent/service/PixelStreamingService.java
deleted file mode 100644
index 7cef34b..0000000
--- a/src/main/java/vassistent/service/PixelStreamingService.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package vassistent.service;
-
-public class PixelStreamingService {
-
-}
diff --git a/src/main/java/vassistent/service/ProcessManagerService.java b/src/main/java/vassistent/service/ProcessManagerService.java
index ae62140..c864257 100644
--- a/src/main/java/vassistent/service/ProcessManagerService.java
+++ b/src/main/java/vassistent/service/ProcessManagerService.java
@@ -12,6 +12,7 @@ public class ProcessManagerService {
private Process pythonProcess;
private Process unrealProcess;
+ private Process unrealSignallingProcess;
public ProcessManagerService(Properties config) {
this.config = config;
@@ -54,34 +55,92 @@ public class ProcessManagerService {
try {
- String exe = config.getProperty("unreal.executable");
-
- ProcessBuilder pb = new ProcessBuilder(
- exe,
- "-RenderOffScreen",
- "-NoSound",
- "-PixelStreamingSignallingURL=ws://127.0.0.1:8888"
- );
-
- pb.redirectErrorStream(true);
-
- unrealProcess = pb.start();
-
- Logger.info("PROCESS", "Unreal Engine Avatar");
+ startSignallingServer();
+ startUnrealEngine();
} catch (IOException e) {
- Logger.error("PROCESS", "Unreal Engine Start fehlgeschlagen", e);
+ Logger.error("PROCESS", "Unreal Start fehlgeschlagen", e);
}
}
+ private void startSignallingServer() throws IOException {
+
+ String script =
+ config.getProperty("unreal.signalling_server.script");
+
+ if (script == null) return;
+
+ ProcessBuilder pb = new ProcessBuilder(
+ "cmd",
+ "/c",
+ script);
+
+ pb.redirectErrorStream(true);
+
+ unrealSignallingProcess = pb.start();
+
+ Logger.info("PROCESS",
+ "Unreal Signalling Server gestartet" + pb.command());
+ }
+
+ private void startUnrealEngine() throws IOException {
+
+ String exe =
+ config.getProperty("unreal.executable");
+
+ ProcessBuilder pb = new ProcessBuilder(
+ exe,
+ "-PixelStreamingURL=ws://127.0.0.1:8888",
+ "-RenderOffscreen",
+ "-NoSound"
+ );
+
+ pb.directory(new File(exe).getParentFile());
+
+ pb.redirectErrorStream(true);
+
+ unrealProcess = pb.start();
+
+ Logger.info("PROCESS",
+ "Unreal Engine gestartet" + pb.command());
+ }
+
public void shutdown() {
- if (pythonProcess != null)
- pythonProcess.destroyForcibly();
+ Logger.info("PROCESS", "Shutdown externe Prozesse gestartet");
- if (unrealProcess != null)
- unrealProcess.destroyForcibly();
+ terminateProcess(pythonProcess);
+ terminateProcess(unrealProcess);
+ terminateProcess(unrealSignallingProcess);
Logger.info("PROCESS", "Externe Prozesse beendet");
}
+
+ private void terminateProcess(Process process) {
+
+ if (process == null)
+ return;
+
+ try {
+
+ long pid = process.pid();
+
+ ProcessBuilder pb = new ProcessBuilder(
+ "taskkill",
+ "/PID",
+ String.valueOf(pid),
+ "/T",
+ "/F"
+ );
+
+ pb.start().waitFor();
+
+ Logger.info("PROCESS",
+ "Process Tree beendet → PID " + pid);
+
+ } catch (Exception e) {
+ Logger.error("PROCESS",
+ "Fehler beim Prozess Kill", e);
+ }
+ }
}
diff --git a/src/main/java/vassistent/service/StateService.java b/src/main/java/vassistent/service/StateService.java
deleted file mode 100644
index 822bfac..0000000
--- a/src/main/java/vassistent/service/StateService.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package vassistent.service;
-
-import vassistent.model.AppState;
-
-public class StateService {
- private static final AppState STATE = new AppState();
-
- private StateService() {}
-
- public static AppState getState() {
- return STATE;
- }
-}
diff --git a/src/main/java/vassistent/service/UnrealWebSocketService.java b/src/main/java/vassistent/service/UnrealWebSocketService.java
deleted file mode 100644
index b04d359..0000000
--- a/src/main/java/vassistent/service/UnrealWebSocketService.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package vassistent.service;
-
-import vassistent.util.Logger;
-
-import java.net.URI;
-import java.util.concurrent.atomic.AtomicBoolean;
-import jakarta.websocket.*;
-
-public class UnrealWebSocketService {
-
- private Session session;
- private final URI serverUri;
- private final AtomicBoolean connected = new AtomicBoolean(false);
-
-
- public UnrealWebSocketService(String serverUrl) {
- this.serverUri = URI.create(serverUrl);
- //connect();
- }
-
- private void connect() {
- try {
- WebSocketContainer container =
- ContainerProvider.getWebSocketContainer();
-
- Logger.info("UNREAL", "Verbinde zu " + serverUri);
- container.connectToServer(this, serverUri);
-
- } catch (Exception e) {
- Logger.error("UNREAL", "WebSocket-Verbindung fehlgeschlagen", e);
- }
- }
-
- public boolean isConnected() {
- return connected.get();
- }
-
- public void disconnect() {
- try {
- if (session != null && session.isOpen()) {
- session.close();
- Logger.info("UNREAL", "WebSocket-Verbindung geschlossen");
- }
- } catch (Exception e) {
- Logger.error("UNREAL", "Fehler beim Schließen der Verbindung", e);
- }
- }
-
- @OnOpen
- public void onOpen(Session session) {
- this.session = session;
- connected.set(true);
- Logger.info("UNREAL", "WebSocket verbunden");
- }
-
- @OnClose
- public void onClose(Session session, CloseReason reason) {
- connected.set(false);
- Logger.warn("UNREAL", "WebSocket geschlossen: " + reason.getReasonPhrase());
- }
-
- @OnError
- public void onError(Session session, Throwable throwable) {
- connected.set(false);
- Logger.error("UNREAL", "WebSocket-Fehler", throwable);
- }
-
- @OnMessage
- public void onMessage(String message) {
- Logger.debug("UNREAL", "Nachricht empfangen: " + message);
- }
-
- public void speak(String text) {
- if (!connected.get()) {
- //Logger.warn("UNREAL", "Nicht verbunden! Text wird nicht gesendet!");
- return;
- }
-
- String json = buildSpeakMessage(text);
-
- session.getAsyncRemote().sendText(json);
- Logger.info("UNREAL", "Sende Speak-Command: " + text);
- }
-
- public String buildSpeakMessage(String text) {
- return "{"
- + "\"type\":\"speak\","
- + "\"text\":\"" + escape(text) + "\""
- + "}";
- }
-
- public String escape(String text) {
- return text.replace("\"", "\\\"");
- }
-}
diff --git a/src/main/java/vassistent/ui/AppWindow.java b/src/main/java/vassistent/ui/AppWindow.java
index 4b03658..4ae7b38 100644
--- a/src/main/java/vassistent/ui/AppWindow.java
+++ b/src/main/java/vassistent/ui/AppWindow.java
@@ -10,15 +10,11 @@ public class AppWindow extends JFrame {
private PixelStreamingView streamingView;
private DashboardView dashboardView;
+ private JTabbedPane tabs;
private JLabel mqttStatusLabel;
- private JLabel websocketStatusLabel;
private JLabel problemLevelLabel;
- private JButton refreshDashboardButton;
- private JButton reloadStreamButton;
- private JButton fullscreenButton;
-
public AppWindow() {
setTitle("Virtueller Gesundheitsassistent");
@@ -28,41 +24,27 @@ public class AppWindow extends JFrame {
setLayout(new BorderLayout());
- add(createToolbar(), BorderLayout.NORTH);
- add(createTabPane(), BorderLayout.CENTER);
+ streamingView = new PixelStreamingView(
+ "http://localhost:80",
+ false,
+ false
+ );
+
+ dashboardView = new DashboardView();
+
+ tabs = createTabPane();
+ add(tabs, BorderLayout.CENTER);
add(createStatusBar(), BorderLayout.SOUTH);
}
- // ---------------- Toolbar ----------------
-
- private JPanel createToolbar() {
-
- JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT));
-
- refreshDashboardButton = new JButton("Dashboard Refresh");
- reloadStreamButton = new JButton("Stream Reload");
- fullscreenButton = new JButton("Fullscreen Stream");
-
- toolbar.add(refreshDashboardButton);
- toolbar.add(reloadStreamButton);
- toolbar.add(fullscreenButton);
-
- return toolbar;
- }
-
// ---------------- Tabs ----------------
private JTabbedPane createTabPane() {
JTabbedPane tabs = new JTabbedPane();
- streamingView = new PixelStreamingView(
- "http://141.75.215.233",
- false,
- false
- );
-
- dashboardView = new DashboardView();
+ JPanel streamingPanel = new JPanel(new BorderLayout());
+ streamingPanel.add(streamingView, BorderLayout.CENTER);
tabs.addTab("Avatar Streaming", streamingView);
tabs.addTab("Dashboard", dashboardView);
@@ -77,13 +59,10 @@ public class AppWindow extends JFrame {
JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT));
mqttStatusLabel = new JLabel("MQTT: Disconnected");
- websocketStatusLabel = new JLabel();
problemLevelLabel = new JLabel("Problem: NONE");
statusBar.add(mqttStatusLabel);
statusBar.add(new JLabel(" | "));
- statusBar.add(websocketStatusLabel);
- statusBar.add(new JLabel(" | "));
statusBar.add(problemLevelLabel);
return statusBar;
@@ -94,11 +73,6 @@ public class AppWindow extends JFrame {
(connected ? "Connected" : "Disconnected"));
}
- public void updateWebsocketStatus(boolean connected) {
- websocketStatusLabel.setText("WebSocket: " +
- (connected ? "Connected" : "Disconnected"));
- }
-
public void updateProblemLevel(String level) {
problemLevelLabel.setText("Problem: " + level);
}
@@ -111,15 +85,7 @@ public class AppWindow extends JFrame {
return streamingView;
}
- public JButton getRefreshDashboardButton() {
- return refreshDashboardButton;
- }
-
- public JButton getReloadStreamButton() {
- return reloadStreamButton;
- }
-
- public JButton getFullscreenButton() {
- return fullscreenButton;
+ public JTabbedPane getTabs() {
+ return tabs;
}
}
diff --git a/src/main/java/vassistent/ui/DashboardView.java b/src/main/java/vassistent/ui/DashboardView.java
index 404ae2b..dc5f32b 100644
--- a/src/main/java/vassistent/ui/DashboardView.java
+++ b/src/main/java/vassistent/ui/DashboardView.java
@@ -7,6 +7,7 @@ import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
+import vassistent.model.ProblemLevel;
import vassistent.model.RatioPoint;
import javax.swing.*;
@@ -20,14 +21,23 @@ import java.util.List;
public class DashboardView extends JPanel {
private TimeSeries series;
+ private ProblemLevelBar levelBar;
+
+ private JButton reloadPixelStreamingViewButton;
+ private JButton openFullscreenButton;
public DashboardView() {
setLayout(new BorderLayout());
+ // ---------- TOP: Problem Level ----------
+ levelBar = new ProblemLevelBar();
+ add(levelBar, BorderLayout.NORTH);
+
+ // ---------- CENTER: Chart ----------
+
series = new TimeSeries("Ratio");
- TimeSeriesCollection dataset =
- new TimeSeriesCollection(series);
+ TimeSeriesCollection dataset = new TimeSeriesCollection(series);
JFreeChart chart = ChartFactory.createTimeSeriesChart(
"Ratio Verlauf",
@@ -36,9 +46,27 @@ public class DashboardView extends JPanel {
dataset
);
- ChartPanel chartPanel = new ChartPanel(chart);
+ add(new ChartPanel(chart), BorderLayout.CENTER);
- add(chartPanel, BorderLayout.CENTER);
+ // ---------- SOUTH: Preview + Buttons ----------
+ add(createBottomPanel(), BorderLayout.SOUTH);
+ }
+
+ private JPanel createBottomPanel() {
+
+ JPanel panel = new JPanel(new BorderLayout());
+
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ reloadPixelStreamingViewButton = new JButton("Reload Preview");
+ openFullscreenButton = new JButton("Open Fullscreen");
+
+ buttonPanel.add(reloadPixelStreamingViewButton);
+ buttonPanel.add(openFullscreenButton);
+
+ panel.add(buttonPanel, BorderLayout.SOUTH);
+
+ return panel;
}
public void updateChart(List points) {
@@ -59,5 +87,15 @@ public class DashboardView extends JPanel {
}
}
+ public void updateProblemLevel(double ratio) {
+ levelBar.setRatio(ratio);
+ }
+ public JButton getReloadPixelStreamingViewButton() {
+ return reloadPixelStreamingViewButton;
+ }
+
+ public JButton getOpenFullscreenButton() {
+ return openFullscreenButton;
+ }
}
diff --git a/src/main/java/vassistent/ui/ProblemLevelBar.java b/src/main/java/vassistent/ui/ProblemLevelBar.java
new file mode 100644
index 0000000..347ebe3
--- /dev/null
+++ b/src/main/java/vassistent/ui/ProblemLevelBar.java
@@ -0,0 +1,89 @@
+package vassistent.ui;
+
+import vassistent.model.ProblemLevel;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ProblemLevelBar extends JPanel {
+
+ private double ratio = 0.0;
+ private ProblemLevel level = ProblemLevel.NONE;
+
+ public ProblemLevelBar() {
+ setPreferredSize(new Dimension(100, 50));
+ }
+
+ public void setRatio(double ratio) {
+ this.ratio = Math.max(0.0, Math.min(1.0, ratio));
+ this.level = calculateLevel(this.ratio);
+ repaint();
+ }
+
+ private ProblemLevel calculateLevel(Double ratio) {
+ if (ratio >= 0.9) {
+ return ProblemLevel.DISASTER;
+ } else if (ratio >= 0.8) {
+ return ProblemLevel.HIGH;
+ } else if (ratio >= 0.5) {
+ return ProblemLevel.WARNING;
+ } else {
+ return ProblemLevel.NONE;
+ }
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+
+ int width = getWidth();
+ int height = getHeight();
+
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ // --- Hintergrund ---
+ g2.setColor(Color.LIGHT_GRAY);
+ g2.fillRoundRect(0, 0, width, height, 20, 20);
+
+ // --- Gefüllter Bereich ---
+ int filledWidth = (int) (width * ratio);
+
+ Color baseColor = getColorForLevel(level);
+ Color darkerColor = baseColor.darker();
+
+ GradientPaint gradient = new GradientPaint(
+ 0, 0, darkerColor,
+ filledWidth, 0, baseColor
+ );
+
+ g2.setPaint(gradient);
+ g2.fillRoundRect(0, 0, filledWidth, height, 20, 20);
+
+ // --- Text ---
+ String text = level.name() + " (" + (int)(ratio * 100) + "%)";
+ FontMetrics fm = g2.getFontMetrics();
+ int textWidth = fm.stringWidth(text);
+
+ g2.setColor(Color.BLACK);
+ g2.drawString(
+ text,
+ (width - textWidth) / 2,
+ (height + fm.getAscent()) / 2 - 3
+ );
+ }
+
+ private Color getColorForLevel(ProblemLevel level) {
+ switch (level) {
+ case DISASTER:
+ return new Color(200, 0, 0);
+ case HIGH:
+ return new Color(255, 140, 0);
+ case WARNING:
+ return new Color(255, 215, 0);
+ default:
+ return new Color(0, 170, 0);
+ }
+ }
+}
diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties
index e9e8d98..30956a3 100644
--- a/src/main/resources/config/application.properties
+++ b/src/main/resources/config/application.properties
@@ -2,16 +2,16 @@
app.mode=test
# ===== PYTHON =====
-python.path="C:\\Program Files\\PyManager\\python.exe"
+python.path=C:\\Program Files\\PyManager\\python.exe
# ===== MQTT CLIENT =====
mqtt.topic=PREDICTION
# ===== MQTT SIMULATOR =====
-mqtt_sim.enabled=false
+mqtt_sim.enabled=true
mqtt_sim.script=src/main/resources/scripts/mqtt_simulator.py
# ===== UNREAL ENGINE =====
-unreal.enabled=false
-unreal.executable=external/unreal/avatar.exe
-unreal.signalling_server.script=external/unreal/start.bat
\ No newline at end of file
+unreal.enabled=true
+unreal.executable=C:\\Privat\\Dokumente\\Niklas_Aumueller\\TH\\MSY\\Semester_2\\Projektarbeit\\Windows\\Prototyp1.exe
+unreal.signalling_server.script=C:\\Privat\\Dokumente\\Niklas_Aumueller\\TH\\MSY\\Semester_2\\Projektarbeit\\Windows\\Prototyp1\\Samples\\PixelStreaming\\WebServers\\SignallingWebServer\\platform_scripts\\cmd\\start_with_stun.bat
\ No newline at end of file
diff --git a/src/main/resources/scripts/mqtt_simulator.py b/src/main/resources/scripts/mqtt_simulator.py
index 8a38386..ae6caa1 100644
--- a/src/main/resources/scripts/mqtt_simulator.py
+++ b/src/main/resources/scripts/mqtt_simulator.py
@@ -1,3 +1,5 @@
+import json
+
import paho.mqtt.client as mqtt
import sys
import random
@@ -44,17 +46,26 @@ def main():
try:
logging.info(f"Verbinde mit Broker {BROKER}:{PORT}")
-
client.connect(BROKER, PORT, 60)
client.loop_start()
logging.info("Starte kontinuierliches Senden...")
- while True:
- message = random.randint(0, 1)
+ current_id = 1
- client.publish(TOPIC, message, qos=QOS)
- logging.info(f"Gesendet an '{TOPIC}': {message}")
+ while True:
+ payload = {
+ "valid": True,
+ "_id": current_id,
+ "prediction": random.randint(0, 1)
+ }
+
+ json_payload = json.dumps(payload)
+
+ client.publish(TOPIC, json_payload, qos=QOS)
+ logging.info(f"Gesendet an '{TOPIC}': {json_payload}")
+
+ current_id += 1
time.sleep(INTERVAL_SECONDS)
diff --git a/src/test/java/vassistent/service/EvaluationServiceTest.java b/src/test/java/vassistent/service/EvaluationServiceTest.java
index 70b5287..f03a63f 100644
--- a/src/test/java/vassistent/service/EvaluationServiceTest.java
+++ b/src/test/java/vassistent/service/EvaluationServiceTest.java
@@ -12,14 +12,14 @@ class EvaluationServiceTest {
private StatisticsService statisticsService;
private AppState appState;
- private UnrealWebSocketService unrealService;
+ private AnimationFileService unrealService;
private EvaluationService evaluationService;
@BeforeEach
void setUp() {
statisticsService = mock(StatisticsService.class);
appState = new AppState();
- unrealService = mock(UnrealWebSocketService.class);
+ unrealService = mock(AnimationFileService.class);
evaluationService = new EvaluationService(statisticsService, appState, unrealService);
}