物联网实战--平台篇之(十四)物模型(用户端)

news2024/11/27 2:26:38

目录

一、底层数据解析

二、物模型后端

三、物模型前端

四、数据下行


本项目的交流QQ群:701889554

物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html

物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html

物联网实战--平台篇https://blog.csdn.net/ypp240124016/category_12653350.html

嵌入式文件 https://download.csdn.net/download/ypp240124016/89409505

APP文件 https://download.csdn.net/download/ypp240124016/89409506

一、底层数据解析

        手机端从MQTT服务器收到消息后,首先需要根据协议对数据进行解析,把最终的应用层数据找出并传递到具体的设备模型中去,具体过程如下。


void CenterMan::parseDeciceRecv(QByteArray msg_ba)
{
#define     OUT_BUFF_SIZE   1500
#define     IN_BUFF_SIZE       1500
    u8 head[]={0xAA, 0x55}, *pBuff=NULL, *pData=NULL;
    u8 pack_num=0, cmd_type=0;
    u32 app_id, curr_dev_sn, next_dev_sn, parent_sn;
    u16 in_len, out_len, crcValue, data_len=0;
    u8 out_buff[OUT_BUFF_SIZE]={0}, in_buff[IN_BUFF_SIZE]={0};


    if( (pBuff=drv_com.memstr((u8*)msg_ba.data(), msg_ba.size(), head, 2)) != NULL )
    {
        ServerHeadStruct *pSeverHead=(ServerHeadStruct*)pBuff;
        crcValue=pSeverHead->crc_h<<8|pSeverHead->crc_l;
        data_len=pSeverHead->data_len_h<<8|pSeverHead->data_len_l;

        pData=pBuff+8;//指到app_id地址
        if(data_len<8 ||  crcValue != drv_com.crc16(pData, data_len))//CRC校验
        {
            qDebug("deviceRecvProcess: crc error!\n");
            return;
        }
        app_id=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
        pData+=4;
        curr_dev_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
        parent_sn=0;
        pData+=4;//指向数据区
//        qDebug("deviceRecvProcess: app_id=%u, gw_sn=%08X", app_id, curr_dev_sn);
        if(app_id==0 || curr_dev_sn==0)
        {
            qDebug("deviceRecvProcess error: app_id==0 || curr_dev_sn==0");
            return;
        }

        in_len=data_len-8;//加密数据长度
        if(in_len>IN_BUFF_SIZE)//数据包过长
        {
            qDebug("deviceRecvProcess error: in_len>DATA_BUFF_SIZE, in_len=%d", in_len);
            return;
        }
        memcpy(in_buff, pData, in_len);//复制数据
        u8 deep_counts=1;


        
        while(curr_dev_sn>0)
        {
            u8 encrypt_mode=ENCRYPT_MODE_DISABLE;
            u8 encrypt_index=0;
            u8 passwd_buff[20]={0};
            if(deep_counts==1)//网关设备
            {
                encrypt_index=pSeverHead->encrypt_index;//密码索引
                encrypt_mode=takeDevicePasswd(curr_dev_sn, encrypt_index, passwd_buff);//获取该设备的通讯密码
            } 

            switch(encrypt_mode)
            {
                case ENCRYPT_MODE_DISABLE: //不加密
                {
                    out_len=in_len;
                    if(out_len>OUT_BUFF_SIZE)
                        out_len=0;
                    memcpy(out_buff, in_buff, in_len);
                    break;
                }
                case ENCRYPT_MODE_TEA: //TEA加密
                {
                    out_len=drv_com.TEA_DecryptBuffer(in_buff, in_len, (u32*)passwd_buff);
                    if(out_len>OUT_BUFF_SIZE)
                        out_len=0;
                    memcpy(out_buff, in_buff, out_len);
                    break;
                }
                case ENCRYPT_MODE_AES: //AES加密
                {
                    out_len=drv_com.aes_decrypt_buff(in_buff, in_len, out_buff, OUT_BUFF_SIZE, passwd_buff);
                    break;
                }
                default: return;//出错
            }
            if(out_len>0)
            {
                pBuff=pData=out_buff;
                data_len=pData[0]<<8|pData[1];
                pData+=2;
                pack_num=pData[0];
                pData++;
                cmd_type=pData[0];
                pData++;//指向数据区或者下一个数据单元
//                qDebug("data_len=%u, pack_num=%u, cmd_type=%u", data_len, pack_num, cmd_type);
                if(data_len<4 || (data_len>IN_BUFF_SIZE))
                {
                    qDebug("deviceRecvProcess error: data_len<4 || data_len>IN_BUFF_SIZE, data_len=%d, pack_num=%d", data_len, pack_num);
                    return;
                }
                crcValue=pBuff[data_len]<<8|pBuff[data_len+1];
                if(crcValue != drv_com.crc16(pBuff, data_len))//单元内校验
                {
                    qDebug("deviceRecvProcess error: crcValue failed!");
                    return;
                }

                if(cmd_type==100)//是否为数据包命令
                {
                    next_dev_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
//                    qDebug("next_dev_sn=0x%08X", next_dev_sn);
                    pData+=4;
                    parent_sn=curr_dev_sn;
                    curr_dev_sn=next_dev_sn;
                    in_len=data_len-8;//下一个数据区的长度
                    memset(in_buff, 0, IN_BUFF_SIZE);
                    memcpy(in_buff, pData, in_len);//复制下一个数据单元
                }
                else//本机数据,进行处理
                {
//                    QByteArray msg_ba((char*)pData, data_len-4);
//                    qDebug()<<"msg_ba="<<msg_ba.toHex(':');
                    if(app_id==m_currAppWork.appID)//本项目设备
                    {
                        WorkDevStruct *pWorkNode=searchWorkNode(curr_dev_sn);
                        if(pWorkNode)
                        {
                            if(parent_sn>0 && pWorkNode->parentSn != parent_sn)//节点父序列号改变
                            {
                                pWorkNode->parentSn=parent_sn;
//                                requestUpdateWorkName(curr_dev_sn, pWorkNode->devName);//更新父序列号
                            }
                            if(parent_sn==0)
                            {
                                pWorkNode->encrypt_index=pSeverHead->encrypt_index;//记录网关的加密模式,方便数据下发
                            }
                            if(pWorkNode->pModel)
                            {
                                pWorkNode->pModel->setRawData(app_id, curr_dev_sn, pack_num, cmd_type, pData, data_len-4);
                            }
                            return;
                        }
                        else//设备未存在
                        {
                            if(m_secCounts>10)//可能是还未从服务器请求下来
                            {
                                InitTypeStruct *pInittype=takeInitType(curr_dev_sn);
                                if(pInittype)
                                {
                                    requestAddDevice(curr_dev_sn, parent_sn);//请求添加设备
                                }
                            }

                        }
                    }
                    curr_dev_sn=0;
                    return;
                }
                deep_counts++;
                if(deep_counts>=5)//嵌套层数
                {
                    qDebug("deviceRecvProcess error: deep_counts>DEEP_MAX! parent_sn=%u", parent_sn);
                    return;
                }
            }
            else
            {
                qDebug("deviceRecvProcess error: out_len<=0!");
                return;
            }
        }

    }
    else
    {
        qDebug("no found head AA 55");
    }
}

