From 3a85b17422b39aa7f773299db0ae2854bff54711 Mon Sep 17 00:00:00 2001 From: Dieter Lang Date: Tue, 25 Aug 2026 17:12:35 +0200 Subject: [PATCH] Windows-Client mit funktionierender Fyne-GUI und Rotor-Startcode --- cmd/rs2322tcp-client/main.go | 24 ++-- internal/client/virtual_port_open_windows.go | 11 +- internal/client/virtual_serial_windows.go | 128 ++++++++++++++++--- 3 files changed, 134 insertions(+), 29 deletions(-) diff --git a/cmd/rs2322tcp-client/main.go b/cmd/rs2322tcp-client/main.go index 4713208..bfd1d9a 100644 --- a/cmd/rs2322tcp-client/main.go +++ b/cmd/rs2322tcp-client/main.go @@ -23,10 +23,11 @@ package main import ( "fmt" + "image/color" + "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/theme" - "image/color" "git.lang-dieter.de/rs2322tcp/internal/gui" "git.lang-dieter.de/rs2322tcp/internal/version" @@ -113,11 +114,6 @@ func main() { return } - // Das technische Client-System wird nach dem Erzeugen des Fensters - // asynchron gestartet. Dadurch bleibt die GUI auch während des - // Verbindungsaufbaus sofort sichtbar und bedienbar. - guiApplication.Start() - // SetOnClosed gehört zum Fyne-Fenster, nicht zur Fyne-Anwendung. // // Beim Schließen des Hauptfensters wird die technische Client- @@ -129,6 +125,16 @@ func main() { }, ) - // ShowAndRun zeigt das Fenster und startet den Fyne-Eventloop. - guiApplication.ShowAndRun() -} + // Zuerst das GUI-Fenster erzeugen und anzeigen. + // + // Dadurch wird ein möglicher Fehler beim Start von Fyne/GLFW erkannt, + // bevor die technische Client-Kommunikation gestartet wird. + guiApplication.Window().Show() + + // Erst nachdem das Fenster erfolgreich erzeugt wurde, wird das + // technische Client-System asynchron gestartet. + guiApplication.Start() + + // Den Fyne-Eventloop starten. + application.Run() +} \ No newline at end of file diff --git a/internal/client/virtual_port_open_windows.go b/internal/client/virtual_port_open_windows.go index 28a08bb..d62a641 100644 --- a/internal/client/virtual_port_open_windows.go +++ b/internal/client/virtual_port_open_windows.go @@ -11,8 +11,8 @@ * Beschreibung: * Windows-spezifische Erzeugung eines verwalteten virtuellen rs2322tcp-Ports. * - * Der konfigurierte virtuelle COM-Port wird über die bereits vorhandene - * serielle Infrastruktur geöffnet. + * Der konfigurierte virtuelle COM-Port wird einmal geöffnet und anschließend + * über dasselbe Handle für Lese- und Schreiboperationen verwendet. * ============================================================================ */ @@ -36,6 +36,9 @@ import ( // The visible COM port is provided by the platform-specific PortPath() // implementation. The actual COM-port pair configuration is managed // externally by the Windows virtual COM-port driver. +// +// The COM port is opened exactly once. The resulting handle is used for +// parallel read and write operations. func openVirtualPort( manager *VirtualPortManager, number int, @@ -59,7 +62,7 @@ func openVirtualPort( StopBits: bugserial.OneStopBit, } - serial, err := bugserial.Open(portPath, mode) + port, err := bugserial.Open(portPath, mode) if err != nil { return nil, fmt.Errorf( "open virtual serial port %s: %w", @@ -71,7 +74,7 @@ func openVirtualPort( virtualPort := NewVirtualPort( portPath, &windowsSerial{ - port: serial, + port: port, path: portPath, }, ) diff --git a/internal/client/virtual_serial_windows.go b/internal/client/virtual_serial_windows.go index 4f8440b..50fdc30 100644 --- a/internal/client/virtual_serial_windows.go +++ b/internal/client/virtual_serial_windows.go @@ -12,8 +12,9 @@ * Windows-spezifische Implementierung der virtuellen seriellen Schnittstelle * über go.bug.st/serial. * - * Der konfigurierte COM-Port wird direkt geöffnet. Die Zuordnung eines - * virtuellen COM-Port-Paares erfolgt außerhalb des rs2322tcp-Clients. + * Ein COM-Port wird unter Windows nur einmal geöffnet. Dasselbe Handle wird + * parallel für Lese- und Schreiboperationen verwendet. Damit kann eine + * Goroutine blockierend lesen, während eine andere Goroutine Daten schreibt. * ============================================================================ */ @@ -30,9 +31,11 @@ import ( // windowsSerial /////////////////////////////////////////////////////////////////////////////// -// windowsSerial implements VirtualSerial using a Windows COM port. +// windowsSerial implements VirtualSerial using one Windows COM-port handle +// for both reading and writing. type windowsSerial struct { - mu sync.Mutex + mu sync.Mutex + port bugserial.Port path string } @@ -51,10 +54,10 @@ func (p *windowsSerial) Path() string { } /////////////////////////////////////////////////////////////////////////////// -// Read / Write +// Read /////////////////////////////////////////////////////////////////////////////// -// Read reads raw bytes from the COM port. +// Read reads raw bytes from the Windows COM-port handle. func (p *windowsSerial) Read(b []byte) (int, error) { if p == nil { return 0, fmt.Errorf("virtual serial port is nil") @@ -62,16 +65,41 @@ func (p *windowsSerial) Read(b []byte) (int, error) { p.mu.Lock() port := p.port + path := p.path p.mu.Unlock() if port == nil { return 0, fmt.Errorf("virtual serial port is closed") } - return port.Read(b) + n, err := port.Read(b) + + if n > 0 { + fmt.Printf( + "WINDOWS SERIAL RX [%s]: %d Bytes\n% X\n", + path, + n, + b[:n], + ) + } + + if err != nil { + fmt.Printf( + "WINDOWS SERIAL RX FEHLER [%s]: nach %d Bytes: %v\n", + path, + n, + err, + ) + } + + return n, err } -// Write writes raw bytes to the COM port. +/////////////////////////////////////////////////////////////////////////////// +// Write +/////////////////////////////////////////////////////////////////////////////// + +// Write writes raw bytes to the Windows COM-port handle. func (p *windowsSerial) Write(b []byte) (int, error) { if p == nil { return 0, fmt.Errorf("virtual serial port is nil") @@ -79,20 +107,67 @@ func (p *windowsSerial) Write(b []byte) (int, error) { p.mu.Lock() port := p.port + path := p.path p.mu.Unlock() if port == nil { return 0, fmt.Errorf("virtual serial port is closed") } - return port.Write(b) + fmt.Printf( + "WINDOWS SERIAL TX [%s]: %d Bytes\n% X\n", + path, + len(b), + b, + ) + + fmt.Printf( + "WINDOWS SERIAL TX [%s]: Rufe port.Write() auf ...\n", + path, + ) + + n, err := port.Write(b) + + fmt.Printf( + "WINDOWS SERIAL TX [%s]: port.Write() zurückgekehrt: %d von %d Bytes\n", + path, + n, + len(b), + ) + + if err != nil { + fmt.Printf( + "WINDOWS SERIAL TX FEHLER [%s]: %v\n", + path, + err, + ) + + return n, err + } + + if n != len(b) { + fmt.Printf( + "WINDOWS SERIAL TX UNVOLLSTÄNDIG [%s]: %d von %d Bytes\n", + path, + n, + len(b), + ) + } else { + fmt.Printf( + "WINDOWS SERIAL TX ERFOLGREICH [%s]: %d Bytes\n", + path, + n, + ) + } + + return n, nil } /////////////////////////////////////////////////////////////////////////////// // Close /////////////////////////////////////////////////////////////////////////////// -// Close closes the Windows COM port. +// Close closes the Windows COM-port handle. func (p *windowsSerial) Close() error { if p == nil { return nil @@ -100,15 +175,36 @@ func (p *windowsSerial) Close() error { p.mu.Lock() - if p.port == nil { - p.mu.Unlock() - return nil - } - port := p.port + path := p.path + p.port = nil p.mu.Unlock() - return port.Close() + if port == nil { + return nil + } + + fmt.Printf( + "WINDOWS SERIAL [%s]: Schließe COM-Port ...\n", + path, + ) + + if err := port.Close(); err != nil { + fmt.Printf( + "WINDOWS SERIAL [%s]: Fehler beim Schließen des COM-Ports: %v\n", + path, + err, + ) + + return err + } + + fmt.Printf( + "WINDOWS SERIAL [%s]: COM-Port geschlossen.\n", + path, + ) + + return nil }