I. Preface
Communication protocol analysis is the core soul of the whole system. Most people do software and hardware communication development. The first step is to write demo to analyze the protocol, and then slowly write the whole interface and operation process. In the field of industrial control, modbus protocol is widely used. This system also uses modbus protocol, which can be easily used by some third parties Configuration software access, stronger compatibility, modbus is only a protocol specification standard, as for the use of serial port or network or other, it is implemented by the manufacturer. Generally speaking, the system using modbus protocol supports 255 devices to communicate on the same network, and most of them will adopt the 485 half duplex mechanism, polling the device status one by one, each communication chain On the road, either sending data or receiving data can not be carried out at the same time. This has the advantage that the distance that can be transmitted can generally reach 1200 meters. If the serial port is full duplex, basically, the dishes will be stopped at 100 meters, depending on the baud rate. Of course, if the transfer equipment can be used for forwarding, that is not the case.
This project supports both serial devices and network devices. Just add the corresponding communication ports in the port management. There is a third-party open-source libmodbus, which is cross platform. Qt has added the bus serial bus library since Qt5.8, including QModbusClient and QModbusServer for modbus protocol analysis. I have used both of them. Datong In general, libmodbus is better than Qt's modbus. But in order to be more flexible, this project uses its own program to parse protocol data, so that it does not need to tangle the meaning of the interface that sends several bytes each time in the encapsulated interface, and it is also very convenient to parse by itself. All modbus communication projects I have written are protocol parsing by myself.
Skin open source: https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo File name: styledemo
Experience address: https://gitee.com/feiyangqingyun/QWidgetExe https://github.com/feiyangqingyun/QWidgetExe File name: bin_sams.zip
II. Functional features
- Data collection port, supporting serial port + network port, serial port supporting free setting of serial port number + baud rate, network supporting free setting of IP address + communication port, each port supporting collection cycle, one address per second by default, supporting setting of communication timeout times, three times by default, supporting maximum reconnection time, for re reading offline devices.
- Controller information, can add controller name, select controller address + controller model, and set the number of detectors under the controller.
- Detector information, can add tag number, can freely choose detector model, gas type, gas symbol, high alarm value, low alarm value, buffer value, reset value, whether to enable, alarm sound, background map, storage cycle, number of decimal places for numerical conversion, alarm delay time, alarm type (HH,LL,HL), etc.
- The controller model + detector model + gas type + gas symbol can be configured freely.
- The map supports import and deletion, and the map positions corresponding to all detectors can be dragged and saved freely.
- Port information + controller information + detector information, support import and export + export to excel + print.
- Operation record + alarm record + user record, support multi condition combination query, such as time period + controller + detector, etc., all records support export to excel + print.
- The records exported to excel support all versions of excel+wps and other table files, and do not rely on Excel and other software.
- It can delete data within the specified time range, support automatic cleaning of early data, and set the maximum number of saved records.
- Support alarm SMS forwarding, support multiple receiving mobile phone numbers, and set the sending interval, such as instant sending or sending all alarm messages once every six hours, the content of SMS is too long, and multiple SMS will be split automatically.
- Support alarm mail forwarding, support multiple receiving mailboxes, and set the sending interval, such as instant sending or sending all alarm information once every six hours, and support the sending of attachments.
- High alarm color + low alarm color + normal color + 0 value color + curve background + curve color, etc. can be freely selected.
- The Chinese title + English title + logo path + copyright of the software can be set freely.
- Provide switch settings for startup operation + alarm sound + automatic login + remember password, etc.
- The alarm sound can set the playing times, and the interface provides 17 skin file options.
- It supports cloud data synchronization and can set information of cloud database, such as database name, user name + password, etc.
- Support network forwarding and network receiving. After network receiving is turned on, the software receives data from udp for analysis. The network forwarding supports multiple target IP S, so that the locally collected software can freely transfer the data to the client and view the detector data at any time.
- Automatically remember the last user interface + other information, and automatically apply after restart.
- The alarm automatically switches to the corresponding map, and the detector button flashes.
- Double click the detector icon to control back.
- Support user rights management, administrator + operator two categories, user login + user exit, can remember password and automatic login, more than three times error prompt and close the program.
- Four monitoring modes are supported: equipment panel monitoring + map monitoring + table data monitoring + curve data monitoring, which can be switched freely and applied synchronously.
- Support alarm relay linkage. A tag number can link multiple modules and relay numbers across serial ports, and support many to many.
- Local data storage supports sqlite+mysql and remote data synchronization to cloud database. Automatic reconnection.
- The data collected by the local device is uploaded to the cloud in real time for mobile APP or web and other methods to extract.
- Support two kinds of data sources, one is serial port and network through the protocol to collect equipment data, the other is database collection. Database collection mode can be used as a general system.
- Equipped with device simulation tools, support 16 device data simulation, and also with database data simulation, so as to test data when there is no device.
- The default communication protocol is modbus protocol, and the support of Internet of things protocol such as mqtt is added later to make a general system.
- Support all windows + linux and other operating systems.
III. renderings
IV. core code
void DeviceClient::checkValue() { if (!isOk) { return; } QMutexLocker locker(&mutex); readData(); //At least how many bytes are needed to ensure that the following fetching data is not wrong 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 //Take out the first byte to determine whether it is the address in the current address set quint8 addr = buffer.at(0); quint8 cmd = buffer.at(1); quint8 len = buffer.at(2); //If it is an error code, parse the error information directly QList<quint8> cmds; cmds << 0x03 << 0x04 << 0x06; if (!cmds.contains(cmd)) { emit receiveError(portName, addr, QString("Data error: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); buffer.clear(); return; } //If the data is too long, the current packet will be discarded, otherwise the wrong data will be accumulated all the time if (size > 517) { emit receiveError(portName, addr, QString("Data error: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); buffer.clear(); return; } //The following data length must be greater than or equal to the length indicated by the length data bit if ((cmd == 0x03 || cmd == 0x04 || cmd == 0x06) && size < len + 5) { emit receiveError(portName, addr, QString("Incomplete data,Wait for the complete data to be parsed: %1").arg(QUIHelper::byteArrayToHexStr(buffer))); return; } //It's accurate and complete to send data here emit receiveData(portName, addr, buffer); //Filter the address that does not exist to prevent the index from crossing the boundary int index = addrs.indexOf(addr); if (index < 0) { emit receiveError(portName, addr, "error in address: The current address is not in the set address set"); buffer.clear(); return; } //The device that has received the message, immediately update the last message time, and judge whether the device is online times[index] = QDateTime::currentDateTime(); if (!onlines.at(index)) { onlines[index] = true; emit receiveOnline(portName, addr, true); emit receiveInfo(portName, addr, "Equipment on-line"); } //Retrieve the corresponding data content according to different cmd + different command types if (cmd == 0x03) { QString info; if (currentType == "Query concentration value") { 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 Return: %2").arg(currentType).arg(list.join(" ")); emit receiveValue(portName, addr, values); } else if (currentType == "Query 4 way AD value") { //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 Return: %2").arg(currentType).arg(list.join(" ")); emit receiveValueAD4(portName, addr, values); } else if (currentType == "Query alarm flag") { //01 03 08 00 00 00 12 07 13 00 12 5D 68 } else if (currentType == "Query calibration date") { //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 Return: %2 year%3 month%4 day").arg(currentType).arg(year).arg(month).arg(day); emit receiveDate(portName, addr, year, month, day); } else if (currentType == "Query factory date") { //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 Return: %2 year%3 month%4 day").arg(currentType).arg(year).arg(month).arg(day); emit receiveDateInit(portName, addr, year, month, day); } //Send corresponding text parsing if (!info.isEmpty()) { emit receiveInfo(portName, addr, info); } } else if (cmd == 0x04) { } else if (cmd == 0x06) { } //Reassign buffer.clear(); }