【无标题】TemperatureMonitor.m matlab2024串口监控温度run_temperature_monitor.m

c 复制代码
% classdef TemperatureMonitor < handle
%     % 温度监测类 - 实时读取、显示和保存串口温度数据
%     % 完整版本,支持串口扫描、波特率选择、数据解析等功能
% 
%     properties
%         serialPort              % 串口对象
%         fig                     % 主图形窗口句柄
%         ax                      % 坐标轴句柄
%         temperaturePlot         % 温度曲线绘图句柄
%         dataBuffer              % 温度数据缓冲区
%         timeBuffer              % 时间数据缓冲区
%         maxPoints = 1000        % 显示的最大数据点数
%         isRunning = false       % 是否正在运行
%         startTime               % 监控开始时间
%         dataTable               % 数据表格
%         queryTimer              % 查询定时器
%         deviceReady = false     % 设备是否准备好
%         sampleCount = 0         % 采样计数
%         expectingTemperature = false  % 是否期望接收温度数据
%         isConnected = false     % 串口连接状态
%     end
% 
%     properties (Access = private)
%         logFileName             % 日志文件名
%         fileID                  % 文件ID
%         receiveText             % 接收数据显示文本框句柄
%         commandHistory          % 命令历史记录
%         portDropdown            % 串口号下拉菜单句柄
%         baudRateDropdown        % 波特率下拉菜单句柄
%         connectButton           % 连接/断开按钮句柄
%         connectionStatusText    % 连接状态文本句柄
%         connectionInfoText      % 连接信息文本句柄
%         messageText             % 消息文本句柄
%         startBtn                % 开始/停止监控按钮句柄
%         resendBtn               % 重新发送命令按钮句柄
%         autoSaveBtn             % 自动保存开关按钮句柄
%         deviceStatusText        % 设备状态文本句柄
%         systemStatusText        % 系统状态文本句柄
%         sampleCountText         % 采样点数文本句柄
%         currentTempText         % 当前温度文本句柄
%         comInfoText             % 串口信息文本句柄
%         timeoutTimer            % 超时定时器
%         tempTimeoutTimer        % 温度数据超时定时器
% 
%         defaultPorts = {'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'COM10'}
%         defaultBaudRates = {'9600', '19200', '38400', '57600', '115200', '230400', '460800', '921600'}
%     end
% 
%     methods
%         function obj = TemperatureMonitor(varargin)
%             % 构造函数
%             % TemperatureMonitor() - 无参数,创建GUI后手动选择串口
%             % TemperatureMonitor(port, baudRate, queryInterval) - 带参数创建
% 
%             fprintf('=== 温度监测系统初始化 ===\n');
% 
%             % 初始化数据缓冲
%             obj.dataBuffer = [];
%             obj.timeBuffer = [];
%             obj.dataTable = table();
%             obj.commandHistory = {};
% 
%             % 解析输入参数
%             if nargin >= 2
%                 port = varargin{1};
%                 baudRate = varargin{2};
%                 if nargin >= 3
%                     queryInterval = varargin{3};
%                 else
%                     queryInterval = 1;
%                 end
% 
%                 % 尝试打开串口
%                 try
%                     obj.connectSerialPort(port, baudRate);
%                 catch ME
%                     warning('串口连接失败: %s', ME.message);
%                     obj.isConnected = false;
%                 end
%             else
%                 % 无参数调用,先创建GUI,不立即连接串口
%                 port = 'COM3';  % 默认串口
%                 baudRate = 9600; % 默认波特率
%                 if nargin == 1
%                     queryInterval = varargin{1};
%                 else
%                     queryInterval = 1;
%                 end
%                 obj.isConnected = false;
%             end
% 
%             % 创建查询定时器
%             obj.queryTimer = timer(...
%                 'ExecutionMode', 'fixedRate', ...
%                 'Period', queryInterval, ...
%                 'TimerFcn', @(~, ~)obj.queryTemperature(), ...
%                 'Name', 'TemperatureQueryTimer', ...
%                 'BusyMode', 'queue');
% 
%             % 创建GUI界面
%             obj.createGUI(port, baudRate, queryInterval);
% 
%             fprintf('温度监测系统初始化完成\n');
%         end
% 
%         function createGUI(obj, defaultPort, defaultBaudRate, queryInterval)
%             % 创建图形界面
% 
%             fprintf('正在创建GUI界面...\n');
% 
%             obj.fig = figure('Name', '温度实时监测与串口调试系统', ...
%                 'NumberTitle', 'off', ...
%                 'Position', [50, 50, 1400, 800], ...
%                 'CloseRequestFcn', @obj.closeFigure, ...
%                 'MenuBar', 'none', ...
%                 'ToolBar', 'figure');
% 
%             % 创建连接配置区域
%             configPanel = uipanel('Parent', obj.fig, ...
%                 'Title', '串口配置', ...
%                 'Position', [0.01, 0.85, 0.98, 0.14], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 串口号选择
%             uicontrol('Parent', configPanel, ...
%                 'Style', 'text', ...
%                 'String', '串口号:', ...
%                 'Position', [20, 80, 80, 30], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 获取可用串口
%             availablePorts = obj.scanSerialPorts();
%             if isempty(availablePorts)
%                 availablePorts = obj.defaultPorts;
%             end
% 
%             % 设置默认选择的端口
%             defaultPortIdx = find(strcmp(availablePorts, defaultPort), 1);
%             if isempty(defaultPortIdx)
%                 defaultPortIdx = 1;
%                 if ~isempty(availablePorts)
%                     defaultPort = availablePorts{1};
%                 else
%                     defaultPort = 'COM1';
%                 end
%             end
% 
%             obj.portDropdown = uicontrol('Parent', configPanel, ...
%                 'Style', 'popupmenu', ...
%                 'String', availablePorts, ...
%                 'Value', defaultPortIdx, ...
%                 'Position', [100, 80, 150, 30], ...
%                 'FontSize', 11, ...
%                 'BackgroundColor', 'white', ...
%                 'Tag', 'portDropdown');
% 
%             % 扫描串口按钮
%             uicontrol('Parent', configPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '扫描串口', ...
%                 'Position', [260, 80, 100, 30], ...
%                 'FontSize', 11, ...
%                 'BackgroundColor', [0.2, 0.6, 1.0], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @(~, ~)obj.scanAndUpdatePorts());
% 
%             % 波特率选择
%             uicontrol('Parent', configPanel, ...
%                 'Style', 'text', ...
%                 'String', '波特率:', ...
%                 'Position', [380, 80, 80, 30], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 设置默认选择的波特率
%             defaultBaudRateIdx = find(strcmp(obj.defaultBaudRates, num2str(defaultBaudRate)), 1);
%             if isempty(defaultBaudRateIdx)
%                 defaultBaudRateIdx = 1;
%             end
% 
%             obj.baudRateDropdown = uicontrol('Parent', configPanel, ...
%                 'Style', 'popupmenu', ...
%                 'String', obj.defaultBaudRates, ...
%                 'Value', defaultBaudRateIdx, ...
%                 'Position', [460, 80, 150, 30], ...
%                 'FontSize', 11, ...
%                 'BackgroundColor', 'white', ...
%                 'Tag', 'baudRateDropdown');
% 
%             % 连接/断开按钮
%             obj.connectButton = uicontrol('Parent', configPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '连接串口', ...
%                 'Position', [620, 80, 120, 30], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.3, 0.7, 0.3], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @obj.toggleConnection);
% 
%             % 退出系统按钮
%             uicontrol('Parent', configPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '退出系统', ...
%                 'Position', [750, 80, 120, 30], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.9, 0.3, 0.3], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @(~, ~)obj.closeFigure());
% 
%             % 连接状态显示
%             obj.connectionStatusText = uicontrol('Parent', configPanel, ...
%                 'Style', 'text', ...
%                 'String', '未连接', ...
%                 'Position', [880, 80, 200, 30], ...
%                 'FontSize', 11, ...
%                 'ForegroundColor', 'red', ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 连接信息显示
%             obj.connectionInfoText = uicontrol('Parent', configPanel, ...
%                 'Style', 'text', ...
%                 'String', sprintf('端口: %s, 波特率: %s', defaultPort, num2str(defaultBaudRate)), ...
%                 'Position', [20, 40, 300, 30], ...
%                 'FontSize', 10, ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 消息显示区域
%             obj.messageText = uicontrol('Parent', configPanel, ...
%                 'Style', 'text', ...
%                 'String', '请选择串口并点击"连接串口"按钮开始', ...
%                 'Position', [20, 10, 500, 30], ...
%                 'FontSize', 10, ...
%                 'ForegroundColor', 'blue', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 创建温度监测区域
%             tempPanel = uipanel('Parent', obj.fig, ...
%                 'Title', '温度监测', ...
%                 'Position', [0.01, 0.52, 0.65, 0.32], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 温度绘图区域
%             obj.ax = axes('Parent', tempPanel, ...
%                 'Position', [0.08, 0.15, 0.88, 0.80]);
%             xlabel(obj.ax, '时间 (秒)', 'FontSize', 11, 'FontWeight', 'bold');
%             ylabel(obj.ax, '温度 (°C)', 'FontSize', 11, 'FontWeight', 'bold');
%             title(obj.ax, '温度实时变化曲线', 'FontSize', 12, 'FontWeight', 'bold');
%             grid(obj.ax, 'on');
%             hold(obj.ax, 'on');
% 
%             % 设置坐标轴颜色和字体
%             set(obj.ax, 'XColor', [0.2, 0.2, 0.2], 'YColor', [0.2, 0.2, 0.2]);
%             set(obj.ax, 'FontSize', 10);
% 
%             % 初始化绘图
%             obj.temperaturePlot = plot(obj.ax, NaN, NaN, ...
%                 'b-', 'LineWidth', 2, ...
%                 'Marker', 'o', 'MarkerSize', 5, ...
%                 'MarkerFaceColor', 'r', ...
%                 'MarkerEdgeColor', 'b');
% 
%             % 设置初始坐标轴范围
%             xlim(obj.ax, [0, 10]);
%             ylim(obj.ax, [0, 50]);
% 
%             % 创建串口调试区域
%             debugPanel = uipanel('Parent', obj.fig, ...
%                 'Title', '串口调试', ...
%                 'Position', [0.01, 0.01, 0.65, 0.50], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 接收数据显示区域
%             uicontrol('Parent', debugPanel, ...
%                 'Style', 'text', ...
%                 'String', '接收数据:', ...
%                 'Position', [10, 440, 100, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 接收数据显示文本框(可滚动)
%             obj.receiveText = uicontrol('Parent', debugPanel, ...
%                 'Style', 'edit', ...
%                 'String', '', ...
%                 'Position', [10, 50, 620, 390], ...
%                 'FontSize', 10, ...
%                 'Max', 100, ...  % 允许多行
%                 'Min', 0, ...    % 允许多行
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', 'white', ...
%                 'Enable', 'inactive', ...  % 设置为只读
%                 'Tag', 'receiveText');    % 添加标签以便查找
% 
%             % 发送命令区域
%             uicontrol('Parent', debugPanel, ...
%                 'Style', 'text', ...
%                 'String', '发送命令:', ...
%                 'Position', [10, 470, 100, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             commandEdit = uicontrol('Parent', debugPanel, ...
%                 'Style', 'edit', ...
%                 'String', 'AT+T?', ...
%                 'Position', [100, 470, 400, 30], ...
%                 'FontSize', 11, ...
%                 'Tag', 'commandEdit', ...
%                 'BackgroundColor', 'white');
% 
%             % 发送按钮
%             uicontrol('Parent', debugPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '发送', ...
%                 'Position', [510, 470, 80, 30], ...
%                 'FontSize', 11, ...
%                 'BackgroundColor', [0.1, 0.5, 0.9], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @(~, ~)obj.sendCustomCommand());
% 
%             % 清空接收区按钮
%             uicontrol('Parent', debugPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '清空接收区', ...
%                 'Position', [600, 470, 100, 30], ...
%                 'FontSize', 11, ...
%                 'BackgroundColor', [0.9, 0.5, 0.1], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'TooltipString', '清除串口调试窗口中的所有内容', ...
%                 'Callback', @(~, ~)obj.clearReceiveText());
% 
%             % 常用命令
%             commands = {'AT+T?', 'AT+VER', 'AT+HELP', 'AT+STATUS'};
%             commandNames = {'查询温度', '版本信息', '帮助信息', '状态查询'};
%             for i = 1:length(commands)
%                 uicontrol('Parent', debugPanel, ...
%                     'Style', 'pushbutton', ...
%                     'String', commandNames{i}, ...
%                     'Position', [100 + (i-1)*120, 20, 100, 25], ...
%                     'FontSize', 10, ...
%                     'BackgroundColor', [0.4, 0.4, 0.8], ...
%                     'ForegroundColor', 'white', ...
%                     'TooltipString', sprintf('发送命令: %s', commands{i}), ...
%                     'Callback', @(src, ~)obj.setCommandText(commands{i}));
%             end
% 
%             % 添加快捷键说明
%             uicontrol('Parent', debugPanel, ...
%                 'Style', 'text', ...
%                 'String', '快捷键: Ctrl+L=清空接收区, Ctrl+S=保存数据', ...
%                 'Position', [10, 20, 250, 25], ...
%                 'FontSize', 9, ...
%                 'ForegroundColor', [0.5, 0.5, 0.5], ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 创建控制面板
%             controlPanel = uipanel('Parent', obj.fig, ...
%                 'Title', '控制面板', ...
%                 'Position', [0.68, 0.52, 0.31, 0.32], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 查询间隔设置
%             uicontrol('Parent', controlPanel, ...
%                 'Style', 'text', ...
%                 'String', '查询间隔 (秒):', ...
%                 'Position', [20, 230, 200, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             uicontrol('Parent', controlPanel, ...
%                 'Style', 'edit', ...
%                 'String', num2str(queryInterval), ...
%                 'Position', [20, 200, 200, 30], ...
%                 'FontSize', 11, ...
%                 'Tag', 'intervalEdit', ...
%                 'BackgroundColor', 'white', ...
%                 'Callback', @(~, ~)obj.updateInterval);
% 
%             % 数据点数设置
%             uicontrol('Parent', controlPanel, ...
%                 'Style', 'text', ...
%                 'String', '显示数据点数:', ...
%                 'Position', [20, 160, 200, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             uicontrol('Parent', controlPanel, ...
%                 'Style', 'edit', ...
%                 'String', num2str(obj.maxPoints), ...
%                 'Position', [20, 130, 200, 30], ...
%                 'FontSize', 11, ...
%                 'Tag', 'maxPointsEdit', ...
%                 'BackgroundColor', 'white', ...
%                 'Callback', @(~, ~)obj.updateMaxPoints);
% 
%             % 开始/停止按钮
%             obj.startBtn = uicontrol('Parent', controlPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '开始监控', ...
%                 'Position', [20, 80, 200, 40], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.3, 0.7, 0.3], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @obj.toggleMonitoring, ...
%                 'Enable', 'off');  % 初始时禁用,连接串口后启用
% 
%             % 重新发送命令按钮
%             obj.resendBtn = uicontrol('Parent', controlPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '重新发送AT+T?', ...
%                 'Position', [20, 30, 200, 40], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.9, 0.8, 0.1], ...
%                 'ForegroundColor', 'black', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @(~, ~)obj.sendTemperatureQuery(), ...
%                 'Enable', 'off');  % 初始时禁用
% 
%             % 创建数据保存面板
%             savePanel = uipanel('Parent', obj.fig, ...
%                 'Title', '数据保存', ...
%                 'Position', [0.68, 0.35, 0.31, 0.16], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 手动保存按钮
%             uicontrol('Parent', savePanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '手动保存数据', ...
%                 'Position', [20, 40, 200, 40], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.1, 0.5, 0.9], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Callback', @obj.saveData);
% 
%             % 自动保存开关
%             obj.autoSaveBtn = uicontrol('Parent', savePanel, ...
%                 'Style', 'togglebutton', ...
%                 'String', '自动保存: 关闭', ...
%                 'Position', [20, 90, 200, 40], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.8, 0.2, 0.2], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'Tag', 'autoSaveBtn', ...
%                 'Callback', @obj.toggleAutoSave);
% 
%             % 创建状态显示面板
%             statusPanel = uipanel('Parent', obj.fig, ...
%                 'Title', '状态信息', ...
%                 'Position', [0.68, 0.01, 0.31, 0.33], ...
%                 'FontSize', 12, ...
%                 'FontWeight', 'bold', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 设备状态
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '设备状态:', ...
%                 'Position', [10, 240, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             obj.deviceStatusText = uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '未连接', ...
%                 'Position', [100, 240, 150, 25], ...
%                 'FontSize', 11, ...
%                 'ForegroundColor', 'red', ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 系统状态
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '系统状态:', ...
%                 'Position', [10, 210, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             obj.systemStatusText = uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '等待连接', ...
%                 'Position', [100, 210, 150, 25], ...
%                 'FontSize', 11, ...
%                 'ForegroundColor', 'blue', ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 采样点数
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '采样点数:', ...
%                 'Position', [10, 180, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             obj.sampleCountText = uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '0', ...
%                 'Position', [100, 180, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 当前温度
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '当前温度:', ...
%                 'Position', [10, 150, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             obj.currentTempText = uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '-- °C', ...
%                 'Position', [100, 150, 100, 25], ...
%                 'FontSize', 12, ...
%                 'ForegroundColor', 'blue', ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 串口信息
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '串口信息:', ...
%                 'Position', [10, 120, 80, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             obj.comInfoText = uicontrol('Parent', statusPanel, ...
%                 'Style', 'text', ...
%                 'String', '未连接', ...
%                 'Position', [100, 120, 150, 25], ...
%                 'FontSize', 11, ...
%                 'FontWeight', 'bold', ...
%                 'HorizontalAlignment', 'left', ...
%                 'BackgroundColor', [0.95, 0.95, 0.95]);
% 
%             % 清除所有数据按钮
%             uicontrol('Parent', statusPanel, ...
%                 'Style', 'pushbutton', ...
%                 'String', '清除所有数据', ...
%                 'Position', [20, 70, 200, 40], ...
%                 'FontSize', 12, ...
%                 'BackgroundColor', [0.8, 0.3, 0.3], ...
%                 'ForegroundColor', 'white', ...
%                 'FontWeight', 'bold', ...
%                 'TooltipString', '清除接收区、温度数据和图表', ...
%                 'Callback', @obj.clearAllData);
% 
%             % 添加快捷键支持
%             set(obj.fig, 'KeyPressFcn', @obj.keyboardShortcut);
% 
%             % 更新控件状态
%             obj.updateControlStates();
% 
%             fprintf('GUI界面创建完成\n');
%         end
% 
%         function ports = scanSerialPorts(obj)
%             % 扫描可用串口
%             try
%                 fprintf('正在扫描可用串口...\n');
% 
%                 % 尝试使用MATLAB的串口列表功能
%                 if exist('serialportlist', 'file')
%                     ports = serialportlist('available');
%                     fprintf('使用 serialportlist 函数\n');
%                 elseif exist('seriallist', 'file')
%                     ports = seriallist;
%                     fprintf('使用 seriallist 函数\n');
%                 else
%                     % 如果上述函数都不存在,使用默认端口列表
%                     ports = obj.defaultPorts;
%                     fprintf('使用默认端口列表\n');
%                 end
% 
%                 % 转换为单元格数组
%                 if isstring(ports)
%                     ports = cellstr(ports);
%                 elseif ischar(ports)
%                     ports = {ports};
%                 end
% 
%                 % 如果没有任何端口,使用默认列表
%                 if isempty(ports)
%                     ports = obj.defaultPorts;
%                     fprintf('未检测到可用串口,使用默认列表\n');
%                 end
% 
%                 fprintf('扫描到 %d 个可用串口:\n', length(ports));
%                 for i = 1:length(ports)
%                     fprintf('  %s\n', ports{i});
%                 end
% 
%             catch ME
%                 fprintf('扫描串口失败: %s\n', ME.message);
%                 ports = obj.defaultPorts;
%             end
%         end
% 
%         function scanAndUpdatePorts(obj)
%             % 扫描并更新串口下拉菜单
%             try
%                 fprintf('手动扫描串口...\n');
% 
%                 % 扫描可用串口
%                 availablePorts = obj.scanSerialPorts();
% 
%                 % 更新下拉菜单
%                 set(obj.portDropdown, 'String', availablePorts);
%                 if ~isempty(availablePorts)
%                     set(obj.portDropdown, 'Value', 1);
%                 end
% 
%                 % 更新消息
%                 set(obj.messageText, 'String', ...
%                     sprintf('扫描完成,找到 %d 个可用串口', length(availablePorts)));
%                 set(obj.messageText, 'ForegroundColor', 'green');
% 
%                 fprintf('串口列表已更新\n');
% 
%             catch ME
%                 warning('扫描串口失败: %s', ME.message);
%                 set(obj.messageText, 'String', '扫描串口失败');
%                 set(obj.messageText, 'ForegroundColor', 'red');
%             end
%         end
% 
%         function updateControlStates(obj)
%             % 更新控件状态(启用/禁用)
% 
%             if obj.isConnected
%                 % 串口已连接,启用相关按钮
%                 set(obj.startBtn, 'Enable', 'on');
%                 set(obj.resendBtn, 'Enable', 'on');
%                 set(obj.connectButton, 'String', '断开连接');
%                 set(obj.connectButton, 'BackgroundColor', [0.9, 0.3, 0.3]);
%                 set(obj.connectionStatusText, 'String', '已连接');
%                 set(obj.connectionStatusText, 'ForegroundColor', 'green');
%             else
%                 % 串口未连接,禁用相关按钮
%                 set(obj.startBtn, 'Enable', 'off');
%                 set(obj.resendBtn, 'Enable', 'off');
%                 set(obj.connectButton, 'String', '连接串口');
%                 set(obj.connectButton, 'BackgroundColor', [0.3, 0.7, 0.3]);
%                 set(obj.connectionStatusText, 'String', '未连接');
%                 set(obj.connectionStatusText, 'ForegroundColor', 'red');
% 
%                 % 如果正在运行,停止监控
%                 if obj.isRunning
%                     obj.stopMonitoring();
%                 end
%             end
%         end
% 
%         function toggleConnection(obj, ~, ~)
%             % 连接/断开串口
%             if ~obj.isConnected
%                 % 连接串口
%                 obj.connectSerialPort();
%             else
%                 % 断开串口
%                 obj.disconnectSerialPort();
%             end
%         end
% 
%         function connectSerialPort(obj, port, baudRate)
%             % 连接串口
%             if nargin < 2
%                 % 从GUI获取参数
%                 portList = get(obj.portDropdown, 'String');
%                 portIdx = get(obj.portDropdown, 'Value');
%                 port = portList{portIdx};
% 
%                 baudRateList = get(obj.baudRateDropdown, 'String');
%                 baudRateIdx = get(obj.baudRateDropdown, 'Value');
%                 baudRate = str2double(baudRateList{baudRateIdx});
%             end
% 
%             try
%                 fprintf('正在连接串口 %s (波特率: %d)...\n', port, baudRate);
% 
%                 % 如果已有串口连接,先断开
%                 if ~isempty(obj.serialPort) && isvalid(obj.serialPort)
%                     obj.disconnectSerialPort();
%                 end
% 
%                 % 创建串口对象
%                 obj.serialPort = serialport(port, baudRate);
%                 obj.serialPort.Timeout = 5;  % 设置超时为5秒
%                 configureTerminator(obj.serialPort, "CR/LF");
% 
%                 % 设置回调函数
%                 configureCallback(obj.serialPort, "terminator", ...
%                     @(src, event)obj.readDataCallback(src, event));
% 
%                 % 更新状态
%                 obj.isConnected = true;
%                 obj.deviceReady = false;
% 
%                 % 更新GUI
%                 set(obj.messageText, 'String', sprintf('串口 %s 连接成功', port));
%                 set(obj.messageText, 'ForegroundColor', 'green');
% 
%                 set(obj.connectionInfoText, 'String', ...
%                     sprintf('端口: %s, 波特率: %d', port, baudRate));
% 
%                 % 更新状态面板中的串口信息
%                 set(obj.comInfoText, 'String', sprintf('%s, %d bps', port, baudRate));
% 
%                 % 更新控件状态
%                 obj.updateControlStates();
% 
%                 % 清空串口缓冲区
%                 flush(obj.serialPort);
% 
%                 % 发送初始测试命令
%                 writeline(obj.serialPort, "AT");
%                 obj.appendToReceiveText('[发送] AT (测试连接)');
% 
%                 fprintf('串口连接成功\n');
% 
%             catch ME
%                 % 连接失败
%                 obj.isConnected = false;
%                 set(obj.messageText, 'String', sprintf('连接失败: %s', ME.message));
%                 set(obj.messageText, 'ForegroundColor', 'red');
% 
%                 fprintf('串口连接失败: %s\n', ME.message);
%                 errordlg(sprintf('无法打开串口 %s:\n%s', port, ME.message), '连接错误');
% 
%                 % 更新控件状态
%                 obj.updateControlStates();
%             end
%         end
% 
%         function disconnectSerialPort(obj)
%             % 断开串口连接
%             if obj.isRunning
%                 obj.stopMonitoring();
%             end
% 
%             if ~isempty(obj.serialPort) && isvalid(obj.serialPort)
%                 try
%                     fprintf('正在断开串口连接...\n');
% 
%                     % 移除回调函数
%                     configureCallback(obj.serialPort, "off");
% 
%                     % 关闭串口
%                     delete(obj.serialPort);
%                     obj.serialPort = [];
% 
%                     set(obj.messageText, 'String', '串口已断开');
%                     set(obj.messageText, 'ForegroundColor', 'blue');
% 
%                     fprintf('串口已断开\n');
% 
%                 catch ME
%                     warning('断开串口时出错: %s', ME.message);
%                 end
%             end
% 
%             % 更新状态
%             obj.isConnected = false;
%             obj.deviceReady = false;
% 
%             % 更新控件状态
%             obj.updateControlStates();
% 
%             % 更新状态面板
%             set(obj.comInfoText, 'String', '未连接');
%             set(obj.deviceStatusText, 'String', '未连接');
%             set(obj.deviceStatusText, 'ForegroundColor', 'red');
%         end
% 
%         function keyboardShortcut(obj, ~, event)
%             % 键盘快捷键处理
%             switch event.Key
%                 case 'l'
%                     % Ctrl+L 清空接收区
%                     if strcmp(event.Modifier, 'control')
%                         obj.clearReceiveText();
%                         disp('快捷键: Ctrl+L - 已清空接收区');
%                     end
%                 case 'c'
%                     % Ctrl+C 复制接收区内容到剪贴板
%                     if strcmp(event.Modifier, 'control')
%                         obj.copyReceiveTextToClipboard();
%                     end
%                 case 's'
%                     % Ctrl+S 保存数据
%                     if strcmp(event.Modifier, 'control')
%                         obj.saveData();
%                     end
%             end
%         end
% 
%         function copyReceiveTextToClipboard(obj)
%             % 复制接收区内容到剪贴板
%             try
%                 currentText = get(obj.receiveText, 'String');
%                 if iscell(currentText)
%                     % 将单元格数组转换为字符串
%                     fullText = '';
%                     for i = 1:length(currentText)
%                         fullText = sprintf('%s%s\n', fullText, currentText{i});
%                     end
%                 elseif ischar(currentText)
%                     fullText = currentText;
%                 else
%                     fullText = '';
%                 end
% 
%                 clipboard('copy', fullText);
%                 fprintf('接收区内容已复制到剪贴板 (%d 字符)\n', length(fullText));
% 
%                 % 显示短暂提示
%                 msg = msgbox('接收区内容已复制到剪贴板', '复制成功', 'help');
%                 pause(1);
%                 if ishandle(msg)
%                     close(msg);
%                 end
%             catch ME
%                 warning('复制到剪贴板失败: %s', ME.message);
%             end
%         end
% 
%         function clearReceiveText(obj)
%             % 清空接收文本框内容
%             try
%                 % 获取接收文本框句柄
%                 receiveTextHandle = findobj(obj.fig, 'Tag', 'receiveText');
%                 if isempty(receiveTextHandle)
%                     receiveTextHandle = obj.receiveText;
%                 end
% 
%                 % 清空文本框内容
%                 set(receiveTextHandle, 'String', '');
% 
%                 % 清除命令历史记录
%                 obj.commandHistory = {};
% 
%                 % 添加一条清空确认消息
%                 currentTime = datetime('now', 'Format', 'HH:mm:ss');
%                 confirmationMsg = ['接收区已清空 [', char(currentTime), ']'];
% 
%                 % 在接收区显示确认消息
%                 obj.appendToReceiveText(confirmationMsg);
% 
%                 fprintf('%s\n', confirmationMsg);
% 
%                 % 强制更新显示
%                 drawnow;
% 
%                 % 显示短暂的成功提示
%                 msg = msgbox('接收区内容已清空', '清空成功', 'help');
%                 pause(0.5);
%                 if ishandle(msg)
%                     close(msg);
%                 end
% 
%             catch ME
%                 warning('清空接收区失败: %s', ME.message);
%                 errordlg(['清空接收区失败: ', ME.message], '错误');
%             end
%         end
% 
%         function clearAllData(obj, ~, ~)
%             % 清除所有数据(接收区、温度数据和图表)
% 
%             % 确认对话框
%             choice = questdlg('确定要清除所有数据吗?', ...
%                 '确认清除', ...
%                 '是', '否', '否');
% 
%             if strcmp(choice, '是')
%                 try
%                     fprintf('清除所有数据...\n');
% 
%                     % 1. 清空接收区
%                     obj.clearReceiveText();
% 
%                     % 2. 清空温度数据
%                     obj.dataBuffer = [];
%                     obj.timeBuffer = [];
%                     obj.dataTable = table();
%                     obj.sampleCount = 0;
% 
%                     % 3. 重置温度图表
%                     set(obj.temperaturePlot, 'XData', NaN, 'YData', NaN);
% 
%                     % 4. 重置温度显示
%                     set(obj.currentTempText, 'String', '-- °C');
% 
%                     % 5. 重置采样计数
%                     set(obj.sampleCountText, 'String', '0');
% 
%                     % 6. 重置坐标轴
%                     xlim(obj.ax, [0, 10]);
%                     ylim(obj.ax, [0, 50]);
% 
%                     fprintf('所有数据已清除\n');
% 
%                     % 显示成功提示
%                     msgbox('所有数据已成功清除', '清除成功', 'help');
% 
%                 catch ME
%                     warning('清除所有数据失败: %s', ME.message);
%                     errordlg(['清除所有数据失败: ', ME.message], '错误');
%                 end
%             end
%         end
% 
%         function setCommandText(obj, command)
%             % 设置命令文本框的内容
%             commandEdit = findobj(obj.fig, 'Tag', 'commandEdit');
%             set(commandEdit, 'String', command);
%         end
% 
%         function sendCustomCommand(obj)
%             % 发送自定义命令
%             if ~obj.isConnected
%                 warndlg('请先连接串口!', '警告');
%                 return;
%             end
% 
%             commandEdit = findobj(obj.fig, 'Tag', 'commandEdit');
%             command = get(commandEdit, 'String');
% 
%             if isempty(command)
%                 return;
%             end
% 
%             try
%                 % 发送命令
%                 writeline(obj.serialPort, command);
% 
%                 % 在接收区显示发送的命令
%                 obj.appendToReceiveText(['[发送] ', command]);
% 
%                 % 记录命令历史
%                 obj.commandHistory{end+1} = command;
% 
%                 % 如果是AT+T?命令,初始化状态
%                 if strcmp(command, 'AT+T?')
%                     obj.expectingTemperature = false;  % 先等待OK
% 
%                     % 更新设备状态
%                     set(obj.deviceStatusText, 'String', '命令已发送,等待响应');
%                     set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
% 
%                     % 启动超时计时器
%                     obj.startTimeoutTimer();
%                 end
% 
%                 fprintf('已发送命令: %s\n', command);
% 
%             catch ME
%                 warning('发送命令失败: %s', ME.message);
%                 obj.appendToReceiveText(['[错误] 发送失败: ', ME.message]);
%             end
%         end
% 
%         function appendToReceiveText(obj, text)
%             % 向接收文本框追加文本
%             if isstring(text)
%                 text = char(text);  % 将string转换为字符向量
%             end
% 
%             % 使用datetime生成时间戳
%             timestamp = datetime('now', 'Format', 'HH:mm:ss.SSS');
% 
%             % 使用sprintf格式化字符串
%             newText = sprintf('[%s] %s', char(timestamp), text);
% 
%             % 获取当前文本
%             currentText = get(obj.receiveText, 'String');
% 
%             % 处理不同类型的输入
%             if iscell(currentText)
%                 % 如果是单元格数组,添加到开头
%                 newCell = [{newText}; currentText];
% 
%                 % 限制显示行数(最多200行)
%                 if length(newCell) > 200
%                     newCell = newCell(1:200);
%                 end
% 
%                 set(obj.receiveText, 'String', newCell);
%             elseif ischar(currentText) || isstring(currentText)
%                 % 如果是字符向量或字符串
%                 if isempty(currentText)
%                     set(obj.receiveText, 'String', {newText});
%                 else
%                     set(obj.receiveText, 'String', [{newText}; {currentText}]);
%                 end
%             else
%                 % 其他情况,直接设置
%                 set(obj.receiveText, 'String', {newText});
%             end
% 
%             % 强制更新显示
%             drawnow;
%         end
% 
%         function updateInterval(obj)
%             % 更新查询间隔
%             try
%                 intervalEdit = findobj(obj.fig, 'Tag', 'intervalEdit');
%                 newInterval = str2double(get(intervalEdit, 'String'));
% 
%                 if isnan(newInterval) || newInterval <= 0
%                     warndlg('请输入有效的正数', '无效输入');
%                     set(intervalEdit, 'String', num2str(obj.queryTimer.Period));
%                     return;
%                 end
% 
%                 % 更新定时器间隔
%                 if strcmp(obj.queryTimer.Running, 'on')
%                     stop(obj.queryTimer);
%                     obj.queryTimer.Period = newInterval;
%                     start(obj.queryTimer);
%                 else
%                     obj.queryTimer.Period = newInterval;
%                 end
% 
%                 fprintf('查询间隔已更新为: %.2f 秒\n', newInterval);
%                 obj.appendToReceiveText(['查询间隔更新为: ', num2str(newInterval), ' 秒']);
% 
%             catch ME
%                 warning('更新查询间隔失败: %s', ME.message);
%             end
%         end
% 
%         function updateMaxPoints(obj)
%             % 更新显示数据点数
%             try
%                 maxPointsEdit = findobj(obj.fig, 'Tag', 'maxPointsEdit');
%                 newMaxPoints = str2double(get(maxPointsEdit, 'String'));
% 
%                 if isnan(newMaxPoints) || newMaxPoints < 10
%                     warndlg('请输入有效的数字(最小10)', '无效输入');
%                     set(maxPointsEdit, 'String', num2str(obj.maxPoints));
%                     return;
%                 end
% 
%                 obj.maxPoints = newMaxPoints;
% 
%                 % 如果当前数据超过新的最大点数,进行截断
%                 if length(obj.dataBuffer) > obj.maxPoints
%                     obj.dataBuffer = obj.dataBuffer(end-obj.maxPoints+1:end);
%                     obj.timeBuffer = obj.timeBuffer(end-obj.maxPoints+1:end);
% 
%                     % 更新绘图
%                     if ~isempty(obj.timeBuffer)
%                         set(obj.temperaturePlot, ...
%                             'XData', obj.timeBuffer, ...
%                             'YData', obj.dataBuffer);
%                     end
%                 end
% 
%                 fprintf('显示数据点数已更新为: %d\n', newMaxPoints);
%                 obj.appendToReceiveText(['显示数据点数更新为: ', num2str(newMaxPoints)]);
% 
%             catch ME
%                 warning('更新数据点数失败: %s', ME.message);
%             end
%         end
% 
%         function toggleMonitoring(obj, ~, ~)
%             % 开始/停止监控
%             if ~obj.isConnected
%                 warndlg('请先连接串口!', '警告');
%                 return;
%             end
% 
%             if ~obj.isRunning
%                 % 开始监控
%                 obj.startMonitoring();
%             else
%                 % 停止监控
%                 obj.stopMonitoring();
%             end
%         end
% 
%         function startMonitoring(obj)
%             % 开始监控
%             fprintf('开始监控温度数据...\n');
% 
%             obj.isRunning = true;
%             obj.startTime = datetime('now');
%             obj.dataBuffer = [];
%             obj.timeBuffer = [];
%             obj.dataTable = table();
%             obj.deviceReady = false;
%             obj.expectingTemperature = false;
%             obj.sampleCount = 0;
% 
%             % 清空串口缓冲区
%             flush(obj.serialPort);
% 
%             % 更新按钮文本
%             set(obj.startBtn, 'String', '停止监控');
%             set(obj.startBtn, 'BackgroundColor', [0.9, 0.3, 0.3]);
% 
%             % 更新状态
%             set(obj.systemStatusText, 'String', '监控中...');
%             set(obj.systemStatusText, 'ForegroundColor', 'green');
% 
%             set(obj.deviceStatusText, 'String', '等待设备响应');
%             set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
% 
%             % 发送温度查询命令
%             obj.sendTemperatureQuery();
% 
%             % 启动定时器
%             start(obj.queryTimer);
% 
%             % 重置采样计数显示
%             set(obj.sampleCountText, 'String', '0');
% 
%             % 使用datetime获取当前时间
%             currentTime = datetime('now', 'Format', 'HH:mm:ss');
% 
%             disp('开始监控温度数据');
%             obj.appendToReceiveText('开始监控温度数据');
%             obj.appendToReceiveText(['已发送AT+T?命令,等待设备响应... [', char(currentTime), ']']);
%         end
% 
%         function stopMonitoring(obj)
%             % 停止监控
%             fprintf('停止监控温度数据...\n');
% 
%             obj.isRunning = false;
%             obj.expectingTemperature = false;
% 
%             % 停止定时器
%             if isvalid(obj.queryTimer)
%                 stop(obj.queryTimer);
%             end
% 
%             % 停止超时计时器
%             if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
%                 stop(obj.timeoutTimer);
%             end
% 
%             if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
%                 stop(obj.tempTimeoutTimer);
%             end
% 
%             % 更新按钮文本
%             set(obj.startBtn, 'String', '开始监控');
%             set(obj.startBtn, 'BackgroundColor', [0.3, 0.7, 0.3]);
% 
%             % 更新状态
%             set(obj.systemStatusText, 'String', '已停止');
%             set(obj.systemStatusText, 'ForegroundColor', 'blue');
% 
%             set(obj.deviceStatusText, 'String', '监控已停止');
%             set(obj.deviceStatusText, 'ForegroundColor', 'red');
% 
%             % 如果有自动保存,则保存数据
%             if strcmp(get(obj.autoSaveBtn, 'String'), '自动保存: 开启')
%                 obj.saveData();
%             end
% 
%             disp('停止监控温度数据');
%             obj.appendToReceiveText('停止监控温度数据');
%         end
% 
%         function sendTemperatureQuery(obj)
%             % 发送AT+T?命令查询温度
%             if ~obj.isRunning
%                 return;
%             end
% 
%             try
%                 % 发送查询命令
%                 writeline(obj.serialPort, "AT+T?");
%                 obj.expectingTemperature = false;  % 先等待OK响应
% 
%                 % 在接收区显示发送的命令
%                 obj.appendToReceiveText('[发送] AT+T?');
% 
%                 % 更新设备状态
%                 set(obj.deviceStatusText, 'String', '命令已发送,等待OK响应');
%                 set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
% 
%                 % 启动超时计时器
%                 obj.startTimeoutTimer();
% 
%             catch ME
%                 warning('发送查询命令失败: %s', ME.message);
%                 obj.appendToReceiveText(['[错误] 发送AT+T?失败: ', ME.message]);
% 
%                 % 更新设备状态
%                 set(obj.deviceStatusText, 'String', '发送失败');
%                 set(obj.deviceStatusText, 'ForegroundColor', 'red');
%             end
%         end
% 
%         function queryTemperature(obj)
%             % 定时查询温度(由定时器调用)
%             if ~obj.isRunning
%                 return;
%             end
% 
%             % 发送查询命令
%             obj.sendTemperatureQuery();
%         end
% 
%         function startTimeoutTimer(obj)
%             % 启动超时计时器,防止等待响应超时
%             if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
%                 stop(obj.timeoutTimer);
%                 delete(obj.timeoutTimer);
%             end
% 
%             obj.timeoutTimer = timer(...
%                 'ExecutionMode', 'singleShot', ...
%                 'StartDelay', 2, ...  % 2秒超时
%                 'TimerFcn', @(~, ~)obj.handleTimeout(), ...
%                 'Name', 'ResponseTimeoutTimer');
% 
%             start(obj.timeoutTimer);
%         end
% 
%         function handleTimeout(obj)
%             % 处理响应超时
%             if ~obj.isRunning
%                 return;
%             end
% 
%             obj.appendToReceiveText('[超时] 等待响应超时');
% 
%             % 更新设备状态
%             set(obj.deviceStatusText, 'String', '等待超时');
%             set(obj.deviceStatusText, 'ForegroundColor', 'orange');
% 
%             % 重置状态
%             obj.expectingTemperature = false;
%         end
% 
%         function startTemperatureTimeoutTimer(obj)
%             % 启动温度数据接收超时计时器
%             if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
%                 stop(obj.tempTimeoutTimer);
%                 delete(obj.tempTimeoutTimer);
%             end
% 
%             obj.tempTimeoutTimer = timer(...
%                 'ExecutionMode', 'singleShot', ...
%                 'StartDelay', 1, ...  % 1秒超时
%                 'TimerFcn', @(~, ~)obj.handleTemperatureTimeout(), ...
%                 'Name', 'TemperatureTimeoutTimer');
% 
%             start(obj.tempTimeoutTimer);
%         end
% 
%         function handleTemperatureTimeout(obj)
%             % 处理温度数据接收超时
%             if ~obj.isRunning
%                 return;
%             end
% 
%             if obj.expectingTemperature
%                 obj.appendToReceiveText('[警告] 温度数据接收超时');
% 
%                 % 更新设备状态
%                 set(obj.deviceStatusText, 'String', '温度数据超时');
%                 set(obj.deviceStatusText, 'ForegroundColor', 'orange');
% 
%                 % 重置期望标志
%                 obj.expectingTemperature = false;
%             end
%         end
% 
%         function readDataCallback(obj, src, ~)
%             % 串口数据读取回调函数 - 完整修复版本
%             % 正确处理通信流程:发送AT+T? -> 接收OK -> 接收温度数据
% 
%             if ~obj.isRunning
%                 return;
%             end
% 
%             try
%                 % 读取一行数据
%                 data = readline(src);
%                 data = strtrim(data);  % 去除首尾空白字符
% 
%                 % 在接收区显示接收到的数据
%                 obj.appendToReceiveText(['[接收] ', data]);
% 
%                 % 如果是空数据,忽略
%                 if isempty(data)
%                     return;
%                 end
% 
%                 % 停止超时计时器
%                 if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
%                     stop(obj.timeoutTimer);
%                 end
% 
%                 if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
%                     stop(obj.tempTimeoutTimer);
%                 end
% 
%                 % 检查是否为OK响应
%                 if strcmpi(data, 'OK')
%                     % 收到OK响应
%                     obj.deviceReady = true;
% 
%                     % 更新设备状态
%                     set(obj.deviceStatusText, 'String', '收到OK,等待温度数据');
%                     set(obj.deviceStatusText, 'ForegroundColor', 'green');
% 
%                     % OK响应后期待温度数据
%                     obj.expectingTemperature = true;
% 
%                     % 启动温度数据接收超时计时器
%                     obj.startTemperatureTimeoutTimer();
% 
%                     return;
%                 end
% 
%                 % 检查是否期望接收温度数据
%                 if obj.expectingTemperature
%                     % 尝试解析温度值
%                     [temperature, success] = obj.parseTemperatureData(data);
% 
%                     if success
%                         % 数据除以10得到实际温度
%                         actualTemperature = temperature / 10;
% 
%                         % 处理温度数据
%                         obj.processTemperatureData(actualTemperature, temperature);
% 
%                         % 重置期望标志
%                         obj.expectingTemperature = false;
% 
%                         % 更新设备状态
%                         set(obj.deviceStatusText, 'String', '温度接收完成');
%                         set(obj.deviceStatusText, 'ForegroundColor', 'green');
% 
%                     else
%                         % 如果不是有效的温度数据
%                         obj.appendToReceiveText(['无效的温度数据: "', data, '"']);
%                         obj.expectingTemperature = false;
% 
%                         % 更新设备状态
%                         set(obj.deviceStatusText, 'String', '数据格式错误');
%                         set(obj.deviceStatusText, 'ForegroundColor', 'red');
%                     end
%                 else
%                     % 如果不是期望接收温度数据,可能是其他响应
%                     obj.appendToReceiveText(['接收到非期望数据: "', data, '"']);
% 
%                     % 尝试自动检测是否为温度数据
%                     [temperature, success] = obj.parseTemperatureData(data);
%                     if success
%                         obj.appendToReceiveText(['自动检测到温度数据: ', num2str(temperature)]);
%                         actualTemperature = temperature / 10;
%                         obj.processTemperatureData(actualTemperature, temperature);
%                     end
%                 end
% 
%             catch ME
%                 warning('数据读取错误: %s', ME.message);
%                 obj.appendToReceiveText(['[错误] 读取数据失败: ', ME.message]);
%             end
%         end
% 
%         function [temperature, success] = parseTemperatureData(obj, data)
%             % 解析温度数据
%             % 支持多种格式:纯数字、包含T=的数字、包含单位的数字等
% 
%             success = false;
%             temperature = NaN;
% 
%             % 尝试直接转换为数字
%             tempValue = str2double(data);
%             if ~isnan(tempValue)
%                 temperature = tempValue;
%                 success = true;
%                 return;
%             end
% 
%             % 尝试从字符串中提取数字
%             % 支持格式:T=253, Temp:253, 253C, 25.3
%             numStr = regexp(data, '[-+]?\d*\.?\d+', 'match');
%             if ~isempty(numStr)
%                 tempValue = str2double(numStr{1});
%                 if ~isnan(tempValue)
%                     temperature = tempValue;
%                     success = true;
%                     return;
%                 end
%             end
% 
%             % 如果都没有成功,尝试其他可能的格式
%             if contains(data, 'OK')
%                 % 这可能是一个误判的OK响应
%                 success = false;
%             elseif contains(data, 'ERROR') || contains(data, 'ERR')
%                 obj.appendToReceiveText(['设备报告错误: ', data]);
%                 success = false;
%             end
%         end
% 
%         function processTemperatureData(obj, actualTemperature, rawTemperature)
%             % 处理温度数据
%             % actualTemperature: 实际温度值
%             % rawTemperature: 原始温度值
% 
%             % 增加采样计数
%             obj.sampleCount = obj.sampleCount + 1;
% 
%             % 计算相对时间
%             currentTime = seconds(datetime('now') - obj.startTime);
% 
%             % 调试输出
%             obj.appendToReceiveText(['温度: ', num2str(actualTemperature, '%.2f'), ...
%                 ' °C (原始值: ', num2str(rawTemperature), ')']);
% 
%             % 更新数据缓冲区
%             obj.dataBuffer(end+1) = actualTemperature;
%             obj.timeBuffer(end+1) = currentTime;
% 
%             % 限制缓冲区大小
%             if length(obj.dataBuffer) > obj.maxPoints
%                 obj.dataBuffer = obj.dataBuffer(end-obj.maxPoints+1:end);
%                 obj.timeBuffer = obj.timeBuffer(end-obj.maxPoints+1:end);
%             end
% 
%             % 更新绘图
%             if ~isempty(obj.timeBuffer)
%                 set(obj.temperaturePlot, ...
%                     'XData', obj.timeBuffer, ...
%                     'YData', obj.dataBuffer);
% 
%                 % 自动调整坐标轴
%                 obj.adjustAxesLimits();
%             end
% 
%             % 更新温度显示
%             set(obj.currentTempText, 'String', [num2str(actualTemperature, '%.2f'), ' °C']);
% 
%             % 更新采样计数显示
%             set(obj.sampleCountText, 'String', num2str(obj.sampleCount));
% 
%             % 添加到数据表
%             newRow = table(datetime('now'), actualTemperature, currentTime, ...
%                 'VariableNames', {'Timestamp', 'Temperature', 'ElapsedTime'});
%             obj.dataTable = [obj.dataTable; newRow];
% 
%             % 实时保存到文件(如果启用自动保存)
%             if strcmp(get(obj.autoSaveBtn, 'String'), '自动保存: 开启')
%                 obj.appendToFile(newRow);
%             end
%         end
% 
%         function adjustAxesLimits(obj)
%             % 自动调整坐标轴范围
%             if ~isempty(obj.timeBuffer)
%                 xlim(obj.ax, [min(obj.timeBuffer), max(obj.timeBuffer)]);
%             end
% 
%             if ~isempty(obj.dataBuffer)
%                 yRange = [min(obj.dataBuffer), max(obj.dataBuffer)];
%                 if yRange(1) == yRange(2)
%                     % 如果所有值相同,设置一个合适的范围
%                     ylim(obj.ax, [yRange(1)-0.5, yRange(1)+0.5]);
%                 else
%                     % 添加10%的边距
%                     yMargin = (yRange(2) - yRange(1)) * 0.1;
%                     ylim(obj.ax, [yRange(1)-yMargin, yRange(2)+yMargin]);
%                 end
%             end
%         end
% 
%         function toggleAutoSave(obj, src, ~)
%             % 切换自动保存状态
%             if strcmp(get(src, 'String'), '自动保存: 关闭')
%                 set(src, 'String', '自动保存: 开启');
%                 set(src, 'BackgroundColor', [0.2, 0.8, 0.2]);
% 
%                 % 开始自动保存
%                 obj.startAutoSave();
%                 obj.appendToReceiveText('自动保存已开启');
%             else
%                 set(src, 'String', '自动保存: 关闭');
%                 set(src, 'BackgroundColor', [0.8, 0.2, 0.2]);
% 
%                 % 停止自动保存
%                 obj.stopAutoSave();
%                 obj.appendToReceiveText('自动保存已关闭');
%             end
%         end
% 
%         function startAutoSave(obj)
%             % 开始自动保存
%             if isempty(obj.fileID)
%                 % 使用datetime生成文件名
%                 currentDateTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
%                 obj.logFileName = ['temperature_log_', char(currentDateTime), '.csv'];
% 
%                 try
%                     obj.fileID = fopen(obj.logFileName, 'w');
% 
%                     if obj.fileID == -1
%                         error('无法创建日志文件: %s', obj.logFileName);
%                     end
% 
%                     % 写入表头
%                     fprintf(obj.fileID, 'Timestamp,ElapsedTime(s),Temperature(C)\n');
%                     obj.appendToReceiveText(['开始自动保存到文件: ', obj.logFileName]);
% 
%                     % 如果已有数据,保存现有数据
%                     if ~isempty(obj.dataTable)
%                         for i = 1:height(obj.dataTable)
%                             % 使用datetime格式化时间戳
%                             timestamp = obj.dataTable.Timestamp(i);
%                             if isdatetime(timestamp)
%                                 timestampStr = char(timestamp);
%                             else
%                                 timestampStr = char(datetime(timestamp, 'Format', 'yyyy-MM-dd HH:mm:ss.SSS'));
%                             end
%                             fprintf(obj.fileID, '%s,%.4f,%.2f\n', ...
%                                 timestampStr, obj.dataTable.ElapsedTime(i), obj.dataTable.Temperature(i));
%                         end
%                         obj.appendToReceiveText(['已保存 ', num2str(height(obj.dataTable)), ...
%                             ' 条现有数据到文件']);
%                     end
%                 catch ME
%                     warning('无法开始自动保存: %s', ME.message);
%                     obj.appendToReceiveText(['[错误] 开始自动保存失败: ', ME.message]);
%                     obj.fileID = [];
%                 end
%             end
%         end
% 
%         function appendToFile(obj, newRow)
%             % 追加新数据到文件
%             if ~isempty(obj.fileID) && obj.fileID > 0
%                 try
%                     % 使用datetime格式化时间戳
%                     timestamp = newRow.Timestamp;
%                     if isdatetime(timestamp)
%                         timestampStr = char(timestamp);
%                     else
%                         timestampStr = char(datetime(timestamp, 'Format', 'yyyy-MM-dd HH:mm:ss.SSS'));
%                     end
%                     fprintf(obj.fileID, '%s,%.4f,%.2f\n', ...
%                         timestampStr, newRow.ElapsedTime, newRow.Temperature);
%                 catch ME
%                     warning('保存数据到文件失败: %s', ME.message);
%                 end
%             end
%         end
% 
%         function stopAutoSave(obj)
%             % 停止自动保存
%             if ~isempty(obj.fileID) && obj.fileID > 0
%                 try
%                     fclose(obj.fileID);
%                     obj.appendToReceiveText(['已停止自动保存数据到文件: ', obj.logFileName]);
%                 catch ME
%                     warning('关闭文件时出错: %s', ME.message);
%                 end
%                 obj.fileID = [];
%             end
%         end
% 
%         function saveData(obj, ~, ~)
%             % 手动保存数据到文件
%             if isempty(obj.dataTable)
%                 warndlg('没有数据可保存!', '警告');
%                 return;
%             end
% 
%             % 使用datetime生成文件名
%             currentDateTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
%             defaultName = ['temperature_data_', char(currentDateTime), '.csv'];
% 
%             % 选择保存位置
%             [selectedFileName, selectedPathName] = uiputfile( ...
%                 {'*.csv', 'CSV文件 (*.csv)'; ...
%                  '*.mat', 'MAT文件 (*.mat)'; ...
%                  '*.xlsx', 'Excel文件 (*.xlsx)'}, ...
%                 '保存数据', defaultName);
% 
%             if selectedFileName ~= 0
%                 fullPath = fullfile(selectedPathName, selectedFileName);
%                 [~, ~, fileExtension] = fileparts(fullPath);
% 
%                 try
%                     switch lower(fileExtension)
%                         case '.csv'
%                             writetable(obj.dataTable, fullPath);
%                         case '.mat'
%                             temperatureData = obj.dataTable;
%                             save(fullPath, 'temperatureData');
%                         case '.xlsx'
%                             writetable(obj.dataTable, fullPath);
%                     end
%                     obj.appendToReceiveText(['数据已保存到: ', fullPath]);
%                     msgbox(['数据已成功保存到: ', fullPath], '保存成功');
% 
%                     fprintf('数据已保存到: %s\n', fullPath);
% 
%                 catch ME
%                     errordlg(['保存失败: ', ME.message], '错误');
%                     obj.appendToReceiveText(['[错误] 保存失败: ', ME.message]);
%                 end
%             end
%         end
% 
%         function closeFigure(obj, ~, ~)
%             % 关闭图形窗口时的清理工作
%             % 确认对话框
%             choice = questdlg('确定要退出系统吗?', ...
%                 '确认退出', ...
%                 '是', '否', '否');
% 
%             if ~strcmp(choice, '是')
%                 return;
%             end
% 
%             fprintf('正在关闭系统...\n');
% 
%             if obj.isRunning
%                 obj.isRunning = false;
%             end
% 
%             % 停止定时器
%             if isvalid(obj.queryTimer)
%                 stop(obj.queryTimer);
%                 delete(obj.queryTimer);
%             end
% 
%             % 停止超时计时器
%             if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
%                 stop(obj.timeoutTimer);
%                 delete(obj.timeoutTimer);
%             end
% 
%             if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
%                 stop(obj.tempTimeoutTimer);
%                 delete(obj.tempTimeoutTimer);
%             end
% 
%             % 断开串口
%             obj.disconnectSerialPort();
% 
%             % 关闭文件(如果打开)
%             obj.stopAutoSave();
% 
%             % 删除图形
%             try
%                 delete(obj.fig);
%             catch ME
%                 warning('删除图形窗口时出错: %s', ME.message);
%             end
% 
%             fprintf('系统已退出\n');
%         end
% 
%         function delete(obj)
%             % 析构函数
%             obj.closeFigure();
%         end
%     end
% end



classdef TemperatureMonitor < handle
    % 温度监测类 - 实时读取、显示和保存串口温度数据
    % 完整版本,支持串口扫描、波特率选择、数据解析等功能
    
    properties
        serialPort              % 串口对象
        fig                     % 主图形窗口句柄
        ax                      % 坐标轴句柄
        temperaturePlot         % 温度曲线绘图句柄
        dataBuffer              % 温度数据缓冲区
        timeBuffer              % 时间数据缓冲区
        maxPoints = 1000        % 显示的最大数据点数
        isRunning = false       % 是否正在运行
        startTime               % 监控开始时间
        dataTable               % 数据表格
        queryTimer              % 查询定时器
        deviceReady = false     % 设备是否准备好
        sampleCount = 0         % 采样计数
        expectingTemperature = false  % 是否期望接收温度数据
        isConnected = false     % 串口连接状态
    end
    
    properties (Access = private)
        logFileName             % 日志文件名
        fileID                  % 文件ID
        receiveText             % 接收数据显示文本框句柄
        commandHistory          % 命令历史记录
        portDropdown            % 串口号下拉菜单句柄
        baudRateDropdown        % 波特率下拉菜单句柄
        connectButton           % 连接/断开按钮句柄
        connectionStatusText    % 连接状态文本句柄
        connectionInfoText      % 连接信息文本句柄
        messageText             % 消息文本句柄
        startBtn                % 开始/停止监控按钮句柄
        resendBtn               % 重新发送命令按钮句柄
        autoSaveBtn             % 自动保存开关按钮句柄
        deviceStatusText        % 设备状态文本句柄
        systemStatusText        % 系统状态文本句柄
        sampleCountText         % 采样点数文本句柄
        currentTempText         % 当前温度文本句柄
        comInfoText             % 串口信息文本句柄
        timeoutTimer            % 超时定时器
        tempTimeoutTimer        % 温度数据超时定时器
        
        defaultPorts = {'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'COM10'}
        defaultBaudRates = {'9600', '19200', '38400', '57600', '115200', '230400', '460800', '921600'}
    end
    
    methods
        function obj = TemperatureMonitor(varargin)
            % 构造函数
            % TemperatureMonitor() - 无参数,创建GUI后手动选择串口
            % TemperatureMonitor(port, baudRate, queryInterval) - 带参数创建
            
            fprintf('=== 温度监测系统初始化 ===\n');
            
            % 初始化数据缓冲
            obj.dataBuffer = [];
            obj.timeBuffer = [];
            obj.dataTable = table();
            obj.commandHistory = {};
            
            % 解析输入参数
            if nargin >= 2
                port = varargin{1};
                baudRate = varargin{2};
                if nargin >= 3
                    queryInterval = varargin{3};
                else
                    queryInterval = 1;
                end
                
                % 尝试打开串口
                try
                    obj.connectSerialPort(port, baudRate);
                catch ME
                    warning('串口连接失败: %s', ME.message);
                    obj.isConnected = false;
                end
            else
                % 无参数调用,先创建GUI,不立即连接串口
                port = 'COM3';  % 默认串口
                baudRate = 9600; % 默认波特率
                if nargin == 1
                    queryInterval = varargin{1};
                else
                    queryInterval = 1;
                end
                obj.isConnected = false;
            end
            
            % 创建查询定时器
            obj.queryTimer = timer(...
                'ExecutionMode', 'fixedRate', ...
                'Period', queryInterval, ...
                'TimerFcn', @(~, ~)obj.queryTemperature(), ...
                'Name', 'TemperatureQueryTimer', ...
                'BusyMode', 'queue');
            
            % 创建GUI界面
            obj.createGUI(port, baudRate, queryInterval);
            
            fprintf('温度监测系统初始化完成\n');
        end
        
        function createGUI(obj, defaultPort, defaultBaudRate, queryInterval)
            % 创建图形界面 - 优化版本,确保所有元素可见
            
            fprintf('正在创建GUI界面...\n');
            
            % 获取屏幕尺寸
            screenSize = get(0, 'ScreenSize');
            screenWidth = screenSize(3);
            screenHeight = screenSize(4);
            
            % 计算窗口尺寸(适应屏幕)
            windowWidth = min(1200, screenWidth * 0.7);
            windowHeight = min(750, screenHeight * 0.7);
            
            % 计算窗口位置(居中显示)
            windowLeft = (screenWidth - windowWidth) / 2;
            windowTop = (screenHeight - windowHeight) / 2;
            
            obj.fig = figure('Name', '温度实时监测与串口调试系统', ...
                'NumberTitle', 'off', ...
                'Position', [windowLeft, windowTop, windowWidth, windowHeight], ...
                'CloseRequestFcn', @obj.closeFigure, ...
                'MenuBar', 'none', ...
                'ToolBar', 'figure', ...
                'Resize', 'on');  % 允许调整窗口大小
            
            % 创建连接配置区域
            configPanel = uipanel('Parent', obj.fig, ...
                'Title', '串口配置', ...
                'Position', [0.02, 0.85, 0.96, 0.14], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 串口号选择
            uicontrol('Parent', configPanel, ...
                'Style', 'text', ...
                'String', '串口号:', ...
                'Position', [20, 70, 80, 25], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 获取可用串口
            availablePorts = obj.scanSerialPorts();
            if isempty(availablePorts)
                availablePorts = obj.defaultPorts;
            end
            
            % 设置默认选择的端口
            defaultPortIdx = find(strcmp(availablePorts, defaultPort), 1);
            if isempty(defaultPortIdx)
                defaultPortIdx = 1;
                if ~isempty(availablePorts)
                    defaultPort = availablePorts{1};
                else
                    defaultPort = 'COM1';
                end
            end
            
            obj.portDropdown = uicontrol('Parent', configPanel, ...
                'Style', 'popupmenu', ...
                'String', availablePorts, ...
                'Value', defaultPortIdx, ...
                'Position', [100, 70, 120, 25], ...
                'FontSize', 10, ...
                'BackgroundColor', 'white', ...
                'Tag', 'portDropdown');
            
            % 扫描串口按钮
            uicontrol('Parent', configPanel, ...
                'Style', 'pushbutton', ...
                'String', '扫描串口', ...
                'Position', [230, 70, 80, 25], ...
                'FontSize', 10, ...
                'BackgroundColor', [0.2, 0.6, 1.0], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @(~, ~)obj.scanAndUpdatePorts());
            
            % 波特率选择
            uicontrol('Parent', configPanel, ...
                'Style', 'text', ...
                'String', '波特率:', ...
                'Position', [320, 70, 80, 25], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 设置默认选择的波特率
            defaultBaudRateIdx = find(strcmp(obj.defaultBaudRates, num2str(defaultBaudRate)), 1);
            if isempty(defaultBaudRateIdx)
                defaultBaudRateIdx = 1;
            end
            
            obj.baudRateDropdown = uicontrol('Parent', configPanel, ...
                'Style', 'popupmenu', ...
                'String', obj.defaultBaudRates, ...
                'Value', defaultBaudRateIdx, ...
                'Position', [400, 70, 120, 25], ...
                'FontSize', 10, ...
                'BackgroundColor', 'white', ...
                'Tag', 'baudRateDropdown');
            
            % 连接/断开按钮
            obj.connectButton = uicontrol('Parent', configPanel, ...
                'Style', 'pushbutton', ...
                'String', '连接串口', ...
                'Position', [530, 70, 100, 25], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.3, 0.7, 0.3], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @obj.toggleConnection);
            
            % 退出系统按钮
            uicontrol('Parent', configPanel, ...
                'Style', 'pushbutton', ...
                'String', '退出系统', ...
                'Position', [640, 70, 100, 25], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.9, 0.3, 0.3], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @(~, ~)obj.closeFigure());
            
            % 连接状态显示
            obj.connectionStatusText = uicontrol('Parent', configPanel, ...
                'Style', 'text', ...
                'String', '未连接', ...
                'Position', [750, 70, 150, 25], ...
                'FontSize', 10, ...
                'ForegroundColor', 'red', ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 连接信息显示
            obj.connectionInfoText = uicontrol('Parent', configPanel, ...
                'Style', 'text', ...
                'String', sprintf('端口: %s, 波特率: %s', defaultPort, num2str(defaultBaudRate)), ...
                'Position', [20, 30, 300, 25], ...
                'FontSize', 9, ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 消息显示区域
            obj.messageText = uicontrol('Parent', configPanel, ...
                'Style', 'text', ...
                'String', '请选择串口并点击"连接串口"按钮开始', ...
                'Position', [20, 5, 500, 25], ...
                'FontSize', 9, ...
                'ForegroundColor', 'blue', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 创建温度监测区域
            tempPanel = uipanel('Parent', obj.fig, ...
                'Title', '温度监测', ...
                'Position', [0.02, 0.48, 0.63, 0.35], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 温度绘图区域
            obj.ax = axes('Parent', tempPanel, ...
                'Position', [0.10, 0.15, 0.85, 0.80]);
            xlabel(obj.ax, '时间 (秒)', 'FontSize', 10, 'FontWeight', 'bold');
            ylabel(obj.ax, '温度 (°C)', 'FontSize', 10, 'FontWeight', 'bold');
            title(obj.ax, '温度实时变化曲线', 'FontSize', 11, 'FontWeight', 'bold');
            grid(obj.ax, 'on');
            hold(obj.ax, 'on');
            
            % 设置坐标轴颜色和字体
            set(obj.ax, 'XColor', [0.2, 0.2, 0.2], 'YColor', [0.2, 0.2, 0.2]);
            set(obj.ax, 'FontSize', 9);
            
            % 初始化绘图
            obj.temperaturePlot = plot(obj.ax, NaN, NaN, ...
                'b-', 'LineWidth', 1.5, ...
                'Marker', 'o', 'MarkerSize', 4, ...
                'MarkerFaceColor', 'r', ...
                'MarkerEdgeColor', 'b');
            
            % 设置初始坐标轴范围
            xlim(obj.ax, [0, 10]);
            ylim(obj.ax, [0, 50]);
            
            % 创建串口调试区域
            debugPanel = uipanel('Parent', obj.fig, ...
                'Title', '串口调试', ...
                'Position', [0.02, 0.02, 0.63, 0.45], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 接收数据显示区域
            uicontrol('Parent', debugPanel, ...
                'Style', 'text', ...
                'String', '接收数据:', ...
                'Position', [10, 370, 80, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 接收数据显示文本框(可滚动)
            obj.receiveText = uicontrol('Parent', debugPanel, ...
                'Style', 'edit', ...
                'String', '', ...
                'Position', [10, 50, 580, 320], ...
                'FontSize', 9, ...
                'Max', 100, ...  % 允许多行
                'Min', 0, ...    % 允许多行
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', 'white', ...
                'Enable', 'inactive', ...  % 设置为只读
                'Tag', 'receiveText');    % 添加标签以便查找
            
            % 发送命令区域
            uicontrol('Parent', debugPanel, ...
                'Style', 'text', ...
                'String', '发送命令:', ...
                'Position', [10, 400, 80, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            commandEdit = uicontrol('Parent', debugPanel, ...
                'Style', 'edit', ...
                'String', 'AT+T?', ...
                'Position', [100, 395, 350, 25], ...
                'FontSize', 10, ...
                'Tag', 'commandEdit', ...
                'BackgroundColor', 'white');
            
            % 发送按钮
            uicontrol('Parent', debugPanel, ...
                'Style', 'pushbutton', ...
                'String', '发送', ...
                'Position', [460, 395, 60, 25], ...
                'FontSize', 10, ...
                'BackgroundColor', [0.1, 0.5, 0.9], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @(~, ~)obj.sendCustomCommand());
            
            % 清空接收区按钮
            uicontrol('Parent', debugPanel, ...
                'Style', 'pushbutton', ...
                'String', '清空接收区', ...
                'Position', [530, 395, 80, 25], ...
                'FontSize', 10, ...
                'BackgroundColor', [0.9, 0.5, 0.1], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'TooltipString', '清除串口调试窗口中的所有内容', ...
                'Callback', @(~, ~)obj.clearReceiveText());
            
            % 常用命令
            commands = {'AT+T?', 'AT+VER', 'AT+HELP', 'AT+STATUS'};
            commandNames = {'查询温度', '版本信息', '帮助信息', '状态查询'};
            for i = 1:length(commands)
                uicontrol('Parent', debugPanel, ...
                    'Style', 'pushbutton', ...
                    'String', commandNames{i}, ...
                    'Position', [100 + (i-1)*120, 20, 100, 20], ...
                    'FontSize', 9, ...
                    'BackgroundColor', [0.4, 0.4, 0.8], ...
                    'ForegroundColor', 'white', ...
                    'TooltipString', sprintf('发送命令: %s', commands{i}), ...
                    'Callback', @(src, ~)obj.setCommandText(commands{i}));
            end
            
            % 添加快捷键说明
            uicontrol('Parent', debugPanel, ...
                'Style', 'text', ...
                'String', '快捷键: Ctrl+L=清空, Ctrl+S=保存', ...
                'Position', [10, 20, 150, 20], ...
                'FontSize', 8, ...
                'ForegroundColor', [0.5, 0.5, 0.5], ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 创建控制面板
            controlPanel = uipanel('Parent', obj.fig, ...
                'Title', '控制面板', ...
                'Position', [0.67, 0.48, 0.31, 0.35], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 查询间隔设置
            uicontrol('Parent', controlPanel, ...
                'Style', 'text', ...
                'String', '查询间隔 (秒):', ...
                'Position', [15, 250, 180, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            uicontrol('Parent', controlPanel, ...
                'Style', 'edit', ...
                'String', num2str(queryInterval), ...
                'Position', [15, 225, 180, 25], ...
                'FontSize', 10, ...
                'Tag', 'intervalEdit', ...
                'BackgroundColor', 'white', ...
                'Callback', @(~, ~)obj.updateInterval);
            
            % 数据点数设置
            uicontrol('Parent', controlPanel, ...
                'Style', 'text', ...
                'String', '显示数据点数:', ...
                'Position', [15, 190, 180, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            uicontrol('Parent', controlPanel, ...
                'Style', 'edit', ...
                'String', num2str(obj.maxPoints), ...
                'Position', [15, 165, 180, 25], ...
                'FontSize', 10, ...
                'Tag', 'maxPointsEdit', ...
                'BackgroundColor', 'white', ...
                'Callback', @(~, ~)obj.updateMaxPoints);
            
            % 开始/停止按钮
            obj.startBtn = uicontrol('Parent', controlPanel, ...
                'Style', 'pushbutton', ...
                'String', '开始监控', ...
                'Position', [15, 120, 180, 35], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.3, 0.7, 0.3], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @obj.toggleMonitoring, ...
                'Enable', 'off');  % 初始时禁用,连接串口后启用
            
            % 重新发送命令按钮
            obj.resendBtn = uicontrol('Parent', controlPanel, ...
                'Style', 'pushbutton', ...
                'String', '重新发送AT+T?', ...
                'Position', [15, 75, 180, 35], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.9, 0.8, 0.1], ...
                'ForegroundColor', 'black', ...
                'FontWeight', 'bold', ...
                'Callback', @(~, ~)obj.sendTemperatureQuery(), ...
                'Enable', 'off');  % 初始时禁用
            
            % 创建数据保存面板
            savePanel = uipanel('Parent', obj.fig, ...
                'Title', '数据保存', ...
                'Position', [0.67, 0.30, 0.31, 0.17], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 手动保存按钮
            uicontrol('Parent', savePanel, ...
                'Style', 'pushbutton', ...
                'String', '手动保存数据', ...
                'Position', [15, 40, 180, 35], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.1, 0.5, 0.9], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Callback', @obj.saveData);
            
            % 自动保存开关
            obj.autoSaveBtn = uicontrol('Parent', savePanel, ...
                'Style', 'togglebutton', ...
                'String', '自动保存: 关闭', ...
                'Position', [15, 85, 180, 35], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.8, 0.2, 0.2], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'Tag', 'autoSaveBtn', ...
                'Callback', @obj.toggleAutoSave);
            
            % 创建状态显示面板
            statusPanel = uipanel('Parent', obj.fig, ...
                'Title', '状态信息', ...
                'Position', [0.67, 0.02, 0.31, 0.27], ...
                'FontSize', 11, ...
                'FontWeight', 'bold', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 设备状态
            uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '设备状态:', ...
                'Position', [15, 190, 70, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            obj.deviceStatusText = uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '未连接', ...
                'Position', [90, 190, 120, 20], ...
                'FontSize', 10, ...
                'ForegroundColor', 'red', ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 系统状态
            uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '系统状态:', ...
                'Position', [15, 165, 70, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            obj.systemStatusText = uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '等待连接', ...
                'Position', [90, 165, 120, 20], ...
                'FontSize', 10, ...
                'ForegroundColor', 'blue', ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 采样点数
            uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '采样点数:', ...
                'Position', [15, 140, 70, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            obj.sampleCountText = uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '0', ...
                'Position', [90, 140, 60, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 当前温度
            uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '当前温度:', ...
                'Position', [15, 115, 70, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            obj.currentTempText = uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '-- °C', ...
                'Position', [90, 115, 80, 20], ...
                'FontSize', 11, ...
                'ForegroundColor', 'blue', ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 串口信息
            uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '串口信息:', ...
                'Position', [15, 90, 70, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            obj.comInfoText = uicontrol('Parent', statusPanel, ...
                'Style', 'text', ...
                'String', '未连接', ...
                'Position', [90, 90, 120, 20], ...
                'FontSize', 10, ...
                'FontWeight', 'bold', ...
                'HorizontalAlignment', 'left', ...
                'BackgroundColor', [0.95, 0.95, 0.95]);
            
            % 清除所有数据按钮
            uicontrol('Parent', statusPanel, ...
                'Style', 'pushbutton', ...
                'String', '清除所有数据', ...
                'Position', [15, 50, 180, 30], ...
                'FontSize', 11, ...
                'BackgroundColor', [0.8, 0.3, 0.3], ...
                'ForegroundColor', 'white', ...
                'FontWeight', 'bold', ...
                'TooltipString', '清除接收区、温度数据和图表', ...
                'Callback', @obj.clearAllData);
            
            % 添加快捷键支持
            set(obj.fig, 'KeyPressFcn', @obj.keyboardShortcut);
            
            % 更新控件状态
            obj.updateControlStates();
            
            fprintf('GUI界面创建完成\n');
        end
        
        function ports = scanSerialPorts(obj)
            % 扫描可用串口
            try
                fprintf('正在扫描可用串口...\n');
                
                % 尝试使用MATLAB的串口列表功能
                if exist('serialportlist', 'file')
                    ports = serialportlist('available');
                    fprintf('使用 serialportlist 函数\n');
                elseif exist('seriallist', 'file')
                    ports = seriallist;
                    fprintf('使用 seriallist 函数\n');
                else
                    % 如果上述函数都不存在,使用默认端口列表
                    ports = obj.defaultPorts;
                    fprintf('使用默认端口列表\n');
                end
                
                % 转换为单元格数组
                if isstring(ports)
                    ports = cellstr(ports);
                elseif ischar(ports)
                    ports = {ports};
                end
                
                % 如果没有任何端口,使用默认列表
                if isempty(ports)
                    ports = obj.defaultPorts;
                    fprintf('未检测到可用串口,使用默认列表\n');
                end
                
                fprintf('扫描到 %d 个可用串口:\n', length(ports));
                for i = 1:length(ports)
                    fprintf('  %s\n', ports{i});
                end
                
            catch ME
                fprintf('扫描串口失败: %s\n', ME.message);
                ports = obj.defaultPorts;
            end
        end
        
        function scanAndUpdatePorts(obj)
            % 扫描并更新串口下拉菜单
            try
                fprintf('手动扫描串口...\n');
                
                % 扫描可用串口
                availablePorts = obj.scanSerialPorts();
                
                % 更新下拉菜单
                set(obj.portDropdown, 'String', availablePorts);
                if ~isempty(availablePorts)
                    set(obj.portDropdown, 'Value', 1);
                end
                
                % 更新消息
                set(obj.messageText, 'String', ...
                    sprintf('扫描完成,找到 %d 个可用串口', length(availablePorts)));
                set(obj.messageText, 'ForegroundColor', 'green');
                
                fprintf('串口列表已更新\n');
                
            catch ME
                warning('扫描串口失败: %s', ME.message);
                set(obj.messageText, 'String', '扫描串口失败');
                set(obj.messageText, 'ForegroundColor', 'red');
            end
        end
        
        function updateControlStates(obj)
            % 更新控件状态(启用/禁用)
            
            if obj.isConnected
                % 串口已连接,启用相关按钮
                set(obj.startBtn, 'Enable', 'on');
                set(obj.resendBtn, 'Enable', 'on');
                set(obj.connectButton, 'String', '断开连接');
                set(obj.connectButton, 'BackgroundColor', [0.9, 0.3, 0.3]);
                set(obj.connectionStatusText, 'String', '已连接');
                set(obj.connectionStatusText, 'ForegroundColor', 'green');
            else
                % 串口未连接,禁用相关按钮
                set(obj.startBtn, 'Enable', 'off');
                set(obj.resendBtn, 'Enable', 'off');
                set(obj.connectButton, 'String', '连接串口');
                set(obj.connectButton, 'BackgroundColor', [0.3, 0.7, 0.3]);
                set(obj.connectionStatusText, 'String', '未连接');
                set(obj.connectionStatusText, 'ForegroundColor', 'red');
                
                % 如果正在运行,停止监控
                if obj.isRunning
                    obj.stopMonitoring();
                end
            end
        end
        
        function toggleConnection(obj, ~, ~)
            % 连接/断开串口
            if ~obj.isConnected
                % 连接串口
                obj.connectSerialPort();
            else
                % 断开串口
                obj.disconnectSerialPort();
            end
        end
        
        function connectSerialPort(obj, port, baudRate)
            % 连接串口
            if nargin < 2
                % 从GUI获取参数
                portList = get(obj.portDropdown, 'String');
                portIdx = get(obj.portDropdown, 'Value');
                port = portList{portIdx};
                
                baudRateList = get(obj.baudRateDropdown, 'String');
                baudRateIdx = get(obj.baudRateDropdown, 'Value');
                baudRate = str2double(baudRateList{baudRateIdx});
            end
            
            try
                fprintf('正在连接串口 %s (波特率: %d)...\n', port, baudRate);
                
                % 如果已有串口连接,先断开
                if ~isempty(obj.serialPort) && isvalid(obj.serialPort)
                    obj.disconnectSerialPort();
                end
                
                % 创建串口对象
                obj.serialPort = serialport(port, baudRate);
                obj.serialPort.Timeout = 5;  % 设置超时为5秒
                configureTerminator(obj.serialPort, "CR/LF");
                
                % 设置回调函数
                configureCallback(obj.serialPort, "terminator", ...
                    @(src, event)obj.readDataCallback(src, event));
                
                % 更新状态
                obj.isConnected = true;
                obj.deviceReady = false;
                
                % 更新GUI
                set(obj.messageText, 'String', sprintf('串口 %s 连接成功', port));
                set(obj.messageText, 'ForegroundColor', 'green');
                
                set(obj.connectionInfoText, 'String', ...
                    sprintf('端口: %s, 波特率: %d', port, baudRate));
                
                % 更新状态面板中的串口信息
                set(obj.comInfoText, 'String', sprintf('%s, %d bps', port, baudRate));
                
                % 更新控件状态
                obj.updateControlStates();
                
                % 清空串口缓冲区
                flush(obj.serialPort);
                
                % 发送初始测试命令
                writeline(obj.serialPort, "AT");
                obj.appendToReceiveText('[发送] AT (测试连接)');
                
                fprintf('串口连接成功\n');
                
            catch ME
                % 连接失败
                obj.isConnected = false;
                set(obj.messageText, 'String', sprintf('连接失败: %s', ME.message));
                set(obj.messageText, 'ForegroundColor', 'red');
                
                fprintf('串口连接失败: %s\n', ME.message);
                errordlg(sprintf('无法打开串口 %s:\n%s', port, ME.message), '连接错误');
                
                % 更新控件状态
                obj.updateControlStates();
            end
        end
        
        function disconnectSerialPort(obj)
            % 断开串口连接
            if obj.isRunning
                obj.stopMonitoring();
            end
            
            if ~isempty(obj.serialPort) && isvalid(obj.serialPort)
                try
                    fprintf('正在断开串口连接...\n');
                    
                    % 移除回调函数
                    configureCallback(obj.serialPort, "off");
                    
                    % 关闭串口
                    delete(obj.serialPort);
                    obj.serialPort = [];
                    
                    set(obj.messageText, 'String', '串口已断开');
                    set(obj.messageText, 'ForegroundColor', 'blue');
                    
                    fprintf('串口已断开\n');
                    
                catch ME
                    warning('断开串口时出错: %s', ME.message);
                end
            end
            
            % 更新状态
            obj.isConnected = false;
            obj.deviceReady = false;
            
            % 更新控件状态
            obj.updateControlStates();
            
            % 更新状态面板
            set(obj.comInfoText, 'String', '未连接');
            set(obj.deviceStatusText, 'String', '未连接');
            set(obj.deviceStatusText, 'ForegroundColor', 'red');
        end
        
        function keyboardShortcut(obj, ~, event)
            % 键盘快捷键处理
            switch event.Key
                case 'l'
                    % Ctrl+L 清空接收区
                    if strcmp(event.Modifier, 'control')
                        obj.clearReceiveText();
                        disp('快捷键: Ctrl+L - 已清空接收区');
                    end
                case 'c'
                    % Ctrl+C 复制接收区内容到剪贴板
                    if strcmp(event.Modifier, 'control')
                        obj.copyReceiveTextToClipboard();
                    end
                case 's'
                    % Ctrl+S 保存数据
                    if strcmp(event.Modifier, 'control')
                        obj.saveData();
                    end
            end
        end
        
        function copyReceiveTextToClipboard(obj)
            % 复制接收区内容到剪贴板
            try
                currentText = get(obj.receiveText, 'String');
                if iscell(currentText)
                    % 将单元格数组转换为字符串
                    fullText = '';
                    for i = 1:length(currentText)
                        fullText = sprintf('%s%s\n', fullText, currentText{i});
                    end
                elseif ischar(currentText)
                    fullText = currentText;
                else
                    fullText = '';
                end
                
                clipboard('copy', fullText);
                fprintf('接收区内容已复制到剪贴板 (%d 字符)\n', length(fullText));
                
                % 显示短暂提示
                msg = msgbox('接收区内容已复制到剪贴板', '复制成功', 'help');
                pause(1);
                if ishandle(msg)
                    close(msg);
                end
            catch ME
                warning('复制到剪贴板失败: %s', ME.message);
            end
        end
        
        function clearReceiveText(obj)
            % 清空接收文本框内容
            try
                % 获取接收文本框句柄
                receiveTextHandle = findobj(obj.fig, 'Tag', 'receiveText');
                if isempty(receiveTextHandle)
                    receiveTextHandle = obj.receiveText;
                end
                
                % 清空文本框内容
                set(receiveTextHandle, 'String', '');
                
                % 清除命令历史记录
                obj.commandHistory = {};
                
                % 添加一条清空确认消息
                currentTime = datetime('now', 'Format', 'HH:mm:ss');
                confirmationMsg = ['接收区已清空 [', char(currentTime), ']'];
                
                % 在接收区显示确认消息
                obj.appendToReceiveText(confirmationMsg);
                
                fprintf('%s\n', confirmationMsg);
                
                % 强制更新显示
                drawnow;
                
                % 显示短暂的成功提示
                msg = msgbox('接收区内容已清空', '清空成功', 'help');
                pause(0.5);
                if ishandle(msg)
                    close(msg);
                end
                
            catch ME
                warning('清空接收区失败: %s', ME.message);
                errordlg(['清空接收区失败: ', ME.message], '错误');
            end
        end
        
        function clearAllData(obj, ~, ~)
            % 清除所有数据(接收区、温度数据和图表)
            
            % 确认对话框
            choice = questdlg('确定要清除所有数据吗?', ...
                '确认清除', ...
                '是', '否', '否');
            
            if strcmp(choice, '是')
                try
                    fprintf('清除所有数据...\n');
                    
                    % 1. 清空接收区
                    obj.clearReceiveText();
                    
                    % 2. 清空温度数据
                    obj.dataBuffer = [];
                    obj.timeBuffer = [];
                    obj.dataTable = table();
                    obj.sampleCount = 0;
                    
                    % 3. 重置温度图表
                    set(obj.temperaturePlot, 'XData', NaN, 'YData', NaN);
                    
                    % 4. 重置温度显示
                    set(obj.currentTempText, 'String', '-- °C');
                    
                    % 5. 重置采样计数
                    set(obj.sampleCountText, 'String', '0');
                    
                    % 6. 重置坐标轴
                    xlim(obj.ax, [0, 10]);
                    ylim(obj.ax, [0, 50]);
                    
                    fprintf('所有数据已清除\n');
                    
                    % 显示成功提示
                    msgbox('所有数据已成功清除', '清除成功', 'help');
                    
                catch ME
                    warning('清除所有数据失败: %s', ME.message);
                    errordlg(['清除所有数据失败: ', ME.message], '错误');
                end
            end
        end
        
        function setCommandText(obj, command)
            % 设置命令文本框的内容
            commandEdit = findobj(obj.fig, 'Tag', 'commandEdit');
            set(commandEdit, 'String', command);
        end
        
        function sendCustomCommand(obj)
            % 发送自定义命令
            if ~obj.isConnected
                warndlg('请先连接串口!', '警告');
                return;
            end
            
            commandEdit = findobj(obj.fig, 'Tag', 'commandEdit');
            command = get(commandEdit, 'String');
            
            if isempty(command)
                return;
            end
            
            try
                % 发送命令
                writeline(obj.serialPort, command);
                
                % 在接收区显示发送的命令
                obj.appendToReceiveText(['[发送] ', command]);
                
                % 记录命令历史
                obj.commandHistory{end+1} = command;
                
                % 如果是AT+T?命令,初始化状态
                if strcmp(command, 'AT+T?')
                    obj.expectingTemperature = false;  % 先等待OK
                    
                    % 更新设备状态
                    set(obj.deviceStatusText, 'String', '命令已发送,等待响应');
                    set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
                    
                    % 启动超时计时器
                    obj.startTimeoutTimer();
                end
                
                fprintf('已发送命令: %s\n', command);
                
            catch ME
                warning('发送命令失败: %s', ME.message);
                obj.appendToReceiveText(['[错误] 发送失败: ', ME.message]);
            end
        end
        
        function appendToReceiveText(obj, text)
            % 向接收文本框追加文本
            if isstring(text)
                text = char(text);  % 将string转换为字符向量
            end
            
            % 使用datetime生成时间戳
            timestamp = datetime('now', 'Format', 'HH:mm:ss.SSS');
            
            % 使用sprintf格式化字符串
            newText = sprintf('[%s] %s', char(timestamp), text);
            
            % 获取当前文本
            currentText = get(obj.receiveText, 'String');
            
            % 处理不同类型的输入
            if iscell(currentText)
                % 如果是单元格数组,添加到开头
                newCell = [{newText}; currentText];
                
                % 限制显示行数(最多200行)
                if length(newCell) > 200
                    newCell = newCell(1:200);
                end
                
                set(obj.receiveText, 'String', newCell);
            elseif ischar(currentText) || isstring(currentText)
                % 如果是字符向量或字符串
                if isempty(currentText)
                    set(obj.receiveText, 'String', {newText});
                else
                    set(obj.receiveText, 'String', [{newText}; {currentText}]);
                end
            else
                % 其他情况,直接设置
                set(obj.receiveText, 'String', {newText});
            end
            
            % 强制更新显示
            drawnow;
        end
        
        function updateInterval(obj)
            % 更新查询间隔
            try
                intervalEdit = findobj(obj.fig, 'Tag', 'intervalEdit');
                newInterval = str2double(get(intervalEdit, 'String'));
                
                if isnan(newInterval) || newInterval <= 0
                    warndlg('请输入有效的正数', '无效输入');
                    set(intervalEdit, 'String', num2str(obj.queryTimer.Period));
                    return;
                end
                
                % 更新定时器间隔
                if strcmp(obj.queryTimer.Running, 'on')
                    stop(obj.queryTimer);
                    obj.queryTimer.Period = newInterval;
                    start(obj.queryTimer);
                else
                    obj.queryTimer.Period = newInterval;
                end
                
                fprintf('查询间隔已更新为: %.2f 秒\n', newInterval);
                obj.appendToReceiveText(['查询间隔更新为: ', num2str(newInterval), ' 秒']);
                
            catch ME
                warning('更新查询间隔失败: %s', ME.message);
            end
        end
        
        function updateMaxPoints(obj)
            % 更新显示数据点数
            try
                maxPointsEdit = findobj(obj.fig, 'Tag', 'maxPointsEdit');
                newMaxPoints = str2double(get(maxPointsEdit, 'String'));
                
                if isnan(newMaxPoints) || newMaxPoints < 10
                    warndlg('请输入有效的数字(最小10)', '无效输入');
                    set(maxPointsEdit, 'String', num2str(obj.maxPoints));
                    return;
                end
                
                obj.maxPoints = newMaxPoints;
                
                % 如果当前数据超过新的最大点数,进行截断
                if length(obj.dataBuffer) > obj.maxPoints
                    obj.dataBuffer = obj.dataBuffer(end-obj.maxPoints+1:end);
                    obj.timeBuffer = obj.timeBuffer(end-obj.maxPoints+1:end);
                    
                    % 更新绘图
                    if ~isempty(obj.timeBuffer)
                        set(obj.temperaturePlot, ...
                            'XData', obj.timeBuffer, ...
                            'YData', obj.dataBuffer);
                    end
                end
                
                fprintf('显示数据点数已更新为: %d\n', newMaxPoints);
                obj.appendToReceiveText(['显示数据点数更新为: ', num2str(newMaxPoints)]);
                
            catch ME
                warning('更新数据点数失败: %s', ME.message);
            end
        end
        
        function toggleMonitoring(obj, ~, ~)
            % 开始/停止监控
            if ~obj.isConnected
                warndlg('请先连接串口!', '警告');
                return;
            end
            
            if ~obj.isRunning
                % 开始监控
                obj.startMonitoring();
            else
                % 停止监控
                obj.stopMonitoring();
            end
        end
        
        function startMonitoring(obj)
            % 开始监控
            fprintf('开始监控温度数据...\n');
            
            obj.isRunning = true;
            obj.startTime = datetime('now');
            obj.dataBuffer = [];
            obj.timeBuffer = [];
            obj.dataTable = table();
            obj.deviceReady = false;
            obj.expectingTemperature = false;
            obj.sampleCount = 0;
            
            % 清空串口缓冲区
            flush(obj.serialPort);
            
            % 更新按钮文本
            set(obj.startBtn, 'String', '停止监控');
            set(obj.startBtn, 'BackgroundColor', [0.9, 0.3, 0.3]);
            
            % 更新状态
            set(obj.systemStatusText, 'String', '监控中...');
            set(obj.systemStatusText, 'ForegroundColor', 'green');
            
            set(obj.deviceStatusText, 'String', '等待设备响应');
            set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
            
            % 发送温度查询命令
            obj.sendTemperatureQuery();
            
            % 启动定时器
            start(obj.queryTimer);
            
            % 重置采样计数显示
            set(obj.sampleCountText, 'String', '0');
            
            % 使用datetime获取当前时间
            currentTime = datetime('now', 'Format', 'HH:mm:ss');
            
            disp('开始监控温度数据');
            obj.appendToReceiveText('开始监控温度数据');
            obj.appendToReceiveText(['已发送AT+T?命令,等待设备响应... [', char(currentTime), ']']);
        end
        
        function stopMonitoring(obj)
            % 停止监控
            fprintf('停止监控温度数据...\n');
            
            obj.isRunning = false;
            obj.expectingTemperature = false;
            
            % 停止定时器
            if isvalid(obj.queryTimer)
                stop(obj.queryTimer);
            end
            
            % 停止超时计时器
            if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
                stop(obj.timeoutTimer);
            end
            
            if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
                stop(obj.tempTimeoutTimer);
            end
            
            % 更新按钮文本
            set(obj.startBtn, 'String', '开始监控');
            set(obj.startBtn, 'BackgroundColor', [0.3, 0.7, 0.3]);
            
            % 更新状态
            set(obj.systemStatusText, 'String', '已停止');
            set(obj.systemStatusText, 'ForegroundColor', 'blue');
            
            set(obj.deviceStatusText, 'String', '监控已停止');
            set(obj.deviceStatusText, 'ForegroundColor', 'red');
            
            % 如果有自动保存,则保存数据
            if strcmp(get(obj.autoSaveBtn, 'String'), '自动保存: 开启')
                obj.saveData();
            end
            
            disp('停止监控温度数据');
            obj.appendToReceiveText('停止监控温度数据');
        end
        
        function sendTemperatureQuery(obj)
            % 发送AT+T?命令查询温度
            if ~obj.isRunning
                return;
            end
            
            try
                % 发送查询命令
                writeline(obj.serialPort, "AT+T?");
                obj.expectingTemperature = false;  % 先等待OK响应
                
                % 在接收区显示发送的命令
                obj.appendToReceiveText('[发送] AT+T?');
                
                % 更新设备状态
                set(obj.deviceStatusText, 'String', '命令已发送,等待OK响应');
                set(obj.deviceStatusText, 'ForegroundColor', 'yellow');
                
                % 启动超时计时器
                obj.startTimeoutTimer();
                
            catch ME
                warning('发送查询命令失败: %s', ME.message);
                obj.appendToReceiveText(['[错误] 发送AT+T?失败: ', ME.message]);
                
                % 更新设备状态
                set(obj.deviceStatusText, 'String', '发送失败');
                set(obj.deviceStatusText, 'ForegroundColor', 'red');
            end
        end
        
        function queryTemperature(obj)
            % 定时查询温度(由定时器调用)
            if ~obj.isRunning
                return;
            end
            
            % 发送查询命令
            obj.sendTemperatureQuery();
        end
        
        function startTimeoutTimer(obj)
            % 启动超时计时器,防止等待响应超时
            if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
                stop(obj.timeoutTimer);
                delete(obj.timeoutTimer);
            end
            
            obj.timeoutTimer = timer(...
                'ExecutionMode', 'singleShot', ...
                'StartDelay', 2, ...  % 2秒超时
                'TimerFcn', @(~, ~)obj.handleTimeout(), ...
                'Name', 'ResponseTimeoutTimer');
            
            start(obj.timeoutTimer);
        end
        
        function handleTimeout(obj)
            % 处理响应超时
            if ~obj.isRunning
                return;
            end
            
            obj.appendToReceiveText('[超时] 等待响应超时');
            
            % 更新设备状态
            set(obj.deviceStatusText, 'String', '等待超时');
            set(obj.deviceStatusText, 'ForegroundColor', 'orange');
            
            % 重置状态
            obj.expectingTemperature = false;
        end
        
        function startTemperatureTimeoutTimer(obj)
            % 启动温度数据接收超时计时器
            if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
                stop(obj.tempTimeoutTimer);
                delete(obj.tempTimeoutTimer);
            end
            
            obj.tempTimeoutTimer = timer(...
                'ExecutionMode', 'singleShot', ...
                'StartDelay', 1, ...  % 1秒超时
                'TimerFcn', @(~, ~)obj.handleTemperatureTimeout(), ...
                'Name', 'TemperatureTimeoutTimer');
            
            start(obj.tempTimeoutTimer);
        end
        
        function handleTemperatureTimeout(obj)
            % 处理温度数据接收超时
            if ~obj.isRunning
                return;
            end
            
            if obj.expectingTemperature
                obj.appendToReceiveText('[警告] 温度数据接收超时');
                
                % 更新设备状态
                set(obj.deviceStatusText, 'String', '温度数据超时');
                set(obj.deviceStatusText, 'ForegroundColor', 'orange');
                
                % 重置期望标志
                obj.expectingTemperature = false;
            end
        end
        
        function readDataCallback(obj, src, ~)
            % 串口数据读取回调函数 - 完整修复版本
            % 正确处理通信流程:发送AT+T? -> 接收OK -> 接收温度数据
            
            if ~obj.isRunning
                return;
            end
            
            try
                % 读取一行数据
                data = readline(src);
                data = strtrim(data);  % 去除首尾空白字符
                
                % 在接收区显示接收到的数据
                obj.appendToReceiveText(['[接收] ', data]);
                
                % 如果是空数据,忽略
                if isempty(data)
                    return;
                end
                
                % 停止超时计时器
                if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
                    stop(obj.timeoutTimer);
                end
                
                if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
                    stop(obj.tempTimeoutTimer);
                end
                
                % 检查是否为OK响应
                if strcmpi(data, 'OK')
                    % 收到OK响应
                    obj.deviceReady = true;
                    
                    % 更新设备状态
                    set(obj.deviceStatusText, 'String', '收到OK,等待温度数据');
                    set(obj.deviceStatusText, 'ForegroundColor', 'green');
                    
                    % OK响应后期待温度数据
                    obj.expectingTemperature = true;
                    
                    % 启动温度数据接收超时计时器
                    obj.startTemperatureTimeoutTimer();
                    
                    return;
                end
                
                % 检查是否期望接收温度数据
                if obj.expectingTemperature
                    % 尝试解析温度值
                    [temperature, success] = obj.parseTemperatureData(data);
                    
                    if success
                        % 数据除以10得到实际温度
                        actualTemperature = temperature / 10;
                        
                        % 处理温度数据
                        obj.processTemperatureData(actualTemperature, temperature);
                        
                        % 重置期望标志
                        obj.expectingTemperature = false;
                        
                        % 更新设备状态
                        set(obj.deviceStatusText, 'String', '温度接收完成');
                        set(obj.deviceStatusText, 'ForegroundColor', 'green');
                        
                    else
                        % 如果不是有效的温度数据
                        obj.appendToReceiveText(['无效的温度数据: "', data, '"']);
                        obj.expectingTemperature = false;
                        
                        % 更新设备状态
                        set(obj.deviceStatusText, 'String', '数据格式错误');
                        set(obj.deviceStatusText, 'ForegroundColor', 'red');
                    end
                else
                    % 如果不是期望接收温度数据,可能是其他响应
                    obj.appendToReceiveText(['接收到非期望数据: "', data, '"']);
                    
                    % 尝试自动检测是否为温度数据
                    [temperature, success] = obj.parseTemperatureData(data);
                    if success
                        obj.appendToReceiveText(['自动检测到温度数据: ', num2str(temperature)]);
                        actualTemperature = temperature / 10;
                        obj.processTemperatureData(actualTemperature, temperature);
                    end
                end
                
            catch ME
                warning('数据读取错误: %s', ME.message);
                obj.appendToReceiveText(['[错误] 读取数据失败: ', ME.message]);
            end
        end
        
        function [temperature, success] = parseTemperatureData(obj, data)
            % 解析温度数据
            % 支持多种格式:纯数字、包含T=的数字、包含单位的数字等
            
            success = false;
            temperature = NaN;
            
            % 尝试直接转换为数字
            tempValue = str2double(data);
            if ~isnan(tempValue)
                temperature = tempValue;
                success = true;
                return;
            end
            
            % 尝试从字符串中提取数字
            % 支持格式:T=253, Temp:253, 253C, 25.3
            numStr = regexp(data, '[-+]?\d*\.?\d+', 'match');
            if ~isempty(numStr)
                tempValue = str2double(numStr{1});
                if ~isnan(tempValue)
                    temperature = tempValue;
                    success = true;
                    return;
                end
            end
            
            % 如果都没有成功,尝试其他可能的格式
            if contains(data, 'OK')
                % 这可能是一个误判的OK响应
                success = false;
            elseif contains(data, 'ERROR') || contains(data, 'ERR')
                obj.appendToReceiveText(['设备报告错误: ', data]);
                success = false;
            end
        end
        
        function processTemperatureData(obj, actualTemperature, rawTemperature)
            % 处理温度数据
            % actualTemperature: 实际温度值
            % rawTemperature: 原始温度值
            
            % 增加采样计数
            obj.sampleCount = obj.sampleCount + 1;
            
            % 计算相对时间
            currentTime = seconds(datetime('now') - obj.startTime);
            
            % 调试输出
            obj.appendToReceiveText(['温度: ', num2str(actualTemperature, '%.2f'), ...
                ' °C (原始值: ', num2str(rawTemperature), ')']);
            
            % 更新数据缓冲区
            obj.dataBuffer(end+1) = actualTemperature;
            obj.timeBuffer(end+1) = currentTime;
            
            % 限制缓冲区大小
            if length(obj.dataBuffer) > obj.maxPoints
                obj.dataBuffer = obj.dataBuffer(end-obj.maxPoints+1:end);
                obj.timeBuffer = obj.timeBuffer(end-obj.maxPoints+1:end);
            end
            
            % 更新绘图
            if ~isempty(obj.timeBuffer)
                set(obj.temperaturePlot, ...
                    'XData', obj.timeBuffer, ...
                    'YData', obj.dataBuffer);
                
                % 自动调整坐标轴
                obj.adjustAxesLimits();
            end
            
            % 更新温度显示
            set(obj.currentTempText, 'String', [num2str(actualTemperature, '%.2f'), ' °C']);
            
            % 更新采样计数显示
            set(obj.sampleCountText, 'String', num2str(obj.sampleCount));
            
            % 添加到数据表
            newRow = table(datetime('now'), actualTemperature, currentTime, ...
                'VariableNames', {'Timestamp', 'Temperature', 'ElapsedTime'});
            obj.dataTable = [obj.dataTable; newRow];
            
            % 实时保存到文件(如果启用自动保存)
            if strcmp(get(obj.autoSaveBtn, 'String'), '自动保存: 开启')
                obj.appendToFile(newRow);
            end
        end
        
        function adjustAxesLimits(obj)
            % 自动调整坐标轴范围
            if ~isempty(obj.timeBuffer)
                xlim(obj.ax, [min(obj.timeBuffer), max(obj.timeBuffer)]);
            end
            
            if ~isempty(obj.dataBuffer)
                yRange = [min(obj.dataBuffer), max(obj.dataBuffer)];
                if yRange(1) == yRange(2)
                    % 如果所有值相同,设置一个合适的范围
                    ylim(obj.ax, [yRange(1)-0.5, yRange(1)+0.5]);
                else
                    % 添加10%的边距
                    yMargin = (yRange(2) - yRange(1)) * 0.1;
                    ylim(obj.ax, [yRange(1)-yMargin, yRange(2)+yMargin]);
                end
            end
        end
        
        function toggleAutoSave(obj, src, ~)
            % 切换自动保存状态
            if strcmp(get(src, 'String'), '自动保存: 关闭')
                set(src, 'String', '自动保存: 开启');
                set(src, 'BackgroundColor', [0.2, 0.8, 0.2]);
                
                % 开始自动保存
                obj.startAutoSave();
                obj.appendToReceiveText('自动保存已开启');
            else
                set(src, 'String', '自动保存: 关闭');
                set(src, 'BackgroundColor', [0.8, 0.2, 0.2]);
                
                % 停止自动保存
                obj.stopAutoSave();
                obj.appendToReceiveText('自动保存已关闭');
            end
        end
        
        function startAutoSave(obj)
            % 开始自动保存
            if isempty(obj.fileID)
                % 使用datetime生成文件名
                currentDateTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
                obj.logFileName = ['temperature_log_', char(currentDateTime), '.csv'];
                
                try
                    obj.fileID = fopen(obj.logFileName, 'w');
                    
                    if obj.fileID == -1
                        error('无法创建日志文件: %s', obj.logFileName);
                    end
                    
                    % 写入表头
                    fprintf(obj.fileID, 'Timestamp,ElapsedTime(s),Temperature(C)\n');
                    obj.appendToReceiveText(['开始自动保存到文件: ', obj.logFileName]);
                    
                    % 如果已有数据,保存现有数据
                    if ~isempty(obj.dataTable)
                        for i = 1:height(obj.dataTable)
                            % 使用datetime格式化时间戳
                            timestamp = obj.dataTable.Timestamp(i);
                            if isdatetime(timestamp)
                                timestampStr = char(timestamp);
                            else
                                timestampStr = char(datetime(timestamp, 'Format', 'yyyy-MM-dd HH:mm:ss.SSS'));
                            end
                            fprintf(obj.fileID, '%s,%.4f,%.2f\n', ...
                                timestampStr, obj.dataTable.ElapsedTime(i), obj.dataTable.Temperature(i));
                        end
                        obj.appendToReceiveText(['已保存 ', num2str(height(obj.dataTable)), ...
                            ' 条现有数据到文件']);
                    end
                catch ME
                    warning('无法开始自动保存: %s', ME.message);
                    obj.appendToReceiveText(['[错误] 开始自动保存失败: ', ME.message]);
                    obj.fileID = [];
                end
            end
        end
        
        function appendToFile(obj, newRow)
            % 追加新数据到文件
            if ~isempty(obj.fileID) && obj.fileID > 0
                try
                    % 使用datetime格式化时间戳
                    timestamp = newRow.Timestamp;
                    if isdatetime(timestamp)
                        timestampStr = char(timestamp);
                    else
                        timestampStr = char(datetime(timestamp, 'Format', 'yyyy-MM-dd HH:mm:ss.SSS'));
                    end
                    fprintf(obj.fileID, '%s,%.4f,%.2f\n', ...
                        timestampStr, newRow.ElapsedTime, newRow.Temperature);
                catch ME
                    warning('保存数据到文件失败: %s', ME.message);
                end
            end
        end
        
        function stopAutoSave(obj)
            % 停止自动保存
            if ~isempty(obj.fileID) && obj.fileID > 0
                try
                    fclose(obj.fileID);
                    obj.appendToReceiveText(['已停止自动保存数据到文件: ', obj.logFileName]);
                catch ME
                    warning('关闭文件时出错: %s', ME.message);
                end
                obj.fileID = [];
            end
        end
        
        function saveData(obj, ~, ~)
            % 手动保存数据到文件
            if isempty(obj.dataTable)
                warndlg('没有数据可保存!', '警告');
                return;
            end
            
            % 使用datetime生成文件名
            currentDateTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
            defaultName = ['temperature_data_', char(currentDateTime), '.csv'];
            
            % 选择保存位置
            [selectedFileName, selectedPathName] = uiputfile( ...
                {'*.csv', 'CSV文件 (*.csv)'; ...
                 '*.mat', 'MAT文件 (*.mat)'; ...
                 '*.xlsx', 'Excel文件 (*.xlsx)'}, ...
                '保存数据', defaultName);
            
            if selectedFileName ~= 0
                fullPath = fullfile(selectedPathName, selectedFileName);
                [~, ~, fileExtension] = fileparts(fullPath);
                
                try
                    switch lower(fileExtension)
                        case '.csv'
                            writetable(obj.dataTable, fullPath);
                        case '.mat'
                            temperatureData = obj.dataTable;
                            save(fullPath, 'temperatureData');
                        case '.xlsx'
                            writetable(obj.dataTable, fullPath);
                    end
                    obj.appendToReceiveText(['数据已保存到: ', fullPath]);
                    msgbox(['数据已成功保存到: ', fullPath], '保存成功');
                    
                    fprintf('数据已保存到: %s\n', fullPath);
                    
                catch ME
                    errordlg(['保存失败: ', ME.message], '错误');
                    obj.appendToReceiveText(['[错误] 保存失败: ', ME.message]);
                end
            end
        end
        
        function closeFigure(obj, ~, ~)
            % 关闭图形窗口时的清理工作
            % 确认对话框
            choice = questdlg('确定要退出系统吗?', ...
                '确认退出', ...
                '是', '否', '否');
            
            if ~strcmp(choice, '是')
                return;
            end
            
            fprintf('正在关闭系统...\n');
            
            if obj.isRunning
                obj.isRunning = false;
            end
            
            % 停止定时器
            if isvalid(obj.queryTimer)
                stop(obj.queryTimer);
                delete(obj.queryTimer);
            end
            
            % 停止超时计时器
            if isfield(obj, 'timeoutTimer') && isvalid(obj.timeoutTimer)
                stop(obj.timeoutTimer);
                delete(obj.timeoutTimer);
            end
            
            if isfield(obj, 'tempTimeoutTimer') && isvalid(obj.tempTimeoutTimer)
                stop(obj.tempTimeoutTimer);
                delete(obj.tempTimeoutTimer);
            end
            
            % 断开串口
            obj.disconnectSerialPort();
            
            % 关闭文件(如果打开)
            obj.stopAutoSave();
            
            % 删除图形
            try
                delete(obj.fig);
            catch ME
                warning('删除图形窗口时出错: %s', ME.message);
            end
            
            fprintf('系统已退出\n');
        end
        
        function delete(obj)
            % 析构函数
            obj.closeFigure();
        end
    end
end

999999999999999

9999999999999999

c 复制代码
% run_temperature_monitor.m
% 温度监测系统启动脚本

clear all;
close all;
clc;

fprintf('========================================\n');
fprintf('    温度实时监测与串口调试系统\n');
fprintf('========================================\n\n');

fprintf('系统正在启动,请稍候...\n\n');

% 创建系统实例
try
    monitor = TemperatureMonitor();
    
    fprintf('\n系统启动成功!\n');
    fprintf('使用说明:\n');
    fprintf('1. 在"串口配置"区域选择正确的串口号\n');
    fprintf('2. 选择正确的波特率(通常为9600或115200)\n');
    fprintf('3. 点击"连接串口"按钮\n');
    fprintf('4. 连接成功后,点击"开始监控"按钮\n');
    fprintf('5. 观察温度曲线和接收数据\n\n');
    
    fprintf('提示:\n');
    fprintf('- 点击"扫描串口"可刷新串口列表\n');
    fprintf('- 接收区可查看详细的收发数据\n');
    fprintf('- 使用Ctrl+L可清空接收区\n');
    fprintf('- 使用Ctrl+S可保存数据\n\n');
    
    fprintf('系统变量已保存为 monitor\n');
    fprintf('========================================\n');
    
catch ME
    fprintf('\n系统启动失败: %s\n', ME.message);
    fprintf('\n可能的原因:\n');
    fprintf('1. Instrument Control Toolbox 未安装\n');
    fprintf('2. MATLAB版本过低(需要R2019b或更高)\n');
    fprintf('3. 串口设备未连接或驱动未安装\n');
    fprintf('========================================\n');
end
相关推荐
Logic1015 小时前
《数据库运维》 郭文明 实验5 数据库性能监视与优化实验核心操作与思路解析
运维·数据库·sql·mysql·计算机网络技术·形考作业·国家开放大学
眠晚晚5 小时前
src挖洞笔记分享_上
服务器·网络·笔记
YANshangqian5 小时前
网页Http远程启动应用工具
网络·网络协议·http
UP_Continue5 小时前
Linux--vim编辑器
linux·编辑器·vim
合才科技5 小时前
【要闻周报】网络安全与数据合规 12-13
网络·安全
鹿衔`5 小时前
StarRocks 4.0.2 (CDH 环境)与Paimon数据湖集成混合部署文档
linux·硬件架构·paimon·starroks
此生只爱蛋5 小时前
【Linux】网络层IP
服务器·网络·tcp/ip
lingggggaaaa5 小时前
CS配合CrossC2插件,实现MacOS/Linux上线
linux·运维·笔记·安全·macos
盼哥PyAI实验室5 小时前
Python验证码处理实战:从12306项目看验证码识别的技术演进
开发语言·网络·python