嵌入式軟件 基於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();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章