Changed all files with hard-coded paths to a config-driven configuration

This commit is contained in:
Niklas Aumueller 2026-03-06 14:55:08 +01:00
parent 9fed2cd420
commit 2a12f5aaa5
9 changed files with 223 additions and 31 deletions

View File

@ -7,18 +7,24 @@ Application settings are loaded from classpath properties files in `src/main/res
| Key | Purpose | Current Default | | Key | Purpose | Current Default |
|---|---|---| |---|---|---|
| `app.mode` | Generic mode flag | `test` | | `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` | | `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.topic` | MQTT topic to subscribe to | `PREDICTION` |
| `mqtt_sim.enabled` | Enables simulator startup | `false` | | `mqtt_sim.enabled` | Enables simulator startup | `false` |
| `mqtt_sim.script` | Simulator script path | `src/main/resources/scripts/mqtt_simulator.py` | | `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.enabled` | Enables Unreal startup flow | `true` |
| `unreal.executable` | PowerShell script to start Unreal process | absolute path | | `unreal.executable` | PowerShell script to start Unreal process | absolute path |
| `unreal.signalling_server.script` | Signalling server BAT file path | 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: Notes:
- MQTT broker host/port are currently hardcoded in Java (`MqttClientService`): `tcp://localhost:1883`. - Paths can be provided with or without wrapping quotes; startup sanitizes surrounding quotes.
- Several Unreal-related paths are absolute and environment-specific. - Unreal-related defaults are environment-specific and should be replaced per machine.
## `logger.properties` ## `logger.properties`

View File

@ -1,16 +1,15 @@
# Known Issues # 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 startup script paths
- Unreal process script paths in `application.properties` - Unreal/signalling PID file paths
- PID file cleanup paths in `ProcessManagerService`
Impact: 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 ## 2. Database Deleted on Shutdown

View File

