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 // If the timer times out, send a request for status 093 DCCppEthernetAdapter.this.getSystemConnectionMemo().getDCCppTrafficController() 094 .sendDCCppMessage(jmri.jmrix.dccpp.DCCppMessage.makeCSStatusMsg(), null); 095 } 096 }; 097 jmri.util.TimerUtil.schedule(keepAliveTimer, keepAliveTimeoutValue, keepAliveTimeoutValue); 098 } 099 100 private boolean mDNSConfigure = false; 101 102 /** 103 * Set whether or not this adapter should be 104 * configured automatically via MDNS. 105 * 106 * @param autoconfig boolean value. 107 */ 108 @Override 109 public void setMdnsConfigure(boolean autoconfig) { 110 log.debug("Setting DCC++ Ethernet adapter autoconfiguration to: {}", autoconfig); 111 mDNSConfigure = autoconfig; 112 } 113 114 /** 115 * Get whether or not this adapter is configured 116 * to use autoconfiguration via MDNS. 117 * 118 * @return true if configured using MDNS. 119 */ 120 @Override 121 public boolean getMdnsConfigure() { 122 return mDNSConfigure; 123 } 124 125 /** 126 * Set the server's host name and port 127 * using mdns autoconfiguration. 128 */ 129 @Override 130 public void autoConfigure() { 131 log.info("Configuring DCC++ interface via JmDNS"); 132 if (getHostName().equals(DEFAULT_IP_ADDRESS)) { 133 setHostName(""); // reset the hostname to none. 134 } 135 String serviceType = Bundle.getMessage("defaultMDNSServiceType"); 136 log.debug("Listening for service: {}", serviceType); 137 138 if (mdnsClient == null) { 139 mdnsClient = new ZeroConfClient(); 140 mdnsClient.startServiceListener(serviceType); 141 } 142 // leave the wait code below commented out for now. It 143 // does not appear to be needed for proper ZeroConf discovery. 144 //try { 145 // synchronized(mdnsClient){ 146 // // we may need to add a timeout here. 147 // mdnsClient.wait(keepAliveTimeoutValue); 148 // if(log.isDebugEnabled()) mdnsClient.listService(serviceType); 149 // } 150 //} catch(java.lang.InterruptedException ie){ 151 // log.error("MDNS auto Configuration failed."); 152 // return; 153 //} 154 try { 155 // if there is a hostname set, use the host name (which can 156 // be changed) to find the service. 157 String qualifiedHostName = m_HostName 158 + "." + Bundle.getMessage("defaultMDNSDomainName"); 159 setHostAddress(mdnsClient.getServiceOnHost(serviceType, 160 qualifiedHostName).getHostAddresses()[0]); 161 } catch (java.lang.NullPointerException npe) { 162 // if there is no hostname set, use the service name (which can't 163 // be changed) to find the service. 164 String qualifiedServiceName = Bundle.getMessage("defaultMDNSServiceName") 165 + "." + serviceType; 166 setHostAddress(mdnsClient.getServicebyAdName(serviceType, 167 qualifiedServiceName).getHostAddresses()[0]); 168 } 169 } 170 171 ZeroConfClient mdnsClient = null; 172 173 /** 174 * Get the ZeroConf/mDNS advertisement name. 175 * this value is fixed on the LIUSB-Ethernet, so return the default 176 * value. 177 */ 178 @Override 179 public String getAdvertisementName() { 180 return Bundle.getMessage("defaultMDNSServiceName"); 181 } 182 183 /** 184 * Get the ZeroConf/mDNS service type. 185 * this value is fixed on the LIUSB-Ethernet, so return the default 186 * value. 187 */ 188 @Override 189 public String getServiceType() { 190 return Bundle.getMessage("defaultMDNSServiceType"); 191 } 192 193 private final static Logger log = LoggerFactory.getLogger(DCCppEthernetAdapter.class); 194 195}