func (d *Device) Send(pkt *packet.Packet, ackRequired, responseRequired bool) (packet.Chan, error) { proxyChan := make(packet.Chan) // Rate limiter <-d.limiter.C // Broadcast vs direct broadcast := d.id == 0 if broadcast { // Broadcast can't be reliable ackRequired = false pkt.SetTagged(true) } else { pkt.SetTarget(d.id) if ackRequired { pkt.SetAckRequired(true) } if responseRequired { pkt.SetResRequired(true) } if ackRequired || responseRequired { seq, res := d.addSeq() pkt.SetSequence(seq) go func() { defer func() { close(res.done) close(proxyChan) }() var ( timeout <-chan time.Time ticker = time.NewTicker(*d.retryInterval) ) if d.timeout == nil || *d.timeout == 0 { timeout = make(<-chan time.Time) } else { timeout = time.After(*d.timeout) } for { select { case pktResponse, ok := <-res.ch: if !ok { return } if pktResponse.Result.GetType() == Acknowledgement { common.Log.Debugf("Got ACK for seq %d on device %d, cancelling retries", seq, d.ID()) ticker.Stop() // Ack does not resolve outstanding request, // continue waiting for response if responseRequired { continue } } proxyChan <- pktResponse return case <-ticker.C: common.Log.Debugf("Retrying send for seq %d on device %d after %d milliseconds", seq, d.ID(), *d.retryInterval/time.Millisecond) if err := pkt.Write(); err != nil { proxyChan <- &packet.Response{ Error: err, } return } case <-timeout: proxyChan <- &packet.Response{ Error: common.ErrTimeout, } return } } }() } } err := pkt.Write() d.resetLimiter(broadcast) return proxyChan, err }
func (d *Device) Send(pkt *packet.Packet, ackRequired, responseRequired bool) (packet.Chan, error) { proxyChan := make(packet.Chan) // Rate limiter <-d.limiter.C // Broadcast vs direct broadcast := d.id == 0 if broadcast { // Broadcast can't be reliable ackRequired = false pkt.SetTagged(true) } else { pkt.SetTarget(d.id) if ackRequired { pkt.SetAckRequired(true) } if responseRequired { pkt.SetResRequired(true) } if ackRequired || responseRequired { inputChan := make(packet.Chan) doneChan := make(chan struct{}) d.Lock() d.sequence++ if d.sequence == 0 { d.sequence++ } seq := d.sequence d.responseMap[seq] = inputChan d.doneMap[seq] = doneChan pkt.SetSequence(seq) d.Unlock() go func() { defer func() { close(doneChan) }() var ( ok bool timeout <-chan time.Time pktResponse = packet.Response{} ticker = time.NewTicker(*d.retryInterval) ) if d.timeout == nil || *d.timeout == 0 { timeout = make(<-chan time.Time) } else { timeout = time.After(*d.timeout) } for { select { case pktResponse, ok = <-inputChan: if !ok { close(proxyChan) return } if pktResponse.Result.GetType() == Acknowledgement { common.Log.Debugf("Got ACK for seq %d on device %d, cancelling retries\n", seq, d.ID()) ticker.Stop() if responseRequired { continue } } proxyChan <- pktResponse return case <-ticker.C: common.Log.Debugf("Retrying send after %d milliseconds: %+v\n", *d.retryInterval/time.Millisecond, *pkt) if err := pkt.Write(); err != nil { pktResponse.Error = err proxyChan <- pktResponse return } case <-timeout: pktResponse.Error = common.ErrTimeout proxyChan <- pktResponse return } } }() } } err := pkt.Write() d.resetLimiter(broadcast) return proxyChan, err }