001package jmri.jmrix.roco.z21; 002 003import jmri.ProgrammingMode; 004import jmri.jmrix.lenz.XNetMessage; 005import jmri.jmrix.lenz.XNetProgrammer; 006import jmri.jmrix.lenz.XNetReply; 007import jmri.jmrix.lenz.XNetTrafficController; 008 009/** 010 * Z21 Programmer support for Lenz XpressNet. 011 * <p> 012 * The read operation state sequence is: 013 * <ul> 014 * <li>Send Register Mode / Paged mode /Direct Mode read request 015 * <li>Wait for Broadcast Service Mode Entry message 016 * <li>Send Request for Service Mode Results request 017 * <li>Wait for results reply, interpret 018 * <li>Send Resume Operations request 019 * <li>Wait for Normal Operations Resumed broadcast 020 * </ul> 021 * 022 * @author Paul Bender Copyright (c) 2014 023 */ 024public class Z21XNetProgrammer extends XNetProgrammer { 025 026 public Z21XNetProgrammer(XNetTrafficController tc) { 027 super(tc); 028 // connect to listen 029 controller().addXNetListener(~0, 030 this); 031 } 032 033 /** 034 * {@inheritDoc} 035 * <p> 036 * Can we read from a specific CV in the specified mode? Answer may not be 037 * correct if the command station type and version sent by the command 038 * station mimics one of the known command stations. 039 */ 040 @Override 041 public boolean getCanRead(String addr) { 042 if (log.isDebugEnabled()) { 043 log.debug("check mode {} CV {}", getMode(), addr); 044 } 045 if (!getCanRead()) { 046 return false; // check basic implementation first 047 } 048 // Multimaus cannot read CVs, unless Rocomotion interface is used, assume other Command Stations do. 049 // To be revised if and when a Rocomotion adapter is introduced!!! 050 if (controller().getCommandStation().getCommandStationType() == 0x10) { 051 return false; 052 } 053 054 if (getMode().equals(ProgrammingMode.DIRECTBITMODE) || getMode().equals(ProgrammingMode.DIRECTBYTEMODE)) { 055 return true; // z21 allows us to specify the CV in 16 bits. 056 } else { 057 return Integer.parseInt(addr) <= 256; 058 } 059 } 060 061 /** 062 * {@inheritDoc} 063 * <p> 064 * Can we write to a specific CV in the specified mode? Answer may not be 065 * correct if the command station type and version sent by the command 066 * station mimics one of the known command stations. 067 */ 068 @Override 069 public boolean getCanWrite(String addr) { 070 if (log.isDebugEnabled()) { 071 log.debug("check CV {}", addr); 072 log.debug("cs Type: {} CS Version: {}", controller().getCommandStation().getCommandStationType(), controller().getCommandStation().getCommandStationSoftwareVersion()); 073 } 074 if (!getCanWrite()) { 075 return false; // check basic implementation first 076 } 077 if (getMode().equals(ProgrammingMode.DIRECTBITMODE) || getMode().equals(ProgrammingMode.DIRECTBYTEMODE)) { 078 return true; // z21 allows us to specify the CV in 16 bits. 079 } else { 080 return Integer.parseInt(addr) <= 256; 081 } 082 } 083 084 /** 085 * {@inheritDoc} 086 */ 087 @Override 088 synchronized public void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException { 089 final int CV = Integer.parseInt(CVname); 090 if (getMode().equals(ProgrammingMode.DIRECTBITMODE) 091 || getMode().equals(ProgrammingMode.DIRECTBYTEMODE)) { 092 if (log.isDebugEnabled()) { 093 log.debug("writeCV {} listens {}", CV, p); 094 } 095 useProgrammer(p); 096 _progRead = false; 097 // set new state & save values 098 progState = REQUESTSENT; 099 _val = val; 100 _cv = 0xffff & CV; 101 102 // start the error timer 103 restartTimer(XNetProgrammerTimeout); 104 105 XNetMessage msg = Z21XNetMessage.getZ21WriteDirectCVMsg(CV, val); 106 controller().sendXNetMessage(msg, this); 107 } else { 108 super.writeCV(CVname, val, p); 109 } 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 synchronized public void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException { 117 final int CV = Integer.parseInt(CVname); 118 if (getMode().equals(ProgrammingMode.DIRECTBITMODE) 119 || getMode().equals(ProgrammingMode.DIRECTBYTEMODE)) { 120 if (log.isDebugEnabled()) { 121 log.debug("readCV {} listens {}", CV, p); 122 } 123 124 useProgrammer(p); 125 _cv = 0xffff & CV; 126 _progRead = true; 127 // set new state 128 progState = REQUESTSENT; 129 // start the error timer 130 restartTimer(XNetProgrammerTimeout); 131 132 // format and send message to go to program mode 133 XNetMessage msg = Z21XNetMessage.getZ21ReadDirectCVMsg(CV); 134 controller().sendXNetMessage(msg, this); 135 } else { 136 super.readCV(CVname, p); 137 } 138 139 } 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override 145 synchronized public void message(XNetReply m) { 146 if (progState == NOTPROGRAMMING) { 147 // we get the complete set of replies now, so ignore these 148 149 } else if (progState == REQUESTSENT 150 || progState == INQUIRESENT) { 151 if (log.isDebugEnabled()) { 152 log.debug("reply in {} state", progState == REQUESTSENT ? "REQUESTSENT" : "INQUIRESENT"); 153 } 154 if (m.getElement(0) == Z21Constants.LAN_X_CV_RESULT_XHEADER 155 && m.getElement(1) == Z21Constants.LAN_X_CV_RESULT_DB0) { 156 // valid operation response, but does it belong to us? 157 int sent_cv = (m.getElement(2) << 8) + m.getElement(3) + 1; 158 if (sent_cv != _cv) { 159 return; // not for us. 160 } // see why waiting 161 if (_progRead) { 162 // read was in progress - get return value 163 _val = m.getElement(4); 164 } 165 progState = NOTPROGRAMMING; 166 stopTimer(); 167 // if this was a read, we cached the value earlier. If its a 168 // write, we're to return the original write value 169 notifyProgListenerEnd(_val, jmri.ProgListener.OK); 170 } else { 171 super.message(m); 172 } 173 } else { 174 if (log.isDebugEnabled()) { 175 log.debug("reply in un-decoded state"); 176 } 177 } 178 } 179 180 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Z21XNetProgrammer.class); 181 182}