嵌入式软件 基于Qt监控系统


项目开发报告
)

1 项目简介

1.1 概述

本系统是一个基于Qt开发的远程控制监控系统。采用了C/S架构,服务端运行在X4418实验平台上,客户端运行在与实验平台网络通畅的主机上。

本系统分为两大模块:登陆模块监控模块

服务器端能够通过外置摄像头监控,保存数据库,响应客户端的登陆,注册,截图,暂停等请求。
客户端能够远程访问服务器,在登陆后,进行截屏,暂停等操作。

1.2 开发环境

  • 基于Cortex-A9 Android&Linux(s4418)
  • X4418实验平台
  • Qt5.0以上版本

1.3 其他支持

  • sqlite数据库(QSql
  • 内核(课程提供)

1.4 应用界面

1.4.1 服务器端

在这里插入图片描述

1.4.2 客户端

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.5 程序使用

  • 1 服务器端运行后,此时没有用户登录,CurrentUser一栏显示为空。

在这里插入图片描述

  • 2 启动客户端,按照192.168.0.101:19999(默认显示)连接到服务器。

在这里插入图片描述

  • 3 开启三个页面,分别测试重复注册,登陆空账户,顺利注册。

重复注册

在这里插入图片描述

登陆无效账户

在这里插入图片描述

顺利注册(test@test)

在这里插入图片描述

  • 4 此时,用户登录,服务端页面CurrentUser显示test。点击暂停Stop,暂停屏幕。

在这里插入图片描述

  • 5 点击截图ScreenShot,获取截图。

在这里插入图片描述

2 项目开发

2.1 搭建基于实验平台数据库

2.1.1 数据库详情

  • Table: User
Item Description Type
UserName User Account[regist and log] varchar
Password Password varchar
  • 变量QSqlDatabase db;

  • 数据库初始化

    //open database
    db = QSqlDatabase::addDatabase("QSQLITE", "QSQLITE");
    db.setDatabaseName("./MyDB.db");
    bool ok = db.open();
    //create table
    if (ok) {
        QSqlQuery qsQuery = QSqlQuery(db);
        QString strSqlText = QString("CREATE TABLE User ( Username       VARCHAR(256),Password    VARCHAR(256));");
        qsQuery.prepare(strSqlText);
        if(!qsQuery.exec())
        {
            QSqlError lastError = qsQuery.lastError();
            qDebug() << lastError.driverText() << QString(QObject::tr("Create failed."));
        } else {
            qDebug() << "Create table user";
        }
        qsQuery.finish();
    }
  • 需要支持:Qt += sql

2.1.2 数据库接口及实现

设计思想:

由类MonitoeServer来控制,数据库接口只提供 服务。

2.1.2.1 用户注册

  • sql语句
INSERT INTO USER (Username, Password)
    VALUES (_name, _pw)
  • void Regist(QString name, QString pw)
//user regist
void MonitorServer::Regist(QString name, QString pw)
{
    QSqlQuery qsQuery = QSqlQuery(db);
    qsQuery.prepare("INSERT INTO User (Username, Password) "
                      "VALUES (:name, :pw)");
    qsQuery.bindValue(":name", name);
    qsQuery.bindValue(":pw", pw);
    if(!qsQuery.exec())
    {
        QSqlError lastError = qsQuery.lastError();
        qDebug() << lastError.driverText() << QString(QObject::tr("Insert failed."));
    }
    qsQuery.finish();
}

2.1.2.2 用户查询

  • sql语句
SELECT * FROM USER
    WHERE Username = _name
        AND Password = _pw
  • bool ComfirmUser(QString name, QString pw)
//comfirm account exsists or not
bool MonitorServer::ComfirmUser(QString name, QString pw)
{
    QSqlQuery qsQuery = QSqlQuery(db);
    qsQuery.exec("select * from User WHERE Username = '" + name + "' AND Password = '" + pw + "'");
    if(!qsQuery.exec())
    {
        QSqlError lastError = qsQuery.lastError();
        qDebug() << lastError.driverText() << QString(QObject::tr("Query failed."));
    }
    if (qsQuery.next()) {
        qsQuery.finish();
        return true;
    } else {
        qsQuery.finish();
        return false;
    }
}

2.2 监控系统客户端及服务端设计

2.2.1 CS模型

采用Client-Server模型。

根据需求,将监控系统(实验平台)设置为服务器端[192.168.0.101:19999],而用户端设置为客户端。

服务器端具有

  • 用户数据库
  • 监控

而客户端不保留有数据(从服务器端请求的数据除外)。

2.2.2 功能

服务端能够实时监控。

客户端在登陆后能够控制客户端暂停 继续 获取截屏

2.2.2 服务机与客户机交互

  • 交互模式

客户端与服务器端[192.168.0.101:19999]连接后。每次交互时,客户端向服务器端发送一条请求,服务器端处理后向客户端发送一条响应。

由于过程简单,因此不设定具体形式。

采用tcp连接。

  • request response集合
  1. 登录请求
  • request: login-{UserName}-{Password}-end
  • response(success): login-success
  • response(fail: No Account): login-fail
  1. 注册请求
  • request: regist-{UserName}-{Password}-end
  • response(success): regist-success
  • response(fail: Has Account): regist-fail
  1. 截屏请求
  • request: shot-{UserName}-end-end
  • response: Image
    • Send two message back, first is response, second is Image(QByteArray)
  1. 暂停请求
  • request: stop-{UserNmae}-end-end
  • response(success): stop-success
  • response(fail: In Stop State): stop-fail
  1. 继续请求
  • request: continue-{UserName}-end-end
  • response(success): continue-success
  • response(fail): continue-fail

2.3 客户端设计

2.3.1 概述

为了配合Server使用QCamera,使用QWidget设计师类。

下面分为操作界面设计 操作流程 实现及关键代码 几个部分进行介绍。

2.3.2 操作界面设计

  • 连接到服务器

在这里插入图片描述

  • 登陆/注册

在这里插入图片描述

  • 登陆/注册失败

在这里插入图片描述

  • 主页面

在这里插入图片描述

  • 总设计图

由于只有两个场景,且每一个场景中的组件数目都不多,因此将所有组件放在一个页面中,通过主类的状态控制及组建的SetVisible()方法来控制显示。

在这里插入图片描述

在这里插入图片描述

2.3.3 操作流程

输入服务器地址及端口号(Initial: 127.0.0.1:19999, Server: 192.168.0.101:19999),并连接到服务器。

输入账号及密码,完成登陆/注册。

输入错误,返回上一页面重新输入。

通过按钮控制。(ScreenShot: 截屏; Stop/Continue: 暂停/继续)

Tips:

1. Stop按钮在点击之后会变成Continue,再次点击恢复。

2. 点击ScreenShot按钮后屏幕上会显示当前得到的截屏。

2.3.4 客户端实现及关键代码

  • 主类MonitorClient
class MonitorClient : public QWidget
{
    Q_OBJECT
    
public:
    explicit MonitorClient(QWidget *parent = 0);
    ~MonitorClient();

    QTcpSocket *pTcpSocketClient;           //socket(client)
    QString name;                           //username

    bool getPic;                            //recieve picture(true), other request
    void SendMesg(QString strMesg);         //send request to server
private:
    Ui::MonitorClient *ui;


private slots:
    //connect to server
    void slotConnectServer();

    //get response from server & deal response
    void slotReadMesg();

    //log in, send login request
    void slotLogin();

    //regist, send regist request
    void slotRegist();

    //return, return to the last layer
    void slotReturn();

    //screen shot, send screen shot request
    void slotShot();

    //stop/continue, send stop/ contimue request
    void slotStop();
};
  • 初始化
    ui->setupUi(this);
    //connect slot
    this->connect(ui->pushButton_ConnectServer,SIGNAL(clicked()),this,SLOT(slotConnectServer()));
    this->connect(ui->pushButton_login,SIGNAL(clicked()),this,SLOT(slotLogin()));
    this->connect(ui->pushButton_regist,SIGNAL(clicked()),this,SLOT(slotRegist()));
    this->connect(ui->pushButton_return,SIGNAL(clicked()),this,SLOT(slotReturn()));
    this->connect(ui->pushButton_shot,SIGNAL(clicked()),this,SLOT(slotShot()));
    this->connect(ui->pushButton_stop,SIGNAL(clicked()),this,SLOT(slotStop()));

    //set views
    getPic = false;
    ui->label_warning->setText("Input the server IP and port");
    ui->label_name->setText("IP:");
    ui->label_pw->setText("Port:");
    ui->lineEdit_name->setText("127.0.0.1");
    ui->lineEdit_pw->setText("19999");
    ui->splitter_log->setVisible(false);
    ui->splitter_screen->setVisible(false);
    ui->label_image->setVisible(false);
  • 连接服务器 void slotConnectServer()
////connect to server
void MonitorClient::slotConnectServer()
{
    //get IP and Port
    if(ui->lineEdit_name->text().isEmpty() ||ui->lineEdit_pw->text().isEmpty())
    {
        //Warning
        QMessageBox::warning(this,tr("Warning"),tr("Please input IP OR NetPort!"));
        return;
    }
    //abort and reconnect
    pTcpSocketClient = new QTcpSocket(this);
    pTcpSocketClient->abort();
    QString qStrIP = ui->lineEdit_name->text();
    quint16 netport = ui->lineEdit_pw->text().toInt();
    pTcpSocketClient->connectToHost(qStrIP,netport);
    
    //bind the slot and readyRead
    connect(pTcpSocketClient,SIGNAL(readyRead()),this,SLOT(slotReadMesg()));

    //set visible
    ui->label_warning->clear();
    ui->lineEdit_name->clear();
    ui->lineEdit_pw->clear();
    ui->label_name->setText("UserName:");
    ui->label_pw->setText("Password:");
    ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
    ui->pushButton_ConnectServer->setVisible(false);
    ui->splitter_log->setVisible(true);
}
  • 向服务端发送请求 void SendMesg(QString strMesg)
//send message to server
void MonitorClient::SendMesg(QString strMesg) //发送消息
{
    pTcpSocketClient->write(strMesg.toStdString().c_str(),strlen(strMesg.toStdString().c_str()));
}
  • 登陆 void slotLogin()
//log in, send login request
void MonitorClient::slotLogin()
{
    QString command = "login";
    QString name = ui->lineEdit_name->text();
    QString pw = ui->lineEdit_pw->text();
    QString msg = command + "-" + name + "-" + pw + "-end";
    this->SendMesg(msg);
}
  • 注册 void slotRegist()
//regist, send regist request
void MonitorClient::slotRegist()
{
    QString command = "regist";
    QString name = ui->lineEdit_name->text();
    QString pw = ui->lineEdit_pw->text();
    QString msg = command + "-" + name + "-" + pw + "-end";
    this->SendMesg(msg);
}
  • 截屏 void slotShot()
//screen shot, send screen shot request
void MonitorClient::slotShot()
{
    //need to get Image from server
    this->getPic = true;
    //set views
    QString command = "shot";
    QString name = ui->lineEdit_name->text();
    QString file = ui->lineEdit_test->text();
    QString msg = command + "-" + name + "-" + file + "-end";
    this->SendMesg(msg);
    ui->label_warning->setText("Screen Shot");
}
  • 暂停/继续 void slotStop()
//stop/continue, send stop/ contimue request
void MonitorClient::slotStop() {
    if (ui->pushButton_stop->text() == "Stop") {
        //set pushButton
        ui->pushButton_stop->setText("Continue");
        QString command = "stop";
        QString name = ui->lineEdit_name->text();
        QString msg = command + "-" + name + "-end-end";
        this->SendMesg(msg);
    } else {
        //set pushButton
        ui->pushButton_stop->setText("Stop");
        QString command = "continue";
        QString name = ui->lineEdit_name->text();
        QString msg = command + "-" + name + "-end-end";
        this->SendMesg(msg);
    }
}
  • 信息读取及处理 void slotReadMesg()
//get response from server & deal response
void MonitorClient::slotReadMesg()
{
    //read the response
    QByteArray qba= pTcpSocketClient->readAll();
    //if it's a screen shot request
    if (getPic) {
        //convert QByteArray to Image
        QImage *image2 = new QImage;
        image2->loadFromData(qba, "PNG");
        //show the Image at the label
        ui->label_image->setPixmap(QPixmap::fromImage(*image2));
        ui->label_image->show();
        //set the getImage flag to false
        getPic = false;
        return;
    }
    //convert the request to the QString to handle
    QString ss=QVariant(qba).toString();
    QStringList resp = ss.split("-");
    if (resp[0] == "login") {
        //login response
        if (resp[1] == "success") {
            ui->label_warning->setText("Login success");
            name = resp[2];
            ui->splitter_user->setVisible(false);
            ui->splitter_screen->setVisible(true);
            ui->label_image->setVisible(true);
        } else{
            ui->label_warning->setText("Login fail");
            ui->splitter_name->setVisible(false);
            ui->splitter_pw->setVisible(false);
            ui->splitter_log->setVisible(false);
        }
    } else if (resp[0] == "regist"){
        //regist response
        if (resp[1] == "success") {
            ui->label_warning->setText("Regist success");
            name = resp[2];
            ui->splitter_user->setVisible(false);
            ui->splitter_screen->setVisible(true);
            ui->label_image->setVisible(true);
        } else{
            ui->label_warning->setText("Regist fail");
            ui->splitter_name->setVisible(false);
            ui->splitter_pw->setVisible(false);
            ui->splitter_log->setVisible(false);
        }
    }
}

2.4 服务端设计

2.4.1 概述

包含数据库及一个摄像头。

启动后,能够相应客户端的请求,客户端能够通过请求控制监控。

  • QT += multimedia
  • QT += multimediawidgets

2.4.2 界面设计

在这里插入图片描述

在这里插入图片描述

2.4.3 实现及关键代码

  • 主类
class MonitorServer : public QWidget
{
    Q_OBJECT
    
public:
    explicit MonitorServer(QWidget *parent = 0);
    ~MonitorServer();
    QTcpServer *m_pTcpServer;

    QTcpSocket *pTcpSocketServer;           //Server

    QSqlDatabase db;                        //user account information database
    QString user;                           //current user

    //user regist
    void Regist(QString name, QString pw);

    //comfirm user exsist or not
    bool ComfirmUser(QString name, QString pw);
    
private:
    Ui::MonitorServer *ui;

    //send response to client
    void SendMesg(QString strMesg);

    QCamera*             m_pCamera;         //load QCamera
    QCameraViewfinder*   m_pViewfinder;     //randered QCamera
    QCameraImageCapture* m_pImageCapture;   //get QCamera current frame

private slots:
    //new client connect
    void slotNewConnect();

    //get request from client and deal with response
    void slotReadMesg();

    //test pushButton
    void slotTest();
};
  • 初始化(数据库初始化见上一节)
    //initial server
    m_pTcpServer = new QTcpServer();
    m_pTcpServer->listen(QHostAddress::Any,19999);
    this->connect(m_pTcpServer,SIGNAL(newConnection()),this,SLOT(slotNewConnect()));
    this->connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(slotTest()));
    //initial QCarema
    m_pCamera = new QCamera(this);
    m_pViewfinder = new QCameraViewfinder(this);
    m_pImageCapture = new QCameraImageCapture(m_pCamera);
    mainLayout->addWidget(m_pViewfinder);
    connect(m_pImageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(cameraImageCaptured(int,QImage)));
    m_pImageCapture->setCaptureDestination(QCameraImageCapture::CaptureToFile);
    m_pCamera->setCaptureMode(QCamera::CaptureStillImage);
    m_pCamera->setViewfinder(m_pViewfinder);
    m_pCamera->start();
