
一、前言
地圖按鈕很常見(jiàn),這也是用戶給錢(qián)就干的一個(gè)模塊。設(shè)備現(xiàn)場(chǎng)提供了對(duì)應(yīng)的地圖文件,其實(shí)就是圖片文件,做的簡(jiǎn)單點(diǎn)就是直接CAD圖紙轉(zhuǎn)成jpg,做的精致點(diǎn)就是搞了3D風(fēng)格的立體樣式圖片,其實(shí)還是圖片,并不是真正的3D,看起來(lái)像是3D的感覺(jué)。很多人看到效果圖都來(lái)問(wèn)這個(gè)3D怎么實(shí)現(xiàn)的,真正的3D實(shí)現(xiàn)都非常復(fù)雜,需要懂opengl,網(wǎng)頁(yè)上用的是webgl,當(dāng)然也有不少的輪子可以直接使用。
地圖按鈕就是一個(gè)終端傳感器作為一個(gè)按鈕點(diǎn),分布在對(duì)應(yīng)的圖片文件上,其實(shí)就是父類(lèi)設(shè)置為label,label顯示對(duì)應(yīng)的圖片。圖片大小的算法,目前采用的當(dāng)前分辨率計(jì)算,當(dāng)前分辨率減去上下左右的尺寸就是圖片應(yīng)該的尺寸,拉伸填充顯示,所以要求用戶提供的圖片最好按照現(xiàn)場(chǎng)電腦的分辨率比例來(lái)設(shè)置,這樣不會(huì)顯得拉伸變形,后期這一塊需要改進(jìn)算法,難點(diǎn)就是按鈕在設(shè)置界面的相對(duì)位置不好計(jì)算,除非是繪制上去的。
地圖按鈕幾個(gè)特色功能:
- 從內(nèi)存中而不是文件取圖片顯示,響應(yīng)速度微秒級(jí)別。
- 報(bào)警自動(dòng)切換到對(duì)應(yīng)的地圖。
- 不同狀態(tài)不同顏色顯示,報(bào)警紅色或者黃色閃爍。
- 按鈕位置在編輯狀態(tài)下,任意拖動(dòng),自動(dòng)保存。
- 提供縮略圖導(dǎo)航,可手動(dòng)切換。
- 在線狀態(tài)顯示對(duì)應(yīng)的值和單位符號(hào),離線狀態(tài)顯示位置編號(hào)。
二、功能特點(diǎn)
2.1 軟件模塊
設(shè)備監(jiān)控模塊,包括數(shù)據(jù)監(jiān)控(表格形式展示)、設(shè)備面板(面板形式展示)、地圖監(jiān)控(地圖形式展示)、曲線監(jiān)控(曲線形式展示)。數(shù)據(jù)查詢模塊,包括報(bào)警記錄、運(yùn)行記錄、操作記錄。系統(tǒng)設(shè)置模塊,包括基本設(shè)置、端口管理、控制器管理、探測(cè)器管理、報(bào)警聯(lián)動(dòng)、類(lèi)型設(shè)置等。其他設(shè)置模塊,包括用戶管理、地圖管理、位置調(diào)整、組態(tài)設(shè)計(jì)、設(shè)備調(diào)試等。2.2 基礎(chǔ)功能
設(shè)備數(shù)據(jù)采集,支持串口、網(wǎng)絡(luò),串口可設(shè)置串口號(hào)、波特率,網(wǎng)絡(luò)可設(shè)置IP地址、通訊端口。每個(gè)端口支持采集周期時(shí)間,默認(rèn)1秒鐘一個(gè)設(shè)備。支持設(shè)置通訊超時(shí)次數(shù),默認(rèn)3次。支持最大重連時(shí)間,用于重新讀取離線的設(shè)備。控制器信息,能夠添加控制器名稱(chēng),選擇控制器地址、控制器型號(hào),設(shè)置該控制器下面的探測(cè)器數(shù)量。探測(cè)器信息,能夠添加位號(hào)、探測(cè)器型號(hào)、氣體種類(lèi)、氣體符號(hào)、高報(bào)值、低報(bào)值、緩沖值、清零值、是否啟用、報(bào)警聲音、背景地圖、存儲(chǔ)周期、數(shù)值換算小數(shù)點(diǎn)位數(shù)、報(bào)警延時(shí)時(shí)間、報(bào)警的類(lèi)型(HH,LL,HL)等。類(lèi)型管理可配置控制器型號(hào)、探測(cè)器型號(hào)、氣體種類(lèi)、氣體符號(hào)等。地圖支持導(dǎo)入和刪除,所有的探測(cè)器在地圖上的位置可自由拖動(dòng)保存。端口信息、控制器信息、探測(cè)器信息、類(lèi)型信息、用戶信息等,都支持導(dǎo)入、導(dǎo)出、導(dǎo)出到excel、打印。運(yùn)行記錄、報(bào)警記錄、操作記錄,都支持多條件組合查詢,比如時(shí)間段、控制器、探測(cè)器等,所有記錄支持導(dǎo)出到excel/pdf和打印。運(yùn)行記錄、報(bào)警記錄、操作記錄都可刪除指定時(shí)間范圍內(nèi)的數(shù)據(jù)。系統(tǒng)設(shè)置可選擇對(duì)應(yīng)表最大保存記錄數(shù),自動(dòng)清理早期數(shù)據(jù),留出足夠的空間存儲(chǔ)重要的數(shù)據(jù)。報(bào)警短信轉(zhuǎn)發(fā),支持多個(gè)接收手機(jī)號(hào)碼,可設(shè)定發(fā)送間隔,比如即時(shí)發(fā)送或者6個(gè)小時(shí)發(fā)送一次所有的報(bào)警信息,短信內(nèi)容過(guò)長(zhǎng),自動(dòng)拆分多條短信。報(bào)警郵件轉(zhuǎn)發(fā),支持多個(gè)接收郵箱,可設(shè)定發(fā)送間隔,比如即時(shí)發(fā)送或者6個(gè)小時(shí)發(fā)送一次所有的報(bào)警信息,支持附件發(fā)送。設(shè)置軟件的中文標(biāo)題、英文標(biāo)題、logo路徑、版權(quán)所有等。開(kāi)關(guān)設(shè)置開(kāi)機(jī)運(yùn)行、報(bào)警聲音、自動(dòng)登錄、記住密碼等。報(bào)警聲音可設(shè)置播放次數(shù),界面風(fēng)格樣式提供18套皮膚文件選擇。用戶管理,包括用戶權(quán)限配置,不同用戶可以有不同模塊的權(quán)限。用戶登錄和用戶退出,可以記住密碼和自動(dòng)登錄,超過(guò)三次報(bào)錯(cuò)提示并關(guān)閉程序。四種監(jiān)控模式,設(shè)備面板監(jiān)控、地圖監(jiān)控、表格數(shù)據(jù)監(jiān)控、曲線數(shù)據(jù)監(jiān)控,可自由切換,四種模式下都實(shí)時(shí)展示采集到的數(shù)據(jù),報(bào)警閃爍等。報(bào)警繼電器聯(lián)動(dòng),一個(gè)位號(hào)可以跨串口聯(lián)動(dòng)多個(gè)模塊和繼電器號(hào),支持多對(duì)多。2.3 特色功能
通信協(xié)議支持modbus_com、modbus_tcp_rtu,后期拓展mqtt等協(xié)議。數(shù)據(jù)源除了真實(shí)的硬件設(shè)備采集,還可選數(shù)據(jù)庫(kù)采集,這樣用戶可以安排其他程序員比如java程序員將前端采集好的數(shù)據(jù)放到數(shù)據(jù)庫(kù),本系統(tǒng)直接從數(shù)據(jù)庫(kù)采集即可。數(shù)據(jù)庫(kù)采集模式可以作為通用的系統(tǒng)使用,更適合多人多系統(tǒng)協(xié)作。智能跳過(guò)超時(shí)的設(shè)備,加快對(duì)在線設(shè)備的采集速度,當(dāng)設(shè)備數(shù)量很多的時(shí)候尤其有用。對(duì)智能跳過(guò)的超時(shí)的設(shè)備,在設(shè)定的重連時(shí)間自動(dòng)采集一次,以便探測(cè)設(shè)備是否又重新上線。每個(gè)探測(cè)器可控是否啟用,不啟用則不會(huì)采集,也不會(huì)在界面顯示,相當(dāng)于運(yùn)行階段臨時(shí)關(guān)閉。探測(cè)器可設(shè)置緩沖值和報(bào)警延時(shí)時(shí)間,在該值附近波動(dòng)產(chǎn)生的報(bào)警,不計(jì)入報(bào)警,只有持續(xù)處于報(bào)警值且超過(guò)報(bào)警延時(shí)時(shí)間才算真正報(bào)警,這樣可以規(guī)避很多波動(dòng)導(dǎo)致的誤報(bào)。探測(cè)器可設(shè)置存儲(chǔ)周期,按照設(shè)定的時(shí)間來(lái)存儲(chǔ)一條運(yùn)行記錄,可以按照重要程度對(duì)重要性高的設(shè)定存儲(chǔ)周期短一些,不重要的設(shè)定大一些,這樣可以節(jié)省不少的存儲(chǔ)空間,也保證了重要的數(shù)據(jù)及時(shí)存儲(chǔ)。探測(cè)器可設(shè)置清零值,在一些高精度高靈敏的設(shè)備可能出廠的時(shí)候默認(rèn)值未必是0,需要設(shè)定清零值來(lái)表示初始值。探測(cè)器可設(shè)置小數(shù)點(diǎn),用于計(jì)算后的真實(shí)數(shù)據(jù)控制小數(shù)點(diǎn)點(diǎn)位顯示,相當(dāng)于除以10、除以100、除以1000,這樣大部分的探測(cè)器數(shù)據(jù)直接通過(guò)小數(shù)點(diǎn)位設(shè)置控制真實(shí)換算后的值,極個(gè)別的需要特殊轉(zhuǎn)換的可以在通信協(xié)議中約定。探測(cè)器報(bào)警的類(lèi)型支持多種,有些設(shè)備是高于某個(gè)值高報(bào),低于某個(gè)值低報(bào),而有些設(shè)備是在最小值最大值范圍內(nèi)是高報(bào),低于最小值低報(bào),高于最大值正常。這樣可以分情況處理,涵蓋各種報(bào)警類(lèi)型。原創(chuàng)數(shù)據(jù)導(dǎo)入、導(dǎo)出、打印機(jī)制,跨平臺(tái)不依賴(lài)任何組件,瞬間導(dǎo)出數(shù)據(jù)。導(dǎo)出到excel的記錄支持所有excel、wps等表格文件版本,不依賴(lài)excel等軟件。高報(bào)顏色、低報(bào)顏色、正常顏色、默認(rèn)值顏色等,都可以自由設(shè)置。支持云端數(shù)據(jù)同步,將本地采集到的數(shù)據(jù)實(shí)時(shí)同步到云端。支持網(wǎng)絡(luò)轉(zhuǎn)發(fā)和網(wǎng)絡(luò)接收,網(wǎng)絡(luò)接收開(kāi)啟后,軟件從udp接收數(shù)據(jù)進(jìn)行解析。網(wǎng)絡(luò)轉(zhuǎn)發(fā)支持多個(gè)目標(biāo)IP,這樣就實(shí)現(xiàn)了本地采集的軟件,自由將數(shù)據(jù)轉(zhuǎn)到客戶端,隨時(shí)查看采集到的數(shù)據(jù)。自動(dòng)記住用戶最后停留的界面以及其他配置信息,重啟后自動(dòng)應(yīng)用。報(bào)警自動(dòng)切換到對(duì)應(yīng)的地圖,探測(cè)器按鈕閃爍,表格數(shù)據(jù)對(duì)應(yīng)顏色顯示。雙擊探測(cè)器圖標(biāo),彈出對(duì)應(yīng)探測(cè)器詳細(xì)信息,可以根據(jù)需要定制回控操作。數(shù)據(jù)庫(kù)支持多種,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金倉(cāng)等。本地設(shè)備采集到的數(shù)據(jù)實(shí)時(shí)上傳到云端,以便手機(jī)APP或者web等其他方式提取。自帶設(shè)備模擬工具,支持不同型號(hào)的多個(gè)設(shè)備數(shù)據(jù)模擬,同時(shí)還帶數(shù)據(jù)庫(kù)數(shù)據(jù)模擬,以便在沒(méi)有設(shè)備的時(shí)候測(cè)試數(shù)據(jù)。標(biāo)準(zhǔn)modbus協(xié)議,各種控制器類(lèi)型、探測(cè)器類(lèi)型、種類(lèi)、符號(hào)等全部自定義,非常靈活和強(qiáng)大,通信協(xié)議示例數(shù)據(jù)非常完整,通用各種modbus協(xié)議系統(tǒng),適用于各種應(yīng)用場(chǎng)景接入。同時(shí)集成了串口通信、網(wǎng)絡(luò)通信、數(shù)據(jù)庫(kù)通信、數(shù)據(jù)導(dǎo)入導(dǎo)出打印、通信協(xié)議解析、界面UI、全局換膚等眾多組件和知識(shí)點(diǎn),非常適合新手入門(mén)和進(jìn)階。支持xp、win7、win10、、win11、linux、mac、各種國(guó)產(chǎn)系統(tǒng)(UOS、中標(biāo)麒麟、銀河麒麟等)、嵌入式linux等系統(tǒng)。注釋完整,項(xiàng)目結(jié)構(gòu)清晰,超級(jí)詳細(xì)完整的使用開(kāi)發(fā)手冊(cè),精確到每個(gè)代碼文件的功能說(shuō)明,不斷持續(xù)迭代版本。三、體驗(yàn)地址
國(guó)內(nèi)站點(diǎn):https://gitee.com/feiyangqingyun國(guó)際站點(diǎn):https://github.com/feiyangqingyun個(gè)人主頁(yè):https://blog.csdn.net/feiyangqingyun知乎主頁(yè):https://www.zhihu.com/people/feiyangqingyun產(chǎn)品主頁(yè):https://blog.csdn.net/feiyangqingyun/article/details/97565652在線文檔:https://feiyangqingyun.gitee.io/qwidgetdemo/iotsystem/體驗(yàn)地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 文件名:bin_iotsystem.zip。文章導(dǎo)航:https://qtchina.blog.csdn.net/article/details/121330922四、效果圖
五、相關(guān)代碼
void DeviceHelper::initDeviceButton(QLabel *label){ if (labMap == NULL) { return; } //先清空原來(lái)的 bool isMain = (labMap == label); if (isMain) { qDeleteAll(btns); btns.clear(); } else { qDeleteAll(btns2); btns2.clear(); } for (int i = 0; i setButtonStyle(DeviceButton::ButtonStyle_Msg2); btn->resize(AppData::DeviceWidth, AppData::DeviceHeight); btn->setText(QString(“%1”).arg(DbData::NodeInfo_PositionID.at(i))); btn->setButtonColor(isMain ? DeviceButton::ButtonColor_Black : DeviceButton::ButtonColor_Green); btn->setCanMove(!isMain); //設(shè)置弱屬性 btn->setProperty(“nodeID”, DbData::NodeInfo_NodeID.at(i)); btn->setProperty(“positionID”, DbData::NodeInfo_PositionID.at(i)); btn->setProperty(“deviceName”, DbData::NodeInfo_DeviceName.at(i)); btn->setProperty(“nodeName”, DbData::NodeInfo_NodeName.at(i)); btn->setProperty(“nodeAddr”, DbData::NodeInfo_NodeAddr.at(i)); btn->setProperty(“nodeType”, DbData::NodeInfo_NodeType.at(i)); btn->setProperty(“nodeSign”, DbData::NodeInfo_NodeSign.at(i)); btn->setProperty(“nodeImage”, DbData::NodeInfo_NodeImage.at(i)); //設(shè)置位置 int x = DbData::NodeInfo_NodeX.at(i); int y = DbData::NodeInfo_NodeY.at(i); btn->move(x, y); if (isMain) { btns << btn; QObject::connect(btn, SIGNAL(doubleClicked()), AppEvent::Instance(), SLOT(slot_doubleClicked())); } else { btns2 height(); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); //繪制背景圖 QImage img(imgName); if (!img.isNull()) { img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter.drawImage(0, 0, img); } //計(jì)算字體 QFont font; font.setPixelSize(height * 0.37); font.setBold(true); //自動(dòng)計(jì)算文字繪制區(qū)域,繪制防區(qū)號(hào) QRectF rect = this->rect(); if (buttonStyle == ButtonStyle_Police) { double y = (30 * height / 60); rect = QRectF(0, y, width, height – y); } else if (buttonStyle == ButtonStyle_Bubble) { double y = (8 * height / 60); rect = QRectF(0, 0, width, height – y); } else if (buttonStyle == ButtonStyle_Bubble2) { double y = (13 * height / 60); rect = QRectF(0, 0, width, height – y); font.setPixelSize(width * 0.33); } else if (buttonStyle == ButtonStyle_Msg) { double y = (17 * height / 60); rect = QRectF(0, 0, width, height – y); } else if (buttonStyle == ButtonStyle_Msg2) { double y = (17 * height / 60); rect = QRectF(0, 0, width, height – y); } //繪制文字標(biāo)識(shí) painter.setFont(font); painter.setPen(Qt::white); painter.drawText(rect, Qt::AlignCenter, text);}bool DeviceButton::eventFilter(QObject *watched, QEvent *event){ //識(shí)別鼠標(biāo) 按下+移動(dòng)+松開(kāi)+雙擊 等事件 QMouseEvent *mouseEvent = static_cast(event); if (event->type() == QEvent::MouseButtonPress) { //限定鼠標(biāo)左鍵 if (mouseEvent->button() == Qt::LeftButton) { lastPoint = mouseEvent->pos(); isPressed = true; emit clicked(); return true; } } else if (event->type() == QEvent::MouseMove) { //允許拖動(dòng)并且鼠標(biāo)按下準(zhǔn)備拖動(dòng) if (canMove && isPressed) { int dx = mouseEvent->pos().x() – lastPoint.x(); int dy = mouseEvent->pos().y() – lastPoint.y(); this->move(this->x() + dx, this->y() + dy); return true; } } else if (event->type() == QEvent::MouseButtonRelease) { isPressed = false; } else if (event->type() == QEvent::MouseButtonDblClick) { emit doubleClicked(); } return QWidget::eventFilter(watched, event);}
鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。