Add API Server (Go/PocketBase), Web App (Go BFF + Vue), Fly App (Flutter/DJI MSDK), Adobe Plugin, and Docker/Docker AIO deployment configs. Design assets and build artifacts are gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
113 lines
3.4 KiB
Go
113 lines
3.4 KiB
Go
package hub
|
|
|
|
import "encoding/json"
|
|
|
|
// Telemetry holds the latest flight-controller / battery values for a device.
|
|
// Pointers distinguish "not yet reported" (nil) from a genuine zero value.
|
|
type Telemetry struct {
|
|
SatelliteCount *int `json:"satelliteCount,omitempty"`
|
|
IsFlying *bool `json:"isFlying,omitempty"`
|
|
FlightMode *string `json:"flightMode,omitempty"`
|
|
Altitude *float64 `json:"altitude,omitempty"`
|
|
Latitude *float64 `json:"latitude,omitempty"`
|
|
Longitude *float64 `json:"longitude,omitempty"`
|
|
VelocityX *float64 `json:"velocityX,omitempty"`
|
|
VelocityY *float64 `json:"velocityY,omitempty"`
|
|
VelocityZ *float64 `json:"velocityZ,omitempty"`
|
|
BatteryPercent *int `json:"batteryPercent,omitempty"`
|
|
}
|
|
|
|
// DeviceState is the server's aggregated view of one app/drone.
|
|
type DeviceState struct {
|
|
DeviceID string `json:"deviceId"`
|
|
Online bool `json:"online"` // app's websocket is connected to the server
|
|
Connected bool `json:"connected"` // a drone is connected to the app
|
|
Model string `json:"model"`
|
|
Registration string `json:"registration"`
|
|
Telemetry Telemetry `json:"telemetry"`
|
|
LastSeenMs int64 `json:"lastSeenMs"`
|
|
}
|
|
|
|
// TrackPoint is one sample of the drone's GPS track (for the map trail).
|
|
type TrackPoint struct {
|
|
Lat float64 `json:"lat"`
|
|
Lng float64 `json:"lng"`
|
|
Alt float64 `json:"alt"`
|
|
TS int64 `json:"ts"`
|
|
}
|
|
|
|
// ServerToUI is the message a dashboard receives over /ws/ui.
|
|
type ServerToUI struct {
|
|
Type string `json:"type"` // "snapshot" | "update" | "removed"
|
|
Device *DeviceState `json:"device,omitempty"`
|
|
Devices []*DeviceState `json:"devices,omitempty"`
|
|
DeviceID string `json:"deviceId,omitempty"` // for "removed"
|
|
Event map[string]any `json:"event,omitempty"` // the raw device event that triggered this
|
|
TS int64 `json:"ts"`
|
|
}
|
|
|
|
// Command is what the server pushes down to a device over /ws/device.
|
|
type Command struct {
|
|
Type string `json:"type"` // always "command"
|
|
Command string `json:"command"`
|
|
Payload map[string]any `json:"payload,omitempty"`
|
|
TS int64 `json:"ts"`
|
|
}
|
|
|
|
// toFloat coerces a JSON-decoded value into a float64.
|
|
func toFloat(v any) (float64, bool) {
|
|
switch n := v.(type) {
|
|
case float64:
|
|
return n, true
|
|
case float32:
|
|
return float64(n), true
|
|
case int:
|
|
return float64(n), true
|
|
case int64:
|
|
return float64(n), true
|
|
case json.Number:
|
|
f, err := n.Float64()
|
|
return f, err == nil
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// toInt coerces a JSON-decoded value into an int.
|
|
func toInt(v any) (int, bool) {
|
|
if f, ok := toFloat(v); ok {
|
|
return int(f), true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// applyTelemetry copies any present telemetry fields from a raw event map.
|
|
func applyTelemetry(t *Telemetry, raw map[string]any) {
|
|
if v, ok := toInt(raw["satelliteCount"]); ok {
|
|
t.SatelliteCount = &v
|
|
}
|
|
if v, ok := raw["isFlying"].(bool); ok {
|
|
t.IsFlying = &v
|
|
}
|
|
if v, ok := raw["flightMode"].(string); ok {
|
|
t.FlightMode = &v
|
|
}
|
|
if v, ok := toFloat(raw["altitude"]); ok {
|
|
t.Altitude = &v
|
|
}
|
|
if v, ok := toFloat(raw["latitude"]); ok {
|
|
t.Latitude = &v
|
|
}
|
|
if v, ok := toFloat(raw["longitude"]); ok {
|
|
t.Longitude = &v
|
|
}
|
|
if v, ok := toFloat(raw["velocityX"]); ok {
|
|
t.VelocityX = &v
|
|
}
|
|
if v, ok := toFloat(raw["velocityY"]); ok {
|
|
t.VelocityY = &v
|
|
}
|
|
if v, ok := toFloat(raw["velocityZ"]); ok {
|
|
t.VelocityZ = &v
|
|
}
|
|
}
|