001package jmri.implementation; 002 003import javax.annotation.Nonnull; 004import jmri.ProgListener; 005import jmri.Programmer; 006import jmri.jmrix.AbstractProgrammerFacade; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Programmer facade which verifies each write via a read, if possible. 012 * <p> 013 * If the underlying programmer (1) can read and (2) is not already doing a read verify, 014 * each write operation is followed by a readback. 015 * If the value doesn't match, an error is signaled. 016 * <p> 017 * State Diagram for read and write operations (click to magnify): 018 * <a href="doc-files/VerifyWriteProgrammerFacade-State-Diagram.png"><img src="doc-files/VerifyWriteProgrammerFacade-State-Diagram.png" alt="UML State diagram" height="50%" width="50%"></a> 019 * 020 * @see jmri.implementation.ProgrammerFacadeSelector 021 * 022 * @author Bob Jacobsen Copyright (C) 2017 023 */ 024 025 /* 026 * @startuml jmri/implementation/doc-files/VerifyWriteProgrammerFacade-State-Diagram.png 027 * [*] --> NOTPROGRAMMING 028 * NOTPROGRAMMING --> READING: readCV()\n(read CV) 029 * READING --> NOTPROGRAMMING: OK reply received\n(return status and value) 030 * NOTPROGRAMMING --> FINISHWRITE: writeCV()\n(write CV) 031 * FINISHWRITE --> FINISHREAD: OK reply & getCanRead()\n(read CV) 032 * FINISHWRITE --> NOTPROGRAMMING: OK reply received && !getCanRead()\n(return OK status and value) 033 * FINISHREAD --> NOTPROGRAMMING: OK reply & value matches\n(return OK status reply and value) 034 * FINISHREAD --> NOTPROGRAMMING: OK reply & value not match\n(return error status reply and value) 035 * READING --> NOTPROGRAMMING : Error reply received 036 * FINISHWRITE --> NOTPROGRAMMING : Error reply received 037 * FINISHREAD --> NOTPROGRAMMING : Error reply received 038 * @enduml 039*/ 040 041 042public class VerifyWriteProgrammerFacade extends AbstractProgrammerFacade implements ProgListener { 043 044 /** 045 * @param prog the programmer to which this facade is attached 046 */ 047 public VerifyWriteProgrammerFacade(Programmer prog) { 048 super(prog); 049 } 050 051 // members for handling the programmer interface 052 int _val; // remember the value being read/written for confirmative reply 053 String _cv; // remember the cv number being read/written 054 055 // programming interface 056 @Override 057 synchronized public void writeCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 058 _val = val; 059 _cv = CV; 060 useProgrammer(p); 061 062 // write value first 063 state = ProgState.FINISHWRITE; 064 prog.writeCV(CV, val, this); 065 } 066 067 @Override 068 synchronized public void readCV(String CV, jmri.ProgListener p) throws jmri.ProgrammerException { 069 _cv = CV; 070 useProgrammer(p); 071 072 state = ProgState.READING; 073 prog.readCV(CV, this); 074 } 075 076 /** 077 * This facade ensures that {@link jmri.Programmer.WriteConfirmMode#ReadAfterWrite} 078 * is done, so long as it has permission to read the CV after writing. 079 */ 080 @Override 081 @Nonnull 082 public WriteConfirmMode getWriteConfirmMode(String addr) { 083 if ( prog.getCanRead(addr) ) { 084 return WriteConfirmMode.ReadAfterWrite; 085 } else { 086 return prog.getWriteConfirmMode(addr); 087 } 088 } 089 090 private jmri.ProgListener _usingProgrammer = null; 091 092 // internal method to remember who's using the programmer 093 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 094 // test for only one! 095 if (_usingProgrammer != null && _usingProgrammer != p) { 096 log.info("programmer already in use by {}", _usingProgrammer); 097 throw new jmri.ProgrammerException("programmer in use"); 098 } else { 099 _usingProgrammer = p; 100 } 101 } 102 103 /** 104 * State machine for VerifyWriteProgrammerFacade (click to magnify): 105 * <a href="doc-files/VerifyWriteProgrammerFacade-State-Diagram.png"><img src="doc-files/VerifyWriteProgrammerFacade-State-Diagram.png" alt="UML State diagram" height="50%" width="50%"></a> 106 */ 107 enum ProgState { 108 /** Waiting for response to read, will end next */ 109 READING, 110 /** Waiting for response to write, issue verify read next */ 111 FINISHWRITE, 112 /** Waiting for response to verify read, will end next */ 113 FINISHREAD, 114 /** No current operation */ 115 NOTPROGRAMMING 116 } 117 ProgState state = ProgState.NOTPROGRAMMING; 118 119 // Get notified of the result from the underlying programmer, and work 120 // through the state machine for needed requests 121 @Override 122 public void programmingOpReply(int value, int status) { 123 log.debug("notifyProgListenerEnd value {} status {} ", value, status); 124 125 if (status != OK ) { 126 // pass abort up 127 jmri.ProgListener temp = _usingProgrammer; 128 _usingProgrammer = null; // done 129 state = ProgState.NOTPROGRAMMING; 130 temp.programmingOpReply(value, status); 131 return; 132 } 133 134 if (_usingProgrammer == null) { 135 log.error("No listener to notify, reset and ignore"); 136 state = ProgState.NOTPROGRAMMING; 137 return; 138 } 139 140 jmri.ProgListener temp = _usingProgrammer; 141 142 switch (state) { 143 case FINISHWRITE: 144 // write completed, can we do read, and is it not already being done? 145 if (prog.getCanRead(_cv) && ! prog.getWriteConfirmMode(_cv).equals(WriteConfirmMode.ReadAfterWrite) ) { 146 state = ProgState.FINISHREAD; 147 try { 148 prog.readCV(_cv, this); 149 } catch (jmri.ProgrammerException e) { 150 // pass abort up 151 _usingProgrammer = null; // done 152 state = ProgState.NOTPROGRAMMING; 153 temp.programmingOpReply(value, ProgListener.ConfirmFailed); 154 return; 155 } 156 break; 157 } 158 // can't read or it's already being done 159 // deliberately fall through to normal completion 160 //$FALL-THROUGH$ 161 case READING: // done, forward the return code and data 162 // the programmingOpReply handler might send an immediate reply, so 163 // clear the current listener _first_ 164 _usingProgrammer = null; // done 165 state = ProgState.NOTPROGRAMMING; 166 temp.programmingOpReply(value, status); 167 break; 168 169 case FINISHREAD: 170 _usingProgrammer = null; // done 171 state = ProgState.NOTPROGRAMMING; 172 // check if we got it right 173 if (value == _val) { 174 // ok, reply OK 175 temp.programmingOpReply(value, status); 176 } else { 177 // error, reply error 178 temp.programmingOpReply(value, ProgListener.ConfirmFailed); 179 } 180 break; 181 182 default: 183 log.error("Unexpected state on reply: {}", state); 184 // clean up as much as possible 185 _usingProgrammer = null; 186 state = ProgState.NOTPROGRAMMING; 187 break; 188 } 189 } 190 191 private final static Logger log = LoggerFactory.getLogger(VerifyWriteProgrammerFacade.class); 192 193}