go-emlid/reach/client/protocol/v1/internal.go

99 lines
2.7 KiB
Go

package v1
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"
)
const (
eventGetConfiguration = "get configuration"
eventCurrentConfiguration = "current configuration"
eventApplyConfiguration = "apply configuration"
eventConfigurationApplied = "configuration applied"
eventResetConfiguration = "reset configuration to default"
eventSettingsResetToDefault = "settings reset to default"
)
type configurationApplied struct {
Configuration *model.Configuration `mapstructure:"configuration,omitempty"`
Result string `mapstructure:"result"`
Constraints *model.Constraints `mapstructure:"constraints,omitempty"`
}
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
}
func (o *Operations) RequestConfiguration(ctx context.Context) (*model.Configuration, error) {
configuration := &model.Configuration{}
if err := o.ReqResp(ctx, eventGetConfiguration, nil, eventCurrentConfiguration, configuration); err != nil {
return nil, err
}
return configuration, nil
}
// ReqResp emits an event with the given data and waits for a response
func (o *Operations) ReqResp(ctx context.Context,
requestEvent string, requestData any,
responseEvent string, res any) error {
o.mutex.RLock()
defer o.mutex.RUnlock()
if o.client == nil || !o.client.Alive() {
return errors.WithStack(protocol.ErrClosed)
}
var err error
var wg sync.WaitGroup
var once sync.Once
done := func() {
o.client.Off(responseEvent)
wg.Done()
}
wg.Add(1)
go func() {
<-ctx.Done()
err = ctx.Err()
once.Do(done)
}()
err = o.client.On(responseEvent, func(_ *socketio.Channel, data interface{}) {
if res != nil {
err = mapstructure.WeakDecode(data, res)
}
once.Do(done)
})
if err != nil {
return errors.Wrapf(err, "error while binding to '%s' event", responseEvent)
}
if err = o.client.Emit(requestEvent, requestData); err != nil {
return errors.Wrapf(err, "error while emitting event '%s'", requestEvent)
}
wg.Wait()
return err
}