二、物模型后端

        数据协议解析完成后,就进入应用数据解析了,就是模型的setRawData函数,这是数据进入物模型的接口,至于模型内的数据和功能,完全是根据产品本身的定义决定的,与嵌入式端对应。


int ModelAp01::setRawData(u32 app_id, u32 dev_sn, u8 pack_num, u8 msg_type, u8 *msg_buff, u16 msg_len)
{
    if(dev_sn!=m_devSn)
        return 0;
    
    u8 *pData=msg_buff;
    msg_len=msg_len;
    if(m_upPackNum==pack_num)
        return 0;
    m_upPackNum=pack_num;
    m_appID=app_id;
//    qDebug()<<"msg_type="<<msg_type;
    switch(msg_type)
    {
        case AP01_CMD_HEART:
        {
            
            break;
        }
        case AP01_CMD_DATA:
        {
            int temp=pData[0]<<8|pData[1];//温度 原始数据
            float temp_f=(temp-1000)/10.f;//温度浮点数据
            pData+=2;
            int humi=pData[0]<<8|pData[1];
            float humi_f=humi/10.f;
            pData+=2;
            int pm25=pData[0]<<8|pData[1];
            pData+=2;
            u8 speed=pData[0];
            pData+=1;
            u8 state=pData[0];
            pData+=1;
            qDebug("temp_f=%.1f C, humi_f=%.1f%%, pm25=%d ug/m3, speed=%d, state=%d", temp_f,  humi_f, pm25, speed,state);
            QString dev_sn_str=QString::asprintf("%08X", m_devSn);
            QString temp_str=QString::asprintf("%.0f", temp_f);
            QString humi_str=QString::asprintf("%.0f", humi_f);
            QString pm25_str=QString::asprintf("%03d", pm25);
            
            int alarm_level=0;
            if(pm25<20)alarm_level=0;
            else if(pm25<30)alarm_level=1;
            else alarm_level=2;
            
            emit siqUpdateSensorValues(dev_sn_str, temp_str, humi_str, pm25_str);
            emit siqUpdateAlarmLevel(alarm_level);
            emit siqUpdateSwitchState(state);
            break;
        }
    }
    QDateTime current_date_time = QDateTime::currentDateTime();
    m_updateTime=current_date_time.toString("hh:mm:ss");

    m_onlineTime=m_secTickets;
    m_onlineState=DEV_STATE_ON_LINE;
    return 0;
}

