diff --git a/README.md b/README.md index c8b524320..7970c30f5 100644 --- a/README.md +++ b/README.md @@ -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() diff --git a/adaptor.go b/adaptor.go index 6eedb50cb..cb66b8315 100644 --- a/adaptor.go +++ b/adaptor.go @@ -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) } diff --git a/doc.go b/doc.go index 2f1eee92a..e20fe04e6 100644 --- a/doc.go +++ b/doc.go @@ -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() diff --git a/driver.go b/driver.go index 7581f2826..650de09b3 100644 --- a/driver.go +++ b/driver.go @@ -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 diff --git a/drivers/MIGRATION.md b/drivers/MIGRATION.md index 2d80d434d..5f5cabdcf 100644 --- a/drivers/MIGRATION.md +++ b/drivers/MIGRATION.md @@ -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 diff --git a/drivers/ble/battery_driver.go b/drivers/ble/battery_driver.go index f97ecafef..9ce222c9c 100644 --- a/drivers/ble/battery_driver.go +++ b/drivers/ble/battery_driver.go @@ -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 } diff --git a/drivers/ble/battery_driver_test.go b/drivers/ble/battery_driver_test.go index 6495317ff..9fe9f5f63 100644 --- a/drivers/ble/battery_driver_test.go +++ b/drivers/ble/battery_driver_test.go @@ -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) } diff --git a/drivers/ble/ble_driver.go b/drivers/ble/ble_driver.go index e1b766b36..3634cbe8a 100644 --- a/drivers/ble/ble_driver.go +++ b/drivers/ble/ble_driver.go @@ -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) } diff --git a/drivers/ble/ble_driver_test.go b/drivers/ble/ble_driver_test.go index 5e40f313b..14cf1dbbd 100644 --- a/drivers/ble/ble_driver_test.go +++ b/drivers/ble/ble_driver_test.go @@ -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()) +} diff --git a/drivers/ble/device_information_driver.go b/drivers/ble/device_information_driver.go index 8eca3c21b..ef6949df7 100644 --- a/drivers/ble/device_information_driver.go +++ b/drivers/ble/device_information_driver.go @@ -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 } diff --git a/drivers/ble/device_information_driver_test.go b/drivers/ble/device_information_driver_test.go index 976849e6c..38f362b5b 100644 --- a/drivers/ble/device_information_driver_test.go +++ b/drivers/ble/device_information_driver_test.go @@ -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) } diff --git a/drivers/ble/generic_access_driver.go b/drivers/ble/generic_access_driver.go index 776db09ce..cdf89a298 100644 --- a/drivers/ble/generic_access_driver.go +++ b/drivers/ble/generic_access_driver.go @@ -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{ diff --git a/drivers/ble/generic_access_driver_test.go b/drivers/ble/generic_access_driver_test.go index 845cc5881..50d7a65c5 100644 --- a/drivers/ble/generic_access_driver_test.go +++ b/drivers/ble/generic_access_driver_test.go @@ -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) } diff --git a/drivers/ble/microbit/accelerometer_driver.go b/drivers/ble/microbit/accelerometer_driver.go index 634bee816..9ffe4c732 100644 --- a/drivers/ble/microbit/accelerometer_driver.go +++ b/drivers/ble/microbit/accelerometer_driver.go @@ -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) diff --git a/drivers/ble/microbit/accelerometer_driver_test.go b/drivers/ble/microbit/accelerometer_driver_test.go index fd59c2528..f0db11a3d 100644 --- a/drivers/ble/microbit/accelerometer_driver_test.go +++ b/drivers/ble/microbit/accelerometer_driver_test.go @@ -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: diff --git a/drivers/ble/microbit/button_driver.go b/drivers/ble/microbit/button_driver.go index a095f3828..e0b2c7bbb 100644 --- a/drivers/ble/microbit/button_driver.go +++ b/drivers/ble/microbit/button_driver.go @@ -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) }) } diff --git a/drivers/ble/microbit/button_driver_test.go b/drivers/ble/microbit/button_driver_test.go index 886bc05e9..0e6303511 100644 --- a/drivers/ble/microbit/button_driver_test.go +++ b/drivers/ble/microbit/button_driver_test.go @@ -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: diff --git a/drivers/ble/microbit/io_pin_driver.go b/drivers/ble/microbit/io_pin_driver.go index 883568fc3..d45b2d6c2 100644 --- a/drivers/ble/microbit/io_pin_driver.go +++ b/drivers/ble/microbit/io_pin_driver.go @@ -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 } diff --git a/drivers/ble/microbit/io_pin_driver_test.go b/drivers/ble/microbit/io_pin_driver_test.go index f73c1d27e..3bdce9f1e 100644 --- a/drivers/ble/microbit/io_pin_driver_test.go +++ b/drivers/ble/microbit/io_pin_driver_test.go @@ -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) diff --git a/drivers/ble/microbit/led_driver.go b/drivers/ble/microbit/led_driver.go index 3de8a5b30..7116c4671 100644 --- a/drivers/ble/microbit/led_driver.go +++ b/drivers/ble/microbit/led_driver.go @@ -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 } diff --git a/drivers/ble/microbit/led_driver_test.go b/drivers/ble/microbit/led_driver_test.go index 18e09e7ef..a3ea15b55 100644 --- a/drivers/ble/microbit/led_driver_test.go +++ b/drivers/ble/microbit/led_driver_test.go @@ -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})) diff --git a/drivers/ble/microbit/magnetometer_driver.go b/drivers/ble/microbit/magnetometer_driver.go index 2fb8972e0..630762210 100644 --- a/drivers/ble/microbit/magnetometer_driver.go +++ b/drivers/ble/microbit/magnetometer_driver.go @@ -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) diff --git a/drivers/ble/microbit/magnetometer_driver_test.go b/drivers/ble/microbit/magnetometer_driver_test.go index 9cd098243..d2d8c6639 100644 --- a/drivers/ble/microbit/magnetometer_driver_test.go +++ b/drivers/ble/microbit/magnetometer_driver_test.go @@ -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: diff --git a/drivers/ble/microbit/temperature_driver.go b/drivers/ble/microbit/temperature_driver.go index 6eb6ac373..5e86a3249 100644 --- a/drivers/ble/microbit/temperature_driver.go +++ b/drivers/ble/microbit/temperature_driver.go @@ -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() diff --git a/drivers/ble/microbit/temperature_driver_test.go b/drivers/ble/microbit/temperature_driver_test.go index 070537ddd..ea8984b4c 100644 --- a/drivers/ble/microbit/temperature_driver_test.go +++ b/drivers/ble/microbit/temperature_driver_test.go @@ -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: diff --git a/drivers/ble/parrot/minidrone_driver.go b/drivers/ble/parrot/minidrone_driver.go index f1fee75cb..e42f1f370 100644 --- a/drivers/ble/parrot/minidrone_driver.go +++ b/drivers/ble/parrot/minidrone_driver.go @@ -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 diff --git a/drivers/ble/parrot/minidrone_driver_test.go b/drivers/ble/parrot/minidrone_driver_test.go index bbae0c67e..add322d30 100644 --- a/drivers/ble/parrot/minidrone_driver_test.go +++ b/drivers/ble/parrot/minidrone_driver_test.go @@ -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()) diff --git a/drivers/ble/serial_port.go b/drivers/ble/serial_port.go index fbf7addcb..86aefe28c 100644 --- a/drivers/ble/serial_port.go +++ b/drivers/ble/serial_port.go @@ -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...) diff --git a/drivers/ble/serial_port_test.go b/drivers/ble/serial_port_test.go index 1d045f3b1..9b2df6316 100644 --- a/drivers/ble/serial_port_test.go +++ b/drivers/ble/serial_port_test.go @@ -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) + } + }) + } +} diff --git a/drivers/ble/sphero/sphero_bb8_driver.go b/drivers/ble/sphero/sphero_bb8_driver.go index e1a459cbc..ee6f9088b 100644 --- a/drivers/ble/sphero/sphero_bb8_driver.go +++ b/drivers/ble/sphero/sphero_bb8_driver.go @@ -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 diff --git a/drivers/ble/sphero/sphero_bb8_driver_test.go b/drivers/ble/sphero/sphero_bb8_driver_test.go index 962239cc0..145c9c41e 100644 --- a/drivers/ble/sphero/sphero_bb8_driver_test.go +++ b/drivers/ble/sphero/sphero_bb8_driver_test.go @@ -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()) +} diff --git a/drivers/ble/sphero/sphero_ollie_driver.go b/drivers/ble/sphero/sphero_ollie_driver.go index fffc4a008..81c2daf9b 100644 --- a/drivers/ble/sphero/sphero_ollie_driver.go +++ b/drivers/ble/sphero/sphero_ollie_driver.go @@ -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 diff --git a/drivers/ble/sphero/sphero_ollie_driver_test.go b/drivers/ble/sphero/sphero_ollie_driver_test.go index d4758c970..671cd1758 100644 --- a/drivers/ble/sphero/sphero_ollie_driver_test.go +++ b/drivers/ble/sphero/sphero_ollie_driver_test.go @@ -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): diff --git a/drivers/ble/sphero/sphero_sprkplus_driver.go b/drivers/ble/sphero/sphero_sprkplus_driver.go index 744d571c9..75af9f310 100644 --- a/drivers/ble/sphero/sphero_sprkplus_driver.go +++ b/drivers/ble/sphero/sphero_sprkplus_driver.go @@ -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 diff --git a/drivers/ble/sphero/sphero_sprkplus_driver_test.go b/drivers/ble/sphero/sphero_sprkplus_driver_test.go index 382c08ed0..ed74f2d85 100644 --- a/drivers/ble/sphero/sphero_sprkplus_driver_test.go +++ b/drivers/ble/sphero/sphero_sprkplus_driver_test.go @@ -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()) +} diff --git a/drivers/ble/testutil/testutil.go b/drivers/ble/testutil/testutil.go index 4724e5689..58f8e558e 100644 --- a/drivers/ble/testutil/testutil.go +++ b/drivers/ble/testutil/testutil.go @@ -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) { +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, err) + 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 } diff --git a/drivers/serial/serial_driver.go b/drivers/serial/serial_driver.go index 9b1aaf724..32e8ec7e7 100644 --- a/drivers/serial/serial_driver.go +++ b/drivers/serial/serial_driver.go @@ -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) } diff --git a/drivers/serial/serial_driver_test.go b/drivers/serial/serial_driver_test.go index b2dda80c6..7e965b609 100644 --- a/drivers/serial/serial_driver_test.go +++ b/drivers/serial/serial_driver_test.go @@ -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" diff --git a/drivers/serial/sphero_driver.go b/drivers/serial/sphero_driver.go index b65c52f7b..6bdd969f5 100644 --- a/drivers/serial/sphero_driver.go +++ b/drivers/serial/sphero_driver.go @@ -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), diff --git a/drivers/serial/sphero_driver_test.go b/drivers/serial/sphero_driver_test.go index 417f474d0..55f57d23c 100644 --- a/drivers/serial/sphero_driver_test.go +++ b/drivers/serial/sphero_driver_test.go @@ -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{} diff --git a/examples/ble_battery.go b/examples/ble_battery.go index 4656954fa..22276b2b6 100644 --- a/examples/ble_battery.go +++ b/examples/ble_battery.go @@ -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) }) } diff --git a/examples/ble_device_info.go b/examples/ble_device_info.go index bff64e7e9..e463a8ccb 100644 --- a/examples/ble_device_info.go +++ b/examples/ble_device_info.go @@ -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", diff --git a/examples/ble_generic_access.go b/examples/ble_generic_access.go index 416571206..aa77064a6 100644 --- a/examples/ble_generic_access.go +++ b/examples/ble_generic_access.go @@ -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", diff --git a/examples/ble_multiple_generic.go b/examples/ble_multiple_generic.go index bbce13d9e..ddc7d313f 100644 --- a/examples/ble_multiple_generic.go +++ b/examples/ble_multiple_generic.go @@ -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, diff --git a/examples/ble_multiple_info.go b/examples/ble_multiple_info.go index 6155138b3..b4960e9a5 100644 --- a/examples/ble_multiple_info.go +++ b/examples/ble_multiple_info.go @@ -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, diff --git a/examples/serial_sphero_conways.go b/examples/serial_sphero_conways.go index 5113ab5a0..c03a0b2df 100644 --- a/examples/serial_sphero_conways.go +++ b/examples/serial_sphero_conways.go @@ -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) diff --git a/examples/serial_sphero_multiple.go b/examples/serial_sphero_multiple.go index c728ce9ff..a5ab95b02 100644 --- a/examples/serial_sphero_multiple.go +++ b/examples/serial_sphero_multiple.go @@ -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() diff --git a/platforms/bleclient/README.md b/platforms/bleclient/README.md index c243cf7a7..271c93880 100644 --- a/platforms/bleclient/README.md +++ b/platforms/bleclient/README.md @@ -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) }) } diff --git a/platforms/bleclient/ble_client_adaptor.go b/platforms/bleclient/ble_client_adaptor.go index f73605930..ffc09f155 100644 --- a/platforms/bleclient/ble_client_adaptor.go +++ b/platforms/bleclient/ble_client_adaptor.go @@ -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) diff --git a/platforms/serialport/adaptor_test.go b/platforms/serialport/adaptor_test.go index 65d7a4486..dd5321e8e 100644 --- a/platforms/serialport/adaptor_test.go +++ b/platforms/serialport/adaptor_test.go @@ -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")) - a.SetName("NewName") - assert.Equal(t, "NewName", a.Name()) + assert.Equal(t, "/dev/null", a.Port()) } -func TestSpheroAdaptor(t *testing.T) { - a, _ := initTestSpheroAdaptor() +func TestName(t *testing.T) { + a, _ := initTestAdaptor() assert.True(t, strings.HasPrefix(a.Name(), "Serial")) - assert.Equal(t, "/dev/null", a.Port()) + a.SetName("NewName") + assert.Equal(t, "NewName", a.Name()) } -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) {