update
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"hamlog/internal/lookup"
|
||||
"hamlog/internal/profile"
|
||||
"hamlog/internal/qso"
|
||||
"hamlog/internal/rotator/pst"
|
||||
"hamlog/internal/settings"
|
||||
|
||||
wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
@@ -53,6 +54,11 @@ const (
|
||||
keyCATPollMs = "cat.poll_ms"
|
||||
keyCATDelayMs = "cat.delay_ms" // pause between commands
|
||||
keyCATDigitalDefault = "cat.digital_default" // mode to use when CAT reports DATA
|
||||
|
||||
keyRotatorEnabled = "rotator.enabled"
|
||||
keyRotatorHost = "rotator.host"
|
||||
keyRotatorPort = "rotator.port"
|
||||
keyRotatorHasElevation = "rotator.has_elevation"
|
||||
)
|
||||
|
||||
// CATSettings is the user-tweakable rig-control configuration. Stored as
|
||||
@@ -473,14 +479,14 @@ func (a *App) OpenADIFFile() (string, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func (a *App) ImportADIF(path string) (adif.ImportResult, error) {
|
||||
func (a *App) ImportADIF(path string, skipDuplicates bool) (adif.ImportResult, error) {
|
||||
if a.qso == nil {
|
||||
return adif.ImportResult{}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
if path == "" {
|
||||
return adif.ImportResult{}, fmt.Errorf("empty path")
|
||||
}
|
||||
im := &adif.Importer{Repo: a.qso}
|
||||
im := &adif.Importer{Repo: a.qso, SkipDuplicates: skipDuplicates}
|
||||
return im.ImportFile(a.ctx, path)
|
||||
}
|
||||
|
||||
@@ -936,3 +942,123 @@ func (a *App) DuplicateProfile(id int64, newName string) (profile.Profile, error
|
||||
}
|
||||
return a.profiles.Duplicate(a.ctx, id, newName)
|
||||
}
|
||||
|
||||
// --- Rotator bindings (PstRotator UDP v0) ---
|
||||
|
||||
// RotatorSettings is the JSON shape for the Hardware → Rotator panel.
|
||||
type RotatorSettings struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"` // default 127.0.0.1
|
||||
Port int `json:"port"` // default 12000
|
||||
HasElevation bool `json:"has_elevation"` // include EL in GoTo packets
|
||||
}
|
||||
|
||||
// GetRotatorSettings returns the persisted rotator config with defaults.
|
||||
func (a *App) GetRotatorSettings() (RotatorSettings, error) {
|
||||
out := RotatorSettings{Host: "127.0.0.1", Port: 12000}
|
||||
if a.settings == nil {
|
||||
return out, fmt.Errorf("db not initialized")
|
||||
}
|
||||
m, err := a.settings.GetMany(a.ctx,
|
||||
keyRotatorEnabled, keyRotatorHost, keyRotatorPort, keyRotatorHasElevation)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Enabled = m[keyRotatorEnabled] == "1"
|
||||
if h := m[keyRotatorHost]; h != "" {
|
||||
out.Host = h
|
||||
}
|
||||
if p, _ := strconv.Atoi(m[keyRotatorPort]); p > 0 && p <= 65535 {
|
||||
out.Port = p
|
||||
}
|
||||
out.HasElevation = m[keyRotatorHasElevation] == "1"
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SaveRotatorSettings persists the rotator config. Connection is per-call
|
||||
// (UDP, no socket to (re)open) so no reload step is needed.
|
||||
func (a *App) SaveRotatorSettings(s RotatorSettings) error {
|
||||
if a.settings == nil {
|
||||
return fmt.Errorf("db not initialized")
|
||||
}
|
||||
if s.Host == "" {
|
||||
s.Host = "127.0.0.1"
|
||||
}
|
||||
if s.Port <= 0 || s.Port > 65535 {
|
||||
s.Port = 12000
|
||||
}
|
||||
for k, v := range map[string]string{
|
||||
keyRotatorEnabled: boolStr(s.Enabled),
|
||||
keyRotatorHost: s.Host,
|
||||
keyRotatorPort: strconv.Itoa(s.Port),
|
||||
keyRotatorHasElevation: boolStr(s.HasElevation),
|
||||
} {
|
||||
if err := a.settings.Set(a.ctx, k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rotatorClient returns a fresh PST UDP client built from current settings,
|
||||
// or an error if the rotator is disabled / misconfigured.
|
||||
func (a *App) rotatorClient() (*pst.Client, RotatorSettings, error) {
|
||||
s, err := a.GetRotatorSettings()
|
||||
if err != nil {
|
||||
return nil, s, err
|
||||
}
|
||||
if !s.Enabled {
|
||||
return nil, s, fmt.Errorf("rotator disabled in settings")
|
||||
}
|
||||
return pst.New(s.Host, s.Port), s, nil
|
||||
}
|
||||
|
||||
// RotatorGoTo points the antenna at the given azimuth (and optional
|
||||
// elevation if the rotator is configured for it).
|
||||
func (a *App) RotatorGoTo(az int, el int) error {
|
||||
c, s, err := a.rotatorClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.GoTo(az, s.HasElevation, el)
|
||||
}
|
||||
|
||||
// RotatorStop interrupts any in-progress rotation.
|
||||
func (a *App) RotatorStop() error {
|
||||
c, _, err := a.rotatorClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Stop()
|
||||
}
|
||||
|
||||
// RotatorPark moves the antenna to its parked position (configured in
|
||||
// PstRotator itself).
|
||||
func (a *App) RotatorPark() error {
|
||||
c, _, err := a.rotatorClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Park()
|
||||
}
|
||||
|
||||
// TestRotator sends a no-op GoTo to the rotator's current heading to
|
||||
// verify the UDP link without actually moving the antenna. We use 0° as
|
||||
// the test target — pick a known direction the user expects to see.
|
||||
// Returns nil on success or a descriptive error.
|
||||
func (a *App) TestRotator(s RotatorSettings) error {
|
||||
if s.Host == "" {
|
||||
s.Host = "127.0.0.1"
|
||||
}
|
||||
if s.Port <= 0 || s.Port > 65535 {
|
||||
s.Port = 12000
|
||||
}
|
||||
return pst.New(s.Host, s.Port).GoTo(0, false, -1)
|
||||
}
|
||||
|
||||
func boolStr(b bool) string {
|
||||
if b {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user