001package jmri.jmrix.qsi; 002 003import java.util.ArrayList; 004import java.util.List; 005import javax.annotation.Nonnull; 006 007import jmri.ProgrammingMode; 008import jmri.jmrix.AbstractProgrammer; 009 010/** 011 * Implements the jmri.Programmer interface via commands for the QSI programmer. 012 * 013 * @author Bob Jacobsen Copyright (C) 2001, 2008 014 */ 015public class QsiProgrammer extends AbstractProgrammer implements QsiListener { 016 017 private QsiSystemConnectionMemo _memo = null; 018 019 protected QsiProgrammer(QsiSystemConnectionMemo memo) { 020 _memo = memo; 021 } 022 023 /** 024 * {@inheritDoc} 025 */ 026 @Override 027 @Nonnull 028 public List<ProgrammingMode> getSupportedModes() { 029 List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>(); 030 ret.add(ProgrammingMode.PAGEMODE); 031 ret.add(ProgrammingMode.DIRECTBITMODE); 032 return ret; 033 } 034 035 // members for handling the programmer interface 036 int progState = 0; 037 static final int NOTPROGRAMMING = 0; // is notProgramming 038 static final int COMMANDSENT = 2; // read/write command sent, waiting ack 039 static final int WAITRESULT = 4; // waiting reply with data 040 static final int WAITRESETSTATUS = 6; // waiting reply from reseting status 041 boolean _progRead = false; 042 int _val; // remember the value being read/written for confirmative reply 043 int _cv; // remember the cv being read/written 044 045 /** 046 * {@inheritDoc} 047 */ 048 @Override 049 public synchronized void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 050 final int CV = Integer.parseInt(CVname); 051 log.debug("writeCV {} listens {}", CV, p); 052 useProgrammer(p); 053 _progRead = false; 054 // set commandPending state 055 progState = COMMANDSENT; 056 _val = val; 057 _cv = CV; 058 059 // start the error timer 060 startShortTimer(); 061 062 // format and send message to do write 063 controller().sendQsiMessage(QsiMessage.getWriteCV(CV, val, getMode()), this); 064 } 065 066 /** 067 * {@inheritDoc} 068 */ 069 @Override 070 public synchronized void confirmCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 071 readCV(CV, p); 072 } 073 074 /** 075 * {@inheritDoc} 076 */ 077 @Override 078 public synchronized void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException { 079 final int CV = Integer.parseInt(CVname); 080 log.debug("writeCV {} listens {}", CV, p); 081 useProgrammer(p); 082 _progRead = true; 083 // set commandPending state 084 progState = COMMANDSENT; 085 _cv = CV; 086 087 // start the error timer 088 startShortTimer(); 089 090 // format and send message to do read 091 // QSI programer is in program mode by default but this doesn't do any harm 092 controller().sendQsiMessage(QsiMessage.getReadCV(CV, getMode()), this); 093 } 094 095 private jmri.ProgListener _usingProgrammer = null; 096 097 // internal method to remember who's using the programmer 098 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 099 // test for only one! 100 if (_usingProgrammer != null && _usingProgrammer != p) { 101 log.info("programmer already in use by {}", _usingProgrammer); 102 throw new jmri.ProgrammerException("programmer in use"); 103 } else { 104 _usingProgrammer = p; 105 return; 106 } 107 } 108 109 /** 110 * {@inheritDoc} 111 */ 112 @Override 113 public void message(QsiMessage m) { 114 log.error("message received unexpectedly: {}", m); 115 } 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 synchronized public void reply(QsiReply m) { 122 if (progState == NOTPROGRAMMING) { 123 // we get the complete set of replies now, so ignore these 124 log.debug("reply in NOTPROGRAMMING state"); 125 return; 126 } else if (progState == COMMANDSENT) { 127 log.debug("reply in COMMANDSENT state"); 128 // operation started, move to next mode 129 progState = WAITRESULT; 130 startLongTimer(); 131 } else if (progState == WAITRESULT) { 132 if (log.isDebugEnabled()) { 133 log.debug("reply in WAITRESULT state"); 134 } 135 stopTimer(); 136 // send QSI ack 137 controller().sendQsiMessage(QsiReply.getAck(m), null); 138 // operation done, capture result, then leave programming mode 139 progState = NOTPROGRAMMING; 140 // check for errors 141 if (m.getElement(4) != 0) { 142 // status present 143 log.debug("handle non-zero status in reply {}", m); 144 // perhaps no loco present? 145 // reset status 146 progState = WAITRESETSTATUS; 147 startShortTimer(); 148 controller().sendQsiMessage(QsiMessage.getClearStatus(), this); 149 } else { 150 // ended OK! 151 if (_progRead == true) { 152 _val = m.value(); 153 } 154 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 155 } 156 } else if (progState == WAITRESETSTATUS) { 157 log.debug("reply in WAITRESETSTATUS state"); 158 // all done, notify listeners of completion 159 progState = NOTPROGRAMMING; 160 stopTimer(); 161 // notify of default error (not timeout) 162 notifyProgListenerEnd(-1, jmri.ProgListener.NoLocoDetected); 163 } else { 164 log.debug("reply in un-decoded state"); 165 } 166 } 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override 172 synchronized protected void timeout() { 173 if (progState != NOTPROGRAMMING) { 174 // we're programming, time to stop 175 log.debug("timeout!"); 176 // perhaps no loco present? Fail back to end of programming 177 progState = NOTPROGRAMMING; 178 // send message to clear error 179 controller().sendQsiMessage(QsiMessage.getClearStatus(), null); 180 // report timeout 181 notifyProgListenerEnd(_val, jmri.ProgListener.FailedTimeout); 182 } 183 } 184 185 // internal method to notify of the final result 186 protected void notifyProgListenerEnd(int value, int status) { 187 log.debug("notifyProgListenerEnd value {} status {}", value, status); 188 // the programmingOpReply handler might send an immediate reply, so 189 // clear the current listener _first_ 190 jmri.ProgListener temp = _usingProgrammer; 191 _usingProgrammer = null; 192 notifyProgListenerEnd(temp, value, status); 193 } 194 195 QsiTrafficController _controller = null; 196 197 protected QsiTrafficController controller() { 198 // connect the first time 199 if (_controller == null) { 200 _controller = _memo.getQsiTrafficController(); 201 } 202 return _controller; 203 } 204 205 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(QsiProgrammer.class); 206 207}