基于QT的华为云平台北向开发

1 华为OceanConnect云平台配置

这里只涉及华为云平台部署以及北向开发。上面链接介绍了如何在旧版的华为云平台进行部署,也有专门的博客介绍新版的华为云平台如何部署。

2 基于QT接入华为OceanConnect云平台

上位机软件与提供的的平台profile是对应的,如果profile的关键参数不一致,需要同步修改软件代码!
2.1 软件介绍
OceanConnect云平台搭建好之后,可以使用模拟器进行发送数据的测试。但是我们必须通过登陆华为OceanConnect云后台,才看到数据,这样其实是不太友好的,因此采用QT平台结合华为发布的API文档来开发一个简单的PC软件。
本次用QT实现与华为OceanConnect云平台(以下简称华为云)的对接,主要包括
    1、用户根据appid password登陆
    2、自动查询当前应用下所有设备
    3、定时向华为云查询最新数据
    4、用户可以根据时间段查询云端的历史数据

2.2 效果预览

基于QT的华为云平台北向开发_第1张图片

基于QT的华为云平台北向开发_第2张图片

  • 用户需要登录时输入其appId/password(其值为华为OceanConnect云平台配置)中邮件中的“应用对接信息"按钮。
  • 登录之后,点击“查询设备”,如果有设备,在下拉框中选择,需要查阅的设备,注意这里显示的为设备名。
  • 设置更新周期,该周期指的是,软件定时向云端获取数据的周期。默认是5秒请求一次
  • 可以根据时间段,查询数据

3 登陆云平台预备知识

待解决的问题

问题:用户联网登录 
1.提供登录界面(客户端),输入用户名和密码; 
2.将用户名和密码发送给服务器端,并进行相关查询,如果用户合法,返回用户信息; 
3.对返回的用户信息进行解析。
假设: 
已经有了服务器和存储着用户名和密码数据库。服务器当接收到客服端的请求的时候,先在数据库中进行查询操作,然后返回用户信息。所以要实现的只是客户端请求信息的配置和对返回信息的解析。
涉及的知识点

网络请求的基础知识
Json

需要解决的技术问题:

在qt中如何设置网络请求
在qt中如何生成和解析Json数据

3.1 Qt中与网络请求相关类介绍

QNetworkAccessManager 

Allows the application to send network requests and receive replies

QNetworkRequest

Holds a request to be sent with QNetworkAccessManager

QNetworkReply

Contains the data and headers for a request sent with QNetworkAccessManager

基于QT的华为云平台北向开发_第3张图片


QNetworkRequest设置网络请求的各个参数

QNetworkReply用于接收服务器返回的数据

QNetworkAccessManager充当着一个中介的作用,设置post、get等方式将request发出去,然后监测服务器是否返回数据(存在QNetworkReply中),并将返回得来的reply委托给一个slot处理。 

当进行网络请求的时候,要做的事情

  • 生成QNetworkRequest对象,设置请求头、请求所用API所需实参、url等信息;
  • 准备解析处理QNetworkReply的slot;
  • 生成QNetworkAccessManager,发送request,并将返回来的reply委托给该slot处理(实现上就是一条connect语句)

3.2 QJson的生成与解析

JSON简介

JSON:JavaScript 对象表示法(JavaScript Object Notation)。

JSON 是存储和交换文本信息的语法。类似 XML。

JSON 比 xml 更小、更快,更易解析。

{
"employees": [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName":"Carter" }
]
}

employee 对象是包含 3 个员工记录(对象)的数组

什么是 JSON ?

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言 *
  • JSON 具有自我描述性,更易理解

    * JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。

JSON 语法规则

JSON 语法是 JavaScript 对象表示法语法的子集。

  • 数据在名称/值对中
  • 数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

JSON 名称/值对

JSON 数据的书写格式是:名称/值对。

名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是

"firstName" : "John"

这很容易理解,等价于这条 JavaScript 语句:

firstName = "John"

JSON 值

JSON 值可以是:

  • 数字(整数或浮点数)
  • 字符串(在双引号中)
  • 逻辑值(true 或 false)
  • 数组(在方括号中)
  • 对象(在花括号中)
  • null

JSON 对象

JSON 对象花括号中书写:

对象可以包含多个名称/值对

{ "firstName":"John" , "lastName":"Doe" }

这一点也容易理解,与这条 JavaScript 语句等价:

firstName = "John"
lastName = "Doe"

JSON 数组

JSON 数组方括号中书写:

数组可包含多个对象:

{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}

对象 "employees" 是包含三个对象的数组。每个对象代表一条关于某人(有姓和名)的记录。

JSON 使用 JavaScript 语法

因为 JSON 使用 JavaScript 语法,所以无需额外的软件就能处理 JavaScript 中的 JSON。

通过 JavaScript,您可以创建一个对象数组,并像这样进行赋值:

例子

var employees = [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName": "Carter" }
];

可以像这样访问 JavaScript 对象数组中的第一项:

employees[0].lastName;

返回的内容是:

Gates

可以像这样修改数据:

employees[0].lastName = "Jobs";

JSON 文件

  • JSON 文件的文件类型是 ".json"
  • JSON 文本的 MIME 类型是 "application/json"

无论是设置request所需的参数信息,还是返回来的reply,其数据均采用QJson的形式表示,所以要实现网络请求,QJson的生成和解析重要。 

Qt中与JSON相关类介绍

QJsonDocument 类提供了读写JSON文档的方式, JSON文档 可以从它的基于文本的表示 使用QJsonDocument::fromJson()转换为QJsonDocument,用.toJSON()将其转换回文字。

基于QT的华为云平台北向开发_第4张图片

QJsonArray封装了JSON中的数组。

QJsonObject封装了JSON中的对象。

QJsonValue封装了JSON中的值。

QJsonParseError 用于报告JSON解析中的错误类型。

基于QJsonObject的Json生成与解析

1.生成json

QJsonObject json;
json.insert("name", QString("Qt"));
json.insert("version", 5);
json.insert("windows", true);

QJsonDocument document;
document.setObject(json);
QByteArray byte_array = document.toJson(QJsonDocument::Compact);
QString json_str(byte_array);

结果

json_str为: {"name": "Qt","version": 5,"windows": true}

2.解析Json

