IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> libmodus源码解读 -> 正文阅读

[C++知识库]libmodus源码解读

libmodbus 源码分析:
1. ?uint8_t 的头文件 ?#include "stdint.h"?
2. ? 两个最核心的结构体

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 (*prepare_response_tid) (const uint8_t *req, int *req_length);
? ? 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);
? ? 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;
? ? struct timeval response_timeout;
? ? struct timeval byte_timeout;
? ? struct timeval indication_timeout;
? ? const modbus_backend_t *backend; ? //把各种操作封装在一个结构体中,也可以不封装。
? ? void *backend_data;
};

typedef struct _modbus modbus_t;

这两个结构体定义在 modbus-private.h ?从中可以借鉴到一点 在取名时不对外的结构体或函数命名可以在前面加 "_"

3. ?应用:
----------------------------random-test-client.c-------------------------- 过程
modbus_t ctx = modbus_new_tcp("127.0.0.1", 1502); ? // ctx表示容器 ?在这里既可以表示cli 也可以表示ser
modbus_connect(ctx);
modbus_read_registers() ?位于modbus.c协议核心层
? ->read_registers ? ? ? 位于modbus.c协议核心层
? ? ?-> ? ?req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); ?//构建requeset基础信息

? ? ? ? ? ?rc = send_msg(ctx, req, req_length);
? ? ? ? ? ?if (rc > 0) {

? ? ? ? ? ?rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
? ? ? ? ? ?rc = check_confirmation(ctx, req, rsp, rc);
? ? ? ? ? ?offset = ctx->backend->header_length;
? ? ? ? ? ? for (i = 0; i < rc; i++) {
? ? ? ? ? ? /* shift reg hi_byte to temp OR with lo_byte */
? ? ? ? ? ? dest[i] = (rsp[offset + 2 + (i << 1)] << 8) |
? ? ? ? ? ? ? ? rsp[offset + 3 + (i << 1)];
? ? ? ? ? ?}
?? ??? ? ? return dest;


?? ??? ? ??
4. modbus_new_tcp 函数过程:
??
modbus_new_tcp-》
? ? modbus_t *ctx;
? ? modbus_tcp_t *ctx_tcp;
? ? ? ctx = (modbus_t *)malloc(sizeof(modbus_t));}
? ? ? _modbus_init_common(ctx); ?//给ctx 赋初始值 ?不管是modbus_tcp 还是modbus_rtu都是调用这个函数。
?? ? ?
? ? ctx->slave = MODBUS_TCP_SLAVE; ? ? ? //tcp?

? ? ctx->backend = &_modbus_tcp_backend; //tcp ?_modbus_tcp_backend 这个是一个已经定义并初始化了的结构体。 位于modbus-tcp.c

? ? ctx->backend_data = (modbus_tcp_t *)malloc(sizeof(modbus_tcp_t)); ?//tcp ?分配内存
?? ?
?? ?ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
?? ?->根据modbus_new_tcp 传入的参数填充ctx_tcp 也就是ctx->backend_data

?? ?>end

?? ??
5. ?? ??
?? ??
// 具体的modbus_backend_t?? ??
?? ?modbus-tcp.c ?_modbus_tcp_backend结构体变量。 全局
const modbus_backend_t _modbus_tcp_backend = {
? ? _MODBUS_BACKEND_TYPE_TCP,
? ? _MODBUS_TCP_HEADER_LENGTH,
? ? _MODBUS_TCP_CHECKSUM_LENGTH,
? ? MODBUS_TCP_MAX_ADU_LENGTH,
? ? _modbus_set_slave,
? ? _modbus_tcp_build_request_basis,
? ? _modbus_tcp_build_response_basis,
? ? _modbus_tcp_prepare_response_tid,
? ? _modbus_tcp_send_msg_pre,
? ? _modbus_tcp_send,
? ? _modbus_tcp_receive,
? ? _modbus_tcp_recv,
? ? _modbus_tcp_check_integrity,
? ? _modbus_tcp_pre_check_confirmation,
? ? _modbus_tcp_connect,
? ? _modbus_tcp_close,
? ? _modbus_tcp_flush,
? ? _modbus_tcp_select,
? ? _modbus_tcp_free
};?? ??
?? ??
6.?? ??
?? ??
//初始化modbus的公共部分
void _modbus_init_common(modbus_t *ctx)
{
? ? /* Slave and socket are initialized to -1 */
? ? ctx->slave = -1;
? ? ctx->s = -1;

? ? ctx->debug = FALSE;
? ? ctx->error_recovery = MODBUS_ERROR_RECOVERY_NONE;

? ? ctx->response_timeout.tv_sec = 0;
? ? ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT;

? ? ctx->byte_timeout.tv_sec = 0;
? ? ctx->byte_timeout.tv_usec = _BYTE_TIMEOUT;

? ? ctx->indication_timeout.tv_sec = 0;
? ? ctx->indication_timeout.tv_usec = 0;
}

