diff --git a/Praktikum/VINF_MaerklinControl/.classpath b/Praktikum/VINF_MaerklinControl/.classpath index ef0f0db4af6180ed5196999f319a0c944249c726..e085ae486dcc2b8f56a70f843aa14ee38dc87090 100644 --- a/Praktikum/VINF_MaerklinControl/.classpath +++ b/Praktikum/VINF_MaerklinControl/.classpath @@ -1,8 +1,8 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> - <classpathentry kind="con" path="org.eclipse.fx.ide.jdt.core.JAVAFX_CONTAINER"/> - <classpathentry kind="lib" path="/home/lukas/VInf/Praktikum/VINF_MaerklinControl/controlsfx-8.40.12.jar"/> - <classpathentry kind="output" path="bin"/> -</classpath> +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.fx.ide.jdt.core.JAVAFX_CONTAINER"/> + <classpathentry exported="true" kind="lib" path="D:/Users/Philipp/gitlab/VInf/Praktikum/VINF_MaerklinControl/controlsfx-8.40.12.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/Praktikum/VINF_MaerklinControl/bin/.gitignore b/Praktikum/VINF_MaerklinControl/bin/.gitignore index 51adac184a4c6f7daf0218f86fffdffc9fe56b4e..872baad916a755cd4f9f95afd79cf08cd6415a06 100644 --- a/Praktikum/VINF_MaerklinControl/bin/.gitignore +++ b/Praktikum/VINF_MaerklinControl/bin/.gitignore @@ -1,3 +1,3 @@ +/common/ /gui/ /server/ -/common/ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class index 5071b96bd11d0b906b01cf676c004fa20ab1c4ff..acf7f7750e043bce3e59358fb38b53dc54945814 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class and b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class differ diff --git a/Praktikum/VINF_MaerklinControl/build.fxbuild b/Praktikum/VINF_MaerklinControl/build.fxbuild index e46771ba6ab7b7dd14e1945fb518a467f8efd31e..448a8d2b23cb4e79e0c6191faffcae701aa3b94c 100644 --- a/Praktikum/VINF_MaerklinControl/build.fxbuild +++ b/Praktikum/VINF_MaerklinControl/build.fxbuild @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="ASCII"?> <anttasks:AntTask xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:anttasks="http://org.eclipse.fx.ide.jdt/1.0" buildDirectory="${project}/build"> <deploy packagingFormat="exe"> - <application name="TestMopped" mainclass="gui.MainApp" version="0.9" toolkit="fx"/> - <info title="Märklin Control" vendor="lf.ps"/> + <application name="MaerklinControl" mainclass="server.MaerklinServerApplication" version="1.0"/> + <info title="Maerklin Control Server" vendor="lf.ps"/> </deploy> <signjar/> </anttasks:AntTask> diff --git a/Praktikum/VINF_MaerklinControl/build/build.xml b/Praktikum/VINF_MaerklinControl/build/build.xml deleted file mode 100644 index 2c02d90adb7e02148433f939c391a2ed488f76c9..0000000000000000000000000000000000000000 --- a/Praktikum/VINF_MaerklinControl/build/build.xml +++ /dev/null @@ -1,142 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <project name="VINF_MaerklinControl" default="do-deploy" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> - <target name="init-fx-tasks"> - <path id="fxant"> - <filelist> - <file name="${java.home}\..\lib\ant-javafx.jar"/> - <file name="${java.home}\lib\jfxrt.jar"/> - <file name="${basedir}"/> - </filelist> - </path> - - <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" - uri="javafx:com.sun.javafx.tools.ant" - classpathref="fxant"/> - </target> - <target name="setup-staging-area"> - <delete dir="externalLibs" /> - <delete dir="project" /> - <delete dir="projectRefs" /> - - <mkdir dir="externalLibs" /> - - <copy todir="externalLibs"> - <fileset dir="D:\Users\Philipp\gitlab\VInf\Praktikum\VINF_MaerklinControl"> - <filename name="controlsfx-8.40.12.jar"/> - </fileset> - </copy> - - <mkdir dir="project" /> - <copy todir="project"> - <fileset dir="D:\Users\Philipp\gitlab\VInf\Praktikum\VINF_MaerklinControl"> - <include name="src/**" /> - </fileset> - </copy> - - <mkdir dir="projectRefs" /> - </target> - <target name='do-compile'> - <delete dir="build" /> - <mkdir dir="build/src" /> - <mkdir dir="build/libs" /> - <mkdir dir="build/classes" /> - - <!-- Copy project-libs references --> - <copy todir="build/libs"> - <fileset dir="externalLibs"> - <include name="controlsfx-8.40.12.jar"/> - </fileset> - </copy> - - <!-- Copy project references --> - - <!-- Copy project sources itself --> - <copy todir="build/src"> - <fileset dir="project/src"> - <include name="**/*"/> - </fileset> - </copy> - - <javac includeantruntime="false" source="1.8" target="1.8" srcdir="build/src" destdir="build/classes" encoding="Cp1252"> - <classpath> - <fileset dir="build/libs"> - <include name="*"/> - </fileset> - </classpath> - </javac> - - <!-- Copy over none Java-Files --> - <copy todir="build/classes"> - <fileset dir="project/src"> - <exclude name="**/*.java"/> - </fileset> - </copy> - - - </target> - <target name="do-deploy" depends="setup-staging-area, do-compile, init-fx-tasks"> - <delete file="dist"/> - <delete file="deploy" /> - - <mkdir dir="dist" /> - <mkdir dir="dist/libs" /> - - <copy todir="dist/libs"> - <fileset dir="externalLibs"> - <include name="*" /> - </fileset> - </copy> - - - <fx:resources id="appRes"> - <fx:fileset dir="dist" includes="VINF_MaerklinControl.jar"/> - <fx:fileset dir="dist" includes="libs/*"/> - <fx:fileset dir="dist" includes="res/**"/> - </fx:resources> - - <fx:application id="fxApplication" - name="Märklin Control" - mainClass="gui.MainApp" - version="1.0" - /> - - <mkdir dir="build/classes/META-INF" /> - - - - <fx:jar destfile="dist/Märklin Control.jar"> - <fx:application refid="fxApplication"/> - <fileset dir="build/classes"> - </fileset> - <fx:resources refid="appRes"/> - - <manifest> - <attribute name="Implementation-Vendor" value="lf.ps"/> - <attribute name="Implementation-Title" value="Märklin Control"/> - <attribute name="Implementation-Version" value="1.0"/> - <attribute name="JavaFX-Feature-Proxy" value="None"/> - </manifest> - </fx:jar> - - - <mkdir dir="deploy" /> - <!-- Need to use ${basedir} because somehow the ant task is calculating the directory differently --> - <fx:deploy - embedJNLP="false" - extension="false" - includeDT="false" - offlineAllowed="true" - outdir="${basedir}/deploy" - outfile="Märklin Control" nativeBundles="exe" - updatemode="background" > - - <fx:platform basedir="${java.home}"/> - <fx:info title="Märklin Control" vendor="lf.ps"/> - - <fx:application refId="fxApplication"/> - <fx:resources refid="appRes"/> - </fx:deploy> - - - </target> -</project> diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp$EstablishConnection.class b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp$EstablishConnection.class index 086baa0b446bdc09440de9a050bfb689e2073252..c84e28384e911211578c3a927ae2ed57ee0c7d2d 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp$EstablishConnection.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp$EstablishConnection.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp.class b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp.class index fc73e783f3fcd5cb1f3668aadaa5e01949446949..d363d3faf2d353880edcb021e977bc41a9b93e54 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/MainApp.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/view/EngineController.class b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/view/EngineController.class index 15553e747ff737404482762152267b90b1151c1a..7f5db2626d708a41262be0a15bdc9cfc8cde2281 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/gui/view/EngineController.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/gui/view/EngineController.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/server/ClientThread.class b/Praktikum/VINF_MaerklinControl/build/build/classes/server/ClientThread.class index 58384503524c74fbe48dc4af31f2c2c707d42c7b..4b244a5c96b4965568fe71e2d8549630f7c23f1c 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/server/ClientThread.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/server/ClientThread.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/server/Engine.class b/Praktikum/VINF_MaerklinControl/build/build/classes/server/Engine.class index 6f7d7d6831e8a4cbf98f77ab16fb7d74a12a6c22..7a54132ef0b3a2c54d8ce6f7f94b1bf1d172d153 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/server/Engine.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/server/Engine.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/server/MaerklinServer.class b/Praktikum/VINF_MaerklinControl/build/build/classes/server/MaerklinServer.class index 1d36d9f7d893fab4c383fab75e5f30994357a8c0..9d230899ffd2bb49dc9ffaba961f702f32bb6d84 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/server/MaerklinServer.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/server/MaerklinServer.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/server/Switch.class b/Praktikum/VINF_MaerklinControl/build/build/classes/server/Switch.class index 9b4c60dc5ac84289301dd24c0b2d095c8aae1eb1..8409392f016f4c6f096dabadf95a8637c434126f 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/server/Switch.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/server/Switch.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/classes/server/UDPListener.class b/Praktikum/VINF_MaerklinControl/build/build/classes/server/UDPListener.class index 98171f3ab58a3d3188d2cb9f6f0ee98cd1b8db25..54603fef4ede55f6fa711510c40c39ec29977c53 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/build/classes/server/UDPListener.class and b/Praktikum/VINF_MaerklinControl/build/build/classes/server/UDPListener.class differ diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/common/Properties.java b/Praktikum/VINF_MaerklinControl/build/build/src/common/Properties.java index 144b96d85315ffd068b2e2bba3fc0cd2b7b83e47..6c684e7aa939d8f95877d88b1c6697c26320dc83 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/common/Properties.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/common/Properties.java @@ -1,5 +1,10 @@ package common; +/* + * In dieser Klasse werden alle klassenuebergreifend verwendeten Werte einmalig deklariert, + * so dass potentielle Aenderungen lediglich einmalig an dieser Stelle durchgefuehrt werden + * muessen und Code-Vervielfachung vermieden wird (Einhaltung des DRY-Prinzips). + */ public class Properties { // Defining server-ip and port diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/MainApp.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/MainApp.java index 992fdaa14ed01b5a760bdea09dded681eb701b61..309010fda8e84b703a0ea47626ef9e95393a1f56 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/MainApp.java +++ b/Praktikum/VINF_MaerklinControl/build/build/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 saemtliche "nach aussen" (zum Server) gerichteten Funktionalitaeten 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 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 tab pane of 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)"); @@ -376,65 +394,83 @@ public class MainApp extends Application { e.printStackTrace(); } finally { - try { - byte[] datagram = {Properties.SEPERATOR, (byte) 0x00, (byte) 0x00, (byte) Properties.SESSION_ABORT, (byte) 0x00, (byte) 0x00}; - out.write(datagram); - client.close(); - connectionEstablished = false; - running = false; - settingsController.updateSettingsStatus(); - - } catch (Exception e) { - setStatus("Error at: establishConnection (shutting down connection)"); - e.printStackTrace(); - } + if (connectionEstablished) { + 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(); + connectionEstablished = false; + running = false; + Platform.runLater(new Runnable() { + + // Sets the connection-esttablishing-buttons text to "Verbindung herstellen" + @Override + public void run() { + CONTROLLER_INSTANCE.settingsController.updateSettingsStatus(); + } + + }); + } catch (Exception e) { + setStatus("Error at: establishConnection (shutting down connection)"); + e.printStackTrace(); + } + } } } } } - + + /* + * 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 +483,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/build/build/src/gui/model/Engine.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Engine.java index 75b59b24d1c4d633f9426b0e47d41eabe22a504f..617e48196c39d195369a453ba46ff64d384fe4dd 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Engine.java +++ b/Praktikum/VINF_MaerklinControl/build/build/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 +/* + * Modell-Klasse fuer einen Zug. */ 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/build/build/src/gui/model/Settings.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Settings.java index a6a737f271c650347fcd7b3d9af0e9db22708841..f2db8d0165abfe4f6aa02309cb4772c8e66093bc 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Settings.java +++ b/Praktikum/VINF_MaerklinControl/build/build/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 +/* + * Modell-Klasse fuer die Einstellungen der App. */ 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/build/build/src/gui/model/Switch.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Switch.java index 6058fb846c77ab7d066974baaa04d7453614a37d..1db5a7203bafa6a1251405d865f53eda64304df5 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/model/Switch.java +++ b/Praktikum/VINF_MaerklinControl/build/build/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 +/* + * Modell-Klasse fuer eine Weiche. */ 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/build/build/src/gui/view/EngineController.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/EngineController.java index 98eded74290c84c51acddd125d109d3ae4572037..fa17d126b00401d2f88b0513380bdbe224d63d57 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/EngineController.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/EngineController.java @@ -10,6 +10,10 @@ import javafx.scene.image.ImageView; import gui.MainApp; import gui.model.Engine; +/* + * Die Klasse EngineController stellt den Controller f�r die Bedienelemente zur Steuerung der Lokomotiven bereit. + */ + public class EngineController { @FXML private ImageView engineImage; @@ -38,9 +42,11 @@ public class EngineController { @FXML private void initialize() { engineSpeedSlider.setValue(0); - engineFwdButton.setSelected(true); // initializes the forward Button as pressed + engineFwdButton.setSelected(true); // initializes the forward Button as pressed - default direction + + // updates the local reference of the selected engine to the Engine selected in the ChoiceBox engineChoiceBox.getSelectionModel().selectedItemProperty().addListener( - (observable, oldValue, newValue) -> setSelectedEngine(newValue)); + (observable, oldValue, newValue) -> setSelectedEngine(newValue)); engineSpeedSlider.valueProperty().addListener(new ChangeListener<Number>() { public void changed(ObservableValue<? extends Number> ov, @@ -64,13 +70,14 @@ public class EngineController { engineImage.setImage(mainApp.getEngines().get(0).getImg().get()); } + // Is called if the state of the direction buttons changes and updates the engine model accordingly public void handleEngineDirection(){ eng.setDirection(engineFwdButton.isSelected()); - eng.setSpeed(0); mainApp.setEngineDirection(eng); updateEngineStatus(); } + // updates the GUI to the values of the currently selected engine public void updateEngineStatus(){ engineImage.setImage(eng.getImg().get()); engineSpeedSlider.setValue(eng.getSpeed().get()/10); @@ -87,6 +94,12 @@ public class EngineController { mainApp.setStatus(status.toString()); } + + /** + * updates the local reference of the selected engine to the given one + * + * @param Engine + */ public void setSelectedEngine(Engine eng){ this.eng = eng; mainApp.setStatus(eng.toString() + " ausgew�hlt"); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/RootLayoutController.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/RootLayoutController.java index f47880d0f782ae54864ad646cee46dc944ef21e3..3432ca83460de551981d3302cc20b603038708d1 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/RootLayoutController.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/RootLayoutController.java @@ -7,6 +7,10 @@ import org.controlsfx.control.StatusBar; import gui.MainApp; +/* + * Die Klasse RootLayoutController stellt den Controller f�r die Bedienelemente des Basislayouts bereit. + */ + public class RootLayoutController { @FXML private TabPane rootTabPane; @@ -17,18 +21,29 @@ public class RootLayoutController { // Reference to the main application. private MainApp mainApp; + /** + * Is called by the main application to give a reference back to itself. + * + * @param mainApp + */ public void setMainApp(MainApp mainApp) { this.mainApp = mainApp; } + /** + * get the tabs from the main app and load them into the local tabPane + * can't be done in the constructor, since mainApp has to be set first + */ public void loadTabs(){ - rootTabPane.getTabs().addAll(mainApp.getTabs()); //can't be in the constructor + rootTabPane.getTabs().addAll(mainApp.getTabs()); } + // sets the StatusBar text public void setStatus(String status){ statusBar.setText(status); } + // passes the Emergency Stop command to the mainApp public void emergencyStopHandler(){ mainApp.emergencyStopHandler(); } diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SettingsController.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SettingsController.java index 85161fac87685a5a2c937269a3abacca54bcd7d9..590111f0ea7dd30ca94381c950513d9bc57e6332 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SettingsController.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SettingsController.java @@ -39,7 +39,11 @@ public class SettingsController { public void setMainApp(MainApp mainApp) { this.mainApp = mainApp; } - + /** + * is called by an OnClick event caused by the Connect Button. + * updates the Settings model to the currently selected IP Adress and + * calls the establishConnection method of the mainApp. + */ @FXML private void connectHandler(){ InetSocketAddress address = new InetSocketAddress(serverIP.getText(), settings.getSocketAddress().get().getPort()); @@ -47,7 +51,8 @@ public class SettingsController { updateSettingsStatus(); mainApp.establishConnetion(); } - + + // updates the GUI public void updateSettingsStatus(){ InetSocketAddress addr = settings.getSocketAddress().get(); StringBuilder status = new StringBuilder("Verbindung herstellen mit "); @@ -62,6 +67,11 @@ public class SettingsController { } } + /** + * updates the local reference of the selected settings model to the given one + * + * @param Settings + */ public void setSettings(Settings settings) { this.settings = settings; } diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchControl.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchControl.java index e14c05fbe2f93d2884930cc518fcc5cee7d41e00..4d4154547a677ca8e6577d57bdaffc74dc246dbb 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchControl.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchControl.java @@ -15,6 +15,11 @@ import org.controlsfx.control.ToggleSwitch; import gui.MainApp; import gui.model.Switch; +/* + * Die Klasse SwitchControl stellt ein Sonderbedienelement dar. Sie l�dt die Bedienelemente f�r jeweils eine Weiche + * und fungiert gleichzeitig als deren Controller. Ihr wird im Konstruktor das zugeh�rige Weichen-Objekt �bergeben. + */ + public class SwitchControl extends HBox{ @FXML private Label nameLabel; @@ -37,6 +42,8 @@ public class SwitchControl extends HBox{ /** * The constructor. * The constructor is called before the initialize() method. + * Unique in that it does also load the Switch.fxml, which is usally done in an + * higher class, not in the controller itself. */ public SwitchControl(Switch sw) { this.sw = sw; @@ -80,7 +87,7 @@ public class SwitchControl extends HBox{ this.mainApp = mainApp; } - + // updates the GUI elements to reflect the current state of the switch public void updateSwitchStatus(){ stateToggleSwitch.setSelected(sw.getState().get()); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchListController.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchListController.java index de5eebb19ff70962904f45575d52bb181f31be79..31bef5d8251a682d5726f5f6c7d54ddbfe0c3c6e 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchListController.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/SwitchListController.java @@ -40,7 +40,11 @@ public class SwitchListController { this.mainApp = mainApp; } - + /** + * this is where the magic happens! + * kidding aside, this method creates and assigns a new SwitchControl instance to every switch defined in the mainApp. + * it then adds these SwitchControls to the VBox provided in the SwitchList.fxml + */ public void setSwitchControls(){ ObservableList<Switch> switches = mainApp.getSwitches(); ObservableList<SwitchControl> switchControls = FXCollections.observableArrayList(); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/TurntableController.java b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/TurntableController.java index 0c8c6c16ae5ea763adf615195f6e70b1a7ad1cb2..324001bbfb237adbbbb462981dab13a6536e47f7 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/TurntableController.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/gui/view/TurntableController.java @@ -5,6 +5,10 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; +/* + * Die Klasse TurntableController stellt den Controller f�r die Bedienelemente zur Steuerung der Drehscheibe bereit. + */ + public class TurntableController { @FXML @@ -34,11 +38,13 @@ public class TurntableController { } + // Is called by an onClick event on the left button and calls the according function in the main app public void handleTurnCCW (){ mainApp.turntableTurnCCW(); mainApp.setStatus("Letztes Kommando: Drehteller links drehen"); } + // Is called by an onClick event on the right button and calls the according function in the main app public void handleTurnCW (){ mainApp.turntableTurnCW(); mainApp.setStatus("Letztes Kommando: Drehteller rechts drehen"); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/Engine.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/Engine.java index 60f39ceeff56b80bc26de0989f3df7adabf4a71e..090944a012f551d536a402cf7c256e5a7502401e 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/Engine.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/Engine.java @@ -3,17 +3,24 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell fuer einen Zug auf Serverseite dar. + * Weiterhin lassen sich ueber eine lokale Instanz des Maerklin-Protokolls + * Befehle an die Maerklin-C2-Steuerung senden. + */ public class Engine { + // Static array containing all created instances of the object static ArrayList<Engine> engines; private int engineID; int engineDirection; int engineSpeed; + // Private locale instance of the maerklin-protocol to send commands to the control-station private MaerklinProtocol udpProtocol; - private UDPListener listener; + // Constructor public Engine(){ this(0, 1, 0); @@ -29,12 +36,12 @@ public class Engine { engineSpeed = 0;*/ } + // Constructor with some initial data public Engine(int id, int direction, int speed){ if (engines == null) { engines = new ArrayList<Engine>(); } udpProtocol = new MaerklinProtocol(); - listener = new UDPListener(); engines.add(this); try { setEngineID(id); @@ -46,10 +53,12 @@ public class Engine { } } + // Sets the Maerklin-ID public void setEngineID(int newID){ engineID = newID; } + // Sets the engine-direction and sends the respective command to the control-station; a 3 as the direction-parameter causes a switch of the direction public void setEngineDirection(int newEngineDirection) throws Exception { if (newEngineDirection == 1 || newEngineDirection == 2){ engineDirection = newEngineDirection; @@ -67,9 +76,10 @@ public class Engine { throw e; } udpProtocol.changeEngineDirection(engineID, engineDirection); - listener.listen(); } + + // Sets the engine-speed and sends the respective command to the control-station public void setEngineSpeed(int newEngineSpeed) throws Exception { if (newEngineSpeed <= 1000 && newEngineSpeed >= 0){ engineSpeed = newEngineSpeed; @@ -81,24 +91,29 @@ public class Engine { udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Increases the engine-speed by 50 and calls changeEngineSpeed with the new speed-value public void increaseEngineSpeed() throws Exception { engineSpeed = engineSpeed+50; udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Decreases the engine-speed by 50 and calls changeEngineSpeed with the new speed-value public void decreaseEngineSpeed() throws Exception{ engineSpeed = engineSpeed-50; udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Returns the Maerklin-ID public int getEngineID(){ return engineID; } + // Returns the engine-direction public int getEngineDirection(){ return engineDirection; } + // Returns the engine-speed public int getEngineSpeed(){ return engineSpeed; } diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinProtocol.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinProtocol.java index 30a97706453531fa3102adddf8cbe7daf618c1c4..8c98b30c6e5703e51723db06748401a2471891ac 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinProtocol.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinProtocol.java @@ -2,15 +2,25 @@ package server; import java.net.*; +/* + * Diese Klasse implementiert das Maerklin-Protokoll zur Kommunikation + * mit der Maerklin-CS2-Steuerung. Die Datagramme werden mittels UDP + * gesendet; fuer jedes Datagram wird ein neuer Sockel geoeffnet. + */ public class MaerklinProtocol { + // Setting the input-port of the maerklin-control-station private final static int port = 15731; + + // IP of the maerklin-control-station private final static String ip ="192.168.74.118"; + // Constructor public MaerklinProtocol(){ //Nothing to do... } + // Tries to open a UDP-socket and send the datagram to the maerklin-control-station private void writeDatagram(String ip, int port, byte[] data){ //System.out.printf("\nGesendet: " + DatatypeConverter.printHexBinary(data)); try(DatagramSocket socket = new DatagramSocket()){ @@ -24,16 +34,19 @@ public class MaerklinProtocol { } } + // Resets the emergency-stop (re-enables power) public void systemGo(){ byte[] out = {(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x78, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00}; this.writeDatagram(ip, port, out); } + // Emergency-stop (cuts the power) public void systemStop(){ byte[] out = {(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x78, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; this.writeDatagram(ip, port, out); } + // Set engine-speed of the given engine to the given value public void changeEngineSpeed(int mfxAddress, int newEngineSpeed){ byte[] engineSpeed = {(byte) Math.floor(newEngineSpeed/256), (byte) Math.floor(newEngineSpeed%256)}; byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; @@ -41,6 +54,7 @@ public class MaerklinProtocol { this.writeDatagram(ip, port, out); } + // Set engine-direction of the given engine to the given value public void changeEngineDirection(int mfxAddress, int newEngineDirection){ byte engineDirection = (byte) Math.floor(newEngineDirection%256); byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; @@ -48,6 +62,7 @@ public class MaerklinProtocol { this.writeDatagram(ip, port, out); } + // Set the switch-direction of the given switch to the given value or move the turntable clockwise or counterclockwise public void changeSwitchDirection(int mfxAddress, int newSwitchDirection){ byte switchDirection = (byte) Math.floor(newSwitchDirection%256); byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServer.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServer.java index c2774bd217d4ce717b1fc7c705dc880e43323880..a979753e47c68b219da1ad8c9c60a3679098b755 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServer.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServer.java @@ -10,23 +10,37 @@ import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.xml.bind.DatatypeConverter; +//import javax.xml.bind.DatatypeConverter; import common.Properties; +/* + * MaerklinServer stellt die Hauptklasse des Servers dar. Alle verwendeten + * Objekte und Services (bspw. Executer-Services) werden initialisiert und + * der Server-Thread wird gestartet. Weiterhin implementiert diese Klasse + * das Maerklin-Protokoll und dient damit fuer alle anderen abgeleiteten oder + * erzeugten Objekte als Referenz fuer die Kommunikation mit dem Server. + * Grundlegende Funktionen wie der Notaus werden direkt von dieser Klasse + * umgesetzt. + */ public class MaerklinServer{ // Declaration of objects + + // List to monitor the registered clients ArrayList<Socket> clients; + // Control-elements and thread to handle new clients private MaerklinProtocol protocol; private UDPListener overwatch; private ServerThread serverThread; + // Executor-services to handle the threads final ExecutorService SERVER_THREAD_EXECUTOR; final ExecutorService CLIENT_THREAD_EXECUTOR; final ExecutorService UTILITY_THREAD_EXECUTOR; + // Accessories private Engine ice; private Engine lok; private Engine reichsbahn; @@ -43,10 +57,11 @@ public class MaerklinServer{ private Switch switch15; private Switch turntable; + // Constructor public MaerklinServer(){ // Initializing - System.out.println("Initializing..."); + System.out.print("Maerklin Control Server\nLukas Friedrichsen, Philipp Stenkamp; (c) 2017\n\nInitializing...\n"); clients = new ArrayList<Socket>(); @@ -54,9 +69,9 @@ public class MaerklinServer{ overwatch = new UDPListener(); serverThread = new ServerThread(this); - SERVER_THREAD_EXECUTOR = Executors.newFixedThreadPool(2); - CLIENT_THREAD_EXECUTOR = Executors.newCachedThreadPool(); - UTILITY_THREAD_EXECUTOR = Executors.newCachedThreadPool(); + SERVER_THREAD_EXECUTOR = Executors.newFixedThreadPool(2); // Server- and listener-thread + CLIENT_THREAD_EXECUTOR = Executors.newCachedThreadPool(); // Client-threads + UTILITY_THREAD_EXECUTOR = Executors.newCachedThreadPool(); // Handle- and update-threads ice = new Engine(); lok = new Engine(); @@ -102,7 +117,7 @@ public class MaerklinServer{ } } - // Emergency stop + // Emergency stop; sets the speed of all engines to 0 out of safety reasons public void emergencyStop() throws Exception { protocol.systemStop(); ice.setEngineSpeed(0); @@ -110,12 +125,12 @@ public class MaerklinServer{ reichsbahn.setEngineSpeed(0); } - // Emergency stop + // Resets emergency stop public void start() throws Exception { protocol.systemGo(); } - // Initialize; setting ("safe") values + // Initialize; setting ("safe") values (engine speed to 0 and switch direction to straight) public void initialize() throws Exception { ice.setEngineDirection(1); ice.setEngineSpeed(0); @@ -148,11 +163,10 @@ public class MaerklinServer{ SERVER_THREAD_EXECUTOR.submit(serverThread); } - // Starts the thread to broadcast the current status + // Starts a thread to broadcast the current status for every registered client public void statusUpdate(){ for (Socket clientSocket : clients) { try { - //System.out.println("Updating client: "+clientSocket.getRemoteSocketAddress()); UTILITY_THREAD_EXECUTOR.submit(new UpdateThread(clientSocket.getOutputStream())); } catch (IOException e) { @@ -163,10 +177,15 @@ public class MaerklinServer{ } -//Opens a server-port and establishes a new handling-thread for every client that connects to the server +/* + * Diese Klasse oeffnet einen Server-Port und erstellt einen neue Client-Thread-Instanz + * fuer jeden Client, der sich neu an dem Server anmeldet. + */ class ServerThread implements Runnable { // Declaration of objects + + // Instance of the server; serves to solve references private final MaerklinServer SERVER_INSTANCE; // Constructor @@ -174,6 +193,7 @@ class ServerThread implements Runnable { SERVER_INSTANCE = server; } + // Waits for clients to connect and submits a new client-thread afterwards @Override public void run() { try(ServerSocket serverSocket = new ServerSocket(Properties.PORT)){ @@ -190,6 +210,7 @@ class ServerThread implements Runnable { run(); } finally { + // Properly shuts down all running threads if the server is closed SERVER_INSTANCE.SERVER_THREAD_EXECUTOR.shutdownNow(); SERVER_INSTANCE.CLIENT_THREAD_EXECUTOR.shutdownNow(); SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.shutdownNow(); @@ -198,13 +219,23 @@ class ServerThread implements Runnable { } -// Handles the communication with the clients in the background +/* + * Die Klasse ClientThread verwaltet die Kommunikation mit dem Client + * im Hintergrund und oeffnet bei eingehenden Befehlen einen neuen Handling-Thread. + */ class ClientThread implements Runnable { // Declaration of objects + + // Instance of the server; serves to solve references + private final MaerklinServer SERVER_INSTANCE; + + // Instance of the client-socket; serves to solve references private final Socket CLIENT; + + // Wrapper for the client-sockets' input-stream to buffer the incoming data private final BufferedInputStream IN_STREAM; - private final MaerklinServer SERVER_INSTANCE; + byte[] data; // Constructor @@ -216,40 +247,42 @@ class ClientThread implements Runnable { data = new byte[Properties.IN_BUFFER_SIZE]; } + // Checks if the client sends a session-abort-message passes client-requests to a handle-thread @Override public void run() { int buffer; while (true) { try { if (IN_STREAM.available() != 0) { - buffer = IN_STREAM.read(); - if ((byte) buffer == (byte) Properties.SESSION_ABORT) { + buffer = IN_STREAM.read(); // Reads one byte from the input-stream + if ((byte) buffer == (byte) Properties.SESSION_ABORT) { // Checks for a session-abort signal and potentially severs the connection potentially SERVER_INSTANCE.clients.remove(CLIENT); CLIENT.close(); System.out.println("Conenction to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); return; } - else if ((byte) buffer == (byte) Properties.SEPERATOR) { - for (int i = 0; i < Properties.IN_BUFFER_SIZE; i++) { + else if ((byte) buffer == (byte) Properties.SEPERATOR) { // Checks for the beginning of a new datagram (all datagrams begin with a seperator-byte to mark their start) + for (int i = 0; i < Properties.IN_BUFFER_SIZE; i++) { // Tries to read the whole datagram buffer = IN_STREAM.read(); - if ((byte) buffer == (byte) Properties.SESSION_ABORT) { + if ((byte) buffer == (byte) Properties.SESSION_ABORT) { // Again checks for a session-abort signal from the client SERVER_INSTANCE.clients.remove(CLIENT); CLIENT.close(); System.out.println("Connection to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); return; } else { - data[i] = (byte) (buffer%(1<<8)); + data[i] = (byte) (buffer%(1<<8)); // Typecast to byte through bitshift } } - SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.submit(new HandleThread(SERVER_INSTANCE, data)); + SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.submit(new HandleThread(SERVER_INSTANCE, data)); // Submits a new handle-thread for the datagram } } else { - Thread.sleep(10); + Thread.sleep(10); // Frees the resources until the next check } } catch (Exception e) { + // Unregister the client if an error occurers System.out.println("An error occured while reading the clients input-stream!"); SERVER_INSTANCE.clients.remove(CLIENT); System.out.println("Conenction to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); @@ -260,11 +293,19 @@ class ClientThread implements Runnable { } -// Processes the incoming datagram and initializes appropriate actions +/* + * HandleThread verarbeitet Anfragen von den Clients und initiiert entsprechende Aktionen. + * Format des Datagrams: 1.+2. Byte: Maerklin-ID + * 3. Byte: Befehl + * 4.+5. Byte: potentielle Parameter + */ class HandleThread implements Runnable { - // Declaration of objects + // Declaration of objects// Declaration of objects + + // Instance of the server; serves to solve references private final MaerklinServer SERVER_INSTANCE; + byte[] data; // Constructor @@ -273,23 +314,24 @@ class HandleThread implements Runnable { data = incomingData; } + // Reads the command and initiates the according action @Override public void run() { try { - switch (data[2]) { - case Properties.SYSTEM_STOP: + switch (data[2]) { // Compare the command-byte to the values set in Properties + case Properties.SYSTEM_STOP: // Emergency-stop (cuts the power) //System.out.println("System stop"); SERVER_INSTANCE.emergencyStop(); break; - case Properties.SYSTEM_GO: + case Properties.SYSTEM_GO: // Resets the emergency-stop (re-enables power) //System.out.println("System go"); SERVER_INSTANCE.start(); break; - case Properties.GET_STATUS: + case Properties.GET_STATUS: // Status-update //System.out.println("Status update"); SERVER_INSTANCE.statusUpdate(); break; - case Properties.ENGINE_SET_SPEED: + case Properties.ENGINE_SET_SPEED: // Set engine-speed to the given value for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set engine speed"); @@ -298,7 +340,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_INCREASE_SPEED: + case Properties.ENGINE_INCREASE_SPEED: // Increase the engine-speed by 50 for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Increase engine speed"); @@ -307,7 +349,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_DECREASE_SPEED: + case Properties.ENGINE_DECREASE_SPEED: // Decrease the engine-speed by 50 for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Decrease engine speed"); @@ -316,7 +358,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_SET_DIRECTION: + case Properties.ENGINE_SET_DIRECTION: // Set engine-direction to the given value for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set engine direction"); @@ -325,7 +367,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_SWITCH_DIRECTION: + case Properties.ENGINE_SWITCH_DIRECTION: // Switch the engine-direction for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Switch engine direction"); @@ -334,7 +376,7 @@ class HandleThread implements Runnable { } } break; - case Properties.SWITCH_SET_DIRECTION: + case Properties.SWITCH_SET_DIRECTION: // Set the switch-direction to the given value or move the turntable clockwise or counterclockwise for (Switch sw : Switch.switches) { if (sw.getSwitchID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set switch direction"); @@ -343,7 +385,7 @@ class HandleThread implements Runnable { } } break; - case Properties.SWITCH_SWITCH_DIRECTION: + case Properties.SWITCH_SWITCH_DIRECTION: // Switch the switch-direction for (Switch sw : Switch.switches) { if (sw.getSwitchID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Switch switch direction"); @@ -352,7 +394,7 @@ class HandleThread implements Runnable { } } break; - default: + default: // Command doesn't match any of the cases System.out.println("No viable command!\n"); } } @@ -363,11 +405,19 @@ class HandleThread implements Runnable { } -//Broadcasts the current status +/* + * Diese Klasse konvertiert bei Aufruf den aktuellen Status der (serverseitigen) + * Modelle in Byte-Format und wandelt diese unter Hinzufuegen von Start- End- und + * Trenn-Bytes in ein Datagram um, dass an den Client gesendet wird. + */ class UpdateThread implements Runnable { // Declaration of objects + + // Output-stream of the client handled by the thread private final OutputStream OUT_STREAM; + + // Buffer for the datagram private byte[] outgoingData; // Constructor @@ -376,30 +426,30 @@ class UpdateThread implements Runnable { // Setting the broadcast-port OUT_STREAM = out; - // Initializing + // Initializing the buffer-array to the size of the datagram outgoingData = new byte[Properties.OUT_BUFFER_SIZE]; } - // Reads every registered objects data into outgoingData and broadcasts the status + // Reads every registered objects data into outgoingData and sends the status @Override public void run() { int position = 0; try { - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Start-bytes outgoingData[position++] = Properties.SEPERATOR; for (Engine train : Engine.engines) { if (position >= Properties.OUT_BUFFER_SIZE-9) { Exception e = new Exception("Overload of registered elements! Update-buffer too small!"); throw e; } - outgoingData[position++] = (byte) ((train.getEngineID()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((train.getEngineID()/(1<<8))%(1<<8)); // Maerklin-ID outgoingData[position++] = (byte) ((train.getEngineID()%(1<<8))); - outgoingData[position++] = (byte) ((train.getEngineSpeed()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((train.getEngineSpeed()/(1<<8))%(1<<8)); // Speed outgoingData[position++] = (byte) ((train.getEngineSpeed()%(1<<8))); - outgoingData[position++] = (byte) ((train.getEngineDirection()%(1<<24))); - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = (byte) ((train.getEngineDirection()%(1<<24))); // Direction + outgoingData[position++] = Properties.SEPERATOR; // Separator-byte between the engines } - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Separator-bytes between the engines and the switches outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; for (Switch sw : Switch.switches) { @@ -407,14 +457,14 @@ class UpdateThread implements Runnable { Exception e = new Exception("Overload of registered elements! Update-buffer too small!"); throw e; } - outgoingData[position++] = (byte) ((sw.getSwitchID()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((sw.getSwitchID()/(1<<8))%(1<<8)); // Maerklin-ID outgoingData[position++] = (byte) ((sw.getSwitchID()%(1<<8))); + outgoingData[position++] = (byte) 0x00; // Inserted two null-bytes to match the engines-format outgoingData[position++] = (byte) 0x00; - outgoingData[position++] = (byte) 0x00; - outgoingData[position++] = (byte) ((sw.getSwitchDirection()%(1<<24))); - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = (byte) ((sw.getSwitchDirection()%(1<<24))); // Direction + outgoingData[position++] = Properties.SEPERATOR; // Separator-byte between the switches } - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Stop-bytes outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; @@ -425,7 +475,7 @@ class UpdateThread implements Runnable { } //System.out.printf("\nSende: " + DatatypeConverter.printHexBinary(outgoingData)); try { - OUT_STREAM.write(outgoingData, 0, Properties.OUT_BUFFER_SIZE); + OUT_STREAM.write(outgoingData, 0, Properties.OUT_BUFFER_SIZE); // Send the datagram to the client } catch (Exception e) { System.out.println("Communication-error whilst status-update!"); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServerApplication.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServerApplication.java index 7bbd8c583b8818b0232a810ef61c347e928926ab..8d8d5379200a20f573e9212a23c02b33d57cf17a 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServerApplication.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/MaerklinServerApplication.java @@ -1,5 +1,11 @@ package server; + +/* + * MaerklinServerApplication startet den Server sowie den Listener, der + * die serverseitigen Modelle aktualisiert. Weiterhin initialisiert die Klasse + * zyklisch ein synchrones Status-Update fuer alle registrierten Clients. + */ public class MaerklinServerApplication { // Defining a threshold value for when the status-update is to be executed (in milliseconds) @@ -19,6 +25,7 @@ public class MaerklinServerApplication { // Starting the client-handling server.listen(); + // Periodically checks if the update-threshold has been exceeded and potentially initializes the status update through statusUpdate while (true){ if (System.currentTimeMillis()-timestamp >= updateThreshold) { server.statusUpdate(); diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/Switch.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/Switch.java index ea03fbf954828e18a33f0b2dd72dabe69f806f16..418adf90c51d8067562fb47617e72bbdc94c6230 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/Switch.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/Switch.java @@ -2,16 +2,23 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell fuer eine Weiche auf Serverseite dar. + * Weiterhin lassen sich über eine lokale Instanz des Maerklin-Protokolls + * Befehle an die Maerklin-C2-Steuerung senden. + */ public class Switch { + // Static array containing all created instances of the object static ArrayList<Switch> switches; private int switchID; int switchDirection; + // Private locale instance of the maerklin-protocol to send commands to the control-station private MaerklinProtocol udpProtocol; - private UDPListener listener; + // Constructor public Switch(){ this(0, 1); @@ -26,12 +33,12 @@ public class Switch { switchDirection = 1;*/ } + // Constructor with some initial data public Switch(int id, int direction){ if (switches == null) { switches = new ArrayList<Switch>(); } udpProtocol = new MaerklinProtocol(); - listener = new UDPListener(); switches.add(this); try { setSwitchID(id); @@ -42,10 +49,12 @@ public class Switch { } } + // Sets the Maerklin-ID public void setSwitchID(int newSwitchID){ switchID = newSwitchID; } + // Sets the switch-direction and sends the respective command to the control-station public void setSwitchDirection(int newSwitchDirection) throws Exception { if (newSwitchDirection == 0x01 || newSwitchDirection == 0x00){ switchDirection = newSwitchDirection; @@ -57,6 +66,7 @@ public class Switch { udpProtocol.changeSwitchDirection(switchID, switchDirection); } + // Changes the switch-direction and calls setSwitchDirection with the new vdirection-alue public void changeSwitchDirection() throws Exception { if (switchDirection == 0x00){ setSwitchDirection(0x01); @@ -66,10 +76,12 @@ public class Switch { } } + // Returns the Maerklin-ID public int getSwitchID(){ return switchID; } + // Returns the switch-direction public int getSwitchDirection(){ return switchDirection; } diff --git a/Praktikum/VINF_MaerklinControl/build/build/src/server/UDPListener.java b/Praktikum/VINF_MaerklinControl/build/build/src/server/UDPListener.java index e2d66c56b28c07eef70e15d8cbf8849c352cc792..d5c3be03c49d32985c58400b0c7ecc848c1c2ea4 100644 --- a/Praktikum/VINF_MaerklinControl/build/build/src/server/UDPListener.java +++ b/Praktikum/VINF_MaerklinControl/build/build/src/server/UDPListener.java @@ -1,24 +1,31 @@ package server; - import java.net.DatagramPacket; import java.net.DatagramSocket; import javax.xml.bind.DatatypeConverter; +/* + * Der UDPListener ist in der Lage auf den Ausgabe-Port der Maerklin-C2-Steuerung + * zu lauschen, ausgehende Datagramme zu verarbeiten und die serverseitigen Modelle + * zu updaten. Es ist sowohl eine "manuelle" Version wie auch eine Automatik (per + * Runnable) implementiert. + */ public class UDPListener implements Runnable{ // Defining the size of the datagrams received from the Maerklin-control-station public final static int MAERKLIN_DATAGRAM_SIZE = 13; - // Listener-port + // Setting the output-port of the maerklin-control-station private final static int port = 15730; private byte[] in; + // Constructor public UDPListener(){ in = new byte[MAERKLIN_DATAGRAM_SIZE]; } + // Listens for answers from the control-station and writes it to the array in public void listen(){ try(DatagramSocket socket = new DatagramSocket(port)){ socket.setReuseAddress(true); @@ -31,30 +38,38 @@ public class UDPListener implements Runnable{ } } + // Returns the last received datagram public byte[] getData(){ return in; } + // Decodes the engine-speed from the datagram public int getEngineSpeed(){ return ((in[9]&0xFF)*(1<<8)+(in[10]&0xFF)); } + + // Decodes the engine-direction from the datagram public int getEngineDirection(){ return (in[9]&0xFF); } + // Decodes the switch-direction from the datagram public int getSwitchDirection(){ return (in[9]&0xFF); } + // Decodes the Maerklin-ID from the datagram public int getAddress(){ return ((in[7]&0xFF)*(1<<8)+(in[8]&0xFF)); } + // Decodes the command from the datagram public int getCommand(){ return ((in[1]&0xFF)&0xFE); } + // Checks if the last received datagram is an answer from the control-station public boolean isAnswer(){ if ((in[1]&0x01) == 1){ return true; @@ -64,6 +79,7 @@ public class UDPListener implements Runnable{ } } + // Prints the last received datagram in human-readable form public void printData(byte[] data){ try{ byte[] kennung = new byte[4]; @@ -79,6 +95,7 @@ public class UDPListener implements Runnable{ } } + // Debugging-functionality public static void main(String args[]){ UDPListener testListener = new UDPListener(); while (true){ @@ -87,40 +104,41 @@ public class UDPListener implements Runnable{ } } + // Automatizes the listener-functionalit and updates the models' values with the received data @Override public void run() { //System.out.println("UDPListener start!"); - try(DatagramSocket socket = new DatagramSocket(port)){ + try(DatagramSocket socket = new DatagramSocket(port)){ // Tries to open a socket set to the output-port of the control-station socket.setReuseAddress(true); while (true){ DatagramPacket incomingData = new DatagramPacket(in, in.length); socket.receive(incomingData); in = incomingData.getData(); - if (isAnswer()){ - if (getCommand() == 0x08){ + if (isAnswer()){ // Checks if the datagram is an answer + if (getCommand() == 0x08){ // Engine-speed 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; } } } - else if (getCommand() == 0x0A){ + else if (getCommand() == 0x0A){ // Engine-direction for (Engine train : Engine.engines){ 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; } } } - else if (getCommand() == 0x16){ + else if (getCommand() == 0x16){ // Switch-direction 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; } } diff --git a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.html b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.html index 86056ec22a15614e3781e9c34b8feacbd913234e..50c5d7c97a0bd4e421259990d8751a5ebab5df9d 100644 --- a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.html +++ b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.html @@ -35,7 +35,7 @@ </script> </head><body> -<h2>Test page for <b>M�rklin Control</b></h2> +<h2>Test page for <b>Maerklin Control</b></h2> <b>Webstart:</b> <a href='VINF_MaerklinControl.jnlp' onclick="return launchApplication('VINF_MaerklinControl.jnlp');">click to launch this app as webstart</a><br><hr><br> <!-- Applet will be inserted here --> diff --git a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jar b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jar index df341d1d5d2c2d6a752541e6866a6a224d47f1d0..412d97f66d7d2f4ecc20e5d05be5341b3c0ea466 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jar and b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jar differ diff --git a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jnlp b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jnlp index 2b4c625462731d61ec9bff0888d3abecc554cdd1..6cc46b4a1e5759c35a4c4b5b311b6ab99509b302 100644 --- a/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jnlp +++ b/Praktikum/VINF_MaerklinControl/build/deploy/VINF_MaerklinControl.jnlp @@ -3,14 +3,14 @@ <information> <title>VINF_MaerklinControl</title> <vendor>lf.ps</vendor> - <description>M�rklin Control</description> + <description>Maerklin Control</description> <offline-allowed/> </information> <resources> <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se"/> - <jar href="VINF_MaerklinControl.jar" size="39342" download="eager" /> + <jar href="VINF_MaerklinControl.jar" size="39675" download="eager" /> <jar href="libs\controlsfx-8.40.12.jar" size="996389" download="eager" /> </resources> - <jfx:javafx-desc width="0" height="0" main-class="gui.MainApp" name="M�rklin Control" /> + <jfx:javafx-desc width="0" height="0" main-class="gui.MainApp" name="Maerklin Control" /> <update check="background"/> </jnlp> diff --git "a/Praktikum/VINF_MaerklinControl/build/deploy/bundles/M\303\244rklin Control-0.9.exe" "b/Praktikum/VINF_MaerklinControl/build/deploy/bundles/M\303\244rklin Control-0.9.exe" deleted file mode 100644 index e539f1e1ed6e882be7ae930c20c4b4711e7b1b8e..0000000000000000000000000000000000000000 Binary files "a/Praktikum/VINF_MaerklinControl/build/deploy/bundles/M\303\244rklin Control-0.9.exe" and /dev/null differ diff --git a/Praktikum/VINF_MaerklinControl/build/dist/VINF_MaerklinControl.jar b/Praktikum/VINF_MaerklinControl/build/dist/VINF_MaerklinControl.jar index eef9eff31d7e45750a5e98aa91e50f3936ac93ab..412d97f66d7d2f4ecc20e5d05be5341b3c0ea466 100644 Binary files a/Praktikum/VINF_MaerklinControl/build/dist/VINF_MaerklinControl.jar and b/Praktikum/VINF_MaerklinControl/build/dist/VINF_MaerklinControl.jar differ diff --git a/Praktikum/VINF_MaerklinControl/res/BR 86.png b/Praktikum/VINF_MaerklinControl/build/dist/res/BR 86.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/BR 86.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/BR 86.png diff --git a/Praktikum/VINF_MaerklinControl/res/E 50.png b/Praktikum/VINF_MaerklinControl/build/dist/res/E 50.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/E 50.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/E 50.png diff --git a/Praktikum/VINF_MaerklinControl/res/ICE 1.png b/Praktikum/VINF_MaerklinControl/build/dist/res/ICE 1.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/ICE 1.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/ICE 1.png diff --git a/Praktikum/VINF_MaerklinControl/res/MagIcon_00_00_a.png b/Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_00_a.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/MagIcon_00_00_a.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_00_a.png diff --git a/Praktikum/VINF_MaerklinControl/res/MagIcon_00_00_i.png b/Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_00_i.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/MagIcon_00_00_i.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_00_i.png diff --git a/Praktikum/VINF_MaerklinControl/res/MagIcon_00_01_a.png b/Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_01_a.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/MagIcon_00_01_a.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_01_a.png diff --git a/Praktikum/VINF_MaerklinControl/res/MagIcon_00_01_i.png b/Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_01_i.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/MagIcon_00_01_i.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/MagIcon_00_01_i.png diff --git a/Praktikum/VINF_MaerklinControl/res/bo.png b/Praktikum/VINF_MaerklinControl/build/dist/res/bo.png similarity index 100% rename from Praktikum/VINF_MaerklinControl/res/bo.png rename to Praktikum/VINF_MaerklinControl/build/dist/res/bo.png diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/common/Properties.java b/Praktikum/VINF_MaerklinControl/build/project/src/common/Properties.java index 144b96d85315ffd068b2e2bba3fc0cd2b7b83e47..6c684e7aa939d8f95877d88b1c6697c26320dc83 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/common/Properties.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/common/Properties.java @@ -1,5 +1,10 @@ package common; +/* + * In dieser Klasse werden alle klassenuebergreifend verwendeten Werte einmalig deklariert, + * so dass potentielle Aenderungen lediglich einmalig an dieser Stelle durchgefuehrt werden + * muessen und Code-Vervielfachung vermieden wird (Einhaltung des DRY-Prinzips). + */ public class Properties { // Defining server-ip and port diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/MainApp.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/MainApp.java index 992fdaa14ed01b5a760bdea09dded681eb701b61..309010fda8e84b703a0ea47626ef9e95393a1f56 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/MainApp.java +++ b/Praktikum/VINF_MaerklinControl/build/project/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 saemtliche "nach aussen" (zum Server) gerichteten Funktionalitaeten 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 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 tab pane of 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)"); @@ -376,65 +394,83 @@ public class MainApp extends Application { e.printStackTrace(); } finally { - try { - byte[] datagram = {Properties.SEPERATOR, (byte) 0x00, (byte) 0x00, (byte) Properties.SESSION_ABORT, (byte) 0x00, (byte) 0x00}; - out.write(datagram); - client.close(); - connectionEstablished = false; - running = false; - settingsController.updateSettingsStatus(); - - } catch (Exception e) { - setStatus("Error at: establishConnection (shutting down connection)"); - e.printStackTrace(); - } + if (connectionEstablished) { + 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(); + connectionEstablished = false; + running = false; + Platform.runLater(new Runnable() { + + // Sets the connection-esttablishing-buttons text to "Verbindung herstellen" + @Override + public void run() { + CONTROLLER_INSTANCE.settingsController.updateSettingsStatus(); + } + + }); + } catch (Exception e) { + setStatus("Error at: establishConnection (shutting down connection)"); + e.printStackTrace(); + } + } } } } } - + + /* + * 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 +483,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/build/project/src/gui/model/Engine.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Engine.java index 75b59b24d1c4d633f9426b0e47d41eabe22a504f..617e48196c39d195369a453ba46ff64d384fe4dd 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Engine.java +++ b/Praktikum/VINF_MaerklinControl/build/project/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 +/* + * Modell-Klasse fuer einen Zug. */ 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/build/project/src/gui/model/Settings.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Settings.java index a6a737f271c650347fcd7b3d9af0e9db22708841..f2db8d0165abfe4f6aa02309cb4772c8e66093bc 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Settings.java +++ b/Praktikum/VINF_MaerklinControl/build/project/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 +/* + * Modell-Klasse fuer die Einstellungen der App. */ 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/build/project/src/gui/model/Switch.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Switch.java index 6058fb846c77ab7d066974baaa04d7453614a37d..1db5a7203bafa6a1251405d865f53eda64304df5 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/model/Switch.java +++ b/Praktikum/VINF_MaerklinControl/build/project/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 +/* + * Modell-Klasse fuer eine Weiche. */ 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/build/project/src/gui/view/EngineController.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/EngineController.java index 98eded74290c84c51acddd125d109d3ae4572037..fa17d126b00401d2f88b0513380bdbe224d63d57 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/EngineController.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/EngineController.java @@ -10,6 +10,10 @@ import javafx.scene.image.ImageView; import gui.MainApp; import gui.model.Engine; +/* + * Die Klasse EngineController stellt den Controller f�r die Bedienelemente zur Steuerung der Lokomotiven bereit. + */ + public class EngineController { @FXML private ImageView engineImage; @@ -38,9 +42,11 @@ public class EngineController { @FXML private void initialize() { engineSpeedSlider.setValue(0); - engineFwdButton.setSelected(true); // initializes the forward Button as pressed + engineFwdButton.setSelected(true); // initializes the forward Button as pressed - default direction + + // updates the local reference of the selected engine to the Engine selected in the ChoiceBox engineChoiceBox.getSelectionModel().selectedItemProperty().addListener( - (observable, oldValue, newValue) -> setSelectedEngine(newValue)); + (observable, oldValue, newValue) -> setSelectedEngine(newValue)); engineSpeedSlider.valueProperty().addListener(new ChangeListener<Number>() { public void changed(ObservableValue<? extends Number> ov, @@ -64,13 +70,14 @@ public class EngineController { engineImage.setImage(mainApp.getEngines().get(0).getImg().get()); } + // Is called if the state of the direction buttons changes and updates the engine model accordingly public void handleEngineDirection(){ eng.setDirection(engineFwdButton.isSelected()); - eng.setSpeed(0); mainApp.setEngineDirection(eng); updateEngineStatus(); } + // updates the GUI to the values of the currently selected engine public void updateEngineStatus(){ engineImage.setImage(eng.getImg().get()); engineSpeedSlider.setValue(eng.getSpeed().get()/10); @@ -87,6 +94,12 @@ public class EngineController { mainApp.setStatus(status.toString()); } + + /** + * updates the local reference of the selected engine to the given one + * + * @param Engine + */ public void setSelectedEngine(Engine eng){ this.eng = eng; mainApp.setStatus(eng.toString() + " ausgew�hlt"); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/RootLayoutController.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/RootLayoutController.java index f47880d0f782ae54864ad646cee46dc944ef21e3..3432ca83460de551981d3302cc20b603038708d1 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/RootLayoutController.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/RootLayoutController.java @@ -7,6 +7,10 @@ import org.controlsfx.control.StatusBar; import gui.MainApp; +/* + * Die Klasse RootLayoutController stellt den Controller f�r die Bedienelemente des Basislayouts bereit. + */ + public class RootLayoutController { @FXML private TabPane rootTabPane; @@ -17,18 +21,29 @@ public class RootLayoutController { // Reference to the main application. private MainApp mainApp; + /** + * Is called by the main application to give a reference back to itself. + * + * @param mainApp + */ public void setMainApp(MainApp mainApp) { this.mainApp = mainApp; } + /** + * get the tabs from the main app and load them into the local tabPane + * can't be done in the constructor, since mainApp has to be set first + */ public void loadTabs(){ - rootTabPane.getTabs().addAll(mainApp.getTabs()); //can't be in the constructor + rootTabPane.getTabs().addAll(mainApp.getTabs()); } + // sets the StatusBar text public void setStatus(String status){ statusBar.setText(status); } + // passes the Emergency Stop command to the mainApp public void emergencyStopHandler(){ mainApp.emergencyStopHandler(); } diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SettingsController.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SettingsController.java index 85161fac87685a5a2c937269a3abacca54bcd7d9..590111f0ea7dd30ca94381c950513d9bc57e6332 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SettingsController.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SettingsController.java @@ -39,7 +39,11 @@ public class SettingsController { public void setMainApp(MainApp mainApp) { this.mainApp = mainApp; } - + /** + * is called by an OnClick event caused by the Connect Button. + * updates the Settings model to the currently selected IP Adress and + * calls the establishConnection method of the mainApp. + */ @FXML private void connectHandler(){ InetSocketAddress address = new InetSocketAddress(serverIP.getText(), settings.getSocketAddress().get().getPort()); @@ -47,7 +51,8 @@ public class SettingsController { updateSettingsStatus(); mainApp.establishConnetion(); } - + + // updates the GUI public void updateSettingsStatus(){ InetSocketAddress addr = settings.getSocketAddress().get(); StringBuilder status = new StringBuilder("Verbindung herstellen mit "); @@ -62,6 +67,11 @@ public class SettingsController { } } + /** + * updates the local reference of the selected settings model to the given one + * + * @param Settings + */ public void setSettings(Settings settings) { this.settings = settings; } diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchControl.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchControl.java index e14c05fbe2f93d2884930cc518fcc5cee7d41e00..4d4154547a677ca8e6577d57bdaffc74dc246dbb 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchControl.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchControl.java @@ -15,6 +15,11 @@ import org.controlsfx.control.ToggleSwitch; import gui.MainApp; import gui.model.Switch; +/* + * Die Klasse SwitchControl stellt ein Sonderbedienelement dar. Sie l�dt die Bedienelemente f�r jeweils eine Weiche + * und fungiert gleichzeitig als deren Controller. Ihr wird im Konstruktor das zugeh�rige Weichen-Objekt �bergeben. + */ + public class SwitchControl extends HBox{ @FXML private Label nameLabel; @@ -37,6 +42,8 @@ public class SwitchControl extends HBox{ /** * The constructor. * The constructor is called before the initialize() method. + * Unique in that it does also load the Switch.fxml, which is usally done in an + * higher class, not in the controller itself. */ public SwitchControl(Switch sw) { this.sw = sw; @@ -80,7 +87,7 @@ public class SwitchControl extends HBox{ this.mainApp = mainApp; } - + // updates the GUI elements to reflect the current state of the switch public void updateSwitchStatus(){ stateToggleSwitch.setSelected(sw.getState().get()); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchListController.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchListController.java index de5eebb19ff70962904f45575d52bb181f31be79..31bef5d8251a682d5726f5f6c7d54ddbfe0c3c6e 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchListController.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/SwitchListController.java @@ -40,7 +40,11 @@ public class SwitchListController { this.mainApp = mainApp; } - + /** + * this is where the magic happens! + * kidding aside, this method creates and assigns a new SwitchControl instance to every switch defined in the mainApp. + * it then adds these SwitchControls to the VBox provided in the SwitchList.fxml + */ public void setSwitchControls(){ ObservableList<Switch> switches = mainApp.getSwitches(); ObservableList<SwitchControl> switchControls = FXCollections.observableArrayList(); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/TurntableController.java b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/TurntableController.java index 0c8c6c16ae5ea763adf615195f6e70b1a7ad1cb2..324001bbfb237adbbbb462981dab13a6536e47f7 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/TurntableController.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/gui/view/TurntableController.java @@ -5,6 +5,10 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; +/* + * Die Klasse TurntableController stellt den Controller f�r die Bedienelemente zur Steuerung der Drehscheibe bereit. + */ + public class TurntableController { @FXML @@ -34,11 +38,13 @@ public class TurntableController { } + // Is called by an onClick event on the left button and calls the according function in the main app public void handleTurnCCW (){ mainApp.turntableTurnCCW(); mainApp.setStatus("Letztes Kommando: Drehteller links drehen"); } + // Is called by an onClick event on the right button and calls the according function in the main app public void handleTurnCW (){ mainApp.turntableTurnCW(); mainApp.setStatus("Letztes Kommando: Drehteller rechts drehen"); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/Engine.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/Engine.java index 60f39ceeff56b80bc26de0989f3df7adabf4a71e..090944a012f551d536a402cf7c256e5a7502401e 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/Engine.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/Engine.java @@ -3,17 +3,24 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell fuer einen Zug auf Serverseite dar. + * Weiterhin lassen sich ueber eine lokale Instanz des Maerklin-Protokolls + * Befehle an die Maerklin-C2-Steuerung senden. + */ public class Engine { + // Static array containing all created instances of the object static ArrayList<Engine> engines; private int engineID; int engineDirection; int engineSpeed; + // Private locale instance of the maerklin-protocol to send commands to the control-station private MaerklinProtocol udpProtocol; - private UDPListener listener; + // Constructor public Engine(){ this(0, 1, 0); @@ -29,12 +36,12 @@ public class Engine { engineSpeed = 0;*/ } + // Constructor with some initial data public Engine(int id, int direction, int speed){ if (engines == null) { engines = new ArrayList<Engine>(); } udpProtocol = new MaerklinProtocol(); - listener = new UDPListener(); engines.add(this); try { setEngineID(id); @@ -46,10 +53,12 @@ public class Engine { } } + // Sets the Maerklin-ID public void setEngineID(int newID){ engineID = newID; } + // Sets the engine-direction and sends the respective command to the control-station; a 3 as the direction-parameter causes a switch of the direction public void setEngineDirection(int newEngineDirection) throws Exception { if (newEngineDirection == 1 || newEngineDirection == 2){ engineDirection = newEngineDirection; @@ -67,9 +76,10 @@ public class Engine { throw e; } udpProtocol.changeEngineDirection(engineID, engineDirection); - listener.listen(); } + + // Sets the engine-speed and sends the respective command to the control-station public void setEngineSpeed(int newEngineSpeed) throws Exception { if (newEngineSpeed <= 1000 && newEngineSpeed >= 0){ engineSpeed = newEngineSpeed; @@ -81,24 +91,29 @@ public class Engine { udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Increases the engine-speed by 50 and calls changeEngineSpeed with the new speed-value public void increaseEngineSpeed() throws Exception { engineSpeed = engineSpeed+50; udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Decreases the engine-speed by 50 and calls changeEngineSpeed with the new speed-value public void decreaseEngineSpeed() throws Exception{ engineSpeed = engineSpeed-50; udpProtocol.changeEngineSpeed(engineID, engineSpeed); } + // Returns the Maerklin-ID public int getEngineID(){ return engineID; } + // Returns the engine-direction public int getEngineDirection(){ return engineDirection; } + // Returns the engine-speed public int getEngineSpeed(){ return engineSpeed; } diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinProtocol.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinProtocol.java index 30a97706453531fa3102adddf8cbe7daf618c1c4..8c98b30c6e5703e51723db06748401a2471891ac 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinProtocol.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinProtocol.java @@ -2,15 +2,25 @@ package server; import java.net.*; +/* + * Diese Klasse implementiert das Maerklin-Protokoll zur Kommunikation + * mit der Maerklin-CS2-Steuerung. Die Datagramme werden mittels UDP + * gesendet; fuer jedes Datagram wird ein neuer Sockel geoeffnet. + */ public class MaerklinProtocol { + // Setting the input-port of the maerklin-control-station private final static int port = 15731; + + // IP of the maerklin-control-station private final static String ip ="192.168.74.118"; + // Constructor public MaerklinProtocol(){ //Nothing to do... } + // Tries to open a UDP-socket and send the datagram to the maerklin-control-station private void writeDatagram(String ip, int port, byte[] data){ //System.out.printf("\nGesendet: " + DatatypeConverter.printHexBinary(data)); try(DatagramSocket socket = new DatagramSocket()){ @@ -24,16 +34,19 @@ public class MaerklinProtocol { } } + // Resets the emergency-stop (re-enables power) public void systemGo(){ byte[] out = {(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x78, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00}; this.writeDatagram(ip, port, out); } + // Emergency-stop (cuts the power) public void systemStop(){ byte[] out = {(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x78, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; this.writeDatagram(ip, port, out); } + // Set engine-speed of the given engine to the given value public void changeEngineSpeed(int mfxAddress, int newEngineSpeed){ byte[] engineSpeed = {(byte) Math.floor(newEngineSpeed/256), (byte) Math.floor(newEngineSpeed%256)}; byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; @@ -41,6 +54,7 @@ public class MaerklinProtocol { this.writeDatagram(ip, port, out); } + // Set engine-direction of the given engine to the given value public void changeEngineDirection(int mfxAddress, int newEngineDirection){ byte engineDirection = (byte) Math.floor(newEngineDirection%256); byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; @@ -48,6 +62,7 @@ public class MaerklinProtocol { this.writeDatagram(ip, port, out); } + // Set the switch-direction of the given switch to the given value or move the turntable clockwise or counterclockwise public void changeSwitchDirection(int mfxAddress, int newSwitchDirection){ byte switchDirection = (byte) Math.floor(newSwitchDirection%256); byte[] address = {(byte) Math.floor(mfxAddress/256), (byte) Math.floor(mfxAddress%256)}; diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServer.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServer.java index c2774bd217d4ce717b1fc7c705dc880e43323880..a979753e47c68b219da1ad8c9c60a3679098b755 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServer.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServer.java @@ -10,23 +10,37 @@ import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.xml.bind.DatatypeConverter; +//import javax.xml.bind.DatatypeConverter; import common.Properties; +/* + * MaerklinServer stellt die Hauptklasse des Servers dar. Alle verwendeten + * Objekte und Services (bspw. Executer-Services) werden initialisiert und + * der Server-Thread wird gestartet. Weiterhin implementiert diese Klasse + * das Maerklin-Protokoll und dient damit fuer alle anderen abgeleiteten oder + * erzeugten Objekte als Referenz fuer die Kommunikation mit dem Server. + * Grundlegende Funktionen wie der Notaus werden direkt von dieser Klasse + * umgesetzt. + */ public class MaerklinServer{ // Declaration of objects + + // List to monitor the registered clients ArrayList<Socket> clients; + // Control-elements and thread to handle new clients private MaerklinProtocol protocol; private UDPListener overwatch; private ServerThread serverThread; + // Executor-services to handle the threads final ExecutorService SERVER_THREAD_EXECUTOR; final ExecutorService CLIENT_THREAD_EXECUTOR; final ExecutorService UTILITY_THREAD_EXECUTOR; + // Accessories private Engine ice; private Engine lok; private Engine reichsbahn; @@ -43,10 +57,11 @@ public class MaerklinServer{ private Switch switch15; private Switch turntable; + // Constructor public MaerklinServer(){ // Initializing - System.out.println("Initializing..."); + System.out.print("Maerklin Control Server\nLukas Friedrichsen, Philipp Stenkamp; (c) 2017\n\nInitializing...\n"); clients = new ArrayList<Socket>(); @@ -54,9 +69,9 @@ public class MaerklinServer{ overwatch = new UDPListener(); serverThread = new ServerThread(this); - SERVER_THREAD_EXECUTOR = Executors.newFixedThreadPool(2); - CLIENT_THREAD_EXECUTOR = Executors.newCachedThreadPool(); - UTILITY_THREAD_EXECUTOR = Executors.newCachedThreadPool(); + SERVER_THREAD_EXECUTOR = Executors.newFixedThreadPool(2); // Server- and listener-thread + CLIENT_THREAD_EXECUTOR = Executors.newCachedThreadPool(); // Client-threads + UTILITY_THREAD_EXECUTOR = Executors.newCachedThreadPool(); // Handle- and update-threads ice = new Engine(); lok = new Engine(); @@ -102,7 +117,7 @@ public class MaerklinServer{ } } - // Emergency stop + // Emergency stop; sets the speed of all engines to 0 out of safety reasons public void emergencyStop() throws Exception { protocol.systemStop(); ice.setEngineSpeed(0); @@ -110,12 +125,12 @@ public class MaerklinServer{ reichsbahn.setEngineSpeed(0); } - // Emergency stop + // Resets emergency stop public void start() throws Exception { protocol.systemGo(); } - // Initialize; setting ("safe") values + // Initialize; setting ("safe") values (engine speed to 0 and switch direction to straight) public void initialize() throws Exception { ice.setEngineDirection(1); ice.setEngineSpeed(0); @@ -148,11 +163,10 @@ public class MaerklinServer{ SERVER_THREAD_EXECUTOR.submit(serverThread); } - // Starts the thread to broadcast the current status + // Starts a thread to broadcast the current status for every registered client public void statusUpdate(){ for (Socket clientSocket : clients) { try { - //System.out.println("Updating client: "+clientSocket.getRemoteSocketAddress()); UTILITY_THREAD_EXECUTOR.submit(new UpdateThread(clientSocket.getOutputStream())); } catch (IOException e) { @@ -163,10 +177,15 @@ public class MaerklinServer{ } -//Opens a server-port and establishes a new handling-thread for every client that connects to the server +/* + * Diese Klasse oeffnet einen Server-Port und erstellt einen neue Client-Thread-Instanz + * fuer jeden Client, der sich neu an dem Server anmeldet. + */ class ServerThread implements Runnable { // Declaration of objects + + // Instance of the server; serves to solve references private final MaerklinServer SERVER_INSTANCE; // Constructor @@ -174,6 +193,7 @@ class ServerThread implements Runnable { SERVER_INSTANCE = server; } + // Waits for clients to connect and submits a new client-thread afterwards @Override public void run() { try(ServerSocket serverSocket = new ServerSocket(Properties.PORT)){ @@ -190,6 +210,7 @@ class ServerThread implements Runnable { run(); } finally { + // Properly shuts down all running threads if the server is closed SERVER_INSTANCE.SERVER_THREAD_EXECUTOR.shutdownNow(); SERVER_INSTANCE.CLIENT_THREAD_EXECUTOR.shutdownNow(); SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.shutdownNow(); @@ -198,13 +219,23 @@ class ServerThread implements Runnable { } -// Handles the communication with the clients in the background +/* + * Die Klasse ClientThread verwaltet die Kommunikation mit dem Client + * im Hintergrund und oeffnet bei eingehenden Befehlen einen neuen Handling-Thread. + */ class ClientThread implements Runnable { // Declaration of objects + + // Instance of the server; serves to solve references + private final MaerklinServer SERVER_INSTANCE; + + // Instance of the client-socket; serves to solve references private final Socket CLIENT; + + // Wrapper for the client-sockets' input-stream to buffer the incoming data private final BufferedInputStream IN_STREAM; - private final MaerklinServer SERVER_INSTANCE; + byte[] data; // Constructor @@ -216,40 +247,42 @@ class ClientThread implements Runnable { data = new byte[Properties.IN_BUFFER_SIZE]; } + // Checks if the client sends a session-abort-message passes client-requests to a handle-thread @Override public void run() { int buffer; while (true) { try { if (IN_STREAM.available() != 0) { - buffer = IN_STREAM.read(); - if ((byte) buffer == (byte) Properties.SESSION_ABORT) { + buffer = IN_STREAM.read(); // Reads one byte from the input-stream + if ((byte) buffer == (byte) Properties.SESSION_ABORT) { // Checks for a session-abort signal and potentially severs the connection potentially SERVER_INSTANCE.clients.remove(CLIENT); CLIENT.close(); System.out.println("Conenction to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); return; } - else if ((byte) buffer == (byte) Properties.SEPERATOR) { - for (int i = 0; i < Properties.IN_BUFFER_SIZE; i++) { + else if ((byte) buffer == (byte) Properties.SEPERATOR) { // Checks for the beginning of a new datagram (all datagrams begin with a seperator-byte to mark their start) + for (int i = 0; i < Properties.IN_BUFFER_SIZE; i++) { // Tries to read the whole datagram buffer = IN_STREAM.read(); - if ((byte) buffer == (byte) Properties.SESSION_ABORT) { + if ((byte) buffer == (byte) Properties.SESSION_ABORT) { // Again checks for a session-abort signal from the client SERVER_INSTANCE.clients.remove(CLIENT); CLIENT.close(); System.out.println("Connection to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); return; } else { - data[i] = (byte) (buffer%(1<<8)); + data[i] = (byte) (buffer%(1<<8)); // Typecast to byte through bitshift } } - SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.submit(new HandleThread(SERVER_INSTANCE, data)); + SERVER_INSTANCE.UTILITY_THREAD_EXECUTOR.submit(new HandleThread(SERVER_INSTANCE, data)); // Submits a new handle-thread for the datagram } } else { - Thread.sleep(10); + Thread.sleep(10); // Frees the resources until the next check } } catch (Exception e) { + // Unregister the client if an error occurers System.out.println("An error occured while reading the clients input-stream!"); SERVER_INSTANCE.clients.remove(CLIENT); System.out.println("Conenction to client "+CLIENT.getRemoteSocketAddress()+" aborted!"); @@ -260,11 +293,19 @@ class ClientThread implements Runnable { } -// Processes the incoming datagram and initializes appropriate actions +/* + * HandleThread verarbeitet Anfragen von den Clients und initiiert entsprechende Aktionen. + * Format des Datagrams: 1.+2. Byte: Maerklin-ID + * 3. Byte: Befehl + * 4.+5. Byte: potentielle Parameter + */ class HandleThread implements Runnable { - // Declaration of objects + // Declaration of objects// Declaration of objects + + // Instance of the server; serves to solve references private final MaerklinServer SERVER_INSTANCE; + byte[] data; // Constructor @@ -273,23 +314,24 @@ class HandleThread implements Runnable { data = incomingData; } + // Reads the command and initiates the according action @Override public void run() { try { - switch (data[2]) { - case Properties.SYSTEM_STOP: + switch (data[2]) { // Compare the command-byte to the values set in Properties + case Properties.SYSTEM_STOP: // Emergency-stop (cuts the power) //System.out.println("System stop"); SERVER_INSTANCE.emergencyStop(); break; - case Properties.SYSTEM_GO: + case Properties.SYSTEM_GO: // Resets the emergency-stop (re-enables power) //System.out.println("System go"); SERVER_INSTANCE.start(); break; - case Properties.GET_STATUS: + case Properties.GET_STATUS: // Status-update //System.out.println("Status update"); SERVER_INSTANCE.statusUpdate(); break; - case Properties.ENGINE_SET_SPEED: + case Properties.ENGINE_SET_SPEED: // Set engine-speed to the given value for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set engine speed"); @@ -298,7 +340,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_INCREASE_SPEED: + case Properties.ENGINE_INCREASE_SPEED: // Increase the engine-speed by 50 for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Increase engine speed"); @@ -307,7 +349,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_DECREASE_SPEED: + case Properties.ENGINE_DECREASE_SPEED: // Decrease the engine-speed by 50 for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Decrease engine speed"); @@ -316,7 +358,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_SET_DIRECTION: + case Properties.ENGINE_SET_DIRECTION: // Set engine-direction to the given value for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set engine direction"); @@ -325,7 +367,7 @@ class HandleThread implements Runnable { } } break; - case Properties.ENGINE_SWITCH_DIRECTION: + case Properties.ENGINE_SWITCH_DIRECTION: // Switch the engine-direction for (Engine train : Engine.engines) { if (train.getEngineID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Switch engine direction"); @@ -334,7 +376,7 @@ class HandleThread implements Runnable { } } break; - case Properties.SWITCH_SET_DIRECTION: + case Properties.SWITCH_SET_DIRECTION: // Set the switch-direction to the given value or move the turntable clockwise or counterclockwise for (Switch sw : Switch.switches) { if (sw.getSwitchID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Set switch direction"); @@ -343,7 +385,7 @@ class HandleThread implements Runnable { } } break; - case Properties.SWITCH_SWITCH_DIRECTION: + case Properties.SWITCH_SWITCH_DIRECTION: // Switch the switch-direction for (Switch sw : Switch.switches) { if (sw.getSwitchID() == ((data[0]&0xFF)*(1<<8)+(data[1]&0xFF))) { //System.out.println("Switch switch direction"); @@ -352,7 +394,7 @@ class HandleThread implements Runnable { } } break; - default: + default: // Command doesn't match any of the cases System.out.println("No viable command!\n"); } } @@ -363,11 +405,19 @@ class HandleThread implements Runnable { } -//Broadcasts the current status +/* + * Diese Klasse konvertiert bei Aufruf den aktuellen Status der (serverseitigen) + * Modelle in Byte-Format und wandelt diese unter Hinzufuegen von Start- End- und + * Trenn-Bytes in ein Datagram um, dass an den Client gesendet wird. + */ class UpdateThread implements Runnable { // Declaration of objects + + // Output-stream of the client handled by the thread private final OutputStream OUT_STREAM; + + // Buffer for the datagram private byte[] outgoingData; // Constructor @@ -376,30 +426,30 @@ class UpdateThread implements Runnable { // Setting the broadcast-port OUT_STREAM = out; - // Initializing + // Initializing the buffer-array to the size of the datagram outgoingData = new byte[Properties.OUT_BUFFER_SIZE]; } - // Reads every registered objects data into outgoingData and broadcasts the status + // Reads every registered objects data into outgoingData and sends the status @Override public void run() { int position = 0; try { - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Start-bytes outgoingData[position++] = Properties.SEPERATOR; for (Engine train : Engine.engines) { if (position >= Properties.OUT_BUFFER_SIZE-9) { Exception e = new Exception("Overload of registered elements! Update-buffer too small!"); throw e; } - outgoingData[position++] = (byte) ((train.getEngineID()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((train.getEngineID()/(1<<8))%(1<<8)); // Maerklin-ID outgoingData[position++] = (byte) ((train.getEngineID()%(1<<8))); - outgoingData[position++] = (byte) ((train.getEngineSpeed()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((train.getEngineSpeed()/(1<<8))%(1<<8)); // Speed outgoingData[position++] = (byte) ((train.getEngineSpeed()%(1<<8))); - outgoingData[position++] = (byte) ((train.getEngineDirection()%(1<<24))); - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = (byte) ((train.getEngineDirection()%(1<<24))); // Direction + outgoingData[position++] = Properties.SEPERATOR; // Separator-byte between the engines } - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Separator-bytes between the engines and the switches outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; for (Switch sw : Switch.switches) { @@ -407,14 +457,14 @@ class UpdateThread implements Runnable { Exception e = new Exception("Overload of registered elements! Update-buffer too small!"); throw e; } - outgoingData[position++] = (byte) ((sw.getSwitchID()/(1<<8))%(1<<8)); + outgoingData[position++] = (byte) ((sw.getSwitchID()/(1<<8))%(1<<8)); // Maerklin-ID outgoingData[position++] = (byte) ((sw.getSwitchID()%(1<<8))); + outgoingData[position++] = (byte) 0x00; // Inserted two null-bytes to match the engines-format outgoingData[position++] = (byte) 0x00; - outgoingData[position++] = (byte) 0x00; - outgoingData[position++] = (byte) ((sw.getSwitchDirection()%(1<<24))); - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = (byte) ((sw.getSwitchDirection()%(1<<24))); // Direction + outgoingData[position++] = Properties.SEPERATOR; // Separator-byte between the switches } - outgoingData[position++] = Properties.SEPERATOR; + outgoingData[position++] = Properties.SEPERATOR; // Stop-bytes outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; outgoingData[position++] = Properties.SEPERATOR; @@ -425,7 +475,7 @@ class UpdateThread implements Runnable { } //System.out.printf("\nSende: " + DatatypeConverter.printHexBinary(outgoingData)); try { - OUT_STREAM.write(outgoingData, 0, Properties.OUT_BUFFER_SIZE); + OUT_STREAM.write(outgoingData, 0, Properties.OUT_BUFFER_SIZE); // Send the datagram to the client } catch (Exception e) { System.out.println("Communication-error whilst status-update!"); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServerApplication.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServerApplication.java index 7bbd8c583b8818b0232a810ef61c347e928926ab..8d8d5379200a20f573e9212a23c02b33d57cf17a 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServerApplication.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/MaerklinServerApplication.java @@ -1,5 +1,11 @@ package server; + +/* + * MaerklinServerApplication startet den Server sowie den Listener, der + * die serverseitigen Modelle aktualisiert. Weiterhin initialisiert die Klasse + * zyklisch ein synchrones Status-Update fuer alle registrierten Clients. + */ public class MaerklinServerApplication { // Defining a threshold value for when the status-update is to be executed (in milliseconds) @@ -19,6 +25,7 @@ public class MaerklinServerApplication { // Starting the client-handling server.listen(); + // Periodically checks if the update-threshold has been exceeded and potentially initializes the status update through statusUpdate while (true){ if (System.currentTimeMillis()-timestamp >= updateThreshold) { server.statusUpdate(); diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/Switch.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/Switch.java index ea03fbf954828e18a33f0b2dd72dabe69f806f16..418adf90c51d8067562fb47617e72bbdc94c6230 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/Switch.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/Switch.java @@ -2,16 +2,23 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell fuer eine Weiche auf Serverseite dar. + * Weiterhin lassen sich über eine lokale Instanz des Maerklin-Protokolls + * Befehle an die Maerklin-C2-Steuerung senden. + */ public class Switch { + // Static array containing all created instances of the object static ArrayList<Switch> switches; private int switchID; int switchDirection; + // Private locale instance of the maerklin-protocol to send commands to the control-station private MaerklinProtocol udpProtocol; - private UDPListener listener; + // Constructor public Switch(){ this(0, 1); @@ -26,12 +33,12 @@ public class Switch { switchDirection = 1;*/ } + // Constructor with some initial data public Switch(int id, int direction){ if (switches == null) { switches = new ArrayList<Switch>(); } udpProtocol = new MaerklinProtocol(); - listener = new UDPListener(); switches.add(this); try { setSwitchID(id); @@ -42,10 +49,12 @@ public class Switch { } } + // Sets the Maerklin-ID public void setSwitchID(int newSwitchID){ switchID = newSwitchID; } + // Sets the switch-direction and sends the respective command to the control-station public void setSwitchDirection(int newSwitchDirection) throws Exception { if (newSwitchDirection == 0x01 || newSwitchDirection == 0x00){ switchDirection = newSwitchDirection; @@ -57,6 +66,7 @@ public class Switch { udpProtocol.changeSwitchDirection(switchID, switchDirection); } + // Changes the switch-direction and calls setSwitchDirection with the new vdirection-alue public void changeSwitchDirection() throws Exception { if (switchDirection == 0x00){ setSwitchDirection(0x01); @@ -66,10 +76,12 @@ public class Switch { } } + // Returns the Maerklin-ID public int getSwitchID(){ return switchID; } + // Returns the switch-direction public int getSwitchDirection(){ return switchDirection; } diff --git a/Praktikum/VINF_MaerklinControl/build/project/src/server/UDPListener.java b/Praktikum/VINF_MaerklinControl/build/project/src/server/UDPListener.java index 0a5c2456a5a7162a3a3c2f9d7faa3968fbcaa613..d5c3be03c49d32985c58400b0c7ecc848c1c2ea4 100644 --- a/Praktikum/VINF_MaerklinControl/build/project/src/server/UDPListener.java +++ b/Praktikum/VINF_MaerklinControl/build/project/src/server/UDPListener.java @@ -1,24 +1,31 @@ package server; - import java.net.DatagramPacket; import java.net.DatagramSocket; import javax.xml.bind.DatatypeConverter; +/* + * Der UDPListener ist in der Lage auf den Ausgabe-Port der Maerklin-C2-Steuerung + * zu lauschen, ausgehende Datagramme zu verarbeiten und die serverseitigen Modelle + * zu updaten. Es ist sowohl eine "manuelle" Version wie auch eine Automatik (per + * Runnable) implementiert. + */ public class UDPListener implements Runnable{ // Defining the size of the datagrams received from the Maerklin-control-station public final static int MAERKLIN_DATAGRAM_SIZE = 13; - // Listener-port + // Setting the output-port of the maerklin-control-station private final static int port = 15730; private byte[] in; + // Constructor public UDPListener(){ in = new byte[MAERKLIN_DATAGRAM_SIZE]; } + // Listens for answers from the control-station and writes it to the array in public void listen(){ try(DatagramSocket socket = new DatagramSocket(port)){ socket.setReuseAddress(true); @@ -31,30 +38,38 @@ public class UDPListener implements Runnable{ } } + // Returns the last received datagram public byte[] getData(){ return in; } + // Decodes the engine-speed from the datagram public int getEngineSpeed(){ return ((in[9]&0xFF)*(1<<8)+(in[10]&0xFF)); } + + // Decodes the engine-direction from the datagram public int getEngineDirection(){ return (in[9]&0xFF); } + // Decodes the switch-direction from the datagram public int getSwitchDirection(){ return (in[9]&0xFF); } + // Decodes the Maerklin-ID from the datagram public int getAddress(){ return ((in[7]&0xFF)*(1<<8)+(in[8]&0xFF)); } + // Decodes the command from the datagram public int getCommand(){ return ((in[1]&0xFF)&0xFE); } + // Checks if the last received datagram is an answer from the control-station public boolean isAnswer(){ if ((in[1]&0x01) == 1){ return true; @@ -64,6 +79,7 @@ public class UDPListener implements Runnable{ } } + // Prints the last received datagram in human-readable form public void printData(byte[] data){ try{ byte[] kennung = new byte[4]; @@ -79,6 +95,7 @@ public class UDPListener implements Runnable{ } } + // Debugging-functionality public static void main(String args[]){ UDPListener testListener = new UDPListener(); while (true){ @@ -87,17 +104,18 @@ public class UDPListener implements Runnable{ } } + // Automatizes the listener-functionalit and updates the models' values with the received data @Override public void run() { //System.out.println("UDPListener start!"); - try(DatagramSocket socket = new DatagramSocket(port)){ + try(DatagramSocket socket = new DatagramSocket(port)){ // Tries to open a socket set to the output-port of the control-station socket.setReuseAddress(true); while (true){ DatagramPacket incomingData = new DatagramPacket(in, in.length); socket.receive(incomingData); in = incomingData.getData(); - if (isAnswer()){ - if (getCommand() == 0x08){ + if (isAnswer()){ // Checks if the datagram is an answer + if (getCommand() == 0x08){ // Engine-speed for (Engine train : Engine.engines){ if (train.getEngineID() == getAddress()){ train.engineSpeed = getEngineSpeed(); @@ -106,7 +124,7 @@ public class UDPListener implements Runnable{ } } } - else if (getCommand() == 0x0A){ + else if (getCommand() == 0x0A){ // Engine-direction for (Engine train : Engine.engines){ if (train.getEngineID() == getAddress()){ train.engineDirection = getEngineDirection(); @@ -116,7 +134,7 @@ public class UDPListener implements Runnable{ } } } - else if (getCommand() == 0x16){ + else if (getCommand() == 0x16){ // Switch-direction for (Switch sw : Switch.switches){ if (sw.getSwitchID() == getAddress()){ sw.switchDirection = getEngineDirection(); diff --git a/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java b/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java index 650262da53efbc60443beee21b6473003700ec96..a979753e47c68b219da1ad8c9c60a3679098b755 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java +++ b/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java @@ -61,7 +61,7 @@ public class MaerklinServer{ public MaerklinServer(){ // Initializing - System.out.println("Initializing..."); + System.out.print("Maerklin Control Server\nLukas Friedrichsen, Philipp Stenkamp; (c) 2017\n\nInitializing...\n"); clients = new ArrayList<Socket>();