diff --git a/app.db b/app.db index fe28804..2230299 100644 Binary files a/app.db and b/app.db differ diff --git a/pom.xml b/pom.xml index a12c09f..6d7a6c7 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ com.iflytop uf - 0.0.40 + 0.0.43 diff --git a/src/main/java/com/iflytop/a800/controller/TestController.java b/src/main/java/com/iflytop/a800/controller/TestController.java index fe5ea86..53e2d09 100644 --- a/src/main/java/com/iflytop/a800/controller/TestController.java +++ b/src/main/java/com/iflytop/a800/controller/TestController.java @@ -104,6 +104,7 @@ public class TestController extends UfApiControllerBase { tube.put("projectIds", UfJsonHelper.objectToJson(projectIds)); var tubeModel = new MdbTubeRackTestTaskTube(); tubeModel.rackId = task.id; + tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "NEW"); tubeModel.setAttributes(tube); tubeModel.save(); } diff --git a/src/main/java/com/iflytop/a800/device/Pipette.java b/src/main/java/com/iflytop/a800/device/Pipette.java index d8b6ad0..3781e4c 100644 --- a/src/main/java/com/iflytop/a800/device/Pipette.java +++ b/src/main/java/com/iflytop/a800/device/Pipette.java @@ -296,6 +296,8 @@ public class Pipette { Double coefficient = UfMdbOption.getDouble("SampleTubeRackWB5mlTubeLiquidLevelFollowCoefficient"); Integer depth = (int)(coefficient * volume); this.aspirateWithLiquidLevelFollow(volume, depth); + + UfActuatorCmdExecutor.execute("ArmXY", "xymotor_move_to", "2385,3100,0"); } // 从全血3ml吸液 diff --git a/src/main/java/com/iflytop/a800/resource/TestCard.java b/src/main/java/com/iflytop/a800/resource/TestCard.java index 9985489..081f479 100644 --- a/src/main/java/com/iflytop/a800/resource/TestCard.java +++ b/src/main/java/com/iflytop/a800/resource/TestCard.java @@ -1,5 +1,9 @@ package com.iflytop.a800.resource; public class TestCard { - public Integer boxIndex; - public String lotCode; + // box index inside warehouse + public Integer boxIndex = -1; + // lot code + public String lotCode = ""; + // slot index in incubator + public Integer incubatorSlotIndex = -1; } diff --git a/src/main/java/com/iflytop/a800/task/TubeRackTask.java b/src/main/java/com/iflytop/a800/task/TubeRackTask.java index 5884b2a..c26c2f1 100644 --- a/src/main/java/com/iflytop/a800/task/TubeRackTask.java +++ b/src/main/java/com/iflytop/a800/task/TubeRackTask.java @@ -41,15 +41,23 @@ public class TubeRackTask extends TaskBase { tubeRack.tubes = new ArrayList<>(); for (int i = 0; i < 10; i++) { var tube = new TestTube(); + tube.index = i; + + var tubeModel = UfActiveRecord.findOne( + MdbTubeRackTestTaskTube.class, + Map.of("rackId", this.taskModel.id, "index", tube.index) + ); + tube.isExisted = feeder.readIsTestTubeExisted(i); if ( !tube.isExisted ) { + tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "IGNORED"); + tubeModel.save(); continue ; } tubeRack.tubes.add(tube); this.samplingTubes.add(tube); - tube.index = i; tube.type = tubeRack.type; if ( !UfMdbDictItem.match("TUBE_TYPE","WB",tube.type) ) { continue; @@ -60,18 +68,18 @@ public class TubeRackTask extends TaskBase { tube.barCode = feeder.readBarCode(i); // 创建任务 - MdbTubeRackTestTaskTube tubeModel = UfActiveRecord.findOne( - MdbTubeRackTestTaskTube.class, - Map.of("rackId", this.taskModel.id, "index", tube.index) - ); for ( var projectId : tubeModel.getProjectIdList() ) { var project = UfActiveRecord.findOne(MdbProject.class, Map.of("id", projectId)); var testTask = new TubeTestTask(); testTask.tube = tube; testTask.project = project; + testTask.tubeModel = tubeModel; testTask.on("TubeSamplingFinished", this::onTubeSamplingFinished); taskMan.append(testTask); } + + tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "WAITING"); + tubeModel.save(); } if ( this.samplingTubes.isEmpty() ) { diff --git a/src/main/java/com/iflytop/a800/task/TubeTestTask.java b/src/main/java/com/iflytop/a800/task/TubeTestTask.java index 0624697..10b3c08 100644 --- a/src/main/java/com/iflytop/a800/task/TubeTestTask.java +++ b/src/main/java/com/iflytop/a800/task/TubeTestTask.java @@ -7,8 +7,10 @@ import com.iflytop.a800.device.Device; import com.iflytop.a800.model.MdbIdChip; import com.iflytop.a800.model.MdbProject; import com.iflytop.a800.model.MdbTest; +import com.iflytop.a800.model.MdbTubeRackTestTaskTube; import com.iflytop.a800.resource.*; import com.iflytop.a800.utils.ScanResultAnalysisAlgo; +import com.iflytop.uf.UfActiveRecord; import com.iflytop.uf.UfActuatorCmdExecutor; import com.iflytop.uf.UfCmdSnippetExecutor; import com.iflytop.uf.model.UfMdbDictItem; @@ -24,18 +26,28 @@ public class TubeTestTask extends TaskBase { public TestTube tube; // 测试项目 public MdbProject project; + // 测试试管 + public MdbTubeRackTestTaskTube tubeModel; + + // 测试记录 + private MdbTest test; + + + + public TestTubeRack tubeRack; public String status; - // 测试记录 - private MdbTest test; + + + // 缓冲液试管 private BufferTube bufferTube; // 大容量缓冲液试管 private LargeBufferTube largeBufferTube; // 测试卡 - private TestCard testCard; + private TestCard testCard = null; // id chip private MdbIdChip idChip; // 孵育槽位 @@ -46,7 +58,7 @@ public class TubeTestTask extends TaskBase { var device = Device.getInstance(); // 申请测试卡 - this.testCard = device.testCard.alloc(this.project); + this.prepareTestcard(); // 缓冲液/探测物质 this.bufferTube = device.bufferTube.alloc(this.testCard); @@ -58,8 +70,10 @@ public class TubeTestTask extends TaskBase { // 测试记录 this.test = new MdbTest(); + this.test.tubeId = this.tubeModel.id; this.test.projectCode = this.project.code; this.test.materialLotCode = this.testCard.lotCode; + this.test.isSamplingFinished = "no"; this.test.save(); // 移动试管架 @@ -85,14 +99,35 @@ public class TubeTestTask extends TaskBase { ))); } + // 申请测试卡 + private void prepareTestcard() { + var device = Device.getInstance(); + this.testCard = device.testCard.alloc(this.project); + this.log("测试卡:Box={}; Lot={}", this.testCard.boxIndex, this.testCard.lotCode); + + var threadPushCardToSlot = new Thread(() -> { + this.log("测试卡推送至孵育盘 :开始"); + this.incubatorSlot = device.incubator.pushNewCard(this.testCard); + this.testCard.incubatorSlotIndex = this.incubatorSlot.index; + this.log("测试卡推送至孵育盘 : 完成 -> Slot={}", this.testCard.incubatorSlotIndex); + synchronized ( this.testCard ) { + this.testCard.notifyAll(); + } + }); + threadPushCardToSlot.start(); + } + // 记录日志 private void log(String message, Object ... args ) { - message = String.format("[TubeTestTask:T%d-P%s] %s", this.tube.index, this.test.projectCode, message); + message = String.format("[TubeTestTask:T%d-P%s] %s", this.tube.index, this.project.code, message); LOG.info(message, args); } @Override public void run() { + this.tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "EXECUTING"); + this.tubeModel.save(); + this.prepare(); ObjectMapper jsonMapper = new ObjectMapper(); @@ -109,23 +144,45 @@ public class TubeTestTask extends TaskBase { continue; } - var stepAction = stepNode.get("action").asText(); - switch ( stepAction ) { - case "shaking" : this.shake(stepNode); break; - case "uncap" : this.uncap(); break; - case "cap" : this.cap(); break; - case "puncture" : this.executeStepPuncture(stepNode); break; - case "aspirate" : this.executeStepAspirate(stepNode); break; - case "mixing" : this.executeStepMixing(stepNode); break; - case "drop-tip" : this.executeStepDropTip(stepNode); break; - case "incubate" : this.executeStepIncubate(stepNode); break; - case "analysis" : this.executeStepAnalysis(stepNode); break; + try { + var stepAction = stepNode.get("action").asText(); + switch ( stepAction ) { + case "shaking" : this.shake(stepNode); break; + case "uncap" : this.uncap(); break; + case "cap" : this.cap(); break; + case "puncture" : this.executeStepPuncture(stepNode); break; + case "aspirate" : this.executeStepAspirate(stepNode); break; + case "mixing" : this.executeStepMixing(stepNode); break; + case "drop-tip" : this.executeStepDropTip(stepNode); break; + case "incubate" : this.executeStepIncubate(stepNode); break; + case "analysis" : this.executeStepAnalysis(stepNode); break; + case "sampling-done" : this.executeStepSamplingDone(stepNode); break; + } + } catch ( InterruptedException e ) { + throw new RuntimeException(e); } } this.taskFinish(); } + // 采样完成 + private void executeStepSamplingDone(JsonNode stepNode) { + this.log("采样完成"); + this.test.isSamplingFinished = "yes"; + this.test.save(); + + var doneSamplingCount = UfActiveRecord.count(MdbTest.class, Map.of( + "isSamplingFinished","yes", + "tubeId", this.tubeModel.id + )); + if ( doneSamplingCount == this.tubeModel.getProjectIdList().size() ) { + this.log("试管采样结束"); + this.tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "FINISHED"); + this.tubeModel.save(); + } + } + // 摇匀 private void shake( JsonNode stepNode ) { int count = stepNode.get("count").asInt(); @@ -207,10 +264,16 @@ public class TubeTestTask extends TaskBase { } // 孵育 - private void executeStepIncubate( JsonNode stepNode ) { - Device device = Device.getInstance(); - this.incubatorSlot = device.incubator.pushNewCard(this.testCard); + private void executeStepIncubate( JsonNode stepNode ) throws InterruptedException { + if ( -1 == this.testCard.incubatorSlotIndex ) { + synchronized ( this.testCard ) { + this.log("测试卡就绪等待"); + this.testCard.wait(); + this.log("测试卡就绪等待完成"); + } + } + Device device = Device.getInstance(); var pipette = device.pipette; pipette.tipPickUp(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index caf4dfc..ee66b1f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,7 @@ spring: profiles: - active: dev \ No newline at end of file + active: dev + +#mybatis: +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl