Browse Source

update

master
zhaohe 2 months ago
parent
commit
773663dcc8
  1. 1
      src/main/java/a8k/app/hardware/driver/PipetteCtrlDriverV2.java
  2. 4
      src/main/java/a8k/app/service/analyzer/A8kEcodeAnalyzer.java
  3. 4
      src/main/java/a8k/app/service/appsetup/A8kSubModuleRegInitService.java
  4. 6
      src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrService.java
  5. 37
      src/main/java/a8k/app/service/module/SamplePreProcessModule.java
  6. 1
      src/main/java/a8k/app/service/statemgr/IncubationPlateStateMgr.java
  7. 36
      src/main/java/a8k/app/service/statemgr/TubeStateMgr.java
  8. 3
      src/main/java/a8k/app/type/a8k/pos/IncubatorPos.java
  9. 24
      src/main/java/a8k/app/type/a8k/state/ProjectPreProcessContext.java
  10. 9
      src/main/java/a8k/app/utils/ActionTaskPool.java
  11. 24
      src/main/java/a8k/app/utils/ProjectParamUtils.java
  12. 48
      src/main/java/a8k/extui/page/driver/pipette_module/PipetteGunTestCtrlPage.java

1
src/main/java/a8k/app/hardware/driver/PipetteCtrlDriverV2.java

@ -333,6 +333,7 @@ public class PipetteCtrlDriverV2 {
}
public void pierceThroughBlock(ContainerCpyId containerCpyId, Integer containerPos) throws AppException {
log.info("pierceThroughBlock: containerCpyId={}, containerPos={}", containerCpyId, containerPos);
callcmd(MId.PipetteMod, CmdId.pipette_pump_pierce_through, containerCpyId.toInteger(), containerPos);
waitForMod(CmdId.pipette_pump_pierce_through);
}

4
src/main/java/a8k/app/service/analyzer/A8kEcodeAnalyzer.java

