zhuguifei
2026-01-14 09c54e2bfa51aa9800f224fda7ad3754b353bfed
功能调整

实时监控、参数设定、高级设置调整
已修改17个文件
已添加9个文件
1550 ■■■■ 文件已修改
app/src/main/AndroidManifest.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/activity/AdvancedSettingActivity.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/activity/HomeActivity.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/activity/ParameterSettingActivity.java 429 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/activity/SystemSettingsActivity.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/fragment/DeviceListFragment.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shlb/comb/fragment/SettingsFragment.java 475 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_arrow_drop_down.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_home.xml 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_parameter_setting.xml 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_system_settings.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/fragment_settings.xml 274 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-hdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-mdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_1.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_2.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_3.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_4.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_5.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/icon_6.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values-land/dimens.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values-w1240dp/dimens.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values-w600dp/dimens.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml
@@ -55,6 +55,8 @@
            />
        <activity android:name=".activity.TestActivity"
            android:screenOrientation="portrait" />
        <activity android:name=".activity.SystemSettingsActivity"
            android:screenOrientation="portrait" />
    </application>
app/src/main/java/com/shlb/comb/activity/AdvancedSettingActivity.java
@@ -37,6 +37,8 @@
    private TextView tv_sensor_status;
    private StringBuilder logBuilder = new StringBuilder();
    private String currentExecutingCmd = "";
    private View currentOperatingButton;
    @Override
    protected void contentView() {
@@ -96,18 +98,26 @@
                int id = v.getId();
                String action = "";
                if (id == R.id.btn_mainboard_read) {
                    currentOperatingButton = btn_mainboard_read;
                    updateButtonState(currentOperatingButton, false);
                    action = "读取主板版本";
                    currentExecutingCmd = CMD.READ_BOARD_VERSION;
                    sendCmdWithCrc(CMD.READ_BOARD_VERSION);
                } else if (id == R.id.btn_display_read) {
                    currentOperatingButton = btn_display_read;
                    updateButtonState(currentOperatingButton, false);
                    action = "读取传感器版本";
                    currentExecutingCmd = CMD.READ_SENSOR_VERSION;
                    sendCmdWithCrc(CMD.READ_SENSOR_VERSION);
                } else if (id == R.id.btn_start_addressing) {
                    currentOperatingButton = btn_start_addressing;
                    updateButtonState(currentOperatingButton, false);
                    action = "开始编址";
                    currentExecutingCmd = CMD.WRITE_START_ADDRESS;
                    sendCmdWithCrc(CMD.WRITE_START_ADDRESS);
                } else if (id == R.id.btn_end_addressing) {
                    currentOperatingButton = btn_end_addressing;
                    updateButtonState(currentOperatingButton, false);
                    action = "结束编址";
                    currentExecutingCmd = CMD.WRITE_END_ADDRESS;
                    sendCmdWithCrc(CMD.WRITE_END_ADDRESS);
@@ -265,6 +275,24 @@
        }
    }
    private void updateButtonState(View btn, boolean enable) {
        if (btn == null) return;
        btn.setEnabled(enable);
        if (enable) {
            btn.getBackground().clearColorFilter();
        } else {
            btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY);
        }
    }
    private void restoreCurrentButton() {
        if (currentOperatingButton != null) {
            updateButtonState(currentOperatingButton, true);
            currentOperatingButton = null;
        }
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(UpdateEvent event) {
        if (event.getType() == UpdateEvent.Type.CONN_STATU) {
@@ -284,12 +312,14 @@
        // å†™å…¥æŒ‡ä»¤çš„返回 (A55A06开头)
        if (hex.startsWith("A55A06") && hex.length() >= 12) {
            restoreCurrentButton();
            parseWriteResponse(hex);
            return;
        }
        // è¯»å–指令的返回 (A55A03开头)
        if (hex.startsWith("A55A03") && hex.length() >= 8) {
             restoreCurrentButton();
             parseReadResponse(hex);
        }
    }
app/src/main/java/com/shlb/comb/activity/HomeActivity.java
@@ -121,7 +121,7 @@
                        }
                    });
                }
                if (iv_right != null) iv_right.setVisibility(View.VISIBLE);
                if (iv_right != null) iv_right.setVisibility(View.GONE);
            }
        });
        
@@ -176,14 +176,37 @@
        });
        // é«˜çº§è®¾ç½®
        findViewById(R.id.menu_advanced_settings).setOnClickListener(v -> {
        View menuAdvancedSettings = findViewById(R.id.menu_advanced_settings);
        menuAdvancedSettings.setVisibility(View.GONE);
        menuAdvancedSettings.setOnClickListener(v -> {
            drawerLayout.closeDrawer(GravityCompat.START);
            startActivity(new Intent(this, AdvancedSettingActivity.class));
        });
        // ç³»ç»Ÿè®¾ç½®
        View menuSystemSettings = findViewById(R.id.menu_system_settings);
        menuSystemSettings.setVisibility(View.GONE);
        menuSystemSettings.setOnClickListener(v -> {
            drawerLayout.closeDrawer(GravityCompat.START);
            startActivity(new Intent(this, SystemSettingsActivity.class));
        });
        // é¡¶éƒ¨comb点击事件
        findViewById(R.id.tv_menu_title).setOnClickListener(new View.OnClickListener() {
            long lastClickTime = 0;
            @Override
            public void onClick(View v) {
                long currentTime = System.currentTimeMillis();
                if (currentTime - lastClickTime < 500) {
                    showPasswordDialog();
                }
                lastClickTime = currentTime;
            }
        });
    }
    private void updateDrawerSelection(int selectedId) {
        int[] menuIds = {R.id.menu_monitor, R.id.menu_devices, R.id.menu_current_device, R.id.menu_settings, R.id.menu_advanced_settings};
        int[] menuIds = {R.id.menu_monitor, R.id.menu_devices, R.id.menu_current_device, R.id.menu_settings, R.id.menu_advanced_settings, R.id.menu_system_settings};
        for (int id : menuIds) {
            View view = findViewById(id);
            if (view != null) {
@@ -237,7 +260,7 @@
                }
            });
        }
        if (iv_right != null) iv_right.setVisibility(View.VISIBLE);
        if (iv_right != null) iv_right.setVisibility(View.GONE);
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        if (deviceListFragment == null) {
@@ -352,4 +375,23 @@
            finish();
        }
    }
    private void showPasswordDialog() {
        final QMUIDialog.EditTextDialogBuilder builder = new QMUIDialog.EditTextDialogBuilder(this);
        builder.setTitle("请输入密码")
                .setPlaceholder("请输入密码")
                .setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD)
                .addAction("取消", (dialog, index) -> dialog.dismiss())
                .addAction("确定", (dialog, index) -> {
                    String text = builder.getEditText().getText().toString();
                    if ("0880".equals(text)) {
                        findViewById(R.id.menu_advanced_settings).setVisibility(View.VISIBLE);
                        findViewById(R.id.menu_system_settings).setVisibility(View.VISIBLE);
                        Toast("已显示隐藏设置");
                        dialog.dismiss();
                    } else {
                        Toast("密码错误");
                    }
                })
                .create(com.qmuiteam.qmui.R.style.QMUI_Dialog).show();
    }
}
app/src/main/java/com/shlb/comb/activity/ParameterSettingActivity.java
@@ -36,35 +36,43 @@
public class ParameterSettingActivity extends BaseActivity {
    // é˜ˆå€¼è®¾å®šç›¸å…³æŽ§ä»¶
    private Spinner sp_threshold_mode;
    private EditText et_threshold_value;
    private Button btn_threshold_read;
    private Button btn_threshold_write;
    // çµæ•åº¦è®¾å®šç›¸å…³æŽ§ä»¶
    private Spinner sp_sensitivity_mode;
    private EditText et_sensitivity_value;
    private Button btn_sensitivity_read;
    private Button btn_sensitivity_write;
    // è‡ªæ£€å’Œå¤ä½æŒ‰é’®
    private Button btn_start_check;
    private Button btn_end_check;
    private Button btn_reset_mainboard;
    private Button btn_reset_sensor;
    // æ—¥å¿—和状态显示
    private TextView tv_log;
    private TextView tv_right_text;
    private androidx.cardview.widget.CardView cvLog;
    
    // New components
    // è‡ªæ£€ç›‘测网格和日志控件
    private RecyclerView rvGrid;
    private QMUIRoundButton btnReadData;
    private QMUIRoundButton btnClearLog;
    private ScrollView svLog;
    // æ•°æ®ç›¸å…³
    private GridAdapter mAdapter;
    private List<BoxStatus> boxStatusList = new ArrayList<>();
    private StringBuilder logBuilder = new StringBuilder();
    private boolean isSelfCheckMode = false;
    private String pendingSettingCmd = "";
    private boolean isSelfCheckMode = false;  // æ ‡è®°å½“前是否处于自检模式
    private String pendingSettingCmd = "";  // è®°å½•待处理的设定指令
    private Button currentOperatingButton; // å½“前正在操作的按钮
    private String pendingToastMessage = null; // å¾…显示的Toast消息
    
    private SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
@@ -85,12 +93,12 @@
        sp_threshold_mode = findViewById(R.id.sp_threshold_mode);
        et_threshold_value = findViewById(R.id.et_threshold_value);
        btn_threshold_read = findViewById(R.id.btn_threshold_read);
        btn_threshold_write = findViewById(R.id.btn_threshold_write);
        sp_sensitivity_mode = findViewById(R.id.sp_sensitivity_mode);
        et_sensitivity_value = findViewById(R.id.et_sensitivity_value);
        btn_sensitivity_read = findViewById(R.id.btn_sensitivity_read);
        btn_sensitivity_write = findViewById(R.id.btn_sensitivity_write);
        btn_start_check = findViewById(R.id.btn_start_check);
@@ -99,15 +107,15 @@
        btn_reset_sensor = findViewById(R.id.btn_reset_sensor);
        tv_log = findViewById(R.id.tv_log);
        cvLog = findViewById(R.id.cv_log);
        tv_right_text = findViewById(R.id.tv_right_text);
        
        // Init new components
        // åˆå§‹åŒ–自检监测相关控件
        rvGrid = findViewById(R.id.rv_grid);
        // btnReadData was removed from layout
        btnClearLog = findViewById(R.id.btn_clear_log);
        svLog = findViewById(R.id.sv_log);
        
        // Log setup
        // è®¾ç½®æ—¥å¿—滚动视图的触摸监听,防止滑动冲突
        if (svLog != null) {
            svLog.setOnTouchListener((v, event) -> {
                v.getParent().requestDisallowInterceptTouchEvent(true);
@@ -118,6 +126,7 @@
            });
        }
        
