001package jmri.jmrit.z21server;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006import java.net.InetAddress;
007import java.util.Arrays;
008
009public class Service40 {
010    private static final String moduleIdent = "[Service 40] ";
011
012    private final static Logger log = LoggerFactory.getLogger(Service40.class);
013
014
015    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
016    justification = "Messages can be of any length, null is used to indicate absence of message for caller")
017    public static byte[] handleService(byte[] data, InetAddress clientAddress) {
018        int command = data[0];
019        switch (command){
020            case 0x21:
021                return handleHeader21(data[1]);
022            case (byte)0xE3:
023                return handleHeaderE3(Arrays.copyOfRange(data, 1, 4), clientAddress);
024            case (byte)0xE4:
025                return handleHeaderE4(Arrays.copyOfRange(data, 1, 5), clientAddress);
026            default:
027                log.debug("{} Header {} not yet supported", moduleIdent, Integer.toHexString(command));
028                break;
029        }
030        return null;
031    }
032
033    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
034    justification = "Messages can be of any length, null is used to indicate absence of message for caller")
035    private static byte[] handleHeader21(int db0){
036        switch (db0){
037            case 0x21:
038                // Get z21 version
039                break;
040            case 0x24:
041                // Get z21 status
042                byte[] answer = new byte[8];
043                answer[0] = (byte) 0x08;
044                answer[1] = (byte) 0x00;
045                answer[2] = (byte) 0x40;
046                answer[3] = (byte) 0x00;
047                answer[4] = (byte) 0x62;
048                answer[5] = (byte) 0x22;
049                answer[6] = (byte) 0x00;
050                answer[7] = ClientManager.xor(answer);
051                return answer;
052            case 0x80:
053                log.debug("{} Set track power to off", moduleIdent);
054                break;
055            case 0x81:
056                log.debug("{} Set track power to on", moduleIdent);
057                break;
058            default:
059                break;
060        }
061        return null;
062    }
063
064    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
065    justification = "Messages can be of any length, null is used to indicate absence of message for caller")
066    private static byte[] handleHeaderE3(byte[] data, InetAddress clientAddress) {
067        int db0 = data[0];
068        if (db0 == (byte)0xF0) {
069            // Get loco status command
070            int locomotiveAddress = (((data[1] & 0xFF) & 0x3F) << 8) + (data[2] & 0xFF);
071            log.debug("{} Get loco no {} status", moduleIdent, locomotiveAddress);
072
073            ClientManager.getInstance().registerLocoIfNeeded(clientAddress, locomotiveAddress);
074
075            return ClientManager.getInstance().getLocoStatusMessage(clientAddress, locomotiveAddress);
076
077        } else {
078            log.debug("{} Header E3 with function {} is not supported", moduleIdent,  Integer.toHexString(db0));
079        }
080        return null;
081    }
082
083    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
084    justification = "Messages can be of any length, null is used to indicate absence of message for caller")
085    private static byte[] handleHeaderE4(byte[] data, InetAddress clientAddress) {
086        if (data[0] == 0x13) {
087            int locomotiveAddress = (((data[1] & 0xFF) & 0x3F) << 8) + (data[2] & 0xFF);
088            int rawSpeedData = data[3] & 0xFF;
089            boolean bForward = ((rawSpeedData & 0x80) >> 7) == 1;
090            int actualSpeed = rawSpeedData & 0x7F;
091            log.debug("Set loco no {} direction {} with speed {}",locomotiveAddress, (bForward ? "FWD" : "RWD"), actualSpeed);
092
093            ClientManager.getInstance().setLocoSpeedAndDirection(clientAddress, locomotiveAddress, actualSpeed, bForward);
094
095            return ClientManager.getInstance().getLocoStatusMessage(clientAddress, locomotiveAddress);
096        }
097        if (data[0] == (byte)0xF8) {
098            int locomotiveAddress = (((data[1] & 0xFF) & 0x3F) << 8) + (data[2] & 0xFF);
099            boolean bOn = (((data[3] & 0xFF) & 0x40) >> 6) == 1;
100            int functionNumber = (data[3] & 0xFF) & 0x3F;
101            log.debug("Set loco no {} function no {} to {}", locomotiveAddress, functionNumber, (bOn ? "ON" : "OFF"));
102
103            ClientManager.getInstance().setLocoFunction(clientAddress, locomotiveAddress, functionNumber, bOn);
104
105            return ClientManager.getInstance().getLocoStatusMessage(clientAddress, locomotiveAddress);
106        }
107        return null;
108    }
109}