C#合并CAN ASC文件:实现与优化
C#合并CAN ASC文件:实现与优化
在汽车电子和工业控制领域,CAN(Controller Area Network)总线是一种广泛使用的通信协议。CAN ASC(American Standard Code)文件则是记录CAN总线通信数据的标准格式,常用于数据分析和故障排查。当需要处理多个时间段的CAN数据时,合并多个ASC文件就成为了必要操作。本文将介绍如何使用C#实现CAN ASC文件的合并功能。
一、ASC文件格式简介
CAN ASC文件通常包含以下部分:
- 文件头部:包含日期、时间戳类型等信息
- 数据记录:每条记录包含时间戳、CAN ID、数据长度和数据内容
一个典型的ASC文件示例:
date Wed May 29 10:30:00 am 2025
base hex timestamps absolute0.000000 123 Tx d 8 01 02 03 04 05 06 07 08 Channel: 11.500000 456 Rx d 8 11 12 13 14 15 16 17 18 Channel: 1
二、合并CAN ASC文件的挑战
合并多个ASC文件看似简单,但实际上需要考虑以下几个关键问题:
-
- 时间戳处理:如何将不同文件中的相对时间戳合并为统一的时间线
-
- 文件顺序:按什么顺序合并文件才能保证时间连续性
-
- 数据一致性:合并后的数据行数是否正确
-
- 重复合并检查:避免重复合并相同的文件
三、C#实现CAN ASC文件合并器
下面是一个完整的C#实现,它能够处理多个ASC文件的合并,并解决上述挑战:
/// <summary>
/// 报文合并
/// </summary>
public class AscMerger
{/// <summary>/// 源文件夹路径,存储需要合并的 ASC 文件/// </summary>private readonly string sourcePath;/// <summary>/// 目标文件夹路径,用于存储合并后的 ASC 文件/// </summary>private readonly string destinationPath;public AscMerger(string sourcePath, string destinationPath){// 设置源文件夹路径this.sourcePath = sourcePath;// 设置目标文件夹路径this.destinationPath = destinationPath;}public (string filePath, ErrorCode errorCode) Merge(){try{if (!Directory.Exists(this.sourcePath)){return ("", ErrorCode.SourceFolderNotFound);}if (!Directory.Exists(this.destinationPath)){return ("", ErrorCode.DestinationFolderNotFound);}var files = this.GetFiles();if (!files.Any()){return ("", ErrorCode.AscFilesNotFound);}var messages = files.Select(f => this.ReadFile(f)).ToList();var headerTime = this.CalculateHeaderTime(messages);var headerTimestamp = ToTimestamp(headerTime);var combinedMessages = this.CombineMessages(messages, headerTimestamp);// 验证文件已存在var targetName = $"{this.ToFileString(headerTime)}.asc";if (files.Any(f => f.Name == targetName)){return ("", ErrorCode.AlreadyMerged);}// 验证行数var lineCount = messages.Select(m => m.Skip(2).Count()).Sum();if (lineCount != combinedMessages.Count){return ("", ErrorCode.Inconsistent);}// 保存var target = Path.Combine(this.destinationPath, targetName);if (!this.SaveDestinationFile(headerTime, combinedMessages, target)){return ("", ErrorCode.FileWriteError);}return (target, ErrorCode.None);}catch (Exception e){Console.WriteLine($"{e.Message}\r\n{e.StackTrace}");return ("", ErrorCode.FileReadError);}}private List<FileInfo> GetFiles(){return new DirectoryInfo(this.sourcePath).GetFiles("*.asc").ToList();}private List<string> ReadFile(FileInfo f){using (var sr = new StreamReader(f.FullName)){var list = new List<string>();var content = "";while (!string.IsNullOrWhiteSpace((content = sr.ReadLine()))){list.Add(content);}return list;}}private DateTime CalculateHeaderTime(List<List<string>> messages){return messages.Where(m => m.Any()).Select(m => m.Take(1).First()).Select(m => this.FromCanHeaderString(m)).Min();}private DateTime FromCanHeaderString(string s){s = s.Replace("date ", "").Replace("am", "AM").Replace("pm", "PM");var format = "ddd MMM dd hh:mm:ss tt yyyy";var culture = CultureInfo.CreateSpecificCulture("en-US");return DateTime.ParseExact(s, format, culture);}private double ToTimestamp(DateTime d){if (d == DateTime.MinValue){return 0;}return new DateTimeOffset(d).ToUnixTimeMilliseconds() / 1000.0;}private List<string> CombineMessages(List<List<string>> messages, double headerTimestamp){return (from dict infrom m in messagesselect this.CalculateTimestamp(m)from kp in dictorderby kp.Item1select this.ReplaceTimestamp(kp.Item2, headerTimestamp, kp.Item1)).ToList();}private List<(double, string)> CalculateTimestamp(List<string> messages){if (messages == null || messages.Count < 2){return new List<(double, string)>();}var header = messages.Take(2).ToList();var time = this.FromCanHeaderString(header[0]);var timestamp = ToTimestamp(time);return messages.Skip(2).Select(m => (this.ExtractTimestamp(m), m)).Select(m => (timestamp + m.Item1, m.Item2)).ToList();}private double ExtractTimestamp(string s){var reg = new Regex("^\\d{1,}.\\d{6}");var match = reg.Match(s);return double.Parse(match.Value);}private string ReplaceTimestamp(string s, double baseTimestamp, double timestamp){return Regex.Replace(s, "^\\d{1,}.\\d{6}", (timestamp - baseTimestamp).ToString("0.000000"));}private bool SaveDestinationFile(DateTime headerTime, List<string> messages, string path){using (var sw = new StreamWriter(path)){sw.WriteLine($"date {this.ToCanHeaderString(headerTime)}");sw.WriteLine("base hex timestamps absolute");sw.Flush();foreach (var m in messages){sw.WriteLine(m);}return true;}}private string ToFileString(DateTime time){return $"{time:yyyyMMdd_HHmmss}";}public string ToCanHeaderString(DateTime d){var format = "ddd MMM dd hh:mm:ss tt yyyy";var culture = CultureInfo.CreateSpecificCulture("en-US");return d.ToString(format, culture).Replace("AM", "am").Replace("PM", "pm");}
}public enum ErrorCode
{None = 0,SourceFolderNotFound = 1,DestinationFolderNotFound = 2,AscFilesNotFound = 3,FileReadError = 4,FileWriteError = 5,Inconsistent = 6,AlreadyMerged = 7
}
四、代码解析
这个ASC文件合并器主要包含以下几个核心功能:
-
- 初始化与路径验证:通过构造函数接收源文件夹和目标文件夹路径,并在合并前验证这些路径是否存在。
-
- 文件读取:
ReadFile
方法负责读取单个ASC文件的内容,将其存储为字符串列表。
- 文件读取:
-
- 时间戳处理:
CalculateHeaderTime
方法确定所有文件中最早的时间戳ExtractTimestamp
方法从每条记录中提取相对时间戳ReplaceTimestamp
方法将所有时间戳转换为相对于合并后文件开始时间的相对时间
-
- 文件合并:
CombineMessages
方法将所有文件的内容按时间顺序合并,并处理时间戳转换。
- 文件合并:
-
- 数据验证:在合并前后进行数据验证,确保合并过程中没有数据丢失。
-
- 错误处理:使用枚举类型
ErrorCode
处理各种可能的错误情况,确保程序的健壮性。
- 错误处理:使用枚举类型
五、使用示例
下面是如何使用这个合并器的简单示例:
static void Main(string[] args)
{string sourcePath = @"C:\CAN\Source";string destinationPath = @"C:\CAN\Destination";var merger = new AscMerger(sourcePath, destinationPath);var result = merger.Merge();if (result.errorCode == ErrorCode.None){Console.WriteLine($"合并成功!文件保存至: {result.filePath}");}else{Console.WriteLine($"合并失败!错误码: {result.errorCode}");}
}
六、性能优化建议
对于处理大量或大型ASC文件的情况,可以考虑以下优化:
- 使用并行处理来加速文件读取
- 实现流式处理,避免将整个文件加载到内存中
- 添加进度报告功能,让用户了解合并进度
- 增加文件过滤功能,只合并特定时间段的文件
通过这种方式实现的CAN ASC文件合并器,不仅能够正确处理时间戳问题,还提供了完善的错误处理机制,确保合并过程的可靠性和数据的完整性。无论是用于汽车诊断、工业自动化还是其他CAN总线应用场景,这个工具都能帮助工程师更高效地处理和分析CAN数据。
相关文章:
C#合并CAN ASC文件:实现与优化
C#合并CAN ASC文件:实现与优化 在汽车电子和工业控制领域,CAN(Controller Area Network)总线是一种广泛使用的通信协议。CAN ASC(American Standard Code)文件则是记录CAN总线通信数据的标准格式ÿ…...

