ToB企服应用市场:ToB评测及商务社交产业平台

标题: 基于 Rust 与 GBT32960 规范的编解码层 [打印本页]

作者: 王國慶    时间: 昨天 03:38
标题: 基于 Rust 与 GBT32960 规范的编解码层
根据架构设计,实现编解码层的代码设计
Cargo.toml 加入二进制序列化支持
  1. # 序列化支持
  2. ...
  3. bincode = "1.3"           # 添加二进制序列化支持
  4. bytes-utils = "0.1"       # 添加字节处理工具
复制代码
开始编码

错误处理惩罚(error.rs):

定义了编解码过程中可能遇到的错误类型,利用罗列定义
  1. use thiserror::Error;
  2. #[derive(Error, Debug)]
  3. pub enum CodecError {
  4.     #[error("数据长度不足")]
  5.     InsufficientData,
  6.     #[error("校验和错误")]
  7.     ChecksumMismatch,
  8.     #[error("无效的起始符")]
  9.     InvalidStartByte,
  10.     #[error("无效的命令标识: {0}")] InvalidCommand(u8),
  11.     #[error("IO错误: {0}")] Io(#[from] std::io::Error),
  12. }
复制代码
数据帧布局(frame.rs):

- 定义了符合 GBT32960 协议的数据帧布局
- 提供了创建和校验数据帧的方法
frame.rs
  1. use bytes::{ Bytes, BytesMut, BufMut };
  2. use chrono::NaiveDateTime;
  3. use serde::{ Serialize, Deserialize };
  4. #[derive(Debug, Clone, Serialize, Deserialize)]
  5. pub struct Frame {
  6.     pub start_byte: u8, // 起始符 0x23
  7.     pub command_flag: u8, // 命令标识
  8.     pub response_flag: u8, // 应答标志
  9.     pub vin: String, // 车辆识别码
  10.     pub encrypt_method: u8, // 加密方式
  11.     pub payload_length: u16, // 数据单元长度
  12.     pub payload: Bytes, // 数据单元
  13.     pub checksum: u8, // BCC校验码
  14. }
  15. impl Frame {
  16.     pub fn new(command: u8, vin: String, payload: Bytes) -> Self {
  17.         let payload_length = payload.len() as u16;
  18.         Self {
  19.             start_byte: 0x23,
  20.             command_flag: command,
  21.             response_flag: 0xfe,
  22.             vin,
  23.             encrypt_method: 0x01,
  24.             payload_length,
  25.             payload,
  26.             checksum: 0x00, // 将在编码时计算
  27.         }
  28.     }
  29.     pub fn calculate_checksum(&self) -> u8 {
  30.         let mut bcc: u8 = 0;
  31.         // 命令标识
  32.         bcc ^= self.command_flag;
  33.         // 应答标志
  34.         bcc ^= self.response_flag;
  35.         // VIN码(17位)
  36.         for byte in self.vin.as_bytes() {
  37.             bcc ^= byte;
  38.         }
  39.         // 加密方式
  40.         bcc ^= self.encrypt_method;
  41.         // 数据单元长度(2字节)
  42.         bcc ^= ((self.payload_length >> 8) & 0xff) as u8;
  43.         bcc ^= (self.payload_length & 0xff) as u8;
  44.         // 数据单元
  45.         for byte in self.payload.iter() {
  46.             bcc ^= byte;
  47.         }
  48.         bcc
  49.     }
  50. }
  51. #[cfg(test)]
  52. mod tests {
  53.     use super::*;
  54.     #[test]
  55.     fn test_calculate_checksum() {
  56.         let payload = Bytes::from_static(&[0x01, 0x02, 0x03]);
  57.         let frame = Frame::new(
  58.             0x01, // command
  59.             "LSVNV2182E0200001".to_string(), // vin
  60.             payload
  61.         );
  62.         let checksum = frame.calculate_checksum();
  63.         assert!(checksum != 0, "校验和不应该为0");
  64.         // 创建相同内容的帧,校验和应该相同
  65.         let frame2 = Frame::new(
  66.             0x01,
  67.             "LSVNV2182E0200001".to_string(),
  68.             Bytes::from_static(&[0x01, 0x02, 0x03])
  69.         );
  70.         assert_eq!(checksum, frame2.calculate_checksum(), "相同内容的帧应该有相同的校验和");
  71.     }
  72.     #[test]
  73.     fn test_different_content_different_checksum() {
  74.         let frame1 = Frame::new(0x01, "LSVNV2182E0200001".to_string(), Bytes::from_static(&[0x01]));
  75.         let frame2 = Frame::new(0x01, "LSVNV2182E0200001".to_string(), Bytes::from_static(&[0x02]));
  76.         assert_ne!(
  77.             frame1.calculate_checksum(),
  78.             frame2.calculate_checksum(),
  79.             "不同内容的帧应该有不同的校验和"
  80.         );
  81.     }
  82. }
复制代码
编解码器(codec.rs):

- 实现了 tokio 的 Decoder 和 Encoder trait
- 负责数据帧的序列化和反序列化
  1. use bytes::{ BytesMut, Buf };
  2. use tokio_util::codec::{ Decoder, Encoder };
  3. use super::{ Frame, CodecError };
  4. pub struct Gbt32960Codec;
  5. impl Decoder for Gbt32960Codec {
  6.     type Item = Frame;
  7.     type Error = CodecError;
  8.     fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
  9.         // 检查数据长度是否足够
  10.         if src.len() < 22 {
  11.             // 最小帧长度
  12.             return Ok(None);
  13.         }
  14.         // 检查起始符
  15.         if src[0] != 0x23 {
  16.             return Err(CodecError::InvalidStartByte);
  17.         }
  18.         // TODO: 实现完整的解码逻辑
  19.         // 1. 读取各个字段
  20.         // 2. 验证校验和
  21.         // 3. 解析数据单元
  22.         Ok(None)
  23.     }
  24. }
  25. impl Encoder<Frame> for Gbt32960Codec {
  26.     type Error = CodecError;
  27.     fn encode(&mut self, frame: Frame, dst: &mut BytesMut) -> Result<(), Self::Error> {
  28.         // TODO: 实现编码逻辑
  29.         // 1. 写入各个字段
  30.         // 2. 计算并写入校验和
  31.         Ok(())
  32.     }
  33. }
