Initial import
This commit is contained in:
185
meshcore.go
Normal file
185
meshcore.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package hamview
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/Vaniog/go-postgis"
|
||||
"github.com/tarm/serial"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"git.maze.io/go/ham/protocol"
|
||||
"git.maze.io/go/ham/protocol/meshcore"
|
||||
)
|
||||
|
||||
type MeshCoreConfig struct {
|
||||
Type string `yaml:"type"`
|
||||
Conf yaml.Node `yaml:"conf"`
|
||||
}
|
||||
|
||||
type MeshCoreCompanionConfig struct {
|
||||
Port string `yaml:"port"`
|
||||
Baud int `yaml:"baud"`
|
||||
Addr string `yaml:"addr"`
|
||||
}
|
||||
|
||||
type MeshCorePrefix byte
|
||||
|
||||
func (prefix *MeshCorePrefix) MarshalJSON() ([]byte, error) {
|
||||
s := fmt.Sprintf("%02x", *prefix)
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
func (prefix *MeshCorePrefix) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if n, err := fmt.Sscanf(s, "%02x", &prefix); err != nil {
|
||||
return err
|
||||
} else if n != 1 {
|
||||
return errors.New("no prefix could be decoded")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewMeshCoreReceiver(config *MeshCoreConfig) (protocol.PacketReceiver, error) {
|
||||
switch config.Type {
|
||||
case "companion", "":
|
||||
return newMeshCoreCompanionReceiver(config.Conf)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("hamview: unsupported MeshCore node type %q", config.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func newMeshCoreCompanionReceiver(node yaml.Node) (protocol.PacketReceiver, error) {
|
||||
var config MeshCoreCompanionConfig
|
||||
if err := node.Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
conn io.ReadWriteCloser
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
case config.Addr != "":
|
||||
Logger.Infof("receiver: connecting to MeshCore companion at tcp://%s", config.Addr)
|
||||
conn, err = net.Dial("tcp", config.Addr)
|
||||
|
||||
default:
|
||||
if config.Port == "" {
|
||||
// TODO: detect serial ports
|
||||
config.Port = "/dev/ttyUSB0"
|
||||
}
|
||||
if config.Baud == 0 {
|
||||
config.Baud = 115200
|
||||
}
|
||||
Logger.Infof("receiver: connecting to MeshCore companion on %s at %d baud", config.Port, config.Baud)
|
||||
conn, err = serial.OpenPort(&serial.Config{
|
||||
Name: config.Port,
|
||||
Baud: config.Baud,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiver, err := meshcore.NewCompanion(conn)
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
Logger.Warnf("receiver: error connecting to companion: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := receiver.Info()
|
||||
Logger.Infof("receiver: connected to MeshCore Companion %q model %q version %q", info.Name, info.Manufacturer, info.FirmwareVersion)
|
||||
return receiver, nil
|
||||
}
|
||||
|
||||
type meshCoreNode struct {
|
||||
Name string `json:"name"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
Prefix MeshCorePrefix `json:"prefix"`
|
||||
NodeType meshcore.NodeType `json:"node_type"`
|
||||
FirstHeard time.Time `json:"first_heard"`
|
||||
LastHeard time.Time `json:"last_heard"`
|
||||
Position *meshcore.Position `json:"position"`
|
||||
}
|
||||
|
||||
type meshCoreNodeDistance struct {
|
||||
meshCoreNode
|
||||
Distance float64 `json:"distance"`
|
||||
}
|
||||
|
||||
func meshCoreRepeaterWithPrefixCloseTo(db *sql.DB, prefix MeshCorePrefix, position *meshcore.Position) (node *meshCoreNodeDistance, err error) {
|
||||
if position == nil {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
node = &meshCoreNodeDistance{
|
||||
meshCoreNode: meshCoreNode{
|
||||
Position: new(meshcore.Position),
|
||||
},
|
||||
}
|
||||
var nodePrefix []byte
|
||||
if err = db.QueryRow(`
|
||||
SELECT
|
||||
n.name,
|
||||
n.public_key,
|
||||
n.prefix,
|
||||
n.first_heard,
|
||||
n.last_heard,
|
||||
n.last_latitude,
|
||||
n.last_longitude,
|
||||
ST_DistanceSphere(
|
||||
last_position,
|
||||
GeomFromEWKB($2)
|
||||
) AS distance
|
||||
FROM
|
||||
meshcore_node n
|
||||
WHERE
|
||||
n.prefix = $1
|
||||
AND
|
||||
n.last_latitude IS NOT NULL
|
||||
AND
|
||||
n.last_longitude IS NOT NULL
|
||||
AND
|
||||
n.last_position IS NOT NULL
|
||||
ORDER BY
|
||||
distance ASC
|
||||
LIMIT 1
|
||||
`,
|
||||
[]byte{byte(prefix)},
|
||||
postgis.PointS{
|
||||
SRID: 4326,
|
||||
X: position.Latitude,
|
||||
Y: position.Longitude,
|
||||
},
|
||||
).Scan(
|
||||
&node.Name,
|
||||
&node.PublicKey,
|
||||
&nodePrefix,
|
||||
&node.FirstHeard,
|
||||
&node.LastHeard,
|
||||
&node.Position.Latitude,
|
||||
&node.Position.Longitude,
|
||||
&node.Distance,
|
||||
); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(nodePrefix) > 0 {
|
||||
node.Prefix = MeshCorePrefix(nodePrefix[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user