news 2026/4/16 15:57:33

【Qt开源项目】— ModbusScope-day 5

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Qt开源项目】— ModbusScope-day 5

第5天:数据处理层深入攻略(ExpressionParser与GraphDataHandler)

目标:用5-6小时掌握表达式解析、寄存器替换、数学计算的核心流程,理解原始数据如何转化为最终曲线数值。


上午(3小时):ExpressionParser解析引擎

学习目标

理解如何将用户输入的表达式{40001}+{40002*2}转换为可计算的数学表达式。

详细攻略

  1. 从场景出发:理解表达式格式

    • 示例表达式{40001[@1][:f32b]} + {40002[@2][:32b]} * 2
    • 打开软件验证
      • 启动ModbusScope,添加一个寄存器
      • 在表达式编辑框中输入类似格式,观察软件如何接受这种语法
    • 对照笔记:查看ExpressionParser类的_cRegisterFunctionTemplate,理解目标是将{...}替换为r(索引)
  2. 分析ExpressionParser类结构

    • 打开expressionparser.hexpressionparser.cpp
    • 对照笔记:仔细阅读ExpressionParser类的"数据成员"部分
    • 找到关键成员
      QStringList _processedExpressions;// 处理后的表达式列表QList<ModbusRegister>_registerList;// 解析出的寄存器列表QRegularExpression _findRegRegex;// 查找寄存器表达式的正则QRegularExpression _regParseRegex;// 解析单个寄存器的正则
  3. 查看正则表达式定义

    • 打开expressionregex.h(如果存在)或在代码中搜索cMatchRegistercParseReg
    • 理解正则模式(笔记中提到):
      • 寄存器表达式形如:{40001[@1][:f32b]}{h0[@1][:f32b]}
      • 中括号内为可选项:连接编号[@N]和数据类型[:type]
    • 关键学习点:理解这种语法设计为什么便于用户使用(同时支持十进制地址和助记符地址)
  4. 深入解析算法

    • 找到ExpressionParser::processExpression函数
    • 逐步分析算法流程
      1.使用_findRegRegex查找所有{...}模式2.对每个匹配,使用_regParseRegex进一步解析3.解析结果:地址、连接ID、数据类型4.创建ModbusRegister临时对象5.检查是否已存在于_registerList,不存在则添加6.获取寄存器在列表中的索引7.r(索引)替换原表达式中的{...}
    • 动手实验:在代码中添加调试输出,观察解析过程
      // 在processExpression函数中添加qDebug()<<"原始表达式:"<<graphExpr;qDebug()<<"匹配到的寄存器:"<<match.captured();qDebug()<<"解析后的寄存器对象:"<<modbusRegister;qDebug()<<"替换为:"<<QString("r(%1)").arg(regIdx);
  5. 测试不同表达式格式

    • 创建测试用例
      QStringList testExpressions={"{40001}",// 简单地址"{40001} + {40002}",// 两个寄存器相加"{h0[@1][:f32b]}",// 助记符地址,连接1,浮点数"{30001[@2][:s16b] * 0.1}",// 带乘法的表达式"sin({40001}) + {40002}"// 使用数学函数};
    • 编写简单测试程序(可选):创建一个小程序,测试ExpressionParser的解析结果
  6. 理解寄存器索引映射

    • 关键概念:相同的寄存器(相同地址、相同连接、相同类型)只会出现在_registerList中一次
    • 思考:为什么需要这样设计?(避免重复读取同一个寄存器)
    • 验证:表达式{40001} + {40001}中的两个{40001}会被映射到同一个索引

上午学习成果

  • ✅ 理解用户表达式的语法规则和设计原理
  • ✅ 掌握ExpressionParser{...}格式替换为r(索引)的完整流程
  • ✅ 理解寄存器去重机制和索引映射原理
  • ✅ 能手动解析简单表达式并确定寄存器索引
  • 检验:表达式{40001[@1]} + {40002[@1]} * {40001[@1]}会被解析成几个不同的寄存器?替换后的表达式是什么?

下午(2-3小时):GraphDataHandler与QMuParser计算引擎

学习目标

掌握表达式如何从字符串变为实际数值的计算过程,理解数据流如何衔接。

详细攻略

  1. 理解GraphDataHandler的桥梁作用

    • 打开graphdatahandler.hgraphdatahandler.cpp
    • 对照笔记:阅读GraphDataHandler类部分,理解它的三个关键容器:
      QList<ModbusRegister>_registerList;// 寄存器列表QList<quint16>_registerIndexList;// 寄存器索引列表(可能已弃用或笔记有误)QList<QMuParser>_expressionParserList;// 表达式解析器列表
    • 注意:根据实际代码,_registerIndexList可能不存在。以实际代码为准。
  2. 分析数据处理流程

    • 找到GraphDataHandler::processActiveRegisters函数
    • 理解调用时机:何时会调用这个函数?
      • 图形激活状态变化时
      • 图形表达式修改时
      • 连接设置变化时
    • 跟踪流程
      1. GraphDataModel获取激活图形的表达式
      2. 创建ExpressionParser实例,解析表达式
      3. 获取解析后的寄存器列表和表达式列表
      4. 用处理后的表达式初始化QMuParser对象
  3. 深入QMuParser计算核心

    • 打开qmuparser.hqmuparser.cpp
    • 对照笔记:仔细阅读QMuParser类部分
    • 理解静态数据成员
      staticQList<Result<double>>_registerValues;// 所有解析器共享的寄存器值
    • 关键问题:为什么寄存器值要设计为静态成员?
      答案:所有表达式计算都需要访问相同的寄存器值,静态成员避免了重复传递数据。
  4. 分析计算回调机制

    • 找到QMuParser构造函数和mu::ParserRegister::setRegisterCallback
    • 理解回调链
      1.QMuParser构造函数设置回调函数为registerValue2.registerValue通过索引从静态_registerValues获取值3.mu::ParserRegister在计算表达式时调用此回调
    • 查看回调函数
      // 伪代码示意voidregisterValue(intidx,double*val,bool*ok){if(idx>=0&&idx<_registerValues.size()){*val=_registerValues[idx].value();*ok=_registerValues[idx].isValid();}}
  5. 跟踪实时计算流程

    • 找到GraphDataHandler::handleRegisterData函数
    • 分析执行步骤
      1.接收来自RegisterValueHandler的原始寄存器值2.调用QMuParser::setRegistersData更新静态寄存器值3.遍历_expressionParserList中的每个QMuParser4.调用evaluate()计算表达式结果5.收集所有结果,发出graphDataReady信号
    • 调试技巧:在此函数设置断点,观察每次数据到达时的计算过程
  6. 理解mu::ParserRegister的扩展功能

    • 查看muparserregister.hmuparserregister.cpp
    • 理解设计mu::ParserRegister继承自mu::ParserBase
    • 关键方法SetExpr设置表达式,Eval计算表达式
    • 扩展能力:除了基本数学运算,还支持哪些函数?(sin, cos, log等)
  7. 动手实验:观察表达式计算

    • 修改表达式:在软件中设置不同的数学表达式
      • 简单加法:{40001} + {40002}
      • 带函数:sin({40001} * 3.14159 / 180)
      • 条件运算:{40001} > 100 ? {40001} : 0
    • 观察计算:在QMuParser::evaluate设置断点,查看不同表达式的计算过程

综合调试任务

  1. 设置完整的断点链

    // 从接收到数据到计算出结果GraphDataHandler::handleRegisterDataQMuParser::setRegistersData(静态方法)QMuParser::evaluate mu::ParserRegister::Eval(第三方库)GraphDataHandler::graphDataReady(信号发射处)
  2. 创建测试场景

    • 配置2个寄存器:40001(值为10),40002(值为20)
    • 设置表达式:{40001} + {40002} * 2
    • 预期结果:10 + 20*2 = 50
  3. 调试观察

    • 逐步执行,观察寄存器值如何传递
    • 查看_registerValues静态成员的变化
    • 观察回调函数registerValue被调用的次数和参数
  4. 异常情况测试

    • 寄存器值无效:模拟一个寄存器读取失败,观察表达式计算结果
    • 语法错误表达式:输入{40001} +(不完整表达式),观察错误处理
    • 除零错误:表达式包含除法且除数为0的情况

数据处理层学习总结

核心概念掌握:

  1. 表达式解析双阶段

    • 阶段一:ExpressionParser将用户友好语法转换为机器友好语法
    • 阶段二:QMuParser(基于muParser)执行数学计算
  2. 数据流清晰分离

    • 寄存器值管理:GraphDataHandler负责接收和分发
    • 表达式管理:每个激活图形对应一个QMuParser实例
    • 值共享机制:静态成员_registerValues确保所有表达式使用相同数据
  3. 扩展性设计

    • 语法易于扩展:通过正则表达式可支持新格式
    • 计算能力强大:借助muParser库支持复杂数学运算
    • 错误处理完善:无效寄存器值不会导致崩溃

典型问题解答:

  • Q: 如果表达式包含10个{40001}引用,这个寄存器会被读取几次?
    A: 只读取1次。ExpressionParser会去重,所有引用指向同一个寄存器索引。

  • Q: 表达式计算是同步还是异步的?
    A: 在handleRegisterData中是同步计算的,但这个过程很快,不会阻塞UI。

  • Q: 如何添加自定义函数?
    A: 可以扩展mu::ParserRegister,添加新的函数定义。

实际应用思考:

  1. 性能优化:表达式解析只在配置改变时进行,计算时直接使用预编译的解析器
  2. 错误恢复:单个寄存器读取失败不会影响其他寄存器的计算
  3. 灵活性:支持复杂的数学运算和条件判断,满足各种数据处理需求

今日完整成果

  • ✅ 掌握从用户表达式到可执行代码的完整转换流程
  • ✅ 理解静态寄存器值共享机制的设计原理
  • ✅ 能解释回调函数如何将寄存器索引映射到实际数值
  • ✅ 掌握表达式计算过程中的错误处理机制
  • ✅ 能设计测试用例验证表达式解析和计算的正确性

明日预告:第6天将进入数据模型层(GraphDataModel),学习数据如何存储、组织,以及模型如何与视图交互。这是连接数据处理和图形显示的关键桥梁。

建议行动:晚上可以尝试修改一个简单表达式,观察软件行为变化,巩固今天所学。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 15:57:00

【Qt开源项目】— ModbusScope-进度规划

ModbusScope学习总进度与时间规划 根据我们之前的讨论和你的学习进度&#xff0c;我来为你制定一个完整的学习计划。 当前进度总结 你已经完成的学习内容&#xff08;按时间顺序&#xff09;&#xff1a; ✅ 第一阶段&#xff08;3天&#xff09;&#xff1a;整体认知、程序骨架…

作者头像 李华
网站建设 2026/4/16 12:42:49

AutoGPT如何制定营销推广计划?实战案例拆解

AutoGPT如何制定营销推广计划&#xff1f;实战案例拆解 在一家新茶饮品牌即将推出“樱花白桃冰”新品的会议室里&#xff0c;市场团队正为推广方案焦头烂额&#xff1a;用户画像不清晰、竞品动作难追踪、内容创意枯竭……如果能有一个助手&#xff0c;只需一句话就能自动生成完…

作者头像 李华
网站建设 2026/4/16 12:32:15

测试用例设计方法:正交试验法详解!

01 正交试验法介绍 正交试验法是研究多因素、多水平的一种试验法&#xff0c;它是利用正交表来对试验进行设计&#xff0c;通过少数的试验替代全面试验&#xff0c;根据正交表的正交性从全面试验中挑选适量的、有代表性的点进行试验&#xff0c;这些有代表性的点具备了“均匀分…

作者头像 李华