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 (
"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()
}

View file

@ -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,
},
)

View file

@ -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
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
}