001package jmri.jmrix.easydcc; 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 EasyDCC 012 * powerstation. 013 * 014 * @author Bob Jacobsen Copyright (C) 2001 015 */ 016public class EasyDccProgrammer extends AbstractProgrammer implements EasyDccListener { 017 018 public EasyDccProgrammer(EasyDccSystemConnectionMemo memo) { 019 tc = memo.getTrafficController(); 020 // need a longer LONG_TIMEOUT 021 LONG_TIMEOUT = 180000; 022 } 023 024 protected EasyDccTrafficController tc = null; 025 026 /** 027 * {@inheritDoc} 028 */ 029 @Override 030 @Nonnull 031 public List<ProgrammingMode> getSupportedModes() { 032 List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>(); 033 ret.add(ProgrammingMode.PAGEMODE); 034 ret.add(ProgrammingMode.REGISTERMODE); 035 return ret; 036 } 037 038 // members for handling the programmer interface 039 int progState = 0; 040 static final int NOTPROGRAMMING = 0;// is notProgramming 041 static final int COMMANDSENT = 2; // read/write command sent, waiting reply 042 boolean _progRead = false; 043 int _val; // remember the value being read/written for confirmative reply 044 int _cv; // remember the cv being read/written 045 046 /** 047 * {@inheritDoc} 048 */ 049 @Override 050 public synchronized void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 051 final int CV = Integer.parseInt(CVname); 052 if (log.isDebugEnabled()) { 053 log.debug("writeCV {} listens {}", CV, p); 054 } 055 useProgrammer(p); 056 _progRead = false; 057 // set commandPending state 058 progState = COMMANDSENT; 059 _val = val; 060 _cv = CV; 061 062 try { 063 // start the error timer 064 startLongTimer(); 065 066 // format and send the write message 067 tc.sendEasyDccMessage(progTaskStart(getMode(), _val, _cv), this); 068 } catch (jmri.ProgrammerException e) { 069 progState = NOTPROGRAMMING; 070 throw e; 071 } 072 } 073 074 /** 075 * {@inheritDoc} 076 */ 077 @Override 078 public synchronized void confirmCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 079 readCV(CV, p); 080 } 081 082 /** 083 * {@inheritDoc} 084 */ 085 @Override 086 public synchronized void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException { 087 final int CV = Integer.parseInt(CVname); 088 if (log.isDebugEnabled()) { 089 log.debug("readCV {} listens {}", CV, p); 090 } 091 useProgrammer(p); 092 _progRead = true; 093 094 progState = COMMANDSENT; 095 _cv = CV; 096 097 try { 098 // start the error timer 099 startLongTimer(); 100 101 // format and send the write message 102 tc.sendEasyDccMessage(progTaskStart(getMode(), -1, _cv), this); 103 } catch (jmri.ProgrammerException e) { 104 progState = NOTPROGRAMMING; 105 throw e; 106 } 107 108 } 109 110 private jmri.ProgListener _usingProgrammer = null; 111 112 // internal method to remember who's using the programmer 113 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 114 // test for only one! 115 if (_usingProgrammer != null && _usingProgrammer != p) { 116 if (log.isDebugEnabled()) { 117 log.debug("programmer already in use by {}", _usingProgrammer); 118 } 119 throw new jmri.ProgrammerException("programmer in use"); 120 } else { 121 _usingProgrammer = p; 122 return; 123 } 124 } 125 126 /** 127 * Internal method to create the EasyDccMessage for programmer task start. 128 * @param mode Programming mode to iniate 129 * @param val Value to program 130 * @param cvnum CV number to address 131 * @return formatted message for layout 132 * @throws jmri.ProgrammerException if programmer or mode not available 133 */ 134 protected EasyDccMessage progTaskStart(ProgrammingMode mode, int val, int cvnum) throws jmri.ProgrammerException { 135 // val = -1 for read command; mode is direct, etc 136 if (val < 0) { 137 // read 138 if (getMode().equals(ProgrammingMode.PAGEMODE)) { 139 return EasyDccMessage.getReadPagedCV(cvnum); 140 } else { 141 return EasyDccMessage.getReadRegister(registerFromCV(cvnum)); 142 } 143 } else { 144 // write 145 if (getMode().equals(ProgrammingMode.PAGEMODE)) { 146 return EasyDccMessage.getWritePagedCV(cvnum, val); 147 } else { 148 return EasyDccMessage.getWriteRegister(registerFromCV(cvnum), val); 149 } 150 } 151 } 152 153 /** 154 * {@inheritDoc} 155 */ 156 @Override 157 public void message(EasyDccMessage m) { 158 log.error("message received unexpectedly: {}", m.toString()); 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 synchronized public void reply(EasyDccReply m) { 166 if (progState == NOTPROGRAMMING) { 167 // we get the complete set of replies now, so ignore these 168 if (log.isDebugEnabled()) { 169 log.debug("reply in NOTPROGRAMMING state"); 170 } 171 return; 172 } else if (progState == COMMANDSENT) { 173 if (log.isDebugEnabled()) { 174 log.debug("reply in COMMANDSENT state"); 175 } 176 // operation done, capture result, then have to leave programming mode 177 progState = NOTPROGRAMMING; 178 // check for errors 179 if (m.match("--") >= 0) { 180 if (log.isDebugEnabled()) { 181 log.debug("handle error reply {}", m); 182 } 183 // perhaps no loco present? Fail back to end of programming 184 notifyProgListenerEnd(-1, jmri.ProgListener.NoLocoDetected); 185 } else { 186 // see why waiting 187 if (_progRead) { 188 // read was in progress - get return value 189 _val = m.value(); 190 } 191 // if this was a read, we retreived the value above. If its a 192 // write, we're to return the original write value 193 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 194 } 195 } 196 } 197 198 /** 199 * {@inheritDoc} 200 */ 201 @Override 202 synchronized protected void timeout() { 203 if (progState != NOTPROGRAMMING) { 204 // we're programming, time to stop 205 if (log.isDebugEnabled()) { 206 log.debug("timeout!"); 207 } 208 // perhaps no loco present? Fail back to end of programming 209 progState = NOTPROGRAMMING; 210 cleanup(); 211 notifyProgListenerEnd(_val, jmri.ProgListener.FailedTimeout); 212 } 213 } 214 215 /** 216 * Internal method to send a cleanup message (if needed) on timeout. 217 * <p> 218 * Here, it sends a request to exit from programming mode. But subclasses, 219 * e.g. ops mode, may redefine that. 220 */ 221 void cleanup() { 222 tc.sendEasyDccMessage(EasyDccMessage.getExitProgMode(), this); 223 } 224 225 // internal method to notify of the final result 226 protected void notifyProgListenerEnd(int value, int status) { 227 if (log.isDebugEnabled()) { 228 log.debug("notifyProgListenerEnd value {} status {}", value, status); 229 } 230 // the programmingOpReply handler might send an immediate reply, so 231 // clear the current listener _first_ 232 jmri.ProgListener temp = _usingProgrammer; 233 _usingProgrammer = null; 234 notifyProgListenerEnd(temp,value,status); 235 } 236 237 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EasyDccProgrammer.class); 238 239}