int Filling_Group(uint8_t *read_buffer, AddrGroup *group, int bytes_read)
{
int i = 0;
// 将数据填充到原始参数位置
for (i = 0; i < group->param_count; i++)
{
DEVICEID_PARAID *param = group->params[i];
int bit_offset = group->bit_offsets[i];
AddrParseResult parsed = parse_address(param->RegAddress);
// 计算数据在缓冲区中的位置
int addr_offset = parsed.base_addr - group->start_addr;
int buffer_offset = addr_offset * (group->is_double_byte ? 2 : 1);
if (buffer_offset < 0 || buffer_offset >= bytes_read)
{
printf("Invalid buffer offset: %d\n", buffer_offset);
continue;
}
// 处理位访问
if (bit_offset >= 0)
{
// 位访问 - 只处理单个位
uint8_t byte_value = read_buffer[buffer_offset];
uint8_t bit_value = extract_bit_value(byte_value, bit_offset);
// 将位值存储在第一个字节中
memset(param->RegByteValue, 0, sizeof(param->RegByteValue));
param->RegByteValue[0] = bit_value;
param->RegByteLen = 1;
}
else
{
// 字节访问 - 根据类型复制数据
int bytes_to_copy = group->is_double_byte ? 4 : 2;
if (buffer_offset + bytes_to_copy > bytes_read)
{
bytes_to_copy = bytes_read - buffer_offset;
}
memcpy(param->RegByteValue, read_buffer + buffer_offset, bytes_to_copy);
param->RegByteLen = bytes_to_copy;
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <stdbool.h>
#include <unistd.h>
#include <time.h>
/* 定义常量 */
#define MODBUS_MAX_REGISTERS 125 // modbus最多只支持采集125个
#define MAX_DATA_PACKET_SIZE 4096
// 地址类型宏定义
#define SINGLE_BYTE_TYPES “%V”, “%IX”, “%QX”, “%MX”, “%DBX”
#define DOUBLE_BYTE_TYPES “%MD”, “%DBD”, “%DBW”, “%DB”
#define BIT_ADDR_TYPES “%MX”, “%DBX”
#define SINGLE_BYTE_THRESHOLD 1 // 单字节地址连续阈值
#define DOUBLE_BYTE_THRESHOLD 2 // 双字节地址连续阈值
/* 寄存器类型枚举 /
typedef enum
{
REG_V, / V寄存器 /
REG_IX, / IX寄存器 /
REG_QX, / QX寄存器 /
REG_MX, / MX寄存器 /
REG_MW, / MW寄存器 /
REG_MD, / MD寄存器 /
REG_DBX, / DBX寄存器 /
REG_DBW, / DBW寄存器 /
REG_DBD, / DBD寄存器 /
REG_DB, / DB寄存器 /
REG_UNKNOWN / 未知类型 */
} RegType;
/* 寄存器类型属性 */
typedef struct
{
RegType type;
const char prefix; / 字符串前缀 /
bool is_bit_addressable; / 是否支持位寻址 /
int byte_size; / 字节大小(2或4) */
} RegTypeInfo;
/* 地址解析结果 /
typedef struct
{
RegType type; / 寄存器类型 /
int base_addr; / 基地址 /
int bit_offset; / 位偏移 (-1表示无位偏移) */
} AddrParseResult;
/* 定义参数位置信息结构体 */
typedef struct
{
DEVICEID_PARAID *param; // 参数指针
int device_index; // 设备在DeviceNumber数组中的索引
int data_index; // 设备数据在DeviceData数组中的索引
int param_index; // 参数在DeviceParaID数组中的索引
} ParamLocation;
/* 地址分组结构 /
typedef struct AddrGroup
{
RegType type; / 寄存器类型 /
int slave_addr; / 从机地址 /
int portfd; / 使用的485端口 /
int start_addr; / 起始地址 /
int end_addr; / 结束地址 /
int param_count; / 包含的参数数量 */
DEVICEID_PARAID *params; / 动态参数指针数组 */ int bit_offsets; / 位偏移数组 */ struct AddrGroup next; / 链表指针 */
} AddrGroup;
// 全局地址组链表头
AddrGroup *addr_groups_head;
void init_addr_groups(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv);
void print_addr_groups();
/* 获取寄存器类型信息 */
const RegTypeInfo *get_reg_type_info(RegType type);
void perform_collection(uint8_t *read_buffer, AddrGroup *group, const RegTypeInfo *info, int bytes_read);
/* 寄存器类型信息表 /
static const RegTypeInfo reg_type_info[] = {
{REG_V, “%V”, false, 2},
{REG_IX, “%IX”, false, 2},
{REG_QX, “%QX”, false, 2},
{REG_MX, “%MX”, true, 2}, / 虽然支持位寻址,但byte_size为2 /
{REG_MW, “%MW”, false, 2},
{REG_MD, “%MD”, false, 4},
{REG_DBX, “%DBX”, true, 2}, / 虽然支持位寻址,但byte_size为2 */
{REG_DBW, “%DBW”, false, 2},
{REG_DBD, “%DBD”, false, 4},
{REG_DB, “%DB”, false, 2},
{REG_UNKNOWN, “”, false, 0}};
/* 获取寄存器类型信息 */
const RegTypeInfo get_reg_type_info(RegType type)
{
size_t i;
for (i = 0; i < sizeof(reg_type_info) / sizeof(reg_type_info[0]); i++)
{
if (reg_type_info[i].type == type)
{
return ®_type_info[i];
}
}
return ®_type_info[sizeof(reg_type_info) / sizeof(reg_type_info[0]) - 1]; / 返回UNKNOWN */
}
/* 解析地址(支持位偏移) */
AddrParseResult parse_address(const char *reg_addr)
{
int i = 0;
AddrParseResult result = {REG_UNKNOWN, -1, -1};
const RegTypeInfo *info;
// 提取寄存器类型和数值地址 char type_str[10] = {0}; int addr_value = 0; int bit_offset = -1; // 解析地址字符串 if (sscanf(reg_addr, “%40-9%d.%d”, type_str, &addr_value, &bit_offset) >= 2 || sscanf(reg_addr, “%40-9%d”, type_str, &addr_value) >= 1) { // 转换为大写以便比较 for (i = 0; type_str[i]; i++) { type_str[i] = toupper(type_str[i]); } /* 确定寄存器类型 */ if (strcmp(type_str, “%V”) == 0) result.type = REG_V; else if (strcmp(type_str, “%IX”) == 0) result.type = REG_IX; else if (strcmp(type_str, “%QX”) == 0) result.type = REG_QX; else if (strcmp(type_str, “%MX”) == 0) result.type = REG_MX; else if (strcmp(type_str, “%MW”) == 0) result.type = REG_MW; else if (strcmp(type_str, “%MD”) == 0) result.type = REG_MD; else if (strcmp(type_str, “%DBX”) == 0) result.type = REG_DBX; else if (strcmp(type_str, “%DBW”) == 0) result.type = REG_DBW; else if (strcmp(type_str, “%DBD”) == 0) result.type = REG_DBD; else if (strcmp(type_str, “%DB”) == 0) result.type = REG_DB; else result.type = REG_UNKNOWN; result.base_addr = addr_value; result.bit_offset = bit_offset; } return result;
}
/* 获取从机地址 */
int get_slave_addr(DEVICE_NUMBER *dev_num)
{
return dev_num->Number;
}
/* 获取端口fd */
int get_portfd(int device_index, DEVICE_ARGV *device_argv)
{
int i;
for (i = 0; i < device_argv->Device_num; i++)
{
if (device_argv->DeviceArgvList[i].DeviceNumber_s[device_index] == ‘1’)
{
return device_argv->DeviceArgvList[i].portfd;
}
}
return -1;
}
/* 创建新的地址分组 */
AddrGroup *create_addr_group(RegType type, int slave_addr, int portfd, int base_addr)
{
AddrGroup *new_group = (AddrGroup *)malloc(sizeof(AddrGroup));
if (!new_group)
return NULL;
new_group->type = type; new_group->slave_addr = slave_addr; new_group->portfd = portfd; new_group->start_addr = base_addr; new_group->end_addr = base_addr; new_group->param_count = 0; new_group->next = NULL; /* 初始化动态数组 */ new_group->params = (DEVICEID_PARAID **)malloc(sizeof(DEVICEID_PARAID *) * 10); new_group->bit_offsets = (int *)malloc(sizeof(int) * 10); if (!new_group->params || !new_group->bit_offsets) { free(new_group->params); free(new_group->bit_offsets); free(new_group); return NULL; } return new_group;
}
/* 向地址组添加参数 */
bool add_param_to_group(AddrGroup *group, DEVICEID_PARAID param, int bit_offset)
{
/ 检查是否需要扩展数组 */
if (group->param_count % 10 == 0)
{
size_t new_size = (group->param_count + 10) * sizeof(DEVICEID_PARAID *);
DEVICEID_PARAID **new_params = (DEVICEID_PARAID **)realloc(
group->params, new_size);
if (!new_params) return false; group->params = new_params; int *new_offsets = (int )realloc( group->bit_offsets, (group->param_count + 10) * sizeof(int)); if (!new_offsets) return false; group->bit_offsets = new_offsets; } / 添加参数 */ group->params[group->param_count] = param; group->bit_offsets[group->param_count] = bit_offset; group->param_count++; return true;
}
/* 查找匹配的地址分组 */
AddrGroup *find_matching_group(RegType type, int slave_addr, int portfd, int base_addr)
{
AddrGroup *current = addr_groups_head;
const RegTypeInfo *info = get_reg_type_info(type);
int threshold;
int min_next;
while (current != NULL) { if (current->type == type && current->slave_addr == slave_addr && current->portfd == portfd) { /* 检查地址连续性 (考虑阈值) / threshold = info->byte_size / 2; / 使用字节大小计算阈值 */ min_next = current->end_addr + threshold; if (base_addr >= current->start_addr && base_addr <= min_next) { return current; } } current = current->next; } return NULL;
}
/* 参数排序比较函数 */
int compare_params(const void *a, const void *b)
{
const ParamLocation *pa = (const ParamLocation *)a;
const ParamLocation *pb = (const ParamLocation *)b;
AddrParseResult res_a = parse_address(pa->param->RegAddress); AddrParseResult res_b = parse_address(pb->param->RegAddress); // 先按类型排序 if (res_a.type != res_b.type) return res_a.type - res_b.type; // 相同类型按基地址排序 return res_a.base_addr - res_b.base_addr;
}
/* 初始化地址分组 */
void init_addr_groups(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv)
{
int i = 0;
int t1, t2, t3;
AddrGroup *current;
AddrGroup *next;
DEVICE_NUMBER *dev_num;
DEVICE_DATA *dev_data;
DEVICEID_PARAID *param;
AddrParseResult parsed;
AddrGroup *group;
int slave_addr;
int portfd;
/* 清空现有分组 / current = addr_groups_head; while (current != NULL) { next = current->next; free(current->params); free(current->bit_offsets); free(current); current = next; } addr_groups_head = NULL; / 第一步:收集所有参数位置信息 */ int total_params = 0; for (t1 = 0; t1 < device_para->DeviceNumberIndex; t1++) { dev_num = &device_para->DeviceNumber[t1]; for (t2 = 0; t2 < dev_num->DeviceDataIndex; t2++) { dev_data = &dev_num->DeviceData[t2]; total_params += dev_data->DeviceIdIndex; } } if (total_params == 0) return; // 创建参数位置数组 ParamLocation *param_locations = (ParamLocation )malloc(total_params * sizeof(ParamLocation)); if (!param_locations) return; int index = 0; for (t1 = 0; t1 < device_para->DeviceNumberIndex; t1++) { dev_num = &device_para->DeviceNumber[t1]; for (t2 = 0; t2 < dev_num->DeviceDataIndex; t2++) { dev_data = &dev_num->DeviceData[t2]; for (t3 = 0; t3 < dev_data->DeviceIdIndex; t3++) { param_locations[index].param = &dev_data->DeviceParaID[t3]; param_locations[index].device_index = t1; param_locations[index].data_index = t2; param_locations[index].param_index = t3; index++; } } } / 第二步:按类型和基地址排序 / qsort(param_locations, total_params, sizeof(ParamLocation), compare_params); / 第三步:处理排序后的参数 / for (i = 0; i < total_params; i++) { param = param_locations[i].param; parsed = parse_address(param->RegAddress); if (parsed.base_addr < 0 || parsed.type == REG_UNKNOWN) continue; // 直接从位置信息获取设备索引 t1 = param_locations[i].device_index; dev_num = &device_para->DeviceNumber[t1]; slave_addr = get_slave_addr(dev_num); portfd = get_portfd(t1, device_argv); if (portfd == -1) continue; / 查找匹配的分组 / group = find_matching_group(parsed.type, slave_addr, portfd, parsed.base_addr); if (group) { / 更新地址范围 / if (parsed.base_addr < group->start_addr) { group->start_addr = parsed.base_addr; } if (parsed.base_addr > group->end_addr) { group->end_addr = parsed.base_addr; } / 添加参数到组 / if (!add_param_to_group(group, param, parsed.bit_offset)) { printf(“Failed to add param to group!\n”); } } else { / 创建新分组 */ AddrGroup new_group = create_addr_group(parsed.type, slave_addr, portfd, parsed.base_addr); if (!new_group) { printf(“Failed to create new group!\n”); continue; } / 添加到链表 / if (!addr_groups_head) { addr_groups_head = new_group; } else { new_group->next = addr_groups_head; addr_groups_head = new_group; } / 添加第一个参数 */ if (!add_param_to_group(new_group, param, parsed.bit_offset)) { printf(“Failed to add param to new group!\n”); } } } free(param_locations);
}
/* 从位偏移中提取位值 */
uint8_t extract_bit_value(uint8_t byte_value, int bit_offset)
{
if (bit_offset < 0 || bit_offset > 7)
return 0;
return (byte_value >> bit_offset) & 0x01;
}
/* 执行采集 */
void perform_collection(uint8_t *read_buffer, AddrGroup *group, const RegTypeInfo *info, int bytes_read)
{
int i;
DEVICEID_PARAID *param;
int bit_offset;
AddrParseResult parsed;
int addr_offset;
int buffer_offset;
int bytes_to_copy;
uint8_t byte_value;
uint8_t bit_value;
/* 处理每个参数 / for (i = 0; i < group->param_count; i++) { param = group->params[i]; bit_offset = group->bit_offsets[i]; parsed = parse_address(param->RegAddress); / 计算数据在缓冲区中的位置 / addr_offset = parsed.base_addr - group->start_addr; buffer_offset = addr_offset * (info->byte_size / 2); / 2字节为单位 / if (buffer_offset < 0 || buffer_offset >= bytes_read) { printf(“Invalid buffer offset: %d\n”, buffer_offset); continue; } / 处理位访问 / if (bit_offset >= 0) { / 位寻址:数据长度为1字节 / byte_value = read_buffer[buffer_offset]; bit_value = extract_bit_value(byte_value, bit_offset); memset(param->RegByteValue, 0, sizeof(param->RegByteValue)); param->RegByteValue[0] = bit_value; param->RegByteLen = 1; } else { / 非位寻址:使用寄存器类型的byte_size */ bytes_to_copy = info->byte_size; if (buffer_offset + bytes_to_copy > bytes_read) { bytes_to_copy = bytes_read - buffer_offset; } memcpy(param->RegByteValue, read_buffer + buffer_offset, bytes_to_copy); param->RegByteLen = bytes_to_copy; } }
}
/* 主采集循环 */
void collection_loop(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv) {
uint32_t start_time;
uint32_t elapsed;
AddrGroup *current;
int i;
/* 初始化分组 / init_addr_groups(device_para, device_argv); print_addr_groups(); / 主循环 / while (1) { / 执行采集 / perform_single_collection(); / 等待下一个采集周期 */ }
}
/* 打印分组信息 */
void print_addr_groups()
{
AddrGroup *current = addr_groups_head;
int group_num = 0;
const RegTypeInfo *info;
printf(“\n===== Address Groups =\n"); while (current != NULL) { info = get_reg_type_info(current->type); printf(“Group %d: %s, slave=%d, portfd=%d, addr_range=%d-%d, params=%d\n”, group_num, info->prefix, current->slave_addr, current->portfd, current->start_addr, current->end_addr, current->param_count); current = current->next; group_num++; } printf("======================\n\n”);
}
// 执行单次采集
void perform_single_collection() {
AddrGroup *current = addr_groups_head;
uint32_t current_time = get_current_timestamp();
while (current != NULL) { AddrGroup group = current; current = current->next; // 检查是否需要采集 if (current_time - group->last_collection_time < COLLECTION_INTERVAL_MS) { continue; } group->last_collection_time = current_time; group->needs_collection = false; const RegTypeInfo info = get_reg_type_info(group->type); int reg_count = group->end_addr - group->start_addr + 1; // 根据数据类型调整寄存器计数 if (info->is_double_byte) { reg_count = (reg_count + 1) / 2; } if (reg_count > MODBUS_MAX_REGISTERS) { reg_count = MODBUS_MAX_REGISTERS; } // 分配缓冲区 uint8_t read_buffer = (uint8_t)malloc(reg_count * 2); if (!read_buffer) { printf(“Memory allocation failed for read buffer!\n”); continue; } // 执行MODBUS读取 int bytes_read = modbus_read(group->portfd, group->slave_addr, group->start_addr, reg_count, read_buffer); if (bytes_read != reg_count * 2) { printf(“MODBUS read error! Expected %d bytes, got %d\n”, reg_count * 2, bytes_read); free(read_buffer); continue; } // 处理每个参数 for (int i = 0; i < group->param_count; i++) { DEVICEID_PARAID *param = group->params[i]; int bit_offset = group->bit_offsets[i]; AddrParseResult parsed = parse_address(param->RegAddress); // 计算数据在缓冲区中的位置 int addr_offset = parsed.base_addr - group->start_addr; int buffer_offset = addr_offset * (info->is_double_byte ? 2 : 1); if (buffer_offset < 0 || buffer_offset >= bytes_read) { printf(“Invalid buffer offset: %d\n”, buffer_offset); continue; } // 处理位访问 if (bit_offset >= 0) { uint8_t byte_value = read_buffer[buffer_offset]; uint8_t bit_value = extract_bit_value(byte_value, bit_offset); memset(param->RegByteValue, 0, sizeof(param->RegByteValue)); param->RegByteValue[0] = bit_value; } else { int bytes_to_copy = info->byte_size; if (buffer_offset + bytes_to_copy > bytes_read) { bytes_to_copy = bytes_read - buffer_offset; } memcpy(param->RegByteValue, read_buffer + buffer_offset, bytes_to_copy); } param->lastUpdate = current_time; } free(read_buffer); }
}
// 测试函数
void test_parse_address() {
const char *test_addresses[] = {
“MX10”, “MX10.3”, “DBD200”, “V123”, “QX55”, “UNKNOWN30”
};
printf(“===== Address Parsing Test =\n"); for (int i = 0; i < sizeof(test_addresses)/sizeof(test_addresses[0]); i++) { AddrParseResult result = parse_address(test_addresses[i]); const RegTypeInfo* info = get_reg_type_info(result.type); printf(“Address: %s\n”, test_addresses[i]); printf(" Type: %s (%d)\n", info->prefix, result.type); printf(" Base: %d\n", result.base_addr); printf(" Bit offset: %d\n", result.bit_offset); printf(" Byte size: %d\n", info->byte_size); printf(" Bit addressable: %s\n", info->is_bit_addressable ? “yes” : “no”); printf(" Double byte: %s\n\n", info->is_double_byte ? “yes” : “no”); } printf("===========================\n\n”);
}
int main() {
// 运行地址解析测试
test_parse_address();
// 初始化设备参数和采集参数 DEVICE_PARA device_para = {0}; DEVICE_ARGV device_argv = {0}; // 设置示例数据 device_para.DeviceNumberIndex = 2; device_para.DeviceNumber = (DEVICE_NUMBER*)malloc(2 * sizeof(DEVICE_NUMBER)); memset(device_para.DeviceNumber, 0, 2 * sizeof(DEVICE_NUMBER)); // 设备1 (包含位访问) device_para.DeviceNumber[0].Number = 1; device_para.DeviceNumber[0].DeviceDataIndex = 1; device_para.DeviceNumber[0].DeviceData = (DEVICE_DATA*)malloc(sizeof(DEVICE_DATA)); memset(device_para.DeviceNumber[0].DeviceData, 0, sizeof(DEVICE_DATA)); device_para.DeviceNumber[0].DeviceData->DeviceIdIndex = 3; device_para.DeviceNumber[0].DeviceData->DeviceParaID = (DEVICEID_PARAID*)malloc(3 * sizeof(DEVICEID_PARAID)); strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[0].RegAddress, “MX10”); device_para.DeviceNumber[0].DeviceData->DeviceParaID[0].ParaId = 101; strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].RegAddress, “MX10.3”); device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].ParaId = 102; strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[2].RegAddress, “MD20”); device_para.DeviceNumber[0].DeviceData->DeviceParaID[2].ParaId = 103; // 设备2 (双字节类型) device_para.DeviceNumber[1].Number = 2; device_para.DeviceNumber[1].DeviceDataIndex = 1; device_para.DeviceNumber[1].DeviceData = (DEVICE_DATA*)malloc(sizeof(DEVICE_DATA)); memset(device_para.DeviceNumber[1].DeviceData, 0, sizeof(DEVICE_DATA)); device_para.DeviceNumber[1].DeviceData->DeviceIdIndex = 2; device_para.DeviceNumber[1].DeviceData->DeviceParaID = (DEVICEID_PARAID*)malloc(2 * sizeof(DEVICEID_PARAID)); strcpy(device_para.DeviceNumber[1].DeviceData->DeviceParaID[0].RegAddress, “DBD200”); device_para.DeviceNumber[1].DeviceData->DeviceParaID[0].ParaId = 201; strcpy(device_para.DeviceNumber[1].DeviceData->DeviceParaID[1].RegAddress, “DBX55”); device_para.DeviceNumber[1].DeviceData->DeviceParaID[1].ParaId = 202; // 设置采集参数 device_argv.Device_num = 2; device_argv.DeviceArgvList[0].portfd = 1; strcpy(device_argv.DeviceArgvList[0].DeviceNumber_s, “10”); device_argv.DeviceArgvList[1].portfd = 2; strcpy(device_argv.DeviceArgvList[1].DeviceNumber_s, “01”); // 进入采集循环 collection_loop(&device_para, &device_argv); // 释放资源 cleanup(&device_para); return 0;
}
采用的是MODBUS采集,还须区分采集的从机地址相同,地址连续的才能合并在一起一次采集,从机地址是DevicePara_t->DeviceNumber[t1].Number决定 ,并且我有两个RS485接口及两个fd,可以随意确定使用那个485采集也用DevicePara_t->DeviceNumber[t1].Number判断决定吧
也不一定是奇数用4851偶数用4852,有可能全用一个485采集
采集寄存器个数和返回值长度关系是:个数*2=长度
这里有以下改动:首先地址分组规则需要改动,首先还是fd优于从机地址优于地址大小排序,然后对地址分组,首先在初始化函数会加一个参数用于区分西门子还是施耐德,还有RegType有变动,现在为QX BDX M MB MW BDW VW DBD MD IX MX ,其中当参数表示西门子时IX MX 和QX分在一组,功能码标志为1 ,表示为施莱德时IX MX 和DBX M分在一组功能码标志为2,剩下其他的功能码都标志为3,但是其中DBD和MD byte_size字节大小为4,采集个数为2,地址连续判断为2,其余的都是字节大小为2,采集个数为1,地址连续判断为1,还希望有些地址中间不连续但相差很小可以分在一组一次采集, 因为在功能码03中采集个数不一致,所以在最后放在原来结构体时需要注意偏移量,依旧支持有些寄存器的位寻址,有些在同一组内地址是相同,计算采集个数时需要注意,fro循环内不能定义变量,也不需要间隔时间,请生成完整C语言代码,不能是伪代码