复制代码
实现完整的解码逻辑

  1. use bytes::{BytesMut, Buf, BufMut};
  2. use tokio_util::codec::{Decoder, Encoder};
  3. use super::{Frame, CodecError};
  4. pub struct Gbt32960Codec;
  5. impl Decoder for Gbt32960Codec {
  6.     type Item = Frame;
  7.     type Error = CodecError;
  8.     fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
  9.         // 检查数据长度是否足够
  10.         if src.len() < 22 {  // 最小帧长度
  11.             return Ok(None);
  12.         }
  13.         // 检查起始符
  14.         if src[0] != 0x23 {
  15.             return Err(CodecError::InvalidStartByte);
  16.         }
  17.         // 读取命令标识和应答标志
  18.         let command_flag = src[1];
  19.         let response_flag = src[2];
  20.         // 读取 VIN 码(17字节)
  21.         let vin = String::from_utf8_lossy(&src[3..20]).to_string();
  22.         // 读取加密方式
  23.         let encrypt_method = src[20];
  24.         // 读取数据单元长度(2字节)
  25.         let payload_length = ((src[21] as u16) << 8) | (src[22] as u16);
  26.         // 检查是否有足够的数据
  27.         let total_length = 23 + payload_length as usize + 1; // 头部 + 数据单元 + 校验码
  28.         if src.len() < total_length {
  29.             return Ok(None);
  30.         }
  31.         // 读取数据单元
  32.         let payload = src.slice(23..23 + payload_length as usize);
  33.         // 读取校验码
  34.         let received_checksum = src[total_length - 1];
  35.         // 创建帧对象进行校验和计算
  36.         let frame = Frame {
  37.             start_byte: 0x23,
  38.             command_flag,
  39.             response_flag,
  40.             vin,
  41.             encrypt_method,
  42.             payload_length,
  43.             payload: payload.freeze(),
  44.             checksum: received_checksum,
  45.         };
  46.         // 验证校验和
  47.         let calculated_checksum = frame.calculate_checksum();
  48.         if calculated_checksum != received_checksum {
  49.             return Err(CodecError::ChecksumMismatch);
  50.         }
  51.         // 消费已处理的字节
  52.         src.advance(total_length);
  53.         Ok(Some(frame))
  54.     }
  55. }
  56. impl Encoder<Frame> for Gbt32960Codec {
  57.     type Error = CodecError;
  58.     fn encode(&mut self, frame: Frame, dst: &mut BytesMut) -> Result<(), Self::Error> {
  59.         // TODO: 实现编码逻辑
  60.         // 1. 写入各个字段
  61.         // 2. 计算并写入校验和
  62.         Ok(())
  63.     }
  64. }
  65. #[cfg(test)]
  66. mod tests {
  67.     use super::*;
  68.     use bytes::Bytes;
  69.     #[test]
  70.     fn test_decode_valid_frame() {
  71.         let mut codec = Gbt32960Codec;
  72.         let mut buffer = BytesMut::new();
  73.         // 构造测试数据
  74.         buffer.put_u8(0x23);                          // 起始符
  75.         buffer.put_u8(0x01);                          // 命令标识
  76.         buffer.put_u8(0xFE);                          // 应答标志
  77.         buffer.extend_from_slice(b"LSVNV2182E0200001"); // VIN码
  78.         buffer.put_u8(0x01);                          // 加密方式
  79.         buffer.put_u16(2);                            // 数据长度
  80.         buffer.extend_from_slice(&[0x01, 0x02]);      // 数据单元
  81.         
  82.         // 计算并添加校验和
  83.         let checksum = buffer[1..buffer.len()].iter().fold(0u8, |acc, &x| acc ^ x);
  84.         buffer.put_u8(checksum);
  85.         // 解码
  86.         let result = codec.decode(&mut buffer).unwrap().unwrap();
  87.         // 验证解码结果
  88.         assert_eq!(result.command_flag, 0x01);
  89.         assert_eq!(result.vin, "LSVNV2182E0200001");
  90.         assert_eq!(result.payload.len(), 2);
  91.         assert_eq!(buffer.len(), 0); // 确保所有数据都被消费
  92.     }
  93.     #[test]
  94.     fn test_decode_invalid_checksum() {
  95.         let mut codec = Gbt32960Codec;
  96.         let mut buffer = BytesMut::new();
  97.         // 构造测试数据(使用错误的校验和)
  98.         buffer.put_u8(0x23);
  99.         buffer.put_u8(0x01);
  100.         buffer.put_u8(0xFE);
  101.         buffer.extend_from_slice(b"LSVNV2182E0200001");
  102.         buffer.put_u8(0x01);
  103.         buffer.put_u16(0);
  104.         buffer.put_u8(0xFF); // 错误的校验和
  105.         // 验证解码失败
  106.         assert!(matches!(
  107.             codec.decode(&mut buffer),
  108.             Err(CodecError::ChecksumMismatch)
  109.         ));
  110.     }
  111. }
复制代码
代码地址

阿里云登录 - 接待登录阿里云,安全稳定的云计算服务平台
总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4