001package jmri.jmrix.ecos; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.List; 005import jmri.jmrix.AbstractMRListener; 006import jmri.jmrix.AbstractMRMessage; 007import jmri.jmrix.AbstractMRReply; 008import jmri.jmrix.AbstractMRTrafficController; 009 010/** 011 * Converts Stream-based I/O to/from ECOS messages. The "EcosInterface" side 012 * sends/receives message objects. 013 * <p> 014 * The connection to a EcosPortController is via a pair of *Streams, which then 015 * carry sequences of characters for transmission. Note that this processing is 016 * handled in an independent thread. 017 * <p> 018 * This handles the state transitions, based on the necessary state in each 019 * message. 020 * 021 * @author Bob Jacobsen Copyright (C) 2001 022 */ 023public class EcosTrafficController extends AbstractMRTrafficController implements EcosInterface { 024 025 /** 026 * Create a new EcosTrafficController instance. 027 */ 028 public EcosTrafficController() { 029 super(); 030 log.debug("creating a new EcosTrafficController object"); 031 // set as command station too 032 this.setAllowUnexpectedReply(true); 033 this.setSynchronizeRx(false); 034 } 035 036 public void setAdapterMemo(EcosSystemConnectionMemo memo) { 037 adaptermemo = memo; 038 } 039 040 EcosSystemConnectionMemo adaptermemo; 041 042 /** {@inheritDoc} */ 043 @Override 044 public synchronized void addEcosListener(EcosListener l) { 045 this.addListener(l); 046 } 047 048 /** {@inheritDoc} */ 049 @Override 050 public synchronized void removeEcosListener(EcosListener l) { 051 this.removeListener(l); 052 } 053 054 /** {@inheritDoc} */ 055 @Override 056 protected int enterProgModeDelayTime() { 057 // we should to wait at least a second after enabling the programming track 058 return 1000; 059 } 060 061 /** 062 * {@inheritDoc} 063 * Forward an EcosMessage to all registered EcosInterface listeners. 064 */ 065 @Override 066 protected void forwardMessage(AbstractMRListener client, AbstractMRMessage m) { 067 ((EcosListener) client).message((EcosMessage) m); 068 } 069 070 /** 071 * {@inheritDoc} 072 * Forward a EcosReply to all registered EcosInterface listeners. 073 */ 074 @Override 075 protected void forwardReply(AbstractMRListener client, AbstractMRReply r) { 076 ((EcosListener) client).reply((EcosReply) r); 077 } 078 079 /** {@inheritDoc} */ 080 @Override 081 protected AbstractMRMessage pollMessage() { 082 return null; 083 } 084 085 /** {@inheritDoc} */ 086 @Override 087 protected AbstractMRListener pollReplyHandler() { 088 return null; 089 } 090 091 /** {@inheritDoc} */ 092 @Override 093 public void sendEcosMessage(EcosMessage m, EcosListener reply) { 094 sendMessage(m, reply); 095 } 096 097 /** {@inheritDoc} */ 098 @Override 099 protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) { 100 super.forwardToPort(m, reply); 101 } 102 103 protected boolean unsolicitedSensorMessageSeen = false; 104 105 /** 106 * ECoS doesn't support this function. 107 */ 108 @Override 109 protected AbstractMRMessage enterProgMode() { 110 return EcosMessage.getProgMode(); 111 } 112 113 /** 114 * ECoS doesn't support this function. 115 */ 116 @Override 117 protected AbstractMRMessage enterNormalMode() { 118 return EcosMessage.getExitProgMode(); 119 } 120 121 @SuppressFBWarnings(value = "MS_PKGPROTECT") 122 // SpotBugs wants this package protected, but we're removing it when multi-connection 123 // migration is complete 124 final static protected EcosTrafficController self = null; 125 126 /** {@inheritDoc} */ 127 @Override 128 protected AbstractMRReply newReply() { 129 EcosReply reply = new EcosReply(); 130 return reply; 131 } 132 133 /** 134 * {@inheritDoc} 135 * @return for now, receive always OK 136 */ 137 @Override 138 protected boolean canReceive() { 139 return true; 140 } 141 142 /** {@inheritDoc} */ 143 @Override 144 protected boolean endOfMessage(AbstractMRReply msg) { 145 // detect that the reply buffer ends with "COMMAND: " (note ending 146 // space) 147 int num = msg.getNumDataElements(); 148 // ptr is offset of last element in EcosReply 149 int ptr = num - 1; 150 151 if ((num >= 2) 152 && // check NL at end of buffer 153 (msg.getElement(ptr) == 0x0A) 154 && (msg.getElement(ptr - 1) == 0x0D) 155 && (msg.getElement(ptr - 2) == '>')) { 156 157 // this might be end of element, check for "<END " 158 return ((EcosReply) msg).containsEnd(); 159 } 160 161 // otherwise, it's not the end 162 return false; 163 } 164 165 public boolean sendWaitMessage(EcosMessage m, AbstractMRListener reply) { 166 if (log.isDebugEnabled()) { 167 log.debug("Send a message and wait for the response"); 168 } 169 if (ostream == null) { 170 return false; 171 } 172 m.setTimeout(500); 173 m.setRetries(10); 174 synchronized (this) { 175 forwardToPort(m, reply); 176 // wait for reply 177 try { 178 if (xmtRunnable != null) { 179 synchronized (xmtRunnable) { 180 xmtRunnable.wait(m.getTimeout()); 181 } 182 } 183 } catch (InterruptedException e) { 184 Thread.currentThread().interrupt(); // retain if needed later 185 log.error("transmit interrupted"); 186 return false; 187 } 188 } 189 return true; 190 } 191 192 /** {@inheritDoc} */ 193 @Override 194 protected void terminate() { 195 if (log.isDebugEnabled()) { 196 log.debug("Cleanup Starts"); 197 } 198 if (ostream == null) { 199 return; // no connection established 200 } 201 EcosPreferences p = adaptermemo.getPreferenceManager(); 202 if (p.getAdhocLocoFromEcos() == 0x01) { 203 return; //Just a double check that we can delete locos 204 } //AbstractMRMessage modeMsg=enterNormalMode(); 205 AbstractMRMessage modeMsg; 206 List<String> en; 207 String ecosObject; 208 209 modeMsg = new EcosMessage("release(10, view)"); 210 modeMsg.setTimeout(50); 211 modeMsg.setRetries(10); 212 synchronized (this) { 213 forwardToPort(modeMsg, null); 214 // wait for reply 215 try { 216 if (xmtRunnable != null) { 217 synchronized (xmtRunnable) { 218 xmtRunnable.wait(modeMsg.getTimeout()); 219 } 220 } 221 } catch (InterruptedException e) { 222 Thread.currentThread().interrupt(); // retain if needed later 223 log.error("transmit interrupted"); 224 } 225 } 226 227 EcosTurnoutManager objEcosTurnManager = adaptermemo.getTurnoutManager(); 228 en = objEcosTurnManager.getEcosObjectList(); 229 for (int i = 0; i < en.size(); i++) { 230 ecosObject = en.get(i); 231 modeMsg = new EcosMessage("release(" + ecosObject + ", view)"); 232 modeMsg.setTimeout(50); 233 modeMsg.setRetries(10); 234 synchronized (this) { 235 forwardToPort(modeMsg, null); 236 // wait for reply 237 try { 238 if (xmtRunnable != null) { 239 synchronized (xmtRunnable) { 240 xmtRunnable.wait(modeMsg.getTimeout()); 241 } 242 } 243 } catch (InterruptedException e) { 244 Thread.currentThread().interrupt(); // retain if needed later 245 log.error("transmit interrupted"); 246 } 247 } 248 } 249 250 EcosLocoAddressManager objEcosLocoManager = adaptermemo.getLocoAddressManager(); 251 en = objEcosLocoManager.getEcosObjectList(); 252 for (int i = 0; i < en.size(); i++) { 253 ecosObject = en.get(i); 254 //we only delete locos if they were a temp entry. 255 if (objEcosLocoManager.getByEcosObject(ecosObject).getEcosTempEntry()) { 256 /*The ecos can be funny in not providing control on the first request, plus we have no way to determine if we have 257 therefore we send the request twice and hope we have control, failure not to have control isn't a problem as the loco 258 will simply be left on the ecos.*/ 259 for (int x = 0; x < 4; x++) { 260 switch (x) { 261 case 3: 262 modeMsg = new EcosMessage("delete(" + ecosObject + ")"); 263 break; 264 case 2: 265 modeMsg = new EcosMessage("set(" + ecosObject + ", stop)"); 266 break; 267 default: 268 modeMsg = new EcosMessage("request(" + ecosObject + ", control)"); 269 break; 270 } 271 modeMsg.setTimeout(50); 272 modeMsg.setRetries(10); 273 synchronized (this) { 274 forwardToPort(modeMsg, null); 275 // wait for reply 276 try { 277 if (xmtRunnable != null) { 278 synchronized (xmtRunnable) { 279 xmtRunnable.wait(modeMsg.getTimeout()); 280 } 281 } 282 } catch (InterruptedException e) { 283 Thread.currentThread().interrupt(); // retain if needed later 284 log.error("transmit interrupted"); 285 } 286 } 287 } 288 } 289 290 } 291 } 292 293 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EcosTrafficController.class); 294 295}