Files
hamview/meshcore.go
maze fb898bb058
Some checks failed
Run tests / test (1.25) (push) Has been cancelled
Run tests / test (stable) (push) Has been cancelled
Initial import
2026-02-22 20:27:07 +01:00

186 lines
3.9 KiB
Go

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
}