        // åˆå§‹åŒ–日志内容
        if (logBuilder.length() > 0) {
            tv_log.setText(Html.fromHtml(logBuilder.toString()));
        } else {
@@ -130,25 +139,36 @@
    protected void initData() {
        updateBluetoothStatus();
        
        // Initialize box status list
        // åˆå§‹åŒ–30个格子的状态列表
        boxStatusList.clear();
        for (int i = 1; i <= 30; i++) {
            boxStatusList.add(new BoxStatus(i));
        }
        
        // Grid Setup
        // è®¾ç½®ç½‘格布局:3行10列
        rvGrid.setLayoutManager(new GridLayoutManager(this, 10)); 
        mAdapter = new GridAdapter();
        rvGrid.setAdapter(mAdapter);
        
        // å¦‚果蓝牙已连接,进入设定模式
        if (BleGlobalManager.getInstance().isConnected()) {
             sendCmdWithCrc(CMD.ENTER_SETTING);
             // sendCmdWithCrc(CMD.READ_DATA);
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        // æ ¹æ®ç³»ç»Ÿè®¾ç½®æŽ§åˆ¶æ—¥å¿—显示
        boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false);
        if (cvLog != null) {
            cvLog.setVisibility(isCmdLogEnabled ? View.VISIBLE : View.GONE);
        }
    }
    @Override
    protected void onDestroy() {
        // é¡µé¢é”€æ¯æ—¶é€€å‡ºè®¾å®šæ¨¡å¼
        if (BleGlobalManager.getInstance().isConnected()) {
            sendCmdWithCrc(CMD.EXIT_SETTING);
        }
@@ -162,27 +182,37 @@
            public void onClick(View v) {
                int id = v.getId();
                String action = "";
                if (id == R.id.btn_threshold_read) {
                    action = "读取阈值";
                    Toast.makeText(ParameterSettingActivity.this, "该功能暂未开放", Toast.LENGTH_SHORT).show();
                } else if (id == R.id.btn_threshold_write) {
                if (id == R.id.btn_threshold_write) {
                    handleThresholdWrite();
                } else if (id == R.id.btn_sensitivity_read) {
                    action = "读取灵敏度";
                    Toast.makeText(ParameterSettingActivity.this, "该功能暂未开放", Toast.LENGTH_SHORT).show();
                } else if (id == R.id.btn_sensitivity_write) {
                    handleSensitivityWrite();
                } else if (id == R.id.btn_start_check) {
                    action = "开始自检";
                    sendCmdWithCrc(CMD.WRITE_START_CHECK);
                    isSelfCheckMode = true;
                    if (BleGlobalManager.getInstance().isConnected()) {
                        currentOperatingButton = btn_start_check;
                        updateButtonState(currentOperatingButton, false);
                        action = "开始自检";
                        setAllGridGray();
                        sendCmdWithCrc(CMD.WRITE_START_CHECK);
                        isSelfCheckMode = true;
                    } else {
                        Toast.makeText(ParameterSettingActivity.this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                    }
                } else if (id == R.id.btn_end_check) {
                    action = "结束自检";
                    sendCmdWithCrc(CMD.WRITE_END_CHECK);
                    isSelfCheckMode = false;
                    if (BleGlobalManager.getInstance().isConnected()) {
                        currentOperatingButton = btn_end_check;
                        updateButtonState(currentOperatingButton, false);
                        action = "结束自检";
                        setAllGridGray();
                        sendCmdWithCrc(CMD.WRITE_END_CHECK);
                        isSelfCheckMode = false;
                    } else {
                         Toast.makeText(ParameterSettingActivity.this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                    }
                } else if (id == R.id.btn_reset_mainboard) {
                    currentOperatingButton = btn_reset_mainboard;
                    showResetConfirmDialog("确定要执行主板复位吗?", CMD.WRITE_BOARD_RESET);
                } else if (id == R.id.btn_reset_sensor) {
                    currentOperatingButton = btn_reset_sensor;
                    showResetConfirmDialog("确定要执行传感器复位吗?", CMD.WRITE_SENSOR_RESET);
                }
@@ -193,14 +223,59 @@
            }
        };
        btn_threshold_read.setOnClickListener(listener);
        btn_threshold_write.setOnClickListener(listener);
        btn_sensitivity_read.setOnClickListener(listener);
        btn_sensitivity_write.setOnClickListener(listener);
        btn_start_check.setOnClickListener(listener);
        btn_end_check.setOnClickListener(listener);
        btn_reset_mainboard.setOnClickListener(listener);
        btn_reset_sensor.setOnClickListener(listener);
        // é˜ˆå€¼æ¨¡å¼é€‰æ‹©ç›‘听,控制输入框是否可用
        sp_threshold_mode.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
                if (position == 0) {
                    // ä¸€é”®è®¾å®šï¼Œéšè—è¾“入框 (使用INVISIBLE保留占位,防止按钮移位)
                    et_threshold_value.setVisibility(View.INVISIBLE);
                    et_threshold_value.setText("");
                } else {
                    // å•层设定,显示输入框
                    et_threshold_value.setVisibility(View.VISIBLE);
                    int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30);
                    et_threshold_value.setHint("1-" + maxLayer);
                }
            }
            @Override
            public void onNothingSelected(android.widget.AdapterView<?> parent) {
            }
        });
        // çµæ•åº¦æ¨¡å¼é€‰æ‹©ç›‘听,控制输入框是否可用
        sp_sensitivity_mode.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
                // 0: æ‰¹é‡å¢žåŠ , 1: æ‰¹é‡å‡å°‘, 2: å•层增加, 3: å•层减少
                if (position == 0 || position == 1) {
                    // æ‰¹é‡æ“ä½œï¼Œéšè—è¾“入框
                    et_sensitivity_value.setVisibility(View.INVISIBLE);
                    et_sensitivity_value.setText("");
                } else {
                    // å•层操作,显示输入框
                    et_sensitivity_value.setVisibility(View.VISIBLE);
                    int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30);
                    et_sensitivity_value.setHint("1-" + maxLayer);
                }
            }
            @Override
            public void onNothingSelected(android.widget.AdapterView<?> parent) {
            }
        });
        
        btnClearLog.setOnClickListener(v -> {
            logBuilder.setLength(0);
@@ -212,7 +287,7 @@
    private String getCmdDescription(String hex, Boolean isSent) {
        if (hex == null) return "";
        
        // Special handling for 3B (Enter/Exit Setting)
        // è¿›å…¥/退出设定模式指令特殊处理
        if (hex.startsWith(CMD.ENTER_SETTING.substring(0, 8))) {
            if (hex.startsWith(CMD.ENTER_SETTING)) return "-进入设定";
            if (hex.startsWith(CMD.EXIT_SETTING)) return "-退出设定";
@@ -249,7 +324,7 @@
        String displayMsg = msg;
        String cmdDesc = "";
        
        // å¦‚果是 hex æŒ‡ä»¤ (纯 0-9 A-F a-f),加空格格式化
        // å¦‚果是十六进制指令,每两位加一个空格格式化显示
        if (msg.matches("^[0-9A-Fa-f]+$")) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < msg.length(); i += 2) {
@@ -267,8 +342,8 @@
        
        String logLine;
        if (isSent != null) {
            String color = isSent ? "#1890ff" : "#05aa87"; // Blue for sent, Green for received
            // æ‹¼æŽ¥åˆ°å‰ç¼€åŽé¢: "发送-读站号: " æˆ– "收到-读站号: "
            // å‘送指令用蓝色,接收数据用绿色
            String color = isSent ? "#1890ff" : "#05aa87";
            String prefix = (isSent ? "发送" : "收到") + cmdDesc + ": ";
            logLine = time + " <font color='" + color + "'>" + prefix + displayMsg + "</font><br>";
        } else {
@@ -278,7 +353,7 @@
        
        logBuilder.append(logLine);
        
        // Update UI
        // æ›´æ–°æ—¥å¿—显示并自动滚动到底部
        if (tv_log != null) {
            tv_log.setText(Html.fromHtml(logBuilder.toString()));
             if (svLog != null) {
@@ -287,7 +362,7 @@
        }
    }
    
    // Kept for backward compatibility if needed, but redirected to appendLog
    // å…¼å®¹æ—§ä»£ç çš„æ—¥å¿—方法
    private void addLog(String message) {
        appendLog(message);
    }
@@ -298,7 +373,7 @@
            updateBluetoothStatus();
        } else if (event.getType() == UpdateEvent.Type.DEVICE_INFO) {
            String hex = event.getMsg();
            appendLog(hex, false); // false for received (Green)
            appendLog(hex, false);  // false表示接收的数据,显示为绿色
            parseAndRefresh(hex);
        }
    }
@@ -306,11 +381,13 @@
    private void parseAndRefresh(String hex) {
        if (hex == null) return;
        
        // å†™å…¥æŒ‡ä»¤çš„返回 (A55A06开头)
        if (hex.startsWith("A55A06") && hex.length() >= 12) {
        // å¤„理写入指令的返回(A55A06开头)
        if (hex.startsWith(CMD.WRITE_SUFFIX) && hex.length() >= 12) {
             // æå–状态字节,01表示成功
             String statusHex = hex.substring(10, 12);
             boolean isSuccess = "01".equals(statusHex);
             // å¤„理进入设定模式的返回
             if (hex.startsWith(CMD.ENTER_SETTING.substring(0, 8))) {
                 if (isSuccess) {
                     appendLog("进入设定模式成功");
@@ -320,57 +397,108 @@
                 return;
             }
             
             // å¤„理退出设定模式的返回
             if (hex.startsWith(CMD.EXIT_SETTING.substring(0, 8))) {
                 appendLog("退出设定模式成功");
                 return;
             }
             // å¤„理阈值设定的返回
             if (hex.startsWith(CMD.WRITE_THRESHOLD_SETTING.substring(0, 8))) {
                 if (isSuccess) {
                     appendLog("阈值写入成功");
                     Toast.makeText(this, "阈值写入成功", Toast.LENGTH_SHORT).show();
                     pendingToastMessage = "阈值写入成功";
                     // é˜ˆå€¼å†™å…¥æˆåŠŸåŽï¼Œç­‰å¾…500毫秒再读取数据
                     appendLog("500毫秒后读取阈值设定结果...");
                     new android.os.Handler().postDelayed(() -> {
                         sendCmdWithCrc(CMD.READ_DATA);
                     }, 500);
                 } else {
                     restoreCurrentButton();
                     appendLog("阈值写入失败");
                     Toast.makeText(this, "阈值写入失败", Toast.LENGTH_SHORT).show();
                 }
             } else if (hex.startsWith(CMD.WRITE_SENSITIVITY_SETTING.substring(0, 8))) {
                 // å¤„理灵敏度设定的返回
                 if (isSuccess) {
                     appendLog("灵敏度写入成功");
                     Toast.makeText(this, "灵敏度写入成功", Toast.LENGTH_SHORT).show();
                     pendingToastMessage = "灵敏度写入成功";
                     // çµæ•åº¦å†™å…¥æˆåŠŸåŽï¼Œç­‰å¾…500毫秒再读取数据
                     appendLog("500毫秒后读取灵敏度设定结果...");
                     new android.os.Handler().postDelayed(() -> {
                         sendCmdWithCrc(CMD.READ_DATA);
                     }, 500);
                 } else {
                     restoreCurrentButton();
                     appendLog("灵敏度写入失败");
                     Toast.makeText(this, "灵敏度写入失败", Toast.LENGTH_SHORT).show();
                 }
             } else if (hex.startsWith(CMD.WRITE_BOARD_RESET.substring(0, 8))) {
             } else if (hex.startsWith(CMD.WRITE_BOARD_RESET.substring(0, 8))) {
                 // å¤„理主板/传感器复位的返回
                 // åˆ¤æ–­æ˜¯ä¸»æ¿å¤ä½è¿˜æ˜¯ä¼ æ„Ÿå™¨å¤ä½
                 boolean isBoardReset = pendingSettingCmd.equals(CMD.WRITE_BOARD_RESET);
                 String name = isBoardReset ? "主板复位" : "传感器复位";
                 
                 if (isSuccess) {
                     appendLog(name + "成功");
                     Toast.makeText(this, name + "成功", Toast.LENGTH_SHORT).show();
                     // å¦‚果是主板复位成功,提示即将返回并跳转到实时监控页面
                     if (isBoardReset) {
                         Toast.makeText(this, "复位成功即将返回", Toast.LENGTH_SHORT).show();
                         appendLog("复位成功,2秒后返回实时监控页面...");
                         // 2秒后返回到实时监控页面
                         new android.os.Handler().postDelayed(() -> {
                             finish();  // å…³é—­å½“前页面,返回到实时监控页面
                         }, 2000);
                     } else {
                         // ä¼ æ„Ÿå™¨å¤ä½æˆåŠŸï¼Œæç¤ºå¹¶åœ¨500毫秒后读取数据
                         Toast.makeText(this, name + "成功", Toast.LENGTH_SHORT).show();
                         // 500毫秒后读取数据,通过现有的parseSelfCheckData解析
                         new android.os.Handler().postDelayed(() -> {
                             sendCmdWithCrc(CMD.READ_DATA);
                         }, 500);
                     }
                 } else {
                     restoreCurrentButton();
                     appendLog(name + "失败");
                     Toast.makeText(this, name + "失败", Toast.LENGTH_SHORT).show();
                 }
             }
        }
        // Check for Self Check command response
        // å¤„理自检指令的返回结果
        // å¼€å§‹è‡ªæ£€å’Œç»“束自检的指令前缀相同,都是 CMD.WRITE_START_CHECK çš„前8位
        if (hex.startsWith(CMD.WRITE_START_CHECK.substring(0, 8)) && hex.length() >= 12) {
             // æå–状态字节(第11-12位),01表示成功,其他表示失败
             String statusHex = hex.substring(10, 12);
             boolean isSuccess = "01".equals(statusHex);
             // æ ¹æ®å½“前模式判断操作名称
             String opName = isSelfCheckMode ? "开始自检" : "结束自检";
             
             if (isSuccess) {
                 appendLog(opName + "成功");
                 Toast.makeText(this, opName + "成功", Toast.LENGTH_SHORT).show();
                 pendingToastMessage = opName + "成功";
                 
                 // If success and isSelfCheckMode, wait 5s then read data
                 // æ ¹æ®ä¸åŒçš„自检阶段,延迟不同时间后读取数据
                 if (isSelfCheckMode) {
                     appendLog("5秒后读取自检结果...");
                     // å¼€å§‹è‡ªæ£€æˆåŠŸåŽï¼Œç­‰å¾… å±‚æ•° * 1000 æ¯«ç§’再读取数据
                     int layerCount = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 3);
                     long delay = layerCount * 1000L;
                     Toast.makeText(this, "开始自检,请等待" + layerCount + "秒", Toast.LENGTH_SHORT).show();
                     // è¿™ä¸ªæ—¶é—´æ˜¯ä¸ºäº†è®©ä¼ æ„Ÿå™¨å®Œæˆè‡ªæ£€åŠ¨ä½œ
                     appendLog(delay + "毫秒后读取自检结果...");
                     new android.os.Handler().postDelayed(() -> {
                         sendCmdWithCrc(CMD.READ_DATA);
                     }, 5000);
                     }, delay);
                 } else {
                     // ç»“束自检成功后,等待500毫秒再读取数据
                     // è¿™ä¸ªæ—¶é—´æ˜¯ä¸ºäº†è®©è®¾å¤‡çŠ¶æ€ç¨³å®šåŽå†è¯»å–æœ€ç»ˆç»“æžœ
                     appendLog("500毫秒后读取自检结果...");
                     new android.os.Handler().postDelayed(() -> {
                         sendCmdWithCrc(CMD.READ_DATA);
                     }, 500);
                 }
             } else {
                 appendLog(opName + "失败");
@@ -378,69 +506,54 @@
             }
        }
        
        // Handle READ_DATA response for Self Check results
        // A55A0301...
        // å¤„理读取数据指令的返回结果,用于显示自检结果
        // æ•°æ®æ ¼å¼ï¼šA55A0301... è‡³å°‘需要26位十六进制字符
        if (hex.startsWith(CMD.READ_DATA.substring(0, 8)) && hex.length() >= 26) {
             restoreCurrentButton();
             parseSelfCheckData(hex);
             if (pendingToastMessage != null) {
                 Toast.makeText(this, pendingToastMessage, Toast.LENGTH_SHORT).show();
                 pendingToastMessage = null;
             }
        }
    }
    
    /**
     * è§£æžè‡ªæ£€æ•°æ®å¹¶æ›´æ–°æ ¼å­çŠ¶æ€
     * @param hex æŽ¥æ”¶åˆ°çš„十六进制数据
     */
    private void parseSelfCheckData(String hex) {
        try {
             // 11th hex -> index 10-18 (8 chars)
             // æå–第11-18位(8个字符)作为第一部分数据
             String part1Hex = hex.substring(10, 18);
             long part1Bits = Long.parseLong(part1Hex, 16);
             
             // 18th hex -> index 18-26 (8 chars)
             // æå–第19-26位(8个字符)作为第二部分数据
             String part2Hex = hex.substring(18, 26);
             long part2Bits = Long.parseLong(part2Hex, 16);
             
             appendLog("自检数据解析: " + part1Hex + ", " + part2Hex);
             
             boolean hasChange = false;
             // éåŽ†æ‰€æœ‰æ ¼å­ï¼Œæ ¹æ®ä½æ•°æ®åˆ¤æ–­è‡ªæ£€ç»“æžœ
             for (BoxStatus box : boxStatusList) {
                 int bitIndex = box.id - 1;
                 int bitIndex = box.id - 1;  // æ ¼å­ID从1开始,位索引从0开始
                 if (bitIndex >= 0 && bitIndex < 32) {
                     // Determine success based on both bits
                     // Assuming both need to be 1, or following specific logic.
                     // Based on user request "11th and 18th hex", we check both.
                     // Usually part1 is Glass (Detection), part2 is Online.
                     // We'll assume success means bit is set in both (Online and Detected?)
                     // OR maybe just Online?
                     // Let's stick to combining them to be safe as user mentioned both.
                     // But if Self Check is just checking if it's working, maybe Online is enough?
                     // Let's use: Success = (part1 & 1) && (part2 & 1) ?
                     // Let's use bitwise AND of the two parts for the result.
                     // æå–对应位的值
                     boolean bit1 = ((part1Bits >> bitIndex) & 1) == 1;
                     boolean bit2 = ((part2Bits >> bitIndex) & 1) == 1;
                     
                     // If user meant "11th OR 18th", or "11th is this, 18th is that".
                     // Given "Success/Fail", I'll assume both must be valid.
                     // However, in normal operation:
                     // Glass=0, Online=1 -> Empty box, but sensor working.
                     // Glass=1, Online=1 -> Full box, sensor working.
                     // Online=0 -> Sensor broken/offline.
                     // If "Self Check" puts the sensor in a state where it should report "1" for Glass?
                     // If so, then we expect Glass=1 AND Online=1.
                     // If "Self Check" just checks health, then Glass might be 0.
                     // "确定哪些格子自检成功" (Determine which grids passed self-check).
                     // If I use bit1 && bit2:
                     // If a sensor is working but empty (Glass=0), it fails self-check?
                     // This implies Self Check expects a "1" signal.
                     // This is common in self-checks (force a signal).
                     // ä¸¤ä¸ªä½éƒ½ä¸º1表示自检成功,否则失败
                     if (bit1 && bit2) {
                         box.checkResult = 1; // Green
                         box.checkResult = 1;  // æˆåŠŸï¼Œæ˜¾ç¤ºç»¿è‰²
                     } else {
                         box.checkResult = 0; // Red
                         box.checkResult = 0;  // å¤±è´¥ï¼Œæ˜¾ç¤ºçº¢è‰²
                     }
                     hasChange = true;
                 }
             }
             
             // å¦‚果有数据变化,刷新界面
             if (hasChange && mAdapter != null) {
                 mAdapter.notifyDataSetChanged();
                 appendLog("自检结果已更新");
@@ -452,7 +565,10 @@
        }
    }
    
    /**
     * å‘送带CRC校验的指令
     * @param cmd è¦å‘送的指令(不含CRC)
     */
    private void sendCmdWithCrc(String cmd) {
        if (!BleGlobalManager.getInstance().isConnected()) {
            Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
@@ -462,42 +578,50 @@
        
        byte[] cmdBytes = BleGlobalManager.hexStringToBytes(cmd);
        if (cmdBytes != null) {
            // è®¡ç®—CRC校验码
            String crc = CRCutil.getCRC(cmdBytes);
            // Pad CRC to 4 chars if needed
            // CRC补齐到4位
            while (crc.length() < 4) {
                crc = "0" + crc;
            }
            String fullCmd = cmd + crc.toUpperCase();
            
            appendLog(fullCmd, true); // true for sent (Blue)
            appendLog(fullCmd, true);  // true表示发送的指令,显示为蓝色
            BleGlobalManager.getInstance().sendCmd(fullCmd);
        } else {
            appendLog("错误: æŒ‡ä»¤è½¬æ¢å¤±è´¥");
        }
    }
    /**
     * å¤„理阈值设定写入
     */
    private void handleThresholdWrite() {
        int position = sp_threshold_mode.getSelectedItemPosition();
        String inputValue = et_threshold_value.getText().toString().trim();
        String modeHex = "";
        String valueHex = "";
        
        if (position == 0) { // ä¸€é”®è®¾å®š
        if (position == 0) {
            // ä¸€é”®è®¾å®š
            modeHex = "00";
            valueHex = "00";
        } else if (position == 1) { // å•层设定
        } else if (position == 1) {
            // å•层设定
            modeHex = "01";
            int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30);
            if (inputValue.isEmpty()) {
                Toast.makeText(this, "请输入阈值(1-30)", Toast.LENGTH_SHORT).show();
                Toast.makeText(this, "请输入阈值(1-" + maxLayer + ")", Toast.LENGTH_SHORT).show();
                return;
            }
            try {
                int val = Integer.parseInt(inputValue);
                if (val < 1 || val > 30) {
                    Toast.makeText(this, "阈值范围1-30", Toast.LENGTH_SHORT).show();
                if (val < 1 || val > maxLayer) {
                    Toast.makeText(this, "阈值范围1-" + maxLayer, Toast.LENGTH_SHORT).show();
                    return;
                }
                valueHex = String.format("%02X", val);
                // å•层设定时,输入参数需要减1
                valueHex = String.format("%02X", val - 1);
            } catch (NumberFormatException e) {
                Toast.makeText(this, "输入格式错误", Toast.LENGTH_SHORT).show();
                return;
@@ -506,19 +630,40 @@
            return;
        }
        
        // Construct pending command: CMD (32) + Len (02) + Mode + Value
        // CMD.WRITE_THRESHOLD_SETTING is "A55A063202"
        // æž„造指令:CMD.WRITE_THRESHOLD_SETTING + æ¨¡å¼ + å€¼
        pendingSettingCmd = CMD.WRITE_THRESHOLD_SETTING + modeHex + valueHex;
        sendCmdWithCrc(pendingSettingCmd);
        if (BleGlobalManager.getInstance().isConnected()) {
            currentOperatingButton = btn_threshold_write;
            updateButtonState(currentOperatingButton, false);
            setAllGridGray();
            sendCmdWithCrc(pendingSettingCmd);
        } else {
            Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * å°†æ‰€æœ‰æ ¼å­è®¾ç½®ä¸ºç°è‰²ï¼ˆç­‰å¾…状态)
     */
    private void setAllGridGray() {
        for (BoxStatus box : boxStatusList) {
            box.checkResult = 2; // 2表示等待状态,显示灰色
        }
        if (mAdapter != null) {
            mAdapter.notifyDataSetChanged();
        }
    }
    /**
     * å¤„理灵敏度设定写入
     */
    private void handleSensitivityWrite() {
        int position = sp_sensitivity_mode.getSelectedItemPosition();
        String inputValue = et_sensitivity_value.getText().toString().trim();
        String modeHex = "";
        String valueHex = "";
        
        // 0: æ‰¹é‡å¢žåŠ (00), 1: æ‰¹é‡å‡å°‘(01), 2: å•层增加(02), 3: å•层减少(03)
        // æ¨¡å¼ï¼š0=批量增加(00), 1=批量减少(01), 2=单层增加(02), 3=单层减少(03)
        if (position == 0) {
            modeHex = "00";
            valueHex = "00";
@@ -533,28 +678,37 @@
            return;
        }
        
        // å•层操作需要输入具体数值
        if (position == 2 || position == 3) {
            int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30);
            if (inputValue.isEmpty()) {
                Toast.makeText(this, "请输入数值(1-30)", Toast.LENGTH_SHORT).show();
                Toast.makeText(this, "请输入数值(1-" + maxLayer + ")", Toast.LENGTH_SHORT).show();
                return;
            }
            try {
                int val = Integer.parseInt(inputValue);
                if (val < 1 || val > 30) {
                    Toast.makeText(this, "数值范围1-30", Toast.LENGTH_SHORT).show();
                if (val < 1 || val > maxLayer) {
                    Toast.makeText(this, "数值范围1-" + maxLayer, Toast.LENGTH_SHORT).show();
                    return;
                }
                valueHex = String.format("%02X", val);
                // å•层设定时,输入参数需要减1
                valueHex = String.format("%02X", val - 1);
            } catch (NumberFormatException e) {
                Toast.makeText(this, "输入格式错误", Toast.LENGTH_SHORT).show();
                return;
            }
        }
        
        // Construct pending command: CMD (33) + Len (02) + Mode + Value
        // CMD.WRITE_SENSITIVITY_SETTING is "A55A063302"
        // æž„造指令:CMD.WRITE_SENSITIVITY_SETTING + æ¨¡å¼ + å€¼
        pendingSettingCmd = CMD.WRITE_SENSITIVITY_SETTING + modeHex + valueHex;
        sendCmdWithCrc(pendingSettingCmd);
        if (BleGlobalManager.getInstance().isConnected()) {
            currentOperatingButton = btn_sensitivity_write;
            updateButtonState(currentOperatingButton, false);
            setAllGridGray();
            sendCmdWithCrc(pendingSettingCmd);
        } else {
            Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
        }
    }
    private void updateBluetoothStatus() {
@@ -580,20 +734,25 @@
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                         pendingSettingCmd = cmd;
                         sendCmdWithCrc(cmd);
                         if (BleGlobalManager.getInstance().isConnected()) {
                             updateButtonState(currentOperatingButton, false);
                             pendingSettingCmd = cmd;
                             sendCmdWithCrc(cmd);
                         } else {
                             Toast.makeText(ParameterSettingActivity.this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                         }
                    }
                })
                .setNegativeButton("取消", null)
                .show();
    }
    
    // Inner classes
    /**
     * æ ¼å­çŠ¶æ€æ•°æ®ç±»
     */
    private static class BoxStatus {
        int id;
        // -1: Unknown/Initial (White), 0: Fail (Red), 1: Success (Green)
        int checkResult = -1;
        int id;  // æ ¼å­ID(1-30)
        int checkResult = -1;  // è‡ªæ£€ç»“果:-1=未知(白色),0=失败(红色),1=成功(绿色),2=等待(灰色)
        
        public BoxStatus(int id) {
            this.id = id;
@@ -612,18 +771,17 @@
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            // Calculate Box ID based on 3 rows of 10, Right to Left logic as seen in image
            // Row 1 (pos 0-9): 10 ... 1
            // Row 2 (pos 10-19): 20 ... 11
            // Row 3 (pos 20-29): 30 ... 21
            // è®¡ç®—格子ID:3行10列,从左到右排列
            // ç¬¬1行(位置0-9):1-10
            // ç¬¬2行(位置10-19):11-20
            // ç¬¬3行(位置20-29):21-30
            int row = position / 10;
            int col = position % 10;
            int boxId = (row + 1) * 10 - col;
            int boxId = row * 10 + col + 1;
            
            holder.tvBoxNumber.setText(String.valueOf(boxId));
            
            // Find status for this box
            // æŸ¥æ‰¾å¯¹åº”格子的状态
            BoxStatus status = null;
            for (BoxStatus s : boxStatusList) {
                if (s.id == boxId) {
@@ -632,16 +790,24 @@
                }
            }
            
            // æ ¹æ®è‡ªæ£€ç»“果设置背景颜色
            if (status != null) {
                if (status.checkResult == 1) {
                    // Success
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // Green
                    // è‡ªæ£€æˆåŠŸï¼Œæ˜¾ç¤ºç»¿è‰²
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_full);
                    holder.viewBox.getBackground().clearColorFilter();
                } else if (status.checkResult == 0) {
                    // Fail
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_error); // Red
                    // è‡ªæ£€å¤±è´¥ï¼Œæ˜¾ç¤ºçº¢è‰²
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_error);
                    holder.viewBox.getBackground().clearColorFilter();
                } else if (status.checkResult == 2) {
                    // ç­‰å¾…状态,显示灰色
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty);
                    holder.viewBox.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY);
                } else {
                    // Unknown / Initial
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // White
                    // æœªçŸ¥çŠ¶æ€ï¼Œæ˜¾ç¤ºç™½è‰²
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty);
                    holder.viewBox.getBackground().clearColorFilter();
                }
            }
        }
