diff --git a/app.db b/app.db index e693238..6a0fe34 100644 Binary files a/app.db and b/app.db differ diff --git a/src/main/java/com/iflytop/digester/DigestionTaskThread.java b/src/main/java/com/iflytop/digester/DigestionTaskThread.java index 05eea2a..18b8a43 100644 --- a/src/main/java/com/iflytop/digester/DigestionTaskThread.java +++ b/src/main/java/com/iflytop/digester/DigestionTaskThread.java @@ -94,25 +94,38 @@ public class DigestionTaskThread extends Thread { public void run() { try { this.prepare(); + + // 解析消解配方并取出首轮和末轮配置 var rounds = this.solution.getDigestionRounds(); - var firstRound = rounds.remove(0); - var lastRound = rounds.remove(rounds.size() - 1); + MdbDigestionSolution.DigestionSolutionRound firstRound = null; + if ( !rounds.isEmpty() && rounds.size() > 1 ) { + firstRound = rounds.remove(0); + } + MdbDigestionSolution.DigestionSolutionRound lastRound = null; + if ( !rounds.isEmpty() ) { + lastRound = rounds.remove(rounds.size() - 1); + } // 执行第一轮 - this.executeRound(firstRound); + if ( null != firstRound ) { + this.executeRound(firstRound); + } // 执行中间轮 for (var round : rounds) { this.executeRound(round); } // 执行最后一轮 - this.executeLastRound(lastRound); + if ( null != lastRound ) { + this.executeLastRound(lastRound); + } this.errorProcessThread.join(); this.updateTaskStatus("Finish", "消解任务结束"); - this.finishCallback.callback(this); } catch (Exception e) { this.updateTaskStatus("Error", e.getMessage()); UfMdbNotification.error("消解任务执行失败 : " + e.getMessage()); + } finally { + this.finishCallback.callback(this); } } @@ -123,17 +136,23 @@ public class DigestionTaskThread extends Thread { var device = Device.getInstance(); // 分配加热位 - this.heatingSlot = device.heatingTurntable.allocSlot(this.taskModel.batchNo); + if ( "manual".equals(this.taskModel.mode) ) { + this.heatingSlot = device.heatingTurntable.getSlotByIndex(this.taskModel.heatingSlotIndex); + } else { + this.heatingSlot = device.heatingTurntable.allocSlot(this.taskModel.batchNo); + } this.heatingSlot.setTubes(this.taskModel.getTubes()); this.taskModel.heatingSlotIndex = this.heatingSlot.index; this.taskModel.save(); - // 打开门 - device.door.open(); - // 等待放入试管架 - this.waitForTubeRackPutIn(false); - // 关闭门 - device.door.close(); + if ( "auto".equals(this.taskModel.mode) ) { + // 打开门 + device.door.open(); + // 等待放入试管架 + this.waitForTubeRackPutIn(); + // 关闭门 + device.door.close(); + } } // 检查试管 @@ -171,7 +190,7 @@ public class DigestionTaskThread extends Thread { if ( !this.errorTubeIndexes.isEmpty() ) { var device = Device.getInstance(); // 等待放入空试管架 - this.waitForTubeRackPutIn(true); + this.waitForEmptyTubeRackPutIn(); // 取出剩余异常试管 device.transferArm.takeOutTubesFromErrorSlot(this.errorTubeIndexes); // 等待取出试管架 @@ -268,17 +287,19 @@ public class DigestionTaskThread extends Thread { device.liquidAddition.addLiquidToTubes(this.heatingSlot.getExistTubeIndexes(), "water", digestionRound.waterVolume); } - // 正常消解结束 - // 移动到加液盘 - device.transferArm.moveTubeRackToLiquidPlate(this.heatingSlot.index); - // 打开门 - device.door.open(); - // 等待取出 - this.waitForTubeRackTakeOut(); - // 关闭门 - device.door.close(); - // 释放加热位 - this.heatingSlot.setTubeRackNo(null); + // 正常消解结束; + if ( "auto".equals(this.taskModel.mode) ) { + // 移动到加液盘 + device.transferArm.moveTubeRackToLiquidPlate(this.heatingSlot.index); + // 打开门 + device.door.open(); + // 等待取出 + this.waitForTubeRackTakeOut(); + // 关闭门 + device.door.close(); + // 释放加热位 + this.heatingSlot.setTubeRackNo(null); + } } // 执行消解异常处理配置 @@ -311,7 +332,7 @@ public class DigestionTaskThread extends Thread { // 将试管架放入异常处理区域 device.transferArm.moveTubeRackToHeatingTurntable(this.heatingSlot.index); // 等待放入空试管架 - this.waitForTubeRackPutIn(true); + this.waitForEmptyTubeRackPutIn(); // 取出消解完成的试管 device.transferArm.takeOutTubesFromErrorSlot(this.finishedErrorTubeIndexes); // 等待取出试管架 @@ -321,14 +342,37 @@ public class DigestionTaskThread extends Thread { } // 等待放入试管架 - private void waitForTubeRackPutIn( Boolean requestEmptyTubeRack ) { - if ( requestEmptyTubeRack ) { - this.manager.sendMessageToTransBot("EmptyTubeRackPutIn", Map.of("batchNo", this.taskModel.batchNo)); + private void waitForTubeRackPutIn() { + this.updateTaskStatus("TubeRackPutInWait", "等待放入试管架"); + + // 发送放入试管架消息 + Map message = Map.of("batchNo", this.taskModel.batchNo, "taskId", this.taskModel.outTaskId); + this.manager.sendMessageToTransBot("TubeRackPutIn", message); + + // 等待试管架放入完成 + synchronized ( this.tubeRackPutInWaitLock ) { + try { + this.tubeRackPutInWaitLock.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + this.updateTaskStatus("TubeRackPutInWaitDone", "试管架已放入"); + } + + // 等待放入试管架 + private void waitForEmptyTubeRackPutIn() { + this.updateTaskStatus("TubeRackPutInWait", "等待放入空试管架"); + + // 发送放入试管架消息 + Map message = Map.of("batchNo", this.taskModel.batchNo, "taskId", this.taskModel.outTaskId); + if ( "auto".equals(this.taskModel.mode) ) { + this.manager.sendMessageToTransBot("EmptyTubeRackPutIn", message); } else { - this.manager.sendMessageToTransBot("TubeRackPutIn", Map.of("batchNo", this.taskModel.batchNo)); + UfMdbNotification.action("EmptyTubeRackPutIn", message); } - this.updateTaskStatus("TubeRackPutInWait", "等待放入试管架"); + // 等待试管架放入完成 synchronized ( this.tubeRackPutInWaitLock ) { try { this.tubeRackPutInWaitLock.wait(); @@ -341,8 +385,15 @@ public class DigestionTaskThread extends Thread { // 等待取出试管架 private void waitForTubeRackTakeOut() { - this.manager.sendMessageToTransBot("TubeRackTakeOut", Map.of("batchNo", this.taskModel.batchNo)); this.updateTaskStatus("TubeRackTakeOutWait", "等待取出试管架"); + + Map message = Map.of("batchNo", this.taskModel.batchNo, "taskId", this.taskModel.outTaskId); + if ( "auto".equals(this.taskModel.mode) ) { + this.manager.sendMessageToTransBot("TubeRackTakeOut", message); + } else { + UfMdbNotification.action("TubeRackTakeOut", message); + } + synchronized ( this.tubeRackTakeOutWaitLock ) { try { this.tubeRackTakeOutWaitLock.wait(); @@ -356,6 +407,9 @@ public class DigestionTaskThread extends Thread { // 拍照检查试管 private void takeShotAndCheckErrorTubes() { this.updateTaskStatus("TubeCheck", "拍照检查试管,等待确认异常试管"); + Map message = Map.of("batchNo", this.taskModel.batchNo, "taskId", this.taskModel.outTaskId); + UfMdbNotification.action("TubeCheck", message); + this.errorTubeIndexes.clear(); synchronized ( this.errorTubeIndexes ) { try { @@ -370,6 +424,9 @@ public class DigestionTaskThread extends Thread { // 拍照检查试管 private void takeShotAndCheckFinishedTubes() { this.updateTaskStatus("ErrorTubeCheck", "拍照检查异常试管,等待确认完成试管"); + Map message = Map.of("batchNo", this.taskModel.batchNo, "taskId", this.taskModel.outTaskId); + UfMdbNotification.action("ErrorTubeCheck", message); + this.finishedErrorTubeIndexes.clear(); synchronized ( this.finishedErrorTubeIndexes ) { try { diff --git a/src/main/java/com/iflytop/digester/controller/CameraController.java b/src/main/java/com/iflytop/digester/controller/CameraController.java new file mode 100644 index 0000000..3e5a0ab --- /dev/null +++ b/src/main/java/com/iflytop/digester/controller/CameraController.java @@ -0,0 +1,15 @@ +package com.iflytop.digester.controller; +import com.iflytop.digester.underframework.controller.UfApiControllerBase; +import com.iflytop.digester.underframework.controller.UfApiResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import java.util.Map; +@Controller +public class CameraController extends UfApiControllerBase { + @ResponseBody + @PostMapping("/api/camera/take-shot") + public UfApiResponse takeShot() { + return this.success(Map.of("data", "https://avatars.githubusercontent.com/u/53512912?v=4")); + } +} diff --git a/src/main/java/com/iflytop/digester/controller/DigestionTaskController.java b/src/main/java/com/iflytop/digester/controller/DigestionTaskController.java index eab13a0..f445e79 100644 --- a/src/main/java/com/iflytop/digester/controller/DigestionTaskController.java +++ b/src/main/java/com/iflytop/digester/controller/DigestionTaskController.java @@ -7,6 +7,7 @@ import com.iflytop.digester.underframework.util.UfJsonHelper; import com.iflytop.digester.underframework.controller.UfApiControllerBase; import com.iflytop.digester.underframework.controller.UfApiResponse; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -20,11 +21,13 @@ public class DigestionTaskController extends UfApiControllerBase { @ResponseBody @PostMapping("/api/digestion-task/start") - public UfApiResponse start(@RequestBody Map params) { + public UfApiResponse start(HttpServletRequest request, @RequestBody Map params) { String taskId = (String)params.get("taskId"); String name = (String)params.get("name"); String batchNo = (String)params.get("batchNo"); List> tubes = (List>)params.get("tubes"); + String mode = (String)params.get("mode"); + Integer slotIndex = (Integer)params.get("slotIndex"); var digestion = UfActiveRecord.findOne(MdbDigestionSolution.class, Map.of("name", name)); if ( null == digestion ) { @@ -39,7 +42,9 @@ public class DigestionTaskController extends UfApiControllerBase { task.status = "pending"; task.message = "等待中"; task.startedAt = (int)(System.currentTimeMillis() / 1000); - task.startedBy = 0; + task.startedBy = this.getUserFromRequest(request).id; + task.mode = mode; + task.heatingSlotIndex = slotIndex; task.save(); this.taskManager.startTask(task); diff --git a/src/main/java/com/iflytop/digester/controller/LiquidController.java b/src/main/java/com/iflytop/digester/controller/LiquidController.java index 783431f..71bfc3e 100644 --- a/src/main/java/com/iflytop/digester/controller/LiquidController.java +++ b/src/main/java/com/iflytop/digester/controller/LiquidController.java @@ -26,8 +26,8 @@ public class LiquidController extends UfApiControllerBase { Integer bucketIndex = (Integer)params.get("index"); String liquidType = (String)params.get("type"); var bucket = this.device.liquidAddition.getLiquidByIndex(bucketIndex); - bucket.type = liquidType; - bucket.volume = bucket.totalVolume; + bucket.setType(liquidType); + bucket.setVolume(bucket.totalVolume); return this.success(); } } diff --git a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java index 7759a1d..d0fcd0e 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java @@ -22,6 +22,11 @@ public class HeatingTurntableInstance { return slots; } + // 根据索引获取槽位 + public HeatingTurntableSlot getSlotByIndex(Integer index) { + return slots.get(index); + } + public HeatingTurntableSlot getSlotByLiquidPlateTubeRack() { for (HeatingTurntableSlot slot : slots) { if (null != slot.tubeRackNo && slot.tubeRackLocation.equals("liquid-plate")) { diff --git a/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionLiquid.java b/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionLiquid.java index 90ec9e3..aed09ad 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionLiquid.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionLiquid.java @@ -1,4 +1,7 @@ package com.iflytop.digester.deviceinstance; + +import com.iflytop.digester.underframework.dao.model.UfMdbRuntimeVariable; + public class LiquidAdditionLiquid { // bucket index public Integer bucketIndex; @@ -8,4 +11,16 @@ public class LiquidAdditionLiquid { public Integer volume; // total volume public Integer totalVolume; + + // set type + public void setType( String type ) { + this.type = type; + UfMdbRuntimeVariable.setString(String.format("LiquidBucketType.%d", this.bucketIndex), type); + } + + // set volume + public void setVolume( Integer volume ) { + this.volume = volume; + UfMdbRuntimeVariable.setInteger(String.format("LiquidBucketVolume.%d", this.bucketIndex), volume); + } } diff --git a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java index ac9734e..4c5d01c 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java @@ -3,6 +3,8 @@ import com.iflytop.digester.underframework.UfCmdSnippetExecutor; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; +import java.util.Map; + @Component public class TransferRobotArmInstance { // 等待锁队列 @@ -16,7 +18,12 @@ public class TransferRobotArmInstance { // 执行从异常槽位取出试管 UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlotStart"); for (Integer tubeIndex : tubeIndexes) { - UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlot." + tubeIndex); + UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlot", Map.of( + "MotorLiquidPlatePos", 100, + "MotorArmLeftRightSrcPos", 100, + "MotorArmLeftRightDstPos", 100, + "MotorHeatingTurntablePos", 100 + )); } UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlotEnd"); @@ -31,7 +38,12 @@ public class TransferRobotArmInstance { // 执行移动试管到异常槽位 UfCmdSnippetExecutor.execute("TubeMoveToErrorSlotStart"); for (Integer tubeIndex : tubeIndexes) { - UfCmdSnippetExecutor.execute("TubeMoveToErrorSlot." + tubeIndex); + UfCmdSnippetExecutor.execute("TubeMoveToErrorSlot", Map.of( + "MotorLiquidPlatePos", 100, + "MotorArmLeftRightSrcPos", 100, + "MotorArmLeftRightDstPos", 100, + "MotorHeatingTurntablePos", 100 + )); } UfCmdSnippetExecutor.execute("TubeMoveToErrorSlotEnd"); diff --git a/src/main/java/com/iflytop/digester/model/MdbDigestionTask.java b/src/main/java/com/iflytop/digester/model/MdbDigestionTask.java index a0144ce..0fcd773 100644 --- a/src/main/java/com/iflytop/digester/model/MdbDigestionTask.java +++ b/src/main/java/com/iflytop/digester/model/MdbDigestionTask.java @@ -30,7 +30,7 @@ public class MdbDigestionTask extends UfActiveRecord { public Integer startedAt; @UfActiveRecordField - public Integer startedBy; + public String startedBy; @UfActiveRecordField public Integer finishedAt = 0; @@ -38,6 +38,9 @@ public class MdbDigestionTask extends UfActiveRecord { @UfActiveRecordField public Integer heatingSlotIndex = -1; + @UfActiveRecordField + public String mode; + // get table name public static String getTableName() { return "app_digestion_tasks"; diff --git a/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbNotification.java b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbNotification.java index 01e0040..48ff79a 100644 --- a/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbNotification.java +++ b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbNotification.java @@ -1,6 +1,10 @@ package com.iflytop.digester.underframework.dao.model; import com.iflytop.digester.underframework.dao.record.UfActiveRecord; import com.iflytop.digester.underframework.dao.record.UfActiveRecordField; +import com.iflytop.digester.underframework.util.UfJsonHelper; + +import java.util.Map; + public class UfMdbNotification extends UfActiveRecord { @UfActiveRecordField public String type; @@ -59,4 +63,15 @@ public class UfMdbNotification extends UfActiveRecord { notification.status = "new"; notification.save(); } + + // notify action + public static void action( String action, Map params) { + var data = Map.of("action", action, "params", params); + var dataJson = UfJsonHelper.objectToJson(data); + var notification = new UfMdbNotification(); + notification.type = "action"; + notification.data = dataJson; + notification.status = "new"; + notification.save(); + } } diff --git a/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbRuntimeVariable.java b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbRuntimeVariable.java index 3f4b3ce..5ce19d7 100644 --- a/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbRuntimeVariable.java +++ b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbRuntimeVariable.java @@ -42,19 +42,19 @@ public class UfMdbRuntimeVariable extends UfActiveRecord { } // set string - public static void setString(String value, String name, Object ... args ) { - var variable = UfMdbRuntimeVariable.getVariable(name, args); + public static void setString(String name, String value) { + var variable = UfActiveRecord.findOne(UfMdbRuntimeVariable.class, Map.of("key", name)); if ( null == variable ) { variable = new UfMdbRuntimeVariable(); - variable.key = UfMdbRuntimeVariable.buildKey(name, args); + variable.key = name; } variable.value = value; variable.save(); } // set integer - public static void setInteger(Integer value, String name, Object ... args ) { - UfMdbRuntimeVariable.setString(value.toString(), name, args); + public static void setInteger(String name, Integer value) { + UfMdbRuntimeVariable.setString(name, value.toString()); } // remove