[TIP] Ubuntu 22.04 配置多个版本的 GCC 环境
问题背景 在 Ubuntu 22.04 中安装 VMware 虚拟机时,提示缺少 VMMON 和 VMNET 模块 编译这两个模块需要 GCC 的版本大于 12.3.0,而 Ubuntu 22.04 自带的 GCC 版本为 11.4.0 因此需要安装对应的 GCC 版本,但为了不影响其他程序,需…...

如何思考?分析篇
现代人每天刷 100 条信息,却难静下心读 10 页书。 前言: 我一直把思考当作一件生活中和工作中最为重要的事情。但是我发现当我想写一篇跟思考有关的文章时,却难以下手。因为思考是一件非常复杂的事情,用文字描述十分的困难。 读书…...

Redis:Hash数据类型
🌈 个人主页:Zfox_ 🔥 系列专栏:Redis 🔥 Hash哈希 🐳 ⼏乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组、映射。在Redis中&#…...
抗辐照MCU在卫星载荷电机控制器中的实践探索
摘要:在航天领域,卫星系统的可靠运行对电子元件的抗辐照性能提出了严苛要求。微控制单元(MCU)作为卫星载荷电机控制器的核心部件,其稳定性与可靠性直接关系到卫星任务的成败。本文聚焦抗辐照MCU在卫星载荷电机控制器中的应用实践&…...

