|
@ -75,10 +75,7 @@ public class SprayTaskExecutor { |
|
|
try { |
|
|
try { |
|
|
webSocketService.pushCMDResponseMsg(FrontResponseGenerator.generateJson(sprayTask.getCmdId(), sprayTask.getCmdCode(), CommandStatus.START, "喷涂任务开始执行")); |
|
|
webSocketService.pushCMDResponseMsg(FrontResponseGenerator.generateJson(sprayTask.getCmdId(), sprayTask.getCmdCode(), CommandStatus.START, "喷涂任务开始执行")); |
|
|
//计算剩余时间 |
|
|
//计算剩余时间 |
|
|
List<Map<String, Object>> finishTimeMap = estimateCompletionTimePerSlide(); |
|
|
|
|
|
FinishTimeWsPushBO finishTimeWsPushBO = new FinishTimeWsPushBO(sprayTask.getCmdId(), sprayTask.getCmdCode(), finishTimeMap); |
|
|
|
|
|
webSocketService.pushMsg(WebSocketMessageType.SPRAY_TASK_FINISH_TIME, finishTimeWsPushBO);//向前端推送当前路径 |
|
|
|
|
|
sprayTask.setFinishTimeMap(finishTimeMap); |
|
|
|
|
|
|
|
|
subFinishTime(); |
|
|
List<SprayTaskParams> sprayTaskParams = sprayTask.getSprayTaskParams(); |
|
|
List<SprayTaskParams> sprayTaskParams = sprayTask.getSprayTaskParams(); |
|
|
for (SprayTaskParams sprayTaskParam : sprayTaskParams) {//循环玻片 |
|
|
for (SprayTaskParams sprayTaskParam : sprayTaskParams) {//循环玻片 |
|
|
if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() <= sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过 |
|
|
if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() <= sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过 |
|
@ -187,6 +184,7 @@ public class SprayTaskExecutor { |
|
|
sprayNum++; |
|
|
sprayNum++; |
|
|
sprayTask.setSprayNum(sprayNum); |
|
|
sprayTask.setSprayNum(sprayNum); |
|
|
sprayTask.setCurrentStep(0); |
|
|
sprayTask.setCurrentStep(0); |
|
|
|
|
|
subFinishTime(); |
|
|
} |
|
|
} |
|
|
sprayTask.setCurrentIndex(sprayTaskParam.getIndex()); |
|
|
sprayTask.setCurrentIndex(sprayTaskParam.getIndex()); |
|
|
sprayTask.setSprayNum(0); |
|
|
sprayTask.setSprayNum(0); |
|
@ -199,6 +197,7 @@ public class SprayTaskExecutor { |
|
|
DeviceCommand nozzleValveCloseCommand = DeviceCommandGenerator.nozzleValveClose();//关闭喷嘴阀 |
|
|
DeviceCommand nozzleValveCloseCommand = DeviceCommandGenerator.nozzleValveClose();//关闭喷嘴阀 |
|
|
CommandFuture nozzleValveCloseCommandFuture = deviceCommandService.sendCommandSprayTask(sprayTask.getCmdId(), sprayTask.getCmdCode(), nozzleValveCloseCommand); |
|
|
CommandFuture nozzleValveCloseCommandFuture = deviceCommandService.sendCommandSprayTask(sprayTask.getCmdId(), sprayTask.getCmdCode(), nozzleValveCloseCommand); |
|
|
commandWait(nozzleValveCloseCommandFuture); |
|
|
commandWait(nozzleValveCloseCommandFuture); |
|
|
|
|
|
sprayTask.setCannotPause(true);//喷涂完毕后就是回原点了,期间不可暂停 |
|
|
|
|
|
|
|
|
//XYZ回原点 |
|
|
//XYZ回原点 |
|
|
DeviceCommand motorXOriginCommand = DeviceCommandGenerator.motorXOrigin(); |
|
|
DeviceCommand motorXOriginCommand = DeviceCommandGenerator.motorXOrigin(); |
|
@ -311,173 +310,132 @@ public class SprayTaskExecutor { |
|
|
return sprayTaskStepList; |
|
|
return sprayTaskStepList; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void subFinishTime() { |
|
|
|
|
|
SprayTask sprayTask = SprayTask.getInstance(); |
|
|
|
|
|
List<Map<String, Object>> finishTimeMap = estimateFinishTimePerSlide(); |
|
|
|
|
|
FinishTimeWsPushBO finishTimeWsPushBO = new FinishTimeWsPushBO(sprayTask.getCmdId(), sprayTask.getCmdCode(), finishTimeMap); |
|
|
|
|
|
webSocketService.pushMsg(WebSocketMessageType.SPRAY_TASK_FINISH_TIME, finishTimeWsPushBO);//向前端推送当前路径 |
|
|
|
|
|
sprayTask.setFinishTimeMap(finishTimeMap); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 返回一个 List,每个元素对应 sprayTaskParams 中每个玻片的预计耗时(毫秒), |
|
|
|
|
|
|
|
|
* 返回每片的预计完成时间列表(包含 index 与 finishTime) |
|
|
*/ |
|
|
*/ |
|
|
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(); |
|
|
|
|
|
int resumeStep = task.getCurrentStep(); |
|
|
|
|
|
|
|
|
|
|
|
// ======================= 常量定义 ======================= |
|
|
|
|
|
final long COMMAND_LATENCY_MS = 12L; // 单条命令平均往返耗时(ms) |
|
|
|
|
|
final double HOME_SPEED_X = 20.0; // X 轴速度(mm/s)——末片回原点/片间移动 |
|
|
|
|
|
final double HOME_SPEED_Y = 20.0; // Y 轴速度(mm/s) |
|
|
|
|
|
final double HOME_SPEED_Z = 20.0; // Z 轴速度(mm/s) |
|
|
|
|
|
final long PER_SPRAY_OVERHEAD_MS = 0L; // 每次喷涂后固定延时(ms) |
|
|
|
|
|
final long TOTAL_JOB_OVERHEAD_MS = 0L; // 全局结束后延时(ms) |
|
|
|
|
|
final long PIPELINE_DELAY_MS = 500L; // 管路切换延时(ms) |
|
|
|
|
|
// ======================================================= |
|
|
|
|
|
|
|
|
|
|
|
double slideHeight = Double.parseDouble( |
|
|
|
|
|
sysSettingsService.getOne( |
|
|
|
|
|
new LambdaQueryWrapper<SysSettings>() |
|
|
|
|
|
.eq(SysSettings::getCode, "slide_height")) |
|
|
|
|
|
.getValue() |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
public List<Map<String, Object>> estimateFinishTimePerSlide() { |
|
|
|
|
|
SprayTask sprayTask = SprayTask.getInstance(); |
|
|
|
|
|
|
|
|
|
|
|
// ===== 常量 ===== |
|
|
|
|
|
final long CMD_LAT_MS = 12L; // 单条命令往返耗时(ms) |
|
|
|
|
|
final long PER_OVERHEAD_MS = 0L; // 每次喷涂后附加延时(ms) |
|
|
|
|
|
final long JOB_OVERHEAD_MS = 0L; // 任务结束后全局延时(ms) |
|
|
|
|
|
// ================ |
|
|
|
|
|
|
|
|
LocalDateTime startTime = LocalDateTime.now(); |
|
|
LocalDateTime startTime = LocalDateTime.now(); |
|
|
List<Map<String, Object>> finishTimes = new ArrayList<>(); |
|
|
List<Map<String, Object>> finishTimes = new ArrayList<>(); |
|
|
long cumulativeMs = 0L; |
|
|
|
|
|
|
|
|
|
|
|
// 上一次喷涂结束位置,初始为原点(0,0,0) |
|
|
|
|
|
double lastX = 0, lastY = 0, lastZ = 0; |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < params.size(); i++) { |
|
|
|
|
|
SprayTaskParams p = params.get(i); |
|
|
|
|
|
int idx = p.getIndex(); |
|
|
|
|
|
|
|
|
|
|
|
// 已完成玻片直接记录并跳过 |
|
|
|
|
|
if (idx < resumeIndex) { |
|
|
|
|
|
Map<String, Object> rec = new HashMap<>(); |
|
|
|
|
|
rec.put("index", idx); |
|
|
|
|
|
rec.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs))); |
|
|
|
|
|
finishTimes.add(rec); |
|
|
|
|
|
|
|
|
List<SprayTaskParams> sprayTaskParams = sprayTask.getSprayTaskParams(); |
|
|
|
|
|
double curX = 0, curY = 0, curZ = 0;//定义当前位置 |
|
|
|
|
|
for (SprayTaskParams sprayTaskParam : sprayTaskParams) {//循环玻片 |
|
|
|
|
|
if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() <= sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过 |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
long sliceMs = 0L; |
|
|
|
|
|
Double[] slide = slideArr[idx]; |
|
|
|
|
|
SprayTimes firstT = p.getTimes().get(0); |
|
|
|
|
|
|
|
|
|
|
|
// 1) 并行移动到喷涂起点(X,Y,Z),暂停恢复时跳过 |
|
|
|
|
|
boolean isResumeSlide = (idx == resumeIndex && resumeStep > 0); |
|
|
|
|
|
double curX, curY, curZ; |
|
|
|
|
|
if (!isResumeSlide) { |
|
|
|
|
|
double targetZ = slideHeight - firstT.getMotorZHeight(); |
|
|
|
|
|
sliceMs += concurrentMoveMs( |
|
|
|
|
|
lastX, slide[0], firstT.getMovingSpeed(), |
|
|
|
|
|
lastY, slide[1], firstT.getMovingSpeed(), |
|
|
|
|
|
lastZ, targetZ, 20, |
|
|
|
|
|
COMMAND_LATENCY_MS |
|
|
|
|
|
); |
|
|
|
|
|
curX = slide[0]; |
|
|
|
|
|
curY = slide[1]; |
|
|
|
|
|
curZ = targetZ; |
|
|
|
|
|
} else { |
|
|
|
|
|
// 恢复喷涂,无需移动到玻片顶点 |
|
|
|
|
|
curX = lastX; |
|
|
|
|
|
curY = lastY; |
|
|
|
|
|
curZ = lastZ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2) 管路切换 & 等待 |
|
|
|
|
|
boolean switched = (idx == resumeIndex && resumeSprayNum > 1); |
|
|
|
|
|
if (!switched) { |
|
|
|
|
|
sliceMs += COMMAND_LATENCY_MS + PIPELINE_DELAY_MS; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3) 喷涂循环 |
|
|
|
|
|
int sprayCount = 1; |
|
|
|
|
|
for (SprayTimes t : p.getTimes()) { |
|
|
|
|
|
// 跳过已完成轮次 |
|
|
|
|
|
if (idx == resumeIndex && sprayCount < resumeSprayNum) { |
|
|
|
|
|
if ("grid".equals(t.getMatrixPathType())) sprayCount++; |
|
|
|
|
|
sprayCount++; |
|
|
|
|
|
|
|
|
long sliceMs = 0L; //当前玻片耗时 |
|
|
|
|
|
int sprayNum = 1; //当前玻片是第几次喷涂 |
|
|
|
|
|
for (SprayTimes sprayTimes : sprayTaskParam.getTimes()) {//每个拨片有多次喷涂,循环每次喷涂 |
|
|
|
|
|
if (sprayNum < sprayTask.getSprayNum()) { |
|
|
|
|
|
sprayNum++; |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
// 指令开销 |
|
|
|
|
|
sliceMs += COMMAND_LATENCY_MS * 3; |
|
|
|
|
|
if (t.getHighVoltage()) sliceMs += COMMAND_LATENCY_MS * 2; |
|
|
|
|
|
|
|
|
|
|
|
// 路径移动(串行),跳过 resumeStep |
|
|
|
|
|
int skip = (idx == resumeIndex && sprayCount == resumeSprayNum) ? resumeStep : 0; |
|
|
|
|
|
for (SprayTaskStep step : getSprayPath(t)) { |
|
|
|
|
|
for (Point2D pt : step.getSprayPathPointList()) { |
|
|
|
|
|
if (skip-- > 0) continue; |
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
Double[] slide = slideArr[sprayTaskParam.getIndex()];//获取玻片原点的坐标 |
|
|
|
|
|
List<SprayTaskStep> sprayTaskStepList = getSprayPath(sprayTimes);//计算本次喷涂的路线 |
|
|
|
|
|
if (sprayTask.getCurrentStep() == 0) { |
|
|
|
|
|
//先移动到玻片左上角位置 |
|
|
|
|
|
SysSettings slideHeightSysSettings = sysSettingsService.getOne(new LambdaQueryWrapper<SysSettings>().eq(SysSettings::getCode, "slide_height")); |
|
|
|
|
|
Double slideHeight = Double.parseDouble(slideHeightSysSettings.getValue()); |
|
|
|
|
|
double height = slideHeight - sprayTimes.getMotorZHeight();//下降z轴高度 |
|
|
|
|
|
sliceMs += concurrentMoveMs( |
|
|
|
|
|
curX, slideArr[sprayTaskParam.getIndex()][0], 20, |
|
|
|
|
|
curY, slideArr[sprayTaskParam.getIndex()][1], 20, |
|
|
|
|
|
curZ, height, 20, |
|
|
|
|
|
CMD_LAT_MS |
|
|
|
|
|
); |
|
|
|
|
|
curX = slideArr[sprayTaskParam.getIndex()][0]; |
|
|
|
|
|
curY = slideArr[sprayTaskParam.getIndex()][1]; |
|
|
|
|
|
curZ = height; |
|
|
|
|
|
} |
|
|
|
|
|
sliceMs += CMD_LAT_MS + 500;//三通阀切换到注射器管路 |
|
|
|
|
|
//因为走的是折线,所以单次指令只需要移动x或y,缓存上一个点位用来判断本次是走x还是y |
|
|
|
|
|
double cacheXPoint = -1; |
|
|
|
|
|
double cacheYPoint = -1; |
|
|
|
|
|
int currentStep = 0; //记录当前线程喷涂步骤序号 |
|
|
|
|
|
for (SprayTaskStep sprayTaskStep : sprayTaskStepList) {//因为田字格喷涂其实是两次 |
|
|
|
|
|
if (currentStep >= sprayTask.getCurrentStep()) { |
|
|
|
|
|
sliceMs += CMD_LAT_MS;//开启喷嘴阀 |
|
|
|
|
|
sliceMs += CMD_LAT_MS;//推动移动注射泵 |
|
|
|
|
|
if (sprayTimes.getHighVoltage()) {//加电 |
|
|
|
|
|
sliceMs += CMD_LAT_MS;//开启高压 |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
// 结束阀门 & 网格延时 |
|
|
|
|
|
sliceMs += COMMAND_LATENCY_MS * 3; |
|
|
|
|
|
if ("grid".equals(t.getMatrixPathType()) && t.getGridDelay() != null) { |
|
|
|
|
|
sliceMs += t.getGridDelay() * 1000; |
|
|
|
|
|
|
|
|
for (int i = 0; i < sprayTaskStep.getSprayPathPointList().size(); i++) {//循环路线 |
|
|
|
|
|
if (currentStep < sprayTask.getCurrentStep()) { |
|
|
|
|
|
currentStep++; |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
Point2D currentPoint = sprayTaskStep.getSprayPathPointList().get(i); |
|
|
|
|
|
if (cacheXPoint != currentPoint.x) { |
|
|
|
|
|
sliceMs += moveMs(curX, slide[0] + currentPoint.x, sprayTimes.getMovingSpeed(), CMD_LAT_MS); |
|
|
|
|
|
cacheXPoint = currentPoint.x; |
|
|
|
|
|
curX = slide[0] + currentPoint.x; |
|
|
|
|
|
} |
|
|
|
|
|
if (cacheYPoint != currentPoint.y) { |
|
|
|
|
|
sliceMs += moveMs(curY, slide[1] + currentPoint.y, sprayTimes.getMovingSpeed(), CMD_LAT_MS); |
|
|
|
|
|
cacheYPoint = currentPoint.y; |
|
|
|
|
|
curY = slide[1] + currentPoint.y; |
|
|
|
|
|
} |
|
|
|
|
|
sliceMs += CMD_LAT_MS * 3;//向前端推送当前路径 |
|
|
|
|
|
currentStep++;//当前喷涂步数。因为暂停可能本次路线还没走完,所以这里只有在走完一次路线后才会自增 |
|
|
|
|
|
} |
|
|
|
|
|
//一次喷涂完毕后停止推注射泵 |
|
|
|
|
|
sliceMs += CMD_LAT_MS; //关闭高压 |
|
|
|
|
|
sliceMs += CMD_LAT_MS; //停止推动注射泵 |
|
|
|
|
|
sliceMs += CMD_LAT_MS; //关闭喷嘴阀 |
|
|
|
|
|
if ("grid".equals(sprayTimes.getMatrixPathType()) && sprayTimes.getGridDelay() != null) { |
|
|
|
|
|
if(sprayTask.getRemainingGridDelay() != null){ |
|
|
|
|
|
sliceMs += sprayTask.getRemainingGridDelay(); |
|
|
|
|
|
}else{ |
|
|
|
|
|
sliceMs += sprayTimes.getGridDelay() * 1000; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// 喷涂后延时 & 附加 |
|
|
|
|
|
sliceMs += t.getDelay() * 1000; |
|
|
|
|
|
sliceMs += PER_SPRAY_OVERHEAD_MS; |
|
|
|
|
|
sprayCount++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 4) 片尾:非末片更新 last 位置;末片并行回原点 + 全局延时 |
|
|
|
|
|
if (i < params.size() - 1) { |
|
|
|
|
|
lastX = curX; |
|
|
|
|
|
lastY = curY; |
|
|
|
|
|
lastZ = curZ; |
|
|
|
|
|
} else { |
|
|
|
|
|
sliceMs += concurrentMoveMs( |
|
|
|
|
|
curX, 0.0, HOME_SPEED_X, |
|
|
|
|
|
curY, 0.0, HOME_SPEED_Y, |
|
|
|
|
|
curZ, 0.0, HOME_SPEED_Z, |
|
|
|
|
|
COMMAND_LATENCY_MS |
|
|
|
|
|
); |
|
|
|
|
|
sliceMs += TOTAL_JOB_OVERHEAD_MS; |
|
|
|
|
|
lastX = lastY = lastZ = 0.0; |
|
|
|
|
|
|
|
|
if(sprayTask.getRemainingDelay() != null){ |
|
|
|
|
|
sliceMs += sprayTask.getRemainingDelay(); |
|
|
|
|
|
}else{ |
|
|
|
|
|
sliceMs += sprayTimes.getDelay() * 1000; |
|
|
|
|
|
} |
|
|
|
|
|
sprayNum++; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 累计并记录完成时间 |
|
|
|
|
|
cumulativeMs += sliceMs; |
|
|
|
|
|
Map<String, Object> rec = new HashMap<>(); |
|
|
Map<String, Object> rec = new HashMap<>(); |
|
|
rec.put("index", idx); |
|
|
|
|
|
rec.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs))); |
|
|
|
|
|
|
|
|
rec.put("index", sprayTaskParam.getIndex()); |
|
|
|
|
|
rec.put("finishTime", startTime.plus(Duration.ofMillis(sliceMs))); |
|
|
finishTimes.add(rec); |
|
|
finishTimes.add(rec); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return finishTimes; |
|
|
return finishTimes; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 计算单轴移动耗时 + 指令平均延迟 |
|
|
|
|
|
* |
|
|
|
|
|
* @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 long moveMs(double from, double to, double speed, long cmdDelayMs) { |
|
|
|
|
|
return (long) (Math.abs(to - from) / speed * 1000) + cmdDelayMs; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 并行移动 XYZ:各轴耗时取最大值;包含单轴指令延迟 |
|
|
|
|
|
*/ |
|
|
|
|
|
private long concurrentMoveMs( |
|
|
private long concurrentMoveMs( |
|
|
double fromX, double toX, double speedX, |
|
|
|
|
|
double fromY, double toY, double speedY, |
|
|
|
|
|
double fromZ, double toZ, double speedZ, |
|
|
|
|
|
long cmdDelayMs) { |
|
|
|
|
|
long tx = moveMs(fromX, toX, speedX, cmdDelayMs); |
|
|
|
|
|
long ty = moveMs(fromY, toY, speedY, cmdDelayMs); |
|
|
|
|
|
long tz = moveMs(fromZ, toZ, speedZ, cmdDelayMs); |
|
|
|
|
|
return Math.max(tx, Math.max(ty, tz)); |
|
|
|
|
|
|
|
|
double fx, double tx, double sx, |
|
|
|
|
|
double fy, double ty, double sy, |
|
|
|
|
|
double fz, double tz, double sz, |
|
|
|
|
|
long cmd) { |
|
|
|
|
|
long x = moveMs(fx, tx, sx, cmd); |
|
|
|
|
|
long y = moveMs(fy, ty, sy, cmd); |
|
|
|
|
|
long z = moveMs(fz, tz, sz, cmd); |
|
|
|
|
|
return Math.max(x, Math.max(y, z)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|