Restructure assets to top-level package and add Reset Map button
- Move assets from internal/assets to top-level assets/ package for clean embed directive - Consolidate all static files in single location (assets/static/) - Remove duplicate static file locations to maintain single source of truth - Add Reset Map button to map controls with full functionality - Implement resetMap() method to return map to calculated origin position - Store origin in this.mapOrigin for reset functionality - Fix go:embed pattern to work without parent directory references 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
af9bf8ecac
commit
1425f0a018
20 changed files with 263 additions and 2139 deletions
|
|
@ -6,7 +6,7 @@ import (
|
|||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
||||
"skyview/internal/beast"
|
||||
"skyview/internal/merger"
|
||||
"skyview/internal/modes"
|
||||
|
|
@ -23,7 +23,7 @@ type BeastClient struct {
|
|||
errChan chan error
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
|
||||
|
||||
reconnectDelay time.Duration
|
||||
maxReconnect time.Duration
|
||||
}
|
||||
|
|
@ -60,9 +60,9 @@ func (c *BeastClient) Stop() {
|
|||
// run is the main client loop
|
||||
func (c *BeastClient) run(ctx context.Context) {
|
||||
defer c.wg.Done()
|
||||
|
||||
|
||||
reconnectDelay := c.reconnectDelay
|
||||
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
@ -71,16 +71,16 @@ func (c *BeastClient) run(ctx context.Context) {
|
|||
return
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
// Connect to Beast TCP stream
|
||||
addr := fmt.Sprintf("%s:%d", c.source.Host, c.source.Port)
|
||||
fmt.Printf("Connecting to Beast stream at %s (%s)...\n", addr, c.source.Name)
|
||||
|
||||
|
||||
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to connect to %s: %v\n", c.source.Name, err)
|
||||
c.source.Active = false
|
||||
|
||||
|
||||
// Exponential backoff
|
||||
time.Sleep(reconnectDelay)
|
||||
if reconnectDelay < c.maxReconnect {
|
||||
|
|
@ -88,21 +88,21 @@ func (c *BeastClient) run(ctx context.Context) {
|
|||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
c.conn = conn
|
||||
c.source.Active = true
|
||||
reconnectDelay = c.reconnectDelay // Reset backoff
|
||||
|
||||
|
||||
fmt.Printf("Connected to %s at %s\n", c.source.Name, addr)
|
||||
|
||||
|
||||
// Create parser for this connection
|
||||
c.parser = beast.NewParser(conn, c.source.ID)
|
||||
|
||||
|
||||
// Start processing messages
|
||||
c.wg.Add(2)
|
||||
go c.readMessages()
|
||||
go c.processMessages()
|
||||
|
||||
|
||||
// Wait for disconnect
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
@ -116,7 +116,7 @@ func (c *BeastClient) run(ctx context.Context) {
|
|||
c.conn.Close()
|
||||
c.source.Active = false
|
||||
}
|
||||
|
||||
|
||||
// Wait for goroutines to finish
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ func (c *BeastClient) readMessages() {
|
|||
// processMessages decodes and merges aircraft data
|
||||
func (c *BeastClient) processMessages() {
|
||||
defer c.wg.Done()
|
||||
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.stopChan:
|
||||
|
|
@ -140,13 +140,13 @@ func (c *BeastClient) processMessages() {
|
|||
if msg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Decode Mode S message
|
||||
aircraft, err := c.decoder.Decode(msg.Data)
|
||||
if err != nil {
|
||||
continue // Skip invalid messages
|
||||
}
|
||||
|
||||
|
||||
// Update merger with new data
|
||||
c.merger.UpdateAircraft(
|
||||
c.source.ID,
|
||||
|
|
@ -154,7 +154,7 @@ func (c *BeastClient) processMessages() {
|
|||
msg.GetSignalStrength(),
|
||||
msg.ReceivedAt,
|
||||
)
|
||||
|
||||
|
||||
// Update source statistics
|
||||
c.source.Messages++
|
||||
}
|
||||
|
|
@ -180,10 +180,10 @@ func NewMultiSourceClient(merger *merger.Merger) *MultiSourceClient {
|
|||
func (m *MultiSourceClient) AddSource(source *merger.Source) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
||||
// Register source with merger
|
||||
m.merger.AddSource(source)
|
||||
|
||||
|
||||
// Create and start client
|
||||
client := NewBeastClient(source, m.merger)
|
||||
m.clients = append(m.clients, client)
|
||||
|
|
@ -193,11 +193,11 @@ func (m *MultiSourceClient) AddSource(source *merger.Source) {
|
|||
func (m *MultiSourceClient) Start(ctx context.Context) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
|
||||
for _, client := range m.clients {
|
||||
client.Start(ctx)
|
||||
}
|
||||
|
||||
|
||||
// Start cleanup routine
|
||||
go m.cleanupRoutine(ctx)
|
||||
}
|
||||
|
|
@ -206,7 +206,7 @@ func (m *MultiSourceClient) Start(ctx context.Context) {
|
|||
func (m *MultiSourceClient) Stop() {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
|
||||
for _, client := range m.clients {
|
||||
client.Stop()
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ func (m *MultiSourceClient) Stop() {
|
|||
func (m *MultiSourceClient) cleanupRoutine(ctx context.Context) {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
@ -231,9 +231,9 @@ func (m *MultiSourceClient) cleanupRoutine(ctx context.Context) {
|
|||
func (m *MultiSourceClient) GetStatistics() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
|
||||
stats := m.merger.GetStatistics()
|
||||
|
||||
|
||||
// Add client-specific stats
|
||||
activeClients := 0
|
||||
for _, client := range m.clients {
|
||||
|
|
@ -241,9 +241,9 @@ func (m *MultiSourceClient) GetStatistics() map[string]interface{} {
|
|||
activeClients++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stats["active_clients"] = activeClients
|
||||
stats["total_clients"] = len(m.clients)
|
||||
|
||||
|
||||
return stats
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue