2023-04-25 21:29:49 +00:00
|
|
|
package xbee
|
2023-04-29 15:58:56 +00:00
|
|
|
|
2023-04-25 21:29:49 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
2023-04-29 15:58:56 +00:00
|
|
|
"sync"
|
2023-04-25 21:29:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// A connTrack is a simple frame mark utility for xbee packets. The xbee api frame
|
|
|
|
// takes a mark that is used when sending the response - this allows to coordinate
|
|
|
|
// the sent packet and the response, since there may be other packets emitted
|
2023-04-29 15:58:56 +00:00
|
|
|
// between them. The data that is stored in the tag is a channel that contains an error - this
|
|
|
|
// is sent by the reader.
|
2023-04-25 21:29:49 +00:00
|
|
|
type connTrack struct {
|
2023-04-29 15:58:56 +00:00
|
|
|
mu sync.RWMutex // use RW mutex to allow for multiple readers
|
|
|
|
internal map[uint8]chan []byte
|
2023-04-25 21:29:49 +00:00
|
|
|
// the map is set when writing a frame, and deleted when recieving a matching frame.
|
|
|
|
}
|
|
|
|
|
2023-05-07 05:00:35 +00:00
|
|
|
func NewConnTrack() *connTrack {
|
|
|
|
return &connTrack{
|
|
|
|
internal: make(map[uint8]chan []byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:29:49 +00:00
|
|
|
// GetMark finds the next available marker and takes it, returning the value of
|
2023-04-29 15:58:56 +00:00
|
|
|
// the mark as well as a channel to use as a semaphore when the mark is cleared.
|
|
|
|
// If no mark can be acquired, it returns an error.
|
|
|
|
func (ct *connTrack) GetMark() (uint8, <-chan []byte, error) {
|
2023-04-25 21:29:49 +00:00
|
|
|
// get a read lock.
|
|
|
|
ct.mu.RLock()
|
|
|
|
|
2023-04-29 15:58:56 +00:00
|
|
|
// NOTE: we start at one. This is because 0 will not return a frame ever - it's
|
2023-04-25 21:29:49 +00:00
|
|
|
// the "silent mode" mark
|
|
|
|
for i := 1; i < 256; i++ {
|
2023-04-29 15:58:56 +00:00
|
|
|
if _, ok := ct.internal[uint8(i)]; !ok {
|
2023-04-25 21:29:49 +00:00
|
|
|
// it's free.
|
|
|
|
// discard our read lock and lock for write.
|
|
|
|
ct.mu.RUnlock()
|
|
|
|
|
|
|
|
ct.mu.Lock()
|
2023-04-29 15:58:56 +00:00
|
|
|
// create the channel, makeit buffered so that we don't
|
|
|
|
// block when we write the error when freeing the mark later.
|
|
|
|
ct.internal[uint8(i)] = make(chan []byte, 1)
|
2023-04-25 21:29:49 +00:00
|
|
|
ct.mu.Unlock()
|
2023-04-29 15:58:56 +00:00
|
|
|
return uint8(i), ct.internal[uint8(i)], nil
|
2023-04-25 21:29:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ct.mu.RUnlock()
|
2023-04-29 15:58:56 +00:00
|
|
|
return 0, nil, errors.New("no available marks")
|
2023-04-25 21:29:49 +00:00
|
|
|
}
|
2023-04-29 15:58:56 +00:00
|
|
|
|
2023-04-25 21:29:49 +00:00
|
|
|
// ClearMark removes a given mark from the set if it exists, or returns an error.
|
2023-04-29 15:58:56 +00:00
|
|
|
// it takes an error (which can be nil) to send to the channel. this is used to free
|
|
|
|
// whatever command wrote that packet - be it a write() call or a custom AT command that is
|
|
|
|
// tracked.
|
|
|
|
func (ct *connTrack) ClearMark(mark uint8, data []byte) error {
|
2023-04-25 21:29:49 +00:00
|
|
|
ct.mu.RLock()
|
2023-05-07 05:00:35 +00:00
|
|
|
|
|
|
|
val, ok := ct.internal[mark]
|
|
|
|
if !ok {
|
2023-04-25 21:29:49 +00:00
|
|
|
ct.mu.RUnlock()
|
2023-05-07 05:00:35 +00:00
|
|
|
return errors.New("mark was not set")
|
2023-04-25 21:29:49 +00:00
|
|
|
}
|
2023-05-07 05:00:35 +00:00
|
|
|
|
2023-04-25 21:29:49 +00:00
|
|
|
ct.mu.RUnlock()
|
2023-05-07 05:00:35 +00:00
|
|
|
ct.mu.Lock()
|
|
|
|
val <- data
|
|
|
|
close(val)
|
|
|
|
delete(ct.internal, mark)
|
|
|
|
ct.mu.Unlock()
|
|
|
|
return nil
|
2023-04-25 21:29:49 +00:00
|
|
|
}
|