diff --git a/app.db b/app.db index 2230299..3fb31de 100644 Binary files a/app.db and b/app.db differ diff --git a/src/main/java/com/iflytop/a800/BoditechA800ApplicationRunner.java b/src/main/java/com/iflytop/a800/BoditechA800ApplicationRunner.java index 28a72b2..ee4354d 100644 --- a/src/main/java/com/iflytop/a800/BoditechA800ApplicationRunner.java +++ b/src/main/java/com/iflytop/a800/BoditechA800ApplicationRunner.java @@ -1,4 +1,5 @@ package com.iflytop.a800; +import com.iflytop.a800.model.MdbTubeRackTestTask; import com.iflytop.a800.model.MdbTubeRackTestTaskTube; import com.iflytop.uf.UfActiveRecord; import com.iflytop.uf.model.UfMdbDictItem; @@ -7,9 +8,7 @@ import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; - import java.util.Map; - @Order(10) @Component public class BoditechA800ApplicationRunner implements ApplicationRunner { @@ -23,5 +22,12 @@ public class BoditechA800ApplicationRunner implements ApplicationRunner { UfActiveRecord.updateAll(MdbTubeRackTestTaskTube.class, cancelStatus, Map.of("status", "NEW")); UfActiveRecord.updateAll(MdbTubeRackTestTaskTube.class, cancelStatus, Map.of("status", "WAITING")); UfActiveRecord.updateAll(MdbTubeRackTestTaskTube.class, cancelStatus, Map.of("status", "EXECUTING")); + + // 将所有试管架任务状态改为已取消 + cancelStatus = Map.of("status", UfMdbDictItem.getValue("TUBE_RACK_TASK_STATUS", "CANCELED")); + UfActiveRecord.updateAll(MdbTubeRackTestTask.class, cancelStatus, Map.of("status", "CONFIGURING")); + UfActiveRecord.updateAll(MdbTubeRackTestTask.class, cancelStatus, Map.of("status", "WAITING")); + UfActiveRecord.updateAll(MdbTubeRackTestTask.class, cancelStatus, Map.of("status", "PROCESSING")); + UfActiveRecord.updateAll(MdbTubeRackTestTask.class, cancelStatus, Map.of("status", "ERROR")); } } diff --git a/src/main/java/com/iflytop/a800/controller/DemoController.java b/src/main/java/com/iflytop/a800/controller/DemoController.java index 7ec6ff5..e91c21b 100644 --- a/src/main/java/com/iflytop/a800/controller/DemoController.java +++ b/src/main/java/com/iflytop/a800/controller/DemoController.java @@ -126,7 +126,7 @@ public class DemoController extends UfApiControllerBase { for ( int i = 0; i < 20; i++ ) { var slot = new IncubatorSlot(); slot.index = i; - incubator.exitToScanner(slot); + incubator.exitCardToScanner(slot); } return this.success(); } diff --git a/src/main/java/com/iflytop/a800/device/DeviceComponent.java b/src/main/java/com/iflytop/a800/device/DeviceComponent.java new file mode 100644 index 0000000..f88f447 --- /dev/null +++ b/src/main/java/com/iflytop/a800/device/DeviceComponent.java @@ -0,0 +1,55 @@ +package com.iflytop.a800.device; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; +abstract public class DeviceComponent { + // logger + public static final Logger LOG = LoggerFactory.getLogger(DeviceComponent.class); + // lock object class + public static class DeviceComponentLock { + public String name; + } + + // locks + private final List locks = new ArrayList<>(); + + // lock + public DeviceComponentLock lock( String name ) { + LOG.info("LOCK : {}", name); + + var lock = new DeviceComponentLock(); + lock.name = name; + synchronized ( this.locks ) { + this.locks.add(lock); + if ( 1 == this.locks.size() ) { + return lock; + } + } + + LOG.info("LOCK WAITING : {}", name); + try { + lock.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + LOG.info("LOCK DONE : {}", name); + return lock; + } + + // unlock + public void unlock( DeviceComponentLock lock ) { + synchronized ( this.locks ) { + LOG.info("UNLOCK : {}", lock.name); + this.locks.remove(lock); + if ( this.locks.isEmpty() ) { + return ; + } + + var next = this.locks.get(0); + LOG.info("NOTIFY NEXT : {}", next.name); + next.notifyAll(); + } + } +} diff --git a/src/main/java/com/iflytop/a800/device/Incubator.java b/src/main/java/com/iflytop/a800/device/Incubator.java index 7f76020..a48babb 100644 --- a/src/main/java/com/iflytop/a800/device/Incubator.java +++ b/src/main/java/com/iflytop/a800/device/Incubator.java @@ -6,7 +6,7 @@ import com.iflytop.uf.model.UfMdbOption; import java.util.ArrayList; import java.util.List; import java.util.Map; -public class Incubator { +public class Incubator extends DeviceComponent { // 槽位 private final List slots; // 槽位起始索引 @@ -23,11 +23,10 @@ public class Incubator { } } - public void lock() {} - public void unlock() {} - // 推送新卡片 public IncubatorSlot pushNewCard(TestCard card) { + var lock = this.lock("PushNewCard"); + IncubatorSlot slot = null; for ( int i=0; i<20; i++ ) { var tmpSlot = this.slots.get(this.slotStartIndex); @@ -57,16 +56,20 @@ public class Incubator { ); UfCmdSnippetExecutor.execute("IncubatorTestCardPushIn", params); slot.card = card; + + this.unlock(lock); return slot; } // 退出到扫描 - public void exitToScanner( IncubatorSlot slot ) { + public void exitCardToScanner( IncubatorSlot slot ) { + var lock = this.lock("ExitCardToScanner"); Integer startPos = UfMdbOption.getInteger("IncubatorSlotExitStartPos"); Integer distance = UfMdbOption.getInteger("IncubatorSlotExitDistance"); Integer slotPos = startPos + distance * slot.index; Map params = Map.of("slot", slotPos); UfCmdSnippetExecutor.execute("IncubatorTestCardExitToScanner", params); slot.card = null; + this.unlock(lock); } } diff --git a/src/main/java/com/iflytop/a800/model/MdbTest.java b/src/main/java/com/iflytop/a800/model/MdbTest.java index d91448e..b9702ca 100644 --- a/src/main/java/com/iflytop/a800/model/MdbTest.java +++ b/src/main/java/com/iflytop/a800/model/MdbTest.java @@ -1,24 +1,68 @@ package com.iflytop.a800.model; import com.iflytop.uf.UfActiveRecord; import com.iflytop.uf.UfActiveRecordField; +import com.iflytop.uf.model.UfMdbRuntimeVariable; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + public class MdbTest extends UfActiveRecord { @UfActiveRecordField public String tubeId; @UfActiveRecordField + public String sampleUid; + + @UfActiveRecordField + public String sampleType; + + @UfActiveRecordField public String isSamplingFinished; @UfActiveRecordField public String projectCode; @UfActiveRecordField + public String projectName; + + @UfActiveRecordField + public String serialCode; + + @UfActiveRecordField + public String barCode; + + @UfActiveRecordField public String materialLotCode; @UfActiveRecordField + public Integer incubateSlotIndex; + + @UfActiveRecordField + public Integer incubateStartedAt; + + @UfActiveRecordField + public Integer incubateDuration; + + @UfActiveRecordField public String result; // get table name public static String getTableName() { return "app_tests"; } + + // 生成序列号 + public static String generateSerialCode() { + var count = UfMdbRuntimeVariable.getInteger("TestCountOfDay", 0); + var date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMdd")); + var runtimeDate = UfMdbRuntimeVariable.getString("TestSerialDate", ""); + if (!date.equals(runtimeDate)) { + UfMdbRuntimeVariable.setString("TestSerialDate", date); + count = 0; + } + + count ++; + UfMdbRuntimeVariable.setInteger("TestCountOfDay", count); + return String.format("%s-%03d", date, count); + } } diff --git a/src/main/java/com/iflytop/a800/model/MdbTubeRackTestTaskTube.java b/src/main/java/com/iflytop/a800/model/MdbTubeRackTestTaskTube.java index d9c0869..7e9ae84 100644 --- a/src/main/java/com/iflytop/a800/model/MdbTubeRackTestTaskTube.java +++ b/src/main/java/com/iflytop/a800/model/MdbTubeRackTestTaskTube.java @@ -3,10 +3,8 @@ import com.iflytop.a800.device.Device; import com.iflytop.uf.UfActiveRecord; import com.iflytop.uf.UfActiveRecordField; import com.iflytop.uf.util.UfJsonHelper; - import java.util.ArrayList; import java.util.List; - public class MdbTubeRackTestTaskTube extends UfActiveRecord { @UfActiveRecordField public String rackId; @@ -38,6 +36,9 @@ public class MdbTubeRackTestTaskTube extends UfActiveRecord { @UfActiveRecordField public String status; + @UfActiveRecordField + public String type = ""; + // get table name public static String getTableName() { return "app_tube_rack_test_task_tubes"; diff --git a/src/main/java/com/iflytop/a800/task/TubeRackTask.java b/src/main/java/com/iflytop/a800/task/TubeRackTask.java index c26c2f1..c08d997 100644 --- a/src/main/java/com/iflytop/a800/task/TubeRackTask.java +++ b/src/main/java/com/iflytop/a800/task/TubeRackTask.java @@ -35,10 +35,11 @@ public class TubeRackTask extends TaskBase { feeder.feed(); this.samplingTubes = new ArrayList<>(); - // test tube rack TestTubeRack tubeRack = new TestTubeRack(); tubeRack.type = feeder.readTubeRackType(); tubeRack.tubes = new ArrayList<>(); + this.taskModel.tubeType = tubeRack.type; + this.taskModel.save(); for (int i = 0; i < 10; i++) { var tube = new TestTube(); tube.index = i; @@ -78,6 +79,8 @@ public class TubeRackTask extends TaskBase { taskMan.append(testTask); } + tubeModel.type = tube.type; + tubeModel.barCode = tube.barCode; tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "WAITING"); tubeModel.save(); } diff --git a/src/main/java/com/iflytop/a800/task/TubeTestTask.java b/src/main/java/com/iflytop/a800/task/TubeTestTask.java index 10b3c08..0d611d7 100644 --- a/src/main/java/com/iflytop/a800/task/TubeTestTask.java +++ b/src/main/java/com/iflytop/a800/task/TubeTestTask.java @@ -71,8 +71,15 @@ public class TubeTestTask extends TaskBase { // 测试记录 this.test = new MdbTest(); this.test.tubeId = this.tubeModel.id; + this.test.sampleUid = this.tubeModel.sampleUid; + this.test.sampleType = this.tubeModel.sampleType; this.test.projectCode = this.project.code; + this.test.projectName = this.project.name; this.test.materialLotCode = this.testCard.lotCode; + this.test.barCode = this.tubeModel.barCode; + this.test.serialCode = MdbTest.generateSerialCode(); + this.test.incubateStartedAt = 0; + this.test.incubateDuration = 0; this.test.isSamplingFinished = "no"; this.test.save(); @@ -109,6 +116,8 @@ public class TubeTestTask extends TaskBase { this.log("测试卡推送至孵育盘 :开始"); this.incubatorSlot = device.incubator.pushNewCard(this.testCard); this.testCard.incubatorSlotIndex = this.incubatorSlot.index; + this.test.incubateSlotIndex = this.testCard.incubatorSlotIndex; + this.test.save(); this.log("测试卡推送至孵育盘 : 完成 -> Slot={}", this.testCard.incubatorSlotIndex); synchronized ( this.testCard ) { this.testCard.notifyAll(); @@ -180,12 +189,19 @@ public class TubeTestTask extends TaskBase { this.log("试管采样结束"); this.tubeModel.status = UfMdbDictItem.getValue("TUBE_STATUS", "FINISHED"); this.tubeModel.save(); + this.emit("TubeSamplingFinished"); } } // 摇匀 private void shake( JsonNode stepNode ) { + if ( !this.isFirstTestOfTube() ) { + this.log("非当前试管第一个项目,无需摇匀操作"); + return ; + } + int count = stepNode.get("count").asInt(); + this.log("摇匀 : {}", count); UfCmdSnippetExecutor.execute("SampleTestShakePrepare"); for ( int i=0; i