You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
4.2 KiB
123 lines
4.2 KiB
|
2 months ago
|
// tera-serial-line-check temporarily applies and restores CH01 line settings.
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"encoding/json"
|
||
|
|
"flag"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"tera-clone/services/tera-api/internal/serial"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
representativeChannel = "CH01"
|
||
|
|
representativeDevice = "/dev/ttyWCH4"
|
||
|
|
representativePCPort = "COM3"
|
||
|
|
)
|
||
|
|
|
||
|
|
type probeClient interface {
|
||
|
|
Probe(context.Context, serial.ChannelConfig) (serial.ProbeResult, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type checkReport struct {
|
||
|
|
Kind string `json:"kind"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
CheckedAt string `json:"checked_at"`
|
||
|
|
Channel string `json:"channel"`
|
||
|
|
Device string `json:"device"`
|
||
|
|
PCPort string `json:"pc_port"`
|
||
|
|
Requested serial.LineSettings `json:"requested"`
|
||
|
|
Probe *serial.ProbeResult `json:"probe,omitempty"`
|
||
|
|
Findings []string `json:"findings,omitempty"`
|
||
|
|
DataSent bool `json:"data_sent"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr, time.Now))
|
||
|
|
}
|
||
|
|
|
||
|
|
func run(args []string, stdout, stderr io.Writer, now func() time.Time) int {
|
||
|
|
flags := flag.NewFlagSet("tera-serial-line-check", flag.ContinueOnError)
|
||
|
|
flags.SetOutput(stderr)
|
||
|
|
socketPath := flags.String("socket", serial.DefaultSocketPath, "tera-seriald Unix socket")
|
||
|
|
baudRate := flags.Int("baud", 115200, "temporary baud rate")
|
||
|
|
dataBits := flags.Int("data-bits", 8, "temporary data bits")
|
||
|
|
parity := flags.String("parity", "none", "temporary parity")
|
||
|
|
stopBits := flags.Int("stop-bits", 1, "temporary stop bits")
|
||
|
|
flowControl := flags.String("flow-control", "none", "temporary flow control")
|
||
|
|
timeout := flags.Duration("timeout", 10*time.Second, "overall probe timeout")
|
||
|
|
if err := flags.Parse(args); err != nil {
|
||
|
|
return 2
|
||
|
|
}
|
||
|
|
if flags.NArg() != 0 || !filepath.IsAbs(*socketPath) || *timeout <= 0 {
|
||
|
|
fmt.Fprintln(stderr, "invalid arguments: require an absolute socket path and positive timeout")
|
||
|
|
return 2
|
||
|
|
}
|
||
|
|
requested := serial.ChannelConfig{
|
||
|
|
Channel: representativeChannel, BaudRate: *baudRate, DataBits: *dataBits,
|
||
|
|
Parity: strings.ToLower(strings.TrimSpace(*parity)), StopBits: *stopBits,
|
||
|
|
FlowControl: strings.ToLower(strings.TrimSpace(*flowControl)),
|
||
|
|
}
|
||
|
|
if _, err := serial.NormalizeLineConfig(serial.LineConfig{
|
||
|
|
Device: representativeDevice, BaudRate: requested.BaudRate, DataBits: requested.DataBits,
|
||
|
|
Parity: requested.Parity, StopBits: requested.StopBits, FlowControl: requested.FlowControl,
|
||
|
|
}); err != nil {
|
||
|
|
fmt.Fprintln(stderr, err)
|
||
|
|
return 2
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
|
||
|
|
defer cancel()
|
||
|
|
report := inspect(ctx, serial.NewSocketClient(*socketPath), requested, now().UTC())
|
||
|
|
encoder := json.NewEncoder(stdout)
|
||
|
|
encoder.SetIndent("", " ")
|
||
|
|
if err := encoder.Encode(report); err != nil {
|
||
|
|
fmt.Fprintln(stderr, "write serial line check report:", err)
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
if report.Status != "pass" {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
func inspect(ctx context.Context, client probeClient, requested serial.ChannelConfig, checkedAt time.Time) checkReport {
|
||
|
|
wanted := serial.LineSettings{
|
||
|
|
BaudRate: requested.BaudRate, DataBits: requested.DataBits, Parity: requested.Parity,
|
||
|
|
StopBits: requested.StopBits, FlowControl: requested.FlowControl,
|
||
|
|
}
|
||
|
|
report := checkReport{
|
||
|
|
Kind: "tera-serial-line-check", Status: "fail", CheckedAt: checkedAt.UTC().Format(time.RFC3339Nano),
|
||
|
|
Channel: representativeChannel, Device: representativeDevice, PCPort: representativePCPort,
|
||
|
|
Requested: wanted, DataSent: false,
|
||
|
|
}
|
||
|
|
result, err := client.Probe(ctx, requested)
|
||
|
|
if err != nil {
|
||
|
|
report.Findings = append(report.Findings, err.Error())
|
||
|
|
return report
|
||
|
|
}
|
||
|
|
report.Probe = &result
|
||
|
|
if result.Channel != representativeChannel {
|
||
|
|
report.Findings = append(report.Findings, "probe returned a non-representative channel")
|
||
|
|
}
|
||
|
|
if result.Device != representativeDevice {
|
||
|
|
report.Findings = append(report.Findings, "probe returned an unexpected target device")
|
||
|
|
}
|
||
|
|
if result.Applied != wanted {
|
||
|
|
report.Findings = append(report.Findings, "applied readback does not match requested line settings")
|
||
|
|
}
|
||
|
|
if !result.Restored {
|
||
|
|
report.Findings = append(report.Findings, "prior line settings were not restored")
|
||
|
|
}
|
||
|
|
if len(report.Findings) == 0 {
|
||
|
|
report.Status = "pass"
|
||
|
|
}
|
||
|
|
return report
|
||
|
|
}
|