diff --git a/doc/检测流程.md b/doc/检测流程.md index 3097c91..a18270f 100644 --- a/doc/检测流程.md +++ b/doc/检测流程.md @@ -220,4 +220,52 @@ sequenceDiagram A8kCanBusConnection ->> A8kCanBusConnection: priSend A8kCanBusConnection ->> A8kCanBusConnection: _priSend A8kCanBusConnection ->> WebSocketClient: send +``` + + +```mermaid +--- +title: 全血样本预处理流程 +--- +flowchart + Begin[开始] --> S1[获取当前试管信息] + S1 --> S2[获取当前试管架配置信息] + + %% 试管样本准备阶段 + S2 --> S3[夹取试管到摇匀位置] + S3 --> S4[摇匀] + S4 --> S5[取试管帽] + S5 --> S6[设置样本预处理完成] + + %% 反应板夹准备阶段 + S6 --> S7[丢弃反应板夹为什么] + S7 --> S8[拉取反应板夹] + S8 --> S9[丢弃反应板夹为什么] + S9 --> S10[推送反应板夹] + S10 --> S11[设置反应板夹准备完毕] + + %% 处理样本 + S11 --> S12[刺破小缓冲液] + S12 --> S13[取样] + S13 --> S14[将孵育盘转到待滴液的位置] + S14 --> S15[滴液] + + %% 孵育处理 + S15 --> S16[] +``` + + +```mermaid +--- +title: 样本处理类图 +--- + +classDiagram + %% 试管 + class Tube + %% 试管架 + class TubeHolder + %% 反应液类型 + class A8kReactionFlowType + ``` \ No newline at end of file diff --git a/doc/系统事件消息总线.md b/doc/系统事件消息总线.md index e400d39..4d3aa45 100644 --- a/doc/系统事件消息总线.md +++ b/doc/系统事件消息总线.md @@ -65,4 +65,16 @@ classDiagram class TubeHolderSettingMgrService class Report -``` \ No newline at end of file + + AppStateWebsocketEndpoint --> AppWebSocketEndpointMgr + AppEventWebsocketEndpoint --> AppWebSocketEndpointMgr + AppWebSocketEndpointMgr --> FrontEndMessageBoxAndEventMgr + AppWebSocketEndpointMgr --> DeviceWorkStateMgrService + AppWebSocketEndpointMgr --> GStateMgrService + AppWebSocketEndpointMgr --> TubeStateMgrService + AppWebSocketEndpointMgr --> IncubationPlateStateMgrService + AppWebSocketEndpointMgr --> OptScanModuleStateMgrService + AppWebSocketEndpointMgr --> ConsumablesMgrService + AppWebSocketEndpointMgr --> TubeHolderSettingMgrService + +``` diff --git a/doc/耗材管理.md b/doc/耗材管理.md new file mode 100644 index 0000000..4eae9af --- /dev/null +++ b/doc/耗材管理.md @@ -0,0 +1,35 @@ +```mermaid +classDiagram + class A8kConsumableContainer + class TipContainer + class ReactionPlateContainer + class LittBottleContainer + class LarBottleContainer + + A8kConsumableContainer "1" --> "3" TipContainer + A8kConsumableContainer "1" --> "6" ReactionPlateContainer + A8kConsumableContainer "1" --> "6" LittBottleContainer + A8kConsumableContainer "1" --> "6" LarBottleContainer + + + class ConsumablesMgrService + ConsumablesMgrService --> A8kConsumableContainer + + class ConsumableInfo { + public String lotid; + public ConsumableGroup group; // 耗材组 + public Integer pos; // 当前耗材信息属于哪个耗材组 + } + +``` + + +```mermaid +--- +title: 耗材状态更新机制 +--- + +sequenceDiagram + + +``` \ No newline at end of file diff --git a/src/main/java/a8k/app/controler/api/v1/app/verification/PipetteGunPerformanceValidationController.java b/src/main/java/a8k/app/controler/api/v1/app/verification/PipetteGunPerformanceValidationController.java new file mode 100644 index 0000000..b2f7359 --- /dev/null +++ b/src/main/java/a8k/app/controler/api/v1/app/verification/PipetteGunPerformanceValidationController.java @@ -0,0 +1,34 @@ +package a8k.app.controler.api.v1.app.verification; + +import a8k.app.service.verification.PipetteGunPerformanceValidator; +import a8k.app.type.exception.AppException; +import a8k.app.type.ui.ApiRet; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Tag(name = "移液枪性能验证", description = "") +@Slf4j +@Controller +@RequestMapping(value = "/api/v1/app/verification") +@ResponseBody +public class PipetteGunPerformanceValidationController { + @Resource + PipetteGunPerformanceValidator pipetteGunPerformanceValidator; + + /** + * 移液枪10ul验证 + * @return + * @throws AppException + */ + @PostMapping("/pipetteGun/10ul") + public ApiRet validate10ul() throws AppException { + pipetteGunPerformanceValidator.aspirate10ul(); + return ApiRet.success(); + } +} diff --git a/src/main/java/a8k/app/service/verification/PipetteGunPerformanceValidator.java b/src/main/java/a8k/app/service/verification/PipetteGunPerformanceValidator.java new file mode 100644 index 0000000..c41e4b3 --- /dev/null +++ b/src/main/java/a8k/app/service/verification/PipetteGunPerformanceValidator.java @@ -0,0 +1,151 @@ +package a8k.app.service.verification; + +import a8k.app.constant.AppConstant; +import a8k.app.hardware.driver.PipetteCtrlDriver; +import a8k.app.hardware.type.LldType; +import a8k.app.hardware.type.PipetteRegIndex; +import a8k.app.service.lowerctrl.HbotMoveCtrlService; +import a8k.app.service.lowerctrl.HbotMoveExCtrlService; +import a8k.app.service.lowerctrl.LiquidOperationCtrlService; +import a8k.app.service.param.exparam.HbotConsumableExParamMgr; +import a8k.app.service.param.hbotpos.HbotTipPosMgr; +import a8k.app.service.param.pipetteparam.PipetteGunExParamMgr; +import a8k.app.service.statemgr.ConsumablesMgrService; +import a8k.app.type.a8k.ConsumableGroup; +import a8k.app.type.a8k.Pos3d; +import a8k.app.type.a8k.pos.TipGroupPos; +import a8k.app.type.a8k.pos.TipPos; +import a8k.app.type.exception.AppException; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 移液枪性能验证器 + */ +@Slf4j +@Component +public class PipetteGunPerformanceValidator { + + @Resource + HbotTipPosMgr hbotTipPosMgr; + + @Resource + PipetteCtrlDriver pipetteCtrlDriver; + @Resource + HbotMoveCtrlService hbotMoveCtrlService; + @Resource + HbotMoveExCtrlService hbotMoveExCtrlService; + + @Resource + HbotConsumableExParamMgr hbotConsumableExParamMgr; + + @Resource + PipetteGunExParamMgr pipetteGunExParamMgr; + + @Resource + ConsumablesMgrService consumablesMgrService; + + /** + * 1. 移液枪获取从当前系统可用的tip位置获取一个tip头 + * 2. 从耗材组1的大缓冲液中汲取10ul液体滴入到耗材组6的大缓冲液中 + * 3. 移液枪移动到耗材组2大缓冲液位置 + * @throws AppException + */ + public void aspirate10ul() throws AppException { + if (!takeOneTip()) { + throw AppException.ofAECodeError("获取tip失败"); + } + Integer liquidPos = lld(); + if (liquidPos == 0) + throw AppException.ofAECodeError("未检测到液体"); + takeLiquid(10.0, liquidPos); + distributeLiquid(); + resetPos(); + } + + + private void distributeLiquid() throws AppException { + hbotMoveExCtrlService.moveToLargeBSSamplePosXY(ConsumableGroup.CG6); + pipetteCtrlDriver.zMotorMoveToBlock(50); + pumpMoveTo(8000, 0.0); + pipetteCtrlDriver.zMotorMoveToBlock(0); + } + + private void resetPos() throws AppException { + hbotMoveExCtrlService.moveToLargeBSSamplePosXY(ConsumableGroup.CG2); + } + + + private Boolean takeOneTip() throws AppException { + hbotMoveCtrlService.hbotMoveTo(hbotTipPosMgr.getDropTipPos()); + pipetteGunInit(); + TipPos tipPos = consumablesMgrService.getFirstTipPos(TipGroupPos.TipG1); + + log.info(" 取TIP {}", tipPos); + return hbotMoveExCtrlService.takeTipNoCheck(tipPos.group, tipPos.index); + } + + + public Integer lld() throws AppException { + Integer liquidPos = 0; + hbotMoveExCtrlService.moveToLargeBSSamplePosXY(ConsumableGroup.CG1); + pipetteCtrlDriver.zMotorMoveToBlock(0); + pumpMoveTo(8000, 0.0); + pumpMoveTo(300, 100.0); + pumpMoveTo(8000, 0.0); + pumpMoveTo(300, 50.0); + + Pos3d toPos = hbotConsumableExParamMgr.getLargeBufferSamplePos(ConsumableGroup.CG1); + Pos3d maxPos = hbotConsumableExParamMgr.getLargeBufferSamplePosEnd(ConsumableGroup.CG1); + pipetteCtrlDriver.zMotorMoveToBlock(toPos.z); + pipetteCtrlDriver.liquidOperationClearParams(); + pipetteCtrlDriver.liquidOperationSetGunRunParams(14, 14, 0, 900, 30); + pipetteCtrlDriver.liquidOperationSetZMotorRunParams(toPos.z, maxPos.z, 60 * 80 / LiquidOperationCtrlService.helicalPitch); + pipetteCtrlDriver.liquidOperationFreshParams(); + pipetteCtrlDriver.pipetteLld(LldType.kplld, 0, 30); + if (pipetteCtrlDriver.lldIsDetectLiquid()) { + liquidPos = pipetteCtrlDriver.getReg(PipetteRegIndex.kreg_pipette_zm_pos); + } + pipetteCtrlDriver.zMotorMoveToBlock(0); + pumpMoveTo(8000, 0.0); + return liquidPos; + } + + public void pumpMoveTo(Integer pumpvmax, Double ul) throws AppException { + pipetteCtrlDriver.liquidOperationClearParams(); + pipetteCtrlDriver.liquidOperationSetGunRunParams(14, 14, 0, 1000, pumpvmax); + pipetteCtrlDriver.liquidOperationFreshParams(); + pipetteCtrlDriver.pipettePumpMoveTo(ul); + } + + /** + * 移液枪初始化 + * @throws AppException + */ + private void pipetteGunInit() throws AppException { + pipetteCtrlDriver.pipetteInitDeviceBlock(); + } + + + /** + * 汲取液体 + * @param ul + * @throws AppException + */ + public void takeLiquid(Double ul, Integer liquidPos) throws AppException { + hbotMoveExCtrlService.moveToLargeBSSamplePosXY(ConsumableGroup.CG1); + pipetteCtrlDriver.zMotorMoveToBlock(0); + pumpMoveTo(8000, 0.0); + pumpMoveTo(8000, 150.0); + pipetteCtrlDriver.zMotorMoveToBlock(liquidPos); + pipetteCtrlDriver.liquidOperationClearParams(); + pipetteCtrlDriver.liquidOperationSetGunRunParams(14, 14, 0, 900, 100); + pipetteCtrlDriver.liquidOperationSetZMotorRunParams(0, 300, 10 * 80 / LiquidOperationCtrlService.helicalPitch); + pipetteCtrlDriver.liquidOperationFreshParams(); + ul = pipetteGunExParamMgr.calibrateVolume(ul); + log.info("取液体 {}", ul); + pipetteCtrlDriver.pipetteAspirate(ul); + pipetteCtrlDriver.zMotorMoveToBlock(0); + } +}