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. Above the top CV, the upper part of the 016 * address is written to a specific CV, followed by an operation to just the 017 * lower part of the address. The upper and lower parts are calculated using a 018 * supplied modulus, e.g. 100. 019 * <p> 020 * For example, to write the value N to CV xyy, this will do (modulo = 100): 021 * <ul> 022 * <li>Write x*10 to CV7 where 10 is cvFactor and 7 is addrCV 023 * <li>Write N to CVyy 024 * </ul> 025 * <p> 026 * This method is used by some Zimo decoders 027 * 028 * @see jmri.implementation.ProgrammerFacadeSelector 029 * 030 * @author Bob Jacobsen Copyright (C) 2013 031 * @author Andrew Crosland Copyright (C) 2021 032 */ 033public class OffsetHighCvProgrammerFacade extends AbstractProgrammerFacade implements ProgListener { 034 035 /** 036 * @param prog the programmer to attach this facade to 037 * @param top CVs above this use the indirect method 038 * @param addrCV CV to which the high part of address is to be written 039 * @param cvFactor CV to which the low part of address is to be written 040 * @param modulo Modulus for determining high/low address parts 041 */ 042 public OffsetHighCvProgrammerFacade(Programmer prog, String top, String addrCV, String cvFactor, String modulo) { 043 super(prog); 044 this.top = Integer.parseInt(top); 045 this.addrCV = addrCV; 046 this.cvFactor = Integer.parseInt(cvFactor); 047 this.modulo = Integer.parseInt(modulo); 048 } 049 050 int top; 051 String addrCV; 052 int cvFactor; 053 int modulo; 054 055 // members for handling the programmer interface 056 int _val; // remember the value being read/written for confirmative reply 057 int _cv; // remember the cv being read/written 058 int _startVal; // remember the starting value (hint) 059 060 @Override 061 public void writeCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 062 log.debug("start writeCV"); 063 _cv = Integer.parseInt(CV); 064 _val = val; 065 useProgrammer(p); 066 if (prog.getCanWrite(CV) || _cv <= top) { 067 state = ProgState.PROGRAMMING; 068 prog.writeCV(CV, val, this); 069 } else { 070 // write index first 071 state = ProgState.FINISHWRITE; 072 prog.writeCV(addrCV, (_cv / modulo) * cvFactor, this); 073 } 074 } 075 076 @Override 077 public void readCV(String CV, jmri.ProgListener p) throws jmri.ProgrammerException { 078 readCV(CV, p, 0); 079 } 080 081 @Override 082 public void readCV(String CV, jmri.ProgListener p, int startVal) throws jmri.ProgrammerException { 083 log.debug("start readCV"); 084 _cv = Integer.parseInt(CV); 085 _startVal = startVal; 086 useProgrammer(p); 087 if (prog.getCanRead(CV) || _cv <= top) { 088 state = ProgState.PROGRAMMING; 089 prog.readCV(CV, this, startVal); 090 } else { 091 // write index first 092 state = ProgState.FINISHREAD; 093 prog.writeCV(addrCV, (_cv / modulo) * cvFactor, this); 094 } 095 } 096 097 private jmri.ProgListener _usingProgrammer = null; 098 099 // internal method to remember who's using the programmer 100 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 101 // test for only one! 102 if (_usingProgrammer != null && _usingProgrammer != p) { 103 if (log.isInfoEnabled()) { 104 log.info("programmer already in use by {}", _usingProgrammer); 105 } 106 throw new jmri.ProgrammerException("programmer in use"); 107 } else { 108 _usingProgrammer = p; 109 } 110 } 111 112 enum ProgState { 113 114 PROGRAMMING, FINISHREAD, FINISHWRITE, NOTPROGRAMMING 115 } 116 ProgState state = ProgState.NOTPROGRAMMING; 117 118 // get notified of the final result 119 // Note this assumes that there's only one phase to the operation 120 @Override 121 public void programmingOpReply(int value, int status) { 122 if (log.isDebugEnabled()) { 123 log.debug("notifyProgListenerEnd value {} status {}", value, status); 124 } 125 126 if (status != OK ) { 127 // pass abort up 128 log.debug("Reset and pass abort up"); 129 jmri.ProgListener temp = _usingProgrammer; 130 _usingProgrammer = null; // done 131 state = ProgState.NOTPROGRAMMING; 132 temp.programmingOpReply(value, status); 133 return; 134 } 135 136 if (_usingProgrammer == null) { 137 log.error("No listener to notify, reset and ignore"); 138 state = ProgState.NOTPROGRAMMING; 139 return; 140 } 141 142 switch (state) { 143 case PROGRAMMING: 144 // the programmingOpReply handler might send an immediate reply, so 145 // clear the current listener _first_ 146 jmri.ProgListener temp = _usingProgrammer; 147 _usingProgrammer = null; // done 148 state = ProgState.NOTPROGRAMMING; 149 temp.programmingOpReply(value, status); 150 break; 151 case FINISHREAD: 152 try { 153 state = ProgState.PROGRAMMING; 154 prog.readCV(String.valueOf(_cv % modulo), this, _startVal); 155 } catch (jmri.ProgrammerException e) { 156 log.error("Exception doing final read", e); 157 } 158 break; 159 case FINISHWRITE: 160 try { 161 state = ProgState.PROGRAMMING; 162 prog.writeCV(""+(_cv % modulo), _val, this); 163 } catch (jmri.ProgrammerException e) { 164 log.error("Exception doing final write", e); 165 } 166 break; 167 default: 168 log.error("Unexpected state on reply: {}", state); 169 // clean up as much as possible 170 _usingProgrammer = null; 171 state = ProgState.NOTPROGRAMMING; 172 173 } 174 175 } 176 177 // Access to full address space provided by this. 178 @Override 179 public boolean getCanRead() { 180 return true; 181 } 182 183 @Override 184 public boolean getCanRead(String addr) { 185 return Integer.parseInt(addr) <= 1024; 186 } 187 188 @Override 189 public boolean getCanWrite() { 190 return true; 191 } 192 193 @Override 194 public boolean getCanWrite(String addr) { 195 return Integer.parseInt(addr) <= 1024; 196 } 197 198 private final static Logger log = LoggerFactory.getLogger(OffsetHighCvProgrammerFacade.class); 199 200}