main
parent
9389eb0030
commit
9a5d784283
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
QT += core gui serialport serialbus
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
modbuscontroller.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h \
|
||||||
|
modbuscontroller.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
#TRANSLATIONS += \
|
||||||
|
# EmsShow_en_US.ts
|
||||||
|
#CONFIG += lrelease
|
||||||
|
#CONFIG += embed_translations
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
DISTFILES += \
|
||||||
|
emsshow.ini
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US"></TS>
|
|
@ -0,0 +1,49 @@
|
||||||
|
;通用设置
|
||||||
|
[general]
|
||||||
|
ver="1.0.0"
|
||||||
|
|
||||||
|
[modbus]
|
||||||
|
;1=RTU 0=TCP
|
||||||
|
type=1
|
||||||
|
|
||||||
|
;if type=1, ignore ip and port setting
|
||||||
|
ip="127.0.0.1"
|
||||||
|
port=502
|
||||||
|
|
||||||
|
com="COM20"
|
||||||
|
baund=9600
|
||||||
|
data=8
|
||||||
|
|
||||||
|
;0=None 1=Odd 2=Even
|
||||||
|
parity=0
|
||||||
|
|
||||||
|
stop=1
|
||||||
|
|
||||||
|
[slaves]
|
||||||
|
;如果从机数量多于1,则从机地址slave_id以:分隔(英文冒号)
|
||||||
|
slave_id="1"
|
||||||
|
|
||||||
|
;每一个从机的配置
|
||||||
|
;由两个节组成:slave_%d 和 slave_%d_function_%d
|
||||||
|
|
||||||
|
[slave_0]
|
||||||
|
;支持的功能码数量
|
||||||
|
function_code_counts=1
|
||||||
|
|
||||||
|
[slave_0_function_0]
|
||||||
|
;功能码
|
||||||
|
function_code=3
|
||||||
|
|
||||||
|
;起始地址,10进制
|
||||||
|
start_addr=40001
|
||||||
|
|
||||||
|
;读取数量
|
||||||
|
quantity=10
|
||||||
|
|
||||||
|
;扫描间隔,单位ms
|
||||||
|
interval=1000
|
||||||
|
|
||||||
|
;serial_number与read_quantity的元素数量要一样
|
||||||
|
equipment_code=81
|
||||||
|
serial_number="0:1:2:3:4:5:6:7:8:9:10:11:12:13:14"
|
||||||
|
read_quantity="1:1:1:1:1:1:1:1:1:1:1:1:1:1:1"
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
const QStringList uiLanguages = QLocale::system().uiLanguages();
|
||||||
|
for (const QString &locale : uiLanguages)
|
||||||
|
{
|
||||||
|
const QString baseName = "EmsShow_" + QLocale(locale).name();
|
||||||
|
if (translator.load(":/i18n/" + baseName))
|
||||||
|
{
|
||||||
|
a.installTranslator(&translator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_btn_Initialize_clicked()
|
||||||
|
{
|
||||||
|
QString appDir = QCoreApplication::applicationDirPath();
|
||||||
|
QString iniFilePath = appDir + QString::fromStdString("/emsshow.ini");
|
||||||
|
|
||||||
|
m_controller.getConfiguration(iniFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::on_btn_Read_clicked()
|
||||||
|
{
|
||||||
|
if (!m_controller.Open())
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Critical Message"), tr("串口打开失败"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_controller.Read();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
#pragma execution_character_set("utf-8")
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include "modbuscontroller.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_btn_Initialize_clicked();
|
||||||
|
|
||||||
|
void on_btn_Read_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ModbusController m_controller;
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<widget class="QPushButton" name="btn_Initialize">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>30</y>
|
||||||
|
<width>93</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Initialize</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="btn_Read">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>130</x>
|
||||||
|
<y>30</y>
|
||||||
|
<width>121</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Read Modbus</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>26</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,191 @@
|
||||||
|
#include "modbuscontroller.h"
|
||||||
|
|
||||||
|
#define _DEBUG_VSPD_
|
||||||
|
|
||||||
|
ModbusController::ModbusController():m_pModbusMaster(nullptr), m_pSettings(nullptr)
|
||||||
|
{
|
||||||
|
m_version= "1.0.0";
|
||||||
|
m_modbus_type = 1;
|
||||||
|
m_modbus_ip = "127.0.0.1";
|
||||||
|
m_modbus_port = 502;
|
||||||
|
m_modbus_com = "COM1";
|
||||||
|
m_modbus_baund = 9600;
|
||||||
|
m_modbus_data = 1;
|
||||||
|
m_modbus_parity = 0;
|
||||||
|
m_modbus_stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusController::~ModbusController()
|
||||||
|
{
|
||||||
|
if (m_pSettings!=nullptr)
|
||||||
|
{
|
||||||
|
delete m_pSettings;
|
||||||
|
m_pSettings = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SlaveData* ptr : m_slaves)
|
||||||
|
{
|
||||||
|
delete ptr;
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_slaves.clear();
|
||||||
|
m_slaves.squeeze();
|
||||||
|
|
||||||
|
if (m_pModbusMaster != nullptr)
|
||||||
|
{
|
||||||
|
m_pModbusMaster->disconnectDevice();
|
||||||
|
delete m_pModbusMaster;
|
||||||
|
m_pModbusMaster = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusController::getConfiguration(QString iniFilePath)
|
||||||
|
{
|
||||||
|
m_pSettings = new QSettings(iniFilePath, QSettings::IniFormat);
|
||||||
|
|
||||||
|
m_version = m_pSettings->value("general/ver").toString();
|
||||||
|
m_modbus_type = m_pSettings->value("modbus/type").toInt();
|
||||||
|
|
||||||
|
m_modbus_ip = m_pSettings->value("modbus/ip").toString();
|
||||||
|
m_modbus_port = m_pSettings->value("modbus/port").toInt();
|
||||||
|
|
||||||
|
m_modbus_com = m_pSettings->value("modbus/com").toString();
|
||||||
|
m_modbus_baund = m_pSettings->value("modbus/baund").toInt();
|
||||||
|
m_modbus_data = m_pSettings->value("modbus/data").toInt();
|
||||||
|
m_modbus_parity = m_pSettings->value("modbus/parity").toInt();
|
||||||
|
m_modbus_stop = m_pSettings->value("modbus/stop").toInt();
|
||||||
|
|
||||||
|
QStringList slaves_list = m_pSettings->value("slaves/slave_id").toString().split(":", Qt::SkipEmptyParts);
|
||||||
|
|
||||||
|
int slave_id = 0;
|
||||||
|
foreach (const QString &str, slaves_list)
|
||||||
|
{
|
||||||
|
QString app = QString("slave_%1/function_code_counts").arg(slave_id);
|
||||||
|
|
||||||
|
int functions_count = m_pSettings->value(app).toInt();
|
||||||
|
for(int i=0; i<functions_count; i++)
|
||||||
|
{
|
||||||
|
QString app2 = QString("slave_%1_function_%2").arg(slave_id).arg(i);
|
||||||
|
|
||||||
|
#ifndef _DEBUG_VSPD_
|
||||||
|
unsigned short equipmentCode = m_pSettings->value(app2 + "/equipment_code").toInt();
|
||||||
|
equipmentCode = equipmentCode << 9;
|
||||||
|
equipmentCode &= 0xFE00; // 二进制掩码: 1111 1110 0000 0000(保留高7位)
|
||||||
|
|
||||||
|
QStringList sn_list = m_pSettings->value(app2 + "/serial_number").toString().split(":", Qt::SkipEmptyParts);
|
||||||
|
QStringList quantity_list = m_pSettings->value(app2 + "/read_quantity").toString().split(":", Qt::SkipEmptyParts);
|
||||||
|
|
||||||
|
assert(sn_list.size() == quantity_list.size());
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
foreach(const QString sn, sn_list)
|
||||||
|
{
|
||||||
|
SlaveData* pSlaveData = new SlaveData();
|
||||||
|
pSlaveData->id = str.toInt();
|
||||||
|
pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt();
|
||||||
|
pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt();
|
||||||
|
|
||||||
|
|
||||||
|
unsigned short serialNumber = sn.toInt();
|
||||||
|
|
||||||
|
serialNumber &= 0x01FF; // 二进制掩码: 0000 0001 1111 1111(保留低9位)
|
||||||
|
|
||||||
|
// 组合操作:高7位左移9位,低9位直接填充
|
||||||
|
unsigned short address = pSlaveData->start_address = (equipmentCode ) | serialNumber; // 关键位操作
|
||||||
|
|
||||||
|
pSlaveData->start_address = address;
|
||||||
|
pSlaveData->quantity = quantity_list.at(j).toInt();
|
||||||
|
m_slaves.push_back(pSlaveData);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SlaveData* pSlaveData = new SlaveData();
|
||||||
|
pSlaveData->id = str.toInt();
|
||||||
|
pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt();
|
||||||
|
pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt();
|
||||||
|
pSlaveData->quantity = m_pSettings->value(app2 + "/quantity").toInt();
|
||||||
|
pSlaveData->start_address = m_pSettings->value(app2 + "/start_addr").toInt();
|
||||||
|
m_slaves.push_back(pSlaveData);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
slave_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusController::Open()
|
||||||
|
{
|
||||||
|
if (m_modbus_type == 1)
|
||||||
|
{
|
||||||
|
return modbus_rtu();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return modbus_tcp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusController::modbus_rtu()
|
||||||
|
{
|
||||||
|
m_pModbusMaster = new QModbusRtuSerialClient();
|
||||||
|
m_pModbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, m_modbus_com); //串口号
|
||||||
|
m_pModbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_modbus_baund); //波特率
|
||||||
|
m_pModbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, m_modbus_parity); //无校验
|
||||||
|
m_pModbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_modbus_data); //数据位
|
||||||
|
m_pModbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_modbus_stop); //停止位
|
||||||
|
m_pModbusMaster->setTimeout(1000); // 设置超时2秒
|
||||||
|
|
||||||
|
return m_pModbusMaster->connectDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusController::modbus_tcp()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusController::Read()
|
||||||
|
{
|
||||||
|
//对每一个从机进行操作
|
||||||
|
for( SlaveData* sd : m_slaves)
|
||||||
|
{
|
||||||
|
//定义读写单元
|
||||||
|
QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, sd->start_address, sd->quantity);
|
||||||
|
//发送请求
|
||||||
|
auto* reply = m_pModbusMaster->sendReadRequest(readUnit, sd->id);
|
||||||
|
if (reply)
|
||||||
|
{
|
||||||
|
connect(reply, &QModbusReply::finished, this, &ModbusController::readRegisters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusController::readRegisters()
|
||||||
|
{
|
||||||
|
//接收信号
|
||||||
|
auto reply = qobject_cast<QModbusReply*>(sender());
|
||||||
|
if (!reply)
|
||||||
|
{
|
||||||
|
qDebug() << "无效回复";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//处理响应
|
||||||
|
const QModbusDataUnit unit = reply->result();
|
||||||
|
// 将数据值转换为字符串
|
||||||
|
QString dataStr;
|
||||||
|
for (int i = 0; i < unit.valueCount(); ++i)
|
||||||
|
{
|
||||||
|
dataStr += QString::number(unit.value(i)) + " ";
|
||||||
|
}
|
||||||
|
if (dataStr.size() != 0)
|
||||||
|
{
|
||||||
|
qDebug() << "读取成功";
|
||||||
|
qDebug() << dataStr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "无数据";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef MODBUSCONTROLLER_H
|
||||||
|
#define MODBUSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSerialPort>
|
||||||
|
#include <QSerialPortInfo>
|
||||||
|
#include <QtSerialBus>
|
||||||
|
#include <QModbusDataUnit>
|
||||||
|
#include <QModbusClient>
|
||||||
|
//#include <QModbusRtuSerialMaster>
|
||||||
|
#include <QtSerialBus/qmodbusrtuserialclient.h>
|
||||||
|
#include <QModbusReply>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
typedef struct _tagSlave
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
int function_code;
|
||||||
|
int start_address;
|
||||||
|
int quantity;
|
||||||
|
int interval;
|
||||||
|
} SlaveData;
|
||||||
|
|
||||||
|
class ModbusController :public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ModbusController();
|
||||||
|
~ModbusController();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void getConfiguration(QString iniFilePath);
|
||||||
|
bool Open();
|
||||||
|
bool Read();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool modbus_rtu();
|
||||||
|
bool modbus_tcp();
|
||||||
|
protected:
|
||||||
|
QString m_version;
|
||||||
|
int m_modbus_type;
|
||||||
|
QString m_modbus_ip;
|
||||||
|
int m_modbus_port;
|
||||||
|
QString m_modbus_com;
|
||||||
|
int m_modbus_baund;
|
||||||
|
int m_modbus_data;
|
||||||
|
int m_modbus_parity;
|
||||||
|
int m_modbus_stop;
|
||||||
|
|
||||||
|
QVector<SlaveData*> m_slaves;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QModbusRtuSerialClient* m_pModbusMaster; //创建modbus对象
|
||||||
|
QSettings* m_pSettings;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void readRegisters(); //读寄存器函数
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODBUSCONTROLLER_H
|
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
QT += core gui
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
libmodbus/modbus-data.c \
|
||||||
|
libmodbus/modbus-rtu.c \
|
||||||
|
libmodbus/modbus-tcp.c \
|
||||||
|
libmodbus/modbus.c \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
libmodbus/config.h \
|
||||||
|
libmodbus/modbus-private.h \
|
||||||
|
libmodbus/modbus-rtu-private.h \
|
||||||
|
libmodbus/modbus-rtu.h \
|
||||||
|
libmodbus/modbus-tcp-private.h \
|
||||||
|
libmodbus/modbus-tcp.h \
|
||||||
|
libmodbus/modbus-version.h \
|
||||||
|
libmodbus/modbus.h \
|
||||||
|
mainwindow.h \
|
||||||
|
slave_define.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
LIBS += -lws2_32
|
||||||
|
|
||||||
|
TRANSLATIONS += \
|
||||||
|
EmsShower_en_US.ts
|
||||||
|
CONFIG += lrelease
|
||||||
|
CONFIG += embed_translations
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
DISTFILES += \
|
||||||
|
emsshower.ini
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US"></TS>
|
|
@ -0,0 +1,67 @@
|
||||||
|
;通用设置
|
||||||
|
[version]
|
||||||
|
ver="1_0_0"
|
||||||
|
|
||||||
|
[modbus]
|
||||||
|
;1=RTU 0=TCP
|
||||||
|
type=1
|
||||||
|
|
||||||
|
;if type=1, ignore ip and port setting
|
||||||
|
ip="127.0.0.1"
|
||||||
|
port=502
|
||||||
|
|
||||||
|
com="COM20"
|
||||||
|
baund=9600
|
||||||
|
data=8
|
||||||
|
|
||||||
|
;0=None 1=Odd 2=Even
|
||||||
|
parity=0
|
||||||
|
|
||||||
|
stop=1
|
||||||
|
|
||||||
|
[slaves]
|
||||||
|
;如果从机数量多于1,则从机地址slave_id以:分隔(英文冒号)
|
||||||
|
slave_id="1"
|
||||||
|
|
||||||
|
;每一个从机的配置
|
||||||
|
;由两个节组成:slave_%d 和 slave_%d_function_%d
|
||||||
|
|
||||||
|
[slave_1]
|
||||||
|
;支持的功能码数量
|
||||||
|
function_code_counts=2
|
||||||
|
|
||||||
|
[slave_1_function_0]
|
||||||
|
;功能码
|
||||||
|
function_code=3
|
||||||
|
|
||||||
|
;起始地址,10进制
|
||||||
|
start_addr=40000
|
||||||
|
|
||||||
|
;读取数量
|
||||||
|
quantity=9
|
||||||
|
|
||||||
|
;扫描间隔,单位ms
|
||||||
|
interval=1000
|
||||||
|
|
||||||
|
;serial_number与read_quantity的元素数量要一样
|
||||||
|
equipment_code=81
|
||||||
|
serial_number="0:1:2:3:4:5:6:7:8:9:10:11:12:13:14"
|
||||||
|
read_quantity="1:1:1:1:1:1:1:1:1:1:1:1:1:1:1"
|
||||||
|
|
||||||
|
[slave_1_function_1]
|
||||||
|
;功能码
|
||||||
|
function_code=3
|
||||||
|
|
||||||
|
;起始地址,10进制
|
||||||
|
start_addr=40009
|
||||||
|
|
||||||
|
;读取数量
|
||||||
|
quantity=6
|
||||||
|
|
||||||
|
;扫描间隔,单位ms
|
||||||
|
interval=1000
|
||||||
|
|
||||||
|
;serial_number与read_quantity的元素数量要一样
|
||||||
|
equipment_code=81
|
||||||
|
serial_number="0:1:2:3:4:5:6:7:8:9:10:11:12:13:14"
|
||||||
|
read_quantity="1:1:1:1:1:1:1:1:1:1:1:1:1:1:1"
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* config.h. Generated from config.h.in by configure. */
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||||
|
/* #undef HAVE_ARPA_INET_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `TIOCSRS485', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
/* #undef HAVE_DECL_TIOCSRS485 */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `__CYGWIN__', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
/* #undef HAVE_DECL___CYGWIN__ */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
/* #undef HAVE_DLFCN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#define HAVE_ERRNO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||||
|
#define HAVE_FCNTL_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `fork' function. */
|
||||||
|
/* #undef HAVE_FORK */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||||
|
/* #undef HAVE_GETADDRINFO */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
|
/* #undef HAVE_GETTIMEOFDAY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <limits.h> header file. */
|
||||||
|
#define HAVE_LIMITS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/serial.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_SERIAL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memset' function. */
|
||||||
|
#define HAVE_MEMSET 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netdb.h> header file. */
|
||||||
|
/* #undef HAVE_NETDB_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||||
|
/* #undef HAVE_NETINET_IN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/tcp.h> header file. */
|
||||||
|
/* #undef HAVE_NETINET_TCP_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `select' function. */
|
||||||
|
/* #undef HAVE_SELECT */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' function. */
|
||||||
|
/* #undef HAVE_SOCKET */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strerror' function. */
|
||||||
|
#define HAVE_STRERROR 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
/* #undef HAVE_STRINGS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strlcpy' function. */
|
||||||
|
/* #undef HAVE_STRLCPY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_IOCTL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_SOCKET_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_TIME_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <termios.h> header file. */
|
||||||
|
/* #undef HAVE_TERMIOS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <time.h> header file. */
|
||||||
|
#define HAVE_TIME_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
/* #undef HAVE_UNISTD_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `vfork' function. */
|
||||||
|
/* #undef HAVE_VFORK */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <vfork.h> header file. */
|
||||||
|
/* #undef HAVE_VFORK_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <winsock2.h> header file. */
|
||||||
|
#define HAVE_WINSOCK2_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if `fork' works. */
|
||||||
|
/* #undef HAVE_WORKING_FORK */
|
||||||
|
|
||||||
|
/* Define to 1 if `vfork' works. */
|
||||||
|
/* #undef HAVE_WORKING_VFORK */
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
/* #undef LT_OBJDIR */
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "https://github.com/stephane/libmodbus/issues"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "libmodbus 3.1.11"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "3.1.11"
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||||
|
/* #undef TIME_WITH_SYS_TIME */
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#define VERSION "3.1.11"
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
/* #undef const */
|
||||||
|
|
||||||
|
/* Define to `int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef pid_t */
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef size_t */
|
||||||
|
|
||||||
|
/* Define as `fork' if `vfork' does not work. */
|
||||||
|
#define vfork fork
|
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include <winsock2.h>
|
||||||
|
#else
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libmodbus/config.h"
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/* Sets many bits from a single byte value (all 8 bits of the byte value are
|
||||||
|
set) */
|
||||||
|
void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
dest[idx + i] = (value & (1 << i)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets many bits from a table of bytes (only the bits between idx and
|
||||||
|
idx + nb_bits are set) */
|
||||||
|
void modbus_set_bits_from_bytes(uint8_t *dest,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
const uint8_t *tab_byte)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int shift = 0;
|
||||||
|
|
||||||
|
for (i = idx; i < idx + nb_bits; i++) {
|
||||||
|
dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0;
|
||||||
|
/* gcc doesn't like: shift = (++shift) % 8; */
|
||||||
|
shift++;
|
||||||
|
shift %= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the byte value from many bits.
|
||||||
|
To obtain a full byte, set nb_bits to 8. */
|
||||||
|
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint8_t value = 0;
|
||||||
|
|
||||||
|
if (nb_bits > 8) {
|
||||||
|
/* Assert is ignored if NDEBUG is set */
|
||||||
|
assert(nb_bits < 8);
|
||||||
|
nb_bits = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nb_bits; i++) {
|
||||||
|
value |= (src[idx + i] << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */
|
||||||
|
float modbus_get_float_abcd(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// Mind: src contains 16-bit numbers in processor-endianness, hence
|
||||||
|
// we use shift operations and do not access memory directly
|
||||||
|
a = (src[0] >> 8) & 0xFF; // high byte of first word
|
||||||
|
b = (src[0] >> 0) & 0xFF; // low byte of first word
|
||||||
|
c = (src[1] >> 8) & 0xFF; // high byte of second word
|
||||||
|
d = (src[1] >> 0) & 0xFF; // low byte of second word
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */
|
||||||
|
float modbus_get_float_dcba(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: dcba
|
||||||
|
d = (src[0] >> 8) & 0xFF;
|
||||||
|
c = (src[0] >> 0) & 0xFF;
|
||||||
|
b = (src[1] >> 8) & 0xFF;
|
||||||
|
a = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */
|
||||||
|
float modbus_get_float_badc(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: badc
|
||||||
|
b = (src[0] >> 8) & 0xFF;
|
||||||
|
a = (src[0] >> 0) & 0xFF;
|
||||||
|
d = (src[1] >> 8) & 0xFF;
|
||||||
|
c = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */
|
||||||
|
float modbus_get_float_cdab(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: cdab
|
||||||
|
c = (src[0] >> 8) & 0xFF;
|
||||||
|
d = (src[0] >> 0) & 0xFF;
|
||||||
|
a = (src[1] >> 8) & 0xFF;
|
||||||
|
b = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */
|
||||||
|
float modbus_get_float(const uint16_t *src)
|
||||||
|
{
|
||||||
|
return modbus_get_float_cdab(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
|
||||||
|
void modbus_set_float_abcd(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
// The straight-forward type conversion won't work because of type-punned pointer aliasing warning
|
||||||
|
// uint32_t i = *(uint32_t*)(&f);
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (a << 8) | b;
|
||||||
|
dest[1] = (c << 8) | d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
|
||||||
|
void modbus_set_float_dcba(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (d << 8) | c;
|
||||||
|
dest[1] = (b << 8) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
|
||||||
|
void modbus_set_float_badc(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (b << 8) | a;
|
||||||
|
dest[1] = (d << 8) | c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
|
||||||
|
void modbus_set_float_cdab(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (c << 8) | d;
|
||||||
|
dest[1] = (a << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
|
||||||
|
void modbus_set_float(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
modbus_set_float_cdab(f, dest);
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_PRIVATE_H
|
||||||
|
#define MODBUS_PRIVATE_H
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
# include <sys/time.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
# include <time.h>
|
||||||
|
typedef int ssize_t;
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
#include "libmodbus/config.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* It's not really the minimal length (the real one is report slave ID
|
||||||
|
* in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
|
||||||
|
* communications to read many values or write a single one.
|
||||||
|
* Maximum between :
|
||||||
|
* - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
|
||||||
|
* - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
|
||||||
|
*/
|
||||||
|
#define _MIN_REQ_LENGTH 12
|
||||||
|
|
||||||
|
#define _REPORT_SLAVE_ID 180
|
||||||
|
|
||||||
|
#define _MODBUS_EXCEPTION_RSP_LENGTH 5
|
||||||
|
|
||||||
|
/* Timeouts in microsecond (0.5 s) */
|
||||||
|
#define _RESPONSE_TIMEOUT 500000
|
||||||
|
#define _BYTE_TIMEOUT 500000
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
_MODBUS_BACKEND_TYPE_RTU = 0,
|
||||||
|
_MODBUS_BACKEND_TYPE_TCP
|
||||||
|
} modbus_backend_type_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ---------- Request Indication ----------
|
||||||
|
* | Client | ---------------------->| Server |
|
||||||
|
* ---------- Confirmation Response ----------
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/* Request message on the server side */
|
||||||
|
MSG_INDICATION,
|
||||||
|
/* Request message on the client side */
|
||||||
|
MSG_CONFIRMATION
|
||||||
|
} msg_type_t;
|
||||||
|
|
||||||
|
/* This structure reduces the number of params in functions and so
|
||||||
|
* optimizes the speed of execution (~ 37%). */
|
||||||
|
typedef struct _sft
|
||||||
|
{
|
||||||
|
int slave;
|
||||||
|
int function;
|
||||||
|
int t_id;
|
||||||
|
} sft_t;
|
||||||
|
|
||||||
|
typedef struct _modbus_backend
|
||||||
|
{
|
||||||
|
unsigned int backend_type;
|
||||||
|
unsigned int header_length;
|
||||||
|
unsigned int checksum_length;
|
||||||
|
unsigned int max_adu_length;
|
||||||
|
int (*set_slave)(modbus_t *ctx, int slave);
|
||||||
|
int (*build_request_basis)(
|
||||||
|
modbus_t *ctx, int function, int addr, int nb, uint8_t *req);
|
||||||
|
int (*build_response_basis)(sft_t *sft, uint8_t *rsp);
|
||||||
|
int (*get_response_tid)(const uint8_t *req);
|
||||||
|
int (*send_msg_pre)(uint8_t *req, int req_length);
|
||||||
|
ssize_t (*send)(modbus_t *ctx, const uint8_t *req, int req_length);
|
||||||
|
int (*receive)(modbus_t *ctx, uint8_t *req);
|
||||||
|
ssize_t (*recv)(modbus_t *ctx, uint8_t *rsp, int rsp_length);
|
||||||
|
int (*check_integrity)(modbus_t *ctx, uint8_t *msg, const int msg_length);
|
||||||
|
int (*pre_check_confirmation)(modbus_t *ctx,
|
||||||
|
const uint8_t *req,
|
||||||
|
const uint8_t *rsp,
|
||||||
|
int rsp_length);
|
||||||
|
int (*connect)(modbus_t *ctx);
|
||||||
|
unsigned int (*is_connected)(modbus_t *ctx);
|
||||||
|
void (*close)(modbus_t *ctx);
|
||||||
|
int (*flush)(modbus_t *ctx);
|
||||||
|
int (*select)(modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
|
||||||
|
void (*free)(modbus_t *ctx);
|
||||||
|
} modbus_backend_t;
|
||||||
|
|
||||||
|
struct _modbus
|
||||||
|
{
|
||||||
|
/* Slave address */
|
||||||
|
int slave;
|
||||||
|
/* Socket or file descriptor */
|
||||||
|
int s;
|
||||||
|
int debug;
|
||||||
|
int error_recovery;
|
||||||
|
int quirks;
|
||||||
|
struct timeval response_timeout;
|
||||||
|
struct timeval byte_timeout;
|
||||||
|
struct timeval indication_timeout;
|
||||||
|
const modbus_backend_t *backend;
|
||||||
|
void *backend_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void _modbus_init_common(modbus_t *ctx);
|
||||||
|
void _error_print(modbus_t *ctx, const char *context);
|
||||||
|
int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
|
||||||
|
|
||||||
|
#ifndef HAVE_STRLCPY
|
||||||
|
size_t strlcpy(char *dest, const char *src, size_t dest_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_PRIVATE_H */
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_RTU_PRIVATE_H
|
||||||
|
#define MODBUS_RTU_PRIVATE_H
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <stdint.h>
|
||||||
|
#else
|
||||||
|
#include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _MODBUS_RTU_HEADER_LENGTH 1
|
||||||
|
#define _MODBUS_RTU_PRESET_REQ_LENGTH 6
|
||||||
|
#define _MODBUS_RTU_PRESET_RSP_LENGTH 2
|
||||||
|
|
||||||
|
#define _MODBUS_RTU_CHECKSUM_LENGTH 2
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if !defined(ENOTSUP)
|
||||||
|
#define ENOTSUP WSAEOPNOTSUPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* WIN32: struct containing serial handle and a receive buffer */
|
||||||
|
#define PY_BUF_SIZE 512
|
||||||
|
|
||||||
|
struct win32_ser {
|
||||||
|
/* File handle */
|
||||||
|
HANDLE fd;
|
||||||
|
/* Receive buffer */
|
||||||
|
uint8_t buf[PY_BUF_SIZE];
|
||||||
|
/* Received chars */
|
||||||
|
DWORD n_bytes;
|
||||||
|
};
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
typedef struct _modbus_rtu {
|
||||||
|
/* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */
|
||||||
|
char *device;
|
||||||
|
/* Bauds: 9600, 19200, 57600, 115200, etc */
|
||||||
|
int baud;
|
||||||
|
/* Data bit */
|
||||||
|
uint8_t data_bit;
|
||||||
|
/* Stop bit */
|
||||||
|
uint8_t stop_bit;
|
||||||
|
/* Parity: 'N', 'O', 'E' */
|
||||||
|
char parity;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
struct win32_ser w_ser;
|
||||||
|
DCB old_dcb;
|
||||||
|
#else
|
||||||
|
/* Save old termios settings */
|
||||||
|
struct termios old_tios;
|
||||||
|
#endif
|
||||||
|
#if HAVE_DECL_TIOCSRS485
|
||||||
|
int serial_mode;
|
||||||
|
#endif
|
||||||
|
#if HAVE_DECL_TIOCM_RTS
|
||||||
|
int rts;
|
||||||
|
int rts_delay;
|
||||||
|
int onebyte_time;
|
||||||
|
void (*set_rts)(modbus_t *ctx, int on);
|
||||||
|
#endif
|
||||||
|
/* To handle many slaves on the same link */
|
||||||
|
int confirmation_to_ignore;
|
||||||
|
} modbus_rtu_t;
|
||||||
|
|
||||||
|
#endif /* MODBUS_RTU_PRIVATE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_RTU_H
|
||||||
|
#define MODBUS_RTU_H
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||||
|
* RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
|
||||||
|
*/
|
||||||
|
#define MODBUS_RTU_MAX_ADU_LENGTH 256
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *
|
||||||
|
modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
|
||||||
|
|
||||||
|
#define MODBUS_RTU_RS232 0
|
||||||
|
#define MODBUS_RTU_RS485 1
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
|
||||||
|
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
|
||||||
|
|
||||||
|
#define MODBUS_RTU_RTS_NONE 0
|
||||||
|
#define MODBUS_RTU_RTS_UP 1
|
||||||
|
#define MODBUS_RTU_RTS_DOWN 2
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
|
||||||
|
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx,
|
||||||
|
void (*set_rts)(modbus_t *ctx, int on));
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
|
||||||
|
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_RTU_H */
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_TCP_PRIVATE_H
|
||||||
|
#define MODBUS_TCP_PRIVATE_H
|
||||||
|
|
||||||
|
#define _MODBUS_TCP_HEADER_LENGTH 7
|
||||||
|
#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
|
||||||
|
#define _MODBUS_TCP_PRESET_RSP_LENGTH 8
|
||||||
|
|
||||||
|
#define _MODBUS_TCP_CHECKSUM_LENGTH 0
|
||||||
|
|
||||||
|
/* In both structures, the transaction ID must be placed on first position
|
||||||
|
to have a quick access not dependent of the TCP backend */
|
||||||
|
typedef struct _modbus_tcp {
|
||||||
|
/* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
|
||||||
|
(page 23/46):
|
||||||
|
The transaction identifier is used to associate the future response
|
||||||
|
with the request. This identifier is unique on each TCP connection. */
|
||||||
|
uint16_t t_id;
|
||||||
|
/* TCP port */
|
||||||
|
int port;
|
||||||
|
/* IP address */
|
||||||
|
char ip[16];
|
||||||
|
} modbus_tcp_t;
|
||||||
|
|
||||||
|
typedef struct _modbus_tcp_pi {
|
||||||
|
/* Transaction ID */
|
||||||
|
uint16_t t_id;
|
||||||
|
/* TCP port */
|
||||||
|
int port;
|
||||||
|
/* Node */
|
||||||
|
char *node;
|
||||||
|
/* Service */
|
||||||
|
char *service;
|
||||||
|
} modbus_tcp_pi_t;
|
||||||
|
|
||||||
|
#endif /* MODBUS_TCP_PRIVATE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_TCP_H
|
||||||
|
#define MODBUS_TCP_H
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
/* Win32 with MinGW, supplement to <errno.h> */
|
||||||
|
#include <winsock2.h>
|
||||||
|
#if !defined(ECONNRESET)
|
||||||
|
#define ECONNRESET WSAECONNRESET
|
||||||
|
#endif
|
||||||
|
#if !defined(ECONNREFUSED)
|
||||||
|
#define ECONNREFUSED WSAECONNREFUSED
|
||||||
|
#endif
|
||||||
|
#if !defined(ETIMEDOUT)
|
||||||
|
#define ETIMEDOUT WSAETIMEDOUT
|
||||||
|
#endif
|
||||||
|
#if !defined(ENOPROTOOPT)
|
||||||
|
#define ENOPROTOOPT WSAENOPROTOOPT
|
||||||
|
#endif
|
||||||
|
#if !defined(EINPROGRESS)
|
||||||
|
#define EINPROGRESS WSAEINPROGRESS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MODBUS_TCP_DEFAULT_PORT 502
|
||||||
|
#define MODBUS_TCP_SLAVE 0xFF
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||||
|
* TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
|
||||||
|
*/
|
||||||
|
#define MODBUS_TCP_MAX_ADU_LENGTH 260
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port);
|
||||||
|
MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
|
||||||
|
MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service);
|
||||||
|
MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
|
||||||
|
MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_TCP_H */
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_VERSION_H
|
||||||
|
#define MODBUS_VERSION_H
|
||||||
|
|
||||||
|
/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MAJOR (3)
|
||||||
|
|
||||||
|
/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MINOR (1)
|
||||||
|
|
||||||
|
/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MICRO (11)
|
||||||
|
|
||||||
|
/* The full version, like 1.2.3 */
|
||||||
|
#define LIBMODBUS_VERSION 3.1.11
|
||||||
|
|
||||||
|
/* The full version, in string form (suited for string concatenation)
|
||||||
|
*/
|
||||||
|
#define LIBMODBUS_VERSION_STRING "3.1.11"
|
||||||
|
|
||||||
|
/* Numerically encoded version, eg. v1.2.3 is 0x010203 */
|
||||||
|
#define LIBMODBUS_VERSION_HEX \
|
||||||
|
((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \
|
||||||
|
(LIBMODBUS_VERSION_MICRO << 0))
|
||||||
|
|
||||||
|
/* Evaluates to True if the version is greater than @major, @minor and @micro
|
||||||
|
*/
|
||||||
|
#define LIBMODBUS_VERSION_CHECK(major, minor, micro) \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR > (major) || \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \
|
||||||
|
LIBMODBUS_VERSION_MICRO >= (micro)))
|
||||||
|
|
||||||
|
#endif /* MODBUS_VERSION_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,350 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_H
|
||||||
|
#define MODBUS_H
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/* Add this for macros that defined unix flavor */
|
||||||
|
#if (defined(__unix__) || defined(unix)) && !defined(USG)
|
||||||
|
# include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "modbus-version.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# if defined(DLLBUILD)
|
||||||
|
/* define DLLBUILD when building the DLL */
|
||||||
|
# define MODBUS_API __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define MODBUS_API __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define MODBUS_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//添加这几行是因为使用源码编译,而不是使用dll/lib方式,
|
||||||
|
#ifdef MODBUS_API
|
||||||
|
#undef MODBUS_API
|
||||||
|
#endif
|
||||||
|
#define MODBUS_API
|
||||||
|
//添加完成
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define MODBUS_BEGIN_DECLS extern "C" {
|
||||||
|
# define MODBUS_END_DECLS }
|
||||||
|
#else
|
||||||
|
# define MODBUS_BEGIN_DECLS
|
||||||
|
# define MODBUS_END_DECLS
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OFF
|
||||||
|
#define OFF 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ON
|
||||||
|
#define ON 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Modbus function codes */
|
||||||
|
#define MODBUS_FC_READ_COILS 0x01
|
||||||
|
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
|
||||||
|
#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
|
||||||
|
#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
|
||||||
|
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
|
||||||
|
#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
|
||||||
|
#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
|
||||||
|
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
|
||||||
|
#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
|
||||||
|
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
|
||||||
|
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
|
||||||
|
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
|
||||||
|
|
||||||
|
#define MODBUS_BROADCAST_ADDRESS 0
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
|
||||||
|
* Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
|
||||||
|
* (chapter 6 section 11 page 29)
|
||||||
|
* Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_READ_BITS 2000
|
||||||
|
#define MODBUS_MAX_WRITE_BITS 1968
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
|
||||||
|
* Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
|
||||||
|
* (chapter 6 section 12 page 31)
|
||||||
|
* Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
|
||||||
|
* (chapter 6 section 17 page 38)
|
||||||
|
* Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_READ_REGISTERS 125
|
||||||
|
#define MODBUS_MAX_WRITE_REGISTERS 123
|
||||||
|
#define MODBUS_MAX_WR_WRITE_REGISTERS 121
|
||||||
|
#define MODBUS_MAX_WR_READ_REGISTERS 125
|
||||||
|
|
||||||
|
/* The size of the MODBUS PDU is limited by the size constraint inherited from
|
||||||
|
* the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256
|
||||||
|
* bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server
|
||||||
|
* address (1 byte) - CRC (2 bytes) = 253 bytes.
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_PDU_LENGTH 253
|
||||||
|
|
||||||
|
/* Consequently:
|
||||||
|
* - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256
|
||||||
|
* bytes.
|
||||||
|
* - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.
|
||||||
|
* so the maximum of both backend in 260 bytes. This size can used to allocate
|
||||||
|
* an array of bytes to store responses and it will be compatible with the two
|
||||||
|
* backends.
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_ADU_LENGTH 260
|
||||||
|
|
||||||
|
/* Random number to avoid errno conflicts */
|
||||||
|
#define MODBUS_ENOBASE 112345678
|
||||||
|
|
||||||
|
/* Protocol exceptions */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01,
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
|
||||||
|
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
|
||||||
|
MODBUS_EXCEPTION_ACKNOWLEDGE,
|
||||||
|
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
|
||||||
|
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
|
||||||
|
MODBUS_EXCEPTION_MEMORY_PARITY,
|
||||||
|
MODBUS_EXCEPTION_NOT_DEFINED,
|
||||||
|
MODBUS_EXCEPTION_GATEWAY_PATH,
|
||||||
|
MODBUS_EXCEPTION_GATEWAY_TARGET,
|
||||||
|
MODBUS_EXCEPTION_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
|
||||||
|
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
|
||||||
|
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
|
||||||
|
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
|
||||||
|
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
|
||||||
|
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
|
||||||
|
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
|
||||||
|
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
|
||||||
|
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
|
||||||
|
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
|
||||||
|
|
||||||
|
/* Native libmodbus error codes */
|
||||||
|
#define EMBBADCRC (EMBXGTAR + 1)
|
||||||
|
#define EMBBADDATA (EMBXGTAR + 2)
|
||||||
|
#define EMBBADEXC (EMBXGTAR + 3)
|
||||||
|
#define EMBUNKEXC (EMBXGTAR + 4)
|
||||||
|
#define EMBMDATA (EMBXGTAR + 5)
|
||||||
|
#define EMBBADSLAVE (EMBXGTAR + 6)
|
||||||
|
|
||||||
|
extern const unsigned int libmodbus_version_major;
|
||||||
|
extern const unsigned int libmodbus_version_minor;
|
||||||
|
extern const unsigned int libmodbus_version_micro;
|
||||||
|
|
||||||
|
typedef struct _modbus modbus_t;
|
||||||
|
|
||||||
|
/*! Memory layout in tab_xxx arrays is processor-endianness.
|
||||||
|
When receiving modbus data, it is converted to processor-endianness,
|
||||||
|
see read_registers().
|
||||||
|
*/
|
||||||
|
typedef struct _modbus_mapping_t
|
||||||
|
{
|
||||||
|
int nb_bits;
|
||||||
|
int start_bits;
|
||||||
|
int nb_input_bits;
|
||||||
|
int start_input_bits;
|
||||||
|
int nb_input_registers;
|
||||||
|
int start_input_registers;
|
||||||
|
int nb_registers;
|
||||||
|
int start_registers;
|
||||||
|
uint8_t *tab_bits;
|
||||||
|
uint8_t *tab_input_bits;
|
||||||
|
uint16_t *tab_input_registers;
|
||||||
|
uint16_t *tab_registers;
|
||||||
|
} modbus_mapping_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MODBUS_ERROR_RECOVERY_NONE = 0,
|
||||||
|
MODBUS_ERROR_RECOVERY_LINK = (1 << 1),
|
||||||
|
MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2)
|
||||||
|
} modbus_error_recovery_mode;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MODBUS_QUIRK_NONE = 0,
|
||||||
|
MODBUS_QUIRK_MAX_SLAVE = (1 << 1),
|
||||||
|
MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2),
|
||||||
|
MODBUS_QUIRK_ALL = 0xFF
|
||||||
|
} modbus_quirks;
|
||||||
|
|
||||||
|
MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave);
|
||||||
|
MODBUS_API int modbus_get_slave(modbus_t *ctx);
|
||||||
|
MODBUS_API int modbus_set_error_recovery(modbus_t *ctx,
|
||||||
|
modbus_error_recovery_mode error_recovery);
|
||||||
|
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
|
||||||
|
MODBUS_API int modbus_get_socket(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_connect(modbus_t *ctx);
|
||||||
|
MODBUS_API void modbus_close(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API void modbus_free(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_flush(modbus_t *ctx);
|
||||||
|
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
|
||||||
|
|
||||||
|
MODBUS_API const char *modbus_strerror(int errnum);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||||
|
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||||
|
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||||
|
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
|
||||||
|
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
|
||||||
|
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
|
||||||
|
MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx,
|
||||||
|
int write_addr,
|
||||||
|
int write_nb,
|
||||||
|
const uint16_t *src,
|
||||||
|
int read_addr,
|
||||||
|
int read_nb,
|
||||||
|
uint16_t *dest);
|
||||||
|
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
|
||||||
|
|
||||||
|
MODBUS_API modbus_mapping_t *
|
||||||
|
modbus_mapping_new_start_address(unsigned int start_bits,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
unsigned int start_input_bits,
|
||||||
|
unsigned int nb_input_bits,
|
||||||
|
unsigned int start_registers,
|
||||||
|
unsigned int nb_registers,
|
||||||
|
unsigned int start_input_registers,
|
||||||
|
unsigned int nb_input_registers);
|
||||||
|
|
||||||
|
MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits,
|
||||||
|
int nb_input_bits,
|
||||||
|
int nb_registers,
|
||||||
|
int nb_input_registers);
|
||||||
|
MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_send_raw_request_tid(modbus_t *ctx,
|
||||||
|
const uint8_t *raw_req,
|
||||||
|
int raw_req_length,
|
||||||
|
int tid);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_reply(modbus_t *ctx,
|
||||||
|
const uint8_t *req,
|
||||||
|
int req_length,
|
||||||
|
modbus_mapping_t *mb_mapping);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code);
|
||||||
|
MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||||||
|
MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTILS FUNCTIONS
|
||||||
|
**/
|
||||||
|
|
||||||
|
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
|
||||||
|
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
|
||||||
|
#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
|
||||||
|
(((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \
|
||||||
|
((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3])
|
||||||
|
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \
|
||||||
|
(((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1])
|
||||||
|
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \
|
||||||
|
(((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1])
|
||||||
|
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
|
||||||
|
do { \
|
||||||
|
((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \
|
||||||
|
((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
|
||||||
|
do { \
|
||||||
|
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
|
||||||
|
do { \
|
||||||
|
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
|
||||||
|
MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
const uint8_t *tab_byte);
|
||||||
|
MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits);
|
||||||
|
MODBUS_API float modbus_get_float(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
|
||||||
|
|
||||||
|
MODBUS_API void modbus_set_float(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
|
||||||
|
|
||||||
|
#include "modbus-rtu.h"
|
||||||
|
#include "modbus-tcp.h"
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_H */
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
const QStringList uiLanguages = QLocale::system().uiLanguages();
|
||||||
|
for (const QString &locale : uiLanguages) {
|
||||||
|
const QString baseName = "EmsShower_" + QLocale(locale).name();
|
||||||
|
if (translator.load(":/i18n/" + baseName)) {
|
||||||
|
a.installTranslator(&translator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,359 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "libmodbus/modbus.h"
|
||||||
|
|
||||||
|
#define _DEBUG_VSPD_
|
||||||
|
|
||||||
|
#define TH08D_TEMPERATURE_EQUIPMENT_81_00_09 40000
|
||||||
|
#define TH08D_TEMPERATURE_EQUIPMENT_81_09_06 40009
|
||||||
|
|
||||||
|
// 处理数组的槽函数
|
||||||
|
void DecodeWorker::processArray(const QVector<uint16_t>& array,int slave_id,int start_addr,int quantity,DeviceData* pData)
|
||||||
|
{
|
||||||
|
switch (start_addr)
|
||||||
|
{
|
||||||
|
case TH08D_TEMPERATURE_EQUIPMENT_81_00_09:
|
||||||
|
{
|
||||||
|
assert(pData->m_device_type == 81);
|
||||||
|
|
||||||
|
TemperatureData* pTemperature = (TemperatureData*)pData;
|
||||||
|
|
||||||
|
uint16_t value;
|
||||||
|
int index = 0;
|
||||||
|
pTemperature->m_device_online_state = array[index++]; //设备通讯状态
|
||||||
|
pTemperature->TempValue = (float)array[index++] * 1.0f / 10.f; //温度
|
||||||
|
pTemperature->HumidityValue = (float)array[index++] * 1.0f / 10.f; //湿度
|
||||||
|
pTemperature->DewPointValue = (float)array[index++] * 1.0f; //露点
|
||||||
|
value = array[index++]; //跳过温度偏移量
|
||||||
|
value = array[index++]; //跳过湿度偏移量
|
||||||
|
pTemperature->DO= array[index++]; //DO
|
||||||
|
pTemperature->DI1 = array[index++]; //DI1
|
||||||
|
pTemperature->DI2 = array[index++]; //DI2
|
||||||
|
pTemperature->bDecodeAlarm = false;
|
||||||
|
pTemperature->bDecodeTemp = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TH08D_TEMPERATURE_EQUIPMENT_81_09_06:
|
||||||
|
{
|
||||||
|
assert(pData->m_device_type == 81);
|
||||||
|
|
||||||
|
TemperatureData* pTemperature = (TemperatureData*)pData;
|
||||||
|
pTemperature->bDecodeAlarm = true;
|
||||||
|
pTemperature->bDecodeTemp = false;
|
||||||
|
|
||||||
|
uint16_t value;
|
||||||
|
int index = 0;
|
||||||
|
float temp_threshold = (float)array[index++] * 1.0f / 10.f; //温度阈值
|
||||||
|
float temp_offset = (float)array[index++] * 1.0f / 10.f; //温控偏移量
|
||||||
|
value = array[index++]; //跳过设备状态
|
||||||
|
pTemperature->TempHighAlarm = array[index++]; //高温告警
|
||||||
|
pTemperature->HumidityHighAlarm = array[index++]; //高湿度告警
|
||||||
|
pTemperature->TempLowAlarm = array[index++]; //低温告警
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//下面是模拟发送进度
|
||||||
|
//for (int i = 0; i < array.size(); ++i)
|
||||||
|
//{
|
||||||
|
// qDebug() << "Worker处理索引:" << i << "值:" << array[i]
|
||||||
|
// << "线程:" << QThread::currentThread();
|
||||||
|
// //QThread::msleep(100); // 模拟耗时处理
|
||||||
|
// emit progress(i);
|
||||||
|
//}
|
||||||
|
|
||||||
|
qDebug() << "Worker thread finished";
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
,m_pModbus(nullptr)
|
||||||
|
,m_pSettings(nullptr)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_version= "1.0.0";
|
||||||
|
m_modbus_type = 1;
|
||||||
|
m_modbus_ip = "127.0.0.1";
|
||||||
|
m_modbus_port = 502;
|
||||||
|
m_modbus_com = "COM1";
|
||||||
|
m_modbus_baund = 9600;
|
||||||
|
m_modbus_data = 1;
|
||||||
|
m_modbus_parity = 0;
|
||||||
|
m_modbus_stop = 1;
|
||||||
|
m_modbus_slave_id = 1;
|
||||||
|
|
||||||
|
InitializeUI();
|
||||||
|
InitializeModbus();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
|
||||||
|
if (m_pModbus)
|
||||||
|
{
|
||||||
|
modbus_close(m_pModbus);
|
||||||
|
modbus_free(m_pModbus);
|
||||||
|
m_pModbus = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SlaveItem* ptr : m_SlaveData)
|
||||||
|
{
|
||||||
|
delete ptr;
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
m_SlaveData.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::getConfiguration(QString iniFilePath)
|
||||||
|
{
|
||||||
|
m_pSettings = new QSettings(iniFilePath, QSettings::IniFormat);
|
||||||
|
|
||||||
|
m_version = m_pSettings->value("version/ver").toString();
|
||||||
|
m_modbus_type = m_pSettings->value("modbus/type").toInt();
|
||||||
|
|
||||||
|
m_modbus_ip = m_pSettings->value("modbus/ip").toString();
|
||||||
|
m_modbus_port = m_pSettings->value("modbus/port").toInt();
|
||||||
|
|
||||||
|
m_modbus_com = m_pSettings->value("modbus/com").toString();
|
||||||
|
m_modbus_baund = m_pSettings->value("modbus/baund").toInt();
|
||||||
|
m_modbus_data = m_pSettings->value("modbus/data").toInt();
|
||||||
|
m_modbus_parity = m_pSettings->value("modbus/parity").toInt();
|
||||||
|
m_modbus_stop = m_pSettings->value("modbus/stop").toInt();
|
||||||
|
|
||||||
|
m_modbus_slave_id = m_pSettings->value("slaves/slave_id").toInt();
|
||||||
|
|
||||||
|
QString app = QString("slave_%1/function_code_counts").arg(m_modbus_slave_id);
|
||||||
|
|
||||||
|
int functions_count = m_pSettings->value(app).toInt();
|
||||||
|
for(int i=0; i<functions_count; i++)
|
||||||
|
{
|
||||||
|
QString app2 = QString("slave_%1_function_%2").arg(m_modbus_slave_id).arg(i);
|
||||||
|
|
||||||
|
#ifndef _DEBUG_VSPD_
|
||||||
|
unsigned short equipmentCode = m_pSettings->value(app2 + "/equipment_code").toInt();
|
||||||
|
equipmentCode = equipmentCode << 9;
|
||||||
|
equipmentCode &= 0xFE00; // 二进制掩码: 1111 1110 0000 0000(保留高7位)
|
||||||
|
|
||||||
|
QStringList sn_list = m_pSettings->value(app2 + "/serial_number").toString().split(":", Qt::SkipEmptyParts);
|
||||||
|
QStringList quantity_list = m_pSettings->value(app2 + "/read_quantity").toString().split(":", Qt::SkipEmptyParts);
|
||||||
|
|
||||||
|
assert(sn_list.size() == quantity_list.size());
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
foreach(const QString sn, sn_list)
|
||||||
|
{
|
||||||
|
SlaveItem* pSlaveData = new SlaveItem();
|
||||||
|
pSlaveData->id = m_modbus_slave_id;
|
||||||
|
pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt();
|
||||||
|
pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt();
|
||||||
|
|
||||||
|
unsigned short serialNumber = sn.toInt();
|
||||||
|
|
||||||
|
serialNumber &= 0x01FF; // 二进制掩码: 0000 0001 1111 1111(保留低9位)
|
||||||
|
|
||||||
|
// 组合操作:高7位左移9位,低9位直接填充
|
||||||
|
unsigned short address = pSlaveData->start_address = (equipmentCode ) | serialNumber; // 关键位操作
|
||||||
|
|
||||||
|
pSlaveData->start_address = address;
|
||||||
|
pSlaveData->quantity = quantity_list.at(j).toInt();
|
||||||
|
m_SlaveData.push_back(pSlaveData);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SlaveItem* pSlaveData = new SlaveItem();
|
||||||
|
pSlaveData->id = m_modbus_slave_id;
|
||||||
|
pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt();
|
||||||
|
pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt();
|
||||||
|
pSlaveData->quantity = m_pSettings->value(app2 + "/quantity").toInt();
|
||||||
|
pSlaveData->start_address = m_pSettings->value(app2 + "/start_addr").toInt();
|
||||||
|
m_SlaveData.push_back(pSlaveData);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::InitializeUI()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::InitializeModbus()
|
||||||
|
{
|
||||||
|
QString appDir = QCoreApplication::applicationDirPath();
|
||||||
|
QString iniFilePath = appDir + QString::fromStdString("/emsshower.ini");
|
||||||
|
|
||||||
|
getConfiguration(iniFilePath);
|
||||||
|
|
||||||
|
switch (m_modbus_type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return InitializeTcp();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case 1:
|
||||||
|
return InitializeRtu();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::InitializeRtu()
|
||||||
|
{
|
||||||
|
if(m_pModbus)
|
||||||
|
{
|
||||||
|
modbus_close(m_pModbus);
|
||||||
|
m_pModbus = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string com = std::string("\\\\.\\") + m_modbus_com.toStdString();
|
||||||
|
|
||||||
|
m_pModbus = modbus_new_rtu(com.c_str(), m_modbus_baund, 'N', m_modbus_data, m_modbus_stop);
|
||||||
|
|
||||||
|
int slave_id = m_modbus_slave_id;
|
||||||
|
//modbus_set_debug(m_pModbus, true);
|
||||||
|
modbus_set_slave(m_pModbus, slave_id); //设置modbus从机地址
|
||||||
|
modbus_rtu_set_serial_mode(m_pModbus, MODBUS_RTU_RS485);
|
||||||
|
|
||||||
|
int rc = modbus_connect(m_pModbus);
|
||||||
|
if (rc == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct timeval t;
|
||||||
|
t.tv_sec=0;
|
||||||
|
t.tv_usec=1000000; //设置modbus超时时间为1000毫秒
|
||||||
|
modbus_set_response_timeout(m_pModbus, t.tv_usec,t.tv_usec);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::InitializeTcp()
|
||||||
|
{
|
||||||
|
if(m_pModbus)
|
||||||
|
{
|
||||||
|
modbus_close(m_pModbus);
|
||||||
|
m_pModbus = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ip = m_modbus_ip.toStdString();
|
||||||
|
int port = m_modbus_port;
|
||||||
|
m_pModbus = modbus_new_tcp(ip.c_str(), port); //由于是tcp client连接,在同一个程序中相同的端口可以连接多次。
|
||||||
|
if (m_pModbus == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int slave_id = m_modbus_slave_id;
|
||||||
|
modbus_set_slave(m_pModbus, slave_id); //从机地址
|
||||||
|
|
||||||
|
int rc = modbus_connect(m_pModbus);
|
||||||
|
if (rc == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct timeval t;
|
||||||
|
t.tv_sec=0;
|
||||||
|
t.tv_usec=1000000; //设置modbus超时时间为1000毫秒
|
||||||
|
modbus_set_response_timeout(m_pModbus, t.tv_usec,t.tv_usec);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::readRegister(int addr,int nb,uint16_t* dest)
|
||||||
|
{
|
||||||
|
for (auto it = m_SlaveData.begin(); it != m_SlaveData.end(); ++it)
|
||||||
|
{
|
||||||
|
SlaveItem* pItem = (SlaveItem*)(*it);
|
||||||
|
|
||||||
|
uint16_t* tab_reg = new uint16_t[pItem->quantity];
|
||||||
|
int regs = modbus_read_registers(m_pModbus, pItem->start_address, pItem->quantity, tab_reg);
|
||||||
|
|
||||||
|
QVector<uint16_t> registers;
|
||||||
|
for (int i = 0; i < regs; ++i)
|
||||||
|
{
|
||||||
|
registers.push_back(tab_reg[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete []tab_reg;
|
||||||
|
|
||||||
|
DeviceData* pDevice = nullptr;
|
||||||
|
|
||||||
|
if (pItem->start_address == TH08D_TEMPERATURE_EQUIPMENT_81_00_09
|
||||||
|
|| pItem->start_address == TH08D_TEMPERATURE_EQUIPMENT_81_09_06)
|
||||||
|
pDevice = new TemperatureData();
|
||||||
|
|
||||||
|
startAsyncProcess(registers,pItem->id,pItem->start_address, pItem->quantity, pDevice);
|
||||||
|
|
||||||
|
if (pItem->start_address == TH08D_TEMPERATURE_EQUIPMENT_81_00_09
|
||||||
|
|| pItem->start_address == TH08D_TEMPERATURE_EQUIPMENT_81_09_06)
|
||||||
|
{
|
||||||
|
TemperatureData* pData = (TemperatureData*)pDevice;
|
||||||
|
assert(pData);
|
||||||
|
if (pData->bDecodeTemp)
|
||||||
|
{
|
||||||
|
ui->txt_content_1->append(QString(tr("温度:%1\n湿度: %2\n露点: %3\n")).arg(pData->TempValue).arg(pData->HumidityValue).arg(pData->DewPointValue));
|
||||||
|
}
|
||||||
|
if (pData->bDecodeAlarm)
|
||||||
|
{
|
||||||
|
ui->txt_content_1->append(QString(tr("高温告警:%1\n低温告警: %2\n湿度告警: %3\n")).arg(pData->TempHighAlarm).arg(pData->TempLowAlarm).arg(pData->HumidityHighAlarm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "同步读取地址" << pItem->start_address;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::startAsyncProcess(const QVector<uint16_t>& array,int slave_id,int start_addr,int quantity, DeviceData* pDevice)
|
||||||
|
{
|
||||||
|
QThread* thread = new QThread;
|
||||||
|
DecodeWorker* worker = new DecodeWorker;
|
||||||
|
worker->moveToThread(thread);
|
||||||
|
|
||||||
|
worker->setParameters(array, slave_id, start_addr, quantity, pDevice);
|
||||||
|
|
||||||
|
// 连接线程启动信号和槽函数
|
||||||
|
connect(this, &MainWindow::startProcessing, worker, &DecodeWorker::processArray);
|
||||||
|
|
||||||
|
// 工作完成后退出线程
|
||||||
|
connect(worker, &DecodeWorker::finished, thread, &QThread::quit);
|
||||||
|
connect(worker, &DecodeWorker::finished, worker, &DecodeWorker::deleteLater);
|
||||||
|
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||||
|
|
||||||
|
//// 可选:连接进度信号更新UI
|
||||||
|
//connect(worker, &DecodeWorker::progress, this, [](int index)
|
||||||
|
//{
|
||||||
|
// qDebug() << "进度更新,当前处理到索引:" << index;
|
||||||
|
//});
|
||||||
|
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(worker, &DecodeWorker::finished, &loop, &QEventLoop::quit);
|
||||||
|
|
||||||
|
// 发射信号传递参数
|
||||||
|
emit startProcessing(array, slave_id, start_addr, quantity, pDevice);
|
||||||
|
|
||||||
|
// 阻塞,等待线程结束
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
qDebug() << "同步读取完成";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::on_btn_read_register_clicked()
|
||||||
|
{
|
||||||
|
readRegister(0,0,0);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "libmodbus/modbus.h"
|
||||||
|
#include "slave_define.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
typedef enum _tagModbusMode : int
|
||||||
|
{
|
||||||
|
modeRtu = 0,
|
||||||
|
modeTcp = 1,
|
||||||
|
} ModbusMode;
|
||||||
|
|
||||||
|
//这是解码类,针对协议进行解码
|
||||||
|
class DecodeWorker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DecodeWorker(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
|
void setParameters(const QVector<uint16_t>& array, int slave_id, int start_addr, int quantity, DeviceData* pDevice)
|
||||||
|
{
|
||||||
|
this->array = array;
|
||||||
|
this->slave_id = slave_id;
|
||||||
|
this->start_addr = start_addr;
|
||||||
|
this->quantity = quantity;
|
||||||
|
this->pDevice = pDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<uint16_t> array;
|
||||||
|
int slave_id;
|
||||||
|
int start_addr;
|
||||||
|
int quantity;
|
||||||
|
DeviceData* pDevice;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// 处理进度信号
|
||||||
|
void progress(int index);
|
||||||
|
// 完成处理信号
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// 处理数组的槽函数
|
||||||
|
// 根据从机id、开始地址、寄存器数量三个变量进行辨别和解码处理
|
||||||
|
void processArray(const QVector<uint16_t>& array,int slave_id,int start_addr,int quantity,DeviceData* pData);
|
||||||
|
};
|
||||||
|
|
||||||
|
//处理主界面
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void getConfiguration(QString iniFilePath);
|
||||||
|
bool InitializeUI();
|
||||||
|
bool InitializeModbus();
|
||||||
|
bool InitializeRtu();
|
||||||
|
bool InitializeTcp();
|
||||||
|
|
||||||
|
bool readRegister(int addr,int nb,uint16_t* dest);
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
modbus_t *m_pModbus;
|
||||||
|
SlaveData m_SlaveData;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_version;
|
||||||
|
int m_modbus_type;
|
||||||
|
QString m_modbus_ip;
|
||||||
|
int m_modbus_port;
|
||||||
|
QString m_modbus_com;
|
||||||
|
int m_modbus_baund;
|
||||||
|
int m_modbus_data;
|
||||||
|
int m_modbus_parity;
|
||||||
|
int m_modbus_stop;
|
||||||
|
int m_modbus_slave_id;
|
||||||
|
QSettings* m_pSettings;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//异步处理数据
|
||||||
|
void startAsyncProcess(const QVector<uint16_t>& array,int slave_id,int start_addr,int quantity,DeviceData* pData);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_btn_read_register_clicked();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// 处理进度信号
|
||||||
|
void progress(int index);
|
||||||
|
// 完成处理信号
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startProcessing(const QVector<uint16_t>& array, int slave_id, int start_addr, int quantity, DeviceData* pData);
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<widget class="QPushButton" name="btn_read_register">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>50</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>93</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>read</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QTextEdit" name="txt_content_1">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>60</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>331</width>
|
||||||
|
<height>331</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QTextEdit" name="txt_content_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>410</x>
|
||||||
|
<y>100</y>
|
||||||
|
<width>341</width>
|
||||||
|
<height>331</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef SLAVE_DEFINE_H
|
||||||
|
#define SLAVE_DEFINE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct _tagSlave
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
int function_code;
|
||||||
|
int start_address;
|
||||||
|
int quantity;
|
||||||
|
int interval;
|
||||||
|
} SlaveItem;
|
||||||
|
|
||||||
|
typedef std::vector<SlaveItem*> SlaveData;
|
||||||
|
|
||||||
|
class DeviceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeviceData()
|
||||||
|
:m_device_type(-1),m_device_online_state(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~DeviceData()
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int m_device_type;
|
||||||
|
int m_device_online_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TemperatureData : public DeviceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TemperatureData()
|
||||||
|
{
|
||||||
|
m_device_type = 81;
|
||||||
|
bDecodeAlarm = false;
|
||||||
|
bDecodeTemp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~TemperatureData() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool bDecodeAlarm;
|
||||||
|
bool bDecodeTemp;
|
||||||
|
float TempValue;
|
||||||
|
float HumidityValue;
|
||||||
|
float DewPointValue;
|
||||||
|
int DO;
|
||||||
|
int DI1;
|
||||||
|
int DI2;
|
||||||
|
int TempHighAlarm;
|
||||||
|
int HumidityHighAlarm;
|
||||||
|
int TempLowAlarm;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SLAVE_DEFINE_H
|
|
@ -0,0 +1,297 @@
|
||||||
|
#include "newdeviceformdialog.h"
|
||||||
|
#include "ui_newdeviceformdialog.h"
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QSqlRecord>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <hv/hlog.h>
|
||||||
|
|
||||||
|
#include "frame_define.h"
|
||||||
|
#include "globalparameters.h"
|
||||||
|
#include "kutilities.h"
|
||||||
|
|
||||||
|
|
||||||
|
NewDeviceFormDialog::NewDeviceFormDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(new Ui::NewDeviceFormDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
InitializeUi();
|
||||||
|
|
||||||
|
connect(ui->pb_OK, &QPushButton::clicked, this, &NewDeviceFormDialog::onOkClicked);
|
||||||
|
connect(ui->pb_Cancel, &QPushButton::clicked, this, &NewDeviceFormDialog::onCancelClicked);
|
||||||
|
connect(ui->pb_Import, &QPushButton::clicked, this, &NewDeviceFormDialog::browseFile);
|
||||||
|
connect(ui->cb_device, SIGNAL(currentTextChanged(const QString &)),this, SLOT(onComboBoxTextChanged(const QString &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
NewDeviceFormDialog::~NewDeviceFormDialog()
|
||||||
|
{
|
||||||
|
// 关闭数据库
|
||||||
|
QSqlDatabase::database().close();
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewDeviceFormDialog::onOkClicked()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewDeviceFormDialog::onCancelClicked()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewDeviceFormDialog::openDatabase(const QString &dbName)
|
||||||
|
{
|
||||||
|
// 创建一个 SQLite 数据库连接
|
||||||
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
|
||||||
|
|
||||||
|
// 设置数据库的文件路径
|
||||||
|
db.setDatabaseName(dbName);
|
||||||
|
|
||||||
|
// 尝试打开数据库
|
||||||
|
if (!db.open())
|
||||||
|
{
|
||||||
|
// 如果打开失败,输出错误信息
|
||||||
|
hloge("Error: Failed to open database! %s",db.lastError().text().toStdString().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hlogd("Database opened successfully!");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewDeviceFormDialog::browseFile()
|
||||||
|
{
|
||||||
|
//首先断开连接
|
||||||
|
disconnect(ui->cb_device, SIGNAL(currentTextChanged(const QString &)),this, SLOT(onComboBoxTextChanged(const QString &)));
|
||||||
|
|
||||||
|
// 打开文件对话框,选择文件路径
|
||||||
|
QString filePath = QFileDialog::getOpenFileName(this, tr("Select Configuration File"), "", tr("Database Files (*.db);;All Files (*.*)"));
|
||||||
|
|
||||||
|
// 如果用户选择了文件,更新 QLineEdit 显示路径
|
||||||
|
if (!filePath.isEmpty())
|
||||||
|
{
|
||||||
|
ui->lineEdit->setText(filePath);
|
||||||
|
qDebug() << "Selected file path: " << filePath;
|
||||||
|
|
||||||
|
openDatabase(filePath);
|
||||||
|
|
||||||
|
QSqlQuery query;
|
||||||
|
vecCfgTab_SmartDeviceType SmartDeviceTypes;
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
if (!query.exec("SELECT DISTINCT Description FROM CfgTab_SmartDeviceType ORDER BY TypeID ASC"))
|
||||||
|
{
|
||||||
|
hloge("Error: Failed to select data! [%s]",query.lastError().text().toStdString().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QVector<QString> devs;
|
||||||
|
while (query.next())
|
||||||
|
{
|
||||||
|
QString s = query.value(0).toString();
|
||||||
|
devs.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QString device, devs)
|
||||||
|
{
|
||||||
|
QString sql = QString("SELECT * FROM CfgTab_SmartDeviceType where Description = '%1' ORDER BY ProtocolVersion desc LIMIT 1").arg(device);
|
||||||
|
if (!query.exec(sql))
|
||||||
|
{
|
||||||
|
hloge("Error: Failed to select data! [%s]",query.lastError().text().toStdString().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 获取列名到 QSqlRecord
|
||||||
|
QSqlRecord record = query.record();
|
||||||
|
while (query.next())
|
||||||
|
{
|
||||||
|
CfgTabSmartDeviceTypeItem smartDeviceTypesItem;
|
||||||
|
smartDeviceTypesItem.TypeID = query.value(record.indexOf("TypeID")).toInt();
|
||||||
|
smartDeviceTypesItem.Description = query.value(record.indexOf("Description")).toString().toStdString();
|
||||||
|
smartDeviceTypesItem.ClassType = query.value(record.indexOf("ClassType")).toInt();
|
||||||
|
smartDeviceTypesItem.PortConfig = query.value(record.indexOf("PortConfig")).toInt();
|
||||||
|
smartDeviceTypesItem.InterfaceType = query.value(record.indexOf("InterfaceType")).toInt();
|
||||||
|
smartDeviceTypesItem.StationType = query.value(record.indexOf("StationType")).toInt();
|
||||||
|
smartDeviceTypesItem.EquipmentCode = query.value(record.indexOf("EquipmentCode")).toInt();
|
||||||
|
smartDeviceTypesItem.EquipmentModel = query.value(record.indexOf("EquipmentModel")).toString().toStdString();
|
||||||
|
smartDeviceTypesItem.Manufacturer = query.value(record.indexOf("Manufacturer")).toString().toStdString();
|
||||||
|
smartDeviceTypesItem.ProtocolVersion = query.value(record.indexOf("ProtocolVersion")).toString().toStdString();
|
||||||
|
smartDeviceTypesItem.SpecialOption = query.value(record.indexOf("SpecialOption")).toString().toStdString();
|
||||||
|
smartDeviceTypesItem.Notes = query.value(record.indexOf("Notes")).toString().toStdString();
|
||||||
|
|
||||||
|
qDebug() << "Type ID:" << smartDeviceTypesItem.TypeID << ", Description:" << smartDeviceTypesItem.Description.c_str() << ", ProtocolVersion:" << smartDeviceTypesItem.ProtocolVersion.c_str();
|
||||||
|
|
||||||
|
SmartDeviceTypes.emplace_back(smartDeviceTypesItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置串口模式
|
||||||
|
ui->cb_device->setView(new QListView(this));
|
||||||
|
QStandardItemModel* model_baund = new QStandardItemModel(this);
|
||||||
|
|
||||||
|
for (auto it = SmartDeviceTypes.begin(); it != SmartDeviceTypes.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->TypeID;
|
||||||
|
QString name = QString::fromStdString(it->Description);
|
||||||
|
QString a = QString("%1:%2:%3").arg(id).arg(name).arg(QString::fromStdString(it->ProtocolVersion));
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(a);
|
||||||
|
item->setData(id);
|
||||||
|
model_baund->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_device->setModel(model_baund);
|
||||||
|
ui->cb_device->setCurrentIndex(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//重新连接槽函数
|
||||||
|
connect(ui->cb_device, SIGNAL(currentTextChanged(const QString &)),this, SLOT(onComboBoxTextChanged(const QString &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewDeviceFormDialog::onComboBoxTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
qDebug() << "ComboBox text changed to" << text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewDeviceFormDialog::InitializeUi()
|
||||||
|
{
|
||||||
|
ui->slave_id->setText("1");
|
||||||
|
|
||||||
|
//设置奇偶校验
|
||||||
|
ui->cb_parity->setView(new QListView(this));
|
||||||
|
|
||||||
|
QStandardItemModel* model_parity = new QStandardItemModel(this);
|
||||||
|
|
||||||
|
vecParityItemData vparity;
|
||||||
|
vparity.push_back(std::make_pair(EMS_PARITY_NONE,tr("None")));
|
||||||
|
vparity.push_back(std::make_pair(EMS_PARITY_ODD,tr("Odd")));
|
||||||
|
vparity.push_back(std::make_pair(EMS_PARITY_EVEN,tr("Even")));
|
||||||
|
|
||||||
|
for (auto it = vparity.begin(); it != vparity.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->first;
|
||||||
|
QString name = it->second;
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
//QVariant var = QVariant::fromValue(data);
|
||||||
|
item->setData(id);
|
||||||
|
model_parity->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_parity->setModel(model_parity);
|
||||||
|
|
||||||
|
//设置数据位
|
||||||
|
ui->cb_data->setView(new QListView(this));
|
||||||
|
QStandardItemModel* model_databits = new QStandardItemModel(this);
|
||||||
|
vecDataBitsItemData vdatabits;
|
||||||
|
vdatabits.push_back(std::make_pair(EMS_DATA_8_BITS,tr("8 Data bits")));
|
||||||
|
vdatabits.push_back(std::make_pair(EMS_DATA_7_BITS,tr("7 Data bits")));
|
||||||
|
|
||||||
|
for (auto it = vdatabits.begin(); it != vdatabits.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->first;
|
||||||
|
QString name = it->second;
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
item->setData(id);
|
||||||
|
model_databits->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_data->setModel(model_databits);
|
||||||
|
|
||||||
|
//设置数据位
|
||||||
|
ui->cb_stop->setView(new QListView(this));
|
||||||
|
QStandardItemModel* model_stopbits = new QStandardItemModel(this);
|
||||||
|
vecDataBitsItemData vstopbits;
|
||||||
|
vstopbits.push_back(std::make_pair(EMS_1_STOP_BITS,tr("1 Stop bit")));
|
||||||
|
vstopbits.push_back(std::make_pair(EMS_2_STOP_BITS,tr("2 Stop bits")));
|
||||||
|
|
||||||
|
for (auto it = vstopbits.begin(); it != vstopbits.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->first;
|
||||||
|
QString name = it->second;
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
item->setData(id);
|
||||||
|
model_stopbits->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_stop->setModel(model_stopbits);
|
||||||
|
|
||||||
|
//设置串口模式
|
||||||
|
ui->cb_baund->setView(new QListView(this));
|
||||||
|
QStandardItemModel* model_baund = new QStandardItemModel(this);
|
||||||
|
vecBaundItemData vbaund;
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("2400")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("4800")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("9600")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("19200")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("38400")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("57600")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("115200")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("128000")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("153600")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("230400")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("460800")));
|
||||||
|
vbaund.push_back(std::make_pair(EMS_BAUND_2400,tr("921600")));
|
||||||
|
|
||||||
|
for (auto it = vbaund.begin(); it != vbaund.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->first;
|
||||||
|
QString name = it->second;
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
item->setData(id);
|
||||||
|
model_baund->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_baund->setModel(model_baund);
|
||||||
|
ui->cb_baund->setCurrentIndex(2);
|
||||||
|
|
||||||
|
//设置串口类型
|
||||||
|
ui->cb_type->setView(new QListView(this));
|
||||||
|
QStandardItemModel* model_type = new QStandardItemModel(this);
|
||||||
|
vecPortTypeItemData vporttype;
|
||||||
|
vporttype.push_back(std::make_pair(EMS_SERIALPORT_TYPE,tr("Serial Port")));
|
||||||
|
|
||||||
|
for (auto it = vporttype.begin(); it != vporttype.end(); it++)
|
||||||
|
{
|
||||||
|
int id = it->first;
|
||||||
|
QString name = it->second;
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
item->setData(id);
|
||||||
|
model_type->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_type->setModel(model_type);
|
||||||
|
|
||||||
|
//设置串口号
|
||||||
|
ui->cb_serialport->setView(new QListView(this));
|
||||||
|
|
||||||
|
QStandardItemModel* model_serialport = new QStandardItemModel(this);
|
||||||
|
|
||||||
|
for (int i=0; i < AppData::getInstance()->SerialPortCount ; i++)
|
||||||
|
{
|
||||||
|
QString name = AppData::getInstance()->SerialPortPrefix + QString("%1").arg(i);
|
||||||
|
|
||||||
|
QStandardItem* item = new QStandardItem(name);
|
||||||
|
//QVariant var = QVariant::fromValue(data);
|
||||||
|
item->setData(name);
|
||||||
|
model_serialport->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->cb_serialport->setModel(model_serialport);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef NEWDEVICEFORMDIALOG_H
|
||||||
|
#define NEWDEVICEFORMDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class NewDeviceFormDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewDeviceFormDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NewDeviceFormDialog(QWidget *parent = nullptr);
|
||||||
|
~NewDeviceFormDialog();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool openDatabase(const QString &dbName);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onOkClicked();
|
||||||
|
void onCancelClicked();
|
||||||
|
void browseFile();
|
||||||
|
void onComboBoxTextChanged(const QString &text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::NewDeviceFormDialog *ui;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void InitializeUi();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NEWDEVICEFORMDIALOG_H
|
|
@ -0,0 +1,339 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>NewDeviceFormDialog</class>
|
||||||
|
<widget class="QDialog" name="NewDeviceFormDialog">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::WindowModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>726</width>
|
||||||
|
<height>526</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>726</width>
|
||||||
|
<height>526</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>726</width>
|
||||||
|
<height>526</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>设备串口设置</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="emscfgres.qrc">
|
||||||
|
<normaloff>:/images/icon.png</normaloff>:/images/icon.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QPushButton" name="pb_OK">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>410</x>
|
||||||
|
<y>450</y>
|
||||||
|
<width>93</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>OK</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="pb_Cancel">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>510</x>
|
||||||
|
<y>450</y>
|
||||||
|
<width>93</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="pb_Import">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>600</x>
|
||||||
|
<y>50</y>
|
||||||
|
<width>93</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>导入</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="lineEdit">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>50</y>
|
||||||
|
<width>551</width>
|
||||||
|
<height>26</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>111</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>预定义文件</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_device">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>120</y>
|
||||||
|
<width>551</width>
|
||||||
|
<height>26</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>40</x>
|
||||||
|
<y>90</y>
|
||||||
|
<width>63</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>设备类型</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>210</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>从机地址</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>slave_id</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_baund">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>450</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>波特率</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>cb_baund</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>60</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>串口号</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>290</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>校验位</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>cb_parity</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>60</x>
|
||||||
|
<y>330</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>停止位</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>cb_stop</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_stop">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>140</x>
|
||||||
|
<y>330</y>
|
||||||
|
<width>181</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_serialport">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>140</x>
|
||||||
|
<y>250</y>
|
||||||
|
<width>181</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_data">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>140</x>
|
||||||
|
<y>290</y>
|
||||||
|
<width>181</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>60</x>
|
||||||
|
<y>210</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>串口类型</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>cb_type</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>60</x>
|
||||||
|
<y>290</y>
|
||||||
|
<width>69</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>数据位</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>cb_data</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_parity">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>450</x>
|
||||||
|
<y>290</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QComboBox" name="cb_type">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>140</x>
|
||||||
|
<y>210</y>
|
||||||
|
<width>181</width>
|
||||||
|
<height>25</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLineEdit" name="slave_id">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>450</x>
|
||||||
|
<y>210</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>30</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>160</y>
|
||||||
|
<width>651</width>
|
||||||
|
<height>231</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>端口配置</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<zorder>groupBox</zorder>
|
||||||
|
<zorder>pb_OK</zorder>
|
||||||
|
<zorder>pb_Cancel</zorder>
|
||||||
|
<zorder>pb_Import</zorder>
|
||||||
|
<zorder>lineEdit</zorder>
|
||||||
|
<zorder>label</zorder>
|
||||||
|
<zorder>cb_device</zorder>
|
||||||
|
<zorder>label_2</zorder>
|
||||||
|
<zorder>label_4</zorder>
|
||||||
|
<zorder>cb_baund</zorder>
|
||||||
|
<zorder>label_6</zorder>
|
||||||
|
<zorder>label_5</zorder>
|
||||||
|
<zorder>label_8</zorder>
|
||||||
|
<zorder>label_9</zorder>
|
||||||
|
<zorder>cb_stop</zorder>
|
||||||
|
<zorder>cb_serialport</zorder>
|
||||||
|
<zorder>cb_data</zorder>
|
||||||
|
<zorder>label_7</zorder>
|
||||||
|
<zorder>label_10</zorder>
|
||||||
|
<zorder>cb_parity</zorder>
|
||||||
|
<zorder>cb_type</zorder>
|
||||||
|
<zorder>slave_id</zorder>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="emscfgres.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
QT = core serialport serialbus
|
||||||
|
|
||||||
|
CONFIG += c++17 cmdline
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
tempctrl.cpp
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
tempctrl.h
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "TempCtrl.h"
|
||||||
|
|
||||||
|
#include <QSerialPort>
|
||||||
|
#include <QSerialPortInfo>
|
||||||
|
#include <QModbusRequest>
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
TempCtrl::TempCtrl():
|
||||||
|
m_channelConnected(false), m_channelAddr(0)
|
||||||
|
{
|
||||||
|
m_modbusDevice.setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::EvenParity);
|
||||||
|
m_modbusDevice.setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
|
||||||
|
m_modbusDevice.setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
|
||||||
|
m_modbusDevice.setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
TempCtrl::~TempCtrl()
|
||||||
|
{
|
||||||
|
disconnectDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
TempCtrl* TempCtrl::instance()
|
||||||
|
{
|
||||||
|
static TempCtrl* theInstance = new TempCtrl();
|
||||||
|
return theInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TempCtrl::connectDevice()
|
||||||
|
{
|
||||||
|
disconnectDevice();
|
||||||
|
|
||||||
|
const auto serialPortInfos = QSerialPortInfo::availablePorts();
|
||||||
|
if(!serialPortInfos.empty())
|
||||||
|
{
|
||||||
|
QModbusRequest echoTest(QModbusRequest::Diagnostics, quint16(0x0000), quint16(0x1234));
|
||||||
|
|
||||||
|
bool comportFound(false);
|
||||||
|
for (const QSerialPortInfo &serialPortInfo : serialPortInfos)
|
||||||
|
{
|
||||||
|
m_modbusDevice.setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant::fromValue(serialPortInfo.portName()));
|
||||||
|
|
||||||
|
if(m_modbusDevice.connectDevice())
|
||||||
|
{
|
||||||
|
auto curTimePt = std::chrono::steady_clock::now();
|
||||||
|
double duration = 0;
|
||||||
|
|
||||||
|
while(m_modbusDevice.state() != QModbusDevice::ConnectedState && duration < 2.0)
|
||||||
|
{
|
||||||
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20);
|
||||||
|
std::chrono::duration d = std::chrono::steady_clock::now()-curTimePt;
|
||||||
|
duration = d.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_modbusDevice.state() == QModbusDevice::ConnectedState)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QModbusResponse reply = sendModbusRawRequest(echoTest);
|
||||||
|
if(reply.isValid())
|
||||||
|
{
|
||||||
|
if(reply.data() == QByteArray::fromHex("00001234"))
|
||||||
|
{
|
||||||
|
if(!comportFound)
|
||||||
|
{
|
||||||
|
comportFound = true;
|
||||||
|
}
|
||||||
|
m_channelConnected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(comportFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_modbusDevice.disconnectDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modbusDevice.setTimeout(500);
|
||||||
|
m_modbusDevice.setNumberOfRetries(3);
|
||||||
|
return m_channelConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempCtrl::disconnectDevice()
|
||||||
|
{
|
||||||
|
if(m_modbusDevice.state() != QModbusDevice::UnconnectedState)
|
||||||
|
{
|
||||||
|
m_modbusDevice.disconnectDevice();
|
||||||
|
}
|
||||||
|
m_channelConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModbusResponse TempCtrl::sendModbusRawRequest(const QModbusRequest &request) const
|
||||||
|
{
|
||||||
|
QModbusReply* reply = m_modbusDevice.sendRawRequest(request, m_channelAddr);
|
||||||
|
|
||||||
|
if(reply)
|
||||||
|
{
|
||||||
|
while(!reply->isFinished())
|
||||||
|
{
|
||||||
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if(reply->error() == QModbusDevice::NoError)
|
||||||
|
{
|
||||||
|
auto response = reply->rawResult();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
else if (reply->error() == QModbusDevice::ProtocolError)
|
||||||
|
{
|
||||||
|
QString error = "Protocol error: " + reply->errorString();
|
||||||
|
throw std::exception(error.toStdString().c_str());
|
||||||
|
}
|
||||||
|
else if (reply->error() == QModbusDevice::TimeoutError)
|
||||||
|
{
|
||||||
|
throw std::exception("Timeout error");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::exception("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QModbusResponse();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef TEMPCTRL_H
|
||||||
|
#define TEMPCTRL_H
|
||||||
|
|
||||||
|
#include <QModbusResponse>
|
||||||
|
#include <QModbusRtuSerialClient>
|
||||||
|
|
||||||
|
class TempCtrl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
TempCtrl();
|
||||||
|
|
||||||
|
public:
|
||||||
|
~TempCtrl();
|
||||||
|
static TempCtrl* instance();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool connectDevice();
|
||||||
|
void disconnectDevice();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QModbusResponse sendModbusRawRequest(const QModbusRequest &request) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_channelConnected;
|
||||||
|
int m_channelAddr;
|
||||||
|
mutable QModbusRtuSerialClient m_modbusDevice;
|
||||||
|
};
|
||||||
|
#endif // TEMPCTRL_H
|
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* config.h. Generated from config.h.in by configure. */
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||||
|
/* #undef HAVE_ARPA_INET_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `TIOCSRS485', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
/* #undef HAVE_DECL_TIOCSRS485 */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `__CYGWIN__', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
/* #undef HAVE_DECL___CYGWIN__ */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
/* #undef HAVE_DLFCN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#define HAVE_ERRNO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||||
|
#define HAVE_FCNTL_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `fork' function. */
|
||||||
|
/* #undef HAVE_FORK */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||||
|
/* #undef HAVE_GETADDRINFO */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
|
/* #undef HAVE_GETTIMEOFDAY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <limits.h> header file. */
|
||||||
|
#define HAVE_LIMITS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/serial.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_SERIAL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memset' function. */
|
||||||
|
#define HAVE_MEMSET 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netdb.h> header file. */
|
||||||
|
/* #undef HAVE_NETDB_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||||
|
/* #undef HAVE_NETINET_IN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/tcp.h> header file. */
|
||||||
|
/* #undef HAVE_NETINET_TCP_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `select' function. */
|
||||||
|
/* #undef HAVE_SELECT */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' function. */
|
||||||
|
/* #undef HAVE_SOCKET */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strerror' function. */
|
||||||
|
#define HAVE_STRERROR 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
/* #undef HAVE_STRINGS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strlcpy' function. */
|
||||||
|
/* #undef HAVE_STRLCPY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_IOCTL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_SOCKET_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_TIME_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <termios.h> header file. */
|
||||||
|
/* #undef HAVE_TERMIOS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <time.h> header file. */
|
||||||
|
#define HAVE_TIME_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
/* #undef HAVE_UNISTD_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `vfork' function. */
|
||||||
|
/* #undef HAVE_VFORK */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <vfork.h> header file. */
|
||||||
|
/* #undef HAVE_VFORK_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <winsock2.h> header file. */
|
||||||
|
#define HAVE_WINSOCK2_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if `fork' works. */
|
||||||
|
/* #undef HAVE_WORKING_FORK */
|
||||||
|
|
||||||
|
/* Define to 1 if `vfork' works. */
|
||||||
|
/* #undef HAVE_WORKING_VFORK */
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
/* #undef LT_OBJDIR */
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "https://github.com/stephane/libmodbus/issues"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "libmodbus 3.1.11"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "libmodbus"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "3.1.11"
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||||
|
/* #undef TIME_WITH_SYS_TIME */
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#define VERSION "3.1.11"
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
/* #undef const */
|
||||||
|
|
||||||
|
/* Define to `int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef pid_t */
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef size_t */
|
||||||
|
|
||||||
|
/* Define as `fork' if `vfork' does not work. */
|
||||||
|
#define vfork fork
|
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include <winsock2.h>
|
||||||
|
#else
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libmodbus/config.h"
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/* Sets many bits from a single byte value (all 8 bits of the byte value are
|
||||||
|
set) */
|
||||||
|
void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
dest[idx + i] = (value & (1 << i)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets many bits from a table of bytes (only the bits between idx and
|
||||||
|
idx + nb_bits are set) */
|
||||||
|
void modbus_set_bits_from_bytes(uint8_t *dest,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
const uint8_t *tab_byte)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int shift = 0;
|
||||||
|
|
||||||
|
for (i = idx; i < idx + nb_bits; i++) {
|
||||||
|
dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0;
|
||||||
|
/* gcc doesn't like: shift = (++shift) % 8; */
|
||||||
|
shift++;
|
||||||
|
shift %= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the byte value from many bits.
|
||||||
|
To obtain a full byte, set nb_bits to 8. */
|
||||||
|
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint8_t value = 0;
|
||||||
|
|
||||||
|
if (nb_bits > 8) {
|
||||||
|
/* Assert is ignored if NDEBUG is set */
|
||||||
|
assert(nb_bits < 8);
|
||||||
|
nb_bits = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nb_bits; i++) {
|
||||||
|
value |= (src[idx + i] << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */
|
||||||
|
float modbus_get_float_abcd(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// Mind: src contains 16-bit numbers in processor-endianness, hence
|
||||||
|
// we use shift operations and do not access memory directly
|
||||||
|
a = (src[0] >> 8) & 0xFF; // high byte of first word
|
||||||
|
b = (src[0] >> 0) & 0xFF; // low byte of first word
|
||||||
|
c = (src[1] >> 8) & 0xFF; // high byte of second word
|
||||||
|
d = (src[1] >> 0) & 0xFF; // low byte of second word
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */
|
||||||
|
float modbus_get_float_dcba(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: dcba
|
||||||
|
d = (src[0] >> 8) & 0xFF;
|
||||||
|
c = (src[0] >> 0) & 0xFF;
|
||||||
|
b = (src[1] >> 8) & 0xFF;
|
||||||
|
a = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */
|
||||||
|
float modbus_get_float_badc(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: badc
|
||||||
|
b = (src[0] >> 8) & 0xFF;
|
||||||
|
a = (src[0] >> 0) & 0xFF;
|
||||||
|
d = (src[1] >> 8) & 0xFF;
|
||||||
|
c = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */
|
||||||
|
float modbus_get_float_cdab(const uint16_t *src)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
// byte order is defined when reading from src: cdab
|
||||||
|
c = (src[0] >> 8) & 0xFF;
|
||||||
|
d = (src[0] >> 0) & 0xFF;
|
||||||
|
a = (src[1] >> 8) & 0xFF;
|
||||||
|
b = (src[1] >> 0) & 0xFF;
|
||||||
|
|
||||||
|
// we assemble 32bit integer always in abcd order via shift operations
|
||||||
|
i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
|
||||||
|
memcpy(&f, &i, 4);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */
|
||||||
|
float modbus_get_float(const uint16_t *src)
|
||||||
|
{
|
||||||
|
return modbus_get_float_cdab(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
|
||||||
|
void modbus_set_float_abcd(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
// The straight-forward type conversion won't work because of type-punned pointer aliasing warning
|
||||||
|
// uint32_t i = *(uint32_t*)(&f);
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (a << 8) | b;
|
||||||
|
dest[1] = (c << 8) | d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
|
||||||
|
void modbus_set_float_dcba(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (d << 8) | c;
|
||||||
|
dest[1] = (b << 8) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
|
||||||
|
void modbus_set_float_badc(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (b << 8) | a;
|
||||||
|
dest[1] = (d << 8) | c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
|
||||||
|
void modbus_set_float_cdab(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
float * fptr = &f;
|
||||||
|
uint32_t * iptr = (uint32_t *)fptr;
|
||||||
|
uint32_t i = *iptr;
|
||||||
|
uint8_t a, b, c, d;
|
||||||
|
|
||||||
|
a = (i >> 24) & 0xFF;
|
||||||
|
b = (i >> 16) & 0xFF;
|
||||||
|
c = (i >> 8) & 0xFF;
|
||||||
|
d = (i >> 0) & 0xFF;
|
||||||
|
|
||||||
|
dest[0] = (c << 8) | d;
|
||||||
|
dest[1] = (a << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
|
||||||
|
void modbus_set_float(float f, uint16_t *dest)
|
||||||
|
{
|
||||||
|
modbus_set_float_cdab(f, dest);
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_PRIVATE_H
|
||||||
|
#define MODBUS_PRIVATE_H
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
# include <sys/time.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
# include <time.h>
|
||||||
|
typedef int ssize_t;
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
#include "libmodbus/config.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* It's not really the minimal length (the real one is report slave ID
|
||||||
|
* in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
|
||||||
|
* communications to read many values or write a single one.
|
||||||
|
* Maximum between :
|
||||||
|
* - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
|
||||||
|
* - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
|
||||||
|
*/
|
||||||
|
#define _MIN_REQ_LENGTH 12
|
||||||
|
|
||||||
|
#define _REPORT_SLAVE_ID 180
|
||||||
|
|
||||||
|
#define _MODBUS_EXCEPTION_RSP_LENGTH 5
|
||||||
|
|
||||||
|
/* Timeouts in microsecond (0.5 s) */
|
||||||
|
#define _RESPONSE_TIMEOUT 500000
|
||||||
|
#define _BYTE_TIMEOUT 500000
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
_MODBUS_BACKEND_TYPE_RTU = 0,
|
||||||
|
_MODBUS_BACKEND_TYPE_TCP
|
||||||
|
} modbus_backend_type_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ---------- Request Indication ----------
|
||||||
|
* | Client | ---------------------->| Server |
|
||||||
|
* ---------- Confirmation Response ----------
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/* Request message on the server side */
|
||||||
|
MSG_INDICATION,
|
||||||
|
/* Request message on the client side */
|
||||||
|
MSG_CONFIRMATION
|
||||||
|
} msg_type_t;
|
||||||
|
|
||||||
|
/* This structure reduces the number of params in functions and so
|
||||||
|
* optimizes the speed of execution (~ 37%). */
|
||||||
|
typedef struct _sft
|
||||||
|
{
|
||||||
|
int slave;
|
||||||
|
int function;
|
||||||
|
int t_id;
|
||||||
|
} sft_t;
|
||||||
|
|
||||||
|
typedef struct _modbus_backend
|
||||||
|
{
|
||||||
|
unsigned int backend_type;
|
||||||
|
unsigned int header_length;
|
||||||
|
unsigned int checksum_length;
|
||||||
|
unsigned int max_adu_length;
|
||||||
|
int (*set_slave)(modbus_t *ctx, int slave);
|
||||||
|
int (*build_request_basis)(
|
||||||
|
modbus_t *ctx, int function, int addr, int nb, uint8_t *req);
|
||||||
|
int (*build_response_basis)(sft_t *sft, uint8_t *rsp);
|
||||||
|
int (*get_response_tid)(const uint8_t *req);
|
||||||
|
int (*send_msg_pre)(uint8_t *req, int req_length);
|
||||||
|
ssize_t (*send)(modbus_t *ctx, const uint8_t *req, int req_length);
|
||||||
|
int (*receive)(modbus_t *ctx, uint8_t *req);
|
||||||
|
ssize_t (*recv)(modbus_t *ctx, uint8_t *rsp, int rsp_length);
|
||||||
|
int (*check_integrity)(modbus_t *ctx, uint8_t *msg, const int msg_length);
|
||||||
|
int (*pre_check_confirmation)(modbus_t *ctx,
|
||||||
|
const uint8_t *req,
|
||||||
|
const uint8_t *rsp,
|
||||||
|
int rsp_length);
|
||||||
|
int (*connect)(modbus_t *ctx);
|
||||||
|
unsigned int (*is_connected)(modbus_t *ctx);
|
||||||
|
void (*close)(modbus_t *ctx);
|
||||||
|
int (*flush)(modbus_t *ctx);
|
||||||
|
int (*select)(modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
|
||||||
|
void (*free)(modbus_t *ctx);
|
||||||
|
} modbus_backend_t;
|
||||||
|
|
||||||
|
struct _modbus
|
||||||
|
{
|
||||||
|
/* Slave address */
|
||||||
|
int slave;
|
||||||
|
/* Socket or file descriptor */
|
||||||
|
int s;
|
||||||
|
int debug;
|
||||||
|
int error_recovery;
|
||||||
|
int quirks;
|
||||||
|
struct timeval response_timeout;
|
||||||
|
struct timeval byte_timeout;
|
||||||
|
struct timeval indication_timeout;
|
||||||
|
const modbus_backend_t *backend;
|
||||||
|
void *backend_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void _modbus_init_common(modbus_t *ctx);
|
||||||
|
void _error_print(modbus_t *ctx, const char *context);
|
||||||
|
int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
|
||||||
|
|
||||||
|
#ifndef HAVE_STRLCPY
|
||||||
|
size_t strlcpy(char *dest, const char *src, size_t dest_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_PRIVATE_H */
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_RTU_PRIVATE_H
|
||||||
|
#define MODBUS_RTU_PRIVATE_H
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <stdint.h>
|
||||||
|
#else
|
||||||
|
#include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _MODBUS_RTU_HEADER_LENGTH 1
|
||||||
|
#define _MODBUS_RTU_PRESET_REQ_LENGTH 6
|
||||||
|
#define _MODBUS_RTU_PRESET_RSP_LENGTH 2
|
||||||
|
|
||||||
|
#define _MODBUS_RTU_CHECKSUM_LENGTH 2
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if !defined(ENOTSUP)
|
||||||
|
#define ENOTSUP WSAEOPNOTSUPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* WIN32: struct containing serial handle and a receive buffer */
|
||||||
|
#define PY_BUF_SIZE 512
|
||||||
|
|
||||||
|
struct win32_ser {
|
||||||
|
/* File handle */
|
||||||
|
HANDLE fd;
|
||||||
|
/* Receive buffer */
|
||||||
|
uint8_t buf[PY_BUF_SIZE];
|
||||||
|
/* Received chars */
|
||||||
|
DWORD n_bytes;
|
||||||
|
};
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
typedef struct _modbus_rtu {
|
||||||
|
/* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */
|
||||||
|
char *device;
|
||||||
|
/* Bauds: 9600, 19200, 57600, 115200, etc */
|
||||||
|
int baud;
|
||||||
|
/* Data bit */
|
||||||
|
uint8_t data_bit;
|
||||||
|
/* Stop bit */
|
||||||
|
uint8_t stop_bit;
|
||||||
|
/* Parity: 'N', 'O', 'E' */
|
||||||
|
char parity;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
struct win32_ser w_ser;
|
||||||
|
DCB old_dcb;
|
||||||
|
#else
|
||||||
|
/* Save old termios settings */
|
||||||
|
struct termios old_tios;
|
||||||
|
#endif
|
||||||
|
#if HAVE_DECL_TIOCSRS485
|
||||||
|
int serial_mode;
|
||||||
|
#endif
|
||||||
|
#if HAVE_DECL_TIOCM_RTS
|
||||||
|
int rts;
|
||||||
|
int rts_delay;
|
||||||
|
int onebyte_time;
|
||||||
|
void (*set_rts)(modbus_t *ctx, int on);
|
||||||
|
#endif
|
||||||
|
/* To handle many slaves on the same link */
|
||||||
|
int confirmation_to_ignore;
|
||||||
|
} modbus_rtu_t;
|
||||||
|
|
||||||
|
#endif /* MODBUS_RTU_PRIVATE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_RTU_H
|
||||||
|
#define MODBUS_RTU_H
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||||
|
* RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
|
||||||
|
*/
|
||||||
|
#define MODBUS_RTU_MAX_ADU_LENGTH 256
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *
|
||||||
|
modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
|
||||||
|
|
||||||
|
#define MODBUS_RTU_RS232 0
|
||||||
|
#define MODBUS_RTU_RS485 1
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
|
||||||
|
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
|
||||||
|
|
||||||
|
#define MODBUS_RTU_RTS_NONE 0
|
||||||
|
#define MODBUS_RTU_RTS_UP 1
|
||||||
|
#define MODBUS_RTU_RTS_DOWN 2
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
|
||||||
|
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx,
|
||||||
|
void (*set_rts)(modbus_t *ctx, int on));
|
||||||
|
|
||||||
|
MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
|
||||||
|
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_RTU_H */
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_TCP_PRIVATE_H
|
||||||
|
#define MODBUS_TCP_PRIVATE_H
|
||||||
|
|
||||||
|
#define _MODBUS_TCP_HEADER_LENGTH 7
|
||||||
|
#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
|
||||||
|
#define _MODBUS_TCP_PRESET_RSP_LENGTH 8
|
||||||
|
|
||||||
|
#define _MODBUS_TCP_CHECKSUM_LENGTH 0
|
||||||
|
|
||||||
|
/* In both structures, the transaction ID must be placed on first position
|
||||||
|
to have a quick access not dependent of the TCP backend */
|
||||||
|
typedef struct _modbus_tcp {
|
||||||
|
/* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
|
||||||
|
(page 23/46):
|
||||||
|
The transaction identifier is used to associate the future response
|
||||||
|
with the request. This identifier is unique on each TCP connection. */
|
||||||
|
uint16_t t_id;
|
||||||
|
/* TCP port */
|
||||||
|
int port;
|
||||||
|
/* IP address */
|
||||||
|
char ip[16];
|
||||||
|
} modbus_tcp_t;
|
||||||
|
|
||||||
|
typedef struct _modbus_tcp_pi {
|
||||||
|
/* Transaction ID */
|
||||||
|
uint16_t t_id;
|
||||||
|
/* TCP port */
|
||||||
|
int port;
|
||||||
|
/* Node */
|
||||||
|
char *node;
|
||||||
|
/* Service */
|
||||||
|
char *service;
|
||||||
|
} modbus_tcp_pi_t;
|
||||||
|
|
||||||
|
#endif /* MODBUS_TCP_PRIVATE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_TCP_H
|
||||||
|
#define MODBUS_TCP_H
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
/* Win32 with MinGW, supplement to <errno.h> */
|
||||||
|
#include <winsock2.h>
|
||||||
|
#if !defined(ECONNRESET)
|
||||||
|
#define ECONNRESET WSAECONNRESET
|
||||||
|
#endif
|
||||||
|
#if !defined(ECONNREFUSED)
|
||||||
|
#define ECONNREFUSED WSAECONNREFUSED
|
||||||
|
#endif
|
||||||
|
#if !defined(ETIMEDOUT)
|
||||||
|
#define ETIMEDOUT WSAETIMEDOUT
|
||||||
|
#endif
|
||||||
|
#if !defined(ENOPROTOOPT)
|
||||||
|
#define ENOPROTOOPT WSAENOPROTOOPT
|
||||||
|
#endif
|
||||||
|
#if !defined(EINPROGRESS)
|
||||||
|
#define EINPROGRESS WSAEINPROGRESS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MODBUS_TCP_DEFAULT_PORT 502
|
||||||
|
#define MODBUS_TCP_SLAVE 0xFF
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||||
|
* TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
|
||||||
|
*/
|
||||||
|
#define MODBUS_TCP_MAX_ADU_LENGTH 260
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port);
|
||||||
|
MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
|
||||||
|
MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
|
||||||
|
|
||||||
|
MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service);
|
||||||
|
MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
|
||||||
|
MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_TCP_H */
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_VERSION_H
|
||||||
|
#define MODBUS_VERSION_H
|
||||||
|
|
||||||
|
/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MAJOR (3)
|
||||||
|
|
||||||
|
/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MINOR (1)
|
||||||
|
|
||||||
|
/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||||
|
#define LIBMODBUS_VERSION_MICRO (11)
|
||||||
|
|
||||||
|
/* The full version, like 1.2.3 */
|
||||||
|
#define LIBMODBUS_VERSION 3.1.11
|
||||||
|
|
||||||
|
/* The full version, in string form (suited for string concatenation)
|
||||||
|
*/
|
||||||
|
#define LIBMODBUS_VERSION_STRING "3.1.11"
|
||||||
|
|
||||||
|
/* Numerically encoded version, eg. v1.2.3 is 0x010203 */
|
||||||
|
#define LIBMODBUS_VERSION_HEX \
|
||||||
|
((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \
|
||||||
|
(LIBMODBUS_VERSION_MICRO << 0))
|
||||||
|
|
||||||
|
/* Evaluates to True if the version is greater than @major, @minor and @micro
|
||||||
|
*/
|
||||||
|
#define LIBMODBUS_VERSION_CHECK(major, minor, micro) \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR > (major) || \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \
|
||||||
|
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \
|
||||||
|
LIBMODBUS_VERSION_MICRO >= (micro)))
|
||||||
|
|
||||||
|
#endif /* MODBUS_VERSION_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,338 @@
|
||||||
|
/*
|
||||||
|
* Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODBUS_H
|
||||||
|
#define MODBUS_H
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/* Add this for macros that defined unix flavor */
|
||||||
|
#if (defined(__unix__) || defined(unix)) && !defined(USG)
|
||||||
|
# include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# include <stdint.h>
|
||||||
|
#else
|
||||||
|
# include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "modbus-version.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# if defined(DLLBUILD)
|
||||||
|
/* define DLLBUILD when building the DLL */
|
||||||
|
# define MODBUS_API __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define MODBUS_API __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define MODBUS_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define MODBUS_BEGIN_DECLS extern "C" {
|
||||||
|
# define MODBUS_END_DECLS }
|
||||||
|
#else
|
||||||
|
# define MODBUS_BEGIN_DECLS
|
||||||
|
# define MODBUS_END_DECLS
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
MODBUS_BEGIN_DECLS
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OFF
|
||||||
|
#define OFF 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ON
|
||||||
|
#define ON 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Modbus function codes */
|
||||||
|
#define MODBUS_FC_READ_COILS 0x01
|
||||||
|
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
|
||||||
|
#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
|
||||||
|
#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
|
||||||
|
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
|
||||||
|
#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
|
||||||
|
#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
|
||||||
|
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
|
||||||
|
#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
|
||||||
|
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
|
||||||
|
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
|
||||||
|
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
|
||||||
|
|
||||||
|
#define MODBUS_BROADCAST_ADDRESS 0
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
|
||||||
|
* Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
|
||||||
|
* (chapter 6 section 11 page 29)
|
||||||
|
* Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_READ_BITS 2000
|
||||||
|
#define MODBUS_MAX_WRITE_BITS 1968
|
||||||
|
|
||||||
|
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
|
||||||
|
* Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
|
||||||
|
* (chapter 6 section 12 page 31)
|
||||||
|
* Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
|
||||||
|
* (chapter 6 section 17 page 38)
|
||||||
|
* Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_READ_REGISTERS 125
|
||||||
|
#define MODBUS_MAX_WRITE_REGISTERS 123
|
||||||
|
#define MODBUS_MAX_WR_WRITE_REGISTERS 121
|
||||||
|
#define MODBUS_MAX_WR_READ_REGISTERS 125
|
||||||
|
|
||||||
|
/* The size of the MODBUS PDU is limited by the size constraint inherited from
|
||||||
|
* the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256
|
||||||
|
* bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server
|
||||||
|
* address (1 byte) - CRC (2 bytes) = 253 bytes.
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_PDU_LENGTH 253
|
||||||
|
|
||||||
|
/* Consequently:
|
||||||
|
* - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256
|
||||||
|
* bytes.
|
||||||
|
* - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.
|
||||||
|
* so the maximum of both backend in 260 bytes. This size can used to allocate
|
||||||
|
* an array of bytes to store responses and it will be compatible with the two
|
||||||
|
* backends.
|
||||||
|
*/
|
||||||
|
#define MODBUS_MAX_ADU_LENGTH 260
|
||||||
|
|
||||||
|
/* Random number to avoid errno conflicts */
|
||||||
|
#define MODBUS_ENOBASE 112345678
|
||||||
|
|
||||||
|
/* Protocol exceptions */
|
||||||
|
enum {
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01,
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
|
||||||
|
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
|
||||||
|
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
|
||||||
|
MODBUS_EXCEPTION_ACKNOWLEDGE,
|
||||||
|
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
|
||||||
|
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
|
||||||
|
MODBUS_EXCEPTION_MEMORY_PARITY,
|
||||||
|
MODBUS_EXCEPTION_NOT_DEFINED,
|
||||||
|
MODBUS_EXCEPTION_GATEWAY_PATH,
|
||||||
|
MODBUS_EXCEPTION_GATEWAY_TARGET,
|
||||||
|
MODBUS_EXCEPTION_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
|
||||||
|
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
|
||||||
|
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
|
||||||
|
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
|
||||||
|
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
|
||||||
|
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
|
||||||
|
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
|
||||||
|
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
|
||||||
|
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
|
||||||
|
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
|
||||||
|
|
||||||
|
/* Native libmodbus error codes */
|
||||||
|
#define EMBBADCRC (EMBXGTAR + 1)
|
||||||
|
#define EMBBADDATA (EMBXGTAR + 2)
|
||||||
|
#define EMBBADEXC (EMBXGTAR + 3)
|
||||||
|
#define EMBUNKEXC (EMBXGTAR + 4)
|
||||||
|
#define EMBMDATA (EMBXGTAR + 5)
|
||||||
|
#define EMBBADSLAVE (EMBXGTAR + 6)
|
||||||
|
|
||||||
|
extern const unsigned int libmodbus_version_major;
|
||||||
|
extern const unsigned int libmodbus_version_minor;
|
||||||
|
extern const unsigned int libmodbus_version_micro;
|
||||||
|
|
||||||
|
typedef struct _modbus modbus_t;
|
||||||
|
|
||||||
|
/*! Memory layout in tab_xxx arrays is processor-endianness.
|
||||||
|
When receiving modbus data, it is converted to processor-endianness,
|
||||||
|
see read_registers().
|
||||||
|
*/
|
||||||
|
typedef struct _modbus_mapping_t {
|
||||||
|
int nb_bits;
|
||||||
|
int start_bits;
|
||||||
|
int nb_input_bits;
|
||||||
|
int start_input_bits;
|
||||||
|
int nb_input_registers;
|
||||||
|
int start_input_registers;
|
||||||
|
int nb_registers;
|
||||||
|
int start_registers;
|
||||||
|
uint8_t *tab_bits;
|
||||||
|
uint8_t *tab_input_bits;
|
||||||
|
uint16_t *tab_input_registers;
|
||||||
|
uint16_t *tab_registers;
|
||||||
|
} modbus_mapping_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MODBUS_ERROR_RECOVERY_NONE = 0,
|
||||||
|
MODBUS_ERROR_RECOVERY_LINK = (1 << 1),
|
||||||
|
MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2)
|
||||||
|
} modbus_error_recovery_mode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MODBUS_QUIRK_NONE = 0,
|
||||||
|
MODBUS_QUIRK_MAX_SLAVE = (1 << 1),
|
||||||
|
MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2),
|
||||||
|
MODBUS_QUIRK_ALL = 0xFF
|
||||||
|
} modbus_quirks;
|
||||||
|
|
||||||
|
MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave);
|
||||||
|
MODBUS_API int modbus_get_slave(modbus_t *ctx);
|
||||||
|
MODBUS_API int modbus_set_error_recovery(modbus_t *ctx,
|
||||||
|
modbus_error_recovery_mode error_recovery);
|
||||||
|
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
|
||||||
|
MODBUS_API int modbus_get_socket(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_connect(modbus_t *ctx);
|
||||||
|
MODBUS_API void modbus_close(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API void modbus_free(modbus_t *ctx);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_flush(modbus_t *ctx);
|
||||||
|
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
|
||||||
|
|
||||||
|
MODBUS_API const char *modbus_strerror(int errnum);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||||
|
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||||
|
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||||
|
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
|
||||||
|
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
|
||||||
|
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
|
||||||
|
MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx,
|
||||||
|
int write_addr,
|
||||||
|
int write_nb,
|
||||||
|
const uint16_t *src,
|
||||||
|
int read_addr,
|
||||||
|
int read_nb,
|
||||||
|
uint16_t *dest);
|
||||||
|
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
|
||||||
|
|
||||||
|
MODBUS_API modbus_mapping_t *
|
||||||
|
modbus_mapping_new_start_address(unsigned int start_bits,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
unsigned int start_input_bits,
|
||||||
|
unsigned int nb_input_bits,
|
||||||
|
unsigned int start_registers,
|
||||||
|
unsigned int nb_registers,
|
||||||
|
unsigned int start_input_registers,
|
||||||
|
unsigned int nb_input_registers);
|
||||||
|
|
||||||
|
MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits,
|
||||||
|
int nb_input_bits,
|
||||||
|
int nb_registers,
|
||||||
|
int nb_input_registers);
|
||||||
|
MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
|
||||||
|
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_send_raw_request_tid(modbus_t *ctx,
|
||||||
|
const uint8_t *raw_req,
|
||||||
|
int raw_req_length,
|
||||||
|
int tid);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
|
||||||
|
|
||||||
|
MODBUS_API int modbus_reply(modbus_t *ctx,
|
||||||
|
const uint8_t *req,
|
||||||
|
int req_length,
|
||||||
|
modbus_mapping_t *mb_mapping);
|
||||||
|
MODBUS_API int
|
||||||
|
modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code);
|
||||||
|
MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||||||
|
MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTILS FUNCTIONS
|
||||||
|
**/
|
||||||
|
|
||||||
|
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
|
||||||
|
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
|
||||||
|
#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
|
||||||
|
(((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \
|
||||||
|
((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3])
|
||||||
|
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \
|
||||||
|
(((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1])
|
||||||
|
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \
|
||||||
|
(((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1])
|
||||||
|
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
|
||||||
|
do { \
|
||||||
|
((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \
|
||||||
|
((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
|
||||||
|
do { \
|
||||||
|
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
|
||||||
|
do { \
|
||||||
|
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \
|
||||||
|
((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
|
||||||
|
MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits,
|
||||||
|
const uint8_t *tab_byte);
|
||||||
|
MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src,
|
||||||
|
int idx,
|
||||||
|
unsigned int nb_bits);
|
||||||
|
MODBUS_API float modbus_get_float(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
|
||||||
|
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
|
||||||
|
|
||||||
|
MODBUS_API void modbus_set_float(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
|
||||||
|
MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
|
||||||
|
|
||||||
|
#include "modbus-rtu.h"
|
||||||
|
#include "modbus-tcp.h"
|
||||||
|
|
||||||
|
MODBUS_END_DECLS
|
||||||
|
|
||||||
|
#endif /* MODBUS_H */
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "libmodbus/modbus.h"
|
||||||
|
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->resize(200, 60);
|
||||||
|
this->setWindowTitle("libmodbus slave test");
|
||||||
|
|
||||||
|
//pbRtu = new QPushButton("以modbus rtu方式读取地址1中前20个寄存器值", this);
|
||||||
|
pbRtu = new QPushButton("modbus rtu", this);
|
||||||
|
pbRtu->resize(200, 30);
|
||||||
|
|
||||||
|
//pbTcp=new QPushButton("以modbus tcp方式读取地址1中前20个寄存器值", this);
|
||||||
|
pbTcp = new QPushButton("modbus tcp", this);
|
||||||
|
pbTcp->resize(200, 30);
|
||||||
|
|
||||||
|
QWidget *w=new QWidget();
|
||||||
|
|
||||||
|
QVBoxLayout *layout=new QVBoxLayout();
|
||||||
|
layout->addWidget(pbRtu);
|
||||||
|
layout->addWidget(pbTcp);
|
||||||
|
|
||||||
|
w->setLayout(layout);
|
||||||
|
|
||||||
|
this->setCentralWidget(w);
|
||||||
|
|
||||||
|
connect(pbRtu, SIGNAL(clicked()), this, SLOT(doRtuQuery()));
|
||||||
|
connect(pbTcp, SIGNAL(clicked()), this, SLOT(doTcpQuery()));
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::doRtuQuery()
|
||||||
|
{
|
||||||
|
modbus_t *mb;
|
||||||
|
uint16_t tab_reg[32]= {0};
|
||||||
|
|
||||||
|
mb = modbus_new_rtu("\\\\.\\COM20", 9600, 'N', 8, 1); //相同的端口只能同时打开一个
|
||||||
|
|
||||||
|
//modbus_set_debug(mb, true);
|
||||||
|
modbus_set_slave(mb, 1); //设置modbus从机地址
|
||||||
|
modbus_rtu_set_serial_mode(mb, MODBUS_RTU_RS485);
|
||||||
|
|
||||||
|
modbus_connect(mb);
|
||||||
|
|
||||||
|
struct timeval t;
|
||||||
|
t.tv_sec=0;
|
||||||
|
t.tv_usec=1000000; //设置modbus超时时间为1000毫秒
|
||||||
|
modbus_set_response_timeout(mb, t.tv_usec,t.tv_usec);
|
||||||
|
|
||||||
|
// uint16_t buffer[8] = { 0 };
|
||||||
|
// for (int i = 0; i < 8; i++)
|
||||||
|
// {
|
||||||
|
// buffer[i] = rand();
|
||||||
|
// qDebug() << buffer[i];
|
||||||
|
// }
|
||||||
|
// modbus_write_registers(mb, 40001, 8, buffer);
|
||||||
|
|
||||||
|
int regs=modbus_read_registers(mb, 40001, 10, tab_reg);
|
||||||
|
|
||||||
|
QMessageBox::about(NULL, "报告", QString("Rtu读取寄存器的个数:%1").arg(regs));
|
||||||
|
modbus_close(mb);
|
||||||
|
modbus_free(mb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::doTcpQuery()
|
||||||
|
{
|
||||||
|
modbus_t *mb;
|
||||||
|
uint16_t tab_reg[32]= {0};
|
||||||
|
|
||||||
|
mb = modbus_new_tcp("127.0.0.1", 502); //由于是tcp client连接,在同一个程序中相同的端口可以连接多次。
|
||||||
|
modbus_set_slave(mb, 1); //从机地址
|
||||||
|
|
||||||
|
modbus_connect(mb);
|
||||||
|
|
||||||
|
struct timeval t;
|
||||||
|
t.tv_sec=0;
|
||||||
|
t.tv_usec=1000000; //设置modbus超时时间为1000毫秒,注意:经测试,如果没有成功建立tcp连接,则该设置无效。
|
||||||
|
modbus_set_response_timeout(mb,t.tv_usec,t.tv_usec);
|
||||||
|
|
||||||
|
int regs=modbus_read_registers(mb, 40001, 10, tab_reg);
|
||||||
|
|
||||||
|
QMessageBox::about(NULL, "报告", QString("Tcp读取寄存器的个数:%1").arg(regs));
|
||||||
|
modbus_close(mb);
|
||||||
|
modbus_free(mb);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void doTcpQuery();
|
||||||
|
void doRtuQuery();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
|
||||||
|
QPushButton* pbRtu;
|
||||||
|
QPushButton* pbTcp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget"/>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>26</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -0,0 +1,38 @@
|
||||||
|
QT += core gui
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
libmodbus/modbus-data.c \
|
||||||
|
libmodbus/modbus-rtu.c \
|
||||||
|
libmodbus/modbus-tcp.c \
|
||||||
|
libmodbus/modbus.c \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
libmodbus/config.h \
|
||||||
|
libmodbus/modbus-private.h \
|
||||||
|
libmodbus/modbus-rtu-private.h \
|
||||||
|
libmodbus/modbus-rtu.h \
|
||||||
|
libmodbus/modbus-tcp-private.h \
|
||||||
|
libmodbus/modbus-tcp.h \
|
||||||
|
libmodbus/modbus-version.h \
|
||||||
|
libmodbus/modbus.h \
|
||||||
|
mainwindow.h
|
||||||
|
|
||||||
|
LIBS += -lws2_32
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
Loading…
Reference in New Issue