news 2026/9/24 16:54:00

F´ 中的 Fw::FilePacket:CFDP 风格的文件分包协议与 C++ 实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
F´ 中的 Fw::FilePacket:CFDP 风格的文件分包协议与 C++ 实现解析

F´ 中的 Fw::FilePacket:CFDP 风格的文件分包协议与 C++ 实现解析

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprime

导读

Fw::FilePacket 是 F´(F Prime)飞行软件框架中用于表示“ISF 文件分组包(file packet)”的核心类型,它定义了将任意文件切分成可在遥测/通信链路上传输的数据包的标准二进制格式。本文以 Fw/FilePacket/docs/sdd.md 设计文档为主体,结合 FilePacket.hpp 及各实现文件,完整讲解 START / DATA / END / CANCEL 四种包的字节布局、序列化与反序列化原理、CFDP 校验和算法,并给出基于 Svc/FileDownlink 的真实调用场景,帮助你直接掌握该格式的构造与解析方法。

1 设计背景:与 CFDP PDU 同源的包格式

Fw::FilePacket 的格式设计参考了 CCSDS 文件传输协议(CFDP)第 5 节中定义的协议数据单元(PDU)结构,因此它与航天工程中广泛使用的 CFDP 具有相似的骨架:

  • 每个包以包类型 + 序号构成的头部开始;
  • 头部之后是随类型而变的载荷(data)
  • 整个文件通过一串带序号的包顺序传输,接收方可以据此重组文件并校验完整性。

在 F´ 中,这个类型被Svc::FileDownlink(文件下传组件)用来把本地文件分片发送,也可被对应的上行/地面侧解析。其核心头文件为 Fw/FilePacket/FilePacket.hpp,实现在 Fw/FilePacket 目录下的各.cpp文件中。

2 总体结构:一个包包含什么

从 sdd.md 与 FilePacket.hpp 可以看出,每个文件包包含以下数据:

字段长度说明
包类型(Packet type)1 字节START(0)、DATA(1)、END(2)、CANCEL(3),另有内部占位T_NONE(255)
序号(Sequence index)4 字节无符号整数,每个文件从 0 开始,后续每包递增 1
包数据(Packet data)可变具体格式取决于包类型

在代码层面,FilePacket被实现为一个联合(union),内部同时包含HeaderStartPacketDataPacketEndPacketCancelPacket五种结构,通过m_header.m_type区分当前“视图”,这与 C++ 中“判别式联合(tagged union)”的经典做法一致。

2.1 头部(Header)的序列化

头部总是最先出现在每个包中,其缓冲区大小固定为 5 字节(sizeof(U8) + sizeof(U32)),见 FilePacket.hpp 中HEADERSIZE与 Header.cpp 的bufferSize()

序列化时先写类型字节(强制转成U8),再写 4 字节序号(Header.cpp);反序列化时依次读回并用static_cast<Type>还原类型(Header.cpp)。这一对toSerialBuffer/fromSerialBuffer是所有包类型共用的基础能力。

3 四种包类型的载荷格式

3.1 START 包(类型 0):声明文件传输开始

START 包的包类型为START,序号恒为0,用于宣告一个新文件传输的开始。其载荷依次为:

字段长度说明
文件大小(File size)4 字节整个文件的字节数(U32
源路径长度1 字节源路径(source path)的字节数(U8
源路径可变源端文件路径
目的路径长度1 字节目的路径(destination path)的字节数(U8
目的路径可变目标端文件路径

对应实现见 StartPacket.cpp 的initialize:头部以T_START与序号 0 初始化,随后保存文件大小,并分别初始化源、目的两个路径。

路径的序列化在 PathName.cpp 中完成:initializeStringUtils::string_length(value, MAX_LENGTH)截取路径长度(MAX_LENGTH = 255,见 FilePacket.hpp),写入m_length(1 字节)与指向路径的指针;序列化时先写 1 字节长度、再写入对应字节数的路径内容;反序列化时同样先读长度,再把m_value指向缓冲区中的字符串地址(PathName.cpp)。因此路径长度上限为 255 字节。

3.2 DATA 包(类型 1):传输文件数据分片

DATA 包承载文件的实际内容分片,载荷为:

字段长度说明
字节偏移(Byte offset)4 字节本包数据在整个文件中的起始字节位置(U32
数据长度(Data size)2 字节本包携带的文件数据字节数(U16
文件数据(File data)可变实际数据内容

实现见 DataPacket.cpp 的initialize,其签名接收sequenceIndexbyteOffsetdataSize与指向数据的指针。序列化顺序为:头部 → 4 字节偏移 → 2 字节数据长度 → 原始数据(DataPacket.cpp)。数据指针在反序列化时被直接指向输入缓冲区内的地址(DataPacket.cpp),因此反序列化得到的 DATA 包不拷贝数据,其生命周期与源缓冲区绑定,使用时需注意。

3.3 END 包(类型 2):结束并携带校验和

END 包宣告文件传输结束,其载荷只有一个字段:

字段长度说明
32 位哈希值(Checksum)4 字节按 CFDP 协议规则由文件数据计算得到的 32 位校验和

实现见 EndPacket.cpp:initialize(sequenceIndex, checksum)将传入的CFDP::Checksum对象的值(getValue())保存为m_checksumValue;序列化/反序列化时直接读写该 4 字节值(EndPacket.cpp)。接收方可用它验证整个文件在传输过程中是否发生损坏。

3.4 CANCEL 包(类型 3):取消传输

CANCEL 包用于中止传输,不携带任何数据,仅由头部构成(类型CANCEL+ 序号)。实现见 CancelPacket.cpp,bufferSize()直接返回头部大小;反序列化时要求缓冲区剩余字节数为 0,否则返回FW_DESERIALIZE_SIZE_MISMATCH(CancelPacket.cpp),保证格式的严格性。

4 统一入口:FilePacket 的 toBuffer / fromBuffer

FilePacket对外提供两个统一接口(FilePacket.cpp):

  • fromBuffer(const Buffer& buffer):将一段原始字节解析为文件包。内部先构造SerialBufferfill(),然后读头部,再依据m_type分派给对应的StartPacket::fromSerialBuffer/DataPacket::fromSerialBuffer/EndPacket::fromSerialBuffer/CancelPacket::fromSerialBuffer;若读到T_NONE返回FW_DESERIALIZE_TYPE_MISMATCH,未知类型触发FW_ASSERT(0)(FilePacket.cpp)。
  • toBuffer(Buffer& buffer):把当前包写入字节缓冲区,同样按类型分派。
  • bufferSize():按类型返回所需的缓冲区大小,T_NONE时返回 0。
  • asStartPacket()/asDataPacket()/asEndPacket()/asCancelPacket():以对应类型视图读取包内容,内部均先断言类型匹配(FilePacket.cpp)。
  • fromStartPacket()/fromDataPacket()/fromEndPacket()/fromCancelPacket():从具体包对象构造FilePacket(FilePacket.cpp)。

因此使用方式非常直观:发送方构造具体包 →toBuffer编码;接收方拿到字节 →fromBuffer解码 →asXxxPacket读取字段。

5 END 包背后的 CFDP 校验和算法

END 包携带的 32 位校验和由 CFDP/Checksum/Checksum.hpp 与 CFDP/Checksum/Checksum.cpp 实现。其核心是update(data, offset, length):把文件视为连续的 4 字节“字(word)”序列,逐字累加到m_value(32 位无符号溢出累加),具体分三阶段处理:

  1. offset % 4 != 0,先按非对齐方式累加第一个不满 4 字节的“字”(addWordUnaligned);
  2. 中间对齐部分以 4 字节为单位直接累加(addWordAligned);
  3. 末尾不足 4 字节的部分再以非对齐方式收尾。

addByteAtOffset将字节按其在 4 字节字内的位置左移8*(3-offset)位后加到校验和上(Checksum.cpp)。注意updateoffset是相对文件起始的字节偏移——这与 DATA 包中的byteOffset语义一致,发送方应按文件全局偏移逐片更新校验和,接收方才能在 END 包中做整体比对。

Checksum还提供构造(默认 0 / 指定值 / 拷贝)、getValue()operator==/operator!=,便于在测试中直接比较校验和值(Checksum.hpp)。

6 在 Svc::FileDownlink 中的实际用法

Fw::FilePacket在仓库中最重要的消费者是文件下传组件Svc::FileDownlink。在 Svc/FileDownlink/FileDownlink.cpp 中可以找到两处关键调用:

  • 发送数据分片时:构造Fw::FilePacket::DataPacket并调用filePacket.fromDataPacket(dataPacket)生成可发送的包(FileDownlink.cpp);
  • 发送起始声明时:构造StartPacket后调用filePacket.fromStartPacket(startPacket)(FileDownlink.cpp)。

即:业务组件负责构造携带具体字段的包对象,再由FilePacket统一完成编码与类型管理,这正好呼应了第 4 节介绍的“构造 → fromXxxPacket → toBuffer”流程。若需了解完整的分片、调度与校验逻辑,可继续阅读 Svc/FileDownlink/docs/sdd.md 及测试 Svc/FileDownlink/test/ut/FileDownlinkTester.cpp。

7 测试验证:四种包的往返一致性

仓库在 Fw/FilePacket/test/ut/FilePacketMain.cpp 中提供了覆盖四种包类型的 GTest 用例,均遵循“构造 → 序列化 → 反序列化 → 逐字段比较”的往返测试模式:

  • TEST(FilePacket, Header):以类型T_DATA、序号 10 构造头部,往返后断言一致(FilePacketMain.cpp);
  • TEST(FilePacket, StartPacket):文件大小 10、源路径"source"、目的路径"dest"(FilePacketMain.cpp);
  • TEST(FilePacket, DataPacket):序号 3、字节偏移 42、数据长度 10(FilePacketMain.cpp);
  • TEST(FilePacket, EndPacket):序号 15、校验和值 42(FilePacketMain.cpp);
  • TEST(FilePacket, CancelPacket):序号 10,验证无载荷包可正确往返(FilePacketMain.cpp)。

比较断言由 GTest 辅助命名空间提供:Fw::GTest::FilePackets定义了各包类型的compare函数(见 Fw/FilePacket/GTest/FilePackets.hpp 及其实现,如 GTest/StartPacket.cpp),通过ASSERT_EQ逐一比对头部、文件大小、路径、偏移、数据长度与校验和等字段。若要运行该模块测试,可在构建目录中启用对应的 CMake 目标(模块定义见 Fw/FilePacket/CMakeLists.txt)。

8 小结

  • Fw::FilePacket是 F´ 内文件传输的统一分组格式,格式骨架取自 CFDP PDU(sdd.md);
  • 每个包由 5 字节头部(1 字节类型 + 4 字节序号)与类型相关载荷组成,四种类型分别为 START(文件大小 + 双路径)、DATA(偏移 + 长度 + 数据)、END(CFDP 32 位校验和)、CANCEL(无载荷);
  • 路径采用“1 字节长度 + 内容”的自描述编码,最长 255 字节;
  • 编解码入口统一为fromBuffer/toBuffer,配合asXxxPacket/fromXxxPacket使用;DATA 包反序列化不拷贝数据,需注意缓冲区生命周期;
  • END 包校验和由CFDP::Checksum::update按 4 字节字累加得到,偏移语义与 DATA 包byteOffset一致;
  • 仓库内通过Svc::FileDownlink实际使用该类型,并有完整的 GTest 往返测试保证格式的稳定性。

如需进一步掌握文件传输的端到端流程(如何分片、如何驱动发送、如何重组与校验),可继续阅读 Svc/FileDownlink/docs/sdd.md 与 Svc/FileUplink 相关文档。

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 16:51:34

dateparse4cj快速上手教程:5分钟完成安装并解析你的第一个日期字符串

dateparse4cj快速上手教程&#xff1a;5分钟完成安装并解析你的第一个日期字符串 【免费下载链接】dateparse4cj dateparse4cj 是一个基于 cangjie 标准库实现的高性能、功能丰富的日期时间解析库。它能够自动识别并解析多种格式的日期字符串&#xff0c;支持全球各种常见日期格…

作者头像 李华
网站建设 2026/9/24 16:50:03

douyin-downloader:一个命令免费把博主主页整体存到本地

douyin-downloader&#xff1a;一个命令免费把博主主页整体存到本地 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback sup…

作者头像 李华
网站建设 2026/9/24 16:47:07

Qwen-Image-2.1-GGUF量化怎么选?Q4_0到Q8_0画质与体积终极对比清单

Qwen-Image-2.1-GGUF量化怎么选&#xff1f;Q4_0到Q8_0画质与体积终极对比清单 【免费下载链接】Qwen-Image-2.1-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/abenzerps/Qwen-Image-2.1-GGUF Qwen-Image-2.1-GGUF 是通义千问 Qwen-Image-2.1 文生图模型的 GGUF …

作者头像 李华