001package jmri.jmrix.dccpp; 002 003import java.util.EnumSet; 004import java.util.HashMap; 005 006import jmri.DccLocoAddress; 007import jmri.DccThrottle; 008import jmri.LocoAddress; 009import jmri.SpeedStepMode; 010import jmri.ThrottleListener; 011import jmri.jmrix.AbstractThrottleManager; 012 013/** 014 * DCC++ implementation of a ThrottleManager based on the 015 * AbstractThrottleManager. 016 * 017 * @author Paul Bender Copyright (C) 2002-2004 018 * @author Mark Underwood Copyright (C) 2015 019 * 020 * Based on XNetThrottleManager by Paul Bender 021 */ 022public class DCCppThrottleManager extends AbstractThrottleManager implements DCCppListener { 023 024 protected HashMap<LocoAddress, DCCppThrottle> throttles = new HashMap<>(5); 025 026 protected DCCppTrafficController tc; 027 028 /** 029 * Constructor. 030 * @param memo the memo for the connection this tm will use 031 */ 032 public DCCppThrottleManager(DCCppSystemConnectionMemo memo) { 033 super(memo); 034 // connect to the TrafficController manager 035 tc = memo.getDCCppTrafficController(); 036 037 // Register to listen for throttle messages 038 tc.addDCCppListener(DCCppInterface.THROTTLE, DCCppThrottleManager.this); 039 //Request number of available slots 040 tc.sendDCCppMessage(DCCppMessage.makeCSMaxNumSlotsMsg(), DCCppThrottleManager.this); 041 } 042 043 /** 044 * Request a new throttle object be created for the address, and let the 045 * throttle listeners know about it. 046 * {@inheritDoc } 047 */ 048 @Override 049 public void requestThrottleSetup(LocoAddress address, boolean control) { 050 DCCppThrottle throttle; 051 log.debug("Requesting Throttle: {}", address); 052 if (throttles.containsKey(address)) { 053 notifyThrottleKnown(throttles.get(address), address); 054 } else { 055 if (tc.getCommandStation().requestNewRegister(address.getNumber()) == DCCppConstants.NO_REGISTER_FREE) { 056 failedThrottleRequest(address, "No Register available for Throttle. Address="+ address); 057 log.error("No Register available for Throttle. Address = {}", address); 058 return; 059 } 060 throttle = new DCCppThrottle((DCCppSystemConnectionMemo) adapterMemo, address, tc); 061 throttles.put(address, throttle); 062 notifyThrottleKnown(throttle, address); 063 } 064 } 065 066 /** 067 * DCC++ based systems DO NOT use the Dispatch Function 068 * (do they?) 069 * {@inheritDoc } 070 */ 071 @Override 072 public boolean hasDispatchFunction() { 073 return false; 074 } 075 076 /** 077 * DCC++ based systems can have multiple throttles for the same 078 * device 079 * <p> 080 * {@inheritDoc} 081 */ 082 @Override 083 protected boolean singleUse() { 084 return false; 085 } 086 087 /** 088 * Address 128 and above is a long address 089 * 090 */ 091 @Override 092 public boolean canBeLongAddress(int address) { 093 return isLongAddress(address); 094 } 095 096 /** 097 * Address between 1 and 127 is a short address 098 * 099 */ 100 @Override 101 public boolean canBeShortAddress(int address) { 102 return (address >= 1 && !isLongAddress(address)); 103 } 104 105 /** 106 * There are no ambiguous addresses on this system. 107 */ 108 @Override 109 public boolean addressTypeUnique() { 110 return true; 111 } 112 113 /** 114 * Local method for deciding short/long address. 115 * (is it?) 116 * @param num the address number to check. 117 * @return true if number greater than or equals 128. 118 */ 119 protected static boolean isLongAddress(int num) { 120 return (num >= 128); 121 } 122 123 /** 124 * {@inheritDoc } 125 * DCC++ supports 14,27,28 and 128 speed step modes 126 */ 127 @Override 128 public EnumSet<SpeedStepMode> supportedSpeedModes() { 129 return EnumSet.of(SpeedStepMode.NMRA_DCC_128); } 130 131 // Handle incoming messages for throttles. 132 @Override 133 public void message(DCCppReply r) { 134 // handle maxNumSlots and set value in commandstation 135 if (r.getElement(0) == DCCppConstants.MAXNUMSLOTS_REPLY) { 136 log.debug("MaxNumSlots reply received: {}", r); 137 tc.getCommandStation().setCommandStationMaxNumSlots(r); 138 // handle loco state reply by finding the proper throttle and asking it to update itself 139 } else if (r.getElement(0) == DCCppConstants.LOCO_STATE_REPLY){ 140 log.debug("LocoState reply received: {}", r); 141 int locoId = r.getLocoIdInt(); 142 DccLocoAddress locoAddress = new DccLocoAddress(locoId, !canBeShortAddress(locoId)); 143 if (throttles.containsKey(locoAddress)) { 144 DCCppThrottle throttle = throttles.get(locoAddress); 145 if (log.isDebugEnabled()) log.debug("Passing locoState to throttle {}", throttle.getLocoAddress()); 146 throttle.handleLocoState(r); 147 } 148 149 } else { 150 log.trace("ignoring reply: {}", r); 151 } 152 } 153 154 // listen for the messages to the command station 155 @Override 156 public void message(DCCppMessage l) { 157 } 158 159 // Handle message timeout notification 160 // If the message still has retries available, reduce retries and send it back to the traffic controller. 161 @Override 162 public void notifyTimeout(DCCppMessage msg) { 163 log.debug("Notified of timeout on message '{}' , {} retries available.", msg, msg.getRetries()); 164 if (msg.getRetries() > 0) { 165 msg.setRetries(msg.getRetries() - 1); 166 tc.sendDCCppMessage(msg, this); 167 } 168 } 169 170 @Override 171 public boolean disposeThrottle(DccThrottle t, ThrottleListener l) { 172 if (super.disposeThrottle(t, l)) { 173 //ask command station to forget this cab 174 DCCppMessage msg = DCCppMessage.makeForgetCabMessage(t.getLocoAddress().getNumber()); 175 tc.sendDCCppMessage(msg, this); 176 //release the "register" for this cab 177 tc.getCommandStation().releaseRegister(t.getLocoAddress().getNumber()); 178 if (t instanceof DCCppThrottle) { 179 DCCppThrottle lnt = (DCCppThrottle) t; 180 throttles.remove(lnt.getLocoAddress()); // remove from throttles map. 181 lnt.throttleDispose(); 182 return true; 183 } 184 } 185 return false; 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 @Override 192 public void dispose() { 193 tc.removeDCCppListener(DCCppInterface.THROTTLE, this); 194 //stopThrottleRequestTimer(); no timer used in this tm 195 } 196 197 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DCCppThrottleManager.class); 198 199}