com.ghgande.j2mod.modbus.net.UDPSlaveTerminal Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of j2mod Show documentation
Show all versions of j2mod Show documentation
A fork of the j2mod library to support the jSerialComm connector
/*
* Copyright 2002-2016 jamod & j2mod development teams
*
* 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 com.ghgande.j2mod.modbus.net;
import com.ghgande.j2mod.modbus.io.ModbusUDPTransport;
import com.ghgande.j2mod.modbus.util.ModbusUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Class implementing a UDPSlaveTerminal.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
class UDPSlaveTerminal extends AbstractUDPTerminal {
private static final Logger logger = LoggerFactory.getLogger(UDPSlaveTerminal.class);
protected Hashtable requests = new Hashtable(342);
private LinkedBlockingQueue sendQueue = new LinkedBlockingQueue();
private LinkedBlockingQueue receiveQueue = new LinkedBlockingQueue();
private PacketSender packetSender;
private PacketReceiver packetReceiver;
/**
* Creates a slave terminal on the specified adapter address
* Use 0.0.0.0 to listen on all adapters
*
* @param localaddress Local address to bind to
*/
protected UDPSlaveTerminal(InetAddress localaddress) {
address = localaddress;
}
@Override
public synchronized void activate() throws Exception {
if (!isActive()) {
logger.debug("UDPSlaveTerminal.activate()");
if (address != null && port != -1) {
socket = new DatagramSocket(port, address);
}
else {
socket = new DatagramSocket();
port = socket.getLocalPort();
address = socket.getLocalAddress();
}
logger.debug("UDPSlaveTerminal::haveSocket():{}", socket.toString());
logger.debug("UDPSlaveTerminal::addr=:{}:port={}", address.toString(), port);
socket.setReceiveBufferSize(1024);
socket.setSendBufferSize(1024);
// Never timeout the receive
socket.setSoTimeout(0);
// Start a sender
packetSender = new PacketSender(socket);
new Thread(packetSender).start();
logger.debug("UDPSlaveTerminal::sender started()");
// Start a receiver
packetReceiver = new PacketReceiver(socket);
new Thread(packetReceiver).start();
logger.debug("UDPSlaveTerminal::receiver started()");
// Create a transport to use
transport = new ModbusUDPTransport(this);
logger.debug("UDPSlaveTerminal::transport created");
active = true;
}
logger.debug("UDPSlaveTerminal::activated");
}
@Override
public synchronized void deactivate() {
try {
if (active) {
// Stop receiver - this will stop and close the socket
packetReceiver.stop();
// Stop sender gracefully
packetSender.stop();
transport = null;
active = false;
}
}
catch (Exception ex) {
logger.error("Error deactivating UDPSlaveTerminal", ex);
}
}
@Override
public void sendMessage(byte[] msg) throws Exception {
sendQueue.add(msg);
}
@Override
public byte[] receiveMessage() throws Exception {
return receiveQueue.take();
}
/**
* The background thread that is responsible for sending messages in response to requests
*/
class PacketSender implements Runnable {
private boolean running;
private boolean closed;
private Thread thread;
private DatagramSocket socket;
/**
* Constructs a sender with th socket
*
* @param socket Socket to use
*/
public PacketSender(DatagramSocket socket) {
running = true;
this.socket = socket;
}
/**
* Stops the sender
*/
public void stop() {
running = false;
thread.interrupt();
while (!closed) {
ModbusUtil.sleep(100);
}
}
/**
* Thread loop that sends messages
*/
public void run() {
closed = false;
thread = Thread.currentThread();
do {
try {
// Pickup the message and corresponding request
byte[] message = sendQueue.take();
DatagramPacket req = requests.remove(ModbusUtil.registersToInt(message));
// Create new Package with corresponding address and port
if (req != null) {
DatagramPacket res = new DatagramPacket(message, message.length, req.getAddress(), req.getPort());
socket.send(res);
logger.debug("Sent package from queue");
}
}
catch (Exception ex) {
// Ignore the error if we are no longer listening
if (running) {
logger.error("Problem reading UDP socket", ex);
}
}
} while (running);
closed = true;
}
}
/**
* The background thread that receives messages and adds them to the process list
* for further analysis
*/
class PacketReceiver implements Runnable {
private boolean running;
private boolean closed;
private DatagramSocket socket;
/**
* A receiver thread for reception of UDP messages
*
* @param socket Socket to use
*/
public PacketReceiver(DatagramSocket socket) {
running = true;
this.socket = socket;
}
/**
* Stops the thread
*/
public void stop() {
running = false;
socket.close();
while (!closed) {
ModbusUtil.sleep(100);
}
}
/**
* Background thread for reading UDP messages
*/
public void run() {
closed = false;
do {
try {
// 1. Prepare buffer and receive package
byte[] buffer = new byte[256];// max size
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
// 2. Extract TID and remember request
Integer tid = ModbusUtil.registersToInt(buffer);
requests.put(tid, packet);
// 3. place the data buffer in the queue
receiveQueue.put(buffer);
logger.debug("Received package to queue");
}
catch (Exception ex) {
// Ignore the error if we are no longer listening
if (running) {
logger.error("Problem reading UDP socket", ex);
}
}
} while (running);
closed = true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy