001package jmri.jmrix.bidib; 002 003import java.util.ArrayList; 004import java.util.List; 005import javax.annotation.Nonnull; 006 007import jmri.AddressedProgrammer; 008import jmri.ProgListener; 009import jmri.ProgrammerException; 010import jmri.ProgrammingMode; 011 012import org.bidib.jbidibc.messages.BidibLibrary; //new 013import org.bidib.jbidibc.messages.exception.ProtocolException; //new 014import org.bidib.jbidibc.messages.Node; 015import org.bidib.jbidibc.messages.AddressData; 016import org.bidib.jbidibc.messages.PomAddressData; 017import org.bidib.jbidibc.core.DefaultMessageListener; 018import org.bidib.jbidibc.core.MessageListener; 019import org.bidib.jbidibc.messages.enums.PomAddressTypeEnum; 020import org.bidib.jbidibc.messages.enums.CommandStationPom; 021import org.bidib.jbidibc.messages.enums.PomAcknowledge; 022import org.bidib.jbidibc.core.node.CommandStationNode; 023 024/** 025 * Provides an Ops mode programming interface for BiDiB Currently only Byte 026 * mode is implemented, though BiDiB also supports bit mode writes for POM 027 * 028 * @see jmri.Programmer 029 * @author Paul Bender Copyright (C) 2003-2010 030 * @author Eckart Meyer Copyright (C) 2019-2020 031 */ 032public class BiDiBOpsModeProgrammer extends jmri.jmrix.AbstractProgrammer implements AddressedProgrammer { 033 034 protected int mAddress; 035 protected int progState = NOTPROGRAMMING; 036 protected int value; 037 protected int cv; 038 protected jmri.ProgListener progListener = null; 039 040 // possible states. 041 static protected final int NOTPROGRAMMING = 0; // is notProgramming 042 static protected final int READREQUEST = 1; // read command sent, waiting for ack and reply 043 static protected final int WRITEREQUEST = 2; // read command sent, waiting for ack 044 045 protected BiDiBTrafficController tc = null; 046 MessageListener messageListener = null; 047 protected Node node = null; 048 049 public BiDiBOpsModeProgrammer(int pAddress, BiDiBTrafficController controller) { 050 tc = controller; 051 node = tc.getFirstCommandStationNode(); 052 if (log.isDebugEnabled()) { 053 log.debug("Creating Ops Mode Programmer for Address {}", pAddress); 054 } 055 mAddress = pAddress; 056 // register as a listener 057 createOpsModeProgrammerListener(); 058 } 059 060 /** 061 * {@inheritDoc} 062 * 063 * Send an ops-mode write request to BiDiB. 064 */ 065 @Override 066 synchronized public void writeCV(String CVname, int val, ProgListener p) throws ProgrammerException { 067 final int CV = Integer.parseInt(CVname); 068 log.info("write ops mode: {}, CV={}, val={}", getMode().getStandardName(), CV, val); 069 /* we need to save the programer and value so we can send messages 070 back to the screen when the programming screen when we receive 071 something from the command station */ 072 progListener = p; 073 cv = CV; 074 value = val; 075 progState = WRITEREQUEST; 076 PomAddressData decoderAddress = new PomAddressData(mAddress, PomAddressTypeEnum.LOCOMOTIVE); 077 // start the error timer 078 restartTimer(5000); 079 tc.addMessageListener(messageListener); 080//TODO bit mode ?? 081//XPom mode?? 082 083// specialized BidibNode variant 084 CommandStationNode csNode = tc.getBidib().getCommandStationNode(node); 085 log.debug("node: {}, csNode: {}", node, csNode); 086 tc.checkProgMode(false, node); //request switch off progmode (PT or GlobalProgrammer!) if it is on 087 088// async operation - start write 089 try { 090 log.trace("start CS_POM asynchroneously, write value: {}", value); 091 csNode.writePom(false, decoderAddress, CommandStationPom.WR_BYTE, cv, value); 092 } 093 catch (ProtocolException ex) { 094 log.error("writePom async failed on node: {}, addr: {} - ", node, decoderAddress, ex); 095 progState = NOTPROGRAMMING; 096 notifyProgListenerEnd(p, 0, PomAcknowledge.NOT_ACKNOWLEDGED); 097 } 098 099 100//// waits for acknowledge synchroneously 101// try { 102// tc.checkProgMode(false, node); //request switch off progmode (PT or GlobalProgrammer!) if it is on 103// 104// PomAcknowledge result = csNode.writePom(decoderAddress, CommandStationPom.WR_BYTE, cv, value); 105// 106// log.debug("writePom result: {}", result); 107// if (result == null || result == PomAcknowledge.NOT_ACKNOWLEDGED) { 108// log.warn("writePom was not acknowledged on node: {}, addr: {}"); 109// } 110// notifyProgListenerEnd(p,CV, result); 111// } 112// catch (ProtocolException ex) { 113// log.error("writePom failed on node: {}, addr: {} - {}", node, decoderAddress, ex); 114// notifyProgListenerEnd(p, value, ProgListener.CommError); 115// } 116// progState = NOTPROGRAMMING; 117 118 } 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 synchronized public void readCV(String CVname, ProgListener p) throws ProgrammerException { 125 final int CV = Integer.parseInt(CVname); 126 log.info("read ops mode: {}, CV={}", getMode().getStandardName(), CV); 127 progListener = p; 128 progState = READREQUEST; 129 cv = CV; 130 value = 42;//preset... 131 PomAddressData decoderAddress = new PomAddressData(mAddress, PomAddressTypeEnum.LOCOMOTIVE); 132 restartTimer(5000); 133 tc.addMessageListener(messageListener); 134//TODO bit mode ?? 135//XPom mode?? 136 137// specialized BidibNode variant 138 CommandStationNode csNode = tc.getBidib().getCommandStationNode(node); 139 tc.checkProgMode(false, node); //request switch off progmode (PT or GlobalProgrammer!) if it is on 140 141 142// async operation - start read 143 try { 144 log.trace("start CS_POM asynchroneously"); 145 csNode.readPom(false, decoderAddress, CommandStationPom.RD_BYTE, cv); 146 } 147 catch (ProtocolException ex) { 148 log.error("readPom async failed on node: {}, addr: {} - ", node, decoderAddress, ex); 149 progState = NOTPROGRAMMING; 150 notifyProgListenerEnd(p, 0, PomAcknowledge.NOT_ACKNOWLEDGED); 151 } 152 153//// waits for acknowledge synchroneously 154// try { 155// tc.checkProgMode(false, node); //request switch off progmode (PT or GlobalProgrammer!) if it is on 156// 157// PomAcknowledge result = csNode.readPom(decoderAddress, CommandStationPom.RD_BYTE, cv); 158// 159// log.debug("readPom result: {}", result); 160// if (result == null || result == PomAcknowledge.NOT_ACKNOWLEDGED) { 161// log.warn("readPom was not acknowledged on node: {}, addr: {}"); 162// progState = NOTPROGRAMMING; 163// notifyProgListenerEnd(p,CV, result); 164// } 165// } 166// catch (ProtocolException ex) { 167// log.error("readPom failed on node: {}, addr: {} - {}", node, decoderAddress, ex); 168// progState = NOTPROGRAMMING; 169// notifyProgListenerEnd(p, value, ProgListener.CommError); 170// } 171 log.trace("Return from readCV"); 172 } 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override 178 public void confirmCV(String CVname, int val, ProgListener p) throws ProgrammerException { 179 int CV = Integer.parseInt(CVname); 180 log.info("confirmCV ops mode: {}, CV={}", getMode().getStandardName(), CV); 181 readCV(CVname, p); 182 } 183 184 public void notifyProgListenerEnd(ProgListener p, int value, PomAcknowledge result) { 185 if (log.isDebugEnabled()) { 186 log.debug("notifyProgListenerEnd value {}", value); 187 } 188 stopTimer(); 189 tc.removeMessageListener(messageListener); 190 progState = NOTPROGRAMMING; 191 try { 192 Thread.sleep(100); 193 } 194 catch (InterruptedException e) { 195 196 } 197 if (result == PomAcknowledge.NOT_ACKNOWLEDGED) { 198 notifyProgListenerEnd(p, value, ProgListener.NoAck); 199 } 200 else { 201 notifyProgListenerEnd(p, value, ProgListener.OK); 202 } 203 //tc.removeMessageListener(messageListener); 204 //progState = NOTPROGRAMMING; 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * Types implemented here. 211 */ 212 @Override 213 @Nonnull 214 public List<ProgrammingMode> getSupportedModes() { 215 List<ProgrammingMode> ret = new ArrayList<>(); 216 ret.add(ProgrammingMode.OPSBYTEMODE); 217 // BiDiB can use all of the following modes, but I'm not sure that JMRI does it right... 218// ret.add(ProgrammingMode.OPSBITMODE); 219// ret.add(ProgrammingMode.OPSACCBITMODE); 220// ret.add(ProgrammingMode.OPSACCBYTEMODE); 221// ret.add(ProgrammingMode.OPSACCEXTBITMODE); 222// ret.add(ProgrammingMode.OPSACCEXTBYTEMODE); 223 return ret; 224 } 225 226 /** 227 * {@inheritDoc} 228 * 229 * Can this ops-mode programmer read back values? 230 * 231 * @return true to allow us to trigger an ops mode read 232 */ 233 @Override 234 public boolean getCanRead() { 235 log.debug("canRead"); 236 //if (tc.getNodeFeature(node, BidibLibrary.FEATURE_BM_CV_AVAILABLE) != 0) { 237 if (tc.getNodeFeature(node, BidibLibrary.FEATURE_BM_CV_ON) != 0) { 238 return true; 239 } 240 else { 241 //return false; 242 return true; 243 } 244 } 245 246 /** {@inheritDoc} 247 * Checks using the current default programming mode 248 */ 249 @Override 250 public boolean getCanRead(String addr) { 251 log.debug("canRead addr: {}", addr); 252 if (!getCanRead()) { 253 return false; // check basic implementation first 254 } 255 //return Integer.parseInt(addr) <= 1024; //???? 256 return true; //TODO validate CV address, depends on the mode 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override 265 public boolean getLongAddress() { 266 return true; 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override 273 public int getAddressNumber() { 274 return mAddress; 275 } 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public String getAddress() { 282 return "" + getAddressNumber() + " " + getLongAddress(); 283 } 284 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 synchronized protected void timeout() { 291 log.trace("** timeout **"); 292 if (progState != NOTPROGRAMMING) { 293 // we're programming, time to stop 294 if (log.isDebugEnabled()) { 295 log.debug("timeout!"); 296 } 297 // perhaps no loco present? Fail back to end of programming 298 progState = NOTPROGRAMMING; 299 if (getCanRead()) { 300 notifyProgListenerEnd(progListener,value,jmri.ProgListener.FailedTimeout); 301 } else { 302 notifyProgListenerEnd(progListener,value,jmri.ProgListener.OK); 303 } 304 } 305 tc.removeMessageListener(messageListener); 306 } 307 308 private void createOpsModeProgrammerListener() { 309 // to listen to messages related to POM 310 messageListener = new DefaultMessageListener() { 311 @Override 312 public void csPomAcknowledge(byte[] address, int messageNum, PomAddressData addressData, PomAcknowledge state) { 313 log.trace("csPomAcknowledge"); 314 if (progState != NOTPROGRAMMING) { 315 log.debug("loco addr: {}, msg loco addr: {}", mAddress, addressData.getAddress()); 316 if (mAddress == addressData.getAddress()) { 317 log.info("OPS PROGRAMMER CS_POM_ACC was signalled, node addr: {}, decoderAddress: {} {}, state: {}", 318 address, addressData.getAddress(), addressData.getType(), state); 319 } 320 if (state == PomAcknowledge.NOT_ACKNOWLEDGED) { 321 log.warn("readPom was not acknowledged on node addr: {}, loco addr: {}", addressData.getAddress(), addressData.getAddress()); 322 stopTimer(); 323 progState = NOTPROGRAMMING; 324 tc.removeMessageListener(messageListener); 325 //notifyProgListenerEnd(progListener, 0, ProgListener.NoAck); 326 notifyProgListenerEnd(progListener, 0, PomAcknowledge.NOT_ACKNOWLEDGED); 327 } 328 else if (progState == WRITEREQUEST) { 329 log.debug("writePom finished - value: {}", value); 330 stopTimer(); 331 progState = NOTPROGRAMMING; 332 tc.removeMessageListener(messageListener); 333 notifyProgListenerEnd(progListener, value, PomAcknowledge.ACKNOWLEDGED); 334 } 335 } 336 log.trace("return from csPomAcknowledge"); 337 } 338 @Override 339 public void feedbackCv(byte[] address, int messageNum, PomAddressData decoderAddress, int cvNumber, int dat) { 340 log.trace("feedbackCv"); 341 if (progState != NOTPROGRAMMING) { 342 //log.debug("node addr: {}, msg node addr: {}", node.getAddr(), address); 343 log.debug("loco addr: {}, msg loco addr: {}", mAddress, decoderAddress.getAddress()); 344 //if (NodeUtils.isAddressEqual(node.getAddr(), address) && cv == cvNumber) { 345 if (mAddress == decoderAddress.getAddress() && cv == cvNumber) { 346 stopTimer(); 347 progState = NOTPROGRAMMING; 348 tc.removeMessageListener(messageListener); 349 log.info("OPS PROGRAMMER BM_CV was signalled, node addr: {}, decoderAddress: {} {}, CV: {}, value: {}", 350 address, decoderAddress.getAddress(), decoderAddress.getType(), cvNumber, dat); 351 value = dat; 352 //notifyProgListenerEnd(progListener, value, jmri.ProgListener.OK); 353 notifyProgListenerEnd(progListener, value, PomAcknowledge.ACKNOWLEDGED); 354 } 355 } 356 else { 357 progState = NOTPROGRAMMING; 358 tc.removeMessageListener(messageListener); 359 } 360 log.trace("return from feedbackCv"); 361 } 362 @Override 363 public void feedbackXPom(byte[] address, int messageNum, AddressData decoderAddress, int cvNumber, int[] data) { 364 log.trace("feedbackXPom"); 365 if (progState != NOTPROGRAMMING) { 366 //log.debug("node addr: {}, msg node addr: {}", node.getAddr(), address); 367 log.debug("loco addr: {}, msg loco addr: {}", mAddress, decoderAddress.getAddress()); 368 //if (NodeUtils.isAddressEqual(node.getAddr(), address) && cv == cvNumber) { 369 if (mAddress == decoderAddress.getAddress() && cv == cvNumber) { 370 stopTimer(); 371 progState = NOTPROGRAMMING; 372 tc.removeMessageListener(messageListener); 373 log.info("OPS PROGRAMMER BM_XCOM was signalled, node addr: {}, decoderAddress: {} {}, CV: {}, values: {}", 374 address, decoderAddress.getAddress(), decoderAddress.getType(), cvNumber, data); 375 value = data[0]; //???? 376 //notifyProgListenerEnd(progListener, value, jmri.ProgListener.OK); 377 notifyProgListenerEnd(progListener, value, PomAcknowledge.ACKNOWLEDGED); 378 } 379 } 380 else { 381 progState = NOTPROGRAMMING; 382 tc.removeMessageListener(messageListener); 383 } 384 } 385 }; 386 //tc.getBidib().getMessageReceiver().addMessageListener(messageListener); 387 } 388 389// // dispose is not defined in superclass... 390// //@Override 391// public void dispose() { 392// if (messageListener != null) { 393// tc.removeMessageListener(messageListener); 394// messageListener = null; 395// } 396// //super.dispose(); 397// } 398 399 400 // initialize logging 401 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BiDiBOpsModeProgrammer.class); 402 403}