fix: protocol v1

This commit is contained in:
wpetit 2024-08-02 12:57:07 +02:00
parent 8f89ed7e77
commit b976bde363
23 changed files with 392 additions and 85 deletions

View File

@ -5,18 +5,22 @@ import (
"encoding/json"
"flag"
"fmt"
"log/slog"
"os"
reach "forge.cadoles.com/cadoles/go-emlid/reach/client"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"github.com/pkg/errors"
)
var (
host string = "192.168.42.1"
filter string = ""
rawLogLevel string = "ERROR"
)
func init() {
flag.StringVar(&rawLogLevel, "log-level", rawLogLevel, "log level")
flag.StringVar(&host, "host", host, "the reachrs module host")
flag.StringVar(&filter, "filter", filter, "filter the broadcast messages by name")
}
@ -27,6 +31,14 @@ func main() {
ctx := context.Background()
client := reach.NewClient(host)
logLevel, err := logger.ParseLevel(rawLogLevel)
if err != nil {
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
os.Exit(1)
}
slog.SetLogLoggerLevel(logLevel)
if err := client.Connect(ctx); err != nil {
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
os.Exit(1)

View File

@ -5,9 +5,11 @@ import (
"encoding/json"
"flag"
"fmt"
"log/slog"
"os"
reach "forge.cadoles.com/cadoles/go-emlid/reach/client"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
v1 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
@ -18,9 +20,11 @@ var (
host string = "192.168.42.1"
preferredProtocol string = string(v2.Identifier)
fallbackProtocol string = string(v1.Identifier)
rawLogLevel string = "ERROR"
)
func init() {
flag.StringVar(&rawLogLevel, "log-level", rawLogLevel, "log level")
flag.StringVar(&host, "host", host, "the reachrs module host")
flag.StringVar(&preferredProtocol, "preferred-protocol", preferredProtocol, "preferred-protocol")
flag.StringVar(&fallbackProtocol, "fallback-protocol", fallbackProtocol, "fallback-protocol")
@ -29,6 +33,14 @@ func init() {
func main() {
flag.Parse()
logLevel, err := logger.ParseLevel(rawLogLevel)
if err != nil {
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
os.Exit(1)
}
slog.SetLogLoggerLevel(logLevel)
ctx := context.Background()
client := reach.NewClient(
host,

35
cmd/discover/main.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"forge.cadoles.com/cadoles/go-emlid/reach/discovery"
"github.com/pkg/errors"
)
func main() {
services, err := discovery.Watch(context.Background())
if err != nil {
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
os.Exit(1)
}
for srv := range services {
data, err := json.MarshalIndent(struct {
Addr string `json:"addr"`
Name string `json:"name"`
}{
Name: srv.Name,
Addr: fmt.Sprintf("%s:%d", srv.AddrV4.String(), srv.Port),
}, "", " ")
if err != nil {
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
os.Exit(1)
}
fmt.Printf("%s\n", data)
}
}

View File

@ -1,46 +0,0 @@
package example_test
import (
"context"
"fmt"
"os"
"testing"
"time"
"forge.cadoles.com/cadoles/go-emlid/reach/discovery"
"github.com/pkg/errors"
)
// Exemple d'utilisation du package "discovery" pour identifier
// les modules Reach présents sur le réseau
func ExampleDiscovery() {
timeout := 10 * time.Second
// Création d'un context avec un timeout de 10 secondes
// pour limiter le temps de recherche des modules
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Recherche des modules annoncés sur le réseau
// La recherche durerera 10 secondes, conformément au
// timeout associé au context.Context
services, err := discovery.Discover(ctx)
if err != nil {
fmt.Printf("could not discover reach modules: %+v\n", errors.WithStack(err))
os.Exit(1)
}
// Affichage des différents services Reach identifiés
// sur le réseau
for idx, srv := range services {
fmt.Printf("Module #%d: %s:%d\n", idx, srv.AddrV4, srv.Port)
}
}
func TestExampleDiscovery(t *testing.T) {
if !testing.Verbose() {
return
}
ExampleDiscovery()
}

View File

@ -30,7 +30,7 @@ func (c *Client) Protocol(ctx context.Context) (protocol.Identifier, protocol.Op
func (c *Client) getProtocol(ctx context.Context) (protocol.Identifier, protocol.Operations, error) {
c.getProtocolOnce.Do(func() {
availables, err := c.opts.Protocols.Availables(ctx, c.addr)
availables, err := c.opts.Protocols.Availables(ctx, c.addr, c.opts.AvailableTimeout, protocol.WithProtocolLogger(c.opts.Logger))
if err != nil {
c.getProtocolOnceErr = errors.WithStack(err)
return

View File

@ -0,0 +1,19 @@
package logger
import (
"log/slog"
"github.com/pkg/errors"
)
type Level = slog.Level
func ParseLevel(s string) (Level, error) {
var level Level
if err := level.UnmarshalText([]byte(s)); err != nil {
return level, errors.WithStack(err)
}
return level, nil
}

View File

@ -0,0 +1,12 @@
package logger
import "log/slog"
var Attr = slog.Any
type Logger interface {
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
}

View File

@ -1,6 +1,10 @@
package client
import (
"log/slog"
"time"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
v1 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
@ -10,6 +14,8 @@ type Options struct {
Protocols *protocol.Registry
PreferredProtocol protocol.Identifier
FallbackProtocol protocol.Identifier
AvailableTimeout time.Duration
Logger logger.Logger
}
type OptionFunc func(opts *Options)
@ -19,6 +25,8 @@ func NewOptions(funcs ...OptionFunc) *Options {
PreferredProtocol: v2.Identifier,
FallbackProtocol: v1.Identifier,
Protocols: protocol.DefaultRegistry(),
AvailableTimeout: 5 * time.Second,
Logger: slog.Default(),
}
for _, fn := range funcs {
@ -45,3 +53,15 @@ func WithProtocols(protocols *protocol.Registry) OptionFunc {
opts.Protocols = protocols
}
}
func WithLogger(logger logger.Logger) OptionFunc {
return func(opts *Options) {
opts.Logger = logger
}
}
func WithAvailableTimeout(timeout time.Duration) OptionFunc {
return func(opts *Options) {
opts.AvailableTimeout = timeout
}
}

View File

@ -2,6 +2,9 @@ package protocol
import (
"context"
"log/slog"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
)
type Identifier string
@ -11,3 +14,29 @@ type Protocol interface {
Available(ctx context.Context, addr string) (bool, error)
Operations(addr string) Operations
}
type ProtocolOptions struct {
Logger logger.Logger
}
type ProtocolFactory func(opts *ProtocolOptions) (Protocol, error)
type ProtocolOptionFunc func(opts *ProtocolOptions)
func NewProtocolOptions(funcs ...ProtocolOptionFunc) *ProtocolOptions {
opts := &ProtocolOptions{
Logger: slog.Default(),
}
for _, fn := range funcs {
fn(opts)
}
return opts
}
func WithProtocolLogger(logger logger.Logger) ProtocolOptionFunc {
return func(opts *ProtocolOptions) {
opts.Logger = logger
}
}

View File

@ -2,27 +2,39 @@ package protocol
import (
"context"
"time"
"github.com/pkg/errors"
)
type Registry struct {
protocols map[Identifier]Protocol
protocols map[Identifier]ProtocolFactory
}
func (r *Registry) Register(protocol Protocol) {
r.protocols[protocol.Identifier()] = protocol
func (r *Registry) Register(identifier Identifier, factory ProtocolFactory) {
r.protocols[identifier] = factory
}
func (r *Registry) Availables(ctx context.Context, addr string) ([]Protocol, error) {
func (r *Registry) Availables(ctx context.Context, addr string, timeout time.Duration, funcs ...ProtocolOptionFunc) ([]Protocol, error) {
availables := make([]Protocol, 0)
protocolOpts := NewProtocolOptions(funcs...)
for _, proto := range r.protocols {
available, err := proto.Available(ctx, addr)
for _, factory := range r.protocols {
proto, err := factory(protocolOpts)
if err != nil {
return nil, errors.WithStack(err)
}
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
available, err := proto.Available(timeoutCtx, addr)
if err != nil {
cancel()
return nil, errors.WithStack(err)
}
cancel()
if !available {
continue
}
@ -35,7 +47,7 @@ func (r *Registry) Availables(ctx context.Context, addr string) ([]Protocol, err
func NewRegistry() *Registry {
return &Registry{
protocols: make(map[Identifier]Protocol),
protocols: make(map[Identifier]ProtocolFactory),
}
}
@ -45,10 +57,10 @@ func DefaultRegistry() *Registry {
return defaultRegistry
}
func Register(protocol Protocol) {
defaultRegistry.Register(protocol)
func Register(identifier Identifier, factory ProtocolFactory) {
defaultRegistry.Register(identifier, factory)
}
func Availables(ctx context.Context, addr string) ([]Protocol, error) {
return defaultRegistry.Availables(ctx, addr)
func Availables(ctx context.Context, addr string, timeout time.Duration) ([]Protocol, error) {
return defaultRegistry.Availables(ctx, addr, timeout)
}

View File

@ -0,0 +1,37 @@
package testsuite
import (
"testing"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
)
type Logger struct {
t *testing.T
}
// Debug implements logger.Logger.
func (l *Logger) Debug(msg string, args ...any) {
l.t.Logf(msg, args...)
}
// Error implements logger.Logger.
func (l *Logger) Error(msg string, args ...any) {
l.t.Logf(msg, args...)
}
// Info implements logger.Logger.
func (l *Logger) Info(msg string, args ...any) {
l.t.Logf(msg, args...)
}
// Warn implements logger.Logger.
func (l *Logger) Warn(msg string, args ...any) {
l.t.Logf(msg, args...)
}
func NewLogger(t *testing.T) *Logger {
return &Logger{t}
}
var _ logger.Logger = &Logger{}

View File

@ -2,7 +2,9 @@ package testsuite
import (
"context"
"math/rand"
"os"
"strconv"
"testing"
"time"
@ -52,6 +54,17 @@ var testCases = []operationTestCase{
Run: func(t *testing.T, ops protocol.Operations) {
ctx := context.Background()
if err := ops.Connect(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
}
defer func() {
if err := ops.Close(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
}
}()
version, stable, err := ops.Version(ctx)
if err != nil {
t.Errorf("%+v", errors.WithStack(err))
@ -104,6 +117,17 @@ var testCases = []operationTestCase{
Run: func(t *testing.T, ops protocol.Operations) {
ctx := context.Background()
if err := ops.Connect(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
}
defer func() {
if err := ops.Close(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
}
}()
config, err := ops.Configuration(ctx)
if err != nil {
t.Errorf("%+v", errors.WithStack(err))
@ -118,13 +142,32 @@ var testCases = []operationTestCase{
Run: func(t *testing.T, ops protocol.Operations) {
ctx := context.Background()
if err := ops.Connect(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
}
defer func() {
if err := ops.Close(ctx); err != nil {
t.Errorf("%+v", errors.WithStack(err))
}
}()
latitude := -90 + rand.Float64()*180
longitude := -180 + rand.Float64()*360
height := rand.Float64() * 1000
antennaOffset := rand.Float64() * 2
opts := []protocol.SetBaseOptionFunc{
protocol.WithBaseLatitude(47.72769),
protocol.WithBaseLongitude(4.72783),
protocol.WithBaseHeight(101),
protocol.WithBaseLatitude(latitude),
protocol.WithBaseLongitude(longitude),
protocol.WithBaseHeight(height),
protocol.WithBaseAntennaOffset(antennaOffset),
protocol.WithBaseMode("manual"),
}
t.Logf("setting base (latitude: %v, longitude: %v, height: %v, antennaOffset: %v", latitude, longitude, height, antennaOffset)
if err := ops.SetBase(ctx, opts...); err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
@ -132,10 +175,26 @@ var testCases = []operationTestCase{
},
},
{
Name: "Reboot",
Run: func(t *testing.T, ops protocol.Operations) {
const doRebootEnvVar = "DO_REBOOT"
rawDoReboot := os.Getenv(doRebootEnvVar)
if rawDoReboot == "" {
rawDoReboot = "false"
}
doReboot, err := strconv.ParseBool(rawDoReboot)
if err != nil {
t.Fatalf("could not parse %s environment variable: %+v", doRebootEnvVar, errors.WithStack(err))
return
}
if !doReboot {
t.Skipf("Reboot test case disabled. To enable, set environment variable %s=true", doRebootEnvVar)
return
}
ctx := context.Background()
if err := ops.Connect(ctx); err != nil {

View File

@ -3,5 +3,9 @@ package v1
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
func init() {
protocol.Register(&Protocol{})
protocol.Register(Identifier, func(opts *protocol.ProtocolOptions) (protocol.Protocol, error) {
return &Protocol{
logger: opts.Logger,
}, nil
})
}

View File

@ -4,9 +4,11 @@ import (
"context"
"sync"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
"github.com/davecgh/go-spew/spew"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
@ -27,10 +29,15 @@ type configurationApplied struct {
}
func (o *Operations) ApplyConfiguration(ctx context.Context, config *model.Configuration) (string, *model.Configuration, error) {
o.logger.Debug("applying configuration", logger.Attr("configuration", spew.Sdump(config)))
res := &configurationApplied{}
if err := o.ReqResp(ctx, eventApplyConfiguration, config, eventConfigurationApplied, res); err != nil {
return configurationApplyFailed, nil, err
}
o.logger.Debug("apply configuration response", logger.Attr("response", res))
return res.Result, res.Configuration, nil
}

View File

@ -6,6 +6,7 @@ import (
"strings"
"sync"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
@ -16,6 +17,7 @@ type Operations struct {
addr string
client *socketio.Client
mutex sync.RWMutex
logger logger.Logger
}
// Close implements protocol.Operations.
@ -54,6 +56,8 @@ func (o *Operations) Connect(ctx context.Context) error {
client := socketio.NewClient(endpoint)
o.logger.Debug("connecting", logger.Attr("endpoint", endpoint))
if err := client.Connect(); err != nil {
return errors.WithStack(err)
}
@ -67,6 +71,8 @@ func (o *Operations) Connect(ctx context.Context) error {
return errors.WithStack(err)
}
o.logger.Debug("connected")
o.client = client
return nil
@ -206,29 +212,58 @@ const (
// SetBase implements protocol.Operations.
func (o *Operations) SetBase(ctx context.Context, funcs ...protocol.SetBaseOptionFunc) error {
rawConfig, err := o.Configuration(ctx)
if err != nil {
return errors.WithStack(err)
}
config := rawConfig.(*model.Configuration)
baseMode := config.BaseMode
var baseCoordinates *model.BaseCoordinates
if baseMode != nil && baseMode.BaseCoordinates != nil {
baseCoordinates = baseMode.BaseCoordinates
}
opts := protocol.NewSetBaseOptions(funcs...)
var lat string
if opts.Latitude != nil {
lat = strconv.FormatFloat(*opts.Latitude, 'f', -1, 64)
} else if baseCoordinates != nil && baseCoordinates.Coordinates != nil && len(baseCoordinates.Coordinates) > 1 {
lat = *baseCoordinates.Coordinates[0]
} else {
lat = "0"
}
var lon string
if opts.Longitude != nil {
lon = strconv.FormatFloat(*opts.Longitude, 'f', -1, 64)
} else if baseCoordinates != nil && baseCoordinates.Coordinates != nil && len(baseCoordinates.Coordinates) > 1 {
lon = *baseCoordinates.Coordinates[1]
} else {
lon = "0"
}
var alt string
if opts.Height != nil {
alt = strconv.FormatFloat(*opts.Height, 'f', -1, 64)
} else if baseCoordinates != nil && baseCoordinates.Coordinates != nil && len(baseCoordinates.Coordinates) > 1 {
alt = *baseCoordinates.Coordinates[2]
} else {
alt = "0"
}
var antennaHeight string
if opts.AntennaOffset != nil {
antennaHeight = strconv.FormatFloat(*opts.AntennaOffset, 'f', -1, 64)
} else if baseCoordinates != nil && baseCoordinates.AntennaOffset != nil && baseCoordinates.AntennaOffset.Up != nil {
antennaHeight = *baseCoordinates.AntennaOffset.Up
} else {
antennaHeight = "0"
}
config := &model.Configuration{
newConfig := &model.Configuration{
BaseMode: &model.BaseMode{
BaseCoordinates: &model.BaseCoordinates{
Accumulation: model.String("1"),
@ -248,7 +283,7 @@ func (o *Operations) SetBase(ctx context.Context, funcs ...protocol.SetBaseOptio
},
}
result, _, err := o.ApplyConfiguration(ctx, config)
result, _, err := o.ApplyConfiguration(ctx, newConfig)
if err != nil {
return errors.New("configuration update failed")
}

View File

@ -10,7 +10,9 @@ import (
)
func TestProtocolV1Operations(t *testing.T) {
proto := &Protocol{}
proto := &Protocol{
logger: testsuite.NewLogger(t),
}
factory := func(addr string) (protocol.Operations, error) {
ctx := context.Background()

View File

@ -2,17 +2,53 @@ package v1
import (
"context"
"log/slog"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
)
const Identifier protocol.Identifier = "v1"
const compatibleVersionConstraint = "^2.24"
type Protocol struct {
logger logger.Logger
}
// Available implements protocol.Protocol.
func (p *Protocol) Available(ctx context.Context, addr string) (bool, error) {
ops := p.Operations(addr).(*Operations)
if err := ops.Connect(ctx); err != nil {
return false, nil
}
defer ops.Close(ctx)
rawVersion, _, err := ops.Version(ctx)
if err != nil {
return false, nil
}
versionConstraint, err := semver.NewConstraint(compatibleVersionConstraint)
if err != nil {
return false, errors.WithStack(err)
}
version, err := semver.NewVersion(rawVersion)
if err != nil {
return false, errors.WithStack(err)
}
p.logger.Debug("checking version", slog.Any("version", rawVersion), slog.Any("constraint", compatibleVersionConstraint))
if !versionConstraint.Check(version) {
return false, nil
}
return true, nil
}
@ -23,7 +59,7 @@ func (p *Protocol) Identifier() protocol.Identifier {
// Operations implements protocol.Protocol.
func (p *Protocol) Operations(addr string) protocol.Operations {
return &Operations{addr: addr}
return &Operations{addr: addr, logger: p.logger}
}
var _ protocol.Protocol = &Protocol{}

View File

@ -3,5 +3,9 @@ package v2
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
func init() {
protocol.Register(&Protocol{})
protocol.Register(Identifier, func(opts *protocol.ProtocolOptions) (protocol.Protocol, error) {
return &Protocol{
logger: opts.Logger,
}, nil
})
}

View File

@ -18,7 +18,7 @@ type Configuration struct {
Lora struct {
AirRate float64 `json:"air_rate,omitempty"`
Frequency int `json:"frequency,omitempty"`
OutputPower int `json:"output_power,omitempty"`
OutputPower float64 `json:"output_power,omitempty"`
} `json:"lora,omitempty"`
Ntripcaster struct {
MountPoint string `json:"mount_point,omitempty"`

View File

@ -4,6 +4,7 @@ import (
"context"
"sync"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2/model"
@ -15,6 +16,7 @@ type Operations struct {
addr string
client *socketio.Client
mutex sync.RWMutex
logger logger.Logger
}
// Reboot implements protocol.Operations.

View File

@ -10,7 +10,9 @@ import (
)
func TestProtocolV2Operations(t *testing.T) {
proto := &Protocol{}
proto := &Protocol{
logger: testsuite.NewLogger(t),
}
factory := func(addr string) (protocol.Operations, error) {
ctx := context.Background()

View File

@ -3,6 +3,7 @@ package v2
import (
"context"
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
@ -13,6 +14,7 @@ const Identifier protocol.Identifier = "v2"
const compatibleVersionConstraint = ">= 32"
type Protocol struct {
logger logger.Logger
}
// Available implements protocol.Protocol.
@ -35,7 +37,7 @@ func (p *Protocol) Available(ctx context.Context, addr string) (bool, error) {
}
if !versionConstraint.Check(version) {
return false, errors.Errorf("reachview version '%s' does not match constraint '%s'", info.Reachview.Version, compatibleVersionConstraint)
return false, nil
}
return true, nil
@ -48,7 +50,7 @@ func (p *Protocol) Identifier() protocol.Identifier {
// Operations implements protocol.Protocol.
func (p *Protocol) Operations(addr string) protocol.Operations {
return &Operations{addr: addr}
return &Operations{addr: addr, logger: p.logger}
}
var _ protocol.Protocol = &Protocol{}

View File

@ -3,9 +3,9 @@ package discovery
import (
"context"
"net"
"sync"
"github.com/grandcat/zeroconf"
"github.com/pkg/errors"
)
// Service is a ReachRS service discovered via MDNS-SD
@ -17,19 +17,34 @@ type Service struct {
// Discover tries to discover ReachRS services on the local network via mDNS-SD
func Discover(ctx context.Context) ([]Service, error) {
var wg sync.WaitGroup
services := make([]Service, 0)
wg.Add(1)
watch, err := Watch(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
for srv := range watch {
services = append(services, srv)
}
return services, nil
}
// Watch watches ReachRS services on the local network via mDNS-SD
func Watch(ctx context.Context) (chan Service, error) {
out := make(chan Service, 0)
resolver, err := zeroconf.NewResolver()
if err != nil {
return nil, err
return nil, errors.WithStack(err)
}
services := make([]Service, 0)
entries := make(chan *zeroconf.ServiceEntry)
go func() {
defer close(out)
for e := range entries {
var addr *net.IP
if len(e.AddrIPv4) > 0 {
@ -40,17 +55,14 @@ func Discover(ctx context.Context) ([]Service, error) {
AddrV4: addr,
Port: e.Port,
}
services = append(services, srv)
out <- srv
}
wg.Done()
}()
if err = resolver.Browse(ctx, "_reach._tcp", ".local", entries); err != nil {
return nil, err
}
wg.Wait()
return services, nil
return out, nil
}