Updates
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package meshcore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -18,6 +21,7 @@ const (
|
||||
maxCompanionFrameSize = 172
|
||||
)
|
||||
|
||||
// Node can be any type of MeshCore node.
|
||||
type Node struct {
|
||||
OnPacket (*Packet)
|
||||
|
||||
@@ -53,6 +57,13 @@ func (dev *Node) Info() *radio.Info {
|
||||
return dev.driver.Info()
|
||||
}
|
||||
|
||||
func (dev *Node) Trace(path []byte) (snr []float64, err error) {
|
||||
if tracer, ok := dev.driver.(nodeTracer); ok {
|
||||
return tracer.Trace(path)
|
||||
}
|
||||
return nil, errors.New("meshcore: node doesn't support running traces")
|
||||
}
|
||||
|
||||
type nodeDriver interface {
|
||||
radio.Device
|
||||
protocol.PacketReceiver
|
||||
@@ -62,6 +73,10 @@ type nodeDriver interface {
|
||||
Packets() <-chan *Packet
|
||||
}
|
||||
|
||||
type nodeTracer interface {
|
||||
Trace(path []byte) (snr []float64, err error)
|
||||
}
|
||||
|
||||
type CompanionError struct {
|
||||
Code byte
|
||||
}
|
||||
@@ -86,11 +101,62 @@ func (err CompanionError) Error() string {
|
||||
}
|
||||
|
||||
type companionDriver struct {
|
||||
conn io.ReadWriteCloser
|
||||
mu sync.Mutex
|
||||
packets chan *Packet
|
||||
rawPackets chan *protocol.Packet
|
||||
info companionInfo
|
||||
conn io.ReadWriteCloser
|
||||
mu sync.Mutex
|
||||
packets chan *Packet
|
||||
rawPackets chan *protocol.Packet
|
||||
waiting chan *companionDriverWaiting
|
||||
info companionInfo
|
||||
traceTag uint32
|
||||
traceAuthCode uint32
|
||||
}
|
||||
|
||||
type companionDriverWaiting struct {
|
||||
expect []byte
|
||||
response chan []byte
|
||||
err chan error
|
||||
}
|
||||
|
||||
func newCompanionDriverWaiting(expect []byte) *companionDriverWaiting {
|
||||
return &companionDriverWaiting{
|
||||
expect: expect,
|
||||
response: make(chan []byte),
|
||||
err: make(chan error),
|
||||
}
|
||||
}
|
||||
|
||||
func (wait *companionDriverWaiting) Respond(response []byte) {
|
||||
select {
|
||||
case wait.response <- response:
|
||||
default:
|
||||
Logger.Warnf("meshcore: waiting for %02x discard response: %02x", wait.expect, response)
|
||||
}
|
||||
}
|
||||
|
||||
func (wait *companionDriverWaiting) Error(err error) {
|
||||
select {
|
||||
case wait.err <- err:
|
||||
default:
|
||||
Logger.Warnf("meshcore: waiting for %02x discard error: %v", wait.expect, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (wait *companionDriverWaiting) Close() {
|
||||
Logger.Tracef("meshcore: waiting for %02x closing", wait.expect)
|
||||
close(wait.response)
|
||||
close(wait.err)
|
||||
}
|
||||
|
||||
func (wait *companionDriverWaiting) Wait() ([]byte, error) {
|
||||
Logger.Tracef("meshcore: waiting for %02x", wait.expect)
|
||||
select {
|
||||
case err := <-wait.err:
|
||||
Logger.Tracef("meshcore: waiting for %02x received error: %v", wait.expect, err)
|
||||
return nil, err
|
||||
case response := <-wait.response:
|
||||
Logger.Tracef("meshcore: waiting for %02x received response: %d", wait.expect, len(response))
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
|
||||
type companionInfo struct {
|
||||
@@ -123,7 +189,10 @@ type companionInfo struct {
|
||||
|
||||
func newCompanionDriver(conn io.ReadWriteCloser) *companionDriver {
|
||||
return &companionDriver{
|
||||
conn: conn,
|
||||
conn: conn,
|
||||
waiting: make(chan *companionDriverWaiting, 16),
|
||||
traceTag: rand.Uint32(),
|
||||
//traceAuthCode: rand.Uint32(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,13 +201,13 @@ func (drv *companionDriver) Close() error {
|
||||
}
|
||||
|
||||
func (drv *companionDriver) Setup() (err error) {
|
||||
go drv.poll()
|
||||
if err = drv.sendAppStart(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = drv.sendDeviceInfo(); err != nil {
|
||||
return
|
||||
}
|
||||
go drv.poll()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -166,11 +235,29 @@ func (drv *companionDriver) Info() *radio.Info {
|
||||
}
|
||||
|
||||
var firmwareDate time.Time
|
||||
firmwareDate, _ = time.Parse("02-01-2006", drv.info.FirmwareBuildDate)
|
||||
for _, layout := range []string{
|
||||
"02 Jan 2006",
|
||||
"02-01-2006",
|
||||
} {
|
||||
var terr error
|
||||
if firmwareDate, terr = time.Parse(layout, drv.info.FirmwareBuildDate); terr == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
manufacturerPart = strings.SplitN(drv.info.Manufacturer, " ", 2)
|
||||
manufacturer = manufacturerPart[0]
|
||||
device string
|
||||
)
|
||||
if len(manufacturerPart) > 1 {
|
||||
device = manufacturerPart[1]
|
||||
}
|
||||
|
||||
return &radio.Info{
|
||||
Name: drv.info.Name,
|
||||
Manufacturer: drv.info.Manufacturer,
|
||||
Manufacturer: manufacturer,
|
||||
Device: device,
|
||||
FirmwareDate: firmwareDate,
|
||||
FirmwareVersion: drv.info.FirmwareVersion,
|
||||
Modulation: protocol.LoRa,
|
||||
@@ -183,6 +270,25 @@ func (drv *companionDriver) Info() *radio.Info {
|
||||
}
|
||||
}
|
||||
|
||||
func (drv *companionDriver) Trace(path []byte) (snr []float64, err error) {
|
||||
var (
|
||||
args = make([]byte, 4+4+1+len(path))
|
||||
data []byte
|
||||
)
|
||||
binary.LittleEndian.PutUint32(args[0:], drv.traceTag) // tag
|
||||
binary.LittleEndian.PutUint32(args[4:], drv.traceAuthCode) // authcode
|
||||
args[8] = 0 // flags
|
||||
copy(args[9:], path) // path
|
||||
|
||||
Logger.Debugf("meshcore: trace %02x tag %08x authcode %08x", path, drv.traceTag, drv.traceAuthCode)
|
||||
if data, err = drv.writeCommand(companionSendTracePath, args, companionResponseSent); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("trace response:\n%s", hex.Dump(data))
|
||||
return
|
||||
}
|
||||
|
||||
func (drv *companionDriver) readFrame() ([]byte, error) {
|
||||
var frame [3 + maxCompanionFrameSize]byte
|
||||
for {
|
||||
@@ -212,7 +318,7 @@ func (drv *companionDriver) readFrame() ([]byte, error) {
|
||||
o += n
|
||||
}
|
||||
|
||||
//log.Printf("read %d:\n%s", size, hex.Dump(frame[:3+size]))
|
||||
Logger.Tracef("read %d:\n%s", size, hex.Dump(frame[:3+size]))
|
||||
return frame[3 : 3+size], nil
|
||||
}
|
||||
}
|
||||
@@ -227,7 +333,7 @@ func (drv *companionDriver) writeFrame(b []byte) (err error) {
|
||||
binary.LittleEndian.PutUint16(frame[1:], uint16(len(b)))
|
||||
n := copy(frame[3:], b)
|
||||
|
||||
//log.Printf("send %d:\n%s", n, hex.Dump(frame[:3+n]))
|
||||
//Logger.Tracef("send %d:\n%s", n, hex.Dump(frame[:3+n]))
|
||||
_, err = drv.conn.Write(frame[:3+n])
|
||||
return
|
||||
}
|
||||
@@ -243,38 +349,34 @@ func (drv *companionDriver) writeCommand(cmd byte, args []byte, wait ...byte) ([
|
||||
return drv.wait(wait...)
|
||||
}
|
||||
|
||||
func (drv *companionDriver) wait(wait ...byte) ([]byte, error) {
|
||||
for {
|
||||
b, err := drv.readFrame()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) < 1 {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case b[0] == companionResponseError:
|
||||
return nil, CompanionError{Code: b[1]}
|
||||
func (drv *companionDriver) wait(expect ...byte) ([]byte, error) {
|
||||
wait := newCompanionDriverWaiting(expect)
|
||||
defer wait.Close()
|
||||
|
||||
case b[0] >= 0x80:
|
||||
drv.handlePushFrame(b)
|
||||
continue
|
||||
drv.waiting <- wait
|
||||
return wait.Wait()
|
||||
}
|
||||
|
||||
case bytes.Contains(wait, b[:1]):
|
||||
return b, nil
|
||||
|
||||
case wait == nil:
|
||||
return b, nil
|
||||
func bytesContains(b byte, slice []byte) bool {
|
||||
for _, v := range slice {
|
||||
if v == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (drv *companionDriver) handlePushFrame(b []byte) {
|
||||
if len(b) < 1 {
|
||||
return // illegal
|
||||
}
|
||||
switch b[0] {
|
||||
case companionPushAdvert:
|
||||
case companionPushMessageWaiting:
|
||||
case companionPushLogRXData:
|
||||
drv.handleRXData(b[1:])
|
||||
default:
|
||||
log.Printf("meshcore: unhandled push %02x:\n%s", b[0], hex.Dump(b[1:]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,8 +426,11 @@ func (drv *companionDriver) handleRXData(b []byte) {
|
||||
}
|
||||
|
||||
func (drv *companionDriver) sendAppStart() (err error) {
|
||||
var b []byte
|
||||
if b, err = drv.writeCommand(companionAppStart, append(make([]byte, 8), []byte("git.maze.io/go/ham")...), companionResponseSelfInfo); err != nil {
|
||||
var (
|
||||
b []byte
|
||||
args = append(make([]byte, 8), []byte("git.maze.io/go/ham")...)
|
||||
)
|
||||
if b, err = drv.writeCommand(companionAppStart, args, companionResponseSelfInfo); err != nil {
|
||||
return fmt.Errorf("meshcore: can't send application start: %v", err)
|
||||
}
|
||||
//log.Printf("companion app start response:\n%s", hex.Dump(b))
|
||||
@@ -358,35 +463,77 @@ func (drv *companionDriver) sendAppStart() (err error) {
|
||||
}
|
||||
|
||||
func (drv *companionDriver) sendDeviceInfo() (err error) {
|
||||
var b []byte
|
||||
if b, err = drv.writeCommand(companionDeviceQuery, []byte{0x03}, companionResponseDeviceInfo); err != nil {
|
||||
var (
|
||||
args = []byte{0x03}
|
||||
data []byte
|
||||
)
|
||||
if data, err = drv.writeCommand(companionDeviceQuery, args, companionResponseDeviceInfo); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
const expect = 4 + 4 + 12 + 40 + 20
|
||||
if len(b) < expect {
|
||||
return fmt.Errorf("companion: expected %d bytes of self info, got %d", expect, len(b))
|
||||
if len(data) < expect {
|
||||
return fmt.Errorf("companion: expected %d bytes of self info, got %d", expect, len(data))
|
||||
}
|
||||
if b[0] != companionResponseDeviceInfo {
|
||||
return fmt.Errorf("companion: expected device info response, got %#02x", b[0])
|
||||
if data[0] != companionResponseDeviceInfo {
|
||||
return fmt.Errorf("companion: expected device info response, got %#02x", data[0])
|
||||
}
|
||||
b = b[1:]
|
||||
data = data[1:]
|
||||
|
||||
drv.info.FirmwareVersionCode = b[0]
|
||||
drv.info.MaxContacts = int(b[1]) * 2
|
||||
drv.info.MaxGroupChannels = int(b[2])
|
||||
drv.info.FirmwareBuildDate = decodeCString(b[7:19])
|
||||
drv.info.Manufacturer = decodeCString(b[19:59])
|
||||
drv.info.FirmwareVersion = decodeCString(b[59:79])
|
||||
drv.info.FirmwareVersionCode = data[0]
|
||||
drv.info.MaxContacts = int(data[1]) * 2
|
||||
drv.info.MaxGroupChannels = int(data[2])
|
||||
drv.info.FirmwareBuildDate = decodeCString(data[7:19])
|
||||
drv.info.Manufacturer = decodeCString(data[19:59])
|
||||
drv.info.FirmwareVersion = decodeCString(data[59:79])
|
||||
|
||||
log.Printf("self info: %#+v", drv.info)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (drv *companionDriver) poll() {
|
||||
for {
|
||||
if _, err := drv.wait(); err != nil {
|
||||
log.Printf("meshcore: companion %s fatal error: %v", drv.info.Name, err)
|
||||
frame, err := drv.readFrame()
|
||||
if err != nil {
|
||||
Logger.Errorf("meshcore: unrecoverable error: %v", err)
|
||||
return
|
||||
} else if len(frame) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
response := frame[0]
|
||||
Logger.Debugf("meshcore: handle %s (%02x, %d bytes)", companionResponseName(response), response, len(frame[1:]))
|
||||
switch {
|
||||
case response == companionResponseError:
|
||||
err := CompanionError{Code: frame[1]}
|
||||
select {
|
||||
case waiting := <-drv.waiting:
|
||||
Logger.Debugf("meshcore: sending error to waiting: %v", err)
|
||||
waiting.Error(err)
|
||||
default:
|
||||
Logger.Debugf("meshcore: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
case response >= 0x80:
|
||||
drv.handlePushFrame(frame)
|
||||
|
||||
default:
|
||||
select {
|
||||
case waiting := <-drv.waiting:
|
||||
if len(waiting.expect) == 0 {
|
||||
//Logger.Debugf("meshcore: respond %02x verbatim", response)
|
||||
waiting.Respond(frame)
|
||||
} else if slices.Contains(waiting.expect, response) {
|
||||
//Logger.Debugf("meshcore: respond %02x to expected %02x", response, waiting.expect)
|
||||
waiting.Respond(frame)
|
||||
} else {
|
||||
//Logger.Debugf("meshcore: unexpected %02x response (want %02x)", response, waiting.expect)
|
||||
waiting.Error(fmt.Errorf("unexpected %02x response", response))
|
||||
}
|
||||
default:
|
||||
Logger.Warnf("meshcore: unhandled %02x response", response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user