diff --git a/Praktikum/VINF_MaerklinControl/src/common/Properties.java b/Praktikum/VINF_MaerklinControl/src/common/Properties.java index 144b96d85315ffd068b2e2bba3fc0cd2b7b83e47..3b4f180653ccf1fa73ab1417e54c2b0760178e64 100644 --- a/Praktikum/VINF_MaerklinControl/src/common/Properties.java +++ b/Praktikum/VINF_MaerklinControl/src/common/Properties.java @@ -1,5 +1,10 @@ package common; +/* + * In dieser Klasse werden alle klassenübergreifen verwendeten Werte einmalig deklariert, + * so dass potentielle Änderungen lediglich einmalig an dieser Stelle durchgeführt werden + * müssen und Code-Vervielfachung vermieden wird. + */ public class Properties { // Defining server-ip and port diff --git a/Praktikum/VINF_MaerklinControl/src/gui/MainApp.java b/Praktikum/VINF_MaerklinControl/src/gui/MainApp.java index 992fdaa14ed01b5a760bdea09dded681eb701b61..ea9b6b778bf1bedb1de3e4fef49e5125b2c0f37f 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/MainApp.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/MainApp.java @@ -1,4 +1,5 @@ package gui; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -28,6 +29,10 @@ import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import common.Properties; +/* + * Die Klasse MainApp stellt die Hauptklasse der Clients dar. In ihr wird die GUI initialisiert + * und geladen sowie sämtliche "nach außen" (zum Server) gerichteten Funktionalitäten implementiert. + */ public class MainApp extends Application { private Stage primaryStage; @@ -56,9 +61,7 @@ public class MainApp extends Application { private Boolean connectionEstablished = false; private Boolean running = false; - /** - * Constructor - */ + // Constructor public MainApp() { UTILITY_THREAD_EXECUTOR = Executors.newFixedThreadPool(2); @@ -69,7 +72,7 @@ public class MainApp extends Application { engines.add(new Engine("Dampflok", Properties.LOK_ID, "res/BR 86.png")); engines.add(new Engine("Reichsbahn", Properties.REICHSBAHN_ID, "res/E 50.png")); - // Add the already known switches // yes, lazy, i know + // Add the already known switches for (int i=4;i<=15;i++){ if (i != 11 && i != 7) { switches.add(new Switch("Weiche "+i, Properties.SWITCH4_ID+i-4)); @@ -79,24 +82,8 @@ public class MainApp extends Application { turntable = new Switch("Drehteller", Properties.TURNTABLE_ID); } - - /** - * Returns the objects as an observable list. - * @return - */ - public ObservableList<Tab> getTabs() { - return tabs; - } - - public ObservableList<Engine> getEngines() { - return engines; - } - - public ObservableList<Switch> getSwitches() { - return switches; - } - + // Initializes the loading of the GUI @Override public void start(Stage primaryStage) { this.primaryStage = primaryStage; @@ -106,16 +93,17 @@ public class MainApp extends Application { initTabs(); } + // Loads and binds the root layouut public void initRootLayout() { try { - // Load root layout from fxml file. + // Load root layout from fxml file FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml")); rootLayout = (BorderPane) loader.load(); rootController = loader.getController(); // only works if loader.load() has been called already rootController.setMainApp(this); - // Show the scene containing the root layout. + // Show the scene containing the root layout Scene scene = new Scene(rootLayout); primaryStage.setScene(scene); primaryStage.show(); @@ -124,9 +112,7 @@ public class MainApp extends Application { } } - /** - * Shows the control tabs inside the root layout. - */ + // Shows the control tabs inside the root layout. public void initTabs() { try { FXMLLoader loader = new FXMLLoader(); @@ -168,24 +154,42 @@ public class MainApp extends Application { } } - - /** - * Returns the main stage. - * @return - */ + public static void main(String[] args) { + launch(args); + } + + // Calls the root-controllers function setStatus to set the statusbar to the given string public void setStatus(String status){ rootController.setStatus(status); } + // Returns the primary stage public Stage getPrimaryStage() { return primaryStage; } - - public static void main(String[] args) { - launch(args); + + // Returns the tabs as an observable list + public ObservableList<Tab> getTabs() { + return tabs; + } + + // Returns the engines as an observable list + public ObservableList<Engine> getEngines() { + return engines; } + + // Returns the switches as an observable list + public ObservableList<Switch> getSwitches() { + return switches; + } + + // Returns the value of connectionEstablished + public Boolean getConnectionEstablished() { + return connectionEstablished; + } + // Called, when the emegency-stop-button is pressed; calls emergencyStop and sets every engines speed to 0 public void emergencyStopHandler() { emergencyStop(); for(Engine eng:engines){ @@ -196,6 +200,7 @@ public class MainApp extends Application { } + // Signalizes the server to cut the train-controls power or to re-enable it depending on its current state (running == false means the emergency-stop is active) public void emergencyStop () { if (connectionEstablished) { try { @@ -216,10 +221,7 @@ public class MainApp extends Application { } } - public Boolean getConnectionEstablished() { - return connectionEstablished; - } - + // Signalizes the server to set the given engines direction to the client-models direction-value public void setEngineDirection (Engine eng) { if (connectionEstablished) { try { @@ -238,6 +240,7 @@ public class MainApp extends Application { } } + // Signalizes the server to set the given engines direction to the client-models speed-value public void setEngineSpeed (Engine eng) { if (connectionEstablished) { try { @@ -251,13 +254,14 @@ public class MainApp extends Application { } } + // Signalizes the server to set the given switch state to the client-models state-value public void setSwitchState (Switch sw) { if (connectionEstablished) { try { byte dir; - if (!sw.getState().get()) //gerade + if (!sw.getState().get()) // Straight dir = (byte) 0x01; - else //krumm + else // Curved dir = (byte) 0x00; byte[] datagram = {Properties.SEPERATOR, (byte) ((sw.getMaerklinID().get()/(1<<8))%(1<<8)), (byte) (sw.getMaerklinID().get()%(1<<8)), (byte) Properties.SWITCH_SET_DIRECTION, dir, (byte) 0x00}; @@ -269,6 +273,8 @@ public class MainApp extends Application { } } + + // Signalizes the server to move the turntable one step in clockwise direction public void turntableTurnCW () { if (connectionEstablished) { try { @@ -281,6 +287,7 @@ public class MainApp extends Application { } } + // Signalizes the server to move the turntable one step in counterclockwise direction public void turntableTurnCCW () { if (connectionEstablished) { try { @@ -293,6 +300,7 @@ public class MainApp extends Application { } } + // Submits a thread to the UTILITY_THREAD_EXECUTOR to establish the connection to the server or closes the active connection depending on the value of connectionEstablished public void establishConnetion () { if (connectionEstablished) { try { @@ -313,14 +321,21 @@ public class MainApp extends Application { } } + /* + * Diese Klasse stellt die Verbindung zum Server her und hält die Input- und Output-Streams + * offen. Weiterhin updated sie bei bestehender Verbindung zyklisch die GUI-Elemente mit den + * Werten der (clientseitig) zugehörigen Modelle. + */ class EstablishConnection implements Runnable { + // Serves to solve relations to the calling instance private final MainApp CONTROLLER_INSTANCE; EstablishConnection (MainApp controller) { CONTROLLER_INSTANCE = controller; } + // Updates all GUI-elements to match the models state public void updateGUI () { engineController.updateEngineStatus(); for (Switch sw : switches){ @@ -328,6 +343,7 @@ public class MainApp extends Application { } } + // Establishes the connection to the server if no connection is established yet, submits a update-thread to the UTILITY_THREAD_EXECUTOR (meaning that updates from the server are processed from now on) and cyclically updates all GUI-elements through updateGUI @Override public void run() { if (!connectionEstablished) { @@ -342,6 +358,7 @@ public class MainApp extends Application { UTILITY_THREAD_EXECUTOR.submit((new UpdateFunctionality(CONTROLLER_INSTANCE, in))); Platform.runLater(new Runnable() { + // Sets the connection-esttablishing-buttons text to "Verbindung trennen" @Override public void run() { CONTROLLER_INSTANCE.setStatus("Verbindung zu "+client.getRemoteSocketAddress()+" aufgebaut!"); @@ -358,14 +375,15 @@ public class MainApp extends Application { } }); - Thread.sleep(50); + Thread.sleep(50); // Frees the resources between the GUI-updates } } catch (Exception e) { - connectionEstablished = false; + connectionEstablished = false; // Interrupts the connection to the server in case of an error running = false; Platform.runLater(new Runnable() { + // Sets the connection-esttablishing-buttons text to "Verbindung herstellen" @Override public void run() { CONTROLLER_INSTANCE.setStatus("Error at: establishConnection (establishing connection)"); @@ -377,6 +395,7 @@ public class MainApp extends Application { } finally { try { + // Properly shuts down the connection to the server in case of an unexpected termination of the client-instance byte[] datagram = {Properties.SEPERATOR, (byte) 0x00, (byte) 0x00, (byte) Properties.SESSION_ABORT, (byte) 0x00, (byte) 0x00}; out.write(datagram); client.close(); @@ -392,49 +411,57 @@ public class MainApp extends Application { } } } - + + /* + * Diese Runnable-Klasse lauscht auf dem Update-Port des Servers auf Status-Updates, + * decodiert diese bei Empfang und updatet die (clientseitigen) Modelle auf die erhaltenen + * Werte. Achtung: die Modelle werden geupdatet, nicht die GUI, da sich ansonsten Nebenläufigkeits- + * Problematiken ergeben können. + */ class UpdateFunctionality implements Runnable { + // Serves to solve relations to the calling instance private final MainApp CONTROLLER_INSTANCE; + + // Due to its length, the incoming datagram can't be handled in one step. Therefore a BufferedInputStream is chosen to buffer it until everything is properly processed. private final BufferedInputStream INPUT_STREAM; UpdateFunctionality (MainApp controller, InputStream in) { CONTROLLER_INSTANCE = controller; INPUT_STREAM = new BufferedInputStream(in, Properties.OUT_BUFFER_SIZE); } - + + // Looks up the correct model related to the datagram and updates it to the received values public void parseDatagram (byte[] datagram) { - //System.out.println("2: "+DatatypeConverter.printHexBinary(datagram)); - if (datagram.length < 5) { + if (datagram.length < 5) { // Checks the given datagrams format setStatus("Error while updating!"); return; } - for (Engine eng : engines){ + for (Engine eng : engines){ // Checks all engines if(eng.getMaerklinID().get()==((datagram[0]&0xFF)*(1<<8)+(datagram[1]&0xFF))){ int engSpeed = (datagram[2]&0xFF)*(1<<8)+(datagram[3]&0xFF); boolean engDirection = (datagram[4] == (0x01)); if (engSpeed != eng.getSpeed().get()) { eng.setSpeed(engSpeed); - //System.out.println("Setting speed of engine "+(datagram[0]&0xFF)*(1<<8)+(datagram[1]&0xFF)+" to "+(datagram[2]&0xFF)*(1<<8)+(datagram[3]&0xFF)+"."); } if (engDirection != eng.getDirection().get()) { eng.setDirection(engDirection); - //System.out.println("Setting direction of engine "+(datagram[0]&0xFF)*(1<<8)+(datagram[1]&0xFF)+" to "+datagram[4]+"."); } if (eng.equals(engineController.getSelectedEngine())){} } } - for (Switch sw : switches){ + for (Switch sw : switches){ // Checks all switches if(sw.getMaerklinID().get()==((datagram[0]&0xFF)*(1<<8)+(datagram[1]&0xFF))){ boolean swState = !(datagram[4] == (byte) 0x01); if (swState != sw.getState().get()) { sw.setState(swState); - //System.out.println("Setting direction of switch "+(datagram[0]&0xFF)*(1<<8)+(datagram[1]&0xFF)+" to "+ datagram[4]+"."); } } } } + + // If a datagram is available, it will be scanned for its start (marked through two seperator-bytes right after another). Afterwards the data is read until the next seperator-byte or the datagram ends, the datas format is checked and, if everything is okay, the (clients) models are updated through parseDatagram. @Override public void run() { int stopCounter, dataCounter, counter; @@ -447,36 +474,35 @@ public class MainApp extends Application { dataCounter = 0; counter = 0; startByte = false; - while (INPUT_STREAM.available() > 0 && stopCounter < 5) { + while (INPUT_STREAM.available() > 0 && stopCounter < 5) { // Checks if a datagram is available and not processed yet buffer[counter%Properties.OUT_BUFFER_SIZE] = (byte) INPUT_STREAM.read(); - if (stopCounter == 2) { + if (stopCounter == 2) { // Checks for the datagrams start startByte = true; } - if (buffer[counter%Properties.OUT_BUFFER_SIZE] == Properties.SEPERATOR && dataCounter != 4) { + if (buffer[counter%Properties.OUT_BUFFER_SIZE] == Properties.SEPERATOR && dataCounter != 4) { // Handles seperator-bytes dataCounter = 0; stopCounter++; } - else { + else { // Handles data-bytes stopCounter = 0; dataCounter++; - if (dataCounter == 5 && startByte) { + if (dataCounter == 5 && startByte) { // If a full datagram consisting of five bytes is read, parseDatagram is called to handle the data dataCounter = 0; for (int i = 0; i < Properties.IN_BUFFER_SIZE; i++) { datagram[i] = buffer[counter-Properties.IN_BUFFER_SIZE+i+1]; } - //System.out.println("1: "+DatatypeConverter.printHexBinary(datagram)); parseDatagram(datagram); } - else if (dataCounter > 5 && startByte) { + else if (dataCounter > 5 && startByte) { // Checks if the datagrams have the correct format throw (new Exception("Wrong data-update-format!")); } } counter++; } - while (INPUT_STREAM.available() != 0) { + while (INPUT_STREAM.available() != 0) { // Flushes the remaining buffer of the BufferedInputStream (doesn't contain any information), so that the next datagram will start at position 0 again (speeds up the next update-process) INPUT_STREAM.read(); } - Thread.sleep(50); + Thread.sleep(50); // Frees the resources between the status-updates } } catch (Exception e) { diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java index 75b59b24d1c4d633f9426b0e47d41eabe22a504f..bb1b3907f7152991821372d6c41b66b3f5cb660a 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java @@ -11,10 +11,8 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.image.Image; -/** - * Model class for a Model Engine. - * - * @author Philipp Stenkamp +/* + * Model class for a Model Engine */ public class Engine { @@ -24,9 +22,7 @@ public class Engine { private final BooleanProperty fwd; private final ObjectProperty<Image> img; - /** - * Default constructor. - */ + // Default constructor public Engine() { this(null, null, null); } @@ -35,13 +31,7 @@ public class Engine { this(name, id, null); } - /** - * Constructor with some initial data. - * - * @param name - * @param id - */ - + // Constructor with some initial data public Engine(String name, Integer id, String imgPath) { this.name = new SimpleStringProperty(name); this.id = new SimpleIntegerProperty(id); @@ -53,39 +43,48 @@ public class Engine { } + // Overrides the toString-functionality to return the instances value of name @Override public String toString() { return name.getValue(); } + // Returns the name public StringProperty getName() { return name; } + // Returns the maerklin-id public IntegerProperty getMaerklinID() { return id; } + // Returns the direction public BooleanProperty getDirection() { return fwd; } + // Sets the direction public void setDirection(Boolean fwd) { this.fwd.set(fwd); } + // Returns the speed public IntegerProperty getSpeed() { return speed; } + // Sets the speed public void setSpeed(Integer speed) { this.speed.set(speed); } + // Returns the objects matching image public ObjectProperty<Image> getImg(){ return img; } + // Sets the objects matchin image public void setImg(Image img){ this.img.set(img); } diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java index a6a737f271c650347fcd7b3d9af0e9db22708841..f96f16b936566e13331f13f48951a0737887ffa3 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java @@ -6,26 +6,23 @@ import common.Properties; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; - -/** - * Model class for a Model Engine. - * - * @author Philipp Stenkamp +/* + * Model class for a Model Engine */ public class Settings { private final ObjectProperty<InetSocketAddress> server; - /** - * Default constructor. - */ + // Default constructor public Settings() { this.server = new SimpleObjectProperty<InetSocketAddress> (new InetSocketAddress("localhost", Properties.PORT)); } + // Returns the server (InetSocketAddress) public ObjectProperty<InetSocketAddress> getSocketAddress() { return server; } + // Sets the server (InetSocketAddress) public void setSocketAddress(InetSocketAddress server) { this.server.set(server); } diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java index 6058fb846c77ab7d066974baaa04d7453614a37d..7f0b53f051929f3d51b4bef87577a6e7d49a74f6 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java @@ -10,10 +10,8 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.property.ObjectProperty; -/** - * Model class for a Model Train Switch. - * - * @author Philipp Stenkamp +/* + * Model class for a Model Train Switch */ public class Switch{ @@ -22,24 +20,17 @@ public class Switch{ private final BooleanProperty state; private final ObjectProperty<SwitchControl> controller; - /** - * Default constructor. - */ + // Default constructor public Switch() { this(null, null, null); } - /** - * Constructor with some initial data. - * - * @param name - * @param id - */ - + // Constructor with some initial data public Switch(String name, Integer id){ this(name, id, null); } + // Constructor with some more initial data public Switch(String name, Integer id, SwitchControl controller) { this.name = new SimpleStringProperty(name); this.id = new SimpleIntegerProperty(id); @@ -50,35 +41,43 @@ public class Switch{ } + // Overrides the toString-functionality to return the instances value of name @Override public String toString() { return name.getValue(); } + // Returns the name public StringProperty getName() { return name; } + // Sets the name public void setName(String name) { this.name.set(name); } + // Returns the maerklin-id public IntegerProperty getMaerklinID() { return id; } + // Returns the state public BooleanProperty getState() { return state; } + // Sets the state public void setState(Boolean state) { this.state.set(state); } + // Returns the objects matching controller-instance public ObjectProperty<SwitchControl> getController(){ return controller; } + // Sets the objects matching controller-instance public void setController(SwitchControl controller){ this.controller.setValue(controller); } diff --git a/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java b/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java index e2d66c56b28c07eef70e15d8cbf8849c352cc792..0cc8fe532868cd0e224b2b859871f8e20769aa48 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java +++ b/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java @@ -1,6 +1,5 @@ package server; - import java.net.DatagramPacket; import java.net.DatagramSocket; import javax.xml.bind.DatatypeConverter; @@ -101,7 +100,7 @@ public class UDPListener implements Runnable{ for (Engine train : Engine.engines){ if (train.getEngineID() == getAddress()){ train.engineSpeed = getEngineSpeed(); - System.out.println("Speed of Engine "+train.getEngineID()+" changed to "+train.getEngineSpeed()); + // System.out.println("Speed of Engine "+train.getEngineID()+" changed to "+train.getEngineSpeed()); break; } } @@ -111,7 +110,7 @@ public class UDPListener implements Runnable{ if (train.getEngineID() == getAddress()){ train.engineDirection = getEngineDirection(); train.engineSpeed = 0; - System.out.println("Direction of Engine "+train.getEngineID()+" changed to "+train.getEngineDirection()); + // System.out.println("Direction of Engine "+train.getEngineID()+" changed to "+train.getEngineDirection()); break; } } @@ -120,7 +119,7 @@ public class UDPListener implements Runnable{ for (Switch sw : Switch.switches){ if (sw.getSwitchID() == getAddress()){ sw.switchDirection = getEngineDirection(); - System.out.println("Direction of Switch "+sw.getSwitchID()+" changed to "+sw.getSwitchDirection()); + // System.out.println("Direction of Switch "+sw.getSwitchID()+" changed to "+sw.getSwitchDirection()); break; } }