@@ -662,4 +828,25 @@
            }
        }
    }
    private void restoreCurrentButton() {
        if (currentOperatingButton != null) {
            updateButtonState(currentOperatingButton, true);
            currentOperatingButton = null;
        }
    }
    private void updateButtonState(Button btn, boolean enable) {
        if (btn == null) return;
        btn.setEnabled(enable);
        if (enable) {
            if (btn.getBackground() != null) {
                btn.getBackground().clearColorFilter();
            }
        } else {
            if (btn.getBackground() != null) {
                btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY);
            }
        }
    }
}
app/src/main/java/com/shlb/comb/activity/SystemSettingsActivity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
package com.shlb.comb.activity;
import android.widget.CompoundButton;
import android.widget.Toast;
import com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView;
import com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;
import com.shlb.comb.R;
import com.shlb.comb.base.BaseActivity;
import com.shlb.comb.event.UpdateEvent;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class SystemSettingsActivity extends BaseActivity {
    private QMUIGroupListView mGroupListView;
    @Override
    protected void contentView() {
        setContentView(R.layout.activity_system_settings);
    }
    @Override
    protected void initView() {
        super.initHead();
        setTitle("系统设置");
        // Set left button to back icon
        if (iv_left != null) {
            iv_left.setImageResource(R.mipmap.icon_back);
            iv_left.setOnClickListener(v -> finish());
        }
        // Hide right text (connection status)
        android.view.View tvRightText = findViewById(R.id.tv_right_text);
        if (tvRightText != null) {
            tvRightText.setVisibility(android.view.View.GONE);
        }
        mGroupListView = findViewById(R.id.groupListView);
        initGroupListView();
        // Hide right button if not needed
        if (iv_right != null) {
            iv_right.setVisibility(android.view.View.GONE);
        }
    }
    @Override
    protected void initData() {
    }
    @Override
    protected void initEvent() {
    }
    private void initGroupListView() {
        QMUICommonListItemView itemCmdLog = mGroupListView.createItemView("CMD日志");
        itemCmdLog.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_SWITCH);
        boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false);
        itemCmdLog.getSwitch().setChecked(isCmdLogEnabled);
        itemCmdLog.getSwitch().setOnCheckedChangeListener((buttonView, isChecked) -> {
            com.blankj.utilcode.util.SPUtils.getInstance().put("cmd_log_enabled", isChecked);
            Toast("CMD日志: " + (isChecked ? "开启" : "关闭"));
        });
