001package jmri.jmrit.z21server;
002
003import jmri.DccThrottle;
004import jmri.LocoAddress;
005import jmri.ThrottleListener;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009import java.net.InetAddress;
010import java.util.HashMap;
011
012public class ClientManager implements ThrottleListener {
013
014    private static ClientManager instance;
015    private static final HashMap<InetAddress, AppClient> registeredClients = new HashMap<>();
016    private static final HashMap<Integer, InetAddress> requestedThrottlesList = new HashMap<>();
017    public static float speedMultiplier = 1.0f / 128.0f;
018
019    private final static Logger log = LoggerFactory.getLogger(ClientManager.class);
020
021    private ClientManager() {
022    }
023
024    synchronized public static ClientManager getInstance() {
025        if (instance == null) {
026            instance = new ClientManager();
027        }
028        return instance;
029    }
030
031    synchronized public void registerLocoIfNeeded(InetAddress clientAddress, int locoAddress) {
032        if (!registeredClients.containsKey(clientAddress)) {
033            AppClient client = new AppClient(clientAddress);
034            registeredClients.put(clientAddress, client);
035        }
036        if (registeredClients.get(clientAddress).getThrottleFromLocoAddress(locoAddress) == null) {
037            jmri.InstanceManager.throttleManagerInstance().requestThrottle(locoAddress, ClientManager.getInstance());
038            requestedThrottlesList.put(locoAddress, clientAddress);
039        }
040    }
041
042    synchronized public void setLocoSpeedAndDirection(InetAddress clientAddress, int locoAddress, int speed, boolean forward) {
043        AppClient client = registeredClients.get(clientAddress);
044        if (client != null) {
045            DccThrottle throttle = client.getThrottleFromLocoAddress(locoAddress);
046            if (throttle != null) {
047                if (throttle.getIsForward() != forward) throttle.setIsForward(forward);
048                throttle.setSpeedSetting(speed * speedMultiplier);
049            } else {
050                log.info("Unable to find throttle for loco {} from client {}", locoAddress, clientAddress);
051            }
052        } else {
053            log.info("App client {} is not registered", clientAddress);
054        }
055    }
056
057    synchronized public void setLocoFunction(InetAddress clientAddress, int locoAddress, int functionNumber, boolean bOn) {
058        AppClient client = registeredClients.get(clientAddress);
059        if (client != null) {
060            DccThrottle throttle = client.getThrottleFromLocoAddress(locoAddress);
061            if (throttle != null) {
062                throttle.setFunction(functionNumber, bOn);
063            } else {
064                log.info("Unable to find throttle for loco {} from client {}", locoAddress, clientAddress);
065            }
066        } else {
067            log.info("App client {} is not registered", clientAddress);
068        }
069    }
070
071    synchronized public void heartbeat(InetAddress clientAddress) {
072        AppClient client = registeredClients.get(clientAddress);
073        if (client != null) client.heartbeat();
074    }
075
076    synchronized public void handleExpiredClients() {
077        for (AppClient c : registeredClients.values()) {
078            if (c.isTimestampExpired()) registeredClients.remove(c.getAddress());
079        }
080    }
081
082    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
083    justification = "Messages can be of any length, null is used to indicate absence of message for caller")
084    synchronized public byte[] getLocoStatusMessage(InetAddress address, Integer locoAddress) {
085        if (registeredClients.containsKey(address)) {
086            AppClient client = registeredClients.get(address);
087            return client.getLocoStatusMessage(locoAddress);
088        } else {
089            return null;
090        }
091    }
092
093    @Override
094    synchronized public void notifyThrottleFound(DccThrottle t) {
095        int locoAddress = t.getLocoAddress().getNumber();
096        InetAddress client = requestedThrottlesList.get(locoAddress);
097        if (client != null) {
098            registeredClients.get(client).addThrottle(locoAddress, t);
099            requestedThrottlesList.remove(locoAddress);
100        }
101    }
102
103    @Override
104    synchronized public void notifyFailedThrottleRequest(LocoAddress address, String reason) {
105        log.info("Unable to get Throttle for loco address {}, reason : {}", address.getNumber(), reason);
106        requestedThrottlesList.remove(address.getNumber());
107    }
108
109    @Override
110    synchronized public void notifyDecisionRequired(LocoAddress address, DecisionType question) {
111        jmri.InstanceManager.throttleManagerInstance().responseThrottleDecision(address, ClientManager.getInstance(), ThrottleListener.DecisionType.SHARE);
112    }
113
114    public static byte xor(byte[] packet) {
115        byte xor = (byte) (packet[0] ^ packet[1]);
116        for (int i = 2; i < (packet.length - 1); i++) {
117            xor = (byte) (xor ^ packet[i]);
118        }
119        return xor;
120    }
121
122}