Browse Source

启动时关闭测试任务

tags/v0
sige 1 year ago
parent
commit
26145278b6
  1. BIN
      app.db
  2. 10
      src/main/java/com/iflytop/a800/BoditechA800ApplicationRunner.java
  3. 2
      src/main/java/com/iflytop/a800/controller/DemoController.java
  4. 55
      src/main/java/com/iflytop/a800/device/DeviceComponent.java
  5. 13
      src/main/java/com/iflytop/a800/device/Incubator.java
  6. 44
      src/main/java/com/iflytop/a800/model/MdbTest.java
  7. 5
      src/main/java/com/iflytop/a800/model/MdbTubeRackTestTaskTube.java
  8. 5
      src/main/java/com/iflytop/a800/task/TubeRackTask.java
  9. 59
      src/main/java/com/iflytop/a800/task/TubeTestTask.java

BIN
app.db

10
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"));
}
}

2
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();
}

55
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<DeviceComponentLock> 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();
}
}
}

13
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<IncubatorSlot> 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<String,Object> params = Map.of("slot", slotPos);
UfCmdSnippetExecutor.execute("IncubatorTestCardExitToScanner", params);
slot.card = null;
this.unlock(lock);
}
}

44
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);
}
}

5
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";

5
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();
}

59
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<count; i++ ) {
UfActuatorCmdExecutor.execute("MotorTubeShake","step_motor_easy_move_to", "60");
@ -196,6 +212,12 @@ public class TubeTestTask extends TaskBase {
// 取盖
private void uncap() {
if ( !this.isFirstTestOfTube() ) {
this.log("非当前试管第一个项目,无需脱帽操作");
return ;
}
this.log("脱帽");
if ( UfMdbDictItem.match("TUBE_TYPE", "WB_5ML", this.tube.type) ) {
UfCmdSnippetExecutor.execute("SampleTestUnCap");
} else if ( UfMdbDictItem.match("TUBE_TYPE", "WB_3ML", this.tube.type) ) {
@ -205,13 +227,17 @@ public class TubeTestTask extends TaskBase {
// 盖帽
private void cap() {
if ( !this.isLastTestOfTube() ) {
this.log("非当前试管最后一个项目,无需盖帽操作");
return ;
}
this.log("盖帽");
if ( UfMdbDictItem.match("TUBE_TYPE", "WB_5ML", this.tube.type) ) {
UfCmdSnippetExecutor.execute("SampleTestCap");
} else if ( UfMdbDictItem.match("TUBE_TYPE", "WB_3ML", this.tube.type) ) {
UfCmdSnippetExecutor.execute("SampleTestCap");
}
this.emit("TubeSamplingFinished");
}
// 穿孔
@ -277,22 +303,28 @@ public class TubeTestTask extends TaskBase {
var pipette = device.pipette;
pipette.tipPickUp();
// 提交样本至孵育盘测试卡
var incubatorLock = device.incubator.lock("SampleCommit");
Integer volume = stepNode.get("volume").asInt();
pipette.aspirateFromBufferTubeAndDispenseToTestCard(this.bufferTube, volume);
device.incubator.unlock(incubatorLock);
pipette.tipDrop();
// 孵育盘
int duration = stepNode.get("duration").asInt();
this.test.incubateStartedAt = (int)(System.currentTimeMillis() / 1000);
this.test.incubateDuration = duration / 1000;
this.test.save();
TimerTask wakeupTask = new TimerTask() {
@Override
public void run() {
TubeTestTask.this.log("孵育完成,等待扫描结果");
TubeTestTask.this.setStatus("READY");
}
};
Timer timer = new Timer();
timer.schedule(wakeupTask, duration);
this.taskWait("IncubateDone");
this.log("孵育完成,等待扫描结果");
}
// 扫描
@ -300,7 +332,7 @@ public class TubeTestTask extends TaskBase {
this.log("扫描结果");
Device device = Device.getInstance();
device.incubator.exitToScanner(this.incubatorSlot);
device.incubator.exitCardToScanner(this.incubatorSlot);
var scanner = device.scanner;
scanner.scanTypeF();
var scanResult = scanner.readResult();
@ -431,4 +463,23 @@ public class TubeTestTask extends TaskBase {
throw new RuntimeException("unknown x source" + xSource);
}
}
/**
* 检查当前测试是否为该试管的第一个项目
* @return Boolean
*/
private Boolean isFirstTestOfTube() {
var projects = this.tubeModel.getProjectIdList();
return Objects.equals(projects.get(0), this.project.id);
}
/**
* 检查当前测试是否为该试管的最后一个项目
* @return Boolean
*/
private Boolean isLastTestOfTube() {
var projects = this.tubeModel.getProjectIdList();
var lastId = projects.get(projects.size() - 1);
return Objects.equals(lastId, this.project.id);
}
}
Loading…
Cancel
Save