initial
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#include "../include/ConnectionWindow.h"
|
||||
#include "imgui.h"
|
||||
#include <cstdio>
|
||||
|
||||
ConnectionWindow::ConnectionWindow()
|
||||
: selected_port_idx(0), is_connected(false), active_port(nullptr) {
|
||||
|
||||
// Заполняем массив стандартными скоростями UART
|
||||
baud_rates = { 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 };
|
||||
|
||||
// По умолчанию выбираем 9600 (индекс 0 в векторе)
|
||||
selected_baud_idx = 0;
|
||||
}
|
||||
|
||||
ConnectionWindow::~ConnectionWindow() {
|
||||
if (is_connected && active_port) {
|
||||
sp_close(active_port);
|
||||
sp_free_port(active_port);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionWindow::Initialize() {
|
||||
RefreshPorts();
|
||||
}
|
||||
|
||||
void ConnectionWindow::RefreshPorts() {
|
||||
ports.clear();
|
||||
selected_port_idx = 0;
|
||||
|
||||
struct sp_port** port_list;
|
||||
if (sp_list_ports(&port_list) == SP_OK) {
|
||||
for (int i = 0; port_list[i] != nullptr; i++) {
|
||||
struct sp_port* port = port_list[i];
|
||||
SerialPortInfo info;
|
||||
const char* name = sp_get_port_name(port);
|
||||
info.port_name = name ? name : "Unknown";
|
||||
const char* desc = sp_get_port_description(port);
|
||||
info.description = desc ? desc : "No Description";
|
||||
ports.push_back(info);
|
||||
}
|
||||
sp_free_port_list(port_list);
|
||||
}
|
||||
|
||||
if (ports.empty()) {
|
||||
ports.push_back({"None", "No serial devices detected"});
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionWindow::ForceLogMessage(const std::string& msg) {
|
||||
tx_log += msg + "\n";
|
||||
}
|
||||
|
||||
void ConnectionWindow::Render(bool* p_open) {
|
||||
if (!ImGui::Begin("Connection Settings", p_open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Refresh Ports") && !is_connected) {
|
||||
RefreshPorts();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
// 1. Выбор COM-порта
|
||||
std::string combo_preview = ports[selected_port_idx].port_name;
|
||||
if (ports[selected_port_idx].port_name != "None") {
|
||||
combo_preview += " (" + ports[selected_port_idx].description + ")";
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(is_connected);
|
||||
if (ImGui::BeginCombo("Serial Port", combo_preview.c_str())) {
|
||||
for (int n = 0; n < ports.size(); n++) {
|
||||
const bool is_selected = (selected_port_idx == n);
|
||||
std::string item_text = ports[n].port_name + " - " + ports[n].description;
|
||||
if (ImGui::Selectable(item_text.c_str(), is_selected)) {
|
||||
selected_port_idx = n;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
// 2. Выбор Baud Rate через выпадающий список
|
||||
std::string baud_preview = std::to_string(baud_rates[selected_baud_idx]);
|
||||
if (ImGui::BeginCombo("Baud Rate", baud_preview.c_str())) {
|
||||
for (int b = 0; b < baud_rates.size(); b++) {
|
||||
const bool is_selected = (selected_baud_idx == b);
|
||||
std::string baud_item_text = std::to_string(baud_rates[b]);
|
||||
|
||||
if (ImGui::Selectable(baud_item_text.c_str(), is_selected)) {
|
||||
selected_baud_idx = b;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// 3. Логика подключения
|
||||
if (!is_connected) {
|
||||
ImGui::BeginDisabled(ports[selected_port_idx].port_name == "None");
|
||||
if (ImGui::Button("Connect", ImVec2(120, 0))) {
|
||||
if (sp_get_port_by_name(ports[selected_port_idx].port_name.c_str(), &active_port) == SP_OK) {
|
||||
if (sp_open(active_port, SP_MODE_READ_WRITE) == SP_OK) {
|
||||
|
||||
// Передаем реальное значение скорости из выбранного индекса массива
|
||||
int real_baud = baud_rates[selected_baud_idx];
|
||||
sp_set_baudrate(active_port, real_baud);
|
||||
|
||||
sp_set_bits(active_port, 8);
|
||||
sp_set_parity(active_port, SP_PARITY_NONE);
|
||||
sp_set_stopbits(active_port, 1);
|
||||
|
||||
is_connected = true;
|
||||
ForceLogMessage("[SYSTEM] Connected to " + ports[selected_port_idx].port_name + " at " + std::to_string(real_baud) + " baud.");
|
||||
} else {
|
||||
ForceLogMessage("[SYSTEM ERROR] Could not open port " + ports[selected_port_idx].port_name);
|
||||
sp_free_port(active_port);
|
||||
active_port = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.1f, 0.1f, 1.0f));
|
||||
if (ImGui::Button("Disconnect", ImVec2(120, 0))) {
|
||||
if (active_port) {
|
||||
sp_close(active_port);
|
||||
sp_free_port(active_port);
|
||||
active_port = nullptr;
|
||||
}
|
||||
is_connected = false;
|
||||
ForceLogMessage("[SYSTEM] Disconnected.");
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Status: %s", is_connected ? "CONNECTED" : "DISCONNECTED");
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "../include/LogWindow.h"
|
||||
#include "../include/ConnectionWindow.h"
|
||||
#include "imgui.h"
|
||||
|
||||
void LogWindow::Render(bool* p_open, ConnectionWindow& conn_win) {
|
||||
if (!ImGui::Begin("Transaction Log", p_open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string& log_ref = conn_win.GetSharedLog();
|
||||
|
||||
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::TextUnformatted(log_ref.c_str());
|
||||
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (ImGui::Button("Clear Log")) {
|
||||
log_ref.clear();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "../include/PayloadWindow.h"
|
||||
|
||||
#include "../include/ConnectionWindow.h"
|
||||
#include <cstdint>
|
||||
#include "imgui.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
PayloadWindow::PayloadWindow() {
|
||||
std::snprintf(hex_input_buffer, sizeof(hex_input_buffer), "AA BB 01 02 03 FF");
|
||||
}
|
||||
|
||||
void PayloadWindow::SendHexPayload(const std::string& hex_str, ConnectionWindow& conn_win) {
|
||||
struct sp_port* port = conn_win.GetActivePort();
|
||||
if (!conn_win.IsConnected() || !port) return;
|
||||
|
||||
std::vector<std::uint8_t> bytes;
|
||||
std::stringstream ss(hex_str);
|
||||
std::string byte_string;
|
||||
|
||||
while (ss >> byte_string) {
|
||||
unsigned int byte_val;
|
||||
std::stringstream converter;
|
||||
converter << std::hex << byte_string;
|
||||
if (converter >> byte_val) {
|
||||
bytes.push_back(static_cast<std::uint8_t>(byte_val));
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes.empty()) {
|
||||
conn_win.ForceLogMessage("[ERROR] No valid bytes found to send.");
|
||||
return;
|
||||
}
|
||||
|
||||
int bytes_written = sp_nonblocking_write(port, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes_written >= 0) {
|
||||
std::stringstream log_ss;
|
||||
log_ss << "[TX] Sent " << bytes_written << " bytes: ";
|
||||
for (size_t i = 0; i < bytes.size(); ++i) {
|
||||
log_ss << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << (int)bytes[i] << " ";
|
||||
}
|
||||
conn_win.ForceLogMessage(log_ss.str());
|
||||
} else {
|
||||
conn_win.ForceLogMessage("[ERROR] Failed to write data to serial port.");
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadWindow::Render(bool* p_open, ConnectionWindow& conn_win) {
|
||||
if (!ImGui::Begin("Raw Payload Terminal", p_open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::InputText("HEX Bytes", hex_input_buffer, IM_ARRAYSIZE(hex_input_buffer));
|
||||
ImGui::TextDisabled("Example: AA BB 0F 45");
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::BeginDisabled(!conn_win.IsConnected());
|
||||
if (ImGui::Button("SEND PACKET", ImVec2(-1, 35))) {
|
||||
SendHexPayload(hex_input_buffer, conn_win);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "../include/SerialApp.h"
|
||||
#include "imgui.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
SerialApp::SerialApp() : should_close(false) {}
|
||||
|
||||
void SerialApp::Initialize() {
|
||||
connection_window.Initialize();
|
||||
|
||||
// Загружаем тему при старте. Если лежит в корне сборки — просто имя файла.
|
||||
if (!LoadTheme("dracula.theme")) {
|
||||
// Если файла нет, оставляем стандартную тему ImGui, чтобы приложение не упало
|
||||
ImGui::StyleColorsDark();
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательная функция перевода HEX строки (например, "282a36ff") в ImVec4
|
||||
static ImVec4 HexToImVec4(const std::string& hex_str) {
|
||||
if (hex_str.length() < 8) return ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
unsigned int r, g, b, a;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << hex_str.substr(0, 2); ss >> r; ss.clear();
|
||||
ss << std::hex << hex_str.substr(2, 2); ss >> g; ss.clear();
|
||||
ss << std::hex << hex_str.substr(4, 2); ss >> b; ss.clear();
|
||||
ss << std::hex << hex_str.substr(6, 2); ss >> a;
|
||||
|
||||
return ImVec4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
|
||||
}
|
||||
|
||||
bool SerialApp::LoadTheme(const std::string& filepath) {
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "[THEME ERROR] Файл темы не найден: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// Карта сопоставления строк из файла конфигурации и индексов цветов ImGui
|
||||
std::unordered_map<std::string, ImGuiCol_> color_map = {
|
||||
{"Color_Text", ImGuiCol_Text},
|
||||
{"Color_WindowBg", ImGuiCol_WindowBg},
|
||||
{"Color_ChildBg", ImGuiCol_ChildBg},
|
||||
{"Color_PopupBg", ImGuiCol_PopupBg},
|
||||
{"Color_Border", ImGuiCol_Border},
|
||||
{"Color_FrameBg", ImGuiCol_FrameBg},
|
||||
{"Color_FrameBgHovered", ImGuiCol_FrameBgHovered},
|
||||
{"Color_FrameBgActive", ImGuiCol_FrameBgActive},
|
||||
{"Color_TitleBg", ImGuiCol_TitleBg},
|
||||
{"Color_TitleBgActive", ImGuiCol_TitleBgActive},
|
||||
{"Color_MenuBarBg", ImGuiCol_MenuBarBg},
|
||||
{"Color_ScrollbarBg", ImGuiCol_ScrollbarBg},
|
||||
{"Color_ScrollbarGrab", ImGuiCol_ScrollbarGrab},
|
||||
{"Color_ScrollbarGrabHovered", ImGuiCol_ScrollbarGrabHovered},
|
||||
{"Color_ScrollbarGrabActive", ImGuiCol_ScrollbarGrabActive},
|
||||
{"Color_CheckMark", ImGuiCol_CheckMark},
|
||||
{"Color_SliderGrab", ImGuiCol_SliderGrab},
|
||||
{"Color_SliderGrabActive", ImGuiCol_SliderGrabActive},
|
||||
{"Color_Button", ImGuiCol_Button},
|
||||
{"Color_ButtonHovered", ImGuiCol_ButtonHovered},
|
||||
{"Color_ButtonActive", ImGuiCol_ButtonActive},
|
||||
{"Color_Header", ImGuiCol_Header},
|
||||
{"Color_HeaderHovered", ImGuiCol_HeaderHovered},
|
||||
{"Color_HeaderActive", ImGuiCol_HeaderActive},
|
||||
{"Color_Separator", ImGuiCol_Separator}
|
||||
};
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
// Пропускаем пустые строки и комментарии
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
std::stringstream ss(line);
|
||||
std::string key;
|
||||
ss >> key;
|
||||
|
||||
if (key.rfind("Color_", 0) == 0) {
|
||||
// Читаем HEX цвета
|
||||
std::string hex_val;
|
||||
ss >> hex_val;
|
||||
if (color_map.find(key) != color_map.end()) {
|
||||
style.Colors[color_map[key]] = HexToImVec4(hex_val);
|
||||
}
|
||||
} else {
|
||||
// Читаем размеры скруглений
|
||||
float val;
|
||||
ss >> val;
|
||||
if (key == "WindowRounding") style.WindowRounding = val;
|
||||
else if (key == "FrameRounding") style.FrameRounding = val;
|
||||
else if (key == "PopupRounding") style.PopupRounding = val;
|
||||
else if (key == "GrabRounding") style.GrabRounding = val;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialApp::RenderUI() {
|
||||
RenderMainMenuBar();
|
||||
|
||||
if (show_connection_window) {
|
||||
connection_window.Render(&show_connection_window);
|
||||
}
|
||||
if (show_payload_window) {
|
||||
payload_window.Render(&show_payload_window, connection_window);
|
||||
}
|
||||
if (show_log_window) {
|
||||
log_window.Render(&show_log_window, connection_window);
|
||||
}
|
||||
}
|
||||
|
||||
void SerialApp::RenderMainMenuBar() {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Exit", "Alt+F4")) {
|
||||
should_close = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Windows")) {
|
||||
ImGui::MenuItem("1. Connection Settings", nullptr, &show_connection_window);
|
||||
ImGui::MenuItem("2. Raw Payload Terminal", nullptr, &show_payload_window);
|
||||
ImGui::MenuItem("3. Transaction Log", nullptr, &show_log_window);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user