Conversation
| private static GpioDriver GetBestDriverForBoard() | ||
| /// <returns>An instance of a GpioDriver that best matches the current hardware</returns> | ||
| /// <exception cref="PlatformNotSupportedException">No matching driver could be found</exception> | ||
| protected static GpioDriver GetBestDriverForBoard() |
There was a problem hiding this comment.
I think perhaps this should be just GpioDriver.Create
There was a problem hiding this comment.
How would we know which driver to create?
c9dfee5 to
aa00c42
Compare
|
|
||
| /// <returns>An instance of a GpioDriver that best matches the current hardware</returns> | ||
| /// <exception cref="PlatformNotSupportedException">No matching driver could be found</exception> | ||
| private static GpioDriver GetBestDriverForBoard() |
There was a problem hiding this comment.
Having this private prevents adding new APIs, but requires copy-pasting the implementation to the Board infrastructure. That's not so big of a deal for now, except that it requires a few ugly try-catch attempts in testing which driver is the right one.
I think we need the identification logic to become public somehow (or at least provide each driver with a IsCompatible check)
There was a problem hiding this comment.
Let's refrain from fixing this in this PR and perhaps suggest GpioDriver.Create in a separate PR/issue
There was a problem hiding this comment.
I agree to do that later. We should just not forget about it.
|
@pgrawehr can you squash and rebase to take nullability changes? |
Ellerbach
left a comment
There was a problem hiding this comment.
Looks very good to me. thanks!
| } | ||
|
|
||
| // TODO: This is still broken. See #974 | ||
| ////[Fact] |
There was a problem hiding this comment.
Only 2 // should be enougth :-) Not blocking, and thanks for adding the comment that this code even if dead should stay here
There was a problem hiding this comment.
If only using two // you get StyleCop errors, because then the comment must be immediatelly followed by code. And a /// comment on the next function (if any) must not, on the other hand, be preceeded by a // comment. So //// is the only way to properly comment out an entire function.
There was a problem hiding this comment.
/*perhaps there are other ways*/
There was a problem hiding this comment.
(I'm fine with quadra comments though)
Yes, will do. |
Need to implement a derived class later
All public methods must translate the NumberingScheme to identify the correct pin that is meant.
These run purely in software.
Simplifies method implementation
aa00c42 to
4a58f81
Compare
|
/azp run |
|
Commenter does not have sufficient privileges for PR 1254 in repo dotnet/iot |
|
@joperezr Can you please restart the build here? |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
This pr looks very good and promising |
|
merging soon? |
|
@colombod Yes, this is just awaiting another quick review from joperezr, because he's most familiar with these pieces. |
|
Perfect! |
|
@joperezr have time to review? |
Is there a reason why we added the OpenPinCore and ClosePinCore (where we are doing the validation on the non-virtual ones) in some cases but not added a SetPinModeCore as well that did the validation above? I'm basically just wondering if we should've just made OpenPin and ClosePin virtual as well and remove the Core ones. Refers to: src/System.Device.Gpio/System/Device/Gpio/GpioController.cs:161 in 41b1c1a. [](commit_id = 41b1c1a, deletion_comment = False) |
| /// <param name="mode">The mode to check.</param> | ||
| /// <returns>The status if the pin supports the mode.</returns> | ||
| public bool IsPinModeSupported(int pinNumber, PinMode mode) | ||
| public virtual bool IsPinModeSupported(int pinNumber, PinMode mode) |
There was a problem hiding this comment.
The implementation of this is using _openPins list which is private (instead of protected) as well as _driver, which is also private. Should those two fields be protected instead so that subclasses can use them in their implementation?
There was a problem hiding this comment.
I also think that we need to have access to the collection maybe via protected methods instead of giving access to the collection itself? But is we can override things like the pincount property and ispinopen there is also the need to have a way to check the collection
| public void Dispose() | ||
| { | ||
| Dispose(true); | ||
| GC.SuppressFinalize(this); |
There was a problem hiding this comment.
NIT: I know this is the standard implementation of the Dispose pattern, but the class has no finalizer so this will not have any effect. I would just remove the line.
|
Left some small comments/questions but this is mostly ready to go as soon as those points are finalized. |
Except for the initial validation that the pin is open, the other methods don't do anything except calling into the driver, so adding the Core variant seems a bit to much, but I could life with that as well.
I think this is not necessary, since the (base) implementation of The |
|
Left one last small comment, but that and the finalizer are the only remaining things to clear out. |
There's no finalizer present
|
@joperezr : Removed the finalizer line, but which other comment do you mean? |
joperezr
left a comment
There was a problem hiding this comment.
Sorry forgot to click send 😀
| /// <param name="pinNumber">The pin number in the controller's numbering scheme.</param> | ||
| /// <returns>The status if the pin is open or closed.</returns> | ||
| public bool IsPinOpen(int pinNumber) | ||
| public virtual bool IsPinOpen(int pinNumber) |
There was a problem hiding this comment.
What is the scenario then for making this one virtual? If pin open/closed status is going to stay in base, kind of feels like we should keep this method not virtual
There was a problem hiding this comment.
I guess either way is ok.
There was a problem hiding this comment.
I would rather keep it non-overridable for now if we don't see a good reason to do it, and in the future change it if we think it is necessary. That would be the non-breaking way to do it. I think if we allow people to override this, they will 100% of the time either call base implementation only, or try to do something different and get it wrong.
| } | ||
|
|
||
| int logicalPinNumber = GetLogicalPinNumber(pinNumber); | ||
| return _driver.GetPinMode(logicalPinNumber); |
There was a problem hiding this comment.
Maybe all this drive access can be exposed via protected methods like *FromDriver, eg GetPinModeFromDriver, OpenPinFromDriver or at least give access to the driver instance via a protected property?
There was a problem hiding this comment.
That adds a lot of complexity to the interface. Why would you need that?
There was a problem hiding this comment.
Yeah, I agree that those would just add complexity and don't see a good use case for them.
| /// Represents a general-purpose I/O (GPIO) controller. | ||
| /// </summary> | ||
| public sealed class GpioController : IDisposable | ||
| public class GpioController : IDisposable |
There was a problem hiding this comment.
I don't think that this class can satisfy the scenario that I am using in the pi-top libraries. I need a controller that is just a proxy to a real controller. With this implementation anything conforming with the controller interface will have to call base class constructors that will try to create a drive. This part of the class contracts seems a little bit too restrictive.
There was a problem hiding this comment.
can you pass null driver and override all methods?
There was a problem hiding this comment.
sounds little bit aggressive if null is allowed then then internal calls should be something like bool TryOpenPin(), that should wrap the driver access. Such protected methods will then potentially address the previous questions about the driver.
I like your suggestion, if we can then protected the access to the driver api via a Try* set of methods that handles the null driver that would be an easier pattern for implementors to adopt.
There was a problem hiding this comment.
@colombod Can you elaborate a bit more on your use case?
It is possible to pass null as the driver, or a DummyDriver() [will be provided in one of my next PRs]. All methods accessing the _driver member are virtual, so if you override all of them (without calling the base implementation, of course), you should be fine.
There was a problem hiding this comment.
in the pi-top libraries we use a gpio controller factory, this returns a wrapper that tracks it's onw pin open/close logic and also events subscriptions. All work si delegated to the concrete controller.
on dispose such controller "projections" dispose all things that where opened via the proxy. That controller will require to have no driver and track the pins open requests. If passing null is safe in this PR then is good for me. And will be able to adopt this new class and finally get rid of the interfaces we introduced. This is the shared controller class
There was a problem hiding this comment.
Yea, I think this should all fit now. You do not need any of your intermediate classes. Even SharedGpioController doesn't seem to do anything more than what is aleady implemented.
I have some questions about constructor requirements too |
* Unseal GpioController Need to implement a derived class later * Fix usage of NumberingScheme All public methods must translate the NumberingScheme to identify the correct pin that is meant. * Add unit tests for GpioController These run purely in software. * Use default NumberingScheme everywhere in Controller Simplifies method implementation * Add missing license header * Remove unnecessary cleanup call There's no finalizer present * Ensure a null driver doesn't cause problems in Dispose * IsPinOpen doesn't need to be virtual
As discussed in several tickets and PRs already: this unseals the GpioController and changes its public API to virtual methods.
In addition, I fixed the PinNumberingScheme.Board use case. Now all methods do a proper translation to the logical scheme the driver is using. The bug #974 is still open, though. This requires further thought and possibly an interface change.
Unit testing for all methods provided as well (using a bit of a hacky approach to mocking, but there's no other way given that all the methods of the GpioDriver class are internal protected). At least the MockableGpioDriver class is reusable.