001/** 002 * Consist Manager for use with the EasyDccConsist class for the 003 * consists it builds. 004 * 005 * @author Paul Bender Copyright (C) 2006 006 */ 007package jmri.jmrix.easydcc; 008 009import jmri.Consist; 010import jmri.LocoAddress; 011import jmri.DccLocoAddress; 012import jmri.implementation.AbstractConsistManager; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016public class EasyDccConsistManager extends AbstractConsistManager { 017 018 private EasyDccConsistReader reader; 019 private EasyDccSystemConnectionMemo _memo = null; 020 private EasyDccTrafficController trafficController = null; 021 022 /** 023 * Constructor - call the constructor for the superclass, and initialize the 024 * consist reader thread, which retrieves consist information from the 025 * command station. 026 * 027 * @param memo the associated connection memo 028 */ 029 public EasyDccConsistManager(EasyDccSystemConnectionMemo memo) { 030 super(); 031 reader = new EasyDccConsistReader(); 032 _memo = memo; 033 // connect to the TrafficManager 034 trafficController = memo.getTrafficController(); 035 } 036 037 /** 038 * This implementation does support advanced consists, so return true. 039 * 040 */ 041 @Override 042 public boolean isCommandStationConsistPossible() { 043 return true; 044 } 045 046 /** 047 * Does a CS consist require a separate consist address? CS consist 048 * addresses are assigned by the user, so return true. 049 * 050 */ 051 @Override 052 public boolean csConsistNeedsSeperateAddress() { 053 return true; 054 } 055 056 /** 057 * Add a new EasyDccConsist with the given address to 058 * consistTable/consistList. 059 */ 060 @Override 061 public Consist addConsist(LocoAddress address) { 062 if (! (address instanceof DccLocoAddress)) { 063 throw new IllegalArgumentException("address is not a DccLocoAddress object"); 064 } 065 if (consistTable.containsKey(address)) { // no duplicates allowed across all connections 066 return consistTable.get(address); 067 } 068 EasyDccConsist consist; 069 consist = new EasyDccConsist((DccLocoAddress) address, _memo); 070 consistTable.put(address, consist); 071 notifyConsistListChanged(); 072 return consist; 073 } 074 075 /* Request an update from the layout, loading 076 * Consists from the command station. 077 */ 078 @Override 079 public void requestUpdateFromLayout() { 080 if (this.shouldRequestUpdateFromLayout()) { 081 reader.searchNext(); 082 } 083 } 084 085 @Override 086 protected boolean shouldRequestUpdateFromLayout() { 087 return (reader.currentState == EasyDccConsistReader.IDLE); 088 } 089 090 /** 091 * Internal class to read consists from the command station. 092 */ 093 private class EasyDccConsistReader implements Runnable, EasyDccListener { 094 095 // Storage for addresses 096 int _lastAddress = 0; 097 // Possible States 098 final static int IDLE = 0; 099 final static int SEARCHREQUESTSENT = 1; 100 // Current State 101 int currentState = IDLE; 102 103 EasyDccConsistReader() { 104 } 105 106 @Override 107 public void run() { 108 } 109 110 private void searchNext() { 111 log.debug("Sending request for next consist, _lastAddress is: {}", _lastAddress); 112 currentState = SEARCHREQUESTSENT; 113 EasyDccMessage msg = EasyDccMessage.getDisplayConsist(++_lastAddress); 114 trafficController.sendEasyDccMessage(msg, this); 115 } 116 117 // Listener for messages from the command station 118 @Override 119 public void reply(EasyDccReply r) { 120 if (currentState == SEARCHREQUESTSENT) { 121 // We sent a request for a consist address. 122 // We need to find out what type of message 123 // was received as a response. If the message 124 // has an opcode of 'G', then it is a response 125 // to the Display Consist instruction we sent 126 // previously. If the message has any other 127 // opcode, we can ignore the message. 128 if (log.isDebugEnabled()) { 129 log.debug("Message Received in SEARCHREQUESTSENT state. Message is: {}", r.toString()); 130 } 131 if (r.getOpCode() == 'G') { 132 // This is the response we're looking for 133 // The bytes 2 and 3 are the ... 134 135 int consistAddr; 136 Boolean newConsist = true; 137 EasyDccConsist currentConsist = null; 138 String sa = "" + (char) r.getElement(1) 139 + (char) r.getElement(2); 140 consistAddr = Integer.valueOf(sa, 16).intValue(); 141 142 // The rest of the message consists of 4 hex digits 143 // for each of up to 8 locomotives. 144 for (int i = 3; i < r.getNumDataElements(); i += 4) { 145 DccLocoAddress locoAddress; 146 int tempAddr; 147 boolean directionNormal; 148 if ((char) r.getElement(i) == ' ') { 149 i++; // skip a space 150 } else if (java.lang.Character.digit((char) r.getElement(i), 16) == -1) { 151 break; // stop the loop if we don't have a hex digit. 152 } 153 String sb = "" + (char) r.getElement(i) 154 + (char) r.getElement(i + 1) 155 + (char) r.getElement(i + 2) 156 + (char) r.getElement(i + 3); 157 tempAddr = Integer.valueOf(sb, 16).intValue(); 158 directionNormal = ((tempAddr & 0x8000) == 0); 159 if (tempAddr != 0) { 160 if (newConsist) { 161 // This is the first address, add the 162 // consist 163 currentConsist = (EasyDccConsist) addConsist( 164 new DccLocoAddress(consistAddr, false)); 165 newConsist = false; 166 } 167 locoAddress = new DccLocoAddress( 168 tempAddr & 0x7fff, (tempAddr & 0x7fff) > 99); 169 if (currentConsist != null) { 170 currentConsist.restore(locoAddress, directionNormal); 171 } else //should never happen since currentConsist gets set in the first pass 172 { 173 log.error("currentConsist is null!"); 174 } 175 } 176 } 177 if (_lastAddress < 255) { 178 searchNext(); 179 } else { 180 currentState = IDLE; 181 notifyConsistListChanged(); 182 } 183 } else { 184 if (log.isDebugEnabled()) { 185 log.debug("Message Received in IDLE state. Message is: {}", r.toString()); 186 } 187 } 188 } 189 } 190 191 // Listener for messages to the command station 192 @Override 193 public void message(EasyDccMessage m) { 194 } 195 } 196 197 private final static Logger log = LoggerFactory.getLogger(EasyDccConsistManager.class); 198 199}