mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-01 16:48:19 +00:00
+4
-1
@@ -335,7 +335,10 @@ func (s *Store) GetPacket(ctx context.Context, packetHash []byte) (*api.Packet,
|
||||
iatas = append(iatas, v.Iata)
|
||||
}
|
||||
}
|
||||
p.ResolvedRoute = s.resolveTraceRoute(ctx, row.ParsedPayload, iatas)
|
||||
var parsed tracePayload
|
||||
if err := json.Unmarshal(row.ParsedPayload, &parsed); err == nil {
|
||||
p.ResolvedRoute = s.resolveTraceRoute(ctx, &parsed, iatas)
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
-76
@@ -9,7 +9,6 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
@@ -94,78 +93,3 @@ func toChannelMessage(id int64, packetHashHex string, channelHash []byte, sender
|
||||
ObservationCount: observationCount,
|
||||
}
|
||||
}
|
||||
|
||||
// resolveTraceRoute resolves the path hashes from a trace parsedPayload across
|
||||
// all provided IATAs, merging results per hop with best-confidence-wins semantics.
|
||||
// Returns nil if the payload cannot be parsed or contains no path hashes.
|
||||
func (s *Store) resolveTraceRoute(ctx context.Context, parsedPayload []byte, iatas []string) []api.ResolvedHop {
|
||||
var tracePayload struct {
|
||||
PathHashes []string `json:"pathHashes"`
|
||||
Flags byte `json:"flags"`
|
||||
SNRValues []float32 `json:"snrValues"`
|
||||
}
|
||||
if err := json.Unmarshal(parsedPayload, &tracePayload); err != nil || len(tracePayload.PathHashes) == 0 {
|
||||
return nil
|
||||
}
|
||||
hashSize := int(1 << (tracePayload.Flags & 0x03))
|
||||
hashes := make([][]byte, 0, len(tracePayload.PathHashes))
|
||||
for _, h := range tracePayload.PathHashes {
|
||||
b, err := hex.DecodeString(h)
|
||||
if err == nil {
|
||||
hashes = append(hashes, b)
|
||||
}
|
||||
}
|
||||
confidenceRank := map[string]int{"none": 0, "ambiguous": 1, "high": 2}
|
||||
type hopResult struct {
|
||||
confidence string
|
||||
entries []api.ResolvedPathEntry
|
||||
}
|
||||
merged := make([]hopResult, len(hashes))
|
||||
for i := range merged {
|
||||
merged[i] = hopResult{confidence: "none"}
|
||||
}
|
||||
for _, iata := range iatas {
|
||||
resolved, err := s.ResolvePathHashes(ctx, iata, hashes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for i, hash := range hashes {
|
||||
key := hex.EncodeToString(hash[:hashSize])
|
||||
entries := resolved[key]
|
||||
var confidence string
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
confidence = "none"
|
||||
case 1:
|
||||
confidence = "high"
|
||||
default:
|
||||
confidence = "ambiguous"
|
||||
}
|
||||
if confidenceRank[confidence] > confidenceRank[merged[i].confidence] {
|
||||
merged[i] = hopResult{confidence: confidence, entries: entries}
|
||||
}
|
||||
}
|
||||
}
|
||||
route := make([]api.ResolvedHop, 0, len(hashes))
|
||||
for i, hr := range merged {
|
||||
hop := api.ResolvedHop{
|
||||
Confidence: hr.confidence,
|
||||
Nodes: make([]api.ResolvedNode, 0, len(hr.entries)),
|
||||
}
|
||||
if i < len(tracePayload.SNRValues) {
|
||||
snr := tracePayload.SNRValues[i]
|
||||
hop.SNR = &snr
|
||||
}
|
||||
for _, e := range hr.entries {
|
||||
hop.Nodes = append(hop.Nodes, api.ResolvedNode{
|
||||
ID: e.NodeID,
|
||||
Name: e.Name,
|
||||
Latitude: e.Latitude,
|
||||
Longitude: e.Longitude,
|
||||
PublicKey: hex.EncodeToString(e.PublicKey),
|
||||
})
|
||||
}
|
||||
route = append(route, hop)
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
+89
-1
@@ -6,6 +6,7 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -14,6 +15,12 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type tracePayload struct {
|
||||
PathHashes []string `json:"pathHashes"`
|
||||
Flags byte `json:"flags"`
|
||||
SNRValues []float32 `json:"snrValues"`
|
||||
}
|
||||
|
||||
func (s *Store) ListTraceTags(ctx context.Context, iatas []string, scope string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
iataFilter := strings.Join(iatas, ",")
|
||||
var sinceTS, untilTS, cursorTS pgtype.Timestamptz
|
||||
@@ -71,6 +78,20 @@ func (s *Store) GetTraceByTag(ctx context.Context, tag string) (*api.TraceDetail
|
||||
FirstHeardAt: r.FirstHeardAt.Time.UnixMilli(),
|
||||
LastHeardAt: r.LastHeardAt.Time.UnixMilli(),
|
||||
}
|
||||
var parsed tracePayload
|
||||
if err := json.Unmarshal(r.ParsedPayload, &parsed); err == nil {
|
||||
// build raw path
|
||||
rawPath := make([]api.RawHop, 0, len(parsed.PathHashes))
|
||||
for i, h := range parsed.PathHashes {
|
||||
hop := api.RawHop{Hash: h}
|
||||
if i < len(parsed.SNRValues) {
|
||||
snr := parsed.SNRValues[i]
|
||||
hop.SNR = &snr
|
||||
}
|
||||
rawPath = append(rawPath, hop)
|
||||
}
|
||||
packet.RawPath = rawPath
|
||||
}
|
||||
// fetch observations to get IATAs for route resolution
|
||||
packetHashBytes, err := hex.DecodeString(r.PacketHashHex)
|
||||
if err == nil {
|
||||
@@ -84,10 +105,77 @@ func (s *Store) GetTraceByTag(ctx context.Context, tag string) (*api.TraceDetail
|
||||
iatas = append(iatas, v.Iata)
|
||||
}
|
||||
}
|
||||
packet.ResolvedRoute = s.resolveTraceRoute(ctx, r.ParsedPayload, iatas)
|
||||
packet.ResolvedRoute = s.resolveTraceRoute(ctx, &parsed, iatas)
|
||||
}
|
||||
}
|
||||
detail.Packets = append(detail.Packets, packet)
|
||||
}
|
||||
return detail, nil
|
||||
}
|
||||
|
||||
func (s *Store) resolveTraceRoute(ctx context.Context, payload *tracePayload, iatas []string) []api.ResolvedHop {
|
||||
if payload == nil || len(payload.PathHashes) == 0 {
|
||||
return nil
|
||||
}
|
||||
hashSize := int(1 << (payload.Flags & 0x03))
|
||||
hashes := make([][]byte, 0, len(payload.PathHashes))
|
||||
for _, h := range payload.PathHashes {
|
||||
b, err := hex.DecodeString(h)
|
||||
if err == nil {
|
||||
hashes = append(hashes, b)
|
||||
}
|
||||
}
|
||||
confidenceRank := map[string]int{"none": 0, "ambiguous": 1, "high": 2}
|
||||
type hopResult struct {
|
||||
confidence string
|
||||
entries []api.ResolvedPathEntry
|
||||
}
|
||||
merged := make([]hopResult, len(hashes))
|
||||
for i := range merged {
|
||||
merged[i] = hopResult{confidence: "none"}
|
||||
}
|
||||
for _, iata := range iatas {
|
||||
resolved, err := s.ResolvePathHashes(ctx, iata, hashes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for i, hash := range hashes {
|
||||
key := hex.EncodeToString(hash[:hashSize])
|
||||
entries := resolved[key]
|
||||
var confidence string
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
confidence = "none"
|
||||
case 1:
|
||||
confidence = "high"
|
||||
default:
|
||||
confidence = "ambiguous"
|
||||
}
|
||||
if confidenceRank[confidence] > confidenceRank[merged[i].confidence] {
|
||||
merged[i] = hopResult{confidence: confidence, entries: entries}
|
||||
}
|
||||
}
|
||||
}
|
||||
route := make([]api.ResolvedHop, 0, len(hashes))
|
||||
for i, hr := range merged {
|
||||
hop := api.ResolvedHop{
|
||||
Confidence: hr.confidence,
|
||||
Nodes: make([]api.ResolvedNode, 0, len(hr.entries)),
|
||||
}
|
||||
if i < len(payload.SNRValues) {
|
||||
snr := payload.SNRValues[i]
|
||||
hop.SNR = &snr
|
||||
}
|
||||
for _, e := range hr.entries {
|
||||
hop.Nodes = append(hop.Nodes, api.ResolvedNode{
|
||||
ID: e.NodeID,
|
||||
Name: e.Name,
|
||||
Latitude: e.Latitude,
|
||||
Longitude: e.Longitude,
|
||||
PublicKey: hex.EncodeToString(e.PublicKey),
|
||||
})
|
||||
}
|
||||
route = append(route, hop)
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
@@ -21,9 +21,15 @@ type TracePacket struct {
|
||||
Scope *string `json:"scope,omitempty"`
|
||||
FirstHeardAt int64 `json:"firstHeardAt"` // epoch ms
|
||||
LastHeardAt int64 `json:"lastHeardAt"` // epoch ms
|
||||
RawPath []RawHop `json:"rawPath"`
|
||||
ResolvedRoute []ResolvedHop `json:"resolvedRoute"`
|
||||
}
|
||||
|
||||
type RawHop struct {
|
||||
Hash string `json:"hash"` // hex-encoded path hash
|
||||
SNR *float32 `json:"snr,omitempty"`
|
||||
}
|
||||
|
||||
// TraceDetail is the full trace series for a given trace tag.
|
||||
type TraceDetail struct {
|
||||
TraceTag string `json:"traceTag"` // hex-encoded 4-byte tag
|
||||
|
||||
Reference in New Issue
Block a user