improved error handling and logging in Salto

This commit is contained in:
yurii 2025-08-01 15:18:38 +01:00
parent 25eab0dc75
commit 8b0e7df582
3 changed files with 71 additions and 45 deletions

View File

@ -80,36 +80,35 @@ func (lock *SaltoLockServer) LockSequence(conn net.Conn) error {
log.Infof("Sending command: %q", string(lock.command)) log.Infof("Sending command: %q", string(lock.command))
const timeout = 10 * time.Second const timeout = 10 * time.Second
var (
err error
resp []byte
)
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
// 1. Send ENQ // 1. Send ENQ
if _, err := conn.Write([]byte{ENQ}); err != nil { if _, e := conn.Write([]byte{ENQ}); e != nil {
return fmt.Errorf("failed to send ENQ: %w", err) return fmt.Errorf("failed to send ENQ: %w", e)
} }
// 2. Expect ACK // 2. Expect ACK
conn.SetReadDeadline(time.Now().Add(timeout)) conn.SetReadDeadline(time.Now().Add(timeout))
b, err := reader.ReadByte() if b, e := reader.ReadByte(); e != nil {
if err != nil { return fmt.Errorf("error awaiting ACK to ENQ: %w", e)
return fmt.Errorf("error awaiting ACK to ENQ: %w", err) } else if b != ACK {
}
if b != ACK {
return fmt.Errorf("expected ACK after ENQ, got 0x%X", b) return fmt.Errorf("expected ACK after ENQ, got 0x%X", b)
} }
// 3. Send command frame // 3. Send command frame
if _, err := conn.Write(lock.command); err != nil { if _, e := conn.Write(lock.command); e != nil {
return fmt.Errorf("failed to send command frame: %w", err) return fmt.Errorf("failed to send command frame: %w", e)
} }
// 4. Expect ACK to command // 4. Expect ACK to command
conn.SetReadDeadline(time.Now().Add(timeout)) conn.SetReadDeadline(time.Now().Add(timeout))
b, err = reader.ReadByte() if b, e := reader.ReadByte(); e != nil {
if err != nil { return fmt.Errorf("error awaiting ACK to command: %w", e)
return fmt.Errorf("error awaiting ACK to command: %w", err) } else if b == NAK {
}
if b == NAK {
return fmt.Errorf("command rejected (NAK)") return fmt.Errorf("command rejected (NAK)")
} else if b != ACK { } else if b != ACK {
return fmt.Errorf("expected ACK to command, got 0x%X", b) 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 // 5. Expect STX
conn.SetReadDeadline(time.Now().Add(timeout)) conn.SetReadDeadline(time.Now().Add(timeout))
stx, err := reader.ReadByte() stx, e := reader.ReadByte()
if err != nil { if e != nil {
return fmt.Errorf("error reading STX: %w", err) return fmt.Errorf("error reading STX: %w", e)
} }
resp = append(resp, stx)
if stx != STX { if stx != STX {
return fmt.Errorf("expected STX, got 0x%X", stx) err = 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)
} }
// 6. Read next two bytes and check for TD error // 6. Read separator after STX
conn.SetReadDeadline(time.Now().Add(timeout)) conn.SetReadDeadline(time.Now().Add(timeout))
b1, err := reader.ReadByte() if sep, e := reader.ReadByte(); e == nil {
if err != nil { resp = append(resp, sep)
return fmt.Errorf("error reading first response byte: %w", err) } else if err == nil {
} err = fmt.Errorf("error reading separator after STX: %w", e)
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)")
} }
// 7. Continue reading until ETX // 7. Read command code (e.g., CN, TD)
resp := []byte{b1, b2} 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 { for {
conn.SetReadDeadline(time.Now().Add(timeout)) conn.SetReadDeadline(time.Now().Add(timeout))
c, err := reader.ReadByte() c, e := reader.ReadByte()
if err != nil { if e != nil {
return fmt.Errorf("error reading response body: %w", err) if err == nil {
err = fmt.Errorf("error reading response body: %w", e)
}
break
} }
resp = append(resp, c) resp = append(resp, c)
if c == ETX { 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)) conn.SetReadDeadline(time.Now().Add(timeout))
if lrc, err := reader.ReadByte(); err == nil { if lrc, e := reader.ReadByte(); e == nil {
resp = append(resp, lrc) resp = append(resp, lrc)
} else { } 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)) log.Infof("LockSequence: received response: %q", string(resp))
return nil return err
} }

View File

@ -30,7 +30,7 @@ import (
) )
const ( const (
buildVersion = "1.0.7" buildVersion = "1.0.8"
serviceName = "hardlink" serviceName = "hardlink"
customLayout = "2006-01-02 15:04:05 -0700" customLayout = "2006-01-02 15:04:05 -0700"
transactionUrl = "http://127.0.0.1:18181/start-transaction/" transactionUrl = "http://127.0.0.1:18181/start-transaction/"

View File

@ -2,6 +2,9 @@
builtVersion is a const in main.go 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 #### 1.0.7 - 25 July 2024
added check if the room exists added check if the room exists