参考链接:https://blog.csdn.net/v6543210/article/details/127426450
https://blog.csdn.net/qq_38158479/article/details/120928043
当我们需要自己搞一个C/C++版的 modbus Server时,总想像C#里面借助个好用的库来实现,但是libmodbus这个库封装的并不好用,从官方的源码中连个example都没有,能参考的也就tests目录下有几个可以借鉴。
但是仔细看了一下,random-test-server.c 还是会阻塞的,单线程。与拿来即用的标准相差甚远。
如果需要实现对多个客户端提供服务,需要参考 bandwidth-server-many-up.c
本文借鉴这篇文章,进行了一点优化,实现了可以为多个客户端提供服务的modbus tcp Server,可以拿来即用。
使用libmodbus库开发modbusTcp从站(支持多个主站连接)_酸菜。的博客-CSDN博客_libmodbus tcp
如果需要自己实现逻辑可以直接在另一个线程函数中对modbus的变量进行修改。
#ifndef RDSMODBUSSLAVE_H
#define RDSMODBUSSLAVE_H
#include
#include
#include
#include
#include
#include
using namespace std;
/*如果是windows平台则要加载相应的静态库和头文件*/
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
//#include
#pragma comment(lib, "Ws2_32.lib")
//#pragma comment(lib, "modbus.lib")
/*linux平台*/
#else
//#include
#include
#include
#include
#include
#include
#include
#endif
//#define MAX_POINT 50000
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class RDSModbusSlave : public QObject
{
Q_OBJECT
public:
explicit RDSModbusSlave(QObject *parent = nullptr);
RDSModbusSlave(string host="0.0.0.0", uint16_t port=502);
~RDSModbusSlave();
public:
void recieveMessages();
bool modbus_set_slave_id(int id);
bool initModbus(std::string Host_Ip, int port, bool debugging);
uint8_t getTab_Input_Bits(int NumBit);
bool setTab_Input_Bits(int NumBit, uint8_t Value);
uint16_t getHoldingRegisterValue(int registerStartaddress);
float getHoldingRegisterFloatValue(int registerStartaddress);
bool setHoldingRegisterValue(int registerStartaddress, uint16_t Value);
bool setHoldingRegisterValue(int registerStartaddress, float Value);
bool setInputRegisterValue(int registerStartaddress, uint16_t Value);
bool setInputRegisterValue(int registerStartaddress, float Value);
private:
std::mutex slavemutex;
int m_errCount{ 0 };
int m_modbusSocket{ -1 };
bool m_initialized{ false };
modbus_t* ctx{ nullptr };
modbus_mapping_t* mapping{ nullptr };
/*Mapping*/
int m_numBits{ 60000 };
int m_numInputBits{ 60000 };
int m_numRegisters{ 60000 };
int m_numInputRegisters{ 60000 };
public:
void loadFromConfigFile();
void run();
signals:
};
/*annotation:
(1)https://www.jianshu.com/p/0ed380fa39eb
(2)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;*/
#endif // RDSMODBUSSLAVE_H
#include "rdsmodbusslave.h"
#ifdef _WIN32
typedef int socklen_t;
#endif
RDSModbusSlave::RDSModbusSlave(QObject *parent) : QObject(parent)
{
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief Constructor
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
RDSModbusSlave::RDSModbusSlave(string host, uint16_t port)
{
initModbus(host, port, false);
//TODO:
this->setHoldingRegisterValue(0, (uint16_t)0x1122);
this->setHoldingRegisterValue(3, (uint16_t)0x3022);
this->setHoldingRegisterValue(6, (uint16_t)0x6022);
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief Destructor
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
RDSModbusSlave::~RDSModbusSlave()
{
modbus_mapping_free(mapping);
modbus_close(ctx);
modbus_free(ctx);
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief 支持多个master同时连接
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
void RDSModbusSlave::recieveMessages()
{
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
int master_socket;
int rc;
fd_set refset;
fd_set rdset;
/* Maximum file descriptor number */
int fdmax;
/* Clear the reference set of socket */
FD_ZERO(&refset);
/* Add the server socket */
FD_SET(m_modbusSocket, &refset);
/* Keep track of the max file descriptor */
fdmax = m_modbusSocket;
while( true )
{
rdset = refset;
if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1)
{
perror("Server select() failure.");
break;
}
/* Run through the existing connections looking for data to be
* read */
for (master_socket = 0; master_socket <= fdmax; master_socket++)
{
if (!FD_ISSET(master_socket, &rdset))
{
continue;
}
if (master_socket == m_modbusSocket)
{
/* A client is asking a new connection */
socklen_t addrlen;
struct sockaddr_in clientaddr;
int newfd;
/* Handle new connections */
addrlen = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
newfd = accept(m_modbusSocket, (struct sockaddr *)&clientaddr, &addrlen);
if (newfd == -1)
{
perror("Server accept() error");
} else
{
FD_SET(newfd, &refset);
if (newfd > fdmax)
{
/* Keep track of the maximum */
fdmax = newfd;
}
printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd);
}
} else
{
modbus_set_socket(ctx, master_socket);
rc = modbus_receive(ctx, query);
if (rc > 0)
{
modbus_reply(ctx, query, rc, mapping);
} else if (rc == -1)
{
/* This example server in ended on connection closing or
* any errors. */
printf("Connection closed on socket %d\n", master_socket);
#ifdef _WIN32
closesocket(master_socket);
#else
close(master_socket);
#endif
/* Remove from reference set */
FD_CLR(master_socket, &refset);
if (master_socket == fdmax)
{
fdmax--;
}
}
}
}
}
m_initialized = false;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief modbus_set_slave_id
* @param id
* @version v1
* @return null
* @date 2021/10/19
**************************************************************/
bool RDSModbusSlave::modbus_set_slave_id(int id)
{
int rc = modbus_set_slave(ctx, id);
if (rc == -1)
{
fprintf(stderr, "Invalid slave id\n");
modbus_free(ctx);
return false;
}
return true;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief modbus initialization
* @param IP/PORT/debugflag
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
bool RDSModbusSlave::initModbus(std::string Host_Ip = "127.0.0.1", int port = 502, bool debugging = false)
{
ctx = modbus_new_tcp(Host_Ip.c_str(), port);
modbus_set_debug(ctx, debugging);
if (ctx == NULL)
{
fprintf(stderr, "There was an error allocating the modbus\n");
throw - 1;
}
m_modbusSocket = modbus_tcp_listen(ctx, 1);
/*设置线圈, 离散输入, 输入寄存器, 保持寄存器个数(数组元素个数))*/
mapping = modbus_mapping_new(m_numBits, m_numInputBits, m_numInputRegisters, m_numRegisters);
if (mapping == NULL)
{
fprintf(stderr, "Unable to assign mapping:%s\n", modbus_strerror(errno));
modbus_free(ctx);
m_initialized = false;
return false;
}
m_initialized = true;
return true;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief getTab_Input_Bits(获取输入寄存器某一位的值)
* @param NumBit(输入寄存器相应的bit位)
* @version v1
* @return null
* @date 2021/10/8
**************************************************************/
uint8_t RDSModbusSlave::getTab_Input_Bits(int NumBit)
{
if (!m_initialized)
{
return -1;
}
return mapping->tab_input_bits[NumBit];
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief setTab_Input_Bits(设置输入寄存器某一位的值)
* @param NumBit(输入寄存器的起始地址)
* @param Value(输入寄存器的值)
* @version v1
* @return null
* @date 2021/10/8
**************************************************************/
bool RDSModbusSlave::setTab_Input_Bits(int NumBit, uint8_t Value)
{
if (NumBit > (m_numInputBits - 1))
{
return false;
}
slavemutex.lock();
mapping->tab_input_bits[NumBit] = Value;
slavemutex.unlock();
return true;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief getRegisterValue(获取保存寄存器的值)
* @param registerStartaddress(保存寄存器的起始地址)
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
uint16_t RDSModbusSlave::getHoldingRegisterValue(int registerStartaddress)
{
if (!m_initialized)
{
return -1;
}
return mapping->tab_registers[registerStartaddress];
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief 获取寄存器里的浮点数
* @param registerStartaddress寄存器起始地址
* @version v1
* @return 两个uint16_t拼接而成的浮点值
* @date 2021/10/6
**************************************************************/
float RDSModbusSlave::getHoldingRegisterFloatValue(int registerStartaddress)
{
if (!m_initialized)
{
return -1.0f;
}
return modbus_get_float_badc(&mapping->tab_registers[registerStartaddress]);
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief setRegisterValue(设置保存寄存器的值,类型为uint16_t)
* @param registerStartaddress(保存寄存器的起始地址)
* @param Value(写入到保存寄存器里的值)
* @version v1
* @return null
* @date 2021/10/6
**************************************************************/
bool RDSModbusSlave::setHoldingRegisterValue(int registerStartaddress, uint16_t Value)
{
if (registerStartaddress > (m_numRegisters - 1))
{
return false;
}
slavemutex.lock();
mapping->tab_registers[registerStartaddress] = Value;
slavemutex.unlock();
return true;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief setRegisterFloatValue(设置浮点值)
* @param (Value:浮点值,registerStartaddress寄存器起始地址)
* @version v1
* @return null
* @date 2021/10/8
**************************************************************/
bool RDSModbusSlave::setHoldingRegisterValue(int registerStartaddress, float Value)
{
if (registerStartaddress > (m_numRegisters - 2))
{
return false;
}
/*小端模式*/
slavemutex.lock();
modbus_set_float(Value, &mapping->tab_registers[registerStartaddress]);
slavemutex.unlock();
return true;
}
bool RDSModbusSlave::setInputRegisterValue(int registerStartaddress, uint16_t Value)
{
if (registerStartaddress > (m_numRegisters - 1))
{
return false;
}
slavemutex.lock();
mapping->tab_input_registers[registerStartaddress] = Value;
slavemutex.unlock();
return true;
}
bool RDSModbusSlave::setInputRegisterValue(int registerStartaddress, float Value)
{
if (registerStartaddress > (m_numRegisters - 2))
{
return false;
}
/*小端模式*/
slavemutex.lock();
modbus_set_float(Value, &mapping->tab_input_registers[registerStartaddress]);
slavemutex.unlock();
return true;
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief loadFromConfigFile
* @version v1
* @return null
* @date 2021/10/18
**************************************************************/
void RDSModbusSlave::loadFromConfigFile()
{
}
/***************************************************************
* @file RDSModbusSlave.cpp
* @author seer-txj
* @brief run
* @version v1
* @return null
* @date 2021/10/18
**************************************************************/
void RDSModbusSlave::run()
{
std::thread loop([this]()
{
while (true)
{
if (m_initialized)
{
recieveMessages();
}
else
{
m_initialized = true;
}
}
});
loop.detach();
return;
}
#include "mainwindow.h"
#include
//#include "rdsmodbusslave.h"
using namespace std;
void modbusRunner(RDSModbusSlave* server)
{
server->recieveMessages();
}
RDSModbusSlave modServer("127.0.0.1", 502);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
std::thread modServerThread(modbusRunner, &modServer);
modServerThread.join();
return a.exec();
}