//        QMUICommonListItemView itemTestPage = mGroupListView.createItemView("测试页面");
//        itemTestPage.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_SWITCH);
//        itemTestPage.getSwitch().setOnCheckedChangeListener((buttonView, isChecked) -> {
//            Toast("测试页面: " + (isChecked ? "开启" : "关闭"));
//        });
        QMUIGroupListView.newSection(this)
                .setTitle("设置")
                .addItemView(itemCmdLog, null)
//                .addItemView(itemTestPage, null)
                .addTo(mGroupListView);
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(UpdateEvent event) {
        // Placeholder to satisfy EventBus requirements in BaseActivity
    }
}
app/src/main/java/com/shlb/comb/fragment/DeviceListFragment.java
@@ -356,8 +356,6 @@
        if (scanManager != null) {
            scanManager.setScanCallbackCompat(null);
            scanManager.setScanOverListener(null);
            // é‡Šæ”¾èµ„源
            scanManager.release();
        }
    }
}
app/src/main/java/com/shlb/comb/fragment/SettingsFragment.java
@@ -1,5 +1,6 @@
package com.shlb.comb.fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,32 +35,50 @@
import android.text.Html;
import android.content.DialogInterface;
/**
 * è®¾ç½®ç•Œé¢ Fragment
 * <p>
 * è¯¥ç±»è´Ÿè´£è®¾å¤‡å‚数的配置、设备状态的监控以及蓝牙通信的处理:
 * 1. æ”¯æŒç«™å·ã€å±‚数、波特率等参数的读取和写入
 * 2. å®žæ—¶ç›‘控设备在线状态和玻璃有无情况
 * 3. æä¾›è“ç‰™è¿žæŽ¥çŠ¶æ€çš„æ˜¾ç¤ºå’Œå¤„ç†
 * 4. å®žçŽ°æ—¥å¿—è®°å½•å’Œæ˜¾ç¤ºåŠŸèƒ½
 * 5. æ”¯æŒå‘¨æœŸæ€§æ•°æ®è¯»å–(每1秒),避免频繁日志导致页面卡顿
 * </p>
 */