快捷键的记录
下面对应的ATL数字 ATL4 显示编译输出 CTRL B 编译 CTRLR 运行exe 菜单栏 ALTF ALTE ALTB ALTD ALTH...

Python读取阿里法拍网的html+解决登录cookie
效果图 import time from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from lxml import etreedef get_taobao_auct…...

electron-vite串口通信
一、构建项目后,安装“串口通信库” npm install serialport二、设置 npm install --save-dev electron-rebuild ./node_modules/.bin/electron-rebuild 注意:如果执行报错以下问题 1、未配置python变量 2、没有Microsoft Visual Studio BuildTools 3…...

中山大学美团港科大提出首个音频驱动多人对话视频生成MultiTalk,输入一个音频和提示,即可生成对应唇部、音频交互视频。
由中山大学、美团、香港科技大学联合提出的MultiTalk是一个用于音频驱动的多人对话视频生成的新框架。给定一个多流音频输入和一个提示,MultiTalk 会生成一个包含提示所对应的交互的视频,其唇部动作与音频保持一致。 相关链接 论文:https://a…...
Maven的配置与运行
maven配置国内镜像 <!-- # %MAVEN_HOME%\conf\settings.xml # 找到 <mirrors> 标签,添加: --> <mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>htt…...
MySQL 迁移至 Docker ,删除本地 mysql
macOS 的删除有大量的配置文件和相关数据文件要删除,如果 update mysql 那么数据更杂。 停止 MYSQL 使用 brew 安装,则 brew services stop mysql 停止 mysql 。 如果没有使用 brew 安装,则 sudo /usr/local/mysql/support-files/mysq…...

redis分片集群架构
主从集群解决高并发,哨兵解决高可用问题。但是任然有两个问题没有解决:1海量数据存储问题;2高并发写的问题(如果服务中有大量写的请求) 那就可以采用分片集群架构解决这些问题 分片集群特征 分片集群中有多个master…...

