package com.mes.connect.modbus;
|
|
import com.mes.connect.IndustrialInterface.AddressParser;
|
import com.mes.connect.IndustrialInterface.IndustrialClient;
|
import com.mes.connect.addressParser.ModbusIpAddressParser;
|
import com.mes.connect.protocol.ProtocolAddress;
|
|
import java.io.IOException;
|
import java.net.DatagramPacket;
|
import java.net.DatagramSocket;
|
import java.net.InetAddress;
|
import java.util.Arrays;
|
import java.util.logging.Logger;
|
|
/**
|
* Modbus IP客户端实现(基于UDP)
|
*/
|
public class ModbusIpClient implements IndustrialClient {
|
private static final Logger logger = Logger.getLogger(ModbusIpClient.class.getName());
|
private DatagramSocket socket;
|
private final String host;
|
private final int port;
|
private boolean connected;
|
private int transactionId = 0;
|
private final AddressParser addressParser = new ModbusIpAddressParser();
|
|
public ModbusIpClient(String host, int port) {
|
this.host = host;
|
this.port = port;
|
}
|
|
@Override
|
public synchronized void connect() throws IOException {
|
if (!connected) {
|
socket = new DatagramSocket();
|
socket.setSoTimeout(5000); // 5秒超时
|
connected = true;
|
logger.info("Connected to Modbus IP server: " + host + ":" + port);
|
}
|
}
|
|
@Override
|
public synchronized void disconnect() {
|
if (connected) {
|
try {
|
if (socket != null) {
|
socket.close();
|
}
|
} finally {
|
socket = null;
|
connected = false;
|
logger.info("Disconnected from Modbus IP server");
|
}
|
}
|
}
|
|
@Override
|
public boolean isConnected() {
|
return connected && socket != null && !socket.isClosed();
|
}
|
|
|
@Override
|
public boolean readBit(String address) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
String[] parts = address.split("\\.");
|
String strAddress ="";
|
if (parts.length >1) {
|
strAddress = parts[0]+"."+parts[1];
|
}
|
int[] data = readRegisters(strAddress, 1);
|
int byteValue = data[0];
|
return (byteValue & (1 << parsedAddress.getBit())) != 0;
|
}
|
|
@Override
|
public void writeBit(String address, boolean value) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
String[] parts = address.split("\\.");
|
String strAddress ="";
|
if (parts.length >1) {
|
strAddress = parts[0]+"."+parts[1];
|
}
|
int[] currentData = readRegisters(strAddress, 1);
|
int byteValue = currentData[0];
|
if (value) {
|
byteValue |= (1 << parsedAddress.getBit());
|
} else {
|
byteValue &= ~(1 << parsedAddress.getBit());
|
}
|
writeRegister(strAddress, byteValue);
|
}
|
|
// @Override
|
// public boolean readBit(String address) throws IOException {
|
// ProtocolAddress parsedAddress = addressParser.parse(address);
|
// int functionCode = parsedAddress.getFunctionCode();
|
// int startAddress = parsedAddress.getAddress();
|
// int bit = parsedAddress.getBit();
|
//
|
// if (functionCode != 1 && functionCode != 2) {
|
// throw new IllegalArgumentException("Invalid function code for bit operation: " + functionCode);
|
// }
|
//
|
// byte[] request = buildReadRequest(functionCode, startAddress, 1);
|
// byte[] response = sendRequest(request);
|
//
|
// if (response[7] == functionCode) {
|
// int byteCount = response[8] & 0xFF;
|
// if (byteCount >= 1) {
|
// byte data = response[9];
|
// return (data & (1 << bit)) != 0;
|
// }
|
// } else if (response[7] == functionCode + 0x80) {
|
// int exceptionCode = response[8] & 0xFF;
|
// throw new IOException("Modbus exception: " + exceptionCode);
|
// }
|
//
|
// throw new IOException("Invalid Modbus response");
|
// }
|
//
|
// @Override
|
// public void writeBit(String address, boolean value) throws IOException {
|
// ProtocolAddress parsedAddress = addressParser.parse(address);
|
// int functionCode = parsedAddress.getFunctionCode();
|
// int startAddress = parsedAddress.getAddress();
|
//
|
// if (functionCode != 5) {
|
// throw new IllegalArgumentException("Invalid function code for write bit operation: " + functionCode);
|
// }
|
//
|
// int writeValue = value ? 0xFF00 : 0x0000;
|
// byte[] request = buildWriteSingleCoilRequest(startAddress, writeValue);
|
// byte[] response = sendRequest(request);
|
//
|
// if (response[7] != functionCode) {
|
// if (response[7] == functionCode + 0x80) {
|
// int exceptionCode = response[8] & 0xFF;
|
// throw new IOException("Modbus exception: " + exceptionCode);
|
// }
|
// throw new IOException("Invalid Modbus response");
|
// }
|
// }
|
|
@Override
|
public int readRegister(String address) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
int functionCode = parsedAddress.getFunctionCode();
|
int startAddress = parsedAddress.getAddress();
|
|
if (functionCode != 3 && functionCode != 4) {
|
throw new IllegalArgumentException("Invalid function code for register operation: " + functionCode);
|
}
|
|
byte[] request = buildReadRequest(functionCode, startAddress, 1);
|
byte[] response = sendRequest(request);
|
|
if (response[7] == functionCode) {
|
int byteCount = response[8] & 0xFF;
|
if (byteCount >= 2) {
|
return ((response[9] & 0xFF) << 8) | (response[10] & 0xFF);
|
}
|
} else if (response[7] == functionCode + 0x80) {
|
int exceptionCode = response[8] & 0xFF;
|
throw new IOException("Modbus exception: " + exceptionCode);
|
}
|
|
throw new IOException("Invalid Modbus response");
|
}
|
|
@Override
|
public void writeRegister(String address, int value) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
int functionCode = parsedAddress.getFunctionCode();
|
int startAddress = parsedAddress.getAddress();
|
|
if (functionCode != 6) {
|
throw new IllegalArgumentException("Invalid function code for write register operation: " + functionCode);
|
}
|
|
byte[] request = buildWriteSingleRegisterRequest(startAddress, value);
|
byte[] response = sendRequest(request);
|
|
if (response[7] != functionCode) {
|
if (response[7] == functionCode + 0x80) {
|
int exceptionCode = response[8] & 0xFF;
|
throw new IOException("Modbus exception: " + exceptionCode);
|
}
|
throw new IOException("Invalid Modbus response");
|
}
|
}
|
|
@Override
|
public int[] readRegisters(String address, int quantity) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
int functionCode = parsedAddress.getFunctionCode();
|
int startAddress = parsedAddress.getAddress();
|
|
if (functionCode != 3 && functionCode != 4) {
|
throw new IllegalArgumentException("Invalid function code for register operation: " + functionCode);
|
}
|
|
byte[] request = buildReadRequest(functionCode, startAddress, quantity);
|
byte[] response = sendRequest(request);
|
|
if (response[7] == functionCode) {
|
int byteCount = response[8] & 0xFF;
|
if (byteCount == quantity * 2) {
|
int[] result = new int[quantity];
|
for (int i = 0; i < quantity; i++) {
|
result[i] = ((response[9 + i*2] & 0xFF) << 8) | (response[10 + i*2] & 0xFF);
|
}
|
return result;
|
}
|
} else if (response[7] == functionCode + 0x80) {
|
int exceptionCode = response[8] & 0xFF;
|
throw new IOException("Modbus exception: " + exceptionCode);
|
}
|
|
throw new IOException("Invalid Modbus response");
|
}
|
|
@Override
|
public void writeRegisters(String address, int[] values) throws IOException {
|
ProtocolAddress parsedAddress = addressParser.parse(address);
|
int functionCode = parsedAddress.getFunctionCode();
|
int startAddress = parsedAddress.getAddress();
|
|
if (functionCode != 16) {
|
throw new IllegalArgumentException("Invalid function code for write registers operation: " + functionCode);
|
}
|
|
byte[] request = buildWriteMultipleRegistersRequest(startAddress, values);
|
byte[] response = sendRequest(request);
|
|
if (response[7] != functionCode) {
|
if (response[7] == functionCode + 0x80) {
|
int exceptionCode = response[8] & 0xFF;
|
throw new IOException("Modbus exception: " + exceptionCode);
|
}
|
throw new IOException("Invalid Modbus response");
|
}
|
}
|
|
@Override
|
public float readFloat(String address) throws IOException {
|
int[] registers = readRegisters(address, 2);
|
int intBits = (registers[0] << 16) | registers[1];
|
return Float.intBitsToFloat(intBits);
|
}
|
|
@Override
|
public void writeFloat(String address, float value) throws IOException {
|
int intBits = Float.floatToIntBits(value);
|
int highWord = (intBits >> 16) & 0xFFFF;
|
int lowWord = intBits & 0xFFFF;
|
writeRegisters(address, new int[]{highWord, lowWord});
|
}
|
|
@Override
|
public String readString(String address, int length) throws IOException {
|
int[] registers = readRegisters(address, (length + 1) / 2);
|
byte[] bytes = new byte[registers.length * 2];
|
|
for (int i = 0; i < registers.length; i++) {
|
bytes[i * 2] = (byte) ((registers[i] >> 8) & 0xFF);
|
bytes[i * 2 + 1] = (byte) (registers[i] & 0xFF);
|
}
|
|
return new String(bytes, 0, length);
|
}
|
|
@Override
|
public void writeString(String address, String value) throws IOException {
|
byte[] bytes = value.getBytes();
|
int[] registers = new int[(bytes.length + 1) / 2];
|
|
for (int i = 0; i < bytes.length; i++) {
|
int regIndex = i / 2;
|
int byteIndex = i % 2;
|
|
if (byteIndex == 0) {
|
registers[regIndex] = (bytes[i] & 0xFF) << 8;
|
} else {
|
registers[regIndex] |= (bytes[i] & 0xFF);
|
}
|
}
|
|
writeRegisters(address, registers);
|
}
|
|
@Override
|
public void close() throws IOException {
|
disconnect();
|
}
|
|
private byte[] buildReadRequest(int functionCode, int startAddress, int quantity) {
|
byte[] request = new byte[12];
|
|
// 事务标识符
|
request[0] = (byte) (transactionId >> 8);
|
request[1] = (byte) transactionId;
|
|
// 协议标识符 (0)
|
request[2] = 0;
|
request[3] = 0;
|
|
// 长度 (6)
|
request[4] = 0;
|
request[5] = 6;
|
|
// 单元标识符 (1)
|
request[6] = 1;
|
|
// 功能码
|
request[7] = (byte) functionCode;
|
|
// 起始地址
|
request[8] = (byte) (startAddress >> 8);
|
request[9] = (byte) startAddress;
|
|
// 数量
|
request[10] = (byte) (quantity >> 8);
|
request[11] = (byte) quantity;
|
|
transactionId = (transactionId + 1) % 65536;
|
|
return request;
|
}
|
|
private byte[] buildWriteSingleCoilRequest(int address, int value) {
|
byte[] request = new byte[12];
|
|
// 事务标识符
|
request[0] = (byte) (transactionId >> 8);
|
request[1] = (byte) transactionId;
|
|
// 协议标识符 (0)
|
request[2] = 0;
|
request[3] = 0;
|
|
// 长度 (6)
|
request[4] = 0;
|
request[5] = 6;
|
|
// 单元标识符 (1)
|
request[6] = 1;
|
|
// 功能码 (5)
|
request[7] = 5;
|
|
// 地址
|
request[8] = (byte) (address >> 8);
|
request[9] = (byte) address;
|
|
// 值
|
request[10] = (byte) (value >> 8);
|
request[11] = (byte) value;
|
|
transactionId = (transactionId + 1) % 65536;
|
|
return request;
|
}
|
|
private byte[] buildWriteSingleRegisterRequest(int address, int value) {
|
byte[] request = new byte[12];
|
|
// 事务标识符
|
request[0] = (byte) (transactionId >> 8);
|
request[1] = (byte) transactionId;
|
|
// 协议标识符 (0)
|
request[2] = 0;
|
request[3] = 0;
|
|
// 长度 (6)
|
request[4] = 0;
|
request[5] = 6;
|
|
// 单元标识符 (1)
|
request[6] = 1;
|
|
// 功能码 (6)
|
request[7] = 6;
|
|
// 地址
|
request[8] = (byte) (address >> 8);
|
request[9] = (byte) address;
|
|
// 值
|
request[10] = (byte) (value >> 8);
|
request[11] = (byte) value;
|
|
transactionId = (transactionId + 1) % 65536;
|
|
return request;
|
}
|
|
private byte[] buildWriteMultipleRegistersRequest(int address, int[] values) {
|
int byteCount = values.length * 2;
|
byte[] request = new byte[13 + byteCount];
|
|
// 事务标识符
|
request[0] = (byte) (transactionId >> 8);
|
request[1] = (byte) transactionId;
|
|
// 协议标识符 (0)
|
request[2] = 0;
|
request[3] = 0;
|
|
// 长度 (7 + byteCount)
|
request[4] = 0;
|
request[5] = (byte) (7 + byteCount);
|
|
// 单元标识符 (1)
|
request[6] = 1;
|
|
// 功能码 (16)
|
request[7] = 16;
|
|
// 地址
|
request[8] = (byte) (address >> 8);
|
request[9] = (byte) address;
|
|
// 寄存器数量
|
request[10] = (byte) (values.length >> 8);
|
request[11] = (byte) values.length;
|
|
// 字节数
|
request[12] = (byte) byteCount;
|
|
// 值
|
for (int i = 0; i < values.length; i++) {
|
request[13 + i*2] = (byte) (values[i] >> 8);
|
request[14 + i*2] = (byte) values[i];
|
}
|
|
transactionId = (transactionId + 1) % 65536;
|
|
return request;
|
}
|
|
private byte[] sendRequest(byte[] request) throws IOException {
|
if (!isConnected()) {
|
connect();
|
}
|
|
InetAddress address = InetAddress.getByName(host);
|
DatagramPacket sendPacket = new DatagramPacket(request, request.length, address, port);
|
|
// 发送请求
|
socket.send(sendPacket);
|
|
// 接收响应
|
byte[] receiveBuffer = new byte[1024];
|
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
|
socket.receive(receivePacket);
|
|
return Arrays.copyOf(receiveBuffer, receivePacket.getLength());
|
}
|
}
|