public class SettingsFragment extends Fragment {
    private RecyclerView rvGrid;
    private EditText etLayer;
    private EditText etStation;
    private Spinner spinnerBaud;
    private QMUIRoundButton btnWriteAll;
    private QMUIRoundButton btnReadData;
    private QMUIRoundButton btnReadParam;
    private QMUIRoundButton btnClearLog;
    private TextView tvStatus;
    private TextView tvLog;
    private TextView tvLayerStatus;
    private TextView tvStationStatus;
    private TextView tvBaudStatus;
    private android.widget.ScrollView svLog;
    private QMUITipDialog mLoadingDialog;
    private GridAdapter mAdapter;
    private List<BoxStatus> boxStatusList = new ArrayList<>();
    private StringBuilder logBuilder = new StringBuilder();
    // ç”¨æˆ·ç•Œé¢ç»„ä»¶
    private RecyclerView rvGrid; // è®¾å¤‡çŠ¶æ€ç½‘æ ¼è§†å›¾
    private TextView etLayer; // å±‚数显示文本框
    private EditText etStation; // ç«™å·è¾“入框
    private Spinner spinnerBaud; // æ³¢ç‰¹çŽ‡é€‰æ‹©å™¨
    private QMUIRoundButton btnWriteAll; // å†™å…¥æ‰€æœ‰å‚数按钮
    private QMUIRoundButton btnReadParam; // è¯»å–参数按钮
    private QMUIRoundButton btnClearLog; // æ¸…除日志按钮
    private TextView tvMonitorTitle; // ç›‘控详情标题
    private TextView tvMonitorUpdateTime; // ç›‘控数据更新时间
    private TextView tvStatus; // çŠ¶æ€æ˜¾ç¤ºæ–‡æœ¬
    private TextView tvLog; // æ—¥å¿—显示文本
    private TextView tvLayerStatus; // å±‚数状态标签
    private TextView tvStationStatus; // ç«™å·çŠ¶æ€æ ‡ç­¾
    private TextView tvBaudStatus; // æ³¢ç‰¹çŽ‡çŠ¶æ€æ ‡ç­¾
    private java.util.Queue<String> cmdQueue = new java.util.LinkedList<>();
    private android.widget.ScrollView svLog; // æ—¥å¿—滚动视图
    private androidx.cardview.widget.CardView cvLog; // æ—¥å¿—容器
    private QMUITipDialog mLoadingDialog; // åŠ è½½å¯¹è¯æ¡†
    private GridAdapter mAdapter; // ç½‘格适配器
    private List<BoxStatus> boxStatusList = new ArrayList<>(); // è®¾å¤‡çŠ¶æ€åˆ—è¡¨
    private StringBuilder logBuilder = new StringBuilder(); // æ—¥å¿—内容构建器
    private java.util.Queue<String> cmdQueue = new java.util.LinkedList<>(); // æŒ‡ä»¤é˜Ÿåˆ—,用于按顺序执行指令
    private String currentExecutingCmd = ""; // å½“前正在执行的指令,用于校验响应
    private boolean isPeriodicRead = false; // æ ‡è®°æ˜¯å¦ä¸ºå‘¨æœŸæ€§è¯»å–,用于控制日志记录
    private boolean isFirstLoad = true; // æ ‡è®°æ˜¯å¦ä¸ºé¦–次加载,避免重复触发读取参数
    private View currentOperatingButton; // å½“前正在操作的按钮
    private static class BoxStatus {
        int id;
        boolean isOnline;
@@ -72,30 +91,61 @@
        }
    }
    private Runnable autoReadRunnable = new Runnable() {
    // å‘¨æœŸæ€§è¯»å–数据相关变量
    private android.os.Handler periodicReadHandler = new android.os.Handler(); // ç”¨äºŽå¤„理周期性读取任务的Handler
    private static final long PERIODIC_READ_INTERVAL = 500; // å‘¨æœŸæ€§è¯»å–间隔(毫秒):1秒
    private Runnable periodicReadRunnable = new Runnable() {
        @Override
        public void run() {
            // æ¯1秒读取一次数据,不记录日志
            if (BleGlobalManager.getInstance().isConnected()) {
                periodicReadData();
                // ç»§ç»­å®‰æŽ’下一次读取
                periodicReadHandler.postDelayed(this, PERIODIC_READ_INTERVAL);
            }
        }
    };
    // è‡ªåŠ¨è¯»å–å‚æ•°ç›¸å…³å˜é‡
    private Runnable autoReadRunnable = new Runnable() { // è“ç‰™è¿žæŽ¥æˆåŠŸåŽè‡ªåŠ¨è¯»å–å‚æ•°çš„ä»»åŠ¡
        @Override
        public void run() {
            // è“ç‰™è¿žæŽ¥æˆåŠŸåŽ è‡ªåŠ¨è§¦å‘ç›‘æŽ§è¯¦æƒ…çš„ è¯»å–数据 å’Œ å‚数设定这里的 è¯»å–参数
            if (BleGlobalManager.getInstance().isConnected()) {
                showLoading("正在同步数据...");
                
                if (btnReadData != null) btnReadData.performClick();
                // å…ˆè§¦å‘读取数据
                tvStatus.setText("状态:正在读取数据...");
                if (tvMonitorTitle != null) tvMonitorTitle.performClick();
                new android.os.Handler().postDelayed(() -> {
                    // å†è§¦å‘读取参数
                    if (btnReadParam != null) btnReadParam.performClick();
                    
                    // å‡è®¾å‚数读取触发后 1.5秒 å…³é—­ loading,或者在解析完所有参数后关闭
                    // è¿™é‡Œç®€å•处理,延时关闭
                    new android.os.Handler().postDelayed(() -> {
                         dismissLoading();
                         // åˆå§‹è®¾ç½®å®ŒæˆåŽå¯åŠ¨å‘¨æœŸæ€§è¯»å–
                         startPeriodicRead(PERIODIC_READ_INTERVAL);
                         isFirstLoad = false; // é¦–次加载完成
                    }, 1500);
                }, 1000); // é—´éš”1秒,避免指令冲突
            }
        }
    };
    private android.os.Handler debounceHandler = new android.os.Handler();
    private static final long DEBOUNCE_DELAY_MS = 1500; // 1.5 seconds debounce
    private android.os.Handler debounceHandler = new android.os.Handler(); // ç”¨äºŽé˜²æŠ–处理的Handler
    private static final long DEBOUNCE_DELAY_MS = 1500; // é˜²æŠ–延迟时间(毫秒):1.5秒
/**
 * Fragment å¯è§æ—¶è°ƒç”¨
 * <p>
 * æ³¨å†Œ EventBus å¹¶æ›´æ–°è“ç‰™è¿žæŽ¥çŠ¶æ€
 * </p>
 */
    @Override
    public void onStart() {
        super.onStart();
@@ -105,11 +155,70 @@
        updateConnectionStatus();
    }
/**
 * Fragment ä¸å¯è§æ—¶è°ƒç”¨
 * <p>
 * å–消注册 EventBus,避免内存泄漏
 * </p>
 */
    @Override
    public void onStop() {
        super.onStop();
        if (EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().unregister(this);
        }
    }
/**
 * Fragment æ¢å¤å¯è§æ—¶è°ƒç”¨
 * \u003cp\u003e
 * å¦‚果蓝牙已连接,恢复周期性读取数据
 * \u003c/p\u003e
 */
    @Override
    public void onResume() {
        super.onResume();
        // æ ¹æ®ç³»ç»Ÿè®¾ç½®æŽ§åˆ¶æ—¥å¿—显示
        boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false);
        if (cvLog != null) {
            cvLog.setVisibility(isCmdLogEnabled ? View.VISIBLE : View.GONE);
        }
        // å¦‚果蓝牙已连接,恢复周期性读取
        if (BleGlobalManager.getInstance().isConnected()) {
            // åªæœ‰å½“不是首次加载(即从其他页面返回)时才手动触发读取参数
            // é¦–次加载会走 autoReadRunnable
            if (!isFirstLoad && btnReadParam != null) {
                btnReadParam.performClick();
            }
            startPeriodicRead(3000);
        }
    }
/**
 * Fragment æš‚停时调用
 * \u003cp\u003e
 * åœæ­¢å‘¨æœŸæ€§è¯»å–数据,节省资源
 * \u003c/p\u003e
 */
    @Override
    public void onPause() {
        super.onPause();
        // ç¦»å¼€é¡µé¢æ—¶åœæ­¢å‘¨æœŸæ€§è¯»å–
        stopPeriodicRead();
    }
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        // æ¸…理所有 Handler å›žè°ƒï¼Œé¿å…å†…存泄漏
        stopPeriodicRead();
        debounceHandler.removeCallbacks(autoReadRunnable);
        // æ¸…理加载对话框
        if (mLoadingDialog != null) {
            mLoadingDialog.dismiss();
            mLoadingDialog = null;
        }
    }
@@ -121,40 +230,66 @@
        }
    }
/**
 * äº‹ä»¶æ€»çº¿ç›‘听器
 * <p>
 * å¤„理蓝牙连接状态变化和设备数据接收事件
 * </p>
 * @param event äº‹ä»¶å¯¹è±¡
 */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEventRefresh(UpdateEvent event) {
        if (event.getType() == UpdateEvent.Type.CONN_STATU) {
            // Check if obj is integer
            // æ£€æŸ¥obj是否为整数
            if (event.getObj() instanceof Integer) {
                 int status = (int) event.getObj();
                 if (status == BluetoothProfile.STATE_CONNECTED) {
                     tvStatus.setText("状态:已连接");
                     Toast.makeText(getContext(), "蓝牙已连接", Toast.LENGTH_SHORT).show();
                     // è“ç‰™è¿žæŽ¥æˆåŠŸåŽ è‡ªåŠ¨è§¦å‘
                     triggerAutoRead();
                 } else {
                     tvStatus.setText("状态:已断开");
                     // è“ç‰™æ–­å¼€æ—¶åœæ­¢å‘¨æœŸæ€§è¯»å–
                     stopPeriodicRead();
                 }
            }
        } else if (event.getType() == UpdateEvent.Type.DEVICE_INFO) {
            // Received data
            // æ”¶åˆ°æ•°æ®
            String hex = event.getMsg();
            tvStatus.setText("收到数据: " + hex);
            appendLog(hex, false); // false for received (Green)
            if (tvStatus != null && !isPeriodicRead) {
                tvStatus.setText("收到数据: " + hex);
            }
            // æ£€æŸ¥æ˜¯å¦ä¸ºå‘¨æœŸæ€§è¯»å–
            if (!isPeriodicRead) {
                appendLog(hex, false); // false表示接收的数据(绿色)
            }
            parseAndRefresh(hex);
            // æ— è®ºæ˜¯å¦ä¸ºå‘¨æœŸæ€§è¯»å–,处理完成后都重置标记
            isPeriodicRead = false;
        }
    }
    
/**
 * è§£æžå¹¶åˆ·æ–°æ•°æ®
 * <p>
 * æ ¹æ®æŒ‡ä»¤ç±»åž‹åˆ†å‘到不同的解析方法
 * </p>
 * @param hex åå…­è¿›åˆ¶æ ¼å¼çš„原始数据
 */
    private void parseAndRefresh(String hex) {
        if (hex == null) return;
        // æ ¹æ®CMD前8位判断指令类型
        // æ ¹æ®æŒ‡ä»¤å‰8位判断指令类型
        if (hex.length() >= 8) {
            // å‚数读取返回 (长度至少12位)
            // è¯»å±‚æ•°: A55A0308...
            // è¯»ç«™å·: A55A0304...
            // è¯»æ³¢ç‰¹çއ: A55A0305...
            // è¯»å±‚数指令: A55A0308...
            // è¯»ç«™å·æŒ‡ä»¤: A55A0304...
            // è¯»æ³¢ç‰¹çŽ‡æŒ‡ä»¤: A55A0305...
            if (hex.length() >= 12 && (
                hex.startsWith(CMD.READ_FLOORS.substring(0, 8)) ||
                hex.startsWith(CMD.READ_STATION_NUM.substring(0, 8)) ||
@@ -164,14 +299,14 @@
            }
            // æ•°æ®è¯»å–返回 (长度至少32位)
            // è¯»æ•°æ®: A55A0301...
            // è¯»æ•°æ®æŒ‡ä»¤: A55A0301...
            if (hex.length() >= 32 && hex.startsWith(CMD.READ_DATA.substring(0, 8))) {
                parseDataResponse(hex);
                return;
            }
        }
        // å†™å…¥æŒ‡ä»¤çš„返回 (A55A06开头)
        // å†™å…¥æŒ‡ä»¤çš„返回 (以A55A06开头)
        if (hex.startsWith("A55A06") && hex.length() >= 12) {
            parseWriteResponse(hex);
            return;
@@ -208,11 +343,18 @@
                }
                setLabelStatus(tvBaudStatus, "(读取值)", R.color.base_color);
                appendLog("读取波特率: " + value);
                // è¯»å–结束,恢复按钮状态
                restoreCurrentButton();
            } else if ("08".equals(cmdType)) {
                // å±‚æ•°
                if (etLayer != null) etLayer.setText(String.valueOf(value));
                if (etLayer != null) {
                    etLayer.setText(String.valueOf(value));
                    etLayer.setTextColor(0xFF333333);
                }
                setLabelStatus(tvLayerStatus, "(读取值)", R.color.base_color);
                appendLog("读取层数: " + value);
                com.blankj.utilcode.util.SPUtils.getInstance().put("layer_count", value);
            }
        } catch (Exception e) {
            e.printStackTrace();
@@ -259,6 +401,7 @@
                    cmdQueue.clear();
                    currentExecutingCmd = "";
                    tvStatus.setText("写入失败");
                    restoreCurrentButton(); // æ¢å¤æŒ‰é’®
                    
                    if (currentExecutingCmd.startsWith(CMD.WRITE_STATION_NUM)) {
                        setLabelStatus(tvStationStatus, "(写入失败)", R.color.orange);
@@ -278,6 +421,7 @@
            // å¼‚常也视为失败
            setLabelStatus(tvStationStatus, "(写入失败)", R.color.orange);
            setLabelStatus(tvBaudStatus, "(写入失败)", R.color.orange);
            restoreCurrentButton(); // å¼‚常恢复按钮
        }
    }
