diff --git a/src/main/java/a8k/app/config/WebConfig.java b/src/main/java/a8k/app/config/WebConfig.java new file mode 100644 index 0000000..f59c752 --- /dev/null +++ b/src/main/java/a8k/app/config/WebConfig.java @@ -0,0 +1,20 @@ +package a8k.app.config; + +import a8k.app.interceptor.RefreshAccessInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + private final RefreshAccessInterceptor refreshInterceptor; + + public WebConfig(RefreshAccessInterceptor refreshInterceptor) { + this.refreshInterceptor = refreshInterceptor; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(refreshInterceptor).addPathPatterns("/api/**"); + } +} \ No newline at end of file diff --git a/src/main/java/a8k/app/controler/api/v1/app/ctrl/DeviceInitControler.java b/src/main/java/a8k/app/controler/api/v1/app/ctrl/DeviceInitControler.java index d17fe71..51132d8 100644 --- a/src/main/java/a8k/app/controler/api/v1/app/ctrl/DeviceInitControler.java +++ b/src/main/java/a8k/app/controler/api/v1/app/ctrl/DeviceInitControler.java @@ -2,11 +2,11 @@ package a8k.app.controler.api.v1.app.ctrl; import a8k.app.service.lowerctrl.DeviceMoveToZeroCtrlService; import a8k.app.service.mainctrl.DeviceInitService; -import a8k.app.type.ui.ApiRet; +import a8k.app.service.statemgr.GStateMgrService; import a8k.app.type.exception.AppException; +import a8k.app.type.ui.ApiRet; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; @@ -21,19 +21,27 @@ import org.springframework.web.bind.annotation.ResponseBody; @ResponseBody @RequiredArgsConstructor public class DeviceInitControler { - private final DeviceInitService deviceInitService; + private final DeviceInitService deviceInitService; + private final GStateMgrService gStateMgrService; + + @Operation(description = "初始化设备") + @PostMapping("/initDevice") + public ApiRet initDevice() throws AppException { + deviceInitService.initDeviceAsync(); + return ApiRet.success(); + } + - @Operation(description = "初始化设备") - @PostMapping("/initDevice") - public ApiRet initDevice() throws AppException { - deviceInitService.initDeviceAsync(); - return ApiRet.success(); - } + @Operation(description = "获取设备初始化状态") + @PostMapping("/getDeviceInitedTaskState") + public ApiRet getDeviceInitedTaskState() { + return ApiRet.success(deviceInitService.getDeviceInitedTaskState()); + } + @Operation(description = "获取设备是否完成启动") + @PostMapping("/isBoardParamInited") + public ApiRet isBoardParamInited() { + return ApiRet.success(gStateMgrService.getBoardParamInited()); + } - @Operation(description = "获取设备初始化状态") - @PostMapping("/getDeviceInitedTaskState") - public ApiRet getDeviceInitedTaskState() { - return ApiRet.success(deviceInitService.getDeviceInitedTaskState()); - } } diff --git a/src/main/java/a8k/app/controler/api/v1/app/setting/DeviceSettingControler.java b/src/main/java/a8k/app/controler/api/v1/app/setting/DeviceSettingControler.java index efa1c0a..c0f1b39 100644 --- a/src/main/java/a8k/app/controler/api/v1/app/setting/DeviceSettingControler.java +++ b/src/main/java/a8k/app/controler/api/v1/app/setting/DeviceSettingControler.java @@ -1,5 +1,6 @@ package a8k.app.controler.api.v1.app.setting; +import a8k.app.service.data.AppUserMgrService; import a8k.app.type.exception.AppException; import a8k.app.type.ui.ApiRet; import a8k.app.dao.type.db.DeviceSetting; diff --git a/src/main/java/a8k/app/controler/api/v1/app/ws/AppWebSocketEndpointMgr.java b/src/main/java/a8k/app/controler/api/v1/app/ws/AppWebSocketEndpointMgr.java index 1653111..7270323 100644 --- a/src/main/java/a8k/app/controler/api/v1/app/ws/AppWebSocketEndpointMgr.java +++ b/src/main/java/a8k/app/controler/api/v1/app/ws/AppWebSocketEndpointMgr.java @@ -156,7 +156,6 @@ public class AppWebSocketEndpointMgr { reportState("TubeHolderSetting", tubeHolderSettingMgrService.getTubeHolderSettings()); } - @Scheduled(fixedDelay = 200) public void reportDeviceState() { reportState("DeviceWorkState", deviceWorkStateMgrService.getDeviceWorkState()); diff --git a/src/main/java/a8k/app/factory/ZAppPromoptFactory.java b/src/main/java/a8k/app/factory/ZAppPromoptFactory.java index 0e30f90..10f0ea3 100644 --- a/src/main/java/a8k/app/factory/ZAppPromoptFactory.java +++ b/src/main/java/a8k/app/factory/ZAppPromoptFactory.java @@ -66,6 +66,7 @@ public class ZAppPromoptFactory { } else { vals[2] = new ZAppPromoptTable.Val(MessageLevel.Info, consumableStatInfo.requireCnt.toString()); } + table.addVar(vals); } return new ZAppPromopt(MessageLevel.Error, "耗材不足", null, table); } else if (error instanceof AETubeError appError) { diff --git a/src/main/java/a8k/app/hardware/type/A8kEcode.java b/src/main/java/a8k/app/hardware/type/A8kEcode.java index 2f73770..b4bc6f0 100644 --- a/src/main/java/a8k/app/hardware/type/A8kEcode.java +++ b/src/main/java/a8k/app/hardware/type/A8kEcode.java @@ -61,6 +61,7 @@ public enum A8kEcode { APPE_DO_ACTION_FAIL_DEVICE_IS_WORKING(152),//设备正在工作中,不允许执行操作 APPE_CONSUMABLES_IS_IN_USE_NOT_ALLOW_UNSTALL(153),//耗材正在使用中,不允许卸载 + DEVICE_STARTUP_IN_PROGRESS(154), // 设备启动中,请稍后 // // 业务流程中的错误 // 1. 点击开始运行后,设备在运行过程中出现的错误 diff --git a/src/main/java/a8k/app/i18n/Internationalization.java b/src/main/java/a8k/app/i18n/Internationalization.java index b589f51..bef1cfe 100644 --- a/src/main/java/a8k/app/i18n/Internationalization.java +++ b/src/main/java/a8k/app/i18n/Internationalization.java @@ -18,7 +18,7 @@ public class Internationalization { case APPE_TUBE_HOLDER_SETTING_IS_LOCKED -> "试管架设置被锁定,已锁定的."; case APPE_EMERGENCY_SAMPLE_IS_PROCESSING -> "添加急诊样本失败,急诊样本还没有处理完成"; case APPE_ADD_EMERGENCY_ACTION_IS_NOT_ALLOWED_WHEN_WORKING -> "添加急诊样本失败,设备正在运行中"; - case APPE_DEVICE_IS_IN_FATAL_ERROR -> "设备处于严重错误,请重新初始化后再启动"; + case APPE_DEVICE_IS_IN_FATAL_ERROR -> "设备处于严重错误,请关闭电源重启设备"; case APPE_SCAN_TUBEHOLDER_TYPE_TIMEOUT -> "扫描试管架类型超时"; case APPE_TUBE_HOLDER_TYPE_IS_NOT_SUPPORT -> "试管架类型不支持"; case APPE_INFEED_OVERTIME_FAIL -> "入料超时失败"; @@ -66,6 +66,7 @@ public class Internationalization { case IP_FORMAT_ERROR -> "IP格式错误"; case SHUTDOWN_ERROR_DEVICE_IS_WORKING -> "设备正在工作中,不允许关机"; case LOGIN_OUT_ERROR_DEVICE_IS_WORKING -> "设备正在工作中,不允许注销"; + case DEVICE_STARTUP_IN_PROGRESS -> "设备启动中,请稍后"; default -> ecode.toString(); }; } diff --git a/src/main/java/a8k/app/interceptor/RefreshAccessInterceptor.java b/src/main/java/a8k/app/interceptor/RefreshAccessInterceptor.java new file mode 100644 index 0000000..9843872 --- /dev/null +++ b/src/main/java/a8k/app/interceptor/RefreshAccessInterceptor.java @@ -0,0 +1,27 @@ +package a8k.app.interceptor; + +import a8k.app.service.data.AppUserMgrService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * Interceptor that refreshes the last-access timestamp on each HTTP request. + */ +@Component +public class RefreshAccessInterceptor implements HandlerInterceptor { + + private final AppUserMgrService userMgr; + + public RefreshAccessInterceptor(AppUserMgrService userMgr) { + this.userMgr = userMgr; + } + + @Override + public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) { + userMgr.recordAccess(); + return true; + } +} diff --git a/src/main/java/a8k/app/service/data/AppUserMgrService.java b/src/main/java/a8k/app/service/data/AppUserMgrService.java index 6d09445..10a6c0d 100644 --- a/src/main/java/a8k/app/service/data/AppUserMgrService.java +++ b/src/main/java/a8k/app/service/data/AppUserMgrService.java @@ -1,123 +1,193 @@ package a8k.app.service.data; import a8k.app.dao.AppUsrDao; +import a8k.app.dao.DeviceSettingDao; import a8k.app.dao.type.db.AppUser; +import a8k.app.dao.type.db.DeviceSetting; import a8k.app.hardware.type.A8kEcode; import a8k.app.type.exception.AppException; +import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import org.slf4j.Logger; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.time.Duration; +import java.time.Instant; import java.util.List; @Component +@EnableScheduling //Enable scheduled tasks public class AppUserMgrService { - static Logger logger = org.slf4j.LoggerFactory.getLogger(AppUserMgrService.class); - - @Resource - AppUsrDao appUsrDao; - - AppUser loginUsr = null; - - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // EXT FUNC - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - - public AppUser login(int id, String password) throws AppException { - var usr = appUsrDao.getUsrById(id); - if (usr == null) - throw new AppException(A8kEcode.USR_NOT_EXIT); - - if (!usr.password.equals(password)) { - throw new AppException(A8kEcode.USR_PASSWORD_ERROR); - } - loginUsr = usr; - return (usr); - } - - - public Void unlogin() { - loginUsr = null; - return null; - } - - - synchronized public AppUser getLoginUsr() { - return (loginUsr); - } - - - public List getUsrlist() { - return (appUsrDao.getAllUsr()); - } - - - public AppUser addUser(String account, String password, AppUser.UsrRole type) throws AppException { - var user = appUsrDao.getUsrByAccount(account); - if (user != null) { - throw new AppException(A8kEcode.USR_ALREADY_EXIST); - } - user = new AppUser(account, password, type); - appUsrDao.addUser(user); - return appUsrDao.getUsrByAccount(account); - } - - - public Void delUser(Integer id) { - appUsrDao.deleteUserById(id); - return null; - } - - public AppUser updateUsr(AppUser usr) { - appUsrDao.updateUser(usr); - return usr; - } - - public AppUser modifyUsrPwd(Integer id, String oldpasswd,String password) throws AppException { - var user = appUsrDao.getUsrById(id); - if (user == null) { - throw new AppException(A8kEcode.USR_NOT_EXIT); - } - - if(!user.password.equals(oldpasswd)){ - throw new AppException(A8kEcode.USR_OLD_PASSWORD_ERROR); - } - - if (password.isEmpty()) { - throw new AppException(A8kEcode.USR_PASSWORD_IS_EMPTY); - } - - user.password = password; - appUsrDao.updateUser(user); - return user; - } - - - public AppUser modifyUsrRole(Integer id, AppUser.UsrRole usrRole) throws AppException { - var user = appUsrDao.getUsrById(id); - if (user == null) { - throw new AppException(A8kEcode.USR_NOT_EXIT); - } - user.usrRole = usrRole; - appUsrDao.updateUser(user); - return user; - - } - - - public AppUser modifyUsrAccount(Integer id, String newaccount) throws AppException { - var user = appUsrDao.getUsrById(id); - if (user == null) { - throw new AppException(A8kEcode.USR_NOT_EXIT); - } - user.account = newaccount; - appUsrDao.updateUser(user); - return user; - - } - + static Logger logger = org.slf4j.LoggerFactory.getLogger(AppUserMgrService.class); + + /** + * current user + */ + @Resource + AppUsrDao appUsrDao; + @Resource + DeviceSettingDao deviceSettingDao; + + AppUser loginUsr = null; + + /** + * timestamp of last access + */ + private volatile Instant lastAccess; + + /** + * auto logout time out duration + */ + private volatile Duration autoLogoutTimeout; + + /** + * flag to enable auto logout + */ + private volatile boolean autoLogout; + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // EXT FUNC + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + /** + * load timeout and switch + */ + @PostConstruct + public void init() { + DeviceSetting deviceSetting = deviceSettingDao.get(); + int minutes = deviceSetting.getAutoLogoutTimeout(); + this.autoLogoutTimeout = Duration.ofMinutes(minutes); + boolean autoLogout = deviceSetting.getAutoLogout(); + this.autoLogout = autoLogout; + logger.debug("initialized autoLogout={}, timeout={} minutes", autoLogout, minutes); + } + + + public AppUser login(int id, String password) throws AppException { + var usr = appUsrDao.getUsrById(id); + if (usr == null) + throw new AppException(A8kEcode.USR_NOT_EXIT); + + if (!usr.password.equals(password)) { + throw new AppException(A8kEcode.USR_PASSWORD_ERROR); + } + loginUsr = usr; + return (usr); + } + + + public Void unlogin() { + loginUsr = null; + return null; + } + + + /** + * refresh lastAccess on each intercepted request + */ + public synchronized void recordAccess() { + if (loginUsr != null) { + this.lastAccess = Instant.now(); + } + } + + public synchronized void updateAutoLogout(boolean autoLogout) { + this.autoLogout = autoLogout; + logger.debug("updated cache autoLogout:{}", autoLogout); + } + + public synchronized void updateAutoLogoutTimeout(int autoLogoutTimeout) { + this.autoLogoutTimeout = Duration.ofMinutes(autoLogoutTimeout); + logger.debug("updated cache autoLogoutTimeout {} minutes", autoLogoutTimeout); + } + + + synchronized public AppUser getLoginUsr() { + return (loginUsr); + } + + + public List getUsrlist() { + return (appUsrDao.getAllUsr()); + } + + + public AppUser addUser(String account, String password, AppUser.UsrRole type) throws AppException { + var user = appUsrDao.getUsrByAccount(account); + if (user != null) { + throw new AppException(A8kEcode.USR_ALREADY_EXIST); + } + user = new AppUser(account, password, type); + appUsrDao.addUser(user); + return appUsrDao.getUsrByAccount(account); + } + + + public Void delUser(Integer id) { + appUsrDao.deleteUserById(id); + return null; + } + + public AppUser updateUsr(AppUser usr) { + appUsrDao.updateUser(usr); + return usr; + } + + public AppUser modifyUsrPwd(Integer id, String oldpasswd, String password) throws AppException { + var user = appUsrDao.getUsrById(id); + if (user == null) { + throw new AppException(A8kEcode.USR_NOT_EXIT); + } + + if (!user.password.equals(oldpasswd)) { + throw new AppException(A8kEcode.USR_OLD_PASSWORD_ERROR); + } + + if (password.isEmpty()) { + throw new AppException(A8kEcode.USR_PASSWORD_IS_EMPTY); + } + + user.password = password; + appUsrDao.updateUser(user); + return user; + } + + + public AppUser modifyUsrRole(Integer id, AppUser.UsrRole usrRole) throws AppException { + var user = appUsrDao.getUsrById(id); + if (user == null) { + throw new AppException(A8kEcode.USR_NOT_EXIT); + } + user.usrRole = usrRole; + appUsrDao.updateUser(user); + return user; + + } + + + public AppUser modifyUsrAccount(Integer id, String newaccount) throws AppException { + var user = appUsrDao.getUsrById(id); + if (user == null) { + throw new AppException(A8kEcode.USR_NOT_EXIT); + } + user.account = newaccount; + appUsrDao.updateUser(user); + return user; + + } + + @Scheduled(fixedDelay = 60_000) + public void autoLogoutTask() { + if (!autoLogout || loginUsr == null) { + return; + } + if (Duration.between(lastAccess, Instant.now()).compareTo(autoLogoutTimeout) >= 0) { + logger.info("auto logout user {} after {} minutes inactivity", loginUsr.account, autoLogoutTimeout.toMinutes()); + unlogin(); + } + } } diff --git a/src/main/java/a8k/app/service/lowerctrl/DeviceMoveToZeroCtrlService.java b/src/main/java/a8k/app/service/lowerctrl/DeviceMoveToZeroCtrlService.java index 73229fe..33221b7 100644 --- a/src/main/java/a8k/app/service/lowerctrl/DeviceMoveToZeroCtrlService.java +++ b/src/main/java/a8k/app/service/lowerctrl/DeviceMoveToZeroCtrlService.java @@ -273,7 +273,9 @@ public class DeviceMoveToZeroCtrlService { if (workThread != null && workThread.isAlive()) { throw AppException.ofAECodeError("设备正在初始化中"); } - + if(!gstate.getBoardParamInited()){ + throw AppException.of(A8kEcode.DEVICE_STARTUP_IN_PROGRESS); + } if (workThread != null) { try { workThread.join(); diff --git a/src/main/java/a8k/app/service/mainctrl/mainflowctrl/action/AC30ScanTubeHolderTask.java b/src/main/java/a8k/app/service/mainctrl/mainflowctrl/action/AC30ScanTubeHolderTask.java index 01b5507..c195b52 100644 --- a/src/main/java/a8k/app/service/mainctrl/mainflowctrl/action/AC30ScanTubeHolderTask.java +++ b/src/main/java/a8k/app/service/mainctrl/mainflowctrl/action/AC30ScanTubeHolderTask.java @@ -91,9 +91,7 @@ public class AC30ScanTubeHolderTask extends A8kActionTask { } public Boolean isReady() { - return tubeStateMgrService.isTubeHolderProcessCompleted() && - getTubeholderEnterPosPPS() && - !deviceWorkStateMgrService.getConsumeNotEnoughErrorFlag(); + return tubeStateMgrService.isTubeHolderProcessCompleted() && getTubeholderEnterPosPPS(); } public String toDisplayString() { @@ -153,7 +151,6 @@ public class AC30ScanTubeHolderTask extends A8kActionTask { log.info("试管架配置 {}", setting); TubeHolder tubeholder = new TubeHolder(); - Assert.isTrue(setting == null || setting.tubeSettings.length == tubeholder.getTubes().length, "试管架配置信息与试管数量不匹配"); //获取试管架类型 A8kTubeHolderType tubeHolderType = A8kTubeHolderType.of(scanResult.tubeHolderType); diff --git a/src/main/java/a8k/app/service/setting/AppSettingsMgrService.java b/src/main/java/a8k/app/service/setting/AppSettingsMgrService.java index 8f03735..af64911 100644 --- a/src/main/java/a8k/app/service/setting/AppSettingsMgrService.java +++ b/src/main/java/a8k/app/service/setting/AppSettingsMgrService.java @@ -1,5 +1,6 @@ package a8k.app.service.setting; +import a8k.app.service.data.AppUserMgrService; import a8k.app.type.exception.AppException; import a8k.app.dao.DeviceSettingDao; import a8k.app.dao.LISSettingDao; @@ -16,6 +17,8 @@ import org.springframework.stereotype.Component; @Component public class AppSettingsMgrService { Logger logger = org.slf4j.LoggerFactory.getLogger(AppSettingsMgrService.class); + @Resource + AppUserMgrService appUserMgrService; @Resource DeviceSettingDao deviceSettingDao; @@ -61,12 +64,14 @@ public class AppSettingsMgrService { DeviceSetting setting = deviceSettingDao.get(); setting.autoLogoutTimeout = val; deviceSettingDao.update(setting); + appUserMgrService.updateAutoLogoutTimeout(val); } public void setAutoLogout(Boolean val) { DeviceSetting setting = deviceSettingDao.get(); setting.autoLogout = val; deviceSettingDao.update(setting); + appUserMgrService.updateAutoLogout(val); } diff --git a/src/main/java/a8k/app/service/statemgr/TubeStateMgrService.java b/src/main/java/a8k/app/service/statemgr/TubeStateMgrService.java index 5d5aa57..78d8196 100644 --- a/src/main/java/a8k/app/service/statemgr/TubeStateMgrService.java +++ b/src/main/java/a8k/app/service/statemgr/TubeStateMgrService.java @@ -124,8 +124,8 @@ public class TubeStateMgrService { //试管状态管理 // public synchronized AppError newTubeHolder(TubeHolder tubeHolder) throws AppException { - this.tubeHolder = tubeHolder; - var tubes = tubeHolder.getTubes(); +// this.tubeHolder = tubeHolder; +// var tubes = tubeHolder.getTubes(); List statInfos = consumableStateAnalyzerService.analyze(tubeHolder); @@ -140,7 +140,8 @@ public class TubeStateMgrService { return new AppError(A8kEcode.APPE_TIP_NOT_ENOUGH); } - + this.tubeHolder = tubeHolder; + var tubes = tubeHolder.getTubes(); for (Tube tube : tubes) { if (tube.getState().equals(TubeState.EMPTY)) { continue; diff --git a/src/main/java/a8k/app/type/error/AEConsumeNotEnoughError.java b/src/main/java/a8k/app/type/error/AEConsumeNotEnoughError.java index c182c93..ab44899 100644 --- a/src/main/java/a8k/app/type/error/AEConsumeNotEnoughError.java +++ b/src/main/java/a8k/app/type/error/AEConsumeNotEnoughError.java @@ -13,6 +13,7 @@ public class AEConsumeNotEnoughError extends AppError { public AEConsumeNotEnoughError(List consumableStatInfos) { super(A8kEcode.APPE_CONSUME_NOT_ENOUGH); + this.consumableStatInfos = consumableStatInfos; } }