------
  • 处理新的客户端连接 void slotNewConnect()
//new client connect
void MonitorServer::slotNewConnect()
{
    pTcpSocketServer =  m_pTcpServer->nextPendingConnection();
    this->connect(pTcpSocketServer,SIGNAL(readyRead()),this,SLOT(slotReadMesg()));
}
  • 用户注册 void Regist(QString name, QString pw)
//user regist
void MonitorServer::Regist(QString name, QString pw)
{
    QSqlQuery qsQuery = QSqlQuery(db);
    qsQuery.prepare("INSERT INTO User (Username, Password) "
                      "VALUES (:name, :pw)");
    qsQuery.bindValue(":name", name);
    qsQuery.bindValue(":pw", pw);
    if(!qsQuery.exec())
    {
        QSqlError lastError = qsQuery.lastError();
        qDebug() << lastError.driverText() << QString(QObject::tr("Insert failed."));
    }
    qsQuery.finish();
}
  • 确认用户是否存在
//comfirm account exsists or not
bool MonitorServer::ComfirmUser(QString name, QString pw)
{
    QSqlQuery qsQuery = QSqlQuery(db);
    qsQuery.exec("select * from User WHERE Username = '" + name + "' AND Password = '" + pw + "'");
    if(!qsQuery.exec())
    {
        QSqlError lastError = qsQuery.lastError();
        qDebug() << lastError.driverText() << QString(QObject::tr("Query failed."));
    }
    if (qsQuery.next()) {
        qsQuery.finish();
        return true;
    } else {
        qsQuery.finish();
        return false;
    }
}
  • 发送截屏 void cameraImageCaptured(int, QImage image)
//capture current Image & send response
void MonitorServer::cameraImageCaptured(int, QImage image)
{
    QByteArray ba;
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer, "PNG");
    QImage *image2 = new QImage;
    image2->loadFromData(ba, "PNG");
    ui->label->setPixmap(QPixmap::fromImage(*image2));
    ui->label->show();
    this->SendMesg(QString(ba));
}
  • 请求接收及处理 void slotReadMesg()
//get request from client and deal with response
void MonitorServer::slotReadMesg()
{
    QByteArray qba= pTcpSocketServer->readAll(); //读取

    QString ss=QVariant(qba).toString();
    QStringList req = ss.split("-");
    if(req[0] == "login") {
        qDebug() << "User Log in";
        if(this->ComfirmUser(req[1], req[2])) {
            qDebug() << "Yes, ok";
            QString resp = "login-success-" + req[1] + "-end";
            this->SendMesg(resp);
            ui->label_user->setText("current user: " + req[1]);
        } else{
            QString resp = "login-fail-" + req[1] + "-end";
            this->SendMesg(resp);
        }
    } else if (req[0] == "regist") {
        qDebug() << "User Regist";
        if(this->ComfirmUser(req[1], req[2])) {
            qDebug() << "No, ok";
            QString resp = "regist-fail-" + req[1] + "-end";
            this->SendMesg(resp);
        } else{
            this->Regist(req[1], req[2]);
            ui->label_user->setText("current user: " + req[1]);
            QString resp = "regist-success-" + req[1] + "-end";
            this->SendMesg(resp);
        }
    } else if(req[0] == "shot") {
        this->m_pImageCapture->capture();
    } else if (req[0] == "stop") {
        ui->label_user->setText("current user: " + this->user + " STOP");
        this->m_pCamera->stop();
    } else {
        ui->label_user->setText("current user: " + this->user);
        this->m_pCamera->start();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章