diff --git a/src/main/java/a8k/baseservice/AppExceptionBuilder.java b/src/main/java/a8k/baseservice/AppExceptionBuilder.java index aaf4456..98ca1ec 100644 --- a/src/main/java/a8k/baseservice/AppExceptionBuilder.java +++ b/src/main/java/a8k/baseservice/AppExceptionBuilder.java @@ -1,14 +1,19 @@ package a8k.baseservice; +import a8k.type.ecode.AppCodeError; +import a8k.type.ecode.AppError; import a8k.type.ecode.ConsumeNotEnoughError; import a8k.type.exception.AppException; import jakarta.annotation.Resource; +import org.slf4j.Logger; import org.springframework.stereotype.Component; import a8k.service.appdata.AppProjInfoMgrService; +import java.util.List; + @Component public class AppExceptionBuilder { - + static Logger logger = org.slf4j.LoggerFactory.getLogger(AppExceptionBuilder.class); @Resource AppProjInfoMgrService projConfigMgrService; @@ -19,4 +24,14 @@ public class AppExceptionBuilder { return new AppException(new ConsumeNotEnoughError(projName, projIndex)); } + public AppException buildMutiErrorAppException(List errors) { + if (errors.size() > 1) { + logger.error("buildMutiErrorAppException errors size > 1,drop all errors except the first one"); + } + return new AppException(errors.get(0)); + } + + public AppException buildAppCodeErrorException(String extraMsg) { + return new AppException(new AppCodeError(extraMsg)); + } } diff --git a/src/main/java/a8k/hardware/type/a8kcanprotocol/A8kEcode.java b/src/main/java/a8k/hardware/type/a8kcanprotocol/A8kEcode.java index bc50cd4..b9e35b6 100644 --- a/src/main/java/a8k/hardware/type/a8kcanprotocol/A8kEcode.java +++ b/src/main/java/a8k/hardware/type/a8kcanprotocol/A8kEcode.java @@ -58,6 +58,7 @@ public enum A8kEcode { TubeHolderTypeIsNotSupport(140),//试管架类型不支持 EmergencySampleIsProcessing(141),//急诊样本正在处理中 ConsumeNotEnough(142),//耗材不足 + TakeTubeFail(143),//取试管失败 /** * 特殊服务错误码 diff --git a/src/main/java/a8k/service/appdevicectrl/AppConsumablesMgrService.java b/src/main/java/a8k/service/appdevicectrl/AppConsumablesMgrService.java index 8e8e80a..e226f67 100644 --- a/src/main/java/a8k/service/appdevicectrl/AppConsumablesMgrService.java +++ b/src/main/java/a8k/service/appdevicectrl/AppConsumablesMgrService.java @@ -8,6 +8,7 @@ import a8k.service.appdata.AppProjInfoMgrService; import a8k.service.appdevicectrl.type.app_consumables_mgr_service.*; import a8k.service.appstate.AppA8kGStateService; import a8k.service.devicedriver.ctrl.ConsumablesScanCtrl; +import a8k.type.ConsumableGroup; import a8k.type.exception.AppException; import a8k.type.projecttype.A8kReactionFlowType; import a8k.type.projecttype.a8kidcard.A8kIdCardInfo; @@ -114,42 +115,13 @@ public class AppConsumablesMgrService { assert a8kIdCardInfo != null; assert reactionType != null; - cState.reactionPlateGroup[ch] = new ReactionPlateGroup(// - result.projIndex, // - a8kIdCardInfo.projName, // - result.lotId, // - a8kIdCardInfo.color, // - EachConsumableNum); - cState.reactionPlateGroup[ch].editable = true; - + cState.reactionPlateGroup[ch] = new ReactionPlateGroup(result.projIndex, a8kIdCardInfo.projName, result.lotId, a8kIdCardInfo.color, EachConsumableNum); if (reactionType.equals(A8kReactionFlowType.ReactionWithLittBS)) { - cState.littBSGroup[ch] = new LittBSGroup(// - result.projIndex, // - a8kIdCardInfo.projName, // - result.lotId, // - a8kIdCardInfo.color, // - EachConsumableNum); - cState.littBSGroup[ch].editable = true; - - cState.larBSGroup[ch] = new LarBSGroup(); - cState.larBSGroup[ch].editable = false; + cState.littBSGroup[ch] = new LittBSGroup(result.projIndex, a8kIdCardInfo.projName, result.lotId, a8kIdCardInfo.color, EachConsumableNum); + cState.larBSGroup[ch] = new LarBSGroup(); } else if (reactionType.equals(A8kReactionFlowType.ReactionWithLarBsAndDetection)) { - cState.littBSGroup[ch] = new LittBSGroup(// - result.projIndex, // - a8kIdCardInfo.projName, // - result.lotId, // - a8kIdCardInfo.color, // - EachConsumableNum); - cState.littBSGroup[ch].editable = true; - - cState.larBSGroup[ch] = new LarBSGroup(// - result.projIndex, // - a8kIdCardInfo.projName, // - result.lotId, // - a8kIdCardInfo.color, // - EachConsumableNum); - cState.larBSGroup[ch].editable = true; - + cState.littBSGroup[ch] = new LittBSGroup(result.projIndex, a8kIdCardInfo.projName, result.lotId, a8kIdCardInfo.color, EachConsumableNum); + cState.larBSGroup[ch] = new LarBSGroup(result.projIndex, a8kIdCardInfo.projName, result.lotId, a8kIdCardInfo.color, EachConsumableNum); } else { throw new RuntimeException("未知的反应流程类型"); } @@ -233,9 +205,9 @@ public class AppConsumablesMgrService { var cState = gstate.getConsumableState(); assert cState != null; cState.reactionPlateGroup[ch].num = num; - if (cState.littBSGroup[ch].editable) + if (cState.littBSGroup[ch].enable) cState.littBSGroup[ch].num = num; - if (cState.larBSGroup[ch].editable) + if (cState.larBSGroup[ch].enable) cState.larBSGroup[ch].num = num; } @@ -257,7 +229,6 @@ public class AppConsumablesMgrService { } synchronized public Boolean isHasEnoughConsumables(List projIndex) { - // //只检查反应板的数量即可 for (Integer index : projIndex) { Integer total = getConsumableNum(index); @@ -273,4 +244,29 @@ public class AppConsumablesMgrService { return total >= 1; } + synchronized public ConsumableGroup getChannelNumByProjIndex(Integer projIndex) { + var cState = gstate.getConsumableState(); + for (int i = 0; i < cState.reactionPlateGroup.length; i++) { + if (cState.reactionPlateGroup[i].projIndex.equals(projIndex)) { + if (cState.reactionPlateGroup[i].num > 0) { + return ConsumableGroup.fromInt(i); + } + } + } + return null; + } + + synchronized public Integer allocConsumable(ConsumableGroup ch) { + if (ch == null) { + return null; + } + var cState = gstate.getConsumableState(); + int chNum = ch.off; + if (cState.reactionPlateGroup[chNum].num < 0) { + return null; + } + Integer posIndex = 25 - cState.reactionPlateGroup[chNum].num; + setReactionConsumableNum(chNum, cState.reactionPlateGroup[chNum].num - 1); + return posIndex; + } } diff --git a/src/main/java/a8k/service/appdevicectrl/AppEmergencySamplePosStateMgrService.java b/src/main/java/a8k/service/appdevicectrl/AppEmergencySamplePosStateMgrService.java index 0c790fc..091ec90 100644 --- a/src/main/java/a8k/service/appdevicectrl/AppEmergencySamplePosStateMgrService.java +++ b/src/main/java/a8k/service/appdevicectrl/AppEmergencySamplePosStateMgrService.java @@ -7,7 +7,7 @@ import a8k.controler.extapi.utils.ExtApiTab; import a8k.hardware.type.a8kcanprotocol.A8kEcode; import a8k.service.appdata.AppSampleRecordMgrService; import a8k.service.appstate.AppA8kGStateService; -import a8k.service.appstate.AppSampleProcessContextMgrService; +import a8k.service.appstate.TubeProcessContextMgrService; import a8k.service.appstate.type.EmergencyTubePos; import a8k.service.appstate.type.Tube; import a8k.service.appstate.type.state.TubeState; @@ -27,7 +27,7 @@ public class AppEmergencySamplePosStateMgrService { AppSampleRecordMgrService sampleRecordMgrService; @Resource - AppSampleProcessContextMgrService appSampleProcessContextMgrService; + TubeProcessContextMgrService tubeProcessContextMgrService; /** * 提交紧急样本设置 @@ -54,7 +54,7 @@ public class AppEmergencySamplePosStateMgrService { } tube.state = TubeState.TO_BE_PROCESSED; sampleRecordMgrService.addEmergencySampleRecord(tube); - appSampleProcessContextMgrService.createNewTubeContext(tube); + tubeProcessContextMgrService.createNewTubeContext(tube); } else { throw new AppException(A8kEcode.EmergencySampleIsProcessing); } diff --git a/src/main/java/a8k/service/appdevicectrl/action/DO_PAUSE.java b/src/main/java/a8k/service/appdevicectrl/action/DO_PAUSE.java index 5a96183..198daab 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/DO_PAUSE.java +++ b/src/main/java/a8k/service/appdevicectrl/action/DO_PAUSE.java @@ -40,7 +40,7 @@ public class DO_PAUSE extends A8kStepAction { return cond1 & cond2; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(); } } diff --git a/src/main/java/a8k/service/appdevicectrl/action/DO_START.java b/src/main/java/a8k/service/appdevicectrl/action/DO_START.java index 2a40a6d..bd4450d 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/DO_START.java +++ b/src/main/java/a8k/service/appdevicectrl/action/DO_START.java @@ -51,7 +51,7 @@ public class DO_START extends A8kStepAction { return cond1 & cond2; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(A8kPublicResourceType.SampleTransferXMotor); } } diff --git a/src/main/java/a8k/service/appdevicectrl/action/DO_STOP.java b/src/main/java/a8k/service/appdevicectrl/action/DO_STOP.java index cccd052..c88226b 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/DO_STOP.java +++ b/src/main/java/a8k/service/appdevicectrl/action/DO_STOP.java @@ -39,7 +39,7 @@ public class DO_STOP extends A8kStepAction { return cond1 & cond2; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(); } } diff --git a/src/main/java/a8k/service/appdevicectrl/action/base/A8kActionStepType.java b/src/main/java/a8k/service/appdevicectrl/action/base/A8kActionStepType.java index 9b4d035..62a2ea9 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/base/A8kActionStepType.java +++ b/src/main/java/a8k/service/appdevicectrl/action/base/A8kActionStepType.java @@ -6,6 +6,11 @@ public enum A8kActionStepType { DO_STOP, //停止 SEQ1_ENTER_TUBEHOLDER_AND_SCAN, //入料并扫描 SEQ2_SWITCH_TO_THE_NEXT_TUBE,// 切换到下一个试管 + SEQ3_PRE_PROCESS, + +// SEQ4_PROCESS, +// SEQ5_POS_PROCESS_TUBE, + DO_EJECT_TUBEHOLDER, //弹出试管架 diff --git a/src/main/java/a8k/service/appdevicectrl/action/base/A8kStepAction.java b/src/main/java/a8k/service/appdevicectrl/action/base/A8kStepAction.java index 5c8156e..3935cda 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/base/A8kStepAction.java +++ b/src/main/java/a8k/service/appdevicectrl/action/base/A8kStepAction.java @@ -22,7 +22,7 @@ public class A8kStepAction { return checkCondition(); } - public List getDeplyResourceList() { + public List getResourceList() { return null; } diff --git a/src/main/java/a8k/service/appdevicectrl/action/mainflow/DO_EJECT_TUBEHOLDER.java b/src/main/java/a8k/service/appdevicectrl/action/mainflow/DO_EJECT_TUBEHOLDER.java index 23a14e8..bd6a105 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/mainflow/DO_EJECT_TUBEHOLDER.java +++ b/src/main/java/a8k/service/appdevicectrl/action/mainflow/DO_EJECT_TUBEHOLDER.java @@ -52,7 +52,7 @@ public class DO_EJECT_TUBEHOLDER extends A8kStepAction { return cond1 & cond2 & cond3; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(A8kPublicResourceType.SampleTransferXMotor); } } diff --git a/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ1_ENTER_TUBEHOLDER_AND_SCAN.java b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ1_ENTER_TUBEHOLDER_AND_SCAN.java index d013ec1..36f401f 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ1_ENTER_TUBEHOLDER_AND_SCAN.java +++ b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ1_ENTER_TUBEHOLDER_AND_SCAN.java @@ -10,7 +10,7 @@ import a8k.service.appdevicectrl.AppTubeSettingMgrService; import a8k.service.appdevicectrl.action.base.A8kActionStepType; import a8k.service.appdevicectrl.action.base.A8kStepAction; import a8k.service.appstate.AppA8kGStateService; -import a8k.service.appstate.AppSampleProcessContextMgrService; +import a8k.service.appstate.TubeProcessContextMgrService; import a8k.service.appstate.MainFlowCtrlState; import a8k.service.appstate.resource.A8kPublicResourceType; import a8k.service.appstate.type.TubeHolder; @@ -72,9 +72,9 @@ public class SEQ1_ENTER_TUBEHOLDER_AND_SCAN extends A8kStepAction { @Resource AppTubeSettingMgrService appTubeSettingMgrService; @Resource - AppSampleRecordMgrService appSampleRecordMgrService; //样本管理服务 + AppSampleRecordMgrService appSampleRecordMgrService; //样本管理服务 @Resource - AppSampleProcessContextMgrService appSampleProcessContextMgrService; + TubeProcessContextMgrService tubeProcessContextMgrService; MainFlowCtrlState mfcs; @@ -221,7 +221,7 @@ public class SEQ1_ENTER_TUBEHOLDER_AND_SCAN extends A8kStepAction { logger.info("将样本信息写入数据库"); appSampleRecordMgrService.addSampleRecord(state.tubes); - appSampleProcessContextMgrService.createNewTubeContexts(state.tubes); + tubeProcessContextMgrService.createNewTubeContexts(state.tubes); logger.info("更新试管架状态"); @@ -238,7 +238,7 @@ public class SEQ1_ENTER_TUBEHOLDER_AND_SCAN extends A8kStepAction { return cond1 && cond2 && cond3; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(A8kPublicResourceType.SampleTransferXMotor); } diff --git a/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ2_SWITCH_TO_THE_NEXT_TUBE.java b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ2_SWITCH_TO_THE_NEXT_TUBE.java index 322b1a0..9a21a4c 100644 --- a/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ2_SWITCH_TO_THE_NEXT_TUBE.java +++ b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ2_SWITCH_TO_THE_NEXT_TUBE.java @@ -137,7 +137,7 @@ public class SEQ2_SWITCH_TO_THE_NEXT_TUBE extends A8kStepAction { return cond1 && cond2 && cond3; } - @Override public List getDeplyResourceList() { + @Override public List getResourceList() { return List.of(A8kPublicResourceType.SampleTransferXMotor); } } diff --git a/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ3_PRE_PROCESS.java b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ3_PRE_PROCESS.java new file mode 100644 index 0000000..dbf8fea --- /dev/null +++ b/src/main/java/a8k/service/appdevicectrl/action/mainflow/SEQ3_PRE_PROCESS.java @@ -0,0 +1,221 @@ +package a8k.service.appdevicectrl.action.mainflow; + +import a8k.OS; +import a8k.baseservice.AppExceptionBuilder; +import a8k.baseservice.appeventbus.AppEventBusService; +import a8k.hardware.type.a8kcanprotocol.A8kEcode; +import a8k.service.appdevicectrl.AppConsumablesMgrService; +import a8k.service.appdevicectrl.action.base.A8kActionStepType; +import a8k.service.appdevicectrl.action.base.A8kStepAction; +import a8k.service.appstate.AppA8kGStateService; +import a8k.service.appstate.TubeProcessContextMgrService; +import a8k.service.appstate.resource.A8kPublicResourceType; +import a8k.service.appstate.type.Tube; +import a8k.service.appstate.type.TubeHolder; +import a8k.service.appstate.type.state.IncubationSubTankState; +import a8k.service.appstate.type.state.TubeState; +import a8k.service.devicedriver.ctrl.HbotControlService; +import a8k.service.devicedriver.ctrl.ReactionPlatesTransmitCtrl; +import a8k.service.devicedriver.ctrl.SampleScanTransportCtrl; +import a8k.service.devicedriver.ctrl.SamplesPreProcesCtrl; +import a8k.type.ConsumableGroup; +import a8k.type.IncubatorPos; +import a8k.type.ecode.AppError; +import a8k.type.exception.AppException; +import a8k.type.type.A8kTubeHolderType; +import a8k.utils.ZFnCall; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.*; + +/** + * + * 核对物料资源是否足够 + * TUBE + * TO_BE_PROCESSED --> PRE_PROCESSING + */ +@Component +public class SEQ3_PRE_PROCESS extends A8kStepAction { + static Logger logger = LoggerFactory.getLogger(SEQ3_PRE_PROCESS.class); + + SEQ3_PRE_PROCESS() { + super(A8kActionStepType.SEQ2_SWITCH_TO_THE_NEXT_TUBE); + } + + @Resource + AppA8kGStateService gstate; + + @Resource + SampleScanTransportCtrl sstc; + + @Resource + AppConsumablesMgrService consumablesMgrService; + + @Resource + AppEventBusService ebus; + + @Resource + AppExceptionBuilder ebuilder; + + @Resource + TubeProcessContextMgrService tubeProcessContextMgrService; + + @Resource + ReactionPlatesTransmitCtrl reactionPlatesTransmitCtrl; + + @Resource + SamplesPreProcesCtrl samplesPreProcesCtrl; + + @Resource + HbotControlService hbotControlService; + + ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10)); + + + @PostConstruct + void init() { + } + + + /** + * 推出反应板夹 + * @throws AppException 异常 + */ + void prepareReactionPlate() throws AppException { + Tube tube = gstate.getCurProcessingTube(); + List projIndex = tube.projIndex; + List incubatorPos = tube.incubatorPos; + List bindConsumablesCh = tube.bindConsumablesCh; + + for (int i = 0; i < projIndex.size(); i++) { + var tank = gstate.getIncubationPlate().getSubTank(incubatorPos.get(i)); + reactionPlatesTransmitCtrl.pushPlate(bindConsumablesCh.get(i), incubatorPos.get(i)); + tank.state = IncubationSubTankState.WAITING_FOR_DROP; + } + } + + /** + * 摇匀并取盖 + * @throws AppException + */ + void shakeAndTakeCap() throws AppException { + Tube tube = gstate.getCurProcessingTube(); + if (tube.isEmergency) { + //如果事急诊位则什么也不做 + return; + } + + TubeHolder tubeHolder = gstate.getTubeHolder(); + if (!tubeHolder.tubeHolderType.equals(A8kTubeHolderType.BloodTube)) { + //如果不是全血试管则什么也不做 + return; + } + + logger.info("摇匀并取盖"); + samplesPreProcesCtrl.takeTubeAndJudgeTubeExist(tube.isHighTube); + samplesPreProcesCtrl.shakeTube(45, 5); + samplesPreProcesCtrl.takeTubeCap(); + } + + void hbotPrepareTip() throws AppException { + + + + Tube tube = gstate.getCurProcessingTube(); + } + + @Override public void doaction() throws AppException { + + Tube tube = gstate.getCurProcessingTube(); + //构建样本和耗材绑定关系 + List bindConsumableCH = new ArrayList<>(); + List consumablePosIndexList = new ArrayList<>(); + List incubatorPosList = new ArrayList<>(); + for (Integer projIndex : tube.projIndex) { + ConsumableGroup chNum = consumablesMgrService.getChannelNumByProjIndex(projIndex); + Integer consumablePosIndex = consumablesMgrService.allocConsumable(chNum); + + + if (chNum == null) { + logger.error("apply consumable failed, projIndex:{}", projIndex); + throw ebuilder.buildAppCodeErrorException("预处理阶段,申请耗材失败"); + } + if (consumablePosIndex == null) { + logger.error("alloc consumable failed, projIndex:{}", projIndex); + throw ebuilder.buildAppCodeErrorException("预处理阶段,申请耗材失败x"); + } + bindConsumableCH.add(chNum); + consumablePosIndexList.add(consumablePosIndex); + incubatorPosList = gstate.getIncubationPlate().getEmptyPosList(tube.projIndex.size()); + } + tube.bindConsumablesCh = bindConsumableCH; + tube.consumablePos = consumablePosIndexList; + tube.incubatorPos = incubatorPosList; + + tubeProcessContextMgrService.updateTubeContext(tube);//更新样本上下文 + + + //检测反应板是否有空位 + if (gstate.getIncubationPlate().getEmptyPosNum() < tube.projIndex.size()) { + throw ebuilder.buildAppCodeErrorException("预处理阶段,反应板空位不足"); + } + + //准备反应板夹 + var doPrepareReactionPlateFuture = executor.submit(() -> ZFnCall.callfn(this::prepareReactionPlate)); + //摇匀并取盖 + var doShakeAndTakeCapFuture = executor.submit(() -> ZFnCall.callfn(this::shakeAndTakeCap)); + //准备Hbot Tip + var doHbotPrepareTipFuture = executor.submit(() -> ZFnCall.callfn(this::hbotPrepareTip)); + + wait(doPrepareReactionPlateFuture); + wait(doShakeAndTakeCapFuture); + wait(doHbotPrepareTipFuture); + + List errors = new java.util.ArrayList<>(List.of()); + try { + errors.add(doPrepareReactionPlateFuture.get()); + errors.add(doShakeAndTakeCapFuture.get()); + errors.add(doHbotPrepareTipFuture.get()); + } catch (InterruptedException | ExecutionException ignored) { + } + errors.removeIf(Objects::isNull); + if (!errors.isEmpty()) { + throw ebuilder.buildMutiErrorAppException(errors); + } + // + + } + + @Override public Boolean checkCondition() { + //当前正在工作 + Boolean cond1 = gstate.isWorking(); + //有需要进行预处理的样本 + Boolean cond2 = gstate.getCurProcessingTube() != null && gstate.getCurProcessingTube().state.equals(TubeState.PRE_PROCESSING); + return cond1 && cond2; + } + + @Override public List getResourceList() { + return List.of( + A8kPublicResourceType.IncubationPlateMotor, + A8kPublicResourceType.PlatesBoxMotor, + A8kPublicResourceType.HBOT + ); + } + + // + // UTILS + // + + void wait(Future future) { + while (!future.isDone()) { + OS.forceSleep(100); + } + } +} diff --git a/src/main/java/a8k/service/appdevicectrl/scheduler/MainFlowCtrlScheduler.java b/src/main/java/a8k/service/appdevicectrl/scheduler/MainFlowCtrlScheduler.java index c90f225..69281e9 100644 --- a/src/main/java/a8k/service/appdevicectrl/scheduler/MainFlowCtrlScheduler.java +++ b/src/main/java/a8k/service/appdevicectrl/scheduler/MainFlowCtrlScheduler.java @@ -117,7 +117,7 @@ public class MainFlowCtrlScheduler { for (A8kStepAction action : stepActionList) { if (action.checkCondition()) { - Boolean relayResourceSuc = appResourceMgrService.applyForResources(this, action.getDeplyResourceList()); + Boolean relayResourceSuc = appResourceMgrService.applyForResources(this, action.getResourceList()); if (relayResourceSuc) { dowhatList.add(action); } diff --git a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LarBSGroup.java b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LarBSGroup.java index f8ad2da..f1b11ac 100644 --- a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LarBSGroup.java +++ b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LarBSGroup.java @@ -5,8 +5,8 @@ public class LarBSGroup { public String projName; //项目名称 public String lotId = ""; //批次号 public String color = ""; //颜色 - public Integer num = 0; - public Boolean editable = false; + public Integer num = 0; + public Boolean enable = false; public LarBSGroup() { } diff --git a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LittBSGroup.java b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LittBSGroup.java index dc9806d..7231907 100644 --- a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LittBSGroup.java +++ b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/LittBSGroup.java @@ -4,11 +4,12 @@ public class LittBSGroup { public Integer projIndex; //项目ID public String projName; //项目名称 public String lotId = ""; //批次号 - public String color = ""; //颜色 - public Boolean editable = false;//是否可以被修改, - public Integer num = 0; + public String color = ""; //颜色 + public Boolean enable = false;//是否可以被修改, + public Integer num = 0; public LittBSGroup() { + enable = false; } public LittBSGroup(Integer projIndex, String projName, String lotId, String color, Integer num) { @@ -17,5 +18,6 @@ public class LittBSGroup { this.lotId = lotId; this.color = color; this.num = num; + enable = true; } } diff --git a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/ReactionPlateGroup.java b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/ReactionPlateGroup.java index 0640a3e..2bbd87f 100644 --- a/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/ReactionPlateGroup.java +++ b/src/main/java/a8k/service/appdevicectrl/type/app_consumables_mgr_service/ReactionPlateGroup.java @@ -3,9 +3,9 @@ package a8k.service.appdevicectrl.type.app_consumables_mgr_service; public class ReactionPlateGroup { public Integer projIndex; //项目ID public String projName; //项目名称 - public String lotId = ""; //批次号 - public String color = ""; //颜色 - public Boolean editable = false; + public String lotId = ""; //批次号 + public String color = ""; //颜色 + public Boolean enable = false; public Integer num = 0; diff --git a/src/main/java/a8k/service/appstate/AppSampleProcessContextMgrService.java b/src/main/java/a8k/service/appstate/TubeProcessContextMgrService.java similarity index 51% rename from src/main/java/a8k/service/appstate/AppSampleProcessContextMgrService.java rename to src/main/java/a8k/service/appstate/TubeProcessContextMgrService.java index 664a0cc..f6b199b 100644 --- a/src/main/java/a8k/service/appstate/AppSampleProcessContextMgrService.java +++ b/src/main/java/a8k/service/appstate/TubeProcessContextMgrService.java @@ -8,22 +8,30 @@ import java.util.ArrayList; import java.util.List; @Component -public class AppSampleProcessContextMgrService { +public class TubeProcessContextMgrService { List contexts = new ArrayList<>(); + + void assignTubeProcessContext(TubeProcessContext cxt, Tube tube) { + cxt.sampleid = tube.sampleid; + cxt.isHighTube = tube.isHighTube; + cxt.isEmergency = tube.isEmergency; + cxt.bloodType = tube.bloodType; + cxt.sampleBarcode = tube.sampleBarcode; + cxt.userid = tube.userid; + cxt.projIndex = tube.projIndex; + cxt.bindConsumablesCh = tube.bindConsumablesCh; + cxt.consumablePos = tube.consumablePos; + cxt.incubatorPos = tube.incubatorPos; + } + synchronized public void createNewTubeContext(Tube tube) { assert tube != null; assert !tube.sampleid.isEmpty(); TubeProcessContext context = new TubeProcessContext(); - context.sampleid = tube.sampleid; - context.isHighTube = tube.isHighTube; - context.isEmergency = tube.isEmergency; - context.bloodType = tube.bloodType; - context.sampleBarcode = tube.sampleBarcode; - context.userid = tube.userid; - context.projIndex = tube.projIndex; + assignTubeProcessContext(context, tube); contexts.add(context); } @@ -42,5 +50,11 @@ public class AppSampleProcessContextMgrService { return null; } + synchronized public void updateTubeContext(Tube tube) { + TubeProcessContext context = getTubeContext(tube.sampleid); + if (context != null) { + assignTubeProcessContext(context, tube); + } + } } diff --git a/src/main/java/a8k/service/appstate/resource/A8kPublicResourceType.java b/src/main/java/a8k/service/appstate/resource/A8kPublicResourceType.java index b8730b5..7f4abcf 100644 --- a/src/main/java/a8k/service/appstate/resource/A8kPublicResourceType.java +++ b/src/main/java/a8k/service/appstate/resource/A8kPublicResourceType.java @@ -2,4 +2,7 @@ package a8k.service.appstate.resource; public enum A8kPublicResourceType { SampleTransferXMotor,//样本平移电机 + IncubationPlateMotor,//孵育盘电机 + PlatesBoxMotor,//板夹仓电机 + HBOT,//HBOT } diff --git a/src/main/java/a8k/service/appstate/type/IncubationPlate.java b/src/main/java/a8k/service/appstate/type/IncubationPlate.java index 9b21170..c90321e 100644 --- a/src/main/java/a8k/service/appstate/type/IncubationPlate.java +++ b/src/main/java/a8k/service/appstate/type/IncubationPlate.java @@ -3,6 +3,8 @@ package a8k.service.appstate.type; import a8k.service.appstate.type.state.IncubationSubTankState; import a8k.type.IncubatorPos; +import java.util.List; + public class IncubationPlate { public IncubationSubTank[] subtanks = new IncubationSubTank[20]; public IncubatorPos incubationPos = IncubatorPos.SPACE01; @@ -13,7 +15,7 @@ public class IncubationPlate { } } - public IncubatorPos getEmptyPos() { + synchronized public IncubatorPos getEmptyPos() { for (IncubationSubTank subtank : subtanks) { if (subtank.state.equals(IncubationSubTankState.EMPTY)) { return subtank.pos; @@ -22,7 +24,23 @@ public class IncubationPlate { return null; } - public Integer getEmptyPosNum() { + synchronized public List getEmptyPosList(Integer num) { + List ret = new java.util.ArrayList<>(); + for (IncubationSubTank subtank : subtanks) { + if (subtank.state.equals(IncubationSubTankState.EMPTY)) { + ret.add(subtank.pos); + if (ret.size() >= num) { + break; + } + } + } + if (ret.size() < num) { + return null; + } + return ret; + } + + synchronized public Integer getEmptyPosNum() { Integer num = 0; for (IncubationSubTank subtank : subtanks) { if (subtank.state.equals(IncubationSubTankState.EMPTY)) { @@ -32,5 +50,14 @@ public class IncubationPlate { return num; } + synchronized public IncubationSubTank getSubTank(IncubatorPos pos) { + for (IncubationSubTank subtank : subtanks) { + if (subtank.pos.equals(pos)) { + return subtank; + } + } + return null; + } + } diff --git a/src/main/java/a8k/service/appstate/type/Tube.java b/src/main/java/a8k/service/appstate/type/Tube.java index 5ac80fa..5c8ddb5 100644 --- a/src/main/java/a8k/service/appstate/type/Tube.java +++ b/src/main/java/a8k/service/appstate/type/Tube.java @@ -1,6 +1,8 @@ package a8k.service.appstate.type; import a8k.service.appstate.type.state.TubeState; +import a8k.type.ConsumableGroup; +import a8k.type.IncubatorPos; import a8k.type.type.BloodType; import java.util.ArrayList; @@ -18,9 +20,9 @@ public class Tube { public List projIndex = new ArrayList<>(); //项目代码 // - public TubeState state = TubeState.EMPTY; //样本被处理的状态 - - - + public TubeState state = TubeState.EMPTY; //样本被处理的状态 + public List bindConsumablesCh = new ArrayList<>(); //耗材绑定的通道号 + public List consumablePos = new ArrayList<>(); //使用的耗材的位置 + public List incubatorPos = new ArrayList<>(); //孵育位置 } diff --git a/src/main/java/a8k/service/appstate/type/TubeProcessContext.java b/src/main/java/a8k/service/appstate/type/TubeProcessContext.java index 0d64bd9..412c732 100644 --- a/src/main/java/a8k/service/appstate/type/TubeProcessContext.java +++ b/src/main/java/a8k/service/appstate/type/TubeProcessContext.java @@ -1,5 +1,7 @@ package a8k.service.appstate.type; +import a8k.type.ConsumableGroup; +import a8k.type.IncubatorPos; import a8k.type.type.BloodType; import java.util.ArrayList; @@ -11,11 +13,13 @@ public class TubeProcessContext { public Boolean isHighTube = false; public Boolean isEmergency = false; - public BloodType bloodType = BloodType.WHOLE_BLOOD; //血液类型 - public String sampleBarcode = ""; //用于请求用户信息的条码ID - public String userid = ""; //用户输入的样本ID,不做逻辑,只做展示 - public List projIndex = new ArrayList<>(); //项目代码 - + public BloodType bloodType = BloodType.WHOLE_BLOOD; //血液类型 + public String sampleBarcode = ""; //用于请求用户信息的条码ID + public String userid = ""; //用户输入的样本ID,不做逻辑,只做展示 + public List projIndex = new ArrayList<>(); //项目代码 + public List bindConsumablesCh = new ArrayList<>(); //耗材绑定的通道号 + public List consumablePos = new ArrayList<>(); //耗材索引 + public List incubatorPos = new ArrayList<>(); //孵育位置 } diff --git a/src/main/java/a8k/service/devicedriver/ctrl/HbotControlService.java b/src/main/java/a8k/service/devicedriver/ctrl/HbotControlService.java index ffbec70..9ce0ab5 100644 --- a/src/main/java/a8k/service/devicedriver/ctrl/HbotControlService.java +++ b/src/main/java/a8k/service/devicedriver/ctrl/HbotControlService.java @@ -3,6 +3,9 @@ package a8k.service.devicedriver.ctrl; import a8k.controler.extapi.pagecontrol.ExtApiTabConfig; import a8k.hardware.A8kPipetteCtrlModule; import a8k.hardware.type.regindex.PipetteRegIndex; +import a8k.service.devicedriver.param.PosParam; +import a8k.service.devicedriver.param.TimeParam; +import a8k.type.ConsumableGroup; import a8k.type.exception.AppException; import a8k.type.cfg.*; import a8k.controler.extapi.utils.*; @@ -30,6 +33,12 @@ public class HbotControlService { @Resource A8kPipetteCtrlModule pipetteCtrlModule; + @Resource + PosParam pp; + + @Resource + TimeParam timep; + Integer ao = 20000; @@ -90,7 +99,6 @@ public class HbotControlService { } } - // // public Integer getGripperZeroYPos() { // 之所以这样写,是因为是为了放置两个类之间的循环依赖 @@ -101,268 +109,177 @@ public class HbotControlService { // return 0; // } - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // 基础控制 - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // private void checkPublicArea() throws AppException { - // if (canBus.miniServoReadPos(MId.ShakeModGripperYSV) > getGripperZeroYPos() + 20) { - // logger.error("ShakeModGripperYSV Pos:{} > {}", canBus.miniServoReadPos(MId.ShakeModGripperYSV), - // getGripperZeroYPos() + 20); - // throw new AppException(A8kEcode.ShakeModGripperYSVInXYPublicArea); - // } - // } + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // 基础控制 + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // /** - // * Hboot 移动 - // * - // * @param targetPos 目标位置 - // * @throws AppException e - // * @throws InterruptedException e - // */ - // public void hbotCheckAndMoveTo(Pos3d targetPos) throws AppException { - // logger.info("hbotCheckAndMoveTo:{}", targetPos); - // if (!canBus.getIOState(IOId.PlateBoxCoverClosurePPS)) { - // throw new AppException(A8kEcode.PlateBoxNotCover); - // } - // - // - // boolean zEnable = true; - // boolean hbotEnable = canBus.moduleGetReg(MId.HbotM, RegIndex.kreg_xyrobot_is_enable) == 1; - // - // if (pipetteCtrlModule.getReg(PipetteRegIndex.kreg_pipette_zm_is_enable) == 0) { - // pipetteCtrlModule.zMotorEnable(1); - // pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); - // zEnable = false; - // } - // - // if (!pipetteCtrlModule.zAixsZeroPointIsTrigger()) { - // pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); - // } - // - // if (!pipetteCtrlModule.zAixsZeroPointIsTrigger()) { - // throw new AppException(A8kEcode.ZMNotAtZPosWhenHbotTryMove); - // } - // Pos2d nowHbotPos = canBus.hbotReadPos(MId.HbotM); - // HbotLimitArea sampleArea = pp.getSampleCollectionArea(); - // - // // 检查采样区域是否有障碍 - // // if (sampleArea.checkIsInArea(nowHbotPos) || sampleArea.checkIsInArea(targetPos.getXYPos())) { - // // checkPublicArea(); - // // } - // - // // HBot移动 - // canBus.hbotEnable(MId.HbotM, 1); - // canBus.hbotMoveToBlock(MId.HbotM, targetPos.x - 20, targetPos.y - 20, timep.getActionOvertime()); - // canBus.hbotMoveToBlock(MId.HbotM, targetPos.x, targetPos.y, timep.getActionOvertime()); - // canBus.hbotMoveToBlock(MId.HbotM, targetPos.x, targetPos.y, timep.getActionOvertime()); - // - // // Z轴移动 - // if (targetPos.z != 0) { - // pipetteCtrlModule.zMotorEnable(1); - // pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); - // pipetteCtrlModule.zMotorMoveToBlock(targetPos.z); - // } - // pipetteCtrlModule.zMotorEnable(zEnable ? 1 : 0); - // canBus.hbotEnable(MId.HbotM, hbotEnable ? 1 : 0); - // } - // - // public void zMoveTo(Integer z) throws AppException { - // if (z == 0) { - // pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); - // } else { - // pipetteCtrlModule.zMotorMoveToBlock(z); - // } - // } - // - // private void hbotCheckAndMoveTo(Pos2d targetPos) throws AppException { - // hbotCheckAndMoveTo(new Pos3d(targetPos.x, targetPos.y, 0)); - // } - // - // private void modGroupMoveToZero() throws AppException { - // if (!canBus.getIOState(IOId.PlateBoxCoverClosurePPS)) { - // throw new AppException(A8kEcode.PlateBoxNotCover); - // } - // - // pipetteCtrlModule.zMotorEnable(1); - // canBus.hbotEnable(MId.HbotM, 1); - // - // canBus.hbotMoveToZeroBlock(MId.HbotM, timep.getHbotRuntoZeroActionOvertime()); - // - // // 丢弃tip - // Pos3d dropPos = pp.getTipDropPos(); - // hbotCheckAndMoveTo(dropPos); - // // TODO: canBus.pipetteCtrlInitDeviceBlock(MId.PipetteMod, timep.getActionOvertime()); - // - // // 快速归零 - // modGroupMoveToZeroQuick(); - // } - // - // private void modGroupMoveToZeroQuick() throws AppException { - // hbotCheckAndMoveTo(new Pos2d(0, 0)); - // } + public void zMoveTo(Integer z) throws AppException { + if (z == 0) { + pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); + } else { + pipetteCtrlModule.zMotorMoveToBlock(z); + } + } - // // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // // 单步测试 - // // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // - // @ExtApiFn(name = "丢Tip", group = "单步测试") - // public void dropTip() throws AppException { - // Pos3d pos = pp.getTipDropPos(); - // hbotMoveTo(pos.x, pos.y); - // zMoveTo(pos.z); - // pipetteCtrlModule.putTipBlock(); - // zMoveTo(0); - // } - // - // @ExtApiFn(name = "取Tip", group = "单步测试") - // public AppRet takeTip(Integer groupId, Integer index) throws AppException { - // logger.info("takeTip groupId:{} index:{}", groupId, index); - // if (groupId > 2 || groupId < 0) { - // throw new AppException(A8kEcode.ParamOutOfRange); - // } - // - // if (index > TipPickUpPosInfo.cgetTipNum() || index < 0) { - // throw new AppException(A8kEcode.ParamOutOfRange); - // } - // - // TipPickUpPosInfo tipPos = pp.getTipPickUpPosInfo(); - // Pos2d pos = tipPos.getTipPos(groupId, index); - // hbotMoveTo(pos.x, pos.y); - // - // //TODO: 补偿,group大于1时,z轴需要补偿0.2mm - // Integer zCompensate = 0; - // if (groupId > 1) { - // zCompensate = 2; - // } - // - // pipetteCtrlModule.zMotorMoveToBlock(tipPos.getPickUpZPos(groupId) + zCompensate); - // pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); - // - // Boolean isGetTip = canBus.moduleGetReg(MId.PipetteMod, RegIndex.kreg_pipette_tip_state) == 1; - // if (!isGetTip) { - // logger.error("takeTip fail"); - // } - // return AppRet.success(isGetTip); - // } - // - // @ExtApiFn(name = "HBot移动到", group = "单步测试") - // public void hbotMoveTo(Integer x, Integer y) throws AppException { - // hbotCheckAndMoveTo(new Pos2d(x, y)); - // } - // - // @ExtApiFn(name = "HBot移动到小瓶缓冲液X孔", group = "单步测试") - // public void hbotMoveToSmallBottleGroup(ConsumableGroup group, Integer Xhole) - // throws AppException { - // BottleGroupsPosInfo posInfo = pp.getBottleBufferPosInfo(); - // if (Xhole < 0 || Xhole > BottlesPosInfo.cgetMAX()) { - // throw new AppException(A8kEcode.ParamOutOfRange); - // } - // Pos2d pos = posInfo.cgetSmallBottleBufferPos(group.off, Xhole); - // hbotCheckAndMoveTo(pos); - // } - // - // @ExtApiFn(name = "HBot移动到探测物质X孔", group = "单步测试") - // public void hbotMoveToDetectMaterialPos(ConsumableGroup group, Integer Xhole) - // throws AppException { - // BottleGroupsPosInfo posInfo = pp.getBottleBufferPosInfo(); - // if (Xhole < 0 || Xhole > BottlesPosInfo.cgetMAX()) { - // throw new AppException(A8kEcode.ParamOutOfRange); - // } - // Pos2d pos = posInfo.cgetDetectMaterialPos(group.off, Xhole); - // hbotCheckAndMoveTo(pos); - // } - // - // @ExtApiFn(name = "HBot移动到大瓶缓冲液X孔", group = "单步测试") - // public void hbotMoveToLargeBottleGroup(ConsumableGroup ch) throws AppException { - // LargeBottleBufferPos posInfo = pp.getLargeBottleBufferPosInfo(); - // Pos2d pos = posInfo.cgetBottlePos(ch.off); - // hbotCheckAndMoveTo(pos); - // } - // - // @ExtApiFn(name = "HBot移动到急诊位", group = "单步测试") - // public void hbotMoveToEmergencyPos() throws AppException { - // hbotCheckAndMoveTo(pp.getEmergencyPos()); - // } + private void hbotCheckAndMoveTo(Pos2d targetPos) throws AppException { + hbotMoveTo(new Pos3d(targetPos.x, targetPos.y, 0)); + } - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // 坐标获取工具 - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // - // @ExtApiFn(name = "Hbot使能", group = "坐标获取工具") - // public void hBotEnable() throws AppException { - // canBus.hbotEnable(MId.HbotM, 1); - // } - // - // @ExtApiFn(name = "Hbot失能", group = "坐标获取工具") - // public void hBotDisable() throws AppException { - // canBus.hbotEnable(MId.HbotM, 0); - // } - // - // @ExtApiFn(name = "Z轴使能", group = "坐标获取工具") - // public void zAxisEnable() throws AppException { - // pipetteCtrlModule.zMotorEnable(1); - // } - // - // @ExtApiFn(name = "Z轴失能", group = "坐标获取工具") - // public void zAxisDisable() throws AppException { - // pipetteCtrlModule.zMotorEnable(0); - // } + private void modGroupMoveToZero() throws AppException { + if (!canBus.getIOState(IOId.PlateBoxCoverClosurePPS)) { + throw new AppException(A8kEcode.PlateBoxNotCover); + } + + pipetteCtrlModule.zMotorEnable(1); + canBus.hbotEnable(MId.HbotM, 1); + + canBus.hbotMoveToZeroBlock(MId.HbotM, timep.getHbotRuntoZeroActionOvertime()); + + // 丢弃tip + Pos3d dropPos = pp.getTipDropPos(); + hbotMoveTo(dropPos); + // TODO: canBus.pipetteCtrlInitDeviceBlock(MId.PipetteMod, timep.getActionOvertime()); + + // 快速归零 + modGroupMoveToZeroQuick(); + } + + private void modGroupMoveToZeroQuick() throws AppException { + hbotCheckAndMoveTo(new Pos2d(0, 0)); + } - // @ExtApiFn(name = "归零读取Z轴坐标", group = "坐标获取工具") - // public AppRet readZAxisPosByMoveToZero() throws HardwareException { - // return AppRet.success(canBus.stepMotorReadPosByMoveToZeroBlock(MId.PipetteModZM, timep.getActionOvertime())); - // } - // - // @ExtApiFn(name = "打开扫码器", group = "坐标获取工具") - // public void openCodeScaner(Boolean power) throws AppException { - // if (power) { - // canBus.codeScanerStartScan(MId.PipetteModCodeScanner); - // } else { - // canBus.codeScanerStopScan(MId.PipetteModCodeScanner); - // } - // } - // - // Pos2d hbotRefPos = new Pos2d(0, 0); - // - // @ExtApiFn(name = "设置HBOT参考坐标", group = "坐标获取工具") - // public void setHbotRefPos(Integer x, Integer y) throws AppException { - // hbotRefPos.x = x; - // hbotRefPos.y = y; - // } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // hbot移动到并扫码 + // 单步测试 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // private AppRet hBotMoveToAndScan(Pos2d pos) throws AppException { - // hbotCheckAndMoveTo(pos); - // canBus.codeScanerStartScan(MId.PipetteModCodeScanner); - // String result = canBus.codeScanerWaittingForResult(MId.PipetteModCodeScanner, timep.getScancodeOvertime()); - // return AppRet.success(result); - // } - // - // // 扫描板夹仓二维码 - // @ExtApiFn(name = "扫描板夹仓二维码", group = "扫码") - // public AppRet scanPlatesCode(ConsumableGroup ch) throws AppException { - // var posInfo = pp.getPlates2dCodeScanPosInfo(); - // return hBotMoveToAndScan(posInfo.cgetScanPos(ch.off)); - // } - // -// // 扫描缓冲液二维码 -// @ExtApiFn(name = "扫描缓冲液区二维码", group = "扫码") -// public AppRet scanBottleBuffersCode(ConsumableGroup group) throws AppException { -// BottleGroupsPosInfo posInfo = pp.getBottleBufferPosInfo(); -// return hBotMoveToAndScan(posInfo.cgetScanPos(group.ordinal())); -// } - // - // // 扫描大缓冲液二维码 - // @ExtApiFn(name = "扫描大缓冲液二维码", group = "扫码") - // public AppRet scanBigBottleBufferCode(ConsumableGroup group) - // throws AppException { - // LargeBottleBufferPos posInfo = pp.getLargeBottleBufferPosInfo(); - // return hBotMoveToAndScan(posInfo.cgetScanPos(group.off)); - // } + @ExtApiFn(name = "丢Tip", group = "单步测试") + public void dropTip() throws AppException { + Pos3d pos = pp.getTipDropPos(); + hbotMoveTo(pos.x, pos.y); + zMoveTo(pos.z); + pipetteCtrlModule.putTipBlock(); + zMoveTo(0); + } + + @ExtApiFn(name = "取Tip", group = "单步测试") + public Boolean takeTip(Integer groupId, Integer index) throws AppException { + logger.info("takeTip groupId:{} index:{}", groupId, index); + if (groupId > 2 || groupId < 0) { + throw new AppException(A8kEcode.ParamOutOfRange); + } + + if (index > TipPickUpPosInfo.cgetTipNum() || index < 0) { + throw new AppException(A8kEcode.ParamOutOfRange); + } + + TipPickUpPosInfo tipPos = pp.getTipPickUpPosInfo(); + Pos2d pos = tipPos.getTipPos(groupId, index); + hbotMoveTo(pos.x, pos.y); + + //TODO: 补偿,group大于1时,z轴需要补偿0.2mm + Integer zCompensate = 0; + if (groupId > 1) { + zCompensate = 2; + } + + pipetteCtrlModule.zMotorMoveToBlock(tipPos.getPickUpZPos(groupId) + zCompensate); + pipetteCtrlModule.zMotorMoveToZeroPointQuickBlock(); + + Boolean isGetTip = canBus.moduleGetReg(MId.PipetteMod, RegIndex.kreg_pipette_tip_state) == 1; + if (!isGetTip) { + logger.error("takeTip fail"); + } + return isGetTip; + } + + @ExtApiFn(name = "HBot移动到", group = "单步测试") + public void hbotMoveTo(Integer x, Integer y) throws AppException { + hbotCheckAndMoveTo(new Pos2d(x, y)); + } + + @ExtApiFn(name = "HBot移动到小瓶缓冲液X孔", group = "单步测试") + public void hbotMoveToSmallBottleGroup(ConsumableGroup group, Integer Xhole) + throws AppException { + BottleGroupsPosInfo posInfo = pp.getBottleBufferPosInfo(); + if (Xhole < 0 || Xhole > BottlesPosInfo.cgetMAX()) { + throw new AppException(A8kEcode.ParamOutOfRange); + } + Pos2d pos = posInfo.cgetSmallBottleBufferPos(group.off, Xhole); + hbotCheckAndMoveTo(pos); + } + + @ExtApiFn(name = "HBot移动到探测物质X孔", group = "单步测试") + public void hbotMoveToDetectMaterialPos(ConsumableGroup group, Integer Xhole) + throws AppException { + BottleGroupsPosInfo posInfo = pp.getBottleBufferPosInfo(); + if (Xhole < 0 || Xhole > BottlesPosInfo.cgetMAX()) { + throw new AppException(A8kEcode.ParamOutOfRange); + } + Pos2d pos = posInfo.cgetDetectMaterialPos(group.off, Xhole); + hbotCheckAndMoveTo(pos); + } + + @ExtApiFn(name = "HBot移动到大瓶缓冲液X孔", group = "单步测试") + public void hbotMoveToLargeBottleGroup(ConsumableGroup ch) throws AppException { + LargeBottleBufferPos posInfo = pp.getLargeBottleBufferPosInfo(); + Pos2d pos = posInfo.cgetBottlePos(ch.off); + hbotCheckAndMoveTo(pos); + } + + @ExtApiFn(name = "HBot移动到急诊位", group = "单步测试") + public void hbotMoveToEmergencyPos() throws AppException { + hbotMoveTo(pp.getEmergencyPos()); + } + + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // 坐标获取工具 + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @ExtApiFn(name = "Hbot使能", group = "坐标获取工具") + public void hBotEnable() throws AppException { + canBus.hbotEnable(MId.HbotM, 1); + } + + @ExtApiFn(name = "Hbot失能", group = "坐标获取工具") + public void hBotDisable() throws AppException { + canBus.hbotEnable(MId.HbotM, 0); + } + + @ExtApiFn(name = "Z轴使能", group = "坐标获取工具") + public void zAxisEnable() throws AppException { + pipetteCtrlModule.zMotorEnable(1); + } + + @ExtApiFn(name = "Z轴失能", group = "坐标获取工具") + public void zAxisDisable() throws AppException { + pipetteCtrlModule.zMotorEnable(0); + } + + @ExtApiFn(name = "归零读取Z轴坐标", group = "坐标获取工具") + public Integer readZAxisPosByMoveToZero() throws AppException { + pipetteCtrlModule.zMotorMeasureDistance(); + return pipetteCtrlModule.zMotorReadMeasureDistanceResult(); + } + + @ExtApiFn(name = "打开扫码器", group = "坐标获取工具") + public void openCodeScaner(Boolean power) throws AppException { + if (power) { + canBus.codeScanerStartScan(MId.PipetteModCodeScanner); + } else { + canBus.codeScanerStopScan(MId.PipetteModCodeScanner); + } + } + + Pos2d hbotRefPos = new Pos2d(0, 0); + + @ExtApiFn(name = "设置HBOT参考坐标", group = "坐标获取工具") + public void setHbotRefPos(Integer x, Integer y) throws AppException { + hbotRefPos.x = x; + hbotRefPos.y = y; + } + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // hbot取样品 diff --git a/src/main/java/a8k/service/devicedriver/ctrl/SamplesPreProcesCtrl.java b/src/main/java/a8k/service/devicedriver/ctrl/SamplesPreProcesCtrl.java index 6fff535..d1db19b 100644 --- a/src/main/java/a8k/service/devicedriver/ctrl/SamplesPreProcesCtrl.java +++ b/src/main/java/a8k/service/devicedriver/ctrl/SamplesPreProcesCtrl.java @@ -153,15 +153,15 @@ public class SamplesPreProcesCtrl { canBus.miniServoMoveToBlock(MId.ShakeModGripperYSV, pp.getGripperTakeTubeYPos(), timep.getActionOvertime()); //打开夹爪 canBus.miniServoMoveToBlock(MId.ShakeModGripperSV, pp.getGripperOpenPos(), timep.getActionOvertime()); - // //Z轴下移动到试管帽有无检测位 - // canBus.stepMotorEasyMoveToBlock(MId.ShakeModGripperZM, gripperJudgeCapZPos, timep.getActionOvertime()); - //闭合夹爪检测试管帽是否存在 - // canBus.miniServoRotateWithTorque(MId.ShakeModGripperSV, -400); - // Thread.sleep(timep.getTubeCapJudgeDelayTime()); - // if (ZEQ.IntEq(canBus.miniServoReadPos(MId.ShakeModGripperSV), pp.getGripperClosePos(), 30)) { - // logger.info("试管帽不存在"); - // tubeCapExist = false; - // } +// //Z轴下移动到试管帽有无检测位 +// canBus.stepMotorEasyMoveToBlock(MId.ShakeModGripperZM, gripperJudgeCapZPos, timep.getActionOvertime()); +// 闭合夹爪检测试管帽是否存在 +// canBus.miniServoRotateWithTorque(MId.ShakeModGripperSV, -400); +// Thread.sleep(timep.getTubeCapJudgeDelayTime()); +// if (ZEQ.IntEq(canBus.miniServoReadPos(MId.ShakeModGripperSV), pp.getGripperClosePos(), 30)) { +// logger.info("试管帽不存在"); +// tubeCapExist = false; +// } if (dp.getDebugMode() || tubeCapExist) { //打开夹爪 canBus.miniServoMoveToBlock(MId.ShakeModGripperSV, pp.getGripperOpenPos(), timep.getActionOvertime()); diff --git a/src/main/java/a8k/type/ConsumableGroup.java b/src/main/java/a8k/type/ConsumableGroup.java index 9de0f42..f8c0d59 100644 --- a/src/main/java/a8k/type/ConsumableGroup.java +++ b/src/main/java/a8k/type/ConsumableGroup.java @@ -9,4 +9,10 @@ public enum ConsumableGroup { this.off = this.ordinal(); } + public static ConsumableGroup fromInt(int i) { + if (i < 0 || i >= ConsumableGroup.values().length) { + return null; + } + return ConsumableGroup.values()[i]; + } } diff --git a/src/main/java/a8k/type/ecode/AppCodeError.java b/src/main/java/a8k/type/ecode/AppCodeError.java new file mode 100644 index 0000000..45c1672 --- /dev/null +++ b/src/main/java/a8k/type/ecode/AppCodeError.java @@ -0,0 +1,12 @@ +package a8k.type.ecode; + +import a8k.hardware.type.a8kcanprotocol.A8kEcode; + +public class AppCodeError extends AppError { + public String exmsg; + + public AppCodeError(String exmsg) { + super(A8kEcode.CodeError); + this.exmsg = exmsg; + } +} diff --git a/src/main/java/a8k/utils/ZFnCall.java b/src/main/java/a8k/utils/ZFnCall.java new file mode 100644 index 0000000..e108863 --- /dev/null +++ b/src/main/java/a8k/utils/ZFnCall.java @@ -0,0 +1,22 @@ +package a8k.utils; + + +import a8k.type.ecode.AppError; +import a8k.type.exception.AppException; + +public class ZFnCall { + + @FunctionalInterface + public interface Fn { + void call() throws AppException; + } + + public static AppError callfn(Fn fn) { + try { + fn.call(); + } catch (AppException e) { + return e.error; + } + return null; + } +}