From 2a12f5aaa5dffb5e7758068fea0c052f316c5640 Mon Sep 17 00:00:00 2001 From: naumueller Date: Fri, 6 Mar 2026 14:55:08 +0100 Subject: [PATCH] Changed all files with hard-coded paths to a config-driven configuration --- docs/CONFIGURATION.md | 10 ++- docs/KNOWN_ISSUES.md | 11 ++-- readme.md | 12 +++- .../bootstrap/ApplicationInitializer.java | 55 +++++++++++++++- .../service/AnimationFileService.java | 6 +- .../vassistent/service/MqttClientService.java | 65 +++++++++++++++++-- .../service/ProcessManagerService.java | 46 +++++++++++-- src/main/java/vassistent/ui/AppWindow.java | 35 +++++++++- .../resources/config/application.properties | 14 +++- 9 files changed, 223 insertions(+), 31 deletions(-) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 39fbf16..5511c56 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -7,18 +7,24 @@ Application settings are loaded from classpath properties files in `src/main/res | Key | Purpose | Current Default | |---|---|---| | `app.mode` | Generic mode flag | `test` | +| `streaming.url` | Initial URL for pixel streaming view | `http://localhost` | | `python.path` | Python interpreter for simulator process | `C:\\Program Files\\PyManager\\python.exe` | +| `mqtt.broker.url` | MQTT broker connection URL | `tcp://localhost:1883` | +| `mqtt.client.id` | MQTT client identifier | `JavaClientPublisherSubscriber` | | `mqtt.topic` | MQTT topic to subscribe to | `PREDICTION` | | `mqtt_sim.enabled` | Enables simulator startup | `false` | | `mqtt_sim.script` | Simulator script path | `src/main/resources/scripts/mqtt_simulator.py` | +| `animation.output.path` | Generated animation state file path | `data/animation.json` | | `unreal.enabled` | Enables Unreal startup flow | `true` | | `unreal.executable` | PowerShell script to start Unreal process | absolute path | | `unreal.signalling_server.script` | Signalling server BAT file path | absolute path | +| `unreal.pid.file` | Unreal PID file for shutdown cleanup | absolute path | +| `unreal.signalling.pid.file` | Signalling PID file for shutdown cleanup | absolute path | Notes: -- MQTT broker host/port are currently hardcoded in Java (`MqttClientService`): `tcp://localhost:1883`. -- Several Unreal-related paths are absolute and environment-specific. +- Paths can be provided with or without wrapping quotes; startup sanitizes surrounding quotes. +- Unreal-related defaults are environment-specific and should be replaced per machine. ## `logger.properties` diff --git a/docs/KNOWN_ISSUES.md b/docs/KNOWN_ISSUES.md index 8f2aeb1..e2f2a5c 100644 --- a/docs/KNOWN_ISSUES.md +++ b/docs/KNOWN_ISSUES.md @@ -1,16 +1,15 @@ # Known Issues -## 1. Hardcoded Environment Paths +## 1. Environment-Specific Defaults -Several runtime paths are absolute and machine-specific: +Configuration is now path-driven, but several default values in `application.properties` are still machine-specific: -- animation output file path in `AnimationFileService` -- Unreal process script paths in `application.properties` -- PID file cleanup paths in `ProcessManagerService` +- Unreal startup script paths +- Unreal/signalling PID file paths Impact: -- project is not portable across machines without local path edits +- configuration updates are required before first run on a different machine ## 2. Database Deleted on Shutdown diff --git a/readme.md b/readme.md index b78678b..b62e127 100644 --- a/readme.md +++ b/readme.md @@ -78,13 +78,19 @@ Incoming MQTT payload must be JSON like: Main config: `src/main/resources/config/application.properties` - `app.mode`: current mode flag (`test`) +- `streaming.url`: initial URL for embedded stream browser - `python.path`: Python executable for simulator startup +- `mqtt.broker.url`: MQTT broker URL +- `mqtt.client.id`: MQTT client id - `mqtt.topic`: subscribed topic (default `PREDICTION`) - `mqtt_sim.enabled`: start simulator process on app startup - `mqtt_sim.script`: simulator script path +- `animation.output.path`: path for generated animation JSON file - `unreal.enabled`: start Unreal-related processes - `unreal.executable`: PowerShell script path for Unreal start - `unreal.signalling_server.script`: signalling server batch path +- `unreal.pid.file`: PID file used for Unreal shutdown cleanup +- `unreal.signalling.pid.file`: PID file used for signalling shutdown cleanup Logger config: `src/main/resources/config/logger.properties` @@ -152,8 +158,8 @@ src/main/resources ## Important Notes -- `AnimationFileService` currently writes to a hardcoded absolute path: -- `C:\Users\Student\Documents\Dannick\Prototyp1\Saved\animation.json` -- Unreal process handling also uses hardcoded PID/script paths. +- Animation output path is now config-driven (`animation.output.path`). +- MQTT broker URL/client id are config-driven (`mqtt.broker.url`, `mqtt.client.id`). +- Unreal PID cleanup paths are config-driven (`unreal.pid.file`, `unreal.signalling.pid.file`). - On app shutdown, `data/health.db` is deleted by `App.deleteDatabase()`. - The signalling server process startup in `ProcessManagerService` is prepared but currently not launched (`pb.start()` commented). diff --git a/src/main/java/vassistent/bootstrap/ApplicationInitializer.java b/src/main/java/vassistent/bootstrap/ApplicationInitializer.java index e63a3d7..12b03a5 100644 --- a/src/main/java/vassistent/bootstrap/ApplicationInitializer.java +++ b/src/main/java/vassistent/bootstrap/ApplicationInitializer.java @@ -31,11 +31,41 @@ public class ApplicationInitializer { context.setAppState(new AppState()); // ===== Infrastructure Services ===== + String animationOutputPath = + normalizeConfigValue( + config.getProperty( + "animation.output.path", + "data/animation.json" + ) + ); + context.setAnimationFileService( - new AnimationFileService() + new AnimationFileService(animationOutputPath) ); - context.setMqttService(new MqttClientService(context.getAppState())); + String mqttBrokerUrl = + normalizeConfigValue( + config.getProperty( + "mqtt.broker.url", + "tcp://localhost:1883" + ) + ); + + String mqttClientId = + normalizeConfigValue( + config.getProperty( + "mqtt.client.id", + "JavaClientPublisherSubscriber" + ) + ); + + context.setMqttService( + new MqttClientService( + context.getAppState(), + mqttBrokerUrl, + mqttClientId + ) + ); context.setPersistenceService(new DataPersistenceService()); @@ -83,4 +113,25 @@ public class ApplicationInitializer { e.printStackTrace(); } } + + /** + * Trims config values and removes optional wrapping single/double quotes. + * + * @param value raw config value + * @return normalized config value or {@code null} + */ + private static String normalizeConfigValue(String value) { + if (value == null) { + return null; + } + + String trimmed = value.trim(); + + if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) + || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { + return trimmed.substring(1, trimmed.length() - 1); + } + + return trimmed; + } } diff --git a/src/main/java/vassistent/service/AnimationFileService.java b/src/main/java/vassistent/service/AnimationFileService.java index 9f63390..e16ddf9 100644 --- a/src/main/java/vassistent/service/AnimationFileService.java +++ b/src/main/java/vassistent/service/AnimationFileService.java @@ -11,14 +11,14 @@ import java.io.IOException; * Writes the current problem level as animation state JSON for Unreal integration. */ public class AnimationFileService { - private static final String PATH = "C:\\Users\\Student\\Documents\\Dannick\\Prototyp1\\Saved\\animation.json"; + private static final String DEFAULT_OUTPUT_PATH = "data/animation.json"; private final String outputPath; /** * Creates the animation file service with default output path. */ public AnimationFileService() { - this(PATH); + this(DEFAULT_OUTPUT_PATH); } /** @@ -26,7 +26,7 @@ public class AnimationFileService { * * @param outputPath target JSON file path */ - AnimationFileService(String outputPath) { + public AnimationFileService(String outputPath) { this.outputPath = outputPath; } diff --git a/src/main/java/vassistent/service/MqttClientService.java b/src/main/java/vassistent/service/MqttClientService.java index 110b17e..06215af 100644 --- a/src/main/java/vassistent/service/MqttClientService.java +++ b/src/main/java/vassistent/service/MqttClientService.java @@ -16,8 +16,10 @@ import java.util.function.Consumer; public class MqttClientService implements MqttCallback { private AppState appState; - private static final String BROKER_URL = "tcp://localhost:1883"; - private static final String CLIENT_ID = "JavaClientPublisherSubscriber"; + private static final String DEFAULT_BROKER_URL = "tcp://localhost:1883"; + private static final String DEFAULT_CLIENT_ID = "JavaClientPublisherSubscriber"; + private final String brokerUrl; + private final String clientId; private final Map> topicListeners = new ConcurrentHashMap<>(); @@ -30,7 +32,30 @@ public class MqttClientService implements MqttCallback { * @param appState shared state updated with connection status */ public MqttClientService(AppState appState) { - this(appState, null, true); + this( + appState, + DEFAULT_BROKER_URL, + DEFAULT_CLIENT_ID, + null, + true + ); + } + + /** + * Creates and connects an MQTT client with configurable broker settings. + * + * @param appState shared state updated with connection status + * @param brokerUrl MQTT broker URL (for example {@code tcp://localhost:1883}) + * @param clientId MQTT client identifier + */ + public MqttClientService(AppState appState, String brokerUrl, String clientId) { + this( + appState, + brokerUrl, + clientId, + null, + true + ); } /** @@ -41,13 +66,41 @@ public class MqttClientService implements MqttCallback { * @param connectOnStart whether the service should connect immediately */ MqttClientService(AppState appState, MqttClient injectedClient, boolean connectOnStart) { + this( + appState, + DEFAULT_BROKER_URL, + DEFAULT_CLIENT_ID, + injectedClient, + connectOnStart + ); + } + + /** + * Creates an MQTT service with configurable broker settings and optional injected client. + * + * @param appState shared state updated with connection status + * @param brokerUrl MQTT broker URL + * @param clientId MQTT client identifier + * @param injectedClient externally provided MQTT client, or {@code null} to create a default client + * @param connectOnStart whether the service should connect immediately + */ + MqttClientService( + AppState appState, + String brokerUrl, + String clientId, + MqttClient injectedClient, + boolean connectOnStart + ) { this.appState = appState; + this.brokerUrl = brokerUrl; + this.clientId = clientId; + try { client = injectedClient != null ? injectedClient : new MqttClient( - BROKER_URL, - CLIENT_ID, + brokerUrl, + clientId, new MemoryPersistence() ); @@ -73,7 +126,7 @@ public class MqttClientService implements MqttCallback { options.setCleanSession(true); options.setAutomaticReconnect(true); - Logger.info("MQTT", "Verbinde mit Broker " + BROKER_URL); + Logger.info("MQTT", "Verbinde mit Broker " + brokerUrl); client.connect(options); appState.setMqttConnected(true); Logger.info("MQTT", "Verbindung hergestellt"); diff --git a/src/main/java/vassistent/service/ProcessManagerService.java b/src/main/java/vassistent/service/ProcessManagerService.java index 614251d..9eeb336 100644 --- a/src/main/java/vassistent/service/ProcessManagerService.java +++ b/src/main/java/vassistent/service/ProcessManagerService.java @@ -43,8 +43,18 @@ public class ProcessManagerService { this( config, ProcessBuilder::start, - DEFAULT_UNREAL_PID_FILE, - DEFAULT_SIGNALLING_PID_FILE + cleanConfigValue( + config.getProperty( + "unreal.pid.file", + DEFAULT_UNREAL_PID_FILE + ) + ), + cleanConfigValue( + config.getProperty( + "unreal.signalling.pid.file", + DEFAULT_SIGNALLING_PID_FILE + ) + ) ); } @@ -89,8 +99,8 @@ public class ProcessManagerService { if (!enabled) return; try { - String script = config.getProperty("mqtt_sim.script"); - String python = config.getProperty("python.path"); + String script = cleanConfigValue(config.getProperty("mqtt_sim.script")); + String python = cleanConfigValue(config.getProperty("python.path")); ProcessBuilder pb = new ProcessBuilder(python, script); pb.redirectErrorStream(true); @@ -130,7 +140,9 @@ public class ProcessManagerService { private void startSignallingServer() throws IOException { String script = - config.getProperty("unreal.signalling_server.script"); + cleanConfigValue( + config.getProperty("unreal.signalling_server.script") + ); if (script == null) return; @@ -155,7 +167,8 @@ public class ProcessManagerService { */ private void startUnrealEngine() throws IOException { - String unrealPsScript = config.getProperty("unreal.executable"); + String unrealPsScript = + cleanConfigValue(config.getProperty("unreal.executable")); ProcessBuilder pb = new ProcessBuilder( "powershell.exe", "-ExecutionPolicy", "Bypass", @@ -277,4 +290,25 @@ public class ProcessManagerService { Process getUnrealSignallingProcess() { return unrealSignallingProcess; } + + /** + * Normalizes string values loaded from properties and strips optional quotes. + * + * @param value raw config value + * @return normalized value or {@code null} + */ + private static String cleanConfigValue(String value) { + if (value == null) { + return null; + } + + String trimmed = value.trim(); + + if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) + || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { + return trimmed.substring(1, trimmed.length() - 1); + } + + return trimmed; + } } diff --git a/src/main/java/vassistent/ui/AppWindow.java b/src/main/java/vassistent/ui/AppWindow.java index f9d434c..ba034bb 100644 --- a/src/main/java/vassistent/ui/AppWindow.java +++ b/src/main/java/vassistent/ui/AppWindow.java @@ -2,16 +2,23 @@ package vassistent.ui; import vassistent.bootstrap.ApplicationContext; import vassistent.controller.DashboardController; +import vassistent.util.ConfigLoader; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; +import java.util.Properties; /** * Main Swing frame containing streaming and dashboard tabs plus status indicators. */ public class AppWindow extends JFrame { + private static final Properties config = + ConfigLoader.loadProperties( + "config/application.properties" + ); + private PixelStreamingView streamingView; private DashboardView dashboardView; private JTabbedPane tabs; @@ -31,8 +38,13 @@ public class AppWindow extends JFrame { setLayout(new BorderLayout(10,10)); + String streamUrl = + normalizeConfigValue( + config.getProperty("streaming.url", "http://localhost") + ); + streamingView = new PixelStreamingView( - "http://localhost", + streamUrl, false, false ); @@ -136,4 +148,25 @@ public class AppWindow extends JFrame { public JTabbedPane getTabs() { return tabs; } + + /** + * Trims a config value and strips optional wrapping quotes. + * + * @param value raw config value + * @return normalized value or {@code null} + */ + private static String normalizeConfigValue(String value) { + if (value == null) { + return null; + } + + String trimmed = value.trim(); + + if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) + || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { + return trimmed.substring(1, trimmed.length() - 1); + } + + return trimmed; + } } diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index c8ae2e4..b05bef3 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -1,17 +1,27 @@ # ===== MODE ===== app.mode=test +# ===== UI ===== +streaming.url=http://localhost + # ===== PYTHON ===== python.path=C:\\Program Files\\PyManager\\python.exe # ===== MQTT CLIENT ===== +mqtt.broker.url=tcp://localhost:1883 +mqtt.client.id=JavaClientPublisherSubscriber mqtt.topic=PREDICTION # ===== MQTT SIMULATOR ===== mqtt_sim.enabled=false mqtt_sim.script=src/main/resources/scripts/mqtt_simulator.py +# ===== ANIMATION ===== +animation.output.path=data/animation.json + # ===== UNREAL ENGINE ===== unreal.enabled=true -unreal.executable="C:\\Users\\Student\\Documents\\Dannick\\avatar\\start_avatar.ps1" -unreal.signalling_server.script=C:\\Users\\Student\\Documents\\Dannick\\avatar\\Windows\\Prototyp1\\Samples\\PixelStreaming\\WebServers\\SignallingWebServer\\platform_scripts\\cmd\\start_with_stun.bat \ No newline at end of file +unreal.executable=C:\\Users\\Student\\Documents\\Dannick\\avatar\\start_avatar.ps1 +unreal.signalling_server.script=C:\\Users\\Student\\Documents\\Dannick\\avatar\\Windows\\Prototyp1\\Samples\\PixelStreaming\\WebServers\\SignallingWebServer\\platform_scripts\\cmd\\start_with_stun.bat +unreal.pid.file=C:\\Users\\Student\\Documents\\Dannick\\avatar\\unreal.pid +unreal.signalling.pid.file=C:\\Users\\Student\\Documents\\Dannick\\avatar\\signalling.pid