28 Commits

Author SHA1 Message Date
LuChiChick 8cdd2662ba Fix node graph moves after focusing on another window which above a node. 2026-05-20 16:25:59 +08:00
LuChiChick 5cc61e9486 Changed the content pointer registered by Register_Package() method to const type && Added three logic gate nodes, now supporting AND, OR, NOT operations. More controlled or conditional type nodes will be added later && Improved the workflow packet passing execution process among existing connectors. 2026-05-20 15:40:50 +08:00
LuChiChick 986028a3a3 Fix node graph pans and starts box selection after focusing on another window (#210) 2026-05-20 14:47:24 +08:00
LuChiChick 80a18b6a79 Implemented Show() method for the connector base class; all inheriting connectors no longer need to maintain their own separate Show() methods. && Assigned distinct drawing colors for exclusive input type connectors and their associated connections. && Improved the workflow packet passing execution process among existing connectors. && Removed some redundant code. 2026-05-19 20:18:50 +08:00
LuChiChick 35636fd438 Fixed a bug in the Del_Link method where node connection deletion failed when source id and target id were swapped. && Added a Get_Socket_Str method to the base connector class, allowing external code to query the socket to determine whether a connection is allowed. && Added a boolean-type connector. && Input connectors can now be initialized to exclusive input mode, meaning that an input connector of this type allows only one input at a time, establishing a new connection will disconnect the existing one. && Added a message filter node that allows incoming messages to pass through when the input conditions are met. 2026-05-18 19:48:22 +08:00
LuChiChick 68fba7efd5 Modified the way content passed between connectors, now content is passed via "Package" && Improved the execution flow of MSG_Input_Node && Fixed the workflow execution. 2026-05-07 13:04:11 +08:00
LuChiChick 3ec4327d45 Refactor DLT_Utilities to use STL containers, removing manual memory management && Add a debug output for command-line argument input 2026-04-30 20:49:57 +08:00
LuChiChick df949ce460 Modified some function name in JSON related classes && Removed the Null flag for Json_Pair && Added function interfaces for exporting and importing configurations in the base node class && Added experimental operation testing for the workflow. 2026-04-28 19:08:31 +08:00
LuChiChick f9f10209ca Resolved an issue where node width expanded infinitely during window dragging across multi‑display screens with different scaling factors.This problem was caused by inaccurate width calculation of placeholder elements.The width calculation for nodes now uses an adaptive approach. 2026-04-28 11:17:58 +08:00
LuChiChick fa4e79dc86 Implemented the copy, paste, and cut functions in the workspace, with corresponding shortcut key responses. The clipboard functionality is implemented internally and does not interact with the operating system for now. 2026-04-24 17:07:07 +08:00
LuChiChick e0afc4963f Implemented the functions of saving the workspace into a json file and loading from it. 2026-04-23 20:23:09 +08:00
LuChiChick 07ee870601 Added null pointer checks && Removed duplicate file existence checks. 2026-04-23 11:10:38 +08:00
LuChiChick 923ba12ae9 Fixed the issue of incorrect DLT message parsing when payload length is 0 && Fixed potential wild pointers and improper memory free. 2026-04-23 10:58:12 +08:00
LuChiChick ce6f9646c4 Added shallow copy constructors for Json_Array and Json_Object && Added a float type constructor for Json_Value && Fixed the incorrect indentation issue when pretty-printing an empty Json_Array. 2026-04-21 20:54:07 +08:00
LuChiChick e80d14d347 Fixed memory leak issue caused by incorrect deallocation order in the Json_Value destructor && Fixed issue where copy construction of read-only reference members in Json_Value failed to pass && Added read-only reference members for Json_Object and Json_Array && Added const qualifiers to methods that do not modify class members && Implemented JSON file parsing && Implemented JSON file export, supporting minified export and pretty print. 2026-04-21 17:59:57 +08:00
LuChiChick b11e74e815 Implemented a JSON file format checking function. 2026-04-17 16:28:44 +08:00
LuChiChick 057438652a Implemented the basic elements of JSON files, including Json_Pair, Json_Arry, Json_Object, and Json_Value. Additionally, some syntactic sugar has been implemented for ease of use, allowing for rapid construction of JSON elements using {} initialization. 2026-04-10 14:11:41 +08:00
LuChiChick 4da02ddb60 Removed the externally exposed Connector_Pool and the useless Separate_Node_List && Redesigned Process_Route && Implemented the construction of a non-conflicting execution sequence and set the corresponding working route for each starting node. 2026-04-07 15:41:37 +08:00
LuChiChick a23f562906 Added key response, now you can use Ctrl+A to select all nodes. Ctrl+X currently only deletes the selected part, and may support clipboard in the future. && Added a delete selected content function in the right-click menu. 2026-04-02 19:04:07 +08:00
LuChiChick 7aa8c6750d Expose some enum types within the public class namespace to remove redundant external definitions && Add node connection loop detection and provide a warning for it. 2026-04-02 11:16:37 +08:00
LuChiChick 975495b2b7 Modified the way connectors associate with storage.Now both input and output interface types are allowed to have multiple connections. && Added some utility methods to the Workflow_Editor class. && Implemented key response, users can now select nodes and connections with a bounding box and delete them using the Delete key. && Added a new node type for connector testing. 2026-03-31 11:24:32 +08:00
LuChiChick f1e29aaba9 Modified the Makefile to enable stripping of extra information during the linking stage, further reducing the size of the Release build. 2026-03-23 10:12:59 +08:00
LuChiChick 20d1c5576f Modified VsCode Launch.json for enable gdb pretty-printing 2026-03-13 16:44:39 +08:00
LuChiChick f6e70335f9 Refactored the implementation of the Workflow_Editor class: now uses shared_ptr and vector for automatic lifecycle management && decoupled nodes and interfaces with abstract base class implementation && implemented an independent unique ID generator && implemented test nodes and interfaces && implemented automatic connection drawing. 2026-03-13 10:55:28 +08:00
LuChiChick b86798db10 Processed unnecessary explicit C include declarations 2026-03-13 10:40:34 +08:00
LuChiChick 2972630f25 Modified Makefile compiler macros && Renamed files 2026-03-13 09:55:42 +08:00
LuChiChick 0f2f2720fc Adjusted UI layout && Tested dynamic window expansion effects && Added node editor area (experimental, currently only one simple test node available with no practical functionality) 2026-01-26 20:57:13 +08:00
LuChiChick cfb1be0efd Fixed an issue with incorrect output format for extended timestamps (unit: 0.1 milliseconds) && Added DLT message sorting.Output message rows are now arranged in ascending order by timestamp. 2026-01-26 20:32:36 +08:00
17 changed files with 7930 additions and 3697 deletions
+10 -1
View File
@@ -7,12 +7,21 @@
"request": "launch",
"preLaunchTask": "project_debug_build_task", // 预构建脚本
"program": "${workspaceFolder}\\Build\\Debug\\DLT_Splitter.exe", // 调试目标
"args": [ // 附加参数
"args": [
// 附加参数
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}", // 工作目录
"environment": [],
"externalConsole": false,
"setupCommands": [
{
// 启用优化打印输出
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
// "ignoreFailures": true
}
]
}
]
}
+47 -89
View File
@@ -1,6 +1,18 @@
/**
* 相关参考资料
* DLTv1 传输协议: https://www.autosar.org/fileadmin/standards/R20-11/FO/AUTOSAR_PRS_LogAndTraceProtocol.pdf
* DLTv2 传输协议: https://www.autosar.org/fileadmin/standards/R22-11/FO/AUTOSAR_PRS_LogAndTraceProtocol.pdf
* COVESA 开源项目组: https://github.com/COVESA
* dlt-viewer开源项目: https://github.com/COVESA/dlt-viewer
* DLT文件结构设计定义: https://github.com/COVESA/dlt-daemon/blob/master/doc/dlt_design_specification.md
*/
#ifndef __DLT_UTILITIES_HPP__
#define __DLT_UTILITIES_HPP__
#include "vector"
#include "string"
extern "C"
{
#include "stdint.h"
@@ -8,13 +20,14 @@ extern "C"
}
// DLT 类型定义
namespace DLT_Type
class DLT_Log
{
#pragma pack(1) // 紧凑字节对齐
protected:
// 紧凑字节对齐
#pragma pack(1)
/**
* The structure of the DLT file storage header. This header is used before each stored DLT message.
*/
// The structure of the DLT file storage header. This header is used before each stored DLT message.
// 存储头不是AUTOSAR规范定义的一部分,来自COVESA开源项目实现
typedef struct
{
char pattern[4]; /**< This pattern should be DLT0x01 */
@@ -23,6 +36,7 @@ namespace DLT_Type
char ecu[4]; /**< The ECU id is added, if it is not already in the DLT message itself */
} DltStorageHeader;
// The structure of the DLT standard header. This header is used in each DLT message.
typedef struct
{
struct
@@ -39,9 +53,7 @@ namespace DLT_Type
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.
*/
// The structure of the DLT extra header parameters. Each parameter is sent only if enabled in htyp.
typedef struct
{
char ecu[4]; /**< ECU id */
@@ -49,39 +61,21 @@ namespace DLT_Type
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.
*/
// 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)
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
{
@@ -89,75 +83,39 @@ namespace DLT_Type
DltStandardHeader standard_header; // 标准头
DltStandardHeaderExtra standard_header_extra; // 标准头额外内容
DltExtendedHeader extendedheader; // 扩展头
size_t payload_length; // 消息长度
uint8_t *payload_buffer; // 载荷
} DLT_Msg; // 文件中的消息条目
std::vector<uint8_t> payload; // 载荷
} DLT_Msg;
// DLT消息链表节点
typedef struct DLT_Msg_Node_Struct
{
DLT_Msg Msg;
DLT_Msg_Node_Struct *p_next;
} DLT_Msg_Node;
}
// DLT 日志类
class DLT_Log
{
private:
size_t loaded_msg_count; // 消息条目计数
size_t max_payload_len; // 最长载荷长度
size_t mem_use; // 存储占用
DLT_Type::DLT_Msg_Node *Msg_List_Head; // 消息列表头
DLT_Type::DLT_Msg_Node *Msg_List_End; // 消息列表尾
/**
* @brief 从文件中读取dlt头
* @param p_file 要读取的文件
* @param Msg 读取后存储的目标结构体
* @return DLT_Type::DLT_Err 错误类型枚举
*/
DLT_Type::DLT_Err dlt_file_read_header(FILE *p_file, DLT_Type::DLT_Msg *Msg);
/**
* @brief 从文件中读取dlt载荷
* @param p_file 要读取的文件
* @param Msg 读取后存储的目标结构体
* @return DLT_Type::DLT_Err 错误类型枚举
*/
DLT_Type::DLT_Err dlt_file_read_payload(FILE *p_file, DLT_Type::DLT_Msg *Msg);
protected:
// 最长载荷长度
size_t private_max_payload_len = 0;
// 消息列表
std::vector<DLT_Msg> private_msg_list;
public:
DLT_Log(); // 构造函数
~DLT_Log(); // 析构函数
/**
* @brief 从文件加载DLT消息,多个文件会顺序加入
* @param file_name_str 目标文件名
* @return DLT_Type::DLT_Err 错误类型枚举
*/
DLT_Type::DLT_Err load_from_file(const char *file_name_str);
// 只读引用
const std::vector<DLT_Msg> &msg_list = this->private_msg_list;
const size_t &max_payload_len = this->private_max_payload_len;
/**
* @brief 获取解析完成的消息列表
* @param p_msg_list 消息列表存入的指针
* @return 消息列表计数
* @brief 从文件加载
* @param path_to_file 文件路径串
* @return std::string 错误信息
*/
size_t get_msg_list(DLT_Type::DLT_Msg_Node *(&p_msg_list));
std::string Load_From_File(const char *path_to_file);
/**
* @brief 导出为CSV
* @param file_name_str 目标文件名
* @return DLT_Type::DLT_Err 错误类型枚举
* @brief 以CSV格式导出到文件
* @param path_to_file 文件路径串
* @return std::string 错误信息
*/
DLT_Type::DLT_Err export_to_csv(const char *file_name_str);
std::string Export_CSV(const char *path_to_file);
/**
* @brief 清理请求的存储并恢复初始状态
* @param null
* @return null
*/
void clear(void);
// 对消息列表进行排序
void Sort(void);
// 清除缓存的信息
void Clear(void);
};
#endif
+6 -2
View File
@@ -5,14 +5,15 @@
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
#include "Workflow_Editor.hpp"
#include "Type_Descriptions.hpp"
extern "C"
{
#include "windows.h"
#include "d3d11.h"
}
#include "Type_Descriptions.hpp"
// 主窗体句柄
extern HWND Main_Window_hWnd;
@@ -29,4 +30,7 @@ extern ID3D11RenderTargetView *g_mainRenderTargetView; // D3D11 渲染目标
extern Input_File_Node *input_dlt_file_list_head;
extern size_t input_dlt_file_count;
// 节点编辑器
extern class Workflow_Editor Workflow_Editor;
#endif
+25
View File
@@ -0,0 +1,25 @@
#ifndef __ID_GENERATOR__
#define __ID_GENERATOR__
#include <vector>
// 唯一ID生成器
class Independent_ID_Generator
{
protected:
// 连续ID串
typedef struct
{
int begin;
int end;
} Continuous_ID_Section;
std::vector<Continuous_ID_Section> continuous_id_list;
public:
// 获取ID
int Request_ID(void);
// 释放ID true-> success / false -> fail
bool Release_ID(const int ID);
};
#endif
+185
View File
@@ -0,0 +1,185 @@
#ifndef __JSON_UTILITIES_HPP__
#define __JSON_UTILITIES_HPP__
extern "C"
{
#include "stdint.h"
}
#include "vector"
#include "string"
class Json_Pair; // Json 键值对
class Json_Arry; // Json 数组 元素必须是值
class Json_Object; // Json 对象 对象内必须是键值对序列
class Json_Value; // Json 值(可以是数组、对象、字符串、浮点数值和整数值、逻辑值、NULL)
/**
* @brief Json文件 格式检查
* @param path_to_file 文件路径串
* @return std::string 错误值
*/
std::string JsonFile_FormatCheck(const char *path_to_file);
/**
* @brief 解析Json文件
* @param path_to_file 文件路径串
* @param pJson_Value Value 指针
* @return std::string 错误值
*/
std::string JsonFile_Parse(const char *path_to_file, Json_Value *pJson_Value);
/**
* @brief Json对象保存
* @param path_to_file 文件路径串
* @param value Json Value
* @param enable_pretty_print 启用美观打印
* @return std::string 错误值
*/
std::string Json_Export(const char *path_to_file, Json_Value value, bool enable_pretty_print = true);
// Json 数组
class Json_Arry
{
private:
// 元素列表
std::vector<Json_Value> private_element_list;
public:
// 只读引用
const std::vector<Json_Value> &element_list = this->private_element_list;
// 默认构造
Json_Arry() = default;
// 列表构造
Json_Arry(std::initializer_list<Json_Value> init_list) : private_element_list(init_list) {};
Json_Arry(std::vector<Json_Value> init_list) : private_element_list(init_list) {};
// 浅拷贝构造
Json_Arry(const Json_Arry &temp);
// 深拷贝
Json_Arry &operator=(const Json_Arry &another);
// 获取元素大小
size_t size(void) const;
// 数组索引重载
Json_Value operator[](size_t index) const;
// 是否为空
bool isEmpty(void) const;
};
// Json 值定义
class Json_Value
{
public:
// 类型枚举
typedef enum
{
TYPE_NULL, // 空值
TYPE_STR, // 字符串
TYPE_ARRY, // 数组
TYPE_OBJ, // 对象
TYPE_NUM_INTEGER, // 整数常数
TYPE_NUM_FLOAT, // 浮点常数
TYPE_BOOLEAN, // 布尔逻辑值
} Type;
protected:
Type private_type = TYPE_NULL; // 类型枚举
void *p_mem = nullptr; // 数据内存
public:
// 只读引用
const Type &type = this->private_type;
// 默认构造
Json_Value() = default;
// 值构造
Json_Value(std::string str);
Json_Value(const char *str) : Json_Value(std::string(str)) {};
Json_Value(Json_Arry arry);
Json_Value(std::initializer_list<Json_Value> init_list) : Json_Value(Json_Arry(init_list)) {};
Json_Value(Json_Object object);
Json_Value(int64_t num_integer);
Json_Value(int num_integer) : Json_Value((int64_t)num_integer) {};
Json_Value(double num_float);
Json_Value(float num_float) : Json_Value((double)num_float) {};
Json_Value(bool boolean);
// 浅拷贝构造
Json_Value(const Json_Value &temp);
// 深拷贝
Json_Value &operator=(const Json_Value &another);
// 析构函数
~Json_Value();
// 获取值
std::string asString(void) const;
Json_Arry asArry(void) const;
Json_Object asObject(void) const;
int64_t asInteger(void) const;
double asFloat(void) const;
bool asBoolean(void) const;
// 是否为空
bool isNull(void) const;
};
// Json 键值对定义
class Json_Pair
{
private:
std::string private_key_str; // 键
Json_Value private_value; // 值
public:
// 只读引用
std::string &key = this->private_key_str;
Json_Value &value = this->private_value;
// 默认构造
Json_Pair() = default;
// 必要构造
Json_Pair(std::string key, Json_Value value) : private_key_str(key), private_value(value) {};
// 浅拷贝构造
Json_Pair(const Json_Pair &temp);
// 深拷贝
Json_Pair &operator=(const Json_Pair &another);
};
// Json 对象
class Json_Object
{
private:
// 元素列表
std::vector<Json_Pair> private_element_list;
public:
// 只读引用
std::vector<Json_Pair> const &element_list = this->private_element_list;
// 默认构造
Json_Object() = default;
// 列表构造
Json_Object(std::initializer_list<Json_Pair> init_list) : private_element_list(init_list) {};
Json_Object(std::vector<Json_Pair> init_list) : private_element_list(init_list) {};
// 浅拷贝构造
Json_Object(const Json_Object &temp);
// 深拷贝
Json_Object &operator=(const Json_Object &another);
// 获取元素大小
size_t size(void) const;
// 数组索引重载
Json_Pair operator[](size_t index) const;
// 由Key 查找值
Json_Value get(std::string key) const;
// 是否为空
bool isEmpty(void) const;
};
#endif
+375
View File
@@ -0,0 +1,375 @@
#ifndef __NODES_AND_CONNECTORS__
#define __NODES_AND_CONNECTORS__
#include <vector>
#include <memory>
#include "imgui.h"
#include "imnodes.h"
#include "ID_Generator.hpp"
#include "DLT_Utilities.hpp"
#include "Json_Utilities.hpp"
// =====================================接口抽象======================================================
class Abs_Connector // 抽象接口类
{
public:
typedef enum
{
CONNECTOR_TYPE_UNKNOWN, // 未知连接点类型
CONNECTOR_TYPE_OUTPUT, // 输出
CONNECTOR_TYPE_INPUT, // 输入
CONNECTOR_TYPE_INPUT_EXCLUSIVE, // 独占输入,新连接必须断开旧连接
} Connector_Type_Enum; // 接口类型枚举
typedef struct
{
size_t package_count = 0; // 包计数 (常规非0)
size_t total_count = 0; // 总计数 (常规非0,不定长包时为0)
void *p_content_mem = nullptr; // 包存储
size_t content_size = 0; // 包大小
} Package; // 接口包定义
protected:
// ID 分配器
Independent_ID_Generator *ID_Generator;
int id; // ID
Connector_Type_Enum type; // 类型标识
const char *socket_str; // 套接字字符串
std::vector<Abs_Connector *> related_connector_list; // 关联列表
Package pkg; // 接口包(仅输出类节点持有)
public:
// 显式禁用默认构造
Abs_Connector() = delete;
// 必要构造
Abs_Connector(Independent_ID_Generator *ID_Generator, Connector_Type_Enum type, const char *socket_str);
// 析构
~Abs_Connector();
// 连接到目标连接点
virtual bool Connect_To(Abs_Connector *target);
// 断开所有连接
virtual void Disconnect_All(void);
// 与指定的接口断开连接
virtual bool Disconnect_To(Abs_Connector *target);
/**
* 注册包
* @param p_content 目标容物指针
* @param content_size 容物大小
* @param count 当前包位置
* @param total_count 包流总长度
*/
virtual bool Register_Package(const void *p_content, size_t content_size, size_t count, size_t total_count);
// 获取包
virtual const Package Get_Package(void);
// 获取关联接口列表
virtual std::vector<Abs_Connector *> Get_Related_List(void);
// 获取ID
virtual int Get_ID(void);
// 获取接口类型
virtual Connector_Type_Enum Get_Type(void);
// 获取套接字字符串
virtual const char *Get_Socket_Str(void);
// 默认风格连接点绘制
virtual void Show(void);
};
// =====================================节点抽象======================================================
class Abs_Node // 抽象节点类
{
public:
typedef enum
{
NODE_TYPE_UNKNOWN, // 未知节点类型
NODE_TYPE_CONNECTOR_TEST, // 接口测试节点
NODE_TYPE_MSG_LINE_INPUT, // 消息输入节点
NODE_TYPE_FILTER, // 筛选器节点
NODE_TYPE_LOGIC_AND, // 逻辑与门节点
NODE_TYPE_LOGIC_OR, // 逻辑或门节点
NODE_TYPE_LOGIC_NOT, // 逻辑非门节点
NODE_TYPE_CSV_EXPORTER, // CSV输出节点
} Node_Type_Enum; // 节点类型枚举
protected:
// ID 分配器
Independent_ID_Generator *Node_ID_Generator; // 节点ID生成器
Independent_ID_Generator *Connector_ID_Generator; // 连接点ID生成器
int id; // ID
Node_Type_Enum type; // 类型
ImVec2 initial_position; // 初始位置
bool close_flag; // 关闭标志位
// 接口列表
std::vector<std::shared_ptr<Abs_Connector>> Connector_List;
public:
// 显式禁用默认构造
Abs_Node() = delete;
// 必要构造
Abs_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
Node_Type_Enum type,
ImVec2 initial_position = ImVec2(0, 0));
// 析构
~Abs_Node();
// 绘制节点
virtual void Show(void) = 0;
// 执行处理流程
virtual bool Execute_Process(void) = 0;
// 获取ID
virtual int Get_ID(void);
// 获取节点类型
virtual Node_Type_Enum Get_Type(void);
// 获取关闭信号
virtual bool Get_CloseFlag(void);
// 获取节点配置参数
virtual Json_Arry Get_Arguments(void);
// 应用配置参数
virtual bool Apply_Arguments(Json_Arry Arg_Arry);
// 获取接口列表
std::vector<std::shared_ptr<Abs_Connector>> &Get_Connector_List(void);
};
// ====================================接口类声明================================================
// Boolean 状态输入接口
class Boolean_InPut_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
Boolean_InPut_Connector() = delete;
// 必要构造
explicit Boolean_InPut_Connector(Independent_ID_Generator *ID_Generator, bool exclusive_flag = false) : Abs_Connector(ID_Generator, exclusive_flag ? CONNECTOR_TYPE_INPUT_EXCLUSIVE : CONNECTOR_TYPE_INPUT, "Boolean") {}
};
// Boolean 状态输出接口
class Boolean_OutPut_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
Boolean_OutPut_Connector() = delete;
// 必要构造
explicit Boolean_OutPut_Connector(Independent_ID_Generator *ID_Generator) : Abs_Connector(ID_Generator, CONNECTOR_TYPE_OUTPUT, "Boolean") {}
};
// DLT信息输入接口
class MSG_InPut_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
MSG_InPut_Connector() = delete;
// 必要构造
explicit MSG_InPut_Connector(Independent_ID_Generator *ID_Generator, bool exclusive_flag = false) : Abs_Connector(ID_Generator, exclusive_flag ? CONNECTOR_TYPE_INPUT_EXCLUSIVE : CONNECTOR_TYPE_INPUT, "DLT MSG") {}
};
// DLT信息输出接口
class MSG_OutPut_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
MSG_OutPut_Connector() = delete;
// 必要构造
explicit MSG_OutPut_Connector(Independent_ID_Generator *ID_Generator) : Abs_Connector(ID_Generator, CONNECTOR_TYPE_OUTPUT, "DLT MSG") {}
};
// CSV 列输入接口
class CSV_Column_Input_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
CSV_Column_Input_Connector() = delete;
// 必要构造
explicit CSV_Column_Input_Connector(Independent_ID_Generator *ID_Generator, bool exclusive_flag = false) : Abs_Connector(ID_Generator, exclusive_flag ? CONNECTOR_TYPE_INPUT_EXCLUSIVE : CONNECTOR_TYPE_INPUT, "CSV Column") {}
};
// CSV 列输出接口
class CSV_Column_Output_Connector : public Abs_Connector
{
public:
// 显式禁用默认构造
CSV_Column_Output_Connector() = delete;
// 必要构造
explicit CSV_Column_Output_Connector(Independent_ID_Generator *ID_Generator) : Abs_Connector(ID_Generator, CONNECTOR_TYPE_OUTPUT, "CSV Column") {}
};
// ====================================节点类声明================================================
// 接口测试节点
class Connector_Test_Node : public Abs_Node
{
private:
float node_width = 0;
public:
// 显式禁用默认构造
Connector_Test_Node() = delete;
// 必要构造
Connector_Test_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
// DLT信息输入类,起始点
class MSG_Input_Node : public Abs_Node
{
private:
float node_width = 0;
DLT_Log log;
// 执行相关
bool excute_flag = false;
size_t private_excute_count = 0;
size_t private_excute_end_count = 0;
bool private_finish_flag = false;
public:
// 只读引用
const bool &finish_flag = this->private_finish_flag;
const size_t &excute_count = this->private_excute_count;
const size_t &excute_end_count = this->private_excute_end_count;
// 显式禁用默认构造
MSG_Input_Node() = delete;
// 必要构造
MSG_Input_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
// 筛选器节点
class Filter_Node : public Abs_Node
{
private:
float node_width = 0;
public:
// 显式禁用默认构造
Filter_Node() = delete;
// 必要构造
Filter_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
// 逻辑与门
class Logic_AND_Node : public Abs_Node
{
private:
float node_width = 0;
public:
// 显式禁用默认构造
Logic_AND_Node() = delete;
// 必要构造
Logic_AND_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
// 逻辑与门
class Logic_OR_Node : public Abs_Node
{
private:
float node_width = 0;
public:
// 显式禁用默认构造
Logic_OR_Node() = delete;
// 必要构造
Logic_OR_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
// 逻辑与门
class Logic_NOT_Node : public Abs_Node
{
private:
float node_width = 0;
public:
// 显式禁用默认构造
Logic_NOT_Node() = delete;
// 必要构造
Logic_NOT_Node(Independent_ID_Generator *Node_ID_Generator,
Independent_ID_Generator *Connector_ID_Generator,
ImVec2 initial_position = ImVec2(0, 0));
// 绘制节点
virtual void Show(void);
// 执行处理流程
virtual bool Execute_Process(void);
};
#endif
+94
View File
@@ -0,0 +1,94 @@
#ifndef __WORKFLOW_EDITOR_HPP__
#define __WORKFLOW_EDITOR_HPP__
#include <vector>
#include <memory>
#include "imgui.h"
#include "imnodes.h"
#include "ID_Generator.hpp"
#include "Nodes_And_Connectors.hpp"
#include "Json_Utilities.hpp"
// 节点编辑器类
class Workflow_Editor
{
protected:
// ID 分配器
Independent_ID_Generator Connector_ID_Generator; // 连接点ID生成器
Independent_ID_Generator Node_ID_Generator; // 节点ID生成器
Independent_ID_Generator Edge_ID_Generator; // 边ID生成器
// 边定义
typedef struct
{
int id = -1; // 边ID
int source_connector_id = -1; // 源连接点
int target_connector_id = -1; // 目标连接点
enum Edge_Type_Enum
{
EDGE_TYPE_NORMAL, // 普通边
EDGE_TYPE_EXCLUSIVE, // 独占边
} type = EDGE_TYPE_NORMAL;
} Edge;
// 数据池
std::vector<std::shared_ptr<Abs_Node>> Node_Pool; // 节点池
std::vector<Edge> Edge_Pool; // 边池
// 配置剪切板
Json_Object Config_Clipboard;
/**
* 顶层索引为起始工作流节点(N个元素即N个处理流程)
* 次级索引为工作序列,表示完成一个处理流程的非冲突序列
*/
std::vector<std::vector<std::shared_ptr<Abs_Node>>> Process_Route; // 工作层级路由
// 删除节点 返回迭代器指定位置的下一个位置的迭代器,仅限内部使用
std::vector<std::shared_ptr<Abs_Node>>::iterator Del_Node(std::vector<std::shared_ptr<Abs_Node>>::iterator iterator);
// 删除边 返回迭代器指定位置的下一个位置的迭代器,仅限内部使用
std::vector<Edge>::iterator Del_Link(std::vector<Edge>::iterator iterator);
// 从节点和边列表构建配置对象
Json_Object Generate_Config(std::vector<std::shared_ptr<Abs_Node>> Node_List, std::vector<Edge> Edge_List);
// 从配置对象应用 true -> success / false -> failed
bool Apply_Config(Json_Object Config_Object, ImVec2 initial_position = ImVec2(0, 0));
public:
// 绘制节点编辑器
void Show(void);
// 新增节点 true -> success / false -> failed
bool Add_Node(Abs_Node::Node_Type_Enum type, ImVec2 initial_position = ImVec2(0, 0));
// 删除节点 true -> success / false -> failed
bool Del_Node(int target_node_id);
// 新增连接 true -> success / false -> failed
bool Add_Link(int source_connector_id, int target_connector_id);
// 删除连接 true -> success / false -> failed
bool Del_Link(int source_connector_id, int target_connector_id);
// 创建工作路由 true -> success / false -> failed
bool Build_Process_Route(void);
// 清除工作路由 true -> success / false -> failed
bool Clear_Process_Route(void);
// 开始执行工作流
bool Execute_Workflow(void);
// 清除整个工作区
void Clear_All(void);
// 加载取配置文件 true -> success / false -> failed
bool Load_Config(const char *file_path, ImVec2 initial_position = ImVec2(0, 0));
// 存储配置文件 true -> success / false -> failed
bool Store_Config(const char *file_path);
};
#endif
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -41,7 +41,7 @@ COMPLIER_PREFIX = \
C_COMPLIER = $(strip $(COMPLIER_PREFIX))gcc
# C++编译工具
C++_COMPLIER = $(strip $(COMPLIER_PREFIX))g++
CXX_COMPLIER = $(strip $(COMPLIER_PREFIX))g++
# Windows 资源文件编译工具
WIN_RES_COMPLIER = windres
@@ -194,13 +194,13 @@ $(BUILD_DIR)/$(SUB_DIR_DEBUG): | $(BUILD_DIR)
# Release 最终可执行文件编译任务
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(TARGET_FILE_NAME).exe: $(RELEASE_OBJECTS)
@echo ====== [Release] All File Compiled. Now Linking... ======
$(C++_COMPLIER) $(RELEASE_OBJECTS) -static -o $@ $(LIB_LINK) $(LDFLAGS)
$(CXX_COMPLIER) $(RELEASE_OBJECTS) -static -s -o $@ $(LIB_LINK) $(LDFLAGS)
@echo ====== [Release] Program Link Finished ======
# Debug 最终可执行文件编译任务
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(TARGET_FILE_NAME).exe: $(DEBUG_OBJECTS)
@echo ====== [Debug] All File Compiled. Now Linking... ======
$(C++_COMPLIER) $(DEBUG_OBJECTS) -static -o $@ $(LIB_LINK) $(LDFLAGS)
$(CXX_COMPLIER) $(DEBUG_OBJECTS) -static -o $@ $(LIB_LINK) $(LDFLAGS)
@echo ====== [Debug] Program Link Finished ======
# C Release 目标文件编译
@@ -211,7 +211,7 @@ $(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.c Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEA
# C++ Release 目标文件编译
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.cpp Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEASE)
@echo ====== [Release] C++ Source File "$<" Compiling... ======
$(C++_COMPLIER) -c $(CXXFLAGS) $(RELEASE_OPT) -Wa,-a,-ad,-ahlms=$(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(notdir $(<:.cpp=.lst)) $< -o $@
$(CXX_COMPLIER) -c $(CXXFLAGS) $(RELEASE_OPT) -Wa,-a,-ad,-ahlms=$(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(notdir $(<:.cpp=.lst)) $< -o $@
# Release 资源脚本文件编译
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.rc Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEASE)
@@ -226,7 +226,7 @@ $(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.c Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
# C++ Debug 目标文件编译
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.cpp Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
@echo ====== [Debug] C++ Source File "$<" Compiling... ======
$(C++_COMPLIER) -c $(CXXFLAGS) $(DEBUG_OPT) -g -Wa,-a,-ad,-ahlms=$(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(notdir $(<:.cpp=.lst)) $< -o $@
$(CXX_COMPLIER) -c $(CXXFLAGS) $(DEBUG_OPT) -g -Wa,-a,-ad,-ahlms=$(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(notdir $(<:.cpp=.lst)) $< -o $@
# Debug 资源脚本文件编译
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.rc Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
+17 -15
View File
@@ -1,12 +1,14 @@
#include "Command_Solve.hpp"
#include "DLT_Utilities.hpp"
#include "Global_Variables.hpp"
extern "C"
{
#include "windows.h"
#include "CSV_Utilities.hpp"
}
#include "CSV_Utilities.hpp"
DLT_Log File;
// 命令行处理
@@ -20,21 +22,21 @@ void Pre_Command_Solve(void)
if (argc <= 1)
return;
DLT_Type::DLT_Err err;
// 输出测试用参数信息
std::wstring cmd_string;
for (int count = 0; count < argc; count++)
{
// int size_needed = WideCharToMultiByte(CP_UTF8, 0, argv[count], -1, NULL, 0, NULL, NULL);
// char *strTo = (char *)malloc(size_needed);
// WideCharToMultiByte(CP_UTF8, 0, argv[count], -1, strTo, size_needed, NULL, NULL);
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);
// printf("Arg[%d]:%s\n", count, strTo);
// free(strTo);
err = File.load_from_file(strTo);
wchar_t Num_str[100] = {'\0'};
wsprintf(Num_str, L"%d", count);
cmd_string += std::wstring(L"Arg[") + Num_str + L"]:" + argv[count] + L"\n";
}
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;
MessageBoxW(Main_Window_hWnd, (L"[Debug] Argument input:\n" + cmd_string).c_str(), L"", MB_OK);
}
+274 -292
View File
@@ -7,366 +7,296 @@ extern "C"
#include "sys/time.h"
}
// 构造函数
DLT_Log::DLT_Log()
/**
* @brief 从文件加载
* @param path_to_file 文件路径串
* @return std::string 错误信息
*/
std::string DLT_Log::Load_From_File(const char *path_to_file)
{
// 初始化内容
this->loaded_msg_count = 0;
this->max_payload_len = 0;
this->Msg_List_Head = nullptr;
this->Msg_List_End = nullptr;
}
// 尝试打开文件
FILE *pFile = fopen(path_to_file, "rb");
if (pFile == nullptr)
return "File \"" + std::string(path_to_file) + "\" open failed.";
// 析构函数
DLT_Log::~DLT_Log()
{
clear();
}
// 清理请求的存储并恢复初始状态
void DLT_Log::clear(void)
{
if (this->Msg_List_Head == nullptr)
return;
// 释放分配的空间
while (this->Msg_List_Head != nullptr)
// Lambda 读取头
auto Lambda_Read_Header = [&](DLT_Msg &Msg) -> std::string
{
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->loaded_msg_count = 0;
this->max_payload_len = 0;
this->mem_use = 0;
this->Msg_List_Head = nullptr;
this->Msg_List_End = nullptr;
}
DLT_Type::DLT_Err DLT_Log::dlt_file_read_header(FILE *p_file, 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, p_file) != 1)
if (fread(&(Msg.storage_header), sizeof(Msg.storage_header), 1, pFile) != 1)
{
if (feof(p_file))
return DLT_Type::DLT_ERROR_END_OF_FILE;
if (feof(pFile))
return "Unexpected EOF.";
else
return DLT_Type::DLT_ERROR_FILE_READ_FAILED;
return "File read failed.";
}
// 检查存储头
if (memcmp(Msg->storage_header.pattern, "DLT\x01", 4))
return DLT_Type::DLT_ERROR_INVALID_HEADER;
}
if (memcmp(Msg.storage_header.pattern, "DLT\x01", 4))
return "Invalid storage header pattern.Must be \"DLT0x01\".";
// 标准头
{
// 读取标准头
if (fread(&(Msg->standard_header), sizeof(Msg->standard_header), 1, p_file) != 1)
if (fread(&(Msg.standard_header), sizeof(Msg.standard_header), 1, pFile) != 1)
{
if (feof(p_file))
return DLT_Type::DLT_ERROR_INCOMPLETE_DATA;
if (feof(pFile))
return "Unexpected EOF.";
else
return DLT_Type::DLT_ERROR_FILE_READ_FAILED;
return "File read failed.";
}
// 检查标准头协议版本
if (Msg.standard_header.htyp.VERS != 0x01)
return "Unsupported protocol version.";
// 反向拼接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;
*((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);
// 消息头长度统计
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, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return "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, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return "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, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return "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, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return "File read failed.";
}
}
// 分配payload空间并无错误信息返回
Msg.payload.resize(Msg.standard_header.len - msg_header_len);
return std::string();
};
// Lambda 读取载荷
auto Lambda_Read_Payload = [&](DLT_Msg &Msg) -> std::string
{
// 含有额外的ECU ID
if (Msg->standard_header.htyp.WEID)
if (fread(&Msg.payload[0], Msg.payload.size(), 1, pFile) != 1)
{
msg_header_len += sizeof(Msg->standard_header_extra.ecu);
if (fread(&(Msg->standard_header_extra.ecu), sizeof(Msg->standard_header_extra.ecu), 1, p_file) != 1)
{
if (feof(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, p_file) != 1)
{
if (feof(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, p_file) != 1)
{
if (feof(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, p_file) != 1)
{
if (feof(p_file))
return DLT_Type::DLT_ERROR_INCOMPLETE_DATA;
if (feof(pFile))
return "Unexpected EOF.";
else
return DLT_Type::DLT_ERROR_FILE_READ_FAILED;
return "File read failed.";
}
}
// 计算Payload长度
Msg->payload_length = Msg->standard_header.len - msg_header_len;
return std::string();
};
return DLT_Type::DLT_ERROR_NONE;
}
DLT_Type::DLT_Err DLT_Log::dlt_file_read_payload(FILE *p_file, 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);
this->mem_use += 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, p_file) != 1)
// 错误信息串
std::string err_str;
// 循环解析文件
while (true)
{
free(Msg->payload_buffer);
this->mem_use -= Msg->payload_length;
Msg->payload_buffer = nullptr;
DLT_Msg Msg = {0};
if (feof(p_file))
return DLT_Type::DLT_ERROR_INCOMPLETE_DATA;
err_str = Lambda_Read_Header(Msg);
if (!err_str.empty())
break;
if (Msg.payload.size() > 0)
err_str = Lambda_Read_Payload(Msg);
if (err_str.empty() == true)
{
if (Msg.payload.size() > this->private_max_payload_len)
this->private_max_payload_len = Msg.payload.size();
this->private_msg_list.push_back(Msg);
}
else
return DLT_Type::DLT_ERROR_FILE_READ_FAILED;
break;
// 读取结束判定
if (fgetc(pFile) == EOF)
break;
else
fseek(pFile, -1, SEEK_CUR);
}
return ::DLT_Type::DLT_ERROR_NONE;
fclose(pFile);
return err_str;
}
DLT_Type::DLT_Err DLT_Log::load_from_file(const char *file_name_str)
/**
* @brief 以CSV格式导出到文件
* @param path_to_file 文件路径串
* @return std::string 错误信息
*/
std::string DLT_Log::Export_CSV(const char *path_to_file)
{
// 打开文件
FILE *p_file = fopen(file_name_str, "rb");
if (p_file == nullptr)
return DLT_Type::DLT_ERROR_FILE_OPEN_FAILED;
// 解析消息内容
{
DLT_Type::DLT_Err err = DLT_Type::DLT_ERROR_NONE;
while (1)
{
DLT_Type::DLT_Msg Msg;
err = dlt_file_read_header(p_file, &Msg);
if (err != DLT_Type::DLT_ERROR_NONE)
{
if (err == DLT_Type::DLT_ERROR_END_OF_FILE)
break;
fclose(p_file);
return err;
}
err = dlt_file_read_payload(p_file, &Msg);
if (err != DLT_Type::DLT_ERROR_NONE)
{
fclose(p_file);
return err;
}
DLT_Type::DLT_Msg_Node *p_target_msg = (DLT_Type::DLT_Msg_Node *)malloc(sizeof(DLT_Type::DLT_Msg_Node));
this->mem_use += sizeof(DLT_Type::DLT_Msg_Node);
if (p_target_msg == nullptr)
{
this->mem_use -= sizeof(DLT_Type::DLT_Msg_Node);
fclose(p_file);
return DLT_Type::DLT_ERROR_MEM_ALLOCATE_FAILED;
}
p_target_msg->p_next = nullptr;
p_target_msg->Msg = Msg;
if (this->Msg_List_Head == nullptr)
{
this->Msg_List_Head = this->Msg_List_End = p_target_msg;
}
else
{
this->Msg_List_End->p_next = p_target_msg;
this->Msg_List_End = p_target_msg;
}
// 统计最长长度
if (Msg.payload_length > this->max_payload_len)
this->max_payload_len = Msg.payload_length;
this->loaded_msg_count++;
}
}
fclose(p_file);
return DLT_Type::DLT_ERROR_NONE;
}
size_t DLT_Log::get_msg_list(DLT_Type::DLT_Msg_Node *(&p_msg_list))
{
if (this->loaded_msg_count != 0)
p_msg_list = this->Msg_List_Head;
else
p_msg_list = nullptr;
return this->loaded_msg_count;
}
DLT_Type::DLT_Err DLT_Log::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;
// 尝试打开文件
FILE *pFile = fopen(path_to_file, "wb");
if (pFile == nullptr)
return "File \"" + std::string(path_to_file) + "\" open failed.";
// 打印首行
{
fprintf(p_file, "Index,Date&Time(Local),Timestamp,Count,ECU ID,Application ID,Context ID ,Session ID,Type,Sub Type,Mode,#Args,Payload,");
fprintf(pFile, "Index,Date&Time(Local),Timestamp,Count,ECU ID,Application ID,Context ID ,Session ID,Type,Sub Type,Mode,#Args,Payload,");
for (size_t count = 0; count < this->max_payload_len; count++)
fprintf(p_file, "B%d,", count);
fseek(p_file, -1, SEEK_CUR);
fprintf(p_file, "\r\n");
fprintf(pFile, "B%d,", count);
fseek(pFile, -1, SEEK_CUR);
fprintf(pFile, "\r\n");
}
// 遍历每行数据
DLT_Type::DLT_Msg_Node *p_msg = this->Msg_List_Head;
for (size_t line_count = 0; line_count < this->loaded_msg_count; line_count++)
size_t line_count = 0;
for (auto Msg : this->private_msg_list)
{
// 输出索引号
fprintf(p_file, "%d,", line_count);
fprintf(pFile, "%d,", line_count);
// 输出日期和时间
{
time_t timestamp = (time_t)p_msg->Msg.storage_header.seconds;
time_t timestamp = (time_t)Msg.storage_header.seconds;
struct tm *time_struct = localtime(&timestamp);
char time_str[100] = {'\0'};
// 格式化为 "YY/MM/DD HH:MM:SS"
strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", time_struct);
fprintf(p_file, "%s.%d,", time_str, p_msg->Msg.storage_header.microseconds);
fprintf(pFile, "%s.%d,", time_str, Msg.storage_header.microseconds);
}
// 输出头附加信息
{
// 存在扩展时间戳
if (p_msg->Msg.standard_header.htyp.WTMS)
if (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;
sprintf(time_str, "%d", Msg.standard_header_extra.tmsp);
if (strlen(time_str) > 4)
{
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);
// 输出秒
for (char *p = time_str; p < p_sec_end; p++)
fputc(*p, pFile);
// 输出剩余的时间
fprintf(pFile, ".%s,", p_sec_end);
}
else
{
fprintf(pFile, "0.");
for (int count = 4 - strlen(time_str); count > 0; count--)
fputc('0', pFile);
fprintf(pFile, "%s,", time_str);
}
}
else
fprintf(p_file, "N/A,");
fprintf(pFile, "N/A,");
// 消息计数
fprintf(p_file, "%d,", p_msg->Msg.standard_header.mcnt);
fprintf(pFile, "%d,", 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);
if (Msg.standard_header.htyp.WEID)
fprintf(pFile, "%.4s,", Msg.standard_header_extra.ecu);
else
fprintf(p_file, "%.4s,", p_msg->Msg.storage_header.ecu);
fprintf(pFile, "%.4s,", Msg.storage_header.ecu);
// 输出 Apid 和 Ctid
if (p_msg->Msg.standard_header.htyp.UEH)
if (Msg.standard_header.htyp.UEH)
{
fprintf(p_file, "%.4s,", p_msg->Msg.extendedheader.apid);
fprintf(p_file, "%.4s,", p_msg->Msg.extendedheader.ctid);
fprintf(pFile, "%.4s,", Msg.extendedheader.apid);
fprintf(pFile, "%.4s,", Msg.extendedheader.ctid);
}
else
{
// skip apid && ctid
fprintf(p_file, "N/A,N/A,");
fprintf(pFile, "N/A,N/A,");
}
// 存在 Session ID
if (p_msg->Msg.standard_header.htyp.WSID)
fprintf(p_file, "%d,", p_msg->Msg.standard_header_extra.seid);
if (Msg.standard_header.htyp.WSID)
fprintf(pFile, "%d,", Msg.standard_header_extra.seid);
else
fprintf(p_file, "N/A,");
fprintf(pFile, "N/A,");
// 输出 Type 、Subtype 、Mode 、#Args
if (p_msg->Msg.standard_header.htyp.UEH)
if (Msg.standard_header.htyp.UEH)
{
const char *Type_str[] = {"log", "trace", "network", "control"};
const char *SubType_Str[][0xF] = {
@@ -381,31 +311,83 @@ DLT_Type::DLT_Err DLT_Log::export_to_csv(const char *file_name_str)
};
// Type && Subtype
fprintf(p_file, "%s,", Type_str[p_msg->Msg.extendedheader.msin.MSTP]);
fprintf(p_file, "%s,", SubType_Str[p_msg->Msg.extendedheader.msin.MSTP][(p_msg->Msg.extendedheader.msin.MTIN - 1) % 0xF]);
fprintf(pFile, "%s,", Type_str[Msg.extendedheader.msin.MSTP]);
fprintf(pFile, "%s,", SubType_Str[Msg.extendedheader.msin.MSTP][(Msg.extendedheader.msin.MTIN - 1) % 0xF]);
// Mode && #Args
fprintf(p_file, "%s,", p_msg->Msg.extendedheader.msin.VERB ? "verbose" : "non-verbose");
fprintf(p_file, "%d,", p_msg->Msg.extendedheader.noar);
fprintf(pFile, "%s,", Msg.extendedheader.msin.VERB ? "verbose" : "non-verbose");
fprintf(pFile, "%d,", Msg.extendedheader.noar);
}
else
fprintf(p_file, "N/A,N/A,N/A,N/A,");
fprintf(pFile, "N/A,N/A,N/A,N/A,");
}
// Payload概览
fprintf(p_file, ",");
// Payload概览列跳过
fprintf(pFile, ",");
// 逐个打印 Payload
for (size_t count = 0; count < p_msg->Msg.payload_length; count++)
fprintf(p_file, "%0X,", p_msg->Msg.payload_buffer[count]);
for (auto data_byte : Msg.payload)
fprintf(pFile, "%0X,", data_byte);
fseek(p_file, -1, SEEK_CUR);
fprintf(p_file, "\r\n");
p_msg = p_msg->p_next;
// 结束行
fseek(pFile, -1, SEEK_CUR);
fprintf(pFile, "\r\n");
line_count++;
}
fclose(p_file);
fclose(pFile);
return std::string();
}
return DLT_Type::DLT_ERROR_NONE;
// 对消息列表进行排序
void DLT_Log::Sort(void)
{
// Quick Sort
qsort(&this->private_msg_list[0],
this->private_msg_list.size(),
sizeof(this->private_msg_list[0]),
[](const void *param1, const void *param2) -> int
{
const DLT_Msg *pMsg1 = (const DLT_Msg *)param1;
const DLT_Msg *pMsg2 = (const DLT_Msg *)param2;
if (pMsg1->storage_header.seconds > pMsg2->storage_header.seconds)
return 1;
else if (pMsg1->storage_header.seconds == pMsg2->storage_header.seconds)
{
if (pMsg1->storage_header.microseconds > pMsg2->storage_header.microseconds)
return 1;
else if (pMsg1->storage_header.microseconds == pMsg2->storage_header.microseconds)
{
// 存在0.1ms时间戳
if (pMsg1->standard_header.htyp.WTMS && pMsg2->standard_header.htyp.WTMS)
{
if (pMsg1->standard_header_extra.tmsp > pMsg2->standard_header_extra.tmsp)
return 1;
else if (pMsg1->standard_header_extra.tmsp == pMsg2->standard_header_extra.tmsp)
return 0;
else
return -1;
}
else
return 0;
}
else
return -1;
}
else
return -1;
return 0;
});
}
// 清除缓存的信息
void DLT_Log::Clear(void)
{
this->private_max_payload_len = 0;
// 清空内容
std::vector<DLT_Msg> empty_list;
this->private_msg_list.swap(empty_list);
}
+4 -1
View File
@@ -14,4 +14,7 @@ ID3D11RenderTargetView *g_mainRenderTargetView = nullptr; // D3D11 渲染目标
// 文件输入相关
Input_File_Node *input_dlt_file_list_head;
size_t input_dlt_file_count;
size_t input_dlt_file_count;
// 节点编辑器
class Workflow_Editor Workflow_Editor;
+98
View File
@@ -0,0 +1,98 @@
#include "ID_Generator.hpp"
// 请求ID
int Independent_ID_Generator::Request_ID()
{
// 没有元素时
if (this->continuous_id_list.size() == 0)
{
this->continuous_id_list.push_back({0, 0});
// 判定分配情况
if (this->continuous_id_list.size() == 0)
return -1;
else
return 0;
}
// 第一段未从最小 ID 开启
auto first_iterator = this->continuous_id_list.begin();
if (first_iterator->begin != 0)
{
if (first_iterator->begin == 1)
first_iterator->begin--;
else
{
// 划分新段
size_t pre_size = this->continuous_id_list.size();
first_iterator = this->continuous_id_list.insert(first_iterator, {0, 0});
if (!(this->continuous_id_list.size() > pre_size))
return -1;
}
return 0;
}
// 仅第一段时
if (this->continuous_id_list.size() == 1)
{
first_iterator->end++;
return first_iterator->end;
}
// 有两段及以上时,判断是否出现合并
auto second_iterator = first_iterator + 1;
if (first_iterator->end + 2 == second_iterator->begin)
{
int target_id = first_iterator->end + 1;
first_iterator->end = second_iterator->end;
this->continuous_id_list.erase(second_iterator);
return target_id;
}
else
{
first_iterator->end++;
return first_iterator->end;
}
return -1;
}
// 释放ID true-> success / false -> fail
bool Independent_ID_Generator::Release_ID(const int ID)
{
// 遍历查找所属片
for (auto iterator = this->continuous_id_list.begin(); iterator != this->continuous_id_list.end(); iterator++)
{
// 找到对应的ID所属片
if (ID >= iterator->begin && ID <= iterator->end)
{
// 散点删除
if (iterator->begin == iterator->end)
this->continuous_id_list.erase(iterator);
else if (ID == iterator->begin) // 前端点
iterator->begin++;
else if (ID == iterator->end) // 后端点
iterator->end--;
else // 中间段
{
// 插入后返回新的迭代器
size_t pre_size = this->continuous_id_list.size();
iterator = this->continuous_id_list.insert(iterator, {iterator->begin, ID - 1});
// 判定分配是否成功
if (!(this->continuous_id_list.size() > pre_size))
return false;
// 记录ID
iterator++;
iterator->begin = ID + 1;
}
// 完成并退出
return true;
}
}
// 没有找到所属片
return false;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+71 -24
View File
@@ -36,24 +36,62 @@ void UI_Layout()
// 设置窗体相关属性,不允许停靠
window_flags |= ImGuiWindowFlags_NoDocking;
// 临时设置一下窗体大小,动态扩张窗体
{
static size_t Width = MAIN_FRAME_SIZE_WIDTH_MIN;
static size_t Height = MAIN_FRAME_SIZE_HEIGHT_MIN;
if (!(Width >= 1400 && Height >= 700))
{
SetWindowPos(
Main_Window_hWnd,
NULL,
(GetSystemMetrics(SM_CXSCREEN) - (Width)) / 2,
(GetSystemMetrics(SM_CYSCREEN) - (Height)) / 2,
Width,
Height,
SWP_NOZORDER);
if (Height < 700)
Height += 5;
if (Width < 1400)
Width += 20;
}
}
ImGui::Begin("Main Panel", NULL, window_flags); // 创建主面板
ImGui::PopStyleVar(2); // 退出绘制风格栈中的设置项
{
const ImGuiStyle &style = ImGui::GetStyle();
float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY));
// 拖拽输入区域
Widgets::Drag_Input_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x / 2 > MAIN_FRAME_SIZE_WIDTH_MIN / 2 ? MAIN_FRAME_SIZE_WIDTH_MIN / 2 : ImGui::GetContentRegionAvail().x / 2));
// 左半部分
ImGui::BeginChild("Left Part", ImVec2(MAIN_FRAME_SIZE_WIDTH_MIN, ImGui::GetContentRegionAvail().y));
{
// 文件列表组件
Widgets::File_List_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - (ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale + style.ItemSpacing.y)));
// 拖拽输入区域
Widgets::Drag_Input_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x / 2 > MAIN_FRAME_SIZE_WIDTH_MIN * main_scale / 2 ? MAIN_FRAME_SIZE_WIDTH_MIN * main_scale / 2 : ImGui::GetContentRegionAvail().x / 2));
// 导出文
Widgets::Control_Panel(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale));
// 文件列表组
Widgets::File_List_Area(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - (ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale + style.ItemSpacing.y)));
// 导出文件
Widgets::Control_Panel(ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() + 20 * main_scale));
}
ImGui::EndChild();
ImGui::SameLine();
// 右半部分
ImGui::BeginChild("Right Part", ImVec2(0, 0));
{
// 工作流节点编辑器
Workflow_Editor.Show();
}
ImGui::EndChild();
}
ImGui::End();
// ImGui::ShowDemoWindow(nullptr);
ImGui::ShowDemoWindow(nullptr);
}
void Widgets::Drag_Input_Area(const ImVec2 &size)
@@ -371,36 +409,45 @@ void Widgets::Control_Panel(const ImVec2 &size)
[&]()
{
DLT_Log Log;
std::string err_str;
Input_File_Node *p_file_list = input_dlt_file_list_head;
DLT_Type::DLT_Err err;
while (p_file_list != nullptr)
{
err = Log.load_from_file(p_file_list->file_name_str);
if (err != DLT_Type::DLT_ERROR_NONE)
err_str = Log.Load_From_File(p_file_list->file_name_str);
if (!err_str.empty())
break;
p_file_list = p_file_list->p_next;
}
if (err != DLT_Type::DLT_ERROR_NONE)
if (err_str.empty())
{
switch (err)
{
case DLT_Type::DLT_ERROR_FILE_OPEN_FAILED: // 文件打开失败
MessageBoxW(Main_Window_hWnd, L"文件打开失败,检查路径是否存在特殊字符", L"", MB_OK | MB_ICONERROR);
break;
default:
MessageBoxW(Main_Window_hWnd, L"文件加载失败", L"", MB_OK | MB_ICONERROR);
}
Log.Sort();
err_str = Log.Export_CSV(export_file_path_str);
}
if (err_str.empty())
{
MessageBoxW(Main_Window_hWnd, L"导出完成", L"", MB_OK);
}
else
{
err = Log.export_to_csv(export_file_path_str);
// 工具Lambda string 转 wstring
auto Lambda_String_To_WString = [](const std::string &str) -> std::wstring
{
if (str.empty())
return std::wstring();
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0);
if (len <= 0)
return std::wstring();
std::wstring wstr(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], len);
return wstr;
};
if (err == DLT_Type::DLT_ERROR_NONE)
MessageBoxW(Main_Window_hWnd, L"导出完成", L"", MB_OK);
else
MessageBoxW(Main_Window_hWnd, L"导出失败,检查文件占用", L"", MB_OK | MB_ICONERROR);
// 输出错误信息
MessageBoxW(Main_Window_hWnd, (L"导出失败\n" + Lambda_String_To_WString(err_str)).c_str(), L"", MB_OK | MB_ICONERROR);
}
export_confirmed = false;
export_task_created = false;
File diff suppressed because it is too large Load Diff