001package jmri.jmrix.mrc; 002 003import java.util.ArrayList; 004import java.util.Date; 005import java.util.List; 006import javax.annotation.Nonnull; 007 008import jmri.ProgrammingMode; 009import jmri.jmrix.AbstractProgrammer; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Convert the jmri.Programmer interface into commands for the MRC power house. 015 * <p> 016 * This has two states: NOTPROGRAMMING, and COMMANDSENT. The transitions to and 017 * from programming mode are now handled in the TrafficController code. 018 * 019 * @author Bob Jacobsen Copyright (C) 2002 020 * @author Ken Cameron Copyright (C) 2014 021 * @author Kevin Dickerson Copyright (C) 2014 022 */ 023public class MrcProgrammer extends AbstractProgrammer implements MrcTrafficListener { 024 025 protected MrcSystemConnectionMemo memo; 026 027 // the traffic controller this programmer is listening to for a reply, 028 // kept so that the listener is removed from the controller it was added 029 // to; null when not listening 030 private MrcTrafficController listeningTo = null; 031 032 public MrcProgrammer(MrcSystemConnectionMemo memo) { 033 this.memo = memo; 034 super.SHORT_TIMEOUT = 15000; 035 super.LONG_TIMEOUT = 700000; 036 } 037 038 int PACKET_TIMEOUT = 5000; 039 int PACKET_READTIMEOUT = 650000; 040 041 /** 042 * {@inheritDoc} 043 * 044 * Types implemented here. 045 */ 046 @Override 047 @Nonnull 048 public List<ProgrammingMode> getSupportedModes() { 049 List<ProgrammingMode> retval = new ArrayList<ProgrammingMode>(); 050 retval.add(AUTOMATICMODE); 051 return retval; 052 } 053 054 static final ProgrammingMode AUTOMATICMODE = new ProgrammingMode("Automatic", Bundle.getMessage("MrcAutomaticMode")); 055 056 /** 057 * {@inheritDoc} 058 */ 059 @Override 060 public boolean getCanRead() { 061 return true; 062 } 063 064 /** 065 * {@inheritDoc} 066 */ 067 @Override 068 public boolean getCanWrite() { 069 return true; 070 } 071 072 /** 073 * {@inheritDoc} 074 * 075 * CV1 to 1024 valid 076 */ 077 @Override 078 public boolean getCanWrite(String cv) { 079 if (Integer.parseInt(cv) > 1024) { 080 return false; 081 } 082 return true; 083 } 084 085 // members for handling the programmer interface 086 int progState = 0; 087 static final int NOTPROGRAMMING = 0;// is notProgramming 088 static final int READCOMMANDSENT = 2; // read command sent, waiting reply 089 static final int WRITECOMMANDSENT = 4; // POM write command sent 090 static final int POMCOMMANDSENT = 6; // ops programming mode, send msg twice 091 boolean _progRead = false; 092 int _val; // remember the value being read/written for confirmative reply 093 int _cv; // remember the cv being read/written 094 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 public synchronized void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 100 final int CV = Integer.parseInt(CVname); 101 log.debug("writeCV {} listens {}", CV, p); // NOI18N 102 useProgrammer(p); 103 _progRead = false; 104 // set state 105 progState = WRITECOMMANDSENT; 106 _val = val; 107 _cv = CV; 108 109 try { 110 // start the error timer 111 startShortTimer();//we get no confirmation back that the packet has been read. 112 // format and send the write message 113 listenForReply().sendMrcMessage(progTaskStart(getMode(), _val, _cv)); 114 } catch (jmri.ProgrammerException e) { 115 progState = NOTPROGRAMMING; 116 throw e; 117 } 118 } 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 public void confirmCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 125 readCV(CVname, p); 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public synchronized void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException { 133 final int CV = Integer.parseInt(CVname); 134 log.debug("readCV {} listens {}", CV, p); // NOI18N 135 useProgrammer(p); 136 _progRead = true; 137 138 // set commandPending state 139 progState = READCOMMANDSENT; 140 _cv = CV; 141 142 try { 143 // start the error timer 144 startLongTimer(); 145 146 // format and send the read message 147 listenForReply().sendMrcMessage(progTaskStart(getMode(), -1, _cv)); 148 } catch (jmri.ProgrammerException e) { 149 progState = NOTPROGRAMMING; 150 throw e; 151 } 152 } 153 154 private jmri.ProgListener _usingProgrammer = null; 155 156 // internal method to remember who's using the programmer 157 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 158 // test for only one! 159 if (_usingProgrammer != null && _usingProgrammer != p) { 160 if (log.isInfoEnabled()) { 161 log.info("programmer already in use by {}", _usingProgrammer); // NOI18N 162 } 163 throw new jmri.ProgrammerException("programmer in use"); // NOI18N 164 } else { 165 _usingProgrammer = p; 166 return; 167 } 168 } 169 170 // internal method to create the MrcMessage for programmer task start 171 /* todo MRC doesn't set the prog mode the command station sorts it out.*/ 172 protected MrcMessage progTaskStart(ProgrammingMode mode, int val, int cvnum) throws jmri.ProgrammerException { 173 // val = -1 for read command; mode is direct, etc 174 MrcMessage m; 175 if (val < 0) { 176 // read 177 178 m = MrcMessage.getReadCV(cvnum); 179 } else { 180 m = MrcMessage.getWriteCV(cvnum, val); 181 } 182 m.setTimeout(PACKET_TIMEOUT); 183 m.setSource(this); 184 return m; 185 } 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override 191 public synchronized void notifyXmit(Date timestamp, MrcMessage m) { 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 public synchronized void notifyFailedXmit(Date timestamp, MrcMessage m) { 199 if (progState == NOTPROGRAMMING && m.getMessageClass() != MrcInterface.PROGRAMMING) { 200 return; 201 } 202 timeout(); 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 @Override 209 public synchronized void notifyRcv(Date timestamp, MrcMessage m) { 210 //public synchronized void message(MrcMessage m) { 211 if (progState == NOTPROGRAMMING) { 212 // we get the complete set of replies now, so ignore these 213 log.debug("reply in NOTPROGRAMMING state"); // NOI18N 214 return; 215 } 216 if (m.getMessageClass() != MrcInterface.PROGRAMMING) { 217 return; 218 } 219 if (MrcPackets.startsWith(m, MrcPackets.PROGCMDSENT)) { 220 progState = NOTPROGRAMMING; 221 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 222 } else if (MrcPackets.startsWith(m, MrcPackets.READCVHEADERREPLY) && progState == READCOMMANDSENT) { 223 progState = NOTPROGRAMMING; 224 //Currently we have no way to know if the write was sucessful or not. 225 if (_progRead) { 226 log.debug("prog Read {}", _cv); 227 // read was in progress - get return value 228 _val = m.value(); 229 } 230 // if this was a read, we retrieved the value above. If its a 231 // write, we're to return the original write value 232 log.debug("Has value {}", _val); // NOI18N 233 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 234 235 } else { 236 log.debug("reply in un-decoded state cv:{} {}", _cv, m.toString()); // NOI18N 237 } 238 } 239 240 /** 241 * {@inheritDoc} 242 * 243 * Internal routine to handle a timeout 244 */ 245 @Override 246 protected synchronized void timeout() { 247 if (progState != NOTPROGRAMMING) { 248 // we're programming, time to stop 249 log.debug("timeout!{}", _cv); 250 // perhaps no loco present? Fail back to end of programming 251 progState = NOTPROGRAMMING; 252 cleanup(); 253 notifyProgListenerEnd(_val, jmri.ProgListener.FailedTimeout); 254 } 255 } 256 257 // Internal method to cleanup in case of a timeout. Separate routine 258 // so it can be changed in subclasses. 259 void cleanup() { 260 } 261 262 // start listening for the reply to a programming message 263 private MrcTrafficController listenForReply() { 264 listeningTo = memo.getMrcTrafficController(); 265 listeningTo.addTrafficListener(MrcInterface.PROGRAMMING, this); 266 return listeningTo; 267 } 268 269 // internal method to notify of the final result 270 protected void notifyProgListenerEnd(int value, int status) { 271 log.debug("notifyProgListenerEnd value {} status {}", value, status); // NOI18N 272 // the programmingOpReply handler might send an immediate reply, so 273 // clear the current listener _first_ 274 if (listeningTo != null) { 275 listeningTo.removeTrafficListener(MrcInterface.PROGRAMMING, this); 276 listeningTo = null; 277 } 278 jmri.ProgListener temp = _usingProgrammer; 279 _usingProgrammer = null; 280 notifyProgListenerEnd(temp,value,status); 281 } 282 283 private static final Logger log = LoggerFactory.getLogger(MrcProgrammer.class); 284}