001package jmri.jmrix.marklin; 002 003import java.util.concurrent.ConcurrentLinkedQueue; 004import jmri.CommandStation; 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 Marklin CS2 messages. The 012 * "MarklinInterface" side sends/receives message objects. 013 * <p> 014 * The connection to a MarklinPortController is via a pair of UDP Streams, which 015 * then carry sequences of characters for transmission. Note that this 016 * processing is handled in an independent thread. 017 * <p> 018 * This handles the state transitions, based on the necessary state in each 019 * message. 020 * 021 * Based on work by Bob Jacobsen 022 * 023 * @author Kevin Dickerson Copyright (C) 2012 024 */ 025public class MarklinTrafficController extends AbstractMRTrafficController implements MarklinInterface, CommandStation { 026 027 /** 028 * Create a new MarklinTrafficController instance. 029 */ 030 public MarklinTrafficController() { 031 super(); 032 log.debug("creating a new MarklinTrafficController object"); 033 // set as command station too 034 jmri.InstanceManager.store(MarklinTrafficController.this, CommandStation.class); 035 this.setAllowUnexpectedReply(true); 036 } 037 038 public void setAdapterMemo(MarklinSystemConnectionMemo memo) { 039 adaptermemo = memo; 040 } 041 042 MarklinSystemConnectionMemo adaptermemo; 043 protected String defaultUserName = "Marklin-CS2"; 044 045 // The methods to implement the MarklinInterface 046 @Override 047 public synchronized void addMarklinListener(MarklinListener l) { 048 this.addListener(l); 049 } 050 051 @Override 052 public synchronized void removeMarklinListener(MarklinListener l) { 053 this.removeListener(l); 054 } 055 056 @Override 057 protected int enterProgModeDelayTime() { 058 // we should to wait at least a second after enabling the programming track 059 return 1000; 060 } 061 062 /** 063 * CommandStation implementation, not yet supported. 064 * {@inheritDoc } 065 */ 066 @Override 067 public boolean sendPacket(byte[] packet, int count) { 068 069 return true; 070 } 071 072 /** 073 * Forward a MarklinMessage to all registered MarklinInterface listeners. 074 */ 075 @Override 076 protected void forwardMessage(AbstractMRListener client, AbstractMRMessage m) { 077 ((MarklinListener) client).message((MarklinMessage) m); 078 } 079 080 /** 081 * Forward a MarklinReply to all registered MarklinInterface listeners. 082 * {@inheritDoc } 083 */ 084 @Override 085 protected void forwardReply(AbstractMRListener client, AbstractMRReply r) { 086 ((MarklinListener) client).reply((MarklinReply) r); 087 } 088 089 /** 090 * Forward a preformatted message to the actual interface. 091 * {@inheritDoc } 092 */ 093 @Override 094 public void sendMarklinMessage(MarklinMessage m, MarklinListener reply) { 095 sendMessage(m, reply); 096 } 097 098 /** 099 * Marklin doesn't support this function. 100 * @return empty Marklin Message. 101 */ 102 @Override 103 protected AbstractMRMessage enterProgMode() { 104 return MarklinMessage.getProgMode(); 105 } 106 107 /** 108 * Marklin doesn't support this function. 109 * @return empty Marklin Message. 110 */ 111 @Override 112 protected AbstractMRMessage enterNormalMode() { 113 return MarklinMessage.getExitProgMode(); 114 } 115 116 @Override 117 protected AbstractMRReply newReply() { 118 return new MarklinReply(); 119 } 120 121 // for now, receive always OK 122 @Override 123 protected boolean canReceive() { 124 return true; 125 } 126 127 //In theory the replies should only be 13bytes long, so the EOM is completed when the reply can take no more data 128 @Override 129 protected boolean endOfMessage(AbstractMRReply msg) { 130 return false; 131 } 132 133 private static class PollMessage { 134 135 MarklinListener ml; 136 MarklinMessage mm; 137 138 PollMessage(MarklinMessage mm, MarklinListener ml) { 139 this.mm = mm; 140 this.ml = ml; 141 } 142 143 MarklinListener getListener() { 144 return ml; 145 } 146 147 MarklinMessage getMessage() { 148 return mm; 149 } 150 } 151 152 private final ConcurrentLinkedQueue<PollMessage> pollQueue = new ConcurrentLinkedQueue<>(); 153 154 private boolean disablePoll = false; 155 156 public boolean getPollQueueDisabled() { 157 return disablePoll; 158 } 159 160 public void setPollQueueDisabled(boolean poll) { 161 disablePoll = poll; 162 } 163 164 /** 165 * As we have to poll the system to get updates we put request into a 166 * queue and allow the abstract traffic controller to handle them when it 167 * is free. 168 * @param mm marklin message to add. 169 * @param ml marklin listener. 170 */ 171 public void addPollMessage(MarklinMessage mm, MarklinListener ml) { 172 mm.setTimeout(500); 173 for (PollMessage pm : pollQueue) { 174 if (pm.getListener() == ml && pm.getMessage().toString().equals(mm.toString())) { 175 log.debug("Message is already in the poll queue so will not add"); 176 return; 177 } 178 } 179 PollMessage pm = new PollMessage(mm, ml); 180 pollQueue.offer(pm); 181 } 182 183 /** 184 * Removes a message that is used for polling from the queue. 185 * @param mm marklin message to remove. 186 * @param ml marklin listener. 187 */ 188 public void removePollMessage(MarklinMessage mm, MarklinListener ml) { 189 for (PollMessage pm : pollQueue) { 190 if (pm.getListener() == ml && pm.getMessage().toString().equals(mm.toString())) { 191 pollQueue.remove(pm); 192 } 193 } 194 } 195 196 /** 197 * Check Tams MC for updates. 198 */ 199 @Override 200 protected AbstractMRMessage pollMessage() { 201 if ( !disablePoll && !pollQueue.isEmpty()) { 202 PollMessage pm = pollQueue.peek(); 203 if (pm != null) { 204 return pm.getMessage(); 205 } 206 } 207 return null; 208 } 209 210 @Override 211 protected AbstractMRListener pollReplyHandler() { 212 if ( !disablePoll && !pollQueue.isEmpty()) { 213 PollMessage pm = pollQueue.poll(); 214 if (pm != null) { 215 pollQueue.offer(pm); 216 return pm.getListener(); 217 } 218 } 219 return null; 220 } 221 222 @Override 223 public String getUserName() { 224 if (adaptermemo == null) { 225 return defaultUserName; 226 } 227 return adaptermemo.getUserName(); 228 } 229 230 @Override 231 public String getSystemPrefix() { 232 if (adaptermemo == null) { 233 return "M"; 234 } 235 return adaptermemo.getSystemPrefix(); 236 } 237 238 public void dispose() { 239 this.terminateThreads(); 240 jmri.InstanceManager.deregister(MarklinTrafficController.this, CommandStation.class); 241 } 242 243 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MarklinTrafficController.class); 244 245}