package com.mes.connect.modbus; import com.mes.connect.IndustrialInterface.AddressParser; import com.mes.connect.IndustrialInterface.IndustrialClient; import com.mes.connect.addressParser.ModbusAddressParser; import com.mes.connect.protocol.ProtocolAddress; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.logging.Logger; /** * Modbus TCP客户端实现 */ public class ModbusTcpClient implements IndustrialClient { private static final Logger logger = Logger.getLogger(ModbusTcpClient.class.getName()); private Socket socket; private DataInputStream inputStream; private DataOutputStream outputStream; private final String host; private final int port; private final int unitId; private int transactionId = 0; private boolean connected; private final AddressParser addressParser = new ModbusAddressParser(); public ModbusTcpClient(String host, int port, int unitId) { this.host = host; this.port = port; this.unitId = unitId; } @Override public synchronized void connect() throws IOException { if (!connected) { socket = new Socket(host, port); socket.setSoTimeout(5000); // 5秒超时 inputStream = new DataInputStream(socket.getInputStream()); outputStream = new DataOutputStream(socket.getOutputStream()); connected = true; logger.info("Connected to Modbus TCP server: " + host + ":" + port); } } @Override public synchronized void disconnect() { if (connected) { try { if (outputStream != null) outputStream.close(); if (inputStream != null) inputStream.close(); if (socket != null) socket.close(); } catch (IOException e) { logger.warning("Error closing Modbus TCP connection: " + e.getMessage()); } finally { outputStream = null; inputStream = null; socket = null; connected = false; logger.info("Disconnected from Modbus TCP server"); } } } @Override public boolean isConnected() { return connected && socket != null && socket.isConnected() && !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(); // if (functionCode != 1 && functionCode != 2) { // throw new IllegalArgumentException("Invalid function code for reading bit: " + functionCode); // } // // byte[] request = buildReadRequest(functionCode, parsedAddress.getAddress(), 1); // byte[] response = sendRequest(request); // // if (response[8] == 0x01) { // 1字节数据 // return (response[9] & (1 << parsedAddress.getBit())) != 0; // } // // throw new IOException("Unexpected response format for reading bit"); // } // // @Override // public void writeBit(String address, boolean value) throws IOException { // ProtocolAddress parsedAddress = addressParser.parse(address); // int functionCode = 5; // 写单个线圈 // // byte[] request = new byte[12]; // request[0] = (byte) (transactionId >> 8); // 事务标识符高字节 // request[1] = (byte) transactionId++; // 事务标识符低字节 // request[2] = 0; // 协议标识符高字节 (0) // request[3] = 0; // 协议标识符低字节 (0) // request[4] = 0; // 长度高字节 // request[5] = 6; // 长度低字节 (6) // request[6] = (byte) unitId; // 单元标识符 // request[7] = (byte) functionCode; // 功能码 // request[8] = (byte) (parsedAddress.getAddress() >> 8); // 地址高字节 // request[9] = (byte) parsedAddress.getAddress(); // 地址低字节 // request[10] = value ? (byte) 0xFF : 0; // 值 (0xFF00 = ON, 0x0000 = OFF) // request[11] = 0; // 值低字节 // // byte[] response = sendRequest(request); // // // 验证响应 // if (response[7] != functionCode) { // throw new IOException("Modbus exception: " + (response[7] & 0xFF)); // } // } @Override public int readRegister(String address) throws IOException { ProtocolAddress parsedAddress = addressParser.parse(address); int functionCode = parsedAddress.getFunctionCode(); if (functionCode != 3 && functionCode != 4) { throw new IllegalArgumentException("Invalid function code for reading register: " + functionCode); } byte[] request = buildReadRequest(functionCode, parsedAddress.getAddress(), 1); byte[] response = sendRequest(request); if (response[8] == 0x02) { // 2字节数据 return ((response[9] & 0xFF) << 8) | (response[10] & 0xFF); } throw new IOException("Unexpected response format for reading register"); } @Override public void writeRegister(String address, int value) throws IOException { ProtocolAddress parsedAddress = addressParser.parse(address); int functionCode = 6; // 写单个寄存器 byte[] request = new byte[12]; request[0] = (byte) (transactionId >> 8); // 事务标识符高字节 request[1] = (byte) transactionId++; // 事务标识符低字节 request[2] = 0; // 协议标识符高字节 (0) request[3] = 0; // 协议标识符低字节 (0) request[4] = 0; // 长度高字节 request[5] = 6; // 长度低字节 (6) request[6] = (byte) unitId; // 单元标识符 request[7] = (byte) functionCode; // 功能码 request[8] = (byte) (parsedAddress.getAddress() >> 8); // 地址高字节 request[9] = (byte) parsedAddress.getAddress(); // 地址低字节 request[10] = (byte) (value >> 8); // 值高字节 request[11] = (byte) value; // 值低字节 byte[] response = sendRequest(request); // 验证响应 if (response[7] != functionCode) { throw new IOException("Modbus exception: " + (response[7] & 0xFF)); } } @Override public int[] readRegisters(String address, int quantity) throws IOException { ProtocolAddress parsedAddress = addressParser.parse(address); int functionCode = parsedAddress.getFunctionCode(); if (functionCode != 3 && functionCode != 4) { throw new IllegalArgumentException("Invalid function code for reading registers: " + functionCode); } byte[] request = buildReadRequest(functionCode, parsedAddress.getAddress(), quantity); byte[] response = sendRequest(request); int byteCount = response[8] & 0xFF; int[] result = new int[byteCount / 2]; for (int i = 0; i < result.length; i++) { int index = 9 + i * 2; result[i] = ((response[index] & 0xFF) << 8) | (response[index + 1] & 0xFF); } return result; } @Override public void writeRegisters(String address, int[] values) throws IOException { ProtocolAddress parsedAddress = addressParser.parse(address); int functionCode = 16; // 写多个寄存器 int byteCount = values.length * 2; int requestLength = 9 + byteCount; byte[] request = new byte[requestLength]; request[0] = (byte) (transactionId >> 8); // 事务标识符高字节 request[1] = (byte) transactionId++; // 事务标识符低字节 request[2] = 0; // 协议标识符高字节 (0) request[3] = 0; // 协议标识符低字节 (0) request[4] = (byte) ((requestLength - 6) >> 8); // 长度高字节 request[5] = (byte) (requestLength - 6); // 长度低字节 request[6] = (byte) unitId; // 单元标识符 request[7] = (byte) functionCode; // 功能码 request[8] = (byte) (parsedAddress.getAddress() >> 8); // 地址高字节 request[9] = (byte) parsedAddress.getAddress(); // 地址低字节 request[10] = (byte) (values.length >> 8); // 寄存器数量高字节 request[11] = (byte) values.length; // 寄存器数量低字节 request[12] = (byte) byteCount; // 字节数 // 填充寄存器值 for (int i = 0; i < values.length; i++) { int index = 13 + i * 2; request[index] = (byte) (values[i] >> 8); // 值高字节 request[index + 1] = (byte) values[i]; // 值低字节 } byte[] response = sendRequest(request); // 验证响应 if (response[7] != functionCode) { throw new IOException("Modbus exception: " + (response[7] & 0xFF)); } } @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 address, int quantity) { byte[] request = new byte[12]; request[0] = (byte) (transactionId >> 8); // 事务标识符高字节 request[1] = (byte) transactionId++; // 事务标识符低字节 request[2] = 0; // 协议标识符高字节 (0) request[3] = 0; // 协议标识符低字节 (0) request[4] = 0; // 长度高字节 request[5] = 6; // 长度低字节 (6) request[6] = (byte) unitId; // 单元标识符 request[7] = (byte) functionCode; // 功能码 request[8] = (byte) (address >> 8); // 地址高字节 request[9] = (byte) address; // 地址低字节 request[10] = (byte) (quantity >> 8); // 数量高字节 request[11] = (byte) quantity; // 数量低字节 return request; } private byte[] sendRequest(byte[] request) throws IOException { if (!isConnected()) { connect(); } // 发送请求 outputStream.write(request); outputStream.flush(); // 接收响应 byte[] header = new byte[6]; int bytesRead = 0; // 读取头部 while (bytesRead < 6) { int count = inputStream.read(header, bytesRead, 6 - bytesRead); if (count == -1) { throw new IOException("Connection closed while reading response header"); } bytesRead += count; } int length = ((header[4] & 0xFF) << 8) | (header[5] & 0xFF); byte[] response = new byte[6 + length]; // 复制头部 System.arraycopy(header, 0, response, 0, 6); // 读取剩余部分 bytesRead = 0; while (bytesRead < length) { int count = inputStream.read(response, 6 + bytesRead, length - bytesRead); if (count == -1) { throw new IOException("Connection closed while reading response body"); } bytesRead += count; } // 检查事务标识符 if ((response[0] & 0xFF) != (request[0] & 0xFF) || (response[1] & 0xFF) != (request[1] & 0xFF)) { logger.warning("Transaction ID mismatch in response - expected: " + ((request[0] & 0xFF) << 8 | (request[1] & 0xFF)) + ", got: " + ((response[0] & 0xFF) << 8 | (response[1] & 0xFF))); // 虽然事务ID不匹配,但可能是之前请求的响应,选择继续处理而不是抛出异常 } // 检查功能码(如果有异常,功能码会是0x80 + 原功能码) if ((response[7] & 0xFF) > 0x80) { int exceptionCode = response[8] & 0xFF; throw new IOException("Modbus exception: " + exceptionCode); } return response; } }