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); }