From 6290b1f4efdc5f79f9fd0f91f1234272098d3dd4 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 30 May 2024 19:23:21 +0200 Subject: [PATCH] integrate with HamDXMap to show the location of the currently worked station --- core/app/app.go | 10 +++++- core/cfg/cfg.go | 6 ++++ core/core.go | 9 +++++ core/entry/entry.go | 18 ++++++++++ core/hamdxmap/hamdxmap.go | 73 +++++++++++++++++++++++++++++++++++++++ go.mod | 4 ++- go.sum | 4 +++ ui/glade/contest.glade | 13 ++++++- ui/statusView.go | 5 +++ 9 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 core/hamdxmap/hamdxmap.go diff --git a/core/app/app.go b/core/app/app.go index 065fe0b..d7508bd 100644 --- a/core/app/app.go +++ b/core/app/app.go @@ -22,6 +22,7 @@ import ( "github.com/ftl/hellocontest/core/export/adif" "github.com/ftl/hellocontest/core/export/cabrillo" "github.com/ftl/hellocontest/core/export/csv" + "github.com/ftl/hellocontest/core/hamdxmap" "github.com/ftl/hellocontest/core/keyer" "github.com/ftl/hellocontest/core/logbook" "github.com/ftl/hellocontest/core/newcontest" @@ -66,6 +67,7 @@ type Controller struct { dxccFinder *dxcc.Finder scpFinder *scp.Finder callHistoryFinder *callhistory.Finder + hamDXMap *hamdxmap.HamDXMap VFO *vfo.VFO Logbook *logbook.Logbook @@ -97,6 +99,7 @@ type View interface { // Configuration provides read access to the configuration data. type Configuration interface { LogDirectory() string + HamDXMapPort() int Station() core.Station Contest() core.Contest KeyerSettings() core.KeyerSettings @@ -144,6 +147,7 @@ func (c *Controller) Startup() { c.dxccFinder = dxcc.New() c.scpFinder = scp.New() c.callHistoryFinder = callhistory.New(c.Settings, c.ServiceStatus.StatusChanged) + c.hamDXMap = hamdxmap.NewHamDXMap(c.configuration.HamDXMapPort()) c.Score = score.NewCounter(c.Settings, c.dxccFinder) c.QSOList = logbook.NewQSOList(c.Settings, c.Score) @@ -156,7 +160,7 @@ func (c *Controller) Startup() { c.Bandmap, c.asyncRunner, ) - c.Entry.Notify(c.Bandmap) + c.Entry.Notify(c.hamDXMap) c.Bandmap.Notify(c.Entry) c.QSOList.Notify(c.Entry) c.Score.Notify(c.Bandmap) @@ -233,6 +237,10 @@ func (c *Controller) Startup() { c.scpFinder.WhenAvailable(func() { c.ServiceStatus.StatusChanged(core.SCPService, true) }) + c.ServiceStatus.StatusChanged(core.MapService, true) + c.hamDXMap.WhenStopped(func() { + c.ServiceStatus.StatusChanged(core.MapService, false) + }) c.Entry.StartAutoRefresh() c.Rate.StartAutoRefresh() diff --git a/core/cfg/cfg.go b/core/cfg/cfg.go index aaae64d..193f092 100644 --- a/core/cfg/cfg.go +++ b/core/cfg/cfg.go @@ -19,6 +19,7 @@ const DefaultSpotLifetime = 10 * time.Minute var Default = Data{ LogDirectory: "$HOME/", + HamDXMapPort: 17300, Station: pb.Station{ Callsign: "DL0ABC", Operator: "DL1ABC", @@ -141,6 +142,7 @@ func AbsoluteFilename() string { type Data struct { LogDirectory string `json:"log_directory"` + HamDXMapPort int `json:"ham_dx_map_port"` Station pb.Station `json:"station"` Contest pb.Contest `json:"contest"` Radios []core.Radio `json:"radios"` @@ -159,6 +161,10 @@ func (c *LoadedConfiguration) LogDirectory() string { return os.ExpandEnv(c.data.LogDirectory) } +func (c *LoadedConfiguration) HamDXMapPort() int { + return c.data.HamDXMapPort +} + func (c *LoadedConfiguration) Station() core.Station { result, err := pb.ToStation(c.data.Station) if err != nil { diff --git a/core/core.go b/core/core.go index e343163..d49306d 100644 --- a/core/core.go +++ b/core/core.go @@ -1025,6 +1025,7 @@ const ( DXCCService SCPService CallHistoryService + MapService ) type ServiceStatusListener interface { @@ -1038,3 +1039,11 @@ func (f ServiceStatusListenerFunc) StatusChanged(service Service, available bool } type AsyncRunner func(func()) + +type CallsignEnteredListener interface { + CallsignEntered(callsign string) +} + +type CallsignLoggedListener interface { + CallsignLogged(callsign string, frequency Frequency) +} diff --git a/core/entry/entry.go b/core/entry/entry.go index 2f98137..4b4a7b9 100644 --- a/core/entry/entry.go +++ b/core/entry/entry.go @@ -147,6 +147,22 @@ func (c *Controller) Notify(listener any) { c.listeners = append(c.listeners, listener) } +func (c *Controller) emitCallsignEntered(callsign string) { + for _, l := range c.listeners { + if listener, ok := l.(core.CallsignEnteredListener); ok { + listener.CallsignEntered(callsign) + } + } +} + +func (c *Controller) emitCallsignLogged(callsign string, frequency core.Frequency) { + for _, l := range c.listeners { + if listener, ok := l.(core.CallsignLoggedListener); ok { + listener.CallsignLogged(callsign, frequency) + } + } +} + func (c *Controller) SetView(view View) { if view == nil { c.view = &nullView{} @@ -525,6 +541,7 @@ func (c *Controller) SendQuestion() { } func (c *Controller) enterCallsign(s string) { + c.emitCallsignEntered(c.input.callsign) if c.callinfo != nil { c.callinfo.ShowInfo(c.input.callsign, c.selectedBand, c.selectedMode, c.input.theirExchange) } @@ -674,6 +691,7 @@ func (c *Controller) Log() { } c.logbook.Log(qso) + c.emitCallsignLogged(qso.Callsign.String(), qso.Frequency) if c.workmode == core.SearchPounce { spot := core.Spot{ diff --git a/core/hamdxmap/hamdxmap.go b/core/hamdxmap/hamdxmap.go new file mode 100644 index 0000000..5c5c0cf --- /dev/null +++ b/core/hamdxmap/hamdxmap.go @@ -0,0 +1,73 @@ +package hamdxmap + +import ( + "fmt" + "log" + "net/http" + + "github.com/ftl/godxmap" + "github.com/ftl/hellocontest/core" +) + +type mapServer interface { + Serve() error + Close() error + ShowPartialCall(string) + ShowLoggedCall(string, float64) +} + +type HamDXMap struct { + server mapServer + closed chan struct{} +} + +func NewHamDXMap(port int) *HamDXMap { + result := &HamDXMap{ + closed: make(chan struct{}), + } + + if port > 0 { + result.server = godxmap.NewServer(fmt.Sprintf("localhost:%d", port)) + } else { + result.server = &nullServer{} + } + + go func() { + err := result.server.Serve() + if err != nil && err != http.ErrServerClosed { + log.Printf("cannot serve HamDXMap server: %v", err) + } + close(result.closed) + }() + + return result +} + +func (m *HamDXMap) Stop() { + err := m.server.Close() + if err != nil { + log.Printf("cannot close HamDXMap server: %v", err) + } +} + +func (m *HamDXMap) WhenStopped(callback func()) { + go func() { + <-m.closed + callback() + }() +} + +func (m *HamDXMap) CallsignEntered(callsign string) { + m.server.ShowPartialCall(callsign) +} + +func (m *HamDXMap) CallsignLogged(callsign string, frequency core.Frequency) { + m.server.ShowLoggedCall(callsign, float64(frequency/1000.0)) +} + +type nullServer struct{} + +func (s *nullServer) Serve() error { return nil } +func (s *nullServer) Close() error { return nil } +func (s *nullServer) ShowPartialCall(string) {} +func (s *nullServer) ShowLoggedCall(string, float64) {} diff --git a/go.mod b/go.mod index a99dedf..4db6a47 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ftl/hellocontest -go 1.20 +go 1.22.3 // replace github.com/ftl/cabrillo => ../cabrillo @@ -37,11 +37,13 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/ftl/godxmap v1.0.0 // indirect github.com/ftl/localcopy v0.0.0-20190616142648-8915fb81f0d9 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect + golang.org/x/net v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f234f24..91a29b0 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/ftl/conval v0.7.3 h1:RZCR9pWZa0yaQF4O241B5pq1E+RPWjNiGGx7CuXh45E= github.com/ftl/conval v0.7.3/go.mod h1:Dzx7lU1NMWXgue6ZBRNqNuhVN1kgZb4mu3PQ9hwB1XY= github.com/ftl/gmtry v0.0.0-20201120192810-fa4a1b99fc04 h1:S7z3LXqDYk4avXKj+B2orGWquOo/A+1ZJbcUPx0duLo= github.com/ftl/gmtry v0.0.0-20201120192810-fa4a1b99fc04/go.mod h1:AQpbHYBSPV1Bc1nqG8vv8BK3qxXMZIn32OQBi/4A7Sc= +github.com/ftl/godxmap v1.0.0 h1:5T1SYRtTd49AoISdFGvvRWcqJ9kFA1fzdx6yWJT54zM= +github.com/ftl/godxmap v1.0.0/go.mod h1:Cl5gtnJFMPtVktcghGTaGeVSFQ1tlZFzkI3p/LylrdI= github.com/ftl/hamradio v0.2.10 h1:3PpmNpYPkap4z6sbYJjXpL++ZOV4/Xu6aUYlcbclj1w= github.com/ftl/hamradio v0.2.10/go.mod h1:BvA+ni3sOKmrIJpLt6f2sYK9vc3VfihZm4x0h8kzOPw= github.com/ftl/localcopy v0.0.0-20190616142648-8915fb81f0d9 h1:ORI3EUKpLTsfA372C6xpuZFDXw+ckmCzLaCcJvakG24= @@ -62,6 +64,8 @@ github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqa github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/ui/glade/contest.glade b/ui/glade/contest.glade index f54f90f..8d4ead3 100644 --- a/ui/glade/contest.glade +++ b/ui/glade/contest.glade @@ -2566,7 +2566,7 @@ For more details see <a href="https://github.com/ftl/hellocontest/wiki/Main-W - + True False @@ -2630,6 +2630,17 @@ For more details see <a href="https://github.com/ftl/hellocontest/wiki/Main-W 0 + + + True + False + Map + + + 5 + 0 + + False diff --git a/ui/statusView.go b/ui/statusView.go index 4674fd5..502e414 100644 --- a/ui/statusView.go +++ b/ui/statusView.go @@ -17,6 +17,7 @@ type statusView struct { dxccLabel *gtk.Label scpLabel *gtk.Label callHistoryLabel *gtk.Label + mapLabel *gtk.Label } const ( @@ -34,6 +35,7 @@ func setupStatusView(builder *gtk.Builder, colors colorProvider) *statusView { result.dxccLabel = getUI(builder, "dxccStatusLabel").(*gtk.Label) result.scpLabel = getUI(builder, "scpStatusLabel").(*gtk.Label) result.callHistoryLabel = getUI(builder, "callHistoryStatusLabel").(*gtk.Label) + result.mapLabel = getUI(builder, "mapStatusLabel").(*gtk.Label) style := result.indicatorStyle(false) setStyledText(result.radioLabel, style, "Radio") @@ -41,6 +43,7 @@ func setupStatusView(builder *gtk.Builder, colors colorProvider) *statusView { setStyledText(result.dxccLabel, style, "DXCC") setStyledText(result.scpLabel, style, "SCP") setStyledText(result.callHistoryLabel, style, "CH") + setStyledText(result.mapLabel, style, "Map") return result } @@ -79,6 +82,8 @@ func (v *statusView) serviceLabel(service core.Service) (*gtk.Label, string) { return v.scpLabel, "SCP" case core.CallHistoryService: return v.callHistoryLabel, "CH" + case core.MapService: + return v.mapLabel, "Map" default: return nil, "" }