2018-09-21 12:31:52 +02:00
|
|
|
package emlid
|
2018-09-18 18:07:40 +02:00
|
|
|
|
|
|
|
import (
|
2018-09-26 12:05:55 +02:00
|
|
|
"context"
|
2018-09-18 18:07:40 +02:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"forge.cadoles.com/Pyxis/golang-socketio"
|
|
|
|
"forge.cadoles.com/Pyxis/golang-socketio/transport"
|
2018-09-20 17:20:52 +02:00
|
|
|
"github.com/mitchellh/mapstructure"
|
2018-09-18 18:07:40 +02:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
// Client is a ReachRS websocket API client
|
|
|
|
type Client struct {
|
2018-09-18 18:07:40 +02:00
|
|
|
opts *Options
|
|
|
|
conn *gosocketio.Client
|
|
|
|
}
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
// EventHandler is an event handler
|
|
|
|
type EventHandler func(data interface{})
|
|
|
|
|
2018-09-18 18:07:40 +02:00
|
|
|
// Connect connects the client to the ReachView endpoint
|
|
|
|
// This method is not safe to call by different coroutines
|
2018-09-21 12:31:52 +02:00
|
|
|
func (c *Client) Connect() error {
|
2018-09-18 18:07:40 +02:00
|
|
|
|
|
|
|
var err error
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
transport := &transport.WebsocketTransport{
|
|
|
|
PingInterval: c.opts.PingInterval,
|
|
|
|
PingTimeout: c.opts.PingTimeout,
|
|
|
|
ReceiveTimeout: c.opts.ReceiveTimeout,
|
|
|
|
SendTimeout: c.opts.SendTimeout,
|
|
|
|
BufferSize: c.opts.BufferSize,
|
|
|
|
}
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
c.Logf("connecting to '%s'", c.opts.Endpoint)
|
2018-09-18 18:07:40 +02:00
|
|
|
conn, err := gosocketio.Dial(c.opts.Endpoint, transport)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error while connecting to endpoint")
|
|
|
|
}
|
|
|
|
|
|
|
|
c.conn = conn
|
|
|
|
|
|
|
|
err = conn.On(gosocketio.OnConnection, func(h *gosocketio.Channel) {
|
2018-09-20 15:44:34 +02:00
|
|
|
conn.Off(gosocketio.OnError)
|
2018-09-21 12:31:52 +02:00
|
|
|
c.Logf("connected with sid '%s'", h.Id())
|
2018-09-18 18:07:40 +02:00
|
|
|
err = c.sendBrowserConnected()
|
|
|
|
wg.Done()
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error while attaching to connection event")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = conn.On(gosocketio.OnError, func(h *gosocketio.Channel) {
|
2018-09-21 12:31:52 +02:00
|
|
|
c.Logf("error")
|
2018-09-18 18:07:40 +02:00
|
|
|
err = errors.Errorf("an unknown error occured")
|
|
|
|
c.conn = nil
|
|
|
|
wg.Done()
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error while attaching to error event")
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the current connection to the ReachView endpoint
|
2018-09-21 12:31:52 +02:00
|
|
|
func (c *Client) Close() {
|
2018-09-18 18:07:40 +02:00
|
|
|
if c.conn == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.conn.Close()
|
|
|
|
c.conn = nil
|
|
|
|
}
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
// Emit a new event with the given data
|
|
|
|
func (c *Client) Emit(event string, data interface{}) error {
|
|
|
|
c.Logf("emit '%s' event", event)
|
|
|
|
if err := c.conn.Emit(event, data); err != nil {
|
|
|
|
return errors.Wrapf(err, "error while emitting '%s' event", event)
|
|
|
|
}
|
|
|
|
c.Logf("'%s' event sent", event)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// On binds and event handler to the given event
|
|
|
|
func (c *Client) On(event string, handler interface{}) error {
|
|
|
|
return c.conn.On(event, handler)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Off remove the handler bound to the given event
|
|
|
|
func (c *Client) Off(event string) {
|
|
|
|
c.conn.Off(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReqResp emits an event with the given data and waits for a response
|
2018-09-26 12:05:55 +02:00
|
|
|
func (c *Client) ReqResp(ctx context.Context,
|
|
|
|
requestEvent string, requestData interface{},
|
|
|
|
responseEvent string, res interface{}) error {
|
2018-09-20 17:20:52 +02:00
|
|
|
|
|
|
|
var err error
|
|
|
|
var wg sync.WaitGroup
|
2018-09-26 12:05:55 +02:00
|
|
|
var once sync.Once
|
|
|
|
|
|
|
|
done := func() {
|
|
|
|
c.conn.Off(responseEvent)
|
|
|
|
wg.Done()
|
|
|
|
}
|
2018-09-20 17:20:52 +02:00
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
2018-09-26 12:05:55 +02:00
|
|
|
go func() {
|
|
|
|
<-ctx.Done()
|
|
|
|
err = ctx.Err()
|
|
|
|
once.Do(done)
|
|
|
|
}()
|
|
|
|
|
2018-09-20 17:20:52 +02:00
|
|
|
err = c.conn.On(responseEvent, func(_ *gosocketio.Channel, data interface{}) {
|
2018-09-26 15:56:06 +02:00
|
|
|
err = mapstructure.WeakDecode(data, res)
|
2018-09-26 12:05:55 +02:00
|
|
|
once.Do(done)
|
2018-09-20 17:20:52 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "error while binding to '%s' event", responseEvent)
|
|
|
|
}
|
|
|
|
|
2018-09-26 12:05:55 +02:00
|
|
|
if err = c.Emit(requestEvent, requestData); err != nil {
|
2018-09-21 12:31:52 +02:00
|
|
|
return errors.Wrapf(err, "error while emitting event '%s'", requestEvent)
|
2018-09-20 17:20:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
// Logf logs the given message with the configured logger
|
|
|
|
func (c *Client) Logf(format string, args ...interface{}) {
|
2018-09-18 18:07:40 +02:00
|
|
|
if c.opts.Logger == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.opts.Logger.Printf(format, args...)
|
|
|
|
}
|
|
|
|
|
2018-09-21 12:31:52 +02:00
|
|
|
// NewClient returns a new ReachRS websocket client
|
|
|
|
func NewClient(opts ...OptionFunc) *Client {
|
2018-09-18 18:07:40 +02:00
|
|
|
options := DefaultOptions()
|
|
|
|
for _, o := range opts {
|
|
|
|
o(options)
|
|
|
|
}
|
2018-09-21 12:31:52 +02:00
|
|
|
return &Client{
|
2018-09-18 18:07:40 +02:00
|
|
|
opts: options,
|
|
|
|
}
|
|
|
|
}
|