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