will try to redo back
This commit is contained in:
Generated
-2
@@ -2,7 +2,5 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/../thirdparty/imgui" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/../thirdparty/implot" vcs="Git" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+86
-26
@@ -6,11 +6,51 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
set(OpenGL_GL_PREFERENCE GLVND)
|
set(OpenGL_GL_PREFERENCE GLVND)
|
||||||
|
|
||||||
# Define file paths for thirdparty modules
|
# ==============================================================================
|
||||||
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/imgui)
|
# ЗАГРУЗКА ЗАВИСИМОСТЕЙ ЧЕРЕЗ FETCHCONTENT (ImGui и ImPlot)
|
||||||
set(IMPLOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/implot)
|
# ==============================================================================
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
# Find Required Packages
|
# 1. Загрузка Dear ImGui
|
||||||
|
FetchContent_Declare(
|
||||||
|
imgui
|
||||||
|
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(imgui)
|
||||||
|
|
||||||
|
# 2. Загрузка ImPlot
|
||||||
|
FetchContent_Declare(
|
||||||
|
implot
|
||||||
|
GIT_REPOSITORY https://github.com/epezent/implot.git
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(implot)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# НАСТРОЙКА ИСХОДНЫХ КОДОВ БИБЛИОТЕК
|
||||||
|
# ==============================================================================
|
||||||
|
# Исходники ImGui
|
||||||
|
set(IMGUI_SOURCES
|
||||||
|
${imgui_SOURCE_DIR}/imgui.cpp
|
||||||
|
${imgui_SOURCE_DIR}/imgui_demo.cpp
|
||||||
|
${imgui_SOURCE_DIR}/imgui_draw.cpp
|
||||||
|
${imgui_SOURCE_DIR}/imgui_tables.cpp
|
||||||
|
${imgui_SOURCE_DIR}/imgui_widgets.cpp
|
||||||
|
${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp
|
||||||
|
${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Исходники ImPlot
|
||||||
|
set(IMPLOT_SOURCES
|
||||||
|
${implot_SOURCE_DIR}/implot.cpp
|
||||||
|
${implot_SOURCE_DIR}/implot_items.cpp
|
||||||
|
${implot_SOURCE_DIR}/implot_demo.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ПОИСК СИСТЕМНЫХ ПАКЕТОВ
|
||||||
|
# ==============================================================================
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
pkg_check_modules(GLFW REQUIRED glfw3)
|
pkg_check_modules(GLFW REQUIRED glfw3)
|
||||||
@@ -18,25 +58,9 @@ pkg_check_modules(LIBSERIALPORT REQUIRED IMPORTED_TARGET libserialport)
|
|||||||
find_package(Protobuf REQUIRED)
|
find_package(Protobuf REQUIRED)
|
||||||
find_package(gRPC REQUIRED)
|
find_package(gRPC REQUIRED)
|
||||||
|
|
||||||
# ImGUI things
|
# ==============================================================================
|
||||||
set(IMGUI_SOURCES
|
# ГЕНЕРАЦИЯ PROTOBUF & GRPC
|
||||||
${IMGUI_DIR}/imgui.cpp
|
# ==============================================================================
|
||||||
${IMGUI_DIR}/imgui_demo.cpp
|
|
||||||
${IMGUI_DIR}/imgui_draw.cpp
|
|
||||||
${IMGUI_DIR}/imgui_tables.cpp
|
|
||||||
${IMGUI_DIR}/imgui_widgets.cpp
|
|
||||||
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
|
||||||
${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(IMPLOT_SOURCES
|
|
||||||
${IMPLOT_DIR}/implot.cpp
|
|
||||||
${IMPLOT_DIR}/implot_items.cpp
|
|
||||||
${IMPLOT_DIR}/implot_demo.cpp
|
|
||||||
sources/SerialApp.cpp
|
|
||||||
include/SerialApp.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/service.pb.cc")
|
set(proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/service.pb.cc")
|
||||||
set(proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/service.pb.h")
|
set(proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/service.pb.h")
|
||||||
set(grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/service.grpc.pb.cc")
|
set(grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/service.grpc.pb.cc")
|
||||||
@@ -65,9 +89,14 @@ target_include_directories(proto_lib PUBLIC
|
|||||||
${Protobuf_INCLUDE_DIRS}
|
${Protobuf_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# СБОРКА ОСНОВНОГО ПРИЛОЖЕНИЯ
|
||||||
|
# ==============================================================================
|
||||||
add_executable(${PROJECT_NAME} main.cpp
|
add_executable(${PROJECT_NAME} main.cpp
|
||||||
${IMGUI_SOURCES}
|
${IMGUI_SOURCES}
|
||||||
${IMPLOT_SOURCES}
|
${IMPLOT_SOURCES}
|
||||||
|
sources/SerialApp.cpp
|
||||||
|
include/SerialApp.h
|
||||||
sources/ConnectionWindow.cpp
|
sources/ConnectionWindow.cpp
|
||||||
include/ConnectionWindow.h
|
include/ConnectionWindow.h
|
||||||
sources/PayloadWindow.cpp
|
sources/PayloadWindow.cpp
|
||||||
@@ -80,9 +109,9 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${Protobuf_INCLUDE_DIRS}
|
${Protobuf_INCLUDE_DIRS}
|
||||||
${IMGUI_DIR}
|
${imgui_SOURCE_DIR}
|
||||||
${IMGUI_DIR}/backends
|
${imgui_SOURCE_DIR}/backends
|
||||||
${IMPLOT_DIR}
|
${implot_SOURCE_DIR}
|
||||||
${GLFW_INCLUDE_DIRS}
|
${GLFW_INCLUDE_DIRS}
|
||||||
${OPENGL_INCLUDE_DIR}
|
${OPENGL_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
@@ -98,3 +127,34 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
configure_file(style/dracula.theme ${CMAKE_CURRENT_BINARY_DIR}/dracula.theme COPYONLY)
|
configure_file(style/dracula.theme ${CMAKE_CURRENT_BINARY_DIR}/dracula.theme COPYONLY)
|
||||||
|
|
||||||
|
|
||||||
|
#Является частой практикой в больших проектах для поддержки
|
||||||
|
#единой схемы данныхмежду сервером и клиентом.
|
||||||
|
#Если .proto лежит в отдельном Git-репозитории на сервере
|
||||||
|
#лучше использовать FetchContent. Он корректно кэширует файлы
|
||||||
|
#и обновляет их только при смене тега или хэша коммита.
|
||||||
|
#```
|
||||||
|
#FetchContent_Declare(
|
||||||
|
#remote_proto
|
||||||
|
#GIT_REPOSITORY git@your-server.com:shared/protocols.git # URL вашего репозитория
|
||||||
|
#GIT_TAG main # Ветка, тег или хэш коммита
|
||||||
|
#)
|
||||||
|
#FetchContent_MakeAvailable(remote_proto)
|
||||||
|
#```
|
||||||
|
#Теперь файл доступен по пути: ${remote_proto_SOURCE_DIR}/service.proto
|
||||||
|
#
|
||||||
|
#не забыть обновить команду компиляции Protobuf,
|
||||||
|
#заменив локальные пути на пути из FetchContent:
|
||||||
|
#```
|
||||||
|
#add_custom_command(
|
||||||
|
#OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}"
|
||||||
|
#COMMAND ${Protobuf_PROTOC_EXECUTABLE}
|
||||||
|
#ARGS --grpc_out="${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
#--cpp_out="${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
#-I "${remote_proto_SOURCE_DIR}" # Корневая папка для импортов
|
||||||
|
#--plugin=protoc-gen-grpc=/usr/bin/grpc_cpp_plugin
|
||||||
|
#"${REMOTE_PROTO_PATH}" # Сам файл
|
||||||
|
#DEPENDS "${REMOTE_PROTO_PATH}"
|
||||||
|
#)
|
||||||
|
#```
|
||||||
|
|||||||
@@ -17,54 +17,67 @@ static void glfw_error_callback(int error, const char* description) {
|
|||||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Простейшая реализация вашего сервиса gRPC (для примера)
|
bool initImGUI(GLFWwindow *&window) {
|
||||||
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
|
if (!glfwInit()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
|
||||||
|
window = glfwCreateWindow(1280, 720, "Serial App", nullptr, nullptr);
|
||||||
|
if (window == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSwapInterval(1);
|
||||||
|
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
const ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
|
ImGui_ImplOpenGL3_Init("#version 130");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Простейшая реализация сервиса gRPC
|
||||||
class MyServiceImpl final : public serial_sample::Greeter::Service {
|
class MyServiceImpl final : public serial_sample::Greeter::Service {
|
||||||
grpc::Status SayHello(grpc::ServerContext* context,
|
grpc::Status SayHello(grpc::ServerContext* context,
|
||||||
const serial_sample::HelloRequest* request,
|
const serial_sample::HelloRequest* request,
|
||||||
serial_sample::HelloReply* reply) override {
|
serial_sample::HelloReply* reply) override {
|
||||||
|
std::cout << request->name() << std::endl;
|
||||||
reply->set_message("Hello, " + request->name() + "!");
|
reply->set_message("Hello, " + request->name() + "!");
|
||||||
return grpc::Status::OK;
|
return grpc::Status::OK;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int, char**) {
|
int main(int, char**) {
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
GLFWwindow *window;
|
||||||
if (!glfwInit()) return 1;
|
if (initImGUI(window)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
SerialApp app; // frontend manager
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
|
|
||||||
GLFWwindow* window = glfwCreateWindow(1280, 720, "Serial App", nullptr, nullptr);
|
|
||||||
if (window == nullptr) return 1;
|
|
||||||
glfwMakeContextCurrent(window);
|
|
||||||
glfwSwapInterval(1);
|
|
||||||
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
||||||
ImGui_ImplOpenGL3_Init("#version 130");
|
|
||||||
|
|
||||||
SerialApp app;
|
|
||||||
app.initialize();
|
app.initialize();
|
||||||
|
|
||||||
// === НАЧАЛО ИНИЦИАЛИЗАЦИИ gRPC СЕРВЕРА ===
|
// инициализация gRPC
|
||||||
std::string server_address("0.0.0.0:50051");
|
const std::string serverAddress("0.0.0.0:50051");
|
||||||
MyServiceImpl grpc_service;
|
MyServiceImpl serviceImpl;
|
||||||
|
|
||||||
grpc::ServerBuilder builder;
|
grpc::ServerBuilder builder;
|
||||||
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
|
||||||
builder.RegisterService(&grpc_service);
|
builder.RegisterService(&serviceImpl);
|
||||||
|
|
||||||
std::unique_ptr<grpc::Server> grpc_server(builder.BuildAndStart());
|
std::unique_ptr gRPCServer(builder.BuildAndStart());
|
||||||
std::cout << "gRPC Server listening on " << server_address << std::endl;
|
std::cout << "gRPC Server listening on " << serverAddress << std::endl;
|
||||||
|
|
||||||
// Запускаем сервер в фоновом потоке
|
// сервер стартует в фоновом БЛОКИРУЮЩЕМ потоке
|
||||||
std::thread grpc_thread([&grpc_server]() {
|
std::thread gRPCThread([&gRPCServer] {
|
||||||
grpc_server->Wait(); // Блокирует фоновый поток, пока сервер работает
|
gRPCServer->Wait();
|
||||||
});
|
});
|
||||||
// =========================================
|
// =========================================
|
||||||
|
|
||||||
@@ -82,9 +95,9 @@ int main(int, char**) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
int display_w, display_h;
|
int width, height;
|
||||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
glfwGetFramebufferSize(window, &width, &height);
|
||||||
glViewport(0, 0, display_w, display_h);
|
glViewport(0, 0, width, height);
|
||||||
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
@@ -92,14 +105,14 @@ int main(int, char**) {
|
|||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === КОРРЕКТНОЕ ВЫКЛЮЧЕНИЕ gRPC СЕРВЕРА ===
|
// выключение gRPC
|
||||||
std::cout << "Shutting down gRPC server..." << std::endl;
|
std::cout << "Shutting down gRPC server..." << std::endl;
|
||||||
// Останавливаем сервер (это разблокирует метод Wait() в фоновом потоке)
|
// Останавливаем сервер (это разблокирует метод Wait() в фоновом потоке)
|
||||||
grpc_server->Shutdown();
|
gRPCServer->Shutdown();
|
||||||
|
|
||||||
// Обязательно дожидаемся завершения фонового потока перед выходом из main
|
// Обязательно дожидаемся завершения фонового потока перед выходом из main
|
||||||
if (grpc_thread.joinable()) {
|
if (gRPCThread.joinable()) {
|
||||||
grpc_thread.join();
|
gRPCThread.join();
|
||||||
}
|
}
|
||||||
std::cout << "gRPC server thread joined." << std::endl;
|
std::cout << "gRPC server thread joined." << std::endl;
|
||||||
// =========================================
|
// =========================================
|
||||||
|
|||||||
@@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* @file serial.proto
|
||||||
|
* @brief Протокол обмена между GUI‑клиентом и backend‑службой
|
||||||
|
* для работы с последовательным (UART/RS‑232/USB‑CDC) портом.
|
||||||
|
*
|
||||||
|
* Протокол построен на базе **Protocol Buffers 3** и **gRPC**.
|
||||||
|
* Он покрывает все типичные задачи:
|
||||||
|
* - перечисление доступных портов;
|
||||||
|
* - открытие/закрытие порта с полным набором параметров;
|
||||||
|
* - отправку и приём произвольных пакетов;
|
||||||
|
* - ведение журнала (лог‑стрим) всех переданных/полученных байтов
|
||||||
|
* – удобно для отладки и верификации протокола.
|
||||||
|
*
|
||||||
|
* Файл снабжён Doxygen‑комментариями, поэтому из него можно
|
||||||
|
* автоматически получить красивую HTML‑/PDF‑документацию.
|
||||||
|
*/
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package serial;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Enums – вспомогательные типы */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum Parity
|
||||||
|
* @brief Возможные варианты чётности (parity) при работе с UART.
|
||||||
|
*
|
||||||
|
* Имена полностью совпадают с теми, что использует библиотека **pyserial**
|
||||||
|
* (и, в принципе, большинство C/C++‑библиотек для последовательных портов).
|
||||||
|
*/
|
||||||
|
enum Parity {
|
||||||
|
PARITY_NONE = 0; /// Без чётности.
|
||||||
|
PARITY_EVEN = 1; /// Чётность чётных битов.
|
||||||
|
PARITY_ODD = 2; /// Чётность нечётных битов.
|
||||||
|
PARITY_MARK = 3; /// Маркер (всегда 1).
|
||||||
|
PARITY_SPACE= 4; /// Пробел (всегда 0).
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum StopBits
|
||||||
|
* @brief Конфигурация стоп‑битов.
|
||||||
|
*
|
||||||
|
* Значения соответствуют типичным настройкам UART‑контроллера.
|
||||||
|
*/
|
||||||
|
enum StopBits {
|
||||||
|
STOPBITS_ONE = 0; /// Один стоп‑бит.
|
||||||
|
STOPBITS_ONE_POINT_FIVE = 1; /// Полтора стоп‑бита (реже используется).
|
||||||
|
STOPBITS_TWO = 2; /// Два стоп‑бита.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum StatusCode
|
||||||
|
* @brief Универсальный набор кодов возврата для большинства RPC‑методов.
|
||||||
|
*
|
||||||
|
* При необходимости можно добавить новые коды, но текущего набора достаточно
|
||||||
|
* для базовых сценариев.
|
||||||
|
*/
|
||||||
|
enum StatusCode {
|
||||||
|
OK = 0; /// Операция выполнена успешно.
|
||||||
|
INVALID_ARGUMENT = 1; /// Переданы некорректные аргументы (например, отрицательная скорость).
|
||||||
|
NOT_FOUND = 2; /// Запрашиваемый объект (порт, файл и т.п.) не найден.
|
||||||
|
INTERNAL_ERROR = 3; /// Внутренняя ошибка сервера (исключение, недоступный ресурс).
|
||||||
|
NOT_CONNECTED = 4; /// Операция невозможна, т.к. порт не открыт.
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Сообщения – описание данных, передаваемых по gRPC */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message SerialDevice
|
||||||
|
* @brief Информация об одном последовательном порте, доступном в системе.
|
||||||
|
*
|
||||||
|
* @param port_name Системное имя порта (например, «COM3», «/dev/ttyUSB0»).
|
||||||
|
* @param description Человекочитаемое описание, если ОС его предоставляет.
|
||||||
|
* @param hardware_id Строка‑идентификатор устройства (VID/PID и т.п.).
|
||||||
|
*/
|
||||||
|
message SerialDevice {
|
||||||
|
string port_name = 1;
|
||||||
|
string description = 2;
|
||||||
|
string hardware_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message SerialConfig
|
||||||
|
* @brief Полный набор параметров, необходимый для открытия последовательного порта.
|
||||||
|
*
|
||||||
|
* @param port_name Должно совпадать с `SerialDevice.port_name`.
|
||||||
|
* @param baud_rate Скорость в бодах (например, 9600, 115200 …).
|
||||||
|
* @param data_bits Количество битов данных (5‑8, обычно 8).
|
||||||
|
* @param parity Чётность (см. enum @ref Parity).
|
||||||
|
* @param stop_bits Стоп‑биты (см. enum @ref StopBits).
|
||||||
|
* @param packet_size Фиксированный размер полезной нагрузки пакета.
|
||||||
|
* 0 → размер произвольный (по умолчанию).
|
||||||
|
*/
|
||||||
|
message SerialConfig {
|
||||||
|
string port_name = 1;
|
||||||
|
uint32 baud_rate = 2;
|
||||||
|
uint32 data_bits = 3;
|
||||||
|
Parity parity = 4;
|
||||||
|
StopBits stop_bits = 5;
|
||||||
|
uint32 packet_size = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message ConfigResponse
|
||||||
|
* @brief Ответ на запрос `Open`/`Close`. Содержит статус операции и,
|
||||||
|
* при ошибке, человекочитаемое сообщение.
|
||||||
|
*
|
||||||
|
* @param status Код статуса (см. enum @ref StatusCode).
|
||||||
|
* @param error Текстовое пояснение, заполнено только если `status != OK`.
|
||||||
|
*/
|
||||||
|
message ConfigResponse {
|
||||||
|
StatusCode status = 1;
|
||||||
|
string error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message Packet
|
||||||
|
* @brief Описание логической "пакетной" единицы, которую GUI отправляет
|
||||||
|
* на backend, а backend – в последовательный порт.
|
||||||
|
*
|
||||||
|
* Полезная нагрузка (`payload`) передаётся «как есть» (raw‑bytes).
|
||||||
|
*
|
||||||
|
* @param seq_id Последовательный номер пакета, генерируется клиентом.
|
||||||
|
* Удобен для отладки и согласования запрос‑ответ.
|
||||||
|
* @param payload Бинарные данные, которые действительно окажутся на линии.
|
||||||
|
* @param tags Необязательная карта «ключ → значение», используемая
|
||||||
|
* только внутри программы (не попадает в кадр UART).
|
||||||
|
*/
|
||||||
|
message Packet {
|
||||||
|
uint64 seq_id = 1;
|
||||||
|
bytes payload = 2;
|
||||||
|
map<string, string> tags = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message SendResponse
|
||||||
|
* @brief Ответ на RPC `SendPacket`. Содержит статус, сообщение об ошибке и
|
||||||
|
* «эхо‑идентификатор», позволяющий клиенту убедиться, что ответ
|
||||||
|
* относится к конкретному запросу.
|
||||||
|
*
|
||||||
|
* @param status Код статуса (см. enum @ref StatusCode).
|
||||||
|
* @param error Текстовое описание ошибки (если есть).
|
||||||
|
* @param echo_seq_id Идентификатор пакета, пришедший в запросе.
|
||||||
|
*/
|
||||||
|
message SendResponse {
|
||||||
|
StatusCode status = 1;
|
||||||
|
string error = 2;
|
||||||
|
uint64 echo_seq_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message SerialLogEntry
|
||||||
|
* @brief Описание одного события (TX или RX) на уровне сырых байтов.
|
||||||
|
*
|
||||||
|
* Это «протокол‑верификация»: каждая запись сохраняется в журнал
|
||||||
|
* и может быть передана клиенту в реальном времени.
|
||||||
|
*
|
||||||
|
* @param dir Направление трафика (TX – передача, RX – приём).
|
||||||
|
* @param timestamp Время события в миллисекундах с начала эпохи Unix.
|
||||||
|
* @param raw_data Точные байты, полученные/отправленные в этом событии.
|
||||||
|
* @param note Необязательная строка‑комментарий (например,
|
||||||
|
* «checksum ok», «frame start», …).
|
||||||
|
*/
|
||||||
|
message SerialLogEntry {
|
||||||
|
/** @brief Направление трафика. */
|
||||||
|
enum Direction {
|
||||||
|
/** @brief Пакет был отправлен в порт. */
|
||||||
|
TX = 0;
|
||||||
|
/** @brief Пакет был получен из порта. */
|
||||||
|
RX = 1;
|
||||||
|
}
|
||||||
|
Direction dir = 1;
|
||||||
|
uint64 timestamp = 2;
|
||||||
|
bytes raw_data = 3;
|
||||||
|
string note = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message LogStreamRequest
|
||||||
|
* @brief Параметры подписки на поток журнала.
|
||||||
|
*
|
||||||
|
* @param from_start Если true – клиент получает *все* накопленные записи,
|
||||||
|
* иначе – только новые, появившиеся после установления
|
||||||
|
* соединения.
|
||||||
|
*/
|
||||||
|
message LogStreamRequest {
|
||||||
|
bool from_start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @message DeviceList
|
||||||
|
* @brief Обёртка, возвращающая список найденных последовательных портов.
|
||||||
|
*
|
||||||
|
* @param devices Список `SerialDevice`.
|
||||||
|
*/
|
||||||
|
message DeviceList {
|
||||||
|
repeated SerialDevice devices = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Service definition – набор RPC‑методов, которые реализует backend */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @service SerialService
|
||||||
|
* @brief gRPC‑сервис, предоставляющий весь функционал работы с
|
||||||
|
* последовательным портом и журналированием.
|
||||||
|
*
|
||||||
|
* Каждый метод описан ниже, вместе с его параметрами и возвращаемыми
|
||||||
|
* типами.
|
||||||
|
*/
|
||||||
|
service SerialService {
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Device discovery
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Возвращает список всех последовательных портов, обнаруженных в системе.
|
||||||
|
*
|
||||||
|
* @param request Пустое сообщение (`google.protobuf.Empty`).
|
||||||
|
* @return `DeviceList` – массив `SerialDevice`.
|
||||||
|
*/
|
||||||
|
rpc ListDevices (google.protobuf.Empty) returns (DeviceList);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Connection handling
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Открывает (или переоткрывает) последовательный порт с указанными
|
||||||
|
* параметрами.
|
||||||
|
*
|
||||||
|
* @param cfg Полный набор параметров соединения (`SerialConfig`).
|
||||||
|
* @return `ConfigResponse` – статус операции.
|
||||||
|
*/
|
||||||
|
rpc Open (SerialConfig) returns (ConfigResponse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Закрывает текущий открытый порт (если он был открыт).
|
||||||
|
*
|
||||||
|
* @param request Пустое сообщение.
|
||||||
|
* @return `ConfigResponse` – статус операции.
|
||||||
|
*/
|
||||||
|
rpc Close (google.protobuf.Empty) returns (ConfigResponse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Возвращает конфигурацию, с которой в данный момент открыт порт.
|
||||||
|
*
|
||||||
|
* Если порт закрыт, возвращается пустой `SerialConfig`.
|
||||||
|
*
|
||||||
|
* @param request Пустое сообщение.
|
||||||
|
* @return `SerialConfig`.
|
||||||
|
*/
|
||||||
|
rpc GetCurrentConfig (google.protobuf.Empty) returns (SerialConfig);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Packet transmission
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Отправка одного пакета в открытый последовательный порт.
|
||||||
|
*
|
||||||
|
* Если в `SerialConfig.packet_size` задан фиксированный размер, то
|
||||||
|
* длина `payload` должна строго соответствовать этому размеру.
|
||||||
|
*
|
||||||
|
* @param pkt Пакет для отправки (`Packet`).
|
||||||
|
* @return `SendResponse` – результат записи в порт.
|
||||||
|
*/
|
||||||
|
rpc SendPacket (Packet) returns (SendResponse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получить один пакет из входящего буфера.
|
||||||
|
*
|
||||||
|
* Если буфер пуст, сервер возвращает gRPC‑ошибку `NOT_FOUND`.
|
||||||
|
*
|
||||||
|
* @param request Пустое сообщение.
|
||||||
|
* @return `Packet` (или ошибка).
|
||||||
|
*/
|
||||||
|
rpc ReceivePacket (google.protobuf.Empty) returns (Packet);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Logging / protocol verification
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Прямо вставить произвольную запись в журнал.
|
||||||
|
*
|
||||||
|
* Полезно для отладки: можно «симулировать» приходящие данные,
|
||||||
|
* не взаимодействуя с реальным оборудованием.
|
||||||
|
*
|
||||||
|
* @param entry Запись журнала (`SerialLogEntry`).
|
||||||
|
* @return Пустое сообщение (`google.protobuf.Empty`).
|
||||||
|
*/
|
||||||
|
rpc LogRaw (SerialLogEntry) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Подписка на поток всех событий журнала (TX и RX).
|
||||||
|
*
|
||||||
|
* Поток остаётся открытым до тех пор, пока клиент не отменит RPC.
|
||||||
|
*
|
||||||
|
* @param req Параметры подписки (`LogStreamRequest`).
|
||||||
|
* @return Поток `SerialLogEntry`.
|
||||||
|
*/
|
||||||
|
rpc StreamLog (LogStreamRequest) returns (stream SerialLogEntry);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user