diff --git a/pom.xml b/pom.xml index af04bce..049e8a1 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,11 @@ 2.1.0 jar + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + diff --git a/src/main/java/efi/projekt/gesundheitsassistent/FxStart.java b/src/main/java/efi/projekt/gesundheitsassistent/FxStart.java index dd9b822..4532ab9 100644 --- a/src/main/java/efi/projekt/gesundheitsassistent/FxStart.java +++ b/src/main/java/efi/projekt/gesundheitsassistent/FxStart.java @@ -1,13 +1,13 @@ package efi.projekt.gesundheitsassistent; -import atlantafx.base.theme.NordDark; -import efi.projekt.gesundheitsassistent.controller.MetadataDialogController; -import efi.projekt.gesundheitsassistent.model.Metadata; +import atlantafx.base.theme.Dracula; +import efi.projekt.gesundheitsassistent.model.FxModel; +import efi.projekt.gesundheitsassistent.controller.FxViewController; +import efi.projekt.gesundheitsassistent.viewmodel.FxViewModel; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.stage.Modality; import javafx.stage.Stage; import java.io.IOException; @@ -18,49 +18,30 @@ import java.io.IOException; * @author naumueller */ public class FxStart extends Application { - - private static Scene scene; - private Metadata metadata; // das zentrale Model - + @Override public void start(Stage primaryStage) throws IOException { + // Lade FXML + FXMLLoader loader = new FXMLLoader(getClass().getResource("/efi/projekt/gesundheitsassistent/view/FxView.fxml")); + Parent root = loader.load(); + + FxViewController controller = loader.getController(); + + // Erzeuge Model & ViewModel + FxModel model = new FxModel(); + FxViewModel viewModel = new FxViewModel(model); + + // Verbinde Controller mit ViewModel + controller.setViewModel(viewModel); - // 1️⃣ Metadata Model erzeugen - metadata = new Metadata(); - - // 2️⃣ Metadata Dialog laden - FXMLLoader dialogLoader = new FXMLLoader(getClass() - .getResource("/efi/projekt/gesundheitsassistent/view/MetadataDialog.fxml")); - Parent dialogRoot = dialogLoader.load(); - - // Controller bekommen und Model übergeben - MetadataDialogController dialogController = dialogLoader.getController(); - dialogController.setMetadata(metadata); - - // 3️⃣ Dialog Stage erzeugen - Stage dialogStage = new Stage(); - dialogStage.setTitle("Metadaten eingeben"); - dialogStage.initModality(Modality.APPLICATION_MODAL); // blockiert Hauptfenster - dialogStage.setScene(new Scene(dialogRoot)); - dialogStage.showAndWait(); // wartet bis Dialog geschlossen wird - - // 4️⃣ Hauptfenster laden - FXMLLoader mainLoader = new FXMLLoader(getClass() - .getResource("/efi/projekt/gesundheitsassistent/view/FxView.fxml")); - Parent mainRoot = mainLoader.load(); - - scene = new Scene(mainRoot, 1280, 720); - Application.setUserAgentStylesheet(new NordDark().getUserAgentStylesheet()); + Scene scene = new Scene(root, 1280, 720); + Application.setUserAgentStylesheet(new Dracula().getUserAgentStylesheet()); primaryStage.setTitle("Virtueller Gesundheitsassistent"); primaryStage.setScene(scene); primaryStage.show(); } - static void setRoot(String fxml) throws IOException { - scene.setRoot(loadFXML(fxml)); - } - private static Parent loadFXML(String fxml) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader( FxStart.class.getResource("/efi/projekt/gesundheitsassistent/view/" + fxml + ".fxml") @@ -71,8 +52,4 @@ public class FxStart extends Application { public static void main(String[] args) { launch(); } - - public Metadata getMetadata() { - return metadata; - } } diff --git a/src/main/java/efi/projekt/gesundheitsassistent/controller/FxViewController.java b/src/main/java/efi/projekt/gesundheitsassistent/controller/FxViewController.java index cf16246..0aa7c2c 100644 --- a/src/main/java/efi/projekt/gesundheitsassistent/controller/FxViewController.java +++ b/src/main/java/efi/projekt/gesundheitsassistent/controller/FxViewController.java @@ -5,13 +5,23 @@ package efi.projekt.gesundheitsassistent.controller; import efi.projekt.gesundheitsassistent.model.FxModel; +import efi.projekt.gesundheitsassistent.model.Metadata; +import efi.projekt.gesundheitsassistent.viewmodel.FxViewModel; +import efi.projekt.gesundheitsassistent.viewmodel.MetadataDialogViewModel; +import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; +import javafx.scene.control.TextField; +import javafx.stage.Modality; +import javafx.stage.Stage; /** * FXML Controller class @@ -20,17 +30,27 @@ import javafx.scene.control.ButtonType; */ public class FxViewController implements Initializable { - private FxModel model; + private FxViewModel viewModel; - @FXML - private Button exitButton; + @FXML private Button exitButton; + @FXML private TextField messageInputField; + @FXML private TextField messageOutputField; + @FXML private Button sendButton; - /** - * Initializes the controller class. - */ @Override public void initialize(URL url, ResourceBundle rb) { - + exitButton.setOnAction(e -> handleExit()); + } + + public void setViewModel(FxViewModel viewModel) { + this.viewModel = viewModel; + initBindings(); + } + + public void initBindings() { + //Bindings: View <-> ViewModel + messageInputField.textProperty().bindBidirectional(viewModel.outgoingMessageProperty()); + messageOutputField.textProperty().bindBidirectional(viewModel.incomingMessageProperty()); } @FXML @@ -38,11 +58,31 @@ public class FxViewController implements Initializable { Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Programm wirklich beenden?"); alert.showAndWait() .filter(response -> response == ButtonType.OK) - .ifPresent(response -> System.exit(0)); - + .ifPresent(response -> System.exit(0)); } + + @FXML + private void handleSendMessage() { + viewModel.sendMessage(messageInputField.getText()); + } + + @FXML + private void handleOptions() throws IOException { + // Dialog + FXMLLoader loader = new FXMLLoader(getClass() + .getResource("/efi/projekt/gesundheitsassistent/view/MetadataDialog.fxml")); + Parent dialogRoot = loader.load(); - public void setModel(FxModel model) { - this.model = model; + // ViewModel erzeugen + Metadata metadata = new Metadata(); + loader.getController().setViewModel( + new MetadataDialogViewModel(metadata) + ); + + // Dialog Stage erzeugen + Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.APPLICATION_MODAL); + dialogStage.setScene(new Scene(dialogRoot)); + dialogStage.showAndWait(); } } diff --git a/src/main/java/efi/projekt/gesundheitsassistent/controller/MetadataDialogController.java b/src/main/java/efi/projekt/gesundheitsassistent/controller/MetadataDialogController.java index 47d90a7..29a486d 100644 --- a/src/main/java/efi/projekt/gesundheitsassistent/controller/MetadataDialogController.java +++ b/src/main/java/efi/projekt/gesundheitsassistent/controller/MetadataDialogController.java @@ -5,12 +5,14 @@ package efi.projekt.gesundheitsassistent.controller; import efi.projekt.gesundheitsassistent.model.Metadata; +import efi.projekt.gesundheitsassistent.viewmodel.MetadataDialogViewModel; import javafx.fxml.FXML; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.ChoiceBox; import javafx.scene.control.TextField; import javafx.stage.Stage; +import javafx.util.converter.NumberStringConverter; /** * @@ -18,59 +20,39 @@ import javafx.stage.Stage; */ public class MetadataDialogController { - @FXML - private TextField nameField; + @FXML private TextField nameField; + @FXML private TextField ageField; + @FXML private ChoiceBox genderChoiceBox; + @FXML private TextField heightField; + @FXML private TextField weightField; - @FXML - private TextField ageField; + private MetadataDialogViewModel viewModel; - @FXML - private ChoiceBox genderChoiceBox; - - @FXML - private TextField heightField; - - @FXML - private TextField weightField; - - private Metadata metadata; - - public void initialize() { - // ChoiceBox füllen + public void setViewModel(MetadataDialogViewModel viewModel) { + this.viewModel = viewModel; + genderChoiceBox.getItems().addAll("Männlich", "Weiblich", "Divers"); - genderChoiceBox.setValue("Männlich"); // default - } - - public void setMetadata(Metadata metadata) { - this.metadata = metadata; - bindFields(); - } - - private void bindFields() { - // Bidirektionales Binding zwischen Model und UI - nameField.textProperty().bindBidirectional(metadata.nameProperty()); - ageField.textProperty().bindBidirectional(metadata.ageProperty(), new javafx.util.converter.NumberStringConverter()); - genderChoiceBox.valueProperty().bindBidirectional(metadata.genderProperty()); - heightField.textProperty().bindBidirectional(metadata.heightProperty(), new javafx.util.converter.NumberStringConverter()); - weightField.textProperty().bindBidirectional(metadata.weightProperty(), new javafx.util.converter.NumberStringConverter()); + + // Bindings + nameField.textProperty().bindBidirectional(viewModel.nameProperty()); + ageField.textProperty().bindBidirectional(viewModel.ageProperty(), new NumberStringConverter()); + genderChoiceBox.valueProperty().bindBidirectional(viewModel.genderProperty()); + heightField.textProperty().bindBidirectional(viewModel.heightProperty(), new NumberStringConverter()); + weightField.textProperty().bindBidirectional(viewModel.weightProperty(), new NumberStringConverter()); } @FXML private void handleOk() { try { - metadata.validate(); - // Dialog schließen - Stage stage = (Stage) nameField.getScene().getWindow(); - stage.close(); + viewModel.save(); + ((Stage) nameField.getScene().getWindow()).close(); } catch (IllegalArgumentException e) { - Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage(), ButtonType.OK); - alert.showAndWait(); + new Alert(Alert.AlertType.ERROR, e.getMessage(), ButtonType.OK).showAndWait(); } } @FXML private void handleCancel() { - Stage stage = (Stage) nameField.getScene().getWindow(); - stage.close(); + ((Stage) nameField.getScene().getWindow()).close(); } } diff --git a/src/main/java/efi/projekt/gesundheitsassistent/model/FxModel.java b/src/main/java/efi/projekt/gesundheitsassistent/model/FxModel.java index 912bbf8..48a9887 100644 --- a/src/main/java/efi/projekt/gesundheitsassistent/model/FxModel.java +++ b/src/main/java/efi/projekt/gesundheitsassistent/model/FxModel.java @@ -19,6 +19,7 @@ import javafx.beans.property.StringProperty; public class FxModel implements Serializable { private final StringProperty id = new SimpleStringProperty(this, "id"); + private MqttJavaClient mqttClient = new MqttJavaClient(); public FxModel() {} @@ -33,5 +34,16 @@ public class FxModel implements Serializable { public void validate() throws IllegalArgumentException { // Subklassen können überschreiben } + + public void publishMessage(String topic, String messageContent, int qos) { + getMqttClient().publishMessage(topic, messageContent, qos); + } + + /** + * @return the mqttClient + */ + public MqttJavaClient getMqttClient() { + return mqttClient; + } } diff --git a/src/main/java/efi/projekt/gesundheitsassistent/model/MqttJavaClient.java b/src/main/java/efi/projekt/gesundheitsassistent/model/MqttJavaClient.java new file mode 100644 index 0000000..3a991db --- /dev/null +++ b/src/main/java/efi/projekt/gesundheitsassistent/model/MqttJavaClient.java @@ -0,0 +1,102 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package efi.projekt.gesundheitsassistent.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +/** + * + * @author aumni + */ +public class MqttJavaClient implements MqttCallback { + + private Map> topicListeners = new HashMap<>(); + + private static final String BROKER_URL = "tcp://localhost:1883"; + private static final String CLIENT_ID = "JavaClientPublisherSubscriber"; + private static final String PUBLISH_TOPIC = "temperatur"; + private static final String SUBSCRIBE_TOPIC = "Text"; + + private MqttClient client; + + public MqttJavaClient() { + try { + client = new MqttClient(BROKER_URL, CLIENT_ID, new MemoryPersistence()); + client.setCallback(this); + + MqttConnectOptions options = new MqttConnectOptions(); + options.setCleanSession(true); + options.setAutomaticReconnect(true); + + System.out.println("Verbinde mit Broker..." + BROKER_URL); + client.connect(options); + System.out.println("Verbunden"); + + System.out.println("Abonniere Topic: " + SUBSCRIBE_TOPIC); + client.subscribe(SUBSCRIBE_TOPIC); + System.out.println("Topic abonniert!"); + + } catch (MqttException ex) { + System.err.println("Fehler beim Initialisieren oder Verbinden: " + ex.getMessage()); + ex.printStackTrace(); + } + } + + public void addTopicListeners(String topic, Consumer listener) { + topicListeners.put(topic, listener); + try { + client.subscribe(topic); + System.out.println("Abonniert: " + topic); + } catch (MqttException e) { + e.printStackTrace(); + } + } + + public void publishMessage(String topic, String message, int qos) { + try { + MqttMessage msg = new MqttMessage(message.getBytes()); + msg.setQos(qos); + client.publish(topic, msg); + System.out.println("Nachricht veröffentlicht: Topic = " + topic + ", Inhalt = " + message + ", QoS = " + qos); + } catch (MqttException ex) { + System.err.println("Fehler beim Veröffentlicher der Nachricht: " + ex.getMessage()); + ex.printStackTrace(); + } + } + + @Override + public void connectionLost(Throwable cause) { + System.out.println("Verbindung zum Broker verloren! Ursache: " + cause.getMessage()); + } + + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + String payload = new String(message.getPayload()); + + System.out.println("Nachricht emmpfangen:"); + System.out.println(" Topic: " + topic); + System.out.println(" Inhalt: " + new String(message.getPayload())); + System.out.println(" QoS: " + message.getQos()); + + Consumer listener = topicListeners.get(topic); + if (listener != null) { + listener.accept(payload); + } + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + System.out.println("Nachricht erfolgreich zugestellt (QoS > 0). Token: " + token.getMessageId()); + } +} diff --git a/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/FxViewModel.java b/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/FxViewModel.java new file mode 100644 index 0000000..6589e1e --- /dev/null +++ b/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/FxViewModel.java @@ -0,0 +1,50 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package efi.projekt.gesundheitsassistent.viewmodel; + +import efi.projekt.gesundheitsassistent.model.FxModel; +import java.util.HashMap; +import java.util.Map; +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +/** + * + * @author naumueller + */ +public class FxViewModel { + + private final FxModel model; + private Map topicMessages = new HashMap<>(); + private final StringProperty incomingMessage = new SimpleStringProperty(""); + private final StringProperty outgoingMessage = new SimpleStringProperty(""); + + private final StringProperty messageToSend = new SimpleStringProperty(""); + + public FxViewModel(FxModel model) { + this.model = model; + + subscribeTopic("Text"); + subscribeTopic("Temperatur"); + } + + public void subscribeTopic(String topic) { + model.getMqttClient().addTopicListeners(topic, msg -> { + Platform.runLater(() -> getTopicProperty(topic).set(msg)); + }); + } + + public void sendMessage(String msg) { + if (msg != null && !msg.isBlank()) { + model.getMqttClient().publishMessage("Text", msg, 1); + outgoingMessage.set(""); + } + } + + public StringProperty incomingMessageProperty() { return incomingMessage; } + public StringProperty outgoingMessageProperty() { return outgoingMessage; } + public StringProperty getTopicProperty(String topic) { return topicMessages.computeIfAbsent(topic, t -> new SimpleStringProperty()); } +} diff --git a/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/MetadataDialogViewModel.java b/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/MetadataDialogViewModel.java new file mode 100644 index 0000000..2634e62 --- /dev/null +++ b/src/main/java/efi/projekt/gesundheitsassistent/viewmodel/MetadataDialogViewModel.java @@ -0,0 +1,33 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package efi.projekt.gesundheitsassistent.viewmodel; + +import efi.projekt.gesundheitsassistent.model.Metadata; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.StringProperty; + +/** + * + * @author naumueller + */ +public class MetadataDialogViewModel { + + private final Metadata metadata; + + public MetadataDialogViewModel(Metadata metadata) { + this.metadata = metadata; + } + + public StringProperty nameProperty() { return metadata.nameProperty(); } + public IntegerProperty ageProperty() { return metadata.ageProperty(); } + public StringProperty genderProperty() { return metadata.genderProperty(); } + public DoubleProperty heightProperty() { return metadata.heightProperty(); } + public DoubleProperty weightProperty() { return metadata.weightProperty(); } + + public void save() { + metadata.validate(); + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 449cd3d..bc2f785 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,6 +3,7 @@ module efi.projekt.gesundheitsassistent { requires javafx.fxml; requires java.base; requires atlantafx.base; + requires org.eclipse.paho.client.mqttv3; opens efi.projekt.gesundheitsassistent to javafx.fxml; opens efi.projekt.gesundheitsassistent.controller to javafx.fxml; diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/Anime_character.fbx b/src/main/resources/efi/projekt/gesundheitsassistent/icons/Anime_character.fbx new file mode 100644 index 0000000..102a1c9 Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/Anime_character.fbx differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-account-male.gif b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-account-male.gif new file mode 100644 index 0000000..7cf2ab0 Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-account-male.gif differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-health-48.png b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-health-48.png new file mode 100644 index 0000000..dd6ea7f Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-health-48.png differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-heart.gif b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-heart.gif new file mode 100644 index 0000000..b9fe642 Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-heart.gif differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-stress-64.png b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-stress-64.png new file mode 100644 index 0000000..0e4d6e0 Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-stress-64.png differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-temperature-48.png b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-temperature-48.png new file mode 100644 index 0000000..f88e945 Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/icons8-temperature-48.png differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/icons/textures.png b/src/main/resources/efi/projekt/gesundheitsassistent/icons/textures.png new file mode 100644 index 0000000..b0c988e Binary files /dev/null and b/src/main/resources/efi/projekt/gesundheitsassistent/icons/textures.png differ diff --git a/src/main/resources/efi/projekt/gesundheitsassistent/view/FxView.fxml b/src/main/resources/efi/projekt/gesundheitsassistent/view/FxView.fxml index 216e4c7..ee4d10a 100644 --- a/src/main/resources/efi/projekt/gesundheitsassistent/view/FxView.fxml +++ b/src/main/resources/efi/projekt/gesundheitsassistent/view/FxView.fxml @@ -3,7 +3,10 @@ + + + @@ -18,7 +21,7 @@