001package jmri.jmrit.z21server;
002
003
004import jmri.DccThrottle;
005
006import java.net.InetAddress;
007import java.time.Duration;
008import java.util.Date;
009import java.util.HashMap;
010
011import static jmri.jmrit.z21server.ClientManager.speedMultiplier;
012
013public class AppClient {
014
015    private InetAddress address;
016    private HashMap<Integer, DccThrottle> throttles;
017
018    private Date timestamp;
019
020    private static final int packetLenght = 14;
021
022
023    public AppClient(InetAddress address) {
024        this.address = address;
025        throttles = new HashMap<>();
026        heartbeat();
027    }
028
029    public void addThrottle(int locoAddress, DccThrottle throttle) {
030        if (!throttles.containsKey(locoAddress)) {
031            throttles.put(locoAddress, throttle);
032        }
033    }
034
035    public DccThrottle getThrottleFromLocoAddress(int locoAddress) {
036        if (throttles.containsKey(locoAddress)) {
037            return throttles.get(locoAddress);
038        } else {
039            return null;
040        }
041    }
042
043    public InetAddress getAddress() {
044        return address;
045    }
046
047    public void heartbeat() {
048        timestamp = new Date();
049    }
050
051    public boolean isTimestampExpired() {
052        Duration duration = Duration.between(timestamp.toInstant(), new Date().toInstant());
053        return (duration.toMinutes() >= 60);
054        /* Per Z21 Spec, clients are deemed lost after one minute of inactivity. */
055    }
056
057    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
058       justification = "Messages can be of any length, null is used to indicate absence of message for caller")
059    public byte[] getLocoStatusMessage(Integer locoAddress) {
060        if (throttles.containsKey(locoAddress)) {
061            return buildLocoPacket(throttles.get(locoAddress));
062        } else {
063            return null;
064        }
065    }
066
067    private byte[] buildLocoPacket(DccThrottle t) {
068        byte[] locoPacket =  new byte[packetLenght];
069
070        // Header
071        locoPacket[0] = (byte) (7 + 7);
072        locoPacket[1] = (byte) 0x00;
073        locoPacket[2] = (byte) 0x40;
074        locoPacket[3] = (byte) 0x00;
075        locoPacket[4] = (byte) 0xEF;
076        // Loco address
077        locoPacket[5] = (byte) (t.getLocoAddress().getNumber() >> 8);
078        locoPacket[6] = (byte) t.getLocoAddress().getNumber();
079        //Loco drive and speed data
080        locoPacket[7] = (byte) 0x04;
081        float speed = t.getSpeedSetting();
082        int packetspeed = Math.round(speed / speedMultiplier);
083        if (speed < 0) packetspeed = 0;
084        if (packetspeed > 128) packetspeed = 128;
085        locoPacket[8] = (byte) ((t.getIsForward() ? (byte) 0x80 : 0) + ((byte) packetspeed));
086        // Loco functions data
087        locoPacket[9] = (byte) ((byte)
088                (t.getFunction(0) ? 0x10 : 0) +
089                (t.getFunction(4) ? 0x08 : 0) +
090                (t.getFunction(3) ? 0x04 : 0) +
091                (t.getFunction(2) ? 0x02 : 0) +
092                (t.getFunction(1) ? 0x01 : 0)
093        );
094        locoPacket[10] = (byte) ((byte)
095                (t.getFunction(12) ? 0x80 : 0) +
096                (t.getFunction(11) ? 0x40 : 0) +
097                (t.getFunction(10) ? 0x20 : 0) +
098                (t.getFunction(9) ? 0x10 : 0) +
099                (t.getFunction(8) ? 0x08 : 0) +
100                (t.getFunction(7) ? 0x04 : 0) +
101                (t.getFunction(6) ? 0x02 : 0) +
102                (t.getFunction(5) ? 0x01 : 0)
103        );
104        locoPacket[11] = (byte) ((byte)
105                (t.getFunction(20) ? 0x80 : 0) +
106                (t.getFunction(19) ? 0x40 : 0) +
107                (t.getFunction(18) ? 0x20 : 0) +
108                (t.getFunction(17) ? 0x10 : 0) +
109                (t.getFunction(16) ? 0x08 : 0) +
110                (t.getFunction(15) ? 0x04 : 0) +
111                (t.getFunction(14) ? 0x02 : 0) +
112                (t.getFunction(13) ? 0x01 : 0)
113        );
114        locoPacket[12] = (byte) ((byte)
115                (t.getFunction(28) ? 0x80 : 0) +
116                (t.getFunction(27) ? 0x40 : 0) +
117                (t.getFunction(26) ? 0x20 : 0) +
118                (t.getFunction(25) ? 0x10 : 0) +
119                (t.getFunction(24) ? 0x08 : 0) +
120                (t.getFunction(23) ? 0x04 : 0) +
121                (t.getFunction(22) ? 0x02 : 0) +
122                (t.getFunction(21) ? 0x01 : 0)
123        );
124        locoPacket[13] = ClientManager.xor(locoPacket);
125
126        return locoPacket;
127    }
128
129
130}