Browse Source

v2.0 |统一工程师模式下,暂停和停止操作和正常模式类似。

master
zhaohe 3 months ago
parent
commit
bb2144b749
  1. 0
      bak/20250429-1app.db
  2. BIN
      bak/20250429-2app.db
  3. BIN
      bak/20250429-3app.db
  4. 2
      src/main/java/a8k/app/constant/AppConstant.java
  5. 42
      src/main/java/a8k/app/engineer/service/executor/EngineerModeActionExecutor.java
  6. 43
      src/main/java/a8k/app/engineer/service/qatest/ExperimentConsistencyTestingService.java
  7. 2
      src/main/java/a8k/app/hardware/type/A8kEcode.java
  8. 2
      src/main/java/a8k/app/i18n/Internationalization.java
  9. 10
      src/main/java/a8k/app/service/lowerctrl/LiquidOperationCtrlService.java
  10. 31
      src/main/java/a8k/app/service/mainctrl/AppDeviceCtrlService.java
  11. 12
      src/main/java/a8k/app/service/mainctrl/mainflowctrl/MainFlowCtrlScheduler.java
  12. 20
      src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java
  13. 6
      src/main/java/a8k/app/type/JudgeFn.java
  14. 5
      src/main/java/a8k/app/type/a8k/state/DeviceWorkState.java
  15. 5
      src/main/java/a8k/app/type/exception/EngineerTaskBreakException.java
  16. 2
      src/main/resources/application.yml

0
bak/20250429-app.db → bak/20250429-1app.db

BIN
bak/20250429-2app.db

BIN
bak/20250429-3app.db

2
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;

42
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);
}
}

43
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<IncubationSubTank> 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<IncubationSubTank> 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();

2
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), //
//
// 参数错误

2
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();
};
}

10
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();
}

31
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);
}
}

12
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<ApplicationSta
synchronized public void startWork() throws AppException {
checkBeforeCall();
checkBeforeStartOrContinueWork();
deviceWorkStateMgrService.setStartActionPending(true);
deviceWorkStateMgrService.setStartActionPending(A8kWorkTaskType.MainFlowTask, true);
deviceWorkStateMgrService.clearConsumeNotEnoughErrorFlag();
}
@ -163,7 +164,6 @@ public class MainFlowCtrlScheduler implements ApplicationListener<ApplicationSta
synchronized public void continueWork() throws AppException {
checkBeforeCall();
checkBeforeStartOrContinueWork();
deviceWorkStateMgrService.setResumeActionPending(true);
deviceWorkStateMgrService.clearConsumeNotEnoughErrorFlag();
}
@ -297,14 +297,18 @@ public class MainFlowCtrlScheduler implements ApplicationListener<ApplicationSta
continue;
}
if (!deviceWorkStateMgrService.isMainFlowTaskRun()) {
OS.forceSleep(500);
continue;
}
switch (state.workState) {
case IDLE -> 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);
}

20
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);
}
}

6
src/main/java/a8k/app/type/JudgeFn.java

@ -0,0 +1,6 @@
package a8k.app.type;
@FunctionalInterface
public interface JudgeFn {
boolean judge();
}

5
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);
}
}

5
src/main/java/a8k/app/type/exception/EngineerTaskBreakException.java

@ -0,0 +1,5 @@
package a8k.app.type.exception;
public class EngineerTaskBreakException extends RuntimeException {
}

2
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

Loading…
Cancel
Save