做ic什么网站好,视屏网站开发者工具无视频文件,商丘河南网站建设,知名网站域名根据架构设计#xff0c;实现编解码层的代码设计
Cargo.toml 加入二进制序列化支持
# 序列化支持
...
bincode 1.3 # 添加二进制序列化支持
bytes-utils 0.1 # 添加字节处理工具
开始编码
错误处理#xff08;error.rs#x…根据架构设计实现编解码层的代码设计
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, // commandLSVNV2182E0200001.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) - ResultOptionSelf::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 EncoderFrame 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) - ResultOptionSelf::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 EncoderFrame 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(bLSVNV2182E0200001); // 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(bLSVNV2182E0200001);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. 添加了单元测试 - 测试有效帧的解码 - 测试校验和错误的情况