From cea08ef0cf73015e5bd00d5aa36ff6c3a889bdbf Mon Sep 17 00:00:00 2001 From: MT <3075067877@qq.com> Date: Sun, 14 Sep 2025 17:43:10 +0800 Subject: [PATCH] add gateab alert --- .../com/ouxuan/oxface/OXFaceOnlineActivity.java | 378 ++++++++++++++++----- .../oxface/abgate/GateUnavailableDialog.java | 278 +++++++++++++++ .../com/ouxuan/oxface/device/GateABController.java | 160 +++++++++ 3 files changed, 732 insertions(+), 84 deletions(-) create mode 100644 app/src/main/java/com/ouxuan/oxface/abgate/GateUnavailableDialog.java diff --git a/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java b/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java index c659bac..a0c98f6 100644 --- a/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java +++ b/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java @@ -53,6 +53,9 @@ import com.ouxuan.oxface.utils.VenueSceneUtils; import com.ouxuan.oxface.network.NetworkStatusIndicator; import com.ouxuan.oxface.device.OxUDP; import com.ouxuan.oxface.device.OxUDPUsageExample; +import com.ouxuan.oxface.abgate.GateUnavailableDialog; +import com.ouxuan.oxface.abgate.ABGateManager; +import com.ouxuan.oxface.device.GateABController; import java.util.List; @@ -194,6 +197,12 @@ public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickLi // UDP门禁控制 private OxUDPUsageExample udpExample; private boolean isUDPInitialized = false; + + // AB门禁不可用弹窗 + private GateUnavailableDialog gateUnavailableDialog; + private GateABController gateABController; + private ABGateManager abGateManager; + private boolean isGateCheckEnabled = false; // AB门检测是否开启 @Override protected void onCreate(Bundle savedInstanceState) { @@ -220,6 +229,9 @@ public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickLi // 初始化网络状态指示器 initNetworkStatusIndicator(); + + // 初始化AB门禁管理和不可用弹窗 + initGateUnavailableDialog(); // 初始化人脸检测状态 lastFaceDetectedTime = System.currentTimeMillis(); @@ -660,6 +672,102 @@ public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickLi LogManager.logError(TAG, "网络状态指示器初始化失败", e); } } + + /** + * 初始化AB门禁管理和不可用弹窗 + */ + private void initGateUnavailableDialog() { + try { + LogManager.logInfo(TAG, "开始初始化AB门禁管理和不可用弹窗"); + + // 初始化AB门禁管理器 + abGateManager = ABGateManager.getInstance(); + abGateManager.initialize(this); + + // 初始化门状态控制器 + gateABController = GateABController.getInstance(); + gateABController.initialize(this); + + // 设置门状态监听器(触发时机1) + gateABController.setGateStateListener(new GateABController.GateUnavailableListener() { + @Override + public void onGateStateChanged(GateABController.GateABState state) { + LogManager.logInfo(TAG, "门状态发生变化: " + state.toString()); + } + + @Override + public void onGateUnavailable(String reason) { + LogManager.logWarning(TAG, "触发门禁不可用弹窗(触发时机1): " + reason); + + // 执行checkShow()判断是否显示弹窗 + boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable()); + if (shouldShow) { + // 显示门状态异常弹窗 + gateUnavailableDialog.showGateStateError(reason); + } else { + LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗"); + } + } + + @Override + public void onGateAvailable() { + LogManager.logInfo(TAG, "门禁恢复可用,隐藏弹窗"); + // 隐藏门禁不可用弹窗 + if (gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) { + gateUnavailableDialog.hide(); + } + } + }); + + // 初始化门禁不可用弹窗 + gateUnavailableDialog = new GateUnavailableDialog(this); + + // 设置弹窗操作监听器 + gateUnavailableDialog.setDialogListener(new GateUnavailableDialog.GateUnavailableDialogListener() { + @Override + public void onDialogShow() { + LogManager.logInfo(TAG, "门禁不可用弹窗显示,暂停摄像头和中断操作"); + // 暂停摄像头和中断其他操作 + pauseCameraForDialog(); + } + + @Override + public void onDialogHide() { + LogManager.logInfo(TAG, "门禁不可用弹窗隐藏,恢复摄像头和操作"); + // 恢复摄像头和操作 + resumeCamera(); + } + }); + + // 检查是否开启AB门检测 + checkGateCheckEnabled(); + + LogManager.logInfo(TAG, "AB门禁管理和不可用弹窗初始化完成"); + + } catch (Exception e) { + LogManager.logError(TAG, "AB门禁管理和不可用弹窗初始化失败", e); + } + } + + /** + * 检查是否开启AB门检测 + */ + private void checkGateCheckEnabled() { + try { + // 从配置中获取AB门检测是否开启 + // 这里可以根据实际业务需要添加配置检查逻辑 + // 例如:从 DeviceSelectDataManager 获取相关配置 + + // 暂时默认开启,后续可以根据实际需要调整 + isGateCheckEnabled = true; + + LogManager.logInfo(TAG, "AB门检测状态: " + (isGateCheckEnabled ? "已开启" : "已关闭")); + + } catch (Exception e) { + LogManager.logError(TAG, "检查AB门检测状态失败", e); + isGateCheckEnabled = false; // 异常时默认关闭 + } + } @Override protected void onResume() { @@ -1202,6 +1310,25 @@ public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickLi } isUDPInitialized = false; + // 释放AB门禁管理和不可用弹窗资源 + if (gateUnavailableDialog != null) { + gateUnavailableDialog.release(); + gateUnavailableDialog = null; + LogManager.logInfo(TAG, "门禁不可用弹窗资源已释放"); + } + + if (gateABController != null) { + gateABController.release(); + gateABController = null; + LogManager.logInfo(TAG, "AB门控制器资源已释放"); + } + + if (abGateManager != null) { + abGateManager.release(); + abGateManager = null; + LogManager.logInfo(TAG, "AB门禁管理器资源已释放"); + } + // 注销广播接收器 if (cameraControlReceiver != null) { unregisterReceiver(cameraControlReceiver); @@ -1374,104 +1501,187 @@ public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickLi if (currentTime - searshTime > 3000) { searshTime = currentTime; needSendFaceImage = true; - - String base64img = getFaceImageBase64(currentLivenessModel); - if (base64img != null) { - // 这里可以处理base64数据,如上传到服务器等 - Log.i(TAG, "checkResultOnline: 获取到人脸base64数据"); - - // 保存人脸base64数据到verifyCode变量 - verifyCode = base64img; - - // 设置模式为2(人脸验证) - modeType = OrderVerificationManager.TYPE_FACE_VERIFICATION; + + // 触发时机2:在线人脸检测开始前进行AB门人数检测 + if (isGateCheckEnabled && abGateManager != null) { + LogManager.logInfo(TAG, "开始执行AB门人数检测(触发时机2)"); - // 判断是进场还是离场场景 - if (VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this)) { - // 离场场景:检查gate_enter_open_enable配置 - VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(OXFaceOnlineActivity.this); - if (gateConfig != null && gateConfig.gateEnterOpenEnable) { - // 如果gate_enter_open_enable为true,直接开门不进行网络核销 - LogManager.logInfo(TAG, "检测到离场场景,gate_enter_open_enable为true,直接开启B门"); - showLoadingStatus("离场验证成功"); + // 在后台线程中执行AB门人数检测 + new Thread(() -> { + try { + // 执行人数检测 + boolean peopleCheckPassed = abGateManager.ABPeopleCheck(); - // 2秒后隐藏提示并开门 - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - hideLoadingStatus(); - - // 直接开启B门 - if (isUDPInitialized && udpExample != null) { - LogManager.logInfo(TAG, "gate_enter_open_enable为true,直接开启B门"); - udpExample.handleFaceRecognitionSuccess(false); // 参数保留兼容性,实际都开B门 + if (!peopleCheckPassed) { + // 人数检测未通过,显示弹窗 + runOnUiThread(() -> { + try { + // 判断场景类型 + boolean isLeaveScene = VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this); + + // 获取实际人数用于显示 + gateABController.get485PeopleNum(new GateABController.PeopleNumCallback() { + @Override + public void onSuccess(int peopleNum) { + // 执行checkShow()判断是否显示弹窗 + boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable()); + if (shouldShow) { + // 显示人数异常弹窗 + gateUnavailableDialog.showPeopleCountError(isLeaveScene, peopleNum); + LogManager.logWarning(TAG, "触发门禁不可用弹窗(触发时机2): 人数异常, 场景=" + (isLeaveScene ? "离场" : "进场") + ", 人数=" + peopleNum); + } else { + LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗"); + } + } + + @Override + public void onError(String errorMessage) { + LogManager.logError(TAG, "获取人数失败,无法显示具体人数: " + errorMessage); + + // 即使获取人数失败,也可以显示一个通用的人数异常弹窗 + boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable()); + if (shouldShow) { + String reason = "门禁内人数不符合要求,请等待"; + gateUnavailableDialog.showGateStateError(reason); + LogManager.logWarning(TAG, "触发门禁不可用弹窗(触发时机2): " + reason); + } + } + }); + + } catch (Exception e) { + LogManager.logError(TAG, "处理人数检测结果异常", e); } - } - }, 2000); - } else { - // gate_enter_open_enable为false或配置不存在,按正常流程进行网络校验 - if (isNetworkAvailable()) { - LogManager.logInfo(TAG, "检测到离场场景,网络可用,执行离场校验"); - performLeaveVerification(base64img); - } else { - LogManager.logWarning(TAG, "检测到离场场景,但网络不可用,直接开启B门"); - showLoadingStatus("网络不可用,直接开门"); - - // 直接开启B门,防止用户卡在场内 - if (isUDPInitialized && udpExample != null) { - LogManager.logInfo(TAG, "离场场景网络不可用,直接开启B门"); - udpExample.handleFaceRecognitionSuccess(false); // 开启B门 - } + }); - // 3秒后隐藏提示 - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - hideLoadingStatus(); - } - }, 3000); - } - } - } else { - // 进场场景:先检查网络状态 - if (isNetworkAvailable()) { - LogManager.logInfo(TAG, "检测到进场场景,网络可用,执行订单核销"); - - // 检查是否已有正在进行的人脸验证请求,避免重复请求 - if (orderVerificationManager != null && orderVerificationManager.isRequestInProgress()) { - LogManager.logInfo(TAG, "已有正在进行的人脸验证请求,跳过本次请求"); + // 人数检测未通过,中断后续的人脸识别流程 + LogManager.logInfo(TAG, "AB门人数检测未通过,中断后续人脸识别流程"); return; + } else { + LogManager.logInfo(TAG, "AB门人数检测通过,继续人脸识别流程"); } - getCheckOrder(); - } else { - LogManager.logWarning(TAG, "检测到进场场景,但网络不可用"); - showLoadingStatus("无网络连接,请检查网络设置"); - // 3秒后隐藏提示 - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - hideLoadingStatus(); - } - }, 3000); - - // 网络不可用时,不执行任何门禁操作,保持安全 - LogManager.logInfo(TAG, "进场场景网络不可用,等待网络恢复"); + } catch (Exception e) { + LogManager.logError(TAG, "AB门人数检测异常", e); + // 异常情况下继续正常流程,不阻塞用户操作 + LogManager.logInfo(TAG, "AB门检测异常,继续正常人脸识别流程"); } - } - - // 处理完成后重置标志 - needSendFaceImage = false; + + // 继续正常的人脸识别流程(只有在检测通过或异常时才执行) + runOnUiThread(() -> processFaceRecognitionFlow()); + + }).start(); + + // AB门检测开启时,先返回,等待后台维续 + return; } else { - Log.i(TAG, "run:checkResultOnline base64img score too low "); - layoutCompareStatus.setVisibility(View.VISIBLE); - textCompareStatus.setTextColor(Color.parseColor("#fec133")); - textCompareStatus.setText("请重新识别"); + // AB门检测未开启,直接继续正常流程 + processFaceRecognitionFlow(); } + } else { + // 时间间隔不够,跳过本次处理 } } }); } + + /** + * 处理人脸识别流程(从 checkResultOnline 中抽取出来) + */ + private void processFaceRecognitionFlow() { + String base64img = getFaceImageBase64(currentLivenessModel); + if (base64img != null) { + // 这里可以处理base64数据,如上传到服务器等 + Log.i(TAG, "processFaceRecognitionFlow: 获取到人脸base64数据"); + + // 保存人脸base64数据到verifyCode变量 + verifyCode = base64img; + + // 设置模式为2(人脸验证) + modeType = OrderVerificationManager.TYPE_FACE_VERIFICATION; + + // 判断是进场还是离场场景 + if (VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this)) { + // 离场场景:检查gate_enter_open_enable配置 + VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(OXFaceOnlineActivity.this); + if (gateConfig != null && gateConfig.gateEnterOpenEnable) { + // 如果 gate_enter_open_enable 为 true,直接开门不进行网络核销 + LogManager.logInfo(TAG, "检测到离场场景,gate_enter_open_enable为true,直接开启B门"); + showLoadingStatus("离场验证成功"); + + // 2秒后隐藏提示并开门 + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + hideLoadingStatus(); + + // 直接开启B门 + if (isUDPInitialized && udpExample != null) { + LogManager.logInfo(TAG, "gate_enter_open_enable为true,直接开启B门"); + udpExample.handleFaceRecognitionSuccess(false); // 参数保留兼容性,实际都开B门 + } + } + }, 2000); + } else { + // gate_enter_open_enable 为 false 或配置不存在,按正常流程进行网络校验 + if (isNetworkAvailable()) { + LogManager.logInfo(TAG, "检测到离场场景,网络可用,执行离场校验"); + performLeaveVerification(base64img); + } else { + LogManager.logWarning(TAG, "检测到离场场景,但网络不可用,直接开启B门"); + showLoadingStatus("网络不可用,直接开门"); + + // 直接开启B门,防止用户卡在场内 + if (isUDPInitialized && udpExample != null) { + LogManager.logInfo(TAG, "离场场景网络不可用,直接开启B门"); + udpExample.handleFaceRecognitionSuccess(false); // 开启B门 + } + + // 3秒后隐藏提示 + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + hideLoadingStatus(); + } + }, 3000); + } + } + } else { + // 进场场景:先检查网络状态 + if (isNetworkAvailable()) { + LogManager.logInfo(TAG, "检测到进场场景,网络可用,执行订单核销"); + + // 检查是否已有正在进行的人脸验证请求,避免重复请求 + if (orderVerificationManager != null && orderVerificationManager.isRequestInProgress()) { + LogManager.logInfo(TAG, "已有正在进行的人脸验证请求,跳过本次请求"); + return; + } + + getCheckOrder(); + } else { + LogManager.logWarning(TAG, "检测到进场场景,但网络不可用"); + showLoadingStatus("无网络连接,请检查网络设置"); + // 3秒后隐藏提示 + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + hideLoadingStatus(); + } + }, 3000); + + // 网络不可用时,不执行任何门禁操作,保持安全 + LogManager.logInfo(TAG, "进场场景网络不可用,等待网络恢复"); + } + } + + // 处理完成后重置标志 + needSendFaceImage = false; + } else { + Log.i(TAG, "processFaceRecognitionFlow: base64img score too low "); + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#fec133")); + textCompareStatus.setText("请重新识别"); + } + } private String getFaceImageBase64(LivenessModel livenessModel) { if (livenessModel == null) return null; diff --git a/app/src/main/java/com/ouxuan/oxface/abgate/GateUnavailableDialog.java b/app/src/main/java/com/ouxuan/oxface/abgate/GateUnavailableDialog.java new file mode 100644 index 0000000..c3f6f3c --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/abgate/GateUnavailableDialog.java @@ -0,0 +1,278 @@ +package com.ouxuan.oxface.abgate; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.ouxuan.oxface.utils.LogManager; + +/** + * 门禁不可用弹窗 + * 当AB门状态异常或门内人数异常时显示 + * + * @author AI Assistant + * @version 1.0 + * @date 2024/09/13 + */ +public class GateUnavailableDialog { + + private static final String TAG = "GateUnavailableDialog"; + + private Context context; + private Dialog dialog; + private TextView tvTitle; + private TextView tvMessage; + /** + * 门禁不可用弹窗操作监听器 + */ + public interface GateUnavailableDialogListener { + /** + * 弹窗显示时触发,需要暂停摄像头和中断其他操作 + */ + void onDialogShow(); + + /** + * 弹窗隐藏时触发,可以恢复摄像头和其他操作 + */ + void onDialogHide(); + } + + private GateUnavailableDialogListener dialogListener; + private boolean isShowing = false; + + /** + * 设置弹窗操作监听器 + * @param listener 监听器 + */ + public void setDialogListener(GateUnavailableDialogListener listener) { + this.dialogListener = listener; + } + + public GateUnavailableDialog(Context context) { + this.context = context; + createDialog(); + } + + /** + * 创建弹窗 + */ + private void createDialog() { + dialog = new Dialog(context); + + // 创建弹窗布局 + LinearLayout mainLayout = new LinearLayout(context); + mainLayout.setOrientation(LinearLayout.VERTICAL); + mainLayout.setBackgroundColor(Color.parseColor("#CC000000")); // 半透明黑色背景 + mainLayout.setGravity(Gravity.CENTER); + mainLayout.setPadding(50, 50, 50, 50); + + // 内容容器 + LinearLayout contentLayout = new LinearLayout(context); + contentLayout.setOrientation(LinearLayout.VERTICAL); + contentLayout.setBackgroundColor(Color.WHITE); + contentLayout.setPadding(40, 30, 40, 30); + contentLayout.setGravity(Gravity.CENTER); + + // 设置圆角效果(通过代码设置) + android.graphics.drawable.GradientDrawable drawable = new android.graphics.drawable.GradientDrawable(); + drawable.setColor(Color.WHITE); + drawable.setCornerRadius(20); + contentLayout.setBackground(drawable); + + // 标题 + tvTitle = new TextView(context); + tvTitle.setText("门禁不可用"); + tvTitle.setTextSize(20); + tvTitle.setTextColor(Color.parseColor("#FF4444")); + tvTitle.setGravity(Gravity.CENTER); + tvTitle.setPadding(0, 0, 0, 20); + + // 消息内容 + tvMessage = new TextView(context); + tvMessage.setTextSize(16); + tvMessage.setTextColor(Color.parseColor("#333333")); + tvMessage.setGravity(Gravity.CENTER); + tvMessage.setLineSpacing(5, 1.2f); + + // 添加视图到容器 + contentLayout.addView(tvTitle); + contentLayout.addView(tvMessage); + + // 设置内容布局参数 + LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams( + (int)(300 * context.getResources().getDisplayMetrics().density), + LinearLayout.LayoutParams.WRAP_CONTENT + ); + contentParams.gravity = Gravity.CENTER; + mainLayout.addView(contentLayout, contentParams); + + // 设置Dialog属性 + dialog.setContentView(mainLayout); + + Window window = dialog.getWindow(); + if (window != null) { + // 设置全屏显示 + window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); + window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + // 设置窗口标志 + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + + // 清除默认动画 + window.setWindowAnimations(0); + } + + // 设置不可取消 + dialog.setCancelable(false); + dialog.setCanceledOnTouchOutside(false); + } + + /** + * 显示门状态异常弹窗 + * @param reason 不可用原因 + */ + public void showGateStateError(String reason) { + if (isShowing) { + return; + } + + LogManager.logInfo(TAG, "显示门状态异常弹窗: " + reason); + + tvTitle.setText("门禁不可用"); + tvMessage.setText(reason); + + show(); + } + + /** + * 显示人数异常弹窗 + * @param isLeaveScene 是否为离场场景 + * @param peopleCount 门内人数 + */ + public void showPeopleCountError(boolean isLeaveScene, int peopleCount) { + if (isShowing) { + return; + } + + String message; + if (isLeaveScene) { + // 离场场景:检测到门内有人 + message = "AB门离场:检测到门内有 " + peopleCount + " 人\n请等待门禁内人员离开后操作"; + LogManager.logInfo(TAG, "显示离场人数异常弹窗,门内人数: " + peopleCount); + } else { + // 进场场景:检测到门内人数大于1 + message = "AB门进场:检测到门内有 " + peopleCount + " 人\n请一次一人进场,确保门禁内只有一人时操作"; + LogManager.logInfo(TAG, "显示进场人数异常弹窗,门内人数: " + peopleCount); + } + + tvTitle.setText("门禁不可用"); + tvMessage.setText(message); + + show(); + } + + /** + * 显示弹窗 + */ + private void show() { + try { + if (dialog != null && !isShowing) { + dialog.show(); + isShowing = true; + LogManager.logInfo(TAG, "门禁不可用弹窗已显示"); + + // 通知监听器弹窗显示,需要暂停摄像头和中断操作 + if (dialogListener != null) { + dialogListener.onDialogShow(); + } + } + } catch (Exception e) { + LogManager.logError(TAG, "显示弹窗失败", e); + } + } + + /** + * 隐藏弹窗 + */ + public void hide() { + try { + if (dialog != null && isShowing) { + dialog.dismiss(); + isShowing = false; + LogManager.logInfo(TAG, "门禁不可用弹窗已隐藏"); + + // 通知监听器弹窗隐藏,可以恢复摄像头和操作 + if (dialogListener != null) { + dialogListener.onDialogHide(); + } + } + } catch (Exception e) { + LogManager.logError(TAG, "隐藏弹窗失败", e); + } + } + + /** + * 检查是否应该显示弹窗 + * 根据业务规则进行判断: + * - 对于离场设备,检测到网络中断后不显示弹窗(不阻碍离场断网直接开门功能) + * - 对于进场设备,始终显示弹窗 + * + * @param context 上下文 + * @param isNetworkAvailable 网络是否可用 + * @return true表示应该显示弹窗 + */ + public boolean checkShow(Context context, boolean isNetworkAvailable) { + try { + // 先检测设备场景 + boolean isLeaveScene = com.ouxuan.oxface.utils.VenueSceneUtils.isLeaveScene(context); + + if (isLeaveScene) { + // 离场设备:检测到网络中断后,弹窗不显示(不阻碍离场断网直接开门功能) + if (!isNetworkAvailable) { + LogManager.logInfo(TAG, "离场设备检测到网络中断,不显示门禁不可用弹窗(保持离场断网直接开门功能)"); + return false; + } + // 离场设备且网络可用,显示弹窗 + LogManager.logInfo(TAG, "离场设备且网络可用,可以显示门禁不可用弹窗"); + return true; + } else { + // 进场设备:始终显示弹窗 + LogManager.logInfo(TAG, "进场设备,可以显示门禁不可用弹窗"); + return true; + } + + } catch (Exception e) { + LogManager.logError(TAG, "检查是否显示弹窗时发生异常,默认显示", e); + return true; // 异常情况下默认显示弹窗 + } + } + + /** + * 检查弹窗是否正在显示 + * @return true表示正在显示 + */ + public boolean isShowing() { + return isShowing; + } + + /** + * 释放资源 + */ + public void release() { + hide(); + if (dialog != null) { + dialog = null; + } + context = null; + LogManager.logInfo(TAG, "门禁不可用弹窗资源已释放"); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/device/GateABController.java b/app/src/main/java/com/ouxuan/oxface/device/GateABController.java index a7b6cef..eabbef5 100644 --- a/app/src/main/java/com/ouxuan/oxface/device/GateABController.java +++ b/app/src/main/java/com/ouxuan/oxface/device/GateABController.java @@ -52,6 +52,65 @@ public class GateABController { private PadApiService acControlApiService; /** + * AB门状态类 + * 记录AB门的开关状态,用于触发门禁不可用弹窗 + */ + public static class GateABState { + public boolean gateAOpen = false; // A门是否开启 + public boolean gateBOpen = false; // B门是否开启 + public boolean udpConnected = true; // UDP连接状态 + public long lastUpdateTime = 0; // 最后更新时间 + public String errorMessage = ""; // 错误信息 + + /** + * 检查是否需要触发门禁不可用弹窗 + * @return true表示需要触发弹窗 + */ + public boolean shouldShowUnavailableDialog() { + return gateAOpen || gateBOpen || !udpConnected; + } + + /** + * 获取不可用原因描述 + * @return 不可用原因 + */ + public String getUnavailableReason() { + if (gateAOpen && gateBOpen) { + return "检测到A门和B门都开启,请先关闭所有门"; + } else if (gateAOpen) { + return "检测到A门开启,请先关闭A门"; + } else if (gateBOpen) { + return "检测到B门开启,请先关闭B门"; + } else if (!udpConnected) { + return "无法检测到AB门状态,请检查门禁电磁锁或网络状态"; + } + return "门禁状态异常"; + } + + @Override + public String toString() { + return "GateABState{" + + "gateAOpen=" + gateAOpen + + ", gateBOpen=" + gateBOpen + + ", udpConnected=" + udpConnected + + ", lastUpdateTime=" + lastUpdateTime + + ", errorMessage='" + errorMessage + '\'' + + '}'; + } + } + + /** + * AB门状态监听器接口 + */ + public interface GateABStateListener { + void onGateStateChanged(GateABState state); + } + + // AB门状态实例和监听器 + private GateABState currentGateState; + private GateABStateListener gateStateListener; + + /** * AB门配置类 * 对应uniapp中的state参数 */ @@ -114,6 +173,7 @@ public class GateABController { udpController = OxUDP.getInstance(); ox485 = Ox485.getInstance(); currentConfig = new GateABConfig(); + currentGateState = new GateABState(); // 初始化门状态 // 初始化中控网络服务 initializeACControlService(); @@ -706,6 +766,9 @@ public class GateABController { LogManager.logInfo(TAG, "UDP门状态更新 - A门: " + (gateAState ? "开启" : "关闭") + ", B门: " + (gateBState ? "开启" : "关闭")); + // 更新AB门状态到GateABState + updateGateState(gateAState, gateBState, true, ""); + // 按照业务逻辑:当A门或B门中有任一个开启时,返回"open" // 两个门都关闭时返回"close" String result = (gateAState || gateBState) ? "open" : "close"; @@ -720,6 +783,8 @@ public class GateABController { @Override public void onUDPError(String error) { LogManager.logError(TAG, "UDP查询门状态错误: " + error); + // 更新状态为UDP连接失败 + updateGateState(false, false, false, error); future.complete("close"); // 错误时返回关闭状态 } }; @@ -863,5 +928,100 @@ public class GateABController { public OxUDP getUdpController() { return udpController; } + + /** + * 设置AB门状态监听器 + * @param listener 状态监听器 + */ + public void setGateStateListener(GateABStateListener listener) { + this.gateStateListener = listener; + } + + /** + * 获取当前AB门状态 + * @return 当前门状态 + */ + public GateABState getCurrentGateState() { + return currentGateState; + } + + /** + * 更新AB门状态 + * @param gateAOpen A门是否开启 + * @param gateBOpen B门是否开启 + * @param udpConnected UDP连接状态 + * @param errorMessage 错误信息 + */ + public void updateGateState(boolean gateAOpen, boolean gateBOpen, boolean udpConnected, String errorMessage) { + boolean stateChanged = false; + boolean shouldShowDialog = false; + + // 检查状态是否发生变化 + if (currentGateState.gateAOpen != gateAOpen || + currentGateState.gateBOpen != gateBOpen || + currentGateState.udpConnected != udpConnected) { + stateChanged = true; + + // 检查是否需要触发门禁不可用弹窗 + boolean oldShouldShow = currentGateState.shouldShowUnavailableDialog(); + + // 暂时更新状态以检查新状态 + boolean tempGateAOpen = currentGateState.gateAOpen; + boolean tempGateBOpen = currentGateState.gateBOpen; + boolean tempUdpConnected = currentGateState.udpConnected; + + currentGateState.gateAOpen = gateAOpen; + currentGateState.gateBOpen = gateBOpen; + currentGateState.udpConnected = udpConnected; + + boolean newShouldShow = currentGateState.shouldShowUnavailableDialog(); + + // 如果从正常状态变为需要显示弹窗状态,则触发弹窗 + if (!oldShouldShow && newShouldShow) { + shouldShowDialog = true; + LogManager.logWarning(TAG, "检测到门状态异常,需要显示门禁不可用弹窗: " + currentGateState.getUnavailableReason()); + } else if (oldShouldShow && !newShouldShow) { + LogManager.logInfo(TAG, "门状态已恢复正常,可以隐藏门禁不可用弹窗"); + } + } + + // 更新状态信息 + currentGateState.gateAOpen = gateAOpen; + currentGateState.gateBOpen = gateBOpen; + currentGateState.udpConnected = udpConnected; + currentGateState.lastUpdateTime = System.currentTimeMillis(); + currentGateState.errorMessage = errorMessage != null ? errorMessage : ""; + + LogManager.logInfo(TAG, "更新AB门状态: " + currentGateState.toString()); + + // 如果状态发生变化且有监听器,通知监听器 + if (stateChanged && gateStateListener != null) { + final boolean finalShouldShowDialog = shouldShowDialog; + syncHandler.post(() -> { + gateStateListener.onGateStateChanged(currentGateState); + // 如果需要显示弹窗,额外通知 + if (finalShouldShowDialog && gateStateListener instanceof GateUnavailableListener) { + ((GateUnavailableListener) gateStateListener).onGateUnavailable(currentGateState.getUnavailableReason()); + } + }); + } + } + + /** + * 门禁不可用监听器接口 + * 扩展GateABStateListener,增加弹窗触发功能 + */ + public interface GateUnavailableListener extends GateABStateListener { + /** + * 门禁变为不可用状态时触发 + * @param reason 不可用原因 + */ + void onGateUnavailable(String reason); + + /** + * 门禁恢复可用状态时触发 + */ + void onGateAvailable(); + } }