@ -8,7 +8,9 @@ public class A8kEcodeAnalyzer {
if (ecode.index >= A8kEcode.LOW_ERROR_HARDWARE_ERROR_START.index) {
//部分底层错误属于非必须停机的错误
return switch (ecode) {
case LOW_ERROR_PIPETTE_ERROR_TIP_POP_ERROR, LOW_ERROR_PIPETTE_ERROR_LLD_ERROR -> false;
case LOW_ERROR_PIPETTE_ERROR_TIP_POP_ERROR,
LOW_ERROR_PIPETTE_LLD_ERROR_NO_LIQUID_DETECTED,
LOW_ERROR_PIPETTE_ERROR_LLD_ERROR -> false;
default -> true;
};
} else {

4
src/main/java/a8k/app/service/appsetup/A8kSubModuleRegInitService.java

@ -48,6 +48,10 @@ public class A8kSubModuleRegInitService {
private void onAppEvent(AppEvent event) {
if (event instanceof A8kCanBusOnConnectEvent) {
if (gStateMgrService.isDeviceInited()) {
log.warn("board param already inited, skip init it again");
return;
}
var initThread = new Thread(this::initModuleRegVal);
initThread.setName("initModuleRegVal");
initThread.start();

6
src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrService.java

@ -111,7 +111,7 @@ public class LiquidOperationCtrService {
pipetteCtrlDriverV2.distributeAllBlock(new DistribuAllParam(
probeSubstanceContainerPos.z,
ContainerCpyId.DetectSubstancesCup,
LiquidConfigCpyIdx.NotSet,
LiquidConfigCpyIdx.Default,
true,
DistribuType.JET_DIST,
0,
@ -163,7 +163,7 @@ public class LiquidOperationCtrService {
ul * 10,
sampleContainerPos.z,
a8kSamplePosToContainerCpyId(from),
LiquidConfigCpyIdx.NotSet,
LiquidConfigCpyIdx.Default,
0,
1,
0,
@ -191,7 +191,7 @@ public class LiquidOperationCtrService {
pircePos = hbotLittleBSPosMgr.getLittleBSPiercePos(pos.group, pos.index);
containerCpyId = ContainerCpyId.LittleBufferCup;
}
hbotMoveExCtrlService.moveToXY(pircePos);
pipetteCtrlDriverV2.pierceThroughBlock(containerCpyId, pircePos.z);
}

37
src/main/java/a8k/app/service/module/SamplePreProcessModule.java

@ -154,17 +154,23 @@ public class SamplePreProcessModule {
// 预定孵育盘位置
for (var cxt : tube.getPreProcessContexts()) {
//预定孵育盘位置
cxt.incubatorPos = incubationPlateStateMgr.takeOneIncubationIDLEPos((IncubatorPos pos) ->
incubationPlateStateMgr.syncCxtInfo(pos, cxt.sampleInfo, cxt.projBuildinInfo, cxt.projExtInfoCard, cxt.consumableInfo)
var incubatorPos = incubationPlateStateMgr.takeOneIncubationIDLEPos((IncubatorPos pos) ->
incubationPlateStateMgr.syncCxtInfo(pos, cxt.getSampleInfo(), cxt.getProjBuildinInfo(), cxt.getProjExtInfoCard(), cxt.getConsumableInfo())
);
//使用耗材
consumablesMgrService.useReserveConsumable(cxt.consumableInfo);
tubeStateMgrService.setTubeCxtIncubationPos(cxt,incubatorPos);
consumablesMgrService.useReserveConsumable(cxt.getConsumableInfo());
}
tubeStateMgrService.changeTubeStateToResourceIsReady();
startTask();
actionTaskPool.waitAllDone();
if (actionTaskPool.isHasError()) {
sampleProcessRollback();
samplePrepareRollback();
}
AppError samplePrepareAppError = actionTaskPool.getError(TaskLine.SamplePrepare);
if (samplePrepareAppError != null && A8kEcodeAnalyzer.isFatalError(samplePrepareAppError)) {
throw AppException.of(samplePrepareAppError);
@ -177,8 +183,6 @@ public class SamplePreProcessModule {
if (samplePrepareAppError != null || sampleProcessAppError != null) {
//发生错误进行回滚
sampleProcessRollback();
samplePrepareRollback();
tubeStateMgrService.changeTubeStateToError(samplePrepareAppError, sampleProcessAppError);
} else {
// 样本处理完成
@ -190,6 +194,7 @@ public class SamplePreProcessModule {
void startTask() {
actionTaskPool.resetState();
actionTaskPool.pushTask(TaskLine.SamplePrepare, () -> {
Tube tube = tubeStateMgrService.getCurProcessingTube();
TubeHolder tubeHolder = tubeStateMgrService.getTubeHolder();
@ -233,34 +238,38 @@ public class SamplePreProcessModule {
List<ProjectPreProcessContext> cxts = tube.getPreProcessContexts();
//! 等待样本准备完成
sampleIsReadyCondition.waitTrue();
for (ProjectPreProcessContext cxt : cxts) {
Assert.isTrue(cxt.getIncubatorPos() != null, "cxt.incubatorPos != null");
boolean finalCxt = cxt.equals(cxts.getLast());
PreReactionPos preReactionPos = ProjectParamUtils.getPreReactionPos(tubeHolder, tube, cxt);
A8kReactionFlowType reactionFlowType = cxt.projBuildinInfo.reactionFlowType;
A8kReactionFlowType reactionFlowType = cxt.getProjBuildinInfo().reactionFlowType;
A8kSamplePos samplePos = ProjectParamUtils.getSamplePos(tubeHolder, tube);
Integer sampleVol = ProjectParamUtils.getSampleUl(cxt);
liquidOperationCtrService.setProjContext(cxt.projBuildinInfo, cxt.projExtInfoCard);
liquidOperationCtrService.setProjContext(cxt.getProjBuildinInfo(), cxt.getProjExtInfoCard());
//预处理
if (reactionFlowType.equals(A8kReactionFlowType.SampleAndBSAndProbeSubstance)) {
UISender.txInfoMsg(log, "取大瓶缓冲液到探测物质中");
LargeBufferPos largeBufferPos = LargeBufferPos.of(cxt.consumableInfo.group);
Integer largeBSVolume = cxt.projBuildinInfo.bigBufferSampleUl;
LargeBufferPos largeBufferPos = LargeBufferPos.of(cxt.getConsumableInfo().group);
Integer largeBSVolume = cxt.getProjBuildinInfo().bigBufferSampleUl;
Assert.notNull(largeBufferPos, "largeBufferPos != null");
docmd("取大瓶缓冲液", () -> {
liquidOperationCtrService.takeLargeBottleBufferLiquidToProbeSubstance(largeBufferPos, preReactionPos, largeBSVolume);
});
} else if (reactionFlowType.equals(A8kReactionFlowType.SampleAndBS)) {
docmd("刺破小瓶缓冲液", () -> liquidOperationCtrService.pirceLittleBuffer(preReactionPos));
}
sampleIsReadyCondition.waitTrue();
//取样本到小缓冲瓶或者探测物质
if (reactionFlowType.equals(A8kReactionFlowType.SampleAndBS)) {
docmd("刺破小瓶缓冲液", () -> liquidOperationCtrService.pirceLittleBuffer(preReactionPos));
UISender.txInfoMsg(log, "取样");
docmd("取样", () -> liquidOperationCtrService.takeSampleToPreReactionPos(samplePos, preReactionPos, sampleVol));
} else if (reactionFlowType.equals(A8kReactionFlowType.SampleAndBSAndProbeSubstance)) {
@ -274,9 +283,9 @@ public class SamplePreProcessModule {
liquidOperationCtrService.takePreReactionLiquid(preReactionPos);
//开始孵育
UISender.txInfoMsg(log, "开始孵育");
Integer incubatedTimeSec = cxt.projBuildinInfo.reactionPlateIncubationTimeMin * 60;
Integer incubatedTimeSec = cxt.getProjBuildinInfo().reactionPlateIncubationTimeMin * 60;
incubationPlateCtrlModule.dropLiquidAndStartIncubating(
cxt.incubatorPos,
cxt.getIncubatorPos(),
() -> docmd("取反应液到孵育盘", liquidOperationCtrService::dropLiquidToReactionPlate),
incubatedTimeSec
);

1
src/main/java/a8k/app/service/statemgr/IncubationPlateStateMgr.java

@ -144,6 +144,7 @@ public class IncubationPlateStateMgr {
return subtank.getPos();
}
}
log.warn("孵育盘没有空闲位置可用");
return null;
}

36
src/main/java/a8k/app/service/statemgr/TubeStateMgr.java

@ -4,6 +4,7 @@ import a8k.app.dao.type.db.DeviceStatistic;
import a8k.app.service.analyzer.ConsumableStateAnalyzerService;
import a8k.app.type.DeviceRunMode;
import a8k.app.type.a8k.pos.ConsumableInfo;
import a8k.app.type.a8k.pos.IncubatorPos;
import a8k.app.type.a8k.state.*;
import a8k.app.type.error.AEConsumeNotEnoughError;
import a8k.app.type.a8k.proj.ProjBriefInfo;
@ -238,7 +239,7 @@ public class TubeStateMgr {
//try delete the last emergency sample
if (tube.getState().isEq(TubeState.TO_BE_PROCESSED)) {
for (ProjectPreProcessContext cxt : tube.getPreProcessContexts()) {
consumablesMgrService.bakReserveConsumable(cxt.consumableInfo);
consumablesMgrService.bakReserveConsumable(cxt.getConsumableInfo());
}
deleteSampleInfo(tube.getSampleId()); //delete sample info
}
@ -279,14 +280,42 @@ public class TubeStateMgr {
String sampleId = tube.getSampleId();
if (sampleId != null && !sampleId.isEmpty()) {
for (ProjectPreProcessContext cxt : tube.getPreProcessContexts()) {
consumablesMgrService.bakReserveConsumable(cxt.consumableInfo);
consumablesMgrService.bakReserveConsumable(cxt.getConsumableInfo());
}
deleteSampleInfo(tube.getSampleId()); //delete sample info
}
throw e;
}
}
synchronized public void setTubeCxtIncubationPos(ProjectPreProcessContext cxt, IncubatorPos pos) {
if (cxt == null || pos == null) {
log.error("设置试管预处理上下文孵育盘位置失败,cxt或pos为空");
return;
}
if (emergencyTubePos.tube != null) {
for (ProjectPreProcessContext context : emergencyTubePos.tube.getPreProcessContexts()) {
if (context.getCxtId().equals(cxt.getCxtId())) {
context.setIncubatorPos(pos);
log.info("设置急诊试管孵育盘预定位置,pos:{}", pos);
return;
}
}
}
for (Tube tube : tubeHolder.getTubes()) {
if (tube == null) {
continue;
}
for (ProjectPreProcessContext context : tube.getPreProcessContexts()) {
if (context.getCxtId().equals(cxt.getCxtId())) {
context.setIncubatorPos(pos);
log.info("设置试管{}孵育盘预定位置, pos:{}", tube.getPos(), pos);
return;
}
}
}
log.error("设置试管预处理上下文孵育盘位置失败,未找到对应的上下文 cxtId:{}", cxt.getCxtId());
}
@ -380,6 +409,7 @@ public class TubeStateMgr {
curProcessingTube.setState(TubeState.RESOURCE_IS_READY);
}
synchronized public void changeTubeStateToProcessing() {
log.info("试管 状态->处理中 SampleId:{}", curProcessingTube.getSampleId());
curProcessingTube.setState(TubeState.PROCESSING);
@ -471,7 +501,7 @@ public class TubeStateMgr {
private void bakReserveConsumable(TubeHolder tubeHolder) {
for (Tube tube : tubeHolder.getTubes()) {
for (ProjectPreProcessContext cxt : tube.getPreProcessContexts()) {
consumablesMgrService.bakReserveConsumable(cxt.consumableInfo);
consumablesMgrService.bakReserveConsumable(cxt.getConsumableInfo());
}
}
}

3
src/main/java/a8k/app/type/a8k/pos/IncubatorPos.java

@ -1,5 +1,7 @@
package a8k.app.type.a8k.pos;
import org.springframework.util.Assert;
/**
* 孵育盘位置 1->20
*/
@ -37,6 +39,7 @@ public enum IncubatorPos {
return pos;
}
}
Assert.isTrue(off >= 0 && off < 20, "IncubatorPos off must be in [0, 19]");
return null;
}
}

24
src/main/java/a8k/app/type/a8k/state/ProjectPreProcessContext.java

@ -5,31 +5,39 @@ import a8k.app.dao.type.db.ProjExtInfoCard;
import a8k.app.type.a8k.pos.ConsumableInfo;
import a8k.app.type.a8k.pos.IncubatorPos;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
import java.io.Serializable;
@Data
public class ProjectPreProcessContext implements Serializable {
public String cxtId = "";
public SampleInfo sampleInfo = null;
public ConsumableInfo consumableInfo = null; //耗材信息
public Integer projId = 0; //项目ID
private String cxtId = "";
private SampleInfo sampleInfo = null;
private ConsumableInfo consumableInfo = null; //耗材信息
private Integer projId = 0; //项目ID
@JsonIgnore
public ProjBuildInInfo projBuildinInfo;
private ProjBuildInInfo projBuildinInfo;
@JsonIgnore
public ProjExtInfoCard projExtInfoCard;
public IncubatorPos incubatorPos = null; //孵育盘位置
private ProjExtInfoCard projExtInfoCard;
private IncubatorPos incubatorPos = null; //孵育盘位置
private Integer off = 0; //项目在样本上的偏移量
static private long uuid = 0;
public ProjectPreProcessContext(SampleInfo sampleInfo,
ConsumableInfo consumableInfo,
Integer projId,
ProjBuildInInfo projBuildinInfo,
ProjExtInfoCard projExtInfoCard, Integer off) {
this.cxtId = String.format("{}-{}-{}", sampleInfo.sampleId, projId, off);
this.cxtId = String.format("%s-%s-%s-%d", sampleInfo.sampleId, projId, off, uuid++);
this.sampleInfo = sampleInfo;
this.consumableInfo = consumableInfo;
this.projId = projId;
this.projBuildinInfo = projBuildinInfo;
this.projExtInfoCard = projExtInfoCard;
this.incubatorPos = null; //孵育盘位置
this.off = off;
}
public ProjectPreProcessContext() {

9
src/main/java/a8k/app/utils/ActionTaskPool.java

@ -30,6 +30,8 @@ public class ActionTaskPool {
Map<Enum<?>, AppError> errorMap = new HashMap<>();
List<BoolCondition> boolConditions = new ArrayList<>();
public ActionTaskPool(Integer threadNum) {
executor = new ThreadPoolExecutor(threadNum, threadNum, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10));
}
@ -51,13 +53,18 @@ public class ActionTaskPool {
}
public BoolCondition createBoolCondition(String name) {
return new BoolCondition(name, () -> errorFlag);
var boolCondition = new BoolCondition(name, () -> errorFlag);
boolConditions.add(boolCondition);
return boolCondition;
}
public void resetState() {
errorFlag = false;
errorMap.clear();
futures.clear();
for (var boolCondition : boolConditions) {
boolCondition.clear();
}
}
public AppError getError(Enum<?> taskLineMaskter) {

24
src/main/java/a8k/app/utils/ProjectParamUtils.java

@ -40,18 +40,18 @@ public class ProjectParamUtils {
}
static public PreReactionPos getPreReactionPos(TubeHolder tubeHolder, Tube tube, ProjectPreProcessContext cxt) {
A8kReactionFlowType reactionFlowType = cxt.projBuildinInfo.reactionFlowType;
A8kReactionFlowType reactionFlowType = cxt.getProjBuildinInfo().reactionFlowType;
PreReactionPos preReactionPos = new PreReactionPos();
switch (reactionFlowType) {
case SampleAndBS -> {
preReactionPos.type = ConsumableType.SmallBottleBuffer;
preReactionPos.group = cxt.consumableInfo.group;
preReactionPos.index = cxt.consumableInfo.pos;
preReactionPos.group = cxt.getConsumableInfo().group;
preReactionPos.index = cxt.getConsumableInfo().pos;
}
case SampleAndBSAndProbeSubstance -> {
preReactionPos.type = ConsumableType.ProbeSubstance;
preReactionPos.group = cxt.consumableInfo.group;
preReactionPos.index = cxt.consumableInfo.pos;
preReactionPos.group = cxt.getConsumableInfo().group;
preReactionPos.index = cxt.getConsumableInfo().pos;
}
}
return preReactionPos;
@ -59,17 +59,17 @@ public class ProjectParamUtils {
static public Integer getSampleUl(ProjectPreProcessContext cxt) {
Integer sampleUl = 0;
switch (cxt.sampleInfo.bloodType) {
switch (cxt.getSampleInfo().bloodType) {
case WHOLE_BLOOD -> {
sampleUl = cxt.projBuildinInfo.wBloodSampleVolUl;
if (cxt.projExtInfoCard.wBloodSampleVolUl > 0) {
sampleUl = cxt.projExtInfoCard.wBloodSampleVolUl;
sampleUl = cxt.getProjBuildinInfo().wBloodSampleVolUl;
if (cxt.getProjExtInfoCard().wBloodSampleVolUl > 0) {
sampleUl = cxt.getProjExtInfoCard().wBloodSampleVolUl;
}
}
case SERUM_OR_PLASMA -> {
sampleUl = cxt.projBuildinInfo.serumSampleVolUl;
if (cxt.projExtInfoCard.serumSampleVolUl > 0) {
sampleUl = cxt.projExtInfoCard.serumSampleVolUl;
sampleUl = cxt.getProjBuildinInfo().serumSampleVolUl;
if (cxt.getProjExtInfoCard().serumSampleVolUl > 0) {
sampleUl = cxt.getProjExtInfoCard().serumSampleVolUl;
}
}
} ;

48
src/main/java/a8k/extui/page/driver/pipette_module/PipetteGunTestCtrlPage.java

@ -1,5 +1,6 @@
package a8k.extui.page.driver.pipette_module;
import a8k.OS;
import a8k.app.hardware.driver.PipetteCtrlDriverV2;
import a8k.app.hardware.type.pipette_module.cpyidx.ContainerCpyId;
import a8k.app.hardware.type.pipette_module.cpyidx.LiquidConfigCpyIdx;
@ -30,6 +31,7 @@ public class PipetteGunTestCtrlPage {
private Integer containerPos = 0; // 默认容器位置
@Setter
private ContainerCpyId containerCpyId = ContainerCpyId.Default; // 默认容器配置索引
private Boolean breakFlag = false;
public ExtApiCurve getAspPressureRecord() throws AppException {
var datas = pipetteCtrlDriverV2.getAspPressureData();
@ -48,6 +50,50 @@ public class PipetteGunTestCtrlPage {
return getAspPressureRecord();
}
public void breakPipetteTestPumpMoveToX100nlV2() {
breakFlag = true;
}
public ExtApiCurve pipetteTestPumpMoveToX100nlV2(Integer x100nl, PMVCpyIdx vcpyidx) throws AppException {
pipetteCtrlDriverV2.pipetteTestPumpMoveToX100nl(x100nl, vcpyidx.toInteger());
pipetteCtrlDriverV2.zMotorEnable(1);
pipetteCtrlDriverV2.zMotorMoveByBlock(-200);
pipetteCtrlDriverV2.zMotorEnable(0);
breakFlag = false;
List<Integer> pressures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
Integer pressure = pipetteCtrlDriverV2.readPressure();
pressures.add(pressure);
log.info("read pressure:{}/{}", i, 1000);
OS.forceSleep(10);
if (breakFlag) {
break;
}
}
List<Integer> aspPressures = pipetteCtrlDriverV2.getAspPressureData();
List<Object[]> points = new ArrayList<>();
int i = 0;
for (Integer pressure : aspPressures) {
points.add(new Object[]{i, pressure});
log.info("AspPressure: {}", pressure);
i++;
}
points.add(new Object[]{i++, 3000});
points.add(new Object[]{i++, 3000});
points.add(new Object[]{i++, 3000});
for (Integer pressure : pressures) {
points.add(new Object[]{i, pressure});
log.info("AfterAspPressure: {}", pressure);
i++;
}
return CurveBuilder.buidCurve("压力曲线", "value", "value", 1500, 3100, points);
}
public void pipetteTestLld(LiquidConfigCpyIdx liquidCpyId) throws AppException {
pipetteCtrlDriverV2.pipetteTestLld(containerPos, containerCpyId, liquidCpyId);
}
@ -97,6 +143,8 @@ public class PipetteGunTestCtrlPage {
page.addFunction("设置容器配置索引", this::setContainerCpyId).setParamVal("containerCpyId", () -> containerCpyId);
page.newGroup(" 操作");
page.addFunction("单元测试-测试泵机移动到X100nl", this::pipetteTestPumpMoveToX100nl);
page.addFunction("单元测试-测试泵机移动到X100nl-V2", this::pipetteTestPumpMoveToX100nlV2);
page.addFunction("单元测试-中断-泵机移动到X100nl-V2", this::breakPipetteTestPumpMoveToX100nlV2);
page.addFunction("单元测试-测试泵机LLD", this::pipetteTestLld);
page.addFunction("泵机移动到容器底部", this::pipetteTestMoveToContainerBottom);

Loading…
Cancel
Save