/* * ============================================================================ * Projekt.....: rs2322tcp * Datei.......: data_handler.go * Copyright (C) 2026 Dieter Lang * * SPDX-License-Identifier: GPL-3.0-or-later * * Beschreibung: * Verbindet eingehende TCP-Datenverbindungen eines Data-Listeners mit * einem vom Server bereitgestellten seriellen Gerätekanal. * ============================================================================ */ package server import ( "fmt" "io" "log" "net" ) /////////////////////////////////////////////////////////////////////////////// // SerialFactory /////////////////////////////////////////////////////////////////////////////// // SerialFactory creates a serial connection for one device. // // The concrete implementation is provided by the serial package later. // Keeping the factory as a function type prevents the server package from // depending directly on the concrete serial implementation. type SerialFactory func() (io.ReadWriteCloser, error) /////////////////////////////////////////////////////////////////////////////// // DataHandler /////////////////////////////////////////////////////////////////////////////// // DataHandler accepts TCP data connections and connects them to a serial // device. type DataHandler struct { listener *DataListener serialFactory SerialFactory hardwareErrorResponse string } /////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////// // NewDataHandler creates a new data handler. // // The listener provides the TCP endpoint. The serial factory is called for // every accepted TCP connection. func NewDataHandler( listener *DataListener, serialFactory SerialFactory, hardwareErrorResponse string, ) (*DataHandler, error) { if listener == nil { return nil, fmt.Errorf("data listener is nil") } if serialFactory == nil { return nil, fmt.Errorf("serial factory is nil") } if hardwareErrorResponse == "" { return nil, fmt.Errorf("hardware error response is empty") } return &DataHandler{ listener: listener, serialFactory: serialFactory, hardwareErrorResponse: hardwareErrorResponse, }, nil } /////////////////////////////////////////////////////////////////////////////// // Serve /////////////////////////////////////////////////////////////////////////////// // Serve waits for incoming TCP data connections. // // Every accepted connection gets its own serial connection and // DataConnection. The handler runs independently for each client. // // Serve terminates when the listener is closed. func (h *DataHandler) Serve() error { if h == nil { return fmt.Errorf("data handler is nil") } for { tcpConn, err := h.listener.Accept() if err != nil { return err } go h.handleConnection(tcpConn) } } /////////////////////////////////////////////////////////////////////////////// // Connection handling /////////////////////////////////////////////////////////////////////////////// // handleConnection connects one accepted TCP connection to one serial // connection. func (h *DataHandler) handleConnection(tcpConn net.Conn) { if tcpConn == nil { return } serialConn, err := h.serialFactory() if err != nil { log.Printf("create serial connection: %v", err) _ = tcpConn.Close() return } dataConnection, err := NewDataConnection( tcpConn, serialConn, h.hardwareErrorResponse, ) if err != nil { log.Printf("create data connection: %v", err) _ = serialConn.Close() _ = tcpConn.Close() return } if err := dataConnection.Run(); err != nil { log.Printf("data connection terminated: %v", err) } } /////////////////////////////////////////////////////////////////////////////// // Test support /////////////////////////////////////////////////////////////////////////////// // HandleConnectionForTest handles one TCP connection using the configured // serial factory. // // This method is intentionally provided for package-level tests without // requiring a real TCP listener. func (h *DataHandler) HandleConnectionForTest(tcpConn net.Conn) { h.handleConnection(tcpConn) }