From badabe167cc2e4c705ea418d43415fd69226bf1e Mon Sep 17 00:00:00 2001 From: zhaohe Date: Thu, 31 Oct 2024 16:11:13 +0800 Subject: [PATCH] update --- README2.md | 2 + .../pagecontrol/ExtApiTabConfig.java | 5 +- .../java/a8k/hardware/A8kModParamInitializer.java | 4 +- .../a8k/hardware/type/a8kcanprotocol/CmdId.java | 1 + src/main/java/a8k/optalgo/A8kOptAlgoV2.java | 2 +- .../app/appctrl/AppMainFlowCtrlService.java | 4 +- .../mainflowctrl/MainFlowCtrlScheduler.java | 4 +- .../action/DO_CLEAR_ERROR_BEFORE_WORK.java | 5 + .../app/appctrl/mainflowctrl/action/DO_STOP.java | 1 + .../mainflowctrl/action/SEQ7_EJECT_TUBEHOLDER.java | 5 +- .../app/appstate/ConsumablesMgrService.java | 5 +- .../a8k/service/app/appstate/GStateService.java | 24 ++-- .../appstate/ProjectProcessContextMgrService.java | 16 +-- .../ctrlservice/DeviceInitCtrlService.java | 3 +- .../app/devicectrl/driver/PipetteCtrlDriver.java | 10 +- .../app/devicectrl/param/LowerDeviceParamMgr.java | 109 +--------------- .../app/devicectrl/script/DeviceCtrlScripter.java | 44 +++++-- .../app/devicectrl/test/PipetteGunTest.java | 114 +++++++++++++++++ .../a8k/service/test/MainflowCtrlTestService.java | 139 +++++++++++++++++++-- .../a8k/service/test/fakeproj/FakeProjInfo.java | 6 +- src/main/java/a8k/type/exception/AppException.java | 18 +-- src/main/resources/app.db | Bin 225280 -> 225280 bytes 22 files changed, 345 insertions(+), 176 deletions(-) create mode 100644 src/main/java/a8k/service/app/devicectrl/test/PipetteGunTest.java diff --git a/README2.md b/README2.md index 6995653..e33ff2c 100644 --- a/README2.md +++ b/README2.md @@ -125,5 +125,7 @@ TODO: 1. tip丢弃失败的问题 2. 舵机死机的问题 3. hbot收不到消息的BUG +4. 增加吸吐混匀速度 +5. 修改处理流程的代码 ``` \ No newline at end of file diff --git a/src/main/java/a8k/extapi_controler/pagecontrol/ExtApiTabConfig.java b/src/main/java/a8k/extapi_controler/pagecontrol/ExtApiTabConfig.java index be8809c..f5e16ae 100644 --- a/src/main/java/a8k/extapi_controler/pagecontrol/ExtApiTabConfig.java +++ b/src/main/java/a8k/extapi_controler/pagecontrol/ExtApiTabConfig.java @@ -23,7 +23,10 @@ public enum ExtApiTabConfig { TemperatureCtrlParamCalibration("校准.温度控制参数校准", true), - A8kPipetteCtrlModule("硬件驱动.移液枪测试", false), + PipetteGunTest("测试.PipetteGun",true), + + + A8kPipetteCtrlModule("硬件驱动.移液枪测试", true), StepMotorCtrlDriver("硬件驱动.步进电机测试", false), ActionReactorService("底层调试.单步调试", false),//OK diff --git a/src/main/java/a8k/hardware/A8kModParamInitializer.java b/src/main/java/a8k/hardware/A8kModParamInitializer.java index 9951345..ced156e 100644 --- a/src/main/java/a8k/hardware/A8kModParamInitializer.java +++ b/src/main/java/a8k/hardware/A8kModParamInitializer.java @@ -113,9 +113,7 @@ public class A8kModParamInitializer { pipetteCtrlDriver.setReg(PipetteRegIndex.kreg_pipette_zm_default_velocity, 1500); - - - + pipetteCtrlDriver.setReg(PipetteRegIndex.kreg_pipette_lld_motor_vel_rpm, 100);//80的时候,刚好和液面齐平 } diff --git a/src/main/java/a8k/hardware/type/a8kcanprotocol/CmdId.java b/src/main/java/a8k/hardware/type/a8kcanprotocol/CmdId.java index fc95ac9..2c6e11d 100644 --- a/src/main/java/a8k/hardware/type/a8kcanprotocol/CmdId.java +++ b/src/main/java/a8k/hardware/type/a8kcanprotocol/CmdId.java @@ -144,6 +144,7 @@ public enum CmdId { pipette_set_lld_p_threshold(0x7421, "移动枪设置液面探测压力阈值"), kpipette_set_llf_startz(0x7422, "移动枪设置液面跟随开始限制高度"), kpipette_set_llf_endz(0x7423, "移动枪设置液面跟随结束限制高度"), + kpipette_clear_hanging_liquid(0x7424, "移动枪清除悬液"), ; diff --git a/src/main/java/a8k/optalgo/A8kOptAlgoV2.java b/src/main/java/a8k/optalgo/A8kOptAlgoV2.java index 2419155..f132514 100644 --- a/src/main/java/a8k/optalgo/A8kOptAlgoV2.java +++ b/src/main/java/a8k/optalgo/A8kOptAlgoV2.java @@ -88,7 +88,7 @@ public class A8kOptAlgoV2 { static void findpeak(double[] data, int search_start, int search_end, A8kOptPeak retpeak) { // find peak - log.info("find peak in [{} {}]", search_start, search_end); +// log.info("find peak in [{} {}]", search_start, search_end); retpeak.state = PeakFindState.NOT_FIND_PEAK; retpeak.area = 0.0; retpeak.peakPos = 0; diff --git a/src/main/java/a8k/service/app/appctrl/AppMainFlowCtrlService.java b/src/main/java/a8k/service/app/appctrl/AppMainFlowCtrlService.java index f9075e3..785eb38 100644 --- a/src/main/java/a8k/service/app/appctrl/AppMainFlowCtrlService.java +++ b/src/main/java/a8k/service/app/appctrl/AppMainFlowCtrlService.java @@ -116,7 +116,7 @@ public class AppMainFlowCtrlService { } void postPorcessA8kEcode(DeviceWorkState state, List ecodeList) { - + logger.info("处理错误 {}", ecodeList); boolean hasErrorUnHandle = false; List noHumanInterventionErrors = new ArrayList<>(); //无需人工干预的错误 noHumanInterventionErrors.add(A8kEcode.APPE_TAKE_TIP_FAIL); @@ -134,7 +134,7 @@ public class AppMainFlowCtrlService { state.errorFlag = true; state.ecodeList = ecodeList; state.workState = A8kWorkState.PAUSE; - }else{ + } else { state.errorFlag = false; state.ecodeList = ecodeList; } diff --git a/src/main/java/a8k/service/app/appctrl/mainflowctrl/MainFlowCtrlScheduler.java b/src/main/java/a8k/service/app/appctrl/mainflowctrl/MainFlowCtrlScheduler.java index 209b7e4..76fb15d 100644 --- a/src/main/java/a8k/service/app/appctrl/mainflowctrl/MainFlowCtrlScheduler.java +++ b/src/main/java/a8k/service/app/appctrl/mainflowctrl/MainFlowCtrlScheduler.java @@ -129,7 +129,7 @@ public class MainFlowCtrlScheduler { } catch (MutiAppException mutiAppe) { List appExceptions = mutiAppe.bindExceptions; for (AppException appe : appExceptions) { - appe.print(); + logger.error("error {}", appe.getMessage(), appe); } if (appExceptions.size() > 1) { logger.warn("发生多个错误,只报告第一个错误"); @@ -137,7 +137,7 @@ public class MainFlowCtrlScheduler { return new A8kErrorContext(key.step, appExceptions.get(0).error); } catch (AppException appe) { - appe.print(); + logger.error("error {}", appe.getMessage(), appe); return new A8kErrorContext(key.step, appe.error); } catch (Exception exception) { logger.error("error {}", exception.getMessage(), exception); diff --git a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_CLEAR_ERROR_BEFORE_WORK.java b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_CLEAR_ERROR_BEFORE_WORK.java index edad4a1..329beb8 100644 --- a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_CLEAR_ERROR_BEFORE_WORK.java +++ b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_CLEAR_ERROR_BEFORE_WORK.java @@ -9,6 +9,8 @@ import a8k.service.app.appstate.type.state.A8kWorkState; import a8k.type.exception.AppException; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.List; @@ -24,6 +26,8 @@ import java.util.List; @Component public class DO_CLEAR_ERROR_BEFORE_WORK extends A8kStepAction { + private static final Logger log = LoggerFactory.getLogger(DO_CLEAR_ERROR_BEFORE_WORK.class); + DO_CLEAR_ERROR_BEFORE_WORK() { super(A8kActionStepType.DO_CLEAR_ERROR_BEFORE_WORK); } @@ -42,6 +46,7 @@ public class DO_CLEAR_ERROR_BEFORE_WORK extends A8kStepAction { @Override public void doaction() throws AppException { mfcs.errorFlag = false; mfcs.ecodeList.clear(); + log.info("运行前清除错误"); } @Override public Boolean checkCondition() { diff --git a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_STOP.java b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_STOP.java index cac9f73..00b62f3 100644 --- a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_STOP.java +++ b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/DO_STOP.java @@ -83,6 +83,7 @@ public class DO_STOP extends A8kStepAction { @Override public void doaction() throws AppException { mfcs.workStateChangeFlag = false; //清空设备 + doClearDevice(); //清空所有反应状态 projectProcessContextMgrService.finishedAll(); diff --git a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/SEQ7_EJECT_TUBEHOLDER.java b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/SEQ7_EJECT_TUBEHOLDER.java index 4322af5..65ad8b7 100644 --- a/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/SEQ7_EJECT_TUBEHOLDER.java +++ b/src/main/java/a8k/service/app/appctrl/mainflowctrl/action/SEQ7_EJECT_TUBEHOLDER.java @@ -37,7 +37,7 @@ public class SEQ7_EJECT_TUBEHOLDER extends A8kStepAction { @Resource - CondtionMgrService cms; + CondtionMgrService cms; @Resource TubeFeedingCtrlService tubeFeedingCtrlService; @@ -55,10 +55,11 @@ public class SEQ7_EJECT_TUBEHOLDER extends A8kStepAction { tubeFeedingCtrlService.ejectTubeHolder(); tubeFeedingCtrlService.moveTubeRackMoveToEnterPos(); } else { - virtualDevice.doVirtualThings("弹出试管架", 2); + virtualDevice.doVirtualThings("弹出试管架", 2); } gstate.getTubeHolder().setState(TubeHolderState.IDLE); + gstate.setCurProcessingTube(null); } @Override public Boolean checkCondition() { diff --git a/src/main/java/a8k/service/app/appstate/ConsumablesMgrService.java b/src/main/java/a8k/service/app/appstate/ConsumablesMgrService.java index 7e69f2f..02130e6 100644 --- a/src/main/java/a8k/service/app/appstate/ConsumablesMgrService.java +++ b/src/main/java/a8k/service/app/appstate/ConsumablesMgrService.java @@ -70,11 +70,12 @@ public class ConsumablesMgrService { synchronized ConsumableGroup priGetConsumableGroupByProjIndex(Integer projId) { var cState = gstate.getConsumableState(); for (int i = 0; i < cState.reactionPlateGroup.length; i++) { - if (cState.reactionPlateGroup[i].projId.equals(projId)) { - if (cState.reactionPlateGroup[i].num > 0) { + if (cState.reactionPlateGroup[i].num > 0) { + if (cState.reactionPlateGroup[i].projId.equals(projId)) { return ConsumableGroup.fromInt(i); } } + } return null; } diff --git a/src/main/java/a8k/service/app/appstate/GStateService.java b/src/main/java/a8k/service/app/appstate/GStateService.java index 6cc3236..68bcf93 100644 --- a/src/main/java/a8k/service/app/appstate/GStateService.java +++ b/src/main/java/a8k/service/app/appstate/GStateService.java @@ -20,27 +20,27 @@ public class GStateService { public static final Logger logger = LoggerFactory.getLogger(GStateService.class); //温度 - Integer temperature = 25; + private Integer temperature = 25; //设备是否初始化过 - Boolean deviceInited = false; + private Boolean deviceInited = false; //当前正在被处理的试管架状态 - TubeHolder tubeHolder = new TubeHolder(); + private TubeHolder tubeHolder = new TubeHolder(); //急诊为状态 - EmergencyTubePos emergencyTubePos = new EmergencyTubePos(); + private EmergencyTubePos emergencyTubePos = new EmergencyTubePos(); //孵育盘状态 - IncubationPlate incubationPlate = new IncubationPlate(); + private IncubationPlate incubationPlate = new IncubationPlate(); //耗材状态 - ConsumableState consumableState = new ConsumableState(); + private ConsumableState consumableState = new ConsumableState(); //光学模组状态 - OptScanModule optScanModule = new OptScanModule(); + private OptScanModule optScanModule = new OptScanModule(); //试管配置 - List tubeHolderSettings = new ArrayList<>(); + private List tubeHolderSettings = new ArrayList<>(); //当前正在处理的试管 - Tube curProcessingTube = null; + private Tube curProcessingTube = null; // - String appVersion = AppConstant.APP_VERSION; - String mcuVersion = "NOTSET"; - String sn = "NOTSET"; + private String appVersion = AppConstant.APP_VERSION; + private String mcuVersion = "NOTSET"; + private String sn = "NOTSET"; SensorState sensorState = new SensorState(); diff --git a/src/main/java/a8k/service/app/appstate/ProjectProcessContextMgrService.java b/src/main/java/a8k/service/app/appstate/ProjectProcessContextMgrService.java index e7ad429..364edbb 100644 --- a/src/main/java/a8k/service/app/appstate/ProjectProcessContextMgrService.java +++ b/src/main/java/a8k/service/app/appstate/ProjectProcessContextMgrService.java @@ -26,17 +26,17 @@ public class ProjectProcessContextMgrService { static Logger logger = org.slf4j.LoggerFactory.getLogger(ProjectProcessContextMgrService.class); @Resource - GStateService gstate; + GStateService gstate; @Resource - SampleRecordDBDao sampleRecordDBDao; + SampleRecordDBDao sampleRecordDBDao; @Resource - ConsumablesMgrService consumablesMgrService; + ConsumablesMgrService consumablesMgrService; @Resource IncubationPlateStateMgrService incubationPlateStateMgrService; @Resource - ProjCfgMgrService projCfgMgrService; + ProjCfgMgrService projCfgMgrService; @Resource - DeviceStatisticDao deviceStatisticDao; + DeviceStatisticDao deviceStatisticDao; List contexts = new ArrayList<>(); @@ -84,7 +84,7 @@ public class ProjectProcessContextMgrService { for (ProjBriefInfo projInfo : projInfos) { ProjProcessContext projProcessContext = new ProjProcessContext(tube.getSampleId()); projProcessContext.state = ProjProcessState.INIT; - projProcessContext.projId = projInfo.projId; + projProcessContext.projId = projInfo.projId; projProcessContext.isHighTube = tube.getIsHighTube(); projProcessContext.isEmergency = tube.getIsEmergency(); projProcessContext.bloodType = tube.getBloodType(); @@ -189,6 +189,8 @@ public class ProjectProcessContextMgrService { logger.info(" 试管ID:{}", tube.getSampleId()); } gstate.setTubeHolder(tubeholder); + gstate.setCurProcessingTube(null); + } synchronized public void newEmergencyTube(Tube tube) { @@ -222,7 +224,7 @@ public class ProjectProcessContextMgrService { * 挂起试管 * @param nextProcessTube 下一个处理的试管 */ - gstate.curProcessingTube = tube; + gstate.setCurProcessingTube(tube); gstate.getCurProcessingTube().setState(TubeState.PENDING); setProjProcessState(tube, ProjProcessState.PENDING); } diff --git a/src/main/java/a8k/service/app/devicectrl/ctrlservice/DeviceInitCtrlService.java b/src/main/java/a8k/service/app/devicectrl/ctrlservice/DeviceInitCtrlService.java index 8366345..55ebd5d 100644 --- a/src/main/java/a8k/service/app/devicectrl/ctrlservice/DeviceInitCtrlService.java +++ b/src/main/java/a8k/service/app/devicectrl/ctrlservice/DeviceInitCtrlService.java @@ -101,6 +101,8 @@ public class DeviceInitCtrlService { for (CheckResult result : results) { if (!result.pass) { log.info("设备初始化失败: check {} fail", result.typechinfo); + stepMotorCtrlDriver.stepMotorEnable(StepMotorMId.ShakeModClampingM, 1); + stepMotorCtrlDriver.stepMotorEasyMoveByBlock(StepMotorMId.ShakeModClampingM, 10, 3000); motorEnableExDriver.forceDisableAllMotor(); return results; } @@ -143,7 +145,6 @@ public class DeviceInitCtrlService { //hbot快速归零 hbotCtrlService.moveQuickToZero(); - pipetteCtrlDriver.lldPrepareBlock(); return results; diff --git a/src/main/java/a8k/service/app/devicectrl/driver/PipetteCtrlDriver.java b/src/main/java/a8k/service/app/devicectrl/driver/PipetteCtrlDriver.java index 1144da0..611e2aa 100644 --- a/src/main/java/a8k/service/app/devicectrl/driver/PipetteCtrlDriver.java +++ b/src/main/java/a8k/service/app/devicectrl/driver/PipetteCtrlDriver.java @@ -245,8 +245,8 @@ public class PipetteCtrlDriver { @ExtApiFn(name = "吸液准备", order = FnOrder.aspiratePrepareBlock) public void aspiratePrepareBlock() throws AppException { - canBusService.callcmd(MId.PipetteMod.toInt(), CmdId.pipette_aspirate_prepare.toInt()); - canBusService.waitForMod(MId.PipetteMod, overtime); + // canBusService.callcmd(MId.PipetteMod.toInt(), CmdId.pipette_aspirate_prepare.toInt()); + // canBusService.waitForMod(MId.PipetteMod, overtime); } @ExtApiFn(name = "设置LLF速度", order = FnOrder.aspirateSetLlfVelocity) @@ -302,6 +302,12 @@ public class PipetteCtrlDriver { canBusService.waitForMod(MId.PipetteMod, overtime + times * 3000); } + @ExtApiFn(name = "清除悬液", order = FnOrder.shakeUpBlock) + public void clearHangingLiquid(Integer repeatTimes) throws AppException { + canBusService.callcmd(MId.PipetteMod.toInt(), CmdId.kpipette_clear_hanging_liquid.toInt(), repeatTimes); + canBusService.waitForMod(MId.PipetteMod, overtime); + } + @ExtApiFn(name = "吸液推算压力", order = FnOrder.aspirateInferPressureBlock) public void aspirateInferPressureBlock(Integer ul) throws AppException { canBusService.callcmd(MId.PipetteMod.toInt(), CmdId.pipette_aspirate_infer_pressure.toInt(), ul); diff --git a/src/main/java/a8k/service/app/devicectrl/param/LowerDeviceParamMgr.java b/src/main/java/a8k/service/app/devicectrl/param/LowerDeviceParamMgr.java index a477fd7..f56b43c 100644 --- a/src/main/java/a8k/service/app/devicectrl/param/LowerDeviceParamMgr.java +++ b/src/main/java/a8k/service/app/devicectrl/param/LowerDeviceParamMgr.java @@ -21,120 +21,19 @@ public class LowerDeviceParamMgr { Assert.isTrue(var != null && var.val != null && !var.val.isEmpty(), String.format("CHECK %s.%s val fail", service, key)); } - @ExtApiFn(name = "恢复参数", group = "基础", order = 1) + @ExtApiFn(name = "!!!!!恢复参数!!!!!", group = "基础", order = 1) public void recoveryParam() { ldpdb.recoveryAllParam(); } - @ExtApiFn(name = "存储参数", group = "基础", order = 1) + @ExtApiFn(name = "!!!!!存储参数!!!!!", group = "基础", order = 2) public void storageParam() { ldpdb.storageAll(); } - @ExtApiFn(name = "检查参数是否全部设置", group = "基础", order = 1) + @ExtApiFn(name = "检查参数是否全部设置", group = "基础", order = 3) public void checkParams() { - check("Hbot2DCodeScanParamMgr", "PBScanPos0"); - check("Hbot2DCodeScanParamMgr", "PBScanPos1"); - check("Hbot2DCodeScanParamMgr", "PBScanPos2"); - check("Hbot2DCodeScanParamMgr", "PBScanPos3"); - check("Hbot2DCodeScanParamMgr", "PBScanPos4"); - check("Hbot2DCodeScanParamMgr", "PBScanPos5"); - check("Hbot2DCodeScanParamMgr", "LittBS0"); - check("Hbot2DCodeScanParamMgr", "LittBS1"); - check("Hbot2DCodeScanParamMgr", "LittBS2"); - check("Hbot2DCodeScanParamMgr", "LittBS3"); - check("Hbot2DCodeScanParamMgr", "LittBS4"); - check("Hbot2DCodeScanParamMgr", "LittBS5"); - check("Hbot2DCodeScanParamMgr", "LarBS0"); - check("Hbot2DCodeScanParamMgr", "LarBS1"); - check("Hbot2DCodeScanParamMgr", "LarBS2"); - check("Hbot2DCodeScanParamMgr", "LarBS3"); - check("Hbot2DCodeScanParamMgr", "LarBS4"); - check("Hbot2DCodeScanParamMgr", "LarBS5"); - check("TurntablePosParamMgr", "PushPos0"); - check("TurntablePosParamMgr", "PullPos0"); - check("TurntablePosParamMgr", "DropLiquidPos0"); - check("TurntablePosParamMgr", "PosSpacing"); - check("TubeFeedingModuleParamMgr", "TubeHolderEnterXPos"); - check("TubeFeedingModuleParamMgr", "TubeHolderExitXPos"); - check("TubeFeedingModuleParamMgr", "TubeHolderScanXPos"); - check("TubeFeedingModuleParamMgr", "Tube0ScanPos"); - check("TubeFeedingModuleParamMgr", "Tube0AltitJudgPos"); - check("TubeFeedingModuleParamMgr", "Tube0ExistJudgPos"); - check("TubeFeedingModuleParamMgr", "Tube0PreProcessPos"); - check("TubeFeedingModuleParamMgr", "TubeScanServoTorque"); - check("TubeFeedingModuleParamMgr", "TubeSpacing"); - check("HbotFixedPosParamMgr", "DropLiquidPos"); - check("HbotSamplePosParamMgr", "EmergencyTubeSamplePos"); - check("HbotSamplePosParamMgr", "EmergencyTubeSampleEndPos"); - check("HbotSamplePosParamMgr", "BloodHTubeSamplePos"); - check("HbotSamplePosParamMgr", "BloodHTubeSampleEndPos"); - check("HbotSamplePosParamMgr", "BloodSTubeSamplePos"); - check("HbotSamplePosParamMgr", "BloodSTubeSampleEndPos"); - check("HbotSamplePosParamMgr", "MiniTubeSamplePos"); - check("HbotSamplePosParamMgr", "MinitubeSampleEndPos"); - check("HbotSamplePosParamMgr", "MiniBloodSamplePos"); - check("HbotSamplePosParamMgr", "MiniBloodSampleEndPos"); - check("HbotSamplePosParamMgr", "Bulltube1P5SamplePos"); - check("HbotSamplePosParamMgr", "Bulltube1P5SampleEndPos"); - check("HbotSamplePosParamMgr", "Bulltube0P5SamplePos"); - check("HbotSamplePosParamMgr", "Bulltube0P5SampleEndPos"); - check("HbotSamplePosParamMgr", "StoolTestTubeSamplePos"); - check("HbotSamplePosParamMgr", "StoolTestTubeSampleEndPos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup0_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup1_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup2_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup3_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup4_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroup5_000Pos"); - check("HbotLittleBSPosMgr", "LittleBufferGroupDX"); - check("HbotLittleBSPosMgr", "LittleBufferGroupDY"); - check("HbotLittleBSPosMgr", "LittleBSPierceZPos"); - check("HbotLittleBSPosMgr", "LittleBSSampleZPos"); - check("HbotLittleBSPosMgr", "LittleBSSampleEndZPos"); - check("HbotLargeBottleBSPosMgr", "LargeBuffer_0Pos"); - check("HbotLargeBottleBSPosMgr", "LargeBuffer_DX"); - check("HbotLargeBottleBSPosMgr", "LargeBuffer_DY"); - check("HbotLargeBottleBSPosMgr", "LargeBSSSampleZPos"); - check("HbotLargeBottleBSPosMgr", "LargeBSSSampleEndZPos"); - check("HbotTipPosMgr", "TipGroup0_000Pos"); - check("HbotTipPosMgr", "TipGroup0_SpaceingX"); - check("HbotTipPosMgr", "TipGroup0_SpaceingY"); - check("HbotTipPosMgr", "TipGroup1_000Pos"); - check("HbotTipPosMgr", "TipGroup1_SpaceingX"); - check("HbotTipPosMgr", "TipGroup1_SpaceingY"); - check("HbotTipPosMgr", "TipGroup2_000Pos"); - check("HbotTipPosMgr", "TipGroup2_SpaceingX"); - check("HbotTipPosMgr", "TipGroup2_SpaceingY"); - check("HbotTipPosMgr", "DropTipPos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup0_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup1_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup2_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup3_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup4_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceGroup5_000Pos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceDX"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceDY"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceSampleZPos"); - check("HbotProbeSubstancePosMgr", "ProbeSubstanceSampleEndZPos"); - check("OptModuleParamsMgr", "PullerTargetPos"); - check("OptModuleParamsMgr", "OptScanerDropPos"); - check("OptModuleParamsMgr", "OptScanerScandbyPos"); - check("OptModuleParamsMgr", "TOptScanStartPos"); - check("OptModuleParamsMgr", "FOptScanStartPos"); - check("PlatesBoxPosParamMgr", "Ch0YPos"); - check("PlatesBoxPosParamMgr", "Ch5YPos"); - check("PlatesBoxPosParamMgr", "PushEndXPos"); - check("TubePreProcesPosParamMgr", "GripperServoOpenPos"); - check("TubePreProcesPosParamMgr", "GripperServoClosePos"); - check("TubePreProcesPosParamMgr", "GripperServoTakeCapPos"); - check("TubePreProcesPosParamMgr", "ShakeClampMotorClampPos"); - check("TubePreProcesPosParamMgr", "ShakeClampMotorReleasePos"); - check("TubePreProcesPosParamMgr", "YServoTakeTubePos"); - check("TubePreProcesPosParamMgr", "YServoShakePos"); - check("TubePreProcesPosParamMgr", "ZMotorTakeHTubePos"); - check("TubePreProcesPosParamMgr", "ZMotorTakeSTubePos"); - check("TubePreProcesPosParamMgr", "ZMotorShakeTubePos"); + } diff --git a/src/main/java/a8k/service/app/devicectrl/script/DeviceCtrlScripter.java b/src/main/java/a8k/service/app/devicectrl/script/DeviceCtrlScripter.java index 57b3fb0..da3d67d 100644 --- a/src/main/java/a8k/service/app/devicectrl/script/DeviceCtrlScripter.java +++ b/src/main/java/a8k/service/app/devicectrl/script/DeviceCtrlScripter.java @@ -75,6 +75,12 @@ public class DeviceCtrlScripter { pipetteCtrlDriver.aspirateBlock(ul); } + void distributeNoLLF(Integer ul) throws AppException { + pipetteCtrlDriver.aspirateSetLlfVelocity(0); + pipetteCtrlDriver.aspirateBlock(-ul); + } + + void distribute(PipetteGunBindActionType actionType, ProjProcessContext ctx, Integer ul) throws AppException { LLFParamPack llfParamPack = pipetteGunParamExMgr.getLLFParam(actionType, ctx); pipetteCtrlDriver.aspirateSetLlfVelocity(llfParamPack.llfVel); @@ -94,6 +100,8 @@ public class DeviceCtrlScripter { void aspirate(PipetteGunBindActionType actionType, ProjProcessContext ctx, Integer ul) throws AppException { LLFParamPack llfParamPack = pipetteGunParamExMgr.getLLFParam(actionType, ctx); + log.info("吸液 {} , llfvel {} , llfStartPos {} , llfEndPos {}", ul, llfParamPack.llfVel, llfParamPack.llfStartPos, llfParamPack.llfEndPos); + pipetteCtrlDriver.aspirateSetLlfVelocity(llfParamPack.llfVel); pipetteCtrlDriver.setLlfStartZ(llfParamPack.llfStartPos); pipetteCtrlDriver.setLlfEndZ(llfParamPack.llfEndPos); @@ -102,6 +110,8 @@ public class DeviceCtrlScripter { void lld(PipetteGunBindActionType actionType, ProjProcessContext ctx) throws AppException { + pipetteCtrlDriver.lldPrepareBlock(); + log.info("液面探测 {}", actionType); LLDParamPack lldparm = pipetteGunParamExMgr.getLLDParam(actionType, ctx); Pos3d sampleStartPos = null; @@ -153,20 +163,23 @@ public class DeviceCtrlScripter { hbotCtrlService.dropTip(); //待机 hbotCtrlService.moveQuickToZero(); - pipetteCtrlDriver.lldPrepareBlock(); + + //提前.取tip, + hbotCtrlService.takeTip(ctx.takeTip()); } else if (type.equals(A8kReactionFlowType.FlowType2)) { log.info("样本预处理,取大瓶缓冲液液到探测物质中"); /* * 取大瓶缓冲液液到探测物质中 */ //ldd准备 - pipetteCtrlDriver.lldPrepareBlock(); //取tip, hbotCtrlService.takeTip(ctx.takeTip()); //移动到取样位置 - hbotCtrlService.moveTo(hbotConsumableParamMgr.getLargeBufferSamplePos(ctx.consumable.getGroup())); + Pos3d pos = hbotConsumableParamMgr.getLargeBufferSamplePos(ctx.consumable.getGroup()); + pos.z = 0; + hbotCtrlService.moveTo(pos); //移动到目标位置,并液面探测 lld(PipetteGunBindActionType.TAKE_LARGE_BUFFER_SOLUTION, ctx); @@ -182,14 +195,13 @@ public class DeviceCtrlScripter { hbotCtrlService.moveTo(hbotConsumableParamMgr.getProbeSubstanceSamplePosEnd(ctx.consumable.getGroup(), ctx.consumable.getPos())); //吐液 - distribute(PipetteGunBindActionType.DISTRIBUTION_LARGE_BUFFER_SOLUTION_PROBE_SUBSTANCE, ctx, -takeul); + distribute(PipetteGunBindActionType.DISTRIBUTION_LARGE_BUFFER_SOLUTION_PROBE_SUBSTANCE, ctx, takeul); //丢tip hbotCtrlService.dropTip(); //归零,待机 hbotCtrlService.moveQuickToZero(); - pipetteCtrlDriver.lldPrepareBlock(); } else { Assert.isTrue(false, "不支持的反应流程类型"); } @@ -199,16 +211,19 @@ public class DeviceCtrlScripter { Integer sampleul = ProjProcessContextUtils.getSampleVol(ctx); Integer reactionul = ProjProcessContextUtils.getReactionPlateDropletVolUl(ctx); - //ldd准备 - pipetteCtrlDriver.lldPrepareBlock(); - - //取tip, - hbotCtrlService.takeTip(ctx.takeTip()); + A8kReactionFlowType type = ctx.projCfg.projectInfo.reactionFlowType; + if (type.equals(A8kReactionFlowType.FlowType1)) { + // hbotCtrlService.takeTip(ctx.takeTip()); + } else { + hbotCtrlService.takeTip(ctx.takeTip()); + } //移动到取样位置 A8kSamplePos samplePos = ProjProcessContextUtils.getSamplePos(ctx); log.info("移动到 {}", samplePos); - hbotCtrlService.moveTo(hbotSamplePosParamMgr.getSampleStartPos(samplePos)); + Pos3d pos = hbotSamplePosParamMgr.getSampleStartPos(samplePos); + pos.z = 0; + hbotCtrlService.moveTo(pos); //ldd准备 lld(PipetteGunBindActionType.SAMPLE, ctx); @@ -244,9 +259,12 @@ public class DeviceCtrlScripter { //旋转转盘 turnableMoveCtrlService.trunableMoveToDropLiquidPos(ctx.incubatorPos); - //取样到反应板 + //吐液到反应板 hbotCtrlService.moveTo(hbotFixedPosParamMgr.getDropLiquidPos()); - aspirateNoLLF(reactionul); + distributeNoLLF(reactionul); + pipetteCtrlDriver.clearHangingLiquid(2); + + //丢tip hbotCtrlService.dropTip(); diff --git a/src/main/java/a8k/service/app/devicectrl/test/PipetteGunTest.java b/src/main/java/a8k/service/app/devicectrl/test/PipetteGunTest.java new file mode 100644 index 0000000..9b9eec5 --- /dev/null +++ b/src/main/java/a8k/service/app/devicectrl/test/PipetteGunTest.java @@ -0,0 +1,114 @@ +package a8k.service.app.devicectrl.test; + +import a8k.extapi_controler.pagecontrol.ExtApiTabConfig; +import a8k.extapi_controler.utils.ExtApiFn; +import a8k.extapi_controler.utils.ExtApiTab; +import a8k.hardware.type.LldType; +import a8k.service.app.devicectrl.driver.PipetteCtrlDriver; +import a8k.service.app.devicectrl.exdriver.HbotBaseMoveExDriver; +import a8k.service.app.devicectrl.exdriver.MotorEnableExDriver; +import a8k.service.app.devicectrl.param.param_mgr.PipetteGunLLDParamMgr; +import a8k.service.app.devicectrl.param.type.PipetteGunLLDThresholdParam; +import a8k.service.app.devicectrl.param.type.PipetteGunLLDTypeParam; +import a8k.type.exception.AppException; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +@ExtApiTab(cfg = ExtApiTabConfig.PipetteGunTest) +@Component +public class PipetteGunTest { + + @Resource + PipetteGunLLDParamMgr pipetteGunLLDParamMgr; + + @Resource + PipetteCtrlDriver pipetteCtrlDriver; + @Resource + HbotBaseMoveExDriver hbotBaseMoveExDriver; + @Resource + MotorEnableExDriver motorEnableExDriver; + + + // 测试工具 + // @ExtApiFn(name = "归零", group = "基础", order = 1) + // public void moveToZero() throws AppException { + // enableModule(); + // pipetteCtrlDriver.zMotorMoveZeroBlock(); + // } + + @ExtApiFn(name = "移液枪初始化(Tip会掉落)", group = "基础", order = 2) + public void pipetteInitDeviceBlock() throws AppException { + pipetteCtrlDriver.pipetteInitDeviceBlock(); + + } + + @ExtApiFn(name = "使能相关模块", group = "基础", order = 3) + public void enableModule() throws AppException { + pipetteCtrlDriver.zMotorEnable(1); + } + + @ExtApiFn(name = "失能相关模块", group = "基础", order = 4) + public void disableModule() throws AppException { + motorEnableExDriver.forceDisableAllMotor(); + } + + @ExtApiFn(name = "获取相关参数", group = "基础", order = 5) + public Object getParams() throws AppException { + return pipetteGunLLDParamMgr.getParams(); + } + + + Integer lldStartPos; + Integer lldEndPos; + + @ExtApiFn(name = "设置LLD开始位置", group = "测试", order = 100) + public void setStartTestPos() throws AppException { + pipetteCtrlDriver.zMotorEnable(1); + pipetteCtrlDriver.zMotorMeasureDistance(); + lldStartPos = pipetteCtrlDriver.zMotorReadMeasureDistanceResult(); + pipetteCtrlDriver.zMotorEnable(0); + } + + @ExtApiFn(name = "设置LLD结束位置", group = "测试", order = 101) + public void setEndTestPos() throws AppException { + pipetteCtrlDriver.zMotorEnable(1); + pipetteCtrlDriver.zMotorMeasureDistance(); + lldEndPos = pipetteCtrlDriver.zMotorReadMeasureDistanceResult(); + pipetteCtrlDriver.zMotorEnable(0); + } + + @ExtApiFn(name = "LLD准备(每次LLD前都要调用一次,同时更换tip头)", group = "测试", order = 102) + public void lldPrepare() throws AppException { + pipetteCtrlDriver.zMotorEnable(1); + pipetteCtrlDriver.zMotorMoveZeroBlock(); + pipetteCtrlDriver.lldPrepareBlock(); + } + + @ExtApiFn(name = "LDD测量液体属性", group = "测试", order = 103) + public Object lldCalibrate() throws AppException { + if (lldStartPos > lldEndPos) { + throw AppException.of("开始位置大于结束位置"); + } + + pipetteCtrlDriver.zMotorEnable(1); + pipetteCtrlDriver.zMotorMoveZeroBlock(); + pipetteCtrlDriver.setStartZ(lldStartPos); + pipetteCtrlDriver.setEndZ(lldEndPos); + pipetteCtrlDriver.lldCalibrationBlock(); + return pipetteCtrlDriver.getSensorSampleData(); + } + + @ExtApiFn(name = "LLD测试", group = "测试", order = 104) + public void lldTest(LldType type, Integer c_val, Integer p_val) throws AppException { + pipetteCtrlDriver.zMotorEnable(1); + pipetteCtrlDriver.zMotorMoveZeroBlock(); + pipetteCtrlDriver.setStartZ(lldStartPos); + pipetteCtrlDriver.setEndZ(lldEndPos); + pipetteCtrlDriver.setLldType(type); + pipetteCtrlDriver.setLldCThreshold(c_val); + pipetteCtrlDriver.setLldPThreshold(p_val); + pipetteCtrlDriver.lldBlock(); + } + + +} diff --git a/src/main/java/a8k/service/test/MainflowCtrlTestService.java b/src/main/java/a8k/service/test/MainflowCtrlTestService.java index 518bdff..45f45ad 100644 --- a/src/main/java/a8k/service/test/MainflowCtrlTestService.java +++ b/src/main/java/a8k/service/test/MainflowCtrlTestService.java @@ -7,22 +7,27 @@ import a8k.extapi_controler.utils.ExtApiTab; import a8k.service.app.appctrl.AppConsumablesScanService; import a8k.service.app.appctrl.AppDeviceCtrlService; import a8k.service.app.appctrl.AppTubeSettingMgrService; +import a8k.service.app.appctrl.mainflowctrl.CondtionMgrService; +import a8k.service.app.appctrl.mainflowctrl.MainFlowCtrlScheduler; import a8k.service.app.appstate.ConsumablesMgrService; import a8k.service.app.appstate.GStateService; +import a8k.service.app.appstate.type.DeviceWorkState; import a8k.service.app.appstate.type.state.A8kWorkState; +import a8k.service.app.devicectrl.ctrlservice.OptScanModuleCtrlService; +import a8k.service.app.devicectrl.driver.StepMotorCtrlDriver; +import a8k.service.app.devicectrl.driver.type.StepMotorMId; +import a8k.service.app.devicectrl.exdriver.MotorEnableExDriver; import a8k.service.dao.A8kProjIdCardDao; import a8k.service.dao.A8kProjInfoDao; import a8k.service.dao.A8kProjOptConfigDao; import a8k.service.test.fakeproj.FAKE_PROJ_01; +import a8k.service.test.fakeproj.FAKE_PROJ_02; import a8k.service.test.fakeproj.FakeProjInfo; import a8k.service.test.fakeproj.FakeProjInfoFactory; import a8k.service.test.state.TestModeState; import a8k.service.test.state.VirtualDevice; import a8k.service.test.type.MainFlowCtrlTestCaseType; -import a8k.type.ConsumableGroup; -import a8k.type.ConsumablesScanReport; -import a8k.type.TubeHolderScanResult; -import a8k.type.TubesScanResult; +import a8k.type.*; import a8k.type.checkpoint.CheckResult; import a8k.type.consumables.ConsumablesScanReportErrorType; import a8k.type.ecode.AECodeError; @@ -32,6 +37,7 @@ import a8k.type.type.A8kTubeHolderType; import a8k.type.type.TipGroup; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; @@ -73,7 +79,18 @@ public class MainflowCtrlTestService { // Ctrl // @Resource - AppDeviceCtrlService appDeviceCtrlService; + AppDeviceCtrlService appDeviceCtrlService; + @Resource + MainFlowCtrlScheduler mainFlowCtrlScheduler; + @Resource + MotorEnableExDriver motorEnableExDriver; + @Resource + StepMotorCtrlDriver stepMotorCtrlDriver; + @Resource + OptScanModuleCtrlService optScanModuleCtrlService; + + @Resource + CondtionMgrService condtionMgrService; void resetProjDB() { @@ -142,12 +159,68 @@ public class MainflowCtrlTestService { testModeState.setEnableOptScan(enable); } + // + // 辅助工具 + // + @ExtApiFn(name = "失能整机", group = "调试工具", order = 200) + public void disableDevice() throws AppException { + + stepMotorCtrlDriver.stepMotorEnable(StepMotorMId.ShakeModClampingM, 1); + stepMotorCtrlDriver.stepMotorEasyMoveByBlock(StepMotorMId.ShakeModClampingM, 10, 3000); + + motorEnableExDriver.forceDisableAllMotor(); + } + + @ExtApiFn(name = "使能整机", group = "调试工具", order = 201) + public void enableDevice() throws AppException { + motorEnableExDriver.enableAllMotor(); + } + + // + // 测试配置 + // + @ExtApiFn(name = "读取设备状态", group = "设备控制", order = 401) + public DeviceWorkState readDeviceState() { + return gstate.getDeviceWorkState(); + } + + @ExtApiFn(name = "获取动作执行条件", group = "设备控制", order = 402) + synchronized public CondtionMgrService getCondtionMgrService() { + return condtionMgrService; + } + + + void waittingForStop() { + while (gstate.getDeviceWorkState().isPending()) { + log.info("等待设备停止工作...."); + OS.forceSleep(1000); + } + } + + @ExtApiFn(name = "停止", group = "设备控制", order = 403) + public void stopWork() { + appDeviceCtrlService.stopWork(); + waittingForStop(); + } + + @ExtApiFn(name = "暂停", group = "设备控制", order = 404) + public void pauseWork() { + appDeviceCtrlService.pauseWork(); + waittingForStop(); + } + + @ExtApiFn(name = "继续", group = "设备控制", order = 405) + public void continueWork() { + appDeviceCtrlService.continueWork(); + waittingForStop(); + } + // // 测试用例 // - @ExtApiFn(name = "设备初始化", order = 200) + @ExtApiFn(name = "设备初始化", order = 300) public Object deviceInit() throws AppException { List checkResults = appDeviceCtrlService.initDevice(); for (CheckResult checkResult : checkResults) { @@ -158,24 +231,31 @@ public class MainflowCtrlTestService { return checkResults; } + @ExtApiFn(name = "清空孵育盘", order = 301) + public void clearIncubatorPos(IncubatorPos incubatorPos) throws AppException { + optScanModuleCtrlService.pullPlate(incubatorPos); + optScanModuleCtrlService.dropPlate(); + } + + void stopAndWaittingForDeviceStop() { log.info("doSimpleTest"); //停止工作 if (!gstate.getDeviceWorkState().workState.equals(A8kWorkState.IDLE)) { appDeviceCtrlService.stopWork(); - while (gstate.getDeviceWorkState().isPending()) { - log.info("等待设备停止工作...."); - OS.forceSleep(1000); - } + waittingForStop(); } } - @ExtApiFn(name = "执行一次简单的测试(无试管测试)", order = 200) + @ExtApiFn(name = "执行一次简单的测试(无试管测试.流程1)", order = 400) public String doSimpleTest(Boolean isHTube, Integer tubeNum) throws AppException { if (tubeNum <= 0) { throw AppException.of("试管数量必须大于0"); } + gstate.setDeviceInited(true); + + stopAndWaittingForDeviceStop(); //清空项目的数据库配置 resetProjDB(); @@ -205,5 +285,42 @@ public class MainflowCtrlTestService { return "开始测试"; } + @ExtApiFn(name = "执行一次简单的测试(无试管测试.流程2)", order = 401) + public String doSimpleTest2(Boolean isHTube, Integer tubeNum) throws AppException { + if (tubeNum <= 0) { + throw AppException.of("试管数量必须大于0"); + } + + + gstate.setDeviceInited(true); + + stopAndWaittingForDeviceStop(); + //清空项目的数据库配置 + resetProjDB(); + + //设置虚拟耗材,只有一个高试管 + testModeState.setVirtualTubeScanResult(createScanResult(A8kTubeHolderType.BloodTube, isHTube, tubeNum)); + //无校验模式 + testModeState.setNoCheckMode(true); + //无光学检查 + testModeState.setEnableOptScan(true); + + + //添加项目信息 + addProjInfo(FAKE_PROJ_02.class); + //设置耗材状态,假定已经扫描过耗材 + LoadingConsumablesDirectly(ConsumableGroup.GROUP1, new FAKE_PROJ_02()); + //设置试管架扫描信息,全部设置成高试管 + appTubeSettingMgrService.removeAllTubeSetting(); + + for (int i = 0; i < 3; i++) { + TubeHolderSetting setting = createOneActiveTubeHolderSetting(A8kTubeHolderType.BloodTube, tubeNum, List.of(new FAKE_PROJ_02().projId)); + appTubeSettingMgrService.newTubeHolderSetting(setting); + } + + //开始工作 + appDeviceCtrlService.startWork(); + return "开始测试"; + } } diff --git a/src/main/java/a8k/service/test/fakeproj/FakeProjInfo.java b/src/main/java/a8k/service/test/fakeproj/FakeProjInfo.java index c3c842f..ae69ed1 100644 --- a/src/main/java/a8k/service/test/fakeproj/FakeProjInfo.java +++ b/src/main/java/a8k/service/test/fakeproj/FakeProjInfo.java @@ -25,10 +25,10 @@ public class FakeProjInfo { public Integer wBloodSampleVolUl = 100; public Integer serumSampleVolUl = 100; public Integer shakeTimes = 5; - public Integer bigBufferSampleUl = 200; - public Integer mixLiquidAspirMixingCnt = 3; + public Integer bigBufferSampleUl = 100; + public Integer mixLiquidAspirMixingCnt = 1; public Integer reactionPlateIncubationTimeMin = 1; - public Integer reactionPlateDropletVolUl = 100; + public Integer reactionPlateDropletVolUl = 50; public Boolean expired = false; FakeProjInfo(Integer projNum) { diff --git a/src/main/java/a8k/type/exception/AppException.java b/src/main/java/a8k/type/exception/AppException.java index ca10783..766db85 100644 --- a/src/main/java/a8k/type/exception/AppException.java +++ b/src/main/java/a8k/type/exception/AppException.java @@ -44,14 +44,14 @@ public class AppException extends Exception { return new AppException(new AECommonError(message)); } - - public void print() { -// printStackTrace(); - log.error("error {}",this.getMessage(), this); -// log.error("exception {}", this.toString()); -// for (StackTraceElement ste : this.getStackTrace()) { -// log.error(ste.toString()); -// } - } +// +// public void print() { +//// printStackTrace(); +// log.error("error {}",this.getMessage(), this); +//// log.error("exception {}", this.toString()); +//// for (StackTraceElement ste : this.getStackTrace()) { +//// log.error(ste.toString()); +//// } +// } } diff --git a/src/main/resources/app.db b/src/main/resources/app.db index 01b88f0392eacedb44b12248e01fc0a00946a3b7..fdf8837c5230ebbe7bcf69c90d478ea5f5c3c489 100644 GIT binary patch delta 3255 zcmeH~O>7%Q6o7Zfabkn*b^btUNRzBlgG4~HyR$zIrR2wP(%>{EjUfU>ZPK`4$yvLJ zt$^CdsrZplK{awD3+03W1r8jNK-^OcQZ50+fpV%))gF*4E~qC^6RFJXUy|K`gj8|r zO5>S%{@#1DGjHCr7jO9&Z~2Rd>h7MSC`!gn!tF3c)vbTj$%DJ+R!`q_`HE~cmQvKk zSh2nQgP;Bh6x%Ax0Xj)ny**lNtz504f2P+Ho-b=av2`t24XdtG%=Pu!_!h%FO*53Y zvDQmBx6=42-5z-YO!I4TchK#-M)|I-*LHv7d+NPobO+T;HMVqkDX#~3ytK#TA^hIQ zdnr-6nQ1B@Bq?9603sn{ z+XbJ6=9j~hZ?aus@))v{$=Uo2*6upb_OM+Xk@jkRp_A=11QDZ8XXaz6nN-fOn9mwB z%^o(mpn2&{XEM_VS^RGX3uKAdVs<{N4&|ql_`TG8I=w3xdt){^w^tbJ+s6#ZjzN}fs1r{`t4eB!Ez=b;?Az7Ig2>dpy`ZgL5G&+tk13`8CpU(ETgc<5nrXvT#!Y* zc2u-bNq~}sO5f%hSkz<3kcA0?pzsFPZN~)TC515Lc_=8Q_62P11t+Gzm5%XY-RUtq z#+%j@ob#Y8>DHciVum#hlVsi6Q9H(2)*vs3In7!-dd`WN)(8em-_1X^erYc2tH+U6 zH{i#gE@p*dZZn(A3eMgiGcC<7H`UTXyDCrWwWfx+9>B3&lg&R2b?Xj440RSg*ig^=R_Gud-^kL- z%YYZl>OF9l^rtU_hVt+Cz^h~u-3BSruWo}0(g%0Iw0hxjHtvATj4UEqk$F*H*@y!(GqQ+(4hkaF zu=W!UOfXl51iWowlFry+2gRF|Kz0L?^xnwE9GH0oB8LzZkwCD*iw+E$r4H8sMGQ*^ zl{#_g05zl5X==`Ltkt^X_I%xsOO0NSIYq!_0v+TH4cgVr^G8HWB>Pei_z4pE!HsUf z4giI(%#^|V)s?S41XsPr^!^kH_6Kq!j)cAj?zW1%+XaH|7M$CKvM=sB)6$8D&)~-5 VMrscE?@^0e4Q{T=2XR-f^>3(=E(-tv delta 470 zcmZp8z}xVEcY>7A!%qwh4CX*=2E+;s41%90>KHLS{IoIQ9y3qBFB?#lfuYf--)H)1 zUdD4w{odPW^D(9}vNQ+vdu`vx&UlG&b1273cBX!>O#*BNESDJgFKreKIKi){!^qFT zDJsaxC@s&($jHRW=;ml(=;kP??B?cd;Oyjt7U0T39P8ygr~m{^*d85!2^Y$)b#xforkSr7b5eEK;{6~P|JNP#r>2FD4!t1dg>HJLn zejsmNVPxv}-9Ckr(Ub+D#UBzD+Zh!Y|NCzjP+(*f*jB(ed-n7Ny38!w=W8>kiEL+8 zVE!+^oyCDk&Yj8JYrcSo)YkQgYaYDKOli