Checkpoint
This commit is contained in:
273
schema/meshcore.go
Normal file
273
schema/meshcore.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"xorm.io/builder"
|
||||
|
||||
"git.maze.io/go/ham/protocol"
|
||||
"git.maze.io/go/ham/protocol/meshcore"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterModel(new(MeshCorePacket))
|
||||
RegisterModel(new(MeshCoreNode))
|
||||
RegisterModel(new(MeshCoreNodePosition))
|
||||
RegisterModel(new(MeshCoreGroup))
|
||||
}
|
||||
|
||||
type MeshCorePacket struct {
|
||||
ID int64 `xorm:"pk autoincr" json:"id"`
|
||||
RadioID int64 `xorm:"index" json:"radio_id"`
|
||||
Radio *Radio `xorm:"-" json:"radio"`
|
||||
SNR float64 `xorm:"not null default 0" json:"snr"`
|
||||
RSSI int `xorm:"not null default 0" json:"rssi"`
|
||||
Version int `xorm:"not null default 1" json:"version"`
|
||||
RouteType uint8 `xorm:"index not null" json:"route_type"`
|
||||
PayloadType uint8 `xorm:"index not null" json:"payload_type"`
|
||||
Hash string `xorm:"varchar(16) index not null" json:"hash"`
|
||||
Path []byte `xorm:"bytea" json:"path"`
|
||||
Payload []byte `xorm:"bytea not null" json:"payload"`
|
||||
Raw []byte `xorm:"bytea not null" json:"raw"`
|
||||
Parsed *string `xorm:"jsonb" json:"parsed"`
|
||||
ChannelHash string `xorm:"varchar(2) index" json:"channel_hash,omitempty"`
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
}
|
||||
|
||||
func (MeshCorePacket) TableName() string {
|
||||
return "meshcore_packet"
|
||||
}
|
||||
|
||||
func GetMeshCorePackets(ctx context.Context, limit int) ([]*MeshCorePacket, error) {
|
||||
packets := make([]*MeshCorePacket, 0, limit)
|
||||
return packets, Query(ctx).
|
||||
OrderBy("`received_at` DESC").
|
||||
Limit(limit).
|
||||
Find(&packets)
|
||||
}
|
||||
|
||||
func GetMeshCorePacketsByHash(ctx context.Context, hash string) ([]*MeshCorePacket, error) {
|
||||
if len(hash) != 16 {
|
||||
return nil, errors.New("invalid hash")
|
||||
} else if _, err := hex.DecodeString(hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packets := make([]*MeshCorePacket, 0, 10)
|
||||
return packets, Query(ctx).
|
||||
Where(builder.Eq{"hash": hash}).
|
||||
OrderBy("`received_at` ASC").
|
||||
Find(&packets)
|
||||
}
|
||||
|
||||
func GetMeshCorePacketsByPayloadType(ctx context.Context, payloadType meshcore.PayloadType) ([]*MeshCorePacket, error) {
|
||||
packets := make([]*MeshCorePacket, 0, 10)
|
||||
return packets, Query(ctx).
|
||||
Where(builder.Eq{"payload_type": int(payloadType)}).
|
||||
OrderBy("`received_at` DESC").
|
||||
Find(&packets)
|
||||
}
|
||||
|
||||
func GetMeshCorePacketsByChannelHash(ctx context.Context, hash string) ([]*MeshCorePacket, error) {
|
||||
packets := make([]*MeshCorePacket, 0, 10)
|
||||
return packets, Query(ctx).
|
||||
Where(builder.Eq{
|
||||
"`payload_type`": int(meshcore.TypeGroupText),
|
||||
"`channel_hash`": hash,
|
||||
}).
|
||||
OrderBy("`received_at` DESC").
|
||||
Find(&packets)
|
||||
}
|
||||
|
||||
type MeshCoreGroup struct {
|
||||
ID int64 `xorm:"pk autoincr" json:"id"`
|
||||
Name string `xorm:"varchar(32) not null unique" json:"name"`
|
||||
Secret string `xorm:"varchar(32) not null" json:"secret"`
|
||||
IsPublic bool `xorm:"boolean not null default false" json:"-"`
|
||||
}
|
||||
|
||||
func (MeshCoreGroup) TableName() string {
|
||||
return "meshcore_group"
|
||||
}
|
||||
|
||||
func GetMeshCoreGroups(ctx context.Context) ([]*MeshCoreGroup, error) {
|
||||
groups := make([]*MeshCoreGroup, 0, 10)
|
||||
return groups, Query(ctx).
|
||||
Where(builder.Eq{"is_public": true}).
|
||||
OrderBy("name asc").
|
||||
Find(&groups)
|
||||
}
|
||||
|
||||
type MeshCoreNode struct {
|
||||
ID int64 `xorm:"pk autoincr" json:"id"`
|
||||
PacketHash string `xorm:"varchar(16) index 'meshcore_packet_hash'" json:"-"`
|
||||
Packets []*MeshCorePacket `xorm:"-" json:"packet"`
|
||||
Name string `xorm:"varchar(100) not null" json:"name"`
|
||||
Type uint8 `xorm:"index not null" json:"type"`
|
||||
Prefix string `xorm:"varchar(2) not null" json:"prefix"`
|
||||
PublicKey string `xorm:"varchar(64) not null unique" json:"public_key"`
|
||||
FirstHeardAt time.Time `xorm:"timestamp not null" json:"first_heard_at"`
|
||||
LastHeardAt time.Time `xorm:"timestamp not null" json:"last_heard_at"`
|
||||
LastLatitude *float64 `json:"last_latitude"`
|
||||
LastLongitude *float64 `json:"last_longitude"`
|
||||
Distance float64 `xorm:"-" json:"distance,omitempty"`
|
||||
}
|
||||
|
||||
func (MeshCoreNode) TableName() string {
|
||||
return "meshcore_node"
|
||||
}
|
||||
|
||||
func GetMeshCoreNodeByPublicKey(ctx context.Context, publicKey string) (*MeshCoreNode, error) {
|
||||
node := new(MeshCoreNode)
|
||||
if ok, err := Query(ctx).
|
||||
Where(builder.Eq{"public_key": publicKey}).
|
||||
Get(node); err != nil {
|
||||
return nil, err
|
||||
} else if !ok {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func GetMeshCoreNodes(ctx context.Context) ([]*MeshCoreNode, error) {
|
||||
nodes := make([]*MeshCoreNode, 0, 100)
|
||||
return nodes, Query(ctx).
|
||||
OrderBy("`last_heard_at` DESC").
|
||||
Find(&nodes)
|
||||
}
|
||||
|
||||
func GetMeshCoreNodesByType(ctx context.Context, nodeType meshcore.NodeType) ([]*MeshCoreNode, error) {
|
||||
nodes := make([]*MeshCoreNode, 0, 100)
|
||||
return nodes, Query(ctx).
|
||||
Where(builder.Eq{"type": nodeType}).
|
||||
OrderBy("`last_heard_at` DESC").
|
||||
Find(&nodes)
|
||||
}
|
||||
|
||||
type MeshCoreNodeWithDistance struct {
|
||||
MeshCoreNode `xorm:"extends"`
|
||||
Distance float64 `xorm:"distance"`
|
||||
}
|
||||
|
||||
func GetMeshCoreNodesCloseTo(ctx context.Context, publicKey string, radius float64) (*MeshCoreNode, []*MeshCoreNode, error) {
|
||||
node, err := GetMeshCoreNodeByPublicKey(ctx, publicKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if node.LastLatitude == nil || node.LastLongitude == nil {
|
||||
return nil, nil, errors.New("node has no location")
|
||||
}
|
||||
|
||||
nodesWithDistance := make([]*MeshCoreNodeWithDistance, 0, 100)
|
||||
selectClause := fmt.Sprintf("*, "+
|
||||
"ST_Distance("+
|
||||
"ST_SetSRID(ST_MakePoint(%f, %f), 4326)::geography, "+
|
||||
"ST_SetSRID(ST_MakePoint(last_longitude, last_latitude), 4326)::geography"+
|
||||
") as distance",
|
||||
*node.LastLongitude, *node.LastLatitude)
|
||||
if err = Query(ctx).
|
||||
Select(selectClause).
|
||||
Where("id != ?", node.ID).
|
||||
Where("ST_DWithin("+
|
||||
"ST_SetSRID(ST_MakePoint(?, ?), 4326)::geography, "+
|
||||
"ST_SetSRID(ST_MakePoint(last_longitude, last_latitude), 4326)::geography, ?)",
|
||||
*node.LastLongitude, *node.LastLatitude, radius).
|
||||
OrderBy("`distance` ASC").
|
||||
Find(&nodesWithDistance); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
nodes := make([]*MeshCoreNode, len(nodesWithDistance))
|
||||
for i, node := range nodesWithDistance {
|
||||
node.MeshCoreNode.Distance = node.Distance
|
||||
nodes[i] = &node.MeshCoreNode
|
||||
}
|
||||
return node, nodes, nil
|
||||
}
|
||||
|
||||
type MeshCoreNodePosition struct {
|
||||
ID int64 `xorm:"pk autoincr" json:"id"`
|
||||
MeshCoreNodeId int64 `xorm:"not null 'meshcore_node_id'" json:"-"`
|
||||
MeshCoreNode *MeshCoreNode `xorm:"-" json:"node"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
ReceivedAt time.Time `xorm:"timestamp not null" json:"received_at"`
|
||||
}
|
||||
|
||||
func (MeshCoreNodePosition) TableName() string {
|
||||
return "meshcore_node_position"
|
||||
}
|
||||
|
||||
type MeshCoreStats struct {
|
||||
Messages int64 `json:"messages"`
|
||||
Nodes int64 `json:"nodes"`
|
||||
Receivers int64 `json:"receivers"`
|
||||
Packets struct {
|
||||
Timestamps []int64 `json:"timestamps"`
|
||||
Packets []int64 `json:"packets"`
|
||||
} `json:"packets"`
|
||||
}
|
||||
|
||||
func GetMeshCoreStats(ctx context.Context) (*MeshCoreStats, error) {
|
||||
var (
|
||||
engine = Query(ctx)
|
||||
stats = new(MeshCoreStats)
|
||||
err error
|
||||
)
|
||||
if stats.Messages, err = engine.
|
||||
In("`payload_type`", meshcore.TypeText, meshcore.TypeGroupText).
|
||||
Count(&MeshCorePacket{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stats.Nodes, err = engine.
|
||||
Count(&MeshCoreNode{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stats.Receivers, err = engine.
|
||||
Where(builder.Eq{"`protocol`": protocol.MeshCore}).
|
||||
Count(&Radio{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------------+--------------------------+-----------+----------+---------------------------------------------
|
||||
id | bigint | | not null | nextval('meshcore_packet_id_seq'::regclass)
|
||||
snr | real | | not null | 0
|
||||
rssi | smallint | | not null | 0
|
||||
hash | bytea | | not null |
|
||||
route_type | smallint | | not null |
|
||||
payload_type | smallint | | not null |
|
||||
path | bytea | | |
|
||||
payload | bytea | | |
|
||||
raw | bytea | | |
|
||||
parsed | jsonb | | |
|
||||
received_at | timestamp with time zone | | not null | now()
|
||||
created_at | timestamp with time zone | | not null | now()
|
||||
*/
|
||||
|
||||
/*
|
||||
id | bigint | | not null | nextval('meshcore_node_id_seq'::regclass)
|
||||
last_advert_id | bigint | | |
|
||||
node_type | smallint | | not null | 0
|
||||
public_key | bytea | | not null |
|
||||
name | text | | |
|
||||
local_time | timestamp with time zone | | not null |
|
||||
first_heard | timestamp with time zone | | not null | now()
|
||||
last_heard | timestamp with time zone | | not null | now()
|
||||
last_latitude | numeric(10,8) | | |
|
||||
last_longitude | numeric(11,8) | | |
|
||||
prefix | bytea | | | generated always as ("substring"(public_key, 0, 2)) stored
|
||||
last_position | geometry(Point,4326) | | | generated always as ( +
|
||||
| | | | CASE +
|
||||
| | | | WHEN last_latitude IS NOT NULL AND last_longitude IS NOT NULL THEN st_setsrid(st_makepoint(last_latitude::double precision, last_longitude::double precision), 4326)+
|
||||
| | | | ELSE NULL::geometry +
|
||||
| | | | END) stored
|
||||
position | geometry(Point,4326) | | |
|
||||
*/
|
||||
Reference in New Issue
Block a user