//go:build windows /* * ============================================================================ * Projekt.....: rs2322tcp * Datei.......: internal/client/virtual_serial_windows.go * Copyright (C) 2026 Dieter Lang * * SPDX-License-Identifier: GPL-3.0-or-later * * Beschreibung: * Windows-spezifische Implementierung der virtuellen seriellen Schnittstelle * über go.bug.st/serial. * * 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. * ============================================================================ */ package client import ( "fmt" "sync" bugserial "go.bug.st/serial" ) /////////////////////////////////////////////////////////////////////////////// // windowsSerial /////////////////////////////////////////////////////////////////////////////// // 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 } /////////////////////////////////////////////////////////////////////////////// // Path /////////////////////////////////////////////////////////////////////////////// // Path returns the Windows COM-port name. func (p *windowsSerial) Path() string { if p == nil { return "" } return p.path } /////////////////////////////////////////////////////////////////////////////// // Read /////////////////////////////////////////////////////////////////////////////// // 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") } p.mu.Lock() port := p.port path := p.path p.mu.Unlock() if port == nil { return 0, fmt.Errorf("virtual serial port is closed") } 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 /////////////////////////////////////////////////////////////////////////////// // 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") } p.mu.Lock() port := p.port path := p.path p.mu.Unlock() if port == nil { return 0, fmt.Errorf("virtual serial port is closed") } 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 handle. func (p *windowsSerial) Close() error { if p == nil { return nil } p.mu.Lock() port := p.port path := p.path p.port = nil p.mu.Unlock() 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 }