154 lines
3.7 KiB
Go
154 lines
3.7 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
|
|
* von /dev/ttyUSBxxx-Schnittstellen.
|
|
*
|
|
* Ports können entweder automatisch über den nächsten freien Port oder
|
|
* gezielt über eine vorgegebene Portnummer reserviert werden.
|
|
*
|
|
* Die gezielte Reservierung ist insbesondere für die aus client.json
|
|
* geladene Konfiguration erforderlich. Dadurch bleibt eine Zuordnung wie
|
|
*
|
|
* /dev/ttyUSB100 -> radio
|
|
*
|
|
* auch technisch exakt erhalten.
|
|
* ============================================================================
|
|
*/
|
|
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"git.lang-dieter.de/rs2322tcp/internal/config"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// VirtualPortManager
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// VirtualPortManager verwaltet den reservierten Bereich virtueller
|
|
// /dev/ttyUSBxxx-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.
|
|
type VirtualPortManager struct {
|
|
mu sync.Mutex
|
|
|
|
first int
|
|
last int
|
|
used map[int]bool
|
|
}
|
|
|
|
// 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),
|
|
}
|
|
}
|
|
|
|
// PortPath erzeugt den für die externe Software sichtbaren Gerätenamen
|
|
// für eine Portnummer.
|
|
func (m *VirtualPortManager) PortPath(number int) string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
|
|
return fmt.Sprintf("/dev/ttyUSB%d", number)
|
|
}
|
|
|
|
// 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 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)
|
|
}
|