基于Rust与GBT32960规范的编解码层
- 电脑硬件
- 2025-09-12 20:39:02

根据架构设计,实现编解码层的代码设计
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, // 起始符 0x23 pub 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 mand_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(), // vin payload ); 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 mand_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. 添加了单元测试: - 测试有效帧的解码 - 测试校验和错误的情况
基于Rust与GBT32960规范的编解码层由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“基于Rust与GBT32960规范的编解码层”