Qt編寫氣體安全管理系統5-數據監控

1、前言

本項目對設備的監控有四種視圖模式,能夠任意切換,數據監控、地圖監控、設備監控、曲線監控,其中數據監控是最經常使用的,因此在主界面導航中也排在第一位,綜合觀察分析了不少氣體安全或者組態監控軟件,大部分用戶習慣都是有個表格一行行顯示對應設備的實時數據和狀態等,加上一些特殊的顏色標識,這樣顯得更直觀,有時候還會分門別類展現。node

Qt提供了表格控件QTableWidget,能夠直接以行列的形式展現數據,使用接口和方法也是很是友好,直接new一個QTableWidgetItem而後指定行列位置setItem便可。通常在系統初始化的時候經過讀取數據庫中的設備列表實例化item而後加載,難點在於如何動態更新這些數據,並根據收到的值以不一樣的顏色顯示,爲此還專門寫了一個方法setColor來對傳進來的數據進行過濾,好比報警的值紅色加粗顯示,這些顏色還特地作了自定義,能夠在系統設置中分別設置低報、高報、離線、正常等各類顏色。mysql

在協議解析的類中,並非收到數據就立馬發送到數據表格中,那樣刷新頻率太大,沒有意義也沒有必要,由於絕大部分時間的數據實際上是不變的,因此在協議解析的類中,有一個隊列專門存儲的每一個設備對應的值,只有當值發生了變化,才須要從新發送到界面進行展現,設備上線下線也是一樣的處理,只有當設備首次上線或者離線的時候,才須要去更新狀態。linux

皮膚開源:https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo 文件名稱:styledemoc++

體驗地址:https://gitee.com/feiyangqingyun/QWidgetExe https://github.com/feiyangqingyun/QWidgetExe 文件名稱:bin_sams.zipgit

2、功能特色

  1. 採集數據端口,支持串口端口+網絡端口,串口支持自由設置串口號+波特率,網絡支持自由設置IP地址+通信端口,每一個端口支持採集週期,默認1秒鐘一個地址,支持設置通信超時次數,默認3次,支持最大重連時間,用於從新讀取離線的設備。
  2. 控制器信息,可以添加控制器名稱,選擇控制器地址+控制器型號,設置該控制器下面的探測器數量。
  3. 探測器信息,可以添加位號,可自由選擇探測器型號,氣體種類,氣體符號,高報值,低報值,緩衝值,清零值,是否啓用,報警聲音,背景地圖,存儲週期,數值換算小數點位數,報警延時時間,報警的類型(HH,LL,HL)等。
  4. 控制器型號+探測器型號+氣體種類+氣體符號,都可自由配置。
  5. 地圖支持導入和刪除,全部的探測器對應地圖位置可自由拖動保存。
  6. 端口信息+控制器信息+探測器信息,支持導入導出+導出到excel+打印。
  7. 運行記錄+報警記錄+用戶記錄,支持多條件組合查詢,好比時間段+控制器+探測器等,全部記錄支持導出到excel+打印。
  8. 導出到excel的記錄支持全部excel+wps等表格文件版本,不依賴excel等軟件。
  9. 可刪除指定時間範圍內的數據,支持自動清理早期數據,設置最大保存記錄數。
  10. 支持報警短信轉發,支持多個接收手機號碼,可設定發送間隔,好比即時發送或者6個小時發送一次全部的報警信息,短信內容過長,自動拆分多條短信。
  11. 支持報警郵件轉發,支持多個接收郵箱,可設定發送間隔,好比即時發送或者6個小時發送一次全部的報警信息,支持附件發送。
  12. 高報顏色+低報顏色+正常顏色+0值顏色+曲線背景+曲線顏色等,均可以自由選擇。
  13. 軟件的中文標題+英文標題+logo路徑+版權全部均可以自由設置。
  14. 提供開關設置開機運行+報警聲音+自動登陸+記住密碼等。
  15. 報警聲音可設置播放次數,界面提供17種皮膚文件選擇。
  16. 支持雲端數據同步,可設置雲端數據庫的信息,好比數據庫名稱,用戶名+密碼等。
  17. 支持網絡轉發和網絡接收,網絡接收開啓後,軟件從udp接收數據進行解析。網絡轉發支持多個目標IP,這樣就實現了本地採集的軟件,自由將數據轉到客戶端,隨時查看探測器數據。
  18. 自動記住用戶最後停留的界面+其餘信息,重啓後自動應用。
  19. 報警自動切換到對應的地圖,探測器按鈕閃爍。
  20. 雙擊探測器圖標,能夠進行回控。
  21. 支持用戶權限管理,管理員+操做員兩大類,用戶登陸+用戶退出,能夠記住密碼和自動登陸,超過三次報錯提示並關閉程序。
  22. 支持四種監控模式,設備面板監控+地圖監控+表格數據監控+曲線數據監控,可自由切換,四種同步應用。
  23. 支持報警繼電器聯動,一個位號能夠跨串口聯動多個模塊和繼電器號,支持多對多。
  24. 本地數據存儲支持sqlite+mysql,支持遠程數據同步到雲端數據庫。自動重連。
  25. 本地設備採集到的數據實時上傳到雲端,以便手機APP或者web等其餘方式提取。
  26. 支持兩種數據源,一種是串口和網絡經過協議採集設備數據,一種是數據庫採集。數據庫採集模式能夠做爲通用的系統使用。
  27. 自帶設備模擬工具,支持16個設備數據模擬,同時還帶數據庫數據模擬,以便在沒有設備的時候測試數據。
  28. 默認通訊協議採用modbus協議,後期增長mqtt等物聯網協議的支持,作成通用系統。
  29. 支持全部windows操做系統+linux操做系統和其餘操做系統。

3、效果圖

4、核心代碼

void DeviceHelper::deviceValue(const QString &positionID, float value)
{
    //下面的不會被頻繁執行,只有數據變更了纔會執行,已經在解析的地方作了過濾

    //處理設備面板
    if (App::PanelMode == 0) {
        foreach (frmDeviceNode *device, devices) {
            if (device->getPositionID() == positionID) {
                device->setValue(value);
                break;
            }
        }
    } else if (App::PanelMode == 1) {
        foreach (frmDeviceNode2 *device, devices2) {
            if (device->getPositionID() == positionID) {
                device->setValue(value);
                break;
            }
        }
    }

    //處理設備按鈕
    bool alarm = false;
    foreach (ButtonDefence *btn, btns) {
        if (btn->property("positionID").toString() == positionID) {
            alarm = (btn->getButtonStatus() == ButtonDefence::ButtonStatus_Alarm);
            btn->setProperty("value", value);

            //有兩個傳感器 是開關量 數值是 一、2  顯示時 用正常 和異常代替
            QString nodeType = btn->property("nodeType").toString();
            if (nodeType == "SJ-0001" || nodeType == "JG-0001") {
                btn->setText(value == 1 ? "正常" : "異常");
            } else {
                btn->setText(QString("%1 %2").arg(value).arg(btn->property("nodeSign").toString()));
            }

            break;
        }
    }

    //處理設備表格
    int count = deviceData->rowCount();
    for (int i = 0; i < count; i++) {
        QString id = deviceData->item(i, 1)->text();
        if (id == positionID) {
            deviceData->item(i, 7)->setText(QString::number(value));
            break;
        }
    }    
}

void DeviceHelper::deviceAlarm(const QString &positionID, quint8 alarmType)
{
    //0-濃度上限報警 2-濃度下限報警 5-其餘報警
    bool alarm = (alarmType == 0 || alarmType == 2 || alarmType == 5);

    //處理設備面板
    if (App::PanelMode == 0) {
        foreach (frmDeviceNode *device, devices) {
            if (device->getPositionID() == positionID) {
                device->setAlarm(alarm);
                break;
            }
        }
    } else if (App::PanelMode == 1) {
        foreach (frmDeviceNode2 *device, devices2) {
            if (device->getPositionID() == positionID) {
                device->setAlarm(alarm);
                break;
            }
        }
    }

    //處理設備按鈕
    foreach (ButtonDefence *btn, btns) {
        if (btn->property("positionID").toString() == positionID) {
            btn->setButtonStatus(alarm ? ButtonDefence::ButtonStatus_Alarm : ButtonDefence::ButtonStatus_Arming);
            //切換到當前地圖
            initDeviceMapCurrent(btn->property("nodeImage").toString());
            break;
        }
    }

    //處理設備表格
    int count = deviceData->rowCount();
    for (int i = 0; i < count; i++) {
        QString id = deviceData->item(i, 1)->text();
        if (id == positionID) {
            setColor(deviceData->item(i, 7), deviceData->item(i, 9), alarmType);
            break;
        }
    }

    //雲端數據同步
    if (App::UseNetDB) {
        int nodeStatus = 1;
        if (alarmType == 0) {
            nodeStatus = 3;
        } else if (alarmType == 2) {
            nodeStatus = 2;
        } else if (alarmType == 5) {
            nodeStatus = 5;
        }

        QString sql = QString("update NodeData set NodeStatus='%1' where PositionID='%2'").arg(nodeStatus).arg(positionID);
        DbTcpClientThread::Instance()->append(sql);
    }
}

void DeviceHelper::setColor(QTableWidgetItem *itemValue, QTableWidgetItem *itemStatus, quint8 alarmType)
{
    //0-低報 1-低報恢復 2-高報 3-高報恢復 5-其餘報警 6-其餘報警恢復
    if (alarmType == 0) {
        itemValue->setForeground(QColor(App::ColorLimit));
        itemStatus->setForeground(QColor(App::ColorLimit));
        itemStatus->setText("低報");
    } else if (alarmType == 2) {
        itemValue->setForeground(QColor(App::ColorUpper));
        itemStatus->setForeground(QColor(App::ColorUpper));
        itemStatus->setText("高報");
    } else if (alarmType == 1 || alarmType == 3 || alarmType == 6) {
        itemValue->setForeground(QColor(App::ColorNormal));
        itemStatus->setForeground(QColor(App::ColorNormal));
        itemStatus->setText("正常");
    } else if (alarmType == 4) {
        itemValue->setForeground(QColor(App::ColorZero));
        itemStatus->setForeground(QColor(App::ColorZero));
        itemStatus->setText("離線");
        itemValue->setText("0");
    } else if (alarmType == 5) {
        itemValue->setForeground(QColor(App::ColorOther));
        itemStatus->setForeground(QColor(App::ColorOther));
        itemStatus->setText("失效");
    }
}

void DeviceHelper::setColor(QTableWidgetItem *itemValue, QTableWidgetItem *itemStatus,
                            float value, float max, float min, const QString &alarmType)
{
    //這裏還有個未處理的,是否須要對初始值進行判斷報警
    if (value == 0) {
        itemValue->setForeground(QColor(App::ColorZero));
        itemStatus->setForeground(QColor(App::ColorZero));
    } else if (value > max) {
        itemValue->setForeground(QColor(App::ColorUpper));
        itemStatus->setForeground(QColor(App::ColorUpper));
    } else if (value < min) {
        itemValue->setForeground(QColor(App::ColorLimit));
        itemStatus->setForeground(QColor(App::ColorLimit));
    } else {
        itemValue->setForeground(QColor(App::ColorNormal));
        itemStatus->setForeground(QColor(App::ColorNormal));
    }

    itemValue->setText(QString::number(value));
}
相關文章
相關標籤/搜索