138 lines
3 KiB
Go
138 lines
3 KiB
Go
package driver
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tarm/serial"
|
|
)
|
|
|
|
// ProSistelDriver implementiert das RotorDriver-Interface für ProSistel-Controller.
|
|
type ProSistelDriver struct {
|
|
mu sync.Mutex
|
|
port io.ReadWriteCloser
|
|
}
|
|
|
|
// NewProSistelDriver initialisiert eine neue Instanz des Treibers.
|
|
func NewProSistelDriver() *ProSistelDriver {
|
|
return &ProSistelDriver{}
|
|
}
|
|
|
|
// Connect öffnet die serielle Schnittstelle zum ProSistel-Controller.
|
|
func (p *ProSistelDriver) Connect(portPath string, baud int) error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
if p.port != nil {
|
|
return fmt.Errorf("port bereits geöffnet")
|
|
}
|
|
|
|
config := &serial.Config{
|
|
Name: portPath,
|
|
Baud: baud,
|
|
ReadTimeout: time.Millisecond * 500,
|
|
}
|
|
|
|
sp, err := serial.OpenPort(config)
|
|
if err != nil {
|
|
return fmt.Errorf("fehler beim öffnen von %s: %w", portPath, err)
|
|
}
|
|
|
|
p.port = sp
|
|
return nil
|
|
}
|
|
|
|
// Disconnect schließt die serielle Schnittstelle sauber.
|
|
func (p *ProSistelDriver) Disconnect() error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
if p.port == nil {
|
|
return nil
|
|
}
|
|
|
|
err := p.port.Close()
|
|
p.port = nil
|
|
return err
|
|
}
|
|
|
|
// SetPosition sendet die Zielkoordinaten an den ProSistel-Controller.
|
|
func (p *ProSistelDriver) SetPosition(azimuth, elevation float64) error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
if p.port == nil {
|
|
return fmt.Errorf("treiber nicht verbunden")
|
|
}
|
|
|
|
azInt := int(azimuth + 0.5)
|
|
elInt := int(elevation + 0.5)
|
|
|
|
if azInt < 0 { azInt = 0 }
|
|
if azInt > 360 { azInt = 360 }
|
|
|
|
cmd := fmt.Sprintf("*O%03d*W%03d\r", azInt, elInt)
|
|
|
|
_, err := p.port.Write([]byte(cmd))
|
|
if err != nil {
|
|
return fmt.Errorf("fehler beim senden des set-befehls: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetPosition liest die aktuellen Ist-Werte des Rotors aus.
|
|
func (p *ProSistelDriver) GetPosition() (az, el float64, err error) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
if p.port == nil {
|
|
return 0, 0, fmt.Errorf("treiber nicht verbunden")
|
|
}
|
|
|
|
az, err = p.queryValue("*A000\r", "A")
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("fehler bei azimut-abfrage: %w", err)
|
|
}
|
|
|
|
el, err = p.queryValue("*E000\r", "E")
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("fehler bei elevations-abfrage: %w", err)
|
|
}
|
|
|
|
return az, el, nil
|
|
}
|
|
|
|
// Hilfsfunktion zum Senden einer Anfrage und Parsen der Antwort
|
|
func (p *ProSistelDriver) queryValue(cmd string, prefix string) (float64, error) {
|
|
if _, err := p.port.Write([]byte(cmd)); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
buf := make([]byte, 32)
|
|
n, err := p.port.Read(buf)
|
|
if err != nil && err != io.EOF {
|
|
return 0, err
|
|
}
|
|
|
|
response := strings.TrimSpace(string(buf[:n]))
|
|
if response == "" {
|
|
return 0, fmt.Errorf("keine antwort vom controller erhalten")
|
|
}
|
|
|
|
cleaned := response
|
|
cleaned = strings.ReplaceAll(cleaned, prefix+"=", "")
|
|
cleaned = strings.ReplaceAll(cleaned, prefix, "")
|
|
cleaned = strings.TrimSpace(cleaned)
|
|
|
|
val, err := strconv.ParseFloat(cleaned, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("ungültiges antwortformat '%s': %w", response, err)
|
|
}
|
|
|
|
return val, nil
|
|
}
|