chore: release v0.20.8
This commit is contained in:
@@ -49,6 +49,7 @@ import (
|
||||
"hamlog/internal/qslcard"
|
||||
"hamlog/internal/qso"
|
||||
"hamlog/internal/relaydev"
|
||||
"hamlog/internal/rotator/gs232"
|
||||
"hamlog/internal/rotator/pst"
|
||||
"hamlog/internal/rotgenius"
|
||||
"hamlog/internal/settings"
|
||||
@@ -158,8 +159,10 @@ const (
|
||||
keyRotatorHost = "rotator.host"
|
||||
keyRotatorPort = "rotator.port"
|
||||
keyRotatorHasElevation = "rotator.has_elevation"
|
||||
keyRotatorType = "rotator.type" // "pst" (PstRotator UDP) | "rotgenius" (4O3A native TCP)
|
||||
keyRotatorNum = "rotator.num" // Rotator Genius rotator index: 1 or 2
|
||||
keyRotatorType = "rotator.type" // "pst" (PstRotator UDP) | "rotgenius" (4O3A native TCP) | "arco" (microHAM ARCO, GS-232A)
|
||||
keyRotatorNum = "rotator.num" // Rotator Genius rotator index: 1 or 2
|
||||
keyRotatorTransport = "rotator.transport" // arco: "tcp" (LAN) | "serial" (USB virtual COM)
|
||||
keyRotatorComPort = "rotator.com_port" // arco serial transport
|
||||
|
||||
// Motorized antenna (Ultrabeam or SteppIR) — Hardware → Antenna. Keys keep the
|
||||
// "ultrabeam." prefix for backward compatibility with configs saved before the
|
||||
@@ -11283,11 +11286,13 @@ func (a *App) DuplicateProfile(id int64, newName string) (profile.Profile, error
|
||||
// RotatorSettings is the JSON shape for the Hardware → Rotator panel.
|
||||
type RotatorSettings struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Type string `json:"type"` // "pst" (PstRotator UDP) | "rotgenius" (4O3A native TCP)
|
||||
Type string `json:"type"` // "pst" (PstRotator UDP) | "rotgenius" (4O3A native TCP) | "arco" (microHAM ARCO, Yaesu GS-232A over TCP)
|
||||
Host string `json:"host"` // default 127.0.0.1
|
||||
Port int `json:"port"` // default 12000 (pst) / 9006 (rotgenius)
|
||||
Port int `json:"port"` // default 12000 (pst) / 9006 (rotgenius) / 4001 (arco — must match the port set in ARCO's LAN CONTROL PROTOCOL)
|
||||
HasElevation bool `json:"has_elevation"` // include EL in GoTo packets (PstRotator)
|
||||
RotatorNum int `json:"rotator_num"` // Rotator Genius index: 1 or 2
|
||||
Transport string `json:"transport"` // arco: "tcp" (LAN) | "serial" (USB virtual COM)
|
||||
ComPort string `json:"com_port"` // arco serial transport
|
||||
}
|
||||
|
||||
// GetRotatorSettings returns the persisted rotator config with defaults.
|
||||
@@ -11297,12 +11302,13 @@ func (a *App) GetRotatorSettings() (RotatorSettings, error) {
|
||||
return out, fmt.Errorf("db not initialized")
|
||||
}
|
||||
m, err := a.settings.GetMany(a.ctx,
|
||||
keyRotatorEnabled, keyRotatorHost, keyRotatorPort, keyRotatorHasElevation, keyRotatorType, keyRotatorNum)
|
||||
keyRotatorEnabled, keyRotatorHost, keyRotatorPort, keyRotatorHasElevation, keyRotatorType, keyRotatorNum,
|
||||
keyRotatorTransport, keyRotatorComPort)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Enabled = m[keyRotatorEnabled] == "1"
|
||||
if t := m[keyRotatorType]; t == "rotgenius" || t == "pst" {
|
||||
if t := m[keyRotatorType]; t == "rotgenius" || t == "pst" || t == "arco" {
|
||||
out.Type = t
|
||||
}
|
||||
if h := m[keyRotatorHost]; h != "" {
|
||||
@@ -11312,11 +11318,18 @@ func (a *App) GetRotatorSettings() (RotatorSettings, error) {
|
||||
out.Port = p
|
||||
} else if out.Type == "rotgenius" {
|
||||
out.Port = 9006 // native default when no port stored yet
|
||||
} else if out.Type == "arco" {
|
||||
out.Port = 4001 // placeholder — the real number is set in ARCO's LAN menu
|
||||
}
|
||||
out.HasElevation = m[keyRotatorHasElevation] == "1"
|
||||
if rn, _ := strconv.Atoi(m[keyRotatorNum]); rn == 2 {
|
||||
out.RotatorNum = 2
|
||||
}
|
||||
out.Transport = "tcp"
|
||||
if m[keyRotatorTransport] == "serial" {
|
||||
out.Transport = "serial"
|
||||
}
|
||||
out.ComPort = m[keyRotatorComPort]
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -11326,18 +11339,21 @@ func (a *App) SaveRotatorSettings(s RotatorSettings) error {
|
||||
if a.settings == nil {
|
||||
return fmt.Errorf("db not initialized")
|
||||
}
|
||||
if s.Type != "rotgenius" {
|
||||
if s.Type != "rotgenius" && s.Type != "arco" {
|
||||
s.Type = "pst"
|
||||
}
|
||||
if s.Host == "" {
|
||||
s.Host = "127.0.0.1"
|
||||
}
|
||||
if s.Port <= 0 || s.Port > 65535 {
|
||||
s.Port = map[string]int{"rotgenius": 9006, "pst": 12000}[s.Type]
|
||||
s.Port = map[string]int{"rotgenius": 9006, "pst": 12000, "arco": 4001}[s.Type]
|
||||
}
|
||||
if s.RotatorNum != 2 {
|
||||
s.RotatorNum = 1
|
||||
}
|
||||
if s.Transport != "serial" {
|
||||
s.Transport = "tcp"
|
||||
}
|
||||
for k, v := range map[string]string{
|
||||
keyRotatorEnabled: boolStr(s.Enabled),
|
||||
keyRotatorType: s.Type,
|
||||
@@ -11345,6 +11361,8 @@ func (a *App) SaveRotatorSettings(s RotatorSettings) error {
|
||||
keyRotatorPort: strconv.Itoa(s.Port),
|
||||
keyRotatorHasElevation: boolStr(s.HasElevation),
|
||||
keyRotatorNum: strconv.Itoa(s.RotatorNum),
|
||||
keyRotatorTransport: s.Transport,
|
||||
keyRotatorComPort: strings.TrimSpace(s.ComPort),
|
||||
} {
|
||||
if err := a.settings.Set(a.ctx, k, v); err != nil {
|
||||
return err
|
||||
@@ -11353,6 +11371,15 @@ func (a *App) SaveRotatorSettings(s RotatorSettings) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// arcoClient builds the GS-232 client for the configured ARCO transport:
|
||||
// USB virtual COM (serial) or the LAN CONTROL PROTOCOL TCP port.
|
||||
func arcoClient(s RotatorSettings) *gs232.Client {
|
||||
if s.Transport == "serial" {
|
||||
return gs232.NewSerial(s.ComPort)
|
||||
}
|
||||
return gs232.New(s.Host, s.Port)
|
||||
}
|
||||
|
||||
// RotatorHeading is the live antenna heading for the status bar.
|
||||
type RotatorHeading struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -11378,6 +11405,13 @@ func (a *App) GetRotatorHeading() RotatorHeading {
|
||||
}
|
||||
return RotatorHeading{Enabled: true, OK: true, Azimuth: st.Azimuth, Raw: raw}
|
||||
}
|
||||
if s.Type == "arco" {
|
||||
az, raw, herr := arcoClient(s).Heading()
|
||||
if herr != nil {
|
||||
return RotatorHeading{Enabled: true, OK: false, Raw: herr.Error()}
|
||||
}
|
||||
return RotatorHeading{Enabled: true, OK: true, Azimuth: az, Raw: raw}
|
||||
}
|
||||
az, raw, herr := pst.New(s.Host, s.Port).Heading()
|
||||
if herr != nil {
|
||||
return RotatorHeading{Enabled: true, OK: false, Raw: raw}
|
||||
@@ -11398,6 +11432,9 @@ func (a *App) RotatorGoTo(az int, el int) error {
|
||||
if s.Type == "rotgenius" {
|
||||
return rotgenius.New(s.Host, s.Port).GoTo(s.RotatorNum, az)
|
||||
}
|
||||
if s.Type == "arco" {
|
||||
return arcoClient(s).GoTo(az)
|
||||
}
|
||||
return pst.New(s.Host, s.Port).GoTo(az, s.HasElevation, el)
|
||||
}
|
||||
|
||||
@@ -11413,6 +11450,9 @@ func (a *App) RotatorStop() error {
|
||||
if s.Type == "rotgenius" {
|
||||
return rotgenius.New(s.Host, s.Port).Stop()
|
||||
}
|
||||
if s.Type == "arco" {
|
||||
return arcoClient(s).Stop()
|
||||
}
|
||||
return pst.New(s.Host, s.Port).Stop()
|
||||
}
|
||||
|
||||
@@ -11430,6 +11470,9 @@ func (a *App) RotatorPark() error {
|
||||
if s.Type == "rotgenius" {
|
||||
return fmt.Errorf("park is a PstRotator feature; not available on the Rotator Genius")
|
||||
}
|
||||
if s.Type == "arco" {
|
||||
return fmt.Errorf("park is a PstRotator feature; not available over the ARCO GS-232 link")
|
||||
}
|
||||
return pst.New(s.Host, s.Port).Park()
|
||||
}
|
||||
|
||||
@@ -11454,6 +11497,18 @@ func (a *App) TestRotator(s RotatorSettings) error {
|
||||
// reply, so a rejected command surfaces as an error instead of a false OK.
|
||||
return rotgenius.New(s.Host, s.Port).GoTo(rn, 0)
|
||||
}
|
||||
if s.Type == "arco" {
|
||||
if s.Port <= 0 || s.Port > 65535 {
|
||||
s.Port = 4001
|
||||
}
|
||||
if s.Transport == "serial" && strings.TrimSpace(s.ComPort) == "" {
|
||||
return fmt.Errorf("select the ARCO's COM port first")
|
||||
}
|
||||
// A heading query proves the link AND that the ARCO port really speaks
|
||||
// GS-232 — without moving the antenna.
|
||||
_, _, err := arcoClient(s).Heading()
|
||||
return err
|
||||
}
|
||||
if s.Port <= 0 || s.Port > 65535 {
|
||||
s.Port = 12000
|
||||
}
|
||||
@@ -12470,6 +12525,15 @@ func (a *App) GetPGXLStatus() powergenius.Status {
|
||||
return a.pgxl.GetStatus()
|
||||
}
|
||||
|
||||
// PGXLSetOperate puts the PowerGenius XL in OPERATE (true) or STANDBY (false)
|
||||
// over its direct TCP link (no Flex needed).
|
||||
func (a *App) PGXLSetOperate(on bool) error {
|
||||
if a.pgxl == nil {
|
||||
return fmt.Errorf("PowerGenius not connected — enable it in Settings → Amplifier")
|
||||
}
|
||||
return a.pgxl.SetOperate(on)
|
||||
}
|
||||
|
||||
// PGXLSetFanMode sets the amplifier fan mode (STANDARD | CONTEST | BROADCAST).
|
||||
func (a *App) PGXLSetFanMode(mode string) error {
|
||||
if a.pgxl == nil {
|
||||
|
||||
Reference in New Issue
Block a user