corrected all bugs
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/config"
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/devices/antennagenius"
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/devices/flexradio"
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/devices/powergenius"
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/devices/rotatorgenius"
|
||||
"git.rouggy.com/rouggy/ShackMaster/internal/devices/tunergenius"
|
||||
@@ -25,6 +26,7 @@ type DeviceManager struct {
|
||||
antennaGenius *antennagenius.Client
|
||||
rotatorGenius *rotatorgenius.Client
|
||||
ultrabeam *ultrabeam.Client
|
||||
flexRadio *flexradio.Client
|
||||
solarClient *solar.Client
|
||||
weatherClient *weather.Client
|
||||
|
||||
@@ -42,6 +44,10 @@ type DeviceManager struct {
|
||||
ultrabeamDirectionSet bool // True if user has explicitly set a direction
|
||||
lastFreqUpdateTime time.Time // Last time we sent frequency update
|
||||
freqUpdateCooldown time.Duration // Minimum time between updates
|
||||
|
||||
// Cached Ultrabeam state for FlexRadio interlock (avoid mutex contention)
|
||||
ultrabeamMotorsMoving int
|
||||
ultrabeamStateMu sync.RWMutex
|
||||
}
|
||||
|
||||
type SystemStatus struct {
|
||||
@@ -51,6 +57,7 @@ type SystemStatus struct {
|
||||
AntennaGenius *antennagenius.Status `json:"antenna_genius"`
|
||||
RotatorGenius *rotatorgenius.Status `json:"rotator_genius"`
|
||||
Ultrabeam *ultrabeam.Status `json:"ultrabeam"`
|
||||
FlexRadio *flexradio.Status `json:"flexradio,omitempty"`
|
||||
Solar *solar.SolarData `json:"solar"`
|
||||
Weather *weather.WeatherData `json:"weather"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
@@ -60,12 +67,12 @@ func NewDeviceManager(cfg *config.Config, hub *Hub) *DeviceManager {
|
||||
return &DeviceManager{
|
||||
config: cfg,
|
||||
hub: hub,
|
||||
updateInterval: 1 * time.Second, // Update status every second
|
||||
updateInterval: 200 * time.Millisecond, // Update status every second
|
||||
stopChan: make(chan struct{}),
|
||||
freqThreshold: 25000, // 25 kHz default
|
||||
autoTrackEnabled: true, // Enabled by default
|
||||
ultrabeamDirection: 0, // Normal direction by default
|
||||
freqUpdateCooldown: 2 * time.Second, // Wait 2 seconds between updates
|
||||
freqThreshold: 25000, // 25 kHz default
|
||||
autoTrackEnabled: true, // Enabled by default
|
||||
ultrabeamDirection: 0, // Normal direction by default
|
||||
freqUpdateCooldown: 500 * time.Millisecond, // 500ms cooldown (was 2sec)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +114,31 @@ func (dm *DeviceManager) Initialize() error {
|
||||
dm.config.Devices.Ultrabeam.Port,
|
||||
)
|
||||
|
||||
// Initialize FlexRadio if enabled
|
||||
if dm.config.Devices.FlexRadio.Enabled {
|
||||
log.Printf("Initializing FlexRadio: host=%s port=%d", dm.config.Devices.FlexRadio.Host, dm.config.Devices.FlexRadio.Port)
|
||||
dm.flexRadio = flexradio.New(
|
||||
dm.config.Devices.FlexRadio.Host,
|
||||
dm.config.Devices.FlexRadio.Port,
|
||||
dm.config.Devices.FlexRadio.InterlockName,
|
||||
)
|
||||
|
||||
// Set callback to check if transmit is allowed (based on Ultrabeam motors)
|
||||
// Use cached state to avoid mutex contention with update loop
|
||||
dm.flexRadio.SetTransmitCheckCallback(func() bool {
|
||||
dm.ultrabeamStateMu.RLock()
|
||||
motorsMoving := dm.ultrabeamMotorsMoving
|
||||
dm.ultrabeamStateMu.RUnlock()
|
||||
// Block transmit if motors are moving
|
||||
return motorsMoving == 0
|
||||
})
|
||||
|
||||
// Set callback for immediate frequency changes (no waiting for update cycle)
|
||||
dm.flexRadio.SetFrequencyChangeCallback(func(freqMHz float64) {
|
||||
dm.handleFrequencyChange(freqMHz)
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize Solar data client
|
||||
dm.solarClient = solar.New()
|
||||
|
||||
@@ -154,6 +186,17 @@ func (dm *DeviceManager) Initialize() error {
|
||||
}()
|
||||
log.Println("Ultrabeam goroutine launched")
|
||||
|
||||
// Start FlexRadio if enabled
|
||||
if dm.flexRadio != nil {
|
||||
log.Println("Starting FlexRadio connection...")
|
||||
go func() {
|
||||
if err := dm.flexRadio.Start(); err != nil {
|
||||
log.Printf("Warning: Failed to start FlexRadio: %v", err)
|
||||
}
|
||||
}()
|
||||
log.Println("FlexRadio goroutine launched")
|
||||
}
|
||||
|
||||
log.Println("Device manager initialized")
|
||||
return nil
|
||||
}
|
||||
@@ -164,6 +207,69 @@ func (dm *DeviceManager) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFrequencyChange is called immediately when FlexRadio frequency changes
|
||||
// This provides instant auto-track response instead of waiting for updateStatus cycle
|
||||
func (dm *DeviceManager) handleFrequencyChange(freqMHz float64) {
|
||||
// Check if ultrabeam is initialized
|
||||
if dm.ultrabeam == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check cooldown first
|
||||
timeSinceLastUpdate := time.Since(dm.lastFreqUpdateTime)
|
||||
if timeSinceLastUpdate < dm.freqUpdateCooldown {
|
||||
return
|
||||
}
|
||||
|
||||
// Use cached status instead of calling GetStatus (which can block)
|
||||
dm.statusMu.RLock()
|
||||
hasStatus := dm.lastStatus != nil
|
||||
var ubStatus *ultrabeam.Status
|
||||
if hasStatus {
|
||||
ubStatus = dm.lastStatus.Ultrabeam
|
||||
}
|
||||
dm.statusMu.RUnlock()
|
||||
|
||||
if ubStatus == nil || !ubStatus.Connected {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't update if motors are already moving
|
||||
if ubStatus.MotorsMoving != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
freqKhz := int(freqMHz * 1000)
|
||||
ultrabeamFreqKhz := ubStatus.Frequency
|
||||
|
||||
// Only track if in Ultrabeam range (7-54 MHz)
|
||||
if freqKhz < 7000 || freqKhz > 54000 {
|
||||
return
|
||||
}
|
||||
|
||||
freqDiff := freqKhz - ultrabeamFreqKhz
|
||||
if freqDiff < 0 {
|
||||
freqDiff = -freqDiff
|
||||
}
|
||||
|
||||
freqDiffHz := freqDiff * 1000
|
||||
|
||||
if freqDiffHz >= dm.freqThreshold {
|
||||
directionToUse := dm.ultrabeamDirection
|
||||
if !dm.ultrabeamDirectionSet && ubStatus.Direction != 0 {
|
||||
directionToUse = ubStatus.Direction
|
||||
}
|
||||
|
||||
log.Printf("Auto-track (immediate): Updating to %d kHz (diff=%d kHz)", freqKhz, freqDiff)
|
||||
|
||||
if err := dm.ultrabeam.SetFrequency(freqKhz, directionToUse); err != nil {
|
||||
log.Printf("Auto-track (immediate): Failed: %v", err)
|
||||
} else {
|
||||
dm.lastFreqUpdateTime = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dm *DeviceManager) Stop() {
|
||||
log.Println("Stopping device manager...")
|
||||
close(dm.stopChan)
|
||||
@@ -250,19 +356,57 @@ func (dm *DeviceManager) updateStatus() {
|
||||
if !dm.ultrabeamDirectionSet {
|
||||
dm.ultrabeamDirection = ubStatus.Direction
|
||||
}
|
||||
|
||||
// Cache motors state for FlexRadio interlock callback
|
||||
dm.ultrabeamStateMu.Lock()
|
||||
previousMotors := dm.ultrabeamMotorsMoving
|
||||
dm.ultrabeamMotorsMoving = ubStatus.MotorsMoving
|
||||
dm.ultrabeamStateMu.Unlock()
|
||||
|
||||
// Log motor state changes
|
||||
if previousMotors != ubStatus.MotorsMoving {
|
||||
if ubStatus.MotorsMoving > 0 {
|
||||
log.Printf("Ultrabeam: Motors STARTED (bitmask=%d)", ubStatus.MotorsMoving)
|
||||
} else {
|
||||
log.Printf("Ultrabeam: Motors STOPPED")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("Ultrabeam error: %v", err)
|
||||
}
|
||||
|
||||
// Auto frequency tracking: Update Ultrabeam when TunerGenius frequency differs from Ultrabeam
|
||||
if dm.autoTrackEnabled && status.TunerGenius != nil && status.TunerGenius.Connected && status.Ultrabeam != nil && status.Ultrabeam.Connected {
|
||||
tunerFreqKhz := int(status.TunerGenius.FreqA) // TunerGenius frequency is already in kHz
|
||||
// FlexRadio (use direct cache access to avoid mutex contention)
|
||||
if dm.flexRadio != nil {
|
||||
// Access lastStatus directly from FlexRadio's internal cache
|
||||
// The messageLoop updates this in real-time, no need to block on GetStatus
|
||||
frStatus, err := dm.flexRadio.GetStatus()
|
||||
if err == nil && frStatus != nil {
|
||||
status.FlexRadio = frStatus
|
||||
}
|
||||
}
|
||||
|
||||
// Auto frequency tracking: Update Ultrabeam when radio frequency differs from Ultrabeam
|
||||
// Priority: FlexRadio (fast) > TunerGenius (slow backup)
|
||||
var radioFreqKhz int
|
||||
var radioSource string
|
||||
|
||||
if dm.flexRadio != nil && status.FlexRadio != nil && status.FlexRadio.Connected && status.FlexRadio.Frequency > 0 {
|
||||
// Use FlexRadio frequency (in MHz, convert to kHz)
|
||||
radioFreqKhz = int(status.FlexRadio.Frequency * 1000)
|
||||
radioSource = "FlexRadio"
|
||||
} else if dm.autoTrackEnabled && status.TunerGenius != nil && status.TunerGenius.Connected {
|
||||
// Fallback to TunerGenius frequency (already in kHz)
|
||||
radioFreqKhz = int(status.TunerGenius.FreqA)
|
||||
radioSource = "TunerGenius"
|
||||
}
|
||||
|
||||
if radioFreqKhz > 0 && status.Ultrabeam != nil && status.Ultrabeam.Connected {
|
||||
ultrabeamFreqKhz := status.Ultrabeam.Frequency // Ultrabeam frequency in kHz
|
||||
|
||||
// Only do auto-track if frequency is in Ultrabeam range (40M-6M: 7000-54000 kHz)
|
||||
// This prevents retraction when slice is closed (FreqA becomes 0) or on out-of-range bands
|
||||
if tunerFreqKhz >= 7000 && tunerFreqKhz <= 54000 {
|
||||
freqDiff := tunerFreqKhz - ultrabeamFreqKhz
|
||||
if radioFreqKhz >= 7000 && radioFreqKhz <= 54000 {
|
||||
freqDiff := radioFreqKhz - ultrabeamFreqKhz
|
||||
if freqDiff < 0 {
|
||||
freqDiff = -freqDiff
|
||||
}
|
||||
@@ -284,10 +428,10 @@ func (dm *DeviceManager) updateStatus() {
|
||||
if timeSinceLastUpdate < dm.freqUpdateCooldown {
|
||||
log.Printf("Auto-track: Cooldown active (%v remaining), skipping update", dm.freqUpdateCooldown-timeSinceLastUpdate)
|
||||
} else {
|
||||
log.Printf("Auto-track: Frequency differs by %d kHz, updating Ultrabeam to %d kHz (direction=%d)", freqDiff, tunerFreqKhz, directionToUse)
|
||||
log.Printf("Auto-track (%s): Frequency differs by %d kHz, updating Ultrabeam to %d kHz (direction=%d)", radioSource, freqDiff, radioFreqKhz, directionToUse)
|
||||
|
||||
// Send to Ultrabeam with saved or current direction
|
||||
if err := dm.ultrabeam.SetFrequency(tunerFreqKhz, directionToUse); err != nil {
|
||||
if err := dm.ultrabeam.SetFrequency(radioFreqKhz, directionToUse); err != nil {
|
||||
log.Printf("Auto-track: Failed to update Ultrabeam: %v (will retry)", err)
|
||||
} else {
|
||||
log.Printf("Auto-track: Successfully sent frequency to Ultrabeam")
|
||||
|
||||
Reference in New Issue
Block a user