/*** * Copyright 2002-2010 jamod development team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***/ package net.wimpi.modbus.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Hashtable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.wimpi.modbus.Modbus; import net.wimpi.modbus.io.ModbusTransport; import net.wimpi.modbus.io.ModbusUDPTransport; import net.wimpi.modbus.io.ModbusUDPTransportFactory; import net.wimpi.modbus.util.LinkedQueue; import net.wimpi.modbus.util.ModbusUtil; /** * Class implementing a <tt>UDPSlaveTerminal</tt>. * * @author Dieter Wimberger * @version @version@ (@date@) */ public class UDPSlaveTerminal implements UDPTerminal { public static class ModbusUDPTransportFactoryImpl implements ModbusUDPTransportFactory { @Override public ModbusTransport create(UDPTerminal terminal) { return new ModbusUDPTransport(terminal); } } private static final Logger logger = LoggerFactory.getLogger(UDPSlaveTerminal.class); public static final int DEFAULT_DEACTIVATION_WAIT_MILLIS = 100; // instance attributes private DatagramSocket m_Socket; private int m_Timeout = Modbus.DEFAULT_TIMEOUT; private boolean m_Active; protected InetAddress m_LocalAddress; private int m_LocalPort = Modbus.DEFAULT_PORT; protected ModbusTransport m_ModbusTransport; private int m_Retries = Modbus.DEFAULT_RETRIES; private LinkedQueue m_SendQueue; private LinkedQueue m_ReceiveQueue; private PacketSender m_PacketSender; private PacketReceiver m_PacketReceiver; private Thread m_Receiver; private Thread m_Sender; protected Hashtable m_Requests; private ModbusUDPTransportFactory m_TransportFactory; /** * Time to wait for Threads to close when deactivate is called. * Note that often no time is enough since threads might be waiting for data. */ private int m_DeactivationWaitMillis = 100; public UDPSlaveTerminal() { this(null); }// constructor public UDPSlaveTerminal(InetAddress localaddress) { this(localaddress, new ModbusUDPTransportFactoryImpl(), DEFAULT_DEACTIVATION_WAIT_MILLIS); } public UDPSlaveTerminal(InetAddress localaddress, ModbusUDPTransportFactory transportFactory, int deactivationWaitMillis) { m_LocalAddress = localaddress; m_TransportFactory = transportFactory; m_DeactivationWaitMillis = deactivationWaitMillis; m_SendQueue = new LinkedQueue(); m_ReceiveQueue = new LinkedQueue(); // m_Requests = new Hashtable(342, 0.75F); m_Requests = new Hashtable(342); }// constructor @Override public InetAddress getLocalAddress() { return m_LocalAddress; }// getLocalAddress @Override public int getLocalPort() { return m_LocalPort; }// getLocalPort public void setLocalPort(int port) { m_LocalPort = port; }// setLocalPort /** * Tests if this <tt>UDPSlaveTerminal</tt> is active. * * @return <tt>true</tt> if active, <tt>false</tt> otherwise. */ @Override public boolean isActive() { return m_Active; }// isActive /** * Activate this <tt>UDPTerminal</tt>. * * @throws Exception if there is a network failure. */ @Override public synchronized void activate() throws Exception { if (!isActive()) { logger.debug("UDPSlaveTerminal::activate()"); if (m_Socket == null) { if (m_LocalAddress != null && m_LocalPort != -1) { m_Socket = new DatagramSocket(m_LocalPort, m_LocalAddress); } else { m_Socket = new DatagramSocket(); m_LocalPort = m_Socket.getLocalPort(); m_LocalAddress = m_Socket.getLocalAddress(); } } if (logger.isDebugEnabled()) { logger.debug("UDPSlaveTerminal::haveSocket():{}", m_Socket.toString()); logger.debug("UDPSlaveTerminal::addr=:{}:port={}", m_LocalAddress.toString(), m_LocalPort); } m_Socket.setReceiveBufferSize(1024); m_Socket.setSendBufferSize(1024); m_PacketReceiver = new PacketReceiver(); m_Receiver = new Thread(m_PacketReceiver); m_Receiver.start(); logger.debug("UDPSlaveTerminal::receiver started()"); m_PacketSender = new PacketSender(); m_Sender = new Thread(m_PacketSender); m_Sender.start(); logger.debug("UDPSlaveTerminal::sender started()"); m_ModbusTransport = m_TransportFactory.create(this); logger.debug("UDPSlaveTerminal::transport created"); m_Active = true; } logger.info("UDPSlaveTerminal::activated"); }// activate /** * Deactivates this <tt>UDPSlaveTerminal</tt>. */ @Override public void deactivate() { try { if (m_Active) { // 1. stop receiver m_PacketReceiver.stop(); m_Receiver.join(m_DeactivationWaitMillis); m_Receiver.interrupt(); // 2. stop sender gracefully m_PacketSender.stop(); m_Sender.join(m_DeactivationWaitMillis); m_Sender.interrupt(); // 3. close socket m_Socket.close(); m_ModbusTransport = null; m_Active = false; } } catch (Exception ex) { ex.printStackTrace(); } }// deactivate /** * Returns the <tt>ModbusTransport</tt> associated with this * <tt>TCPMasterConnection</tt>. * * @return the connection's <tt>ModbusTransport</tt>. */ @Override public ModbusTransport getModbusTransport() { return m_ModbusTransport; }// getModbusTransport protected boolean hasResponse() { return !m_ReceiveQueue.isEmpty(); }// hasResponse /** * Returns the timeout for this <tt>UDPSlaveTerminal</tt>. * * @return the timeout as <tt>int</tt>. * * public int getReceiveTimeout() { * return m_Timeout; * }//getReceiveTimeout * * /** * Sets the timeout for this <tt>UDPSlaveTerminal</tt>. * * @param timeout the timeout as <tt>int</tt>. * * public void setReceiveTimeout(int timeout) { * m_Timeout = timeout; * try { * m_Socket.setSoTimeout(m_Timeout); * } catch (IOException ex) { * ex.printStackTrace(); * //handle? * } * }//setReceiveTimeout */ /** * Returns the socket of this <tt>UDPSlaveTerminal</tt>. * * @return the socket as <tt>DatagramSocket</tt>. */ public DatagramSocket getSocket() { return m_Socket; }// getSocket /** * Sets the socket of this <tt>UDPTerminal</tt>. * * @param sock the <tt>DatagramSocket</tt> for this terminal. */ protected void setSocket(DatagramSocket sock) { m_Socket = sock; }// setSocket @Override public void sendMessage(byte[] msg) throws Exception { m_SendQueue.put(msg); }// sendPackage @Override public byte[] receiveMessage() throws Exception { return (byte[]) m_ReceiveQueue.take(); }// receiveMessage class PacketSender implements Runnable { private boolean m_Continue; public PacketSender() { m_Continue = true; }// constructor @Override public void run() { do { try { // 1. pickup the message and corresponding request byte[] message = (byte[]) m_SendQueue.take(); DatagramPacket req = (DatagramPacket) m_Requests .remove(new Integer(ModbusUtil.registersToInt(message))); // 2. create new Package with corresponding address and port DatagramPacket res = new DatagramPacket(message, message.length, req.getAddress(), req.getPort()); m_Socket.send(res); logger.trace("Sent package from queue"); } catch (Exception ex) { DEBUG: ex.printStackTrace(); } } while (m_Continue || !m_SendQueue.isEmpty()); }// run public void stop() { m_Continue = false; }// stop }// PacketSender class PacketReceiver implements Runnable { private boolean m_Continue; public PacketReceiver() { m_Continue = true; }// constructor @Override public void run() { do { try { // 1. Prepare buffer and receive package byte[] buffer = new byte[256];// max size DatagramPacket packet = new DatagramPacket(buffer, buffer.length); m_Socket.receive(packet); // 2. Extract TID and remember request Integer tid = new Integer(ModbusUtil.registersToInt(buffer)); m_Requests.put(tid, packet); // 3. place the data buffer in the queue m_ReceiveQueue.put(buffer); logger.trace("Received package placed in queue"); } catch (Exception ex) { DEBUG: ex.printStackTrace(); } } while (m_Continue); }// run public void stop() { m_Continue = false; }// stop }// PacketReceiver }// class UDPTerminal