From 77cc14fa49dc35b46017b42edb3e197150156a2b Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Tue, 6 Jan 2026 21:24:47 +0800 Subject: [PATCH] it works --- BasaltPresenter/Presenter/command_client.cpp | 89 +++++++++---------- BasaltPresenter/Presenter/command_client.hpp | 58 +++++++----- BasaltPresenter/Presenter/main.cpp | 11 ++- .../Shared/basalt/pipe_operator.hpp | 1 + BasaltTrainer/command_server.py | 53 ++++++----- BasaltTrainer/main.py | 27 +++++- BasaltTrainer/pipe_operator.py | 4 - Document/PROTOCOL.md | 5 +- 8 files changed, 148 insertions(+), 100 deletions(-) diff --git a/BasaltPresenter/Presenter/command_client.cpp b/BasaltPresenter/Presenter/command_client.cpp index 83a48c7..d2ffbf0 100644 --- a/BasaltPresenter/Presenter/command_client.cpp +++ b/BasaltPresenter/Presenter/command_client.cpp @@ -1,77 +1,68 @@ #include "command_client.hpp" +#include #include namespace Basalt::Presenter { - CommandClient::CommandClient(const std::basic_string_view pipe_name) - : m_PipeOperator(pipe_name) { - } + CommandClient::CommandClient() : m_PipeOperator(BSTEXT("ed0e3f1f-d214-4880-9562-640bce15e72e")), m_Status(ClientStatus::Ready) {} - CommandClient::~CommandClient() { - } + CommandClient::~CommandClient() {} - void CommandClient::WaitHandshake(std::uint8_t pixel_kind, std::uint32_t width, std::uint32_t height) { - if (m_Handshaked) { - throw std::runtime_error("Handshake already completed"); + HandshakePayload CommandClient::WaitHandshake() { + if (m_Status != ClientStatus::Ready) { + throw std::runtime_error("unexcpected client status"); } // Wait for handshake request from Trainer (code 0x61) - std::uint8_t received_code; - m_PipeOperator.Read(&received_code, sizeof(received_code)); - - if (received_code != HANDSHAKE_CODE_REQUEST) { - throw std::runtime_error("Expected handshake code 0x61, got 0x" + - std::to_string(static_cast(received_code))); + ProtocolCode request_code; + m_PipeOperator.Read(&request_code, sizeof(request_code)); + if (request_code != ProtocolCode::HANDSHAKE_REQUEST) { + throw std::runtime_error("unexpcted handshake code"); } + // Accept payload + HandshakePayload handshake_payload; + m_PipeOperator.Read(&handshake_payload, sizeof(HandshakePayload)); // Send handshake response (code 0x62) back to Trainer - std::uint8_t handshake_response = HANDSHAKE_CODE_RESPONSE; - m_PipeOperator.Write(&handshake_response, sizeof(handshake_response)); + ProtocolCode response_code = ProtocolCode::HANDSHAKE_RESPONSE; + m_PipeOperator.Write(&response_code, sizeof(response_code)); - // Send data properties after handshake - m_PipeOperator.Write(&pixel_kind, sizeof(pixel_kind)); - m_PipeOperator.Write(&width, sizeof(width)); - m_PipeOperator.Write(&height, sizeof(height)); - - m_Handshaked = true; + // Set status and return + m_Status = ClientStatus::Running; + return handshake_payload; } bool CommandClient::Tick(bool actively_stop) { - if (!m_Handshaked) { - throw std::runtime_error("Handshake must be completed before calling Tick"); + if (m_Status != ClientStatus::Running) { + throw std::runtime_error("unexcpected client status"); } - // If actively_stop is true, send actively stop code to Trainer + // If actively stop, send actively stop code to Trainer first if (actively_stop) { - std::uint8_t stop_code = ACTIVELY_STOP_CODE; - m_PipeOperator.Write(&stop_code, sizeof(stop_code)); + ProtocolCode sent_code = ProtocolCode::STOP_REQUEST; + m_PipeOperator.Write(&sent_code, sizeof(sent_code)); } // Send data ready code to Trainer - std::uint8_t data_ready_code = DATA_READY_CODE; - m_PipeOperator.Write(&data_ready_code, sizeof(data_ready_code)); + ProtocolCode sent_code = ProtocolCode::DATA_READY; + m_PipeOperator.Write(&sent_code, sizeof(sent_code)); - // Wait for response from Trainer - std::uint8_t received_code; - m_PipeOperator.Read(&received_code, sizeof(received_code)); + // Process the response from Trainer + while (true) { + ProtocolCode recv_code; + m_PipeOperator.Read(&recv_code, sizeof(recv_code)); - // Handle the received code - if (received_code == DATA_RECEIVED_CODE) { - // Normal response, continue processing - return false; // Not stopping - } else if (received_code == STOP_CODE) { - // Trainer wants to stop - return true; // Should stop - } else if (received_code == HANDSHAKE_CODE_REQUEST || received_code == HANDSHAKE_CODE_RESPONSE) { - // Unexpected handshake code during Tick - throw std::runtime_error("Unexpected handshake code 0x" + - std::to_string(static_cast(received_code)) + - " received during Tick operation"); - } else { - // Unknown code - throw std::runtime_error("Unknown code 0x" + - std::to_string(static_cast(received_code)) + - " received during Tick operation"); + switch (recv_code) { + case ProtocolCode::DATA_RECEIVED: + // Normal response, continue processing + return false; // Not stopping + case Basalt::Presenter::ProtocolCode::STOP: + // Trainer wants to stop + m_Status = ClientStatus::Stop; + return true; // Should stop + default: + throw std::runtime_error("unexpected code when running"); + } } } diff --git a/BasaltPresenter/Presenter/command_client.hpp b/BasaltPresenter/Presenter/command_client.hpp index 2530ffe..122d4ee 100644 --- a/BasaltPresenter/Presenter/command_client.hpp +++ b/BasaltPresenter/Presenter/command_client.hpp @@ -1,38 +1,56 @@ #pragma once - #include #include namespace Basalt::Presenter { + // Protocol codes + enum class ProtocolCode : std::uint8_t { + HANDSHAKE_REQUEST = 0x61, //< Trainer -> Presenter + HANDSHAKE_RESPONSE = 0x62, //< Presenter -> Trainer + DATA_READY = 0x01, //< Presenter -> Trainer + DATA_RECEIVED = 0x02, //< Trainer -> Presenter + STOP_REQUEST = 0x71, //< Both directions + STOP = 0x71 //< Both directions (same code) + }; + + // Pixel kind values + enum class PixelKind : std::uint8_t { + GRAY_FLOAT32 = 0x01, ///< Grayscale represented by one float32 + GRAY_U8 = 0x02, ///< Grayscale represented by one u8 + RGB_FLOAT32 = 0x03, ///< RGB represented by three float32 + RGB_U8 = 0x04 ///< RGB represented by three u8 + }; + +#pragma pack(push, 1) + struct HandshakePayload { + PixelKind pixel_kind; + std::uint32_t width; + std::uint32_t height; + }; +#pragma pack(pop) + + // Status + enum class ClientStatus { + Ready, + Running, + Stop, + }; + class CommandClient { public: - // Protocol codes - static constexpr std::uint8_t HANDSHAKE_CODE_REQUEST = 0x61; // Trainer -> Presenter - static constexpr std::uint8_t HANDSHAKE_CODE_RESPONSE = 0x62; // Presenter -> Trainer - static constexpr std::uint8_t DATA_READY_CODE = 0x01; // Presenter -> Trainer - static constexpr std::uint8_t DATA_RECEIVED_CODE = 0x02; // Trainer -> Presenter - static constexpr std::uint8_t ACTIVELY_STOP_CODE = 0x71; // Both directions - static constexpr std::uint8_t STOP_CODE = 0x71; // Both directions (same code) - - // Pixel kind values - static constexpr std::uint8_t PIXEL_GRAY_FLOAT32 = 0x01; // Grayscale represented by one float32 - static constexpr std::uint8_t PIXEL_GRAY_U8 = 0x02; // Grayscale represented by one u8 - static constexpr std::uint8_t PIXEL_RGB_FLOAT32 = 0x03; // RGB represented by three float32 - static constexpr std::uint8_t PIXEL_RGB_U8 = 0x04; // RGB represented by three u8 - - CommandClient(const std::basic_string_view pipe_name = BSTEXT("ed0e3f1f-d214-4880-9562-640bce15e72e")); + CommandClient(); ~CommandClient(); + public: // Wait for handshake from Trainer, send response with data properties - void WaitHandshake(std::uint8_t pixel_kind, std::uint32_t width, std::uint32_t height); - + HandshakePayload WaitHandshake(); // Tick function called every frame to send data ready and wait for response bool Tick(bool actively_stop = false); private: - Basalt::Shared::PipeOperator m_PipeOperator; - bool m_Handshaked = false; // Track handshake state + Shared::PipeOperator m_PipeOperator; + ClientStatus m_Status; }; } // namespace Basalt::Presenter \ No newline at end of file diff --git a/BasaltPresenter/Presenter/main.cpp b/BasaltPresenter/Presenter/main.cpp index 81588b9..1bac1fd 100644 --- a/BasaltPresenter/Presenter/main.cpp +++ b/BasaltPresenter/Presenter/main.cpp @@ -1,4 +1,5 @@ #include "dll_loader.hpp" +#include "command_client.hpp" #include #include @@ -8,14 +9,20 @@ namespace Kernel = ::Basalt::Shared::Kernel; int main(int argc, char* argv[]) { auto engine_dll = Presenter::DllLoader(Presenter::DllKind::Engine, BSTEXT("BasaltDirectX11Engine")); auto deliver_dll = Presenter::DllLoader(Presenter::DllKind::Deliver, BSTEXT("BasaltPipeDeliver")); + + auto client = Presenter::CommandClient(); + auto payload = client.WaitHandshake(); + auto* engine = engine_dll.CreateInstance(); auto* deliver = deliver_dll.CreateInstance(); - Kernel::EngineConfig engine_config{.headless = false, .title = BSTEXT("Fuck You"), .width = 800, .height = 600}; + Kernel::EngineConfig engine_config{.headless = false, .title = BSTEXT("Fuck You"), .width = payload.width, .height = payload.height}; engine->Startup(std::move(engine_config)); while (true) { - if (engine->Tick()) break; + auto req_stop = engine->Tick(); + auto can_stop = client.Tick(req_stop); + if (can_stop) break; } engine->Shutdown(); diff --git a/BasaltPresenter/Shared/basalt/pipe_operator.hpp b/BasaltPresenter/Shared/basalt/pipe_operator.hpp index f2b51e7..c635b5a 100644 --- a/BasaltPresenter/Shared/basalt/pipe_operator.hpp +++ b/BasaltPresenter/Shared/basalt/pipe_operator.hpp @@ -1,3 +1,4 @@ +#pragma once #include "char_types.hpp" #include diff --git a/BasaltTrainer/command_server.py b/BasaltTrainer/command_server.py index 5045bcb..77b6783 100644 --- a/BasaltTrainer/command_server.py +++ b/BasaltTrainer/command_server.py @@ -1,11 +1,12 @@ import struct from enum import IntEnum, Enum, auto from dataclasses import dataclass -from typing import Tuple, Optional +from typing import Callable from pipe_operator import PipeOperator PIPE_NAME: str = "ed0e3f1f-d214-4880-9562-640bce15e72e" + class ProtocolCode(IntEnum): HANDSHAKE_REQUEST = 0x61 # Trainer -> Presenter HANDSHAKE_RESPONSE = 0x62 # Presenter -> Trainer @@ -14,18 +15,21 @@ class ProtocolCode(IntEnum): REQUEST_STOP = 0x71 # Presenter -> Trainer (request stop) STOP = 0x71 # Trainer -> Presenter (confirm stop) + class PixelKind(IntEnum): GRAY_FLOAT32 = 0x01 # Grayscale represented by one float32 GRAY_U8 = 0x02 # Grayscale represented by one u8 RGB_FLOAT32 = 0x03 # RGB represented by three float32 RGB_U8 = 0x04 # RGB represented by three u8 + @dataclass -class ImageProperties: +class HandshakePayload: pixel_kind: PixelKind width: int height: int + class ServerStatus(Enum): Ready = auto() Running = auto() @@ -33,7 +37,8 @@ class ServerStatus(Enum): CODE_PACKER: struct.Struct = struct.Struct("=B") -HANDSHAKE_RESPONSE_PACKER: struct.Struct = struct.Struct("=BII") +HANDSHAKE_REQUEST_PACKER: struct.Struct = struct.Struct("=BII") + class CommandServer: """ @@ -41,71 +46,75 @@ class CommandServer: """ pipe_operator: PipeOperator - statue: ServerStatus + status: ServerStatus def __init__(self): self.pipe_operator = PipeOperator(PIPE_NAME) - self.handshaked = ServerStatus.Ready + self.status = ServerStatus.Ready def __del__(self): """Cleanup resources when object is destroyed.""" self.pipe_operator.close() - def wait_handshake(self) -> ImageProperties: + def wait_handshake(self, payload: HandshakePayload) -> None: """ Wait for handshake from Presenter, send request first and wait for response with data properties. Returns a tuple of (pixel_kind, width, height) from the Presenter. """ - if self.statue != ServerStatus.Ready: + if self.status != ServerStatus.Ready: raise RuntimeError("unexpected server status") # Send handshake request to Presenter (code 0x61) - self.pipe_operator.write(CODE_PACKER.pack(ProtocolCode.HANDSHAKE_REQUEST)) + self.pipe_operator.write( + CODE_PACKER.pack(ProtocolCode.HANDSHAKE_REQUEST)) + # And the payload data + self.pipe_operator.write( + HANDSHAKE_REQUEST_PACKER.pack(payload.pixel_kind, payload.width, + payload.height)) # Wait for handshake response from Presenter (code 0x62) code_bytes = self.pipe_operator.read(CODE_PACKER.size) (code, ) = CODE_PACKER.unpack(code_bytes) if ProtocolCode(code) != ProtocolCode.HANDSHAKE_RESPONSE: - raise RuntimeError("expect handshake response code, but got another") - - # Read data properties from Presenter (pixel_kind, width, height) - handshake_response_payload = self.pipe_operator.read(HANDSHAKE_RESPONSE_PACKER.size) - (raw_pixel_kind, width, height) = HANDSHAKE_RESPONSE_PACKER.unpack(handshake_response_payload) + raise RuntimeError( + "expect handshake response code, but got another") # Set status and return - self.statue = ServerStatus.Running - return ImageProperties(PixelKind(raw_pixel_kind), width, height) + self.status = ServerStatus.Running + return - def tick(self, request_stop: bool) -> bool: + def tick(self, data_receiver: Callable[[], None], + request_stop: bool) -> bool: """ Tick function called every frame to wait for data ready from Presenter and send response. Returns True if a stop code was received (meaning the process should stop), False otherwise. """ - if self.statue != ServerStatus.Running: + if self.status != ServerStatus.Running: raise RuntimeError("unexpected server status") # If there is stop requested, we post it first and return if request_stop: self.pipe_operator.write(CODE_PACKER.pack(ProtocolCode.STOP)) - self.statue = ServerStatus.Stop + self.status = ServerStatus.Stop return True # Wait for code from Presenter code_bytes = self.pipe_operator.read(CODE_PACKER.size) (code, ) = CODE_PACKER.unpack(code_bytes) - + # Analyse code match ProtocolCode(code): case ProtocolCode.DATA_READY: # Receive data - print('Data received') + data_receiver() # Send data received symbol - self.pipe_operator.write(CODE_PACKER.pack(ProtocolCode.DATA_RECEIVED)) + self.pipe_operator.write( + CODE_PACKER.pack(ProtocolCode.DATA_RECEIVED)) case ProtocolCode.REQUEST_STOP: # Presenter requested stop. # Agree with it, send code and return. self.pipe_operator.write(CODE_PACKER.pack(ProtocolCode.STOP)) - self.statue = ServerStatus.Stop + self.status = ServerStatus.Stop return True case _: raise RuntimeError("unexpected protocol code when running") diff --git a/BasaltTrainer/main.py b/BasaltTrainer/main.py index e30b506..66415ce 100644 --- a/BasaltTrainer/main.py +++ b/BasaltTrainer/main.py @@ -1,6 +1,31 @@ +from command_server import CommandServer, HandshakePayload, PixelKind +import logging + + +def receive_data() -> None: + logging.info('Data received') + + def main(): - print("Hello from basalt-trainer!") + server = CommandServer() + + print( + 'Please launch BasaltPresenter now and then press Enter to continue.') + input() + + logging.info('Waiting BasaltPresenter...') + server.wait_handshake(HandshakePayload(PixelKind.GRAY_U8, 600, 600)) + + logging.info('Start to running.') + while True: + if server.tick(receive_data, False): + break + + logging.info('Program stop.') if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG, + format='[%(asctime)s] [%(levelname)s] %(message)s', + datefmt='%Y-%m-%d %H:%M:%S') main() diff --git a/BasaltTrainer/pipe_operator.py b/BasaltTrainer/pipe_operator.py index 1cc9deb..a3470d8 100644 --- a/BasaltTrainer/pipe_operator.py +++ b/BasaltTrainer/pipe_operator.py @@ -45,9 +45,6 @@ class PipeOperator: ) if self.pipe_handle == win32file.INVALID_HANDLE_VALUE: raise RuntimeError("Failed to create named pipe.") - - # Wait for client to connect - win32pipe.ConnectNamedPipe(self.pipe_handle, None) else: # POSIX implementation self.pipe_name = f"/tmp/{name}" @@ -158,4 +155,3 @@ class PipeOperator: total_written += bytes_written except OSError as e: raise RuntimeError(f"Failed to write to named pipe: {e}") - diff --git a/Document/PROTOCOL.md b/Document/PROTOCOL.md index c5f9a9c..f3abe63 100644 --- a/Document/PROTOCOL.md +++ b/Document/PROTOCOL.md @@ -29,8 +29,9 @@ At the beginning of execution, Trainer send handshake code to Presenter first and then Presenter send another handshake code to Trainer back. After this, both 2 applications start running. -When Presenter send handshake code back, -Presenter should attach some values following it to indicate some essential properties of data which will be passed to Trainer in future. +When Trainer send handshake code, +Trainer should attach some values following it to indicate some essential properties of data +which instruct Representer how to generate data. There is a table introduce these properties: |Data Type|Comment|