后端模型文件基本上就是一个类文件,较为简单,开发展也只需集中精力完善这个模型即可。

三、物模型前端

        前端模型分为两部分,一个是简易网格模型,一个是具体模型,这个在上一节演示视频的APP界面上显而易见了。至于其它AP01开头的QML文件,就是延续了净化器项目的各个前端模块,比如开关、气泡等等。

        详细模型的代码如下:

import QtQuick 2.7
import QtQuick.Controls 1.4
import "../base"

BaseModelView
{
    id:id_baseModelView
    parent: theModelAp01.takeModelParent()
    
    devName: theModelAp01.takeDeviceName()
    devSnStr: theModelAp01.takeDevSnStr()
    
    ScrollView {
        id:id_zoneView
        implicitWidth: 400
        width: parent.width
        clip: true
        anchors
        {
            top:parent.top
            topMargin:50
            bottom:parent.bottom
        }
        verticalScrollBarPolicy:Qt.ScrollBarAlwaysOff
        horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
        Rectangle
        {
            id:id_centerRect
            width: parent.width
            height: 800
            color: "#FAFAFA"
            Ap01CenterShow//中心圆圈画面
            {
                id:id_airCenterShow
                width: parent.width
                height: parent.height*0.6
                anchors
                {
                    top:parent.top
                }
            }
    
            Ap01SwitchItem//开关栏
            {
                id:id_switchItem
                width: parent.width*0.96
                anchors
                {
                    horizontalCenter:parent.horizontalCenter
                    top:id_airCenterShow.bottom
                }
            } 
            
            Ap01ControlItem //风速控制
            {
                id:id_airControlItem
                anchors
                {
                    top:id_switchItem.bottom
                    topMargin:5
                    horizontalCenter:parent.horizontalCenter
                }
            }
        }
        
        
    }
    
    Component.onCompleted: 
    {
        id_zoneView.width=width
        id_centerRect.width=width
//        console.log("width=", width)
    }
    
    Connections
    {
        target: theModelAp01
        onSiqUpdateSensorValues:
        {
            id_airCenterShow.funUpdateThText(temp, humi)//更新温湿度数值
            id_airCenterShow.funUpdatePm25Text(pm25)//更新PM2.5数值
        }
        onSiqUpdateOnlineState:
        {
            funSetDevState(state)//更新在线状态
        }
        onSiqUpdateDeviceName:
        {
            devName=dev_name
        }
    }
    
}


四、数据下行

        像设置开关、调速的指令都是通过信号从模型发送到处理中心的,这个在添加工作设备的时候已经进行信号槽的连接了。

这个槽函数的内容就是对数据进行封装下发


