#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 TCP_IP_MAXSIZE 2
#define MODBUS_MAX_REGISTERS 125
#define MAX_ADDR_STR_LEN 20
#define MAX_DATA_PACKET_SIZE 4096
#define COLLECTION_INTERVAL_MS 1000
/* 寄存器类型枚举 */
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;
/* 寄存器类型信息表 */
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}
};
/* 设备参数列表结构体 */
typedef struct {
int portfd; /* 485端口号 */
int plc_addr; /* PLC设备地址 */
char plc_ip[20]; /* TCP IP地址 */
int DeviceNumber; /* PLC编号 */
char DeviceNumber_s[16]; /* 采集设备集合标识 */
} DEVICE_ARGV_LIST;
/* 采集多个设备 */
typedef struct {
int Device_num; /* 采集PLC个数 */
DEVICE_ARGV_LIST DeviceArgvList[TCP_IP_MAXSIZE]; /* 参数列表集 */
} DEVICE_ARGV;
/* 参数结构体 */
typedef struct {
int ParaId; /* 参数ID */
char DataType; /* 参数数据类型 */
char RegAddress[MAX_ADDR_STR_LEN]; /* 参数地址 */
uint8_t RegByteValue[8];/* 采集到的寄存器值 */
} DEVICEID_PARAID;
/* 设备数据结构体 */
typedef struct {
char DeviceIdIndex; /* 参数个数 */
uint8_t TypeLen; /* DeviceType长度 */
uint8_t DeviceType[20]; /* 设备类型+ID */
DEVICEID_PARAID *DeviceParaID; /* 参数列表 */
} DEVICE_DATA;
/* 设备编号结构体 */
typedef struct {
char Number; /* 设备编号 */
char DeviceDataIndex; /* 数据库地址个数 */
DEVICE_DATA *DeviceData; /* 数据库集 */
} DEVICE_NUMBER;
/* 设备参数主结构体 */
typedef struct {
char date[14]; /* 时间 */
char DeviceNumberIndex; /* 设备数 */
char Poffset; /* 偏移 */
char SampleFre; /* 采集频率 */
char MessageId[10]; /* 消息ID */
DEVICE_NUMBER *DeviceNumber; /* 采集设备集 */
} DEVICE_PARA;
/* 地址解析结果 */
typedef struct {
RegType type; /* 寄存器类型 */
int base_addr; /* 基地址 */
int bit_offset; /* 位偏移 (-1表示无位偏移) */
} AddrParseResult;
/* 地址分组结构 */
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;
/* 数据包结构 */
typedef struct {
uint8_t data[MAX_DATA_PACKET_SIZE];
size_t size;
uint32_t timestamp;
} DataPacket;
/* 全局变量 */
static AddrGroup *addr_groups_head = NULL;
static DataPacket current_packet = {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 */
}
/* 获取当前时间戳(毫秒) */
uint32_t get_current_timestamp() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
/* 解析地址(支持位偏移) */
AddrParseResult parse_address(const char *reg_addr) {
AddrParseResult result = {REG_UNKNOWN, -1, -1};
char buf[MAX_ADDR_STR_LEN] = {0};
char *type_end;
char *addr_part;
char *dot_ptr;
const RegTypeInfo* info;
/* 复制地址字符串到缓冲区 */
strncpy(buf, reg_addr, MAX_ADDR_STR_LEN - 1);
/* 查找寄存器类型部分 */
type_end = strchr(buf, ' ');
if (!type_end) return result;
/* 提取寄存器类型字符串 */
*type_end = '\0'; /* 在空格处截断 */
/* 确定寄存器类型 */
if (strcmp(buf, "%V") == 0)
result.type = REG_V;
else if (strcmp(buf, "%IX") == 0)
result.type = REG_IX;
else if (strcmp(buf, "%QX") == 0)
result.type = REG_QX;
else if (strcmp(buf, "%MX") == 0)
result.type = REG_MX;
else if (strcmp(buf, "%MW") == 0)
result.type = REG_MW;
else if (strcmp(buf, "%MD") == 0)
result.type = REG_MD;
else if (strcmp(buf, "%DBX") == 0)
result.type = REG_DBX;
else if (strcmp(buf, "%DBW") == 0)
result.type = REG_DBW;
else if (strcmp(buf, "%DBD") == 0)
result.type = REG_DBD;
else if (strcmp(buf, "%DB") == 0)
result.type = REG_DB;
else
result.type = REG_UNKNOWN;
/* 解析地址数值部分 */
addr_part = type_end + 1;
dot_ptr = strchr(addr_part, '.');
if (dot_ptr) {
/* 处理带位偏移的地址 */
*dot_ptr = '\0';
result.base_addr = atoi(addr_part);
result.bit_offset = atoi(dot_ptr + 1);
/* 验证是否支持位访问 */
info = get_reg_type_info(result.type);
if (!info->is_bit_addressable) {
result.bit_offset = -1;
}
} else {
/* 没有位偏移 */
result.base_addr = atoi(addr_part);
result.bit_offset = -1;
}
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;
}
/* 初始化地址分组 */
void init_addr_groups(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv) {
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;
for (t1 = 0; t1 < device_para->DeviceNumberIndex; t1++) {
dev_num = &device_para->DeviceNumber[t1];
slave_addr = get_slave_addr(dev_num);
portfd = get_portfd(t1, device_argv);
if (portfd == -1) continue;
for (t2 = 0; t2 < dev_num->DeviceDataIndex; t2++) {
dev_data = &dev_num->DeviceData[t2];
for (t3 = 0; t3 < dev_data->DeviceIdIndex; t3++) {
param = &dev_data->DeviceParaID[t3];
parsed = parse_address(param->RegAddress);
if (parsed.base_addr < 0 || parsed.type == REG_UNKNOWN) 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");
}
}
}
}
}
}
/* 从位偏移中提取位值 */
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;
}
/* 模拟MODBUS读取函数 */
int modbus_read(int portfd, int slave_addr, int start_addr, int reg_count, uint8_t *buffer) {
/* 实际应用中这里应调用MODBUS库函数 */
printf("Reading MODBUS: portfd=%d, slave=%d, addr=%d, count=%d\n",
portfd, slave_addr, start_addr, reg_count);
int i;
for (i = 0; i < reg_count * 2; i++) {
buffer[i] = (uint8_t)((start_addr + i) % 256); /* 模拟数据 */
}
return reg_count * 2;
}
/* 执行单次采集 */
void perform_single_collection() {
AddrGroup *current = addr_groups_head;
AddrGroup *group;
const RegTypeInfo* info;
int reg_count;
uint8_t *read_buffer;
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;
while (current != NULL) {
group = current;
current = current->next;
info = get_reg_type_info(group->type);
reg_count = group->end_addr - group->start_addr + 1;
/* 根据数据类型调整寄存器计数 */
reg_count = (reg_count + info->byte_size - 1) / info->byte_size;
if (reg_count > MODBUS_MAX_REGISTERS) {
reg_count = MODBUS_MAX_REGISTERS;
}
/* 分配缓冲区 */
read_buffer = (uint8_t*)malloc(reg_count * 2);
if (!read_buffer) {
printf("Memory allocation failed for read buffer!\n");
continue;
}
/* 执行MODBUS读取 */
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 (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;
} 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);
}
}
free(read_buffer);
}
}
/* 将数据添加到数据包 */
void add_to_data_packet(DEVICEID_PARAID *param) {
const RegTypeInfo* info;
AddrParseResult parsed;
size_t required_size;
uint8_t data_len;
/* 解析地址获取数据类型 */
parsed = parse_address(param->RegAddress);
info = get_reg_type_info(parsed.type);
/* 计算所需空间 */
if (parsed.bit_offset >= 0) {
/* 位寻址:数据长度1字节 */
data_len = 1;
} else {
/* 非位寻址:使用寄存器类型的byte_size */
data_len = (uint8_t)info->byte_size;
}
required_size = sizeof(int) + sizeof(uint8_t) + data_len;
if (current_packet.size + required_size > MAX_DATA_PACKET_SIZE) {
send_data_packet();
}
/* 添加时间戳(如果新包) */
if (current_packet.size == 0) {
current_packet.timestamp = get_current_timestamp();
memcpy(current_packet.data, ¤t_packet.timestamp, sizeof(current_packet.timestamp));
current_packet.size += sizeof(current_packet.timestamp);
}
/* 添加参数ID */
memcpy(current_packet.data + current_packet.size, ¶m->ParaId, sizeof(param->ParaId));
current_packet.size += sizeof(param->ParaId);
/* 添加数据长度 */
memcpy(current_packet.data + current_packet.size, &data_len, sizeof(data_len));
current_packet.size += sizeof(data_len);
/* 添加数据内容 */
memcpy(current_packet.data + current_packet.size, param->RegByteValue, data_len);
current_packet.size += data_len;
}
/* 发送数据包 */
void send_data_packet() {
size_t i;
if (current_packet.size == 0) return;
/* 模拟发送数据包 */
printf("\n===== Sending Data Packet =====\n");
printf("Timestamp: %u\n", current_packet.timestamp);
printf("Size: %zu bytes\n", current_packet.size);
printf("Content:\n");
for (i = 0; i < current_packet.size; i++) {
printf("%02X ", current_packet.data[i]);
if ((i+1) % 16 == 0) printf("\n");
}
printf("\n===============================\n\n");
/* 重置数据包 */
current_packet.size = 0;
}
/* 主采集循环 */
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) {
start_time = get_current_timestamp();
/* 执行采集 */
perform_single_collection();
/* 将采集到的数据添加到数据包 */
current = addr_groups_head;
while (current != NULL) {
for (i = 0; i < current->param_count; i++) {
add_to_data_packet(current->params[i]);
}
current = current->next;
}
/* 发送数据包 */
send_data_packet();
/* 等待下一个采集周期 */
elapsed = get_current_timestamp() - start_time;
if (elapsed < COLLECTION_INTERVAL_MS) {
usleep((COLLECTION_INTERVAL_MS - elapsed) * 1000);
}
}
}
/* 打印分组信息 */
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 test_parse_address() {
const char *test_addresses[] = {
"%MX 10", "%MX 10.3", "%DBD 200", "%V 123", "%QX 55", "%UNKNOWN 30",
"%DBX 100.5", "%MD 500", "%DB 300"
};
int i;
AddrParseResult result;
const RegTypeInfo* info;
printf("===== Address Parsing Test =====\n");
for (i = 0; i < sizeof(test_addresses)/sizeof(test_addresses[0]); i++) {
result = parse_address(test_addresses[i]);
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("\n");
}
printf("===============================\n\n");
}
int main() {
/* 运行地址解析测试 */
test_parse_address();
/* 初始化设备参数和采集参数 */
DEVICE_PARA device_para = {0};
DEVICE_ARGV device_argv = {0};
int i, j, k;
/* 设置示例数据 */
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, "%MX 10");
device_para.DeviceNumber[0].DeviceData->DeviceParaID[0].ParaId = 101;
strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].RegAddress, "%MX 10.3");
device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].ParaId = 102;
strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[2].RegAddress, "%MD 20");
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, "%DBD 200");
device_para.DeviceNumber[1].DeviceData->DeviceParaID[0].ParaId = 201;
strcpy(device_para.DeviceNumber[1].DeviceData->DeviceParaID[1].RegAddress, "%DBX 55.2");
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);
/* 释放资源 */
for (i = 0; i < device_para.DeviceNumberIndex; i++) {
for (j = 0; j < device_para.DeviceNumber[i].DeviceDataIndex; j++) {
free(device_para.DeviceNumber[i].DeviceData[j].DeviceParaID);
}
free(device_para.DeviceNumber[i].DeviceData);
}
free(device_para.DeviceNumber);
return 0;
}
帮我查看这份代码有没有将同一个fd,同一个从机地址同一个采集类型(指Md,DBD等)的所有采集地址RegAddress排序后只有前后地址超过设置的阈值后才分在一个组内,实现采集地址的分组高效采集
最新发布