186 lines
3.9 KiB
Go
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
|
|
}
|