164 lines
4.1 KiB
Go
164 lines
4.1 KiB
Go
/*
|
|
* ============================================================================
|
|
* Projekt.....: rs2322tcp
|
|
* Datei.......: main.go
|
|
* Copyright (C) 2026 Dieter Lang
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Beschreibung:
|
|
* Einfacher Simulator für ein serielles Endgerät.
|
|
*
|
|
* Der Simulator öffnet eine angegebene serielle Schnittstelle und protokolliert
|
|
* alle vom angeschlossenen System empfangenen Bytes als Hexdump.
|
|
*
|
|
* Der Simulator stellt bewusst keine eigene PTY-Erzeugung bereit. Für
|
|
* Entwicklung und Integrationstests kann ihm beispielsweise ein unter Linux
|
|
* erzeugter PTY-Slave (/dev/pts/N) übergeben werden. Im späteren Testaufbau
|
|
* kann derselbe Simulator jedoch auch direkt mit einer echten seriellen
|
|
* Schnittstelle verbunden werden.
|
|
*
|
|
* Der Simulator dient zunächst als passives Gegenstück zum
|
|
* rs2322tcp-server. Er soll insbesondere ermöglichen, die vom Server an ein
|
|
* simuliertes Gerät übertragenen Bytes sichtbar zu machen.
|
|
*
|
|
* Zukünftige Erweiterungen können unter anderem das manuelle Senden von
|
|
* Bytes, automatische Geräteantworten, Protokollsimulation und das Aufzeichnen
|
|
* bzw. Wiedergeben von Datenströmen umfassen.
|
|
*
|
|
* Aufruf:
|
|
*
|
|
* rs2322tcp-device-sim -port /dev/pts/7
|
|
*
|
|
* Ausgabe:
|
|
*
|
|
* rs2322tcp-device-sim
|
|
* Port: /dev/pts/7
|
|
* Warte auf Daten ...
|
|
*
|
|
* Empfangene Daten werden mit Zeitstempel, Richtung und Byteanzahl ausgegeben:
|
|
*
|
|
* 12:34:01.123 RX 5 Bytes
|
|
* 46 41 00 10 0D
|
|
*
|
|
* Der Simulator interpretiert die empfangenen Daten nicht. Die Darstellung
|
|
* erfolgt ausschließlich als Rohdaten in hexadezimaler Form.
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func main() {
|
|
port := flag.String(
|
|
"port",
|
|
"",
|
|
"serielle Schnittstelle, z.B. /dev/pts/7",
|
|
)
|
|
flag.Parse()
|
|
|
|
if *port == "" {
|
|
log.Fatal("serielle Schnittstelle fehlt: -port <device>")
|
|
}
|
|
|
|
// Die serielle Schnittstelle wird als normaler Dateideskriptor geöffnet.
|
|
// Der Simulator benötigt zunächst keine eigene Serial-Abstraktion, da er
|
|
// bewusst ein möglichst einfaches externes Testprogramm darstellen soll.
|
|
fd, err := unix.Open(
|
|
*port,
|
|
unix.O_RDWR|unix.O_NOCTTY,
|
|
0,
|
|
)
|
|
if err != nil {
|
|
log.Fatalf(
|
|
"serielle Schnittstelle %s öffnen: %v",
|
|
*port,
|
|
err,
|
|
)
|
|
}
|
|
defer unix.Close(fd)
|
|
|
|
fmt.Println("rs2322tcp-device-sim")
|
|
fmt.Printf("Port: %s\n", *port)
|
|
fmt.Println("Warte auf Daten ...")
|
|
fmt.Println()
|
|
|
|
// Der Simulator arbeitet byteorientiert. Die Größe des Puffers ist
|
|
// bewusst ausreichend groß gewählt, damit auch größere Datenblöcke
|
|
// ohne unnötige Fragmentierung gelesen werden können.
|
|
buf := make([]byte, 4096)
|
|
|
|
for {
|
|
n, err := unix.Read(fd, buf)
|
|
if err != nil {
|
|
// Ein unterbrochener Systemaufruf ist kein Fehler des
|
|
// seriellen Datenstroms und wird einfach wiederholt.
|
|
if err == unix.EINTR {
|
|
continue
|
|
}
|
|
|
|
// Bei einem PTY kann das Schließen der Gegenstelle unter
|
|
// Linux als EIO gemeldet werden. Für den Simulator bedeutet
|
|
// dies, dass die serielle Gegenstelle nicht mehr verfügbar ist.
|
|
if err == unix.EIO {
|
|
fmt.Println("Serielle Schnittstelle wurde geschlossen.")
|
|
return
|
|
}
|
|
|
|
if err == io.EOF {
|
|
fmt.Println("Serielle Schnittstelle wurde geschlossen.")
|
|
return
|
|
}
|
|
|
|
log.Fatalf(
|
|
"Lesen von %s: %v",
|
|
*port,
|
|
err,
|
|
)
|
|
}
|
|
|
|
if n == 0 {
|
|
continue
|
|
}
|
|
|
|
printRX(buf[:n])
|
|
}
|
|
}
|
|
|
|
// printRX gibt einen empfangenen Datenblock als Hexdump aus.
|
|
//
|
|
// Die Daten werden absichtlich nicht als Text interpretiert. Dadurch bleiben
|
|
// auch nicht druckbare Bytes und Bytes oberhalb von 0x7F sichtbar und können
|
|
// bei der Analyse serieller Protokolle eindeutig nachvollzogen werden.
|
|
func printRX(data []byte) {
|
|
fmt.Printf(
|
|
"%s RX %d Bytes\n",
|
|
time.Now().Format("15:04:05.000"),
|
|
len(data),
|
|
)
|
|
|
|
for offset := 0; offset < len(data); offset += 16 {
|
|
end := offset + 16
|
|
if end > len(data) {
|
|
end = len(data)
|
|
}
|
|
|
|
fmt.Printf(" ")
|
|
|
|
for _, b := range data[offset:end] {
|
|
fmt.Printf("%02X ", b)
|
|
}
|
|
|
|
fmt.Println()
|
|
}
|
|
}
|