001package jmri.jmrit.z21server;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006import java.net.DatagramPacket;
007import java.net.DatagramSocket;
008import java.net.InetAddress;
009import java.net.SocketException;
010import java.util.Arrays;
011
012public class MainServer implements Runnable {
013
014    private final static Logger log = LoggerFactory.getLogger(MainServer.class);
015    private final static int port = 21105;
016    DatagramSocket mySS;
017    @Override
018    public void run() {
019        try {
020            mySS = new DatagramSocket(port);
021
022            byte[] buf = new byte[256];
023            DatagramPacket packet = new DatagramPacket(buf, buf.length);
024
025            log.info("Created socket, listening for connections");
026
027            while (true) {
028
029                if (Thread.interrupted()) break;
030                boolean bReceivedData;
031
032                mySS.setSoTimeout(500);
033                try {
034                    mySS.receive(packet);
035                    bReceivedData = true;
036                } catch (Exception e) {
037                    bReceivedData = false;
038                }
039
040                if (!bReceivedData) continue;
041
042                InetAddress clientAddress = packet.getAddress();
043
044                ClientManager.getInstance().heartbeat(clientAddress);
045
046                byte[] rawData = packet.getData();
047                int dataLenght = rawData[0];
048                byte[] actualData = Arrays.copyOf(rawData, dataLenght);
049                String ident = "[" + clientAddress + "]  ";
050                log.debug("{}: raw frame {} ", ident, bytesToHex(actualData));
051
052                if (actualData.length < 3) {
053                    log.debug("error, frame : {}", bytesToHex(actualData));
054                }
055
056                byte[] response = null;
057
058                switch (actualData[2]) {
059                    case 0x50:
060                        byte[] maskArray = Arrays.copyOfRange(actualData, HEADER_SIZE, dataLenght);
061                        int mask = fromByteArrayLittleEndian(maskArray);
062                        log.debug("{} Broadcast request with mask : {}", ident, Integer.toBinaryString(mask));
063                        break;
064                    case 0x30:
065                        log.debug("{} Disconnect frame", ident);
066                        break;
067                    case 0x40:
068                        byte[] payloadData = Arrays.copyOfRange(actualData, HEADER_SIZE, dataLenght);
069                        response = Service40.handleService(payloadData, clientAddress);
070                        break;
071
072                    default:
073                        log.debug("{} Service not yet implemented : 0x{}", ident,  Integer.toHexString(actualData[2]));
074                }
075
076                if (response != null) {
077                    DatagramPacket responsePacket = new DatagramPacket(response, response.length, clientAddress, port);
078                    try {
079                        mySS.send(responsePacket);
080                    } catch (Exception e) {
081                        log.debug("Unable to send packet to client {}", clientAddress.toString());
082                    }
083                }
084
085                ClientManager.getInstance().handleExpiredClients();
086            }
087
088            log.info("Z21 App Server shut down.");
089
090        } catch (SocketException e) {
091            log.info("Z21 App Server encountered an error, exiting.", e);
092        }
093
094        if (mySS != null) {
095            mySS.close();
096        }
097
098    }
099
100    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
101
102    private static String bytesToHex(byte[] bytes) {
103        char[] hexChars = new char[bytes.length * 2];
104        for (int j = 0; j < bytes.length; j++) {
105            int v = bytes[j] & 0xFF;
106            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
107            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
108        }
109        return new String(hexChars);
110    }
111
112
113    private static int fromByteArrayLittleEndian(byte[] bytes) {
114        return ((bytes[2] & 0xFF) << 24) |
115                ((bytes[3] & 0xFF) << 16) |
116                ((bytes[0] & 0xFF) << 8) |
117                ((bytes[1] & 0xFF) << 0);
118    }
119
120    private static final short HEADER_SIZE = 4;
121
122
123}