作者 钟来

明牛协议解析完成

正在显示 15 个修改的文件 包含 1403 行增加678 行删除
... ... @@ -48,6 +48,8 @@ public class InitPlcConfig {
cachPlcConfig.setProtocolType(plc.getProtocolType());
cachPlcConfig.setConnectConfig(plc.getConnectConfig());
cachPlcConfig.setPlcMap(new HashMap());
cachPlcConfig.setZeroBasedAddress(plc.getZeroBasedAddress());
cachPlcConfig.setByteOrder(plc.getByteOrder());
plcsConfigMap.put(plc.getId(), cachPlcConfig);
}
... ...
... ... @@ -5,6 +5,7 @@ import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.data.TopicFactoryAdapter;
import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite;
import com.zhonglai.luhui.device.modbus.terminal.modbus.ModbusMasterMessage;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.Message;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint;
import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic;
... ... @@ -31,15 +32,16 @@ public class PutTopic extends TopicFactoryAdapter {
{
JSONObject plcCommand = jsonObject.getJSONObject(key);
String id = key;
CachPlcConfig cachPlcConfig = InitPlcConfig.getPlcsConfigMap().get(id);
List<PlcPoint> plcPoints = getPlcPoints(id, plcCommand);
Modbus4jWrite modbus4jWrite = null;
try {
modbus4jWrite = new Modbus4jWrite(id);
modbus4jWrite.batchWrite(plcPoints,true);
modbus4jWrite.batchWrite(plcPoints,cachPlcConfig.getZeroBasedAddress());
} catch (Exception e) {
if(null != modbus4jWrite)
{
ModbusMasterMessage.closeMaster(id); // 销毁旧连接
ModbusMasterMessage.closeMasterByPlcId(id); // 销毁旧连接
}
logger.error("写plc异常",e);
try {
... ...
... ... @@ -6,6 +6,7 @@ import com.zhonglai.luhui.device.modbus.terminal.data.TopicFactoryAdapter;
import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jRead;
import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite;
import com.zhonglai.luhui.device.modbus.terminal.modbus.ModbusMasterMessage;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint;
import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic;
import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService;
... ... @@ -34,16 +35,18 @@ public class ReadTopic extends TopicFactoryAdapter {
String plcCommand = jsonObject.getString(key);
String id = key;
List<PlcPoint> plcPoints = getPlcPoints(id, plcCommand);
CachPlcConfig cachPlcConfig = InitPlcConfig.getPlcsConfigMap().get(id);
Modbus4jRead modbus4jRead = null;
try {
modbus4jRead = new Modbus4jRead(id);
Map<String, Object> map = modbus4jRead.batchRead(plcPoints,true);
Map<String, Object> map = modbus4jRead.batchRead(plcPoints,cachPlcConfig.getZeroBasedAddress());
rJsonObject.put(key, map);
} catch (Exception e) {
logger.error("读plc异常",e);
if(null != modbus4jRead)
{
ModbusMasterMessage.closeMaster(id); // 销毁旧连接
ModbusMasterMessage.closeMasterByPlcId(id); // 销毁旧连接
}
return;
}
... ...
package com.zhonglai.luhui.device.modbus.terminal.modbus;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.msg.*;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcDataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* 修正并重构后的增强版 Modbus TCP 客户端(字节序与功能码修复)
*/
public class EnhancedModbusTcpClientWithByteOrder {
private static final Logger log = LoggerFactory.getLogger(EnhancedModbusTcpClientWithByteOrder.class);
/** 浮点字节序 */
public enum ByteOrderType { ABCD, CDAB, BADC, DCBA }
private final String host;
private final int port;
private final ExecutorService executor;
private final ReentrantLock connectLock = new ReentrantLock();
// 配置 master 的参数
private final int timeoutMillis;
private final int retries;
private final boolean encapsulated;
public static class ModbusAddress {
private final int startAddress; // 起始寄存器/线圈地址
private final int bitIndex; // -1 表示不是位访问
private final int length; // 连续寄存器/线圈长度
private ModbusAddress(int startAddress, int bitIndex, int length) {
this.startAddress = startAddress;
this.bitIndex = bitIndex;
this.length = length;
}
public int getStartAddress() { return startAddress; }
public int getBitIndex() { return bitIndex; }
public int getLength() { return length; }
/**
* 支持格式:
* 40001
* 40001.01
* 40001-40003
*/
public static ModbusAddress parse(String addressStr) {
addressStr = addressStr.trim();
if (addressStr.contains("-")) {
String[] parts = addressStr.split("-");
if (parts.length != 2) throw new IllegalArgumentException("地址区间格式错误: " + addressStr);
int start = Integer.parseInt(parts[0].trim());
int end = Integer.parseInt(parts[1].trim());
if (end < start) throw new IllegalArgumentException("结束地址不能小于起始地址: " + addressStr);
return new ModbusAddress(start, -1, end - start + 1);
} else if (addressStr.contains(".")) {
String[] parts = addressStr.split("\\.");
if (parts.length != 2) throw new IllegalArgumentException("位地址格式错误: " + addressStr);
int reg = Integer.parseInt(parts[0].trim());
int bit = Integer.parseInt(parts[1].trim()) - 1; // 位从1开始
if (bit < 0 || bit > 15) throw new IllegalArgumentException("位索引必须在1-16: " + addressStr);
return new ModbusAddress(reg, bit, 1);
} else {
int reg = Integer.parseInt(addressStr);
return new ModbusAddress(reg, -1, 1);
}
}
}
public EnhancedModbusTcpClientWithByteOrder(String host, int port, int poolSize) {
this(host, port, poolSize, 3000, 1, true);
}
public EnhancedModbusTcpClientWithByteOrder(String host, int port, int poolSize, int timeoutMillis, int retries, boolean encapsulated) {
this.host = host;
this.port = port;
this.executor = Executors.newFixedThreadPool(poolSize);
this.timeoutMillis = timeoutMillis;
this.retries = retries;
this.encapsulated = encapsulated;
}
private ModbusMaster getMaster() throws Exception {
String key = String.format("tcp:%s:%d", host, port);
return ModbusConnectionManager.getTcpMaster(host, port, timeoutMillis, retries, encapsulated);
}
private <T> Future<T> submit(Callable<T> task) { return executor.submit(task); }
/**
* 将给定地址转换到 0..9999 的 offset。
* zeroBasedAddress == true 表示用户传入的是从 0 开始的地址(例如 0, 40000 等),
* 否则表示用户传入的是从 1 开始的地址(常见的 40001 表示 offset 0)。
*/
private int toOffset(int address, boolean zeroBasedAddress) {
if (zeroBasedAddress) {
// 例如 address=40000 -> offset = 40000 % 10000 = 0
int off = address % 10000;
return (off + 10000) % 10000;
} else {
// one-based input (40001 -> offset 0)
int off = (address - 1) % 10000;
return (off + 10000) % 10000;
}
}
/**
* 基于经典 Modbus 地址段做判断:
* 0xxxx (1..9999) -> Coil (FC1 read)
* 1xxxx (10001..19999) -> Discrete Input (FC2 read)
* 3xxxx (30001..39999) -> Input Register (FC4 read)
* 4xxxx (40001..49999) -> Holding Register (FC3 read, write FC6/16)
*
* write 为 true 时,函数尝试返回合适的写功能码(单写/多写)
*/
private int getFunctionCode(int address, boolean write, Object value) {
if (address <= 0) throw new IllegalArgumentException("地址必须为正数: " + address);
int prefix = address / 10000; // 0 -> coils (1..9999), 1 -> discrete inputs, 3 -> input regs, 4 -> holding regs
switch (prefix) {
case 0: // 00001 - 09999 => coils
if (write) {
if (value instanceof boolean[]) return 15; // Write Multiple Coils
return 5; // Write Single Coil
} else {
return 1; // Read Coils
}
case 1: // 10001 - 19999 => discrete input (read-only)
if (write) throw new IllegalArgumentException("Discrete Input 为只读地址段,不能写: " + address);
return 2; // Read Discrete Inputs
case 3: // 30001 - 39999 => input registers (read-only)
if (write) throw new IllegalArgumentException("Input Register 为只读地址段,不能写: " + address);
return 4; // Read Input Registers
case 4: // 40001 - 49999 => holding registers
if (write) {
// 16 = Write Multiple Registers, 6 = Write Single Register
if (value instanceof Number) {
// 无法仅凭 value 类型精确决定是否使用单寄存器还是多寄存器(例如 INT32 需要 2 寄存器)
// 所以这里默认多寄存器(16)以通用支持复杂类型;调用处可以选择适配单寄存器写(WriteRegisterRequest)。
return 16;
} else if (value instanceof int[] || value instanceof short[] || value instanceof byte[]) {
return 16;
} else {
// For boolean writing to holding register bit - handled separately by API
return 16;
}
} else {
return 3; // Read Holding Registers
}
default:
throw new IllegalArgumentException("地址超出支持范围: " + address);
}
}
private ModbusResponse execute(ModbusRequest req, Class<? extends ModbusResponse> respType) throws Exception {
ModbusMaster master = getMaster();
ModbusResponse response = master.send(req);
if (response == null) throw new ModbusTransportException("设备无响应");
if (response.isException()) throw new ModbusTransportException("异常响应: " + response.getExceptionMessage());
return respType.cast(response);
}
// -------------------- 读接口 --------------------
public Future<Object> read(int slaveId, int address, PlcDataType dataType, boolean zeroBasedAddress, ByteOrderType floatByteOrder) {
return submit(() -> {
int offset = toOffset(address, zeroBasedAddress);
final ByteOrderType effectiveByteOrder = (floatByteOrder == null) ? ByteOrderType.CDAB : floatByteOrder;
switch (dataType) {
case BOOLEAN:
int fcBool = getFunctionCode(address, false, null);
if (fcBool == 1) return ((ReadCoilsResponse) execute(new ReadCoilsRequest(slaveId, offset, 1), ReadCoilsResponse.class)).getBooleanData()[0];
if (fcBool == 2) return ((ReadDiscreteInputsResponse) execute(new ReadDiscreteInputsRequest(slaveId, offset, 1), ReadDiscreteInputsResponse.class)).getBooleanData()[0];
throw new IllegalArgumentException("BOOLEAN 类型无法读取地址:" + address);
case INT16:
short[] regs16 = readRegisters(slaveId, offset, 1, address);
// 保持与之前兼容:返回 int(Java 没有 unsigned short,按 signed short 扩展)
return (int) regs16[0];
case INT32:
short[] regs32 = readRegisters(slaveId, offset, 2, address);
return toInt(regs32, effectiveByteOrder);
case INT64:
short[] regs64 = readRegisters(slaveId, offset, 4, address);
return toLong(regs64, effectiveByteOrder);
case FLOAT32:
short[] f32Regs = readRegisters(slaveId, offset, 2, address);
return toFloat(f32Regs, effectiveByteOrder);
case FLOAT64:
short[] f64Regs = readRegisters(slaveId, offset, 4, address);
return toDouble(f64Regs, effectiveByteOrder);
default:
throw new IllegalArgumentException("未知 DataType:" + dataType);
}
});
}
// -------------------- 写接口 --------------------
public Future<Void> write(int slaveId, int address, PlcDataType dataType, Object value, boolean zeroBasedAddress, ByteOrderType floatByteOrder) {
return submit(() -> {
int offset = toOffset(address, zeroBasedAddress);
final ByteOrderType effectiveByteOrder = (floatByteOrder == null) ? ByteOrderType.CDAB : floatByteOrder;
switch (dataType) {
case BOOLEAN:
if (value instanceof Boolean) {
execute(new WriteCoilRequest(slaveId, offset, (Boolean) value), WriteCoilResponse.class);
} else if (value instanceof boolean[]) {
execute(new WriteCoilsRequest(slaveId, offset, (boolean[]) value), WriteCoilsResponse.class);
} else throw new IllegalArgumentException("BOOLEAN 写入必须是 Boolean 或 boolean[]");
return null;
case INT16:
// 写单个寄存器(用 WriteRegisterRequest -> FC6)
execute(new WriteRegisterRequest(slaveId, offset, (Long.valueOf(String.valueOf(value))).shortValue()), WriteRegisterResponse.class);
return null;
case INT32:
// 写 2 个寄存器(FC16)
execute(new WriteRegistersRequest(slaveId, offset, toShorts(Integer.parseInt(String.valueOf(value)), effectiveByteOrder)), WriteRegistersResponse.class);
return null;
case INT64:
execute(new WriteRegistersRequest(slaveId, offset, toShorts(Long.valueOf(String.valueOf(value)), effectiveByteOrder)), WriteRegistersResponse.class);
return null;
case FLOAT32:
execute(new WriteRegistersRequest(slaveId, offset, fromFloat(Float.valueOf(String.valueOf(value)) , effectiveByteOrder)), WriteRegistersResponse.class);
return null;
case FLOAT64:
execute(new WriteRegistersRequest(slaveId, offset, fromDouble(Double.valueOf(String.valueOf(value)), effectiveByteOrder)), WriteRegistersResponse.class);
return null;
default:
throw new IllegalArgumentException("未知 DataType:" + dataType);
}
});
}
/**
* 读取 Holding Register 中的某一位(支持 40001.01 格式)
*/
public boolean readBooleanFromRegisterBit(int slaveId, String addressStr, boolean zeroBasedAddress) throws Exception {
ModbusAddress addr = ModbusAddress.parse(addressStr);
if (addr.getBitIndex() < 0) throw new IllegalArgumentException("地址不是位访问: " + addressStr);
int offset = toOffset(addr.getStartAddress(), zeroBasedAddress);
short[] regs = readRegisters(slaveId, offset, 1, addr.getStartAddress());
return ((regs[0] >> addr.getBitIndex()) & 0x1) == 1;
}
/**
* 写入 Holding Register 中的某一位(支持 40001.01 格式)
*/
public void writeBooleanToRegisterBit(int slaveId, String addressStr, boolean value, boolean zeroBasedAddress) throws Exception {
ModbusAddress addr = ModbusAddress.parse(addressStr);
if (addr.getBitIndex() < 0) throw new IllegalArgumentException("地址不是位访问: " + addressStr);
int offset = toOffset(addr.getStartAddress(), zeroBasedAddress);
// 读取原寄存器
short[] regs = readRegisters(slaveId, offset, 1, addr.getStartAddress());
// 修改对应 bit
if (value) regs[0] |= (1 << addr.getBitIndex());
else regs[0] &= ~(1 << addr.getBitIndex());
// 写回寄存器(用 WriteRegistersRequest)
execute(new WriteRegistersRequest(slaveId, offset, regs), WriteRegistersResponse.class);
}
private short[] readRegisters(int slaveId, int offset, int length, int address) throws Exception {
int fc = getFunctionCode(address, false, null); // 使用原始地址
if (fc == 3) return ((ReadHoldingRegistersResponse) execute(new ReadHoldingRegistersRequest(slaveId, offset, length), ReadHoldingRegistersResponse.class)).getShortData();
if (fc == 4) return ((ReadInputRegistersResponse) execute(new ReadInputRegistersRequest(slaveId, offset, length), ReadInputRegistersResponse.class)).getShortData();
throw new IllegalArgumentException("寄存器类型无法读取,offset=" + offset + " address=" + address);
}
// -------------------- 通用字节重排工具 --------------------
private static byte[] regsToRawBytes(short[] regs) {
byte[] raw = new byte[regs.length * 2];
for (int i = 0; i < regs.length; i++) {
raw[i * 2] = (byte) (regs[i] >> 8);
raw[i * 2 + 1] = (byte) regs[i];
}
return raw;
}
/**
* 根据目标字节长度(4 或 8)和字节序类型重排 bytes。
* 当 bytes.length == 4 或 8 时按表格重排;否则返回原数组拷贝。
*/
private static byte[] reorderBytesForType(byte[] src, ByteOrderType boType) {
int len = src.length;
byte[] dst = new byte[len];
if (len == 4) {
int[] map;
switch (boType) {
case ABCD: map = new int[]{0,1,2,3}; break;
case CDAB: map = new int[]{2,3,0,1}; break;
case BADC: map = new int[]{1,0,3,2}; break;
case DCBA: map = new int[]{3,2,1,0}; break;
default: map = new int[]{0,1,2,3};
}
for (int i = 0; i < 4; i++) dst[i] = src[map[i]];
return dst;
} else if (len == 8) {
int[] map;
switch (boType) {
case ABCD: map = new int[]{0,1,2,3,4,5,6,7}; break;
case CDAB: map = new int[]{2,3,0,1,6,7,4,5}; break; // swap 2-word pairs
case BADC: map = new int[]{1,0,3,2,5,4,7,6}; break; // swap bytes in each word
case DCBA: map = new int[]{7,6,5,4,3,2,1,0}; break; // reverse
default: map = new int[]{0,1,2,3,4,5,6,7};
}
for (int i = 0; i < 8; i++) dst[i] = src[map[i]];
return dst;
} else {
// 不支持的长度,返回原数组副本
System.arraycopy(src, 0, dst, 0, len);
return dst;
}
}
private static byte[] reorderBytesForRead(byte[] src, ByteOrderType type) {
int len = src.length;
byte[] dst = new byte[len];
int[] m;
if (len == 4) {
switch (type) {
case ABCD:
m = new int[]{0, 1, 2, 3};
break;
case CDAB:
m = new int[]{2, 3, 0, 1};
break;
case BADC:
m = new int[]{1, 0, 3, 2};
break;
case DCBA:
m = new int[]{3, 2, 1, 0};
break;
default:
m = new int[]{0, 1, 2, 3};
}
for (int i = 0; i < 4; i++) dst[i] = src[m[i]];
} else if (len == 8) {
switch (type) {
case ABCD:
m = new int[]{0,1,2,3,4,5,6,7};
break;
case CDAB:
m = new int[]{2,3,0,1,6,7,4,5};
break;
case BADC:
m = new int[]{1,0,3,2,5,4,7,6};
break;
case DCBA:
m = new int[]{7,6,5,4,3,2,1,0};
break;
default:
m = new int[]{0,1,2,3,4,5,6,7};
}
for (int i = 0; i < 8; i++) dst[i] = src[m[i]];
} else {
System.arraycopy(src, 0, dst, 0, len);
}
return dst;
}
private static byte[] reorderBytesForWrite(byte[] src, ByteOrderType type) {
int len = src.length;
byte[] dst = new byte[len];
int[] m;
if (len == 4) {
switch (type) {
case ABCD:
m = new int[]{0, 1, 2, 3};
break;
case CDAB:
m = new int[]{2, 3, 0, 1};
break;
case BADC:
m = new int[]{1, 0, 3, 2};
break;
case DCBA:
m = new int[]{3, 2, 1, 0};
break;
default:
m = new int[]{0, 1, 2, 3};
}
for (int i = 0; i < 4; i++) dst[m[i]] = src[i];
} else if (len == 8) {
switch (type) {
case ABCD:
m = new int[]{0,1,2,3,4,5,6,7};
break;
case CDAB:
m = new int[]{2,3,0,1,6,7,4,5};
break;
case BADC:
m = new int[]{1,0,3,2,5,4,7,6};
break;
case DCBA:
m = new int[]{7,6,5,4,3,2,1,0};
break;
default:
m = new int[]{0,1,2,3,4,5,6,7};
}
for (int i = 0; i < 8; i++) dst[m[i]] = src[i];
} else {
System.arraycopy(src, 0, dst, 0, len);
}
return dst;
}
private static short[] rawBytesToRegs(byte[] raw) {
if (raw.length % 2 != 0) throw new IllegalArgumentException("raw length must be even");
short[] regs = new short[raw.length / 2];
for (int i = 0; i < regs.length; i++) {
regs[i] = (short) (((raw[i * 2] & 0xFF) << 8) | (raw[i * 2 + 1] & 0xFF));
}
return regs;
}
// -------------------- 数值转换实现(统一、可维护) --------------------
private int toInt(short[] regs, ByteOrderType boType) {
if (regs.length == 1) {
return regs[0]; // 单寄存器,保留 signed short 扩展
}
if (regs.length < 2) throw new IllegalArgumentException("regs 长度必须 >= 2");
byte[] raw = regsToRawBytes(new short[]{regs[0], regs[1]});
byte[] reordered = reorderBytesForRead(raw, boType);
return ByteBuffer.wrap(reordered).order(ByteOrder.BIG_ENDIAN).getInt();
}
private long toLong(short[] regs, ByteOrderType boType) {
if (regs.length != 4) throw new IllegalArgumentException("regs 长度必须为4");
byte[] raw = regsToRawBytes(regs);
byte[] reordered = reorderBytesForRead(raw, boType);
return ByteBuffer.wrap(reordered).order(ByteOrder.BIG_ENDIAN).getLong();
}
private float toFloat(short[] regs, ByteOrderType boType) {
if (regs.length < 2) throw new IllegalArgumentException("regs 长度必须 >= 2");
byte[] raw = regsToRawBytes(new short[]{regs[0], regs[1]});
byte[] reordered = reorderBytesForRead(raw, boType);
return ByteBuffer.wrap(reordered).order(ByteOrder.BIG_ENDIAN).getFloat();
}
private double toDouble(short[] regs, ByteOrderType boType) {
if (regs.length != 4) throw new IllegalArgumentException("regs 长度必须为4");
byte[] raw = regsToRawBytes(regs);
byte[] reordered = reorderBytesForRead(raw, boType);
return ByteBuffer.wrap(reordered).order(ByteOrder.BIG_ENDIAN).getDouble();
}
// 从基本类型生成寄存器(short[])
private short[] fromFloat(float value, ByteOrderType boType) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(value).array();
// inverse: 根据字节序先获得 raw 字节,应当把 `bytes` 视为网络顺序(ABCD),
// 因此要把 `bytes` 按相反的 reordering 写回 regs:
byte[] raw = reorderBytesForWrite(bytes, boType); // 使其成为 "寄存器大端序" 的原始 bytes
return rawBytesToRegs(raw);
}
private short[] fromDouble(double value, ByteOrderType boType) {
byte[] bytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putDouble(value).array();
byte[] raw = reorderBytesForWrite(bytes, boType);
return rawBytesToRegs(raw);
}
private short[] toShorts(int value, ByteOrderType boType) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value).array();
byte[] raw = reorderBytesForWrite(bytes, boType);
return rawBytesToRegs(raw);
}
private short[] toShorts(long value, ByteOrderType boType) {
byte[] bytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(value).array();
byte[] raw = reorderBytesForWrite(bytes, boType);
return rawBytesToRegs(raw);
}
/**
* 返回用于逆向重排的类型(因为我们上面 reorderBytesForType 的定义是“从 regs->字节->按字节序重排得到目标顺序”)。
* 当我们有一个按网络顺序(BIG_ENDIAN)的字节数组(例如 ByteBuffer.putInt 产生的),
* 为得到写入寄存器的 raw bytes(每个寄存器高字节在前),需要对这个数组做 inverse reorder。
*/
private static ByteOrderType inverseOrder(ByteOrderType boType) {
switch (boType) {
case ABCD: return ByteOrderType.ABCD;
case CDAB: return ByteOrderType.CDAB; // 自反映射在 reorderBytesForType 中是对称的(因为 map 的逆映射与 map 本身一致)
case BADC: return ByteOrderType.BADC;
case DCBA: return ByteOrderType.DCBA;
default: return ByteOrderType.CDAB;
}
}
// -------------------- 关闭 --------------------
public void close() {
executor.shutdown();
try {
if (!executor.awaitTermination(3, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
executor.shutdownNow();
}
log.info("EnhancedModbusTcpClientWithByteOrder closed");
}
// -------------------- 简单 main 测试示例 --------------------
public static void main(String[] args) throws Exception {
EnhancedModbusTcpClientWithByteOrder client =
new EnhancedModbusTcpClientWithByteOrder("127.0.0.1", 2010, 10);
// // 示例:读取 10001 (coil) — 注意 zeroBasedAddress 参数语义
// Future<Object> bit1 = client.read(1, 10001, PlcDataType.BOOLEAN, false, ByteOrderType.CDAB);
// System.out.println("10001 = " + bit1.get());
//
// // 写入 FLOAT32
// client.write(1, 40001,PlcDataType.FLOAT32, 38.5f, false, ByteOrderType.CDAB);
// // 读取 FLOAT32
// Float temp = (Float) client.read(1, 40001,PlcDataType.FLOAT32, false, ByteOrderType.CDAB).get();
// System.out.println("40001 FLOAT32 = " + temp);
// // 写入 FLOAT64
// client.write(1, 40003,PlcDataType.FLOAT64, 102.23, false, ByteOrderType.CDAB);
// // 读取 FLOAT64
// Double pressure = (Double) client.read(1, 40003,PlcDataType.FLOAT64, false, ByteOrderType.CDAB).get();
// System.out.println("40003 FLOAT64 = " + pressure);
// // 写入 FLOAT32
// client.write(1, 40053, PlcDataType.FLOAT32, 56.5f, false, ByteOrderType.CDAB);
// Float one = (Float) client.read(1, 40053,PlcDataType.FLOAT32, false, ByteOrderType.CDAB).get();
// System.out.println("40008 INT16 = " + one);
// // 写入 40009 寄存器的第 2 位
client.writeBooleanToRegisterBit(1, "40051.13", true, false);
// // 读取 40009 寄存器的第 2 位
// boolean bitValue = client.readBooleanFromRegisterBit(1, "40009.02", false);
// System.out.println("40009.01 = " + bitValue);
// 写入 FLOAT32
// client.write(1, 40053,PlcDataType.FLOAT32, 9, false, ByteOrderType.ABCD);
// System.out.println(client.read(1, 40053, PlcDataType.FLOAT32, false, ByteOrderType.ABCD).get());
// System.out.println(client.read(1, 10001,PlcDataType.BOOLEAN, false, ByteOrderType.CDAB).get());
// System.out.println(client.read(1, 10002,PlcDataType.BOOLEAN, false, ByteOrderType.CDAB).get());
// System.out.println(client.read(1, 10003,PlcDataType.BOOLEAN, false, ByteOrderType.CDAB).get());
client.close();
}
}
... ...
... ... @@ -8,6 +8,7 @@ import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcDataType;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint;
import com.zhonglai.luhui.device.modbus.terminal.task.CollectPlcDataTask;
import org.slf4j.Logger;
... ... @@ -18,6 +19,7 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
/**
* modbus通讯工具类,采用modbus4j实现
... ... @@ -46,94 +48,20 @@ public class Modbus4jRead {
/**
* 批量读取点位
*/
public Map<String, Object> batchRead(List<PlcPoint> plcPoints, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException {
BatchRead<String> batch = new BatchRead<>();
for (PlcPoint plcPoint : plcPoints) {
String key = plcPoint.getSystem();
// 解析 Modbus 地址,生成 BaseLocator
BaseLocator<?> locator = ModbusAddressParser.parseLocator(
plcPoint.getAddress(),
plcPoint.getDataType(),
plcPoint.getSlaveId(),
zeroBasedAddress,
plcPoint.getOrder()
);
batch.addLocator(key, locator);
}
batch.setContiguousRequests(false);
// 发送批量请求
BatchResults<String> results = null;
try {
results = ModbusMasterMessage.getMaster( id).send(batch);
} catch (Exception e) {
logger.error("第一次批量读取异常,尝试一次", e);
if (e instanceof ModbusInitException || e instanceof ModbusTransportException || e.getCause() instanceof IOException) {
ModbusMasterMessage.closeMaster(id);
try {
results = ModbusMasterMessage.getMaster( id).send(batch);
} catch (Exception ex) {
logger.error("第二次批量读取异常,不处理", ex);
return null;
}
} else {
logger.info("第一次批量读不是联系异常,不尝试了");
return null;
}
}
// 转换成 JSON 对象
public Map<String, Object> batchRead( List<PlcPoint> plcPoints, boolean zeroBasedAddress) throws Exception {
EnhancedModbusTcpClientWithByteOrder client = ModbusMasterMessage.getEnhancedModbusTcpClientWithByteOrder(id);
Map<String, Object> jsonMap = new HashMap<>();
for (PlcPoint plcPoint : plcPoints) {
// 输出所有结果
String key = plcPoint.getSystem();
Object value = results.getValue(key);
switch (plcPoint.getDataType())
Object value;
String addrStr = plcPoint.getAddress();
EnhancedModbusTcpClientWithByteOrder.ModbusAddress addr = EnhancedModbusTcpClientWithByteOrder.ModbusAddress.parse(addrStr);
if (addr.getBitIndex()>-1)
{
value = client.readBooleanFromRegisterBit(1, addrStr, zeroBasedAddress);
}else
{
case FLOAT32:
if (value instanceof Integer) {
int raw = (int) value;
switch (plcPoint.getOrder()) {
case "ABCD":
value = ModbusAddressParser.intToFloatABCD(raw);
break;
case "BADC":
value = ModbusAddressParser.intToFloatBADC(raw);
break;
case "CDAB":
value = ModbusAddressParser.intToFloatCDAB(raw);
break;
case "DCBA":
value = ModbusAddressParser.intToFloatDCBA(raw);
break;
}
}
break;
case INT32:
if (value instanceof Integer) {
int raw = (int) value;
switch (plcPoint.getOrder()) {
case "ABCD":
value = ModbusAddressParser.int32ABCD(raw);
break;
case "BADC":
value = ModbusAddressParser.int32BADC(raw);
break;
case "CDAB":
value = ModbusAddressParser.int32CDAB(raw);
break;
case "DCBA":
value = ModbusAddressParser.int32DCBA(raw);
break;
}
}
break;
value = client.read(1, addr.getStartAddress(),plcPoint.getDataType(),zeroBasedAddress,null == plcPoint.getOrder()? EnhancedModbusTcpClientWithByteOrder.ByteOrderType.ABCD: EnhancedModbusTcpClientWithByteOrder.ByteOrderType.valueOf(plcPoint.getOrder())).get();
}
jsonMap.put(key, value);
}
... ...
... ... @@ -34,120 +34,24 @@ public class Modbus4jWrite {
public Modbus4jWrite(String id) throws Exception {
this.id = id;
}
private final Object writeLock = new Object(); // 全局串行化写锁
public void batchWrite(List<PlcPoint> points, boolean zeroBasedAddress) throws Exception {
if (points == null || points.isEmpty()) return;
synchronized (writeLock) {
ModbusMaster master = ModbusMasterMessage.getMaster(id);
try {
sendBatch(master, points, zeroBasedAddress);
} catch (Exception e) {
log.info("第一次批量写入异常,尝试重连后再写入", e);
if (e instanceof ModbusInitException || e instanceof ModbusTransportException || e.getCause() instanceof IOException) {
ModbusMasterMessage.closeMaster(id);
master = ModbusMasterMessage.getMaster(id);
sendBatch(master, points, zeroBasedAddress);
} else {
log.info("第二次批量写入异常,不处理", e);
}
}
}
}
// 批量写入封装
private void sendBatch(ModbusMaster master, List<PlcPoint> points, boolean zeroBasedAddress) throws Exception {
for (PlcPoint point : points) {
BaseLocator<?> locator = ModbusAddressParser.parseLocator(
point.getAddress(),
point.getDataType(),
point.getSlaveId(),
zeroBasedAddress,
point.getOrder()
);
Object value = convertValue(point);
if (point.getDataType() == PlcDataType.BIT && point.getAddress().contains(".")) {
// ⚡ 先读寄存器,再修改 bit 位,再写回
int offset = ModbusAddressParser.parseOffset(point.getAddress(), zeroBasedAddress);
int bitIndex = ModbusAddressParser.parseBitIndex(point.getAddress());
if (bitIndex < 0) {
throw new IllegalArgumentException("无效的 bit 地址: " + point.getAddress());
}
// 读 holding register
int rawValue = master.getValue(
BaseLocator.holdingRegister(point.getSlaveId(), offset, DataType.TWO_BYTE_INT_UNSIGNED)
).intValue();
// 修改 bit
boolean bitVal = (Boolean) value;
if (bitVal) {
rawValue = rawValue | (1 << bitIndex); // 置位
} else {
rawValue = rawValue & ~(1 << bitIndex); // 清位
}
// 写回
master.setValue(
ModbusAddressParser.parseLocator(
point.getAddress().split("\\.")[0],
PlcDataType.INT16,
point.getSlaveId(),
zeroBasedAddress,
point.getOrder()
),
rawValue
);
log.info("写入成功(Bit): {}[{}] = {}", point.getName(), point.getAddress(), value);
} else {
// ⚡ 其它类型照旧
master.setValue(locator, value);
log.info("写入成功: {}[{}] = {}", point.getName(), point.getAddress(), value);
}
}
}
// 值转换逻辑沿用你的 writePoint
private Object convertValue(PlcPoint point) {
Object value = point.getValue();
if (value instanceof String) {
String strVal = (String) value;
switch (point.getDataType()) {
case BIT: return Boolean.parseBoolean(strVal);
case INT16: return Short.parseShort(strVal);
case UINT16: return Integer.parseInt(strVal);
case INT32:
int raw = Integer.parseInt(strVal);
switch (point.getOrder()) {
case "ABCD": return ModbusAddressParser.int32ToABCD(raw);
case "BADC": return ModbusAddressParser.int32ToBADC(raw);
case "CDAB": return ModbusAddressParser.int32ToCDAB(raw);
case "DCBA": return ModbusAddressParser.int32ToDCBA(raw);
default: return raw;
}
case INT64: return Long.parseLong(strVal);
case FLOAT32:
float fvalue = Float.parseFloat(strVal);
switch (point.getOrder()) {
case "ABCD": return ModbusAddressParser.floatToIntABCD(fvalue);
case "BADC": return ModbusAddressParser.floatToIntBADC(fvalue);
case "CDAB": return ModbusAddressParser.floatToIntCDAB(fvalue);
case "DCBA": return ModbusAddressParser.floatToIntDCBA(fvalue);
default: return fvalue;
}
case DOUBLE64: return Double.parseDouble(strVal);
default: throw new IllegalArgumentException("不支持的数据类型: " + point.getDataType());
EnhancedModbusTcpClientWithByteOrder client = ModbusMasterMessage.getEnhancedModbusTcpClientWithByteOrder(id);
for (PlcPoint plcPoint : points)
{
EnhancedModbusTcpClientWithByteOrder.ModbusAddress addr = EnhancedModbusTcpClientWithByteOrder.ModbusAddress.parse(plcPoint.getAddress());
if (addr.getBitIndex()>-1)
{
log.info("BIT写入数据:addr {},plcPoint {}",addr,plcPoint);
client.writeBooleanToRegisterBit(1, plcPoint.getAddress(), Boolean.valueOf(String.valueOf(plcPoint.getValue())), zeroBasedAddress);
}else
{
log.info(plcPoint.getDataType()+"写入数据:addr {},plcPoint {}",addr,plcPoint);
client.write(1, addr.getStartAddress(),plcPoint.getDataType(),plcPoint.getValue(), zeroBasedAddress, null == plcPoint.getOrder()? EnhancedModbusTcpClientWithByteOrder.ByteOrderType.CDAB: EnhancedModbusTcpClientWithByteOrder.ByteOrderType.valueOf(plcPoint.getOrder()));
}
}
return value;
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.device.modbus.terminal.modbus;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.code.DataType;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcDataType;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ModbusAddressParser {
/**
* 解析 Modbus 地址,生成 BaseLocator
* @param rawAddress 原始地址 (40001, 40001.01, 40003-40004)
* @param type 数据类型 (BIT, INT16, UINT16, INT32, INT64, FLOAT32, DOUBLE64)
* @param slaveId 从站ID
* @return BaseLocator 对象
*/
public static BaseLocator<?> parseLocator(String rawAddress, PlcDataType type, int slaveId, boolean zeroBasedAddress,String order) {
int offset = parseOffset(rawAddress,zeroBasedAddress);
int bitIndex = parseBitIndex(rawAddress);
switch (type) {
case BIT:
if (rawAddress.startsWith("0")) {
// Coil (线圈输出)
return BaseLocator.coilStatus(slaveId, offset);
} else if (rawAddress.startsWith("1")) {
// Discrete Input (离散量输入)
return BaseLocator.inputStatus(slaveId, offset);
} else if (rawAddress.startsWith("3") && bitIndex >= 0) {
// Input Register 内部 bit 位
return BaseLocator.inputRegisterBit(slaveId, offset, bitIndex);
} else if (rawAddress.startsWith("4") && bitIndex >= 0) {
// Holding Register 内部 bit 位
return BaseLocator.holdingRegisterBit(slaveId, offset, bitIndex);
} else {
throw new IllegalArgumentException("地址不支持 BIT 类型: " + rawAddress);
}
case INT16:
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED);
}
break;
case UINT16:
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);
}
break;
case INT32:
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED);
}
break;
case INT64:
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED);
}
break;
case FLOAT32:
if (order == null) {
order = "ABCD"; // 默认大端
}
int floatDataType = DataType.FOUR_BYTE_FLOAT;
switch (order) {
case "ABCD": // 大端 IEEE754
floatDataType = DataType.FOUR_BYTE_FLOAT;
break;
case "CDAB": // 寄存器交换
floatDataType = DataType.FOUR_BYTE_FLOAT_SWAPPED;
break;
case "BADC": // 字节交换
floatDataType = DataType.FOUR_BYTE_FLOAT_SWAPPED_INVERTED;
break;
case "DCBA": // 全反转
floatDataType = DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED;
// ⚠ modbus4j 没有直接 DCBA 的 float,可以用 int SWAP-SWAP 再手动转 float
break;
}
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, floatDataType);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, floatDataType);
}
break;
case DOUBLE64:
if (rawAddress.startsWith("3")) {
return BaseLocator.inputRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT);
} else if (rawAddress.startsWith("4")) {
return BaseLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT);
}
break;
default:
throw new IllegalArgumentException("不支持的数据类型: " + type);
}
throw new IllegalArgumentException("地址和数据类型不匹配: " + rawAddress + " -> " + type);
}
/**
* 解析寄存器地址(不包含小数点后的 bit 部分)
* @param addressStr e.g. "40050.06" 或 "40050"
* @param zeroBasedAddress 是否零基地址
* @return int registerAddress
*/
public static int parseRegisterAddress(String addressStr, boolean zeroBasedAddress) {
String[] parts = addressStr.split("\\.");
int baseAddr = Integer.parseInt(parts[0]);
// 去掉40001/30001等逻辑前缀,Modbus4j用的是零基
if (baseAddr >= 40001 && baseAddr <= 49999) {
baseAddr = baseAddr - 40001;
} else if (baseAddr >= 30001 && baseAddr <= 39999) {
baseAddr = baseAddr - 30001;
} else if (baseAddr >= 1 && baseAddr <= 9999) {
baseAddr = baseAddr - 1;
}
if (!zeroBasedAddress) {
baseAddr = baseAddr + 1; // 用户配置非零基,则+1
}
return baseAddr;
}
/** 解析寄存器偏移量 */
public static int parseOffset(String rawAddress, boolean zeroBasedAddress) {
if (rawAddress == null || rawAddress.isEmpty()) {
throw new IllegalArgumentException("地址不能为空");
}
// 处理区间地址:40003-40004
if (rawAddress.contains("-")) {
String[] parts = rawAddress.split("-");
rawAddress = parts[0]; // 取起始地址
}
// 去掉小数点后部分:40001.01 -> 40001
String basePart = rawAddress.split("\\.")[0];
int address = Integer.parseInt(basePart);
int offset;
if (address >= 1 && address <= 9999) { // Coil (0xxxx)
offset = address - 1;
} else if (address >= 10001 && address <= 19999) { // Discrete Input (1xxxx)
offset = address - 10001;
} else if (address >= 30001 && address <= 39999) { // Input Register (3xxxx)
offset = address - 30001;
} else if (address >= 40001 && address <= 49999) { // Holding Register (4xxxx)
offset = address - 40001;
} else {
throw new IllegalArgumentException("无效的Modbus地址: " + rawAddress);
}
// 根据 zeroBasedAddress 参数调整偏移
if (!zeroBasedAddress) {
offset += 1;
}
return offset;
}
/** 解析 bit 索引 */
public static int parseBitIndex(String rawAddress) {
if (rawAddress.contains(".")) {
String[] parts = rawAddress.split("\\.");
return Integer.parseInt(parts[1]) - 1; // 转成从0开始
}
return -1; // 没有 bit 位
}
public static float intToFloatDCBA(int value) {
byte[] bytes = new byte[4];
bytes[0] = (byte) (value & 0xFF);
bytes[1] = (byte) ((value >> 8) & 0xFF);
bytes[2] = (byte) ((value >> 16) & 0xFF);
bytes[3] = (byte) ((value >> 24) & 0xFF);
// 反转成 DCBA
byte tmp;
tmp = bytes[0]; bytes[0] = bytes[3]; bytes[3] = tmp;
tmp = bytes[1]; bytes[1] = bytes[2]; bytes[2] = tmp;
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();
}
public static int floatToIntDCBA(float f) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array();
// 反转成 DCBA
byte tmp;
tmp = bytes[0]; bytes[0] = bytes[3]; bytes[3] = tmp;
tmp = bytes[1]; bytes[1] = bytes[2]; bytes[2] = tmp;
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static float intToFloatABCD(int value) {
byte[] bytes = intToBytes(value);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();
}
public static float intToFloatBADC(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 1);
swap(bytes, 2, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();
}
public static float intToFloatCDAB(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 2);
swap(bytes, 1, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();
}
// ========== 32位整型的不同字节序 ==========
public static int int32ABCD(int value) {
return value; // 默认就是ABCD
}
public static int int32BADC(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 1);
swap(bytes, 2, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int int32CDAB(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 2);
swap(bytes, 1, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int int32DCBA(int value) {
byte[] bytes = intToBytes(value);
reverse(bytes);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
// ========== 通用工具 ==========
private static byte[] intToBytes(int value) {
return new byte[] {
(byte) ((value >> 24) & 0xFF),
(byte) ((value >> 16) & 0xFF),
(byte) ((value >> 8) & 0xFF),
(byte) (value & 0xFF)
};
}
private static void swap(byte[] arr, int i, int j) {
byte tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
private static void reverse(byte[] arr) {
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
swap(arr, i, j);
}
}
// ========== Float 写入 ==========
public static int floatToIntABCD(float f) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array();
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int floatToIntBADC(float f) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array();
swap(bytes, 0, 1);
swap(bytes, 2, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int floatToIntCDAB(float f) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array();
swap(bytes, 0, 2);
swap(bytes, 1, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
// ========== Int32 写入 ==========
public static int int32ToABCD(int value) {
return value; // 默认
}
public static int int32ToBADC(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 1);
swap(bytes, 2, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int int32ToCDAB(int value) {
byte[] bytes = intToBytes(value);
swap(bytes, 0, 2);
swap(bytes, 1, 3);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
public static int int32ToDCBA(int value) {
byte[] bytes = intToBytes(value);
reverse(bytes);
return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
}
private short[] toRegisters(Object value, PlcDataType dataType, String order) {
switch (dataType) {
case INT16:
return new short[]{(Short) value};
case UINT16:
return new short[]{((Integer) value).shortValue()};
case INT32:
int intVal = (Integer) value;
return int32ToShorts(intVal, order);
case FLOAT32:
float fVal = (Float) value;
return floatToShorts(fVal, order);
case DOUBLE64:
double dVal = (Double) value;
return doubleToShorts(dVal, order);
default:
throw new IllegalArgumentException("不支持的数据类型: " + dataType);
}
}
// INT32 转 short[2]
private short[] int32ToShorts(int value, String order) {
short high = (short) ((value >> 16) & 0xFFFF);
short low = (short) (value & 0xFFFF);
if ("ABCD".equals(order)) return new short[]{high, low};
if ("BADC".equals(order)) return new short[]{(short)((high & 0xFF) << 8 | (high >> 8 & 0xFF)),
(short)((low & 0xFF) << 8 | (low >> 8 & 0xFF))};
if ("CDAB".equals(order)) return new short[]{low, high};
if ("DCBA".equals(order)) return new short[]{(short)((low & 0xFF) << 8 | (low >> 8 & 0xFF)),
(short)((high & 0xFF) << 8 | (high >> 8 & 0xFF))};
return new short[]{high, low};
}
// FLOAT32 转 short[2]
private short[] floatToShorts(float f, String order) {
int bits = Float.floatToIntBits(f);
return int32ToShorts(bits, order);
}
// DOUBLE64 转 short[4]
private short[] doubleToShorts(double d, String order) {
long bits = Double.doubleToLongBits(d);
short[] regs = new short[4];
regs[0] = (short)((bits >> 48) & 0xFFFF);
regs[1] = (short)((bits >> 32) & 0xFFFF);
regs[2] = (short)((bits >> 16) & 0xFFFF);
regs[3] = (short)(bits & 0xFFFF);
switch (order) {
case "ABCD": return regs;
case "BADC": return new short[]{regs[1], regs[0], regs[3], regs[2]};
case "CDAB": return new short[]{regs[2], regs[3], regs[0], regs[1]};
case "DCBA": return new short[]{regs[3], regs[2], regs[1], regs[0]};
default: return regs;
}
}
}
package com.zhonglai.luhui.device.modbus.terminal.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.JSerialCommWrapper;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 统一管理 ModbusMaster 的连接池/缓存(按 key 缓存)
* key 建议:tcp -> "tcp:host:port" ; rtu/ascii/udp -> 使用 PLC id 或可序列化配置信息
*/
public class ModbusConnectionManager {
private static final Logger log = LoggerFactory.getLogger(ModbusConnectionManager.class);
private static final ModbusFactory factory = new ModbusFactory();
// key -> ModbusMaster
private static final ConcurrentHashMap<String, ModbusMaster> masterCache = new ConcurrentHashMap<>();
// key -> lock object for double-check init
private static final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
private ModbusConnectionManager() {}
/**
* 获取或创建 TCP Master(host + port 作为 key)
*/
public static ModbusMaster getTcpMaster(String host, int port, int timeout, int retries, boolean encapsulated) throws Exception {
String key = String.format("tcp:%s:%d", Objects.requireNonNull(host), port);
ModbusMaster m = masterCache.get(key);
if (m != null) return m;
Object lock = lockMap.computeIfAbsent(key, k -> new Object());
synchronized (lock) {
m = masterCache.get(key);
if (m != null) return m;
IpParameters params = new IpParameters();
params.setHost(host);
params.setPort(port);
// encapsulated param controls MBAP or not (depends on device); keep configurable
m = factory.createTcpMaster(params, encapsulated);
m.setTimeout(timeout);
m.setRetries(retries);
try {
// set custom IO log file if needed
m.setIoLog(new MyIOLog(new File("logs/modbus.log")));
} catch (Exception e) {
// 忽略日志初始化失败
}
m.init();
masterCache.put(key, m);
log.info("Created TCP ModbusMaster [{}]", key);
return m;
}
}
/**
* 通用创建接口 —— 通过传入 protocolType 与 connectConfig(用于兼容 InitPlcConfig)
* 支持:TCP / RTU / ASCII / UDP (与原 createMaster 保持一致)
*
* protocolType: "TCP" / "RTU" / "ASCII" / "UDP"
* connectConfig: JSONObject(与原代码互操作)
*/
public static ModbusMaster getOrCreateMaster(String key, String protocolType, JSONObject connectConfig, int timeout, int retries) throws Exception {
ModbusMaster m = masterCache.get(key);
if (m != null) return m;
Object lock = lockMap.computeIfAbsent(key, k -> new Object());
synchronized (lock) {
m = masterCache.get(key);
if (m != null) return m;
ModbusMaster created;
switch (protocolType) {
case "TCP": {
IpParameters parameters = JSONObject.parseObject(connectConfig.toJSONString(), IpParameters.class);
created = factory.createTcpMaster(parameters, true);
break;
}
case "RTU": {
JSerialCommWrapper wrapper = JSONObject.parseObject(connectConfig.toJSONString(), JSerialCommWrapper.class);
created = factory.createRtuMaster(wrapper);
break;
}
case "ASCII": {
JSerialCommWrapper wrapper = JSONObject.parseObject(connectConfig.toJSONString(), JSerialCommWrapper.class);
created = factory.createAsciiMaster(wrapper);
break;
}
case "UDP": {
IpParameters parameters = JSONObject.parseObject(connectConfig.toJSONString(), IpParameters.class);
created = factory.createUdpMaster(parameters);
break;
}
default:
throw new IllegalArgumentException("不支持的协议类型: " + protocolType);
}
created.setTimeout(timeout);
created.setRetries(retries);
try {
created.setIoLog(new MyIOLog(new File("logs/modbus.log")));
} catch (Exception ignored) {}
created.init();
masterCache.put(key, created);
log.info("Created ModbusMaster [{}] protocol={}", key, protocolType);
return created;
}
}
/**
* 关闭并移除指定 key 的 master
*/
public static void closeMaster(String key) {
Object lock = lockMap.computeIfAbsent(key, k -> new Object());
synchronized (lock) {
ModbusMaster m = masterCache.remove(key);
if (m != null) {
try {
m.destroy();
log.info("Closed ModbusMaster [{}]", key);
} catch (Exception e) {
log.warn("Close ModbusMaster [{}] failed: {}", key, e.getMessage());
}
}
lockMap.remove(key);
}
}
/**
* 关闭所有 master
*/
public static void closeAll() {
masterCache.forEach((k, v) -> {
try { v.destroy(); } catch (Exception ignored) {}
});
masterCache.clear();
lockMap.clear();
log.info("Closed all ModbusMaster instances");
}
}
... ...
package com.zhonglai.luhui.device.modbus.terminal.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.alibaba.fastjson.JSONObject;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.serotonin.modbus4j.ModbusMaster;
import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.JSerialCommWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通过 PLC ID 获取 ModbusMaster 或增强的 Modbus TCP Client。
* 所有 ModbusMaster 交由 ModbusConnectionManager 管理生命周期。
*
* 支持:
* - PLC ID 生成唯一 key
* - 安全缓存 Enhanced 客户端
* - 自动加载字节序
*/
public class ModbusMasterMessage {
private static final Logger log = LoggerFactory.getLogger(ModbusMasterMessage.class);
private static final ConcurrentHashMap<String, ModbusMaster> masterCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
private static final ModbusFactory factory = new ModbusFactory();
/** 获取或创建 ModbusMaster,不做连接性检测 */
public static ModbusMaster getMaster(String id) throws Exception {
ModbusMaster master = masterCache.get(id);
if (master != null) {
return master;
}
// double-check locking 保证只创建一次
Object lock = lockMap.computeIfAbsent(id, k -> new Object());
synchronized (lock) {
master = masterCache.get(id);
if (master == null) {
CachPlcConfig config = InitPlcConfig.getPlcSystems(id);
if (config == null) {
throw new IllegalArgumentException("未找到PLC配置: " + id);
}
master = createMaster(config);
master.setIoLog(new MyIOLog(new File("logs/modbus.log")));
master.init();
masterCache.put(id, master);
log.info("ModbusMaster[{}] 已创建", id);
}
/**
* 记录 key 与 plcId 的映射(目前主要用于 debug,可扩展)
*/
private static final ConcurrentHashMap<String, String> keyToPlcId = new ConcurrentHashMap<>();
/**
* 缓存增强版 TCP 客户端,避免多次创建造成连接池重复。
*/
private static final ConcurrentHashMap<String, EnhancedModbusTcpClientWithByteOrder> enhancedCache =
new ConcurrentHashMap<>();
/**
* 根据 PLC ID 获取 ModbusMaster,如不存在则由 ConnectionManager 创建。
*/
public static ModbusMaster getMaster(String plcId) throws Exception {
CachPlcConfig config = InitPlcConfig.getPlcSystems(plcId);
if (config == null) {
throw new IllegalArgumentException("未找到 PLC 配置: " + plcId);
}
String key = "modbus:master:" + plcId;
ModbusMaster master = ModbusConnectionManager.getOrCreateMaster(
key,
config.getProtocolType().name(),
config.getConnectConfig(),
3000,
1
);
keyToPlcId.put(key, plcId);
return master;
}
/**
* 创建 Master
* 获取或创建 EnhancedModbusTcpClientWithByteOrder(带缓存)
*/
private static ModbusMaster createMaster(CachPlcConfig config) throws Exception {
System.out.println("config:"+JSONObject.toJSONString(config));
JSONObject connectConfig = config.getConnectConfig();
switch (config.getProtocolType()) {
case TCP:
IpParameters parameters = JSONObject.parseObject(connectConfig.toJSONString(), IpParameters.class);
// parameters.setEncapsulated(false);
return factory.createTcpMaster(parameters
, true);
case RTU:
return factory.createRtuMaster(
JSONObject.parseObject(connectConfig.toJSONString(), JSerialCommWrapper.class));
case ASCII:
return factory.createAsciiMaster(
JSONObject.parseObject(connectConfig.toJSONString(), JSerialCommWrapper.class));
case UDP:
return factory.createUdpMaster(
JSONObject.parseObject(connectConfig.toJSONString(), IpParameters.class));
default:
throw new Exception("不支持协议");
}
public static EnhancedModbusTcpClientWithByteOrder getEnhancedModbusTcpClientWithByteOrder(String plcId)
throws Exception {
return enhancedCache.computeIfAbsent(plcId, id -> {
try {
CachPlcConfig config = InitPlcConfig.getPlcSystems(plcId);
if (config == null) {
throw new IllegalArgumentException("未找到 PLC 配置: " + plcId);
}
JSONObject conn = config.getConnectConfig();
String host = conn.getString("host");
// 修复 bug:原代码误写为 "post"
Integer portObj = conn.getInteger("port");
if (portObj == null) {
throw new IllegalArgumentException("PLC " + plcId + " 缺少 port 字段");
}
int port = portObj;
int poolSize = conn.getInteger("poolSize") != null && conn.getInteger("poolSize") > 0
? conn.getInteger("poolSize")
: 10;
EnhancedModbusTcpClientWithByteOrder client =
new EnhancedModbusTcpClientWithByteOrder(host, port, poolSize);
// 处理字节序设置
if (config.getByteOrder() != null) {
try {
EnhancedModbusTcpClientWithByteOrder.ByteOrderType order =
EnhancedModbusTcpClientWithByteOrder.ByteOrderType.valueOf(
config.getByteOrder().toUpperCase()
);
} catch (Exception e) {
log.warn("PLC {} 字节序配置不正确: {},使用默认字节序",
plcId, config.getByteOrder());
}
}
log.info("为 PLC {} 创建并缓存 EnhancedModbusTcpClientWithByteOrder: {}:{} (poolSize={})",
plcId, host, port, poolSize);
return client;
} catch (Exception e) {
log.error("创建 EnhancedModbusTcpClientWithByteOrder 失败 (PLC {}): {}", plcId, e.getMessage(), e);
throw new RuntimeException(e);
}
});
}
/**
* 关闭 Master
* 关闭指定 PLC 的 ModbusMaster
*/
public static void closeMaster(String id) {
Object lock = lockMap.computeIfAbsent(id, k -> new Object());
synchronized (lock) {
ModbusMaster master = masterCache.remove(id);
if (master != null) master.destroy();
lockMap.remove(id);
}
public static void closeMasterByPlcId(String plcId) {
String key = "modbus:master:" + plcId;
ModbusConnectionManager.closeMaster(key);
keyToPlcId.remove(key);
}
/**
* 关闭所有 Master
* 关闭所有 ModbusMaster,同时清空映射缓存
*/
public static void closeAll() {
if(null != masterCache)
{
masterCache.values().forEach(ModbusMaster::destroy);
masterCache.clear();
}
if (null != lockMap)
{
lockMap.clear();
}
ModbusConnectionManager.closeAll();
keyToPlcId.clear();
}
}
... ...
... ... @@ -8,10 +8,20 @@ import java.util.Map;
public class CachPlcConfig {
private String id;
private String systemName;
private String byteOrder;
private Boolean zeroBasedAddress;
private ProtocolType protocolType;
private JSONObject connectConfig;
private Map<String, PlcPoint> plcMap;
public Boolean getZeroBasedAddress() {
return zeroBasedAddress;
}
public void setZeroBasedAddress(Boolean zeroBasedAddress) {
this.zeroBasedAddress = zeroBasedAddress;
}
public String getSystemName() {
return systemName;
}
... ... @@ -51,4 +61,12 @@ public class CachPlcConfig {
public void setId(String id) {
this.id = id;
}
public String getByteOrder() {
return byteOrder;
}
public void setByteOrder(String byteOrder) {
this.byteOrder = byteOrder;
}
}
... ...
... ... @@ -3,14 +3,12 @@ package com.zhonglai.luhui.device.modbus.terminal.modbus.dto;
import com.fasterxml.jackson.annotation.JsonCreator;
public enum PlcDataType {
BIT, // 线圈/开关量
INPUT_BIT, // 输入离散量
BOOLEAN,
INT16,
UINT16,
INT32,
INT64,
INT32, // 新增
INT64, // 新增
FLOAT32,
DOUBLE64;
FLOAT64;
//忽略大小写
@JsonCreator
... ... @@ -18,13 +16,11 @@ public enum PlcDataType {
if (value == null) return null;
switch (value.toUpperCase()) {
case "BIT":
return BIT;
return BOOLEAN;
case "INT":
return INT16;
case "INT16":
return INT16;
case "UINT":
case "UINT16":
return UINT16;
case "LONG": // 兼容 long
return INT32;
case "INT32":
... ... @@ -32,14 +28,15 @@ public enum PlcDataType {
case "INT64":
return INT64;
case "FLOAT":
return FLOAT32;
case "FLOAT32":
return FLOAT32;
case "DOUBLE":
case "DOUBLE64":
return DOUBLE64;
case "INPUT_BIT":
case "INPUT":
return INPUT_BIT;
return FLOAT32;
case "BOOLEAN":
return BOOLEAN;
case "FLOAT64":
return FLOAT64;
default:
throw new IllegalArgumentException("未知的枚举值: " + value);
}
... ...
... ... @@ -6,9 +6,25 @@ import java.util.List;
public class PlcSystem {
private String id;
private String systemName;
private String byteOrder;
private Boolean zeroBasedAddress;
private ProtocolType protocolType;
private JSONObject connectConfig;
private List<PlcPoint> points;
public Boolean getZeroBasedAddress() {
return zeroBasedAddress;
}
public void setZeroBasedAddress(Boolean zeroBasedAddress) {
this.zeroBasedAddress = zeroBasedAddress;
}
public String getByteOrder() {
return byteOrder;
}
public void setByteOrder(String byteOrder) {
this.byteOrder = byteOrder;
}
public String getSystemName() {
return systemName;
... ...
... ... @@ -62,7 +62,7 @@ public class CollectPlcDataTask {
count++;
if (count == maxDataLenth) {
boolean success = subMqttData(mqttService, plcId, plcPoints);
boolean success = subMqttData(mqttService, plcId, plcPoints,cachPlcConfig.getZeroBasedAddress());
System.out.println("plc " + plcId + " 发送数据 " + plcPoints.size() + " 条");
if (!success) {
... ... @@ -85,7 +85,7 @@ public class CollectPlcDataTask {
// 处理剩余未满一批的数据
if (!plcPoints.isEmpty()) {
boolean success = subMqttData(mqttService, plcId, plcPoints);
boolean success = subMqttData(mqttService, plcId, plcPoints,cachPlcConfig.getZeroBasedAddress());
logger.info("plc " + plcId + " 发送数据 " + plcPoints.size() + " 条");
if (!success) {
logger.info("plc " + plcId + " 数据发送失败,本批次丢弃");
... ... @@ -94,11 +94,11 @@ public class CollectPlcDataTask {
}
private boolean subMqttData(MqttService mqttService, String plcId, List<PlcPoint> plcPoints)
private boolean subMqttData(MqttService mqttService, String plcId, List<PlcPoint> plcPoints, boolean zeroBasedAddress)
{
//通知
try {
Map<String, Object> datMap = new Modbus4jRead(plcId).batchRead(plcPoints,true);
Map<String, Object> datMap = new Modbus4jRead(plcId).batchRead(plcPoints,zeroBasedAddress);
JSONObject mqttSendData = new JSONObject();
mqttSendData.put(plcId+"", datMap);
String rdata = JSONObject.toJSONString(mqttSendData);
... ... @@ -107,7 +107,7 @@ public class CollectPlcDataTask {
if (e instanceof ModbusTransportException )
{
logger.error("plc通讯超时:"+plcId);
ModbusMasterMessage.closeMaster(plcId); // 销毁旧连接
ModbusMasterMessage.closeMasterByPlcId(plcId); // 销毁旧连接
return false;
}else
if(e instanceof MqttException)
... ... @@ -117,7 +117,7 @@ public class CollectPlcDataTask {
if (e instanceof ErrorResponseException)
{
logger.error("plc返回指令异常",e);
ModbusMasterMessage.closeMaster(plcId); // 销毁旧连接
ModbusMasterMessage.closeMasterByPlcId(plcId); // 销毁旧连接
return false;
}else{
throw new RuntimeException(e);
... ...
{
"plcs": [
{
"id": "2_1",
"systemName": "成鱼系统1",
"protocolType": "TCP",
"byteOrder": "CBAD",
"connectConfig": { "host": "192.168.2.11", "port": 2010,"poolSize": 10},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
{"name": "补水泵启动", "system": "bsbqd", "address": "10003", "dataType": "bit"},
{"name": "水泵1运行", "system": "sb1", "address": "10004", "dataType": "bit"},
{"name": "水泵2运行", "system": "sb2", "address": "10005", "dataType": "bit"},
{"name": "氧锥泵1运行", "system": "yzb1yx", "address": "10006", "dataType": "bit"},
{"name": "氧锥泵2运行", "system": "yzb2yx", "address": "10007", "dataType": "bit"},
{"name": "氧锥泵3运行", "system": "yzb3yx", "address": "10008", "dataType": "bit"},
{"name": "氧锥泵4运行", "system": "yzb4yx", "address": "10009", "dataType": "bit"},
{"name": "排污泵运行", "system": "pwb", "address": "10010", "dataType": "bit"},
{"name": "微滤机电源合闸", "system": "wlj", "address": "10011", "dataType": "bit"},
{"name": "紫外灯电源合闸", "system": "zwd", "address": "10012", "dataType": "bit"},
{"name": "微滤池高液位", "system": "wlq", "address": "10013", "dataType": "bit"},
{"name": "微滤池低液位", "system": "wld", "address": "10014", "dataType": "bit"},
{"name": "蝶阀1开到位", "system": "df1kdw", "address": "10015", "dataType": "bit"},
{"name": "蝶阀1关到位", "system": "df1gdw", "address": "10016", "dataType": "bit"},
{"name": "蝶阀2开到位", "system": "df2kdw", "address": "10017", "dataType": "bit"},
{"name": "蝶阀2关到位", "system": "df2gdw", "address": "10018", "dataType": "bit"},
{"name": "蝶阀3开到位", "system": "df3kdw", "address": "10019", "dataType": "bit"},
{"name": "蝶阀3关到位", "system": "df3gdw", "address": "10020", "dataType": "bit"},
{"name": "蝶阀4开到位", "system": "df4kdw", "address": "10021", "dataType": "bit"},
{"name": "蝶阀4关到位", "system": "df4gdw", "address": "10022", "dataType": "bit"},
{"name": "蝶阀5开到位", "system": "df5kdw", "address": "10023", "dataType": "bit"},
{"name": "蝶阀5关到位", "system": "df5gdw", "address": "10024", "dataType": "bit"},
{"name": "蝶阀6开到位", "system": "df6kdw", "address": "10025", "dataType": "bit"},
{"name": "蝶阀6关到位", "system": "df6gdw", "address": "10026", "dataType": "bit"},
{"name": "蝶阀7开到位", "system": "df7kdw", "address": "10027", "dataType": "bit"},
{"name": "蝶阀7关到位", "system": "df7gdw", "address": "10028", "dataType": "bit"},
{"name": "蝶阀8开到位", "system": "df8kdw", "address": "10029", "dataType": "bit"},
{"name": "蝶阀8关到位", "system": "df8gdw", "address": "10030", "dataType": "bit"},
{"name": "循环水泵运行", "system": "xhsbyx", "address": "10031", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "水泵1故障", "system": "sb1gz", "address": "40001.09", "dataType": "bit"},
{"name": "水泵2故障", "system": "sb2gz", "address": "40001.10", "dataType": "bit"},
{"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.11", "dataType": "bit"},
{"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.12", "dataType": "bit"},
{"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.13", "dataType": "bit"},
{"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.14", "dataType": "bit"},
{"name": "排污泵故障", "system": "pwb_gz", "address": "40001.15", "dataType": "bit"},
{"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.01", "dataType": "bit"},
{"name": "排污阀1关不到位", "system": "pwf1gbdw", "address": "40001.02", "dataType": "bit"},
{"name": "排污阀2开不到位", "system": "pwf2kbdw", "address": "40001.03", "dataType": "bit"},
{"name": "排污阀2关不到位", "system": "pwf2gbdw", "address": "40001.04", "dataType": "bit"},
{"name": "排污阀3开不到位", "system": "pwf3kbdw", "address": "40001.05", "dataType": "bit"},
{"name": "排污阀3关不到位", "system": "pwf3gbdw", "address": "40001.06", "dataType": "bit"},
{"name": "排污阀4开不到位", "system": "pwf4kbdw", "address": "40001.07", "dataType": "bit"},
{"name": "排污阀4关不到位", "system": "pwf4gbdw", "address": "40001.08", "dataType": "bit"},
{"name": "排污阀5开不到位", "system": "pwf5kbdw", "address": "40002.09", "dataType": "bit"},
{"name": "排污阀5关不到位", "system": "pwf5gbdw", "address": "40002.10", "dataType": "bit"},
{"name": "排污阀6开不到位", "system": "pwf6kbdw", "address": "40002.11", "dataType": "bit"},
{"name": "排污阀6关不到位", "system": "pwf6gbdw", "address": "40002.12", "dataType": "bit"},
{"name": "排污阀7开不到位", "system": "pwf7kbdw", "address": "40002.13", "dataType": "bit"},
{"name": "排污阀7关不到位", "system": "pwf7gbdw", "address": "40002.14", "dataType": "bit"},
{"name": "排污阀8开不到位", "system": "pwf8kbdw", "address": "40002.15", "dataType": "bit"},
{"name": "排污阀8关不到位", "system": "pwf8gbdw", "address": "40002.16", "dataType": "bit"},
{"name": "补水高液位超时", "system": "bsgywdcs", "address": "40002.03", "dataType": "bit"},
{"name": "微滤池高液位超时", "system": "wlcgywdcs", "address": "40002.04", "dataType": "bit"},
{"name": "微滤机跳闸", "system": "wljtz", "address": "40002.05", "dataType": "bit"},
{"name": "紫外杀菌灯跳闸故障", "system": "zwsjdtz", "address": "40002.06", "dataType": "bit"},
{"name": "溶氧超限报警", "system": "rycxbj", "address": "40002.07", "dataType": "bit"},
{"name": "微滤池低液位长时间不消失报警", "system": "wlcdywbcsbj", "address": "40002.08", "dataType": "bit"},
{"name": "溶氧值", "system": "ryz", "address": "40003-40004","order": "ABCD", "dataType": "float32"},
{"name": "温度值", "system": "wdz", "address": "40005-40006","order": "ABCD", "dataType": "float32"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float32"},
{"name": "当前氧锥泵运行台数", "system": "dqyzb", "address": "40009", "dataType": "int"},
{"name": "氧锥泵1运行时间", "system": "yzb1_sj", "address": "40011-40012","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵2运行时间", "system": "yzb2_sj", "address": "40013-40014","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵3运行时间", "system": "yzb3_sj", "address": "40015-40016","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵4运行时间", "system": "yzb4_sj", "address": "40017-40018","order": "ABCD", "dataType": "int32"},
{"name": "生化池水温", "system": "shcsw", "address": "40019-40020","order": "ABCD", "dataType": "float32"},
{"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.09", "dataType": "bit"},
{"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.10", "dataType": "bit"},
{"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.11", "dataType": "bit"},
{"name": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.01", "dataType": "bit"},
{"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.02", "dataType": "bit"},
{"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.03", "dataType": "bit"},
{"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.04", "dataType": "bit"},
{"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.05", "dataType": "bit"},
{"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.06", "dataType": "bit"},
{"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.07", "dataType": "bit"},
{"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.08", "dataType": "bit"},
{"name": "水泵1启动", "system": "sb1start", "address": "40051.09", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.10", "dataType": "bit"},
{"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.11", "dataType": "bit"},
{"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.12", "dataType": "bit"},
{"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.13", "dataType": "bit"},
{"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.14", "dataType": "bit"},
{"name": "排污泵启动", "system": "pwb_qd", "address": "40051.15", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40052.01", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40052.02", "dataType": "bit"},
{"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.03", "dataType": "bit"},
{"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.04", "dataType": "bit"},
{"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.05", "dataType": "bit"},
{"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.06", "dataType": "bit"},
{"name": "排污泵停止", "system": "pwb_tz", "address": "40052.07", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.10", "dataType": "bit"},
{"name": "溶氧上限报警设定值", "system": "rysjup", "address": "40053-40054","order": "ABCD", "dataType": "float32"},
{"name": "溶氧下限报警设定值", "system": "rysjdown", "address": "40055-40056","order": "ABCD", "dataType": "float32"}
]
},
{
"id": "2_2",
"systemName": "成鱼系统2",
"protocolType": "TCP",
"byteOrder": "CBAD",
"connectConfig": { "host": "192.168.2.2", "port": 2001,"poolSize": 10},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
{"name": "补水泵启动", "system": "bsbqd", "address": "10003", "dataType": "bit"},
{"name": "水泵1运行", "system": "sb1", "address": "10004", "dataType": "bit"},
{"name": "水泵2运行", "system": "sb2", "address": "10005", "dataType": "bit"},
{"name": "氧锥泵1运行", "system": "yzb1yx", "address": "10006", "dataType": "bit"},
{"name": "氧锥泵2运行", "system": "yzb2yx", "address": "10007", "dataType": "bit"},
{"name": "氧锥泵3运行", "system": "yzb3yx", "address": "10008", "dataType": "bit"},
{"name": "氧锥泵4运行", "system": "yzb4yx", "address": "10009", "dataType": "bit"},
{"name": "排污泵运行", "system": "pwb", "address": "10010", "dataType": "bit"},
{"name": "微滤机电源合闸", "system": "wlj", "address": "10011", "dataType": "bit"},
{"name": "紫外灯电源合闸", "system": "zwd", "address": "10012", "dataType": "bit"},
{"name": "微滤池高液位", "system": "wlq", "address": "10013", "dataType": "bit"},
{"name": "微滤池低液位", "system": "wld", "address": "10014", "dataType": "bit"},
{"name": "蝶阀1开到位", "system": "df1kdw", "address": "10015", "dataType": "bit"},
{"name": "蝶阀1关到位", "system": "df1gdw", "address": "10016", "dataType": "bit"},
{"name": "蝶阀2开到位", "system": "df2kdw", "address": "10017", "dataType": "bit"},
{"name": "蝶阀2关到位", "system": "df2gdw", "address": "10018", "dataType": "bit"},
{"name": "蝶阀3开到位", "system": "df3kdw", "address": "10019", "dataType": "bit"},
{"name": "蝶阀3关到位", "system": "df3gdw", "address": "10020", "dataType": "bit"},
{"name": "蝶阀4开到位", "system": "df4kdw", "address": "10021", "dataType": "bit"},
{"name": "蝶阀4关到位", "system": "df4gdw", "address": "10022", "dataType": "bit"},
{"name": "蝶阀5开到位", "system": "df5kdw", "address": "10023", "dataType": "bit"},
{"name": "蝶阀5关到位", "system": "df5gdw", "address": "10024", "dataType": "bit"},
{"name": "蝶阀6开到位", "system": "df6kdw", "address": "10025", "dataType": "bit"},
{"name": "蝶阀6关到位", "system": "df6gdw", "address": "10026", "dataType": "bit"},
{"name": "蝶阀7开到位", "system": "df7kdw", "address": "10027", "dataType": "bit"},
{"name": "蝶阀7关到位", "system": "df7gdw", "address": "10028", "dataType": "bit"},
{"name": "蝶阀8开到位", "system": "df8kdw", "address": "10029", "dataType": "bit"},
{"name": "蝶阀8关到位", "system": "df8gdw", "address": "10030", "dataType": "bit"},
{"name": "循环水泵运行", "system": "xhsbyx", "address": "10031", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "水泵1故障", "system": "sb1gz", "address": "40001.09", "dataType": "bit"},
{"name": "水泵2故障", "system": "sb2gz", "address": "40001.10", "dataType": "bit"},
{"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.11", "dataType": "bit"},
{"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.12", "dataType": "bit"},
{"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.13", "dataType": "bit"},
{"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.14", "dataType": "bit"},
{"name": "排污泵故障", "system": "pwb_gz", "address": "40001.15", "dataType": "bit"},
{"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.01", "dataType": "bit"},
{"name": "排污阀1关不到位", "system": "pwf1gbdw", "address": "40001.02", "dataType": "bit"},
{"name": "排污阀2开不到位", "system": "pwf2kbdw", "address": "40001.03", "dataType": "bit"},
{"name": "排污阀2关不到位", "system": "pwf2gbdw", "address": "40001.04", "dataType": "bit"},
{"name": "排污阀3开不到位", "system": "pwf3kbdw", "address": "40001.05", "dataType": "bit"},
{"name": "排污阀3关不到位", "system": "pwf3gbdw", "address": "40001.06", "dataType": "bit"},
{"name": "排污阀4开不到位", "system": "pwf4kbdw", "address": "40001.07", "dataType": "bit"},
{"name": "排污阀4关不到位", "system": "pwf4gbdw", "address": "40001.08", "dataType": "bit"},
{"name": "排污阀5开不到位", "system": "pwf5kbdw", "address": "40002.09", "dataType": "bit"},
{"name": "排污阀5关不到位", "system": "pwf5gbdw", "address": "40002.10", "dataType": "bit"},
{"name": "排污阀6开不到位", "system": "pwf6kbdw", "address": "40002.11", "dataType": "bit"},
{"name": "排污阀6关不到位", "system": "pwf6gbdw", "address": "40002.12", "dataType": "bit"},
{"name": "排污阀7开不到位", "system": "pwf7kbdw", "address": "40002.13", "dataType": "bit"},
{"name": "排污阀7关不到位", "system": "pwf7gbdw", "address": "40002.14", "dataType": "bit"},
{"name": "排污阀8开不到位", "system": "pwf8kbdw", "address": "40002.15", "dataType": "bit"},
{"name": "排污阀8关不到位", "system": "pwf8gbdw", "address": "40002.16", "dataType": "bit"},
{"name": "补水高液位超时", "system": "bsgywdcs", "address": "40002.03", "dataType": "bit"},
{"name": "微滤池高液位超时", "system": "wlcgywdcs", "address": "40002.04", "dataType": "bit"},
{"name": "微滤机跳闸", "system": "wljtz", "address": "40002.05", "dataType": "bit"},
{"name": "紫外杀菌灯跳闸故障", "system": "zwsjdtz", "address": "40002.06", "dataType": "bit"},
{"name": "溶氧超限报警", "system": "rycxbj", "address": "40002.07", "dataType": "bit"},
{"name": "微滤池低液位长时间不消失报警", "system": "wldc", "address": "40002.08", "dataType": "bit"},
{"name": "溶氧值", "system": "ryz", "address": "40003-40004","order": "ABCD", "dataType": "float32"},
{"name": "温度值", "system": "wdz", "address": "40005-40006","order": "ABCD", "dataType": "float32"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float32"},
{"name": "当前氧锥泵运行台数", "system": "dqyzb", "address": "40009", "dataType": "int"},
{"name": "氧锥泵1运行时间", "system": "yzb1_sj", "address": "40011-40012","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵2运行时间", "system": "yzb2_sj", "address": "40013-40014","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵3运行时间", "system": "yzb3_sj", "address": "40015-40016","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵4运行时间", "system": "yzb4_sj", "address": "40017-40018","order": "ABCD", "dataType": "int32"},
{"name": "生化池水温", "system": "shcsw", "address": "40019-40020","order": "ABCD", "dataType": "float32"},
{"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.09", "dataType": "bit"},
{"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.10", "dataType": "bit"},
{"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.11", "dataType": "bit"},
{"name": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.01", "dataType": "bit"},
{"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.02", "dataType": "bit"},
{"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.03", "dataType": "bit"},
{"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.04", "dataType": "bit"},
{"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.05", "dataType": "bit"},
{"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.06", "dataType": "bit"},
{"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.07", "dataType": "bit"},
{"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.08", "dataType": "bit"},
{"name": "水泵1启动", "system": "sb1start", "address": "40051.09", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.10", "dataType": "bit"},
{"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.11", "dataType": "bit"},
{"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.12", "dataType": "bit"},
{"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.13", "dataType": "bit"},
{"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.14", "dataType": "bit"},
{"name": "排污泵启动", "system": "pwb_qd", "address": "40051.15", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40052.01", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40052.02", "dataType": "bit"},
{"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.03", "dataType": "bit"},
{"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.04", "dataType": "bit"},
{"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.05", "dataType": "bit"},
{"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.06", "dataType": "bit"},
{"name": "排污泵停止", "system": "pwb_tz", "address": "40052.07", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.10", "dataType": "bit"},
{"name": "溶氧上限报警设定值", "system": "rysjup", "address": "40053-40054","order": "ABCD", "dataType": "float32"},
{"name": "溶氧下限报警设定值", "system": "rysjdown", "address": "40055-40056","order": "ABCD", "dataType": "float32"}
]
},
{
"id": "2_3",
"systemName": "源水处理区",
"protocolType": "TCP",
"byteOrder": "CBAD",
"connectConfig": { "host": "192.168.2.5", "port": 2004,"poolSize": 10},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
{"name": "水源泵1启动", "system": "syp1", "address": "10003", "dataType": "bit"},
{"name": "水源泵2启动", "system": "syp2", "address": "10004", "dataType": "bit"},
{"name": "水源泵3启动", "system": "syp3", "address": "10005", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1", "address": "10006", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2", "address": "10007", "dataType": "bit"},
{"name": "紫外灯电源合闸", "system": "zwd", "address": "10008", "dataType": "bit"},
{"name": "生化池高液位", "system": "shg", "address": "10009", "dataType": "bit"},
{"name": "生化池低液位", "system": "shd", "address": "10010", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float"},
{"name": "当前水源泵启动台数", "system": "dqsy", "address": "40009", "dataType": "int"},
{"name": "当前风机运行台数", "system": "dqfj", "address": "40010", "dataType": "int"},
{"name": "水源泵1运行时间", "system": "syp1sj", "address": "40011-40012","order": "ABCD", "dataType": "long"},
{"name": "水源泵2运行时间", "system": "syp2sj", "address": "40013-40014","order": "ABCD", "dataType": "long"},
{"name": "水源泵3运行时间", "system": "syp3sj", "address": "40015-40016","order": "ABCD", "dataType": "long"},
{"name": "风机1运行时间", "system": "fj1t", "address": "40017-40018","order": "ABCD", "dataType": "long"},
{"name": "风机2运行时间", "system": "fj2t", "address": "40019-40020","order": "ABCD", "dataType": "long"},
{"name": "水源泵1故障", "system": "syp1g", "address": "40001.09", "dataType": "bit"},
{"name": "水源泵2故障", "system": "syp2g", "address": "40001.10", "dataType": "bit"},
{"name": "水源泵3故障", "system": "syp3g", "address": "40001.11", "dataType": "bit"},
{"name": "风机1故障", "system": "fj1g", "address": "40001.12", "dataType": "bit"},
{"name": "风机2故障", "system": "fj2g", "address": "40001.13", "dataType": "bit"},
{"name": "紫外杀菌灯跳闸故障", "system": "zwsjdtz", "address": "40001.14", "dataType": "bit"},
{"name": "水源泵1启动", "system": "syp1s", "address": "40051.01", "dataType": "bit"},
{"name": "水源泵2启动", "system": "syp2s", "address": "40051.02", "dataType": "bit"},
{"name": "水源泵3启动", "system": "syp3s", "address": "40051.03", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.04", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.05", "dataType": "bit"},
{"name": "水源泵1停止", "system": "syp1t", "address": "40051.09", "dataType": "bit"},
{"name": "水源泵2停止", "system": "syp2t", "address": "40051.10", "dataType": "bit"},
{"name": "水源泵3停止", "system": "syp3t", "address": "40051.11", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40051.12", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40051.13", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.02", "dataType": "bit"}
]
},
{
"id": "2_4",
"systemName": "育苗系统",
"protocolType": "TCP",
"byteOrder": "CBAD",
"connectConfig": { "host": "192.168.2.4", "port": 2002,"poolSize": 10},
"points": [
{"name": "手动/自动", "system": "sdz", "address": "10001", "dataType": "bit"},
{"name": "本地/远程", "system": "bdyy", "address": "10002", "dataType": "bit"},
{"name": "水泵1运行", "system": "sb1", "address": "10004", "dataType": "bit"},
{"name": "水泵2运行", "system": "sb2", "address": "10005", "dataType": "bit"},
{"name": "风机1运行", "system": "fj1", "address": "10006", "dataType": "bit"},
{"name": "风机2运行", "system": "fj2", "address": "10007", "dataType": "bit"},
{"name": "热源泵1电源合闸", "system": "ryb1", "address": "10008", "dataType": "bit"},
{"name": "热源泵2电源合闸", "system": "ryb2", "address": "10009", "dataType": "bit"},
{"name": "微滤机电源合闸", "system": "wlj", "address": "10010", "dataType": "bit"},
{"name": "紫外灯电源合闸", "system": "zwd", "address": "10011", "dataType": "bit"},
{"name": "补水池高液位", "system": "bsc", "address": "10012", "dataType": "bit"},
{"name": "微滤池高液位", "system": "wlq", "address": "10013", "dataType": "bit"},
{"name": "微滤池低液位", "system": "wld", "address": "10015", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "溶氧值", "system": "ryz", "address": "40003-40004","order": "ABCD", "dataType": "float32"},
{"name": "温度值", "system": "wdz", "address": "40005-40006","order": "ABCD", "dataType": "float32"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float32"},
{"name": "当前风机运行台数", "system": "dqfj", "address": "40009", "dataType": "int"},
{"name": "风机1运行时间", "system": "fj1t", "address": "40011-40012","order": "ABCD", "dataType": "long"},
{"name": "风机2运行时间", "system": "fj2t", "address": "40013-40014","order": "ABCD", "dataType": "long"},
{"name": "水泵1故障", "system": "sb1gz", "address": "40001.10", "dataType": "bit"},
{"name": "水泵2故障", "system": "sb2gz", "address": "40001.11", "dataType": "bit"},
{"name": "风机1故障", "system": "fj1g", "address": "40001.12", "dataType": "bit"},
{"name": "风机2故障", "system": "fj2g", "address": "40001.13", "dataType": "bit"},
{"name": "热泵1跳闸", "system": "rb1tz", "address": "40001.16", "dataType": "bit"},
{"name": "热泵2跳闸", "system": "rb2tz", "address": "40001.01", "dataType": "bit"},
{"name": "微滤机跳闸", "system": "wljtz", "address": "40001.02", "dataType": "bit"},
{"name": "紫外杀菌灯跳闸故障", "system": "zwsjdtz", "address": "40001.03", "dataType": "bit"},
{"name": "补水上液位超时", "system": "bssywcs", "address": "40001.04", "dataType": "bit"},
{"name": "微滤池上液位超时", "system": "wlcsywcs", "address": "40001.05", "dataType": "bit"},
{"name": "溶氧超限报警", "system": "rycxbj", "address": "40001.06", "dataType": "bit"},
{"name": "补水泵3故障(没有)", "system": "bsb3g", "address": "40001.07", "dataType": "bit"},
{"name": "微滤池低液位长时间不消失报警", "system": "wldc", "address": "40001.08", "dataType": "bit"},
{"name": "水泵1启动", "system": "sb1start", "address": "40051.01", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.02", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.03", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.04", "dataType": "bit"},
{"name": "补水泵3启动", "system": "bsb3s", "address": "40051.05", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40051.09", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40051.10", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40051.11", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40051.12", "dataType": "bit"},
{"name": "补水泵3停止", "system": "bsb3p", "address": "40051.13", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.02", "dataType": "bit"},
{"name": "溶氧上限报警设定值", "system": "rysjup", "address": "40053-40054","order": "ABCD", "dataType": "float32"},
{"name": "溶氧下限报警设定值", "system": "rysjdown", "address": "40055-40056","order": "ABCD", "dataType": "float32"}
]
},
{
"id": "2_5",
"systemName": "设备房系统",
"protocolType": "TCP",
"byteOrder": "CBAD",
"connectConfig": { "host": "192.168.2.3", "port": 2003,"poolSize": 10},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
{"name": "风机1运行", "system": "fj1", "address": "10003", "dataType": "bit"},
{"name": "风机2运行", "system": "fj2", "address": "10004", "dataType": "bit"},
{"name": "风机3运行", "system": "fj3", "address": "10005", "dataType": "bit"},
{"name": "风机4运行", "system": "fj4", "address": "10006", "dataType": "bit"},
{"name": "补水泵1运行", "system": "bsb1", "address": "10007", "dataType": "bit"},
{"name": "补水泵2运行", "system": "bsb2", "address": "10008", "dataType": "bit"},
{"name": "热泵1电源合闸", "system": "rb1", "address": "10010", "dataType": "bit"},
{"name": "热泵2电源合闸", "system": "rb2", "address": "10011", "dataType": "bit"},
{"name": "空压机电源合闸", "system": "kyj", "address": "10012", "dataType": "bit"},
{"name": "补水阀1开到位", "system": "bsf1", "address": "10013", "dataType": "bit"},
{"name": "补水阀2开到位", "system": "bsf2", "address": "10014", "dataType": "bit"},
{"name": "补水阀1关到位", "system": "bsf1g", "address": "10016", "dataType": "bit"},
{"name": "补水阀2关到位", "system": "bsf2g", "address": "10017", "dataType": "bit"},
{"name": "补水1高液位", "system": "bsg1", "address": "10019", "dataType": "bit"},
{"name": "补水2高液位", "system": "bsg2", "address": "10020", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float"},
{"name": "当前风机运行台数", "system": "dqfj", "address": "40009", "dataType": "int"},
{"name": "风机1运行时间", "system": "fj1t", "address": "40011-40012","order": "ABCD", "dataType": "long"},
{"name": "风机2运行时间", "system": "fj2t", "address": "40013-40014","order": "ABCD", "dataType": "long"},
{"name": "风机3运行时间", "system": "fj3t", "address": "40015-40016","order": "ABCD", "dataType": "long"},
{"name": "风机4运行时间", "system": "fj4t", "address": "40017-40018","order": "ABCD", "dataType": "long"},
{"name": "风机1故障", "system": "fj1g", "address": "40001.09", "dataType": "bit"},
{"name": "风机2故障", "system": "fj2g", "address": "40001.10", "dataType": "bit"},
{"name": "风机3故障", "system": "fj3g", "address": "40001.11", "dataType": "bit"},
{"name": "风机4故障", "system": "fj4g", "address": "40001.12", "dataType": "bit"},
{"name": "补水泵1故障", "system": "bsb1g", "address": "40001.13", "dataType": "bit"},
{"name": "补水泵2故障", "system": "bsb2g", "address": "40001.14", "dataType": "bit"},
{"name": "热泵1跳闸故障", "system": "rb1g", "address": "40001.16", "dataType": "bit"},
{"name": "热泵2跳闸故障", "system": "rb2g", "address": "40001.01", "dataType": "bit"},
{"name": "补水阀1开不到位", "system": "bsf1b", "address": "40001.02", "dataType": "bit"},
{"name": "补水阀1关不到位", "system": "bsf1bg", "address": "40001.03", "dataType": "bit"},
{"name": "补水阀2开不到位", "system": "bsf2b", "address": "40001.04", "dataType": "bit"},
{"name": "补水阀2关不到位", "system": "bsf2bg", "address": "40001.05", "dataType": "bit"},
{"name": "空压机跳闸故障", "system": "kyjg", "address": "40001.08", "dataType": "bit"},
{"name": "补水阀1开OR关", "system": "bsf1c", "address": "40051.01", "dataType": "bit"},
{"name": "补水阀2开OR关", "system": "bsf2c", "address": "40051.02", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.09", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.10", "dataType": "bit"},
{"name": "风机3启动", "system": "fj3s", "address": "40051.11", "dataType": "bit"},
{"name": "风机4启动", "system": "fj4s", "address": "40051.12", "dataType": "bit"},
{"name": "补水泵1启动", "system": "bsb1s", "address": "40051.13", "dataType": "bit"},
{"name": "补水泵2启动", "system": "bsb2s", "address": "40051.14", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40052.01", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40052.02", "dataType": "bit"},
{"name": "风机3停止", "system": "fj3p", "address": "40052.03", "dataType": "bit"},
{"name": "风机4停止", "system": "fj4p", "address": "40052.04", "dataType": "bit"},
{"name": "补水泵1停止", "system": "bsb1p", "address": "40052.05", "dataType": "bit"},
{"name": "补水泵2停止", "system": "bsb2p", "address": "40052.06", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.10", "dataType": "bit"}
]
}
]
}
... ...
{}
\ No newline at end of file
{
"plcs": [
{
"id": "2_1",
"systemName": "成鱼系统1",
"protocolType": "TCP",
"byteOrder": "CBAD",
"zeroBasedAddress": false,
"connectConfig": { "host": "127.0.0.1", "port": 2010,"poolSize": 10},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
{"name": "补水泵启动", "system": "bsbqd", "address": "10003", "dataType": "bit"},
{"name": "水泵1运行", "system": "sb1", "address": "10004", "dataType": "bit"},
{"name": "水泵2运行", "system": "sb2", "address": "10005", "dataType": "bit"},
{"name": "氧锥泵1运行", "system": "yzb1yx", "address": "10006", "dataType": "bit"},
{"name": "氧锥泵2运行", "system": "yzb2yx", "address": "10007", "dataType": "bit"},
{"name": "氧锥泵3运行", "system": "yzb3yx", "address": "10008", "dataType": "bit"},
{"name": "氧锥泵4运行", "system": "yzb4yx", "address": "10009", "dataType": "bit"},
{"name": "排污泵运行", "system": "pwb", "address": "10010", "dataType": "bit"},
{"name": "微滤机电源合闸", "system": "wlj", "address": "10011", "dataType": "bit"},
{"name": "紫外灯电源合闸", "system": "zwd", "address": "10012", "dataType": "bit"},
{"name": "微滤池高液位", "system": "wlq", "address": "10013", "dataType": "bit"},
{"name": "微滤池低液位", "system": "wld", "address": "10014", "dataType": "bit"},
{"name": "蝶阀1开到位", "system": "df1kdw", "address": "10015", "dataType": "bit"},
{"name": "蝶阀1关到位", "system": "df1gdw", "address": "10016", "dataType": "bit"},
{"name": "蝶阀2开到位", "system": "df2kdw", "address": "10017", "dataType": "bit"},
{"name": "蝶阀2关到位", "system": "df2gdw", "address": "10018", "dataType": "bit"},
{"name": "蝶阀3开到位", "system": "df3kdw", "address": "10019", "dataType": "bit"},
{"name": "蝶阀3关到位", "system": "df3gdw", "address": "10020", "dataType": "bit"},
{"name": "蝶阀4开到位", "system": "df4kdw", "address": "10021", "dataType": "bit"},
{"name": "蝶阀4关到位", "system": "df4gdw", "address": "10022", "dataType": "bit"},
{"name": "蝶阀5开到位", "system": "df5kdw", "address": "10023", "dataType": "bit"},
{"name": "蝶阀5关到位", "system": "df5gdw", "address": "10024", "dataType": "bit"},
{"name": "蝶阀6开到位", "system": "df6kdw", "address": "10025", "dataType": "bit"},
{"name": "蝶阀6关到位", "system": "df6gdw", "address": "10026", "dataType": "bit"},
{"name": "蝶阀7开到位", "system": "df7kdw", "address": "10027", "dataType": "bit"},
{"name": "蝶阀7关到位", "system": "df7gdw", "address": "10028", "dataType": "bit"},
{"name": "蝶阀8开到位", "system": "df8kdw", "address": "10029", "dataType": "bit"},
{"name": "蝶阀8关到位", "system": "df8gdw", "address": "10030", "dataType": "bit"},
{"name": "循环水泵运行", "system": "xhsbyx", "address": "10031", "dataType": "bit"},
{"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"},
{"name": "水泵1故障", "system": "sb1gz", "address": "40001.09", "dataType": "bit"},
{"name": "水泵2故障", "system": "sb2gz", "address": "40001.10", "dataType": "bit"},
{"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.11", "dataType": "bit"},
{"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.12", "dataType": "bit"},
{"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.13", "dataType": "bit"},
{"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.14", "dataType": "bit"},
{"name": "排污泵故障", "system": "pwb_gz", "address": "40001.15", "dataType": "bit"},
{"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.01", "dataType": "bit"},
{"name": "排污阀1关不到位", "system": "pwf1gbdw", "address": "40001.02", "dataType": "bit"},
{"name": "排污阀2开不到位", "system": "pwf2kbdw", "address": "40001.03", "dataType": "bit"},
{"name": "排污阀2关不到位", "system": "pwf2gbdw", "address": "40001.04", "dataType": "bit"},
{"name": "排污阀3开不到位", "system": "pwf3kbdw", "address": "40001.05", "dataType": "bit"},
{"name": "排污阀3关不到位", "system": "pwf3gbdw", "address": "40001.06", "dataType": "bit"},
{"name": "排污阀4开不到位", "system": "pwf4kbdw", "address": "40001.07", "dataType": "bit"},
{"name": "排污阀4关不到位", "system": "pwf4gbdw", "address": "40001.08", "dataType": "bit"},
{"name": "排污阀5开不到位", "system": "pwf5kbdw", "address": "40002.09", "dataType": "bit"},
{"name": "排污阀5关不到位", "system": "pwf5gbdw", "address": "40002.10", "dataType": "bit"},
{"name": "排污阀6开不到位", "system": "pwf6kbdw", "address": "40002.11", "dataType": "bit"},
{"name": "排污阀6关不到位", "system": "pwf6gbdw", "address": "40002.12", "dataType": "bit"},
{"name": "排污阀7开不到位", "system": "pwf7kbdw", "address": "40002.13", "dataType": "bit"},
{"name": "排污阀7关不到位", "system": "pwf7gbdw", "address": "40002.14", "dataType": "bit"},
{"name": "排污阀8开不到位", "system": "pwf8kbdw", "address": "40002.15", "dataType": "bit"},
{"name": "排污阀8关不到位", "system": "pwf8gbdw", "address": "40002.16", "dataType": "bit"},
{"name": "补水高液位超时", "system": "bsgywdcs", "address": "40002.03", "dataType": "bit"},
{"name": "微滤池高液位超时", "system": "wlcgywdcs", "address": "40002.04", "dataType": "bit"},
{"name": "微滤机跳闸", "system": "wljtz", "address": "40002.05", "dataType": "bit"},
{"name": "紫外杀菌灯跳闸故障", "system": "zwsjdtz", "address": "40002.06", "dataType": "bit"},
{"name": "溶氧超限报警", "system": "rycxbj", "address": "40002.07", "dataType": "bit"},
{"name": "微滤池低液位长时间不消失报警", "system": "wlcdywbcsbj", "address": "40002.08", "dataType": "bit"},
{"name": "溶氧值", "system": "ryz", "address": "40003-40004","order": "ABCD", "dataType": "float32"},
{"name": "温度值", "system": "wdz", "address": "40005-40006","order": "ABCD", "dataType": "float32"},
{"name": "电能值", "system": "dnz", "address": "40007-40008","order": "ABCD", "dataType": "float32"},
{"name": "当前氧锥泵运行台数", "system": "dqyzb", "address": "40009", "dataType": "int"},
{"name": "氧锥泵1运行时间", "system": "yzb1_sj", "address": "40011-40012","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵2运行时间", "system": "yzb2_sj", "address": "40013-40014","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵3运行时间", "system": "yzb3_sj", "address": "40015-40016","order": "ABCD", "dataType": "int32"},
{"name": "氧锥泵4运行时间", "system": "yzb4_sj", "address": "40017-40018","order": "ABCD", "dataType": "int32"},
{"name": "生化池水温", "system": "shcsw", "address": "40019-40020","order": "ABCD", "dataType": "float32"},
{"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.09", "dataType": "bit"},
{"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.10", "dataType": "bit"},
{"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.11", "dataType": "bit"}
]
}
]
}
... ...