作者 钟来

plc终端控制功能完成

... ... @@ -42,6 +42,12 @@ public class PutTopic extends TopicFactoryAdapter {
ModbusMasterMessage.closeMaster(id); // 销毁旧连接
}
logger.error("写plc异常",e);
try {
mqttService.publish("PUT_REQ/"+topicDto.getTime(),JSONObject.toJSONString(new Message(0,"写plc异常",jsonObject)));
} catch (MqttException ex) {
logger.error("饭hi结果异常",e);
}
return;
}
}
try {
... ...
... ... @@ -14,6 +14,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
... ... @@ -70,8 +71,20 @@ public class Modbus4jRead {
try {
results = ModbusMasterMessage.getMaster( id).send(batch);
} catch (Exception e) {
logger.error("批量读取点位异常", e);
return null;
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 对象
... ...
package com.zhonglai.luhui.device.modbus.terminal.modbus;
import com.alibaba.fastjson.JSONObject;
import com.serotonin.modbus4j.msg.*;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcDataType;
import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
... ... @@ -12,16 +15,11 @@ import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.io.IOException;
import java.util.*;
/**
* modbus4j写入数据
... ... @@ -30,108 +28,126 @@ import java.util.List;
*
*/
public class Modbus4jWrite {
private static Log log = LogFactory.getLog(Modbus4jWrite.class);
private static final Logger log = LoggerFactory.getLogger(Modbus4jWrite.class);
private String id;
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;
/**
* 单点写
*/
public void writePoint(PlcPoint point, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException {
BaseLocator<?> locator = ModbusAddressParser.parseLocator(
point.getAddress(),
point.getDataType(),
point.getSlaveId(),
zeroBasedAddress,
point.getOrder()
);
Object value = point.getValue();
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
);
// 如果 value 是 String,则根据 dataType 转换
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:
value = Boolean.parseBoolean(strVal);
break;
case INT16:
value = Short.parseShort(strVal);
break;
case UINT16:
value = Integer.parseInt(strVal); // Modbus4j 内部支持 unsigned
break;
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":
value = ModbusAddressParser.int32ToABCD(raw);
break;
case "BADC":
value = ModbusAddressParser.int32ToBADC(raw);
break;
case "CDAB":
value = ModbusAddressParser.int32ToCDAB(raw);
break;
case "DCBA":
value = ModbusAddressParser.int32ToDCBA(raw);
break;
default:
value = raw;
break;
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;
}
break;
case INT64:
value = Long.parseLong(strVal);
break;
case INT64: return Long.parseLong(strVal);
case FLOAT32:
float fvalue = Float.parseFloat(strVal);
switch (point.getOrder()) {
case "ABCD":
value = ModbusAddressParser.floatToIntABCD(fvalue);
break;
case "BADC":
value = ModbusAddressParser.floatToIntBADC(fvalue);
break;
case "CDAB":
value = ModbusAddressParser.floatToIntCDAB(fvalue);
break;
case "DCBA":
value = ModbusAddressParser.floatToIntDCBA(fvalue);
break;
default:
value = fvalue;
break;
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;
}
break;
case DOUBLE64:
value = Double.parseDouble(strVal);
break;
default:
throw new IllegalArgumentException("不支持的数据类型: " + point.getDataType());
case DOUBLE64: return Double.parseDouble(strVal);
default: throw new IllegalArgumentException("不支持的数据类型: " + point.getDataType());
}
}
// 设置值
try {
ModbusMasterMessage.getMaster(id).setValue(locator, value);
} catch (Exception e) {
log.error("写入失败",e);
return;
}
log.info("写入成功: " + point.getName() + " = " + point.getValue());
return value;
}
/**
* 批量写(兼容 3.1.0,没有 BatchWrite)
*/
public void batchWrite(List<PlcPoint> points, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException {
for (PlcPoint plcPoint:points) {
writePoint(plcPoint, zeroBasedAddress);
}
}
}
\ No newline at end of file
... ...
... ... @@ -112,6 +112,32 @@ public class ModbusAddressParser {
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()) {
... ... @@ -299,4 +325,62 @@ public class ModbusAddressParser {
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.alibaba.fastjson.JSONObject;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.alibaba.fastjson.JSONObject;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.*;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
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;
public class ModbusMasterMessage {
/**
* 工厂
*/
static ModbusFactory modbusFactory;
static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}
// 存储每个系统的 ModbusMaster
private static final Logger log = LoggerFactory.getLogger(ModbusMasterMessage.class);
private static final ConcurrentHashMap<String, ModbusMaster> masterCache = new ConcurrentHashMap<>();
// 每个系统 ID 对应的锁对象
private static final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
private static final ModbusFactory factory = new ModbusFactory();
/**
* 创建或获取 ModbusMaster (线程安全)
*/
/** 获取或创建 ModbusMaster,不做连接性检测 */
public static ModbusMaster getMaster(String id) throws Exception {
ModbusMaster master = masterCache.get(id);
if (master != null && master.isConnected()) {
if (master != null) {
return master;
}
// 获取该 ID 的专用锁
// double-check locking 保证只创建一次
Object lock = lockMap.computeIfAbsent(id, k -> new Object());
synchronized (lock) {
// 双重检查,避免重复创建
master = masterCache.get(id);
if (master != null && master.isConnected()) {
return master;
}
// 如果旧的存在但断开了,销毁
if (master != null) {
master.destroy();
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);
}
// 创建新 master
ModbusMaster newMaster = selectMaster(id);
newMaster.setIoLog(new MyIOLog(new File("/app/logs/modbus-" + id + ".log")));
newMaster.init();
masterCache.put(id, newMaster);
return newMaster;
}
return master;
}
private static ModbusMaster selectMaster(String id) throws Exception {
CachPlcConfig cachPlcConfig = InitPlcConfig.getPlcSystems(id);
if (null == cachPlcConfig)
{
throw new Exception("系统: " + id+" 未配置");
}
JSONObject connectConfig = cachPlcConfig.getConnectConfig();
switch (cachPlcConfig.getProtocolType())
{
case RTU:
return modbusFactory.createRtuMaster(JSONObject.parseObject(connectConfig.toJSONString(),JSerialCommWrapper.class));
/**
* 创建 Master
*/
private static ModbusMaster createMaster(CachPlcConfig config) throws Exception {
System.out.println("config:"+JSONObject.toJSONString(config));
JSONObject connectConfig = config.getConnectConfig();
switch (config.getProtocolType()) {
case TCP:
return modbusFactory.createTcpMaster(JSONObject.parseObject(connectConfig.toJSONString(),IpParameters.class), true); //这里传的是 false → 表示短连接模式,每次用完连接就会关闭。
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 modbusFactory.createAsciiMaster(JSONObject.parseObject(connectConfig.toJSONString(),JSerialCommWrapper.class));
return factory.createAsciiMaster(
JSONObject.parseObject(connectConfig.toJSONString(), JSerialCommWrapper.class));
case UDP:
return modbusFactory.createUdpMaster(JSONObject.parseObject(connectConfig.toJSONString(),IpParameters.class));
return factory.createUdpMaster(
JSONObject.parseObject(connectConfig.toJSONString(), IpParameters.class));
default:
throw new Exception("系统: " + id+" 不支持的协议");
throw new Exception("不支持协议");
}
}
/**
* 关闭指定系统的 Master
* 关闭 Master
*/
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();
}
if (master != null) master.destroy();
lockMap.remove(id);
}
}
... ... @@ -114,3 +94,4 @@ public class ModbusMasterMessage {
lockMap.clear();
}
}
... ...
{
"cameras": [
{
"id": "G28890297",
"connectConfig": {"pass": "WQMJWP"}
"id": "FW8199460",
"connectConfig": {"pass": "Luhui586"}
}
],
"plcs": [
{
"id": "2_6",
"systemName": "测试",
"protocolType": "TCP",
"connectConfig": { "host": "192.168.1.82", "port": 2000},
"points": [
{"name": "水泵1故障", "system": "sb1gz", "address": "40001.01", "dataType": "bit","access":"r"},
{"name": "水泵2故障", "system": "sb2gz", "address": "40001.02", "dataType": "bit","access":"r"},
{"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.03", "dataType": "bit","access":"r"},
{"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.04", "dataType": "bit","access":"r"},
{"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.05", "dataType": "bit","access":"r"},
{"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.06", "dataType": "bit","access":"r"},
{"name": "排污泵故障", "system": "pwb_gz", "address": "40001.07", "dataType": "bit","access":"r"},
{"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.09", "dataType": "bit","access":"r"},
{"name": "溶氧上限报警设定值", "system": "ry_sxsz", "address": "40053-40054", "dataType": "float32","order": "ABCD","access":"rw"},
{"name": "溶氧下限报警设定值", "system": "ry_xxsz", "address": "40055-40056", "dataType": "float32","order": "ABCD","access":"rw"}
]
},
{
"id": "2_1",
"systemName": "成鱼系统1",
"protocolType": "TCP",
"connectConfig": { "host": "192.168.2.11", "port": 2000},
"connectConfig": { "host": "192.168.2.11", "port": 2010},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
... ... @@ -94,7 +76,7 @@
{"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": "int16"},
{"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"},
... ... @@ -103,30 +85,33 @@
{"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.01", "dataType": "bit"},
{"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.02", "dataType": "bit"},
{"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.03", "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": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.09", "dataType": "bit"},
{"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.10", "dataType": "bit"},
{"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.11", "dataType": "bit"},
{"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.12", "dataType": "bit"},
{"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.13", "dataType": "bit"},
{"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.14", "dataType": "bit"},
{"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.15", "dataType": "bit"},
{"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.16", "dataType": "bit"},
{"name": "水泵1启动", "system": "sb1start", "address": "40051.01", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.02", "dataType": "bit"},
{"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.03", "dataType": "bit"},
{"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.04", "dataType": "bit"},
{"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.05", "dataType": "bit"},
{"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.06", "dataType": "bit"},
{"name": "排污泵启动", "system": "pwb_qd", "address": "40051.07", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40052.09", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40052.10", "dataType": "bit"},
{"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.11", "dataType": "bit"},
{"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.12", "dataType": "bit"},
{"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.13", "dataType": "bit"},
{"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.14", "dataType": "bit"},
{"name": "排污泵停止", "system": "pwb_tz", "address": "40052.15", "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"}
]
... ... @@ -201,7 +186,7 @@
{"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": "int16"},
{"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"},
... ... @@ -210,30 +195,33 @@
{"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.01", "dataType": "bit"},
{"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.02", "dataType": "bit"},
{"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.03", "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": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.09", "dataType": "bit"},
{"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.10", "dataType": "bit"},
{"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.11", "dataType": "bit"},
{"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.12", "dataType": "bit"},
{"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.13", "dataType": "bit"},
{"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.14", "dataType": "bit"},
{"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.15", "dataType": "bit"},
{"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.16", "dataType": "bit"},
{"name": "水泵1启动", "system": "sb1start", "address": "40051.01", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.02", "dataType": "bit"},
{"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.03", "dataType": "bit"},
{"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.04", "dataType": "bit"},
{"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.05", "dataType": "bit"},
{"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.06", "dataType": "bit"},
{"name": "排污泵启动", "system": "pwb_qd", "address": "40051.07", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40052.09", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40052.10", "dataType": "bit"},
{"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.11", "dataType": "bit"},
{"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.12", "dataType": "bit"},
{"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.13", "dataType": "bit"},
{"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.14", "dataType": "bit"},
{"name": "排污泵停止", "system": "pwb_tz", "address": "40052.15", "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"}
]
... ... @@ -272,18 +260,18 @@
{"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": "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"}
{"name": "水源泵1启动", "system": "syp1s", "address": "40051.09", "dataType": "bit"},
{"name": "水源泵2启动", "system": "syp2s", "address": "40051.10", "dataType": "bit"},
{"name": "水源泵3启动", "system": "syp3s", "address": "40051.11", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.12", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.13", "dataType": "bit"},
{"name": "水源泵1停止", "system": "syp1t", "address": "40051.01", "dataType": "bit"},
{"name": "水源泵2停止", "system": "syp2t", "address": "40051.02", "dataType": "bit"},
{"name": "水源泵3停止", "system": "syp3t", "address": "40051.03", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40051.04", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40051.05", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.10", "dataType": "bit"}
]
},
{
... ... @@ -325,23 +313,23 @@
{"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": "int32"},
{"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": "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": "水泵1启动", "system": "sb1start", "address": "40051.09", "dataType": "bit"},
{"name": "水泵2启动", "system": "sb2start", "address": "40051.10", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.11", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.12", "dataType": "bit"},
{"name": "补水泵3启动", "system": "bsb3s", "address": "40051.13", "dataType": "bit"},
{"name": "水泵1停止", "system": "sb1stop", "address": "40051.01", "dataType": "bit"},
{"name": "水泵2停止", "system": "sb2stop", "address": "40051.02", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40051.03", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40051.04", "dataType": "bit"},
{"name": "补水泵3停止", "system": "bsb3p", "address": "40051.05", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.02", "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"}
... ... @@ -351,7 +339,7 @@
"id": "2_5",
"systemName": "设备房系统",
"protocolType": "TCP",
"connectConfig": { "host": "192.168.2.3", "port": 2003},
"connectConfig": { "host": "127.0.0.1", "port": 2003},
"points": [
{"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"},
{"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"},
... ... @@ -393,22 +381,22 @@
{"name": "风机3运行时间", "system": "fj3t", "address": "40015-40016","order": "ABCD", "dataType": "long"},
{"name": "风机4运行时间", "system": "fj4t", "address": "40017-40018","order": "ABCD", "dataType": "long"},
{"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"}
{"name": "补水阀1开OR关", "system": "bsf1c", "address": "40051.09", "dataType": "bit"},
{"name": "补水阀2开OR关", "system": "bsf2c", "address": "40051.10", "dataType": "bit"},
{"name": "风机1启动", "system": "fj1s", "address": "40051.01", "dataType": "bit"},
{"name": "风机2启动", "system": "fj2s", "address": "40051.02", "dataType": "bit"},
{"name": "风机3启动", "system": "fj3s", "address": "40051.03", "dataType": "bit"},
{"name": "风机4启动", "system": "fj4s", "address": "40051.04", "dataType": "bit"},
{"name": "补水泵1启动", "system": "bsb1s", "address": "40051.05", "dataType": "bit"},
{"name": "补水泵2启动", "system": "bsb2s", "address": "40051.06", "dataType": "bit"},
{"name": "风机1停止", "system": "fj1p", "address": "40052.09", "dataType": "bit"},
{"name": "风机2停止", "system": "fj2p", "address": "40052.10", "dataType": "bit"},
{"name": "风机3停止", "system": "fj3p", "address": "40052.11", "dataType": "bit"},
{"name": "风机4停止", "system": "fj4p", "address": "40052.12", "dataType": "bit"},
{"name": "补水泵1停止", "system": "bsb1p", "address": "40052.13", "dataType": "bit"},
{"name": "补水泵2停止", "system": "bsb2p", "address": "40052.14", "dataType": "bit"},
{"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"},
{"name": "累计时间清零", "system": "ljtq", "address": "40052.02", "dataType": "bit"}
]
}
]
... ...
import com.alibaba.fastjson.JSONObject;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig;
import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jRead;
import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite;
... ... @@ -11,7 +16,7 @@ import java.util.Map;
public class TestModbus {
public static void main(String[] args) throws Exception {
args = new String[]{"E:\\work\\idea\\Luhui\\lh-modules\\lh-device-modbus-terminal\\src\\main\\resources\\configs\\plcs.json","false","测试", "ry_sxsz=3.16","ry_xxsz=2.1"};
args = new String[]{"E:\\work\\idea\\Luhui\\lh-modules\\lh-device-modbus-terminal\\src\\main\\resources\\configs\\plcs.json","false","2_5", "fj1","fj4p=false"};
if (args.length < 2) {
System.out.println("用法: java -jar modbus-app.jar <plcs.json路径> <点位名1> <点位名2> ...");
return;
... ... @@ -42,27 +47,38 @@ public class TestModbus {
Map<String, Object> map = new Modbus4jRead(id).batchRead(plcPoints,zeroBasedAddress);
System.out.println(JSONObject.toJSONString(map));
}
private static void testWrite(String[] args) throws Exception
{
private static void testWrite(String[] args) throws Exception {
String jsonPath = args[0];
boolean zeroBasedAddress = new Boolean(args[1]);
String id = args[2];
boolean zeroBasedAddress = Boolean.parseBoolean(args[1]);
String id = args[2];
List<String> pointNames = Arrays.asList(Arrays.copyOfRange(args, 3, args.length));
// 初始化 PLC 配置
InitPlcConfig.initPlcConfigFromFile(jsonPath);
// 构建写入点列表
List<PlcPoint> plcPoints = new ArrayList<>();
for (String pointName :pointNames)
{
for (String pointName : pointNames) {
String[] pointNameArr = pointName.split("=");
PlcPoint plcPoint = InitPlcConfig.getPlcSystem(id, pointNameArr[0]);
if ( null != plcPoint)
{
if (plcPoint != null) {
plcPoint.setValue(pointNameArr[1]);
plcPoints.add(plcPoint);
}
}
new Modbus4jWrite(id).batchWrite(plcPoints,zeroBasedAddress);
// 写入
Modbus4jWrite writer = new Modbus4jWrite(id);
writer.batchWrite(plcPoints, zeroBasedAddress);
System.out.println("写入完成");
// 写入完成后稍微等待
Thread.sleep(100);
// 读取刚才写入的点并打印
Modbus4jRead reader = new Modbus4jRead(id); // 假设有类似的读类
Map<String, Object> objectMap = reader.batchRead(plcPoints, zeroBasedAddress);
System.out.println("读取结果:"+JSONObject.toJSONString(objectMap));
}
}
... ...