linux_learn/learn/CAN/include/can_protocol.h

187 lines
6.3 KiB
C

/**
* @file can_protocol.h
* @brief CAN 协议栈主接口 — 电力二次设备通讯库 (v2.0)
*
* v2.0 变更:
* - ID 编码采用优先级类别 + 报文类型两级方案
* - 遥信双位置 (2-bit DPS), 遥测浮点 (IEEE 754)
* - 突发数据走内部队列 → FD 打包 → 批量发送
* - 定时上送走独立周期调度,长间隔"主动总召"模式
* - 批次号机制用于接收方去重和丢帧检测
*/
#ifndef CAN_PROTOCOL_H
#define CAN_PROTOCOL_H
#include <stdint.h>
#include "can_id.h"
#include "can_frame.h"
#include "can_xml_parser.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ================================================================
* 初始化与清理
* ================================================================ */
int can_protocol_init(const char *xml_config_path);
int can_protocol_bind(const char *ifname);
const can_config_ctx_t* can_protocol_get_config(void);
void can_protocol_deinit(void);
/* ================================================================
* 回调注册 (回调类型定义见 can_xml_parser.h)
* ================================================================ */
/** 注册遥控回调 */
int can_protocol_register_yk_callback(can_yk_callback_t cb, void *arg);
/** 注册定值写入回调 (float 值) */
int can_protocol_register_dz_callback(can_dz_callback_t cb, void *arg);
/** 注册遥测读取回调 (返回 float) */
int can_protocol_register_yc_read_callback(can_yc_read_callback_t cb,
void *arg);
/** 注册遥信读取回调 (返回完整位对数据 bitmap, 2-bit/点) */
int can_protocol_register_yx_read_callback(can_yx_read_callback_t cb,
void *arg);
/** 注册文件接收回调 */
int can_protocol_register_ft_recv_callback(can_ft_recv_callback_t cb,
void *arg);
/* ================================================================
* 定时发送 (周期遥信/遥测 — "主动总召"模式)
* ================================================================ */
/**
* @brief 发送一轮所有定时信号
*
* 内部扫描 XML 配置中所有 period_ms > 0 的信号,
* 只发送距离上次发送已超过周期的信号。
* 定时数据使用 PRIO_PERIODIC 优先级,不抢占总线。
*
* @return 发送的 CAN 帧数
*/
int can_protocol_send_periodic(void);
/* ================================================================
* 突发数据接口 (应用层推入队列 → 协议栈批量发出)
* ================================================================ */
/**
* @brief 推入遥信变位到突发队列 (应用层调用,可在中断上下文)
*
* 应用层检测到遥信变位后立即调用此函数。
* 协议栈主循环会短时间内取空队列,用 FD 帧打包发出。
*
* @param addr 遥信地址
* @param state 新状态 (DPS编码: 0=中间态,1=分,2=合,3=无效)
* @param timestamp_ms 时标 (毫秒)
* @return 0=成功, -1=队列满
*/
int can_protocol_push_yx_burst(uint16_t addr, uint8_t state,
uint32_t timestamp_ms);
/**
* @brief 推入遥测越限到突发队列
* @return 0=成功, -1=队列满
*/
int can_protocol_push_yc_burst(uint16_t addr, float value,
uint32_t timestamp_ms);
/**
* @brief 排空突发队列并发送 (协议栈主循环自动调用)
* @return 发送的 CAN 帧数
*
* 尽可能将队列中的突变点打包进 FD 帧:
* 遥信: 每帧最多 9 个点 (每点7字节), CAN 2.0 最多 1 个点
* 遥测: 每帧最多 6 个点 (每点10字节), CAN 2.0 最多 1 个点
*/
int can_protocol_flush_burst(void);
/* ================================================================
* 命令发送接口
* ================================================================ */
int can_protocol_send_yk_ack(uint8_t dst, uint8_t point,
uint8_t result, uint16_t err_code);
int can_protocol_send_dz_ack(uint8_t dst, uint16_t addr,
uint8_t result, uint16_t err_code);
int can_protocol_send_heartbeat(void);
/**
* @brief 发送定值写入请求 (主站→从站)
*/
int can_protocol_send_dz_write(uint8_t dst, uint16_t addr, float value);
/**
* @brief 发送定值召唤请求 (主站→从站: 读取当前定值)
* @param dst 目标设备地址
* @param addr 定值地址, 0xFFFF=召唤全部定值
*/
int can_protocol_send_dz_read(uint8_t dst, uint16_t addr);
/**
* @brief 发送定值应答
*/
int can_protocol_send_dz_ack(uint8_t dst, uint16_t addr,
uint8_t result, uint16_t err_code);
/* ================================================================
* 文件传输
* ================================================================ */
/**
* @brief 主动下发文件 (本端→对端)
*/
int can_protocol_send_file(const char *filepath, uint16_t file_id);
/**
* @brief 召唤远端文件 (请求对端上传文件, 主站→从站)
*
* 从站收到请求后:
* 若文件存在 → 发送 MSG_FT_READ_ACK → 自动调用 can_protocol_send_file() 上传
* 若文件不存在 → 发送 MSG_FT_READ_NAK
*
* @param dst 目标设备地址
* @param file_id 给此传输分配的文件 ID
* @param filepath 请求的文件路径 (如 "/rec/wave_20260713.dat")
*/
int can_protocol_request_file(uint8_t dst, uint16_t file_id,
const char *filepath);
int can_protocol_file_progress(uint16_t file_id,
uint32_t *sent_bytes,
uint32_t *total_bytes);
/* ================================================================
* 主循环
* ================================================================ */
/**
* @brief 协议栈主循环 (阻塞)
*
* 循环执行:
* 1. 检查定时器 → 到期则发送周期信号 (PRIO_PERIODIC)
* 2. 排空突发队列 → 打包发送 (PRIO_BURST)
* 3. recvfrom() → 解析 ID → 分发处理
* 4. 文件传输发送任务
* 5. 心跳发送
*/
int can_protocol_run(void);
void can_protocol_stop(void);
/**
* @brief 手动分发单帧 (适用于自控主循环)
*/
int can_protocol_dispatch(const struct can_frame *frame);
#ifdef __cplusplus
}
#endif
#endif /* CAN_PROTOCOL_H */