183 lines
4.9 KiB
Go
183 lines
4.9 KiB
Go
/*
|
|
* ============================================================================
|
|
* Projekt.....: rs2322tcp
|
|
* Datei.......: internal/client/virtual_port_manager.go
|
|
* Copyright (C) 2026 Dieter Lang
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Beschreibung:
|
|
* Verwaltung und Auswahl der für rs2322tcp reservierten virtuellen
|
|
* seriellen Ports.
|
|
*
|
|
* Der VirtualPortManager verwaltet den für rs2322tcp vorgesehenen Bereich
|
|
* virtueller serieller Schnittstellen.
|
|
*
|
|
* Die konkrete Darstellung eines virtuellen Ports ist plattformabhängig.
|
|
* Unter Linux werden beispielsweise /dev/ttyUSBxxx-Schnittstellen verwendet,
|
|
* während unter Windows COMxxx-Schnittstellen verwendet werden.
|
|
*
|
|
* Die eigentliche Erzeugung beziehungsweise Bereitstellung des seriellen
|
|
* Ports erfolgt ebenfalls plattformabhängig und ist nicht Bestandteil dieses
|
|
* Managers.
|
|
*
|
|
* Ports können entweder automatisch über den nächsten freien Port oder
|
|
* gezielt über eine vorgegebene Portnummer reserviert werden.
|
|
* ============================================================================
|
|
*/
|
|
|
|
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"git.lang-dieter.de/rs2322tcp/internal/config"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// VirtualPortManager
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// VirtualPortManager verwaltet den für rs2322tcp reservierten Bereich
|
|
// virtueller serieller Schnittstellen.
|
|
//
|
|
// Der reservierte Portbereich und die Belegungstabelle werden durch einen
|
|
// Mutex geschützt, da mehrere Bridges beziehungsweise Goroutinen gleichzeitig
|
|
// virtuelle Ports öffnen und freigeben können.
|
|
//
|
|
// Die konkrete Darstellung des sichtbaren Portnamens wird über
|
|
// virtualPortPath() von der jeweiligen Plattform bestimmt.
|
|
type VirtualPortManager struct {
|
|
mu sync.Mutex
|
|
|
|
first int
|
|
last int
|
|
used map[int]bool
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constructor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NewVirtualPortManager erzeugt einen VirtualPortManager aus dem
|
|
// konfigurierten Portbereich.
|
|
func NewVirtualPortManager(
|
|
cfg config.VirtualPortRangeConfig,
|
|
) *VirtualPortManager {
|
|
return &VirtualPortManager{
|
|
first: cfg.First,
|
|
last: cfg.Last,
|
|
used: make(map[int]bool),
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Port path
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// PortPath erzeugt den für die externe Software sichtbaren Gerätenamen
|
|
// für eine Portnummer.
|
|
//
|
|
// Die konkrete Darstellung ist plattformabhängig:
|
|
//
|
|
// Linux /dev/ttyUSB100
|
|
// Windows COM100
|
|
//
|
|
// Die Plattformimplementierung befindet sich in
|
|
// virtual_port_path_linux.go beziehungsweise virtual_port_path_windows.go.
|
|
func (m *VirtualPortManager) PortPath(number int) string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
|
|
return virtualPortPath(number)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Reservation
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Reserve reserviert den nächsten freien virtuellen Port.
|
|
//
|
|
// Es wird immer mit dem kleinsten freien Port im konfigurierten Bereich
|
|
// begonnen.
|
|
//
|
|
// Diese Methode bleibt für Fälle erhalten, in denen keine bestimmte
|
|
// Portnummer vorgegeben ist.
|
|
func (m *VirtualPortManager) Reserve() (int, error) {
|
|
if m == nil {
|
|
return 0, fmt.Errorf("virtual port manager is nil")
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
for number := m.first; number <= m.last; number++ {
|
|
if m.used[number] {
|
|
continue
|
|
}
|
|
|
|
m.used[number] = true
|
|
|
|
return number, nil
|
|
}
|
|
|
|
return 0, fmt.Errorf(
|
|
"no free virtual port in range %d-%d",
|
|
m.first,
|
|
m.last,
|
|
)
|
|
}
|
|
|
|
// ReserveSpecific reserviert exakt den angegebenen virtuellen Port.
|
|
//
|
|
// Der Port muss innerhalb des konfigurierten Bereichs liegen und darf
|
|
// momentan nicht bereits reserviert sein.
|
|
//
|
|
// Diese Methode wird für konfigurierte Zuordnungen verwendet, bei denen
|
|
// die Portnummer aus client.json verbindlich ist.
|
|
func (m *VirtualPortManager) ReserveSpecific(number int) error {
|
|
if m == nil {
|
|
return fmt.Errorf("virtual port manager is nil")
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if number < m.first || number > m.last {
|
|
return fmt.Errorf(
|
|
"virtual port %d is outside configured range %d-%d",
|
|
number,
|
|
m.first,
|
|
m.last,
|
|
)
|
|
}
|
|
|
|
if m.used[number] {
|
|
return fmt.Errorf(
|
|
"virtual port %d is already reserved",
|
|
number,
|
|
)
|
|
}
|
|
|
|
m.used[number] = true
|
|
|
|
return nil
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Release
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Release gibt einen zuvor reservierten Port wieder frei.
|
|
func (m *VirtualPortManager) Release(number int) {
|
|
if m == nil {
|
|
return
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
delete(m.used, number)
|
|
}
|