void CenterMan::slotSendDownMsg(u32 app_id, u32 dev_sn, u8 pack_num, u8 msg_type, u8 *msg_buff, u16 msg_len)
{
    //    qDebug()<<"slotSendDownMsg****";
        u8 data_buff[1500]={0}, make_buff[1500]={0};
        u16 make_len, data_len;
        data_len=msg_len;
        if(msg_len>sizeof(data_buff))
        {
            qDebug()<<"slotSendDownMsg error, msg_len>sizeof(data_buff)";
            return;
        }
        memcpy(data_buff, msg_buff, data_len);
        u32 currDevSn=dev_sn;
        if(app_id==m_currAppWork.appID)//自身设备
        {
            int deep=0;
            while(currDevSn>0)
            {
                WorkDevStruct *pWorkNode=searchWorkNode(currDevSn);
                if(pWorkNode==nullptr)
                {
                    qDebug()<<"pWorkNode==nullptr";
                    return;
                }
                if(deep>0)
                {
                    msg_type=200;//转发指令为200
                }
                qDebug("down curr_sn=0x%08X, parent_sn=0x%08X", pWorkNode->devSn, pWorkNode->parentSn);
                if(pWorkNode->parentSn==0)//网关设备
                {   
                    make_len=makeGwSendBuff(app_id, currDevSn, pack_num, pWorkNode->encrypt_index,
                                                                msg_type, data_buff, data_len, make_buff, sizeof(make_buff));
                    publishDownMsg(app_id, currDevSn, make_buff, make_len);//发布
                    currDevSn=0;
                }
                else//节点设备
                {
                    make_len=makeNodeSendBuff(currDevSn, pack_num, msg_type, data_buff, data_len, make_buff, sizeof(make_buff));//组合节点数据
                    if(make_len>0 && make_len<sizeof(data_buff))
                    {
                        data_len=make_len;
                        memcpy(data_buff, make_buff, make_len);
                        memset(make_buff, 0, sizeof(make_buff));
                    }
                    else
                    {
                        qDebug("error01, make_len=%u", make_len);
                        return;
                    }
                    currDevSn=pWorkNode->parentSn;//父节点序列号
                }
                deep++;
            }
        }
}


//发送设备下行数据 
void CenterMan::publishDownMsg(u32 app_id, u32 gw_sn, u8 *msg_buff, u16 msg_len)
{
    QString topic=QString(TOPIC_HEAD)+QString::asprintf("dev/sub/data/%u/%08X", app_id, gw_sn);
    QByteArray msg_ba((char*)msg_buff, msg_len);
//    qDebug()<<"000 pub topic="<<topic<<"\nmsg_ba="<<msg_ba.toHex(':');
     emit sigMqttPushMessage(topic, msg_ba);
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1805378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python的AI动物识别技术研究

基于Python的AI动物识别技术研究 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系统功能实现 系统的登录模块设计 本次设计的AI动物识别系统为了保证用户的数据安全&#xff0c;设计了登录的模块&…

明天15点!如何打好重保预防针:迎战HVV经验分享

在当今数字化时代&#xff0c;网络攻击日益猖獗&#xff0c;各行各业面临的网络安全威胁不断升级。从钓鱼邮件到复杂的APT攻击&#xff0c;网络犯罪分子的手法层出不穷&#xff0c;给各行各业的信息安全带来了前所未有的挑战。 在这样的背景下&#xff0c;"HVV行动"应…

免费,C++蓝桥杯等级考试真题--第7级(含答案解析和代码)

C蓝桥杯等级考试真题--第7级 答案&#xff1a;D 解析&#xff1a;步骤如下&#xff1a; 首先&#xff0c;--a 操作会使 a 的值减1&#xff0c;因此 a 变为 3。判断 a > b 即 3 > 3&#xff0c;此时表达式为假&#xff0c;因为 --a 后 a 并不大于 b。因此&#xff0c;程…

如何远程连接Linux服务器?

远程连接Linux服务器是通过网络连接到位于远程位置的Linux服务器&#xff0c;以进行服务器管理和操作。远程连接使得系统管理员可以方便地远程访问服务器&#xff0c;进行配置、维护和故障排除等操作&#xff0c;而不必亲自在服务器前工作。以下是一些常用的远程连接方法&#…

使用小黄鸟(HttpCanary)、VMOS Pro虚拟机对手机APP进行抓包(附带软件)

老规矩先看&#xff0c;效果图&#xff1a; 文章很详细&#xff0c;希望可以耐心看完&#xff0c;保证可以学会抓包&#xff0c;不再走冤枉路&#xff0c;小编在之前看过太多类似文章&#xff0c;折腾了太久才搞懂的&#xff0c;写这篇文章就是不想希望你们像小编一样再花时间…

qmt量化交易策略小白学习笔记第15期【qmt编程之获取龙虎榜数据】

qmt编程之获取龙虎榜数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 获取龙虎榜数据 获取指定日期区间内的龙虎榜数据 内置python C.get_longhubang(stock_list, startTime, endTime)参…

十八、【源码】二级缓存

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/18-second-levelcache 二级缓存 二级缓存&#xff0c;namespace级别&#xff0c;默认关闭&#xff0c;需要手动开启&#xff0c;在xml加入…

论文阅读《SELECTIVE DOMAIN-INVARIANT FEATURE FOR GENERALIZABLE DEEPFAKEDETECTION》

作者&#xff1a;Yingxin Lai、 Guoqing Yang1、Yifan He2、Zhiming Luo、Shaozi Li 期刊&#xff1a;ICASSP-2024 目的&#xff1a;解决泛化性的问题&#xff0c;提出了3个模块 论文整体的架构图&#xff1a;&#xff08;挑选域特征不变&#xff0c;减少对图像内容或者风格…

我的编程语言学习记录:一段不断探索的旅程

目录 我的编程语言学习记录&#xff1a;一段不断探索的旅程 1.引言 2.我的编程之旅开始 第一站&#xff1a;Python — 简洁之美 第二站&#xff1a;JavaScript — 网页的魔法 第三站&#xff1a;Java — 企业级的力量 3.学习过程中的挑战与克服 1.理解概念 3.记忆语法…

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第七周) - 结构化预测

