blob: ecb813202a4cea0be99cc094d04cedc665dd8d78 [file] [log] [blame]
package nmcoap
import (
"bytes"
"encoding/hex"
"sort"
"strings"
"time"
"github.com/runtimeco/go-coap"
)
type MsgCriteria struct {
Token []byte
Path string
}
type Listener struct {
Criteria MsgCriteria
RspChan chan coap.Message
ErrChan chan error
tmoChan chan time.Time
timer *time.Timer
}
func (mc *MsgCriteria) String() string {
s := "token="
if mc.Token == nil {
s += "nil"
} else {
s += hex.EncodeToString(mc.Token)
}
s += " path="
if mc.Path == "" {
s += "nil"
} else {
s += mc.Path
}
return s
}
func CompareMsgCriteria(mc1 MsgCriteria, mc2 MsgCriteria) int {
// First sort key: path.
if diff := strings.Compare(mc1.Path, mc2.Path); diff != 0 {
return diff
}
// Second sort key: token.
if mc1.Token == nil && mc2.Token != nil {
return -1
}
if mc1.Token != nil && mc2.Token == nil {
return 1
}
if mc1.Token != nil {
if diff := bytes.Compare(mc1.Token, mc2.Token); diff != 0 {
return diff
}
}
return 0
}
// Determines if a listener matches an incoming message.
func MatchMsgCriteria(listenc MsgCriteria, msgc MsgCriteria) bool {
// First sort key: path.
if listenc.Path != "" && listenc.Path != msgc.Path {
return false
}
// Second sort key: token.
if listenc.Token != nil {
if bytes.Compare(listenc.Token, msgc.Token) != 0 {
return false
}
}
return true
}
func CriteriaFromMsg(msg coap.Message) MsgCriteria {
return MsgCriteria{
Token: msg.Token(),
Path: msg.PathString(),
}
}
func NewListener(mc MsgCriteria) *Listener {
return &Listener{
Criteria: mc,
RspChan: make(chan coap.Message, 1),
ErrChan: make(chan error, 1),
tmoChan: make(chan time.Time, 1),
}
}
func (ol *Listener) AfterTimeout(tmo time.Duration) <-chan time.Time {
fn := func() {
if ol.tmoChan != nil {
ol.tmoChan <- time.Now()
}
}
ol.timer = time.AfterFunc(tmo, fn)
return ol.tmoChan
}
func (ol *Listener) Close() {
if ol.timer != nil {
ol.timer.Stop()
}
close(ol.RspChan)
close(ol.ErrChan)
close(ol.tmoChan)
ol.tmoChan = nil
}
type listenerSorter struct {
listeners []*Listener
}
func (s listenerSorter) Len() int {
return len(s.listeners)
}
func (s listenerSorter) Swap(i, j int) {
s.listeners[i], s.listeners[j] = s.listeners[j], s.listeners[i]
}
func (s listenerSorter) Less(i, j int) bool {
li := s.listeners[i]
lj := s.listeners[j]
return CompareMsgCriteria(li.Criteria, lj.Criteria) < 0
}
func SortListeners(listeners []*Listener) {
sorter := listenerSorter{
listeners: listeners,
}
// Reverse the sort order; most specific must come first.
sort.Sort(sort.Reverse(sorter))
}