QJsonParseError json_error;
QJsonDocument parse_doucment = QJsonDocument::fromJson(byte_array, &json_error); 
if(json_error.error == QJsonParseError::NoError) 
{  
    if(parse_doucment.isObject())  
    {  
        QJsonObject obj = parse_doucment.object(); 
        if(obj.contains("name"))  
        {  
            QJsonValue name_value = obj.take("name");
            if(name_value.isString())
            {
                QString name = name_value.toString();
            }
        } 
        if(obj.contains("version"))  
        {  
            QJsonValue version_value = obj.take("version");
            if(version_value.isDouble())
            {
                int version = version_value.toVariant().toInt();
            }
        } 
        if(obj.contains("windows"))
        {
            QJsonValue version_value = obj.take("windows");
            if(version_value.isBool())
            {
                bool flag = version_value.toBool();
            } 
        }
    }  
}  

结果:

name:Qt
version:5
windows:true

基于QJsonArray的的Json生成与解析 

1.生成json

QJsonArray json;
json.insert(0, QString("Qt"));
json.insert(1, QString("version"));
json.insert(2, true);

QJsonDocument document;
document.setArray(json);
QByteArray byte_array = document.toJson(QJsonDocument::Compact);
QString json_str(byte_array);

结果

json_str:["Qt","version",true]

2.解析json

QJsonParseError json_error;
QJsonDocument parse_doucment = QJsonDocument::fromJson(byte_array, &json_error); 
if(json_error.error == QJsonParseError::NoError) 
{  
    if(parse_doucment.isArray())  
    {  
        QJsonArray array = parse_doucment.array(); 
        int size = array.size();
        for(int i=0; i

结果

数组不同下标对应的值
0:Qt
1:version
2:true

4.login.h / login.cpp / login.ui

login.ui

四个标签,四个文本框,两个按钮

基于QT的华为云平台北向开发_第5张图片

login.h

#ifndef LOGIN_H
#define LOGIN_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Ui {
class login;
}

class login : public QDialog
{
    Q_OBJECT

public:
    explicit login(QWidget *parent = nullptr);
    ~login();

private slots:
    //登陆
    void on_pushButton_login_clicked();
    //载入
    void on_pushButton_clicked();
    //当鉴权成功后会发送给登陆一个信号,然后再登陆中,绑定此信号到onSignLogin槽函数
    void onSignLogin(int codeNum);
private:
    Ui::login *ui;
};

#endif // LOGIN_H

login.cpp

#include "login.h"
#include "ui_login.h"
#include "datacomponent.h"

extern DataComponent g_DC;
login::login(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::login)
{
    ui->setupUi(this);
    connect(&g_DC, SIGNAL(signMsgToLogin(int)), this, SLOT(onSignLogin(int)));
}

login::~login()
{
    delete ui;
}

//载入文件
void login::on_pushButton_clicked()
{
    QFile file;
    QByteArray configData;//用于保存json文档
    QString path = "./Config.json";
    file.setFileName(path);
    file.open(QIODevice::ReadOnly);
    configData = file.readAll();

    //解析json
    QJsonParseError json_error; 
    QJsonDocument jsonDoc(QJsonDocument::fromJson(configData, &json_error));
     
    if(json_error.error != QJsonParseError::NoError)
    {
        qDebug() << "json data is error!";
        return;
    }

    QJsonObject rootObj = jsonDoc.object();

    if(rootObj.contains("IP") && rootObj.contains("Port") && rootObj.contains("AppId") && rootObj.contains("Password"))
    {
        ui->lineEdit_IP->setText(rootObj.value("IP").toString());
        ui->lineEdit_Port->setText(rootObj.value("Port").toString());
        ui->lineEdit_AppId->setText(rootObj.value("AppId").toString());
        ui->lineEdit_Password->setText(rootObj.value("Password").toString());
    }
    else
    {
        QMessageBox::information(nullptr, "Title", "输入数据错误", QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
    }

}

//登陆 完成网络请求的初始化和鉴权工作
void login::on_pushButton_login_clicked()
{
    UConfig cfg;
    cfg.ip = ui->lineEdit_IP->text();
    cfg.port = ui->lineEdit_Port->text();
    cfg.appid = ui->lineEdit_AppId->text();
    cfg.password = ui->lineEdit_Password->text();

    g_DC.init(cfg);
    g_DC.auth();
}

//隐藏模态对话框并将结果代码设置为Accepted
void login::onSignLogin(int codeNum)
{
    if(SIGN_CODE_AUTH_SUCCESS == codeNum)
    {
        qDebug() << "认证成功";
        accept();//用户操作成功
    }
    else
    {
        qDebug() << "认证失败";
    }
}

5.datacomponent.h / datacomponent.cpp

UConfig 登陆相关的结构体

typedef struct config_s
{
    QString ip;
    QString port;
    QString appid;
    QString password;
}UConfig;

UAuthConfig 鉴权相关结构体

保存鉴权相关返回值的结构体

应用安全接入

Application携带在IoT Platform产生的AppId ID密钥,调用鉴权接口,获取鉴权token

Auth(鉴权)

    接口功能

        实现第三方系统在访问开放API之前的认证

   调用方法

          post

  接口路径

https://server:port/iocm/app/sec/v1.1.0/login

    注意事项:

        鉴权接口是调用其他API的前提,北向接口除了鉴权接口(Auth),其他接口调用都需要在request header

        中携带参数app_key和Authorization:Bearer {accessToken}。

        app_key为参数中的appId, Authorization中{accessToken}的值即为调用Auth接口获取到的accessToken。

        如果多次获取令牌,则之前的令牌失效, 最后一次获取的令牌才有效。请勿并发获取令牌

        当用户登录的时候,生成access_token和refresh_token,并返回给APP。

        当access_token失效时,APP使用refresh_token来请求刷新token。

        如果refresh_token过期,需要用户重新登录,。也就是说,用户每一次登陆的时候refresh_token都会重新更改,

       accessToken;鉴权参数,用于其他接口的Authorization头域中

       refreshToken;鉴权参数,用来刷新accessToken

       expiresIn; 平台生成并返回accessToken的有效时间,单位秒

typedef struct authConfig_s
{
    QString accessToken;
    QString refreshToken;
    int     expiresIn; 
}UAuthConfig;

SIGN_TYPE 枚举,信号标志

表征信号此时处于什么状态

typedef enum SIGN_TYPE_E
{
    SIGN_TYPE_INIT,
    SIGN_TYPE_AUTH,
    SIGN_TYPE_GETDEVLIST,
    SIGN_TYPE_GETFASTDATA,
    SIGN_TYPE_GETDATA_BYDATE

}SIGN_TYPE;

DataComponent类

 QNetworkRequest 类保存要与 QNetworkAccessManager 一起发送的请求。QNetworkRequest 是网络访问API的一部分,它是一个类,包含通过网络发送请求所需的信息。它包含一个URL和一些可用于修改请求的辅助信息。

QNetworkAccessManager类允许应用程序发送网络请求接收回复。网络访问API是围绕一个 QNetworkAccessManager 对象构建的,它保存了它发送的请求的通用配置和设置。它包含代理和缓存配置,以及与这些问题相关的信号,以及可用于监视网络操作进度的回复信号。一个 QNetworkAccessManager 实例应该足够用于整个Qt应用程序。由于QNetworkAccessManager基于QObject,因此只能从它所属的线程使用它。一旦创建了 QNetworkAccessManager 对象,应用程序就可以使用它通过网络发送请求。提供了一组接受请求和可选数据的标准函数,每个函数返回一个QNetworkReply对象。返回的对象用于获取响应相应请求返回的任何数据。


QNetworkReply 类包含与使用 QNetworkAccessManager 发布的请求相关的数据和元数据。
与 QNetworkRequest 一样,它包含一个URL和头(以解析和原始形式),一些关于回复状态回复本身内容的信息
QNetworkreply 是一个顺序访问 QIODevice,这意味着一旦从对象中读取数据设备就不再保留它。因此,如果需要,应用程序有责任保留这些数据。每当从网络接收到更多数据并进行处理时,就会发出readyread()信号。

class DataComponent : public QObject
{
    Q_OBJECT

public:
    explicit DataComponent(QObject *parent = nullptr);
    QNetworkRequest DC_request;
    QNetworkReply *DC_pReply;
    QNetworkAccessManager *DC_pManager;

    UConfig DC_ucfg; //登陆相关的结构体
    SIGN_TYPE DC_signType;//信号标志
    UAuthConfig DC_authCfg;//保存鉴权相关返回值的结构体
    
    //设备相关
    QJsonArray DC_devRootArray; //设备数组 
    QJsonObject DC_currentDev;  //当前选中的设备

    //数据
    QJsonObject DC_resultFastData; //最近的一条数据
    QJsonArray DC_resultDataArray; //查找出来的结果

    void init(UConfig cfg);
    void auth();
    void getDeviceList();
    void getDeviceDataByCount(QString deviceId, int count);
    void getDeviceDataByDate(QString deviceId, QString startDate, QString endDate);

public slots:
    void onFinished();
    void onError(QNetworkReply::NetworkError errorCode);

signals:
    void signMsgToLogin(int codeNum);
    void signMsgToMain(int codeNum);

};

DataComponent构造函数

DataComponent::DataComponent(QObject *parent) : QObject(parent)
{
     DC_ucfg.ip = "";
     DC_ucfg.port = "";
     DC_ucfg.appid = "";
     DC_ucfg.password = "";

     DC_signType = SIGN_TYPE_INIT;

     DC_authCfg.accessToken = "";
     DC_authCfg.refreshToken = "";
     DC_authCfg.expiresIn = 0;
}

IoT平台北向应用证书使用指南

为了保障在业务中鉴权、密码、业务数据等敏感信息的传输安全。IoT平台提供HTTPS接口,与北向Application(以下简称NA)对接。

公钥和私钥

公钥和私钥可理解为两个有关系的数字密码。经公钥加密的数据只可以被私钥解密。经私钥加密的数据也只可以被公钥解密。通常情况下公钥是对外公开的,私钥是保密的。

例:A与B通信,通信内容不想被他人知道,可以采用如下方式进行通信

  1. A与B互相交换公钥(A与B各自有自己的公钥和私钥)。
  2. A使用B的公钥加密通信内容后发给B(这样只有B手上的私钥才能解密)。
  3. B收到A发的信后,使用自己的私钥解密,再用A的公钥对回信内容进行加密后发给A。
  4. A收到B的回信,使用自己的私钥进行解密。

数字证书

一个数字证书包含的内容有:

  1. 一个公钥(证书使用者的公钥) 
  2. 证书的基本信息,主要包括使用者的名字(通常是IP或域名)、证书有效期、加密算法等公开信息
  3. 签发该数字证书的颁发者(称为CA)的信息和签名(使用颁发者的私钥加密的一段数据,只有使用颁发者的公钥才能解密)。

正式的数字证书的颁发者必须是权威机构,权威机构的证书(包含权威机构的公钥的数字证书,也就是权威机构的CA证书)是公开的,所以只要拿到权威机构的CA证书,我们就能对其签发数字证书签名进行解密,解密成功证明该数字证书是权威机构所签发的。

于是,A与B之间的通信就变成这样:

  1. A与B互相交换数字证书
  2. A收到的证书为“某权威机构C签发给B的证书”
  3. A拿到权威机构C的CA证书,对B证书上的签名(权威机构C的签名)进行解密,解密成功,证明这个证书是权威机构C签发给B的证书。如果A信任权威机构C,则也信任权威机构C签发给B的证书,再使用B证书中的公钥对通信内容进行加密发给B。
  4. B收到A的证书也做类似处理。

HTTPs认证过程

 http (超文本传输协议)协议被用于在客户端服务端之间传递信息,http协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了客户端和服务端之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息。

 为了解决http协议的这一缺陷,需要使用另一种协议: https(安全套接字层超文本传输协议),https在http的基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证双方的身份,验证通过后为客户和服务端之间的通信进行加密

https向认证简单过程:

  1. 客户端与服务端交换证书
  2. 先通过CA证书校验证书的真实性,再通过证书得到对端身份和公钥,校验对端(校验对端请求中的主机地址与证书上的使用者是否一致)
  3. 双方协商此次消息交互过程中使用的对称加密算法(协商过程使用对方证书中的公钥对随机加密密钥进行加密)
  4. 后续消息中使用对称加密算法对消息内容进行加密,以保证双方通信内容的安全

说明

  1. 每次https双向认证都会进行对称加密算法的协商,使用不同的加密密钥,而不是直接使用对端公钥进行加密,这样做的好处是:对称加密算法效率高,加密解密快;每次加密密钥都是随机产生的,不会重复,不易被破解。
  2. HTTPs单向认证与双向认证的主要区别在于,单向认证时,客户端对服务端身份进行校验,而服务端不对客户端身份进行校验。是否进行双向认证可在服务端进行配置。

下图就是双向认证的过程,去掉红色框中的步骤即为单向认证。

基于QT的华为云平台北向开发_第6张图片

了解了以上知识,我们可以总结出:

  1. 客户端用的证书叫客户端证书,服务端用的证书叫服务端证书,两个证书在本质上没有区别,检验证书的方法也是一样的。
  2. 证书中有公钥信息,交换了证书相当于交换了公钥;双向认证时,每次HTTPs握手都会交换证书。但通信过程中的加密密钥却不是直接使用证书中的公钥,而是每次握手时都进行对称加密算法协商,生成随机加密密钥。
  3. CA证书是证书签发者(往往是权威机构)的证书,一般不是握手过程中交换的证书(除非直接使用自签名的CA证书,因为是自签名的证书,开发者证书才对应有私钥)。CA证书是用于对证书签发者的签名进行校验的,校验通过就认为证书是CA签发的,证书可信。

应用对接平台

应用对接平台有两种情况:

  1. 应用调用平台提供的鉴权、注册设备等接口,完成各种业务操作。 本文使用的方法
  2. 平台调用应用提供的消息推送接口,给应用推送消息。

应用调用平台接口

平台是服务端,应用是客户端。应用要完成的工作有:

  1. 把平台的CA证书加到信任列表里。这样,进行HTTPs握手时应用才能信任平台的证书
  2. 使用平台签发的证书(包括公私钥)做为客户端证书。由于平台上有该证书签发者的CA证书,所以进行HTTPs握手时平台能信任应用的证书。
  3. 测试环境下,没有使用域名访问,所以应用要设置不校验服务端的域名;商用环境下,需进行域名校验。

QSslConfiguration 类

QSslConfiguration 类保存 SSL 连接的配置和状态
    Qt网络类使用QSslConfiguration来 传达 有关开放SSL连接的信息,并允许应用程序控制该连接的某些功能。
    QSslConfiguration目前支持的设置是:

  •         要使用的SSL / TLS协议
  •         连接期间要提供给对等方的证书及其关联的私钥密码允许用于加密连接
  •         密码允许用于加密连接
  •         用于验证对等方证书的证书颁发机构证书列表

    这些设置仅在连接握手期间应用 建立连接后设置它们无效。

    QSslConfiguration对象中的状态无法更改
    QSslConfiguration可以与QSslSocket和网络访问API一起使用。

  QSslCertificate 类 

        QSslCertificate存储X509证书,通常用于验证身份和存储有关本地主机,远程连接的对等方受信任的第三方证书颁发机构的信息。 有许多方法可以构建QSslCertificate。最常见的方法是调用QSslSocket :: peerCertificate(),它返回一个QSslCertificate对象,或QSslSocket :: peerCertificateChain(),它返回它们的列表。您还可以从DER(二进制)或PEM(Base64)编码的包中加载证书,通常存储为一个或多个本地文件,或者存储在Qt资源中。

    QFile file;
    QString path = "./certificate/";
    file.setFileName( path + "cert.pem");
    file.open(QIODevice::ReadOnly);
    QSslCertificate local(&file, QSsl::Pem);
    m_sslConfig.setLocalCertificate( local );
    file.close();

        certificate文件夹下有三个文件:cert.pem、key.pem、truststore.pem,需要把certificate这个文件夹放在运行程序exe的同级目录下。将SSL握手期间要提供给对等方的证书设置为证书。建立连接后设置证书无效。证书是SSL过程中使用的标识手段。 远程端使用本地证书来验证本地用户对其证书颁发机构列表的身份。

QSslKey类

QSslKey提供了一个用于管理密钥的简单API。QSslKey类为私钥和公钥提供接口,将连接的私钥设置为key。 私钥本地证书由必须向SSL对等方证明其身份的客户端和服务器使用。通过使用指定的算法编码格式从设备读取和解码数据来构造QSslKey。 type指定密钥是公共密钥还是私有密钥。 如果密钥已加密,则使用passPhrase对其进行解密。 构造完成后,使用isNull()检查设备是否提供了有效密钥。

QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding = QSsl::Pem, QSsl::KeyType type = QSsl::PrivateKey, const QByteArray &passPhrase = QByteArray())
    file.setFileName( path + "key.pem");
    file.open(QIODevice::ReadOnly);
    QSslKey key(&file, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray("YeNiu1234"));
    m_sslConfig.setPrivateKey( key );
    file.close();

   搜索并解析设备中以指定格式编码的所有证书,并将其返回到证书列表中。将此套接字的CA证书数据库设置为证书。必须在SSL握手之前设置证书数据库。 在握手阶段,套接字使用CA证书数据库来验证对等方的证书。将此网络请求的SSL配置设置为m_sslConfig。适用的设置包括私钥,本地证书,SSL协议(SSLv2,SSLv3,适用的TLSv1.0),CA证书以及允许SSL后端使用的密码。

void DataComponent::init(UConfig cfg)
{
    DC_pManager = new QNetworkAccessManager();

    QSslConfiguration m_sslConfig = DC_request.sslConfiguration();//返回此网络请求的SSL配置。
    m_sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);//QSslSocket不会从对等方请求证书。

    m_sslConfig.setProtocol(QSsl::TlsV1SslV3);//将此配置的 协议 设置为QSsl::TlsV1SslV3协议。

    //客户端证书
    QFile file;
    QString path = "./certificate/";
    file.setFileName( path + "cert.pem");
    file.open(QIODevice::ReadOnly);
    QSslCertificate local(&file, QSsl::Pem);
    m_sslConfig.setLocalCertificate( local );
    file.close();

    //私钥
    file.setFileName( path + "key.pem");
    file.open(QIODevice::ReadOnly);
    QSslKey key(&file, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray("YeNiu1234"));
    m_sslConfig.setPrivateKey( key );
    file.close();

    //信任库
    file.setFileName( path + "truststore.pem");
    file.open(QIODevice::ReadOnly);
    QList list = QSslCertificate::fromDevice(&file, QSsl::Pem);
    //搜索并解析设备中以指定格式编码的所有证书,并将其返回到证书列表中。
    file.close();
    m_sslConfig.setCaCertificates(list);
    //将此套接字的CA证书数据库设置为证书。
    //必须在SSL握手之前设置证书数据库。 在握手阶段,套接字使用CA证书数据库来验证对等方的证书。

    DC_request.setSslConfiguration(m_sslConfig);
    //将此网络请求的SSL配置设置为m_sslConfig。
    //适用的设置包括私钥,本地证书,SSL协议(SSLv2,SSLv3,适用的TLSv1.0),CA证书以及允许SSL后端使用的密码。
   
    DC_ucfg.ip = cfg.ip;
    DC_ucfg.port = cfg.port;
    DC_ucfg.appid = cfg.appid;
    DC_ucfg.password = cfg.password;

}

Auth(鉴权) 详情见华为IoT平台北向API参考

   接口功能

        实现第三方系统在访问开放API之前的认证

   调用方法

          post

  接口路径

https://server:port/iocm/app/sec/v1.1.0/login

QUrl类为处理URL提供了方便的接口。它可以解码和构造编码和未编码形式的URL

content-type请求头

        http请求头有四种类型,分别是通用头部请求头部响应头部以及内容头部。首先,content-type是属于 内容头部,是用来向 接收端 解释传递的该内容主体的,content-type的取值是告诉服务端,你传递过去的内容是啥,你应该准备好如何接收application/x-www-form-urlencoded 这个类型是ajax默认的 content-type类型,这时前端可以以对象方式直接给后端,或者以json方式传给后端。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server

        将HTTP POST请求发送到请求指定的目标,并返回打开以供读取的新QNetworkReply对象,该对象将包含服务器发送的回复。数据设备的内容将上传到服务器。数据必须打开才能读取,并且必须保持有效,直到为此回复发出finished()信号。

    GET - 从指定的资源请求数据。

        请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发送的:

    POST - 向指定的资源提交要被处理的数据

        请注意,查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的

    connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));

    回复处理完毕后会发出此信号。 发出此信号后,将不再更新回复的数据或元数据。注意:请勿删除连接到此信号的插槽中的对象。 使用deleteLater()。 您甚至可以在收到finished()信号之前使用isFinished()检查QNetworkReply是否已完成。

connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));

    当回复检测到处理中的错误时,发出该信号。 完成信号可能会跟随,表明连接已结束。code参数包含检测到的错误代码。 调      用errorString()以获取错误条件的文本表示。注意:请勿删除连接到此信号的插槽中的对象。 使用deleteLater()。

//鉴权 实现第三方系统在访问开放API之前的认证 POST
void DataComponent::auth()
{
    QString url = "";
    QString form = "";
    QByteArray bytePost;

    url = QString("https://%1:%2/iocm/app/sec/v1.1.0/login").arg(DC_ucfg.ip).arg(DC_ucfg.port);
    DC_request.setUrl(QUrl(url)); //设置此网络请求引用的URL为url。
    DC_request.setRawHeader("content-type","application/x-www-form-urlencoded");//将标头headerName设置为值headerValue。

    form = QString("appId=%1&secret=%2").arg(DC_ucfg.appid).arg(DC_ucfg.password);
    bytePost.append(form);

    DC_signType = SIGN_TYPE_AUTH;
    DC_pReply = DC_pManager->post(DC_request, bytePost);

    connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}

按条件批量查询设备信息列表
       接口功能
              按条件批量查询设备信息列表,如查询指定网关下的所有设备信息列表。
       调用方法
              GET
       接口路径

https://server:port/iocm/app/dm/v1.3.0/devices?
appId={appId}&gatewayId={gatewayId}&nodeType={nodeType}&deviceType={deviceType}&protocolType={proto
colType}&pageNo={pageNo}&pageSize={pageSize}&startTime={startTime}&endTime={endTime}&status={status
}&sort={sort}

       注意事项
              携带头域信息
                      app_key:{appId}
                      Authorization:Bearer {accessToken}
                      Content-Type:application/json
       当action为get时候,浏览器用x-www-form-urlencoded的编码方式,把form数据转换成一个字串                  (name1=value1&name2=value2),然后把这个字串append到url后面,用?分割,加载这个新的url。

       可选参数

               pageNo=0&pageSize=5 查询的数据为最新的5条数据。

网络操作发送与接收是异步的,当https接收完成或者异常等事件,会调用注册的对应槽函数来处理。信号与槽的连接
当HTTPS正常收发完成后,会在onFinished()函数里面进行出处理,这里就可以对接收的数据进行解析,获取到我们想要的数据信息。

//获取设备列表 get
void DataComponent::getDeviceList()
{
    QString url = "";
    QString form = "";
    QByteArray bytePost;

    url = QString("https://%1:%2/iocm/app/dm/v1.3.0/devices?pageNo=0&pageSize=5").arg(DC_ucfg.ip).arg(DC_ucfg.port);// pageNo=0&pageSize=5 查询的数据为最新的5条数据。
    QNetworkRequest         DC_request;
    DC_request.setUrl(QUrl(url));
    DC_request.setRawHeader("content-type","application/x-www-form-urlencoded");
    DC_request.setRawHeader("app_key",QString(DC_ucfg.appid).toLatin1());
    DC_request.setRawHeader("Authorization",QString("Bearer " + QString(DC_authCfg.accessToken)).toLatin1());
    DC_request.setRawHeader("content-type",QString("application/json").toLatin1());

    DC_signType = SIGN_TYPE_GETDEVLIST;
    DC_pReply = DC_pManager->get(DC_request);

    connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}

 获取设备数据

根据设备id查询数据往前查询count条 get

查询设备历史数据
       接口功能
              Application查询设备历史数据。
       调用方法
              GET
       接口路径

https://server:port/iocm/app/data/v1.1.0/deviceDataHistory?deviceId={deviceId}&
gatewayId={gatewayId}&serviceId ={serviceId}&pageNo={pageNo}&pageSize
={pageSize}&startTime={startTime}&endTime={endTime}&property={property}&appId={appId}

        注意事项
              携带头域信息
              app_key:{appId}
              Authorization:Bearer {accessToken}
              Content-Type:application/json
     deviceId 设备唯一标识, 1-64个字节
     gatewayId 网关的设备唯一标识。

  • 当设备是直连设备时, gatewayId为设备本身的deviceId。
  • 当设备是非直连设备时, gatewayId为设备所关联的直连设备(即网关)的deviceId
//获取设备数据 根据设备id查询数据往前查询count条 get
void DataComponent::getDeviceDataByCount(QString deviceId, int count)
{
    QString url = "";
    QString form = "";
    QByteArray bytePost;

    url = QString("https://%1:%2/iocm/app/data/v1.1.0/deviceDataHistory?"
                  "deviceId=%3&"
                  "gatewayId=%4&"
                  "pageNo=%5&"
                  "pageSize=%6").arg(DC_ucfg.ip).arg(DC_ucfg.port).arg(deviceId).arg(deviceId).arg(0).arg(count);

    QNetworkRequest         DC_request;
    DC_request.setUrl(QUrl(url));
    DC_request.setRawHeader("content-type","application/x-www-form-urlencoded");
    DC_request.setRawHeader("app_key",QString(DC_ucfg.appid).toLatin1());
    DC_request.setRawHeader("Authorization",QString("Bearer " + QString(DC_authCfg.accessToken)).toLatin1());
    DC_request.setRawHeader("content-type",QString("application/json").toLatin1());


    DC_signType = SIGN_TYPE_GETFASTDATA;
    DC_pReply = DC_pManager->get(DC_request);

    connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}

 根据设备id+上报时间查询数据--最多500条 get

//根据设备id+上报时间查询数据--最多500条 get
void DataComponent::getDeviceDataByDate(QString deviceId, QString startDate, QString endDate)
{
    QString url = "";
    QString form = "";
    QByteArray bytePost;

    url = QString("https://%1:%2/iocm/app/data/v1.1.0/deviceDataHistory?"
                  "deviceId=%3&"
                  "gatewayId=%4&"
                  "startTime=%5&"
                  "endTime=%6&"
                  "pageNo=0&pageSize=500").arg(DC_ucfg.ip).arg(DC_ucfg.port).arg(deviceId).arg(deviceId).arg(startDate).arg(endDate);

    QNetworkRequest         DC_request;
    DC_request.setUrl(QUrl(url));
    DC_request.setRawHeader("content-type","application/x-www-form-urlencoded");
    DC_request.setRawHeader("app_key",QString(DC_ucfg.appid).toLatin1());
    DC_request.setRawHeader("Authorization",QString("Bearer " + QString(DC_authCfg.accessToken)).toLatin1());
    DC_request.setRawHeader("content-type",QString("application/json").toLatin1());

    DC_signType = SIGN_TYPE_GETDATA_BYDATE;
    DC_pReply = DC_pManager->get(DC_request);

    connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}

当https接收发生异常,会调用注册的对应槽函数来处理。

void DataComponent::onError(QNetworkReply::NetworkError errorCode)
{
    qDebug() << "请求异常";

    QNetworkReply * pReplay = qobject_cast(sender());

    //输出错误及错误信息
    qDebug() << errorCode;
    qDebug() << pReplay->errorString();
}

当https接收完成,会调用注册的对应槽函数来处理。

    void auth();
    void getDeviceList();
    void getDeviceDataByCount(QString deviceId, int count);
    void getDeviceDataByDate(QString deviceId, QString startDate, QString endDate);
connect(DC_pReply, SIGNAL(finished()), this, SLOT(onFinished()));
connect(DC_pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));

DC_signType == SIGN_TYPE_AUTH

Method:POST
request: (非JSON格式)

https://server:port/iocm/app/sec/v1.1.0/login
Content-Type:application/x-www-form-urlencoded
appId={appId}&secret={secret}

response:

Status Code: 200 OK
Content-Type: application/json
{
"scope":"default",
"tokenType":"bearer",
"expiresIn":"*******",
"accessToken":"*******",
"refreshToken":"*******"
}

DC_signType == SIGN_TYPE_GETDEVLIST 
Method:
GET
request:

https://server:port/iocm/app/dm/v1.3.0/devices?
appId={appId}&gatewayId={gatewayId}&nodeType={nodeType}&deviceType={deviceType}&protocolType={proto
colType}&pageNo={pageNo}&pageSize={pageSize}&startTime={startTime}&endTime={endTime}&status={status
}&sort={sort}
app_key:{appId}
Authorization:Bearer {accessToken}
Content-Type:application/json

response:

Status Code: 200 OK
Content-Type: application/json
{
    "totalCount":1,
    "pageNo":0,
    "pageSize":10,
    "devices":[
    {
        "deviceId":"*************************",
        "gatewayId":"*************************",
        "nodeType":"GATEWAY",
        "createTime":"20170426T022604Z",
        "lastModifiedTime":"20170426T022604Z",
        "deviceInfo":{
            "nodeId":null,
            "name":null,
            "description":null,
            "manufacturerId":null,
            "manufacturerName":null,
            "mac":null,
            "location":null,
            "deviceType":null,
            "model":null,
            "swVersion":null,
            "fwVersion":null,
            "hwVersion":null,
            "protocolType":null,
            "bridgeId":null,
            "status":"OFFLINE",
            "statusDetail":"NOT_ACTIVE",
            "mute":null,
            "supportedSecurity":null,
            "isSecurity":null,
            "signalStrength":null,
            "sigVersion":null,
            "serialNumber":null,
            "batteryLevel":null
        },
        "services":[
            {
                "serviceId": "****",
                "serviceType": "****",
                "data": {
                    "p1": "****",
                    "p2": "****"
                },
                "eventTime": "20180125T141525Z",
                "serviceInfo": null
            }
        ],
        "connectionInfo":{
            "protocolType":null
            },
            "location":null,
            "devGroupIds":[]
        }
    ]
}

DC_signType == SIGN_TYPE_GETFASTDATA

Method:
GET
request:

https://server:port/iocm/app/data/v1.1.0/deviceDataHistory?deviceId={deviceId}&
gatewayId={gatewayId}&serviceId ={serviceId}&pageNo={pageNo}&pageSize
={pageSize}&startTime={startTime}&endTime={endTime}&property={property}&appId={appId}

app_key:{appId}
Authorization:Bearer {accessToken}
Content-Type:application/json

response:

Status Code: 200 OK
Content-Type:application/json
{
    "totalCount": 2,
    "pageNo": 0,
    "pageSize": 10,
    "deviceDataHistoryDTOs": [
    {
        "deviceId": "0d34bb9d-73e1-4780-a12f-9243345a4567",
        "gatewayId": "f6ee5aff-53f1-4459-be2f-4fc05ccd7004",
        "appId": "c8855f40-d7a3-4d51-8b46-9f7747839ee2",
        "serviceId": "Battery",
        "data": {
            "batteryLevel": 100
         },
        "timestamp": "20160708T025828Z"
    },
    {
        "deviceId": "0d34bb9d-73e1-4780-a12f-9243345a4567",
        "gatewayId": "f6ee5aff-53f1-4459-be2f-4fc05ccd7004",
        "appId": "c8855f40-d7a3-4d51-8b46-9f7747839ee2",
        "serviceId": "Temperature",
        "data": {
            "temperature": "33.5"
        },
        "timestamp": "20160708T025827Z"
    }
  ]
}

 

void DataComponent::onFinished()
{
    qDebug() << "onFinished" << DC_signType;
    if(DC_signType == SIGN_TYPE_AUTH)
    {
        QNetworkReply *pReplay = qobject_cast(sender());
        QByteArray replyContent = pReplay->readAll();

        QJsonParseError json_error;
        QJsonDocument jsonDoc(QJsonDocument::fromJson(replyContent, &json_error));

        if(json_error.error != QJsonParseError::NoError)
        {
            qDebug() << "SIGN_TYPE_AUTH json data is error!";
            return;
        }

        QJsonObject rootObj = jsonDoc.object();
        if(rootObj.contains("accessToken") && rootObj.contains("tokenType")
        && rootObj.contains("refreshToken") && rootObj.contains("expiresIn"))
        {
            DC_authCfg.accessToken = rootObj.value("accessToken").toString();
            DC_authCfg.refreshToken = rootObj.value("refreshToken").toString();
            DC_authCfg.expiresIn = rootObj.value("expiresIn").toInt();

            //向login发出信号
            emit signMsgToLogin(SIGN_CODE_AUTH_SUCCESS);
        }
        else
        {
            QMessageBox::information(nullptr, "Title", "输入数据错误", QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
            emit signMsgToLogin(SIGN_CODE_AUTH_FAIL);
        }

    }
    else if(DC_signType == SIGN_TYPE_GETDEVLIST)
    {
        QNetworkReply *pReplay = qobject_cast(sender());
        QByteArray replyContent = pReplay->readAll();

        QJsonObject devRootObj;
        QJsonParseError json_error;
        QJsonDocument jsonDoc(QJsonDocument::fromJson(replyContent, &json_error));

        if(json_error.error != QJsonParseError::NoError)
        {
            qDebug() << "SIGN_TYPE_GETDEVLIST json data is error!";
            return;
        }

        devRootObj = jsonDoc.object();
        if(devRootObj.contains("devices") && devRootObj.value("devices").isArray())
        {
            DC_devRootArray = devRootObj.value("devices").toArray();
            //通知主界面处理
            emit signMsgToMain(SIGN_CODE_GETDEVLIST_SUCCESS);
        }
    }
    else if(DC_signType == SIGN_TYPE_GETFASTDATA)
    {
        QNetworkReply *pReplay = qobject_cast(sender());
        QByteArray replyContent = pReplay->readAll();

        QJsonObject devRootObj;
        QJsonParseError json_error;
        QJsonDocument jsonDoc(QJsonDocument::fromJson(replyContent, &json_error));

        if(json_error.error != QJsonParseError::NoError)
        {
            qDebug() << "SIGN_TYPE_GETFASTDATA json data is error!";
            return;
        }

        devRootObj = jsonDoc.object();
        if(devRootObj.contains("deviceDataHistoryDTOs")
        && devRootObj.value("deviceDataHistoryDTOs").isArray()
        && devRootObj.value("deviceDataHistoryDTOs").toArray().size())
        {
            DC_resultFastData = devRootObj.value("deviceDataHistoryDTOs").toArray().at(0).toObject();
            emit signMsgToMain(SIGN_CODE_GETFASTDATA_SUCCESS);
        }

        qDebug() << replyContent;
    }
    else if(DC_signType == SIGN_TYPE_GETDATA_BYDATE)
    {
        QNetworkReply *pReplay = qobject_cast(sender());
        QByteArray replyContent = pReplay->readAll();

        QJsonObject devRootObj;
        QJsonParseError json_error;
        QJsonDocument jsonDoc(QJsonDocument::fromJson(replyContent, &json_error));

        if(json_error.error != QJsonParseError::NoError)
        {
            qDebug() << "SIGN_TYPE_GETDATA_BYDATE json data is error!";
            return;
        }

        devRootObj = jsonDoc.object();
        if(devRootObj.contains("deviceDataHistoryDTOs")
        && devRootObj.value("deviceDataHistoryDTOs").isArray()
        && devRootObj.value("deviceDataHistoryDTOs").toArray().size())
        {
            DC_resultDataArray = devRootObj.value("deviceDataHistoryDTOs").toArray();
            emit signMsgToMain(SIGN_CODE_GETDATA_BYDATE_SUCCESS);
        }
        qDebug() << replyContent;
    }
}

 6.mainwindow.h / mainwindow.cpp / mainwindow.ui

mainwindow.ui

基于QT的华为云平台北向开发_第7张图片

 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QTimer *updateTimer;
private slots:
    //ui生成的槽函数
    //获取数据
    void on_pushButton_4_clicked();
    //查询设备
    void on_pushButton_findDev_clicked();
    //自定义的槽函数
    void onSignDataComponent(int codeNum);
    //查询设备下标改变
    void on_comboBox_Dev_currentIndexChanged(const QString &arg1);

    void updateTimeOut();
    //更新周期下表改变
    void on_comboBox_updataCycle_currentIndexChanged(const QString &arg1);
    //获取数据 500
    void on_pushButton_5_clicked();



private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "datacomponent.h"

DataComponent g_DC;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //设置窗口大小,不允许用户最大化
    setFixedSize(this->width(), this->height());
    ui->dateEdit_start->setCalendarPopup(true);
    //此属性保存当前日历弹出显示模式。单击箭头按钮后将显示日历弹出窗口。 仅当存在有效的日期显示格式时,此属性才有效
    ui->dateEdit_end->setCalendarPopup(true);

    QDate date;
    date = QDate::currentDate();
    ui->dateEdit_end->setDate(date);
    date = date.addDays(-3);//往回退3天
    ui->dateEdit_start->setDate(date);

    updateTimer = nullptr;
    ui->comboBox_updataCycle->addItem(QString::fromLocal8Bit("设定"));
    ui->comboBox_updataCycle->addItem("5");
    ui->comboBox_updataCycle->addItem("10");
    ui->comboBox_updataCycle->addItem("30");
    ui->comboBox_updataCycle->addItem("60");

    //单条数据更新周期
    updateTimer = new QTimer(this);
    connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateTimeOut()));
    connect(&g_DC, SIGNAL(signMsgToMain(int)), this, SLOT(onSignDataComponent(int)));

    ui->comboBox_updataCycle->setCurrentIndex(2);
}

MainWindow::~MainWindow()
{
    delete ui;
}
//通过QNetworkAccessManager API接口获取到数据,我们需要对其进行处理。
//所以在datacomponent类中的onFinished()函数的最后发送一个自定义的信号到主界面线程中
void MainWindow::onSignDataComponent(int codeNum)
{
    if( codeNum == SIGN_CODE_GETDEVLIST_SUCCESS )
    {
        //获取设备列表
        qDebug() << "SIGN_CODE_GETDEVLIST_SUCCESS";
        ui->comboBox_Dev->clear();
        for(int i=0; icomboBox_Dev->addItem(devName);
        }
    }
    else if( codeNum == SIGN_CODE_GETFASTDATA_SUCCESS )
    {
        //获取第一条数据
        if(g_DC.DC_resultFastData.isEmpty())
        {
            QMessageBox::about(nullptr, "Error", "数据接受异常");
        }
        QJsonObject dataObj = g_DC.DC_resultFastData.value("data").toObject();

        ui->lineEdit_Temp->setText(dataObj.value("Temperature").toString());
        ui->lineEdit_Hum->setText(dataObj.value("Humidity").toString());
        ui->lineEdit_ill->setText(dataObj.value("illumination").toString());
        ui->lineEdit_time->setText(g_DC.DC_resultFastData.value("timestamp").toString());
        QString current_date = QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss");
        ui->lineEdit_timeReq->setText(current_date);
    }
    else if( codeNum == SIGN_CODE_GETDATA_BYDATE_SUCCESS)
    {
        if(g_DC.DC_resultDataArray.isEmpty())
        {
            QMessageBox::about(nullptr, "Error", "数据接受异常");
        }

        QJsonArray dataArray = g_DC.DC_resultDataArray;
        QJsonObject dataObj;
        QString dataLine = "";
        dataLine = "  ";
        dataLine += QString("温度").leftJustified(18, ' ');//返回一个大小为18的字符串不够填充空格
        dataLine += QString("湿度").leftJustified(18, ' ');//返回一个大小为18的字符串不够填充空格
        dataLine += QString("光照").leftJustified(18, ' ');//返回一个大小为18的字符串不够填充空格
        dataLine += QString("上报时间");
        dataLine += QString("\r\n");

        ui->textEdit->setText("");
        ui->textEdit->insertPlainText(dataLine);

        for(int j=0; jtextEdit->insertPlainText(dataLine);
        }
    }
}

//获取数据
void MainWindow::on_pushButton_4_clicked()
{
    QString devId = "";
    if(g_DC.DC_currentDev.isEmpty())
    {
        QMessageBox::about(nullptr, "Error", "请先选择需要查询的设备");
        return;
    }
    devId = g_DC.DC_currentDev.value("deviceId").toString();
    g_DC.getDeviceDataByCount(devId,1);
}

//查询设备
void MainWindow::on_pushButton_findDev_clicked()
{
    g_DC.getDeviceList();
}

//查询设备下标改变
void MainWindow::on_comboBox_Dev_currentIndexChanged(const QString &arg1)
{
    QString devName = arg1;
    QString devId = "";
    if(g_DC.DC_devRootArray.size() == 0)
    {
        QMessageBox::about(nullptr, "Error", "设备检测异常");
    }

    for(int i=0; icomboBox_updataCycle->currentText();
            updateTimer->start(timeCycle.toInt()*1000);

        }
    }
}

//更新周期下表改变
void MainWindow::on_comboBox_updataCycle_currentIndexChanged(const QString &arg1)
{
    (void)arg1;

    qDebug() << "on_comboBox_updataCycle_currentIndexChanged";
    if(updateTimer != nullptr && !g_DC.DC_currentDev.isEmpty())
    {
        QString timeCycle = ui->comboBox_updataCycle->currentText();
        updateTimer->start(timeCycle.toInt()*1000);
    }
}


void MainWindow::updateTimeOut()
{
    qDebug() << "updateTimeOut";

    QString devId = "";
    if(g_DC.DC_currentDev.isEmpty())
    {
            QMessageBox::about(nullptr, "Erro", "请先选择需要查询的设备");
            return;
    }

    devId = g_DC.DC_currentDev.value("deviceId").toString();
    g_DC.getDeviceDataByCount(devId, 1);
}

//查询数据 500
void MainWindow::on_pushButton_5_clicked()
{
    qDebug() << "on_pushButton_5_clicked_500";
    QString devId = "";
    QString startDate = "";
    QString endDate = "";

    if(g_DC.DC_currentDev.isEmpty())
    {
            QMessageBox::about(nullptr, "Error", "请先选择需要查询的设备");
            return;
    }

    devId = g_DC.DC_currentDev.value("deviceId").toString();
    startDate = ui->dateEdit_start->date().toString("yyyyMMddT000000Z");
    endDate = ui->dateEdit_end->date().toString("yyyyMMddT235959Z");
    g_DC.getDeviceDataByDate(devId, startDate, endDate);
}

mainwindow主要是信号与槽

提醒一点如下:

datacomponent类中 getDeviceList  会发出 信号 finished 等待槽函数  onFinished 处理。

onFinished中会向主线程发送信号 emit signMsgToMain(SIGN_CODE_GETDEVLIST_SUCCESS)

mainwindow.cpp构造函数中

connect(&g_DC, SIGNAL(signMsgToMain(int)), this, SLOT(onSignDataComponent(int)));

onSignDataComponent会对数据进行显示处理

完整工程链接

你可能感兴趣的:(QT)