结构化预测 0. 写在大模型前面的话1. 词法分析 1.1. 分词1.2. 词性标注 2.2. 句法分析 2.3. 成分句法分析2.3. 依存句法分析 3. 序列标注 3.1. 使用分类器进行标注 4. 语义分析 0. 写在大模型前面的话 在介绍大语言模型之前&#xff0c;先把自然语言处理中遗漏的结构化预测补…

Word Split Line

Word Split Line 分割线 https://download.csdn.net/download/spencer_tseng/89413772

Verilog实战学习到RiscV - 4 : ICEStick 评估板计数器

这篇是关于always 时序逻辑的。直接上代码。 引脚配置文件 set_io leds[0] 99 set_io leds[1] 98 set_io leds[2] 97 set_io leds[3] 96set_io -pullup yes pmod[0] 78 set_io -pullup yes pmod[1] 79参看icestick的原理图 这里在pmod上使用了内部的上拉电阻。…

两轮自平衡小车资料(L298N 模块原理图及使用说明+c源码)

本文详细介绍了基于STM32微控制器的两轮自平衡小车的设计与实现过程。内容包括小车的硬件选型、电路设计、软件编程以及PID控制算法的应用。通过陀螺仪和加速度计获取小车的姿态信息&#xff0c;利用PID控制算法调整电机输出&#xff0c;实现小车的自主平衡。此外&#xff0c;还…

mac Network: use --host to expose

本地启动无法访问&#xff0c;这个不是权限问题是mac 主机端口安全策略&#xff0c;现在我们只需要开启端口自动检测就可以 npm run dev --host 网络&#xff1a;未暴露 方案一 1、执行 npm run dev -- --host 方案二 1、请在 vite.config.js server: {host: true } 1…

c++之旅第十弹——IO流

大家好啊&#xff0c;这里是c之旅第十弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一.流的概念&…

知乎网站只让知乎用户看文章,普通人看不了

知乎默认不显示全部文章&#xff0c;需要点击展开阅读全文 然而点击后却要登录&#xff0c;这意味着普通人看不了博主写的文章&#xff0c;只有成为知乎用户才有权力查看文章。我想这不是知乎创作者希望的情况&#xff0c;他们写文章肯定是希望所有人都能看到。 这个网站篡改…

力扣每日一题129:从根节点到叶子节点的和

题目 中等 相关标签 相关企业 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a; 例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节…

Linux基础 (十三):计算机网络基础概论

一、网络基本概念 1.1 网络 把独立自主的计算机通过传输介质和网络设备链接起来&#xff0c;就构成一个网络 &#xff0c;网络是由若干结点和连接这些结点的链路组成&#xff0c;网络中的结点可以是计算机&#xff0c;交换机、 路由器等设备。 网络设备有&#xff1a;交换机、…

跨域、JSONP、CORS、Spring、Spring Security解决方案

概述 JavaScript出于安全方面的考虑&#xff0c;不允许跨域调用其他页面的对象。跨域是浏览器&#xff08;如Chrome浏览器基于JS V8引擎&#xff0c;可以简单理解为JS解释器&#xff09;的一种同源安全策略&#xff0c;是浏览器单方面限制脚本的跨域访问。因此&#xff0c;仅有…

Chrome浏览器书签同步不及时怎么办?两种方法帮你解决!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…