From 8f015784116808e2f01535c6ef7dbc06ba55f2b0 Mon Sep 17 00:00:00 2001 From: LuChiChick <1084116302@qq.com> Date: Wed, 19 Nov 2025 17:47:20 +0800 Subject: [PATCH] Added a new DLT File class that implements basic payload parsing && Added a CSV export function for the DLT File class (currently in testing and not fully completed) && Added a command-line parsing entry point. --- Inc/Command_Solve.hpp | 7 + Inc/DLT_Utilities.hpp | 167 +++++++++++++++++++ Src/Command_Solve.cpp | 39 +++++ Src/DLT_Utilities.cpp | 374 ++++++++++++++++++++++++++++++++++++++++++ Src/Main.cpp | 4 + 5 files changed, 591 insertions(+) create mode 100644 Inc/Command_Solve.hpp create mode 100644 Inc/DLT_Utilities.hpp create mode 100644 Src/Command_Solve.cpp create mode 100644 Src/DLT_Utilities.cpp diff --git a/Inc/Command_Solve.hpp b/Inc/Command_Solve.hpp new file mode 100644 index 0000000..1a51cda --- /dev/null +++ b/Inc/Command_Solve.hpp @@ -0,0 +1,7 @@ +#ifndef __COMMAND_SOLVE_HPP__ +#define __COMMAND_SOLVE_HPP__ + +// 命令行处理 +void Pre_Command_Solve(void); + +#endif \ No newline at end of file diff --git a/Inc/DLT_Utilities.hpp b/Inc/DLT_Utilities.hpp new file mode 100644 index 0000000..8e8499e --- /dev/null +++ b/Inc/DLT_Utilities.hpp @@ -0,0 +1,167 @@ +#ifndef __DLT_UTILITIES_HPP__ +#define __DLT_UTILITIES_HPP__ + +extern "C" +{ +#include "stdint.h" +#include "stdio.h" +} + +// DLT 类型定义 +namespace DLT_Type +{ +#pragma pack(1) // 紧凑字节对齐 + + /** + * The structure of the DLT file storage header. This header is used before each stored DLT message. + */ + typedef struct + { + char pattern[4]; /**< This pattern should be DLT0x01 */ + uint32_t seconds; /**< seconds since 1.1.1970 */ + int32_t microseconds; /**< Microseconds */ + char ecu[4]; /**< The ECU id is added, if it is not already in the DLT message itself */ + } DltStorageHeader; + + typedef struct + { + struct + { + uint8_t UEH : 1; // Use Extended Header 1 启用扩展头 0 不启用 + uint8_t MSBF : 1; // Most Significant Byte First 1 Payload大端序列 0 小端序列 + uint8_t WEID : 1; // With ECU ID (4 Byte) 1 包含ECU_ID 0 不含 + uint8_t WSID : 1; // With Session ID (4 Byte) 1 包含会话 ID 0 不含 + uint8_t WTMS : 1; // With Timestamp (4 Byte) 1 含时间戳 0 不含 + uint8_t VERS : 3; // Version Number DLT协议版本 + + } htyp; /**< This parameter contains several informations, see definitions below */ + uint8_t mcnt; /**< The message counter is increased with each sent DLT message */ + uint16_t len; /**< Length of the complete message, without storage header */ + } DltStandardHeader; + + /** + * The structure of the DLT extra header parameters. Each parameter is sent only if enabled in htyp. + */ + typedef struct + { + char ecu[4]; /**< ECU id */ + uint32_t seid; /**< Session number */ + uint32_t tmsp; /**< Timestamp since system start in 0.1 milliseconds */ + } DltStandardHeaderExtra; + + /** + * The structure of the DLT extended header. This header is only sent if enabled in htyp parameter. + */ + typedef struct + { + struct + { + uint8_t VERB : 1; // (Verbose) 冗余模式 1 启用 0 不启用 + uint8_t MSTP : 3; // (Message Type) + uint8_t MTIN : 4; // (Message Type Info) + } msin; /**< messsage info */ + uint8_t noar; /**< number of arguments */ + char apid[4]; /**< application id */ + char ctid[4]; /**< context id */ + } DltExtendedHeader; + +#pragma pack(0) // 恢复字节对齐 + + // DLT 错误信息枚举 + typedef enum + { + DLT_ERROR_NONE = 0, // 无错误 + DLT_ERROR_NULL_POINTER, // 空指针 + DLT_ERROR_FILE_OPEN_FAILED, // 文件打开失败 + DLT_ERROR_FILE_READ_FAILED, // 文件读取失败 + DLT_ERROR_FILE_CREATE_FAILED, // 文件创建失败 + DLT_ERROR_END_OF_FILE, // 文件结尾 + DLT_ERROR_INVALID_HEADER, // 非法头 + DLT_ERROR_INCOMPLETE_DATA, // 数据不完整 + DLT_ERROR_MEM_ALLOCATE_FAILED, // 内存分配失败 + DLT_ERROR_MSG_LIST_EMPTY, // 消息列表为空 + } DLT_Err; + + // DLT消息条目结构定义 + typedef struct + { + DltStorageHeader storage_header; // 存储头 + DltStandardHeader standard_header; // 标准头 + DltStandardHeaderExtra standard_header_extra; // 标准头额外内容 + DltExtendedHeader extendedheader; // 扩展头 + size_t payload_length; // 消息长度 + uint8_t *payload_buffer; // 载荷 + } DLT_Msg; // 文件中的消息条目 + + // DLT消息链表节点 + typedef struct DLT_Msg_Node_Struct + { + DLT_Msg Msg; + DLT_Msg_Node_Struct *p_next; + } DLT_Msg_Node; +} + +// DLT 文件类 +class DLT_File +{ +private: + FILE *p_file; // 文件指针 + size_t msg_count; // 消息条目计数 + size_t payload_max_len; // 最长载荷长度 + + DLT_Type::DLT_Msg_Node *Msg_List_Head; // 消息列表 + + /** + * @brief 读取dlt文件头 + * @param Msg 读取后存储的目标结构体 + * @return DLT_Type::DLT_Err 错误类型枚举 + */ + DLT_Type::DLT_Err dlt_file_read_header(DLT_Type::DLT_Msg *Msg); + + /** + * @brief 读取dlt文件载荷 + * @param Msg 读取后存储的目标结构体 + * @return DLT_Type::DLT_Err 错误类型枚举 + */ + DLT_Type::DLT_Err dlt_file_read_payload(DLT_Type::DLT_Msg *Msg); + + /** + * @brief 解析dlt文件 + * @param null + * @return DLT_Type::DLT_Err 错误类型枚举 + */ + DLT_Type::DLT_Err dlt_file_parse(void); + +public: + DLT_File(); // 构造函数 + ~DLT_File(); // 析构函数 + /** + * @brief 打开DLT文件 + * @param file_name_str 目标文件名 + * @return DLT_Type::DLT_Err 错误类型枚举 + */ + DLT_Type::DLT_Err open(const char *file_name_str); + + /** + * @brief 获取解析完成的消息列表 + * @param p_msg_list 消息列表存入的指针 + * @return 消息列表计数 + */ + size_t get_msg_list(DLT_Type::DLT_Msg_Node *(&p_msg_list)); + + /** + * @brief 导出为CSV + * @param file_name_str 目标文件名 + * @return DLT_Type::DLT_Err 错误类型枚举 + */ + DLT_Type::DLT_Err export_to_csv(const char *file_name_str); + + /** + * @brief 清理请求的存储并恢复初始状态 + * @param null + * @return null + */ + void clear(void); +}; + +#endif \ No newline at end of file diff --git a/Src/Command_Solve.cpp b/Src/Command_Solve.cpp new file mode 100644 index 0000000..b5944b1 --- /dev/null +++ b/Src/Command_Solve.cpp @@ -0,0 +1,39 @@ +#include "Command_Solve.hpp" +#include "DLT_Utilities.hpp" + +extern "C" +{ +#include "windows.h" +} + +DLT_File File; + +// 命令行处理 +void Pre_Command_Solve(void) +{ + // 获取命令行 + int argc; + LPWSTR *argv; + argv = CommandLineToArgvW(GetCommandLineW(), &argc); + + if (argc <= 1) + return; + + DLT_Type::DLT_Err err; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, argv[1], -1, NULL, 0, NULL, NULL); + char *strTo = (char *)malloc(size_needed); + WideCharToMultiByte(CP_UTF8, 0, argv[1], -1, &strTo[0], size_needed, NULL, NULL); + + err = File.open(strTo); + + DLT_Type::DLT_Msg_Node *p_list; + File.get_msg_list(p_list); + + free(strTo); + + err = File.export_to_csv("test.csv"); + + if (err == DLT_Type::DLT_ERROR_NONE) + return; +} \ No newline at end of file diff --git a/Src/DLT_Utilities.cpp b/Src/DLT_Utilities.cpp new file mode 100644 index 0000000..79017c9 --- /dev/null +++ b/Src/DLT_Utilities.cpp @@ -0,0 +1,374 @@ +#include "DLT_Utilities.hpp" + +extern "C" +{ +#include "stdlib.h" +#include "string.h" +#include "sys/time.h" +} + +// 构造函数 +DLT_File::DLT_File() +{ + // 初始化内容 + this->p_file = nullptr; + this->msg_count = 0; + this->payload_max_len = 0; + this->Msg_List_Head = nullptr; +} + +// 析构函数 +DLT_File::~DLT_File() +{ + clear(); +} + +// 清理请求的存储并恢复初始状态 +void DLT_File::clear(void) +{ + + if (this->Msg_List_Head == nullptr) + return; + + // 释放分配的空间 + while (this->Msg_List_Head != nullptr) + { + DLT_Type::DLT_Msg_Node *p_target = this->Msg_List_Head; + this->Msg_List_Head = this->Msg_List_Head->p_next; + free(p_target->Msg.payload_buffer); + free(p_target); + } + + // 初始化内容 + this->p_file = nullptr; + this->msg_count = 0; + this->payload_max_len = 0; + this->Msg_List_Head = nullptr; +} + +DLT_Type::DLT_Err DLT_File::dlt_file_read_header(DLT_Type::DLT_Msg *Msg) +{ + if (Msg == nullptr) + return DLT_Type::DLT_ERROR_NULL_POINTER; + + // 存储头 + { + + // 读取存储头 + if (fread(&(Msg->storage_header), sizeof(Msg->storage_header), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_END_OF_FILE; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + + // 检查存储头 + if (memcmp(Msg->storage_header.pattern, "DLT\x01", 4)) + return DLT_Type::DLT_ERROR_INVALID_HEADER; + } + + // 标准头 + { + // 读取标准头 + if (fread(&(Msg->standard_header), sizeof(Msg->standard_header), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + + // 反向拼接len字段 + // [PRS_Dlt_00091] ⌈The Standard Header and the Extended Header shall be in big endian format (MSB first).⌋ (RS_LT_00016,RS_LT_00013) + // DLT标准头是大端序,与小端序存储的uint16正好相反,所以需要反向 + { + uint16_t len_swap; + *((uint8_t *)&len_swap) = *(((uint8_t *)&Msg->standard_header.len) + 1); + *(((uint8_t *)&len_swap) + 1) = *((uint8_t *)&Msg->standard_header.len); + Msg->standard_header.len = len_swap; + } + } + + // 消息头长度统计 + size_t msg_header_len = sizeof(Msg->standard_header); + + // 标准头扩展部分 + { + // 含有额外的ECU ID + if (Msg->standard_header.htyp.WEID) + { + msg_header_len += sizeof(Msg->standard_header_extra.ecu); + + if (fread(&(Msg->standard_header_extra.ecu), sizeof(Msg->standard_header_extra.ecu), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + } + + // 含有额外的 Session ID (会话ID) + if (Msg->standard_header.htyp.WSID) + { + msg_header_len += sizeof(Msg->standard_header_extra.seid); + + if (fread(&(Msg->standard_header_extra.seid), sizeof(Msg->standard_header_extra.seid), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + + // 反向拼接Session ID字段 + // [PRS_Dlt_00091] ⌈The Standard Header and the Extended Header shall be in big endian format (MSB first).⌋ (RS_LT_00016,RS_LT_00013) + { + uint32_t data_swap; + + *((uint8_t *)&data_swap) = *(((uint8_t *)&Msg->standard_header_extra.seid) + 3); + *(((uint8_t *)&data_swap) + 1) = *(((uint8_t *)&Msg->standard_header_extra.seid) + 2); + *(((uint8_t *)&data_swap) + 2) = *(((uint8_t *)&Msg->standard_header_extra.seid) + 1); + *(((uint8_t *)&data_swap) + 3) = *((uint8_t *)&Msg->standard_header_extra.seid); + + Msg->standard_header_extra.seid = data_swap; + } + } + + // 含有额外的 timestamp (0.1ms 自系统启动后的时间计数) + if (Msg->standard_header.htyp.WSID) + { + msg_header_len += sizeof(Msg->standard_header_extra.tmsp); + + if (fread(&(Msg->standard_header_extra.tmsp), sizeof(Msg->standard_header_extra.tmsp), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + + // 反向拼接Session ID字段 + // [PRS_Dlt_00091] ⌈The Standard Header and the Extended Header shall be in big endian format (MSB first).⌋ (RS_LT_00016,RS_LT_00013) + { + uint32_t data_swap; + + *((uint8_t *)&data_swap) = *(((uint8_t *)&Msg->standard_header_extra.tmsp) + 3); + *(((uint8_t *)&data_swap) + 1) = *(((uint8_t *)&Msg->standard_header_extra.tmsp) + 2); + *(((uint8_t *)&data_swap) + 2) = *(((uint8_t *)&Msg->standard_header_extra.tmsp) + 1); + *(((uint8_t *)&data_swap) + 3) = *((uint8_t *)&Msg->standard_header_extra.tmsp); + + Msg->standard_header_extra.tmsp = data_swap; + } + } + } + + // 扩展头读取 + if (Msg->standard_header.htyp.UEH) + { + msg_header_len += sizeof(Msg->extendedheader); + + // 读取标扩展头 + if (fread(&(Msg->extendedheader), sizeof(Msg->extendedheader), 1, this->p_file) != 1) + { + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + } + + // 计算Payload长度 + Msg->payload_length = Msg->standard_header.len - msg_header_len; + + // 统计最长长度 + if (Msg->payload_length > this->payload_max_len) + this->payload_max_len = Msg->payload_length; + + return DLT_Type::DLT_ERROR_NONE; +} + +DLT_Type::DLT_Err DLT_File::dlt_file_read_payload(DLT_Type::DLT_Msg *Msg) +{ + if (Msg == nullptr) + return DLT_Type::DLT_ERROR_NULL_POINTER; + + // 读取Payload + Msg->payload_buffer = (uint8_t *)malloc(Msg->payload_length); + if (Msg->payload_buffer == nullptr) + return DLT_Type::DLT_ERROR_MEM_ALLOCATE_FAILED; + + if (fread(Msg->payload_buffer, Msg->payload_length, 1, this->p_file) != 1) + { + free(Msg->payload_buffer); + Msg->payload_buffer = nullptr; + + if (feof(this->p_file)) + return DLT_Type::DLT_ERROR_INCOMPLETE_DATA; + else + return DLT_Type::DLT_ERROR_FILE_READ_FAILED; + } + + return ::DLT_Type::DLT_ERROR_NONE; +} + +DLT_Type::DLT_Err DLT_File::dlt_file_parse(void) +{ + DLT_Type::DLT_Msg_Node *p_list_end = nullptr; + + DLT_Type::DLT_Err err = DLT_Type::DLT_ERROR_NONE; + while (1) + { + DLT_Type::DLT_Msg Msg; + + err = dlt_file_read_header(&Msg); + if (err != DLT_Type::DLT_ERROR_NONE) + { + if (err == DLT_Type::DLT_ERROR_END_OF_FILE) + break; + return err; + } + err = dlt_file_read_payload(&Msg); + if (err != DLT_Type::DLT_ERROR_NONE) + return err; + + if (this->Msg_List_Head == nullptr) + { + this->Msg_List_Head = (DLT_Type::DLT_Msg_Node *)malloc(sizeof(DLT_Type::DLT_Msg_Node)); + if (this->Msg_List_Head == nullptr) + return DLT_Type::DLT_ERROR_MEM_ALLOCATE_FAILED; + this->Msg_List_Head->p_next = nullptr; + this->Msg_List_Head->Msg = Msg; + + p_list_end = Msg_List_Head; + } + else + { + DLT_Type::DLT_Msg_Node *p_target = (DLT_Type::DLT_Msg_Node *)malloc(sizeof(DLT_Type::DLT_Msg_Node)); + if (p_target == nullptr) + return DLT_Type::DLT_ERROR_MEM_ALLOCATE_FAILED; + p_target->p_next = nullptr; + p_target->Msg = Msg; + + p_list_end->p_next = p_target; + p_list_end = p_target; + } + + this->msg_count++; + } + + return DLT_Type::DLT_ERROR_NONE; +} + +DLT_Type::DLT_Err DLT_File::open(const char *file_name_str) +{ + // 打开文件 + this->p_file = fopen(file_name_str, "rb"); + if (this->p_file == nullptr) + return DLT_Type::DLT_ERROR_FILE_OPEN_FAILED; + + // 求解文件 + return dlt_file_parse(); +} + +size_t DLT_File::get_msg_list(DLT_Type::DLT_Msg_Node *(&p_msg_list)) +{ + if (this->msg_count != 0) + p_msg_list = this->Msg_List_Head; + else + p_msg_list = nullptr; + + return this->msg_count; +} + +DLT_Type::DLT_Err DLT_File::export_to_csv(const char *file_name_str) +{ + FILE *p_file = fopen(file_name_str, "wb"); + if (p_file == nullptr) + return DLT_Type::DLT_ERROR_FILE_CREATE_FAILED; + + DLT_Type::DLT_Msg_Node *p_msg = this->Msg_List_Head; + + // 打印首行 + { + fprintf(p_file, "Index,Date,Time,Timestamp,Count,ECU ID,Application ID,Context ID ,Session ID,Type,Sub Type,Mode,#Args,Payload,"); + + for (size_t count = 0; count < this->payload_max_len; count++) + fprintf(p_file, "B%d,", count); + fseek(p_file, -1, SEEK_CUR); + fprintf(p_file, "\r\n"); + } + + // 遍历每行数据 + for (size_t line_count = 0; line_count < this->msg_count; line_count++) + { + + // 输出索引号 + fprintf(p_file, "%d,", line_count); + + // 输出日期和时间 + { + time_t timestamp = (time_t)p_msg->Msg.storage_header.seconds; + struct tm *time_struct = localtime(×tamp); + char time_str[100] = {'\0'}; + // 格式化为 "YY/MM/DD 时区" + strftime(time_str, sizeof(time_str), "%Y/%m/%d UTC%z", time_struct); + fprintf(p_file, "%s,", time_str); + + // 格式化为 "HH:MM:SS" + strftime(time_str, sizeof(time_str), "%H:%M:%S", time_struct); + fprintf(p_file, "T-%s.%d,", time_str, p_msg->Msg.storage_header.microseconds); + } + + // 输出头附加信息 + { + // 存在扩展时间戳 + if (p_msg->Msg.standard_header.htyp.WTMS) + { + /**< Timestamp since system start in 0.1 milliseconds */ + char time_str[20]; + sprintf(time_str, "%d", p_msg->Msg.standard_header_extra.tmsp); + char *p_sec_end = time_str + strlen(time_str) - 4; + + // 输出秒 + for (char *p = time_str; p < p_sec_end; p++) + fputc(*p, p_file); + // 输出剩余的时间 + fprintf(p_file, ".%s,", p_sec_end); + } + else + fprintf(p_file, "N/A,"); + + // 消息计数 + fprintf(p_file, "%d,", p_msg->Msg.standard_header.mcnt); + + // 存在扩展ECU ID + if (p_msg->Msg.standard_header.htyp.WEID) + fprintf(p_file, "%.4s,", p_msg->Msg.standard_header_extra.ecu); + else + fprintf(p_file, "%.4s,", p_msg->Msg.storage_header.ecu); + + // 存在扩展头 + if (p_msg->Msg.standard_header.htyp.UEH) + { + fprintf(p_file, "%.4s,", p_msg->Msg.extendedheader.apid); + fprintf(p_file, "%.4s,", p_msg->Msg.extendedheader.ctid); + + if (p_msg->Msg.standard_header.htyp.WSID) + fprintf(p_file, "%d,", p_msg->Msg.standard_header_extra.seid); + else + fprintf(p_file, "N/A,"); + } + } + + for (size_t count = 0; count < p_msg->Msg.payload_length; count++) + fprintf(p_file, "%0X,", p_msg->Msg.payload_buffer[count]); + + p_msg = p_msg->p_next; + fseek(p_file, -1, SEEK_CUR); + fprintf(p_file, "\r\n"); + } + + return DLT_Type::DLT_ERROR_NONE; +} \ No newline at end of file diff --git a/Src/Main.cpp b/Src/Main.cpp index 164c4b3..40e91e1 100644 --- a/Src/Main.cpp +++ b/Src/Main.cpp @@ -18,6 +18,7 @@ extern "C" #include "Config.hpp" // 配置文件 #include "Global_Variables.hpp" // 全局变量相关 #include "UI_Layout.hpp" // UI布局相关 +#include "Command_Solve.hpp" // 命令处理相关 // 辅助函数声明 bool CreateDeviceD3D(HWND hWnd); // 创建 Direct 3D 设备 @@ -34,6 +35,9 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg // 入口主函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + // 命令行前置处理 + Pre_Command_Solve(); + // 启用DPI响应并获取主显示器分辨率 ImGui_ImplWin32_EnableDpiAwareness(); float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY));