Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-7152 Analog Reader API rdk changes #3946

Merged
merged 20 commits into from
May 16, 2024

Conversation

oliviamiller
Copy link
Member

Numato was weird due to the driver being able to support multiple numato versions that all have different ADCs. the driver will search for the product id and if its one of the 4 that we "officially" support we return the correct values, otherwise we just return zeros.

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label May 13, 2024
Copy link
Contributor

Warning your change may break code samples. If your change modifies any of the following functions please contact @viamrobotics/fleet-management. Thanks!

component function
base IsMoving
board GPIOPinByName
camera Properties
encoder Properties
motor IsMoving
sensor Readings
servo Position
arm EndPosition
audio MediaProperties
gantry Lengths
gripper IsMoving
input_controller Controls
movement_sensor LinearAcceleration
power_sensor Power
pose_tracker Poses
motion GetPose
vision ClassificationsFromCamera

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 13, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 13, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 13, 2024
@oliviamiller oliviamiller marked this pull request as ready for review May 13, 2024 20:26
Copy link
Member

@penguinland penguinland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple small but important bugs, and then a whole bunch of optional suggestions and discussion questions that don't have obvious answers (so, maybe ignore those and leave things as-is).

@@ -77,12 +77,19 @@ 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{}) (int, AnalogRange, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion question: what are the pros and cons of this approach vs. making the new struct AnalogValue and having all 4 fields in it (the 3 currently in AnalogRange and the value itself)? I find the new function signature to Read surprising, because I'd expect it to return the data we read and an error, and having a third return value is unexpected. AnalogValue would also more closely match the protobuf structure (though matching the protobuf structure is not a goal in and of itself; it's just an unsurprising and natural thing to do).

I'm not opposed to this approach, I just think it's surprising, and am wondering if there's a reason you went with this choice instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change to returning anAnalogValue if you prefer it. I thought that adding a new return would be less breaking since if you still just want the bit value only you can get it and do _ for the analogrange, but still a breaking change either way so probably doesn't matter that much

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong preference; I just think it's something to consider, in the hopes that on further thought one design is better than the other. Leave as-is if you want. 🤷

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay I changed it to one return value, prob better to just have one if theres not a great reason to have 2

components/board/board.go Show resolved Hide resolved
components/board/fake/board.go Outdated Show resolved Hide resolved
components/board/mcp3008helper/mcp3008.go Outdated Show resolved Hide resolved
components/board/server.go Outdated Show resolved Hide resolved
components/board/numato/board_test.go Outdated Show resolved Hide resolved
components/board/numato/darwin_utils.go Show resolved Hide resolved
stepSize = max / 1024
default:
}
stepSize *= 1000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we multiplying stepSize by a thousand? The whole idea of "maximum voltage divided by maximum bit size" seemed like a good idea to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scope doc converted the example calculation to mV/bit, I think it makes it more readable otherwise its very small values but willing to discuss changing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense! Put a comment here pointing out the right units.

(apologies for the confusion where I remarked on this in the test)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...wait, so Min and Max are in volts, but StepSize is in millivolts? Put a comment in the definition of the struct explaining that.

...or rather, it seems surprising that they have different units, and perhaps the way to make it less surprising, instead of documenting the surprise, is to change the units so they're either all volts or all millivolts. I checked the scope doc, but it doesn't explicitly mention the units on any of these values (two examples mention mV and rejected alternative 3 mentions units, but I don't think this was ever laid out clearly).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah thats fair ill just keep it in volts/bit

components/board/numato/board.go Show resolved Hide resolved
components/board/numato/board.go Outdated Show resolved Hide resolved
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 14, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 14, 2024
@viambot viambot removed the safe to test This pull request is marked safe to test from a trusted zone label May 14, 2024
@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label May 14, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 14, 2024
@oliviamiller oliviamiller requested a review from penguinland May 14, 2024 19:18
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
Copy link
Member

@penguinland penguinland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All my feedback is either optional or trivial. LGTM!

// 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks for this comment!

case 0xC05:
// 1 channel usb relay module numato - 10 bit resolution
max = 5.0
stepSize = max / 1024
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion question: none of these ever change, right? Would it be simpler to calculate them once on initialization, and then just use the stored values within Read()? I'm unsure: we don't really care about them until here, but it feels wasteful to have a switch statement (which contains a memory fence, which slows everything down) every time we read the value. and yet, such an optimization is probably small enough it's not even worth worrying over, and this version is just fine. I dunno; what do you think? Maybe leave as-is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that makes sense to me, ill do this at the start instead.

}

if productID != 0x800 && productID != 0x802 && productID != 0x805 && productID != 0xC05 {
logger.Warnf("analog range and step size is not supported for numato with product id %d", productID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change is to are.


import "go.viam.com/utils/usb"

// getSerialDevices returns all devices connected by USB on a mac.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment doesn't really match code: I think this returns descriptions of devices, not devices themselves. and yet, I can get that from the function signature, so it doesn't necessarily need to be in the comment, too. but I still don't easily see what this function is for. I think the point is that we use it to find the productID of the numato board plugged in, and use that to figure out what capabilities the board has? That's the part that really belongs in a comment: supply the "why" that isn't easily gotten from the code (which supplies the "what").

Same suggestion in linux_utils.go, too.

// 32 channel usb numato has 10 bit resolution
max = 3.3
stepSize = max / 1024
case 0x800:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mild preference to sort the cases in the switch statement, so it's easier to find the one you care about.

as.lastError.Store(&errValue{err != nil, err})
as.mu.Lock()
as.analogVal = analogVal
as.mu.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it look like the point of the mutex is to protect analogVal. If that's the case, we should also lock the mutex before line 65. I wonder if lines 70-71 (locking and deferring unlocking the mutex) should just be moved to the top of the function for simplicity.

Another option is to change Read to never return as.analogVal itself, but always to create a new board.AnalogValue struct instead and copy data into that. Then, as.analogVal is constant after this line, and we don't need the mutex at all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did the second option and removed the mutex

@@ -104,6 +110,14 @@ func (as *AnalogSmoother) Start() {
as.workers = utils.NewStoppableWorkers(func(ctx context.Context) {
consecutiveErrors := 0
var lastError error

// Store the full analog reader value
analogVal, err := as.Raw.Read(ctx, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a race condition here: if someone calls Read() before the background goroutine has gotten to here, the min/max/stepsize will all be 0. I suspect this paragraph should happen in SmoothAnalogReader() instead of here, so the entire struct is initialized before it can be used.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels May 16, 2024
@oliviamiller oliviamiller merged commit 1f3cbd0 into viamrobotics:main May 16, 2024
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants