集成modbus连接本地设备读取数据代码-配置文件方式读取;
PCS开关机功能通过modbus连接设备发送控制命令;
This commit is contained in:
@ -194,6 +194,8 @@ mqtt:
|
|||||||
connection-timeout: 15
|
connection-timeout: 15
|
||||||
keep-alive-interval: 30
|
keep-alive-interval: 30
|
||||||
automatic-reconnect: true
|
automatic-reconnect: true
|
||||||
|
topic: 021_DDS_01_UP
|
||||||
|
siteId: 021_DDS_01
|
||||||
|
|
||||||
modbus:
|
modbus:
|
||||||
pool:
|
pool:
|
||||||
|
|||||||
@ -194,6 +194,8 @@ mqtt:
|
|||||||
connection-timeout: 15
|
connection-timeout: 15
|
||||||
keep-alive-interval: 30
|
keep-alive-interval: 30
|
||||||
automatic-reconnect: true
|
automatic-reconnect: true
|
||||||
|
topic:
|
||||||
|
siteId:
|
||||||
|
|
||||||
modbus:
|
modbus:
|
||||||
pool:
|
pool:
|
||||||
|
|||||||
@ -196,6 +196,8 @@ mqtt:
|
|||||||
connection-timeout: 15
|
connection-timeout: 15
|
||||||
keep-alive-interval: 30
|
keep-alive-interval: 30
|
||||||
automatic-reconnect: true
|
automatic-reconnect: true
|
||||||
|
topic:
|
||||||
|
siteId:
|
||||||
|
|
||||||
modbus:
|
modbus:
|
||||||
pool:
|
pool:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- 日志存放路径 -->
|
<!-- 日志存放路径 -->
|
||||||
<property name="log.path" value="../logs" />
|
<property name="log.path" value="/etc/xzzn/logs" />
|
||||||
<!-- 日志输出格式 -->
|
<!-- 日志输出格式 -->
|
||||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||||
|
|
||||||
|
|||||||
@ -119,6 +119,12 @@
|
|||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- modbus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.infiniteautomation</groupId>
|
||||||
|
<artifactId>modbus4j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package com.xzzn.common.core.modbus;
|
||||||
|
|
||||||
|
import com.serotonin.modbus4j.ModbusMaster;
|
||||||
|
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Modbus4jConnectionManager {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Modbus4jConnectionManager.class);
|
||||||
|
|
||||||
|
private final Map<String, GenericObjectPool<ModbusMaster>> connectionPools = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
|
||||||
|
String poolKey = getPoolKey(config);
|
||||||
|
GenericObjectPool<ModbusMaster> pool = connectionPools.computeIfAbsent(poolKey, key -> {
|
||||||
|
PooledModbusMasterFactory factory = new PooledModbusMasterFactory(config.getHost(), config.getPort());
|
||||||
|
GenericObjectPoolConfig<ModbusMaster> poolConfig = new GenericObjectPoolConfig<>();
|
||||||
|
poolConfig.setMaxTotal(5); // 池中最大连接数
|
||||||
|
poolConfig.setMinIdle(1); // 最小空闲连接数
|
||||||
|
poolConfig.setTestOnBorrow(true); // 借用时测试连接有效性
|
||||||
|
|
||||||
|
return new GenericObjectPool<>(factory, poolConfig);
|
||||||
|
});
|
||||||
|
return pool.borrowObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void returnMaster(DeviceConfig config, ModbusMaster master) {
|
||||||
|
if (master == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String poolKey = getPoolKey(config);
|
||||||
|
GenericObjectPool<ModbusMaster> pool = connectionPools.get(poolKey);
|
||||||
|
if (pool != null) {
|
||||||
|
pool.returnObject(master);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPoolKey(DeviceConfig config) {
|
||||||
|
return config.getHost() + ":" + config.getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
connectionPools.values().forEach(GenericObjectPool::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,384 @@
|
|||||||
|
package com.xzzn.common.core.modbus;
|
||||||
|
|
||||||
|
import com.serotonin.modbus4j.BatchRead;
|
||||||
|
import com.serotonin.modbus4j.BatchResults;
|
||||||
|
import com.serotonin.modbus4j.ModbusMaster;
|
||||||
|
import com.serotonin.modbus4j.code.DataType;
|
||||||
|
import com.serotonin.modbus4j.exception.ErrorResponseException;
|
||||||
|
import com.serotonin.modbus4j.exception.ModbusTransportException;
|
||||||
|
import com.serotonin.modbus4j.locator.BaseLocator;
|
||||||
|
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 com.serotonin.modbus4j.msg.WriteRegistersResponse;
|
||||||
|
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||||
|
import com.xzzn.common.core.modbus.domain.TagConfig;
|
||||||
|
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
||||||
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
|
import com.xzzn.common.enums.ModBusType;
|
||||||
|
import com.xzzn.common.enums.RegisterType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import static com.xzzn.common.enums.RegisterType.COIL;
|
||||||
|
import static com.xzzn.common.enums.RegisterType.DISCRETE_INPUT;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ModbusProcessor {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ModbusProcessor.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
@Autowired
|
||||||
|
private Modbus4jConnectionManager connectionManager;
|
||||||
|
|
||||||
|
public boolean writeDataToDevice(DeviceConfig config) {
|
||||||
|
ModbusMaster master = null;
|
||||||
|
boolean result = true;
|
||||||
|
try {
|
||||||
|
master = connectionManager.borrowMaster(config);
|
||||||
|
// 设置了Modbus通信的超时时间为3000毫秒(3秒)。当主设备与从设备通信时,若在3秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
|
||||||
|
master.setTimeout(5000);
|
||||||
|
writeTagValue(master, config, config.getWriteTags());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// 关键:无论成功与否,都必须将连接归还到池中
|
||||||
|
if (master != null) {
|
||||||
|
connectionManager.returnMaster(config, master);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTagValue(ModbusMaster master, DeviceConfig config, List<WriteTagConfig> tags) {
|
||||||
|
tags.forEach(tag -> {
|
||||||
|
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||||
|
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||||
|
int address = 0;
|
||||||
|
int addressLength = tag.getAddress().length();
|
||||||
|
int exp = (int) Math.pow(10, addressLength-1);
|
||||||
|
if (firstDigit != 0){
|
||||||
|
int digit = Integer.parseInt(tag.getAddress());
|
||||||
|
address = digit % (exp);
|
||||||
|
}else {
|
||||||
|
address = Integer.parseInt(tag.getAddress());
|
||||||
|
}
|
||||||
|
RegisterType registerType = type.get(firstDigit);
|
||||||
|
logger.info("Register type: {}, address: {}, firstDigit: {}", registerType, tag.getAddress(), firstDigit);
|
||||||
|
switch (registerType) {
|
||||||
|
case COIL: {
|
||||||
|
writeCoilRequest(master, config.getSlaveId(), address, Boolean.parseBoolean(String.valueOf(tag.getValue())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HOLDING_REGISTER: {
|
||||||
|
writeRegisterRequest(master, config.getSlaveId(), address, Integer.parseInt(String.valueOf(tag.getValue())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logger.error("Unsupported register type: {}", registerType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeCoilRequest(ModbusMaster master, int slaveId, int address, boolean value) {
|
||||||
|
try {
|
||||||
|
WriteCoilRequest request = new WriteCoilRequest(slaveId, address, value);
|
||||||
|
WriteCoilResponse response = (WriteCoilResponse)master.send(request);
|
||||||
|
if (response.isException()) {
|
||||||
|
logger.info("Write coil failed: " + response.getExceptionMessage());
|
||||||
|
} else {
|
||||||
|
logger.info("Write coil successful");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to write coil value '{}' to address '{}'", value, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeCoilsRequest(ModbusMaster master, int slaveId, int address, boolean[] values) {
|
||||||
|
try {
|
||||||
|
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, address, values);
|
||||||
|
WriteCoilsResponse response = (WriteCoilsResponse)master.send(request);
|
||||||
|
if (response.isException()) {
|
||||||
|
logger.info("Write coils failed: " + response.getExceptionMessage());
|
||||||
|
} else {
|
||||||
|
logger.info("Write coils successful");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to write coils value '{}' to address '{}'", values, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeRegisterRequest(ModbusMaster master, int slaveId, int address, int value) {
|
||||||
|
try {
|
||||||
|
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, address, value);
|
||||||
|
WriteRegisterResponse response = (WriteRegisterResponse)master.send(request);
|
||||||
|
if (response.isException()) {
|
||||||
|
logger.info("Write register failed: " + response.getExceptionMessage());
|
||||||
|
} else {
|
||||||
|
logger.info("Write register successful");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to write register value '{}' to address '{}'", value, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeRegistersRequest(ModbusMaster master, int slaveId, int address, short[] values) {
|
||||||
|
try {
|
||||||
|
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, address, values);
|
||||||
|
WriteRegistersResponse response = (WriteRegistersResponse)master.send(request);
|
||||||
|
if (response.isException()) {
|
||||||
|
logger.info("Write registers failed: " + response.getExceptionMessage());
|
||||||
|
} else {
|
||||||
|
logger.info("Write registers successful");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to write registers value '{}' to address '{}'", values, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCoilValue(ModbusMaster master, int slaveId, int address, int value) {
|
||||||
|
// 写入单个线圈(从站ID, 地址, 布尔值)
|
||||||
|
BaseLocator<Boolean> coilLocator = BaseLocator.coilStatus(slaveId, address);
|
||||||
|
try {
|
||||||
|
// 写入布尔值到线圈
|
||||||
|
master.setValue(coilLocator, value);
|
||||||
|
logger.info("set coil successful");
|
||||||
|
} catch (ModbusTransportException | ErrorResponseException e) {
|
||||||
|
logger.error("Failed to set coil value '{}' to address '{}'", value, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRegisterValue(ModbusMaster master, int slaveId, int address, int value) {
|
||||||
|
// 写入单个保持寄存器(从站, 地址, 16位整数)
|
||||||
|
BaseLocator<Number> holdingLocator = BaseLocator.holdingRegister(slaveId, address, DataType.TWO_BYTE_INT_UNSIGNED);
|
||||||
|
try {
|
||||||
|
// 写入整数值到保持寄存器
|
||||||
|
master.setValue(holdingLocator, value);
|
||||||
|
logger.info("set register successful");
|
||||||
|
} catch (ModbusTransportException | ErrorResponseException e) {
|
||||||
|
logger.error("Failed to set register value '{}' to address '{}'", value, address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Map<String, Object> readDataFromDevice(DeviceConfig config, ModbusMaster master) {
|
||||||
|
Map<String, Object> deviceData = new HashMap<>();
|
||||||
|
// ModbusMaster master = null; // 将master的声明提前
|
||||||
|
try {
|
||||||
|
master = connectionManager.borrowMaster(config);
|
||||||
|
// 设置了Modbus通信的超时时间为3000毫秒(3秒)。当主设备与从设备通信时,若在3秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
|
||||||
|
master.setTimeout(5000);
|
||||||
|
BatchResults<String> results = readTagValues(master, config.getSlaveId(), config.getTags());
|
||||||
|
for (TagConfig tag : config.getTags()) {
|
||||||
|
if (Objects.equals(tag.getDataType(), "FOUR_BYTE_FLOAT_DBCA")){
|
||||||
|
Object value = results.getValue(tag.getKey());
|
||||||
|
value = convertValueToFloat(value);
|
||||||
|
deviceData.put(tag.getKey(), value);
|
||||||
|
}else {
|
||||||
|
deviceData.put(tag.getKey(), results.getValue(tag.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// Object value = readTagValue(master, config.getSlaveId(), tag);
|
||||||
|
// if (value != null) {
|
||||||
|
// deviceData.put(tag.getKey(), value);
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// logger.error("Failed to read tag '{}' from devices '{}'", tag.getKey(), config.getDeviceName(), e);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to borrow connection or read from devices '{}'", config.getDeviceName(), e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// 关键:无论成功与否,都必须将连接归还到池中
|
||||||
|
if (master != null) {
|
||||||
|
connectionManager.returnMaster(config, master);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String deviceNumber = config.getDeviceNumber();
|
||||||
|
redisCache.setCacheObject(deviceNumber, deviceData);
|
||||||
|
return deviceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readTagValue(ModbusMaster master, int slaveId, TagConfig tag) throws Exception {
|
||||||
|
Object value;
|
||||||
|
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||||
|
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
|
||||||
|
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||||
|
int address = 0;
|
||||||
|
int addressLength = tag.getAddress().length();
|
||||||
|
int exp = (int) Math.pow(10, addressLength-1);
|
||||||
|
if (firstDigit != 0){
|
||||||
|
int digit = Integer.parseInt(tag.getAddress());
|
||||||
|
address = digit % (exp);
|
||||||
|
}else {
|
||||||
|
address = Integer.parseInt(tag.getAddress());
|
||||||
|
}
|
||||||
|
RegisterType registerType = type.get(firstDigit);
|
||||||
|
int dataLength = DATA_LENGTH.get(tag.getDataType());
|
||||||
|
// 增加配置校验和警告
|
||||||
|
if ((Objects.equals(registerType, COIL) || Objects.equals(registerType, DISCRETE_INPUT))) {
|
||||||
|
logger.warn("Configuration warning for tag '{}': ModBusType is {} but DataType is {}. DataType should be BOOLEAN. Proceeding with BOOLEAN read.",
|
||||||
|
tag.getKey(), registerType, tag.getDataType());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (registerType) {
|
||||||
|
case COIL: {
|
||||||
|
|
||||||
|
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
|
||||||
|
value = master.getValue(loc);
|
||||||
|
logger.info("读取线圈{}成功: {}",tag.getAddress(), value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DISCRETE_INPUT: {
|
||||||
|
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
|
||||||
|
value = master.getValue(loc);
|
||||||
|
logger.info("读取输入{}成功: {}",tag.getAddress(), value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HOLDING_REGISTER: {
|
||||||
|
if (dataLength == 28){
|
||||||
|
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
|
||||||
|
value = master.getValue(locator);
|
||||||
|
//将 value 转换为二进制数据
|
||||||
|
value = convertValueToFloat(value);
|
||||||
|
}else{
|
||||||
|
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, dataLength);
|
||||||
|
value = master.getValue(locator);
|
||||||
|
}
|
||||||
|
logger.info("读取保持寄存器{}成功: {}",tag.getAddress(), value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INPUT_REGISTER: {
|
||||||
|
BaseLocator<Number> locator = BaseLocator.inputRegister(slaveId, address, dataLength);
|
||||||
|
value= master.getValue(locator);
|
||||||
|
logger.info("读取输入寄存器{}成功: {}",tag.getAddress(), value);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logger.error("Unsupported register type: {}", registerType);
|
||||||
|
value = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("Failed to read tag '{}'", tag.getKey(), e);
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private BatchResults<String> readTagValues(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
|
||||||
|
try {
|
||||||
|
BatchRead<String> batch = new BatchRead<>();
|
||||||
|
tags.forEach(tag -> {
|
||||||
|
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||||
|
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
|
||||||
|
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||||
|
int address = 0;
|
||||||
|
int addressLength = tag.getAddress().length();
|
||||||
|
int exp = (int) Math.pow(10, addressLength-1);
|
||||||
|
if (firstDigit != 0){
|
||||||
|
int digit = Integer.parseInt(tag.getAddress());
|
||||||
|
address = digit % (exp);
|
||||||
|
}else {
|
||||||
|
address = Integer.parseInt(tag.getAddress());
|
||||||
|
}
|
||||||
|
RegisterType registerType = type.get(firstDigit);
|
||||||
|
int dataLength = DATA_LENGTH.get(tag.getDataType());
|
||||||
|
switch (registerType) {
|
||||||
|
case COIL: {
|
||||||
|
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
|
||||||
|
batch.addLocator(tag.getKey(), loc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DISCRETE_INPUT: {
|
||||||
|
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
|
||||||
|
batch.addLocator(tag.getKey(), loc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HOLDING_REGISTER: {
|
||||||
|
logger.info("HOLDING_REGISTER: {}",tag.getAddress());
|
||||||
|
if (dataLength == 28){
|
||||||
|
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
|
||||||
|
batch.addLocator(tag.getKey(), locator);
|
||||||
|
}else {
|
||||||
|
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, address, dataLength);
|
||||||
|
batch.addLocator(tag.getKey(), loc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INPUT_REGISTER: {
|
||||||
|
logger.info("INPUT_REGISTER: {}",tag.getAddress());
|
||||||
|
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, address, dataLength);
|
||||||
|
batch.addLocator(tag.getKey(), loc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BatchResults<String> results = master.send(batch);
|
||||||
|
for (TagConfig tag : tags){
|
||||||
|
if (tag.getBit()!=null){
|
||||||
|
logger.info("批处理读取寄存器成功: {}",tag.getAddress() +"(" + tag.getBit() + "):" + results.getValue(tag.getKey()));
|
||||||
|
|
||||||
|
}else {
|
||||||
|
logger.info("批处理读取寄存器成功: {}",tag.getAddress() + ":" + results.getValue(tag.getKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.error("Failed to read master '{}'", slaveId, e);
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float convertValueToFloat(Object value) {
|
||||||
|
if (!(value instanceof Number)) {
|
||||||
|
throw new IllegalArgumentException("Input must be a Number");
|
||||||
|
}
|
||||||
|
|
||||||
|
int intValue = ((Number) value).intValue();
|
||||||
|
String binaryData = Integer.toBinaryString(intValue);
|
||||||
|
|
||||||
|
// 补足32位二进制字符串
|
||||||
|
if (binaryData.length() < 32) {
|
||||||
|
binaryData = String.format("%32s", binaryData).replace(' ', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割为4个字节并重组为 dcba 格式
|
||||||
|
byte[] reorderedBytes = new byte[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int start = i * 8;
|
||||||
|
String byteStr = binaryData.substring(start, start + 8);
|
||||||
|
reorderedBytes[3 - i] = (byte) Integer.parseInt(byteStr, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 ByteBuffer 提高可读性和安全性
|
||||||
|
return Float.intBitsToFloat(
|
||||||
|
java.nio.ByteBuffer.wrap(reorderedBytes).getInt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.xzzn.common.core.modbus;
|
||||||
|
|
||||||
|
|
||||||
|
import com.serotonin.modbus4j.ModbusFactory;
|
||||||
|
import com.serotonin.modbus4j.ModbusMaster;
|
||||||
|
import com.serotonin.modbus4j.ip.IpParameters;
|
||||||
|
|
||||||
|
import org.apache.commons.pool2.BasePooledObjectFactory;
|
||||||
|
import org.apache.commons.pool2.PooledObject;
|
||||||
|
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||||
|
|
||||||
|
public class PooledModbusMasterFactory extends BasePooledObjectFactory<ModbusMaster> {
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
public PooledModbusMasterFactory(String host, int port) {
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModbusMaster create() throws Exception {
|
||||||
|
IpParameters params = new IpParameters();
|
||||||
|
params.setHost(this.host);
|
||||||
|
params.setPort(this.port);
|
||||||
|
params.setEncapsulated(false);
|
||||||
|
|
||||||
|
ModbusMaster master = new ModbusFactory().createTcpMaster(params, true);
|
||||||
|
master.init();
|
||||||
|
return master;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PooledObject<ModbusMaster> wrap(ModbusMaster master) {
|
||||||
|
return new DefaultPooledObject<>(master);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyObject(PooledObject<ModbusMaster> p) throws Exception {
|
||||||
|
if (p.getObject() != null) {
|
||||||
|
p.getObject().destroy();
|
||||||
|
}
|
||||||
|
super.destroyObject(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validateObject(PooledObject<ModbusMaster> p) {
|
||||||
|
// testConnectivity() 方法在 modbus4j 中不存在。
|
||||||
|
// 一个真正的验证需要执行一次读操作,但这需要 slaveId,而工厂是通用的,不知道 slaveId。
|
||||||
|
// 因此,我们使用一个较弱的验证,只检查 master 对象是否已初始化。
|
||||||
|
// 这无法检测到被远程设备断开的连接,错误将在实际读取操作中被捕获。
|
||||||
|
if (p.getObject() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return p.getObject().isInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package com.xzzn.common.core.modbus.domain;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class DeviceConfig {
|
||||||
|
|
||||||
|
|
||||||
|
private String deviceNumber;
|
||||||
|
private String deviceName;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private long time;
|
||||||
|
private int slaveId;
|
||||||
|
private boolean enabled;
|
||||||
|
private List<TagConfig> tags;
|
||||||
|
private List<WriteTagConfig> writeTags;
|
||||||
|
|
||||||
|
public String getDeviceNumber() {
|
||||||
|
return deviceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceNumber(String deviceNumber) {
|
||||||
|
this.deviceNumber = deviceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceName(String deviceName) {
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(long time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSlaveId() {
|
||||||
|
return slaveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSlaveId(int slaveId) {
|
||||||
|
this.slaveId = slaveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TagConfig> getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTags(List<TagConfig> tags) {
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WriteTagConfig> getWriteTags() {
|
||||||
|
return writeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWriteTags(List<WriteTagConfig> writeTags) {
|
||||||
|
this.writeTags = writeTags;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package com.xzzn.common.core.modbus.domain;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class TagConfig {
|
||||||
|
private String key;
|
||||||
|
private String des;
|
||||||
|
private String address;
|
||||||
|
private String dataType;
|
||||||
|
private float a;
|
||||||
|
private float k;
|
||||||
|
private float b;
|
||||||
|
private Integer bit;
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDes() {
|
||||||
|
return des;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDes(String des) {
|
||||||
|
this.des = des;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataType() {
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataType(String dataType) {
|
||||||
|
this.dataType = dataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getA() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setA(float a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getK() {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setK(float k) {
|
||||||
|
this.k = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getB() {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setB(float b) {
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getBit() {
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBit(Integer bit) {
|
||||||
|
this.bit = bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.xzzn.common.core.modbus.domain;
|
||||||
|
|
||||||
|
public class WriteTagConfig {
|
||||||
|
private Object value;
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
public Object getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package com.xzzn.common.enums;
|
||||||
|
|
||||||
|
import com.serotonin.modbus4j.code.DataType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class ModBusType {
|
||||||
|
|
||||||
|
|
||||||
|
public static final Map<Integer, RegisterType> REGISTER_TYPE = new HashMap<Integer, RegisterType>();
|
||||||
|
public static final Map<String, Integer> LENGTH = new HashMap<String, Integer>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
REGISTER_TYPE.put(0, RegisterType.COIL);
|
||||||
|
REGISTER_TYPE.put(1, RegisterType.DISCRETE_INPUT);
|
||||||
|
REGISTER_TYPE.put(4, RegisterType.HOLDING_REGISTER);
|
||||||
|
REGISTER_TYPE.put(3, RegisterType.INPUT_REGISTER);
|
||||||
|
|
||||||
|
|
||||||
|
LENGTH.put("BINARY",DataType.BINARY);
|
||||||
|
LENGTH.put("TWO_BYTE_INT_UNSIGNED",DataType.TWO_BYTE_INT_UNSIGNED);
|
||||||
|
LENGTH.put("TWO_BYTE_INT_SIGNED",DataType.TWO_BYTE_INT_SIGNED);
|
||||||
|
LENGTH.put("TWO_BYTE_INT_UNSIGNED_SWAPPED",DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED);
|
||||||
|
LENGTH.put("TWO_BYTE_INT_SIGNED_SWAPPED",DataType.TWO_BYTE_INT_SIGNED_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_UNSIGNED",DataType.FOUR_BYTE_INT_UNSIGNED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_SIGNED",DataType.FOUR_BYTE_INT_SIGNED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT",DataType.FOUR_BYTE_FLOAT);
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED",DataType.FOUR_BYTE_FLOAT_SWAPPED);
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED_INVERTED",DataType.FOUR_BYTE_FLOAT_SWAPPED_INVERTED);
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT_INVERTED",DataType.FOUR_BYTE_FLOAT_INVERTED);
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED_SWAPPED",DataType.FOUR_BYTE_FLOAT_SWAPPED_SWAPPED);
|
||||||
|
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED",DataType.EIGHT_BYTE_INT_UNSIGNED);
|
||||||
|
LENGTH.put("EIGHT_BYTE_INT_SIGNED",DataType.EIGHT_BYTE_INT_SIGNED);
|
||||||
|
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_UNSIGNED_SWAPPED);
|
||||||
|
LENGTH.put("EIGHT_BYTE_INT_SIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_SIGNED_SWAPPED);
|
||||||
|
LENGTH.put("EIGHT_BYTE_FLOAT",DataType.EIGHT_BYTE_FLOAT);
|
||||||
|
LENGTH.put("EIGHT_BYTE_FLOAT_SWAPPED",DataType.EIGHT_BYTE_FLOAT_SWAPPED);
|
||||||
|
LENGTH.put("TWO_BYTE_BCD",DataType.TWO_BYTE_BCD);
|
||||||
|
LENGTH.put("FOUR_BYTE_BCD",DataType.FOUR_BYTE_BCD);
|
||||||
|
LENGTH.put("FOUR_BYTE_BCD_SWAPPED",DataType.FOUR_BYTE_BCD_SWAPPED);
|
||||||
|
LENGTH.put("CHAR",DataType.CHAR);
|
||||||
|
LENGTH.put("VARCHAR",DataType.VARCHAR);
|
||||||
|
|
||||||
|
LENGTH.put("FOUR_BYTE_FLOAT_DBCA",28);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.xzzn.common.enums;
|
||||||
|
|
||||||
|
public enum PcsControlCommand {
|
||||||
|
TOTAL_START(0x0004, "总运行"),
|
||||||
|
TOTAL_STOP(0x0010, "总停机"),
|
||||||
|
TOTAL_RESET(0x0080, "总复位"),
|
||||||
|
// CLUSTER1_START(0x1004, "电池簇1运行"),
|
||||||
|
// CLUSTER1_STOP(0x1010, "电池簇1停机"),
|
||||||
|
// CLUSTER1_RESET(0x1080, "电池簇1复位"),
|
||||||
|
// CLUSTER2_START(0x2004, "电池簇2运行"),
|
||||||
|
// CLUSTER2_STOP(0x2010, "电池簇2停机"),
|
||||||
|
// CLUSTER2_RESET(0x2080, "电池簇2复位"),
|
||||||
|
// CLUSTER3_START(0x3004, "电池簇3运行"),
|
||||||
|
// CLUSTER3_STOP(0x3010, "电池簇3停机"),
|
||||||
|
// CLUSTER3_RESET(0x3080, "电池簇3复位"),
|
||||||
|
// CLUSTER4_START(0x4004, "电池簇4运行"),
|
||||||
|
// CLUSTER4_STOP(0x4010, "电池簇4停机"),
|
||||||
|
// CLUSTER4_RESET(0x4080, "电池簇4复位"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
PcsControlCommand(int code, String description) {
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PcsControlCommand fromDeviceStatus(String deviceStatus) {
|
||||||
|
if (DeviceRunningStatus.RUNNING.getCode().equals(deviceStatus)) {
|
||||||
|
return PcsControlCommand.TOTAL_START;
|
||||||
|
} else if (DeviceRunningStatus.SHUTDOWN.getCode().equals(deviceStatus) ) {
|
||||||
|
return PcsControlCommand.TOTAL_STOP;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.xzzn.common.enums;
|
||||||
|
|
||||||
|
public enum RegisterType {
|
||||||
|
COIL,
|
||||||
|
DISCRETE_INPUT,
|
||||||
|
HOLDING_REGISTER,
|
||||||
|
INPUT_REGISTER
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.xzzn.quartz.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ScheduledTask {
|
||||||
|
|
||||||
|
|
||||||
|
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
|
||||||
|
private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void startTask(String deviceId, Runnable task, long period) {
|
||||||
|
stopTask(deviceId); // 如果已有同ID任务在运行,先停止
|
||||||
|
ScheduledFuture<?> future = executor.scheduleAtFixedRate(task, 0, period, TimeUnit.MILLISECONDS);
|
||||||
|
futureMap.put(deviceId, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopTask(String deviceId) {
|
||||||
|
ScheduledFuture<?> future = futureMap.get(deviceId);
|
||||||
|
if (future != null && !future.isDone()) {
|
||||||
|
future.cancel(true);
|
||||||
|
}
|
||||||
|
futureMap.remove(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,179 +1,204 @@
|
|||||||
package com.xzzn.quartz.task;
|
package com.xzzn.quartz.task;
|
||||||
|
|
||||||
import com.xzzn.common.enums.DeviceRunningStatus;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.xzzn.ems.domain.EmsDeviceChangeLog;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.xzzn.ems.mapper.EmsDeviceChangeLogMapper;
|
import com.serotonin.modbus4j.ModbusMaster;
|
||||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
import com.xzzn.common.constant.RedisKeyConstants;
|
||||||
import com.xzzn.ems.mapper.EmsMqttMessageMapper;
|
import com.xzzn.common.core.modbus.ModbusProcessor;
|
||||||
import com.xzzn.ems.service.impl.EmsDeviceSettingServiceImpl;
|
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||||
import com.xzzn.framework.manager.ModbusConnectionManager;
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
import com.xzzn.framework.manager.ModbusConnectionWrapper;
|
import com.xzzn.common.utils.DateUtils;
|
||||||
|
import com.xzzn.ems.service.IEmsAlarmRecordsService;
|
||||||
|
import com.xzzn.ems.service.impl.DeviceDataProcessServiceImpl;
|
||||||
import com.xzzn.framework.manager.MqttLifecycleManager;
|
import com.xzzn.framework.manager.MqttLifecycleManager;
|
||||||
import com.xzzn.framework.web.service.ModbusService;
|
import com.xzzn.framework.web.service.MqttPublisher;
|
||||||
|
import com.xzzn.quartz.config.ScheduledTask;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 轮询设备-通过modbus协议读取数据
|
* 轮询设备-通过modbus协议读取数据
|
||||||
*/
|
*/
|
||||||
@Component("modbusPoller")
|
@Component("modbusPoller")
|
||||||
public class ModbusPoller {
|
public class ModbusPoller {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ModbusPoller.class);
|
private static final Logger log = LoggerFactory.getLogger(ModbusPoller.class);
|
||||||
|
|
||||||
private final MqttLifecycleManager mqttLifecycleManager;
|
private final MqttLifecycleManager mqttLifecycleManager;
|
||||||
|
private final ScheduledTask scheduledTask;
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ModbusConnectionManager connectionManager;
|
private ModbusProcessor modbusProcessor;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ModbusService modbusService;
|
private IEmsAlarmRecordsService iEmsAlarmRecordsService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsDevicesSettingMapper deviceRepo;
|
private DeviceDataProcessServiceImpl deviceDataProcessServiceImpl;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsMqttMessageMapper emsMqttMessageMapper;
|
private RedisCache redisCache;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsDeviceSettingServiceImpl emsDeviceSettingServiceImpl;
|
private MqttPublisher mqttPublisher;
|
||||||
@Autowired
|
|
||||||
private EmsDeviceChangeLogMapper emsDeviceChangeLogMapper;
|
@Value("${mqtt.topic}")
|
||||||
|
private String topic;
|
||||||
|
@Value("${mqtt.siteId}")
|
||||||
|
private String siteId;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager) {
|
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager, ScheduledTask scheduledTask) {
|
||||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||||
|
this.scheduledTask = scheduledTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void pollAllDevices() {
|
public void pollAllDevices() {
|
||||||
logger.info("开始执行Modbus设备轮询...");
|
Path devicesDir = Paths.get(System.getProperty("user.dir"), "devices");
|
||||||
EmsDevicesSetting selectEntity = new EmsDevicesSetting();
|
if (!Files.exists(devicesDir)) {
|
||||||
selectEntity.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
log.error("Devices目录不存在: {}", devicesDir);
|
||||||
List<EmsDevicesSetting> activeDevices = deviceRepo.selectEmsDevicesSettingList(selectEntity);
|
|
||||||
|
|
||||||
EmsDevicesSetting device = activeDevices.get(0);
|
|
||||||
try {
|
|
||||||
//pollSingleDevice(device);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
|
||||||
}
|
|
||||||
/*activeDevices.forEach(device -> {
|
|
||||||
try {
|
|
||||||
CompletableFuture.runAsync(() -> pollSingleDevice(device))
|
|
||||||
.exceptionally(e -> {
|
|
||||||
logger.error("设备{}轮询异常", device.getId(), e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pollSingleDevice(EmsDevicesSetting device) {
|
|
||||||
logger.debug("开始轮询设备: {}", device.getSiteId(), device.getDeviceName(), device.getId());
|
|
||||||
|
|
||||||
ModbusConnectionWrapper wrapper = null;
|
|
||||||
try {
|
|
||||||
// 获取连接
|
|
||||||
wrapper = connectionManager.getConnection(device);
|
|
||||||
|
|
||||||
if(wrapper == null || !wrapper.isActive()){
|
|
||||||
logger.error("轮询设备{}连接失败: {}", device.getId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 读取保持寄存器
|
|
||||||
int[] data = modbusService.readHoldingRegisters(
|
|
||||||
wrapper.getConnection(),
|
|
||||||
1, //从站ID
|
|
||||||
10 // 寄存器数量
|
|
||||||
);
|
|
||||||
|
|
||||||
// 处理读取到的数据
|
|
||||||
processData(device, data);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("轮询设备{}失败: {}", device.getId(), e.getMessage());
|
|
||||||
// 标记连接为无效
|
|
||||||
if (wrapper != null) {
|
|
||||||
wrapper.close();
|
|
||||||
connectionManager.removeConnection(Integer.parseInt(device.getDeviceId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设备轮询不到修改运行状态
|
|
||||||
String beforeStatus = device.getDeviceStatus();
|
|
||||||
device.setDeviceStatus(DeviceRunningStatus.SHUTDOWN.getCode());
|
|
||||||
emsDeviceSettingServiceImpl.updateDevice(device);
|
|
||||||
|
|
||||||
// 轮询设备,设备状态变更日志
|
|
||||||
EmsDeviceChangeLog log = createLogEntity(beforeStatus,device);
|
|
||||||
emsDeviceChangeLogMapper.insertEmsDeviceChangeLog(log);
|
|
||||||
|
|
||||||
throw new RuntimeException("轮询设备失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理获取到的数据
|
|
||||||
private void processData(EmsDevicesSetting device, int[] data) throws MqttException {
|
|
||||||
String beforeStatus = device.getDeviceStatus();
|
|
||||||
Boolean error = true;
|
|
||||||
if (data == null || data.length == 0) {
|
|
||||||
logger.warn("设备{}返回空数据", device.getId());
|
|
||||||
// 设备读取不到-设置设备故障
|
|
||||||
device.setDeviceStatus(DeviceRunningStatus.FAULT.getCode());
|
|
||||||
error = false;
|
|
||||||
} else {
|
|
||||||
// 恢复设备状态 - 运行
|
|
||||||
device.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
|
||||||
}
|
|
||||||
emsDeviceSettingServiceImpl.updateDevice(device);
|
|
||||||
// 轮询设备,设备状态变更日志
|
|
||||||
EmsDeviceChangeLog log = createLogEntity(beforeStatus,device);
|
|
||||||
emsDeviceChangeLogMapper.insertEmsDeviceChangeLog(log);
|
|
||||||
// 错误数据-不处理直接返回
|
|
||||||
if (!error) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据处理逻辑
|
List<Path> jsonFiles = null;
|
||||||
StringBuilder sb = new StringBuilder();
|
try {
|
||||||
sb.append("设备[").append(device.getDeviceName()).append("]数据: ");
|
jsonFiles = Files.list(devicesDir)
|
||||||
for (int i = 0; i < data.length; i++) {
|
.filter(path -> path.toString().endsWith(".json"))
|
||||||
sb.append("R").append(i).append("=").append(data[i]).append(" ");
|
.collect(Collectors.toList());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("modbusPoller.loadConfigs 获取设备配置文件失败: {}", devicesDir, e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = sb.toString();
|
// 按 host:port 分组
|
||||||
logger.info(sb.toString());
|
Map<String, List<DeviceConfig>> groupedConfigs = new HashMap<>();
|
||||||
/*
|
for (Path filePath : jsonFiles) {
|
||||||
String siteId = device.getSiteId();
|
DeviceConfig config = null;
|
||||||
if (siteId.startsWith("021_DDS")) {
|
try {
|
||||||
dDSDataProcessService.handleDdsData(message);
|
config = objectMapper.readValue(filePath.toFile(), DeviceConfig.class);
|
||||||
} else if (siteId.startsWith("021_FXX")) {
|
} catch (IOException e) {
|
||||||
fXXDataProcessService.handleFxData(message);
|
log.error("modbusPoller.loadConfigs 解析设备配置文件失败: {}", filePath, e);
|
||||||
}*/
|
continue;
|
||||||
|
}
|
||||||
|
if (config.isEnabled()) {
|
||||||
|
String key = config.getHost() + ":" + config.getPort();
|
||||||
|
groupedConfigs.computeIfAbsent(key, k -> new ArrayList<>()).add(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 测试发送mqtt
|
// 为每个 host:port 启动一个任务
|
||||||
/* EmsMqttMessage msg = emsMqttMessageMapper.selectEmsMqttMessageById(1L);
|
for (Map.Entry<String, List<DeviceConfig>> entry : groupedConfigs.entrySet()) {
|
||||||
String dataJson = msg.getMqttMessage();
|
String groupKey = entry.getKey();
|
||||||
String topic = msg.getMqttTopic();
|
List<DeviceConfig> configs = entry.getValue();
|
||||||
logger.info("topic:" + topic);
|
// 取其中一个配置的时间间隔作为该组任务的执行周期
|
||||||
logger.info("dataJson:" + dataJson);
|
long interval = configs.get(0).getTime();
|
||||||
// 将设备数据下发到mqtt服务器上
|
scheduledTask.startTask(groupKey, () -> {
|
||||||
mqttLifecycleManager.publish(topic, dataJson, 0);*/
|
for (DeviceConfig config : configs) {
|
||||||
|
try {
|
||||||
|
scheduledStart(config, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("采集设备数据异常: {}", config.getDeviceName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmsDeviceChangeLog createLogEntity(String beforeStatus, EmsDevicesSetting device) {
|
public void scheduledStart(DeviceConfig config, ModbusMaster master) {
|
||||||
EmsDeviceChangeLog log = new EmsDeviceChangeLog();
|
try {
|
||||||
log.setLogId(UUID.randomUUID().toString());
|
if (config.isEnabled()) {
|
||||||
log.setLogTime(new Date());
|
log.info("Reading data from devices: {}", config.getDeviceName());
|
||||||
log.setSiteId(device.getSiteId());
|
Map<String, Object> data = modbusProcessor.readDataFromDevice(config, master);
|
||||||
log.setDeviceId(device.getDeviceId());
|
// 在这里处理采集到的数据
|
||||||
log.setBeforeStatus(beforeStatus);
|
config.getTags().forEach(tag -> {
|
||||||
log.setAfterStatus(device.getDeviceStatus());
|
Object rawValue = data.get(tag.getKey());
|
||||||
log.setCreateBy("sys");
|
if (rawValue != null) {
|
||||||
log.setCreateTime(new Date());
|
float value = 0;
|
||||||
return log;
|
if (rawValue instanceof Number) {
|
||||||
|
value = ((Number) rawValue).floatValue(); // 安全地转换为 float
|
||||||
|
} else {
|
||||||
|
log.error("tag:{},无法将数据转换为数字: {}", tag.getKey(), rawValue);
|
||||||
|
}
|
||||||
|
value = tag.getA() * value * value + tag.getK() * value + tag.getB();
|
||||||
|
|
||||||
|
int intValue = (int) value;
|
||||||
|
if (tag.getBit() != null) {
|
||||||
|
log.info("tag:{},bit:{},value:{}", tag.getKey(), tag.getBit(), value);
|
||||||
|
String binary = Integer.toBinaryString(intValue);
|
||||||
|
data.put(tag.getKey(), binary);
|
||||||
|
} else {
|
||||||
|
data.put(tag.getKey(), value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.put(tag.getKey(), rawValue);
|
||||||
|
log.warn("tag:{},数据为空: {}", tag.getKey(), rawValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log.info("Data from {}: {}", config.getDeviceName(), data);
|
||||||
|
String deviceNumber = config.getDeviceNumber();
|
||||||
|
//处理数据并发送MQTT消息、保存Redis数据和数据入库
|
||||||
|
processingData(data, deviceNumber);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("设备数据采集异常: {}", config.getDeviceName(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processingData(Map<String, Object> data, String deviceNumber) {
|
||||||
|
if (data == null || data.size() == 0) {
|
||||||
|
// 添加设备告警
|
||||||
|
iEmsAlarmRecordsService.addEmptyDataAlarmRecord(siteId, deviceNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Long timestamp = System.currentTimeMillis();
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("Data", data);
|
||||||
|
json.put("timestamp", timestamp);
|
||||||
|
json.put("Device", deviceNumber);
|
||||||
|
sendMqttMsg(json);
|
||||||
|
saveRedisData(json, deviceNumber);
|
||||||
|
saveDataToDatabase(data, deviceNumber, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMqttMsg(JSONObject json) {
|
||||||
|
try {
|
||||||
|
mqttPublisher.publish(topic, Collections.singletonList(json).toString(), 0);
|
||||||
|
} catch (MqttException e) {
|
||||||
|
log.error("MQTT消息发布失败: {}", json.toJSONString(), e);
|
||||||
|
}
|
||||||
|
log.info("已发送数据: {}", json.toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void saveRedisData(JSONObject obj, String deviceNumber) {
|
||||||
|
try {
|
||||||
|
// 存放mqtt原始每个设备最晚一次数据,便于后面点位获取数据
|
||||||
|
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA + siteId + "_" + deviceNumber, obj);
|
||||||
|
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据和保护策略查询
|
||||||
|
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceNumber, obj, 1, TimeUnit.MINUTES);
|
||||||
|
log.info("数据已成功存储在Redis: {}", deviceNumber);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("无法在设备的Redis中存储数据: {}", deviceNumber, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveDataToDatabase(Map<String, Object> data, String deviceNumber, Long timestamp) {
|
||||||
|
deviceDataProcessServiceImpl.processingDeviceData(siteId, deviceNumber, JSON.toJSONString(data), DateUtils.convertUpdateTime(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
package com.xzzn.ems.domain;
|
package com.xzzn.ems.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.xzzn.common.annotation.Excel;
|
||||||
|
import com.xzzn.common.core.domain.BaseEntity;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import com.xzzn.common.core.domain.BaseEntity;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import com.xzzn.common.annotation.Excel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单体电池实时数据对象 ems_battery_data
|
* 单体电池实时数据对象 ems_battery_data
|
||||||
@ -70,6 +72,10 @@ public class EmsBatteryData extends BaseEntity
|
|||||||
@Excel(name = "单体电池内阻")
|
@Excel(name = "单体电池内阻")
|
||||||
private BigDecimal interResistance;
|
private BigDecimal interResistance;
|
||||||
|
|
||||||
|
/** 单体电池电流 */
|
||||||
|
@Excel(name = "单体电池电流")
|
||||||
|
private BigDecimal current;
|
||||||
|
|
||||||
public void setId(Long id)
|
public void setId(Long id)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -200,6 +206,14 @@ public class EmsBatteryData extends BaseEntity
|
|||||||
return interResistance;
|
return interResistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCurrent() {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrent(BigDecimal current) {
|
||||||
|
this.current = current;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||||
@ -221,6 +235,7 @@ public class EmsBatteryData extends BaseEntity
|
|||||||
.append("deviceId", getDeviceId())
|
.append("deviceId", getDeviceId())
|
||||||
.append("clusterDeviceId", getClusterDeviceId())
|
.append("clusterDeviceId", getClusterDeviceId())
|
||||||
.append("interResistance", getInterResistance())
|
.append("interResistance", getInterResistance())
|
||||||
|
.append("current", getCurrent())
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
package com.xzzn.ems.domain;
|
package com.xzzn.ems.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.xzzn.common.annotation.Excel;
|
||||||
|
import com.xzzn.common.core.domain.BaseEntity;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import com.xzzn.common.core.domain.BaseEntity;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import com.xzzn.common.annotation.Excel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PCS数据对象 ems_pcs_data
|
* PCS数据对象 ems_pcs_data
|
||||||
@ -34,8 +36,8 @@ public class EmsPcsData extends BaseEntity
|
|||||||
@Excel(name = "并网状态:0-并网 1-未并网")
|
@Excel(name = "并网状态:0-并网 1-未并网")
|
||||||
private String gridStatus;
|
private String gridStatus;
|
||||||
|
|
||||||
/** 设备状态:0-在线 1-离线 2-维修中 */
|
/** 设备运行状态:0-离线、1-待机、2-运行、3-故障、4-停机 */
|
||||||
@Excel(name = "设备状态:0-在线 1-离线 2-维修中")
|
@Excel(name = "设备运行状态:0-离线、1-待机、2-运行、3-故障、4-停机")
|
||||||
private String deviceStatus;
|
private String deviceStatus;
|
||||||
|
|
||||||
/** 控制模式:0-远程 1-本地 */
|
/** 控制模式:0-远程 1-本地 */
|
||||||
@ -234,6 +236,22 @@ public class EmsPcsData extends BaseEntity
|
|||||||
@Excel(name = "4#模块IGBT最高温")
|
@Excel(name = "4#模块IGBT最高温")
|
||||||
private BigDecimal module4Temp;
|
private BigDecimal module4Temp;
|
||||||
|
|
||||||
|
/** 电池簇1PCS有功功率给定 */
|
||||||
|
@Excel(name = "电池簇1PCS有功功率给定")
|
||||||
|
private BigDecimal cluster1ActivePower;
|
||||||
|
|
||||||
|
/** 电池簇2PCS有功功率给定 */
|
||||||
|
@Excel(name = "电池簇2PCS有功功率给定")
|
||||||
|
private BigDecimal cluster2ActivePower;
|
||||||
|
|
||||||
|
/** 电池簇3PCS有功功率给定 */
|
||||||
|
@Excel(name = "电池簇3PCS有功功率给定")
|
||||||
|
private BigDecimal cluster3ActivePower;
|
||||||
|
|
||||||
|
/** 电池簇4PCS有功功率给定 */
|
||||||
|
@Excel(name = "电池簇4PCS有功功率给定")
|
||||||
|
private BigDecimal cluster4ActivePower;
|
||||||
|
|
||||||
public void setId(Long id)
|
public void setId(Long id)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -774,6 +792,38 @@ public class EmsPcsData extends BaseEntity
|
|||||||
return module4Temp;
|
return module4Temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCluster1ActivePower() {
|
||||||
|
return cluster1ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCluster1ActivePower(BigDecimal cluster1ActivePower) {
|
||||||
|
this.cluster1ActivePower = cluster1ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCluster2ActivePower() {
|
||||||
|
return cluster2ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCluster2ActivePower(BigDecimal cluster2ActivePower) {
|
||||||
|
this.cluster2ActivePower = cluster2ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCluster3ActivePower() {
|
||||||
|
return cluster3ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCluster3ActivePower(BigDecimal cluster3ActivePower) {
|
||||||
|
this.cluster3ActivePower = cluster3ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCluster4ActivePower() {
|
||||||
|
return cluster4ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCluster4ActivePower(BigDecimal cluster4ActivePower) {
|
||||||
|
this.cluster4ActivePower = cluster4ActivePower;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||||
@ -836,6 +886,10 @@ public class EmsPcsData extends BaseEntity
|
|||||||
.append("module2Temp", getModule2Temp())
|
.append("module2Temp", getModule2Temp())
|
||||||
.append("module3Temp", getModule3Temp())
|
.append("module3Temp", getModule3Temp())
|
||||||
.append("module4Temp", getModule4Temp())
|
.append("module4Temp", getModule4Temp())
|
||||||
|
.append("cluster1ActivePower", getCluster1ActivePower())
|
||||||
|
.append("cluster2ActivePower", getCluster2ActivePower())
|
||||||
|
.append("cluster3ActivePower", getCluster3ActivePower())
|
||||||
|
.append("cluster4ActivePower", getCluster4ActivePower())
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.xzzn.ems.domain.vo;
|
package com.xzzn.ems.domain.vo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,6 +24,10 @@ public class DeviceUpdateRequest {
|
|||||||
/** 设备类型 */
|
/** 设备类型 */
|
||||||
private String deviceCategory;
|
private String deviceCategory;
|
||||||
|
|
||||||
|
/** 设备点位数据匹配表字段 */
|
||||||
|
private List<String> matchFields;
|
||||||
|
|
||||||
|
|
||||||
public String getSiteId() {
|
public String getSiteId() {
|
||||||
return siteId;
|
return siteId;
|
||||||
}
|
}
|
||||||
@ -53,4 +59,12 @@ public class DeviceUpdateRequest {
|
|||||||
public void setDeviceCategory(String deviceCategory) {
|
public void setDeviceCategory(String deviceCategory) {
|
||||||
this.deviceCategory = deviceCategory;
|
this.deviceCategory = deviceCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getMatchFields() {
|
||||||
|
return matchFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMatchFields(List<String> matchFields) {
|
||||||
|
this.matchFields = matchFields;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
package com.xzzn.ems.mapper;
|
package com.xzzn.ems.mapper;
|
||||||
|
|
||||||
|
import com.xzzn.ems.domain.EmsPointMatch;
|
||||||
|
import com.xzzn.ems.domain.vo.DevicePointMatchExportVo;
|
||||||
|
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
|
||||||
|
import com.xzzn.ems.domain.vo.GeneralQueryDataVo;
|
||||||
|
import com.xzzn.ems.domain.vo.PointQueryResponse;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.xzzn.ems.domain.EmsPointMatch;
|
|
||||||
import com.xzzn.ems.domain.vo.DevicePointMatchExportVo;
|
|
||||||
import com.xzzn.ems.domain.vo.DevicePointMatchVo;
|
|
||||||
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
|
|
||||||
import com.xzzn.ems.domain.vo.GeneralQueryDataVo;
|
|
||||||
import com.xzzn.ems.domain.vo.PointQueryResponse;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,5 +171,5 @@ public interface EmsPointMatchMapper
|
|||||||
|
|
||||||
int getDevicePointAlarmNum(@Param("siteId") String siteId, @Param("deviceId") String deviceId, @Param("deviceCategory") String deviceCategory);
|
int getDevicePointAlarmNum(@Param("siteId") String siteId, @Param("deviceId") String deviceId, @Param("deviceCategory") String deviceCategory);
|
||||||
|
|
||||||
EmsPointMatch selectDeviceStatusPoint(@Param("request") DeviceUpdateRequest request);
|
List<EmsPointMatch> selectDeviceStatusPoint(@Param("request") DeviceUpdateRequest request);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,32 +161,37 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
|
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
|
||||||
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
|
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
|
||||||
|
|
||||||
if (deviceId.contains(SiteDevice.BMSD.name())) {
|
// 处理设备数据
|
||||||
batteryStackDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
processingDeviceData(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
batteryGroupDataProcess(siteId, deviceId, jsonData);
|
}
|
||||||
batteryDataProcessFromBmsd(siteId, deviceId, jsonData, dataUpdateTime);
|
}
|
||||||
} else if (deviceId.contains(SiteDevice.BMSC.name())) {
|
|
||||||
batteryClusterDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
public void processingDeviceData(String siteId, String deviceId, String jsonData, Date dataUpdateTime) {
|
||||||
batteryDataProcessFromBmsc(siteId, deviceId, jsonData, dataUpdateTime);
|
if (deviceId.contains(SiteDevice.BMSD.name())) {
|
||||||
} else if (deviceId.contains(SiteDevice.PCS.name())) {
|
batteryStackDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
pcsDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
batteryGroupDataProcess(siteId, deviceId, jsonData);
|
||||||
pcsBranchDataProcess(siteId, deviceId, jsonData);
|
batteryDataProcessFromBmsd(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
batteryClusterDataProcess(siteId, jsonData, dataUpdateTime);
|
} else if (deviceId.contains(SiteDevice.BMSC.name())) {
|
||||||
} else if (deviceId.contains(SiteDevice.LOAD.name())) {
|
batteryClusterDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
loadDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
batteryDataProcessFromBmsc(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
} else if (deviceId.contains(SiteDevice.METEGF.name())
|
} else if (deviceId.contains(SiteDevice.PCS.name())) {
|
||||||
|| deviceId.contains(SiteDevice.METE.name())
|
pcsDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|| deviceId.contains(SiteDevice.METE0.name())) {
|
pcsBranchDataProcess(siteId, deviceId, jsonData);
|
||||||
meteDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
batteryClusterDataProcess(siteId, jsonData, dataUpdateTime);
|
||||||
} else if (deviceId.contains(SiteDevice.XF.name())) {
|
} else if (deviceId.contains(SiteDevice.LOAD.name())) {
|
||||||
meteXFProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
loadDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
} else if (deviceId.contains(SiteDevice.DH.name()) || deviceId.contains(SiteDevice.donghuan.name())) {
|
} else if (deviceId.contains(SiteDevice.METEGF.name())
|
||||||
dhDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
|| deviceId.contains(SiteDevice.METE.name())
|
||||||
} else if (deviceId.contains(SiteDevice.ZSLQ.name())) {
|
|| deviceId.contains(SiteDevice.METE0.name())) {
|
||||||
coolingDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
meteDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
} else if (deviceId.contains(SiteDevice.EMS.name())) {
|
} else if (deviceId.contains(SiteDevice.XF.name())) {
|
||||||
emsDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
meteXFProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
}
|
} else if (deviceId.contains(SiteDevice.DH.name()) || deviceId.contains(SiteDevice.donghuan.name())) {
|
||||||
|
dhDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
} else if (deviceId.contains(SiteDevice.ZSLQ.name())) {
|
||||||
|
coolingDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
} else if (deviceId.contains(SiteDevice.EMS.name())) {
|
||||||
|
emsDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,14 +254,14 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void meteXFProcess(String siteId, String deviceId, String dataJson, Date dataUpdateTime) {
|
public void meteXFProcess(String siteId, String deviceId, String dataJson, Date dataUpdateTime) {
|
||||||
//消防
|
//消防
|
||||||
Map<String, Object> obj = JSON.parseObject(dataJson, new TypeReference<Map<String, Object>>() {
|
Map<String, Object> obj = JSON.parseObject(dataJson, new TypeReference<Map<String, Object>>() {
|
||||||
});
|
});
|
||||||
// 点位匹配数据
|
// 点位匹配数据
|
||||||
List<EmsPointMatch> pointMatchList = devicePointMatchDataProcessor.getDevicePointMatch(siteId, deviceId, DeviceMatchTable.XF.getCode());
|
List<EmsPointMatch> pointMatchList = devicePointMatchDataProcessor.getDevicePointMatch(siteId, deviceId, DeviceMatchTable.XF.getCode());
|
||||||
if (CollectionUtils.isEmpty(pointMatchList)) {
|
if (CollectionUtils.isEmpty(pointMatchList)) {
|
||||||
log.info("未找到匹配的点位数据,无法处理动环数据,siteId: " + siteId + ",deviceId: " + deviceId);
|
log.info("未找到匹配的点位数据,无法处理消防数据,siteId: " + siteId + ",deviceId: " + deviceId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, List<EmsPointEnumMatch>> pointEnumMatchMap = devicePointMatchDataProcessor.getPointEnumMatchMap(siteId, DeviceMatchTable.XF.getCode());
|
Map<String, List<EmsPointEnumMatch>> pointEnumMatchMap = devicePointMatchDataProcessor.getPointEnumMatchMap(siteId, DeviceMatchTable.XF.getCode());
|
||||||
@ -1415,20 +1420,8 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA_ALARM + siteId + "_" + deviceId, obj);
|
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA_ALARM + siteId + "_" + deviceId, obj);
|
||||||
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
|
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
|
||||||
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
|
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
|
||||||
|
// 处理设备数据,根据不同设备类型进行不同的数据处理
|
||||||
String deviceCategory = "";
|
String deviceCategory = processingDeviceAlarmData(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
if (deviceId.contains(SiteDevice.ZSLQ.name())) {
|
|
||||||
coolingAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
|
||||||
} else if (deviceId.contains(SiteDevice.BMSD.name())) {
|
|
||||||
deviceCategory = DeviceCategory.STACK.getCode();
|
|
||||||
stackAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
|
||||||
} else if (deviceId.contains(SiteDevice.BMSC.name())) {
|
|
||||||
deviceCategory = DeviceCategory.CLUSTER.getCode();
|
|
||||||
clusterAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
|
||||||
} else if (deviceId.contains(SiteDevice.PCS.name())) {
|
|
||||||
deviceCategory = DeviceCategory.PCS.getCode();
|
|
||||||
pcsAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(deviceCategory)) {
|
if (StringUtils.isEmpty(deviceCategory)) {
|
||||||
// 处理告警信息
|
// 处理告警信息
|
||||||
alarmDataProcess(siteId, deviceId, jsonData, alarmMatchInfo, deviceCategory);
|
alarmDataProcess(siteId, deviceId, jsonData, alarmMatchInfo, deviceCategory);
|
||||||
@ -1436,6 +1429,24 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String processingDeviceAlarmData(String siteId, String deviceId, String jsonData, Date dataUpdateTime) {
|
||||||
|
String deviceCategory = "";
|
||||||
|
if (deviceId.contains(SiteDevice.ZSLQ.name())) {
|
||||||
|
coolingAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
} else if (deviceId.contains(SiteDevice.BMSD.name())) {
|
||||||
|
deviceCategory = DeviceCategory.STACK.getCode();
|
||||||
|
stackAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
} else if (deviceId.contains(SiteDevice.BMSC.name())) {
|
||||||
|
deviceCategory = DeviceCategory.CLUSTER.getCode();
|
||||||
|
clusterAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
} else if (deviceId.contains(SiteDevice.PCS.name())) {
|
||||||
|
deviceCategory = DeviceCategory.PCS.getCode();
|
||||||
|
pcsAlarmDataProcess(siteId, deviceId, jsonData, dataUpdateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
private void pcsAlarmDataProcess(String siteId, String deviceId, String jsonData, Date dataUpdateTime) {
|
private void pcsAlarmDataProcess(String siteId, String deviceId, String jsonData, Date dataUpdateTime) {
|
||||||
//pcs
|
//pcs
|
||||||
Map<String, Object> obj = JSON.parseObject(jsonData, new TypeReference<Map<String, Object>>() {
|
Map<String, Object> obj = JSON.parseObject(jsonData, new TypeReference<Map<String, Object>>() {
|
||||||
|
|||||||
@ -4,15 +4,19 @@ import com.alibaba.fastjson2.JSON;
|
|||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.alibaba.fastjson2.TypeReference;
|
import com.alibaba.fastjson2.TypeReference;
|
||||||
import com.xzzn.common.constant.RedisKeyConstants;
|
import com.xzzn.common.constant.RedisKeyConstants;
|
||||||
|
import com.xzzn.common.core.modbus.ModbusProcessor;
|
||||||
|
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||||
|
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
||||||
import com.xzzn.common.core.redis.RedisCache;
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
import com.xzzn.common.enums.DeviceCategory;
|
import com.xzzn.common.enums.DeviceCategory;
|
||||||
import com.xzzn.common.enums.DeviceRunningStatus;
|
import com.xzzn.common.enums.DeviceRunningStatus;
|
||||||
|
import com.xzzn.common.enums.DeviceType;
|
||||||
|
import com.xzzn.common.enums.PcsControlCommand;
|
||||||
import com.xzzn.common.enums.PointType;
|
import com.xzzn.common.enums.PointType;
|
||||||
import com.xzzn.common.enums.SiteEnum;
|
import com.xzzn.common.enums.SiteEnum;
|
||||||
import com.xzzn.common.exception.ServiceException;
|
import com.xzzn.common.exception.ServiceException;
|
||||||
import com.xzzn.common.utils.DateUtils;
|
import com.xzzn.common.utils.DateUtils;
|
||||||
import com.xzzn.common.utils.StringUtils;
|
import com.xzzn.common.utils.StringUtils;
|
||||||
import com.xzzn.common.utils.file.ImageUtils;
|
|
||||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||||
import com.xzzn.ems.domain.EmsPointMatch;
|
import com.xzzn.ems.domain.EmsPointMatch;
|
||||||
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
|
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
|
||||||
@ -23,15 +27,22 @@ import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
|||||||
import com.xzzn.ems.mapper.EmsPointMatchMapper;
|
import com.xzzn.ems.mapper.EmsPointMatchMapper;
|
||||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 站点信息 服务层实现
|
* 站点信息 服务层实现
|
||||||
@ -43,6 +54,8 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
|
|||||||
private static final Logger log = LoggerFactory.getLogger(EmsDeviceSettingServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(EmsDeviceSettingServiceImpl.class);
|
||||||
|
|
||||||
private static final String DDS_SITE_ID = "021_DDS_01";
|
private static final String DDS_SITE_ID = "021_DDS_01";
|
||||||
|
private static final String DEVICE_STATUS_FIELD = "device_status";
|
||||||
|
private static final List<String> CLUSTER_ACTIVE_POWER_FIELDS = Arrays.asList("cluster1_active_power", "cluster2_active_power", "cluster3_active_power", "cluster4_active_power");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsDevicesSettingMapper emsDevicesMapper;
|
private EmsDevicesSettingMapper emsDevicesMapper;
|
||||||
@ -54,6 +67,8 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
|
|||||||
private EmsBatteryDataMinutesMapper emsBatteryDataMinutesMapper;
|
private EmsBatteryDataMinutesMapper emsBatteryDataMinutesMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsBatteryClusterServiceImpl emsBatteryClusterServiceImpl;
|
private EmsBatteryClusterServiceImpl emsBatteryClusterServiceImpl;
|
||||||
|
@Autowired
|
||||||
|
private ModbusProcessor modbusProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取设备详细信息
|
* 获取设备详细信息
|
||||||
@ -486,11 +501,13 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
|
|||||||
throw new ServiceException("未找到对应设备配置信息");
|
throw new ServiceException("未找到对应设备配置信息");
|
||||||
}
|
}
|
||||||
// 查询设备配置的设备状态对应点位
|
// 查询设备配置的设备状态对应点位
|
||||||
// request.setDeviceCategory(DeviceCategory.PCS.getCode());
|
CLUSTER_ACTIVE_POWER_FIELDS.add(DEVICE_STATUS_FIELD);
|
||||||
// EmsPointMatch pointMatch = emsPointMatchMapper.selectDeviceStatusPoint(request);
|
request.setMatchFields(CLUSTER_ACTIVE_POWER_FIELDS);
|
||||||
// if (pointMatch == null) {
|
request.setDeviceCategory(DeviceCategory.PCS.getCode());
|
||||||
// throw new ServiceException("未找到对应设备状态点位");
|
List<EmsPointMatch> pointMatchList = emsPointMatchMapper.selectDeviceStatusPoint(request);
|
||||||
// }
|
if (CollectionUtils.isEmpty(pointMatchList) || pointMatchList.stream().noneMatch(point -> DEVICE_STATUS_FIELD.equals(point.getMatchField()))) {
|
||||||
|
throw new ServiceException("未找到对应设备状态点位");
|
||||||
|
}
|
||||||
if (DeviceRunningStatus.getShutdownCodeList().contains(request.getDeviceStatus())) {
|
if (DeviceRunningStatus.getShutdownCodeList().contains(request.getDeviceStatus())) {
|
||||||
// 开机逻辑
|
// 开机逻辑
|
||||||
device.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
device.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
||||||
@ -500,10 +517,50 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO 调用Modbus向设备发送指令
|
// 根据设备类型和请求状态确定控制命令
|
||||||
|
PcsControlCommand command = PcsControlCommand.fromDeviceStatus(device.getDeviceStatus());
|
||||||
|
if (command == null) {
|
||||||
|
throw new ServiceException("不支持的设备状态操作");
|
||||||
|
}
|
||||||
|
// 调用Modbus向设备发送指令
|
||||||
|
DeviceConfig deviceConfig = getDeviceConfig(device);
|
||||||
|
deviceConfig.setWriteTags(getWriteTags(device, pointMatchList, command));
|
||||||
|
log.info("设备控制指令发送数据: {}", JSON.toJSONString(deviceConfig));
|
||||||
|
boolean result = modbusProcessor.writeDataToDevice(deviceConfig);
|
||||||
|
if (!result) {
|
||||||
|
throw new ServiceException("设备控制指令发送失败");
|
||||||
|
}
|
||||||
device.setUpdatedAt(DateUtils.getNowDate());
|
device.setUpdatedAt(DateUtils.getNowDate());
|
||||||
emsDevicesMapper.updateEmsDevicesSetting(device);
|
emsDevicesMapper.updateEmsDevicesSetting(device);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeviceConfig getDeviceConfig(EmsDevicesSetting device) {
|
||||||
|
DeviceConfig deviceConfig = new DeviceConfig();
|
||||||
|
deviceConfig.setDeviceNumber(device.getDeviceId());
|
||||||
|
deviceConfig.setDeviceName(device.getDeviceName());
|
||||||
|
deviceConfig.setSlaveId(device.getSlaveId().intValue());
|
||||||
|
if (DeviceType.TCP.name().equals(device.getDeviceType())) {
|
||||||
|
deviceConfig.setHost(device.getIpAddress());
|
||||||
|
deviceConfig.setPort(device.getIpPort().intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WriteTagConfig> getWriteTags(EmsDevicesSetting device, List<EmsPointMatch> pointMatchList, PcsControlCommand command) {
|
||||||
|
List<WriteTagConfig> writeTags = new ArrayList<>();
|
||||||
|
for (EmsPointMatch pointMatch : pointMatchList) {
|
||||||
|
WriteTagConfig writeTag = new WriteTagConfig();
|
||||||
|
writeTag.setAddress(pointMatch.getIpAddress());
|
||||||
|
if (DEVICE_STATUS_FIELD.equals(pointMatch.getMatchField())) {
|
||||||
|
writeTag.setValue(command.getCode());
|
||||||
|
} else {
|
||||||
|
// 电池簇PCS有功功率给定置0
|
||||||
|
writeTag.setValue(0);
|
||||||
|
}
|
||||||
|
writeTags.add(writeTag);
|
||||||
|
}
|
||||||
|
return writeTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,11 +23,12 @@
|
|||||||
<result property="deviceId" column="device_id" />
|
<result property="deviceId" column="device_id" />
|
||||||
<result property="clusterDeviceId" column="cluster_device_id" />
|
<result property="clusterDeviceId" column="cluster_device_id" />
|
||||||
<result property="interResistance" column="inter_resistance" />
|
<result property="interResistance" column="inter_resistance" />
|
||||||
|
<result property="current" column="current" />
|
||||||
<result property="dayTime" column="day_time" />
|
<result property="dayTime" column="day_time" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectEmsBatteryDataDayVo">
|
<sql id="selectEmsBatteryDataDayVo">
|
||||||
select id, battery_pack, battery_cluster, battery_cell_id, voltage, temperature, soc, soh, data_timestamp, create_by, create_time, update_by, update_time, remark, site_id, device_id, cluster_device_id, inter_resistance, day_time from ems_battery_data_day
|
select id, battery_pack, battery_cluster, battery_cell_id, voltage, temperature, soc, soh, data_timestamp, create_by, create_time, update_by, update_time, remark, site_id, device_id, cluster_device_id, inter_resistance, current, day_time from ems_battery_data_day
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectEmsBatteryDataDayList" parameterType="EmsBatteryDataDay" resultMap="EmsBatteryDataDayResult">
|
<select id="selectEmsBatteryDataDayList" parameterType="EmsBatteryDataDay" resultMap="EmsBatteryDataDayResult">
|
||||||
@ -45,6 +46,7 @@
|
|||||||
<if test="deviceId != null and deviceId != ''"> and device_id = #{deviceId}</if>
|
<if test="deviceId != null and deviceId != ''"> and device_id = #{deviceId}</if>
|
||||||
<if test="clusterDeviceId != null and clusterDeviceId != ''"> and cluster_device_id = #{clusterDeviceId}</if>
|
<if test="clusterDeviceId != null and clusterDeviceId != ''"> and cluster_device_id = #{clusterDeviceId}</if>
|
||||||
<if test="interResistance != null "> and inter_resistance = #{interResistance}</if>
|
<if test="interResistance != null "> and inter_resistance = #{interResistance}</if>
|
||||||
|
<if test="current != null "> and current = #{current}</if>
|
||||||
<if test="dayTime != null "> and day_time = #{dayTime}</if>
|
<if test="dayTime != null "> and day_time = #{dayTime}</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
@ -74,6 +76,7 @@
|
|||||||
<if test="deviceId != null">device_id,</if>
|
<if test="deviceId != null">device_id,</if>
|
||||||
<if test="clusterDeviceId != null">cluster_device_id,</if>
|
<if test="clusterDeviceId != null">cluster_device_id,</if>
|
||||||
<if test="interResistance != null">inter_resistance,</if>
|
<if test="interResistance != null">inter_resistance,</if>
|
||||||
|
<if test="current != null">current,</if>
|
||||||
<if test="dayTime != null">day_time,</if>
|
<if test="dayTime != null">day_time,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
@ -118,6 +121,7 @@
|
|||||||
<if test="deviceId != null">device_id = #{deviceId},</if>
|
<if test="deviceId != null">device_id = #{deviceId},</if>
|
||||||
<if test="clusterDeviceId != null">cluster_device_id = #{clusterDeviceId},</if>
|
<if test="clusterDeviceId != null">cluster_device_id = #{clusterDeviceId},</if>
|
||||||
<if test="interResistance != null">inter_resistance = #{interResistance},</if>
|
<if test="interResistance != null">inter_resistance = #{interResistance},</if>
|
||||||
|
<if test="current != null">current = #{current},</if>
|
||||||
<if test="dayTime != null">day_time = #{dayTime},</if>
|
<if test="dayTime != null">day_time = #{dayTime},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
@ -164,6 +168,7 @@
|
|||||||
device_id,
|
device_id,
|
||||||
cluster_device_id,
|
cluster_device_id,
|
||||||
inter_resistance,
|
inter_resistance,
|
||||||
|
current,
|
||||||
day_time
|
day_time
|
||||||
) VALUES
|
) VALUES
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
@ -184,6 +189,7 @@
|
|||||||
#{item.deviceId},
|
#{item.deviceId},
|
||||||
#{item.clusterDeviceId},
|
#{item.clusterDeviceId},
|
||||||
#{item.interResistance},
|
#{item.interResistance},
|
||||||
|
#{item.current},
|
||||||
#{item.dayTime}
|
#{item.dayTime}
|
||||||
)
|
)
|
||||||
</foreach>
|
</foreach>
|
||||||
@ -195,6 +201,7 @@
|
|||||||
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
|
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
|
||||||
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
|
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
|
||||||
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance),
|
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance),
|
||||||
|
current = IF(VALUES(temperature) > temperature, VALUES(current), current),
|
||||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by),
|
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by),
|
||||||
temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
|
temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
|
||||||
</insert>
|
</insert>
|
||||||
|
|||||||
@ -64,10 +64,22 @@
|
|||||||
<result property="module2Temp" column="module2_temp" />
|
<result property="module2Temp" column="module2_temp" />
|
||||||
<result property="module3Temp" column="module3_temp" />
|
<result property="module3Temp" column="module3_temp" />
|
||||||
<result property="module4Temp" column="module4_temp" />
|
<result property="module4Temp" column="module4_temp" />
|
||||||
|
<result property="cluster1ActivePower" column="cluster1_active_power" />
|
||||||
|
<result property="cluster2ActivePower" column="cluster2_active_power" />
|
||||||
|
<result property="cluster3ActivePower" column="cluster3_active_power" />
|
||||||
|
<result property="cluster4ActivePower" column="cluster4_active_power" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectEmsPcsDataVo">
|
<sql id="selectEmsPcsDataVo">
|
||||||
select id, data_update_time, work_status, grid_status, device_status, control_mode, total_active_power, daily_ac_charge_energy, a_phase_voltage, a_phase_current, total_reactive_power, daily_ac_discharge_energy, b_phase_voltage, b_phase_current, total_apparent_power, pcs_module_temperature, c_phase_voltage, c_phase_current, total_power_factor, pcs_environment_temperature, ac_frequency, branch_status, discharge_status, dc_power, dc_voltage, dc_current, create_by, create_time, update_by, update_time, remark, site_id, device_id, date_month, date_day, total_ac_charge_energy, total_ac_discharge_energy, ac_charge_active_power, ac_capacitive_reactive_power, ac_discharge_active_power, ac_inductive_reactive_power, max_capacitive_power_capacity, max_inductive_power_capacity, max_charge_power_capacity, max_discharge_power_capacity, ac_switch_status, dc_switch_status, remote_control_status, sys_u_current, sys_v_current, sys_w_current, dw_frequency, u_temperature, v_temperature, w_temperature, module1_temp, module2_temp, module3_temp, module4_temp from ems_pcs_data
|
select id, data_update_time, work_status, grid_status, device_status, control_mode, total_active_power, daily_ac_charge_energy,
|
||||||
|
a_phase_voltage, a_phase_current, total_reactive_power, daily_ac_discharge_energy, b_phase_voltage, b_phase_current,
|
||||||
|
total_apparent_power, pcs_module_temperature, c_phase_voltage, c_phase_current, total_power_factor, pcs_environment_temperature,
|
||||||
|
ac_frequency, branch_status, discharge_status, dc_power, dc_voltage, dc_current, create_by, create_time, update_by, update_time,
|
||||||
|
remark, site_id, device_id, date_month, date_day, total_ac_charge_energy, total_ac_discharge_energy, ac_charge_active_power,
|
||||||
|
ac_capacitive_reactive_power, ac_discharge_active_power, ac_inductive_reactive_power, max_capacitive_power_capacity,
|
||||||
|
max_inductive_power_capacity, max_charge_power_capacity, max_discharge_power_capacity, ac_switch_status, dc_switch_status,
|
||||||
|
remote_control_status, sys_u_current, sys_v_current, sys_w_current, dw_frequency, u_temperature, v_temperature, w_temperature,
|
||||||
|
module1_temp, module2_temp, module3_temp, module4_temp, cluster1_active_power, cluster2_active_power, cluster3_active_power, cluster4_active_power from ems_pcs_data
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectEmsPcsDataList" parameterType="EmsPcsData" resultMap="EmsPcsDataResult">
|
<select id="selectEmsPcsDataList" parameterType="EmsPcsData" resultMap="EmsPcsDataResult">
|
||||||
@ -126,6 +138,10 @@
|
|||||||
<if test="module2Temp != null "> and module2_temp = #{module2Temp}</if>
|
<if test="module2Temp != null "> and module2_temp = #{module2Temp}</if>
|
||||||
<if test="module3Temp != null "> and module3_temp = #{module3Temp}</if>
|
<if test="module3Temp != null "> and module3_temp = #{module3Temp}</if>
|
||||||
<if test="module4Temp != null "> and module4_temp = #{module4Temp}</if>
|
<if test="module4Temp != null "> and module4_temp = #{module4Temp}</if>
|
||||||
|
<if test="cluster1ActivePower != null "> and cluster1_active_power = #{cluster1ActivePower}</if>
|
||||||
|
<if test="cluster2ActivePower != null "> and cluster2_active_power = #{cluster2ActivePower}</if>
|
||||||
|
<if test="cluster3ActivePower != null "> and cluster3_active_power = #{cluster3ActivePower}</if>
|
||||||
|
<if test="cluster4ActivePower != null "> and cluster4_active_power = #{cluster4ActivePower}</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@ -195,6 +211,10 @@
|
|||||||
<if test="module2Temp != null">module2_temp,</if>
|
<if test="module2Temp != null">module2_temp,</if>
|
||||||
<if test="module3Temp != null">module3_temp,</if>
|
<if test="module3Temp != null">module3_temp,</if>
|
||||||
<if test="module4Temp != null">module4_temp,</if>
|
<if test="module4Temp != null">module4_temp,</if>
|
||||||
|
<if test="cluster1ActivePower != null">cluster1_active_power,</if>
|
||||||
|
<if test="cluster2ActivePower != null">cluster2_active_power,</if>
|
||||||
|
<if test="cluster3ActivePower != null">cluster3_active_power,</if>
|
||||||
|
<if test="cluster4ActivePower != null">cluster4_active_power,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="dataUpdateTime != null">#{dataUpdateTime},</if>
|
<if test="dataUpdateTime != null">#{dataUpdateTime},</if>
|
||||||
@ -255,6 +275,10 @@
|
|||||||
<if test="module2Temp != null">#{module2Temp},</if>
|
<if test="module2Temp != null">#{module2Temp},</if>
|
||||||
<if test="module3Temp != null">#{module3Temp},</if>
|
<if test="module3Temp != null">#{module3Temp},</if>
|
||||||
<if test="module4Temp != null">#{module4Temp},</if>
|
<if test="module4Temp != null">#{module4Temp},</if>
|
||||||
|
<if test="cluster1ActivePower != null">#{cluster1ActivePower},</if>
|
||||||
|
<if test="cluster2ActivePower != null">#{cluster2ActivePower},</if>
|
||||||
|
<if test="cluster3ActivePower != null">#{cluster3ActivePower},</if>
|
||||||
|
<if test="cluster4ActivePower != null">#{cluster4ActivePower},</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
@ -319,6 +343,10 @@
|
|||||||
<if test="module2Temp != null">module2_temp = #{module2Temp},</if>
|
<if test="module2Temp != null">module2_temp = #{module2Temp},</if>
|
||||||
<if test="module3Temp != null">module3_temp = #{module3Temp},</if>
|
<if test="module3Temp != null">module3_temp = #{module3Temp},</if>
|
||||||
<if test="module4Temp != null">module4_temp = #{module4Temp},</if>
|
<if test="module4Temp != null">module4_temp = #{module4Temp},</if>
|
||||||
|
<if test="cluster1ActivePower != null">cluster1_active_power = #{cluster1ActivePower},</if>
|
||||||
|
<if test="cluster2ActivePower != null">cluster2_active_power = #{cluster2ActivePower},</if>
|
||||||
|
<if test="cluster3ActivePower != null">cluster3_active_power = #{cluster3ActivePower},</if>
|
||||||
|
<if test="cluster4ActivePower != null">cluster4_active_power = #{cluster4ActivePower},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|||||||
@ -541,12 +541,15 @@
|
|||||||
and device_id = #{deviceId}
|
and device_id = #{deviceId}
|
||||||
and device_category = #{deviceCategory}
|
and device_category = #{deviceCategory}
|
||||||
</select>
|
</select>
|
||||||
<select id="selectDeviceStatusPoint" resultType="EmsPointMatch" parameterType="com.xzzn.ems.domain.vo.DeviceUpdateRequest">
|
<select id="selectDeviceStatusPoint" parameterType="com.xzzn.ems.domain.vo.DeviceUpdateRequest" resultMap="EmsPointMatchResult" >
|
||||||
<include refid="selectEmsPointMatchVo"/>
|
<include refid="selectEmsPointMatchVo"/>
|
||||||
where site_id = #{request.siteId}
|
where site_id = #{request.siteId}
|
||||||
and device_id = #{request.deviceId}
|
and device_id = #{request.deviceId}
|
||||||
and device_category = #{request.deviceCategory}
|
and device_category = #{request.deviceCategory}
|
||||||
and match_field = 'device_status'
|
and match_field in
|
||||||
|
<foreach collection="list" item="matchField" open="(" separator="," close=")">
|
||||||
|
#{matchField}
|
||||||
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
6
pom.xml
6
pom.xml
@ -171,6 +171,12 @@
|
|||||||
<artifactId>j2mod</artifactId>
|
<artifactId>j2mod</artifactId>
|
||||||
<version>3.1.0</version> <!-- 提供TCPMasterConnection、SerialConnection等类 -->
|
<version>3.1.0</version> <!-- 提供TCPMasterConnection、SerialConnection等类 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- modbus4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.infiniteautomation</groupId>
|
||||||
|
<artifactId>modbus4j</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
<!-- Resilience4j 断路器核心 -->
|
<!-- Resilience4j 断路器核心 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.resilience4j</groupId>
|
<groupId>io.github.resilience4j</groupId>
|
||||||
|
|||||||
Reference in New Issue
Block a user