1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-24 13:48:49 +08:00

ble: add support for functional options, add tests (#1059)

This commit is contained in:
Thomas Kohler 2024-02-10 18:02:09 +01:00 committed by GitHub
parent 3ac63bfd27
commit d96aa52fb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 771 additions and 164 deletions

View File

@ -174,8 +174,7 @@ import (
func NewSwarmBot(port string) *gobot.Robot {
spheroAdaptor := serialport.NewAdaptor(port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor)
spheroDriver.SetName("Sphero" + port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor, serial.WithName("Sphero" + port))
work := func() {
spheroDriver.Stop()

View File

@ -235,7 +235,7 @@ type BLEConnector interface {
ReadCharacteristic(cUUID string) ([]byte, error)
WriteCharacteristic(cUUID string, data []byte) error
Subscribe(cUUID string, f func([]byte, error)) error
Subscribe(cUUID string, f func(data []byte)) error
WithoutResponses(use bool)
}

3
doc.go
View File

@ -85,8 +85,7 @@ Finally, you can use Master Gobot to add the complete Gobot API or control swarm
func NewSwarmBot(port string) *gobot.Robot {
spheroAdaptor := serialport.NewAdaptor(port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor)
spheroDriver.SetName("Sphero" + port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor, serial.WithName("Sphero" + port))
work := func() {
spheroDriver.Stop()

View File

@ -5,7 +5,7 @@ type Driver interface {
// Name returns the label for the Driver
Name() string
// SetName sets the label for the Driver.
// Please prefer to use options [gpio.WithName or aio.WithName] instead, if possible.
// Please use options [aio.WithName, ble.WithName, gpio.WithName or serial.WithName] instead.
SetName(s string)
// Start initiates the Driver
Start() error

View File

@ -39,6 +39,44 @@ import(
...
```
### BLE client adaptor changed signature for Subscribe()
Since introducing the usage of "github.com/muka/go-bluetooth" in 2020, the callback do not support the given error
parameter anymore. The switch to usage of "tinygo.org/x/bluetooth" has not changed this. Therefore it is removed now
from the function.
### BLE generic drivers changed signature for Get*() functions
All those functions log an error only or panic, so the caller gets no nice programmatic feedback. The error is now
returned instead and the log output needs to be done at caller side.
```go
// old
...
devName := access.GetDeviceName()
appearance := access.GetAppearance()
modelNo := info.GetModelNumber()
fwRev := info.GetFirmwareRevision()
hwRev := info.GetHardwareRevision()
manuName := info.GetManufacturerName()
pid := info.GetPnPId()
level := battery.GetBatteryLevel()
...
// new
...
devName, err := access.GetDeviceName()
if err != nil {
fmt.Println(err)
}
appearance, err := access.GetAppearance()
if err != nil {
fmt.Println(err)
}
...
...
```
### Sphero adaptor split off
The Serial Based Sphero adaptor was split off into a generic serial adaptor and the driver part. With this, the imports

View File

@ -2,7 +2,6 @@ package ble
import (
"bytes"
"log"
"gobot.io/x/gobot/v2"
)
@ -16,9 +15,9 @@ type BatteryDriver struct {
}
// NewBatteryDriver creates a new driver
func NewBatteryDriver(a gobot.BLEConnector) *BatteryDriver {
func NewBatteryDriver(a gobot.BLEConnector, opts ...OptionApplier) *BatteryDriver {
d := &BatteryDriver{
Driver: NewDriver(a, "Battery", nil, nil),
Driver: NewDriver(a, "Battery", nil, nil, opts...),
Eventer: gobot.NewEventer(),
}
@ -26,14 +25,13 @@ func NewBatteryDriver(a gobot.BLEConnector) *BatteryDriver {
}
// GetBatteryLevel reads and returns the current battery level
func (d *BatteryDriver) GetBatteryLevel() uint8 {
func (d *BatteryDriver) GetBatteryLevel() (uint8, error) {
c, err := d.Adaptor().ReadCharacteristic(batteryCharaShort)
if err != nil {
log.Println(err)
return 0
return 0, err
}
buf := bytes.NewBuffer(c)
val, _ := buf.ReadByte()
level := val
return level
return level, nil
}

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
@ -13,12 +14,27 @@ import (
var _ gobot.Driver = (*BatteryDriver)(nil)
func TestNewBatteryDriver(t *testing.T) {
// arrange
d := NewBatteryDriver(testutil.NewBleTestAdaptor())
// act & assert
assert.True(t, strings.HasPrefix(d.Name(), "Battery"))
assert.NotNil(t, d.Eventer)
}
func TestNewBatteryDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewBatteryDriver(a, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestBatteryDriverRead(t *testing.T) {
// arrange
a := testutil.NewBleTestAdaptor()
d := NewBatteryDriver(a)
a.SetReadCharacteristicTestFunc(func(cUUID string) ([]byte, error) {
@ -28,6 +44,9 @@ func TestBatteryDriverRead(t *testing.T) {
return nil, nil
})
assert.Equal(t, uint8(20), d.GetBatteryLevel())
// act
level, err := d.GetBatteryLevel()
// assert
require.NoError(t, err)
assert.Equal(t, uint8(20), level)
}

View File

@ -7,8 +7,8 @@ import (
"gobot.io/x/gobot/v2"
)
// optionApplier needs to be implemented by each configurable option type
type optionApplier interface {
// OptionApplier needs to be implemented by each configurable option type
type OptionApplier interface {
apply(cfg *configuration)
}
@ -31,7 +31,11 @@ type Driver struct {
}
// NewDriver creates a new basic BLE gobot driver.
func NewDriver(a interface{}, name string, afterStart func() error, beforeHalt func() error) *Driver {
func NewDriver(
a interface{}, name string,
afterStart func() error, beforeHalt func() error,
opts ...OptionApplier,
) *Driver {
if afterStart == nil {
afterStart = func() error { return nil }
}
@ -49,11 +53,15 @@ func NewDriver(a interface{}, name string, afterStart func() error, beforeHalt f
mutex: &sync.Mutex{},
}
for _, o := range opts {
o.apply(d.driverCfg)
}
return &d
}
// WithName is used to replace the default name of the driver.
func WithName(name string) optionApplier {
func WithName(name string) OptionApplier {
return nameOption(name)
}
@ -63,7 +71,7 @@ func (d *Driver) Name() string {
}
// SetName sets the name of the driver.
// Deprecated: Please use option [aio.WithName] instead.
// Deprecated: Please use option [ble.WithName] instead.
func (d *Driver) SetName(name string) {
WithName(name).apply(d.driverCfg)
}

View File

@ -37,6 +37,21 @@ func TestNewDriver(t *testing.T) {
assert.NotNil(t, d.mutex)
}
func TestNewDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const (
name = "mybot"
newName = "overwrite mybot"
)
a := testutil.NewBleTestAdaptor()
// act
d := NewDriver(a, name, nil, nil, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func Test_applyWithName(t *testing.T) {
// arrange
const name = "mybot"
@ -68,3 +83,18 @@ func TestHalt(t *testing.T) {
// act, assert
require.EqualError(t, d.Halt(), "before halt error")
}
func TestAdaptor(t *testing.T) {
wrongConnectorType := struct {
a uint32
}{}
// arrange
a := testutil.NewBleTestAdaptor()
d := NewDriver(a, "BLE_BASIC", nil, nil)
// act, assert
assert.Equal(t, a, d.Adaptor())
// arrange wrong connector type
d.connection = wrongConnectorType
// act, assert
assert.Nil(t, d.Adaptor())
}

View File

@ -2,7 +2,6 @@ package ble
import (
"bytes"
"log"
"gobot.io/x/gobot/v2"
)
@ -22,9 +21,9 @@ type DeviceInformationDriver struct {
}
// NewDeviceInformationDriver creates a new driver
func NewDeviceInformationDriver(a gobot.BLEConnector) *DeviceInformationDriver {
func NewDeviceInformationDriver(a gobot.BLEConnector, opts ...OptionApplier) *DeviceInformationDriver {
n := &DeviceInformationDriver{
Driver: NewDriver(a, "DeviceInformation", nil, nil),
Driver: NewDriver(a, "DeviceInformation", nil, nil, opts...),
Eventer: gobot.NewEventer(),
}
@ -32,61 +31,56 @@ func NewDeviceInformationDriver(a gobot.BLEConnector) *DeviceInformationDriver {
}
// GetModelNumber returns the model number for the BLE Peripheral
func (d *DeviceInformationDriver) GetModelNumber() string {
func (d *DeviceInformationDriver) GetModelNumber() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(deviceInformationModelNumberCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
model := buf.String()
return model
return model, nil
}
// GetFirmwareRevision returns the firmware revision for the BLE Peripheral
func (d *DeviceInformationDriver) GetFirmwareRevision() string {
func (d *DeviceInformationDriver) GetFirmwareRevision() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(deviceInformationFirmwareRevisionCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
val := buf.String()
return val
return val, nil
}
// GetHardwareRevision returns the hardware revision for the BLE Peripheral
func (d *DeviceInformationDriver) GetHardwareRevision() string {
func (d *DeviceInformationDriver) GetHardwareRevision() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(deviceInformationHardwareRevisionCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
val := buf.String()
return val
return val, nil
}
// GetManufacturerName returns the manufacturer name for the BLE Peripheral
func (d *DeviceInformationDriver) GetManufacturerName() string {
func (d *DeviceInformationDriver) GetManufacturerName() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(deviceInformationManufacturerNameCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
val := buf.String()
return val
return val, nil
}
// GetPnPId returns the PnP ID for the BLE Peripheral
func (d *DeviceInformationDriver) GetPnPId() string {
func (d *DeviceInformationDriver) GetPnPId() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(deviceInformationPnPIdCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
val := buf.String()
return val
return val, nil
}

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
@ -13,32 +14,71 @@ import (
var _ gobot.Driver = (*DeviceInformationDriver)(nil)
func TestNewDeviceInformationDriver(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
// act & assert
assert.True(t, strings.HasPrefix(d.Name(), "DeviceInformation"))
assert.NotNil(t, d.Eventer)
}
func TestNewDeviceInformationDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewDeviceInformationDriver(a, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestDeviceInformationGetModelNumber(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a24", d.GetModelNumber())
// act
modelNo, err := d.GetModelNumber()
// assert
require.NoError(t, err)
assert.Equal(t, "2a24", modelNo)
}
func TestDeviceInformationGetFirmwareRevision(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a26", d.GetFirmwareRevision())
// act
fwRev, err := d.GetFirmwareRevision()
// assert
require.NoError(t, err)
assert.Equal(t, "2a26", fwRev)
}
func TestDeviceInformationGetHardwareRevision(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a27", d.GetHardwareRevision())
// act
hwRev, err := d.GetHardwareRevision()
// assert
require.NoError(t, err)
assert.Equal(t, "2a27", hwRev)
}
func TestDeviceInformationGetManufacturerName(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a29", d.GetManufacturerName())
// act
manuName, err := d.GetManufacturerName()
// assert
require.NoError(t, err)
assert.Equal(t, "2a29", manuName)
}
func TestDeviceInformationGetPnPId(t *testing.T) {
// arrange
d := NewDeviceInformationDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a50", d.GetPnPId())
// act
pid, err := d.GetPnPId()
// assert
require.NoError(t, err)
assert.Equal(t, "2a50", pid)
}

View File

@ -3,7 +3,6 @@ package ble
import (
"bytes"
"encoding/binary"
"log"
"gobot.io/x/gobot/v2"
)
@ -20,9 +19,9 @@ type GenericAccessDriver struct {
}
// NewGenericAccessDriver creates a GenericAccessDriver
func NewGenericAccessDriver(a gobot.BLEConnector) *GenericAccessDriver {
func NewGenericAccessDriver(a gobot.BLEConnector, opts ...OptionApplier) *GenericAccessDriver {
d := &GenericAccessDriver{
Driver: NewDriver(a, "GenericAccess", nil, nil),
Driver: NewDriver(a, "GenericAccess", nil, nil, opts...),
Eventer: gobot.NewEventer(),
}
@ -30,33 +29,31 @@ func NewGenericAccessDriver(a gobot.BLEConnector) *GenericAccessDriver {
}
// GetDeviceName returns the device name for the BLE Peripheral
func (d *GenericAccessDriver) GetDeviceName() string {
func (d *GenericAccessDriver) GetDeviceName() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(genericAccessDeviceNameCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
val := buf.String()
return val
return val, nil
}
// GetAppearance returns the appearance string for the BLE Peripheral
func (d *GenericAccessDriver) GetAppearance() string {
func (d *GenericAccessDriver) GetAppearance() (string, error) {
c, err := d.Adaptor().ReadCharacteristic(genericAccessAppearanceCharaShort)
if err != nil {
log.Println(err)
return ""
return "", err
}
buf := bytes.NewBuffer(c)
var val uint16
if err := binary.Read(buf, binary.LittleEndian, &val); err != nil {
panic(err)
return "", err
}
return appearances[val]
return appearances[val], nil
}
var appearances = map[uint16]string{

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
@ -13,17 +14,37 @@ import (
var _ gobot.Driver = (*GenericAccessDriver)(nil)
func TestNewGenericAccessDriver(t *testing.T) {
// arrange
d := NewGenericAccessDriver(testutil.NewBleTestAdaptor())
// act
assert.True(t, strings.HasPrefix(d.Name(), "GenericAccess"))
assert.NotNil(t, d.Eventer)
}
func TestNewGenericAccessDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewGenericAccessDriver(a, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestGenericAccessDriverGetDeviceName(t *testing.T) {
// arrange
d := NewGenericAccessDriver(testutil.NewBleTestAdaptor())
assert.Equal(t, "2a00", d.GetDeviceName())
// act
devName, err := d.GetDeviceName()
// assert
require.NoError(t, err)
assert.Equal(t, "2a00", devName)
}
func TestGenericAccessDriverGetAppearance(t *testing.T) {
// arrange
a := testutil.NewBleTestAdaptor()
d := NewGenericAccessDriver(a)
a.SetReadCharacteristicTestFunc(func(cUUID string) ([]byte, error) {
@ -32,6 +53,9 @@ func TestGenericAccessDriverGetAppearance(t *testing.T) {
}
return nil, nil
})
assert.Equal(t, "Generic Computer", d.GetAppearance())
// act
app, err := d.GetAppearance()
// assert
require.NoError(t, err)
assert.Equal(t, "Generic Computer", app)
}

View File

@ -28,11 +28,11 @@ type AccelerometerData struct {
}
// NewAccelerometerDriver creates a AccelerometerDriver
func NewAccelerometerDriver(a gobot.BLEConnector) *AccelerometerDriver {
func NewAccelerometerDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *AccelerometerDriver {
d := &AccelerometerDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit Accelerometer", d.initialize, nil)
d.Driver = ble.NewDriver(a, "Microbit Accelerometer", d.initialize, nil, opts...)
d.AddEvent(AccelerometerEvent)
@ -42,7 +42,7 @@ func NewAccelerometerDriver(a gobot.BLEConnector) *AccelerometerDriver {
// initialize tells driver to get ready to do work
func (d *AccelerometerDriver) initialize() error {
// subscribe to accelerometer notifications
return d.Adaptor().Subscribe(accelerometerChara, func(data []byte, e error) {
return d.Adaptor().Subscribe(accelerometerChara, func(data []byte) {
a := struct{ x, y, z int16 }{x: 0, y: 0, z: 0}
buf := bytes.NewBuffer(data)

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -22,6 +23,18 @@ func TestNewAccelerometerDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewAccelerometerDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewAccelerometerDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestAccelerometerStartAndHalt(t *testing.T) {
d := NewAccelerometerDriver(testutil.NewBleTestAdaptor())
require.NoError(t, d.Start())
@ -43,7 +56,7 @@ func TestAccelerometerReadData(t *testing.T) {
require.NoError(t, err)
a.SendTestDataToSubscriber([]byte{0x22, 0x22, 0x23, 0x23, 0x24, 0x24}, nil)
a.SendTestDataToSubscriber([]byte{0x22, 0x22, 0x23, 0x23, 0x24, 0x24})
select {
case <-sem:

View File

@ -21,12 +21,12 @@ type ButtonDriver struct {
}
// NewButtonDriver creates a new driver
func NewButtonDriver(a gobot.BLEConnector) *ButtonDriver {
func NewButtonDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *ButtonDriver {
d := &ButtonDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit Button", d.initialize, nil)
d.Driver = ble.NewDriver(a, "Microbit Button", d.initialize, nil, opts...)
d.AddEvent(ButtonAEvent)
d.AddEvent(ButtonBEvent)
@ -37,14 +37,14 @@ func NewButtonDriver(a gobot.BLEConnector) *ButtonDriver {
// initialize tells driver to get ready to do work
func (d *ButtonDriver) initialize() error {
// subscribe to button A notifications
if err := d.Adaptor().Subscribe(buttonAChara, func(data []byte, e error) {
if err := d.Adaptor().Subscribe(buttonAChara, func(data []byte) {
d.Publish(d.Event(ButtonAEvent), data)
}); err != nil {
return err
}
// subscribe to button B notifications
return d.Adaptor().Subscribe(buttonBChara, func(data []byte, e error) {
return d.Adaptor().Subscribe(buttonBChara, func(data []byte) {
d.Publish(d.Event(ButtonBEvent), data)
})
}

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -21,6 +22,18 @@ func TestNewButtonDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewButtonDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewButtonDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestButtonStartAndHalt(t *testing.T) {
d := NewButtonDriver(testutil.NewBleTestAdaptor())
require.NoError(t, d.Start())
@ -38,7 +51,7 @@ func TestButtonReadData(t *testing.T) {
})
require.NoError(t, err)
a.SendTestDataToSubscriber([]byte{1}, nil)
a.SendTestDataToSubscriber([]byte{1})
select {
case <-sem:

View File

@ -33,12 +33,12 @@ type pinData struct {
}
// NewIOPinDriver creates a new driver
func NewIOPinDriver(a gobot.BLEConnector) *IOPinDriver {
func NewIOPinDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *IOPinDriver {
d := &IOPinDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit IO Pins", d.initialize, nil)
d.Driver = ble.NewDriver(a, "Microbit IO Pins", d.initialize, nil, opts...)
return d
}

View File

@ -10,6 +10,7 @@ import (
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/aio"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
"gobot.io/x/gobot/v2/drivers/gpio"
)
@ -31,6 +32,18 @@ func TestNewIOPinDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewIOPinDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewIOPinDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestIOPinStartAndHalt(t *testing.T) {
a := testutil.NewBleTestAdaptor()
d := NewIOPinDriver(a)

View File

@ -19,12 +19,12 @@ type LEDDriver struct {
}
// NewLEDDriver creates a Microbit LEDDriver
func NewLEDDriver(a gobot.BLEConnector) *LEDDriver {
func NewLEDDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *LEDDriver {
d := &LEDDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit LED", nil, nil)
d.Driver = ble.NewDriver(a, "Microbit LED", nil, nil, opts...)
return d
}

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -25,6 +26,18 @@ func TestNewLEDDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewLEDDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewLEDDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestLEDWriteMatrix(t *testing.T) {
d := initTestLEDDriver()
require.NoError(t, d.WriteMatrix([]byte{0x01, 0x02}))

View File

@ -28,11 +28,11 @@ type MagnetometerData struct {
}
// NewMagnetometerDriver creates a Microbit MagnetometerDriver
func NewMagnetometerDriver(a gobot.BLEConnector) *MagnetometerDriver {
func NewMagnetometerDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *MagnetometerDriver {
d := &MagnetometerDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit Magnetometer", d.initialize, nil)
d.Driver = ble.NewDriver(a, "Microbit Magnetometer", d.initialize, nil, opts...)
d.AddEvent(MagnetometerEvent)
@ -42,7 +42,7 @@ func NewMagnetometerDriver(a gobot.BLEConnector) *MagnetometerDriver {
// initialize tells driver to get ready to do work
func (d *MagnetometerDriver) initialize() error {
// subscribe to magnetometer notifications
return d.Adaptor().Subscribe(magnetometerChara, func(data []byte, e error) {
return d.Adaptor().Subscribe(magnetometerChara, func(data []byte) {
a := struct{ x, y, z int16 }{x: 0, y: 0, z: 0}
buf := bytes.NewBuffer(data)

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -27,6 +28,18 @@ func TestMagnetometerDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewMagnetometerDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewMagnetometerDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestMagnetometerStartAndHalt(t *testing.T) {
d := initTestMagnetometerDriver()
require.NoError(t, d.Start())
@ -46,7 +59,7 @@ func TestMagnetometerReadData(t *testing.T) {
})
require.NoError(t, err)
a.SendTestDataToSubscriber([]byte{0x22, 0x22, 0x23, 0x23, 0x24, 0x24}, nil)
a.SendTestDataToSubscriber([]byte{0x22, 0x22, 0x23, 0x23, 0x24, 0x24})
select {
case <-sem:

View File

@ -21,11 +21,11 @@ type TemperatureDriver struct {
}
// NewTemperatureDriver creates a Microbit TemperatureDriver
func NewTemperatureDriver(a gobot.BLEConnector) *TemperatureDriver {
func NewTemperatureDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *TemperatureDriver {
d := &TemperatureDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Microbit Temperature", d.initialize, nil)
d.Driver = ble.NewDriver(a, "Microbit Temperature", d.initialize, nil, opts...)
d.AddEvent(TemperatureEvent)
@ -35,7 +35,7 @@ func NewTemperatureDriver(a gobot.BLEConnector) *TemperatureDriver {
// initialize tells driver to get ready to do work
func (d *TemperatureDriver) initialize() error {
// subscribe to temperature notifications
return d.Adaptor().Subscribe(temperatureChara, func(data []byte, e error) {
return d.Adaptor().Subscribe(temperatureChara, func(data []byte) {
var l int8
buf := bytes.NewBuffer(data)
val, _ := buf.ReadByte()

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -19,13 +20,25 @@ func initTestTemperatureDriver() *TemperatureDriver {
return d
}
func TestTemperatureDriver(t *testing.T) {
d := initTestTemperatureDriver()
func TestNewTemperatureDriver(t *testing.T) {
d := NewTemperatureDriver(testutil.NewBleTestAdaptor())
assert.IsType(t, &TemperatureDriver{}, d)
assert.True(t, strings.HasPrefix(d.Name(), "Microbit Temperature"))
assert.NotNil(t, d.Eventer)
}
func TestNewTemperatureDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewTemperatureDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestTemperatureStartAndHalt(t *testing.T) {
d := initTestTemperatureDriver()
require.NoError(t, d.Start())
@ -43,7 +56,7 @@ func TestTemperatureReadData(t *testing.T) {
})
require.NoError(t, err)
a.SendTestDataToSubscriber([]byte{0x22}, nil)
a.SendTestDataToSubscriber([]byte{0x22})
select {
case <-sem:

View File

@ -81,7 +81,7 @@ type Pcmd struct {
}
// NewDriver creates a Parrot Minidrone Driver
func NewMinidroneDriver(a gobot.BLEConnector) *MinidroneDriver {
func NewMinidroneDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *MinidroneDriver {
d := &MinidroneDriver{
Pcmd: Pcmd{
Flag: 0,
@ -93,7 +93,7 @@ func NewMinidroneDriver(a gobot.BLEConnector) *MinidroneDriver {
},
Eventer: gobot.NewEventer(),
}
d.Driver = ble.NewDriver(a, "Minidrone", d.initialize, d.shutdown)
d.Driver = ble.NewDriver(a, "Minidrone", d.initialize, d.shutdown, opts...)
d.AddEvent(BatteryEvent)
d.AddEvent(FlightStatusEvent)
@ -349,14 +349,14 @@ func (d *MinidroneDriver) initialize() error {
}
// subscribe to battery notifications
if err := d.Adaptor().Subscribe(batteryChara, func(data []byte, e error) {
if err := d.Adaptor().Subscribe(batteryChara, func(data []byte) {
d.Publish(d.Event(BatteryEvent), data[len(data)-1])
}); err != nil {
return err
}
// subscribe to flying status notifications
if err := d.Adaptor().Subscribe(flightStatusChara, func(data []byte, e error) {
if err := d.Adaptor().Subscribe(flightStatusChara, func(data []byte) {
d.processFlightStatus(data)
}); err != nil {
return err

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -27,6 +28,18 @@ func TestNewMinidroneDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewMinidroneDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewMinidroneDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestMinidroneHalt(t *testing.T) {
d := initTestMinidroneDriver()
require.NoError(t, d.Halt())

View File

@ -18,9 +18,9 @@ type SerialPortDriver struct {
}
// NewSerialPortDriver returns a new serial over Bluetooth LE connection
func NewSerialPortDriver(a gobot.BLEConnector, rid string, tid string) *SerialPortDriver {
func NewSerialPortDriver(a gobot.BLEConnector, rid string, tid string, opts ...OptionApplier) *SerialPortDriver {
d := &SerialPortDriver{
Driver: NewDriver(a, "BleSerial", nil, nil),
Driver: NewDriver(a, "BleSerial", nil, nil, opts...),
rid: rid,
tid: tid,
}
@ -35,7 +35,7 @@ func (p *SerialPortDriver) Open() error {
}
// subscribe to response notifications
return p.Adaptor().Subscribe(p.rid, func(data []byte, e error) {
return p.Adaptor().Subscribe(p.rid, func(data []byte) {
p.responseMutex.Lock()
defer p.responseMutex.Unlock()
p.responseData = append(p.responseData, data...)

View File

@ -1,10 +1,12 @@
package ble
import (
"fmt"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
@ -14,7 +16,193 @@ var _ gobot.Driver = (*SerialPortDriver)(nil)
var _ io.ReadWriteCloser = (*SerialPortDriver)(nil)
func TestBLESerialPort(t *testing.T) {
func TestNewSerialPortDriver(t *testing.T) {
d := NewSerialPortDriver(testutil.NewBleTestAdaptor(), "123", "456")
assert.Equal(t, "01:02:03:0A:0B:0C", d.Address())
}
func TestNewSerialPortDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewSerialPortDriver(a, "123", "456", WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestSerialPortOpen(t *testing.T) {
const receiveCharacteristicUUID = "123"
tests := map[string]struct {
simConnectErr bool
simSubscribeErr bool
wantErr string
}{
"open_ok": {},
"error_connect": {
simConnectErr: true,
wantErr: "connect error",
},
"error_subscribe": {
simSubscribeErr: true,
wantErr: "subscribe error",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
a := testutil.NewBleTestAdaptor()
a.SetSimulateConnectError(tc.simConnectErr)
a.SetSimulateSubscribeError(tc.simSubscribeErr)
d := NewSerialPortDriver(a, receiveCharacteristicUUID, "456")
// act
err := d.Open()
// assert
if tc.wantErr == "" {
require.NoError(t, err)
a.SendTestDataToSubscriber([]byte{3, 5, 7})
assert.Equal(t, []byte{3, 5, 7}, d.responseData)
assert.Equal(t, receiveCharacteristicUUID, a.SubscribeCharaUUID())
} else {
require.EqualError(t, err, tc.wantErr)
}
})
}
}
func TestSerialPortRead(t *testing.T) {
tests := map[string]struct {
availableData []byte
readDataBuffer []byte
wantCount int
wantData []byte
wantRemaining []byte
}{
"no_data": {
availableData: []byte{},
readDataBuffer: []byte{0, 0, 0},
wantCount: 0,
wantData: []byte{0, 0, 0},
wantRemaining: nil,
},
"read_all": {
availableData: []byte{1, 2, 3},
readDataBuffer: []byte{0, 0, 0},
wantCount: 3,
wantData: []byte{1, 2, 3},
wantRemaining: nil,
},
"read_smaller": {
availableData: []byte{4, 6, 7},
readDataBuffer: []byte{0, 0},
wantCount: 2,
wantData: []byte{4, 6},
wantRemaining: []byte{7},
},
"read_bigger": {
availableData: []byte{7, 8},
readDataBuffer: []byte{0, 0, 0},
wantCount: 2,
wantData: []byte{7, 8, 0},
wantRemaining: nil,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
d := NewSerialPortDriver(testutil.NewBleTestAdaptor(), "123", "456")
d.responseData = append(d.responseData, tc.availableData...)
// act
gotCount, err := d.Read(tc.readDataBuffer)
// assert
require.NoError(t, err)
assert.Equal(t, tc.wantCount, gotCount)
assert.Equal(t, tc.wantData, tc.readDataBuffer)
assert.Equal(t, tc.wantRemaining, d.responseData)
})
}
}
func TestSerialPortWrite(t *testing.T) {
const transmitCharacteristicUUID = "456"
tests := map[string]struct {
writeData []byte
simError bool
wantCount int
wantData []byte
wantErr string
}{
"write_ok": {
writeData: []byte{1, 2, 3},
wantCount: 3,
wantData: []byte{1, 2, 3},
},
"error_write": {
writeData: []byte{1, 2, 3},
simError: true,
wantCount: 3,
wantData: []byte{1, 2, 3},
wantErr: "write error",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
a := testutil.NewBleTestAdaptor()
var gotUUID string
var gotData []byte
a.SetWriteCharacteristicTestFunc(func(cUUID string, data []byte) error {
gotUUID = cUUID
gotData = append(gotData, data...)
if tc.simError {
return fmt.Errorf("write error")
}
return nil
})
d := NewSerialPortDriver(a, "123", transmitCharacteristicUUID)
// act
gotCount, err := d.Write(tc.writeData)
// assert
if tc.wantErr == "" {
require.NoError(t, err)
} else {
require.EqualError(t, err, tc.wantErr)
}
assert.Equal(t, tc.wantCount, gotCount)
assert.Equal(t, transmitCharacteristicUUID, gotUUID)
assert.Equal(t, tc.wantData, gotData)
})
}
}
func TestSerialPortClose(t *testing.T) {
tests := map[string]struct {
simDisconnectErr bool
wantErr string
}{
"close_ok": {},
"error_close": {
simDisconnectErr: true,
wantErr: "disconnect error",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
a := testutil.NewBleTestAdaptor()
a.SetSimulateDisconnectError(tc.simDisconnectErr)
d := NewSerialPortDriver(a, "123", "456")
// act
err := d.Close()
// assert
if tc.wantErr == "" {
require.NoError(t, err)
} else {
require.EqualError(t, err, tc.wantErr)
}
})
}
}

View File

@ -2,6 +2,7 @@ package sphero
import (
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/common/sphero"
)
@ -11,8 +12,8 @@ type BB8Driver struct {
}
// NewBB8Driver creates a driver for a Sphero BB-8
func NewBB8Driver(a gobot.BLEConnector) *BB8Driver {
return &BB8Driver{OllieDriver: newOllieBaseDriver(a, "BB8", bb8DefaultCollisionConfig())}
func NewBB8Driver(a gobot.BLEConnector, opts ...ble.OptionApplier) *BB8Driver {
return &BB8Driver{OllieDriver: newOllieBaseDriver(a, "BB8", bb8DefaultCollisionConfig(), opts...)}
}
// bb8DefaultCollisionConfig returns a CollisionConfig with sensible collision defaults

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -19,3 +20,15 @@ func TestNewBB8Driver(t *testing.T) {
assert.NotNil(t, d.OllieDriver)
assert.Equal(t, d.defaultCollisionConfig, bb8DefaultCollisionConfig())
}
func TestNewBB8DriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewBB8Driver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}

View File

@ -74,17 +74,20 @@ type OllieDriver struct {
}
// NewOllieDriver creates a driver for a Sphero Ollie
func NewOllieDriver(a gobot.BLEConnector) *OllieDriver {
return newOllieBaseDriver(a, "Ollie", ollieDefaultCollisionConfig())
func NewOllieDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *OllieDriver {
return newOllieBaseDriver(a, "Ollie", ollieDefaultCollisionConfig(), opts...)
}
func newOllieBaseDriver(a gobot.BLEConnector, name string, dcc sphero.CollisionConfig) *OllieDriver {
func newOllieBaseDriver(
a gobot.BLEConnector, name string,
dcc sphero.CollisionConfig, opts ...ble.OptionApplier,
) *OllieDriver {
d := &OllieDriver{
defaultCollisionConfig: dcc,
Eventer: gobot.NewEventer(),
packetChannel: make(chan *packet, 1024),
}
d.Driver = ble.NewDriver(a, name, d.initialize, d.shutdown)
d.Driver = ble.NewDriver(a, name, d.initialize, d.shutdown, opts...)
d.AddEvent(sphero.ErrorEvent)
d.AddEvent(sphero.CollisionEvent)
@ -283,7 +286,7 @@ func (d *OllieDriver) shutdown() error {
}
// handleResponses handles responses returned from Ollie
func (d *OllieDriver) handleResponses(data []byte, e error) {
func (d *OllieDriver) handleResponses(data []byte) {
// since packets can only be 20 bytes long, we have to puzzle them together
newMessage := false

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
"gobot.io/x/gobot/v2/drivers/common/sphero"
)
@ -29,6 +30,18 @@ func TestNewOllieDriver(t *testing.T) {
assert.NotNil(t, d.packetChannel)
}
func TestNewOllieDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewOllieDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestOllieStartAndHalt(t *testing.T) {
d := initTestOllieDriver()
require.NoError(t, d.Start())
@ -62,7 +75,7 @@ func TestLocatorData(t *testing.T) {
d.GetLocatorData(func(p Point2D) {
assert.Equal(t, point.y, p.Y)
})
d.handleResponses(packet, nil)
d.handleResponses(packet)
}
}
@ -100,11 +113,11 @@ func TestDataStreaming(t *testing.T) {
c := uint16(b)
bytes = append(bytes, byte(c))
}
d.handleResponses(bytes, nil)
d.handleResponses(bytes)
}
// send empty packet to indicate start of next message
d.handleResponses([]byte{0xFF}, nil)
d.handleResponses([]byte{0xFF})
select {
case <-responseChan:
case <-time.After(10 * time.Millisecond):

View File

@ -2,6 +2,7 @@ package sphero
import (
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/common/sphero"
)
@ -11,8 +12,8 @@ type SPRKPlusDriver struct {
}
// NewSPRKPlusDriver creates a driver for a Sphero SPRK+
func NewSPRKPlusDriver(a gobot.BLEConnector) *SPRKPlusDriver {
return &SPRKPlusDriver{OllieDriver: newOllieBaseDriver(a, "SPRKPlus", sprkplusDefaultCollisionConfig())}
func NewSPRKPlusDriver(a gobot.BLEConnector, opts ...ble.OptionApplier) *SPRKPlusDriver {
return &SPRKPlusDriver{OllieDriver: newOllieBaseDriver(a, "SPRKPlus", sprkplusDefaultCollisionConfig(), opts...)}
}
// sprkplusDefaultCollisionConfig returns a CollisionConfig with sensible collision defaults

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/ble"
"gobot.io/x/gobot/v2/drivers/ble/testutil"
)
@ -19,3 +20,15 @@ func TestNewSPRKPlusDriver(t *testing.T) {
assert.NotNil(t, d.OllieDriver)
assert.Equal(t, d.defaultCollisionConfig, sprkplusDefaultCollisionConfig())
}
func TestNewSPRKPlusDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := testutil.NewBleTestAdaptor()
// act
d := NewSPRKPlusDriver(a, ble.WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}

View File

@ -1,6 +1,7 @@
package testutil
import (
"fmt"
"sync"
"gobot.io/x/gobot/v2"
@ -14,9 +15,13 @@ type bleTestClientAdaptor struct {
mtx sync.Mutex
withoutResponses bool
simulateConnectErr bool
simulateSubscribeErr bool
simulateDisconnectErr bool
readCharacteristicFunc func(string) ([]byte, error)
writeCharacteristicFunc func(string, []byte) error
subscribeFunc func([]byte, error)
subscribeFunc func([]byte)
subscribeCharaUUID string
}
func NewBleTestAdaptor() *bleTestClientAdaptor {
@ -31,6 +36,10 @@ func NewBleTestAdaptor() *bleTestClientAdaptor {
}
}
func (t *bleTestClientAdaptor) SubscribeCharaUUID() string {
return t.subscribeCharaUUID
}
func (t *bleTestClientAdaptor) SetReadCharacteristicTestFunc(f func(cUUID string) (data []byte, err error)) {
t.mtx.Lock()
defer t.mtx.Unlock()
@ -43,15 +52,40 @@ func (t *bleTestClientAdaptor) SetWriteCharacteristicTestFunc(f func(cUUID strin
t.writeCharacteristicFunc = f
}
func (t *bleTestClientAdaptor) SendTestDataToSubscriber(data []byte, err error) {
t.mtx.Lock()
defer t.mtx.Unlock()
t.subscribeFunc(data, err)
func (t *bleTestClientAdaptor) SetSimulateConnectError(val bool) {
t.simulateConnectErr = val
}
func (t *bleTestClientAdaptor) SetSimulateSubscribeError(val bool) {
t.simulateSubscribeErr = val
}
func (t *bleTestClientAdaptor) SetSimulateDisconnectError(val bool) {
t.simulateDisconnectErr = val
}
func (t *bleTestClientAdaptor) SendTestDataToSubscriber(data []byte) {
t.mtx.Lock()
defer t.mtx.Unlock()
t.subscribeFunc(data)
}
func (t *bleTestClientAdaptor) Connect() error {
if t.simulateConnectErr {
return fmt.Errorf("connect error")
}
return nil
}
func (t *bleTestClientAdaptor) Reconnect() error { return nil }
func (t *bleTestClientAdaptor) Disconnect() error {
if t.simulateDisconnectErr {
return fmt.Errorf("disconnect error")
}
return nil
}
func (t *bleTestClientAdaptor) Connect() error { return nil }
func (t *bleTestClientAdaptor) Reconnect() error { return nil }
func (t *bleTestClientAdaptor) Disconnect() error { return nil }
func (t *bleTestClientAdaptor) Finalize() error { return nil }
func (t *bleTestClientAdaptor) Name() string { return t.name }
func (t *bleTestClientAdaptor) SetName(n string) { t.name = n }
@ -70,7 +104,11 @@ func (t *bleTestClientAdaptor) WriteCharacteristic(cUUID string, data []byte) er
return t.writeCharacteristicFunc(cUUID, data)
}
func (t *bleTestClientAdaptor) Subscribe(cUUID string, f func([]byte, error)) error {
func (t *bleTestClientAdaptor) Subscribe(cUUID string, f func(data []byte)) error {
if t.simulateSubscribeErr {
return fmt.Errorf("subscribe error")
}
t.subscribeCharaUUID = cUUID
t.subscribeFunc = f
return nil
}

View File

@ -39,7 +39,7 @@ type driver struct {
}
// newDriver creates a new basic serial gobot driver.
func newDriver(a interface{}, name string) *driver {
func newDriver(a interface{}, name string, opts ...optionApplier) *driver {
d := driver{
Commander: gobot.NewCommander(),
connection: a,
@ -49,6 +49,10 @@ func newDriver(a interface{}, name string) *driver {
mutex: &sync.Mutex{},
}
for _, o := range opts {
o.apply(d.driverCfg)
}
return &d
}
@ -63,7 +67,7 @@ func (d *driver) Name() string {
}
// SetName sets the name of the driver.
// Deprecated: Please use option [aio.WithName] instead.
// Deprecated: Please use option [serial.WithName] instead.
func (d *driver) SetName(name string) {
WithName(name).apply(d.driverCfg)
}

View File

@ -36,6 +36,21 @@ func Test_newDriver(t *testing.T) {
assert.NotNil(t, d.mutex)
}
func Test_newDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const (
name = "mybot"
newName = "overwrite mybot"
)
a := newSerialTestAdaptor()
// act
d := newDriver(a, name, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func Test_applyWithName(t *testing.T) {
// arrange
const name = "mybot"

View File

@ -51,9 +51,9 @@ type SpheroDriver struct {
// "SetStabilization" - See SpheroDriver.SetStabilization
// "SetDataStreaming" - See SpheroDriver.SetDataStreaming
// "SetRotationRate" - See SpheroDriver.SetRotationRate
func NewSpheroDriver(a spheroSerialAdaptor) *SpheroDriver {
func NewSpheroDriver(a spheroSerialAdaptor, opts ...optionApplier) *SpheroDriver {
d := &SpheroDriver{
driver: newDriver(a, "Sphero"),
driver: newDriver(a, "Sphero", opts...),
Eventer: gobot.NewEventer(),
packetChannel: make(chan *packet, 1024),
responseChannel: make(chan []uint8, 1024),

View File

@ -27,6 +27,18 @@ func TestNewSpheroDriver(t *testing.T) {
assert.NotNil(t, d.Eventer)
}
func TestNewSpheroDriverWithName(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithName() option. Further
// tests for options can also be done by call of "WithOption(val).apply(cfg)".
// arrange
const newName = "new name"
a := newSerialTestAdaptor()
// act
d := NewSpheroDriver(a, WithName(newName))
// assert
assert.Equal(t, newName, d.Name())
}
func TestSpheroCommands(t *testing.T) {
d := initTestSpheroDriver()
var ret interface{}

View File

@ -31,7 +31,11 @@ func main() {
work := func() {
gobot.Every(5*time.Second, func() {
fmt.Println("Battery level:", battery.GetBatteryLevel())
level, err := battery.GetBatteryLevel()
if err != nil {
fmt.Println(err)
}
fmt.Println("Battery level:", level)
})
}

View File

@ -29,11 +29,35 @@ func main() {
info := ble.NewDeviceInformationDriver(bleAdaptor)
work := func() {
fmt.Println("Model number:", info.GetModelNumber())
fmt.Println("Firmware rev:", info.GetFirmwareRevision())
fmt.Println("Hardware rev:", info.GetHardwareRevision())
fmt.Println("Manufacturer name:", info.GetManufacturerName())
fmt.Println("PnPId:", info.GetPnPId())
modelNo, err := info.GetModelNumber()
if err != nil {
fmt.Println(err)
}
fmt.Println("Model number:", modelNo)
fwRev, err := info.GetFirmwareRevision()
if err != nil {
fmt.Println(err)
}
fmt.Println("Firmware rev:", fwRev)
hwRev, err := info.GetHardwareRevision()
if err != nil {
fmt.Println(err)
}
fmt.Println("Hardware rev:", hwRev)
manuName, err := info.GetManufacturerName()
if err != nil {
fmt.Println(err)
}
fmt.Println("Manufacturer name:", manuName)
pid, err := info.GetPnPId()
if err != nil {
fmt.Println(err)
}
fmt.Println("PnPId:", pid)
}
robot := gobot.NewRobot("bleBot",

View File

@ -29,8 +29,17 @@ func main() {
access := ble.NewGenericAccessDriver(bleAdaptor)
work := func() {
fmt.Println("Device name:", access.GetDeviceName())
fmt.Println("Appearance:", access.GetAppearance())
devName, err := access.GetDeviceName()
if err != nil {
fmt.Println(err)
}
fmt.Println("Device name:", devName)
appearance, err := access.GetAppearance()
if err != nil {
fmt.Println(err)
}
fmt.Println("Appearance:", appearance)
}
robot := gobot.NewRobot("bleBot",

View File

@ -30,8 +30,17 @@ func NewSwarmBot(port string) *gobot.Robot {
access := ble.NewGenericAccessDriver(bleAdaptor)
work := func() {
fmt.Println("Device name:", access.GetDeviceName())
fmt.Println("Appearance:", access.GetAppearance())
devName, err := access.GetDeviceName()
if err != nil {
fmt.Println(err)
}
fmt.Println("Device name:", devName)
appearance, err := access.GetAppearance()
if err != nil {
fmt.Println(err)
}
fmt.Println("Appearance:", appearance)
}
robot := gobot.NewRobot("bot "+port,

View File

@ -30,11 +30,35 @@ func NewSwarmBot(port string) *gobot.Robot {
info := ble.NewDeviceInformationDriver(bleAdaptor)
work := func() {
fmt.Println("Model number:", info.GetModelNumber())
fmt.Println("Firmware rev:", info.GetFirmwareRevision())
fmt.Println("Hardware rev:", info.GetHardwareRevision())
fmt.Println("Manufacturer name:", info.GetManufacturerName())
fmt.Println("PnPId:", info.GetPnPId())
modelNo, err := info.GetModelNumber()
if err != nil {
fmt.Println(err)
}
fmt.Println("Model number:", modelNo)
fwRev, err := info.GetFirmwareRevision()
if err != nil {
fmt.Println(err)
}
fmt.Println("Firmware rev:", fwRev)
hwRev, err := info.GetHardwareRevision()
if err != nil {
fmt.Println(err)
}
fmt.Println("Hardware rev:", hwRev)
manuName, err := info.GetManufacturerName()
if err != nil {
fmt.Println(err)
}
fmt.Println("Manufacturer name:", manuName)
pid, err := info.GetPnPId()
if err != nil {
fmt.Println(err)
}
fmt.Println("PnPId:", pid)
}
robot := gobot.NewRobot("bot "+port,

View File

@ -35,8 +35,7 @@ func main() {
for _, port := range spheros {
spheroAdaptor := serialport.NewAdaptor(port)
cell := serial.NewSpheroDriver(spheroAdaptor)
cell.SetName("Sphero" + port)
cell := serial.NewSpheroDriver(spheroAdaptor, serial.WithName("Sphero"+port))
work := func() {
conway := new(conway)

View File

@ -19,8 +19,7 @@ import (
func NewSwarmBot(port string) *gobot.Robot {
spheroAdaptor := serialport.NewAdaptor(port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor)
spheroDriver.SetName("Sphero" + port)
spheroDriver := serial.NewSpheroDriver(spheroAdaptor, serial.WithName("Sphero"+port))
work := func() {
spheroDriver.Stop()

View File

@ -71,7 +71,11 @@ func main() {
work := func() {
gobot.Every(5*time.Second, func() {
fmt.Println("Battery level:", battery.GetBatteryLevel())
level, err := battery.GetBatteryLevel()
if err != nil {
fmt.Println(err)
}
fmt.Println("Battery level:", level)
})
}

View File

@ -181,7 +181,7 @@ func (a *Adaptor) WriteCharacteristic(cUUID string, data []byte) error {
// Subscribe subscribes to notifications from the BLE device for the
// requested service and characteristic
func (a *Adaptor) Subscribe(cUUID string, f func([]byte, error)) error {
func (a *Adaptor) Subscribe(cUUID string, f func([]byte)) error {
if !a.connected {
return fmt.Errorf("Cannot subscribe to BLE device until connected")
}
@ -189,10 +189,7 @@ func (a *Adaptor) Subscribe(cUUID string, f func([]byte, error)) error {
cUUID = convertUUID(cUUID)
if char, ok := a.characteristics[cUUID]; ok {
fn := func(d []byte) {
f(d, nil)
}
return char.EnableNotifications(fn)
return char.EnableNotifications(f)
}
return fmt.Errorf("Unknown characteristic: %s", cUUID)

View File

@ -46,7 +46,7 @@ func NewNullReadWriteCloser() *nullReadWriteCloser {
}
}
func initTestSpheroAdaptor() (*Adaptor, *nullReadWriteCloser) {
func initTestAdaptor() (*Adaptor, *nullReadWriteCloser) {
a := NewAdaptor("/dev/null")
rwc := NewNullReadWriteCloser()
@ -56,34 +56,34 @@ func initTestSpheroAdaptor() (*Adaptor, *nullReadWriteCloser) {
return a, rwc
}
func TestSpheroAdaptorName(t *testing.T) {
a, _ := initTestSpheroAdaptor()
func TestNewAdaptor(t *testing.T) {
a := NewAdaptor("/dev/null")
assert.True(t, strings.HasPrefix(a.Name(), "Serial"))
assert.Equal(t, "/dev/null", a.Port())
}
func TestName(t *testing.T) {
a, _ := initTestAdaptor()
assert.True(t, strings.HasPrefix(a.Name(), "Serial"))
a.SetName("NewName")
assert.Equal(t, "NewName", a.Name())
}
func TestSpheroAdaptor(t *testing.T) {
a, _ := initTestSpheroAdaptor()
assert.True(t, strings.HasPrefix(a.Name(), "Serial"))
assert.Equal(t, "/dev/null", a.Port())
}
func TestSpheroAdaptorReconnect(t *testing.T) {
a, _ := initTestSpheroAdaptor()
_ = a.Connect()
func TestReconnect(t *testing.T) {
a, _ := initTestAdaptor()
require.NoError(t, a.Connect())
assert.True(t, a.connected)
_ = a.Reconnect()
require.NoError(t, a.Reconnect())
assert.True(t, a.connected)
_ = a.Disconnect()
require.NoError(t, a.Disconnect())
assert.False(t, a.connected)
_ = a.Reconnect()
require.NoError(t, a.Reconnect())
assert.True(t, a.connected)
}
func TestSpheroAdaptorFinalize(t *testing.T) {
a, rwc := initTestSpheroAdaptor()
_ = a.Connect()
func TestFinalize(t *testing.T) {
a, rwc := initTestAdaptor()
require.NoError(t, a.Connect())
require.NoError(t, a.Finalize())
rwc.testAdaptorClose = func() error {
@ -94,8 +94,8 @@ func TestSpheroAdaptorFinalize(t *testing.T) {
require.ErrorContains(t, a.Finalize(), "close error")
}
func TestSpheroAdaptorConnect(t *testing.T) {
a, _ := initTestSpheroAdaptor()
func TestConnect(t *testing.T) {
a, _ := initTestAdaptor()
require.NoError(t, a.Connect())
a.connect = func(string) (io.ReadWriteCloser, error) {