基于 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, // 起始符 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.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(), // 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.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. 添加了单元测试:
   - 测试有效帧的解码
   - 测试校验和错误的情况

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【实战 ES】实战 Elasticsearch:快速上手与深度实践-2.2.3案例:电商订单日志每秒10万条写入优化

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 Elasticsearch批量写入性能调优实战&#xff1a;2.2.3 案例&#xff1a;电商订单日志每秒10万条写入优化1. 原始架构与瓶颈分析1.1 初始集群配置1.2 性能瓶颈定位 2. 全链路…

解决redis lettuce连接池经常出现连接拒绝(Connection refused)问题

一.软件环境 windows10、11系统、springboot2.x、redis 6 7 linux&#xff08;centos&#xff09;系统没有出现这问题&#xff0c;如果你是linux系统碰到的&#xff0c;本文也有一定大参考价值。 根本思路就是&#xff1a;tcp/ip连接的保活(keepalive)。 二.问题描述 在spr…

【开源项目-AI研发】ai-engineer-toolkit

项目地址&#xff08;Fork: 40, Star: 301&#xff09; GitHub - break-into-data/ai-engineer-toolkit: Projects & Resources to help you become a better AI Developer. 项目介绍 官方介绍&#xff1a;帮助你成为更好的 AI 开发者的工具和资源 项目本身是个表格&am…

白帽子讲Web安全资源下载

资源简介 本仓库提供《白帽子讲Web安全》一书的资源下载。这本书由阿里巴巴安全专家刺总编写&#xff0c;是网络安全领域的经典之作&#xff0c;对于从事网络安全工作的专业人士来说是必备的参考资料。 资源描述 书名: 白帽子讲Web安全作者: 阿里巴巴刺总适用人群: 网络安全…

深度学习架构Seq2Seq-添加并理解注意力机制(一)

第一章&#xff1a;人工智能之不同数据类型及其特点梳理 第二章&#xff1a;自然语言处理(NLP)&#xff1a;文本向量化从文字到数字的原理 第三章&#xff1a;循环神经网络RNN&#xff1a;理解 RNN的工作机制与应用场景(附代码) 第四章&#xff1a;循环神经网络RNN、LSTM以及GR…

基于springboot的丢失儿童的基因比对系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 本丢失儿童的基因比对系统采用B/S架构&#xff0c;数据库是MySQL&#xff0c;网站的搭建与开发采用了先进的Java进行编写&#xff0c;使用了Spring Boot框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。用户主要功能包括&#xff1a;用户注册、登…

Mysql面试篇笔记:

优化&#xff1a; 1.如何定位慢查询&#xff1a; 首先压测接口&#xff0c;查看那个接口比较慢&#xff0c;可以通过多种工具&#xff0c;比如Skywaking 可以查看各个接口响应时间&#xff0c;查看接口最慢&#xff0c;然后去跟踪接口&#xff0c;查看详细信息&#…

嵌入式产品级-超小尺寸游戏机(从0到1 硬件-软件-外壳)

Ultra-small size gaming console。 超小尺寸游戏机-Pico This embedded product is mainly based on miniaturization, followed by his game functions are also very complete, for all kinds of games can be played, and there will be relevant illustrations in the fo…

计算机网络-实验四子网划分

三、实验内容及步骤 1.要求 【题目】某单位申请了⼀个 C 类⽹络&#xff0c;单位内部有3个部门&#xff0c;各部门约50台主机&#xff0c;需要划分为3个⼦⽹&#xff0c;各部门接⼊到汇聚交换机&#xff0c;在汇聚层进⾏路由连通。假定申请到的C类网络为200.200.200.0。 2.实…

deepseek+mermaid【自动生成流程图】

成果&#xff1a; 第一步打开deepseek官网(或百度版&#xff08;更快一点&#xff09;)&#xff1a; 百度AI搜索 - 办公学习一站解决 第二步&#xff0c;生成对应的Mermaid流程图&#xff1a; 丢给deepseek代码&#xff0c;或题目要求 生成mermaid代码 第三步将代码复制到me…

SQL Server2022版+SSMS安装教程(保姆级)

SQL Server2022版SSMS安装教程&#xff08;保姆级&#xff09; 一&#xff0c;安装SQL Server数据库 1.下载安装包 &#xff08;1&#xff09;百度网盘下载安装包 链接&#xff1a;https://pan.baidu.com/s/1A-WRVES4EGv8EVArGNF2QQpwd6uvs 提取码&#xff1a;6uvs &#…

Pany-v2:LFI漏洞探测与敏感文件(私钥窃取/其他)自动探测工具

地址:https://github.com/MartinxMax/pany 关于Pany-v2 Pany-v2 是一款 LFI&#xff08;本地文件包含&#xff09;漏洞探测工具&#xff0c;具备自动识别敏感文件的能力。它能够利用 LFI 漏洞检测并提取 id_rsa 私钥、系统密码文件以及其他可能导致安全风险的敏感信息。该工具…

【音视频】视频基本概念

一、视频的基本概念 1.1 视频码率&#xff08;kb/s&#xff09; 视频码率是指视频文件在单位时间内使用的数据流量&#xff0c;也叫码流率。码率越大&#xff0c;说明单位时间内取样率越大&#xff0c;数据流进度也就越高 1.2 视频帧率&#xff08;fps&#xff09; 视频帧率…

三维数据可视化与表面重建:Marching Cubes算法的原理与应用

1. 引言 随着现代医学影像技术的飞速发展&#xff0c;三维数据的可视化与重建已成为医学研究、临床诊断和手术规划的重要工具。在众多三维重建算法中&#xff0c;Marching Cubes算法因其高效、稳定的特性成为从离散数据场中提取等值面的经典方法。本报告将深入探讨Marching Cu…

探秘基带算法:从原理到5G时代的通信变革【七】FFT/DFT

文章目录 2.6 FFT/DFT2.6.1 离散傅里叶变换&#xff08;DFT&#xff09;2.6.2 快速傅里叶变换&#xff08;FFT&#xff09;2.6.3 方法论与分类体系2.6.4 优缺点与应用2.6.5 实现细节 本博客为系列博客&#xff0c;主要讲解各基带算法的原理与应用&#xff0c;包括&#xff1a;v…

水仙花数(华为OD)

题目描述 所谓水仙花数&#xff0c;是指一个n位的正整数&#xff0c;其各位数字的n次方和等于该数本身。 例如153是水仙花数&#xff0c;153是一个3位数&#xff0c;并且153 13 53 33。 输入描述 第一行输入一个整数n&#xff0c;表示一个n位的正整数。n在3到7之间&#x…

《Python实战进阶》No 7: 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战

第7集&#xff1a; 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战 在现代 Web 开发中&#xff0c;实时通信已经成为许多应用的核心需求。无论是聊天应用、股票行情推送&#xff0c;还是多人协作工具&#xff0c;WebSocket 都是实现高效实时通信的最佳选择之一。本…

极简Redis速成学习

redis是什么&#xff1f; 是一种以键值对形式存储的数据库&#xff0c;特点是基于内存存储&#xff0c;读写快&#xff0c;性能高&#xff0c;常用于缓存、消息队列等应用情境 redis的五种数据类型是什么&#xff1f; 分别是String、Hash、List、Set和Zset&#xff08;操作命…

ADC采集模块与MCU内置ADC性能对比

2.5V基准电压源&#xff1a; 1. 精度更高&#xff0c;误差更小 ADR03B 具有 0.1% 或更小的初始精度&#xff0c;而 电阻分压方式的误差主要来自电阻的容差&#xff08;通常 1% 或 0.5%&#xff09;。长期稳定性更好&#xff0c;分压电阻容易受到温度、老化的影响&#xff0c;长…

python数据容器切片

从一个序列中取出一个子序列 序列[起始位置:结束位置:步长] 起始位置和结束位置 省略,表示从头取到尾 步长省略表示1 步长负数,表示从后往前取 步长-1 等同于将序列反转了