feat(config): externalize runtime paths and script settings for local setup

This commit is contained in:
2026-03-06 15:09:15 +01:00
parent 2a12f5aaa5
commit 16bcf7f06d
6 changed files with 289 additions and 54 deletions
@@ -104,6 +104,7 @@ public class ProcessManagerService {
ProcessBuilder pb = new ProcessBuilder(python, script);
pb.redirectErrorStream(true);
configureMqttSimulatorEnvironment(pb);
pythonProcess = processLauncher.launch(pb);
@@ -176,6 +177,8 @@ public class ProcessManagerService {
unrealPsScript
);
configureUnrealScriptEnvironment(pb);
unrealProcess = processLauncher.launch(pb);
//pb.directory(new File(exe).getParentFile());
@@ -311,4 +314,69 @@ public class ProcessManagerService {
return trimmed;
}
/**
* Passes optional simulator settings from app config to the Python process environment.
*
* @param processBuilder simulator process builder
*/
private void configureMqttSimulatorEnvironment(ProcessBuilder processBuilder) {
putEnvFromConfig(processBuilder, "mqtt_sim.broker", "MQTT_SIM_BROKER");
putEnvFromConfig(processBuilder, "mqtt_sim.port", "MQTT_SIM_PORT");
putEnvFromConfig(processBuilder, "mqtt_sim.topic", "MQTT_SIM_TOPIC");
putEnvFromConfig(processBuilder, "mqtt_sim.username", "MQTT_SIM_USERNAME");
putEnvFromConfig(processBuilder, "mqtt_sim.password", "MQTT_SIM_PASSWORD");
putEnvFromConfig(processBuilder, "mqtt_sim.qos", "MQTT_SIM_QOS");
putEnvFromConfig(processBuilder, "mqtt_sim.interval_seconds", "MQTT_SIM_INTERVAL_SECONDS");
putEnvFromConfig(processBuilder, "mqtt_sim.start_id", "MQTT_SIM_START_ID");
putEnvFromConfig(processBuilder, "mqtt_sim.client_id", "MQTT_SIM_CLIENT_ID");
putEnvFromConfig(processBuilder, "mqtt_sim.log_file", "MQTT_SIM_LOG_FILE");
}
/**
* Passes optional Unreal launcher settings from app config to the PowerShell process environment.
*
* @param processBuilder Unreal process builder
*/
private void configureUnrealScriptEnvironment(ProcessBuilder processBuilder) {
putEnvFromConfig(processBuilder, "unreal.target.executable", "VGA_UE_EXE");
putEnvFromConfig(processBuilder, "unreal.target.args", "VGA_UE_ARGS");
putEnvFromConfig(processBuilder, "unreal.target.working_dir", "VGA_UE_WORKDIR");
putEnvFromConfig(processBuilder, "unreal.signalling_server.script", "VGA_SIGNALLING_BAT");
putEnvFromConfig(processBuilder, "unreal.startup.delay.seconds", "VGA_SIGNALLING_DELAY");
String pidDir = cleanConfigValue(config.getProperty("unreal.pid.dir"));
if (pidDir == null || pidDir.isBlank()) {
try {
Path parent = Path.of(unrealPidFile).getParent();
if (parent != null) {
pidDir = parent.toString();
}
} catch (Exception ignored) {
pidDir = null;
}
}
if (pidDir != null && !pidDir.isBlank()) {
processBuilder.environment().put("VGA_PID_DIR", pidDir);
}
}
/**
* Adds a config value to process environment when present.
*
* @param processBuilder target process builder
* @param configKey application properties key
* @param envName environment variable name
*/
private void putEnvFromConfig(
ProcessBuilder processBuilder,
String configKey,
String envName
) {
String value = cleanConfigValue(config.getProperty(configKey));
if (value != null && !value.isBlank()) {
processBuilder.environment().put(envName, value);
}
}
}
@@ -15,6 +15,16 @@ mqtt.topic=PREDICTION
# ===== MQTT SIMULATOR =====
mqtt_sim.enabled=false
mqtt_sim.script=src/main/resources/scripts/mqtt_simulator.py
mqtt_sim.broker=localhost
mqtt_sim.port=1883
mqtt_sim.topic=PREDICTION
mqtt_sim.qos=0
mqtt_sim.interval_seconds=5
# mqtt_sim.username=
# mqtt_sim.password=
# mqtt_sim.start_id=1
# mqtt_sim.client_id=mqtt-simulator
# mqtt_sim.log_file=logs/mqtt_simulator.log
# ===== ANIMATION =====
animation.output.path=data/animation.json
@@ -25,3 +35,8 @@ unreal.executable=C:\\Users\\Student\\Documents\\Dannick\\avatar\\start_avatar.p
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
# unreal.pid.dir=C:\\Users\\Student\\Documents\\Dannick\\avatar
# unreal.target.executable=C:\\Users\\Student\\Documents\\Dannick\\avatar\\Windows\\Prototyp1.exe
# unreal.target.args=-PixelStreamingURL=ws://127.0.0.1 -RenderOffscreen -PixelStreamingWebRTCDisableTransmitAudio
# unreal.target.working_dir=C:\\Users\\Student\\Documents\\Dannick\\avatar\\Windows
# unreal.startup.delay.seconds=5
+106 -40
View File
@@ -1,85 +1,151 @@
import argparse
import json
import logging
import os
import random
import sys
import time
import paho.mqtt.client as mqtt
import sys
import random
import time
import logging
# ===== KONFIGURATION =====
BROKER = "141.75.223.13"
PORT = 1883
TOPIC = "PREDICTION"
USERNAME = None
PASSWORD = None
QOS = 0
INTERVAL_SECONDS = 5
# ==========================
# Logging konfigurieren (Console + Datei)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("logs/mqtt_simulator.log"),
logging.StreamHandler(sys.stdout)
]
)
DEFAULT_BROKER = "141.75.223.13"
DEFAULT_PORT = 1883
DEFAULT_TOPIC = "PREDICTION"
DEFAULT_QOS = 0
DEFAULT_INTERVAL_SECONDS = 5.0
DEFAULT_START_ID = 1
DEFAULT_CLIENT_ID = "mqtt-simulator"
DEFAULT_LOG_FILE = "logs/mqtt_simulator.log"
def env_int(name, default):
value = os.getenv(name)
if value is None or value.strip() == "":
return default
try:
return int(value)
except ValueError:
return default
def env_float(name, default):
value = os.getenv(name)
if value is None or value.strip() == "":
return default
try:
return float(value)
except ValueError:
return default
def build_args():
parser = argparse.ArgumentParser(
description="MQTT simulator for binary prediction events."
)
parser.add_argument("--broker", default=os.getenv("MQTT_SIM_BROKER", DEFAULT_BROKER))
parser.add_argument("--port", type=int, default=env_int("MQTT_SIM_PORT", DEFAULT_PORT))
parser.add_argument("--topic", default=os.getenv("MQTT_SIM_TOPIC", DEFAULT_TOPIC))
parser.add_argument("--username", default=os.getenv("MQTT_SIM_USERNAME"))
parser.add_argument("--password", default=os.getenv("MQTT_SIM_PASSWORD"))
parser.add_argument("--qos", type=int, choices=[0, 1, 2], default=env_int("MQTT_SIM_QOS", DEFAULT_QOS))
parser.add_argument(
"--interval-seconds",
type=float,
default=env_float("MQTT_SIM_INTERVAL_SECONDS", DEFAULT_INTERVAL_SECONDS),
)
parser.add_argument("--start-id", type=int, default=env_int("MQTT_SIM_START_ID", DEFAULT_START_ID))
parser.add_argument("--client-id", default=os.getenv("MQTT_SIM_CLIENT_ID", DEFAULT_CLIENT_ID))
parser.add_argument("--log-file", default=os.getenv("MQTT_SIM_LOG_FILE", DEFAULT_LOG_FILE))
return parser.parse_args()
def configure_logging(log_file):
log_dir = os.path.dirname(log_file)
if log_dir:
os.makedirs(log_dir, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler(sys.stdout),
],
force=True,
)
def on_connect(client, userdata, flags, rc):
if rc == 0:
logging.info("Erfolgreich mit Broker verbunden")
else:
logging.error(f"Verbindung fehlgeschlagen mit Code {rc}")
logging.error("Verbindung fehlgeschlagen mit Code %s", rc)
def main():
args = build_args()
configure_logging(args.log_file)
logging.info("Python script gestartet")
logging.info(
"Konfiguration: broker=%s port=%s topic=%s qos=%s interval=%s",
args.broker,
args.port,
args.topic,
args.qos,
args.interval_seconds,
)
client = mqtt.Client()
client = mqtt.Client(client_id=args.client_id)
if USERNAME and PASSWORD:
client.username_pw_set(USERNAME, PASSWORD)
if args.username:
client.username_pw_set(args.username, args.password)
client.on_connect = on_connect
try:
logging.info(f"Verbinde mit Broker {BROKER}:{PORT}")
client.connect(BROKER, PORT, 60)
logging.info("Verbinde mit Broker %s:%s", args.broker, args.port)
client.connect(args.broker, args.port, 60)
client.loop_start()
logging.info("Starte kontinuierliches Senden...")
current_id = 1
current_id = args.start_id
while True:
payload = {
"valid": True,
"_id": current_id,
"prediction": random.randint(0, 1)
"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}")
client.publish(args.topic, json_payload, qos=args.qos)
logging.info("Gesendet an '%s': %s", args.topic, json_payload)
current_id += 1
time.sleep(INTERVAL_SECONDS)
time.sleep(args.interval_seconds)
except KeyboardInterrupt:
logging.info("Publisher manuell beendet")
client.loop_stop()
client.disconnect()
sys.exit(0)
except Exception as e:
except Exception:
logging.exception("Unerwarteter Fehler")
sys.exit(1)
finally:
try:
client.loop_stop()
client.disconnect()
except Exception:
pass
if __name__ == "__main__":
main()
main()
+56 -14
View File
@@ -1,22 +1,64 @@
$pidDir = "C:\Users\Student\Documents\Dannick\avatar"
param(
[string]$PidDir = $env:VGA_PID_DIR,
[string]$UeExe = $env:VGA_UE_EXE,
[string]$UeArgs = $env:VGA_UE_ARGS,
[string]$SignallingBat = $env:VGA_SIGNALLING_BAT,
[string]$UeWorkingDir = $env:VGA_UE_WORKDIR,
[int]$SignallingStartupDelaySeconds = $(if ($env:VGA_SIGNALLING_DELAY) { [int]$env:VGA_SIGNALLING_DELAY } else { 5 }),
[switch]$ShowSignallingWindow
)
$ueExe = "C:\Users\Student\Documents\Dannick\avatar\Windows\Prototyp1.exe"
$ueArgs = "-PixelStreamingURL=ws://127.0.0.1 -RenderOffscreen -PixelStreamingWebRTCDisableTransmitAudio"
if (-not $PidDir) {
$PidDir = "C:\Users\Student\Documents\Dannick\avatar"
}
$signallingBat = "C:\Users\Student\Documents\Dannick\avatar\Windows\Prototyp1\Samples\PixelStreaming\WebServers\SignallingWebServer\platform_scripts\cmd\start_with_stun.bat"
if (-not $UeExe) {
$UeExe = "C:\Users\Student\Documents\Dannick\avatar\Windows\Prototyp1.exe"
}
$ueWorkingDir = "C:\Users\Student\Documents\Dannick\avatar\Windows"
if (-not $UeArgs) {
$UeArgs = "-PixelStreamingURL=ws://127.0.0.1 -RenderOffscreen -PixelStreamingWebRTCDisableTransmitAudio"
}
$signalling = Start-Process -FilePath $signallingBat -PassThru -WindowStyle Hidden
if (-not $SignallingBat) {
$SignallingBat = "C:\Users\Student\Documents\Dannick\avatar\Windows\Prototyp1\Samples\PixelStreaming\WebServers\SignallingWebServer\platform_scripts\cmd\start_with_stun.bat"
}
Start-Sleep -Seconds 5
if (-not $UeWorkingDir) {
$UeWorkingDir = Split-Path -Path $UeExe -Parent
}
$ue = Start-Process `
-FilePath $ueExe `
-ArgumentList $ueArgs `
-WorkingDirectory $ueWorkingDir `
-PassThru
if (-not (Test-Path -LiteralPath $SignallingBat)) {
throw "Signalling script not found: $SignallingBat"
}
$ue.Id | Out-File "$pidDir\unreal.pid" -Encoding ascii
$signalling.Id | Out-File "$pidDir\signalling.pid" -Encoding ascii
if (-not (Test-Path -LiteralPath $UeExe)) {
throw "Unreal executable not found: $UeExe"
}
if (-not (Test-Path -LiteralPath $UeWorkingDir)) {
throw "Unreal working directory not found: $UeWorkingDir"
}
New-Item -ItemType Directory -Path $PidDir -Force | Out-Null
$signallingWindowStyle = if ($ShowSignallingWindow.IsPresent) { "Normal" } else { "Hidden" }
$signalling = Start-Process -FilePath $SignallingBat -PassThru -WindowStyle $signallingWindowStyle
Start-Sleep -Seconds $SignallingStartupDelaySeconds
$ueStartParams = @{
FilePath = $UeExe
WorkingDirectory = $UeWorkingDir
PassThru = $true
}
if ($UeArgs) {
$ueStartParams["ArgumentList"] = $UeArgs
}
$ue = Start-Process @ueStartParams
$ue.Id | Out-File (Join-Path $PidDir "unreal.pid") -Encoding ascii
$signalling.Id | Out-File (Join-Path $PidDir "signalling.pid") -Encoding ascii