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