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 var tempMap = new HashMap<>(registeredClients); // to avoid concurrent modification 078 for (AppClient c : tempMap.values()) { 079 if (c.isTimestampExpired()) registeredClients.remove(c.getAddress()); 080 } 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 synchronized public byte[] getLocoStatusMessage(InetAddress address, Integer locoAddress) { 086 if (registeredClients.containsKey(address)) { 087 AppClient client = registeredClients.get(address); 088 return client.getLocoStatusMessage(locoAddress); 089 } else { 090 return null; 091 } 092 } 093 094 @Override 095 synchronized public void notifyThrottleFound(DccThrottle t) { 096 int locoAddress = t.getLocoAddress().getNumber(); 097 InetAddress client = requestedThrottlesList.get(locoAddress); 098 if (client != null) { 099 registeredClients.get(client).addThrottle(locoAddress, t); 100 requestedThrottlesList.remove(locoAddress); 101 } 102 } 103 104 @Override 105 synchronized public void notifyFailedThrottleRequest(LocoAddress address, String reason) { 106 log.info("Unable to get Throttle for loco address {}, reason : {}", address.getNumber(), reason); 107 requestedThrottlesList.remove(address.getNumber()); 108 } 109 110 @Override 111 synchronized public void notifyDecisionRequired(LocoAddress address, DecisionType question) { 112 jmri.InstanceManager.throttleManagerInstance().responseThrottleDecision(address, ClientManager.getInstance(), ThrottleListener.DecisionType.SHARE); 113 } 114 115 public static byte xor(byte[] packet) { 116 byte xor = (byte) (packet[0] ^ packet[1]); 117 for (int i = 2; i < (packet.length - 1); i++) { 118 xor = (byte) (xor ^ packet[i]); 119 } 120 return xor; 121 } 122 123}