-
Notifications
You must be signed in to change notification settings - Fork 6
/
ack.go
90 lines (80 loc) · 1.92 KB
/
ack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package lifxlan
import (
"context"
"fmt"
"net"
)
// WaitForAcks helps device API implementations to wait for acks.
//
// It blocks until acks for all sequences are received,
// in which case it returns nil error.
// It also returns when the context is cancelled.
//
// This function drops all received messages that is not an ack,
// or ack messages that the sequence and source don't match.
// Therefore, there shouldn't be more than one WaitForAcks functions running for
// the same connection at the same time,
// and this function should only be used when no other responses are expected.
//
// If this function returns an error,
// the error would be of type *WaitForAcksError.
func WaitForAcks(
ctx context.Context,
conn net.Conn,
source uint32,
sequences ...uint8,
) error {
e := &WaitForAcksError{
Received: make([]uint8, 0, len(sequences)),
Total: make([]uint8, len(sequences)),
}
copy(e.Total, sequences)
if ctx.Err() != nil {
e.Cause = ctx.Err()
return e
}
if len(sequences) == 0 {
return nil
}
seqMap := make(map[uint8]bool)
for _, seq := range sequences {
seqMap[seq] = true
}
for {
resp, err := ReadNextResponse(ctx, conn)
if err != nil {
e.Cause = err
return e
}
if resp.Source != source || resp.Message != Acknowledgement {
continue
}
if seqMap[resp.Sequence] {
e.Received = append(e.Received, resp.Sequence)
delete(seqMap, resp.Sequence)
if len(seqMap) == 0 {
// All ack received.
return nil
}
}
}
}
// WaitForAcksError defines the error returned by WaitForAcks.
type WaitForAcksError struct {
Received []uint8
Total []uint8
Cause error
}
var _ error = (*WaitForAcksError)(nil)
func (e *WaitForAcksError) Error() string {
return fmt.Sprintf(
"lifxlan.WaitForAcks: %d of %d ack(s) received: %v",
len(e.Received),
len(e.Total),
e.Cause,
)
}
// Unwrap returns the underlying error.
func (e *WaitForAcksError) Unwrap() error {
return e.Cause
}