diff --git a/Praktikum/VINF_MaerklinControl/bin/.gitignore b/Praktikum/VINF_MaerklinControl/bin/.gitignore index d0380dd1b383afcf938027633d2902f4e14690f3..51adac184a4c6f7daf0218f86fffdffc9fe56b4e 100644 --- a/Praktikum/VINF_MaerklinControl/bin/.gitignore +++ b/Praktikum/VINF_MaerklinControl/bin/.gitignore @@ -1,2 +1,3 @@ /gui/ /server/ +/common/ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/ClientThread.class b/Praktikum/VINF_MaerklinControl/bin/server/ClientThread.class index 87ad4494d5fb304483eaa093de83bbcc1a5259c5..b520937cae4530d4037535e6c1406d83e64c6f56 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/ClientThread.class and b/Praktikum/VINF_MaerklinControl/bin/server/ClientThread.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/Engine.class b/Praktikum/VINF_MaerklinControl/bin/server/Engine.class index ddeb21d94820b3679bf8989d78b055a8e2a0c416..728dcfbfb291af49ba5d7f284c6d95d080eaaec2 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/Engine.class and b/Praktikum/VINF_MaerklinControl/bin/server/Engine.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/HandleThread.class b/Praktikum/VINF_MaerklinControl/bin/server/HandleThread.class index b716bf9e72a489e3a5572a1845d5821058fad1b0..70ac4ae37d34ed43b35682bfaf3d7c4b19378421 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/HandleThread.class and b/Praktikum/VINF_MaerklinControl/bin/server/HandleThread.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinProtocol.class b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinProtocol.class index 079482e9fb9cb8be301c625d735e2123fde8f0db..463513cf891565df613ad7889e2c109b8f12a0fa 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinProtocol.class and b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinProtocol.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServer.class index afe7a4d2062fca83c9266f25ce3c80bae44dadef..5071b96bd11d0b906b01cf676c004fa20ab1c4ff 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/bin/server/MaerklinServerApplication.class b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServerApplication.class index 49cade3d83850e8a50e66f49229e30d5f0feb2be..7b3ecdb3eb9afda4fd94a8a74be2e6fda159bbc6 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServerApplication.class and b/Praktikum/VINF_MaerklinControl/bin/server/MaerklinServerApplication.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/ServerThread.class b/Praktikum/VINF_MaerklinControl/bin/server/ServerThread.class index 6be948c4b99495eecf0e236214e7c41563a5bc3a..4dba35bb006f200f8fe7a09c3e86644e8bc787a9 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/ServerThread.class and b/Praktikum/VINF_MaerklinControl/bin/server/ServerThread.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/Switch.class b/Praktikum/VINF_MaerklinControl/bin/server/Switch.class index 7c0cfac672cecd1bf8ff2626284e237c0984424a..708496fe39adc0112d3d470eeddb417a11ed5c14 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/Switch.class and b/Praktikum/VINF_MaerklinControl/bin/server/Switch.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/UDPListener.class b/Praktikum/VINF_MaerklinControl/bin/server/UDPListener.class index c116d862c6b31c41dc3fd90997f8b57dca65c1d7..b27ad59957a3ed2598c055c57492d9e9bc53263d 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/UDPListener.class and b/Praktikum/VINF_MaerklinControl/bin/server/UDPListener.class differ diff --git a/Praktikum/VINF_MaerklinControl/bin/server/UpdateThread.class b/Praktikum/VINF_MaerklinControl/bin/server/UpdateThread.class index baf588be8710d5a1c39e4f5038ce009fd276d972..e6127497fd4f6c236051e375017e66a9e54e2711 100644 Binary files a/Praktikum/VINF_MaerklinControl/bin/server/UpdateThread.class and b/Praktikum/VINF_MaerklinControl/bin/server/UpdateThread.class differ diff --git a/Praktikum/VINF_MaerklinControl/src/common/Properties.java b/Praktikum/VINF_MaerklinControl/src/common/Properties.java index 3b4f180653ccf1fa73ab1417e54c2b0760178e64..f4fc6cb5e2a631c3b9bbf46719bf43c4744aadde 100644 --- a/Praktikum/VINF_MaerklinControl/src/common/Properties.java +++ b/Praktikum/VINF_MaerklinControl/src/common/Properties.java @@ -3,7 +3,7 @@ package common; /* * In dieser Klasse werden alle klassenübergreifen verwendeten Werte einmalig deklariert, * so dass potentielle Änderungen lediglich einmalig an dieser Stelle durchgeführt werden - * müssen und Code-Vervielfachung vermieden wird. + * müssen und Code-Vervielfachung vermieden wird (Einhaltung des DRY-Prinzips). */ public class Properties { diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java index bb1b3907f7152991821372d6c41b66b3f5cb660a..49d87f682ecec11eb10706d09e82a9dcebd140fb 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Engine.java @@ -12,7 +12,7 @@ import javafx.beans.property.StringProperty; import javafx.scene.image.Image; /* - * Model class for a Model Engine + * Modell-Klasse für einen Zug. */ public class Engine { @@ -54,7 +54,7 @@ public class Engine { return name; } - // Returns the maerklin-id + // Returns the Maerklin-ID public IntegerProperty getMaerklinID() { return id; } diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java index f96f16b936566e13331f13f48951a0737887ffa3..fba05f56e323485f7da484e539a3294257bf0508 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Settings.java @@ -7,7 +7,7 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; /* - * Model class for a Model Engine + * Modell-Klasse für die Einstellungen der App. */ public class Settings { private final ObjectProperty<InetSocketAddress> server; diff --git a/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java b/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java index 7f0b53f051929f3d51b4bef87577a6e7d49a74f6..7f76416bec8a90e82d1f2a304062c00fe31449e3 100644 --- a/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java +++ b/Praktikum/VINF_MaerklinControl/src/gui/model/Switch.java @@ -11,7 +11,7 @@ import javafx.beans.property.StringProperty; import javafx.beans.property.ObjectProperty; /* - * Model class for a Model Train Switch + * Modell-Klasse für eine Weiche. */ public class Switch{ diff --git a/Praktikum/VINF_MaerklinControl/src/server/Engine.java b/Praktikum/VINF_MaerklinControl/src/server/Engine.java index be0e29c6cddac6f3843b8dd2fe1e23e3a4532ce2..33c450314ffcebd41135cf643c9fd4fae3181fe3 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/Engine.java +++ b/Praktikum/VINF_MaerklinControl/src/server/Engine.java @@ -3,17 +3,24 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell für einen Zug auf Serverseite dar. + * Weiterhin lassen sich über 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; @@ -69,6 +78,8 @@ public class Engine { udpProtocol.changeEngineDirection(engineID, engineDirection); } + + // 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; @@ -80,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/src/server/MaerklinProtocol.java b/Praktikum/VINF_MaerklinControl/src/server/MaerklinProtocol.java index 30a97706453531fa3102adddf8cbe7daf618c1c4..e44d6ba1812ca0c24b0c8c930afa958721e8a3a3 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/MaerklinProtocol.java +++ b/Praktikum/VINF_MaerklinControl/src/server/MaerklinProtocol.java @@ -2,15 +2,25 @@ package server; import java.net.*; +/* + * Diese Klasse implementiert das Maerklin-Protokoll zur Kommunikation + * mit der Maerklin-C2-Steuerung. Die Datagramme werden mittels UDP + * gesendet; für jedes Datagram wird ein neuer Sockel geöffnet. + */ 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/src/server/MaerklinServer.java b/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java index c2774bd217d4ce717b1fc7c705dc880e43323880..63c137d25977294fd5ca8ff69583b7dbab9ebbe0 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java +++ b/Praktikum/VINF_MaerklinControl/src/server/MaerklinServer.java @@ -14,19 +14,33 @@ 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 für alle anderen abgeleiteten oder + * erzeugten Objekte als Referenz für 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,6 +57,7 @@ public class MaerklinServer{ private Switch switch15; private Switch turntable; + // Constructor public MaerklinServer(){ // Initializing @@ -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 öffnet einen Server-Port und erstellt einen neue Client-Thread-Instanz + * für 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 öffnet 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 Hinzufügen 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/src/server/MaerklinServerApplication.java b/Praktikum/VINF_MaerklinControl/src/server/MaerklinServerApplication.java index 7bbd8c583b8818b0232a810ef61c347e928926ab..ec89c47754700c5eebb7d3d176131a30ce1dfdaf 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/MaerklinServerApplication.java +++ b/Praktikum/VINF_MaerklinControl/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 für 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/src/server/Switch.java b/Praktikum/VINF_MaerklinControl/src/server/Switch.java index ea03fbf954828e18a33f0b2dd72dabe69f806f16..d4880af8c0e99f60869b7704e373b76c86b8145d 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/Switch.java +++ b/Praktikum/VINF_MaerklinControl/src/server/Switch.java @@ -2,16 +2,23 @@ package server; import java.util.ArrayList; +/* + * Diese Klasse stellt das Modell für 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/src/server/UDPListener.java b/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java index 0cc8fe532868cd0e224b2b859871f8e20769aa48..d5c3be03c49d32985c58400b0c7ecc848c1c2ea4 100644 --- a/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java +++ b/Praktikum/VINF_MaerklinControl/src/server/UDPListener.java @@ -4,20 +4,28 @@ 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); @@ -30,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; @@ -63,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]; @@ -78,6 +95,7 @@ public class UDPListener implements Runnable{ } } + // Debugging-functionality public static void main(String args[]){ UDPListener testListener = new UDPListener(); while (true){ @@ -86,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(); @@ -105,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(); @@ -115,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();