001package jmri.jmrix.dccpp.network; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.Arrays; 005import javax.swing.SwingUtilities; 006import jmri.jmrix.AbstractMRListener; 007import jmri.jmrix.AbstractMRMessage; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * An extension of the DCCppPacketizer to handle the device specific 013 * requirements of the DCC++ Ethernet. 014 * <p> 015 * In particular, DCCppEthernetPacketizer counts the number of commands 016 * received. 017 * 018 * Based on LIUSBEthernetXnetPacketizer 019 * 020 * @author Paul Bender, Copyright (C) 2011 021 * @author Mark Underwood, Copyright (C) 2015 022 */ 023public class DCCppEthernetPacketizer extends jmri.jmrix.dccpp.serial.SerialDCCppPacketizer { 024 025 public DCCppEthernetPacketizer(jmri.jmrix.dccpp.DCCppCommandStation pCommandStation) { 026 super(pCommandStation); 027 log.debug("Loading DCC++ Ethernet Extension to DCCppPacketizer"); 028 } 029 030 /** 031 * Actually transmits the next message to the port 032 */ 033 // NOTE: This is (for now) an EXACT copy of the content of AbstractMRTrafficController.forwardToPort() 034 // except for adding the call to controller.recover() at the bottom in the "catch" 035 @Override 036 @SuppressFBWarnings(value = {"TLW_TWO_LOCK_WAIT"},justification = "Two locks needed for synchronization here, this is OK") 037 synchronized protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) { 038 log.debug("forwardToPort message: [{}]", m); 039 // remember who sent this 040 mLastSender = reply; 041 042 // forward the message to the registered recipients, 043 // which includes the communications monitor, except the sender. 044 // Schedule notification via the Swing event queue to ensure order 045 Runnable r = new XmtNotifier(m, mLastSender, this); 046 SwingUtilities.invokeLater(r); 047 048 // stream to port in single write, as that's needed by serial 049 byte[] msg = new byte[lengthOfByteStream(m)]; 050 // add header 051 int offset = addHeaderToOutput(msg, m); 052 053 // add data content 054 int len = m.getNumDataElements(); 055 for (int i = 0; i < len; i++) { 056 msg[i + offset] = (byte) m.getElement(i); 057 } 058 // add trailer 059 addTrailerToOutput(msg, len + offset, m); 060 // and stream the bytes 061 try { 062 if (ostream != null) { 063 if (log.isDebugEnabled()) { 064 StringBuilder f = new StringBuilder(); 065 for (byte b : msg) { 066 f.append(Integer.toHexString(0xFF & b)); 067 f.append(" "); 068 } 069 log.debug("formatted message: {}", f); 070 } 071 while (m.getRetries() >= 0) { 072 if (portReadyToSend(controller)) { 073 ostream.write(msg); 074 ostream.flush(); 075 log.debug("written, msg timeout: {} mSec", m.getTimeout()); 076 break; 077 } else if (m.getRetries() >= 0) { 078 log.debug("Retry message: '{}' attempts remaining: {}", m, m.getRetries()); 079 m.setRetries(m.getRetries() - 1); 080 try { 081 int timeOut = m.getTimeout(); 082 synchronized (xmtRunnable) { 083 if (timeOut > 0) { 084 xmtRunnable.wait(timeOut); 085 } 086 } 087 } catch (InterruptedException e) { 088 Thread.currentThread().interrupt(); // retain if needed later 089 log.error("retry wait interrupted"); 090 } 091 } else { 092 log.warn("sendMessage: port not ready for data sending: {}", Arrays.toString(msg)); 093 } 094 } 095 } else { // ostream is null 096 // no stream connected 097 connectionWarn(); 098 } 099 } catch (java.io.IOException e) { 100 // TODO Currently there's no port recovery if an exception occurs 101 // must restart JMRI to clear xmtException. 102 //xmtException = true; 103 portWarn(e); 104 // Attempt to recover the connection... 105 controller.recover(); 106 } 107 } 108 109 private final static Logger log = LoggerFactory.getLogger(DCCppEthernetPacketizer.class); 110 111}