32 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
LuChiChick 7666ec39fe Added control buttons to enable selective file export functionality && Fixed the issue where files were not closed after DLT Log export && Resolved the problem of files failing to open due to Chinese path characters && Modified alert pop-up icons && Adjusted the initial display position 2025-12-05 17:49:09 +08:00
LuChiChick 34509a68a6 Implemented file input via explorer by invoking Win32 API && Modified the file input validity detection strategy && Adjusted UI layout 2025-12-02 20:43:53 +08:00
LuChiChick 38b498e841 Demo widgets and layout for drag drop and file list 2025-11-27 21:57:17 +08:00
LuChiChick 0814aaaf17 Implement Win32 drag drop file and change main window size 2025-11-27 21:56:32 +08:00
21 changed files with 8436 additions and 3724 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
}
]
}
]
}
+7
View File
@@ -4,4 +4,11 @@
// 主窗体标题
#define MAIN_FRAME_TITTLE L"DLT Splitter —— Dev by : LuChiChick"
// 主窗体最小面积
#define MAIN_FRAME_SIZE_WIDTH_MIN 400
#define MAIN_FRAME_SIZE_HEIGHT_MIN 400 * 1.5
// 单次允许选择的最大文件数量
#define MAX_SINGLE_SELECT_FILE_COUNT 512
#endif
+46 -88
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 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
+13
View File
@@ -5,12 +5,18 @@
#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"
}
// 主窗体句柄
extern HWND Main_Window_hWnd;
// D3D11 相关
extern ID3D11Device *g_pd3dDevice; // D3D11 设备句柄
extern ID3D11DeviceContext *g_pd3dDeviceContext; // D3D11 设备上下文句柄
@@ -20,4 +26,11 @@ extern bool g_SwapChainOccluded; // 交换链是否阻塞
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
+11
View File
@@ -0,0 +1,11 @@
#ifndef __TYPE_DESCRIPTIONS_HPP__
#define __TYPE_DESCRIPTIONS_HPP__
// 文件节点
typedef struct Input_File_Node
{
char *file_name_str;
Input_File_Node *p_next;
} Input_File_Node;
#endif
+15
View File
@@ -6,5 +6,20 @@
// UI布局
void UI_Layout();
// 组件
namespace Widgets
{
// 拖拽输入区域
void Drag_Input_Area(const ImVec2 &size = ImVec2(0, 0));
// 文件列表区域
void File_List_Area(const ImVec2 &size = ImVec2(0, 0));
// 工作流区域
void Workflow_Area(const ImVec2 &size = ImVec2(0, 0));
// 控制面板区域
void Control_Panel(const ImVec2 &size = ImVec2(0, 0));
}
#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
+3 -2
View File
@@ -578,7 +578,8 @@ void BeginNodeSelection(ImNodesEditorContext& editor, const int node_idx)
// Don't start selecting a node if we are e.g. already creating and dragging
// a new link! New link creation can happen when the mouse is clicked over
// a node, but within the hover radius of a pin.
if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None)
if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None ||
!ImGui::IsWindowHovered())
{
return;
}
@@ -724,7 +725,7 @@ void BeginCanvasInteraction(ImNodesEditorContext& editor)
const bool mouse_not_in_canvas = !MouseInCanvas();
if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None ||
any_ui_element_hovered || mouse_not_in_canvas)
any_ui_element_hovered || mouse_not_in_canvas || !ImGui::IsWindowHovered())
{
return;
}
+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 -36
View File
@@ -1,38 +1,19 @@
#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;
// 命令行处理
void Pre_Command_Solve(void)
{
CSV_Table CSV;
CSV.insert_column_before((unsigned)0, "First insert");
CSV.append_column("Test column");
CSV.append_column("Test column 2");
CSV.append_column("Test column 3");
CSV.insert_column_after("Test column 2", "Test insert column");
CSV.insert_column_after("Test column 3", "Test insert column after End");
CSV.insert_column_before("Test column", "Test insert column before Head");
CSV.insert_column_before("Test column", "Test insert column before Origin");
CSV.insert_cell_before("Test column 2", 0, "0");
CSV.append_cell("Test column 2", "1");
CSV.insert_cell_before("Test column 2", 0, "Insert");
CSV.insert_cell_after("Test column 3", (unsigned)0, "Insert Cell");
CSV.append_cell("Test column 3", "1");
CSV.append_cell("Test column 3", "MyWawwww,dwadawdawd");
CSV.append_cell("Test column 3", "3");
CSV.export_to_file("new.csv");
// 获取命令行
int argc;
LPWSTR *argv;
@@ -41,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);
}
+216 -232
View File
@@ -7,120 +7,87 @@ 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, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return "File read failed.";
}
// 标准头
{
// 读取标准头
if (fread(&(Msg->standard_header), sizeof(Msg->standard_header), 1, p_file) != 1)
{
if (feof(p_file))
return DLT_Type::DLT_ERROR_INCOMPLETE_DATA;
else
return DLT_Type::DLT_ERROR_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)
if (Msg.standard_header.htyp.WEID)
{
msg_header_len += sizeof(Msg->standard_header_extra.ecu);
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 (fread(&(Msg.standard_header_extra.ecu), sizeof(Msg.standard_header_extra.ecu), 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.";
}
}
// 含有额外的 Session ID (会话ID)
if (Msg->standard_header.htyp.WSID)
if (Msg.standard_header.htyp.WSID)
{
msg_header_len += sizeof(Msg->standard_header_extra.seid);
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 (fread(&(Msg.standard_header_extra.seid), sizeof(Msg.standard_header_extra.seid), 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.";
}
// 反向拼接Session ID字段
@@ -128,26 +95,26 @@ DLT_Type::DLT_Err DLT_Log::dlt_file_read_header(FILE *p_file, DLT_Type::DLT_Msg
{
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);
*((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;
Msg.standard_header_extra.seid = data_swap;
}
}
// 含有额外的 timestamp (0.1ms 自系统启动后的时间计数)
if (Msg->standard_header.htyp.WSID)
if (Msg.standard_header.htyp.WSID)
{
msg_header_len += sizeof(Msg->standard_header_extra.tmsp);
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 (fread(&(Msg.standard_header_extra.tmsp), sizeof(Msg.standard_header_extra.tmsp), 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.";
}
// 反向拼接Session ID字段
@@ -155,218 +122,181 @@ DLT_Type::DLT_Err DLT_Log::dlt_file_read_header(FILE *p_file, DLT_Type::DLT_Msg
{
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);
*((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;
Msg.standard_header_extra.tmsp = data_swap;
}
}
}
// 扩展头读取
if (Msg->standard_header.htyp.UEH)
if (Msg.standard_header.htyp.UEH)
{
msg_header_len += sizeof(Msg->extendedheader);
msg_header_len += sizeof(Msg.extendedheader);
// 读取标扩展头
if (fread(&(Msg->extendedheader), sizeof(Msg->extendedheader), 1, p_file) != 1)
if (fread(&(Msg.extendedheader), sizeof(Msg.extendedheader), 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.";
}
}
// 计算Payload长度
Msg->payload_length = Msg->standard_header.len - msg_header_len;
// 分配payload空间并无错误信息返回
Msg.payload.resize(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)
// Lambda 读取载荷
auto Lambda_Read_Payload = [&](DLT_Msg &Msg) -> std::string
{
free(Msg->payload_buffer);
this->mem_use -= Msg->payload_length;
Msg->payload_buffer = nullptr;
if (feof(p_file))
return DLT_Type::DLT_ERROR_INCOMPLETE_DATA;
if (fread(&Msg.payload[0], Msg.payload.size(), 1, pFile) != 1)
{
if (feof(pFile))
return "Unexpected EOF.";
else
return DLT_Type::DLT_ERROR_FILE_READ_FAILED;
return "File read failed.";
}
return ::DLT_Type::DLT_ERROR_NONE;
}
return std::string();
};
DLT_Type::DLT_Err DLT_Log::load_from_file(const char *file_name_str)
{
// 打开文件
FILE *p_file = fopen(file_name_str, "rb");
if (p_file == nullptr)
return DLT_Type::DLT_ERROR_FILE_OPEN_FAILED;
// 错误信息串
std::string err_str;
// 循环解析文件
while (true)
{
DLT_Msg Msg = {0};
// 解析消息内容
{
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)
err_str = Lambda_Read_Header(Msg);
if (!err_str.empty())
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 (Msg.payload.size() > 0)
err_str = Lambda_Read_Payload(Msg);
if (this->Msg_List_Head == nullptr)
if (err_str.empty() == true)
{
this->Msg_List_Head = this->Msg_List_End = p_target_msg;
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
{
this->Msg_List_End->p_next = p_target_msg;
this->Msg_List_End = p_target_msg;
}
break;
// 统计最长长度
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;
// 读取结束判定
if (fgetc(pFile) == EOF)
break;
else
p_msg_list = nullptr;
fseek(pFile, -1, SEEK_CUR);
}
return this->loaded_msg_count;
fclose(pFile);
return err_str;
}
DLT_Type::DLT_Err DLT_Log::export_to_csv(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, "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);
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);
fputc(*p, pFile);
// 输出剩余的时间
fprintf(p_file, ".%s,", p_sec_end);
fprintf(pFile, ".%s,", p_sec_end);
}
else
fprintf(p_file, "N/A,");
{
fprintf(pFile, "0.");
for (int count = 4 - strlen(time_str); count > 0; count--)
fputc('0', pFile);
fprintf(pFile, "%s,", time_str);
}
}
else
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,29 +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++;
}
return DLT_Type::DLT_ERROR_NONE;
fclose(pFile);
return std::string();
}
// 对消息列表进行排序
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);
}
+10
View File
@@ -1,5 +1,8 @@
#include "Global_Variables.hpp"
// 主窗体句柄
HWND Main_Window_hWnd = nullptr;
// D3D11 相关
ID3D11Device *g_pd3dDevice = nullptr; // D3D11 设备句柄
ID3D11DeviceContext *g_pd3dDeviceContext = nullptr; // D3D11 设备上下文句柄
@@ -8,3 +11,10 @@ IDXGISwapChain *g_pSwapChain = nullptr; // DXGI 交换链句柄
bool g_SwapChainOccluded = false; // 交换链是否阻塞
ID3D11RenderTargetView *g_mainRenderTargetView = nullptr; // D3D11 渲染目标
// 文件输入相关
Input_File_Node *input_dlt_file_list_head;
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
+96 -11
View File
@@ -12,6 +12,7 @@ extern "C"
#include "d3d11.h"
#include "tchar.h"
#include "wchar.h"
#include "locale.h"
}
// 自定义头文件
@@ -35,6 +36,9 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg
// 入口主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// 设置本地化以支持特殊字符
setlocale(LC_ALL, ".UTF-8");
// 命令行前置处理
Pre_Command_Solve();
@@ -61,20 +65,20 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
RegisterClassExW(&wc);
// 创建窗体
HWND hwnd = CreateWindowW(wc.lpszClassName, // 创建的窗体类名
Main_Window_hWnd = CreateWindowW(wc.lpszClassName, // 创建的窗体类名
MAIN_FRAME_TITTLE, // 窗体标题
WS_OVERLAPPEDWINDOW, // 窗体风格为标准可重叠的顶层窗口
100, // 起始坐标x
100, // 起始坐标y
(int)(1280 * main_scale), // 窗体长
(int)(720 * main_scale), // 窗体宽
(GetSystemMetrics(SM_CXSCREEN) - (MAIN_FRAME_SIZE_WIDTH_MIN * main_scale)) / 2, // 起始坐标x,设置为屏幕正中
(GetSystemMetrics(SM_CYSCREEN) - (MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale)) / 2, // 起始坐标y,设置为屏幕正中
(int)(MAIN_FRAME_SIZE_WIDTH_MIN * main_scale), // 窗体长
(int)(MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale), // 窗体宽
nullptr, // 父窗体句柄
nullptr, // 菜单句柄
wc.hInstance, // 创建实例句柄
nullptr); // 额外的事件参数指针
// 在窗体内创建 Direct 3D 设备
if (!CreateDeviceD3D(hwnd))
if (!CreateDeviceD3D(Main_Window_hWnd))
{
// 未创建完成时清理 Direct 3D 设备并清理窗体注册类
CleanupDeviceD3D();
@@ -84,9 +88,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
return -1;
}
// 启用文件拖拽输入
DragAcceptFiles(Main_Window_hWnd, TRUE);
// 显示窗体并强制更新一次窗体
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
ShowWindow(Main_Window_hWnd, SW_SHOWDEFAULT);
UpdateWindow(Main_Window_hWnd);
// 检查版本
IMGUI_CHECKVERSION();
@@ -130,7 +137,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
}
// 初始化渲染器后端(Renderer backends)
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplWin32_Init(Main_Window_hWnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
// 字体配置相关
@@ -161,7 +168,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
{
// 非阻塞式获取所有窗体消息,Pick消息后从消息队列删除,并转译和派发消息
MSG msg;
while (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
while (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg); // 转译
DispatchMessageW(&msg); // 派发
@@ -233,7 +240,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
ImNodes::DestroyContext(); // 销毁 ImNodes 上下文
CleanupDeviceD3D(); // 清理 Direct 3D 设备
DestroyWindow(hwnd); // 销毁窗体
DestroyWindow(Main_Window_hWnd); // 销毁窗体
UnregisterClassW(wc.lpszClassName, wc.hInstance); // 取消窗体注册类
return 0;
@@ -333,6 +340,84 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
// 筛查并处理余下的消息事件
switch (msg)
{
case WM_DROPFILES: // 文件拖拽事件
{
HDROP hDrop = (HDROP)wParam; // 拖拽数据句柄
// 获取拖拽的文件总数
UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
// 非法输入检测
bool with_illegal_input = false;
bool with_duplicated_input = false;
// 添加到文件列表
for (UINT count = 0; count < fileCount; count++)
{
wchar_t filePath[MAX_PATH] = {L'\0'};
DragQueryFileW(
hDrop, // 拖拽句柄
count, // 文件索引(0 表示第一个文件)
filePath, // 存储路径的缓冲区
MAX_PATH // 缓冲区大小
);
char file_path_multi_byte[MAX_PATH] = {'\0'};
int size_needed = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, filePath, -1, file_path_multi_byte, size_needed, NULL, NULL);
// 重复文件剔除
bool duplicated = false;
for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next)
if (!strcmp(p_file_node->file_name_str, file_path_multi_byte))
{
duplicated = true;
with_duplicated_input = true;
break;
}
if (duplicated)
continue;
// 文件类型后缀过滤
const char *p_extension_name = file_path_multi_byte + (strlen(file_path_multi_byte) - 4);
if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'D' || toupper(p_extension_name[2]) != 'L' || toupper(p_extension_name[3]) != 'T')
{
with_illegal_input = true;
continue;
}
// 开辟新文件节点
Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node));
p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1);
memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1);
memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1);
p_new_file_node->p_next = nullptr;
// 插入文件列表
if (input_dlt_file_list_head == nullptr)
input_dlt_file_list_head = p_new_file_node;
else
{
Input_File_Node *p_target_node = input_dlt_file_list_head;
for (size_t count = 1; count < input_dlt_file_count; count++)
p_target_node = p_target_node->p_next;
p_target_node->p_next = p_new_file_node;
}
input_dlt_file_count++;
}
// 释放拖拽资源(必须调用,否则内存泄漏)
DragFinish(hDrop);
if (with_duplicated_input)
MessageBoxW(hWnd, L"存在重复输入", L"", MB_OK | MB_ICONWARNING);
if (with_illegal_input)
MessageBoxW(hWnd, L"存在非法输入,仅支持 .dlt 文件", L"", MB_OK | MB_ICONWARNING);
return 0;
}
case WM_SIZE: // 窗体大小变事件
if (wParam != SIZE_MINIMIZED) // 窗体未处于最小化时进行重绘
{
File diff suppressed because it is too large Load Diff
+414 -3
View File
@@ -1,4 +1,16 @@
#include "UI_Layout.hpp"
#include "imgui_impl_win32.h"
#include "Global_Variables.hpp"
#include "Config.hpp"
#include "DLT_Utilities.hpp"
extern "C"
{
#include "windows.h"
#include "stdio.h"
}
#include "thread"
// UI布局
void UI_Layout()
@@ -24,10 +36,259 @@ void UI_Layout()
// 设置窗体相关属性,不允许停靠
window_flags |= ImGuiWindowFlags_NoDocking;
ImGui::Begin("Main Panel", NULL, window_flags); // 创建Label
// 临时设置一下窗体大小,动态扩张窗体
{
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));
// 左半部分
ImGui::BeginChild("Left Part", ImVec2(MAIN_FRAME_SIZE_WIDTH_MIN, ImGui::GetContentRegionAvail().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::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);
}
void Widgets::Drag_Input_Area(const ImVec2 &size)
{
ImGui::BeginChild("Drag_Drop_Area", size);
{
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f); // 绘制风格栈压入窗口圆角为0
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
// 显示一个按钮,作为拖拽源
static bool pressed_hover = false;
if (ImGui::Button(pressed_hover ? u8"松开左键以添加" : u8"单击选择文件或拖拽文件到此处", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y)))
{
OPENFILENAMEW ofn; // common dialog box structure
wchar_t szFile[MAX_PATH * MAX_SINGLE_SELECT_FILE_COUNT] = {L'\0'}; // buffer for file name
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn); // 结构体大小
ofn.hwndOwner = Main_Window_hWnd; // 对话框唤起窗体
ofn.lpstrFile = szFile; // 输出缓冲区
ofn.nMaxFile = sizeof(szFile); // 缓冲区大小
ofn.lpstrFilter = L"(.dlt)\0*.dlt\0"; // 文件格式筛选器
ofn.nFilterIndex = 1; // 默认选择的筛选器
// 文件路径必须存在 | 文件本身必须存在 | 允许多文件选择 | 使用资源管理器
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
// Display the Open dialog box.
// 处理文件输入
if (GetOpenFileNameW(&ofn))
{
// 重复输入检测
bool with_duplicated_input = false;
wchar_t *p_first_str = ofn.lpstrFile;
// 单文件
if ((p_first_str[wcslen(p_first_str)] == L'\0') && (p_first_str[wcslen(p_first_str) + 1] == L'\0'))
{
char file_path_multi_byte[MAX_PATH] = {'\0'};
int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, file_path_multi_byte, size_needed, NULL, NULL);
for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next)
if (!strcmp(p_file_node->file_name_str, file_path_multi_byte))
{
with_duplicated_input = true;
break;
}
if (with_duplicated_input)
{
MessageBoxW(Main_Window_hWnd, L"存在重复输入", L"", MB_OK | MB_ICONWARNING);
}
else
{
// 开辟新文件节点
Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node));
p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1);
memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1);
memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1);
p_new_file_node->p_next = nullptr;
// 插入文件列表
if (input_dlt_file_list_head == nullptr)
input_dlt_file_list_head = p_new_file_node;
else
{
Input_File_Node *p_target_node = input_dlt_file_list_head;
for (size_t count = 1; count < input_dlt_file_count; count++)
p_target_node = p_target_node->p_next;
p_target_node->p_next = p_new_file_node;
}
input_dlt_file_count++;
}
}
else
{
// 多文件输入时
char public_path_multi_byte[MAX_PATH] = {'\0'};
int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, p_first_str, -1, public_path_multi_byte, size_needed, NULL, NULL);
for (wchar_t *p_file_name_str = ofn.lpstrFile + wcslen(ofn.lpstrFile) + 1; *p_file_name_str != L'\0'; p_file_name_str += (wcslen(p_file_name_str) + 1))
{
char file_name_multi_byte[MAX_PATH] = {'\0'};
char file_path_multi_byte[MAX_PATH] = {'\0'};
int size_needed = WideCharToMultiByte(CP_UTF8, 0, p_file_name_str, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, p_file_name_str, -1, file_name_multi_byte, size_needed, NULL, NULL);
// 拼接完整路径
sprintf(file_path_multi_byte, "%s\\%s", public_path_multi_byte, file_name_multi_byte);
for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next)
if (!strcmp(p_file_node->file_name_str, file_path_multi_byte))
{
with_duplicated_input = true;
break;
}
if (with_duplicated_input)
continue;
// 开辟新文件节点
Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node));
p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1);
memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1);
memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1);
p_new_file_node->p_next = nullptr;
// 插入文件列表
if (input_dlt_file_list_head == nullptr)
input_dlt_file_list_head = p_new_file_node;
else
{
Input_File_Node *p_target_node = input_dlt_file_list_head;
for (size_t count = 1; count < input_dlt_file_count; count++)
p_target_node = p_target_node->p_next;
p_target_node->p_next = p_new_file_node;
}
input_dlt_file_count++;
}
if (with_duplicated_input)
{
MessageBoxW(Main_Window_hWnd, L"存在重复输入", L"", MB_OK);
}
}
}
}
// 仅在外部拖拽入内时触发,内部拖拽不触发
pressed_hover = (ImGui::IsItemHovered() && (GetAsyncKeyState(VK_LBUTTON) < 0) && (!ImGui::IsMouseDown(ImGuiMouseButton_Left)));
ImGui::PopStyleVar(2);
}
ImGui::EndChild();
}
void Widgets::File_List_Area(const ImVec2 &size)
{
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("File_List_Area_Wrapper", size, ImGuiChildFlags_Border);
ImGui::PopStyleVar();
{
int element_id = 0;
Input_File_Node *p_file_node_pre = nullptr;
Input_File_Node *p_file_node = input_dlt_file_list_head;
for (size_t count = 0; count < input_dlt_file_count; count++)
{
ImGui::PushID(element_id++);
// 点击了删除
if (ImGui::Button("X"))
{
if (p_file_node == input_dlt_file_list_head)
{
free(input_dlt_file_list_head->file_name_str);
input_dlt_file_list_head = input_dlt_file_list_head->p_next;
free(p_file_node);
p_file_node = input_dlt_file_list_head;
}
else
{
p_file_node_pre->p_next = p_file_node->p_next;
free(p_file_node->file_name_str);
free(p_file_node);
}
input_dlt_file_count--;
count--;
ImGui::PopID();
continue;
}
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputText("##Text_Show", p_file_node->file_name_str, strlen(p_file_node->file_name_str) + 1, ImGuiInputTextFlags_ElideLeft | ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoUndoRedo);
ImGui::PopID();
p_file_node = p_file_node->p_next;
if (count == 0)
p_file_node_pre = input_dlt_file_list_head;
else
p_file_node_pre = p_file_node_pre->p_next;
}
}
ImGui::EndChild();
}
void Widgets::Workflow_Area(const ImVec2 &size)
{
ImNodes::BeginNodeEditor();
{
@@ -48,8 +309,158 @@ void UI_Layout()
ImNodes::EndNode();
}
ImNodes::EndNodeEditor();
}
void Widgets::Control_Panel(const ImVec2 &size)
{
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::BeginChild("Control_Panel", size, ImGuiChildFlags_Border);
ImGui::PopStyleVar(2);
{
static bool export_confirmed = false;
static bool export_task_created = false;
static char export_file_path_str[MAX_PATH] = {'\0'};
if (!export_confirmed)
{
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f);
if (ImGui::Button(u8"导出", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y)))
{
if (input_dlt_file_list_head == nullptr)
MessageBoxW(Main_Window_hWnd, L"需要至少输入一个文件", L"", MB_OK | MB_ICONERROR);
else
{
OPENFILENAMEW ofn; // common dialog box structure
static wchar_t szFile[MAX_PATH] = {L'\0'}; // buffer for file name
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn); // 结构体大小
ofn.hwndOwner = Main_Window_hWnd; // 对话框唤起窗体
ofn.lpstrFile = szFile; // 输出缓冲区
ofn.nMaxFile = sizeof(szFile); // 缓冲区大小
ofn.lpstrFilter = L"(.csv)\0*.csv\0"; // 文件格式筛选器
ofn.nFilterIndex = 1; // 默认选择的筛选器
// 文件路径必须存在 | 文件本身必须存在 | 使用资源管理器
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;
if (GetSaveFileNameW(&ofn))
{
// 提取文件路径
memset(export_file_path_str, '\0', sizeof(export_file_path_str));
int size_needed = WideCharToMultiByte(CP_UTF8, 0, szFile, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, szFile, -1, export_file_path_str, size_needed, NULL, NULL);
wchar_t File_Name[MAX_PATH] = {L'\0'};
wchar_t *p_str_end = szFile + wcslen(szFile);
// 处理后缀
char *p_extension_name = export_file_path_str + (strlen(export_file_path_str) - 4);
if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'C' || toupper(p_extension_name[2]) != 'S' || toupper(p_extension_name[3]) != 'V')
{
p_extension_name += 4;
p_extension_name[0] = '.';
p_extension_name[1] = 'c';
p_extension_name[2] = 's';
p_extension_name[3] = 'v';
p_str_end[0] = L'.';
p_str_end[1] = L'c';
p_str_end[2] = L's';
p_str_end[3] = L'v';
}
while (p_str_end != szFile && *(p_str_end - 1) != L'\\')
p_str_end--;
size_t len = wcslen(p_str_end);
for (size_t count = 0; count < len; count++, p_str_end++)
File_Name[count] = *p_str_end;
memcpy(szFile, File_Name, sizeof(File_Name));
// 判定是否覆盖文件
FILE *p_file = fopen(export_file_path_str, "rb");
if (p_file != nullptr)
{
fclose(p_file);
if (MessageBoxW(Main_Window_hWnd, L"文件已存在,是否覆盖?", L"", MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES)
export_confirmed = true;
}
else
export_confirmed = true;
}
}
ImGui::End();
}
ImGui::PopStyleVar();
}
else
{
// 创建导出任务
if (!export_task_created)
{
export_task_created = true;
// 执行导出任务的线程
std::thread export_thread(
[&]()
{
DLT_Log Log;
std::string err_str;
Input_File_Node *p_file_list = input_dlt_file_list_head;
while (p_file_list != nullptr)
{
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_str.empty())
{
Log.Sort();
err_str = Log.Export_CSV(export_file_path_str);
}
ImGui::ShowDemoWindow(nullptr);
if (err_str.empty())
{
MessageBoxW(Main_Window_hWnd, L"导出完成", L"", MB_OK);
}
else
{
// 工具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;
};
// 输出错误信息
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;
});
// 分离线程
export_thread.detach();
}
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 12.5f);
ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), u8"正在导出...");
ImGui::PopStyleVar();
}
}
ImGui::EndChild();
}
File diff suppressed because it is too large Load Diff