397 lines
14 KiB
C
397 lines
14 KiB
C
/**
|
||
* @file can_frame.h
|
||
* @brief CAN 帧数据域组帧/解帧 — 电力二次设备协议栈 (v2.0)
|
||
*
|
||
* v2.0 变更:
|
||
* - 遥信从 1-bit 改为 2-bit 双位置 (IEC 61850 DPS)
|
||
* - 遥测从 uint16 改为 float (IEEE 754 大端)
|
||
* - 增加突发打包格式 (一帧可携带多个突变点)
|
||
* - 定时数据与突发数据使用不同帧格式
|
||
*
|
||
* 遥信双位置编码:
|
||
* 00 = 中间态/不确定 (intermediate)
|
||
* 01 = 分 (OFF)
|
||
* 10 = 合 (ON)
|
||
* 11 = 无效/故障 (invalid/faulty)
|
||
*
|
||
* 遥测浮点:IEEE 754 single-precision, 大端字节序 (与网络字节序一致)
|
||
*/
|
||
|
||
#ifndef CAN_FRAME_H
|
||
#define CAN_FRAME_H
|
||
|
||
#include <stdint.h>
|
||
#include <linux/can.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* ================================================================
|
||
* 常量
|
||
* ================================================================ */
|
||
#define CAN2_MAX_DLEN 8 /* CAN 2.0 最大数据长度 */
|
||
#define CANFD_MAX_DLEN 64 /* CAN FD 最大数据长度 */
|
||
|
||
/* 遥信双位置编码 */
|
||
#define YX_DPS_INTERMEDIATE 0 /* 中间态 */
|
||
#define YX_DPS_OFF 1 /* 分 */
|
||
#define YX_DPS_ON 2 /* 合 */
|
||
#define YX_DPS_INVALID 3 /* 无效/故障 */
|
||
|
||
/* 遥测浮点数特殊值 */
|
||
#define YC_FLOAT_NAN 0x7FC00000 /* NaN — 无效/未初始化 */
|
||
#define YC_FLOAT_OVERFLOW 0x7F800000 /* +Inf — 溢出/超量程 */
|
||
|
||
/* 突发队列容量 */
|
||
#define BURST_QUEUE_MAX 256 /* 最大排队点数 */
|
||
#define BURST_MAX_PER_FRAME 9 /* CAN FD 单帧最多打包点数 (每个点7字节) */
|
||
|
||
/* ================================================================
|
||
* 错误码
|
||
* ================================================================ */
|
||
typedef enum {
|
||
ERR_OK = 0x0000,
|
||
ERR_INVALID_POINT = 0x0001,
|
||
ERR_NOT_SELECTED = 0x0002,
|
||
ERR_TIMEOUT = 0x0003,
|
||
ERR_ADDR_OUT_RANGE = 0x0004,
|
||
ERR_VALUE_OUT_RANGE = 0x0005,
|
||
ERR_WRITE_FLASH_FAIL = 0x0006,
|
||
ERR_CRC_FAIL = 0x0007,
|
||
ERR_FILE_NOT_FOUND = 0x0008,
|
||
ERR_SEG_OUT_ORDER = 0x0009,
|
||
ERR_BUSY = 0x000A,
|
||
ERR_UNKNOWN_CMD = 0x000B,
|
||
} can_err_code_e;
|
||
|
||
/* ================================================================
|
||
* 数据域结构体 — 过程数据 (遥信/遥测)
|
||
* ================================================================ */
|
||
|
||
/**
|
||
* 定时遥信帧 — 多点上送
|
||
* 数据域格式: 起始地址(2B) + 遥信位对数据(N字节)
|
||
* 每字节 = 4 个遥信点 (每点 2-bit)
|
||
* bit[1:0] = 点0, bit[3:2] = 点1, bit[5:4] = 点2, bit[7:6] = 点3
|
||
*/
|
||
typedef struct {
|
||
uint16_t start_addr; /* 遥信起始地址 */
|
||
uint8_t data[CANFD_MAX_DLEN - 2]; /* 位对数据 */
|
||
uint8_t data_len; /* 有效字节数 (每字节=4个点) */
|
||
} can_yx_timed_t;
|
||
|
||
/**
|
||
* 定时遥测帧 — 多点上送
|
||
* 数据域格式: 起始地址(2B) + 浮点值数组 (每个4字节, IEEE 754)
|
||
*/
|
||
typedef struct {
|
||
uint16_t start_addr; /* 遥测起始地址 */
|
||
float values[(CANFD_MAX_DLEN - 2) / 4]; /* 浮点值数组 */
|
||
uint8_t value_count; /* 遥测值个数 */
|
||
} can_yc_timed_t;
|
||
|
||
/**
|
||
* 突发遥信点 — 单点 (用于队列元素)
|
||
* 变位时推入队列,协议栈短时间内取空并打包发送
|
||
*/
|
||
typedef struct {
|
||
uint16_t addr; /* 遥信地址 */
|
||
uint8_t state; /* 新状态 (2-bit DPS 编码) */
|
||
uint32_t timestamp_ms; /* 时标 (毫秒, CLOCK_MONOTONIC) */
|
||
} can_yx_burst_point_t;
|
||
|
||
/**
|
||
* 突发遥信打包帧 — 可携带多个突变点 (CAN FD 模式)
|
||
* 数据域格式: 点数(1B) + [地址(2B)+状态(1B)+时标(4B)] * N
|
||
* 每个点 7 字节, FD 64字节最多 9 个点
|
||
*/
|
||
typedef struct {
|
||
uint8_t point_count; /* 本帧包含的突发点数 */
|
||
can_yx_burst_point_t points[BURST_MAX_PER_FRAME];
|
||
} can_yx_burst_packed_t;
|
||
|
||
/**
|
||
* 突发遥测点 — 单点 (用于队列元素)
|
||
*/
|
||
typedef struct {
|
||
uint16_t addr; /* 遥测地址 */
|
||
float value; /* 遥测值 (IEEE 754) */
|
||
uint32_t timestamp_ms; /* 时标 (毫秒) */
|
||
} can_yc_burst_point_t;
|
||
|
||
/**
|
||
* 突发遥测打包帧 — 可携带多个突变点
|
||
* 数据域格式: 点数(1B) + [地址(2B)+浮点值(4B)+时标(4B)] * N
|
||
* 每个点 10 字节, FD 64字节最多 6 个点
|
||
*/
|
||
typedef struct {
|
||
uint8_t point_count;
|
||
can_yc_burst_point_t points[6]; /* 64/10 = 6 */
|
||
} can_yc_burst_packed_t;
|
||
|
||
/* ================================================================
|
||
* 数据域结构体 — 命令类
|
||
* ================================================================ */
|
||
|
||
/* 遥控选择/执行 */
|
||
typedef struct {
|
||
uint8_t point; /* 遥控点号 */
|
||
uint16_t timeout_ms; /* 超时(仅选择步骤有效) */
|
||
} can_yk_req_t;
|
||
|
||
/* 遥控应答 */
|
||
typedef struct {
|
||
uint8_t point;
|
||
uint8_t result;
|
||
uint16_t err_code;
|
||
} can_yk_ack_t;
|
||
|
||
/* 定值写入请求 */
|
||
typedef struct {
|
||
uint16_t addr;
|
||
float value; /* 定值(浮点) */
|
||
} can_dz_req_t;
|
||
|
||
/* 定值召唤请求 — 读取当前定值 (addr=0xFFFF 表示召唤全部) */
|
||
typedef struct {
|
||
uint16_t addr; /* 定值地址, 0xFFFF=召唤全部 */
|
||
} can_dz_read_req_t;
|
||
|
||
/* 定值召唤应答 — 返回当前定值 */
|
||
typedef struct {
|
||
uint16_t addr;
|
||
uint8_t result; /* 0=成功 */
|
||
uint16_t err_code;
|
||
float value; /* 当前定值 (仅 result=0 时有效) */
|
||
} can_dz_read_ack_t;
|
||
|
||
/* 定值应答 */
|
||
typedef struct {
|
||
uint16_t addr;
|
||
uint8_t result;
|
||
uint16_t err_code;
|
||
} can_dz_ack_t;
|
||
|
||
/* 遥调请求 */
|
||
typedef struct {
|
||
uint8_t point;
|
||
uint8_t op_type; /* 0=升, 1=降, 2=急停, 3=设值 */
|
||
float value; /* 目标值(浮点) */
|
||
} can_yt_req_t;
|
||
|
||
/* ================================================================
|
||
* 数据域结构体 — 紧急类
|
||
* ================================================================ */
|
||
|
||
/* 保护动作信号 */
|
||
typedef struct {
|
||
uint8_t event_type; /* 事件类型码 */
|
||
uint8_t phase; /* 故障相别 (0=A,1=B,2=C,3=AB,...) */
|
||
uint16_t fault_value; /* 故障量 (规格化值) */
|
||
uint32_t timestamp_ms; /* 时标 */
|
||
} can_urgent_protect_t;
|
||
|
||
/* ================================================================
|
||
* 数据域结构体 — 文件传输类
|
||
* ================================================================ */
|
||
|
||
typedef struct {
|
||
uint16_t file_id;
|
||
uint16_t total_seg;
|
||
uint32_t file_size;
|
||
char filename[56]; /* FD: 64-8=56 字节留给文件名 */
|
||
} can_ft_first_t;
|
||
|
||
typedef struct {
|
||
uint16_t file_id;
|
||
uint16_t seg_num;
|
||
uint8_t data[CANFD_MAX_DLEN - 4];
|
||
uint8_t data_len;
|
||
} can_ft_seg_t;
|
||
|
||
typedef struct {
|
||
uint16_t file_id;
|
||
uint16_t seg_num;
|
||
uint8_t data[CANFD_MAX_DLEN - 8];
|
||
uint8_t data_len;
|
||
uint32_t crc32; /* 末帧末尾 4 字节 */
|
||
} can_ft_last_t;
|
||
|
||
typedef struct {
|
||
uint16_t file_id;
|
||
uint8_t result;
|
||
uint16_t err_code;
|
||
} can_ft_ack_t;
|
||
|
||
/* 文件召唤请求 (主站→从站: 请求上传文件) */
|
||
typedef struct {
|
||
uint16_t file_id; /* 文件 ID (主站分配) */
|
||
char filepath[56]; /* 请求的文件路径/通配符 */
|
||
} can_ft_read_req_t;
|
||
|
||
/* 文件召唤就绪应答 (从站→主站: 文件存在, 即将开始发送) */
|
||
typedef struct {
|
||
uint16_t file_id;
|
||
uint32_t file_size; /* 文件大小 */
|
||
uint16_t total_seg; /* 总段数 */
|
||
char filename[48]; /* 实际文件名 */
|
||
} can_ft_read_ack_t;
|
||
|
||
/* ================================================================
|
||
* 数据域结构体 — 对时 / 管理类
|
||
* ================================================================ */
|
||
|
||
typedef struct {
|
||
uint32_t sec; /* 秒 (UNIX 时间戳) */
|
||
uint32_t nsec; /* 纳秒 */
|
||
} can_ts_frame_t;
|
||
|
||
typedef enum {
|
||
MG_CMD_HEARTBEAT = 0x01,
|
||
MG_CMD_LINK_TEST = 0x02,
|
||
MG_CMD_VER_QUERY = 0x03,
|
||
MG_CMD_VER_ACK = 0x04,
|
||
} mg_cmd_e;
|
||
|
||
typedef struct {
|
||
uint8_t cmd;
|
||
uint8_t param;
|
||
uint16_t reserved;
|
||
} can_mg_frame_t;
|
||
|
||
/* ================================================================
|
||
* 突发数据队列 (应用层 → 协议栈)
|
||
* ================================================================ */
|
||
|
||
/**
|
||
* 遥信突发队列
|
||
* 应用层检测到遥信变位后 push,协议栈定时 drain 并打包发送
|
||
*/
|
||
typedef struct {
|
||
can_yx_burst_point_t items[BURST_QUEUE_MAX];
|
||
volatile int head; /* 生产者(应用层/中断)写入位置 */
|
||
volatile int tail; /* 消费者(协议栈)读取位置 */
|
||
volatile int count; /* 当前队列长度 */
|
||
} can_burst_yx_queue_t;
|
||
|
||
typedef struct {
|
||
can_yc_burst_point_t items[BURST_QUEUE_MAX];
|
||
volatile int head;
|
||
volatile int tail;
|
||
volatile int count;
|
||
} can_burst_yc_queue_t;
|
||
|
||
/* ---- 队列操作 (lock-free, 单生产者单消费者) ---- */
|
||
|
||
/** 初始化突发队列 */
|
||
void can_burst_yx_queue_init(can_burst_yx_queue_t *q);
|
||
void can_burst_yc_queue_init(can_burst_yc_queue_t *q);
|
||
|
||
/** 推入队列 (应用层调用,可在中断上下文) */
|
||
int can_burst_yx_queue_push(can_burst_yx_queue_t *q,
|
||
const can_yx_burst_point_t *pt);
|
||
int can_burst_yc_queue_push(can_burst_yc_queue_t *q,
|
||
const can_yc_burst_point_t *pt);
|
||
|
||
/** 弹出队列 (协议栈调用) 返回实际弹出的点数 */
|
||
int can_burst_yx_queue_pop(can_burst_yx_queue_t *q,
|
||
can_yx_burst_point_t *out, int max);
|
||
int can_burst_yc_queue_pop(can_burst_yc_queue_t *q,
|
||
can_yc_burst_point_t *out, int max);
|
||
|
||
/** 队列是否为空 */
|
||
int can_burst_yx_queue_empty(const can_burst_yx_queue_t *q);
|
||
int can_burst_yc_queue_empty(const can_burst_yc_queue_t *q);
|
||
|
||
/* ================================================================
|
||
* Pack / Unpack API
|
||
* ================================================================ */
|
||
|
||
/* 过程数据 — 定时 */
|
||
int can_pack_yx_timed(const can_yx_timed_t *yx, uint8_t *data, int max_len);
|
||
int can_pack_yc_timed(const can_yc_timed_t *yc, uint8_t *data, int max_len);
|
||
int can_unpack_yx_timed(can_yx_timed_t *yx, const uint8_t *data, int len);
|
||
int can_unpack_yc_timed(can_yc_timed_t *yc, const uint8_t *data, int len);
|
||
|
||
/* 过程数据 — 突发打包 */
|
||
int can_pack_yx_burst(const can_yx_burst_packed_t *pkt,
|
||
uint8_t *data, int max_len);
|
||
int can_pack_yc_burst(const can_yc_burst_packed_t *pkt,
|
||
uint8_t *data, int max_len);
|
||
int can_unpack_yx_burst(can_yx_burst_packed_t *pkt,
|
||
const uint8_t *data, int len);
|
||
int can_unpack_yc_burst(can_yc_burst_packed_t *pkt,
|
||
const uint8_t *data, int len);
|
||
|
||
/* 命令类 */
|
||
int can_pack_yk_req(const can_yk_req_t *yk, uint8_t *data, int max_len);
|
||
int can_pack_yk_ack(const can_yk_ack_t *ack, uint8_t *data, int max_len);
|
||
int can_pack_dz_req(const can_dz_req_t *dz, uint8_t *data, int max_len);
|
||
int can_pack_dz_ack(const can_dz_ack_t *ack, uint8_t *data, int max_len);
|
||
int can_pack_dz_read_req(const can_dz_read_req_t *dz, uint8_t *data, int max_len);
|
||
int can_pack_dz_read_ack(const can_dz_read_ack_t *ack, uint8_t *data, int max_len);
|
||
int can_pack_yt_req(const can_yt_req_t *yt, uint8_t *data, int max_len);
|
||
|
||
int can_unpack_yk_req(can_yk_req_t *yk, const uint8_t *data, int len);
|
||
int can_unpack_yk_ack(can_yk_ack_t *ack, const uint8_t *data, int len);
|
||
int can_unpack_dz_req(can_dz_req_t *dz, const uint8_t *data, int len);
|
||
int can_unpack_dz_ack(can_dz_ack_t *ack, const uint8_t *data, int len);
|
||
int can_unpack_dz_read_req(can_dz_read_req_t *dz, const uint8_t *data, int len);
|
||
int can_unpack_dz_read_ack(can_dz_read_ack_t *ack, const uint8_t *data, int len);
|
||
int can_unpack_yt_req(can_yt_req_t *yt, const uint8_t *data, int len);
|
||
|
||
/* 文件类 */
|
||
int can_pack_ft_first(const can_ft_first_t *ft, uint8_t *data, int max_len);
|
||
int can_pack_ft_seg(const can_ft_seg_t *ft, uint8_t *data, int max_len);
|
||
int can_pack_ft_last(const can_ft_last_t *ft, uint8_t *data, int max_len);
|
||
int can_pack_ft_ack(const can_ft_ack_t *ack, uint8_t *data, int max_len);
|
||
int can_pack_ft_read_req(const can_ft_read_req_t *req, uint8_t *data, int max_len);
|
||
int can_pack_ft_read_ack(const can_ft_read_ack_t *ack, uint8_t *data, int max_len);
|
||
|
||
int can_unpack_ft_first(can_ft_first_t *ft, const uint8_t *data, int len);
|
||
int can_unpack_ft_seg(can_ft_seg_t *ft, const uint8_t *data, int len);
|
||
int can_unpack_ft_last(can_ft_last_t *ft, const uint8_t *data, int len);
|
||
int can_unpack_ft_ack(can_ft_ack_t *ack, const uint8_t *data, int len);
|
||
int can_unpack_ft_read_req(can_ft_read_req_t *req, const uint8_t *data, int len);
|
||
int can_unpack_ft_read_ack(can_ft_read_ack_t *ack, const uint8_t *data, int len);
|
||
|
||
/* 对时/管理 */
|
||
int can_pack_ts(const can_ts_frame_t *ts, uint8_t *data, int max_len);
|
||
int can_pack_mg(const can_mg_frame_t *mg, uint8_t *data, int max_len);
|
||
int can_unpack_ts(can_ts_frame_t *ts, const uint8_t *data, int len);
|
||
int can_unpack_mg(can_mg_frame_t *mg, const uint8_t *data, int len);
|
||
|
||
/* 工具函数 */
|
||
uint32_t can_crc32(const uint8_t *data, int len);
|
||
const char* can_err_str(uint16_t err_code);
|
||
|
||
/**
|
||
* 从 2-bit 编码中提取单个遥信点的状态 (0-3)
|
||
* @param bitmap 遥信位对数据
|
||
* @param offset 点偏移 (0 = 第一个点)
|
||
*/
|
||
static inline uint8_t can_yx_get_dps(const uint8_t *bitmap, int offset)
|
||
{
|
||
int byte_idx = offset / 4;
|
||
int bit_pos = (offset % 4) * 2;
|
||
return (bitmap[byte_idx] >> bit_pos) & 0x03;
|
||
}
|
||
|
||
/**
|
||
* 设置单个遥信点的双位置状态
|
||
*/
|
||
static inline void can_yx_set_dps(uint8_t *bitmap, int offset, uint8_t state)
|
||
{
|
||
int byte_idx = offset / 4;
|
||
int bit_pos = (offset % 4) * 2;
|
||
bitmap[byte_idx] &= ~(0x03 << bit_pos);
|
||
bitmap[byte_idx] |= ((state & 0x03) << bit_pos);
|
||
}
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* CAN_FRAME_H */
|