fix: protocol v1
This commit is contained in:
parent
8f89ed7e77
commit
b976bde363
|
@ -5,18 +5,22 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
reach "forge.cadoles.com/cadoles/go-emlid/reach/client"
|
reach "forge.cadoles.com/cadoles/go-emlid/reach/client"
|
||||||
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
host string = "192.168.42.1"
|
host string = "192.168.42.1"
|
||||||
filter string = ""
|
filter string = ""
|
||||||
|
rawLogLevel string = "ERROR"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
flag.StringVar(&rawLogLevel, "log-level", rawLogLevel, "log level")
|
||||||
flag.StringVar(&host, "host", host, "the reachrs module host")
|
flag.StringVar(&host, "host", host, "the reachrs module host")
|
||||||
flag.StringVar(&filter, "filter", filter, "filter the broadcast messages by name")
|
flag.StringVar(&filter, "filter", filter, "filter the broadcast messages by name")
|
||||||
}
|
}
|
||||||
|
@ -27,6 +31,14 @@ func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
client := reach.NewClient(host)
|
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 {
|
if err := client.Connect(ctx); err != nil {
|
||||||
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
|
fmt.Printf("[FATAL] %+v", errors.WithStack(err))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -5,9 +5,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
reach "forge.cadoles.com/cadoles/go-emlid/reach/client"
|
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"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
||||||
v1 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
|
v1 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
|
||||||
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
|
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
|
||||||
|
@ -18,9 +20,11 @@ var (
|
||||||
host string = "192.168.42.1"
|
host string = "192.168.42.1"
|
||||||
preferredProtocol string = string(v2.Identifier)
|
preferredProtocol string = string(v2.Identifier)
|
||||||
fallbackProtocol string = string(v1.Identifier)
|
fallbackProtocol string = string(v1.Identifier)
|
||||||
|
rawLogLevel string = "ERROR"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
flag.StringVar(&rawLogLevel, "log-level", rawLogLevel, "log level")
|
||||||
flag.StringVar(&host, "host", host, "the reachrs module host")
|
flag.StringVar(&host, "host", host, "the reachrs module host")
|
||||||
flag.StringVar(&preferredProtocol, "preferred-protocol", preferredProtocol, "preferred-protocol")
|
flag.StringVar(&preferredProtocol, "preferred-protocol", preferredProtocol, "preferred-protocol")
|
||||||
flag.StringVar(&fallbackProtocol, "fallback-protocol", fallbackProtocol, "fallback-protocol")
|
flag.StringVar(&fallbackProtocol, "fallback-protocol", fallbackProtocol, "fallback-protocol")
|
||||||
|
@ -29,6 +33,14 @@ func init() {
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
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()
|
ctx := context.Background()
|
||||||
client := reach.NewClient(
|
client := reach.NewClient(
|
||||||
host,
|
host,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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) {
|
func (c *Client) getProtocol(ctx context.Context) (protocol.Identifier, protocol.Operations, error) {
|
||||||
c.getProtocolOnce.Do(func() {
|
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 {
|
if err != nil {
|
||||||
c.getProtocolOnceErr = errors.WithStack(err)
|
c.getProtocolOnceErr = errors.WithStack(err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
|
v1 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1"
|
||||||
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
|
v2 "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2"
|
||||||
|
@ -10,6 +14,8 @@ type Options struct {
|
||||||
Protocols *protocol.Registry
|
Protocols *protocol.Registry
|
||||||
PreferredProtocol protocol.Identifier
|
PreferredProtocol protocol.Identifier
|
||||||
FallbackProtocol protocol.Identifier
|
FallbackProtocol protocol.Identifier
|
||||||
|
AvailableTimeout time.Duration
|
||||||
|
Logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type OptionFunc func(opts *Options)
|
type OptionFunc func(opts *Options)
|
||||||
|
@ -19,6 +25,8 @@ func NewOptions(funcs ...OptionFunc) *Options {
|
||||||
PreferredProtocol: v2.Identifier,
|
PreferredProtocol: v2.Identifier,
|
||||||
FallbackProtocol: v1.Identifier,
|
FallbackProtocol: v1.Identifier,
|
||||||
Protocols: protocol.DefaultRegistry(),
|
Protocols: protocol.DefaultRegistry(),
|
||||||
|
AvailableTimeout: 5 * time.Second,
|
||||||
|
Logger: slog.Default(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fn := range funcs {
|
for _, fn := range funcs {
|
||||||
|
@ -45,3 +53,15 @@ func WithProtocols(protocols *protocol.Registry) OptionFunc {
|
||||||
opts.Protocols = protocols
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identifier string
|
type Identifier string
|
||||||
|
@ -11,3 +14,29 @@ type Protocol interface {
|
||||||
Available(ctx context.Context, addr string) (bool, error)
|
Available(ctx context.Context, addr string) (bool, error)
|
||||||
Operations(addr string) Operations
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,27 +2,39 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
protocols map[Identifier]Protocol
|
protocols map[Identifier]ProtocolFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) Register(protocol Protocol) {
|
func (r *Registry) Register(identifier Identifier, factory ProtocolFactory) {
|
||||||
r.protocols[protocol.Identifier()] = protocol
|
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)
|
availables := make([]Protocol, 0)
|
||||||
|
protocolOpts := NewProtocolOptions(funcs...)
|
||||||
|
|
||||||
for _, proto := range r.protocols {
|
for _, factory := range r.protocols {
|
||||||
available, err := proto.Available(ctx, addr)
|
proto, err := factory(protocolOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
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 {
|
if !available {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -35,7 +47,7 @@ func (r *Registry) Availables(ctx context.Context, addr string) ([]Protocol, err
|
||||||
|
|
||||||
func NewRegistry() *Registry {
|
func NewRegistry() *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
protocols: make(map[Identifier]Protocol),
|
protocols: make(map[Identifier]ProtocolFactory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,10 +57,10 @@ func DefaultRegistry() *Registry {
|
||||||
return defaultRegistry
|
return defaultRegistry
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(protocol Protocol) {
|
func Register(identifier Identifier, factory ProtocolFactory) {
|
||||||
defaultRegistry.Register(protocol)
|
defaultRegistry.Register(identifier, factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Availables(ctx context.Context, addr string) ([]Protocol, error) {
|
func Availables(ctx context.Context, addr string, timeout time.Duration) ([]Protocol, error) {
|
||||||
return defaultRegistry.Availables(ctx, addr)
|
return defaultRegistry.Availables(ctx, addr, timeout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{}
|
|
@ -2,7 +2,9 @@ package testsuite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -52,6 +54,17 @@ var testCases = []operationTestCase{
|
||||||
Run: func(t *testing.T, ops protocol.Operations) {
|
Run: func(t *testing.T, ops protocol.Operations) {
|
||||||
ctx := context.Background()
|
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)
|
version, stable, err := ops.Version(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%+v", errors.WithStack(err))
|
t.Errorf("%+v", errors.WithStack(err))
|
||||||
|
@ -104,6 +117,17 @@ var testCases = []operationTestCase{
|
||||||
Run: func(t *testing.T, ops protocol.Operations) {
|
Run: func(t *testing.T, ops protocol.Operations) {
|
||||||
ctx := context.Background()
|
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)
|
config, err := ops.Configuration(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%+v", errors.WithStack(err))
|
t.Errorf("%+v", errors.WithStack(err))
|
||||||
|
@ -118,13 +142,32 @@ var testCases = []operationTestCase{
|
||||||
Run: func(t *testing.T, ops protocol.Operations) {
|
Run: func(t *testing.T, ops protocol.Operations) {
|
||||||
ctx := context.Background()
|
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{
|
opts := []protocol.SetBaseOptionFunc{
|
||||||
protocol.WithBaseLatitude(47.72769),
|
protocol.WithBaseLatitude(latitude),
|
||||||
protocol.WithBaseLongitude(4.72783),
|
protocol.WithBaseLongitude(longitude),
|
||||||
protocol.WithBaseHeight(101),
|
protocol.WithBaseHeight(height),
|
||||||
|
protocol.WithBaseAntennaOffset(antennaOffset),
|
||||||
protocol.WithBaseMode("manual"),
|
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 {
|
if err := ops.SetBase(ctx, opts...); err != nil {
|
||||||
t.Errorf("%+v", errors.WithStack(err))
|
t.Errorf("%+v", errors.WithStack(err))
|
||||||
return
|
return
|
||||||
|
@ -132,10 +175,26 @@ var testCases = []operationTestCase{
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Name: "Reboot",
|
Name: "Reboot",
|
||||||
Run: func(t *testing.T, ops protocol.Operations) {
|
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()
|
ctx := context.Background()
|
||||||
|
|
||||||
if err := ops.Connect(ctx); err != nil {
|
if err := ops.Connect(ctx); err != nil {
|
||||||
|
|
|
@ -3,5 +3,9 @@ package v1
|
||||||
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
protocol.Register(&Protocol{})
|
protocol.Register(Identifier, func(opts *protocol.ProtocolOptions) (protocol.Protocol, error) {
|
||||||
|
return &Protocol{
|
||||||
|
logger: opts.Logger,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"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"
|
||||||
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
|
||||||
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"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) {
|
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{}
|
res := &configurationApplied{}
|
||||||
if err := o.ReqResp(ctx, eventApplyConfiguration, config, eventConfigurationApplied, res); err != nil {
|
if err := o.ReqResp(ctx, eventApplyConfiguration, config, eventConfigurationApplied, res); err != nil {
|
||||||
return configurationApplyFailed, nil, err
|
return configurationApplyFailed, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.logger.Debug("apply configuration response", logger.Attr("response", res))
|
||||||
|
|
||||||
return res.Result, res.Configuration, nil
|
return res.Result, res.Configuration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"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"
|
||||||
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v1/model"
|
||||||
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/socketio"
|
||||||
|
@ -16,6 +17,7 @@ type Operations struct {
|
||||||
addr string
|
addr string
|
||||||
client *socketio.Client
|
client *socketio.Client
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements protocol.Operations.
|
// Close implements protocol.Operations.
|
||||||
|
@ -54,6 +56,8 @@ func (o *Operations) Connect(ctx context.Context) error {
|
||||||
|
|
||||||
client := socketio.NewClient(endpoint)
|
client := socketio.NewClient(endpoint)
|
||||||
|
|
||||||
|
o.logger.Debug("connecting", logger.Attr("endpoint", endpoint))
|
||||||
|
|
||||||
if err := client.Connect(); err != nil {
|
if err := client.Connect(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -67,6 +71,8 @@ func (o *Operations) Connect(ctx context.Context) error {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.logger.Debug("connected")
|
||||||
|
|
||||||
o.client = client
|
o.client = client
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -206,29 +212,58 @@ const (
|
||||||
|
|
||||||
// SetBase implements protocol.Operations.
|
// SetBase implements protocol.Operations.
|
||||||
func (o *Operations) SetBase(ctx context.Context, funcs ...protocol.SetBaseOptionFunc) error {
|
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...)
|
opts := protocol.NewSetBaseOptions(funcs...)
|
||||||
|
|
||||||
var lat string
|
var lat string
|
||||||
if opts.Latitude != nil {
|
if opts.Latitude != nil {
|
||||||
lat = strconv.FormatFloat(*opts.Latitude, 'f', -1, 64)
|
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
|
var lon string
|
||||||
if opts.Longitude != nil {
|
if opts.Longitude != nil {
|
||||||
lon = strconv.FormatFloat(*opts.Longitude, 'f', -1, 64)
|
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
|
var alt string
|
||||||
if opts.Height != nil {
|
if opts.Height != nil {
|
||||||
alt = strconv.FormatFloat(*opts.Height, 'f', -1, 64)
|
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
|
var antennaHeight string
|
||||||
if opts.AntennaOffset != nil {
|
if opts.AntennaOffset != nil {
|
||||||
antennaHeight = strconv.FormatFloat(*opts.AntennaOffset, 'f', -1, 64)
|
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{
|
BaseMode: &model.BaseMode{
|
||||||
BaseCoordinates: &model.BaseCoordinates{
|
BaseCoordinates: &model.BaseCoordinates{
|
||||||
Accumulation: model.String("1"),
|
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 {
|
if err != nil {
|
||||||
return errors.New("configuration update failed")
|
return errors.New("configuration update failed")
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProtocolV1Operations(t *testing.T) {
|
func TestProtocolV1Operations(t *testing.T) {
|
||||||
proto := &Protocol{}
|
proto := &Protocol{
|
||||||
|
logger: testsuite.NewLogger(t),
|
||||||
|
}
|
||||||
|
|
||||||
factory := func(addr string) (protocol.Operations, error) {
|
factory := func(addr string) (protocol.Operations, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
|
@ -2,17 +2,53 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Identifier protocol.Identifier = "v1"
|
const Identifier protocol.Identifier = "v1"
|
||||||
|
|
||||||
|
const compatibleVersionConstraint = "^2.24"
|
||||||
|
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available implements protocol.Protocol.
|
// Available implements protocol.Protocol.
|
||||||
func (p *Protocol) Available(ctx context.Context, addr string) (bool, error) {
|
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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +59,7 @@ func (p *Protocol) Identifier() protocol.Identifier {
|
||||||
|
|
||||||
// Operations implements protocol.Protocol.
|
// Operations implements protocol.Protocol.
|
||||||
func (p *Protocol) Operations(addr string) protocol.Operations {
|
func (p *Protocol) Operations(addr string) protocol.Operations {
|
||||||
return &Operations{addr: addr}
|
return &Operations{addr: addr, logger: p.logger}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ protocol.Protocol = &Protocol{}
|
var _ protocol.Protocol = &Protocol{}
|
||||||
|
|
|
@ -3,5 +3,9 @@ package v2
|
||||||
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
import "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
protocol.Register(&Protocol{})
|
protocol.Register(Identifier, func(opts *protocol.ProtocolOptions) (protocol.Protocol, error) {
|
||||||
|
return &Protocol{
|
||||||
|
logger: opts.Logger,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Configuration struct {
|
||||||
Lora struct {
|
Lora struct {
|
||||||
AirRate float64 `json:"air_rate,omitempty"`
|
AirRate float64 `json:"air_rate,omitempty"`
|
||||||
Frequency int `json:"frequency,omitempty"`
|
Frequency int `json:"frequency,omitempty"`
|
||||||
OutputPower int `json:"output_power,omitempty"`
|
OutputPower float64 `json:"output_power,omitempty"`
|
||||||
} `json:"lora,omitempty"`
|
} `json:"lora,omitempty"`
|
||||||
Ntripcaster struct {
|
Ntripcaster struct {
|
||||||
MountPoint string `json:"mount_point,omitempty"`
|
MountPoint string `json:"mount_point,omitempty"`
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"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"
|
||||||
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2/model"
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol/v2/model"
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ type Operations struct {
|
||||||
addr string
|
addr string
|
||||||
client *socketio.Client
|
client *socketio.Client
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reboot implements protocol.Operations.
|
// Reboot implements protocol.Operations.
|
||||||
|
|
|
@ -10,7 +10,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProtocolV2Operations(t *testing.T) {
|
func TestProtocolV2Operations(t *testing.T) {
|
||||||
proto := &Protocol{}
|
proto := &Protocol{
|
||||||
|
logger: testsuite.NewLogger(t),
|
||||||
|
}
|
||||||
|
|
||||||
factory := func(addr string) (protocol.Operations, error) {
|
factory := func(addr string) (protocol.Operations, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package v2
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -13,6 +14,7 @@ const Identifier protocol.Identifier = "v2"
|
||||||
const compatibleVersionConstraint = ">= 32"
|
const compatibleVersionConstraint = ">= 32"
|
||||||
|
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available implements protocol.Protocol.
|
// Available implements protocol.Protocol.
|
||||||
|
@ -35,7 +37,7 @@ func (p *Protocol) Available(ctx context.Context, addr string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !versionConstraint.Check(version) {
|
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
|
return true, nil
|
||||||
|
@ -48,7 +50,7 @@ func (p *Protocol) Identifier() protocol.Identifier {
|
||||||
|
|
||||||
// Operations implements protocol.Protocol.
|
// Operations implements protocol.Protocol.
|
||||||
func (p *Protocol) Operations(addr string) protocol.Operations {
|
func (p *Protocol) Operations(addr string) protocol.Operations {
|
||||||
return &Operations{addr: addr}
|
return &Operations{addr: addr, logger: p.logger}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ protocol.Protocol = &Protocol{}
|
var _ protocol.Protocol = &Protocol{}
|
||||||
|
|
|
@ -3,9 +3,9 @@ package discovery
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/grandcat/zeroconf"
|
"github.com/grandcat/zeroconf"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service is a ReachRS service discovered via MDNS-SD
|
// 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
|
// Discover tries to discover ReachRS services on the local network via mDNS-SD
|
||||||
func Discover(ctx context.Context) ([]Service, error) {
|
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()
|
resolver, err := zeroconf.NewResolver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
services := make([]Service, 0)
|
|
||||||
entries := make(chan *zeroconf.ServiceEntry)
|
entries := make(chan *zeroconf.ServiceEntry)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
|
||||||
for e := range entries {
|
for e := range entries {
|
||||||
var addr *net.IP
|
var addr *net.IP
|
||||||
if len(e.AddrIPv4) > 0 {
|
if len(e.AddrIPv4) > 0 {
|
||||||
|
@ -40,17 +55,14 @@ func Discover(ctx context.Context) ([]Service, error) {
|
||||||
AddrV4: addr,
|
AddrV4: addr,
|
||||||
Port: e.Port,
|
Port: e.Port,
|
||||||
}
|
}
|
||||||
services = append(services, srv)
|
out <- srv
|
||||||
}
|
}
|
||||||
wg.Done()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err = resolver.Browse(ctx, "_reach._tcp", ".local", entries); err != nil {
|
if err = resolver.Browse(ctx, "_reach._tcp", ".local", entries); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
return out, nil
|
||||||
|
|
||||||
return services, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue