Windows-Client mit funktionierender Fyne-GUI und Rotor-Startcode

This commit is contained in:
Dieter Lang 2026-08-25 17:12:35 +02:00
parent c9e3ba052a
commit 3a85b17422
3 changed files with 134 additions and 29 deletions

View file

@ -23,10 +23,11 @@ package main
import ( import (
"fmt" "fmt"
"image/color"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"image/color"
"git.lang-dieter.de/rs2322tcp/internal/gui" "git.lang-dieter.de/rs2322tcp/internal/gui"
"git.lang-dieter.de/rs2322tcp/internal/version" "git.lang-dieter.de/rs2322tcp/internal/version"
@ -113,11 +114,6 @@ func main() {
return 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. // SetOnClosed gehört zum Fyne-Fenster, nicht zur Fyne-Anwendung.
// //
// Beim Schließen des Hauptfensters wird die technische Client- // Beim Schließen des Hauptfensters wird die technische Client-
@ -129,6 +125,16 @@ func main() {
}, },
) )
// ShowAndRun zeigt das Fenster und startet den Fyne-Eventloop. // Zuerst das GUI-Fenster erzeugen und anzeigen.
guiApplication.ShowAndRun() //
} // 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()
}

View file

@ -11,8 +11,8 @@
* Beschreibung: * Beschreibung:
* Windows-spezifische Erzeugung eines verwalteten virtuellen rs2322tcp-Ports. * Windows-spezifische Erzeugung eines verwalteten virtuellen rs2322tcp-Ports.
* *
* Der konfigurierte virtuelle COM-Port wird über die bereits vorhandene * Der konfigurierte virtuelle COM-Port wird einmal geöffnet und anschließend
* serielle Infrastruktur geöffnet. * ü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() // The visible COM port is provided by the platform-specific PortPath()
// implementation. The actual COM-port pair configuration is managed // implementation. The actual COM-port pair configuration is managed
// externally by the Windows virtual COM-port driver. // 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( func openVirtualPort(
manager *VirtualPortManager, manager *VirtualPortManager,
number int, number int,
@ -59,7 +62,7 @@ func openVirtualPort(
StopBits: bugserial.OneStopBit, StopBits: bugserial.OneStopBit,
} }
serial, err := bugserial.Open(portPath, mode) port, err := bugserial.Open(portPath, mode)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"open virtual serial port %s: %w", "open virtual serial port %s: %w",
@ -71,7 +74,7 @@ func openVirtualPort(
virtualPort := NewVirtualPort( virtualPort := NewVirtualPort(
portPath, portPath,
&windowsSerial{ &windowsSerial{
port: serial, port: port,
path: portPath, path: portPath,
}, },
) )

View file

@ -12,8 +12,9 @@
* Windows-spezifische Implementierung der virtuellen seriellen Schnittstelle * Windows-spezifische Implementierung der virtuellen seriellen Schnittstelle
* über go.bug.st/serial. * über go.bug.st/serial.
* *
* Der konfigurierte COM-Port wird direkt geöffnet. Die Zuordnung eines * Ein COM-Port wird unter Windows nur einmal geöffnet. Dasselbe Handle wird
* virtuellen COM-Port-Paares erfolgt außerhalb des rs2322tcp-Clients. * 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
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// 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 { type windowsSerial struct {
mu sync.Mutex mu sync.Mutex
port bugserial.Port port bugserial.Port
path string 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) { func (p *windowsSerial) Read(b []byte) (int, error) {
if p == nil { if p == nil {
return 0, fmt.Errorf("virtual serial port is 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() p.mu.Lock()
port := p.port port := p.port
path := p.path
p.mu.Unlock() p.mu.Unlock()
if port == nil { if port == nil {
return 0, fmt.Errorf("virtual serial port is closed") 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) { func (p *windowsSerial) Write(b []byte) (int, error) {
if p == nil { if p == nil {
return 0, fmt.Errorf("virtual serial port is 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() p.mu.Lock()
port := p.port port := p.port
path := p.path
p.mu.Unlock() p.mu.Unlock()
if port == nil { if port == nil {
return 0, fmt.Errorf("virtual serial port is closed") 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
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Close closes the Windows COM port. // Close closes the Windows COM-port handle.
func (p *windowsSerial) Close() error { func (p *windowsSerial) Close() error {
if p == nil { if p == nil {
return nil return nil
@ -100,15 +175,36 @@ func (p *windowsSerial) Close() error {
p.mu.Lock() p.mu.Lock()
if p.port == nil {
p.mu.Unlock()
return nil
}
port := p.port port := p.port
path := p.path
p.port = nil p.port = nil
p.mu.Unlock() 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
} }