Browse Source

fix:喷涂时间校准

master
白凤吉 2 weeks ago
parent
commit
3a3d52c290
  1. 252
      src/main/java/com/qyft/ms/app/device/spray/SprayTaskExecutor.java
  2. 5
      src/main/java/com/qyft/ms/app/device/status/SprayTask.java
  3. 3
      src/main/java/com/qyft/ms/app/front/cmd/business/MatrixSprayPause.java

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

@ -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();
long sliceMs = 0L; //当前玻片耗时
int sprayNum = 1; //当前玻片是第几次喷涂
for (SprayTimes sprayTimes : sprayTaskParam.getTimes()) {//每个拨片有多次喷涂循环每次喷涂
if (sprayNum < sprayTask.getSprayNum()) {
sprayNum++;
continue;
}
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( sliceMs += concurrentMoveMs(
lastX, slide[0], firstT.getMovingSpeed(),
lastY, slide[1], firstT.getMovingSpeed(),
lastZ, targetZ, 20,
COMMAND_LATENCY_MS
curX, slideArr[sprayTaskParam.getIndex()][0], 20,
curY, slideArr[sprayTaskParam.getIndex()][1], 20,
curZ, height, 20,
CMD_LAT_MS
); );
curX = slide[0];
curY = slide[1];
curZ = targetZ;
} else {
// 恢复喷涂无需移动到玻片顶点
curX = lastX;
curY = lastY;
curZ = lastZ;
curX = slideArr[sprayTaskParam.getIndex()][0];
curY = slideArr[sprayTaskParam.getIndex()][1];
curZ = height;
} }
// 2) 管路切换 & 等待
boolean switched = (idx == resumeIndex && resumeSprayNum > 1);
if (!switched) {
sliceMs += COMMAND_LATENCY_MS + PIPELINE_DELAY_MS;
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;//开启高压
} }
// 3) 喷涂循环
int sprayCount = 1;
for (SprayTimes t : p.getTimes()) {
// 跳过已完成轮次
if (idx == resumeIndex && sprayCount < resumeSprayNum) {
if ("grid".equals(t.getMatrixPathType())) sprayCount++;
sprayCount++;
}
for (int i = 0; i < sprayTaskStep.getSprayPathPointList().size(); i++) {//循环路线
if (currentStep < sprayTask.getCurrentStep()) {
currentStep++;
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;
}
// 结束阀门 & 网格延时
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) 片尾非末片更新 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;
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;
}
}
}
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));
} }

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

@ -74,6 +74,10 @@ public class SprayTask {
*/ */
private volatile boolean paused = false; private volatile boolean paused = false;
/** /**
* 当前设备状态是否可以暂停
*/
private volatile boolean cannotPause = false;
/**
* 标志喷涂任务结束 * 标志喷涂任务结束
*/ */
private volatile boolean close = false; private volatile boolean close = false;
@ -96,6 +100,7 @@ public class SprayTask {
cmdId = null; cmdId = null;
cmdCode = null; cmdCode = null;
paused = false; paused = false;
cannotPause = false;
close = false; close = false;
spraying = false; spraying = false;
currentStep = 0; currentStep = 0;

3
src/main/java/com/qyft/ms/app/front/cmd/business/MatrixSprayPause.java

@ -41,6 +41,9 @@ public class MatrixSprayPause extends BaseCommandHandler {
if(sprayTask.isPaused()){ if(sprayTask.isPaused()){
throw new RuntimeException("设备暂停中"); throw new RuntimeException("设备暂停中");
} }
if (sprayTask.isCannotPause()) {
throw new RuntimeException("当前喷涂任务不可暂停");
}
sprayTask.setPaused(true);//设置已暂停 sprayTask.setPaused(true);//设置已暂停
try { try {
sprayTaskExecutor.stopTask();//终止喷涂任务线程 sprayTaskExecutor.stopTask();//终止喷涂任务线程

Loading…
Cancel
Save