使用libmodbus库开发modbusTcp从站(支持多个主站连接)

Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)

参考链接: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的变量进行修改。

rdsmodbusslave.h

#ifndef RDSMODBUSSLAVE_H
#define RDSMODBUSSLAVE_H

#include <iostream>
#include <thread>
#include <stdlib.h>
#include <iostream>
#include <mutex>
#include <string>
using namespace std;
/*如果是windows平台则要加载相应的静态库和头文件*/
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
//#include <modbus.h>
#pragma comment(lib, "Ws2_32.lib")
//#pragma comment(lib, "modbus.lib")
/*linux平台*/
#else
//#include <modbus/modbus.h>
#include <unistd.h>
#include <error.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#endif


//#define  MAX_POINT  50000
#include <QObject>
#include <QThread>
#include <stdio.h>
#include <libmodbus/config.h>
#include <libmodbus/modbus.h>
#include <libmodbus/modbus-rtu.h>
#include <QTimer>
#include <QDebug>
#include <QStringList>
#include <QSerialPortInfo>
#include <QSerialPort>

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

rdsmodbusslave.cpp

#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;
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

//#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();
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值