@@ -303,25 +447,38 @@
            currentExecutingCmd = "";
            tvStatus.setText("参数写入完成");
            Toast.makeText(getContext(), "参数写入成功", Toast.LENGTH_SHORT).show();
            restoreCurrentButton(); // å…¨éƒ¨å®Œæˆï¼Œæ¢å¤æŒ‰é’®
        }
    }
    /**
     * è§£æžç›‘控数据返回 (是否有玻璃、在线状态)
     */
/**
 * è§£æžç›‘控数据返回
 * <p>
 * è§£æžè®¾å¤‡åœ¨çº¿çŠ¶æ€å’ŒçŽ»ç’ƒæœ‰æ— æƒ…å†µ
 * </p>
 * @param hex åå…­è¿›åˆ¶æ ¼å¼çš„原始数据
 */
    private void parseDataResponse(String hex) {
        try {
            // è§£æžæ˜¯å¦æœ‰çŽ»ç’ƒ: ç¬¬11-18位 (8个字符 = 32位)
            // ç´¢å¼• 10-17
            String glassHex = hex.substring(10, 18);
            appendLog("解析玻璃数据: " + glassHex);
            if(!isPeriodicRead){
                appendLog("解析玻璃数据: " + glassHex);
            }
            
            // ç›´æŽ¥è§£æž hex ä¸º long (Big Endian)
            // "00000004" -> 4 -> ...00100 -> Bit 2 -> Box 3
            // ç›´æŽ¥å°†åå…­è¿›åˆ¶è§£æžä¸ºé•¿æ•´åž‹ (大端序)
            // "00000004" -> 4 -> ...00100 -> ç¬¬2位 -> ç¬¬3个格子
            long glassBits = Long.parseLong(glassHex, 16);
            
            String onlineHex = hex.substring(18, 26);
            if(!isPeriodicRead){
            appendLog("解析在线数据: " + onlineHex);
            }
            long onlineBits = Long.parseLong(onlineHex, 16);
            
            // æ›´æ–°30个格子的状态
@@ -337,6 +494,12 @@
            // åˆ·æ–°åˆ—表显示
            if (mAdapter != null) {
                mAdapter.notifyDataSetChanged();
            }
            // æ›´æ–°ç›‘控数据接收时间
            if (tvMonitorUpdateTime != null) {
                String currentTime = com.blankj.utilcode.util.TimeUtils.getNowString(new java.text.SimpleDateFormat("HH:mm:ss"));
                tvMonitorUpdateTime.setText(currentTime);
            }
            
        } catch (Exception e) {
@@ -358,7 +521,7 @@
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        
        // Initialize box status list
        // åˆå§‹åŒ–设备状态列表
        boxStatusList.clear();
        for (int i = 1; i <= 30; i++) {
            boxStatusList.add(new BoxStatus(i));
@@ -378,6 +541,82 @@
        debounceHandler.postDelayed(autoReadRunnable, DEBOUNCE_DELAY_MS);
    }
    
    private void startPeriodicRead(long delay) {
        // å…ˆåœæ­¢å¯èƒ½å­˜åœ¨çš„任务,避免重复启动
        stopPeriodicRead();
        // å¯åŠ¨å‘¨æœŸæ€§è¯»å–
        periodicReadHandler.postDelayed(periodicReadRunnable, delay);
    }
    private void stopPeriodicRead() {
        periodicReadHandler.removeCallbacks(periodicReadRunnable);
    }
    /**
     * ä¸“门用于周期性读取数据的方法,不记录日志
     */
/**
 * å‘¨æœŸæ€§è¯»å–数据(每1秒)
 * <p>
 * ä¸“门用于周期性读取数据,不记录日志以避免页面卡顿
 * </p>
 */
    private void periodicReadData() {
        try {
            if (BleGlobalManager.getInstance() != null && BleGlobalManager.getInstance().isConnected()) {
                if (tvStatus != null && isPeriodicRead) {
                    tvStatus.setText("状态:正在读取数据...");
                }
                // è®¾ç½®å‘¨æœŸæ€§è¯»å–标记
                isPeriodicRead = true;
                // ç›´æŽ¥å‘送命令,不记录日志
                sendCmdWithCrcNoLog(CMD.READ_DATA);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // å¼‚常也不记录日志,避免卡顿
            // ç¡®ä¿æ ‡è®°è¢«é‡ç½®
            isPeriodicRead = false;
        }
    }
    /**
     * å‘送命令但不记录日志(用于周期性读取)
     */
/**
 * å‘送带 CRC æ ¡éªŒçš„æŒ‡ä»¤ï¼ˆä¸è®°å½•日志)
 * <p>
 * ä¸“门用于周期性读取数据,避免频繁记录日志导致页面卡顿
 * </p>
 * @param cmd åå…­è¿›åˆ¶æ ¼å¼çš„æŒ‡ä»¤å†…容
 */
    private void sendCmdWithCrcNoLog(String cmd) {
        if (!BleGlobalManager.getInstance().isConnected()) {
            return; // é™é»˜è¿”回,不显示 Toast æˆ–日志
        }
        byte[] cmdBytes = BleGlobalManager.hexStringToBytes(cmd);
        if (cmdBytes != null) {
            String crc = CRCutil.getCRC(cmdBytes);
            // ç¡®ä¿CRC为4个字符,不足则补零
            while (crc.length() < 4) {
                crc = "0" + crc;
            }
            String fullCmd = cmd + crc.toUpperCase();
            // ä¸è®°å½•日志,直接发送命令
            BleGlobalManager.getInstance().sendCmd(fullCmd);
        }
    }
/**
 * æ˜¾ç¤ºåŠ è½½å¯¹è¯æ¡†
 * <p>
 * ç”¨äºŽæ“ä½œè¿‡ç¨‹ä¸­çš„等待提示
 * </p>
 * @param msg åŠ è½½æç¤ºä¿¡æ¯
 */
    private void showLoading(String msg) {
        if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
            mLoadingDialog.dismiss();
@@ -386,25 +625,36 @@
                .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
                .setTipWord(msg)
                .create();
        // å…è®¸ç‚¹å‡»å¤–部或返回键取消 loading(只是关闭 dialog)
        // å…è®¸ç‚¹å‡»å¤–部或返回键取消加载(仅关闭对话框)
        mLoadingDialog.setCancelable(true);
        mLoadingDialog.setCanceledOnTouchOutside(true);
        mLoadingDialog.show();
    }
    
/**
 * å…³é—­åŠ è½½å¯¹è¯æ¡†
 */
    private void dismissLoading() {
        if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
            mLoadingDialog.dismiss();
        }
    }
/**
 * åˆå§‹åŒ–界面组件
 * <p>
 * è®¾ç½®å„个UI组件的引用和监听器
 * </p>
 * @param view Fragment çš„æ ¹è§†å›¾
 */
    private void initView(View view) {
        rvGrid = view.findViewById(R.id.rv_grid);
        etLayer = view.findViewById(R.id.et_layer);
        etStation = view.findViewById(R.id.et_station);
        spinnerBaud = view.findViewById(R.id.spinner_baud);
        btnWriteAll = view.findViewById(R.id.btn_write_all);
        btnReadData = view.findViewById(R.id.btn_read_data);
        tvMonitorTitle = view.findViewById(R.id.tv_monitor_title);
        tvMonitorUpdateTime = view.findViewById(R.id.tv_monitor_update_time);
        btnReadParam = view.findViewById(R.id.btn_read_param);
        btnClearLog = view.findViewById(R.id.btn_clear_log);
        tvStatus = view.findViewById(R.id.tv_status);
@@ -412,7 +662,9 @@
        svLog = view.findViewById(R.id.sv_log);
        tvLayerStatus = view.findViewById(R.id.tv_layer_status);
        tvStationStatus = view.findViewById(R.id.tv_station_status);
        tvBaudStatus = view.findViewById(R.id.tv_baud_status);
        cvLog = view.findViewById(R.id.cv_log);
        // è§£å†³æ—¥å¿—区域滑动冲突
        svLog.setOnTouchListener((v, event) -> {
@@ -423,7 +675,7 @@
            return false;
        });
        
        // Restore logs if any
        // æ¢å¤æ—¥å¿—(如果有)
        if (logBuilder.length() > 0) {
            tvLog.setText(Html.fromHtml(logBuilder.toString()));
        } else {
@@ -431,21 +683,25 @@
            tvLog.setText(Html.fromHtml(logBuilder.toString()));
        }
        // Grid Setup
        // 10 columns to match the image (10 boxes per row)
        // Since we are in a horizontal scroll view, this will layout correctly
        // ç½‘格布局设置
        // æ¯è¡Œ10个格子
        // ç”±äºŽåœ¨æ°´å¹³æ»šåŠ¨è§†å›¾ä¸­ï¼Œå¸ƒå±€ä¼šè‡ªåŠ¨è°ƒæ•´
        rvGrid.setLayoutManager(new GridLayoutManager(getContext(), 10)); 
        mAdapter = new GridAdapter();
        rvGrid.setAdapter(mAdapter);
        // Spinner Setup
        // æ³¢ç‰¹çŽ‡é€‰æ‹©å™¨è®¾ç½®
        String[] baudRates = new String[]{"156Kbps", "625Kbps", "2.5Mbps", "5Mbps", "10Mbps"};
        ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, baudRates);
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerBaud.setAdapter(spinnerAdapter);
        spinnerBaud.setSelection(0); // Select 156Kbps by default
        spinnerBaud.setSelection(0); // é»˜è®¤é€‰æ‹©156Kbps
        // Button Listeners
        tvMonitorTitle.setOnClickListener(v -> {
            onMonitorTitleClick();
        });
        // æŒ‰é’®ç›‘听器设置
        btnWriteAll.setOnClickListener(v -> {
            if (!BleGlobalManager.getInstance().isConnected()) {
                Toast.makeText(getContext(), "请先连接蓝牙", Toast.LENGTH_SHORT).show();
@@ -470,6 +726,9 @@
                return;
            }
            currentOperatingButton = btnWriteAll;
            updateButtonState(currentOperatingButton, false); // Disable buttons, gray out write button
            // æž„造指令队列
            cmdQueue.clear();
            
@@ -493,26 +752,19 @@
            processNextCmd();
        });
        btnReadData.setOnClickListener(v -> {
             if (BleGlobalManager.getInstance().isConnected()) {
                 tvStatus.setText("状态:正在读取数据...");
                 sendCmdWithCrc(CMD.READ_DATA);
             } else {
                 Toast.makeText(getContext(), "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                 appendLog("错误: è“ç‰™æœªè¿žæŽ¥");
             }
        });
        btnReadParam.setOnClickListener(v -> {
             if (BleGlobalManager.getInstance().isConnected()) {
                 currentOperatingButton = btnReadParam;
                 updateButtonState(currentOperatingButton, false); // Disable buttons, gray out read button
                 tvStatus.setText("状态:正在读取参数...");
                 sendCmdWithCrc(CMD.READ_FLOORS);
                 new android.os.Handler().postDelayed(() -> {
                     sendCmdWithCrc(CMD.READ_STATION_NUM);
                 }, 200);
                 }, 500);
                 new android.os.Handler().postDelayed(() -> {
                     sendCmdWithCrc(CMD.READ_BAUD_RATE);
                 }, 400);
                 }, 1000);
             } else {
                 Toast.makeText(getContext(), "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                 appendLog("错误: è“ç‰™æœªè¿žæŽ¥");
@@ -526,6 +778,41 @@
        });
    }
/**
 * ç›‘控详情标题点击事件
 * <p>
 * æ‰‹åŠ¨è§¦å‘æ•°æ®è¯»å–ï¼Œä¼šè®°å½•å®Œæ•´æ—¥å¿—
 * </p>
 */
    public void onMonitorTitleClick() {
        try {
            if (BleGlobalManager.getInstance() != null && BleGlobalManager.getInstance().isConnected()) {
                if (tvStatus != null) {
                    tvStatus.setText("状态:正在读取数据...");
                }
                // ç¡®ä¿ä¸æ˜¯å‘¨æœŸæ€§è¯»å–,以便记录日志
                isPeriodicRead = false;
                sendCmdWithCrc(CMD.READ_DATA);
            } else {
                Context context = getContext();
                if (context != null) {
                    Toast.makeText(context, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
                }
                appendLog("错误: è“ç‰™æœªè¿žæŽ¥");
            }
        } catch (Exception e) {
            e.printStackTrace();
            appendLog("点击监控详情时发生错误: " + e.getMessage());
        }
    }
/**
 * è®°å½•普通日志
 * <p>
 * å°†æ—¥å¿—信息添加到日志构建器并显示
 * </p>
 * @param msg æ—¥å¿—内容
 */
    private void appendLog(String msg) {
        appendLog(msg, null);
    }
@@ -579,13 +866,38 @@
        return "";
    }
    private void updateButtonState(View btn, boolean enable) {
        if (btn == null) return;
        btn.setEnabled(enable);
        if (enable) {
            btn.getBackground().clearColorFilter();
        } else {
            btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY);
        }
    }
    private void restoreCurrentButton() {
        if (currentOperatingButton != null) {
            updateButtonState(currentOperatingButton, true);
            currentOperatingButton = null;
        }
    }
/**
 * è®°å½•日志
 * <p>
 * å°†æ—¥å¿—信息添加到日志构建器并显示,支持发送/接收类型的区分
 * </p>
 * @param msg æ—¥å¿—内容
 * @param isSent true表示发送的指令,false表示接收的数据
 */
    private void appendLog(String msg, Boolean isSent) {
        String time = com.blankj.utilcode.util.TimeUtils.getNowString(new java.text.SimpleDateFormat("HH:mm:ss.SSS"));
        
        String displayMsg = msg;
        String cmdDesc = "";
        
        // å¦‚果是 hex æŒ‡ä»¤ (纯 0-9 A-F a-f),加空格格式化
        // å¦‚果是十六进制指令 (纯 0-9 A-F a-f),加空格格式化
        if (msg.matches("^[0-9A-Fa-f]+$")) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < msg.length(); i += 2) {
@@ -603,7 +915,7 @@
        String logLine;
        if (isSent != null) {
            String color = isSent ? "#1890ff" : "#05aa87"; // Blue for sent, Green for received
            String color = isSent ? "#1890ff" : "#05aa87"; // å‘送为蓝色,接收为绿色
            // æ‹¼æŽ¥åˆ°å‰ç¼€åŽé¢: "发送-读站号: " æˆ– "收到-读站号: "
            String prefix = (isSent ? "发送" : "收到") + cmdDesc + ": ";
            logLine = time + " <font color='" + color + "'>" + prefix + displayMsg + "</font><br>";
@@ -621,6 +933,13 @@
        }
    }
/**
 * å‘送带 CRC æ ¡éªŒçš„æŒ‡ä»¤
 * <p>
 * è®¡ç®—指令的 CRC æ ¡éªŒå€¼å¹¶å‘送,同时记录日志
 * </p>
 * @param cmd åå…­è¿›åˆ¶æ ¼å¼çš„æŒ‡ä»¤å†…容
 */
    private void sendCmdWithCrc(String cmd) {
        if (!BleGlobalManager.getInstance().isConnected()) {
            Toast.makeText(getContext(), "请先连接蓝牙", Toast.LENGTH_SHORT).show();
@@ -637,7 +956,7 @@
            }
            String fullCmd = cmd + crc.toUpperCase();
            
            appendLog(fullCmd, true); // true for sent (Blue)
            appendLog(fullCmd, true); // true表示发送的指令(蓝色)
            BleGlobalManager.getInstance().sendCmd(fullCmd);
        } else {
            appendLog("错误: æŒ‡ä»¤è½¬æ¢å¤±è´¥");
@@ -655,14 +974,14 @@
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            // Calculate Box ID based on 3 rows of 10, Right to Left logic as seen in image
            // Row 1 (pos 0-9): 10 ... 1
            // Row 2 (pos 10-19): 20 ... 11
            // Row 3 (pos 20-29): 30 ... 21
            // æ ¹æ®3行10列的网格,从左到右计算格子ID
            // ç¬¬1行 (位置0-9): 1 ... 10
            // ç¬¬2行 (位置10-19): 11 ... 20
            // ç¬¬3行 (位置20-29): 21 ... 30
            
            int row = position / 10;
            int col = position % 10;
            int boxId = (row + 1) * 10 - col;
            int boxId = row * 10 + (col + 1);
            
            holder.tvBoxNumber.setText(String.valueOf(boxId));
            
@@ -676,18 +995,18 @@
            }
            
            if (status != null) {
                // Priority: Online > Glass
                // User requirement: "优先显示是否在线" (Prioritize displaying online status)
                // "19-26为也是16进制,解析成二进制32代码我这30个格子是否在线"
                // Usually this means if offline, show offline color. If online, show state (glass/no glass).
                // ä¼˜å…ˆçº§: åœ¨çº¿çŠ¶æ€ > çŽ»ç’ƒçŠ¶æ€
                // ç”¨æˆ·éœ€æ±‚: "优先显示是否在线"
                // "19-26位也是16进制,解析成二进制32位表示30个格子的在线状态"
                // é€šå¸¸æ„å‘³ç€å¦‚果离线,显示离线颜色;如果在线,显示玻璃状态(有/无)
                
                if (!status.isOnline) {
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_offline); // Offline (Grey)
                    holder.viewBox.setBackgroundResource(R.drawable.bg_box_offline); // ç¦»çº¿ï¼ˆç°è‰²ï¼‰
                } else {
                    if (status.hasGlass) {
                        holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // Green
                        holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // ç»¿è‰²
                    } else {
                        holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // Online but empty (White)
                        holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // åœ¨çº¿ä½†æ— çŽ»ç’ƒï¼ˆç™½è‰²ï¼‰
                    }
                }
            }
app/src/main/res/drawable/ic_arrow_drop_down.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#999999">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M7,10l5,5 5,-5H7z"/>
</vector>
app/src/main/res/layout/activity_home.xml
@@ -70,6 +70,7 @@
                android:orientation="vertical">
                <TextView
                    android:id="@+id/tv_menu_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="COMB"
@@ -112,8 +113,8 @@
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/icon_record"
                        app:tint="@color/drawer_item_text_gray" />
                        android:src="@mipmap/icon_1"
                        />
                    <TextView
                        android:layout_width="wrap_content"
@@ -131,14 +132,14 @@
                    android:layout_height="48dp"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:paddingLeft="20dp"
                    android:paddingLeft="21dp"
                    android:paddingRight="20dp">
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/ic_bluetooth"
                        app:tint="@color/drawer_item_text_gray" />
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:src="@mipmap/icon_2"
                         />
                    <TextView
                        android:layout_width="wrap_content"
@@ -148,13 +149,6 @@
                        android:textColor="@color/drawer_item_text_gray"
                        android:textSize="15sp" />
                </LinearLayout>
                <View
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:layout_marginTop="5dp"
                    android:layout_marginBottom="5dp"
                    android:background="@color/drawer_divider" />
                <!-- å‚数设定 -->
                <LinearLayout
@@ -169,8 +163,8 @@
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/icon_topbar_overflow"
                        app:tint="@color/drawer_item_text_gray" />
                        android:src="@mipmap/icon_3"
                          />
                    <TextView
                        android:layout_width="wrap_content"
@@ -181,12 +175,7 @@
                        android:textSize="15sp" />
                </LinearLayout>
                <View
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:layout_marginTop="5dp"
                    android:layout_marginBottom="5dp"
                    android:background="@color/drawer_divider" />
                <!-- å½“前设备 -->
                <LinearLayout
@@ -201,8 +190,8 @@
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/icon_topbar_about"
                        app:tint="@color/drawer_item_text_gray" />
                        android:src="@mipmap/icon_4"
                         />
                    <TextView
                        android:layout_width="wrap_content"
@@ -226,8 +215,8 @@
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/icon_topbar_overflow"
                        app:tint="@color/drawer_item_text_gray" />
                        android:src="@mipmap/icon_5"
                        />
                    <TextView
                        android:layout_width="wrap_content"
@@ -238,6 +227,32 @@
                        android:textSize="15sp" />
                </LinearLayout>
                <!-- ç³»ç»Ÿè®¾ç½® -->
                <LinearLayout
                    android:id="@+id/menu_system_settings"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:paddingLeft="20dp"
                    android:paddingRight="20dp"
                    android:visibility="gone">
                    <ImageView
                        android:layout_width="22dp"
                        android:layout_height="22dp"
                        android:src="@mipmap/icon_6"
                         />
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="25dp"
                        android:text="系统设置"
                        android:textColor="@color/drawer_item_text_gray"
                        android:textSize="15sp" />
                </LinearLayout>
            </LinearLayout>
        </ScrollView>
app/src/main/res/layout/activity_parameter_setting.xml
@@ -39,7 +39,7 @@
                        android:orientation="horizontal"
                        android:gravity="center_vertical"
                        android:padding="16dp"
                        android:background="#FAFAFA">
                        android:background="@color/white">
                        
                        <View
                            android:layout_width="4dp"
@@ -103,7 +103,7 @@
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="自检成功"
                                android:text="成功"
                                android:textSize="12sp"
                                android:textColor="#666666"
                                android:layout_marginEnd="16dp"/>
@@ -116,7 +116,7 @@
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="自检失败"
                                android:text="失败"
                                android:textSize="12sp"
                                android:textColor="#666666"
                                android:layout_marginEnd="16dp"/>
@@ -147,11 +147,9 @@
                                android:layout_height="48dp"
                                android:layout_weight="1"
                                android:text="结束自检"
                                android:textColor="@color/base_color"
                                android:textColor="@color/white"
                                android:textSize="14sp"
                                app:qmui_backgroundColor="@color/white"
                                app:qmui_borderColor="@color/base_color"
                                app:qmui_borderWidth="1dp"
                                app:qmui_backgroundColor="@color/base_color"
                                app:qmui_radius="8dp"
                                android:layout_marginLeft="10dp"/>
                        </LinearLayout>
@@ -189,15 +187,29 @@
                        android:layout_height="wrap_content"
                        android:orientation="horizontal">
                        <Spinner
                            android:id="@+id/sp_threshold_mode"
                        <FrameLayout
                            android:layout_width="0dp"
                            android:layout_height="48dp"
                            android:layout_weight="1.5"
                            android:entries="@array/threshold_modes"
                            android:background="@drawable/bg_input_field"
                            android:paddingStart="10dp"
                            android:layout_marginRight="10dp" />
                            android:layout_marginRight="10dp">
                            <Spinner
                                android:id="@+id/sp_threshold_mode"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"
                                android:entries="@array/threshold_modes"
                                android:background="@drawable/bg_input_field"
                                android:paddingStart="10dp"
                                android:paddingEnd="32dp" />
                            <ImageView
                                android:layout_width="24dp"
                                android:layout_height="24dp"
                                android:src="@drawable/ic_arrow_drop_down"
                                android:layout_gravity="center_vertical|end"
                                android:layout_marginEnd="8dp"
                                android:importantForAccessibility="no"/>
                        </FrameLayout>
                        <EditText
                            android:id="@+id/et_threshold_value"
@@ -218,18 +230,6 @@
                        android:orientation="horizontal">
                        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
                            android:id="@+id/btn_threshold_read"
                            android:layout_width="0dp"
                            android:layout_height="45dp"
                            android:layout_weight="1"
                            android:text="读取"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"
                            android:layout_marginRight="10dp"/>
                        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
                            android:id="@+id/btn_threshold_write"
                            android:layout_width="0dp"
                            android:layout_height="45dp"