关于物联网的基础知识(一)
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于物联网的基础知识(一&a…...
浏览器后台服务 vs 在线教育:QPS、并发模型与架构剖析
本文深入分析浏览器后台服务与在线教育平台在高并发场景下的架构设计差异,涵盖 QPS(每秒请求数)承压能力、服务模型、数据一致性、容灾机制等多个维度,力图为系统架构师和后端工程师提供实战参考。 一、什么是高并发场景ÿ…...

电脑商城--用户注册登录
用户注册 1 用户-创建数据表 1.使用use命令先选中store数据库。 USE store; 2.在store数据库中创建t_user用户数据表。 CREATE TABLE t_user (uid INT AUTO_INCREMENT COMMENT 用户id,username VARCHAR(20) NOT NULL UNIQUE COMMENT 用户名,password CHAR(32) NOT NULL COMME…...
Riverpod与GetX的优缺点对比
Riverpod 与 GetX 的优缺点对比 在 Flutter 开发领域,Riverpod 和 GetX 都是备受关注的状态管理与依赖注入框架,它们各有优劣,适用于不同的开发场景。以下从多个维度详细对比二者的优缺点。 一、Riverpod 的优缺点 (一)优点 架构清晰,数据流向明确:基于 Provider 模…...
Three.js怎么工作的?
Three.js 是怎么工作的? Three.js 的核心工作是: 构建一个虚拟的 3D 世界(Scene) 模拟摄像机视角(Camera) 用 WebGL 把这个场景“渲染成一张图片” 把这张图片画在 canvas 上 👉 所以 Three…...
LangChain面试内容整理-知识点1:LangChain架构与核心理念
LangChain 是一个用于构建基于大型语言模型(LLM)的应用的框架,其架构采用模块化设计,核心理念是将语言模型与外部工具、数据源相结合,以实现复杂任务的分解与执行medium.com。整个框架可以理解为一系列可组合的组件,包括链(Chain)、智能体(Agent)、工具(Tool)和LLM…...
双面沉金线路板制作流程解析:高可靠性PCB的核心工艺
在高端电子制造领域,双面沉金(ENIG)线路板因其优异的焊接性能、抗氧化能力和信号完整性,已成为5G通信、医疗设备和汽车电子等领域的首选。本文将深入解析其制作流程的关键环节,帮助工程师更好地理解这一核心工艺。 一、…...

什么是梯度磁场
梯度磁场是叠加在均匀主磁场(如MRI中的静磁场B₀)上的一种特殊磁场,其强度会沿着特定方向(如X、Y或Z轴)呈线性变化。这种磁场在磁共振成像和粒子控制等领域发挥着关键作用,主要用于实现空间位置的精确编码和…...

从零开始的python学习(七)P102+P103+P104+P105+P106+P107
本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...

Linux--进程的调度
1.进程切换 CPU上下⽂切换:其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运⾏另外的任务时, 它保存正在运⾏任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中, ⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该…...
Hadolint:Dockerfile 语法检查与最佳实践验证的终极工具
在容器化应用开发的浪潮中,Dockerfile 作为构建 Docker 镜像的核心配置文件,其质量直接影响着应用的安全性、稳定性和可维护性。然而,随着项目复杂度的增加,手动检查 Dockerfile 不仅耗时,还容易遗漏潜在问题。今天,我要向大家介绍一款强大的工具——Hadolint,它将彻底改…...
Python爬虫实战:研究Hyper 相关技术
一、项目概述 本项目展示了如何结合 Python 的异步编程技术与 Hyper 框架开发一个高性能、可扩展的网络爬虫系统。该系统不仅能够高效地爬取网页内容,还提供了 RESTful API 接口,方便用户通过 API 控制爬虫的运行状态和获取爬取结果。 二、系统架构设计 1. 整体架构 系统采…...
基于langchain的简单RAG的实现
闲来无事,想研究一下RAG的实现流程,看网上用langchain的比较多,我自己在下面也跑了跑,代码很简单,以次博客记录一下,方便回顾 langchain LangChain 是一个基于大型语言模型(LLM)开发…...

VmWare Ubuntu22.04 搭建DPDK 20.11.1
一、开发环境 Ubuntu 版本 二、增加虚拟机的网卡 给虚拟机增加1个网卡,加上原来的网卡,一共2个 网络适配器作为 ssh 连接的网卡,网络适配器2作为 DPDK 运行的网卡。 三、NAT模式简介 这里待补充,网上都是那一张图,看不懂 四、使网卡名称从0开始命名 进入管理员权限 s…...

selenium-自动更新谷歌浏览器驱动
1、简介 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题,因为有些网页数据是通过JavaScript动态加载的。selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如输入…...
34、协程
在Linux系统中,协程是一种轻量级的线程,它们允许在多个任务之间切换,而不需要操作系统的线程调度。协程可以分为有栈协程和无栈协程,以及对称协程和非对称协程。 有栈协程 有栈协程每个协程都有自己的栈空间,允许协程…...
Apache POI操作Excel详解
Maven依赖 <!-- 核心库(支持.xls) --> <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId> </dependency><!-- 支持.xlsx格式 --> <dependency><groupId>org.a…...

Docker容器部署elasticsearch8.*与Kibana8.*版本使用filebeat采集日志
第 1 步:使用 Docker Compose 部署 Elasticsearch 和 Kibana 首先,我们需要创建一个 docker-compose.yml 文件来定义和运行 Elasticsearch 和 Kibana 服务。这种方式可以轻松管理两个容器的配置和网络。 创建 docker-compose.yml 文件 在一个新的文件夹…...