rs2322tcp/internal/serial/serial.go
2026-08-10 10:38:14 +02:00

159 lines
3.4 KiB
Go

/*
* ============================================================================
* Projekt.....: rs2322tcp
* Datei.......: serial.go
* Copyright (C) 2026 Dieter Lang
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Beschreibung:
* Öffnet und verwaltet eine serielle Schnittstelle auf Basis der
* konfigurierten Geräteparameter.
* ============================================================================
*/
package serial
import (
"fmt"
"io"
bugserial "go.bug.st/serial"
"git.lang-dieter.de/rs2322tcp/internal/config"
)
///////////////////////////////////////////////////////////////////////////////
// Connection
///////////////////////////////////////////////////////////////////////////////
// Connection represents an opened serial connection.
type Connection struct {
port bugserial.Port
}
///////////////////////////////////////////////////////////////////////////////
// Open
///////////////////////////////////////////////////////////////////////////////
// Open opens the serial device described by the configuration.
func Open(device config.DeviceConfig) (*Connection, error) {
mode, err := createMode(device)
if err != nil {
return nil, err
}
port, err := bugserial.Open(device.SerialPort, mode)
if err != nil {
return nil, fmt.Errorf(
"open serial port %q: %w",
device.SerialPort,
err,
)
}
return &Connection{
port: port,
}, nil
}
///////////////////////////////////////////////////////////////////////////////
// Mode
///////////////////////////////////////////////////////////////////////////////
func createMode(device config.DeviceConfig) (*bugserial.Mode, error) {
mode := &bugserial.Mode{
BaudRate: device.BaudRate,
DataBits: device.DataBits,
Parity: bugserial.NoParity,
StopBits: bugserial.OneStopBit,
}
if device.BaudRate <= 0 {
return nil, fmt.Errorf(
"invalid baud rate: %d",
device.BaudRate,
)
}
switch device.DataBits {
case 5, 6, 7, 8:
// Supported by go.bug.st/serial.
default:
return nil, fmt.Errorf(
"unsupported data bits: %d",
device.DataBits,
)
}
switch device.Parity {
case "none":
mode.Parity = bugserial.NoParity
case "odd":
mode.Parity = bugserial.OddParity
case "even":
mode.Parity = bugserial.EvenParity
default:
return nil, fmt.Errorf(
"unsupported parity: %q",
device.Parity,
)
}
switch device.StopBits {
case 1:
mode.StopBits = bugserial.OneStopBit
case 2:
mode.StopBits = bugserial.TwoStopBits
default:
return nil, fmt.Errorf(
"unsupported stop bits: %d",
device.StopBits,
)
}
return mode, nil
}
///////////////////////////////////////////////////////////////////////////////
// Read / Write
///////////////////////////////////////////////////////////////////////////////
// Read reads data from the serial connection.
func (c *Connection) Read(p []byte) (int, error) {
if c == nil || c.port == nil {
return 0, io.ErrClosedPipe
}
return c.port.Read(p)
}
// Write writes data to the serial connection.
func (c *Connection) Write(p []byte) (int, error) {
if c == nil || c.port == nil {
return 0, io.ErrClosedPipe
}
return c.port.Write(p)
}
///////////////////////////////////////////////////////////////////////////////
// Close
///////////////////////////////////////////////////////////////////////////////
// Close closes the serial connection.
func (c *Connection) Close() error {
if c == nil || c.port == nil {
return nil
}
err := c.port.Close()
c.port = nil
return err
}