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}