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.

This commit is contained in:
LuChiChick 2025-11-19 17:47:20 +08:00
parent 33674ec54a
commit 8f01578411
5 changed files with 591 additions and 0 deletions

7
Inc/Command_Solve.hpp Normal file
View File

@ -0,0 +1,7 @@
#ifndef __COMMAND_SOLVE_HPP__
#define __COMMAND_SOLVE_HPP__
// 命令行处理
void Pre_Command_Solve(void);
#endif

167
Inc/DLT_Utilities.hpp Normal file
View File

@ -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

39
Src/Command_Solve.cpp Normal file
View File

@ -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;
}

374
Src/DLT_Utilities.cpp Normal file
View File

@ -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(&timestamp);
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;
}

View File

@ -18,6 +18,7 @@ extern "C"
#include "Config.hpp" // 配置文件 #include "Config.hpp" // 配置文件
#include "Global_Variables.hpp" // 全局变量相关 #include "Global_Variables.hpp" // 全局变量相关
#include "UI_Layout.hpp" // UI布局相关 #include "UI_Layout.hpp" // UI布局相关
#include "Command_Solve.hpp" // 命令处理相关
// 辅助函数声明 // 辅助函数声明
bool CreateDeviceD3D(HWND hWnd); // 创建 Direct 3D 设备 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) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ {
// 命令行前置处理
Pre_Command_Solve();
// 启用DPI响应并获取主显示器分辨率 // 启用DPI响应并获取主显示器分辨率
ImGui_ImplWin32_EnableDpiAwareness(); ImGui_ImplWin32_EnableDpiAwareness();
float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY)); float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY));