// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace System.Device.Gpio { /// /// Represents a general-purpose I/O (GPIO) controller. /// public sealed partial class GpioController : IDisposable { private readonly GpioDriver _driver; private readonly HashSet _openPins; /// /// Initializes a new instance of the class that will use the logical pin numbering scheme as default. /// public GpioController() : this(PinNumberingScheme.Logical) { } /// /// Initializes a new instance of the class that will use the specified numbering scheme and driver. /// /// The numbering scheme used to represent pins provided by the controller. /// The driver that manages all of the pin operations for the controller. public GpioController(PinNumberingScheme numberingScheme, GpioDriver driver) { _driver = driver; NumberingScheme = numberingScheme; _openPins = new HashSet(); } /// /// The numbering scheme used to represent pins provided by the controller. /// public PinNumberingScheme NumberingScheme { get; } /// /// The number of pins provided by the controller. /// public int PinCount => _driver.PinCount; /// /// Gets the logical pin number in the controller's numbering scheme. /// /// The pin number in the controller's numbering scheme. /// The logical pin number in the controller's numbering scheme. private int GetLogicalPinNumber(int pinNumber) { return (NumberingScheme == PinNumberingScheme.Logical) ? pinNumber : _driver.ConvertPinNumberToLogicalNumberingScheme(pinNumber); } /// /// Opens a pin in order for it to be ready to use. /// /// The pin number in the controller's numbering scheme. public void OpenPin(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("The selected pin is already open."); } _driver.OpenPin(logicalPinNumber); _openPins.Add(logicalPinNumber); } /// /// Opens a pin and sets it to a specific mode. /// /// The pin number in the controller's numbering scheme. /// The mode to be set. public void OpenPin(int pinNumber, PinMode mode) { OpenPin(pinNumber); SetPinMode(pinNumber, mode); } /// /// Closes an open pin. /// /// The pin number in the controller's numbering scheme. public void ClosePin(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not close a pin that is not open."); } _driver.ClosePin(logicalPinNumber); _openPins.Remove(logicalPinNumber); } /// /// Sets the mode to a pin. /// /// The pin number in the controller's numbering scheme. /// The mode to be set. public void SetPinMode(int pinNumber, PinMode mode) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not set a mode to a pin that is not open."); } if (!_driver.IsPinModeSupported(logicalPinNumber, mode)) { throw new InvalidOperationException("The pin does not support the selected mode."); } _driver.SetPinMode(logicalPinNumber, mode); } /// /// Gets the mode of a pin. /// /// The pin number in the controller's numbering scheme. /// The mode of the pin. public PinMode GetPinMode(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not get the mode of a pin that is not open."); } return _driver.GetPinMode(logicalPinNumber); } /// /// Checks if a specific pin is open. /// /// The pin number in the controller's numbering scheme. /// The status if the pin is open or closed. public bool IsPinOpen(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); return _openPins.Contains(logicalPinNumber); } /// /// Checks if a pin supports a specific mode. /// /// The pin number in the controller's numbering scheme. /// The mode to check. /// The status if the pin supports the mode. public bool IsPinModeSupported(int pinNumber, PinMode mode) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); return _driver.IsPinModeSupported(logicalPinNumber, mode); } /// /// Reads the current value of a pin. /// /// The pin number in the controller's numbering scheme. /// The value of the pin. public PinValue Read(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not read from a pin that is not open."); } return _driver.Read(logicalPinNumber); } /// /// Writes a value to a pin. /// /// The pin number in the controller's numbering scheme. /// The value to be written to the pin. public void Write(int pinNumber, PinValue value) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not write to a pin that is not open."); } if (_driver.GetPinMode(logicalPinNumber) != PinMode.Output) { throw new InvalidOperationException("Can not write to a pin that is not set to Output mode."); } _driver.Write(logicalPinNumber, value); } /// /// Blocks execution until an event of type eventType is received or a period of time has expired. /// /// The pin number in the controller's numbering scheme. /// The event types to wait for. /// The time to wait for the event. /// A structure that contains the result of the waiting operation. public WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, TimeSpan timeout) { using (CancellationTokenSource tokenSource = new CancellationTokenSource(timeout)) { return WaitForEvent(pinNumber, eventTypes, tokenSource.Token); } } /// /// Blocks execution until an event of type eventType is received or a cancellation is requested. /// /// The pin number in the controller's numbering scheme. /// The event types to wait for. /// The cancellation token of when the operation should stop waiting for an event. /// A structure that contains the result of the waiting operation. public WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not wait for events from a pin that is not open."); } return _driver.WaitForEvent(logicalPinNumber, eventTypes, cancellationToken); } /// /// Async call to wait until an event of type eventType is received or a period of time has expired. /// /// The pin number in the controller's numbering scheme. /// The event types to wait for. /// The time to wait for the event. /// A task representing the operation of getting the structure that contains the result of the waiting operation. public async ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, TimeSpan timeout) { using (CancellationTokenSource tokenSource = new CancellationTokenSource(timeout)) { return await WaitForEventAsync(pinNumber, eventTypes, tokenSource.Token).ConfigureAwait(false); } } /// /// Async call until an event of type eventType is received or a cancellation is requested. /// /// The pin number in the controller's numbering scheme. /// The event types to wait for. /// The cancellation token of when the operation should stop waiting for an event. /// A task representing the operation of getting the structure that contains the result of the waiting operation public ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken token) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not wait for events from a pin that is not open."); } return _driver.WaitForEventAsync(logicalPinNumber, eventTypes, token); } /// /// Adds a callback that will be invoked when pinNumber has an event of type eventType. /// /// The pin number in the controller's numbering scheme. /// The event types to wait for. /// The callback method that will be invoked. public void RegisterCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not add callback for a pin that is not open."); } _driver.AddCallbackForPinValueChangedEvent(logicalPinNumber, eventTypes, callback); } /// /// Removes a callback that was being invoked for pin at pinNumber. /// /// The pin number in the controller's numbering scheme. /// The callback method that will be invoked. public void UnregisterCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); if (!_openPins.Contains(logicalPinNumber)) { throw new InvalidOperationException("Can not add callback for a pin that is not open."); } _driver.RemoveCallbackForPinValueChangedEvent(logicalPinNumber, callback); } private void Dispose(bool disposing) { foreach (int pin in _openPins) { _driver.ClosePin(pin); } _openPins.Clear(); _driver.Dispose(); } /// public void Dispose() { Dispose(true); } /// /// Write the given pins with the given values. /// /// The pin/value pairs to write. public void Write(ReadOnlySpan pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { Write(pinValuePairs[i].PinNumber, pinValuePairs[i].PinValue); } } /// /// Read the given pins with the given pin numbers. /// /// The pin/value pairs to read. public void Read(Span pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { int pin = pinValuePairs[i].PinNumber; pinValuePairs[i] = new PinValuePair(pin, Read(pin)); } } } }