| 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)) |
| } |