rs2322tcp/internal/client/connection.go

124 lines
3.2 KiB
Go

/*
* ============================================================================
* Projekt.....: rs2322tcp
* Datei.......: internal/client/connection.go
* Copyright (C) 2026 Dieter Lang
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Beschreibung:
* Aufbau einer Verbindung zwischen einem lokalen virtuellen seriellen Port
* und dem zugehörigen entfernten Gerät.
*
* Die konfigurierte lokale virtuelle Schnittstelle wird dabei exakt
* übernommen.
* ============================================================================
*/
package client
import (
"fmt"
"net"
"git.lang-dieter.de/rs2322tcp/internal/config"
"git.lang-dieter.de/rs2322tcp/internal/transport"
)
///////////////////////////////////////////////////////////////////////////////
// Virtual port connection
///////////////////////////////////////////////////////////////////////////////
// connectVirtualPort opens the configured local virtual port and the
// corresponding remote TCP data connection and returns both endpoints.
//
// The caller owns the returned virtual port and TCP connection.
//
// The local virtual port is opened using exactly the port number specified
// in virtualPortConfig.Port. This preserves the user's configured mapping
// between local virtual interfaces and remote devices.
func connectVirtualPort(
c *Client,
manager *VirtualPortManager,
virtualPortConfig config.VirtualPortConfig,
devices []transport.RemoteDeviceInfo,
) (*ManagedVirtualPort, net.Conn, error) {
if c == nil || c.conn == nil {
return nil, nil, fmt.Errorf("client is not connected")
}
if manager == nil {
return nil, nil, fmt.Errorf("virtual port manager is nil")
}
device, err := findRemoteDevice(virtualPortConfig, devices)
if err != nil {
return nil, nil, err
}
portNumber, err := virtualPortNumber(virtualPortConfig.Port)
if err != nil {
return nil, nil, err
}
virtualPort, err := manager.OpenSpecific(portNumber)
if err != nil {
return nil, nil, fmt.Errorf(
"open virtual port %q for %q: %w",
virtualPortConfig.Port,
virtualPortConfig.RemoteDevice,
err,
)
}
conn, err := c.OpenDataConnection(device)
if err != nil {
_ = virtualPort.Close()
return nil, nil, fmt.Errorf(
"open data connection for %q: %w",
device.ID,
err,
)
}
return virtualPort, conn, nil
}
///////////////////////////////////////////////////////////////////////////////
// Bridge connection
///////////////////////////////////////////////////////////////////////////////
// connectBridge creates the local virtual port, opens the corresponding
// remote TCP data connection and creates the data bridge.
//
// The caller owns the returned bridge and its endpoints.
func connectBridge(
c *Client,
manager *VirtualPortManager,
virtualPortConfig config.VirtualPortConfig,
devices []transport.RemoteDeviceInfo,
) (*Bridge, *ManagedVirtualPort, net.Conn, error) {
virtualPort, conn, err := connectVirtualPort(
c,
manager,
virtualPortConfig,
devices,
)
if err != nil {
return nil, nil, nil, err
}
bridge, err := NewBridge(virtualPort, conn)
if err != nil {
_ = conn.Close()
_ = virtualPort.Close()
return nil, nil, nil, fmt.Errorf(
"create bridge for %q: %w",
virtualPortConfig.RemoteDevice,
err,
)
}
return bridge, virtualPort, conn, nil
}