001package jmri.jmrix.lenz; 002 003import jmri.Consist; 004import jmri.LocoAddress; 005import jmri.DccLocoAddress; 006import jmri.implementation.AbstractConsistManager; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Consist Manager for use with the XNetConsist class for the consists it builds 012 * @author Paul Bender Copyright (C) 2004-2010 013 * @navassoc 1 - * jmri.jmrix.lenz.XNetConsist 014 */ 015public class XNetConsistManager extends AbstractConsistManager { 016 017 protected XNetTrafficController tc; 018 private boolean requestingUpdate = false; 019 020 /** 021 * Constructor - call the constructor for the superclass, and initialize the 022 * consist reader thread, which retrieves consist information from the 023 * command station. 024 * @param systemMemo system connection. 025 */ 026 public XNetConsistManager(XNetSystemConnectionMemo systemMemo) { 027 super(); 028 tc = systemMemo.getXNetTrafficController(); 029 this.systemMemo = systemMemo; 030 } 031 final XNetSystemConnectionMemo systemMemo; 032 033 /** 034 * This implementation does command station consists, so return true. 035 */ 036 @Override 037 public boolean isCommandStationConsistPossible() { 038 return true; 039 } 040 041 /** 042 * Does a CS consist require a separate consist address? CS consist 043 * addresses are assigned by the command station, so no consist address is 044 * needed, so return false. 045 */ 046 @Override 047 public boolean csConsistNeedsSeperateAddress() { 048 return false; 049 } 050 051 /** 052 * Add a new XNetConsist with the given address to consistTable/consistList. 053 */ 054 @Override 055 public Consist addConsist(LocoAddress address) { 056 if (! (address instanceof DccLocoAddress)) { 057 throw new IllegalArgumentException("address is not a DccLocoAddress object"); 058 } 059 if (consistTable.containsKey(address)) { // no duplicates allowed. 060 return consistTable.get(address); 061 } 062 XNetConsist consist; 063 consist = new XNetConsist((DccLocoAddress) address, tc, systemMemo); 064 consistTable.put(address, consist); 065 notifyConsistListChanged(); 066 return (consist); 067 } 068 069 /** 070 * Request an update from the layout, loading Consists from the command 071 * station. 072 */ 073 @Override 074 public void requestUpdateFromLayout() { 075 if (shouldRequestUpdateFromLayout()) { 076 // Initilize the consist reader. 077 new XNetConsistReader(); 078 } 079 } 080 081 @Override 082 protected boolean shouldRequestUpdateFromLayout() { 083 return !requestingUpdate; 084 } 085 086 /** 087 * Internal class to read consists from the Command Station. 088 */ 089 private class XNetConsistReader implements XNetListener { 090 091 // Storage for addresses 092 int _lastMUAddress = 0; 093 int _lastAddress = 0; 094 int _lastMemberAddress = 0; 095 XNetConsist currentConsist = null; 096 // Possible States 097 static final int IDLE = 0; 098 static final int SEARCHREQUESTSENT = 1; 099 static final int MUSEARCHSENT = 2; 100 static final int MUINFOREQUESTSENT = 4; 101 static final int DHADDRESS1INFO = 8; 102 static final int DHADDRESS2INFO = 16; 103 // Current State 104 int currentState = IDLE; 105 106 XNetConsistReader() { 107 // Register as an XPressNet Listener 108 tc.addXNetListener(XNetInterface.COMMINFO 109 | XNetInterface.THROTTLE 110 | XNetInterface.CONSIST, 111 this); 112 searchNext(); 113 } 114 115 private void searchNext() { 116 requestingUpdate = true; 117 log.debug("Sending search for next DB Entry, _lastAddress is: {}",_lastAddress); 118 currentState = SEARCHREQUESTSENT; 119 XNetMessage msg = XNetMessage.getNextAddressOnStackMsg(_lastAddress, true); 120 tc.sendXNetMessage(msg, this); 121 } 122 123 private void searchMU() { 124 log.debug("Sending search for next MU Entry, _lastMUAddress is: {} _lastMemberAddress is: {}",_lastMUAddress,_lastMemberAddress); 125 currentState = MUSEARCHSENT; 126 XNetMessage msg = XNetMessage.getDBSearchMsgNextMULoco(_lastMUAddress, _lastMemberAddress, true); 127 tc.sendXNetMessage(msg, this); 128 } 129 130 private void requestInfoMU() { 131 log.debug("Sending search for next MU Entry information , _lastMemberAddress is: {}",_lastMemberAddress); 132 currentState = MUINFOREQUESTSENT; 133 XNetMessage msg = XNetMessage.getLocomotiveInfoRequestMsg(_lastMemberAddress); 134 tc.sendXNetMessage(msg, this); 135 } 136 137 // Listener for messages from the command station 138 @Override 139 public void message(XNetReply l) { 140 switch (currentState) { 141 case SEARCHREQUESTSENT: 142 // We sent a request to search the stack. 143 // We need to find out what type of message 144 // was recived as a response. If We're 145 // told the message is for an MU base address 146 // a locomotive in a Double Header, we 147 // want to take further action. If the 148 // message tells us we've reached the end 149 // of the stack, then we can quit. Otherwise, 150 // we just request the next address. 151 if (log.isDebugEnabled()) { 152 log.debug("Message Received in SEARCHREQUESTSENT state. Message is: {}",l); 153 } 154 if (l.getElement(0) == XNetConstants.LOCO_INFO_RESPONSE) { 155 switch (l.getElement(1)) { 156 case XNetConstants.LOCO_SEARCH_RESPONSE_N: 157 case XNetConstants.LOCO_SEARCH_RESPONSE_MU: 158 _lastAddress = l.getThrottleMsgAddr(); 159 searchNext(); 160 break; 161 case XNetConstants.LOCO_SEARCH_RESPONSE_DH: 162 _lastAddress = l.getThrottleMsgAddr(); 163 _lastMUAddress = _lastAddress; 164 _lastMemberAddress = _lastAddress; 165 if (log.isDebugEnabled()) { 166 log.debug("Sending search for first DH Entry information , _lastMemberAddress is: {}", _lastMemberAddress); 167 } 168 currentState = DHADDRESS1INFO; 169 XNetMessage msg = XNetMessage.getLocomotiveInfoRequestMsg(_lastMemberAddress); 170 tc.sendXNetMessage(msg, this); 171 break; 172 case XNetConstants.LOCO_SEARCH_RESPONSE_MU_BASE: 173 _lastAddress = l.getThrottleMsgAddr(); 174 _lastMUAddress = _lastAddress; 175 currentConsist = (XNetConsist) addConsist( 176 new DccLocoAddress(_lastMUAddress, false)); 177 searchMU(); 178 break; 179 case XNetConstants.LOCO_SEARCH_NO_RESULT: 180 currentState = IDLE; 181 notifyConsistListChanged(); 182 requestingUpdate = false; 183 break; 184 case XNetConstants.LOCO_NOT_AVAILABLE: 185 case XNetConstants.LOCO_FUNCTION_STATUS: 186 default: // Do Nothing by default 187 } 188 } 189 break; 190 case MUSEARCHSENT: 191 if (log.isDebugEnabled()) { 192 log.debug("Message Received in MUSEARCHSENT state. Message is: {}",l); 193 } 194 if (l.getElement(0) == XNetConstants.LOCO_INFO_RESPONSE) { 195 switch (l.getElement(1)) { 196 case XNetConstants.LOCO_SEARCH_RESPONSE_MU: 197 _lastMemberAddress = l.getThrottleMsgAddr(); 198 if (_lastMemberAddress != 0) { 199 // Find out the direction information 200 // for the address in question. 201 requestInfoMU(); 202 } else { 203 // We reached the end of this consist, 204 // find the next one 205 searchNext(); 206 } 207 break; 208 case XNetConstants.LOCO_SEARCH_NO_RESULT: 209 searchNext(); 210 break; 211 case XNetConstants.LOCO_SEARCH_RESPONSE_DH: 212 case XNetConstants.LOCO_SEARCH_RESPONSE_MU_BASE: 213 case XNetConstants.LOCO_SEARCH_RESPONSE_N: 214 case XNetConstants.LOCO_NOT_AVAILABLE: 215 case XNetConstants.LOCO_FUNCTION_STATUS: 216 default: // Do Nothing by default 217 } 218 } 219 break; 220 case MUINFOREQUESTSENT: 221 if (log.isDebugEnabled()) { 222 log.debug("Message Received in MUINFOREQUESTSENT state. Message is: {}",l); 223 } 224 if (l.getElement(0) == XNetConstants.LOCO_INFO_MUED_UNIT) { 225 currentConsist.restore(new DccLocoAddress(_lastMemberAddress, _lastMemberAddress > 99), 226 (l.getElement(2) & 0x80) == 0x80); 227 searchMU(); 228 } 229 break; 230 case DHADDRESS1INFO: 231 if (log.isDebugEnabled()) { 232 log.debug("Message Received in DHADDRESS1INFO state. Message is: {}",l); 233 } 234 if (l.getElement(0) == XNetConstants.LOCO_INFO_DH_UNIT) { 235 DccLocoAddress firstMember = new DccLocoAddress(_lastMemberAddress, _lastMemberAddress > 99); 236 int AH = l.getElement(5); 237 int AL = l.getElement(6); 238 if (AH == 0x00) { 239 _lastMemberAddress = AL; 240 } else { 241 _lastMemberAddress = ((AH * 256) & 0xFF00) 242 + (AL & 0xFF) 243 - 0xC000; 244 } 245 246 // We need to check and see if this consist exists 247 if (!XNetConsistManager.this.consistTable.containsKey(firstMember) 248 && !XNetConsistManager.this.consistTable.containsKey(new DccLocoAddress(_lastMemberAddress, _lastMemberAddress > 99))) { 249 currentConsist = (XNetConsist) addConsist(firstMember); 250 currentConsist.setConsistType(Consist.CS_CONSIST); 251 currentConsist.restore(firstMember, 252 (l.getElement(2) & 0x80) == 0x80); 253 if (log.isDebugEnabled()) { 254 log.debug("Sending search for second DH Entry information , _lastMemberAddress is: {}", _lastMemberAddress); 255 } 256 currentState = DHADDRESS2INFO; 257 XNetMessage msg = XNetMessage 258 .getLocomotiveInfoRequestMsg( 259 _lastMemberAddress); 260 tc.sendXNetMessage(msg, this); 261 } else { 262 // This consist already exists 263 searchNext(); 264 } 265 } 266 break; 267 case DHADDRESS2INFO: 268 log.debug("Message Received in DHADDRESS2INFO state. Message is: {}",l); 269 if (l.getElement(0) == XNetConstants.LOCO_INFO_DH_UNIT) { 270 currentConsist.restore(new DccLocoAddress(_lastMemberAddress, _lastMemberAddress > 99), 271 (l.getElement(2) & 0x80) == 0x80); 272 } 273 // We reached the end of this consist, 274 // find the next one 275 searchNext(); 276 break; 277 case IDLE: 278 default: 279 log.debug("Message Received in default(IDLE) state. Message is: {}", l); 280 } 281 } 282 283 /** 284 * Listener for messages to the command station. 285 */ 286 @Override 287 public void message(XNetMessage l) { 288 // this class does not utilize outgoing messages 289 } 290 291 /** 292 * Handle a timeout notification. 293 */ 294 @Override 295 public void notifyTimeout(XNetMessage msg) { 296 if (log.isDebugEnabled()) { 297 log.debug("Notified of timeout on message {}", msg); 298 } 299 } 300 } 301 302 private static final Logger log = LoggerFactory.getLogger(XNetConsistManager.class); 303}