001package jmri.jmrix.dccpp.network; 002 003import jmri.jmrix.dccpp.DCCppCommandStation; 004import jmri.jmrix.dccpp.DCCppInitializationManager; 005import jmri.jmrix.dccpp.DCCppNetworkPortController; 006import jmri.jmrix.dccpp.DCCppTrafficController; 007import jmri.util.zeroconf.ZeroConfClient; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * Provide access to DCC++ Base Station via Ethernet. NOTES: By default, 013 * the LIUSBEthernet has an IP address of 192.168.0.200 and listens to port 014 * 5550. The LIUSBEtherenet disconnects both ports if there is 60 seconds of 015 * inactivity on the port. 016 * 017 * @author Paul Bender (C) 2011-2013 018 * @author Mark Underwood (C) 2015 019 * Based on LIUSBEthernetAdapter 020 */ 021public class DCCppEthernetAdapter extends DCCppNetworkPortController { 022 023 static final int COMMUNICATION_TCP_PORT = 2560; 024 static final String DEFAULT_IP_ADDRESS = "192.168.0.200"; 025 026 private java.util.TimerTask keepAliveTimer; // Timer used to periodically 027 // send a message to both 028 // ports to keep the ports 029 // open 030 private static final long keepAliveTimeoutValue = 30000; // Interval 031 // to send a message 032 // Must be < 60s. 033 034 public DCCppEthernetAdapter() { 035 super(); 036 log.debug("Constructor Called"); 037 setHostName(DEFAULT_IP_ADDRESS); 038 setPort(COMMUNICATION_TCP_PORT); 039 this.manufacturerName = jmri.jmrix.dccpp.DCCppConnectionTypeList.DCCPP; 040 } 041 042 @Override 043 public void connect() throws java.io.IOException { 044 super.connect(); 045 log.debug("openPort called"); 046 keepAliveTimer(); 047 } 048 049 /** 050 * Can the port accept additional characters? 051 * 052 * @return true if the port is opened 053 */ 054 @Override 055 public boolean okToSend() { 056 return status(); 057 } 058 059 @Override 060 public boolean status() { 061 return (opened); 062 } 063 064 /** 065 * Set up all of the other objects to operate with a LIUSB Ethernet 066 * interface. 067 */ 068 @Override 069 public void configure() { 070 log.debug("configure called"); 071 // connect to a packetizing traffic controller 072 DCCppTrafficController packets = (new DCCppEthernetPacketizer(new DCCppCommandStation())); 073 packets.connectPort(this); 074 075 // start operation 076 // packets.startThreads(); 077 this.getSystemConnectionMemo().setDCCppTrafficController(packets); 078 079 new DCCppInitializationManager(this.getSystemConnectionMemo()); 080 } 081 082 /** 083 * Set up the keepAliveTimer, and start it. 084 */ 085 private void keepAliveTimer() { 086 if (keepAliveTimer != null) { 087 return; //one already exists, exit 088 } 089 keepAliveTimer = new java.util.TimerTask(){ 090 @Override 091 public void run() { 092 // When the timer times out, send a heartbeat (status request on DCC++, max num slots request on DCC-EX 093 DCCppTrafficController tc = DCCppEthernetAdapter.this.getSystemConnectionMemo().getDCCppTrafficController(); 094 DCCppCommandStation cs = tc.getCommandStation(); 095 if (cs.isMaxNumSlotsMsgSupported()) { 096 tc.sendDCCppMessage(jmri.jmrix.dccpp.DCCppMessage.makeCSMaxNumSlotsMsg(), null); 097 } else { 098 tc.sendDCCppMessage(jmri.jmrix.dccpp.DCCppMessage.makeCSStatusMsg(), null); 099 } 100 } 101 }; 102 jmri.util.TimerUtil.schedule(keepAliveTimer, keepAliveTimeoutValue, keepAliveTimeoutValue); 103 } 104 105 private boolean mDNSConfigure = false; 106 107 /** 108 * Set whether or not this adapter should be 109 * configured automatically via MDNS. 110 * 111 * @param autoconfig boolean value. 112 */ 113 @Override 114 public void setMdnsConfigure(boolean autoconfig) { 115 log.debug("Setting DCC++ Ethernet adapter autoconfiguration to: {}", autoconfig); 116 mDNSConfigure = autoconfig; 117 } 118 119 /** 120 * Get whether or not this adapter is configured 121 * to use autoconfiguration via MDNS. 122 * 123 * @return true if configured using MDNS. 124 */ 125 @Override 126 public boolean getMdnsConfigure() { 127 return mDNSConfigure; 128 } 129 130 /** 131 * Set the server's host name and port 132 * using mdns autoconfiguration. 133 */ 134 @Override 135 public void autoConfigure() { 136 log.info("Configuring DCC++ interface via JmDNS"); 137 if (getHostName().equals(DEFAULT_IP_ADDRESS)) { 138 setHostName(""); // reset the hostname to none. 139 } 140 String serviceType = Bundle.getMessage("defaultMDNSServiceType"); 141 log.debug("Listening for service: {}", serviceType); 142 143 if (mdnsClient == null) { 144 mdnsClient = new ZeroConfClient(); 145 mdnsClient.startServiceListener(serviceType); 146 } 147 // leave the wait code below commented out for now. It 148 // does not appear to be needed for proper ZeroConf discovery. 149 //try { 150 // synchronized(mdnsClient){ 151 // // we may need to add a timeout here. 152 // mdnsClient.wait(keepAliveTimeoutValue); 153 // if(log.isDebugEnabled()) mdnsClient.listService(serviceType); 154 // } 155 //} catch(java.lang.InterruptedException ie){ 156 // log.error("MDNS auto Configuration failed."); 157 // return; 158 //} 159 try { 160 // if there is a hostname set, use the host name (which can 161 // be changed) to find the service. 162 String qualifiedHostName = m_HostName 163 + "." + Bundle.getMessage("defaultMDNSDomainName"); 164 setHostAddress(mdnsClient.getServiceOnHost(serviceType, 165 qualifiedHostName).getHostAddresses()[0]); 166 } catch (java.lang.NullPointerException npe) { 167 // if there is no hostname set, use the service name (which can't 168 // be changed) to find the service. 169 String qualifiedServiceName = Bundle.getMessage("defaultMDNSServiceName") 170 + "." + serviceType; 171 setHostAddress(mdnsClient.getServicebyAdName(serviceType, 172 qualifiedServiceName).getHostAddresses()[0]); 173 } 174 } 175 176 ZeroConfClient mdnsClient = null; 177 178 /** 179 * Get the ZeroConf/mDNS advertisement name. 180 * this value is fixed on the LIUSB-Ethernet, so return the default 181 * value. 182 */ 183 @Override 184 public String getAdvertisementName() { 185 return Bundle.getMessage("defaultMDNSServiceName"); 186 } 187 188 /** 189 * Get the ZeroConf/mDNS service type. 190 * this value is fixed on the LIUSB-Ethernet, so return the default 191 * value. 192 */ 193 @Override 194 public String getServiceType() { 195 return Bundle.getMessage("defaultMDNSServiceType"); 196 } 197 198 private final static Logger log = LoggerFactory.getLogger(DCCppEthernetAdapter.class); 199 200}