ProgessLevelBar, ProcessManagement with SignallingServer
This commit is contained in:
parent
f2846aa46a
commit
99e768313d
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (gesundheitsassistent)" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
@ -8,7 +11,5 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="ms-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="ms-17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
disconnectMqtt();
|
||||
stopExternalProcesses();
|
||||
}
|
||||
|
||||
if (context.getUnrealService() != null) {
|
||||
context.getUnrealService().disconnect();
|
||||
private void disconnectMqtt() {
|
||||
|
||||
try {
|
||||
MqttClientService mqtt = context.getMqttService();
|
||||
|
||||
if (mqtt != null) {
|
||||
mqtt.disconnect();
|
||||
}
|
||||
|
||||
if (context.getProcessManagerService() != null) {
|
||||
context.getProcessManagerService().shutdown();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,11 +78,13 @@ 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");
|
||||
}
|
||||
|
||||
private void onStateChanged(AppState appState) {
|
||||
|
||||
if (window == null) {
|
||||
Logger.warn("Controller",
|
||||
@ -78,6 +92,8 @@ public class AppWindowController {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 -> {
|
||||
|
||||
if (state.getDataVersion() != lastDataVersion) {
|
||||
lastDataVersion = state.getDataVersion();
|
||||
|
||||
List<RatioPoint> points = statisticsService.getLastNAverages(20);
|
||||
dashboardView.updateChart(points);
|
||||
appState.addListener(this::onStateChanged);
|
||||
}
|
||||
|
||||
private void onStateChanged(AppState state) {
|
||||
|
||||
List<RatioPoint> points =
|
||||
statisticsService.getLastNAverages(20);
|
||||
|
||||
if (points.isEmpty())
|
||||
return;
|
||||
|
||||
double ratio = points.get(points.size() - 1).getRatio();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
dashboardView.updateChart(points);
|
||||
dashboardView.updateProblemLevel(ratio);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
50
src/main/java/vassistent/service/AnimationFileService.java
Normal file
50
src/main/java/vassistent/service/AnimationFileService.java
Normal file
@ -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";
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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<String> listener = topicListeners.get(topic);
|
||||
if (listener != null) {
|
||||
listener.accept(payload);
|
||||
Logger.debug("MQTT", "Payload accepted");
|
||||
} else {
|
||||
Logger.warn(
|
||||
"MQTT",
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
package vassistent.service;
|
||||
|
||||
public class PixelStreamingService {
|
||||
|
||||
}
|
||||
@ -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");
|
||||
startSignallingServer();
|
||||
startUnrealEngine();
|
||||
|
||||
} catch (IOException 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,
|
||||
"-RenderOffScreen",
|
||||
"-NoSound",
|
||||
"-PixelStreamingSignallingURL=ws://127.0.0.1:8888"
|
||||
"-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 Avatar");
|
||||
|
||||
} catch (IOException e) {
|
||||
Logger.error("PROCESS", "Unreal Engine Start fehlgeschlagen", e);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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("\"", "\\\"");
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<RatioPoint> 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;
|
||||
}
|
||||
}
|
||||
|
||||
89
src/main/java/vassistent/ui/ProblemLevelBar.java
Normal file
89
src/main/java/vassistent/ui/ProblemLevelBar.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
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
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user