Commit 04c19f2a authored by wanghao's avatar wanghao

1 上料 时 读状态 写时间,以及 托盘 和 层之间绑定关系的处理。

2 72 小时老化后 托盘 和 层之间 关系处理。
3 下料 时 断电 以及  托盘 和 层之间绑定关系的处理。
parent 9808fa2d
......@@ -209,25 +209,25 @@ public class TestTaskController {
Integer registerOffset = entry.getKey();
String registerValue = entry.getValue();
if ("true".equals(registerValue)) {
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
modbusDeviceData.getDeviceCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset +1,
registerOffset
));
// eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
// this,
// modbusDeviceData.getDeviceCode(),
// modbusDeviceData.getfPowerOutageIp(),
// modbusDeviceData.getfPowerOutagePort(),
// registerOffset +1,
// registerOffset
// ));
isRun = true;
// 要给这个 层 发断电的 指令
} else {
// 发布断电指令事件(不再直接执行)
eventPublisher.publishEvent(new PowerOffCommandEvent(
this,
modbusDeviceData.getDeviceCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset
));
// eventPublisher.publishEvent(new PowerOffCommandEvent(
// this,
// modbusDeviceData.getDeviceCode(),
// modbusDeviceData.getfPowerOutageIp(),
// modbusDeviceData.getfPowerOutagePort(),
// registerOffset
// ));
}
}
if (isRun) {
......
......@@ -52,7 +52,7 @@ public class PalletDeviceBinding extends BaseEntity
private Date unbindingTime;
/**
* 状态
* 状态 0-预热;1-正常;3-传感器故障;4-报警;5-通讯故障;6-程序写时间失败
*/
private String status;
......@@ -81,6 +81,26 @@ public class PalletDeviceBinding extends BaseEntity
*/
private String recordMinute;
/**
* 写时间状态 0-失败;1-成功
*/
private String writeTimeStatus;
/**
* 零点校准AD
*/
private String adjustmentZeroAd;
/**
* 零点校准AD
*/
private String calibrationAd;
/**
* 浓度
*/
private String concentration;
public String getStatus() {
return status;
}
......@@ -201,6 +221,38 @@ public class PalletDeviceBinding extends BaseEntity
this.recordMinute = recordMinute;
}
public String getWriteTimeStatus() {
return writeTimeStatus;
}
public void setWriteTimeStatus(String writeTimeStatus) {
this.writeTimeStatus = writeTimeStatus;
}
public String getAdjustmentZeroAd() {
return adjustmentZeroAd;
}
public void setAdjustmentZeroAd(String adjustmentZeroAd) {
this.adjustmentZeroAd = adjustmentZeroAd;
}
public String getCalibrationAd() {
return calibrationAd;
}
public void setCalibrationAd(String calibrationAd) {
this.calibrationAd = calibrationAd;
}
public String getConcentration() {
return concentration;
}
public void setConcentration(String concentration) {
this.concentration = concentration;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
......@@ -67,9 +67,15 @@ public class TStoreyInfo extends BaseEntity
/** 老化开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "老化开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date fAgingStartTime;
/** 老化结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "老化结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date fAgingEndTime;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
......@@ -224,6 +230,14 @@ public class TStoreyInfo extends BaseEntity
this.fEquipmentCode = fEquipmentCode;
}
public Date getfAgingEndTime() {
return fAgingEndTime;
}
public void setfAgingEndTime(Date fAgingEndTime) {
this.fAgingEndTime = fAgingEndTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
......@@ -29,7 +29,7 @@ public class TTrayInfo extends BaseEntity
private String fStoreyCode;
/** 状态:0.空闲 1.运行 */
@Excel(name = "状态:0.空闲 1.运行")
@Excel(name = "状态:0.空闲 1.运行 2老化完成 3标定完成")
private String fStatus;
/** 绑定设备数 */
......
......@@ -21,6 +21,10 @@ public interface TTrayInfoMapper
public TTrayInfo selectTTrayInfoByCode(String code);
public TTrayInfo selectTTrayInfoByStoreyCode(String storeyCode);
public int clearStoreyCodeByStoreyCode(String storeyCode);
/**
* 查询托盘信息列表
*
......
......@@ -88,6 +88,7 @@ public class PalletDeviceBindingServiceImpl implements IPalletDeviceBindingServi
@Override
public int batchUpdateDeviceCode(List<PalletDeviceBinding> palletDeviceBindingList) {
palletDeviceBindingList.forEach(palletDeviceBinding -> {
palletDeviceBinding.setStatus("1");
palletDeviceBinding.setUpdateTime(DateUtils.getNowDate());
});
return palletDeviceBindingMapper.batchUpdateDeviceCode(palletDeviceBindingList);
......
......@@ -23,6 +23,7 @@ import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.netty.handler.NettyUdpServerHandler;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import com.zehong.system.task.CheckPowerOnCommandEvent;
import com.zehong.system.task.PowerOffCommandEvent;
import com.zehong.system.udp.UdpCommandSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -207,26 +208,23 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
log.info("command != null && \"2\".equals(command.getStatus()");
// 发送上电指令
try {
// 上料的指令 需要去上电
if("0".equals(command.getType())){
log.info("\"0\".equals(command.getType()");
// 发送上电指令给机械臂
String storeyCode = command.getStoreyCode();
String equitmentCode = "";
Integer registerOffset = null;
if(storeyCode.contains("-")) {
log.info("storeyCode.contains(\"-\")");
String[] parts = storeyCode.split("-");
equitmentCode = parts[0];
registerOffset = Integer.parseInt(parts[1]);
TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(equitmentCode);
if(tEquipmentInfo != null) {
log.info("tEquipmentInfo != null");
String powerOutageIp = tEquipmentInfo.getfPowerOutageIp();
Integer powerOutagePort = tEquipmentInfo.getfPowerOutagePort();
if(StringUtils.isNotBlank(powerOutageIp) && powerOutagePort != null) {
String storeyCode = command.getStoreyCode();
String equitmentCode = "";
Integer registerOffset = null;
if(storeyCode.contains("-")) {
log.info("storeyCode.contains(\"-\")");
String[] parts = storeyCode.split("-");
equitmentCode = parts[0];
registerOffset = Integer.parseInt(parts[1]);
TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(equitmentCode);
if(tEquipmentInfo != null) {
log.info("tEquipmentInfo != null");
String powerOutageIp = tEquipmentInfo.getfPowerOutageIp();
Integer powerOutagePort = tEquipmentInfo.getfPowerOutagePort();
if(StringUtils.isNotBlank(powerOutageIp) && powerOutagePort != null) {
if("0".equals(command.getType())) {
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
equitmentCode,
......@@ -235,21 +233,17 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
registerOffset,
registerOffset - 1
));
} else {
eventPublisher.publishEvent(new PowerOffCommandEvent(
this,
equitmentCode,
powerOutageIp,
powerOutagePort,
registerOffset
));
}
}
}
// 下料的话 得解除 托盘 和 层的绑定关系
} else {
TStoreyInfo tStoreyInfo = new TStoreyInfo();
tStoreyInfo.setfStoreyCode(command.getStoreyCode());
tStoreyInfo.setUpdateTime(new Date());
storeyInfoMapper.unbindByCode(tStoreyInfo);
TTrayInfo tTrayInfo = new TTrayInfo();
tTrayInfo.setfTrayCode(command.getTrayCode());
tTrayInfo.setUpdateTime(new Date());
tTrayInfo.setfUnbindingTime(new Date());
tTrayInfoMapper.unbindByCode(tTrayInfo);
}
command.setStatus("4");
log.info("指令执行成功,状态设置为4");
......
......@@ -173,25 +173,25 @@ public class AgingCabinetInspectionAndPowerCheckTask {
log.info("registerValue = " + registerValue);
log.info("true equals registerValue" + Boolean.TRUE.equals(registerValue));
if (Boolean.TRUE.equals(registerValue)) {
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
modbusDeviceData.getfEquipmentCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset + 1,
registerOffset
));
// eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
// this,
// modbusDeviceData.getfEquipmentCode(),
// modbusDeviceData.getfPowerOutageIp(),
// modbusDeviceData.getfPowerOutagePort(),
// registerOffset + 1,
// registerOffset
// ));
isRun = true;
// 要给这个 层 发断电的 指令
} else {
// 发布断电指令事件(不再直接执行)
eventPublisher.publishEvent(new PowerOffCommandEvent(
this,
modbusDeviceData.getfEquipmentCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset + 1
));
// eventPublisher.publishEvent(new PowerOffCommandEvent(
// this,
// modbusDeviceData.getfEquipmentCode(),
// modbusDeviceData.getfPowerOutageIp(),
// modbusDeviceData.getfPowerOutagePort(),
// registerOffset + 1
// ));
}
}
if (isRun) {
......
......@@ -7,6 +7,8 @@ import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.domain.TTrayInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.mapper.TTrayInfoMapper;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
......@@ -29,8 +31,12 @@ public class AllCommandHandler {
@Resource
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Resource
private ITStoreyInfoService tStoreyInfoService;
private TTrayInfoMapper tTrayInfoMapper;
@Resource
private ITTrayInfoService trayInfoService;
......@@ -40,6 +46,7 @@ public class AllCommandHandler {
/**
* check是否启动 ,没启动就启动下 开始老化
*
* @param event event
*/
@Async
......@@ -55,20 +62,20 @@ public class AllCommandHandler {
String storeyCode = event.getDeviceCode() + "-" + event.getLayer();
log.info("需要发送上电指令 - 设备:{} 层:{} ip:{} 端口号:{}", event.getDeviceCode(),layer, fip, fport);
log.info("需要发送上电指令 - 设备:{} 层:{} ip:{} 端口号:{}", event.getDeviceCode(), layer, 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) {
log.info("当前 - 设备:{} 层:{} -的 状态是:{}", event.getDeviceCode(), event.getLayer(), aBoolean);
if (!aBoolean) {
Modbus4jUtils.writeCoil(master, 1, registerOffset, true);
TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode);
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoByCode(storeyCode);
tStoreyInfo.setfStatus("1");
tStoreyInfo.setfAgingStartTime(new Date());
tStoreyInfoService.updateTStoreyInfo(tStoreyInfo);
tStoreyInfoMapper.updateTStoreyInfo(tStoreyInfo);
// 同时 把 托盘的 状态更新
TTrayInfo tTrayInfo = new TTrayInfo();
......@@ -76,7 +83,7 @@ public class AllCommandHandler {
tTrayInfo.setfStoreyCode(storeyCode);
trayInfoService.updateStatusByTrayCode(tTrayInfo);
deviceTaskScheduler.scheduleDeviceMonitoring(tStoreyInfo.getfStoreyId(),fip,fport);
deviceTaskScheduler.scheduleDeviceMonitoring(tStoreyInfo.getfStoreyId(), fip, fport);
}
} catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
......@@ -98,7 +105,7 @@ public class AllCommandHandler {
String ip = event.getIp();
int port = event.getPort();
log.info("需要发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer());
TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode);
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoByCode(storeyCode);
if (tStoreyInfo == null) {
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
// 记录异常数据
......@@ -107,11 +114,18 @@ public class AllCommandHandler {
alarmData.setfAlarmData("下属" + storeyCode + "号老化层不存在");
alarmDataService.insertTEquipmentAlarmData(alarmData);
} else {
//
ModbusMaster master = Modbus4jUtils.getMaster(ip, port);
Modbus4jUtils.writeCoil(master, 1, event.getLayer(), false);
log.info("已发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer());
master.destroy();
// 下料后断电
ModbusMaster master = Modbus4jUtils.getMaster(ip, port);
Modbus4jUtils.writeCoil(master, 1, event.getLayer(), false);
log.info("已发送断电指令 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer());
master.destroy();
tStoreyInfo.setfStoreyCode(tStoreyInfo.getfStoreyCode());
tStoreyInfo.setUpdateTime(new Date());
tStoreyInfoMapper.unbindByCode(tStoreyInfo);
// 清理 托盘 和 层的关联关系
tTrayInfoMapper.clearStoreyCodeByStoreyCode(storeyCode);
}
} catch (ModbusInitException | ModbusTransportException e) {
log.error("断电指令执行失败 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer(), e);
......
......@@ -49,8 +49,7 @@ public class DeviceCommunicationJob implements Job {
private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备超时:10秒
// Modbus配置:取消内置重试,统一用自定义重试
private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // 连接超时:3秒
private static final int CUSTOM_RETRY_TIMES = 1; // 自定义重试次数:1次
private static final int RETRY_DELAY_MS = 200; // 重试间隔:200ms
private static final int CUSTOM_RETRY_TIMES = 2; // 自定义重试次数:1次
// Modbus寄存器配置
private static final int REG_START_ADDR = 0;
private static final int REG_READ_COUNT = 10;
......@@ -70,8 +69,6 @@ public class DeviceCommunicationJob implements Job {
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Autowired
private ModbusResultHandler resultHandler;
@Resource
private PalletDeviceBindingMapper palletDeviceBindingMapper;
......@@ -234,34 +231,58 @@ public class DeviceCommunicationJob implements Job {
*/
private int[] readDeviceWithRetry(String ip, int port, int deviceId) {
ModbusMaster master = null;
int[] lastResult = null; // 用于记录最后一次读取的结果(无论是否满足停止条件)
for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
try {
master = createModbusMaster(ip, port);
int[] result = readDeviceRegisters(master, deviceId);
// 检查停止条件
if (resultHandler != null && ModbusResultHandler.createDefaultStopCondition().test(result)) {
log.info("设备{}读取成功: ip={}, port={}", deviceId, ip, port);
return result;
}
if (retry < CUSTOM_RETRY_TIMES) {
Thread.sleep(200);
}
} catch (Exception e) {
if (retry < CUSTOM_RETRY_TIMES) {
log.info("设备{}读取失败,准备重试: ip={}, port={}", deviceId, ip, port);
} else {
log.info("设备{}读取重试耗尽: ip={}, port={}", deviceId, ip, port, e);
throw new RuntimeException("设备读取失败", e);
try {
// 只创建一次ModbusMaster,循环内复用
master = createModbusMaster(ip, port);
for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
try {
// 执行读取操作,获取本次结果
int[] currentResult = readDeviceRegisters(master, deviceId);
// 更新最后一次结果(无论是否满足停止条件,都记录)
lastResult = currentResult;
// 检查停止条件,如果满足则提前返回(无需等到重试耗尽)
if (ModbusResultHandler.createDefaultStopCondition().test(currentResult)) {
log.info("设备{}第{}次读取成功(满足条件): ip={}, port={}",
deviceId, retry + 1, ip, port);
return currentResult;
}
// 未满足条件且不是最后一次重试,休眠后继续
if (retry < CUSTOM_RETRY_TIMES) {
log.info("设备{}第{}次读取未满足条件,准备重试: ip={}, port={}",
deviceId, retry + 1, ip, port);
Thread.sleep(200);
}
} catch (Exception e) {
// 本次读取发生异常,记录日志但不中断重试(继续下一次)
log.warn("设备{}第{}次读取发生异常: ip={}, port={}",
deviceId, retry + 1, ip, port, e);
// 如果是最后一次重试,异常时lastResult可能为null(需后续处理)
}
} finally {
destroyModbusMaster(master, deviceId);
}
// 循环结束(重试耗尽),此时lastResult为最后一次的结果(可能是正常读取但不满足条件,或null)
log.info("设备{}重试次数耗尽,返回最后一次结果: ip={}, port={}",
deviceId, ip, port);
} catch (Exception e) {
// 捕获创建ModbusMaster或休眠时的异常(非读取操作的异常)
log.error("设备{}连接创建或休眠失败: ip={}, port={}",
deviceId, ip, port, e);
throw new RuntimeException("设备连接或操作异常", e);
} finally {
// 无论结果如何,最终销毁连接
destroyModbusMaster(master, deviceId);
}
throw new RuntimeException("设备读取未满足条件");
// 处理最后一次结果可能为null的情况(例如所有重试都异常)
if (lastResult == null) {
throw new RuntimeException("设备所有读取尝试均失败(无有效结果)");
}
return lastResult;
}
/**
* 写入当前时间到设备
......@@ -292,9 +313,11 @@ public class DeviceCommunicationJob implements Job {
binding.setRecordMinute(String.valueOf(minute));
log.debug("设备{}时间写入成功", deviceId);
} else {
binding.setWriteTimeStatus("0");
recordAlarmByBinding(binding, "设备时间写入失败");
}
} catch (Exception e) {
binding.setWriteTimeStatus("0");
log.error("设备{}时间写入异常", deviceId, e);
recordAlarmByBinding(binding, "设备时间写入异常: " + e.getMessage());
}
......
......@@ -236,14 +236,25 @@ public class DeviceTaskScheduler {
.storeDurably(false)
.build();
Date executeTime = Date.from(Instant.now().plus(5, ChronoUnit.MINUTES));
// 关键修复:使用StartAt而不是StartNow
CronTrigger trigger = TriggerBuilder.newTrigger()
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?") // 改为3分钟
.withMisfireHandlingInstructionDoNothing())
.startAt(new Date()) // 显式设置开始时间:cite[1]
.withDescription("设备" + fStoreyId + "最终任务触发器,触发时间是:" + executeTime)
.startAt(executeTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow() // 错过立即执行
.withRepeatCount(0)) // 仅一次
.build();
// CronTrigger trigger = TriggerBuilder.newTrigger()
// .withIdentity(triggerKey)
// .forJob(jobKey)
// .withDescription("设备" + fStoreyId + "写时间任务,触发时间是:" + executeTime)
// .withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?") // 改为3分钟
// .withMisfireHandlingInstructionDoNothing())
// .startAt(new Date()) // 显式设置开始时间:cite[1]
// .build();
Date nextFireTime = scheduler.scheduleJob(job, trigger);
log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
......
......@@ -7,8 +7,10 @@ import com.zehong.common.utils.StringUtils;
import com.zehong.system.domain.RobotArmCommand;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.domain.TTrayInfo;
import com.zehong.system.mapper.RobotArmCommandMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.mapper.TTrayInfoMapper;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
......@@ -34,11 +36,12 @@ public class FinalExecutionJob implements Job {
private RobotArmWebSocketHandler robotArmWebSocketHandler;
@Resource
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private ITStoreyInfoService tStoreyInfoService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Resource
private TTrayInfoMapper tTrayInfoMapper;
@Resource
private Scheduler scheduler;
......@@ -47,17 +50,18 @@ public class FinalExecutionJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 1. 初始化变量,避免空指针
JobDataMap data = null;
String fPowerOutageIp = null;
JobDataMap data;
String fPowerOutageIp;
Long fStoreyId = null;
Integer fPowerOutagePort = null;
Integer fPowerOutagePort;
TStoreyInfo tStoreyInfo = null;
TTrayInfo tTrayInfo;
try {
// 2. 提取并校验所有参数
data = context.getJobDetail().getJobDataMap();
if (data == null) {
log.error("JobDataMap为空,终止执行");
log.info("JobDataMap为空,终止执行");
return;
}
fStoreyId = data.getLong("fStoreyId");
......@@ -65,45 +69,53 @@ public class FinalExecutionJob implements Job {
fPowerOutagePort = data.getInt("fPowerOutagePort");
if(StringUtils.isBlank(fPowerOutageIp)) {
log.error("参数缺失:fStoreyId={}, ip={}, port={},终止执行", fStoreyId, fPowerOutageIp, fPowerOutagePort);
log.info("参数缺失:fStoreyId={}, ip={}, port={},终止执行", fStoreyId, fPowerOutageIp, fPowerOutagePort);
return;
}
// 4. 查询设备信息
tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
if (tStoreyInfo == null) {
log.error("未查询到设备信息:fStoreyId={},终止执行", fStoreyId);
log.info("未查询到设备信息:fStoreyId={},终止执行", fStoreyId);
return;
}
// 5. 执行业务逻辑(Modbus写操作,单独捕获异常)
String storeyCode = tStoreyInfo.getfStoreyCode();
if (StringUtils.isBlank(storeyCode)) {
log.error("设备编码为空:fStoreyId={},终止执行", fStoreyId);
return;
}
int registerOffsets;
try {
registerOffsets = Integer.parseInt(storeyCode.split("-")[1]) - 1;
} catch (Exception e) {
log.error("设备编码解析失败:storeyCode={},终止执行", storeyCode);
log.info("设备编码为空:fStoreyId={},终止执行", fStoreyId);
return;
}
tTrayInfo = tTrayInfoMapper.selectTTrayInfoByStoreyCode(storeyCode);
// Modbus写操作(容错:失败不影响后续清理任务)
try {
executeBusinessLogic(fPowerOutageIp, fPowerOutagePort, registerOffsets);
log.info("Modbus写操作完成:fStoreyId={}", fStoreyId);
} catch (Exception e) {
log.error("Modbus写操作异常:fStoreyId={}", fStoreyId, e);
// 记录告警,但不终止执行(后续清理任务必须执行)
recordAlarm(tStoreyInfo, "Modbus写操作失败:" + e.getMessage());
}
// 科强说 老化完成先不断电,等下料后才断电
// int registerOffsets;
// try {
// registerOffsets = Integer.parseInt(storeyCode.split("-")[1]) - 1;
// } catch (Exception e) {
// log.error("设备编码解析失败:storeyCode={},终止执行", storeyCode);
// return;
// }
// try {
// executeBusinessLogic(fPowerOutageIp, fPowerOutagePort, registerOffsets);
// log.info("Modbus写操作完成:fStoreyId={}", fStoreyId);
// } catch (Exception e) {
// log.error("Modbus写操作异常:fStoreyId={}", fStoreyId, e);
// // 记录告警,但不终止执行(后续清理任务必须执行)
// recordAlarm(tStoreyInfo, "Modbus写操作失败:" + e.getMessage());
// }
// 6. 更新设备状态(DB操作单独捕获异常)
try {
tStoreyInfo.setfStatus("0");
tStoreyInfo.setfAgingStartTime(null);
tStoreyInfoService.updateTStoreyInfo(tStoreyInfo);
tStoreyInfo.setfStatus("4");
tStoreyInfo.setfAgingEndTime(new Date());
tStoreyInfoMapper.updateTStoreyInfo(tStoreyInfo);
tTrayInfo.setfStatus("2");
tTrayInfoMapper.updateTTrayInfo(tTrayInfo);
log.info("设备状态更新完成:fStoreyId={}", fStoreyId);
} catch (Exception e) {
log.error("设备状态更新异常:fStoreyId={}", fStoreyId, e);
......@@ -111,15 +123,16 @@ public class FinalExecutionJob implements Job {
}
// 7.清理 job
cleanUpJobs(fStoreyId,context);
// 为什么不用清理,是因为 写时间的任务 创建的时候是 一次性执行完的 job
// cleanUpJobs(fStoreyId,context);
// 8. 发送机械臂指令(单独捕获异常)
// try {
// createRoboticArm(tStoreyInfo.getfTrayCode(), storeyCode, tStoreyInfo.getBlankingCommand());
// log.info("机械臂指令发送完成:fStoreyId={}", fStoreyId);
// } catch (Exception e) {
// log.error("机械臂指令发送异常:fStoreyId={}", fStoreyId, e);
// recordAlarm(tStoreyInfo, "机械臂指令发送失败:" + e.getMessage());
// }
try {
createRoboticArm(tStoreyInfo.getfTrayCode(), storeyCode, tStoreyInfo.getBlankingCommand());
log.info("机械臂指令发送完成:fStoreyId={}", fStoreyId);
} catch (Exception e) {
log.error("机械臂指令发送异常:fStoreyId={}", fStoreyId, e);
recordAlarm(tStoreyInfo, "机械臂指令发送失败:" + e.getMessage());
}
log.info("=== FinalExecutionJob 执行完成:fStoreyId={} ===", fStoreyId);
......
......@@ -20,11 +20,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="recordDate" column="f_record_date" />
<result property="recordHour" column="f_record_hour" />
<result property="recordMinute" column="f_record_minute" />
<result property="writeTimeStatus" column="f_write_time_status" />
<result property="adjustmentZeroAd" column="f_adjustment_zero_ad" />
<result property="calibrationAd" column="f_calibration_ad" />
<result property="concentration" column="f_concentration" />
</resultMap>
<sql id="selectPalletDeviceBindingVo">
select f_pallet_device_binding_id, f_tray_id, f_device_code, f_row, f_col, f_index,f_binding_time,
f_unbinding_time, f_create_time,f_status,f_record_year,f_record_month,f_record_date,f_record_hour,f_record_minute from t_pallet_device_binding
f_unbinding_time, f_create_time,f_status,f_record_year,f_record_month,f_record_date,f_record_hour,
f_record_minute, f_write_time_status, f_adjustment_zero_ad, f_calibration_ad, f_concentration from t_pallet_device_binding
</sql>
<select id="selectPalletDeviceBindingList" parameterType="PalletDeviceBinding" resultMap="PalletDeviceBindingResult">
......@@ -61,7 +67,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_month,
f_record_date,
f_record_hour,
f_record_minute from t_pallet_device_binding palDeviceBinding where palDeviceBinding.f_tray_id = (
f_record_minute ,
f_write_time_status,
f_adjustment_zero_ad,
f_calibration_ad,
f_concentration
from t_pallet_device_binding palDeviceBinding where palDeviceBinding.f_tray_id = (
SELECT
trayInfo.f_tray_id
FROM
......@@ -132,6 +143,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="recordDate != null">f_record_date = #{recordDate},</if>
<if test="recordHour != null">f_record_hour = #{recordHour},</if>
<if test="recordMinute != null">f_record_minute = #{recordMinute},</if>
<if test="writeTimeStatus != null">f_write_time_status = #{writeTimeStatus},</if>
<if test="adjustmentZeroAd != null">f_adjustment_zero_ad = #{adjustmentZeroAd},</if>
<if test="calibrationAd != null">f_calibration_ad = #{calibrationAd},</if>
<if test="concentration != null">f_concentration = #{concentration},</if>
</trim>
where f_pallet_device_binding_id = #{palletDeviceBindingId}
</update>
......
......@@ -14,6 +14,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="fStatus" column="f_status" />
<result property="fPort" column="f_port" />
<result property="fAgingStartTime" column="f_aging_start_time" />
<result property="fAgingEndTime" column="f_aging_end_time" />
<result property="fUpdateTime" column="f_update_time" />
<result property="fCreateTime" column="f_create_time" />
<result property="fAlarmTime" column="f_alarm_time" />
......@@ -30,6 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
storeyInfo.f_status,
storeyInfo.f_port,
storeyInfo.f_aging_start_time,
storeyInfo.f_aging_end_time,
storeyInfo.f_update_time,
storeyInfo.f_create_time,
storeyInfo.f_alarm_time,
......@@ -67,6 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="queryByDepartmentId" parameterType="long" resultMap="TStoreyInfoResult">
<include refid="selectTStoreyInfoVo"/>
where storeyInfo.f_equipment_id = #{fEquipmentId}
order by storeyInfo.f_storey_id asc
</select>
<select id="selectTStoreyInfoById" parameterType="Long" resultMap="TStoreyInfoResult">
......@@ -88,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_status,
f_port,
f_aging_start_time,
f_aging_end_time,
f_update_time,
f_create_time,
f_alarm_time,
......@@ -176,9 +180,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<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 = null,</if>
<if test="fAgingEndTime != null">f_aging_end_time = #{fAgingEndTime},</if>
<if test="fAgingEndTime == null">f_aging_end_time = null,</if>
<if test="fUpdateTime != null">f_update_time = #{fUpdateTime},</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 = null,</if>
<if test="blankingCommand != null">f_blanking_command = #{blankingCommand},</if>
<if test="feedingCommand != null">f_feeding_command = #{feedingCommand},</if>
......@@ -191,6 +198,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="fStatus != null">f_status = #{fStatus},</if>
<if test="fAgingStartTime != null">f_aging_start_time = #{fAgingStartTime},</if>
<if test="fAgingStartTime == null">f_aging_start_time = null,</if>
<if test="fAgingEndTime != null">f_aging_end_time = #{fAgingEndTime},</if>
<if test="fAgingEndTime == null">f_aging_end_time = null,</if>
</trim>
</update>
......@@ -199,6 +208,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<trim prefix="SET" suffixOverrides=",">
<if test="fStatus != null">f_status = #{fStatus},</if>
<if test="fAgingStartTime == null">f_aging_start_time = null,</if>
<if test="fAgingEndTime == null">f_aging_end_time = null,</if>
<if test="fUpdateTime != null">f_update_time = #{fUpdateTime},</if>
</trim>
where f_storey_code = #{fStoreyCode}
......
......@@ -57,6 +57,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectTTrayInfoVo"/>
where f_tray_code = #{fTrayCode}
</select>
<select id="selectTTrayInfoByStoreyCode" parameterType="string" resultMap="TTrayInfoResult">
<include refid="selectTTrayInfoVo"/>
where f_storey_code = #{fStoreyCode}
</select>
<insert id="insertTTrayInfo" parameterType="TTrayInfo" useGeneratedKeys="true" keyProperty="fTrayId">
insert into t_tray_info
......@@ -90,6 +94,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
set f_status = #{fStatus}
where f_storey_code = #{fStoreyCode}
</update>
<update id="clearStoreyCodeByStoreyCode" parameterType="string">
update t_tray_info
set f_storey_code = null
where f_storey_code = #{fStoreyCode}
</update>
<update id="updateTTrayInfo" parameterType="TTrayInfo">
update t_tray_info
<trim prefix="SET" suffixOverrides=",">
......
......@@ -34,7 +34,8 @@
'status-running': layer.fStatus === '1',
'status-fault': layer.fStatus === '2',
'status-idle': layer.fStatus === '0',
'status-power_outage': layer.fStatus === '3'
'status-power_outage': layer.fStatus === '3',
'status-completed': layer.fStatus === '4' // 新增完成状态的样式类绑定
}
]"
@click="selectLayer(index)"
......@@ -128,6 +129,14 @@ export default {
trayInfo: {
deep: true,
handler(newVal) {
// 状态为4(完成)时,停止计时器,保留当前时长
if (newVal.fStatus === '4') {
this.stopAgingTimer();
// 计算并显示固定时长
this.agingTimeDisplay = this.calcFixedAgingTime(newVal.fAgingStartTime, newVal.fAgingEndTime);
return;
}
if (newVal.fAgingStartTime) {
this.startAgingTimer(newVal.fAgingStartTime);
} else {
......@@ -162,6 +171,12 @@ export default {
// 启动老化计时器
startAgingTimer(startTime) {
// 状态为4(完成)时,直接返回不启动计时
if (this.trayInfo.fStatus === '4') return;
this.stopAgingTimer();
this.agingStartTime = new Date(startTime);
......@@ -173,7 +188,20 @@ export default {
this.updateAgingTime();
}, 1000);
},
// 计算固定老化时长(fAgingEndTime - fAgingStartTime)
calcFixedAgingTime(startTime, endTime) {
if (!startTime || !endTime) return "00:00:00";
const start = new Date(startTime);
const end = new Date(endTime);
const diff = Math.floor((end - start) / 1000); // 秒数(确保非负)
const hours = Math.floor(diff / 3600);
const minutes = Math.floor((diff % 3600) / 60);
const seconds = diff % 60;
return `${this.padZero(hours)}:${this.padZero(minutes)}:${this.padZero(seconds)}`;
},
// 更新老化时间显示
updateAgingTime() {
if (!this.agingStartTime) return;
......@@ -213,12 +241,18 @@ export default {
productModel: `PQC-${1000 + i}`,
status: this.getStatusText(item.fStatus),
statusClass: this.getStatusClass(item.fStatus),
fAgingStartTime: item.fAgingStartTime
fAgingStartTime: item.fAgingStartTime,
fAgingEndTime: item.fAgingEndTime
}));
// 默认选中第一层
if (this.layers.length > 0) {
this.selectLayer(0);
// 默认选中层为完成状态时,初始化固定时长
const firstLayer = this.layers[0];
if (firstLayer.fStatus === '4') {
this.agingTimeDisplay = this.calcFixedAgingTime(firstLayer.fAgingStartTime, firstLayer.fAgingEndTime);
}
}
} else {
this.$message.error("加载层数据失败");
......@@ -238,6 +272,14 @@ export default {
// 选择层
selectLayer(index) {
this.trayInfo = { ...this.layers[index] };
const currentLayer = this.layers[index];
// 状态为4(完成)时,计算固定时长
if (currentLayer.fStatus === '4') {
this.agingTimeDisplay = this.calcFixedAgingTime(currentLayer.fAgingStartTime, currentLayer.fAgingEndTime);
this.stopAgingTimer(); // 确保停止计时
}
},
// 操作按钮方法
......@@ -275,6 +317,7 @@ export default {
case '1': return 'status-running';
case '2': return 'status-fault';
case '3': return 'status-power_outage';
case '4': return 'status-completed'; // 新增完成状态类名
default: return 'status-idle';
}
},
......@@ -286,6 +329,7 @@ export default {
case '1': return '运行中';
case '2': return '故障';
case '3': return '断电';
case '4': return '完成';
default: return '未知状态';
}
},
......@@ -738,4 +782,35 @@ export default {
color: #66b1ff;
transform: translateX(-5px);
}
/* 层状态样式 - 完成 */
.status-completed {
/* 完成状态 - 青色 #409EFF */
color: #409EFF;
border: 1px solid rgba(64, 158, 255, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(64, 158, 255, 0.5);
}
.status-completed.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(64, 158, 255, 0.4)
rgba(64, 158, 255, 0.8)
rgba(64, 158, 255, 0.8)
rgba(64, 158, 255, 0.4);
}
.status-completed:hover {
background: linear-gradient(to bottom, rgba(64, 158, 255, 0.2), rgba(0,0,0,0.3));
box-shadow:
0 4px 15px rgba(64, 158, 255, 0.4),
inset 0 0 10px rgba(64, 158, 255, 0.4);
}
/* 右侧信息区 - 完成状态文本样式 */
.info-value.status-completed {
color: #409EFF;
font-weight: bold;
}
</style>
......@@ -42,7 +42,7 @@
<div class="status-bar">
<i class="fas fa-info-circle"></i>
<span v-if="trayStatus === '0'">已绑定设备: <span class="filled-count">{{ filledCount }}</span>/72</span>
<span v-if="trayStatus === '2'">待处理异常设备: <span class="filled-count">{{ abnormalCount }}</span>/{{ initialAbnormalCount }}</span>
<span v-if="trayStatus === '3'">待处理异常设备: <span class="filled-count">{{ abnormalCount }}</span>/{{ initialAbnormalCount }}</span>
</div>
</div>
</div>
......@@ -57,7 +57,7 @@
:class="{
'active': activeCell === index,
'empty': !device.deviceCode,
'error': device.deviceCode && device.status === '0'
'error': device.deviceCode && (device.status === '0' || device.status === '5')
}"
@click="setActiveCell(index)"
>
......@@ -87,7 +87,7 @@
:disabled="bindButtonDisabled"
>
<i class="fas fa-paper-plane"></i>
{{ trayStatus === '2' ? '提交修复' : '提交绑定' }}
{{ trayStatus === '3' ? '提交修复' : '提交绑定' }}
</button>
</div>
......@@ -95,12 +95,12 @@
<div class="instructions">
<h3><i class="fas fa-lightbulb"></i> 使用说明</h3>
<ul>
<li v-if="trayStatus === '0'">1. 点击矩阵中的单元格或按顺序扫描<span class="highlight">设备条码</span>(扫描枪自动识别)</li>
<li v-if="trayStatus === '0'">2. 设备条码会自动填充到当前激活的单元格中</li>
<li v-if="trayStatus === '2'">1. 请扫描异常设备条码进行修复处理</li>
<li v-if="trayStatus === '2'">2. 每修复一个异常设备,待处理数量将减少</li>
<li>3. 可以手动点击任何单元格进行修改或重新扫描</li>
<li>4. 完成操作后,点击<span class="highlight">{{ trayStatus === '2' ? '提交修复' : '提交绑定' }}</span>按钮</li>
<li v-if="trayStatus === '0'"> 点击矩阵中的单元格或按顺序扫描<span class="highlight">设备条码</span>(扫描枪自动识别)</li>
<li v-if="trayStatus === '0'"> 设备条码会自动填充到当前激活的单元格中</li>
<li v-if="trayStatus === '3'"> 请扫描异常设备条码进行修复处理</li>
<li v-if="trayStatus === '3'"> 每修复一个异常设备,待处理数量将减少</li>
<li> 可以手动点击任何单元格进行修改或重新扫描</li>
<li> 完成操作后,点击<span class="highlight">{{ trayStatus === '3' ? '提交修复' : '提交绑定' }}</span>按钮</li>
<li><i class="fas fa-bolt scanner-icon"></i> 提示:使用扫描枪时,请确保输入框获得焦点</li>
</ul>
</div>
......@@ -158,7 +158,7 @@ export default {
trayInput: '',
// 托盘状态
trayStatus: '0', // 0:空闲, 1:运行中, 2:标检完成
trayStatus: '0', // 0:空闲, 1:运行中, 2:老化完成,3:标定完成
// 异常设备计数
abnormalCount: 0,
......@@ -191,7 +191,7 @@ export default {
bindButtonDisabled() {
if (this.trayStatus === '0') {
return this.filledCount === 0 || !this.fTrayCode;
} else if (this.trayStatus === '2') {
} else if (this.trayStatus === '3') {
return this.abnormalCount > 0;
}
return true;
......@@ -202,7 +202,8 @@ export default {
const map = {
'0': 'success', // 空闲 - 绿色
'1': 'warning', // 运行中 - 黄色
'2': 'danger' // 标检完成 - 红色
'2': 'danger', // 老化完成 - 红色
'3': 'primary' // 标定完成
};
return map[this.trayStatus] || 'info';
},
......@@ -212,14 +213,15 @@ export default {
const map = {
'0': '空闲',
'1': '运行中',
'2': '标检完成'
'2': '老化完成',
'3': '标定完成'
};
return map[this.trayStatus] || '未知状态';
},
// 扫描输入框提示语
scanPlaceholder() {
return this.trayStatus === '2'
return this.trayStatus === '3'
? '扫描异常设备条码...'
: '扫描设备条码...';
}
......@@ -234,10 +236,10 @@ export default {
if(res.code === 200 && res.data.length > 0) {
this.devices = res.data;
// 计算初始异常设备数量(标完成状态)
if (this.trayStatus === '2') {
// 计算初始异常设备数量(标完成状态)
if (this.trayStatus === '3') {
this.abnormalCount = this.devices.filter(
d => d.deviceCode && d.fstatus != '1'
d => d.deviceCode && d.status !== '1'
).length;
this.initialAbnormalCount = this.abnormalCount;
}
......@@ -298,11 +300,11 @@ export default {
if (this.deviceInput) {
// 标完成状态下的特殊处理
if (this.trayStatus === '2') {
// 标完成状态下的特殊处理
if (this.trayStatus === '3') {
// 检查扫描的设备是否是异常设备
const deviceIndex = this.devices.findIndex(
d => d.deviceCode === this.deviceInput && d.status === '0'
d => d.deviceCode === this.deviceInput && (d.status === '0' || d.status === '5')
);
if (deviceIndex !== -1) {
......@@ -370,8 +372,8 @@ export default {
})
}
}
} else if (this.trayStatus === '2') {
// 标完成状态 - 提交修复
} else if (this.trayStatus === '3') {
// 标完成状态 - 提交修复
batchUpdateDeviceCode(this.devices).then(res => {
if (res.code === 200) {
this.msgSuccess("修复信息提交成功");
......
......@@ -51,7 +51,8 @@
<el-table-column prop="status" label="状态" align="center">
<template slot-scope="scope">
<el-tag :type="statusTagType(scope.row.fStatus)" v-if="scope.row.fStatus === '1'" class="status-tag">运行中</el-tag>
<el-tag :type="statusTagType(scope.row.fStatus)" v-else-if="scope.row.fStatus === '2'" class="status-tag">质检完成</el-tag>
<el-tag :type="statusTagType(scope.row.fStatus)" v-else-if="scope.row.fStatus === '2'" class="status-tag">老化完成</el-tag>
<el-tag :type="statusTagType(scope.row.fStatus)" v-else-if="scope.row.fStatus === '3'" class="status-tag">标定完成</el-tag>
<el-tag :type="statusTagType(scope.row.fStatus)" v-else class="status-tag">空闲</el-tag>
</template>
</el-table-column>
......@@ -177,6 +178,7 @@ export default {
const statusMap = {
'0': 'success',
'2': 'warning',
'3': 'warning',
'1': 'primary',
};
return statusMap[status] || 'info';
......
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