chore: release v0.11.2

This commit is contained in:
2026-06-17 23:15:12 +02:00
parent 8b1609f5ce
commit b2a8b1946f
8 changed files with 81 additions and 25 deletions
+51 -19
View File
@@ -42,6 +42,7 @@ func (a *App) SetLiveStatusEnabled(on bool) error {
return err
}
if on {
applog.Printf("livestatus: enabled (logbook backend=%q, mysql conn=%v)", a.dbBackend, a.logDb != nil)
go a.publishLiveStatus() // show up right away
} else {
a.clearLiveStatus()
@@ -52,16 +53,13 @@ func (a *App) SetLiveStatusEnabled(on bool) error {
// liveStatusLoop heartbeats the current activity while enabled. Started once at
// startup; cheap no-op when disabled or not on MySQL.
func (a *App) liveStatusLoop() {
defer func() { _ = recover() }() // never crash the app from here
applog.Printf("livestatus: loop started")
a.publishLiveStatus() // attempt immediately, don't wait the first tick
t := time.NewTicker(15 * time.Second)
defer t.Stop()
for {
select {
case <-a.ctx.Done():
a.clearLiveStatus()
return
case <-t.C:
a.publishLiveStatus()
}
for range t.C {
a.publishLiveStatus()
}
}
@@ -71,28 +69,47 @@ func (a *App) liveStatusActive() bool {
}
// liveStatusOperator returns this instance's operator id (the operator callsign,
// falling back to the station callsign for a single-op setup).
// falling back to the station callsign for a single-op setup). The callsign and
// operator live on the ACTIVE PROFILE (station_profiles table), NOT in the
// settings KV — read them there.
func (a *App) liveStatusOperator() (op, station string) {
if a.settings == nil {
if a.profiles == nil {
return "", ""
}
o, _ := a.settings.Get(a.ctx, keyStationOperator)
s, _ := a.settings.Get(a.ctx, keyStationCallsign)
op = strings.ToUpper(strings.TrimSpace(o))
station = strings.ToUpper(strings.TrimSpace(s))
p, err := a.profiles.Active(a.ctx)
if err != nil {
return "", ""
}
station = strings.ToUpper(strings.TrimSpace(p.Callsign))
op = strings.ToUpper(strings.TrimSpace(p.Operator))
if op == "" {
op = station
}
return op, station
}
// publishLiveStatus upserts this operator's current activity. Best effort.
// ReportLiveActivity is called by the UI with the current entry-strip freq/band/
// mode, used as a fallback for live status when the CAT isn't connected.
func (a *App) ReportLiveActivity(freqHz int64, band, mode string) {
a.liveActMu.Lock()
a.liveFreqHz = freqHz
a.liveBand = strings.ToUpper(strings.TrimSpace(band))
a.liveMode = strings.ToUpper(strings.TrimSpace(mode))
a.liveActMu.Unlock()
}
// publishLiveStatus upserts this operator's current activity. Best effort, with
// explicit logging so a silent no-op is diagnosable.
func (a *App) publishLiveStatus() {
if !a.liveStatusActive() {
return
if a.logDb == nil || a.dbBackend != "mysql" {
return // not a MySQL logbook — nothing to do (silent, runs every 15s)
}
if !a.GetLiveStatusEnabled() {
return // disabled (silent)
}
op, station := a.liveStatusOperator()
if op == "" {
applog.Printf("livestatus: nothing published — no operator/callsign set (Settings → Station)")
return
}
var freqHz int64
@@ -103,8 +120,21 @@ func (a *App) publishLiveStatus() {
freqHz, band, mode = st.FreqHz, st.Band, st.Mode
}
}
// Fall back to whatever the entry strip last reported (so band/mode/freq are
// published even when the CAT isn't connected).
a.liveActMu.Lock()
if freqHz == 0 {
freqHz = a.liveFreqHz
}
if band == "" {
band = a.liveBand
}
if mode == "" {
mode = a.liveMode
}
a.liveActMu.Unlock()
if err := a.ensureLiveStatusTable(); err != nil {
applog.Printf("livestatus: ensure table: %v", err)
applog.Printf("livestatus: CREATE TABLE failed: %v", err)
return
}
_, err := a.logDb.ExecContext(a.ctx,
@@ -114,8 +144,10 @@ func (a *App) publishLiveStatus() {
"band=VALUES(band), mode=VALUES(mode), updated_at=UTC_TIMESTAMP()",
op, station, freqHz, band, mode)
if err != nil {
applog.Printf("livestatus: publish: %v", err)
applog.Printf("livestatus: INSERT failed: %v", err)
return
}
applog.Printf("livestatus: published op=%s station=%s %dHz %s %s", op, station, freqHz, band, mode)
}
func (a *App) ensureLiveStatusTable() error {