diff --git a/bak/20250429-app.db b/bak/20250429-1app.db similarity index 100% rename from bak/20250429-app.db rename to bak/20250429-1app.db diff --git a/bak/20250429-2app.db b/bak/20250429-2app.db new file mode 100644 index 0000000..b9e8cc2 Binary files /dev/null and b/bak/20250429-2app.db differ diff --git a/bak/20250429-3app.db b/bak/20250429-3app.db new file mode 100644 index 0000000..d5409f9 Binary files /dev/null and b/bak/20250429-3app.db differ diff --git a/src/main/java/a8k/app/constant/AppConstant.java b/src/main/java/a8k/app/constant/AppConstant.java index 17d0736..e8471f1 100644 --- a/src/main/java/a8k/app/constant/AppConstant.java +++ b/src/main/java/a8k/app/constant/AppConstant.java @@ -6,7 +6,7 @@ public class AppConstant { public static final int CONSUMABLE_COL_NUM = 5; public static final int CONSUMABLE_ROW_NUM = 5; public static final int TIP_NUM = 120; - public static final String APP_VERSION = "1.3.3"; + public static final String APP_VERSION = "2.0"; public static final int CONSUMABLE_CHANNEL_NUM = 6; diff --git a/src/main/java/a8k/app/engineer/service/executor/EngineerModeActionExecutor.java b/src/main/java/a8k/app/engineer/service/executor/EngineerModeActionExecutor.java index 82d3343..d13f3b4 100644 --- a/src/main/java/a8k/app/engineer/service/executor/EngineerModeActionExecutor.java +++ b/src/main/java/a8k/app/engineer/service/executor/EngineerModeActionExecutor.java @@ -4,9 +4,13 @@ import a8k.OS; import a8k.app.engineer.service.state.EngineerModeStateMgrService; import a8k.app.engineer.service.type.A8kCmdRunnable; import a8k.app.engineer.service.type.EngineerWorkState; +import a8k.app.service.statemgr.DeviceWorkStateMgrService; import a8k.app.service.statemgr.GStateMgrService; import a8k.app.type.DeviceRunMode; +import a8k.app.type.a8k.state.enumtype.A8kWorkState; +import a8k.app.type.a8k.state.enumtype.A8kWorkTaskType; import a8k.app.type.exception.AppException; +import a8k.app.type.exception.EngineerTaskBreakException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -28,9 +32,6 @@ import java.util.concurrent.TimeUnit; @Slf4j @RequiredArgsConstructor public class EngineerModeActionExecutor { - static class BreakException extends RuntimeException { - - } @FunctionalInterface public interface Action { @@ -39,53 +40,66 @@ public class EngineerModeActionExecutor { private final EngineerModeStateMgrService stateMgr; private final GStateMgrService gStateMgrService; + private final DeviceWorkStateMgrService deviceWorkStateMgrService; Boolean stoppingFlag = false; + Boolean isWorking = false; ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1)); synchronized public void start(Action action) { - if (stateMgr.getState().equals(EngineerWorkState.WORKING)) { + if (isWorking) stoppingFlag = true; - } - while (stateMgr.getState().equals(EngineerWorkState.WORKING)) { + while (isWorking) { OS.hsleep(100); log.info("Waiting for the previous action to finish..."); } + stoppingFlag = false; + deviceWorkStateMgrService.setStartActionPending(A8kWorkTaskType.EngineerTask, true); executor.submit(() -> { try { + deviceWorkStateMgrService.clearPending(); + deviceWorkStateMgrService.updateWorkState(A8kWorkState.WORKING); stateMgr.setEngineerWorkState(EngineerWorkState.WORKING); + isWorking = true; action.run(); - } catch (BreakException e) { + } catch (EngineerTaskBreakException e) { log.info("action was interrupted"); } catch (Exception e) { log.error("Error in action execution", e); - } finally { - stateMgr.setEngineerWorkState(EngineerWorkState.IDLE); } + stateMgr.setEngineerWorkState(EngineerWorkState.IDLE); + deviceWorkStateMgrService.updateWorkState(A8kWorkState.IDLE); + isWorking = false; }); } synchronized public void stop() { log.info("Stopping action..."); stoppingFlag = true; - while (!stateMgr.getState().equals(EngineerWorkState.IDLE)) { - OS.hsleep(100); + deviceWorkStateMgrService.setStopActionPending(true); + while (isWorking) { + OS.hsleep(1000); log.info("Waiting for the action to stop..."); } + deviceWorkStateMgrService.clearPending(); stoppingFlag = false; } public void sleep(int ms) { if (stoppingFlag) { - throw new BreakException(); + throw new EngineerTaskBreakException(); } OS.hsleep(ms); } + public Boolean isStopping() { + return stoppingFlag; + } + public void docmd(String mark, A8kCmdRunnable runnable, A8kCmdRunnable virtualRunable) throws AppException { log.info("DO {}", mark); @@ -102,5 +116,9 @@ public class EngineerModeActionExecutor { }); } + public boolean isInRealMode() { + return gStateMgrService.isInMode(DeviceRunMode.RealMode); + } + } diff --git a/src/main/java/a8k/app/engineer/service/qatest/ExperimentConsistencyTestingService.java b/src/main/java/a8k/app/engineer/service/qatest/ExperimentConsistencyTestingService.java index 1160708..75b17bc 100644 --- a/src/main/java/a8k/app/engineer/service/qatest/ExperimentConsistencyTestingService.java +++ b/src/main/java/a8k/app/engineer/service/qatest/ExperimentConsistencyTestingService.java @@ -35,6 +35,7 @@ import a8k.app.type.a8k.state.SampleInfo; import a8k.app.type.a8k.state.Tube; import a8k.app.type.error.AppError; import a8k.app.type.exception.AppException; +import a8k.app.type.exception.EngineerTaskBreakException; import a8k.app.type.param.type.A8kSamplePos; import a8k.app.type.ui.TubeHolderSetting; import a8k.app.type.ui.TubeSetting; @@ -181,7 +182,9 @@ public class ExperimentConsistencyTestingService { } void docmd(String mark, A8kCmdRunnable runnable, A8kCmdRunnable virtualRunable) throws AppException { + actionExecutor.sleep(2); actionExecutor.docmd(mark, runnable, virtualRunable); + } void docmd(String mark, A8kCmdRunnable runnable) throws AppException { @@ -202,20 +205,26 @@ public class ExperimentConsistencyTestingService { public void processException(Exception e) { try { log.error("Catch exception: ", e); - UISender.txErrorPrompt(e); - docmd("丢tip", hbotMoveExCtrlService::dropTip); - docmd("HBOT复位", hbotMoveCtrlService::moveToZero); - docmd("推出试管架", tubeFeedingCtrlService::ejectTubeHolder); - //清空光学模组 - optScanModuleStateMgrService.changeOptScanModuleStateToEmpty(); - docmd("丢反应板", optScanModuleCtrlService::dropPlate); - - //清空孵育盘 - List all = incubationPlateStateMgrService.getAllNotFreeIncubationSubTanks(); - for (IncubationSubTank notFreeTank : all) { - docmd("拉取反应板", () -> optScanModuleCtrlService.pullPlate(notFreeTank.pos)); - docmd("丢反应板", optScanModuleCtrlService::dropPlate); + if (!(e instanceof EngineerTaskBreakException)) { + UISender.txErrorPrompt(e); } + // + // Action + // + if (actionExecutor.isInRealMode()) { + optScanModuleCtrlService.dropPlate(); + hbotMoveExCtrlService.dropTip(); + tubeFeedingCtrlService.ejectTubeHolder(); + List all = incubationPlateStateMgrService.getAllNotFreeIncubationSubTanks(); + for (IncubationSubTank notFreeTank : all) { + optScanModuleCtrlService.pullPlate(notFreeTank.pos); + optScanModuleCtrlService.dropPlate(); + } + } + // + // State + // + optScanModuleStateMgrService.changeOptScanModuleStateToEmpty(); incubationPlateStateMgrService.resetAll(); } catch (AppException ex) { log.error("Catch exception: ", e); @@ -391,7 +400,8 @@ public class ExperimentConsistencyTestingService { liquidOperationCtrlService.setProjContext(projBuildinInfo, projExtInfoCard); var preProcessPos = new PreReactionPos(ConsumableType.SmallBottleBuffer, consumableInfo.group, consumableInfo.pos); //取tip - liquidOperationCtrlService.forceTakeTip(); + liquidOperationCtrlService.forceTakeTip(() -> !actionExecutor.isStopping()); + actionExecutor.sleep(1); //刺破缓冲液 hbotMoveExCtrlService.moveToLittleBufferPiercePos(consumableInfo.group, consumableInfo.pos); //取样品 @@ -400,11 +410,12 @@ public class ExperimentConsistencyTestingService { Assert.isTrue(!samplePos.equals(A8kSamplePos.BloodSTubePos), "samplePos cannot be BloodSTubePos"); hbotMoveExCtrlService.moveToSamplePosXY(samplePos); liquidOperationCtrlService.takeSample(samplePos, preProcessPos, takeSampleUl); + actionExecutor.sleep(1); //取混合液到预反应位 liquidOperationCtrlService.takePreReactionLiquidToReation(preProcessPos); + actionExecutor.sleep(1); incubationPlateStateMgrService.startIncubating(freeIncubationPos, System.currentTimeMillis(), projBuildinInfo.reactionPlateIncubationTimeMin * 60); - - + actionExecutor.sleep(1); } catch (AppException e) { if (e.getError().eq(A8kEcode.APPE_TAKE_SAMPLE_FAIL)) { hbotMoveExCtrlService.moveQuickToZero(); diff --git a/src/main/java/a8k/app/hardware/type/A8kEcode.java b/src/main/java/a8k/app/hardware/type/A8kEcode.java index 589f6cc..5170a2c 100644 --- a/src/main/java/a8k/app/hardware/type/A8kEcode.java +++ b/src/main/java/a8k/app/hardware/type/A8kEcode.java @@ -29,6 +29,8 @@ public enum A8kEcode { DEVICE_IS_BUSY(17), //设备正在工作中,不允许操作 SYS_EXCEPTION(18), //系统异常 DEVICE_NOT_INIT(19), //设备未初始化 + APPE_PAUSE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK(20), // + APPE_CONTINUE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK(21), // // // 参数错误 diff --git a/src/main/java/a8k/app/i18n/Internationalization.java b/src/main/java/a8k/app/i18n/Internationalization.java index ba20272..0a1cbbc 100644 --- a/src/main/java/a8k/app/i18n/Internationalization.java +++ b/src/main/java/a8k/app/i18n/Internationalization.java @@ -72,6 +72,8 @@ public class Internationalization { case DEVICE_IS_BUSY -> "设备忙"; case SYS_EXCEPTION -> "系统异常"; case DEVICE_NOT_INIT -> "设备未初始化"; + case APPE_PAUSE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK -> "工程师任务,不支持暂停操作"; + case APPE_CONTINUE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK -> "工程师任务,不支持继续操作"; default -> ecode.toString(); }; } diff --git a/src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrlService.java b/src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrlService.java index 0cca8ab..41671c7 100644 --- a/src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrlService.java +++ b/src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrlService.java @@ -15,6 +15,7 @@ import a8k.app.service.statemgr.ConsumablesMgrService; import a8k.app.service.statemgr.GStateMgrService; import a8k.app.service.utils.ZAppChecker; import a8k.app.type.DeviceRunMode; +import a8k.app.type.JudgeFn; import a8k.app.type.a8k.ConsumableType; import a8k.app.type.a8k.Pos3d; import a8k.app.type.a8k.pos.LargeBufferPos; @@ -108,7 +109,7 @@ public class LiquidOperationCtrlService { hbotMoveExCtrlService.takeTip(tipPos); } - public void forceTakeTip() throws AppException { + public void forceTakeTip(JudgeFn judgeFn) throws AppException { if (hbotMoveExCtrlService.isHasTip()) { return; } @@ -121,9 +122,16 @@ public class LiquidOperationCtrlService { if (hbotMoveExCtrlService.isHasTip()) { break; } + if (judgeFn != null && !judgeFn.judge()) { + break; + } } } + public void forceTakeTip() throws AppException { + forceTakeTip(null); + } + public void dropTip() throws AppException { hbotMoveExCtrlService.dropTip(); } diff --git a/src/main/java/a8k/app/service/mainctrl/AppDeviceCtrlService.java b/src/main/java/a8k/app/service/mainctrl/AppDeviceCtrlService.java index 8e7cfcf..3c125d8 100644 --- a/src/main/java/a8k/app/service/mainctrl/AppDeviceCtrlService.java +++ b/src/main/java/a8k/app/service/mainctrl/AppDeviceCtrlService.java @@ -1,6 +1,9 @@ package a8k.app.service.mainctrl; +import a8k.app.engineer.service.qatest.EngineerModeActionCtrlService; +import a8k.app.hardware.type.A8kEcode; import a8k.app.service.mainctrl.mainflowctrl.MainFlowCtrlScheduler; +import a8k.app.service.statemgr.DeviceWorkStateMgrService; import a8k.app.service.statemgr.GStateMgrService; import a8k.app.type.exception.AppException; import jakarta.annotation.Resource; @@ -15,7 +18,13 @@ public class AppDeviceCtrlService { GStateMgrService gstate; @Resource - MainFlowCtrlScheduler mainFlowCtrlScheduler; //主流程控制模块 + MainFlowCtrlScheduler mainFlowCtrlScheduler; //主流程控制模块 + + @Resource + EngineerModeActionCtrlService engineerModeActionCtrlService; + + @Resource + DeviceWorkStateMgrService deviceWorkStateMgrService; /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * 设备控制 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ @@ -28,19 +37,33 @@ public class AppDeviceCtrlService { public void stopWork() throws AppException { log.info("停止工作"); - mainFlowCtrlScheduler.stopWork(); + if (deviceWorkStateMgrService.isMainFlowTaskRun()) { + mainFlowCtrlScheduler.stopWork(); + } else if (deviceWorkStateMgrService.isEngineerTaskRun()) { + engineerModeActionCtrlService.stop(); + } else { + log.warn("Unknown take run state {}", deviceWorkStateMgrService.getDeviceWorkState()); + } } public void pauseWork() throws AppException { log.info("暂停工作"); - mainFlowCtrlScheduler.pauseWork(); + if (deviceWorkStateMgrService.isMainFlowTaskRun()) { + mainFlowCtrlScheduler.pauseWork(); + } else if (deviceWorkStateMgrService.isEngineerTaskRun()) { + throw AppException.of(A8kEcode.APPE_PAUSE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK); + } } public void continueWork() throws AppException { log.info("继续工作"); - mainFlowCtrlScheduler.continueWork(); + if (deviceWorkStateMgrService.isMainFlowTaskRun()) { + mainFlowCtrlScheduler.continueWork(); + } else if (deviceWorkStateMgrService.isEngineerTaskRun()) { + throw AppException.of(A8kEcode.APPE_CONTINUE_OPERATION_NOT_SUPPORT_IN_ENGINEER_TASK); + } } diff --git a/src/main/java/a8k/app/service/mainctrl/mainflowctrl/MainFlowCtrlScheduler.java b/src/main/java/a8k/app/service/mainctrl/mainflowctrl/MainFlowCtrlScheduler.java index facaf2c..8ba4f5f 100644 --- a/src/main/java/a8k/app/service/mainctrl/mainflowctrl/MainFlowCtrlScheduler.java +++ b/src/main/java/a8k/app/service/mainctrl/mainflowctrl/MainFlowCtrlScheduler.java @@ -9,6 +9,7 @@ import a8k.app.type.DeviceRunMode; //import a8k.app.a8ktype.appevent.A8kErrorsPromptEvent; import a8k.app.type.a8k.pos.TipGroupPos; import a8k.app.type.a8k.pos.TipPos; +import a8k.app.type.a8k.state.enumtype.A8kWorkTaskType; import a8k.app.type.error.AECodeError; import a8k.app.type.error.AppError; import a8k.app.type.exception.AppException; @@ -144,7 +145,7 @@ public class MainFlowCtrlScheduler implements ApplicationListener onIDLE(); case WORKING -> onWorking(); case PAUSE -> onPause(); } - onPostProcessing(); - OS.forceSleep(500); + OS.forceSleep(100); + log.debug("workFn running ..."); } catch (Exception e) { log.error("workFn error {}", e.getMessage(), e); } diff --git a/src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java b/src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java index c3167c4..b9280e5 100644 --- a/src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java +++ b/src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java @@ -3,6 +3,7 @@ package a8k.app.service.statemgr; import a8k.app.service.mainctrl.mainflowctrl.base.AppActionErrorContext; import a8k.app.type.a8k.state.DeviceWorkState; import a8k.app.type.a8k.state.enumtype.A8kWorkState; +import a8k.app.type.a8k.state.enumtype.A8kWorkTaskType; import a8k.app.type.error.AppError; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -56,8 +57,12 @@ public class DeviceWorkStateMgrService { deviceWorkState.resumeActionPending = resumeActionPending; } - synchronized public void setStartActionPending(Boolean startActionPending) { - deviceWorkState.startActionPending = startActionPending; + synchronized public void setStartActionPending(A8kWorkTaskType workTaskType, Boolean startActionPending) { + deviceWorkState.startActionPending = startActionPending; + deviceWorkState.workTaskType = workTaskType; + deviceWorkState.stopActionPending = false; + deviceWorkState.pauseActionPending = false; + deviceWorkState.resumeActionPending = false; } @@ -95,7 +100,6 @@ public class DeviceWorkStateMgrService { } - // //GETTER // @@ -119,4 +123,14 @@ public class DeviceWorkStateMgrService { return deviceWorkState.tipNotEnoughErrorFlag; } + synchronized public Boolean isMainFlowTaskRun() { + return deviceWorkState.workTaskType.equals(A8kWorkTaskType.MainFlowTask); + } + + synchronized public Boolean isEngineerTaskRun() { + return deviceWorkState.workTaskType.equals(A8kWorkTaskType.EngineerTask); + } + + + } diff --git a/src/main/java/a8k/app/type/JudgeFn.java b/src/main/java/a8k/app/type/JudgeFn.java new file mode 100644 index 0000000..a82b4e6 --- /dev/null +++ b/src/main/java/a8k/app/type/JudgeFn.java @@ -0,0 +1,6 @@ +package a8k.app.type; + +@FunctionalInterface +public interface JudgeFn { + boolean judge(); +} diff --git a/src/main/java/a8k/app/type/a8k/state/DeviceWorkState.java b/src/main/java/a8k/app/type/a8k/state/DeviceWorkState.java index d6b7ba7..360d7ca 100644 --- a/src/main/java/a8k/app/type/a8k/state/DeviceWorkState.java +++ b/src/main/java/a8k/app/type/a8k/state/DeviceWorkState.java @@ -3,6 +3,7 @@ package a8k.app.type.a8k.state; import a8k.app.service.mainctrl.mainflowctrl.base.AppActionErrorContext; import a8k.app.type.a8k.state.enumtype.A8kWorkState; import a8k.app.type.a8k.state.enumtype.A8kWorkTaskType; +import a8k.app.utils.ZJsonHelper; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -66,5 +67,9 @@ public class DeviceWorkState implements Serializable { resumeActionPending = false; startActionPending = false; } + + public String toString() { + return ZJsonHelper.objectToJson(this); + } } diff --git a/src/main/java/a8k/app/type/exception/EngineerTaskBreakException.java b/src/main/java/a8k/app/type/exception/EngineerTaskBreakException.java new file mode 100644 index 0000000..d7488e8 --- /dev/null +++ b/src/main/java/a8k/app/type/exception/EngineerTaskBreakException.java @@ -0,0 +1,5 @@ +package a8k.app.type.exception; + +public class EngineerTaskBreakException extends RuntimeException { + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 57f4a44..5ba34b5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,7 +8,7 @@ device.enableCanBus: true iflytophald: # ip: 192.168.8.10 - # ip: 192.168.8.10 +# ip: 192.168.8.10 ip: 127.0.0.1 cmdch.port: 19004 datach.port: 19005