package uart import ( "context" "fmt" "log" "time" "alox.tool/api" "alox.tool/eventbus" "go.bug.st/serial" ) type Com struct { bus eventbus.EventBus port serial.Port cancel context.CancelFunc } func NewCom(bus eventbus.EventBus) (*Com, error) { return &Com{ bus: bus, port: nil, cancel: nil, }, nil } func (c *Com) Connect(portName string, baudrate int) error { if c.port != nil { return fmt.Errorf("Port already connected") } mode := &serial.Mode{BaudRate: baudrate} port, err := serial.Open(portName, mode) if err != nil { return err } ctx, cancel := context.WithCancel(context.Background()) drv := New(c.bus) go func() { buff := make([]byte, 1024) for { select { case <-ctx.Done(): return default: n, err := port.Read(buff) if err != nil { log.Print("[Warning]: Read Error:", err) return // Loop beenden bei Hardware-Fehler } if n > 0 { for _, b := range buff[:n] { //log.Printf("[RAW][RX] % X", b) drv.ParseByte(b) } } } } }() c.port = port c.cancel = cancel return nil } func (c *Com) Close() { c.cancel() c.port.Close() } func packFrame(id byte, payload []byte) []byte { out := make([]byte, 0, len(payload)+5) // Guessing extra Puffer size checksum := id out = append(out, StartByte) // Helper für Escaping writeEscaped := func(b byte) { if b == StartByte || b == EscapeByte || b == EndByte { out = append(out, EscapeByte) } out = append(out, b) } writeEscaped(id) for _, b := range payload { writeEscaped(b) checksum ^= b } writeEscaped(checksum) out = append(out, EndByte) return out } func (c *Com) Send(id byte, payload []byte) error { raw := packFrame(id, payload) log.Printf("[RAW]: %v", raw) //log.Printf("RAW: % X", raw) _, err := c.port.Write(raw) c.bus.Publish(api.TopicUARTTx, api.Frame{ Time: uint64(time.Now().UnixNano()), ID: id, Data: payload, }) return err } func (c *Com) EventbusHandler(ctx context.Context) error { UActions := c.bus.Subscribe(api.TopicUartAction) for { select { case <-ctx.Done(): return nil case msgT := <-UActions: switch msg := msgT.(type) { case api.ActionUartConnect: err := c.Connect(msg.Adapter, msg.Baudrate) c.bus.Publish(api.TopicUartAction, api.ActionUartConnected{ Adapter: msg.Adapter, Baudrate: msg.Baudrate, Error: err, }) case api.ActionUartDisconnect: c.Close() c.bus.Publish(api.TopicUartAction, api.ActionUartDisconnected{}) case api.ActionUartSendMessage: c.Send(msg.MsgId, msg.Data) } } } }