Browse Source

feat:计算喷涂剩余时间

master
白凤吉 2 weeks ago
parent
commit
3b8af4b584
  1. 4
      src/main/java/com/qyft/ms/app/common/constant/WebSocketMessageType.java
  2. 1
      src/main/java/com/qyft/ms/app/controller/SprayTaskController.java
  3. 135
      src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java
  4. 7
      src/main/java/com/qyft/ms/app/device/status/SprayTask.java
  5. 18
      src/main/java/com/qyft/ms/app/model/vo/SprayTaskStatusVO.java
  6. 26
      src/main/java/com/qyft/ms/system/common/utils/TimeUtils.java

4
src/main/java/com/qyft/ms/app/common/constant/WebSocketMessageType.java

@ -18,6 +18,10 @@ public class WebSocketMessageType {
*/
public static final String SPRAY_POINT = "spray_point";
/**
* 喷涂预计完成时间
*/
public static final String SPRAY_TASK_FINISH_TIME = "spray_task_finish_time";
/**
* 传感器
*/
public static final String SENSOR = "sensor";

1
src/main/java/com/qyft/ms/app/controller/SprayTaskController.java

@ -31,6 +31,7 @@ public class SprayTaskController {
sprayTaskStatusVO.setCmdCode(sprayTask.getCmdCode());
sprayTaskStatusVO.setSprayTaskSprayedList(sprayTask.getSprayTaskSprayedList());
sprayTaskStatusVO.setSprayTaskParams(sprayTask.getSprayTaskParams());
sprayTaskStatusVO.setFinishTime(sprayTask.getFinishTimeMap());
return Result.success(sprayTaskStatusVO);
}

135
src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java

@ -16,6 +16,7 @@ import com.qyft.ms.system.common.constant.CommandStatus;
import com.qyft.ms.system.common.device.command.CommandFuture;
import com.qyft.ms.system.common.device.command.DeviceCommandGenerator;
import com.qyft.ms.system.common.device.command.FrontResponseGenerator;
import com.qyft.ms.system.common.utils.TimeUtils;
import com.qyft.ms.system.model.bo.DeviceCommand;
import com.qyft.ms.system.service.WebSocketService;
import com.qyft.ms.system.service.device.DeviceCommandService;
@ -24,9 +25,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
@ -74,6 +75,10 @@ public class SprayTaskExecutor {
taskThread = new Thread(() -> {
try {
webSocketService.pushCMDResponseMsg(FrontResponseGenerator.generateJson(sprayTask.getCmdId(), sprayTask.getCmdCode(), CommandStatus.START, "喷涂任务开始执行"));
//计算剩余时间
List<Map<String, Object>> finishTimeMap = estimateCompletionTimePerSlide();
webSocketService.pushMsg(WebSocketMessageType.SPRAY_TASK_FINISH_TIME, finishTimeMap);//向前端推送当前路径
sprayTask.setFinishTimeMap(finishTimeMap);
List<SprayTaskParams> sprayTaskParams = sprayTask.getSprayTaskParams();
for (SprayTaskParams sprayTaskParam : sprayTaskParams) {//循环玻片
if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() < sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过
@ -107,7 +112,7 @@ public class SprayTaskExecutor {
SysSettings slideHeightSysSettings = sysSettingsService.getOne(new LambdaQueryWrapper<SysSettings>().eq(SysSettings::getCode, "slide_height"));
Double slideHeight = Double.parseDouble(slideHeightSysSettings.getValue());
Double height = slideHeight - sprayTimes.getMotorZHeight();//下降z轴高度
DeviceCommand motorZPositionSetAboveSlideCommand = DeviceCommandGenerator.motorZPositionSet(height, 15.0);
DeviceCommand motorZPositionSetAboveSlideCommand = DeviceCommandGenerator.motorZPositionSet(height, 20.0);
CommandFuture motorZPositionSetAboveSlideCommandFuture = deviceCommandService.sendCommandSprayTask(sprayTask.getCmdId(), sprayTask.getCmdCode(), motorZPositionSetAboveSlideCommand);
commandWait(motorXPositionSetCommandFuture, motorYPositionSetCommandFuture, motorZPositionSetAboveSlideCommandFuture);
}
@ -305,6 +310,128 @@ public class SprayTaskExecutor {
return sprayTaskStepList;
}
/**
* 返回一个 List每个元素对应 sprayTaskParams 中每个玻片的预计耗时毫秒
*/
public List<Map<String, Object>> estimateCompletionTimePerSlide() {
SprayTask task = SprayTask.getInstance();
List<SprayTaskParams> params = task.getSprayTaskParams();
int resumeIndex = task.getCurrentIndex() == null ? 0 : task.getCurrentIndex();
int resumeSprayNum = task.getSprayNum();
// 常量
final long COMMAND_LATENCY_MS = 12;// 指令往返平均耗时(ms)
final double HOME_SPEED_X = 20;// 回原点速度 X (mm/s)
final double HOME_SPEED_Y = 20;// 回原点速度 Y (mm/s)
final double HOME_SPEED_Z = 20;// 回原点速度 Z (mm/s)
final long PER_SPRAY_OVERHEAD_MS = 1;// 每次喷涂固定附加时间(ms)
final long TOTAL_JOB_OVERHEAD_MS = 1;// 全局附加时间(ms)
double slideHeight = Double.parseDouble(
sysSettingsService.getOne(
new LambdaQueryWrapper<SysSettings>()
.eq(SysSettings::getCode, "slide_height"))
.getValue()
);
LocalDateTime startTime = LocalDateTime.now();
List<Map<String, Object>> finishTimeMap = new ArrayList<>(params.size());
long cumulativeMs = 0;
for (int i = 0; i < params.size(); i++) {
SprayTaskParams p = params.get(i);
int idx = p.getIndex();
// 如果此玻片已在之前完成直接添加当前累计时间
if (idx < resumeIndex) {
Map<String, Object> ft = new HashMap<>();
ft.put("index", p.getIndex());
ft.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs)));
finishTimeMap.add(ft);
continue;
}
long sliceMs = 0;
// 1) 移到玻片起点 + 下降
Double[] slide = slideArr[idx];
SprayTimes firstT = p.getTimes().get(0);
sliceMs += moveMs(0, slide[0], firstT.getMovingSpeed(), COMMAND_LATENCY_MS);
sliceMs += moveMs(0, slide[1], firstT.getMovingSpeed(), COMMAND_LATENCY_MS);
double targetZ = slideHeight - firstT.getMotorZHeight();
sliceMs += moveMs(0, targetZ, 20, COMMAND_LATENCY_MS);
// 2) 管路切换 & 等待已在中途恢复时跳过
boolean switched = (idx == resumeIndex && resumeSprayNum > 1);
if (!switched) {
sliceMs += COMMAND_LATENCY_MS + 500;//500是切换注射器固定等待时间
}
// 3) 喷涂循环
double curX = slide[0], curY = slide[1];
int sprayCount = 1;
for (SprayTimes t : p.getTimes()) {
// 跳过已完成的喷涂轮次
if (idx == resumeIndex && sprayCount < resumeSprayNum) {
// grid 模式双计数
if ("grid".equals(t.getMatrixPathType())) sprayCount++;
sprayCount++;
continue;
}
// 开阀//高压指令
sliceMs += COMMAND_LATENCY_MS * 3;
if (t.getHighVoltage()) sliceMs += COMMAND_LATENCY_MS * 2;
// 路径移动
for (SprayTaskStep step : getSprayPath(t)) {
for (Point2D pt : step.getSprayPathPointList()) {
double nx = slide[0] + pt.getX();
double ny = slide[1] + pt.getY();
sliceMs += moveMs(curX, nx, t.getMovingSpeed(), COMMAND_LATENCY_MS);
curX = nx;
sliceMs += moveMs(curY, ny, t.getMovingSpeed(), COMMAND_LATENCY_MS);
curY = ny;
}
// 结束阀门指令 + grid 延时
sliceMs += COMMAND_LATENCY_MS * 3;
if ("grid".equals(t.getMatrixPathType()) && t.getGridDelay() != null) {
sliceMs += t.getGridDelay() * 1000;
}
}
// 喷涂后延时 + 附加时间
sliceMs += t.getDelay() * 1000;
sliceMs += PER_SPRAY_OVERHEAD_MS;
sprayCount++;
}
// 4) 回原点
sliceMs += moveMs(curX, 0, HOME_SPEED_X, COMMAND_LATENCY_MS);
sliceMs += moveMs(curY, 0, HOME_SPEED_Y, COMMAND_LATENCY_MS);
sliceMs += moveMs(targetZ, 0, HOME_SPEED_Z, COMMAND_LATENCY_MS);
// 累计并加全局附加
cumulativeMs += sliceMs;
if (i == params.size() - 1) cumulativeMs += TOTAL_JOB_OVERHEAD_MS;
Map<String, Object> ft = new HashMap<>();
ft.put("index", p.getIndex());
ft.put("finishTime", TimeUtils.toEpochMilli(startTime.plus(Duration.ofMillis(cumulativeMs))));
finishTimeMap.add(ft);
}
return finishTimeMap;
}
/**
* 计算单轴移动耗时 + 指令平均延迟
*
* @param from 起始坐标 (mm)
* @param to 目标坐标 (mm)
* @param speed 运动速度 (mm/s)
* @param cmdDelay 每条指令往返延迟 (ms)
*/
private long moveMs(double from, double to, double speed, long cmdDelay) {
double dist = Math.abs(to - from);
return (long) (dist / speed * 1000) + cmdDelay;
}
private void delay(long millisecond) throws InterruptedException {
delay(null, millisecond);
}

7
src/main/java/com/qyft/ms/app/device/status/SprayTask.java

@ -8,6 +8,7 @@ import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Data
public class SprayTask {
@ -64,7 +65,10 @@ public class SprayTask {
* 已喷涂点位
*/
private volatile List<SprayTaskSprayed> sprayTaskSprayedList = new ArrayList<>();
/**
* 喷涂任务预计剩余时间
*/
private List<Map<String, Object>> finishTimeMap = null;
/**
* 标志喷涂任务暂停
*/
@ -104,6 +108,7 @@ public class SprayTask {
operationLogId = null;
remainingGridDelay = null;
remainingDelay = null;
finishTimeMap = null;
sprayTaskSprayedList.clear();
}

18
src/main/java/com/qyft/ms/app/model/vo/SprayTaskStatusVO.java

@ -13,25 +13,19 @@ import java.util.Map;
*/
@Data
public class SprayTaskStatusVO {
/**
* 前端指令id
*/
private String cmdId;
/**
* 前端指令code
*/
private String cmdCode;
/**
* 已喷涂点位
*/
@Schema(description = "已喷涂点位")
private volatile List<SprayTaskSprayed> sprayTaskSprayedList;
/**
* 喷涂参数
*/
@Schema(description = "喷涂参数")
private List<SprayTaskParams> sprayTaskParams;
@Schema(description = "喷涂任务预计剩余时间")
private List<Map<String, Object>> finishTime;
}

26
src/main/java/com/qyft/ms/system/common/utils/TimeUtils.java

@ -0,0 +1,26 @@
package com.qyft.ms.system.common.utils;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class TimeUtils {
/**
* LocalDateTime 转为毫秒级时间戳使用系统默认时区
*/
public static long toEpochMilli(LocalDateTime ldt) {
return ldt
.atZone(ZoneId.systemDefault()) // LocalDateTime ZonedDateTime系统默认时区
.toInstant() // ZonedDateTime Instant
.toEpochMilli(); // Instant 毫秒时间戳
}
/**
* LocalDateTime 转为秒级时间戳使用系统默认时区
*/
public static long toEpochSecond(LocalDateTime ldt) {
return ldt
.atZone(ZoneId.systemDefault())
.toInstant()
.getEpochSecond();
}
}
Loading…
Cancel
Save