Example #1
0
File: device.go Project: pdf/golifx
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
}
Example #2
0
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
}