210 lines
4 KiB
Go
210 lines
4 KiB
Go
//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
|
|
}
|