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

Adding GpioPin #1895

Merged
merged 18 commits into from
Dec 1, 2022
Merged

Adding GpioPin #1895

merged 18 commits into from
Dec 1, 2022

Conversation

Ellerbach
Copy link
Member

@Ellerbach Ellerbach commented Jul 23, 2022

Adding GpioPin. Following design discussions here: #1671

This PR now implements what's been discussed during the API proposal call and is summarized here: #1920

Microsoft Reviewers: Open in CodeFlow

@joperezr
Copy link
Member

I was expecting this to be a draft PR, or a PR against a feature branch. What is the intention of this @Ellerbach? This would have to go through APIReview first before it can get merged.

@Ellerbach
Copy link
Member Author

This would have to go through APIReview first before it can get merged

That's fine. I can mark the PR as Draft then. If you prefer, I can just push the branch directly to the repo so it will be its own feature branch.

@krwq
Copy link
Member

krwq commented Jul 28, 2022

@joperezr can we do small API review during triage meeting? This API is not part of the framework and we didn't go through API review for other smaller things

@Ellerbach
Copy link
Member Author

If the breaking change is acceptable for 3.0 and well documented, it's fine to wait for that version to be out. In the mean time, it does allow time to align on the design.

@joperezr
Copy link
Member

I don't think this is a small thing to be honest. This was one of the biggest design decisions made on the original design, and I do think that getting this reviewed by more people would help as most of us are already a bit biased.

@krwq
Copy link
Member

krwq commented Aug 19, 2022

Some notes (+ my after-thoughts) from the offline conversation between me and @Ellerbach today:

  • abstract class/interface has benefits of being able to remove all validation code and do some pre-calculations and really save time
  • interface can be implemented with struct which has extra benefit of being able to efficiently store list of pins if used directly
  • class implementing interface/abstract class when used directly rather than base type it will not have virtual cost (although such usage might be much harder)
  • two interesting opposite use cases: LED Matrix (stretch of the perf), LCD character screen (many pins which might need to come from different controllers)
  • non-abstract/no-interface can also remove extra validation if i.e. using non-validating driver overload and since we pre-validate that's safe
  • pin management makes things much safer so non-validating pin might not always be a good idea
  • if we cannot make specialized abstract class work fast enough with i.e. LED Matrix then why bother making it abstract if it doesn't solve largest use case for that?
  • LED matrix is currently using registers directly, perhaps improving that interface to work with other controllers than just RPi3 might be a better idea for perf scenarios than Pin class - possibly register should always return a pointer and be part of GpioController/GpioDriver - this way pref-interested parties can use that, for drivers which don't support that it would either throw or return null sentinel
  • in general we should do some experimentation here and we're in agreement that low-level concepts are really hard to get right in high level APIs and solving both perf and safety at the same time might not be possible so we should find a good sweet spot between or shift to one side

@Ellerbach
Copy link
Member Author

Few elements to take into consideration:

  • The changes are mainly about removing the sealed so it will allow to create other GpioPin based on a driver for example if needed
  • From the conversation mentioned up, having an abstract class does not need to be necessary as we will use the controller in almost all cases

Copy link
Contributor

@pgrawehr pgrawehr left a comment

Choose a reason for hiding this comment

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

Looks good overall. What's not so nice IMHO is that the code to keep the current state of the pin for the Toggle operation is duplicated all over the place.

Please also add a test case for this functionality.

I'll have to take a look whether the now valid duplicate open causes some problems in the pin management for the board. I don't expect so, as this should have triggered a test then.

/// Toggle the current value of a pin.
/// </summary>
/// <param name="pinNumber">The pin number in the driver's logical numbering scheme.</param>
protected internal abstract void Toggle(int pinNumber);
Copy link
Contributor

Choose a reason for hiding this comment

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

That's a real breaking change (binary as well as source). I think this should have a default implementation instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not PinValue Toggle(int pinNumber)?
I don't know if the hw implementation provides the value, but in the other cases it is certainly available.
If the hardware driver does not provide the value, we can decide to either return null or read the value and return it.
I prefer this signature to avoid future breaking changes in case the hw driver will provide the value in the next releases.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a real breaking change (binary as well as source). I think this should have a default implementation instead.

Some hardware provide this feature, so does not. We agreeded that the braking change is ok as it then surfaced on the GpioController following the API call. And the best way to handle the change is to move it to the driver because it's the only place tracking really the state of the pins.

Copy link
Contributor

@raffaeler raffaeler left a comment

Choose a reason for hiding this comment

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

Great work @Ellerbach!

}

[Fact]
public void TestOpnPin()
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: TestOpenPin in place of TestOpnPin

/// Toggle the current value of a pin.
/// </summary>
/// <param name="pinNumber">The pin number in the driver's logical numbering scheme.</param>
protected internal abstract void Toggle(int pinNumber);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not PinValue Toggle(int pinNumber)?
I don't know if the hw implementation provides the value, but in the other cases it is certainly available.
If the hardware driver does not provide the value, we can decide to either return null or read the value and return it.
I prefer this signature to avoid future breaking changes in case the hw driver will provide the value in the next releases.

@Ellerbach
Copy link
Member Author

Why not PinValue Toggle(int pinNumber)?

Hardware does not provide. I know that for all current implementation we keep track, so could be provided but in real hardware, it's not.

Copy link
Contributor

@raffaeler raffaeler left a comment

Choose a reason for hiding this comment

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

At this point I guess we can go on with this :)

@raffaeler
Copy link
Contributor

We were discussing in the triage to shape the api as PinValue? Toggle(int pinNumber).
My point was the following:

  • In the software implementation returning the value is free because we track the values
  • In the hardware implementation providing the value, we return it
  • In the hardware implementation not providing the value, we can return null (telling that is not available)

@joperezr Do you have an opinion on this?

Comment on lines 22 to +25
private readonly ConcurrentDictionary<int, SafeLineHandle> _pinNumberToSafeLineHandle;
private readonly ConcurrentDictionary<int, LibGpiodDriverEventHandler> _pinNumberToEventHandler;
private readonly int _pinCount;
private readonly ConcurrentDictionary<int, PinValue> _pinValue;
Copy link
Member

Choose a reason for hiding this comment

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

I'm looking at this and wondering - perhaps GpioDriver could have virtual CreateGpioPin with default implementation - then in here you'd return LibGpiodPin and move CurrentPinValue, EventHandler, SafeLineHandle to LibGpiodPin - this way you could have just one ConcurrentDictionary here and using pin directly would have advantage of not touching ConcurrentDictionary

Copy link
Member

@krwq krwq Nov 18, 2022

Choose a reason for hiding this comment

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

we talked offline and we can do this as a separate change most likely without any API changes (only addition suggested here)

GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input);
// Assert
Assert.True(pin.PinNumber == PinNumber);
Assert.True(pin.GetPinMode() == PinMode.Input);
Copy link
Member

Choose a reason for hiding this comment

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

nit: Assert.Equal

@@ -14,6 +14,7 @@ public class Windows10Driver : GpioDriver
{
private static readonly WinGpio.GpioController s_winGpioController = WinGpio.GpioController.GetDefault();
private readonly Dictionary<int, Windows10DriverPin> _openPins = new Dictionary<int, Windows10DriverPin>();
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
Copy link
Member

Choose a reason for hiding this comment

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

why does LibGpiodDriver need ConcurrentDictionary while this one is ok with regular Dictionary?

Copy link
Member

Choose a reason for hiding this comment

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

(it's an existing issue anyway so don't worry about this in this PR)

// Simulate a key press
Interop.keybd_event((byte)virtualKey,
0x45,
Interop.KEYEVENTF_EXTENDEDKEY | 0,
Copy link
Member

Choose a reason for hiding this comment

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

nit: | 0 is a nop so Interop.KEYEVENTF_EXTENDEDKEY | 0 is the same as Interop.KEYEVENTF_EXTENDEDKEY

@@ -519,12 +532,14 @@ private void ThrowBadPin(string argument)
protected override void OpenPin(int pinNumber)
{
// No-op
_pinValues.TryAdd(pinNumber, PinValue.Low);
Copy link
Member

Choose a reason for hiding this comment

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

nit: comment above is no longer true

@@ -161,6 +161,8 @@ protected override PinValue Read(int pinNumber)
return PinValue.Low;
}

protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber));
Copy link
Member

