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