From 8b0e7df582cbdcb14f8890bb359737a54758ffd5 Mon Sep 17 00:00:00 2001 From: yurii Date: Fri, 1 Aug 2025 15:18:38 +0100 Subject: [PATCH] improved error handling and logging in Salto --- lockserver/saltolockserver.go | 111 ++++++++++++++++++++-------------- main.go | 2 +- release notes.md | 3 + 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/lockserver/saltolockserver.go b/lockserver/saltolockserver.go index 33ef8c3..9ed5d1f 100644 --- a/lockserver/saltolockserver.go +++ b/lockserver/saltolockserver.go @@ -80,36 +80,35 @@ func (lock *SaltoLockServer) LockSequence(conn net.Conn) error { log.Infof("Sending command: %q", string(lock.command)) const timeout = 10 * time.Second - + var ( + err error + resp []byte + ) reader := bufio.NewReader(conn) // 1. Send ENQ - if _, err := conn.Write([]byte{ENQ}); err != nil { - return fmt.Errorf("failed to send ENQ: %w", err) + if _, e := conn.Write([]byte{ENQ}); e != nil { + return fmt.Errorf("failed to send ENQ: %w", e) } // 2. Expect ACK conn.SetReadDeadline(time.Now().Add(timeout)) - b, err := reader.ReadByte() - if err != nil { - return fmt.Errorf("error awaiting ACK to ENQ: %w", err) - } - if b != ACK { + if b, e := reader.ReadByte(); e != nil { + return fmt.Errorf("error awaiting ACK to ENQ: %w", e) + } else if b != ACK { return fmt.Errorf("expected ACK after ENQ, got 0x%X", b) } // 3. Send command frame - if _, err := conn.Write(lock.command); err != nil { - return fmt.Errorf("failed to send command frame: %w", err) + if _, e := conn.Write(lock.command); e != nil { + return fmt.Errorf("failed to send command frame: %w", e) } // 4. Expect ACK to command conn.SetReadDeadline(time.Now().Add(timeout)) - b, err = reader.ReadByte() - if err != nil { - return fmt.Errorf("error awaiting ACK to command: %w", err) - } - if b == NAK { + if b, e := reader.ReadByte(); e != nil { + return fmt.Errorf("error awaiting ACK to command: %w", e) + } else if b == NAK { return fmt.Errorf("command rejected (NAK)") } else if b != ACK { return fmt.Errorf("expected ACK to command, got 0x%X", b) @@ -117,40 +116,64 @@ func (lock *SaltoLockServer) LockSequence(conn net.Conn) error { // 5. Expect STX conn.SetReadDeadline(time.Now().Add(timeout)) - stx, err := reader.ReadByte() - if err != nil { - return fmt.Errorf("error reading STX: %w", err) + stx, e := reader.ReadByte() + if e != nil { + return fmt.Errorf("error reading STX: %w", e) } + resp = append(resp, stx) if stx != STX { - return fmt.Errorf("expected STX, got 0x%X", stx) - } - conn.SetReadDeadline(time.Now().Add(timeout)) - _ , err = reader.ReadByte() - if err != nil { - return fmt.Errorf("error reading separator after STX: %w", err) + err = fmt.Errorf("expected STX, got 0x%X", stx) } - // 6. Read next two bytes and check for TD error + // 6. Read separator after STX conn.SetReadDeadline(time.Now().Add(timeout)) - b1, err := reader.ReadByte() - if err != nil { - return fmt.Errorf("error reading first response byte: %w", err) - } - b2, err := reader.ReadByte() - if err != nil { - return fmt.Errorf("error reading second response byte: %w", err) - } - if b1 == 'T' && b2 == 'D' { - return fmt.Errorf("lock response indicates room does not exist (TD)") + if sep, e := reader.ReadByte(); e == nil { + resp = append(resp, sep) + } else if err == nil { + err = fmt.Errorf("error reading separator after STX: %w", e) } - // 7. Continue reading until ETX - resp := []byte{b1, b2} + // 7. Read command code (e.g., CN, TD) + conn.SetReadDeadline(time.Now().Add(timeout)) + b1, e1 := reader.ReadByte() + if e1 == nil { + resp = append(resp, b1) + } else if err == nil { + err = fmt.Errorf("error reading first response byte: %w", e1) + } + b2, e2 := reader.ReadByte() + if e2 == nil { + resp = append(resp, b2) + } else if err == nil { + err = fmt.Errorf("error reading second response byte: %w", e2) + } + + switch { + case b1 == 'C' && b2 == 'N': + log.Infof("LockSequence: command response is CN (normal)") + case b1 == 'C' && b2 == 'C': + log.Infof("LockSequence: command response is CC (follow-up)") + case b1 == 'T' && b2 == 'D': + log.Warnf("LockSequence: command response is TD (room does not exist)") + if err == nil { + err = fmt.Errorf("lock response indicates room does not exist") + } + default: + log.Warnf("LockSequence: unexpected command response %q", string(resp)) + if err == nil { + err = fmt.Errorf("error encoding keycard, unexpected response") + } + } + + // 8. Read rest of message until ETX for { conn.SetReadDeadline(time.Now().Add(timeout)) - c, err := reader.ReadByte() - if err != nil { - return fmt.Errorf("error reading response body: %w", err) + c, e := reader.ReadByte() + if e != nil { + if err == nil { + err = fmt.Errorf("error reading response body: %w", e) + } + break } resp = append(resp, c) if c == ETX { @@ -158,14 +181,14 @@ func (lock *SaltoLockServer) LockSequence(conn net.Conn) error { } } - // 8. Optional: read LRC or trailing byte + // 9. Optional: read trailing byte (LRC or CR) conn.SetReadDeadline(time.Now().Add(timeout)) - if lrc, err := reader.ReadByte(); err == nil { + if lrc, e := reader.ReadByte(); e == nil { resp = append(resp, lrc) } else { - log.Warnf("LockSequence: failed to read trailing LRC/CR: %v", err) + log.Warnf("LockSequence: failed to read trailing LRC/CR: %v", e) } log.Infof("LockSequence: received response: %q", string(resp)) - return nil + return err } diff --git a/main.go b/main.go index f5c604e..a027ffa 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ import ( ) const ( - buildVersion = "1.0.7" + buildVersion = "1.0.8" serviceName = "hardlink" customLayout = "2006-01-02 15:04:05 -0700" transactionUrl = "http://127.0.0.1:18181/start-transaction/" diff --git a/release notes.md b/release notes.md index 442b1d1..74fa6da 100644 --- a/release notes.md +++ b/release notes.md @@ -2,6 +2,9 @@ builtVersion is a const in main.go +#### 1.0.8 - 1 August 2024 +improved error handling and logging in Salto + #### 1.0.7 - 25 July 2024 added check if the room exists