feat: When a call is cleared from MSHV/WSJTx/JTDX it also clears the call field of OpsLog
This commit is contained in:
@@ -8887,6 +8887,12 @@ func (a *App) consumeUDPEvents() {
|
|||||||
"source": ev.Source,
|
"source": ev.Source,
|
||||||
"adif": ev.LoggedADIF,
|
"adif": ev.LoggedADIF,
|
||||||
})
|
})
|
||||||
|
case ev.ClearCall:
|
||||||
|
applog.Printf("udp: emit udp:clear_call (DX Call cleared in the digital app)\n")
|
||||||
|
wruntime.EventsEmit(a.ctx, "udp:clear_call", map[string]any{
|
||||||
|
"service": string(ev.Service),
|
||||||
|
"source": ev.Source,
|
||||||
|
})
|
||||||
case ev.DXCall != "" && ev.Service == udp.ServiceRemoteCall:
|
case ev.DXCall != "" && ev.Service == udp.ServiceRemoteCall:
|
||||||
applog.Printf("udp: emit udp:remote_call %q\n", ev.DXCall)
|
applog.Printf("udp: emit udp:remote_call %q\n", ev.DXCall)
|
||||||
wruntime.EventsEmit(a.ctx, "udp:remote_call", ev.DXCall)
|
wruntime.EventsEmit(a.ctx, "udp:remote_call", ev.DXCall)
|
||||||
|
|||||||
@@ -1787,6 +1787,12 @@ export default function App() {
|
|||||||
const unsubRC = EventsOn('udp:remote_call', (raw: string) => {
|
const unsubRC = EventsOn('udp:remote_call', (raw: string) => {
|
||||||
if (applyUdpCall(raw, true)) restartRecordingForNewTarget(String(raw ?? '')); // explicit remote pick
|
if (applyUdpCall(raw, true)) restartRecordingForNewTarget(String(raw ?? '')); // explicit remote pick
|
||||||
});
|
});
|
||||||
|
// The DX Call was cleared in WSJT-X / JTDX / MSHV → clear our entry to match.
|
||||||
|
// Only when something is actually in the entry, so an idle digital app doesn't
|
||||||
|
// wipe a call being typed by hand.
|
||||||
|
const unsubClear = EventsOn('udp:clear_call', () => {
|
||||||
|
if (callsignRef.current?.value?.trim() || callsign.trim()) resetEntry();
|
||||||
|
});
|
||||||
// Clicked one of OpsLog's spots on the FlexRadio panadapter → fill the call
|
// Clicked one of OpsLog's spots on the FlexRadio panadapter → fill the call
|
||||||
// (the radio already tuned via trigger_action=Tune, and CAT reads the freq).
|
// (the radio already tuned via trigger_action=Tune, and CAT reads the freq).
|
||||||
// An explicit click always wins over whatever call is currently in the field.
|
// An explicit click always wins over whatever call is currently in the field.
|
||||||
@@ -1811,7 +1817,7 @@ export default function App() {
|
|||||||
else setError('UDP auto-log: ' + msg);
|
else setError('UDP auto-log: ' + msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => { unsubDX?.(); unsubRC?.(); unsubFlexSpot?.(); unsubProg?.(); unsubLog?.(); };
|
return () => { unsubDX?.(); unsubRC?.(); unsubClear?.(); unsubFlexSpot?.(); unsubProg?.(); unsubLog?.(); };
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ type Event struct {
|
|||||||
DecodeFreqHz int64 // RF frequency (dial + audio offset)
|
DecodeFreqHz int64 // RF frequency (dial + audio offset)
|
||||||
DecodeSNR int // reported SNR (dB)
|
DecodeSNR int // reported SNR (dB)
|
||||||
DecodeCQ bool // the decode was a CQ
|
DecodeCQ bool // the decode was a CQ
|
||||||
|
|
||||||
|
// ClearCall is set when a WSJT/JTDX/MSHV Status message reports an EMPTY DX
|
||||||
|
// Call after previously reporting one — i.e. the operator cleared the call in
|
||||||
|
// the digital app. OpsLog clears its entry to match.
|
||||||
|
ClearCall bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is a single inbound UDP listener.
|
// Server is a single inbound UDP listener.
|
||||||
@@ -64,7 +69,8 @@ type Server struct {
|
|||||||
stopped bool
|
stopped bool
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
lastDialHz int64 // WSJT: dial freq from the last Status, added to Decode offsets
|
lastDialHz int64 // WSJT: dial freq from the last Status, added to Decode offsets
|
||||||
|
lastDX string // WSJT: last non-empty DX Call seen, to detect a clear
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer(cfg Config, out chan<- Event) *Server {
|
func newServer(cfg Config, out chan<- Event) *Server {
|
||||||
@@ -213,6 +219,17 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
|
|||||||
ev.Mode = w.Mode
|
ev.Mode = w.Mode
|
||||||
ev.FreqHz = w.FreqHz
|
ev.FreqHz = w.FreqHz
|
||||||
ev.LoggedADIF = w.LoggedADIF
|
ev.LoggedADIF = w.LoggedADIF
|
||||||
|
// A Status with an empty DX Call, right after one that had a call, means the
|
||||||
|
// operator cleared it in WSJT-X / JTDX / MSHV. Fire ONE clear (tracked per
|
||||||
|
// server) — an idle app sends empty Status every second, and we must not
|
||||||
|
// re-clear (which would fight a manual entry) on each of those.
|
||||||
|
s.mu.Lock()
|
||||||
|
prev := s.lastDX
|
||||||
|
s.lastDX = w.DXCall
|
||||||
|
s.mu.Unlock()
|
||||||
|
if w.DXCall == "" && prev != "" {
|
||||||
|
ev.ClearCall = true
|
||||||
|
}
|
||||||
case ServiceADIF:
|
case ServiceADIF:
|
||||||
// JTAlert / GridTracker forward a text ADIF record after a QSO is
|
// JTAlert / GridTracker forward a text ADIF record after a QSO is
|
||||||
// logged. Guard against keep-alive / non-ADIF chatter on the socket:
|
// logged. Guard against keep-alive / non-ADIF chatter on the socket:
|
||||||
@@ -268,8 +285,9 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
|
|||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Empty events are useless; skip.
|
// Empty events are useless; skip — EXCEPT a clear signal, which is meant to be
|
||||||
if ev.DXCall == "" && ev.LoggedADIF == "" && ev.DecodeCall == "" {
|
// empty (the DX Call was cleared in the digital app).
|
||||||
|
if ev.DXCall == "" && ev.LoggedADIF == "" && ev.DecodeCall == "" && !ev.ClearCall {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|||||||
Reference in New Issue
Block a user