2020-10-18 14:21:58 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"crypto/rand"
|
|
|
|
|
"encoding/binary"
|
2020-10-19 09:45:49 +02:00
|
|
|
"errors"
|
2020-10-18 14:21:58 +02:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/nonoo/kappanhang/log"
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-18 15:40:25 +02:00
|
|
|
const pkt7TimeoutDuration = 3 * time.Second
|
|
|
|
|
|
2020-10-18 14:21:58 +02:00
|
|
|
type pkt7Type struct {
|
|
|
|
|
sendSeq uint16
|
2020-10-18 18:59:11 +02:00
|
|
|
randIDBytes [2]byte
|
2020-10-18 14:21:58 +02:00
|
|
|
lastConfirmedSeq uint16
|
|
|
|
|
|
|
|
|
|
sendTicker *time.Ticker
|
|
|
|
|
timeoutTimer *time.Timer
|
|
|
|
|
latency time.Duration
|
|
|
|
|
lastSendAt time.Time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *pkt7Type) isPkt7(r []byte) bool {
|
|
|
|
|
return len(r) == 21 && bytes.Equal(r[1:6], []byte{0x00, 0x00, 0x00, 0x07, 0x00}) // Note that the first byte can be 0x15 or 0x00, so we ignore that.
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-18 18:34:22 +02:00
|
|
|
func (p *pkt7Type) tryReceive(timeout time.Duration, s *streamCommon) []byte {
|
|
|
|
|
return s.tryReceivePacket(timeout, 21, 1, []byte{0x00, 0x00, 0x00, 0x07, 0x00})
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-18 14:21:58 +02:00
|
|
|
func (p *pkt7Type) handle(s *streamCommon, r []byte) {
|
|
|
|
|
gotSeq := binary.LittleEndian.Uint16(r[6:8])
|
|
|
|
|
if r[16] == 0x00 { // This is a pkt7 request from the radio.
|
|
|
|
|
// Replying to the radio.
|
|
|
|
|
// Example request from radio: 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1c, 0x0e, 0xe4, 0x35, 0xdd, 0x72, 0xbe, 0xd9, 0xf2, 0x63, 0x00, 0x57, 0x2b, 0x12, 0x00
|
|
|
|
|
// Example answer from PC: 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1c, 0x0e, 0xbe, 0xd9, 0xf2, 0x63, 0xe4, 0x35, 0xdd, 0x72, 0x01, 0x57, 0x2b, 0x12, 0x00
|
2020-10-20 19:47:25 +02:00
|
|
|
if p.sendTicker != nil { // Only replying if the auth is already done.
|
2020-10-18 18:34:22 +02:00
|
|
|
p.sendReply(s, r[17:21], gotSeq)
|
|
|
|
|
}
|
2020-10-18 14:21:58 +02:00
|
|
|
} else { // This is a pkt7 reply to our request.
|
2020-10-20 19:47:25 +02:00
|
|
|
if p.sendTicker != nil { // Auth is already done?
|
|
|
|
|
if p.timeoutTimer != nil {
|
|
|
|
|
p.timeoutTimer.Stop()
|
|
|
|
|
p.timeoutTimer.Reset(pkt7TimeoutDuration)
|
|
|
|
|
}
|
2020-10-18 14:21:58 +02:00
|
|
|
|
|
|
|
|
// Only measure latency after the timeout has been initialized, so the auth is already done.
|
|
|
|
|
p.latency += time.Since(p.lastSendAt)
|
|
|
|
|
p.latency /= 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expectedSeq := p.lastConfirmedSeq + 1
|
2020-10-21 15:27:18 +02:00
|
|
|
if expectedSeq != gotSeq && gotSeq != p.lastConfirmedSeq {
|
2020-10-18 14:21:58 +02:00
|
|
|
var missingPkts int
|
|
|
|
|
if gotSeq > expectedSeq {
|
|
|
|
|
missingPkts = int(gotSeq) - int(expectedSeq)
|
|
|
|
|
} else {
|
|
|
|
|
missingPkts = int(gotSeq) + 65536 - int(expectedSeq)
|
|
|
|
|
}
|
2020-10-18 15:40:25 +02:00
|
|
|
log.Error(s.name+"/lost ", missingPkts, " packets")
|
2020-10-18 14:21:58 +02:00
|
|
|
}
|
|
|
|
|
p.lastConfirmedSeq = gotSeq
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *pkt7Type) sendDo(s *streamCommon, replyID []byte, seq uint16) {
|
|
|
|
|
// Example request from PC: 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x09, 0x00, 0xbe, 0xd9, 0xf2, 0x63, 0xe4, 0x35, 0xdd, 0x72, 0x00, 0x78, 0x40, 0xf6, 0x02
|
|
|
|
|
// Example reply from radio: 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x09, 0x00, 0xe4, 0x35, 0xdd, 0x72, 0xbe, 0xd9, 0xf2, 0x63, 0x01, 0x78, 0x40, 0xf6, 0x02
|
|
|
|
|
var replyFlag byte
|
|
|
|
|
if replyID == nil {
|
|
|
|
|
replyID = make([]byte, 4)
|
|
|
|
|
var randID [2]byte
|
|
|
|
|
_, err := rand.Read(randID[:])
|
|
|
|
|
if err != nil {
|
2020-10-19 09:45:49 +02:00
|
|
|
exit(err)
|
2020-10-18 14:21:58 +02:00
|
|
|
}
|
|
|
|
|
replyID[0] = randID[0]
|
|
|
|
|
replyID[1] = randID[1]
|
2020-10-18 18:59:11 +02:00
|
|
|
replyID[2] = p.randIDBytes[0]
|
|
|
|
|
replyID[3] = p.randIDBytes[1]
|
2020-10-18 14:21:58 +02:00
|
|
|
} else {
|
|
|
|
|
replyFlag = 0x01
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:28 +02:00
|
|
|
d := []byte{0x15, 0x00, 0x00, 0x00, 0x07, 0x00, byte(seq), byte(seq >> 8),
|
2020-10-18 14:21:58 +02:00
|
|
|
byte(s.localSID >> 24), byte(s.localSID >> 16), byte(s.localSID >> 8), byte(s.localSID),
|
|
|
|
|
byte(s.remoteSID >> 24), byte(s.remoteSID >> 16), byte(s.remoteSID >> 8), byte(s.remoteSID),
|
2020-10-21 15:05:28 +02:00
|
|
|
replyFlag, replyID[0], replyID[1], replyID[2], replyID[3]}
|
|
|
|
|
s.send(d)
|
|
|
|
|
s.send(d)
|
2020-10-18 14:21:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *pkt7Type) send(s *streamCommon) {
|
|
|
|
|
p.sendDo(s, nil, p.sendSeq)
|
|
|
|
|
p.lastSendAt = time.Now()
|
|
|
|
|
p.sendSeq++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *pkt7Type) sendReply(s *streamCommon, replyID []byte, seq uint16) {
|
|
|
|
|
p.sendDo(s, replyID, seq)
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-20 19:47:25 +02:00
|
|
|
func (p *pkt7Type) startPeriodicSend(s *streamCommon, firstSeqNo uint16, checkPingTimeout bool) {
|
2020-10-18 21:35:36 +02:00
|
|
|
p.sendSeq = firstSeqNo
|
2020-10-18 14:21:58 +02:00
|
|
|
p.lastConfirmedSeq = p.sendSeq - 1
|
|
|
|
|
|
|
|
|
|
p.sendTicker = time.NewTicker(100 * time.Millisecond)
|
2020-10-20 19:47:25 +02:00
|
|
|
if checkPingTimeout {
|
|
|
|
|
p.timeoutTimer = time.NewTimer(pkt7TimeoutDuration)
|
|
|
|
|
}
|
2020-10-18 14:21:58 +02:00
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
2020-10-20 19:47:25 +02:00
|
|
|
if checkPingTimeout {
|
|
|
|
|
select {
|
|
|
|
|
case <-p.sendTicker.C:
|
|
|
|
|
p.send(s)
|
|
|
|
|
case <-p.timeoutTimer.C:
|
|
|
|
|
exit(errors.New(s.name + "/ping timeout"))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
<-p.sendTicker.C
|
2020-10-18 14:21:58 +02:00
|
|
|
p.send(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|