基于 Rust 与 GBT32960 规范的编解码层

根据架构设计,实现编解码层的代码设计

Cargo.toml 加入二进制序列化支持

# 序列化支持
...
bincode = "1.3"           # 添加二进制序列化支持
bytes-utils = "0.1"       # 添加字节处理工具

开始编码

错误处理(error.rs):

定义了编解码过程中可能遇到的错误类型,使用枚举定义

use thiserror::Error;#[derive(Error, Debug)]
pub enum CodecError {#[error("数据长度不足")]InsufficientData,#[error("校验和错误")]ChecksumMismatch,#[error("无效的起始符")]InvalidStartByte,#[error("无效的命令标识: {0}")] InvalidCommand(u8),#[error("IO错误: {0}")] Io(#[from] std::io::Error),
}

数据帧结构(frame.rs):

- 定义了符合 GBT32960 协议的数据帧结构
- 提供了创建和校验数据帧的方法

frame.rs

use bytes::{ Bytes, BytesMut, BufMut };
use chrono::NaiveDateTime;
use serde::{ Serialize, Deserialize };#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Frame {pub start_byte: u8, // 起始符 0x23pub command_flag: u8, // 命令标识pub response_flag: u8, // 应答标志pub vin: String, // 车辆识别码pub encrypt_method: u8, // 加密方式pub payload_length: u16, // 数据单元长度pub payload: Bytes, // 数据单元pub checksum: u8, // BCC校验码
}impl Frame {pub fn new(command: u8, vin: String, payload: Bytes) -> Self {let payload_length = payload.len() as u16;Self {start_byte: 0x23,command_flag: command,response_flag: 0xfe,vin,encrypt_method: 0x01,payload_length,payload,checksum: 0x00, // 将在编码时计算}}pub fn calculate_checksum(&self) -> u8 {let mut bcc: u8 = 0;// 命令标识bcc ^= self.command_flag;// 应答标志bcc ^= self.response_flag;// VIN码(17位)for byte in self.vin.as_bytes() {bcc ^= byte;}// 加密方式bcc ^= self.encrypt_method;// 数据单元长度(2字节)bcc ^= ((self.payload_length >> 8) & 0xff) as u8;bcc ^= (self.payload_length & 0xff) as u8;// 数据单元for byte in self.payload.iter() {bcc ^= byte;}bcc}
}#[cfg(test)]
mod tests {use super::*;#[test]fn test_calculate_checksum() {let payload = Bytes::from_static(&[0x01, 0x02, 0x03]);let frame = Frame::new(0x01, // command"LSVNV2182E0200001".to_string(), // vinpayload);let checksum = frame.calculate_checksum();assert!(checksum != 0, "校验和不应该为0");// 创建相同内容的帧,校验和应该相同let frame2 = Frame::new(0x01,"LSVNV2182E0200001".to_string(),Bytes::from_static(&[0x01, 0x02, 0x03]));assert_eq!(checksum, frame2.calculate_checksum(), "相同内容的帧应该有相同的校验和");}#[test]fn test_different_content_different_checksum() {let frame1 = Frame::new(0x01, "LSVNV2182E0200001".to_string(), Bytes::from_static(&[0x01]));let frame2 = Frame::new(0x01, "LSVNV2182E0200001".to_string(), Bytes::from_static(&[0x02]));assert_ne!(frame1.calculate_checksum(),frame2.calculate_checksum(),"不同内容的帧应该有不同的校验和");}
}

编解码器(codec.rs):

- 实现了 tokio 的 Decoder 和 Encoder trait
- 负责数据帧的序列化和反序列化

use bytes::{ BytesMut, Buf };
use tokio_util::codec::{ Decoder, Encoder };
use super::{ Frame, CodecError };pub struct Gbt32960Codec;impl Decoder for Gbt32960Codec {type Item = Frame;type Error = CodecError;fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {// 检查数据长度是否足够if src.len() < 22 {// 最小帧长度return Ok(None);}// 检查起始符if src[0] != 0x23 {return Err(CodecError::InvalidStartByte);}// TODO: 实现完整的解码逻辑// 1. 读取各个字段// 2. 验证校验和// 3. 解析数据单元Ok(None)}
}impl Encoder<Frame> for Gbt32960Codec {type Error = CodecError;fn encode(&mut self, frame: Frame, dst: &mut BytesMut) -> Result<(), Self::Error> {// TODO: 实现编码逻辑// 1. 写入各个字段// 2. 计算并写入校验和Ok(())}
}

实现完整的解码逻辑

use bytes::{BytesMut, Buf, BufMut};
use tokio_util::codec::{Decoder, Encoder};
use super::{Frame, CodecError};pub struct Gbt32960Codec;impl Decoder for Gbt32960Codec {type Item = Frame;type Error = CodecError;fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {// 检查数据长度是否足够if src.len() < 22 {  // 最小帧长度return Ok(None);}// 检查起始符if src[0] != 0x23 {return Err(CodecError::InvalidStartByte);}// 读取命令标识和应答标志let command_flag = src[1];let response_flag = src[2];// 读取 VIN 码(17字节)let vin = String::from_utf8_lossy(&src[3..20]).to_string();// 读取加密方式let encrypt_method = src[20];// 读取数据单元长度(2字节)let payload_length = ((src[21] as u16) << 8) | (src[22] as u16);// 检查是否有足够的数据let total_length = 23 + payload_length as usize + 1; // 头部 + 数据单元 + 校验码if src.len() < total_length {return Ok(None);}// 读取数据单元let payload = src.slice(23..23 + payload_length as usize);// 读取校验码let received_checksum = src[total_length - 1];// 创建帧对象进行校验和计算let frame = Frame {start_byte: 0x23,command_flag,response_flag,vin,encrypt_method,payload_length,payload: payload.freeze(),checksum: received_checksum,};// 验证校验和let calculated_checksum = frame.calculate_checksum();if calculated_checksum != received_checksum {return Err(CodecError::ChecksumMismatch);}// 消费已处理的字节src.advance(total_length);Ok(Some(frame))}
}impl Encoder<Frame> for Gbt32960Codec {type Error = CodecError;fn encode(&mut self, frame: Frame, dst: &mut BytesMut) -> Result<(), Self::Error> {// TODO: 实现编码逻辑// 1. 写入各个字段// 2. 计算并写入校验和Ok(())}
}#[cfg(test)]
mod tests {use super::*;use bytes::Bytes;#[test]fn test_decode_valid_frame() {let mut codec = Gbt32960Codec;let mut buffer = BytesMut::new();// 构造测试数据buffer.put_u8(0x23);                          // 起始符buffer.put_u8(0x01);                          // 命令标识buffer.put_u8(0xFE);                          // 应答标志buffer.extend_from_slice(b"LSVNV2182E0200001"); // VIN码buffer.put_u8(0x01);                          // 加密方式buffer.put_u16(2);                            // 数据长度buffer.extend_from_slice(&[0x01, 0x02]);      // 数据单元// 计算并添加校验和let checksum = buffer[1..buffer.len()].iter().fold(0u8, |acc, &x| acc ^ x);buffer.put_u8(checksum);// 解码let result = codec.decode(&mut buffer).unwrap().unwrap();// 验证解码结果assert_eq!(result.command_flag, 0x01);assert_eq!(result.vin, "LSVNV2182E0200001");assert_eq!(result.payload.len(), 2);assert_eq!(buffer.len(), 0); // 确保所有数据都被消费}#[test]fn test_decode_invalid_checksum() {let mut codec = Gbt32960Codec;let mut buffer = BytesMut::new();// 构造测试数据(使用错误的校验和)buffer.put_u8(0x23);buffer.put_u8(0x01);buffer.put_u8(0xFE);buffer.extend_from_slice(b"LSVNV2182E0200001");buffer.put_u8(0x01);buffer.put_u16(0);buffer.put_u8(0xFF); // 错误的校验和// 验证解码失败assert!(matches!(codec.decode(&mut buffer),Err(CodecError::ChecksumMismatch)));}
}

代码地址

阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

总结

1. 完整的帧解析逻辑:
   - 起始符验证,根据接口协议验证是否0x23开头
   - 命令标识和应答标志解析
   - VIN码解析,vin码17个字节长度
   - 加密方式解析,读取加密方式,测试的时候可以先不使用,上生产环境后要打开
   - 数据单元长度解析,表示数据payload的总长度
   - 数据单元提取
   - 校验和验证
2. 数据完整性检查:
   - 最小帧长度检查
   - 完整数据长度检查
   - 校验和验证
3. 添加了单元测试:
   - 测试有效帧的解码
   - 测试校验和错误的情况

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896880.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MOM成功实施分享(七)电力电容制造MOM工艺分析与解决方案(第一部分)

声明&#xff1a;文章仅用于交流学习&#xff0c;不用于商业项目实施&#xff0c;图片来源于网络&#xff0c;如有侵犯权利&#xff0c;请联系作者及时删除。 本方案旨在对电力电容&#xff08;PEC和PQM型号&#xff09;制造工艺深度分析&#xff0c;结合管理要求设计MOM相关功…

FPGA开发,使用Deepseek V3还是R1(1):应用场景

以下都是Deepseek生成的答案 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;1&#xff09;&#xff1a;应用场景 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;2&#xff09;&#xff1a;V3和R1的区别 FPGA开发&#xff0c;使用Deepseek V3还是R1&#x…

JavaWeb后端基础(3)

原打算把Mysql操作数据库的一些知识写进去&#xff0c;但是感觉没必要&#xff0c;要是现在会的都是简单的增删改查&#xff0c;所以&#xff0c;这一篇&#xff0c;我直接从java操作数据库开始写&#xff0c;所以这一篇大致就是记一下JDBC、MyBatis、以及SpringBoot的配置文件…

Pytorch实现之SRGAN+CBAM的结构设计

简介 简介:在SRGAN的残差连接中加入了CBAM注意力机制,同时设计了四类损失来训练。 论文题目:Super-resolution Generative Adversarial Networks Based on Attention Model(基于注意力模型的超分辨率生成对抗网络) 会议:2020 IEEE第六届计算机与通信国际会议 摘要:基…

移动端国际化翻译同步解决方案-V3

1.前言 因为软件出海&#xff0c;从在上上家公司就开始做翻译系统&#xff0c;到目前为止已经出了两个比较大的版本了&#xff0c;各个版本解决的痛点如下&#xff1a; V1版本&#xff1a; 主要针对的是AndroidiOS翻译不一致和翻译内容管理麻烦的问题&#xff0c;通过这个工具…

2.css简介

什么是css&#xff1a; CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用来为结构化文档&#xff08;如 HTML 文档或 XML 应用&#xff09;添加样式&#xff08;字体、间距和颜色等&#xff09;的计算机语言&#xff0c;CSS 文件扩展名为 .…

机器人学习模拟框架 robosuite (3) 机器人控制代码示例

Robosuite框架是一个用于机器人模拟和控制的强大工具&#xff0c;支持多种类型的机器人。 官方文档&#xff1a;Overview — robosuite 1.5 documentation 开源地址&#xff1a;https://github.com/ARISE-Initiative/robosuite 目录 1、通过键盘或SpaceMouse远程控制机器人…

可终身授权的外国工具,不限次数使用!PDF转CAD的软件

最近有不少朋友问我有没有好用的CAD转换工具&#xff0c;今天就来给大家分享两款超实用的小软件&#xff0c;希望能帮到大家。 第一款软件是一款国外开发的&#xff0c;它专门用来把PDF文件转换成CAD格式&#xff0c;特别方便。 这款软件的操作非常简单&#xff0c;打开后无需安…

Ubuntu系统上部署Node.js项目的完整流程

以下是在Ubuntu系统上部署Node.js项目的完整流程&#xff0c;分为系统初始化、环境配置、项目部署三个部分&#xff1a; 一、系统初始化 & 环境准备 bash # 1. 更新系统软件包 sudo apt update && sudo apt upgrade -y# 2. 安装基础工具 sudo apt install -y buil…

Android内存优化指南:从数据结构到5R法则的全面策略

目录 一、APP 内存限制 二、内存的三大问题 2.1、内存抖动(Memory Churn) 2.1.1 频繁创建短生命周期对象 2.1.2 系统API或第三方库的不合理使用 2.1.3 Handler使用不当 2.2、内存泄漏(Memory Leak) 2.2.1 静态变量持有Activity或Context引用 2.2.2 未取消的回调或…

ffmpeg源码编译支持cuda

1.安装cuda CUDA Toolkit 11.3 Downloads | NVIDIA Developer 在选择组件的时候&#xff0c;将CUDA中的Nsight VSE和Visual Studio Integration取消勾选 不然会安装失败 2.编译ffmpeg 把cuda编译宏定义开启&#xff0c;再编译avcodec 3.编译livavutil报错struct "Cuda…

Git强制覆盖分支:将任意分支完全恢复为main分支内容

Git强制覆盖分支&#xff1a;将任意分支完全恢复为main分支内容 场景背景完整操作步骤一、前置准备二、操作流程步骤 1&#xff1a;更新本地 main 分支步骤 2&#xff1a;强制重置目标分支步骤 3&#xff1a;强制推送至远程仓库 三、操作示意图 关键风险提示&#xff08;必读&a…

【Java反序列化测试】

Java反序列化测试 1. 识别反序列化入口点2. 构造探测Payload3. 发送Payload并观察结果4. 绕过可能的防护5. 自动化工具注意事项总结 Java反序列化测试&#xff1a; 1. 识别反序列化入口点 常见入口&#xff1a; HTTP请求参数&#xff08;如POST数据、Cookie、Headers&#xff…

golang的io

https://www.bilibili.com/list/BV1gx4y1r7xb 1. 原生io包 io包是Go语言标准库中底层的I/O接口层&#xff0c;定义了通用的读写规则和错误处理逻辑。每次读写都是直接调用底层系统 I/O&#xff0c;每次读取1字节&#xff0c;系统调用次数多。适用于小数据量、实时性要求高。io…

【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发-第4章 UART基础知识

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

【计算机网络入门】初学计算机网络(十)(重要)

目录 1. 网络层的作用 2. IPV4 2.1 IP 数据报格式 2.2 IP地址分类方案 2.3 数据的转发 2.4 特殊用途的IP地址 3. 子网划分和子网掩码 3.1 子网划分 3.2 子网掩码 1. 网络层的作用 按照教学五层模型&#xff0c;应用层、传输层、网络层、数据链路层、物理层&#xff…

机器学习(五)

一&#xff0c;多类&#xff08;Multiclass&#xff09; 多类是指输出不止有两个输出标签&#xff0c;想要对多个种类进行分类。 Softmax回归算法&#xff1a; Softmax回归算法是Logistic回归在多类问题上的推广&#xff0c;和线性回归一样&#xff0c;将输入的特征与权重进行…

基于 Vue 和 SSM 的前后端分离项目实战:登录与注册功能实现

文章目录 前言项目概述前端部分&#xff08;Vue&#xff09;1. 项目初始化2. 页面布局Login.vueRegister.vue 3. 路由配置4. 主组件 后端部分&#xff08;SSM&#xff09;1. 项目结构2. 数据库设计3. MyBatis 配置4. DAO 层5. Service 层6. Controller 层7. 配置文件8. Spring …

Windows安装nvm【超详细图解】

目录 前言 一、NVM下载 方式一&#xff1a;官网下载 方式二&#xff1a;GitHub 下载 二、NVM安装 镜像源配置 三、Node安装 四、环境变量配置 前言 NVM&#xff08;Node Version Manager&#xff09;是一个命令行工具&#xff0c;用于在一台计算机上轻松管理和切换多…

KVM虚拟机磁盘创建探究-2

使用 virt-install 命令自动创建磁盘镜像和使用 qemu-img 手动创建磁盘镜像&#xff0c;在磁盘镜像本身格式和基本功能上是一致的&#xff0c;但在一些特性如初始占用磁盘空间、创建时的可配置性等方面存在区别&#xff0c;下面以 QCOW2 格式磁盘镜像为例进行详细说明。 初始占…