remote-shack/internal/driver/dummy.go

217 lines
5.2 KiB
Go

// Package driver implementiert herstellerunabhängige Hardware-Treiber.
// Diese Datei enthält die Dummy-Implementierungen zur Simulation von Hardware ohne physische Geräte.
package driver
import (
"log"
"sync"
"time"
)
// DummyRig simuliert ein Funkgerät im Arbeitsspeicher.
type DummyRig struct {
mu sync.Mutex
connected bool
freq int64
mode string
ptt bool
}
// NewDummyRig erstellt eine neue Instanz eines simulierten Funkgeräts mit Standardwerten.
func NewDummyRig() *DummyRig {
return &DummyRig{
freq: 14250000, // 20m Band Standardfrequenz (14.250 MHz)
mode: "USB",
}
}
func (d *DummyRig) Connect(port string, baud int) error {
d.mu.Lock()
defer d.mu.Unlock()
d.connected = true
log.Printf("[Dummy-Rig] Erfolgreich verbunden via %s (%d Baud)", port, baud)
return nil
}
func (d *DummyRig) Disconnect() error {
d.mu.Lock()
defer d.mu.Unlock()
d.connected = false
log.Println("[Dummy-Rig] Verbindung sauber getrennt.")
return nil
}
func (d *DummyRig) SetFrequency(hz int64) error {
d.mu.Lock()
defer d.mu.Unlock()
d.freq = hz
log.Printf("[Dummy-Rig] Frequenz geändert auf: %d Hz", hz)
return nil
}
func (d *DummyRig) GetFrequency() (int64, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.freq, nil
}
func (d *DummyRig) SetMode(mode string) error {
d.mu.Lock()
defer d.mu.Unlock()
d.mode = mode
log.Printf("[Dummy-Rig] Betriebsart geändert auf: %s", mode)
return nil
}
func (d *DummyRig) GetMode() (string, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.mode, nil
}
func (d *DummyRig) SetPTT(active bool) error {
d.mu.Lock()
defer d.mu.Unlock()
d.ptt = active
log.Printf("[Dummy-Rig] PTT Status geändert: %t (TX = %t)", active, active)
return nil
}
func (d *DummyRig) GetSMeter() (int, error) {
d.mu.Lock()
defer d.mu.Unlock()
// Simuliert ein leichtes Rauschen um S3, wenn kein TX aktiv ist
if d.ptt {
return 0, nil
}
return 3, nil
}
// ----------------------------------------------------------------------------
// DummyRotor simuliert einen Antennenrotor mit einer Trägheits-Animation.
type DummyRotor struct {
mu sync.Mutex
azTarget float64
elTarget float64
azCurrent float64
elCurrent float64
}
// NewDummyRotor erstellt einen simulierten Rotor und startet die Hintergrund-Bewegung.
func NewDummyRotor() *DummyRotor {
r := &DummyRotor{
azCurrent: 180.0, // Startet im Süden
elCurrent: 0.0, // Flach am Horizont
}
r.azTarget = r.azCurrent
r.elTarget = r.elCurrent
// Startet die flüssige Bewegungssimulation im Hintergrund
go r.startMovementSimulation()
return r
}
func (d *DummyRotor) Connect(port string, baud int) error {
log.Printf("[Dummy-Rotor] Erfolgreich verbunden via %s (%d Baud)", port, baud)
return nil
}
func (d *DummyRotor) Disconnect() error {
log.Println("[Dummy-Rotor] Verbindung getrennt.")
return nil
}
func (d *DummyRotor) SetPosition(azimuth, elevation float64) error {
d.mu.Lock()
defer d.mu.Unlock()
d.azTarget = azimuth
d.elTarget = elevation
log.Printf("[Dummy-Rotor] Fahre auf Soll-Position -> Az: %.1f°, El: %.1f°", azimuth, elevation)
return nil
}
func (d *DummyRotor) GetPosition() (az, el float64, err error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.azCurrent, d.elCurrent, nil
}
// startMovementSimulation nähert die Ist-Werte alle 100ms Schritt für Schritt an die Soll-Werte an.
func (d *DummyRotor) startMovementSimulation() {
ticker := time.NewTicker(100 * time.Millisecond)
for range ticker.C {
d.mu.Lock()
// Azimut bewegen (Simuliert 4 Grad pro Sekunde -> 0.4 Grad pro 100ms)
if d.azCurrent < d.azTarget {
d.azCurrent += 0.4
if d.azCurrent > d.azTarget {
d.azCurrent = d.azTarget
}
} else if d.azCurrent > d.azTarget {
d.azCurrent -= 0.4
if d.azCurrent < d.azTarget {
d.azCurrent = d.azTarget
}
}
// Elevation bewegen (Simuliert 2 Grad pro Sekunde -> 0.2 Grad pro 100ms)
if d.elCurrent < d.elTarget {
d.elCurrent += 0.2
if d.elCurrent > d.elTarget {
d.elCurrent = d.elTarget
}
} else if d.elCurrent > d.elTarget {
d.elCurrent -= 0.2
if d.elCurrent < d.elTarget {
d.elCurrent = d.elTarget
}
}
d.mu.Unlock()
}
}
// ----------------------------------------------------------------------------
// DummyRelais simuliert ein Relais-Board im Arbeitsspeicher.
type DummyRelais struct {
mu sync.Mutex
pins map[int]bool
}
// NewDummyRelais erstellt eine neue Instanz einer simulierten Relaiskarte.
func NewDummyRelais() *DummyRelais {
return &DummyRelais{
pins: make(map[int]bool),
}
}
func (d *DummyRelais) Connect(address string) error {
log.Printf("[Dummy-Relais] Board an Adresse %s initialisiert.", address)
return nil
}
func (d *DummyRelais) SetPin(pin int, state bool) error {
d.mu.Lock()
defer d.mu.Unlock()
d.pins[pin] = state
log.Printf("[Dummy-Relais] Pin %d geschaltet auf: %t", pin, state)
return nil
}
func (d *DummyRelais) GetPin(pin int) (bool, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.pins[pin], nil
}
func (d *DummyRelais) GetAllPins() (map[int]bool, error) {
d.mu.Lock()
defer d.mu.Unlock()
// Kopie der Map zurückgeben, um Race-Conditions außerhalb zu verhindern
copyMap := make(map[int]bool)
for k, v := range d.pins {
copyMap[k] = v
}
return copyMap, nil
}