001package jmri.jmrix.srcp; 002 003import java.util.ArrayList; 004import java.util.List; 005import javax.annotation.Nonnull; 006 007import jmri.ProgrammingMode; 008import jmri.jmrix.AbstractProgrammer; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Implements the jmri.Programmer interface via commands for the SRCP 014 * powerstation 015 * 016 * @author Bob Jacobsen Copyright (C) 2001, 2008 017 */ 018public class SRCPProgrammer extends AbstractProgrammer implements SRCPListener { 019 020 protected SRCPBusConnectionMemo _memo = null; 021 private int _bus; 022 023 public SRCPProgrammer(SRCPBusConnectionMemo memo) { 024 _bus = memo.getBus(); 025 _memo = memo; 026 // need a longer LONG_TIMEOUT 027 LONG_TIMEOUT = 180000; 028 } 029 030 /** 031 * {@inheritDoc} 032 * 033 * Types implemented here. 034 */ 035 @Override 036 @Nonnull 037 public List<ProgrammingMode> getSupportedModes() { 038 List<ProgrammingMode> ret = new ArrayList<>(); 039 ret.add(ProgrammingMode.DIRECTBYTEMODE); 040 ret.add(ProgrammingMode.REGISTERMODE); 041 return ret; 042 } 043 044 // members for handling the programmer interface 045 int progState = 0; 046 static final int NOTPROGRAMMING = 0; // is notProgramming 047 static final int COMMANDSENT = 2; // read/write command sent, waiting reply 048 boolean _progRead = false; 049 boolean _progConfirm = false; 050 int _confirmVal; // remember the value to be confirmed for reply 051 int _val; // remember the value being read/written for confirmative reply 052 int _cv; // remember the cv being read/written 053 054 /** 055 * {@inheritDoc} 056 */ 057 @Override 058 synchronized public void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 059 final int CV = Integer.parseInt(CVname); 060 if (log.isDebugEnabled()) { 061 log.debug("writeCV {} listens {}", CV, p); 062 } 063 useProgrammer(p); 064 _progRead = false; 065 _progConfirm = false; 066 // set commandPending state 067 progState = COMMANDSENT; 068 _val = val; 069 _cv = CV; 070 071 try { 072 SRCPMessage m; 073 // start the error timer 074 startLongTimer(); 075 076 // write 077 if (getMode() == ProgrammingMode.DIRECTBYTEMODE) { 078 m = SRCPMessage.getWriteDirectCV(_bus, _cv, _val); 079 } else { 080 m = SRCPMessage.getWriteRegister(_bus, registerFromCV(_cv), _val); 081 } 082 // format and send the write message 083 controller().sendSRCPMessage(m, this); 084 } catch (jmri.ProgrammerException e) { 085 progState = NOTPROGRAMMING; 086 throw e; 087 } 088 } 089 090 /** 091 * {@inheritDoc} 092 */ 093 @Override 094 synchronized public void confirmCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 095 int CV = Integer.parseInt(CVname); 096 if (log.isDebugEnabled()) { 097 log.debug("confirmCV {} val {} listens {}", CV, val, p); 098 } 099 useProgrammer(p); 100 _progRead = false; 101 _progConfirm = true; 102 103 progState = COMMANDSENT; 104 _cv = CV; 105 _confirmVal = val; 106 107 try { 108 SRCPMessage m; 109 // start the error timer 110 startLongTimer(); 111 112 if (getMode() == ProgrammingMode.DIRECTBYTEMODE) { 113 m = SRCPMessage.getConfirmDirectCV(_bus, _cv, _confirmVal); 114 } else { 115 m = SRCPMessage.getConfirmRegister(_bus, registerFromCV(_cv), _confirmVal); 116 } 117 118 // format and send the confirm message 119 controller().sendSRCPMessage(m, this); 120 } catch (jmri.ProgrammerException e) { 121 progState = NOTPROGRAMMING; 122 throw e; 123 } 124 } 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override 130 synchronized public void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException { 131 final int CV = Integer.parseInt(CVname); 132 if (log.isDebugEnabled()) { 133 log.debug("readCV {} listens {}", CV, p); 134 } 135 useProgrammer(p); 136 _progRead = true; 137 _progConfirm = false; 138 139 progState = COMMANDSENT; 140 _cv = CV; 141 142 try { 143 SRCPMessage m; 144 // start the error timer 145 startLongTimer(); 146 147 // format and send the write message 148 if (getMode() == ProgrammingMode.DIRECTBYTEMODE) { 149 m = SRCPMessage.getReadDirectCV(_bus, _cv); 150 } else { 151 m = SRCPMessage.getReadRegister(_bus, registerFromCV(_cv)); 152 } 153 154 controller().sendSRCPMessage(m, this); 155 } catch (jmri.ProgrammerException e) { 156 progState = NOTPROGRAMMING; 157 throw e; 158 } 159 160 } 161 162 private jmri.ProgListener _usingProgrammer = null; 163 164 // internal method to remember who's using the programmer 165 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 166 // test for only one! 167 if (_usingProgrammer != null && _usingProgrammer != p) { 168 if (log.isDebugEnabled()) { 169 log.debug("programmer already in use by {}", _usingProgrammer); 170 } 171 throw new jmri.ProgrammerException("programmer in use"); 172 } else { 173 _usingProgrammer = p; 174 } 175 } 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override 181 public void message(SRCPMessage m) { 182 log.error("message received unexpectedly: {}", m.toString()); 183 } 184 185 /** 186 * {@inheritDoc} 187 */ 188 @Override 189 synchronized public void reply(SRCPReply m) { 190 if (progState == NOTPROGRAMMING) { 191 // we get the complete set of replies now, so ignore these 192 if (log.isDebugEnabled()) { 193 log.debug("reply in NOTPROGRAMMING state"); 194 } 195 if (!m.isResponseOK()) { 196 log.warn("Reply \"{}\"", m.toString()); 197 } 198 } else if (progState == COMMANDSENT) { 199 if (log.isDebugEnabled()) { 200 log.debug("reply in COMMANDSENT state"); 201 } 202 // operation done, capture result, then have to leave programming mode 203 progState = NOTPROGRAMMING; 204 // check for errors 205 if (!m.isResponseOK()) { 206 if (log.isDebugEnabled()) { 207 log.debug("handle error reply {}", m); 208 } 209 log.warn("Reply \"{}\"", m.toString()); 210 if (_progConfirm && m.getResponseCode().equals("412")) { 211 // handle the Verify return message "412 ERROR wrong value" 212 notifyProgListenerEnd(_val, jmri.ProgListener.ConfirmFailed); 213 return; 214 } 215 // perhaps no loco present? Fail back to end of programming 216 notifyProgListenerEnd(-1, jmri.ProgListener.NoLocoDetected); 217 } else { 218 // see why waiting 219 if (_progRead) { 220 // read was in progress - get return value 221 _val = m.value(); 222 } 223 if (_progConfirm) { 224 _val = _confirmVal; 225 } 226 // If this was a read or verify, we retreived the value above. 227 // If its a write, we're to return the original write value. 228 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 229 } 230 } 231 } 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override 237 synchronized public void reply(jmri.jmrix.srcp.parser.SimpleNode n) { 238 if (log.isDebugEnabled()) { 239 log.debug("reply called with simpleNode {}", n.jjtGetValue()); 240 } 241 if (n.jjtGetChild(3) instanceof jmri.jmrix.srcp.parser.ASTsm) { 242 reply(new SRCPReply(n)); 243 } 244 } 245 246 /** 247 * {@inheritDoc} 248 * 249 * Internal routine to handle a timeout 250 */ 251 @Override 252 synchronized protected void timeout() { 253 if (progState != NOTPROGRAMMING) { 254 // we're programming, time to stop 255 if (log.isDebugEnabled()) { 256 log.debug("timeout!"); 257 } 258 // perhaps no loco present? Fail back to end of programming 259 progState = NOTPROGRAMMING; 260 cleanup(); 261 notifyProgListenerEnd(_val, jmri.ProgListener.FailedTimeout); 262 } 263 } 264 265 /** 266 * Internal method to send a cleanup message (if needed) on timeout. 267 * <p> 268 * Here, it sends a request to exit from programming mode. But subclasses 269 * may redefine that. 270 */ 271 void cleanup() { 272 controller().sendSRCPMessage(SRCPMessage.getExitProgMode(_bus), this); 273 } 274 275 // internal method to notify of the final result 276 protected void notifyProgListenerEnd(int value, int status) { 277 if (log.isDebugEnabled()) { 278 log.debug("notifyProgListenerEnd value {} status {}", value, status); 279 } 280 // the programmingOpReply handler might send an immediate reply, so 281 // clear the current listener _first_ 282 jmri.ProgListener temp = _usingProgrammer; 283 _usingProgrammer = null; 284 notifyProgListenerEnd(temp,value,status); 285 } 286 287 SRCPTrafficController _controller = null; 288 289 protected SRCPTrafficController controller() { 290 // connect the first time 291 if (_controller == null) { 292 _controller = _memo.getTrafficController(); 293 } 294 return _controller; 295 } 296 297 private final static Logger log = LoggerFactory.getLogger(SRCPProgrammer.class); 298 299}