add:增加7月份的一些新增需求实现;
This commit is contained in:
@@ -66,6 +66,8 @@ public interface AcsConfig {
|
||||
String AutoCleanDays = "AutoCleanDays";
|
||||
//最大任务下发时间
|
||||
String MAXSENDTASKTIME = "maxSendTaskTime";
|
||||
//任务超时时间阈值(分钟),超过该时间未完成的任务视为超时异常
|
||||
String TASKTIMEOUTMINUTES = "taskTimeoutMinutes";
|
||||
//指令下发立库
|
||||
String INSTSENDLK = "instSendLk";
|
||||
/**
|
||||
|
||||
@@ -23,12 +23,14 @@ import org.nl.acs.ext.wms.service.AcsToWmsService;
|
||||
import org.nl.acs.ext.wms.service.impl.AcsToWmsServiceImpl;
|
||||
import org.nl.acs.history.ErrorUtil;
|
||||
import org.nl.acs.instruction.domain.Instruction;
|
||||
import org.nl.acs.instruction.enums.InstructionStatusEnum;
|
||||
import org.nl.acs.instruction.service.InstructionService;
|
||||
import org.nl.acs.instruction.service.impl.InstructionServiceImpl;
|
||||
import org.nl.acs.log.LokiLog;
|
||||
import org.nl.acs.log.LokiLogType;
|
||||
import org.nl.acs.log.service.DeviceExecuteLogService;
|
||||
import org.nl.acs.opc.DeviceAppService;
|
||||
import org.nl.acs.task.enums.TaskStatusEnum;
|
||||
import org.nl.acs.task.service.TaskService;
|
||||
import org.nl.acs.task.service.dto.TaskDto;
|
||||
import org.nl.acs.task.service.impl.TaskServiceImpl;
|
||||
@@ -93,8 +95,9 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
*/
|
||||
@Data
|
||||
private static class FeiShuErrorRecord {
|
||||
private int num = 0; // 收到同一报警的次数
|
||||
private long lastSendTime = 0; // 最后一次推送的时间戳(毫秒)
|
||||
private int pushCount = 0; // 已推送同一报警的次数
|
||||
private long lastSendTime = 0; // 最后一次推送的时间戳(毫秒)
|
||||
private long lastReceiveTime = 0; // 最后一次收到报警的时间戳(毫秒),用于缓存过期判断
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +106,8 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
private volatile String feiShuSendTime = "8:00-22:00";
|
||||
private volatile String feiShuSendFre = "2";
|
||||
private volatile long lastParamLoadTime = 0;
|
||||
private static final long PARAM_CACHE_TIMEOUT = 600000; // 参数缓存超时时间:120秒
|
||||
private static final long PARAM_CACHE_TIMEOUT = 600000; // 参数缓存超时时间:600秒
|
||||
private static final long FEI_SHU_ERROR_CACHE_TIMEOUT = 5 * 60 * 1000; // 飞书报警缓存过期时间:5分钟
|
||||
|
||||
String transportOrder = "";
|
||||
boolean isCharge = false;
|
||||
@@ -197,7 +201,7 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
//到达取货点
|
||||
//(需要WCS反馈)
|
||||
} else if (phase == 0x03) {
|
||||
inst.setExecute_status("1");
|
||||
inst.setExecute_status(InstructionStatusEnum.BUSY.getIndex());
|
||||
instructionService.update(inst);
|
||||
//添加车号
|
||||
if (StringUtils.isBlank(task.getCar_no())) {
|
||||
@@ -212,7 +216,9 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
//(需要WCS反馈)
|
||||
} else if (phase == 0x05) {
|
||||
data = ndcAgvService.sendAgvOneModeInst(phase, index, 0);
|
||||
task.setTask_status("4");
|
||||
inst.setExecute_status(InstructionStatusEnum.PICKUP.getIndex());
|
||||
instructionService.update(inst);
|
||||
task.setTask_status(TaskStatusEnum.FINISHEDMOVE.getIndex());
|
||||
//车辆执行任务开始计时字段
|
||||
if (StringUtils.isBlank(task.getTo_x())) {
|
||||
task.setTo_x(DateUtil.now());
|
||||
@@ -384,35 +390,28 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
int endMinutes = endHour * 60 + endMinute;
|
||||
// 判断当前时间是否在工作时间内
|
||||
if (nowMinutes < startMinutes || nowMinutes >= endMinutes) {
|
||||
// log.info("当前时间{}不在工作时间范围内({}),跳过发送飞书消息",
|
||||
// DateUtil.formatTime(now), feiShuSendTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. 计数与间隔推送逻辑:第1次收到不推送,第2次收到首次推送,之后按间隔推送
|
||||
String cacheKey = this.getDeviceCode() + errorMessage;
|
||||
// 2. 全局缓存过期清理:清除超过5分钟未收到报警的记录
|
||||
long currentTime = System.currentTimeMillis();
|
||||
cleanExpiredFeiShuErrorCache(currentTime);
|
||||
// 3. 计数与间隔推送逻辑:首次推送至feiShuUrl1,累计推送5次后同时推送至feiShuUrl1和feiShuUrl2
|
||||
String cacheKey = this.getDeviceCode() + errorMessage;
|
||||
FeiShuErrorRecord record = feiShuErrorCache.get(cacheKey);
|
||||
if (record == null) {
|
||||
// 第一次收到该报警,num=1,不推送
|
||||
record = new FeiShuErrorRecord();
|
||||
record.setNum(1);
|
||||
feiShuErrorCache.put(cacheKey, record);
|
||||
return;
|
||||
}
|
||||
// 每次收到报警,计数+1;超过5次则清除缓存,重新开始计数
|
||||
record.setNum(record.getNum() + 1);
|
||||
if (record.getNum() > 5) {
|
||||
feiShuErrorCache.remove(cacheKey);
|
||||
return;
|
||||
}
|
||||
if (record.getNum() > 2) {
|
||||
// num > 2,已推送过,按间隔时间控制后续推送
|
||||
// 更新最后收到报警的时间
|
||||
record.setLastReceiveTime(currentTime);
|
||||
// 频率控制:非首次推送时,检查是否在推送间隔内
|
||||
if (record.getPushCount() > 0) {
|
||||
int feiShuSendFreMin = Integer.parseInt(feiShuSendFre);
|
||||
long diffTime = currentTime - record.getLastSendTime();
|
||||
if (diffTime < feiShuSendFreMin * 60000L) {
|
||||
log.debug("设备{}的{}在{}分钟内已有过提醒,过滤掉本次重复提醒(距今{}ms)",
|
||||
log.info("设备{}的{}在{}分钟内已有过提醒,过滤掉本次重复提醒(距今{}ms)",
|
||||
this.getDeviceCode(), errorMessage, feiShuSendFreMin, diffTime);
|
||||
return;
|
||||
}
|
||||
@@ -460,22 +459,35 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
post.put("zh_cn", zh_cn);
|
||||
content.put("post", post);
|
||||
requestBody.put("content", content);
|
||||
String feiShuUrl = "https://open.feishu.cn/open-apis/bot/v2/hook/c3fcb100-72e0-48e6-bdf6-e266e67858a1";
|
||||
// 5. 发送 HTTP 请求到飞书机器人
|
||||
String response = HttpRequest.post(feiShuUrl)
|
||||
String feiShuUrl1 = "https://open.feishu.cn/open-apis/bot/v2/hook/c3fcb100-72e0-48e6-bdf6-e266e67858a1";
|
||||
String feiShuUrl2 = "https://open.feishu.cn/open-apis/bot/v2/hook/c3fcb100-72e0-48e6-bdf6-e266e67858a2";
|
||||
// 5. 发送 HTTP 请求到飞书机器人(首次~第5次仅推送到feiShuUrl1,第6次起同时推送到feiShuUrl1和feiShuUrl2)
|
||||
boolean pushToBoth = record.getPushCount() >= 5;
|
||||
String response1 = HttpRequest.post(feiShuUrl1)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(requestBody.toJSONString())
|
||||
.timeout(15000) // 5秒超时
|
||||
.timeout(15000) // 15秒超时
|
||||
.execute()
|
||||
.body();
|
||||
if (pushToBoth) {
|
||||
log.info("设备{}的{}已连续推送{}次,同时推送至feiShuUrl2",
|
||||
this.getDeviceCode(), errorMessage, record.getPushCount() + 1);
|
||||
HttpRequest.post(feiShuUrl2)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(requestBody.toJSONString())
|
||||
.timeout(15000)
|
||||
.execute()
|
||||
.body();
|
||||
}
|
||||
// 6. 检查响应结果并记录日志
|
||||
JSONObject jsonResponse = JSONObject.parseObject(response);
|
||||
JSONObject jsonResponse = JSONObject.parseObject(response1);
|
||||
if (jsonResponse != null && jsonResponse.getIntValue("code") == 0) {
|
||||
//log.info("设备{}的故障消息发送成功:{}", this.getDeviceCode(), errorMessage);
|
||||
// 更新记录:记录最后发送时间
|
||||
log.info("设备{}的报警消息发送成功:{}", this.getDeviceCode(), errorMessage);
|
||||
// 更新记录:记录最后发送时间和推送计数
|
||||
record.setLastSendTime(currentTime);
|
||||
record.setPushCount(record.getPushCount() + 1);
|
||||
} else {
|
||||
log.error("设备{}的故障消息发送失败:{}", this.getDeviceCode(), response);
|
||||
log.error("设备{}的故障消息发送失败:{}", this.getDeviceCode(), response1);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -507,5 +519,16 @@ public class AgvNdcOneDeviceDriver extends AbstractDeviceDriver implements Devic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的飞书报警错误缓存:遍历所有缓存条目,清除超过5分钟未收到报警的记录
|
||||
*
|
||||
* @param currentTime 当前时间戳(毫秒)
|
||||
*/
|
||||
private void cleanExpiredFeiShuErrorCache(long currentTime) {
|
||||
long expireThreshold = currentTime - FEI_SHU_ERROR_CACHE_TIMEOUT;
|
||||
feiShuErrorCache.entrySet().removeIf(entry ->
|
||||
entry.getValue().getLastReceiveTime() > 0
|
||||
&& entry.getValue().getLastReceiveTime() < expireThreshold);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ public enum InstructionStatusEnum {
|
||||
*/
|
||||
READY("0", "READY", "就绪"),
|
||||
BUSY("1", "BUSY", "执行中"),
|
||||
PICKUP("1.1", "FINISHED_MOVE", "取货完成"),
|
||||
RELEASE("1.2", "FINISHED_PUT", "放货完成"),
|
||||
FINISHED("2", "FINISHED", "完成"),
|
||||
|
||||
CANCEL("3", "CANCEL", "取消"),
|
||||
|
||||
ERROR("99", "CANCEL", "异常");
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.nl.acs.route.service.dto.RouteLineDto;
|
||||
import org.nl.acs.route.service.impl.RouteLineServiceImpl;
|
||||
import org.nl.acs.task.TaskInstructionLock;
|
||||
import org.nl.acs.task.domain.Task;
|
||||
import org.nl.acs.task.enums.TaskStatusEnum;
|
||||
import org.nl.acs.task.service.TaskService;
|
||||
import org.nl.acs.task.service.dto.TaskDto;
|
||||
import org.nl.acs.task.service.mapper.TaskMapper;
|
||||
@@ -188,7 +189,9 @@ public class InstructionServiceImpl extends CommonServiceImpl<InstructionMapper,
|
||||
String priority = (String) whereJson.get("priority");
|
||||
String is_over = (String) whereJson.get("is_over");
|
||||
String instruction_type = (String) whereJson.get("instruction_type");
|
||||
|
||||
if (StringUtils.isNotBlank(status) && TaskStatusEnum.FINISHEDMOVE.getIndex().equals(status)) {
|
||||
status = InstructionStatusEnum.PICKUP.getIndex();
|
||||
}
|
||||
IPage<InstructionMybatis> queryPage = PageUtil.toMybatisPage(page);
|
||||
LambdaQueryWrapper<InstructionMybatis> wrapper = new LambdaQueryWrapper<>();
|
||||
// wrapper.eq(InstructionMybatis::getIs_delete,0);
|
||||
@@ -199,7 +202,7 @@ public class InstructionServiceImpl extends CommonServiceImpl<InstructionMapper,
|
||||
wrapper.eq(StringUtils.isNotBlank(status), InstructionMybatis::getInstruction_status, status);
|
||||
wrapper.and(StringUtils.isNotBlank(point_code), instructionMybatis -> instructionMybatis.like(InstructionMybatis::getStart_point_code, point_code).or().like(InstructionMybatis::getNext_point_code, point_code));
|
||||
wrapper.eq(StringUtils.isNotBlank(instruction_type), InstructionMybatis::getInstruction_type, instruction_type);
|
||||
wrapper.le(InstructionMybatis::getInstruction_status, 1);
|
||||
wrapper.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.FINISHED.getIndex());
|
||||
if (!StrUtil.isEmpty(is_over)) {
|
||||
if (StrUtil.equals(is_over, CommonFinalParam.ONE)) {
|
||||
wrapper.ge(InstructionMybatis::getInstruction_status, InstructionStatusEnum.FINISHED.getIndex());
|
||||
@@ -587,13 +590,11 @@ public class InstructionServiceImpl extends CommonServiceImpl<InstructionMapper,
|
||||
// + "'")
|
||||
// .uniqueResult(0);
|
||||
InstructionMybatis ins = new LambdaQueryChainWrapper<>(instructionMapper)
|
||||
.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.CANCEL.getIndex())
|
||||
.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.FINISHED.getIndex())
|
||||
.eq(InstructionMybatis::getNext_point_code, dto.getNext_point_code())
|
||||
.eq(InstructionMybatis::getStart_point_code, dto.getStart_point_code())
|
||||
.eq(InstructionMybatis::getTask_id, dto.getTask_id())
|
||||
.one();
|
||||
|
||||
|
||||
if (ins != null) {
|
||||
throw new Exception(dto.getTask_code() + ":该任务已存在待完成指令!");
|
||||
}
|
||||
|
||||
@@ -185,4 +185,11 @@ public class TaskController {
|
||||
public ResponseEntity<Object> queryTaskSheet(@RequestBody Map whereJson) {
|
||||
return new ResponseEntity<>(taskService.queryTaskSheet(whereJson), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@SaIgnore
|
||||
@Log("查询超时异常任务")
|
||||
@PostMapping(value = "/queryTimeoutTask")
|
||||
public ResponseEntity<Object> queryTimeoutTask(@RequestBody Map whereJson) {
|
||||
return new ResponseEntity<>(taskService.queryTimeoutTask(whereJson), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,4 +525,11 @@ public interface TaskService extends CommonService<Task> {
|
||||
|
||||
|
||||
List<JSONObject> queryTaskSheet(Map whereJson);
|
||||
|
||||
/**
|
||||
* 查询超时异常任务(create_time 到 update_time 超过60分钟)
|
||||
* @param whereJson 查询条件
|
||||
* @return 超时任务列表
|
||||
*/
|
||||
List<JSONObject> queryTimeoutTask(Map whereJson);
|
||||
}
|
||||
|
||||
@@ -1032,6 +1032,15 @@ public class TaskServiceImpl extends CommonServiceImpl<TaskMapper, Task> impleme
|
||||
List<InstructionMybatis> unfinishedList = new LambdaQueryChainWrapper<>(instructionMapper)
|
||||
.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.FINISHED.getIndex())
|
||||
.eq(InstructionMybatis::getIs_delete, "0").list();
|
||||
// 检查是否存在相同task_code的指令,如果存在则报错
|
||||
String taskCode = acsTask.getTask_code();
|
||||
InstructionMybatis existByTaskcode = unfinishedList.stream()
|
||||
.filter(r -> StrUtil.equals(r.getTask_code(), taskCode))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (existByTaskcode != null) {
|
||||
throw new BadRequestException("该任务号已存在指令号为:" + existByTaskcode.getInstruction_code() + "的指令,请核查取消后再创建!");
|
||||
}
|
||||
maxManInstNumber = paramService.findByCode(AcsConfig.MAXMANINSTNUMBER).getValue();
|
||||
unfinishedManInstructionCount = unfinishedList.stream().filter(r -> "2".equals(r.getCreate_type())).count();
|
||||
long maxManInst = Long.parseLong(maxManInstNumber);
|
||||
@@ -1732,6 +1741,49 @@ public class TaskServiceImpl extends CommonServiceImpl<TaskMapper, Task> impleme
|
||||
|
||||
|
||||
@Override
|
||||
public List<JSONObject> queryTimeoutTask(Map whereJson) {
|
||||
// 日期格式
|
||||
String create_time = (String) whereJson.get("startDate");
|
||||
String end_time = (String) whereJson.get("endDate");
|
||||
if (StrUtil.isEmpty(create_time) || StrUtil.isEmpty(end_time)) {
|
||||
String today = DateUtil.today();
|
||||
create_time = today + " 00:00:00";
|
||||
end_time = today + " 23:59:59";
|
||||
} else {
|
||||
if (create_time.length() == 10) {
|
||||
create_time += " 00:00:00";
|
||||
}
|
||||
if (end_time.length() == 10) {
|
||||
end_time += " 23:59:59";
|
||||
}
|
||||
}
|
||||
// 查询已完成的任务(task_status = 99 表示完成)
|
||||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Task::getTask_status, TaskStatusEnum.FINISHED.getIndex())
|
||||
.between(Task::getCreate_time, create_time, end_time)
|
||||
.orderByDesc(Task::getCreate_time);
|
||||
List<Task> taskList = taskMapper.selectList(wrapper);
|
||||
// 过滤出 create_time 到 update_time 超过超时阈值的任务
|
||||
int timeoutMinutes = getTaskTimeoutMinutes();
|
||||
List<JSONObject> result = new ArrayList<>();
|
||||
for (Task task : taskList) {
|
||||
Double durationMinutes = calculateDuration(task.getCreate_time(), task.getUpdate_time());
|
||||
if (durationMinutes != null && durationMinutes > timeoutMinutes) {
|
||||
JSONObject jo = new JSONObject();
|
||||
jo.put("task_code", task.getTask_code());
|
||||
jo.put("start_point_code", task.getStart_point_code());
|
||||
jo.put("next_point_code", task.getNext_point_code());
|
||||
jo.put("create_time", task.getCreate_time());
|
||||
jo.put("update_time", task.getUpdate_time());
|
||||
jo.put("to_y", String.format("%.2f", durationMinutes));
|
||||
jo.put("vehicle_code", task.getVehicle_code());
|
||||
result.add(jo);
|
||||
}
|
||||
}
|
||||
log.info("查询超时异常任务,时间范围:{} ~ {},结果数量:{}", create_time, end_time, result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<JSONObject> queryTaskSheet(Map whereJson) {
|
||||
//日期格式"2026-05-21"
|
||||
String create_time = (String) whereJson.get("startDate");
|
||||
@@ -1943,6 +1995,23 @@ public class TaskServiceImpl extends CommonServiceImpl<TaskMapper, Task> impleme
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务超时时间阈值(分钟),从系统参数表读取,默认60分钟
|
||||
*
|
||||
* @return 超时阈值(分钟)
|
||||
*/
|
||||
private int getTaskTimeoutMinutes() {
|
||||
try {
|
||||
String value = paramService.findByCode(AcsConfig.TASKTIMEOUTMINUTES).getValue();
|
||||
if (StrUtil.isNotBlank(value)) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取任务超时时间参数失败,使用默认值60分钟", e);
|
||||
}
|
||||
return 60; // 默认60分钟
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分钟格式化为 小时h分钟m 的字符串
|
||||
*/
|
||||
|
||||
@@ -140,7 +140,7 @@ public class AutoCreateInst {
|
||||
continue;
|
||||
}
|
||||
List<InstructionMybatis> activeInstructions = new LambdaQueryChainWrapper<>(instructionMapper)
|
||||
.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.FINISHED.getIndex())
|
||||
.lt(InstructionMybatis::getInstruction_status, InstructionStatusEnum.PICKUP.getIndex())
|
||||
.eq(InstructionMybatis::getIs_delete, "0")
|
||||
.list();
|
||||
//将执行中的任务也加入指令列表参与起点校验
|
||||
|
||||
Reference in New Issue
Block a user