From 22d3d138a49ca10d6dbfaa6ec860dec08daec923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E5=87=A4=E5=90=89?= Date: Fri, 18 Jul 2025 16:02:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=E5=96=B7=E6=B6=82?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ms/app/device/spray/SprayTaskExecutor.java | 164 +++++++++++++-------- .../qyft/ms/app/service/VirtualDeviceService.java | 4 +- 2 files changed, 109 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java b/src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java index 85f68df..7ed7553 100644 --- a/src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java +++ b/src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java @@ -16,7 +16,6 @@ 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; @@ -82,10 +81,9 @@ public class SprayTaskExecutor { sprayTask.setFinishTimeMap(finishTimeMap); List sprayTaskParams = sprayTask.getSprayTaskParams(); for (SprayTaskParams sprayTaskParam : sprayTaskParams) {//循环玻片 - if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() < sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过 + if (sprayTask.getCurrentIndex() != null && sprayTaskParam.getIndex() <= sprayTask.getCurrentIndex()) {//喷涂过的玻片跳过 continue; } - sprayTask.setCurrentIndex(sprayTaskParam.getIndex()); int sprayNum = 1; //当前玻片是第几次喷涂 sprayTask.setCurrentCountSprayNum(1); for (SprayTimes sprayTimes : sprayTaskParam.getTimes()) {//每个拨片有多次喷涂,循环每次喷涂 @@ -128,20 +126,21 @@ public class SprayTaskExecutor { double cacheYPoint = -1; int currentStep = 0; //记录当前线程喷涂步骤序号 for (SprayTaskStep sprayTaskStep : sprayTaskStepList) {//因为田字格喷涂其实是两次 - DeviceCommand nozzleValveOpenCommand = DeviceCommandGenerator.nozzleValveOpen();//开启喷嘴阀 - CommandFuture nozzleValveOpenCommandFuture = deviceCommandService.sendCommand(sprayTask.getCmdId(), sprayTask.getCmdCode(), nozzleValveOpenCommand); - commandWait(nozzleValveOpenCommandFuture); - - DeviceCommand syringePumpForwardCommand = DeviceCommandGenerator.syringePumpForward(sprayTimes.getVolume());//推动移动注射泵 - CommandFuture syringePumpForwardCommandFuture = deviceCommandService.sendCommandSprayTask(sprayTask.getCmdId(), sprayTask.getCmdCode(), syringePumpForwardCommand); - commandWait(syringePumpForwardCommandFuture); - - if (sprayTimes.getHighVoltage()) {//加电 - DeviceCommand highVoltageOpenCommand = DeviceCommandGenerator.highVoltageOpen(sprayTimes.getHighVoltageValue());//开启高压 - CommandFuture highVoltageOpenCommandFuture = deviceCommandService.sendCommand(sprayTask.getCmdId(), sprayTask.getCmdCode(), highVoltageOpenCommand); - commandWait(highVoltageOpenCommandFuture); + if (currentStep >= sprayTask.getCurrentStep()) { + DeviceCommand nozzleValveOpenCommand = DeviceCommandGenerator.nozzleValveOpen();//开启喷嘴阀 + CommandFuture nozzleValveOpenCommandFuture = deviceCommandService.sendCommand(sprayTask.getCmdId(), sprayTask.getCmdCode(), nozzleValveOpenCommand); + commandWait(nozzleValveOpenCommandFuture); + + DeviceCommand syringePumpForwardCommand = DeviceCommandGenerator.syringePumpForward(sprayTimes.getVolume());//推动移动注射泵 + CommandFuture syringePumpForwardCommandFuture = deviceCommandService.sendCommandSprayTask(sprayTask.getCmdId(), sprayTask.getCmdCode(), syringePumpForwardCommand); + commandWait(syringePumpForwardCommandFuture); + + if (sprayTimes.getHighVoltage()) {//加电 + DeviceCommand highVoltageOpenCommand = DeviceCommandGenerator.highVoltageOpen(sprayTimes.getHighVoltageValue());//开启高压 + CommandFuture highVoltageOpenCommandFuture = deviceCommandService.sendCommand(sprayTask.getCmdId(), sprayTask.getCmdCode(), highVoltageOpenCommand); + commandWait(highVoltageOpenCommandFuture); + } } - for (int i = 0; i < sprayTaskStep.getSprayPathPointList().size(); i++) {//循环路线 if (currentStep < sprayTask.getCurrentStep()) { currentStep++; @@ -189,6 +188,7 @@ public class SprayTaskExecutor { sprayTask.setSprayNum(sprayNum); sprayTask.setCurrentStep(0); } + sprayTask.setCurrentIndex(sprayTaskParam.getIndex()); sprayTask.setSprayNum(0); } //喷涂完毕 @@ -319,14 +319,17 @@ public class SprayTaskExecutor { List 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) + 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( @@ -336,53 +339,74 @@ public class SprayTaskExecutor { ); LocalDateTime startTime = LocalDateTime.now(); - List> finishTimeMap = new ArrayList<>(params.size()); + List> finishTimes = new ArrayList<>(); + long cumulativeMs = 0L; + + // 上一次喷涂结束位置,初始为原点(0,0,0) + double lastX = 0, lastY = 0, lastZ = 0; - long cumulativeMs = 0; for (int i = 0; i < params.size(); i++) { SprayTaskParams p = params.get(i); int idx = p.getIndex(); - // 如果此玻片已在之前完成,直接添加当前累计时间 + + // 已完成玻片直接记录并跳过 if (idx < resumeIndex) { - Map ft = new HashMap<>(); - ft.put("index", p.getIndex()); - ft.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs))); - finishTimeMap.add(ft); + Map rec = new HashMap<>(); + rec.put("index", idx); + rec.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs))); + finishTimes.add(rec); continue; } - long sliceMs = 0; - // 1) 移到玻片起点 + 下降 + long sliceMs = 0L; 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) 管路切换 & 等待(已在中途恢复时跳过) + // 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 + 500;//500是切换注射器固定等待时间 + sliceMs += COMMAND_LATENCY_MS + PIPELINE_DELAY_MS; } // 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; - // 路径移动 + + // 路径移动(串行),跳过 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); @@ -390,35 +414,45 @@ public class SprayTaskExecutor { 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); + // 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; + } - // 累计并加全局附加 + // 累计并记录完成时间 cumulativeMs += sliceMs; - if (i == params.size() - 1) cumulativeMs += TOTAL_JOB_OVERHEAD_MS; - Map ft = new HashMap<>(); - ft.put("index", p.getIndex()); - ft.put("finishTime", TimeUtils.toEpochMilli(startTime.plus(Duration.ofMillis(cumulativeMs)))); - finishTimeMap.add(ft); + Map rec = new HashMap<>(); + rec.put("index", idx); + rec.put("finishTime", startTime.plus(Duration.ofMillis(cumulativeMs))); + finishTimes.add(rec); } - - return finishTimeMap; + return finishTimes; } + /** * 计算单轴移动耗时 + 指令平均延迟 * @@ -432,6 +466,20 @@ public class SprayTaskExecutor { return (long) (dist / speed * 1000) + cmdDelay; } + /** + * 并行移动 XYZ:各轴耗时取最大值;包含单轴指令延迟 + */ + 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)); + } + private void delay(long millisecond) throws InterruptedException { delay(null, millisecond); diff --git a/src/main/java/com/qyft/ms/app/service/VirtualDeviceService.java b/src/main/java/com/qyft/ms/app/service/VirtualDeviceService.java index 6165297..c36be28 100644 --- a/src/main/java/com/qyft/ms/app/service/VirtualDeviceService.java +++ b/src/main/java/com/qyft/ms/app/service/VirtualDeviceService.java @@ -33,7 +33,9 @@ public class VirtualDeviceService { String action = cmdToDevice.getAction(); String device = cmdToDevice.getDevice(); if (code.contains("controlMotorCmd")) { - if (!action.contains("set")) {//非设置电机参数,也就是电机移动 + if(action.contains("origin")){ + Thread.sleep(3000); + }else if (!action.contains("set")) {//非设置电机参数,也就是电机移动 Thread.sleep(300); } } else if (code.contains("getInfoCmd")) {