001package jmri.implementation; 002 003import jmri.ProgListener; 004import jmri.Programmer; 005import jmri.jmrix.AbstractProgrammerFacade; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Programmer facade, at this point just an example. 011 * <p> 012 * This is for decoders that have an alternate high-CV access method for command 013 * stations that can't address all 1024. It falls back to that mode if the CS 014 * can't directly address an requested CV address. In the fall back, CVs from 0 015 * to "top" are addressed directly. (Top being a supplied parameter) Above the 016 * top CV, the upper part of the CV address written to a specific CV, followed 017 * by an write with just the lower part to a second CV, then access to a 3rd CV 018 * for the value read/write. The upper and lower parts are calculated using a 019 * supplied modulus, e.g. 100. 020 * <p> 021 * This method is used by some ESU decoders. 022 * 023 * @see jmri.implementation.ProgrammerFacadeSelector 024 * 025 * @author Bob Jacobsen Copyright (C) 2013 026 * @author Andrew Crosland Copyright (C) 2021 027 */ 028public class AddressedHighCvProgrammerFacade extends AbstractProgrammerFacade implements ProgListener { 029 030 /** 031 * @param prog the programmer associated with this facade 032 * @param top CVs above this use the indirect method 033 * @param addrCVhigh CV to which the high part of address is to be written 034 * @param addrCVlow CV to which the low part of address is to be written 035 * @param valueCV Value read/written here once address has been written 036 * @param modulo Modulus for determining high/low address parts 037 */ 038 public AddressedHighCvProgrammerFacade(Programmer prog, String top, String addrCVhigh, String addrCVlow, String valueCV, String modulo) { 039 super(prog); 040 this.top = Integer.parseInt(top); 041 this.addrCVhigh = addrCVhigh; 042 this.addrCVlow = addrCVlow; 043 this.valueCV = valueCV; 044 this.modulo = Integer.parseInt(modulo); 045 _prog = prog; 046 log.debug("Created with {}, {}, {}, {}, {}, {}", prog, this.top, this.addrCVhigh, this.addrCVlow, this.valueCV, this.modulo); 047 } 048 049 int top; 050 String addrCVhigh; 051 String addrCVlow; 052 String valueCV; 053 int modulo; 054 Programmer _prog; 055 056 // members for handling the programmer interface 057 int _val; // remember the value being read/written for confirmative reply 058 int _cv; // remember the cv being read/written 059 int _startVal; // remember the starting value (hint) 060 061 // programming interface 062 @Override 063 public void writeCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 064 log.debug("start writeCV"); 065 _cv = Integer.parseInt(CV); 066 _val = val; 067 useProgrammer(p); 068 if (prog.getCanWrite(CV) || _cv <= top) { 069 state = ProgState.PROGRAMMING; 070 prog.writeCV(CV, val, this); 071 } else { 072 // write index first 073 state = ProgState.WRITELOWWRITE; 074 prog.writeCV(addrCVhigh, _cv / modulo, this); 075 } 076 } 077 078 @Override 079 public void readCV(String CV, jmri.ProgListener p) throws jmri.ProgrammerException { 080 readCV(CV, p, 0); 081 } 082 083 @Override 084 public void readCV(String CV, jmri.ProgListener p, int startVal) throws jmri.ProgrammerException { 085 log.debug("start readCV"); 086 _cv = Integer.parseInt(CV); 087 _startVal = startVal; 088 useProgrammer(p); 089 if (prog.getCanRead(CV) || _cv <= top) { 090 state = ProgState.PROGRAMMING; 091 prog.readCV(CV, this, startVal); 092 } else { 093 // write index first 094 state = ProgState.WRITELOWREAD; 095 prog.writeCV(addrCVhigh, _cv / modulo, this); 096 } 097 } 098 099 private jmri.ProgListener _usingProgrammer = null; 100 101 // internal method to remember who's using the programmer 102 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 103 // test for only one! 104 if (_usingProgrammer != null && _usingProgrammer != p) { 105 log.info("programmer already in use by {}", _usingProgrammer); 106 throw new jmri.ProgrammerException("programmer in use"); 107 } else { 108 _usingProgrammer = p; 109 return; 110 } 111 } 112 113 enum ProgState { 114 /** 115 * A pass-through operation, waiting reply, when done the entire 116 * operation is done 117 */ 118 PROGRAMMING, 119 /** 120 * Wrote 1st index on a read operation, waiting for reply 121 */ 122 WRITELOWREAD, 123 /** 124 * Wrote 1st index on a write operation, waiting for reply 125 */ 126 WRITELOWWRITE, 127 /** 128 * Wrote 2nd index on a read operation, waiting for reply 129 */ 130 FINISHREAD, 131 /** 132 * Wrote 2nd index on a write operation, waiting for reply 133 */ 134 FINISHWRITE, 135 /** 136 * nothing happening, no reply expected 137 */ 138 NOTPROGRAMMING 139 } 140 ProgState state = ProgState.NOTPROGRAMMING; 141 142 // get notified of the final result 143 // Note this assumes that there's only one phase to the operation 144 @Override 145 public void programmingOpReply(int value, int status) { 146 if (log.isDebugEnabled()) { 147 log.debug("notifyProgListenerEnd value {} status {}", value, status); 148 } 149 150 if (status != OK) { 151 // pass abort up 152 log.debug("Reset and pass abort up"); 153 jmri.ProgListener temp = _usingProgrammer; 154 _usingProgrammer = null; // done 155 state = ProgState.NOTPROGRAMMING; 156 temp.programmingOpReply(value, status); 157 return; 158 } 159 160 if (_usingProgrammer == null) { 161 log.error("No listener to notify, reset and ignore"); 162 state = ProgState.NOTPROGRAMMING; 163 return; 164 } 165 166 switch (state) { 167 case PROGRAMMING: 168 // the programmingOpReply handler might send an immediate reply, so 169 // clear the current listener _first_ 170 jmri.ProgListener temp = _usingProgrammer; 171 _usingProgrammer = null; // done 172 state = ProgState.NOTPROGRAMMING; 173 temp.programmingOpReply(value, status); 174 break; 175 case WRITELOWREAD: 176 try { 177 state = ProgState.FINISHREAD; 178 prog.writeCV(addrCVlow, _cv % modulo, this); 179 } catch (jmri.ProgrammerException e) { 180 log.error("Exception doing final read", e); 181 } 182 break; 183 case WRITELOWWRITE: 184 try { 185 state = ProgState.FINISHWRITE; 186 prog.writeCV(addrCVlow, _cv % modulo, this); 187 } catch (jmri.ProgrammerException e) { 188 log.error("Exception doing final write", e); 189 } 190 break; 191 case FINISHREAD: 192 try { 193 state = ProgState.PROGRAMMING; 194 prog.readCV(valueCV, this, _startVal); 195 } catch (jmri.ProgrammerException e) { 196 log.error("Exception doing final read", e); 197 } 198 break; 199 case FINISHWRITE: 200 try { 201 state = ProgState.PROGRAMMING; 202 prog.writeCV(valueCV, _val, this); 203 } catch (jmri.ProgrammerException e) { 204 log.error("Exception doing final write", e); 205 } 206 break; 207 default: 208 log.error("Unexpected state on reply: {}", state); 209 // clean up as much as possible 210 _usingProgrammer = null; 211 state = ProgState.NOTPROGRAMMING; 212 213 } 214 215 } 216 217 // Access to full address space provided by this. 218 @Override 219 public boolean getCanRead() { 220 return _prog.getCanRead(); 221 } 222 223 @Override 224 public boolean getCanRead(String addr) { 225 return _prog.getCanRead() && (Integer.parseInt(addr) <= 1024); 226 } 227 228 @Override 229 public boolean getCanWrite() { 230 return _prog.getCanWrite(); 231 } 232 233 @Override 234 public boolean getCanWrite(String addr) { 235 return _prog.getCanWrite() && (Integer.parseInt(addr) <= 1024); 236 } 237 238 private final static Logger log = LoggerFactory.getLogger(AddressedHighCvProgrammerFacade.class); 239 240}