Choose a reason for hiding this comment

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

can be removed given current virtual implementation

}

/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure dictionary actually makes it faster for those low level drivers - reading/writing pin is supposedely fastest operation you can make if you think about it. It might be currently faster this way because of OS but in principal that shouldn't be the case

Copy link
Member Author

Choose a reason for hiding this comment

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

We should measure this later on. Also some drivers do not support reading when mode is output.

@@ -65,6 +65,8 @@ protected override PinValue Read(int pinNumber)
return _hardwareLevelAccess.ReadPin(pinNumber);
}

protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber));
Copy link
Member

Choose a reason for hiding this comment

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

nit: can be removed

Copy link
Member

@krwq krwq left a comment

Choose a reason for hiding this comment

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

LGTM with couple of nits. feel free to ignore or fix separately

@Ellerbach Ellerbach merged commit ddbfc0d into dotnet:main Dec 1, 2022
richlander pushed a commit to richlander/iot that referenced this pull request Dec 13, 2022
* Adding GpioPin

* adjusting based on PR feedback

* Adjusting to have natively drivers.

* make methods virtual

* adding Toggle to System.Device drivers

* Board and Mcp23xxx adjustments

* adding FT4222, FT232H and Pcx857x

* adding Arduino and Seasaw

* adjsting file encoding after conflict merge

* Adding missing Toggle implementation in Arduino native

* Adjusting test for new pattern with GpioPin

* adjusting PR feedback

* typo in test

* fixing nit

* fixing test

* fixing test
richlander added a commit that referenced this pull request Jan 16, 2023
* Add IS31FL3731 LED matrix binding

* Update sample

* Make program more generic

* Add specific bindings

* Add Phat 17x7 binding

* Add Phat 17x7 binding

* Add LED Shim 28x1 driver

* Update switch expression

* Update TPN

* Add README

* Add Led Matrix Breakout 11x7 binding

* Update README for led matrix breakout

* Add Led RGB Matrix 5x5 binding

* Add 5x5 RGB Matrix sample

* Switch integers to decimals

* Fix syntax

* Rename type

* Update per feedback

* Update per feedback

* Switch to enums for const data

* Switch from enum to static class

* Switch to ROS

* Fix flipped field

* Update brightness

* Update TFMs

* Adding GpioPin (#1895)

* Adding GpioPin

* adjusting based on PR feedback

* Adjusting to have natively drivers.

* make methods virtual

* adding Toggle to System.Device drivers

* Board and Mcp23xxx adjustments

* adding FT4222, FT232H and Pcx857x

* adding Arduino and Seasaw

* adjsting file encoding after conflict merge

* Adding missing Toggle implementation in Arduino native

* Adjusting test for new pattern with GpioPin

* adjusting PR feedback

* typo in test

* fixing nit

* fixing test

* fixing test

* Bump Microsoft.CodeAnalysis.CSharp.Scripting in /tools/DevicesApiTester (#1981)

Bumps [Microsoft.CodeAnalysis.CSharp.Scripting](https://github.com/dotnet/roslyn) from 4.3.1 to 4.4.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits/Visual-Studio-2019-Version-16.0-Preview-4.4)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp.Scripting
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump Microsoft.Net.Compilers.Toolset in /tools/DevicesApiTester (#1980)

Bumps [Microsoft.Net.Compilers.Toolset](https://github.com/dotnet/roslyn) from 4.3.1 to 4.4.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits/Visual-Studio-2019-Version-16.0-Preview-4.4)

---
updated-dependencies:
- dependency-name: Microsoft.Net.Compilers.Toolset
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Upgrade to .NET 7 GA (#1988)

* Upgrade to .NET 7 GA

* Update global.json

Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>

* Update blinking

* Add 'Port of' comments

* Update sample

* Update sample

* Update comment

* Update per feedback

* Add 'Port of' comment

* Move samples to directories

* Update README

* Change exception type

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Laurent Ellerbach <laurelle@microsoft.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>
@github-actions github-actions bot locked and limited conversation to collaborators Dec 8, 2023
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants