通訊協議解析是整個系統的核心靈魂,絕大部分人作軟硬件通訊開發,第一步估計就是寫demo將協議解析好,而後再慢慢寫整個界面和操做流程等,在工業控制領域,modbus協議應用仍是很是普遍的,這個系統用的也是modbus協議,採用此協議能夠方便的用一些第三方的組態軟件接入,兼容性更強,modbus只是個協議規約標準,至於使用串口仍是網絡仍是其餘,都由廠家自行具體實現,通常正常來講,使用modbus協議的系統支持255個設備在同一個網絡上進行通訊,並且大部分都會採用485半雙工的機制,挨個輪詢設備狀態,每次通訊鏈路上要麼在發送數據,要麼在接收數據,不能同時進行,這樣有個好處就是能夠傳輸的距離通常能夠到1200米,而若是是串口全雙工的話,基本上在100米就歇菜,具體還要看波特率,固然若是能夠用中轉設備進行轉發那除外。mysql
本項目即支持串口設備也支持網絡設備,在端口管理中添加好對應的通訊端口便可,modbus通訊協議的解析庫,有個第三方開源的libmodbus,跨平臺的,Qt從Qt5.8以來新增了總線serialbus庫,其中就包括了QModbusClient和QModbusServer來作modbus協議的解析,本人兩種都用過,大同小異,使用方法都差很少,整體上libmodbus要優於Qt自帶的modbus。可是爲了更大的靈活性,這個項目採用的本身程序解析協議數據來實現,這樣不用去糾結封裝的接口中每次發送幾個字節那些接口對應的含義等,並且本身解析也很方便,我寫過的全部的modbus通訊項目,都是採用的本身作協議解析。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
void DeviceClient::checkValue() { if (!isOk) { return; } QMutexLocker locker(&mutex); readData(); //至少要多少個字節,保證下面取數據不出錯 int size = buffer.size(); if (size < 5) { return; } //01 03 08 00 00 00 00 00 00 00 00 95 D7 //01 03 08 00 14 03 12 00 00 00 00 79 E6 //取出首字節,判斷是否爲當前地址集合中的地址 quint8 addr = buffer.at(0); quint8 cmd = buffer.at(1); quint8 len = buffer.at(2); //若是是錯誤碼則直接解析錯誤信息 QList<quint8> cmds; cmds << 0x03 << 0x04 << 0x06; if (!cmds.contains(cmd)) { emit receiveError(portName, addr, QString("數據出錯: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); buffer.clear(); return; } //若是數據過長則丟棄當前數據包,否則一旦產生了錯誤的數據會一直累積 if (size > 517) { emit receiveError(portName, addr, QString("數據出錯: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); buffer.clear(); return; } //後面的數據長度必須大於等於長度數據位表示的長度 if ((cmd == 0x03 || cmd == 0x04 || cmd == 0x06) && size < len + 5) { emit receiveError(portName, addr, QString("數據不全,等待完整數據再解析: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); return; } //放在這裏發出去數據是準確的完整的 emit receiveData(portName, addr, buffer); //過濾不存在的地址,防止索引越界 int index = addrs.indexOf(addr); if (index < 0) { emit receiveError(portName, addr, "地址出錯: 當前地址不在設定的地址集合中"); buffer.clear(); return; } //來過消息的設備,立馬更新最後的消息時間,以及判斷設備上線 times[index] = QDateTime::currentDateTime(); if (!onlines.at(index)) { onlines[index] = true; emit receiveOnline(portName, addr, true); emit receiveInfo(portName, addr, "設備上線"); } //根據不一樣的cmd+不一樣的命令類型 取出對應的數據內容 if (cmd == 0x03) { QString info; if (currentType == "查詢濃度值") { QList<quint16> values; for (int i = 3; i < size - 2; i = i + 2) { values << (float)QUIHelper::byteToUShort(buffer.mid(i, 2)); } QStringList list; foreach (quint16 value, values) { list << QString::number(value); } info = QString("%1返回: %2").arg(currentType).arg(list.join(" ")); emit receiveValue(portName, addr, values); } else if (currentType == "查詢4路AD值") { //01 03 08 02 07 08 0C 00 00 00 00 73 87 QList<quint16> values; for (int i = 3; i < size - 2; i = i + 2) { values << (float)QUIHelper::byteToUShort(buffer.mid(i, 2)); } QStringList list; foreach (quint16 value, values) { list << QString::number(value); } info = QString("%1返回: %2").arg(currentType).arg(list.join(" ")); emit receiveValueAD4(portName, addr, values); } else if (currentType == "查詢報警標誌") { //01 03 08 00 00 00 12 07 13 00 12 5D 68 } else if (currentType == "查詢標定日期") { //01 03 04 00 12 07 13 19 CB int year = QString("20%1").arg(QString::number(buffer.at(4), todate)).toInt(); int month = QString::number(buffer.at(5), todate).toInt(); int day = QString::number(buffer.at(6), todate).toInt(); info = QString("%1返回: %2年%3月%4日").arg(currentType).arg(year).arg(month).arg(day); emit receiveDate(portName, addr, year, month, day); } else if (currentType == "查詢出廠日期") { //01 03 04 00 12 07 13 19 CB int year = QString("20%1").arg(QString::number(buffer.at(4), todate)).toInt(); int month = QString::number(buffer.at(5), todate).toInt(); int day = QString::number(buffer.at(6), todate).toInt(); info = QString("%1返回: %2年%3月%4日").arg(currentType).arg(year).arg(month).arg(day); emit receiveDateInit(portName, addr, year, month, day); } //發送對應的文字解析 if (!info.isEmpty()) { emit receiveInfo(portName, addr, info); } } else if (cmd == 0x04) { } else if (cmd == 0x06) { } //從新賦值 buffer.clear(); }