Commit 4162d6a0 authored by wanghao's avatar wanghao

1 quartz 实现 通电以后 71个小时后断电,以及 中间 多次通信获取数据

parent 2a70f9ff
...@@ -271,7 +271,7 @@ public class EquipmentDataCollection { ...@@ -271,7 +271,7 @@ public class EquipmentDataCollection {
int num = 1; int num = 1;
try { try {
ip = storey.getfIp(); ip = storey.getfIp();
port = storey.getfPort(); port = Integer.parseInt(storey.getfPort()); // 使用 parseInt 确保字符串到整数的转换
if (ip == null || port == null) { if (ip == null || port == null) {
receiveMsg = storey.getfStoreyCode(); receiveMsg = storey.getfStoreyCode();
} }
......
...@@ -13,6 +13,7 @@ import com.zehong.system.service.ITEquipmentAlarmDataService; ...@@ -13,6 +13,7 @@ import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITEquipmentInfoService; import com.zehong.system.service.ITEquipmentInfoService;
import com.zehong.system.service.ITStoreyInfoService; import com.zehong.system.service.ITStoreyInfoService;
import com.zehong.web.task.AgingCabinetInspectionAndPowerCheckTask; import com.zehong.web.task.AgingCabinetInspectionAndPowerCheckTask;
import com.zehong.web.task.CheckPowerOnCommandEvent;
import com.zehong.web.task.PowerOffCommandEvent; import com.zehong.web.task.PowerOffCommandEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -49,6 +50,25 @@ public class TestTaskController { ...@@ -49,6 +50,25 @@ public class TestTaskController {
@Resource @Resource
private ApplicationEventPublisher eventPublisher; // 新增事件发布器 private ApplicationEventPublisher eventPublisher; // 新增事件发布器
/**
* 断电
*/
@GetMapping("/testPowerOutage")
public void powerOutage() {
// 10 层
List<Integer> registerOffsets = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
ModbusMaster master;
try {
master = Modbus4jUtils.getMaster("192.168.1.102", 504);
for (Integer registerOffset : registerOffsets) {
Boolean aBoolean = Modbus4jUtils.writeCoil(master, 1,registerOffset,false );
}
} catch (ModbusInitException | ModbusTransportException e) {
throw new RuntimeException(e);
}
}
/** /**
* 五分钟一次 * 五分钟一次
* 1.老化柜、标定柜巡查 * 1.老化柜、标定柜巡查
...@@ -182,6 +202,8 @@ public class TestTaskController { ...@@ -182,6 +202,8 @@ public class TestTaskController {
deviceData.setDeviceCode(equipmentInfo.getfEquipmentCode()); deviceData.setDeviceCode(equipmentInfo.getfEquipmentCode());
deviceData.setDeviceStatus(status); deviceData.setDeviceStatus(status);
deviceData.setErrorReason(errorReason); deviceData.setErrorReason(errorReason);
deviceData.setfPowerOutageIp(equipmentInfo.getfPowerOutageIp());
deviceData.setfPowerOutagePort(equipmentInfo.getfPowerOutagePort());
if (integerObjectMap != null) { if (integerObjectMap != null) {
deviceData.setRegisterValues(integerObjectMap.entrySet().stream() deviceData.setRegisterValues(integerObjectMap.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
...@@ -198,12 +220,19 @@ public class TestTaskController { ...@@ -198,12 +220,19 @@ public class TestTaskController {
for (ModbusDeviceData modbusDeviceData : deviceDataList) { for (ModbusDeviceData modbusDeviceData : deviceDataList) {
Map<Integer, String> registerValues = modbusDeviceData.getRegisterValues(); Map<Integer, String> registerValues = modbusDeviceData.getRegisterValues();
// 大于0 说明 柜能通信;小于0 说明柜 不能通信 // 大于0 说明 柜能通信;小于0 说明柜 不能通信
if(registerValues.size() > 0) { if (registerValues.size() > 0) {
boolean isRun = false; boolean isRun = false;
for (Map.Entry<Integer, String> entry : registerValues.entrySet()) { for (Map.Entry<Integer, String> entry : registerValues.entrySet()) {
Integer registerOffset = entry.getKey(); Integer registerOffset = entry.getKey();
String registerValue = entry.getValue(); String registerValue = entry.getValue();
if ("true".equals(registerValue)) { if ("true".equals(registerValue)) {
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
modbusDeviceData.getDeviceCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset
));
isRun = true; isRun = true;
// 要给这个 层 发断电的 指令 // 要给这个 层 发断电的 指令
} else { } else {
...@@ -211,8 +240,8 @@ public class TestTaskController { ...@@ -211,8 +240,8 @@ public class TestTaskController {
eventPublisher.publishEvent(new PowerOffCommandEvent( eventPublisher.publishEvent(new PowerOffCommandEvent(
this, this,
modbusDeviceData.getDeviceCode(), modbusDeviceData.getDeviceCode(),
modbusDeviceData.getfIp(), modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPort(), modbusDeviceData.getfPowerOutagePort(),
registerOffset registerOffset
)); ));
} }
...@@ -222,6 +251,8 @@ public class TestTaskController { ...@@ -222,6 +251,8 @@ public class TestTaskController {
} else { } else {
modbusDeviceData.setDeviceStatus("0"); modbusDeviceData.setDeviceStatus("0");
} }
} else {
modbusDeviceData.setDeviceStatus("0");
} }
} }
return deviceDataList; return deviceDataList;
......
package com.zehong.web.task; package com.zehong.web.task;
import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.framework.modbus4j.Modbus4jUtils; import com.zehong.framework.modbus4j.Modbus4jUtils;
...@@ -8,16 +9,18 @@ import com.zehong.system.domain.TEquipmentAlarmData; ...@@ -8,16 +9,18 @@ import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo; import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.service.ITEquipmentAlarmDataService; import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService; import com.zehong.system.service.ITStoreyInfoService;
import org.quartz.SchedulerException;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Date;
/** /**
* @author lenovo * @author lenovo
* @date 2025/6/17 * @date 2025/6/17
* @description TODO * @description 事件接收器
*/ */
@Component @Component
public class AllCommandHandler { public class AllCommandHandler {
...@@ -28,11 +31,59 @@ public class AllCommandHandler { ...@@ -28,11 +31,59 @@ public class AllCommandHandler {
@Resource @Resource
private ITStoreyInfoService tStoreyInfoService; private ITStoreyInfoService tStoreyInfoService;
@Resource
private DeviceTaskScheduler deviceTaskScheduler;
/**
* check是否启动 ,没启动就启动下 开始老化
* @param event event
*/
@Async
@EventListener(CheckPowerOnCommandEvent.class)
public void handleCheckPowerOnCommand(CheckPowerOnCommandEvent event) {
String fip = event.getIp();
int fport = event.getPort();
int registerOffset = event.getLayer();
String storeyCode = event.getDeviceCode() + "-" + (event.getLayer()+1);
log.info("需要发送上电指令 - 设备:{} 层:{} ip:{} 端口号:{}", event.getDeviceCode(), event.getLayer(), fip, fport);
ModbusMaster master;
try {
master = Modbus4jUtils.getMaster(fip, fport);
Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffset);
log.info("当前 - 设备:{} 层:{} -的 状态是:{}", event.getDeviceCode(), event.getLayer(),aBoolean);
if(!aBoolean) {
Modbus4jUtils.writeCoil(master, 1, registerOffset, true);
TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode);
tStoreyInfo.setfStatus("1");
tStoreyInfo.setfAgingStartTime(new Date());
tStoreyInfoService.updateTStoreyInfo(tStoreyInfo);
deviceTaskScheduler.scheduleDeviceMonitoring(tStoreyInfo.getfStoreyId(),fip,fport);
}
} catch (ModbusInitException | ModbusTransportException | ErrorResponseException | SchedulerException e) {
// 记录异常
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
alarmData.setfAlarmType("03"); // 老化层
alarmData.setfEquipmentCode(event.getDeviceCode());
alarmData.setfAlarmData("上电或者是启动任务检测时异常: " + e.getMessage());
alarmDataService.insertTEquipmentAlarmData(alarmData);
}
}
@Async // 异步执行 @Async // 异步执行
@EventListener(PowerOffCommandEvent.class) @EventListener(PowerOffCommandEvent.class)
public void handlePowerOffCommand(PowerOffCommandEvent event) { public void handlePowerOffCommand(PowerOffCommandEvent event) {
try { try {
String storeyCode = event.getDeviceCode() + "-" + event.getLayer(); String storeyCode = event.getDeviceCode() + "-" + event.getLayer();
String ip = event.getIp();
int port = event.getPort();
log.info("需要发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer()); log.info("需要发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer());
TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode); TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode);
if (tStoreyInfo == null) { if (tStoreyInfo == null) {
...@@ -43,10 +94,11 @@ public class AllCommandHandler { ...@@ -43,10 +94,11 @@ public class AllCommandHandler {
alarmData.setfAlarmData("下属" + storeyCode + "号老化层不存在"); alarmData.setfAlarmData("下属" + storeyCode + "号老化层不存在");
alarmDataService.insertTEquipmentAlarmData(alarmData); alarmDataService.insertTEquipmentAlarmData(alarmData);
} else { } else {
ModbusMaster master = Modbus4jUtils.getMaster(tStoreyInfo.getfIp(), tStoreyInfo.getfPort()); //
Modbus4jUtils.writeCoil(master, 1, event.getLayer(), false); ModbusMaster master = Modbus4jUtils.getMaster(ip, port);
log.info("已发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer()); Modbus4jUtils.writeCoil(master, 1, event.getLayer(), false);
master.destroy(); log.info("已发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer());
master.destroy();
} }
} catch (ModbusInitException | ModbusTransportException e) { } catch (ModbusInitException | ModbusTransportException e) {
log.error("断电指令执行失败 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer(), e); log.error("断电指令执行失败 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer(), e);
......
package com.zehong.web.task;
import org.springframework.context.ApplicationEvent;
/**
* @author lenovo
* @date 2025/6/25
* @description TODO
*/
public class CheckPowerOnCommandEvent extends ApplicationEvent {
private final String ip;
private final int port;
private final int layer;
private final String deviceCode;
public CheckPowerOnCommandEvent(Object source, String deviceCode, String ip, int port, int layer) {
super(source);
this.deviceCode = deviceCode;
this.ip = ip;
this.port = port;
this.layer = layer;
}
// Getters
public String getIp() { return ip; }
public int getPort() { return port; }
public int getLayer() { return layer; }
public String getDeviceCode() { return deviceCode; }
}
package com.zehong.web.task;
import com.serotonin.modbus4j.ModbusMaster;
import com.zehong.framework.modbus4j.Modbus4jUtils;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author lenovo
* @date 2025/6/25
* @description TODO
*/
@Component
public class DeviceCommunicationJob implements Job {
private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);
@Resource
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private ITStoreyInfoService tStoreyInfoService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyId = data.getString("fStoreyId");
log.info("DeviceCommunicationJob.execute 的 fStoreyId:{}", fStoreyId);
long l = Long.parseLong(fStoreyId);
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(l);
try {
// 1. 执行Modbus通信
String s = tStoreyInfo.getfPort();
String ip = tStoreyInfo.getfIp();
// 三个端口号,对应三组Modbus通信
String[] split = s.split(",");
List<Integer> registerOffsetsOne = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26);
log.info("registerOffsetsOne.ip:{}, port{}", ip, Integer.parseInt(split[0]));
Map<Integer, Object> integerObjectMapOne = Modbus4jUtils.batchReadAgingCabinetStatus(Modbus4jUtils.getMaster(ip, Integer.parseInt(split[0])), registerOffsetsOne);
for (Map.Entry<Integer, Object> integerObjectEntry : integerObjectMapOne.entrySet()) {
log.info("integerObjectMapOne 的 key是:{} value: {}", integerObjectEntry.getKey(), integerObjectEntry.getValue());
}
List<Integer> registerOffsetsTwo = Arrays.asList(27,28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53);
log.info("integerObjectMapTwo.ip:{}, port{}", ip, Integer.parseInt(split[1]));
Map<Integer, Object> integerObjectMapTwo = Modbus4jUtils.batchReadAgingCabinetStatus(Modbus4jUtils.getMaster(ip, Integer.parseInt(split[1])), registerOffsetsTwo);
for (Map.Entry<Integer, Object> integerObjectEntry : integerObjectMapTwo.entrySet()) {
log.info("integerObjectMapTwo 的 key是:{} value: {}", integerObjectEntry.getKey(), integerObjectEntry.getValue());
}
List<Integer> registerOffsetsThree = Arrays.asList(54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71);
Map<Integer, Object> integerObjectMapThree = Modbus4jUtils.batchReadAgingCabinetStatus(Modbus4jUtils.getMaster(ip, Integer.parseInt(split[2])), registerOffsetsThree);
log.info("integerObjectMapThree.ip:{}, port{}", ip, Integer.parseInt(split[2]));
for (Map.Entry<Integer, Object> integerObjectEntry : integerObjectMapThree.entrySet()) {
log.info("integerObjectMapThree 的 key是:{} value: {}", integerObjectEntry.getKey(), integerObjectEntry.getValue());
}
// 2. 检查是否到达特殊时间点
//checkSpecialTimePoints(tStoreyInfo);
} catch (Exception e) {
// 记录异常
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
alarmData.setfAlarmType("03"); // 老化层
alarmData.setfEquipmentCode(tStoreyInfo.getfStoreyCode());
alarmData.setfAlarmData("通信失败: " + e.getMessage());
alarmDataService.insertTEquipmentAlarmData(alarmData);
}
}
private void checkSpecialTimePoints(TStoreyInfo tStoreyInfo) {
if (tStoreyInfo == null || "COMPLETED".equals(tStoreyInfo.getfStatus())) return;
// 计算已运行小时数
long hours = Duration.between(
tStoreyInfo.getfAgingStartTime().toInstant(), Instant.now()).toHours();
// 特殊时间点处理 (24小时和48小时)
if (hours == 24 || hours == 48) {
log.info("设备[{}]到达特殊时间点: {}小时", tStoreyInfo.getfStoreyCode(), hours);
// 执行特殊操作
performSpecialOperation(tStoreyInfo);
}
}
// ... 其他辅助方法
private void performSpecialOperation(TStoreyInfo tStoreyInfo) {
}
}
package com.zehong.web.task;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
/**
* @author lenovo
* @date 2025/6/25
* @description 定时调度任务
*/
@Service
public class DeviceTaskScheduler {
private static final Logger log = LoggerFactory.getLogger(DeviceTaskScheduler.class);
@Autowired
private Scheduler scheduler;
/**
* 创建设备监控任务
* @param fStoreyId 设备ID
*/
public void scheduleDeviceMonitoring(Long fStoreyId,String fPowerOutageIp,Integer fPowerOutagePort) throws SchedulerException {
// 2. 创建每小时通信任务
createHourlyCommunicationJob(fStoreyId);
// 3. 创建71小时后执行任务
createFinalExecutionJob(fStoreyId,fPowerOutageIp,fPowerOutagePort);
}
/**
* 创建每小时通信任务
*/
private void createHourlyCommunicationJob(Long fStoreyId) throws SchedulerException {
// 任务标识 = 设备ID + 任务类型
String jobId = "COMM_" + fStoreyId;
String fStoreyIdStr = fStoreyId + "";
JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
.withIdentity(jobId, "DEVICE_TASKS")
.usingJobData("fStoreyId", fStoreyIdStr)
.storeDurably()
.build();
// 测试每两分钟 读一次数据
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobId + "_TRIGGER", "DEVICE_TRIGGERS")
// .withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")) 每小时执行一次 (0分0秒执行)
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?"))
.build();
scheduler.scheduleJob(job, trigger);
}
/**
* 创建71小时后执行任务
*/
private void createFinalExecutionJob(Long fStoreyId,String fPowerOutageIp,Integer fPowerOutagePort) throws SchedulerException {
String jobId = "FINAL_" + fStoreyId;
String fStoreyIdStr = fStoreyId.toString();
String fPowerOutagePortStr = fPowerOutagePort.toString();
JobDetail job = JobBuilder.newJob(FinalExecutionJob.class)
.withIdentity(jobId, "DEVICE_TASKS")
.usingJobData("fStoreyId", fStoreyIdStr)
.usingJobData("fPowerOutageIp", fPowerOutageIp)
.usingJobData("fPowerOutagePort", fPowerOutagePortStr)
.storeDurably()
.build();
// 计算71小时后的时间点
// Date executeTime = Date.from(Instant.now().plus(71, ChronoUnit.HOURS));
// 测试 先用 10分钟
Date executeTime = Date.from(Instant.now().plus(10, ChronoUnit.MINUTES));
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobId + "_TRIGGER", "DEVICE_TRIGGERS")
.startAt(executeTime)
.build();
scheduler.scheduleJob(job, trigger);
}
}
package com.zehong.web.task;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.common.utils.StringUtils;
import com.zehong.framework.modbus4j.Modbus4jUtils;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* @author lenovo
* @date 2025/6/25
* @description TODO
*/
@Component
public class FinalExecutionJob implements Job {
private static final Logger log = LoggerFactory.getLogger(FinalExecutionJob.class);
@Resource
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private ITStoreyInfoService tStoreyInfoService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Autowired
private Scheduler scheduler;
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyId = data.getString("fStoreyId");
String fPowerOutageIp = data.getString("fPowerOutageIp");
String fPowerOutagePort = data.getString("fPowerOutagePort");
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(Long.parseLong(fStoreyId));
if(tStoreyInfo == null || StringUtils.isBlank(fPowerOutagePort) || StringUtils.isBlank(fPowerOutageIp)) {
// 记录异常
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
alarmData.setfAlarmType("03"); // 老化层
alarmData.setfEquipmentCode(fStoreyId);
alarmData.setfAlarmData("71小时到老化层业务逻辑时数据丢失");
alarmDataService.insertTEquipmentAlarmData(alarmData);
} else{
try {
log.info("设备[{}]开始执行最终业务逻辑", tStoreyInfo.getfStoreyCode());
// 1. 执行业务逻辑
String s = tStoreyInfo.getfStoreyCode().split("-")[1];
int i = Integer.parseInt(s);
executeBusinessLogic(fPowerOutageIp,Integer.parseInt(fPowerOutagePort),(i - 1));
// 2. 更新任务状态
tStoreyInfo.setfStatus("0");
tStoreyInfo.setfAgingStartTime(null);
tStoreyInfoService.updateTStoreyInfo(tStoreyInfo);
// 3. 清理相关任务
cleanUpJobs(fStoreyId,context);
log.info("设备[{}]最终业务逻辑执行完成", fStoreyId);
} catch (Exception e) {
// 记录异常
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
alarmData.setfAlarmType("03"); // 老化层
alarmData.setfEquipmentCode(tStoreyInfo.getfStoreyCode());
alarmData.setfAlarmData("老化层清除任务时失败");
alarmDataService.insertTEquipmentAlarmData(alarmData);
}
}
}
private void cleanUpJobs(String deviceId,JobExecutionContext context) throws SchedulerException {
// 删除通信任务
scheduler.deleteJob(new JobKey("COMM_" + deviceId, "DEVICE_TASKS"));
// 删除自身任务
scheduler.deleteJob(context.getJobDetail().getKey());
}
// ... 业务逻辑方法
private void executeBusinessLogic(String fPowerOutageIp, int fPowerOutagePort,int registerOffsets) {
// 10 层
ModbusMaster master;
try {
master = Modbus4jUtils.getMaster(fPowerOutageIp, fPowerOutagePort);
Boolean aBoolean = Modbus4jUtils.writeCoil(master, 1,registerOffsets,false );
} catch (ModbusInitException | ModbusTransportException e) {
throw new RuntimeException(e);
}
}
}
...@@ -96,6 +96,24 @@ public class Modbus4jUtils { ...@@ -96,6 +96,24 @@ public class Modbus4jUtils {
return value; return value;
} }
/**
* 读一下 开关数据
* @param master
* @param slaveId
* @param offset
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static Boolean readCoilStatus(ModbusMaster master, int slaveId, int offset)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 01 Coil Status
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
Boolean value = master.getValue(loc);
return value;
}
/** /**
* 读取[02 Input Status 1x]类型 开关数据 * 读取[02 Input Status 1x]类型 开关数据
* *
......
package com.zehong.quartz.config;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author lenovo
* @date 2025/6/25
* @description TODO
*/
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
@Override
public void setApplicationContext(ApplicationContext ac) {
context = ac;
}
}
\ No newline at end of file
package com.zehong.quartz.config; package com.zehong.quartz.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import javax.annotation.Resource;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.Properties; import java.util.Properties;
...@@ -14,10 +19,19 @@ import java.util.Properties; ...@@ -14,10 +19,19 @@ import java.util.Properties;
@Configuration @Configuration
public class ScheduleConfig public class ScheduleConfig
{ {
@Autowired
private ApplicationContext applicationContext;
@Bean
public SpringBeanJobFactory springBeanJobFactory() {
SpringQuartzJobFactory jobFactory = new SpringQuartzJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
@Bean @Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
{ {
SchedulerFactoryBean factory = new SchedulerFactoryBean(); SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(springBeanJobFactory());
factory.setDataSource(dataSource); factory.setDataSource(dataSource);
// quartz参数 // quartz参数
......
package com.zehong.quartz.config;
import org.quartz.Job;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;
/**
* @author lenovo
* @date 2025/6/25
* @description TODO
*/
@Component
public class SpringQuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job); // 进行依赖注入
return job;
}
}
\ No newline at end of file
...@@ -82,6 +82,13 @@ public class TEquipmentInfo extends BaseEntity ...@@ -82,6 +82,13 @@ public class TEquipmentInfo extends BaseEntity
@Excel(name = "报警时间") @Excel(name = "报警时间")
private String fAlarmTime; private String fAlarmTime;
/** 断电层IP地址 */
private String fPowerOutageIp;
/** 断电层端口号 */
private Integer fPowerOutagePort;
public void setfEquipmentId(Long fEquipmentId) public void setfEquipmentId(Long fEquipmentId)
{ {
this.fEquipmentId = fEquipmentId; this.fEquipmentId = fEquipmentId;
...@@ -227,6 +234,22 @@ public class TEquipmentInfo extends BaseEntity ...@@ -227,6 +234,22 @@ public class TEquipmentInfo extends BaseEntity
return fAlarmTime; return fAlarmTime;
} }
public String getfPowerOutageIp() {
return fPowerOutageIp;
}
public void setfPowerOutageIp(String fPowerOutageIp) {
this.fPowerOutageIp = fPowerOutageIp;
}
public Integer getfPowerOutagePort() {
return fPowerOutagePort;
}
public void setfPowerOutagePort(Integer fPowerOutagePort) {
this.fPowerOutagePort = fPowerOutagePort;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
...@@ -43,7 +43,13 @@ public class TStoreyInfo extends BaseEntity ...@@ -43,7 +43,13 @@ public class TStoreyInfo extends BaseEntity
@Excel(name = "IP地址") @Excel(name = "IP地址")
private String fIp; private String fIp;
/** 端口号 */ /** 端口号
* 0 位置端口号管
* 1 位置端口号管
* 2 位置端口号管
* 3 位置端口号管断电
*
* */
@Excel(name = "端口号") @Excel(name = "端口号")
private String fPort; private String fPort;
......
...@@ -30,6 +30,12 @@ public class ModbusDeviceData { ...@@ -30,6 +30,12 @@ public class ModbusDeviceData {
*/ */
private String errorReason; private String errorReason;
/** 断电层IP地址 */
private String fPowerOutageIp;
/** 断电层端口号 */
private Integer fPowerOutagePort;
private Map<Integer, String> registerValues; // key: 寄存器地址, value: 读取结果 private Map<Integer, String> registerValues; // key: 寄存器地址, value: 读取结果
public String getDeviceCode() { public String getDeviceCode() {
...@@ -87,4 +93,20 @@ public class ModbusDeviceData { ...@@ -87,4 +93,20 @@ public class ModbusDeviceData {
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getfPowerOutageIp() {
return fPowerOutageIp;
}
public void setfPowerOutageIp(String fPowerOutageIp) {
this.fPowerOutageIp = fPowerOutageIp;
}
public Integer getfPowerOutagePort() {
return fPowerOutagePort;
}
public void setfPowerOutagePort(Integer fPowerOutagePort) {
this.fPowerOutagePort = fPowerOutagePort;
}
} }
...@@ -21,10 +21,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -21,10 +21,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="fUpdateTime" column="f_update_time" /> <result property="fUpdateTime" column="f_update_time" />
<result property="fStatus" column="f_status" /> <result property="fStatus" column="f_status" />
<result property="fAlarmTime" column="f_alarm_time" /> <result property="fAlarmTime" column="f_alarm_time" />
<result property="fPowerOutageIp" column="f_power_outage_ip" />
<result property="fPowerOutagePort" column="f_power_outage_port" />
</resultMap> </resultMap>
<sql id="selectTEquipmentInfoVo"> <sql id="selectTEquipmentInfoVo">
select f_equipment_id, f_equipment_code, f_equipment_name, f_equipment_type, f_equipment_factory, f_beyong_department, f_ip, f_port, f_install_location, f_is_inspect, f_responsible_person, f_responsible_person_mobile, f_create_time, f_update_time, f_status, f_alarm_time from t_equipment_info select f_equipment_id, f_equipment_code, f_equipment_name, f_equipment_type, f_equipment_factory,
f_beyong_department, f_ip, f_port, f_install_location, f_is_inspect, f_responsible_person,
f_responsible_person_mobile, f_create_time, f_update_time, f_status, f_alarm_time, f_power_outage_ip, f_power_outage_port from t_equipment_info
</sql> </sql>
<select id="selectTEquipmentInfoList" parameterType="TEquipmentInfo" resultMap="TEquipmentInfoResult"> <select id="selectTEquipmentInfoList" parameterType="TEquipmentInfo" resultMap="TEquipmentInfoResult">
......
...@@ -84,6 +84,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -84,6 +84,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="fStatus != null">f_status = #{fStatus},</if> <if test="fStatus != null">f_status = #{fStatus},</if>
<if test="fPort != null">f_port = #{fPort},</if> <if test="fPort != null">f_port = #{fPort},</if>
<if test="fAgingStartTime != null">f_aging_start_time = #{fAgingStartTime},</if> <if test="fAgingStartTime != null">f_aging_start_time = #{fAgingStartTime},</if>
<if test="fAgingStartTime == null">f_aging_start_time = null,</if>
<if test="fUpdateTime != null">f_update_time = #{fUpdateTime},</if> <if test="fUpdateTime != null">f_update_time = #{fUpdateTime},</if>
<if test="fCreateTime != null">f_create_time = #{fCreateTime},</if> <if test="fCreateTime != null">f_create_time = #{fCreateTime},</if>
<if test="fAlarmTime != null">f_alarm_time = #{fAlarmTime},</if> <if test="fAlarmTime != null">f_alarm_time = #{fAlarmTime},</if>
......
...@@ -8,3 +8,11 @@ export function getAgingCabinetAndPowerCheck() { ...@@ -8,3 +8,11 @@ export function getAgingCabinetAndPowerCheck() {
method: 'get' method: 'get'
}) })
} }
// 查询用户列表
export function testPowerOutage() {
return request({
url: '/testTaskController/testPowerOutage',
method: 'get'
})
}
...@@ -59,6 +59,42 @@ export default { ...@@ -59,6 +59,42 @@ export default {
return { return {
// 示例数据格式,实际从后端获取 // 示例数据格式,实际从后端获取
cabinets: [ cabinets: [
// { id: 1, deviceStatus: '1' },
// { id: 2, deviceStatus: '2' },
// { id: 3, deviceStatus: '0' },
// { id: 4, deviceStatus: '0' },
// { id: 5, deviceStatus: '0' },
// { id: 6, deviceStatus: '0' },
// { id: 7, deviceStatus: '0' },
// { id: 8, deviceStatus: '0' },
// { id: 9, deviceStatus: '0' },
// { id: 10, deviceStatus: '0' },
// { id: 11, deviceStatus: '0' },
// { id: 12, deviceStatus: '0' },
// { id: 13, deviceStatus: '0' },
// { id: 14, deviceStatus: '0' },
// { id: 15, deviceStatus: '0' },
// { id: 16, deviceStatus: '0' },
// { id: 17, deviceStatus: '0' },
// { id: 18, deviceStatus: '0' },
// { id: 19, deviceStatus: '0' },
// { id: 20, deviceStatus: '0' },
// { id: 21, deviceStatus: '0' },
// { id: 22, deviceStatus: '0' },
// { id: 23, deviceStatus: '0' },
// { id: 24, deviceStatus: '0' },
// { id: 25, deviceStatus: '0' },
// { id: 26, deviceStatus: '0' },
// { id: 27, deviceStatus: '0' },
// { id: 28, deviceStatus: '0' },
// { id: 29, deviceStatus: '0' },
// { id: 30, deviceStatus: '0' },
// { id: 31, deviceStatus: '0' },
// { id: 32, deviceStatus: '0' },
// { id: 33, deviceStatus: '0' },
// { id: 34, deviceStatus: '0' },
// { id: 35, deviceStatus: '0' },
// { id: 36, deviceStatus: '0' },
// ...共36个 // ...共36个
], ],
// 状态对应的颜色类名 // 状态对应的颜色类名
......
...@@ -92,6 +92,7 @@ ...@@ -92,6 +92,7 @@
<script> <script>
import { queryByDepartmentId } from "@/api/storey/storey"; import { queryByDepartmentId } from "@/api/storey/storey";
import { testPowerOutage } from "@/api/testScheduledTasks/testTasks";
export default { export default {
name: "AgingLayer", name: "AgingLayer",
props: { props: {
...@@ -240,6 +241,12 @@ export default { ...@@ -240,6 +241,12 @@ export default {
powerOn() { powerOn() {
this.$message.success("上电操作已执行"); this.$message.success("上电操作已执行");
testPowerOutage().then(response => {
if(response.code === 200) {
this.$message.success("断电操作已执行");
}
})
}, },
// 获取状态类名 // 获取状态类名
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
<div class="tray-binding-container"> <div class="tray-binding-container">
<!-- 头部区域 --> <!-- 头部区域 -->
<div class="header"> <div class="header">
<h1><i class="fas fa-qrcode"></i> 托盘数据绑定系统</h1>
<p>通过扫描枪扫描设备条码,自动将设备绑定到托盘指定位置</p> <p>通过扫描枪扫描设备条码,自动将设备绑定到托盘指定位置</p>
</div> </div>
......
<template>
<div class="container">
<div class="header">
<div class="search-box">
<el-input
class="search-input"
v-model="searchKeyword"
placeholder="请输入托盘编号"
clearable
@keyup.enter.native="searchTray"
></el-input>
<el-button
type="primary"
icon="el-icon-search"
@click="searchTray"
>搜索</el-button>
</div>
</div>
<div class="table-container">
<el-table
:data="filteredTrays"
border
style="width: 100%"
@row-click="handleRowClick"
:row-class-name="tableRowClassName"
>
<el-table-column prop="trayId" label="托盘编号" width="200"></el-table-column>
<el-table-column prop="status" label="状态" width="150">
<template slot-scope="scope">
<el-tag :type="statusTagType(scope.row.status)">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="boardCount" label="主板数量" width="150"></el-table-column>
<el-table-column prop="location" label="位置"></el-table-column>
</el-table>
<div class="pagination">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 20, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalTrays"
></el-pagination>
</div>
</div>
</div>
</template>
<script>
import {queryByDepartmentId} from "@/api/storey/storey";
export default {
name: "TrayInformation",
data() {
return {
searchKeyword: '',
trays: [],
currentPage: 1,
pageSize: 10
};
},
created() {
this.fetchTrays();
},
computed: {
// 计算过滤后的托盘数据
filteredTrays() {
let filtered = this.trays;
if (this.searchKeyword) {
filtered = filtered.filter(tray =>
tray.trayId.includes(this.searchKeyword)
);
}
// 分页处理
const start = (this.currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
return filtered.slice(start, end);
},
totalTrays() {
if (this.searchKeyword) {
return this.trays.filter(tray =>
tray.trayId.includes(this.searchKeyword)
).length;
}
return this.trays.length;
}
},
methods: {
// 模拟API请求
fetchTrays() {
// 实际项目中替换为真实的API请求
setTimeout(() => {
this.trays = [
{trayId: 'TP20240001', status: '空闲', boardCount: 0, location: 'A区-1号架'},
{trayId: 'TP20240002', status: '使用中', boardCount: 8, location: 'B区-3号架'},
{trayId: 'TP20240003', status: '满载', boardCount: 12, location: 'C区-2号架'},
{trayId: 'TP20240004', status: '维护中', boardCount: 0, location: '维修区'},
{trayId: 'TP20240005', status: '空闲', boardCount: 0, location: 'D区-4号架'},
{trayId: 'TP20240006', status: '使用中', boardCount: 5, location: 'E区-1号架'},
{trayId: 'TP20240007', status: '满载', boardCount: 12, location: 'F区-2号架'},
{trayId: 'TP20240008', status: '空闲', boardCount: 0, location: 'G区-5号架'},
{trayId: 'TP20240009', status: '使用中', boardCount: 10, location: 'H区-3号架'},
{trayId: 'TP20240110', status: '满载', boardCount: 12, location: 'I区-1号架'}
];
}, 500);
},
// 搜索托盘
searchTray() {
this.currentPage = 1;
},
// 处理行点击
handleRowClick(row) {
this.$router.push({name: 'TrayConfig', params: {id: row.trayId}});
},
// 设置行样式
tableRowClassName() {
return 'clickable-row';
},
// 状态标签类型
statusTagType(status) {
const statusMap = {
'空闲': 'success',
'使用中': 'primary',
'满载': 'warning',
'维护中': 'danger'
};
return statusMap[status] || 'info';
},
// 分页处理
handleSizeChange(size) {
this.pageSize = size;
this.currentPage = 1;
},
handleCurrentChange(page) {
this.currentPage = page;
}
}
}
</script>
<style scoped>
.container {
padding: 20px;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
}
.search-box {
display: flex;
margin-bottom: 20px;
max-width: 400px;
}
.search-input {
flex: 1;
margin-right: 10px;
}
.table-container {
margin-top: 20px;
}
.pagination {
margin-top: 20px;
text-align: right;
}
.clickable-row {
cursor: pointer;
}
.clickable-row:hover {
background-color: #f5f7fa;
}
</style>
...@@ -58,24 +58,26 @@ import AgingCabinetBoard from './components/AgingCabinetBoard.vue' ...@@ -58,24 +58,26 @@ import AgingCabinetBoard from './components/AgingCabinetBoard.vue'
import AgingLayer from './components/AgingLayer' import AgingLayer from './components/AgingLayer'
import RealTimeData from './components/RealTimeData' import RealTimeData from './components/RealTimeData'
import TrayBinding from "@/views/screen/components/TrayBinding"; import TrayBinding from "@/views/screen/components/TrayBinding";
import TrayInformation from "@/views/screen/components/TrayInformation";
export default { export default {
components: { components: {
AgingCabinetBoard, AgingCabinetBoard,
AgingLayer, AgingLayer,
RealTimeData, RealTimeData,
TrayBinding TrayBinding,
TrayInformation
}, },
data() { data() {
return { return {
selectedMenu: 0, // 默认选中第一个 selectedMenu: 0, // 默认选中第一个
menuItems: [ menuItems: [
{ icon: '📷', text: '老化柜看板', component: 'AgingCabinetBoard' }, { icon: '📷', text: '老化柜看板', component: 'AgingCabinetBoard' },
{ icon: '📊', text: '托盘绑定', component: 'TrayBinding' }, { icon: '📊', text: '托盘信息', component: 'TrayInformation' },
{ icon: '📦', text: '实时数据', component: 'RealTimeData' }, { icon: '📦', text: '实时数据', component: 'RealTimeData' },
], ],
selectMenuItems: [ selectMenuItems: [
{ icon: '📷', text: '老化柜看板', component: 'AgingCabinetBoard' }, { icon: '📷', text: '老化柜看板', component: 'AgingCabinetBoard' },
{ icon: '📊', text: '托盘绑定', component: 'TrayBinding' }, { icon: '📊', text: '托盘信息', component: 'TrayInformation' },
{ icon: '📦', text: '实时数据', component: 'RealTimeData' }, { icon: '📦', text: '实时数据', component: 'RealTimeData' },
{ icon: '🔍', text: '老化层详情', component: 'AgingLayer' } // 新增菜单项 { icon: '🔍', text: '老化层详情', component: 'AgingLayer' } // 新增菜单项
], ],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment