diff --git a/components/board/board.go b/components/board/board.go index 12b778f4aaa..c203d7c03f8 100644 --- a/components/board/board.go +++ b/components/board/board.go @@ -77,12 +77,23 @@ type Board interface { // An Analog represents an analog pin that resides on a board. type Analog interface { // Read reads off the current value. - Read(ctx context.Context, extra map[string]interface{}) (int, error) + Read(ctx context.Context, extra map[string]interface{}) (AnalogValue, error) // Write writes a value to the analog pin. Write(ctx context.Context, value int, extra map[string]interface{}) error } +// AnalogValue contains all info about the analog reading. +// Value represents the reading in bits. +// Min and Max represent the range of raw analog values. +// StepSize is the precision per bit of the reading. +type AnalogValue struct { + Value int + Min float32 + Max float32 + StepSize float32 +} + // FromDependencies is a helper for getting the named board from a collection of // dependencies. func FromDependencies(deps resource.Dependencies, name string) (Board, error) { diff --git a/components/board/client.go b/components/board/client.go index 05df78fbf04..4e8b28631c1 100644 --- a/components/board/client.go +++ b/components/board/client.go @@ -134,10 +134,10 @@ type analogClient struct { analogName string } -func (ac *analogClient) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +func (ac *analogClient) Read(ctx context.Context, extra map[string]interface{}) (AnalogValue, error) { ext, err := protoutils.StructToStructPb(extra) if err != nil { - return 0, err + return AnalogValue{}, err } // the api method is named ReadAnalogReader, it is named differently than // the board interface functions. @@ -147,9 +147,9 @@ func (ac *analogClient) Read(ctx context.Context, extra map[string]interface{}) Extra: ext, }) if err != nil { - return 0, err + return AnalogValue{}, err } - return int(resp.Value), nil + return AnalogValue{Value: int(resp.Value), Min: resp.MinRange, Max: resp.MaxRange, StepSize: resp.StepSize}, nil } func (ac *analogClient) Write(ctx context.Context, value int, extra map[string]interface{}) error { diff --git a/components/board/client_test.go b/components/board/client_test.go index cf183421997..0265766e165 100644 --- a/components/board/client_test.go +++ b/components/board/client_test.go @@ -141,13 +141,16 @@ func TestWorkingClient(t *testing.T) { test.That(t, injectBoard.AnalogByNameCap(), test.ShouldResemble, []interface{}{"analog1"}) // Analog: Read - injectAnalog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { + injectAnalog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { actualExtra = extra - return 6, nil + return board.AnalogValue{Value: 6, Min: 0, Max: 10, StepSize: 0.1}, nil } - readVal, err := analog1.Read(context.Background(), expectedExtra) + analogVal, err := analog1.Read(context.Background(), expectedExtra) test.That(t, err, test.ShouldBeNil) - test.That(t, readVal, test.ShouldEqual, 6) + test.That(t, analogVal.Value, test.ShouldEqual, 6) + test.That(t, analogVal.Min, test.ShouldEqual, 0) + test.That(t, analogVal.Max, test.ShouldEqual, 10) + test.That(t, analogVal.StepSize, test.ShouldEqual, 0.1) test.That(t, actualExtra, test.ShouldResemble, expectedExtra) actualExtra = nil diff --git a/components/board/collector.go b/components/board/collector.go index 82026cfeb4f..64a77bfdaa0 100644 --- a/components/board/collector.go +++ b/components/board/collector.go @@ -40,13 +40,13 @@ func newAnalogCollector(resource interface{}, params data.CollectorParams) (data } cFunc := data.CaptureFunc(func(ctx context.Context, arg map[string]*anypb.Any) (interface{}, error) { - var value int + var analogValue AnalogValue if _, ok := arg[analogReaderNameKey]; !ok { return nil, data.FailedToReadErr(params.ComponentName, analogs.String(), errors.New("Must supply reader_name in additional_params for analog collector")) } if reader, err := board.AnalogByName(arg[analogReaderNameKey].String()); err == nil { - value, err = reader.Read(ctx, data.FromDMExtraMap) + analogValue, err = reader.Read(ctx, data.FromDMExtraMap) if err != nil { // A modular filter component can be created to filter the readings from a component. The error ErrNoCaptureToStore // is used in the datamanager to exclude readings from being captured and stored. @@ -57,7 +57,10 @@ func newAnalogCollector(resource interface{}, params data.CollectorParams) (data } } return pb.ReadAnalogReaderResponse{ - Value: int32(value), + Value: int32(analogValue.Value), + MinRange: analogValue.Min, + MaxRange: analogValue.Max, + StepSize: analogValue.StepSize, }, nil }) return data.NewCollector(cFunc, params) diff --git a/components/board/collectors_test.go b/components/board/collectors_test.go index a2ea313dc5c..18d5c2f85d5 100644 --- a/components/board/collectors_test.go +++ b/components/board/collectors_test.go @@ -47,7 +47,10 @@ func TestCollectors(t *testing.T) { }, collector: board.NewAnalogCollector, expected: tu.ToProtoMapIgnoreOmitEmpty(pb.ReadAnalogReaderResponse{ - Value: 1, + Value: 1, + MinRange: 0, + MaxRange: 10, + StepSize: 0.1, }), shouldError: false, }, @@ -96,8 +99,8 @@ func TestCollectors(t *testing.T) { func newBoard() board.Board { b := &inject.Board{} analog := &inject.Analog{} - analog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { - return 1, nil + analog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { + return board.AnalogValue{Value: 1, Min: 0, Max: 10, StepSize: 0.1}, nil } b.AnalogByNameFunc = func(name string) (board.Analog, error) { return analog, nil diff --git a/components/board/fake/board.go b/components/board/fake/board.go index 652c2df721b..fb907324f60 100644 --- a/components/board/fake/board.go +++ b/components/board/fake/board.go @@ -289,14 +289,15 @@ func (a *Analog) reset(pin string) { a.Mu.Unlock() } -func (a *Analog) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +func (a *Analog) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { a.Mu.RLock() defer a.Mu.RUnlock() if a.pin != analogTestPin { - a.Value = a.fakeValue a.fakeValue++ + a.fakeValue %= 1001 + a.Value = a.fakeValue } - return a.Value, nil + return board.AnalogValue{Value: a.Value, Min: 0, Max: 1000, StepSize: 1}, nil } func (a *Analog) Write(ctx context.Context, value int, extra map[string]interface{}) error { diff --git a/components/board/genericlinux/board.go b/components/board/genericlinux/board.go index bca89cd14d2..4f6d940d4c1 100644 --- a/components/board/genericlinux/board.go +++ b/components/board/genericlinux/board.go @@ -351,11 +351,11 @@ func newWrappedAnalogReader(ctx context.Context, chipSelect string, reader *pinw return &wrapped } -func (a *wrappedAnalogReader) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +func (a *wrappedAnalogReader) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { a.mu.RLock() defer a.mu.RUnlock() if a.reader == nil { - return 0, errors.New("closed") + return board.AnalogValue{}, errors.New("closed") } return a.reader.Read(ctx, extra) } diff --git a/components/board/mcp3008helper/mcp3008.go b/components/board/mcp3008helper/mcp3008.go index 198f256dff2..722ca189971 100644 --- a/components/board/mcp3008helper/mcp3008.go +++ b/components/board/mcp3008helper/mcp3008.go @@ -7,6 +7,7 @@ import ( "go.uber.org/multierr" + "go.viam.com/rdk/components/board" "go.viam.com/rdk/components/board/genericlinux/buses" "go.viam.com/rdk/grpc" "go.viam.com/rdk/resource" @@ -37,7 +38,9 @@ func (config *MCP3008AnalogConfig) Validate(path string) error { return nil } -func (mar *MCP3008AnalogReader) Read(ctx context.Context, extra map[string]interface{}) (value int, err error) { +func (mar *MCP3008AnalogReader) Read(ctx context.Context, extra map[string]interface{}) ( + analogVal board.AnalogValue, err error, +) { var tx [3]byte tx[0] = 1 // start bit tx[1] = byte((8 + mar.Channel) << 4) // single-ended @@ -45,7 +48,7 @@ func (mar *MCP3008AnalogReader) Read(ctx context.Context, extra map[string]inter bus, err := mar.Bus.OpenHandle() if err != nil { - return 0, err + return board.AnalogValue{}, err } defer func() { err = multierr.Combine(err, bus.Close()) @@ -53,13 +56,14 @@ func (mar *MCP3008AnalogReader) Read(ctx context.Context, extra map[string]inter rx, err := bus.Xfer(ctx, 1000000, mar.Chip, 0, tx[:]) if err != nil { - return 0, err + return board.AnalogValue{}, err } // Reassemble the 10-bit value. Do not include bits before the final 10, because they contain // garbage and might be non-zero. val := 0x03FF & ((int(rx[1]) << 8) | int(rx[2])) - return val, nil + // returning no analog range since mcp3008 will be removed soon. + return board.AnalogValue{Value: val}, nil } // Close does nothing. diff --git a/components/board/numato/board.go b/components/board/numato/board.go index 02bb84009ba..2ae7f6665e9 100644 --- a/components/board/numato/board.go +++ b/components/board/numato/board.go @@ -117,6 +117,9 @@ type numatoBoard struct { sent map[string]bool sentMu sync.Mutex workers rdkutils.StoppableWorkers + + maxAnalogVoltage float32 + stepSize float32 } func (b *numatoBoard) addToSent(msg string) { @@ -354,12 +357,18 @@ type analog struct { pin string } -func (a *analog) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +// Read returns the analog value with the range and step size in V/bit. +func (a *analog) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { res, err := a.b.doSendReceive(ctx, fmt.Sprintf("adc read %s", a.pin)) if err != nil { - return 0, err + return board.AnalogValue{}, err + } + reading, err := strconv.Atoi(res) + if err != nil { + return board.AnalogValue{}, err } - return strconv.Atoi(res) + + return board.AnalogValue{Value: reading, Min: 0, Max: a.b.maxAnalogVoltage, StepSize: a.b.stepSize}, nil } func (a *analog) Write(ctx context.Context, value int, extra map[string]interface{}) error { @@ -384,6 +393,49 @@ func connect(ctx context.Context, name resource.Name, conf *Config, logger loggi path = devs[0].Path } + // Find the numato board's productid + products := getSerialDevices() + + var productID int + for _, product := range products { + if product.ID.Vendor != 0x2a19 { + continue + } + // we can safely get the first numato productID we find because + // we only support one board being used at a time + productID = product.ID.Product + break + } + + // Find the max analog voltage and stepSize based on the productID. + var max float32 + var stepSize float32 + switch productID { + case 0x800: + // 8 and 16 pin usb versions have the same product ID but different voltage ranges + // both have 10 bit resolution + if conf.Pins == 8 { + max = 5.0 + } else if conf.Pins == 16 { + max = 3.3 + } + stepSize = max / 1024 + case 0x802: + // 32 channel usb numato has 10 bit resolution + max = 3.3 + stepSize = max / 1024 + case 0x805: + // 128 channel usb numato has 12 bit resolution + max = 3.3 + stepSize = max / 4096 + case 0xC05: + // 1 channel usb relay module numato - 10 bit resolution + max = 5.0 + stepSize = max / 1024 + default: + logger.Warnf("analog range and step size are not supported for numato with product id %d", productID) + } + options := goserial.OpenOptions{ PortName: path, BaudRate: 19200, @@ -396,12 +448,13 @@ func connect(ctx context.Context, name resource.Name, conf *Config, logger loggi if err != nil { return nil, err } - b := &numatoBoard{ - Named: name.AsNamed(), - pins: pins, - port: device, - logger: logger, + Named: name.AsNamed(), + pins: pins, + port: device, + logger: logger, + maxAnalogVoltage: max, + stepSize: stepSize, } b.analogs = map[string]*pinwrappers.AnalogSmoother{} @@ -419,6 +472,5 @@ func connect(ctx context.Context, name resource.Name, conf *Config, logger loggi return nil, multierr.Combine(b.Close(ctx), err) } b.logger.CDebugw(ctx, "numato startup", "version", ver) - return b, nil } diff --git a/components/board/numato/board_test.go b/components/board/numato/board_test.go index 24eef77b09e..9a598ce9926 100644 --- a/components/board/numato/board_test.go +++ b/components/board/numato/board_test.go @@ -93,21 +93,25 @@ func TestNumato1(t *testing.T) { res2, err := ar.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, res2, test.ShouldBeLessThan, 100) + test.That(t, res2.Value, test.ShouldBeLessThan, 100) + + // Only testing these since the values depend on what board version is connected. + test.That(t, res2.Min, test.ShouldEqual, 0) + test.That(t, res2.Max, test.ShouldBeGreaterThanOrEqualTo, 3.3) err = zeroPin.Set(context.Background(), true, nil) test.That(t, err, test.ShouldBeNil) res2, err = ar.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, res2, test.ShouldBeGreaterThan, 1000) + test.That(t, res2.Value, test.ShouldBeGreaterThan, 1000) err = zeroPin.Set(context.Background(), false, nil) test.That(t, err, test.ShouldBeNil) res2, err = ar.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, res2, test.ShouldBeLessThan, 100) + test.That(t, res2.Value, test.ShouldBeLessThan, 100) } func TestConfigValidate(t *testing.T) { diff --git a/components/board/numato/darwin_utils.go b/components/board/numato/darwin_utils.go new file mode 100644 index 00000000000..c1e15d1133a --- /dev/null +++ b/components/board/numato/darwin_utils.go @@ -0,0 +1,13 @@ +//go:build darwin + +package numato + +import "go.viam.com/utils/usb" + +// getSerialDevices returns all device descriptions connected by USB on mac. This is used to get the +// productID of the numato board being used. +func getSerialDevices() []usb.Description { + return usb.Search(usb.NewSearchFilter("AppleUSBACMData", "usbmodem"), func(vendorID, productID int) bool { + return true + }) +} diff --git a/components/board/numato/linux_utils.go b/components/board/numato/linux_utils.go new file mode 100644 index 00000000000..e7e0d7faa30 --- /dev/null +++ b/components/board/numato/linux_utils.go @@ -0,0 +1,13 @@ +//go:build linux + +package numato + +import "go.viam.com/utils/usb" + +// getSerialDevices returns all devices connected by USB on a linux machine. +// This is used to get the productID of the numato board being used. +func getSerialDevices() []usb.Description { + return usb.Search(usb.SearchFilter{}, func(vendorID, productID int) bool { + return true + }) +} diff --git a/components/board/pi/impl/external_hardware_test.go b/components/board/pi/impl/external_hardware_test.go index 8d543ab1c8f..0a17794fa59 100644 --- a/components/board/pi/impl/external_hardware_test.go +++ b/components/board/pi/impl/external_hardware_test.go @@ -67,7 +67,7 @@ func TestPiHardware(t *testing.T) { v, err := reader.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, v, test.ShouldAlmostEqual, 0, 150) + test.That(t, v.Value, test.ShouldAlmostEqual, 0, 150) // try to set high err = p.SetGPIOBcom(26, true) @@ -75,7 +75,7 @@ func TestPiHardware(t *testing.T) { v, err = reader.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, v, test.ShouldAlmostEqual, 1023, 150) + test.That(t, v.Value, test.ShouldAlmostEqual, 1023, 150) // back to low err = p.SetGPIOBcom(26, false) @@ -83,7 +83,7 @@ func TestPiHardware(t *testing.T) { v, err = reader.Read(ctx, nil) test.That(t, err, test.ShouldBeNil) - test.That(t, v, test.ShouldAlmostEqual, 0, 150) + test.That(t, v.Value, test.ShouldAlmostEqual, 0, 150) }) t.Run("basic interrupts", func(t *testing.T) { diff --git a/components/board/pinwrappers/analog_smoother_test.go b/components/board/pinwrappers/analog_smoother_test.go index 0f5d3b9ae53..a86c6cc2c80 100644 --- a/components/board/pinwrappers/analog_smoother_test.go +++ b/components/board/pinwrappers/analog_smoother_test.go @@ -23,14 +23,14 @@ type testAnalog struct { stop bool } -func (t *testAnalog) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +func (t *testAnalog) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { t.mu.Lock() defer t.mu.Unlock() if t.stop || t.n >= t.lim { - return 0, errStopReading + return board.AnalogValue{}, errStopReading } t.n++ - return t.r.Intn(100), nil + return board.AnalogValue{Value: t.r.Intn(100), Min: 0, Max: 3.3, StepSize: 0.1}, nil } func (t *testAnalog) Write(ctx context.Context, value int, extra map[string]interface{}) error { @@ -62,7 +62,10 @@ func TestAnalogSmoother1(t *testing.T) { tb.Helper() v, err := as.Read(context.Background(), nil) test.That(tb, err, test.ShouldEqual, errStopReading) - test.That(tb, v, test.ShouldEqual, 52) + test.That(tb, v.Value, test.ShouldEqual, 52) + test.That(tb, v.Min, test.ShouldEqual, 0) + test.That(tb, v.Max, test.ShouldEqual, 3.3) + test.That(tb, v.StepSize, test.ShouldEqual, 0.1) // need lock to access testReader.n testReader.mu.Lock() diff --git a/components/board/pinwrappers/analogs.go b/components/board/pinwrappers/analogs.go index 77d80670764..7af73c00275 100644 --- a/components/board/pinwrappers/analogs.go +++ b/components/board/pinwrappers/analogs.go @@ -26,6 +26,7 @@ type AnalogSmoother struct { lastError atomic.Pointer[errValue] logger logging.Logger workers utils.StoppableWorkers + analogVal board.AnalogValue } // SmoothAnalogReader wraps the given reader in a smoother. @@ -40,6 +41,12 @@ func SmoothAnalogReader(r board.Analog, c board.AnalogReaderConfig, logger loggi logger.Debug("Can't read nonpositive samples per second; defaulting to 1 instead") smoother.SamplesPerSecond = 1 } + + // Store the analog reader info + analogVal, err := smoother.Raw.Read(context.Background(), nil) + smoother.lastError.Store(&errValue{err != nil, err}) + smoother.analogVal = analogVal + smoother.Start() return smoother } @@ -57,21 +64,28 @@ func (as *AnalogSmoother) Close(ctx context.Context) error { } // Read returns the smoothed out reading. -func (as *AnalogSmoother) Read(ctx context.Context, extra map[string]interface{}) (int, error) { - if as.data == nil { // We're using raw data, and not averaging - return as.lastData, nil +func (as *AnalogSmoother) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { + analogVal := board.AnalogValue{ + Min: as.analogVal.Min, + Max: as.analogVal.Max, + StepSize: as.analogVal.StepSize, } + if as.data == nil { // We're using raw data, and not averaging + analogVal.Value = as.lastData + return as.analogVal, nil + } avg := as.data.Average() lastErr := as.lastError.Load() + analogVal.Value = avg if lastErr == nil { - return avg, nil + return analogVal, nil } //nolint:forcetypeassert if lastErr.present { - return avg, lastErr.err + return analogVal, lastErr.err } - return avg, nil + return analogVal, nil } // Start begins the smoothing routine that reads from the underlying @@ -104,6 +118,7 @@ func (as *AnalogSmoother) Start() { as.workers = utils.NewStoppableWorkers(func(ctx context.Context) { consecutiveErrors := 0 var lastError error + for { select { case <-ctx.Done(): @@ -114,9 +129,9 @@ func (as *AnalogSmoother) Start() { reading, err := as.Raw.Read(ctx, nil) as.lastError.Store(&errValue{err != nil, err}) if err == nil { - as.lastData = reading + as.lastData = reading.Value if as.data != nil { - as.data.Add(reading) + as.data.Add(reading.Value) } consecutiveErrors = 0 } else { // Non-nil error @@ -136,6 +151,7 @@ func (as *AnalogSmoother) Start() { } } lastError = err + end := time.Now() toSleep := int64(nanosBetween) - (end.UnixNano() - start.UnixNano()) diff --git a/components/board/server.go b/components/board/server.go index 08fcb276d0b..89441731890 100644 --- a/components/board/server.go +++ b/components/board/server.go @@ -144,11 +144,16 @@ func (s *serviceServer) ReadAnalogReader( return nil, err } - val, err := theReader.Read(ctx, req.Extra.AsMap()) + analogValue, err := theReader.Read(ctx, req.Extra.AsMap()) if err != nil { return nil, err } - return &pb.ReadAnalogReaderResponse{Value: int32(val)}, nil + return &pb.ReadAnalogReaderResponse{ + Value: int32(analogValue.Value), + MinRange: analogValue.Min, + MaxRange: analogValue.Max, + StepSize: analogValue.StepSize, + }, nil } // WriteAnalog writes the analog value to the analog writer pin of the underlying robot. diff --git a/components/board/server_test.go b/components/board/server_test.go index 678b9c9dda2..79e764ce1a6 100644 --- a/components/board/server_test.go +++ b/components/board/server_test.go @@ -451,7 +451,6 @@ func TestServerSetPWMFrequency(t *testing.T) { } } -//nolint:dupl func TestServerReadAnalogReader(t *testing.T) { type request = pb.ReadAnalogReaderRequest type response = pb.ReadAnalogReaderResponse @@ -464,7 +463,7 @@ func TestServerReadAnalogReader(t *testing.T) { tests := []struct { injectAnalog *inject.Analog injectAnalogErr error - injectResult int + injectVal board.AnalogValue injectErr error req *request expCapAnalogArgs []interface{} @@ -475,7 +474,7 @@ func TestServerReadAnalogReader(t *testing.T) { { injectAnalog: nil, injectAnalogErr: errAnalog, - injectResult: 0, + injectVal: board.AnalogValue{Value: 0}, injectErr: nil, req: &request{BoardName: missingBoardName}, expCapAnalogArgs: []interface{}(nil), @@ -486,7 +485,7 @@ func TestServerReadAnalogReader(t *testing.T) { { injectAnalog: nil, injectAnalogErr: errAnalog, - injectResult: 0, + injectVal: board.AnalogValue{Value: 0}, injectErr: nil, req: &request{BoardName: testBoardName, AnalogReaderName: "analog1"}, expCapAnalogArgs: []interface{}{"analog1"}, @@ -497,7 +496,7 @@ func TestServerReadAnalogReader(t *testing.T) { { injectAnalog: &inject.Analog{}, injectAnalogErr: nil, - injectResult: 0, + injectVal: board.AnalogValue{Value: 0}, injectErr: errFoo, req: &request{BoardName: testBoardName, AnalogReaderName: "analog1"}, expCapAnalogArgs: []interface{}{"analog1"}, @@ -508,12 +507,12 @@ func TestServerReadAnalogReader(t *testing.T) { { injectAnalog: &inject.Analog{}, injectAnalogErr: nil, - injectResult: 8, + injectVal: board.AnalogValue{Value: 8, Min: 0, Max: 10, StepSize: 0.1}, injectErr: nil, req: &request{BoardName: testBoardName, AnalogReaderName: "analog1", Extra: pbExpectedExtra}, expCapAnalogArgs: []interface{}{"analog1"}, expCapArgs: []interface{}{ctx}, - expResp: &response{Value: 8}, + expResp: &response{Value: 8, MinRange: 0, MaxRange: 10, StepSize: 0.1}, expRespErr: "", }, } @@ -529,9 +528,9 @@ func TestServerReadAnalogReader(t *testing.T) { } if tc.injectAnalog != nil { - tc.injectAnalog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { + tc.injectAnalog.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { actualExtra = extra - return tc.injectResult, tc.injectErr + return tc.injectVal, tc.injectErr } } @@ -619,7 +618,6 @@ func TestServerWriteAnalog(t *testing.T) { } } -//nolint:dupl func TestServerGetDigitalInterruptValue(t *testing.T) { type request = pb.GetDigitalInterruptValueRequest type response = pb.GetDigitalInterruptValueResponse diff --git a/components/board/status.go b/components/board/status.go index 003d289f096..2d0d67a5c42 100644 --- a/components/board/status.go +++ b/components/board/status.go @@ -24,7 +24,7 @@ func CreateStatus(ctx context.Context, b Board) (*pb.Status, error) { if err != nil { return nil, errors.Wrapf(err, "couldn't read analog (%s)", name) } - status.Analogs[name] = int32(val) + status.Analogs[name] = int32(val.Value) } } diff --git a/components/gripper/softrobotics/gripper.go b/components/gripper/softrobotics/gripper.go index 4394c2f6849..ed72f96e93d 100644 --- a/components/gripper/softrobotics/gripper.go +++ b/components/gripper/softrobotics/gripper.go @@ -174,7 +174,7 @@ func (g *softGripper) Open(ctx context.Context, extra map[string]interface{}) er return multierr.Combine(err, g.Stop(ctx, extra)) } - if val > 500 { + if val.Value > 500 { break } @@ -209,7 +209,7 @@ func (g *softGripper) Grab(ctx context.Context, extra map[string]interface{}) (b return false, multierr.Combine(err, g.Stop(ctx, extra)) } - if val <= 200 { + if val.Value <= 200 { break } diff --git a/components/input/gpio/gpio.go b/components/input/gpio/gpio.go index 4d7b8d065bc..4c78780f5f3 100644 --- a/components/input/gpio/gpio.go +++ b/components/input/gpio/gpio.go @@ -327,29 +327,29 @@ func (c *Controller) newAxis(ctx context.Context, brd board.Board, analogName st c.logger.CError(ctx, err) } - if rawVal > cfg.Max { - rawVal = cfg.Max - } else if rawVal < cfg.Min { - rawVal = cfg.Min + if rawVal.Value > cfg.Max { + rawVal.Value = cfg.Max + } else if rawVal.Value < cfg.Min { + rawVal.Value = cfg.Min } var outVal float64 if cfg.Bidirectional { center := (cfg.Min + cfg.Max) / 2 - if abs(rawVal-center) < cfg.Deadzone { - rawVal = center + if abs(rawVal.Value-center) < cfg.Deadzone { + rawVal.Value = center outVal = 0.0 } else { - outVal = scaleAxis(rawVal, cfg.Min, cfg.Max, -1, 1) + outVal = scaleAxis(rawVal.Value, cfg.Min, cfg.Max, -1, 1) } } else { - if abs(rawVal-cfg.Min) < cfg.Deadzone { - rawVal = cfg.Min + if abs(rawVal.Value-cfg.Min) < cfg.Deadzone { + rawVal.Value = cfg.Min } - outVal = scaleAxis(rawVal, cfg.Min, cfg.Max, 0, 1) + outVal = scaleAxis(rawVal.Value, cfg.Min, cfg.Max, 0, 1) } - if abs(rawVal-prevVal) < cfg.MinChange { + if abs(rawVal.Value-prevVal) < cfg.MinChange { continue } @@ -357,7 +357,7 @@ func (c *Controller) newAxis(ctx context.Context, brd board.Board, analogName st outVal *= -1 } - prevVal = rawVal + prevVal = rawVal.Value eventOut := input.Event{ Time: time.Now(), Event: input.PositionChangeAbs, diff --git a/components/input/gpio/gpio_test.go b/components/input/gpio/gpio_test.go index ef7d3f64377..fdd517e1221 100644 --- a/components/input/gpio/gpio_test.go +++ b/components/input/gpio/gpio_test.go @@ -96,20 +96,20 @@ func setup(t *testing.T) *setupResult { return nil } - s.analog1.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { + s.analog1.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { s.mu.Lock() defer s.mu.Unlock() - return analog1Val, nil + return board.AnalogValue{Value: analog1Val}, nil } - s.analog2.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { + s.analog2.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { s.mu.Lock() defer s.mu.Unlock() - return analog2Val, nil + return board.AnalogValue{Value: analog2Val}, nil } - s.analog3.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (int, error) { + s.analog3.ReadFunc = func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { s.mu.Lock() defer s.mu.Unlock() - return analog3Val, nil + return board.AnalogValue{Value: analog3Val}, nil } s.analog2.WriteFunc = func(ctx context.Context, value int, extra map[string]interface{}) error { diff --git a/testutils/inject/analog.go b/testutils/inject/analog.go index e3b9550e0c7..70ed8122588 100644 --- a/testutils/inject/analog.go +++ b/testutils/inject/analog.go @@ -9,14 +9,14 @@ import ( // Analog is an injected analog pin. type Analog struct { board.Analog - ReadFunc func(ctx context.Context, extra map[string]interface{}) (int, error) + ReadFunc func(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) readCap []interface{} WriteFunc func(ctx context.Context, value int, extra map[string]interface{}) error writeCap []interface{} } // Read calls the injected Read or the real version. -func (a *Analog) Read(ctx context.Context, extra map[string]interface{}) (int, error) { +func (a *Analog) Read(ctx context.Context, extra map[string]interface{}) (board.AnalogValue, error) { a.readCap = []interface{}{ctx} if a.ReadFunc == nil { return a.Analog.Read(ctx, extra)