| | |
| | | |
| | | 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()); |
| | | |
| | |
| | | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | }); |
| | | } |
| | | |
| | | // 初始化日志内容 |
| | | if (logBuilder.length() > 0) { |
| | | tv_log.setText(Html.fromHtml(logBuilder.toString())); |
| | | } else { |
| | |
| | | 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); |
| | | } |
| | |
| | | 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) { |
| | | 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) { |
| | | 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); |
| | | } |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | 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); |
| | |
| | | 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 "-退出设定"; |
| | |
| | | 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) { |
| | |
| | | |
| | | 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 { |
| | |
| | | |
| | | logBuilder.append(logLine); |
| | | |
| | | // Update UI |
| | | // 更新日志显示并自动滚动到底部 |
| | | if (tv_log != null) { |
| | | tv_log.setText(Html.fromHtml(logBuilder.toString())); |
| | | if (svLog != null) { |
| | |
| | | } |
| | | } |
| | | |
| | | // Kept for backward compatibility if needed, but redirected to appendLog |
| | | // 兼容旧代码的日志方法 |
| | | private void addLog(String message) { |
| | | appendLog(message); |
| | | } |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | 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("进入设定模式成功"); |
| | |
| | | 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))) { |
| | | // 处理主板/传感器复位的返回 |
| | | // 判断是主板复位还是传感器复位 |
| | | 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 + "失败"); |
| | |
| | | } |
| | | } |
| | | |
| | | // 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("自检结果已更新"); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 发送带CRC校验的指令 |
| | | * @param cmd 要发送的指令(不含CRC) |
| | | */ |
| | | private void sendCmdWithCrc(String cmd) { |
| | | if (!BleGlobalManager.getInstance().isConnected()) { |
| | | Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show(); |
| | |
| | | |
| | | 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; |
| | |
| | | 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; |
| | | 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"; |
| | |
| | | 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; |
| | | 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() { |
| | |
| | | .setPositiveButton("确定", new DialogInterface.OnClickListener() { |
| | | @Override |
| | | public void onClick(DialogInterface dialog, int which) { |
| | | 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; |
| | |
| | | |
| | | @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) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 根据自检结果设置背景颜色 |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |