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

目录

一、底层数据解析

二、物模型后端

三、物模型前端

四、数据下行


本项目的交流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服务器收到消息后,首先需要根据协议对数据进行解析,把最终的应用层数据找出并传递到具体的设备模型中去,具体过程如下。

cpp 复制代码
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函数,这是数据进入物模型的接口,至于模型内的数据和功能,完全是根据产品本身的定义决定的,与嵌入式端对应。

cpp 复制代码
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文件,就是延续了净化器项目的各个前端模块,比如开关、气泡等等。

详细模型的代码如下:

javascript 复制代码
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
        }
    }
    
}
四、数据下行

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

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

cpp 复制代码
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);
}
相关推荐
BY组态41 分钟前
【技术分析】Ricon组态系统的模块化架构设计
物联网·iot·web组态·组态
BY组态2 小时前
【教程】如何使用Ricon组态系统快速构建监控画面
物联网·iot·web组态·组态
BY组态4 小时前
【对比分析】Ricon组态系统 vs 传统组态软件
运维·物联网·web组态·组态
zhaoshuzhaoshu20 小时前
BLE(蓝牙低功耗)连接过程详解
物联网·蓝牙·无线
搜佛说20 小时前
下一代跨语言原生操作系统商业计划书
物联网·软件工程
BY组态20 小时前
Ricon组态系统在实际项目中的应用案例分享
物联网·web组态·组态
Zevalin爱灰灰1 天前
零基础入门学用物联网(ESP8266) 第一部分 基础知识篇(五)
单片机·物联网·嵌入式·esp8266
Web3_Daisy1 天前
Token 分红机制详解:实现逻辑、激励结构与风险分析
大数据·人工智能·物联网·web3·区块链
BY组态1 天前
从零开始:Ricon组态系统快速入门指南
运维·物联网·web组态·组态
次旅行的库1 天前
MQTT学习笔记
数据库·笔记·物联网·学习