7. ----------------------------random-test-server.c--------------------------
modbus_mapping_t定义了modbus的四种寄存器,并进行了内存数据映射,以方便快速访问和读取寄存器的值。

其他跟client差不多,多了一个modbus_mapping_new

modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int nb_registers, int nb_input_registers)
{
? ? return modbus_mapping_new_start_address(
? ? ? ? 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers);
}


modbus_mapping_new_start_address :映射内存,实际是分配了4个内存。

实现如下:
? ? modbus_mapping_t *mb_mapping;

? ? mb_mapping = (modbus_mapping_t *)malloc(sizeof(modbus_mapping_t)); //申请内存
? ? /* 0X */
? ? mb_mapping->nb_bits = nb_bits; ? nb:numbers 表示个数。
? ? mb_mapping->start_bits = start_bits;
? ? mb_mapping->tab_bits =(uint8_t *) malloc(nb_bits * sizeof(uint8_t)); 注意这里的长度
? ? memset(mb_mapping->tab_bits, 0, nb_bits * sizeof(uint8_t));


main函数实现:
? ? modbus_t *ctx;
? ? modbus_mapping_t *mb_mapping;
? ? ctx = modbus_new_tcp("127.0.0.1", 1502);
? ? mb_mapping = modbus_mapping_new(500, 500, 500, 500);
? ? s = modbus_tcp_listen(ctx, 1);
? ? modbus_tcp_accept(ctx, &s);
? ? rc = modbus_receive(ctx, query);
?? ?//free malloc
? ? modbus_mapping_free(mb_mapping);
? ? modbus_close(ctx);
? ? modbus_free(ctx);


8. 超时处理 如果需要精确到ms us可以 也可以参考这里面的。

比如里面用到延时的例子:
static void _sleep_response_timeout(modbus_t *ctx)
{
? ? /* Response timeout is always positive */
#ifdef _WIN32
? ? /* usleep doesn't exist on Windows */
? ? Sleep((ctx->response_timeout.tv_sec * 1000) +
? ? ? ? ? (ctx->response_timeout.tv_usec / 1000));
#else
? ? /* usleep source code */
? ? struct timespec request, remaining;
? ? request.tv_sec = ctx->response_timeout.tv_sec;
? ? request.tv_nsec = ((long int)ctx->response_timeout.tv_usec) * 1000;
? ? while (nanosleep(&request, &remaining) == -1 && errno == EINTR) {
? ? ? ? request = remaining;
? ? }
#endif
}
?? ?
?? ?
9.困惑已久的问题:使用enum 或#define ?场景:
如果所有的整型值是连续的建议使用enum,如果中间可能会有非连续的建议使用#define?
例如 libmodubs里面:
/* 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
};

/* 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

10 #define 宏定义
#define a b ,后接两个参数,表示用a代替b。
例如 :#define PI 3.14

? ? ? ?#define uint8_t ?unsigned char?

#define 后只有一个参数
定义宏替换为空字符串,可以理解为后一个参数为空字符串

11.?宏函数与函数:
一般来说,应该用宏去替换小的、可重复的代码段,这样可以使程序运行速度更快;当任务比较复杂,需要多行代码才能实现时,或者要求程序越小越好时,就应该使用函数。

11.总结:libmodubs 构建了两个结构体 modbus_t 和 modbus_backend_t ,其中modbus_t用来表示modbus client或者server 通称为ctx(容器),modbus_backend_t 封装了各种modbus操作,作为指针放置在modbus_t 里面。


modbus_backend_t * 指向不同的具体类型设备 tcp/rtu。重点在于理解面向对象的设计方法

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章           查看所有文章
加:2022-05-16 11:12:18  更:2022-05-16 11:13:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:18:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码