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