@ -78,13 +78,19 @@ Incoming MQTT payload must be JSON like:
Main config: `src/main/resources/config/application.properties` Main config: `src/main/resources/config/application.properties`
- `app.mode`: current mode flag (`test`) - `app.mode`: current mode flag (`test`)
- `streaming.url`: initial URL for embedded stream browser
- `python.path`: Python executable for simulator startup - `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.topic`: subscribed topic (default `PREDICTION`)
- `mqtt_sim.enabled`: start simulator process on app startup - `mqtt_sim.enabled`: start simulator process on app startup
- `mqtt_sim.script`: simulator script path - `mqtt_sim.script`: simulator script path
- `animation.output.path`: path for generated animation JSON file
- `unreal.enabled`: start Unreal-related processes - `unreal.enabled`: start Unreal-related processes
- `unreal.executable`: PowerShell script path for Unreal start - `unreal.executable`: PowerShell script path for Unreal start
- `unreal.signalling_server.script`: signalling server batch path - `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` Logger config: `src/main/resources/config/logger.properties`
@ -152,8 +158,8 @@ src/main/resources
## Important Notes ## Important Notes
- `AnimationFileService` currently writes to a hardcoded absolute path: - Animation output path is now config-driven (`animation.output.path`).
- `C:\Users\Student\Documents\Dannick\Prototyp1\Saved\animation.json` - MQTT broker URL/client id are config-driven (`mqtt.broker.url`, `mqtt.client.id`).
- Unreal process handling also uses hardcoded PID/script paths. - 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()`. - 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). - The signalling server process startup in `ProcessManagerService` is prepared but currently not launched (`pb.start()` commented).

View File

@ -31,11 +31,41 @@ public class ApplicationInitializer {
context.setAppState(new AppState()); context.setAppState(new AppState());
// ===== Infrastructure Services ===== // ===== Infrastructure Services =====
context.setAnimationFileService( String animationOutputPath =
new AnimationFileService() normalizeConfigValue(
config.getProperty(
"animation.output.path",
"data/animation.json"
)
); );
context.setMqttService(new MqttClientService(context.getAppState())); context.setAnimationFileService(
new AnimationFileService(animationOutputPath)
);
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()); context.setPersistenceService(new DataPersistenceService());
@ -83,4 +113,25 @@ public class ApplicationInitializer {
e.printStackTrace(); 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;
}
} }

View File

@ -11,14 +11,14 @@ import java.io.IOException;
* Writes the current problem level as animation state JSON for Unreal integration. * Writes the current problem level as animation state JSON for Unreal integration.
*/ */
public class AnimationFileService { 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; private final String outputPath;
/** /**
* Creates the animation file service with default output path. * Creates the animation file service with default output path.
*/ */
public AnimationFileService() { public AnimationFileService() {
this(PATH); this(DEFAULT_OUTPUT_PATH);
} }
/** /**
@ -26,7 +26,7 @@ public class AnimationFileService {
* *
* @param outputPath target JSON file path * @param outputPath target JSON file path
*/ */
AnimationFileService(String outputPath) { public AnimationFileService(String outputPath) {
this.outputPath = outputPath; this.outputPath = outputPath;
} }

View File

@ -16,8 +16,10 @@ import java.util.function.Consumer;
public class MqttClientService implements MqttCallback { public class MqttClientService implements MqttCallback {
private AppState appState; private AppState appState;
private static final String BROKER_URL = "tcp://localhost:1883"; private static final String DEFAULT_BROKER_URL = "tcp://localhost:1883";
private static final String CLIENT_ID = "JavaClientPublisherSubscriber"; private static final String DEFAULT_CLIENT_ID = "JavaClientPublisherSubscriber";
private final String brokerUrl;
private final String clientId;
private final Map<String, Consumer<String>> topicListeners = private final Map<String, Consumer<String>> topicListeners =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
@ -30,7 +32,30 @@ public class MqttClientService implements MqttCallback {
* @param appState shared state updated with connection status * @param appState shared state updated with connection status
*/ */
public MqttClientService(AppState appState) { 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 * @param connectOnStart whether the service should connect immediately
*/ */
MqttClientService(AppState appState, MqttClient injectedClient, boolean connectOnStart) { 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.appState = appState;
this.brokerUrl = brokerUrl;
this.clientId = clientId;
try { try {
client = injectedClient != null client = injectedClient != null
? injectedClient ? injectedClient
: new MqttClient( : new MqttClient(
BROKER_URL, brokerUrl,
CLIENT_ID, clientId,
new MemoryPersistence() new MemoryPersistence()
); );
@ -73,7 +126,7 @@ public class MqttClientService implements MqttCallback {
options.setCleanSession(true); options.setCleanSession(true);
options.setAutomaticReconnect(true); options.setAutomaticReconnect(true);
Logger.info("MQTT", "Verbinde mit Broker " + BROKER_URL); Logger.info("MQTT", "Verbinde mit Broker " + brokerUrl);
client.connect(options); client.connect(options);
appState.setMqttConnected(true); appState.setMqttConnected(true);
Logger.info("MQTT", "Verbindung hergestellt"); Logger.info("MQTT", "Verbindung hergestellt");

View File

@ -43,8 +43,18 @@ public class ProcessManagerService {
this( this(
config, config,
ProcessBuilder::start, ProcessBuilder::start,
DEFAULT_UNREAL_PID_FILE, cleanConfigValue(
config.getProperty(
"unreal.pid.file",
DEFAULT_UNREAL_PID_FILE
)
),
cleanConfigValue(
config.getProperty(
"unreal.signalling.pid.file",
DEFAULT_SIGNALLING_PID_FILE DEFAULT_SIGNALLING_PID_FILE
)
)
); );
} }
@ -89,8 +99,8 @@ public class ProcessManagerService {
if (!enabled) return; if (!enabled) return;
try { try {
String script = config.getProperty("mqtt_sim.script"); String script = cleanConfigValue(config.getProperty("mqtt_sim.script"));
String python = config.getProperty("python.path"); String python = cleanConfigValue(config.getProperty("python.path"));
ProcessBuilder pb = new ProcessBuilder(python, script); ProcessBuilder pb = new ProcessBuilder(python, script);
pb.redirectErrorStream(true); pb.redirectErrorStream(true);
@ -130,7 +140,9 @@ public class ProcessManagerService {
private void startSignallingServer() throws IOException { private void startSignallingServer() throws IOException {
String script = String script =
config.getProperty("unreal.signalling_server.script"); cleanConfigValue(
config.getProperty("unreal.signalling_server.script")
);
if (script == null) return; if (script == null) return;
@ -155,7 +167,8 @@ public class ProcessManagerService {
*/ */
private void startUnrealEngine() throws IOException { private void startUnrealEngine() throws IOException {
String unrealPsScript = config.getProperty("unreal.executable"); String unrealPsScript =
cleanConfigValue(config.getProperty("unreal.executable"));
ProcessBuilder pb = new ProcessBuilder( ProcessBuilder pb = new ProcessBuilder(
"powershell.exe", "powershell.exe",
"-ExecutionPolicy", "Bypass", "-ExecutionPolicy", "Bypass",
@ -277,4 +290,25 @@ public class ProcessManagerService {
Process getUnrealSignallingProcess() { Process getUnrealSignallingProcess() {
return unrealSignallingProcess; 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;
}
} }

View File

@ -2,16 +2,23 @@ package vassistent.ui;
import vassistent.bootstrap.ApplicationContext; import vassistent.bootstrap.ApplicationContext;
import vassistent.controller.DashboardController; import vassistent.controller.DashboardController;
import vassistent.util.ConfigLoader;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import java.awt.*; import java.awt.*;
import java.util.Properties;
/** /**
* Main Swing frame containing streaming and dashboard tabs plus status indicators. * Main Swing frame containing streaming and dashboard tabs plus status indicators.
*/ */
public class AppWindow extends JFrame { public class AppWindow extends JFrame {
private static final Properties config =
ConfigLoader.loadProperties(
"config/application.properties"
);
private PixelStreamingView streamingView; private PixelStreamingView streamingView;
private DashboardView dashboardView; private DashboardView dashboardView;
private JTabbedPane tabs; private JTabbedPane tabs;
@ -31,8 +38,13 @@ public class AppWindow extends JFrame {
setLayout(new BorderLayout(10,10)); setLayout(new BorderLayout(10,10));
String streamUrl =
normalizeConfigValue(
config.getProperty("streaming.url", "http://localhost")
);
streamingView = new PixelStreamingView( streamingView = new PixelStreamingView(
"http://localhost", streamUrl,
false, false,
false false
); );
@ -136,4 +148,25 @@ public class AppWindow extends JFrame {
public JTabbedPane getTabs() { public JTabbedPane getTabs() {
return tabs; 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;
}
} }

View File

@ -1,17 +1,27 @@
# ===== MODE ===== # ===== MODE =====
app.mode=test app.mode=test
# ===== UI =====
streaming.url=http://localhost
# ===== PYTHON ===== # ===== PYTHON =====
python.path=C:\\Program Files\\PyManager\\python.exe python.path=C:\\Program Files\\PyManager\\python.exe
# ===== MQTT CLIENT ===== # ===== MQTT CLIENT =====
mqtt.broker.url=tcp://localhost:1883
mqtt.client.id=JavaClientPublisherSubscriber
mqtt.topic=PREDICTION mqtt.topic=PREDICTION
# ===== MQTT SIMULATOR ===== # ===== MQTT SIMULATOR =====
mqtt_sim.enabled=false mqtt_sim.enabled=false
mqtt_sim.script=src/main/resources/scripts/mqtt_simulator.py mqtt_sim.script=src/main/resources/scripts/mqtt_simulator.py
# ===== ANIMATION =====
animation.output.path=data/animation.json
# ===== UNREAL ENGINE ===== # ===== UNREAL ENGINE =====
unreal.enabled=true unreal.enabled=true
unreal.executable="C:\\Users\\Student\\Documents\\Dannick\\avatar\\start_avatar.ps1" 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.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