@@ -237,7 +237,7 @@
                            android:text="写入"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/base_color_s"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"/>
                    </LinearLayout>
                </LinearLayout>
@@ -269,15 +269,29 @@
                        android:layout_height="wrap_content"
                        android:orientation="horizontal">
                        <Spinner
                            android:id="@+id/sp_sensitivity_mode"
                        <FrameLayout
                            android:layout_width="0dp"
                            android:layout_height="48dp"
                            android:layout_weight="1.5"
                            android:entries="@array/sensitivity_modes"
                            android:background="@drawable/bg_input_field"
                            android:paddingStart="10dp"
                            android:layout_marginRight="10dp" />
                            android:layout_marginRight="10dp">
                            <Spinner
                                android:id="@+id/sp_sensitivity_mode"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"
                                android:entries="@array/sensitivity_modes"
                                android:background="@drawable/bg_input_field"
                                android:paddingStart="10dp"
                                android:paddingEnd="32dp" />
                            <ImageView
                                android:layout_width="24dp"
                                android:layout_height="24dp"
                                android:src="@drawable/ic_arrow_drop_down"
                                android:layout_gravity="center_vertical|end"
                                android:layout_marginEnd="8dp"
                                android:importantForAccessibility="no"/>
                        </FrameLayout>
                        <EditText
                            android:id="@+id/et_sensitivity_value"
@@ -298,18 +312,6 @@
                        android:orientation="horizontal">
                        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
                            android:id="@+id/btn_sensitivity_read"
                            android:layout_width="0dp"
                            android:layout_height="45dp"
                            android:layout_weight="1"
                            android:text="读取"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"
                            android:layout_marginRight="10dp"/>
                        <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
                            android:id="@+id/btn_sensitivity_write"
                            android:layout_width="0dp"
                            android:layout_height="45dp"
@@ -317,7 +319,7 @@
                            android:text="写入"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/base_color_s"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"/>
                    </LinearLayout>
                </LinearLayout>
@@ -356,7 +358,7 @@
                            android:text="主板复位"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/orange"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"
                            android:layout_marginRight="10dp"/>
@@ -368,7 +370,7 @@
                            android:text="传感复位"
                            android:textColor="@color/white"
                            android:textSize="14sp"
                            app:qmui_backgroundColor="@color/orange"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"
                            android:layout_marginLeft="10dp"/>
                    </LinearLayout>
@@ -377,6 +379,7 @@
            <!-- Log Display -->
            <androidx.cardview.widget.CardView
                android:id="@+id/cv_log"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardCornerRadius="12dp"
app/src/main/res/layout/activity_system_settings.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/qmui_config_color_background"
    android:orientation="vertical">
    <include layout="@layout/include_head"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView
            android:id="@+id/groupListView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="20dp"/>
    </ScrollView>
</LinearLayout>
app/src/main/res/layout/fragment_settings.xml
@@ -28,40 +28,40 @@
                <!-- Card Header -->
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal"
                    android:gravity="center_vertical"
                    android:padding="16dp"
                    android:background="#FAFAFA">
                    <View
                        android:layout_width="4dp"
                        android:layout_height="16dp"
                        android:background="@color/base_color"
                        android:layout_marginEnd="8dp"/>
                    <TextView
                        android:layout_width="0dp"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="监控详情"
                        android:textSize="16sp"
                        android:textStyle="bold"
                        android:textColor="#333333" />
                    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
                        android:id="@+id/btn_read_data"
                        android:layout_width="wrap_content"
                        android:layout_height="32dp"
                        android:text="读取数据"
                        android:textColor="@color/white"
                        android:textSize="12sp"
                        android:paddingStart="12dp"
                        android:paddingEnd="12dp"
                        app:qmui_backgroundColor="@color/base_color"
                        app:qmui_radius="4dp"/>
                </LinearLayout>
                        android:orientation="horizontal"
                        android:gravity="center_vertical"
                        android:padding="16dp"
                        android:background="@color/white">
                        <View
                            android:layout_width="4dp"
                            android:layout_height="16dp"
                            android:background="@color/base_color"
                            android:layout_marginEnd="8dp"/>
                        <TextView
                            android:id="@+id/tv_monitor_title"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:layout_weight="1"
                            android:text="监控详情"
                            android:textSize="16sp"
                            android:textStyle="bold"
                            android:textColor="#333333"
                            android:clickable="true"
                            android:focusable="true"
                             />
                        <TextView
                            android:id="@+id/tv_monitor_update_time"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text=""
                            android:textSize="12sp"
                            android:textColor="#999999"/>
                    </LinearLayout>
                <View
                    android:layout_width="match_parent"
@@ -163,7 +163,7 @@
                    android:orientation="horizontal"
                    android:gravity="center_vertical"
                    android:padding="16dp"
                    android:background="#FAFAFA">
                    android:background="@color/white">
                    
                    <View
                        android:layout_width="4dp"
@@ -192,145 +192,144 @@
                    android:orientation="vertical"
                    android:padding="16dp">
                    <!-- Layer Input -->
                    <!-- Grid Layout for Parameters -->
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:layout_marginBottom="16dp">
                        <!-- Row 1: Labels -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:layout_marginBottom="8dp">
                            <TextView
                                android:layout_width="wrap_content"
                            <!-- Layer Label -->
                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:text="层数"
                                android:textSize="14sp"
                                android:textColor="#666666"/>
                            <TextView
                                android:id="@+id/tv_layer_status"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:textSize="10sp"
                                android:layout_marginStart="8dp"/>
                        </LinearLayout>
                        <EditText
                            android:id="@+id/et_layer"
                            android:layout_width="match_parent"
                            android:layout_height="48dp"
                            android:background="@drawable/bg_input_field"
                            android:paddingStart="16dp"
                            android:paddingEnd="16dp"
                            android:hint="层数 (1-30)"
                            android:textColorHint="#CCCCCC"
                            android:textSize="14sp"
                            android:focusable="false"
                            android:focusableInTouchMode="false"
                            android:inputType="none"/>
                    </LinearLayout>
                                android:layout_weight="1"
                                android:orientation="horizontal"
                                android:gravity="center">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="层数"
                                    android:textSize="14sp"
                                    android:textColor="#666666"/>
                                <TextView
                                    android:id="@+id/tv_layer_status"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:textSize="10sp"
                                    android:layout_marginStart="4dp"/>
                            </LinearLayout>
                    <!-- Station Input -->
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:layout_marginBottom="16dp">
                            <!-- Station Label -->
                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:orientation="horizontal"
                                android:gravity="center">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="站号"
                                    android:textSize="14sp"
                                    android:textColor="#666666"/>
                                <TextView
                                    android:id="@+id/tv_station_status"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:textSize="10sp"
                                    android:layout_marginStart="4dp"/>
                            </LinearLayout>
                            <!-- Baud Label -->
                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1.5"
                                android:orientation="horizontal"
                                android:gravity="center">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="波特率"
                                    android:textSize="14sp"
                                    android:textColor="#666666"/>
                                <TextView
                                    android:id="@+id/tv_baud_status"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:textSize="10sp"
                                    android:layout_marginStart="4dp"/>
                            </LinearLayout>
                        </LinearLayout>
                        <!-- Row 2: Values -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:layout_marginBottom="8dp">
                            android:gravity="center_vertical">
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="站号"
                                android:textSize="14sp"
                                android:textColor="#666666"/>
                            <!-- Layer Value -->
                             <TextView
                                android:id="@+id/et_layer"
                                android:layout_width="0dp"
                                android:layout_height="48dp"
                                android:layout_weight="1"
                                android:background="@android:color/transparent"
                                android:gravity="center"
                                android:text="待读取"
                                android:textSize="16sp"
                                android:textStyle="bold"
                                android:textColor="#999999"
                                android:layout_marginEnd="4dp"
                                />
                            <TextView
                                android:id="@+id/tv_station_status"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:textSize="10sp"
                                android:layout_marginStart="8dp"/>
                        </LinearLayout>
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal">
                            <!-- Station Value -->
                            <EditText
                                android:id="@+id/et_station"
                                android:layout_width="0dp"
                                android:layout_height="48dp"
                                android:layout_weight="1"
                                android:background="@drawable/bg_input_field"
                                android:paddingStart="16dp"
                                android:paddingEnd="16dp"
                                android:hint="请输入站号 (1-64)"
                                android:gravity="center"
                                android:hint="站号"
                                android:textColorHint="#CCCCCC"
                                android:textSize="14sp"
                                android:inputType="number"/>
                        </LinearLayout>
                    </LinearLayout>
                                android:inputType="number"
                                android:layout_marginStart="4dp"
                                android:layout_marginEnd="4dp"/>
                    <!-- Baud Rate -->
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical">
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:layout_marginBottom="8dp">
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="波特率"
                                android:textSize="14sp"
                                android:textColor="#666666"/>
                            <TextView
                                android:id="@+id/tv_baud_status"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:textSize="10sp"
                                android:layout_marginStart="8dp"/>
                        </LinearLayout>
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal">
                            <!-- Baud Value -->
                            <FrameLayout
                                android:layout_width="0dp"
                                android:layout_height="48dp"
                                android:layout_weight="1"
                                android:background="@drawable/bg_input_field">
                                android:layout_weight="1.5"
                                android:background="@drawable/bg_input_field"
                                android:layout_marginStart="4dp">
                                
                                <Spinner
                                    android:id="@+id/spinner_baud"
                                    android:layout_width="match_parent"
                                    android:layout_height="match_parent"
                                    android:background="@null"
                                    android:paddingStart="8dp"/>
                                    android:paddingEnd="32dp"
                                    />
                                <ImageView
                                    android:layout_width="24dp"
                                    android:layout_height="24dp"
                                    android:src="@drawable/ic_arrow_drop_down"
                                    android:layout_gravity="center_vertical|end"
                                    android:layout_marginEnd="8dp"
                                    android:importantForAccessibility="no"/>
                            </FrameLayout>
                        </LinearLayout>
                    </LinearLayout>
@@ -364,7 +363,7 @@
                            android:textColor="@color/white"
                            android:textSize="16sp"
                            android:textStyle="bold"
                            app:qmui_backgroundColor="@color/base_color_s"
                            app:qmui_backgroundColor="@color/base_color"
                            app:qmui_radius="8dp"/>
                    </LinearLayout>
@@ -384,6 +383,7 @@
        <!-- Log Display -->
        <androidx.cardview.widget.CardView
            android:id="@+id/cv_log"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:cardCornerRadius="12dp"
app/src/main/res/mipmap-hdpi/ic_launcher.png

app/src/main/res/mipmap-mdpi/ic_launcher.png

app/src/main/res/mipmap-xhdpi/ic_launcher.png

app/src/main/res/mipmap-xhdpi/icon_1.png
app/src/main/res/mipmap-xhdpi/icon_2.png
app/src/main/res/mipmap-xhdpi/icon_3.png
app/src/main/res/mipmap-xhdpi/icon_4.png
app/src/main/res/mipmap-xhdpi/icon_5.png
app/src/main/res/mipmap-xhdpi/icon_6.png
app/src/main/res/mipmap-xxhdpi/ic_launcher.png

app/src/main/res/mipmap-xxxhdpi/ic_launcher.png

app/src/main/res/values-land/dimens.xml
@@ -1,3 +1,3 @@
<resources>
    <dimen name="fab_margin">48dp</dimen>
</resources>
app/src/main/res/values-w1240dp/dimens.xml
@@ -1,3 +1,3 @@
<resources>
    <dimen name="fab_margin">200dp</dimen>
</resources>
app/src/main/res/values-w600dp/dimens.xml
@@ -1,3 +1,3 @@
<resources>
    <dimen name="fab_margin">48dp</dimen>
</resources>