diff --git a/Unosquare.RaspberryIO/BluetoothErrorException.cs b/Unosquare.RaspberryIO/BluetoothErrorException.cs
index 7f746a4..a8a6b78 100644
--- a/Unosquare.RaspberryIO/BluetoothErrorException.cs
+++ b/Unosquare.RaspberryIO/BluetoothErrorException.cs
@@ -1,20 +1,16 @@
-namespace Unosquare.RaspberryIO
-{
- using System;
-
- ///
+using System;
+
+namespace Unosquare.RaspberryIO {
+ ///
+ ///
+ /// Occurs when an exception is thrown in the Bluetooth component.
+ ///
+ public class BluetoothErrorException : Exception {
///
- /// Occurs when an exception is thrown in the Bluetooth component.
+ /// Initializes a new instance of the class.
///
- public class BluetoothErrorException : Exception
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The message.
- public BluetoothErrorException(string message)
- : base(message)
- {
- }
- }
+ /// The message.
+ public BluetoothErrorException(String message) : base(message) {
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Camera/CameraColor.cs b/Unosquare.RaspberryIO/Camera/CameraColor.cs
index 4e06b34..bb6cc65 100644
--- a/Unosquare.RaspberryIO/Camera/CameraColor.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraColor.cs
@@ -1,134 +1,140 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System;
- using System.Linq;
- using Swan;
-
+using System;
+using System.Linq;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// A simple RGB color class to represent colors in RGB and YUV colorspaces.
+ ///
+ public class CameraColor {
///
- /// A simple RGB color class to represent colors in RGB and YUV colorspaces.
+ /// Initializes a new instance of the class.
///
- public class CameraColor
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The red.
- /// The green.
- /// The blue.
- public CameraColor(int r, int g, int b)
- : this(r, g, b, string.Empty)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The red.
- /// The green.
- /// The blue.
- /// The well-known color name.
- public CameraColor(int r, int g, int b, string name)
- {
- RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) };
-
- var y = (R * .299000f) + (G * .587000f) + (B * .114000f);
- var u = (R * -.168736f) + (G * -.331264f) + (B * .500000f) + 128f;
- var v = (R * .500000f) + (G * -.418688f) + (B * -.081312f) + 128f;
-
- YUV = new[] { (byte)y.Clamp(0, 255), (byte)u.Clamp(0, 255), (byte)v.Clamp(0, 255) };
- Name = name;
- }
-
- #region Static Definitions
-
- ///
- /// Gets the predefined white color.
- ///
- public static CameraColor White => new CameraColor(255, 255, 255, nameof(White));
-
- ///
- /// Gets the predefined red color.
- ///
- public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red));
-
- ///
- /// Gets the predefined green color.
- ///
- public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green));
-
- ///
- /// Gets the predefined blue color.
- ///
- public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue));
-
- ///
- /// Gets the predefined black color.
- ///
- public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black));
-
- #endregion
-
- ///
- /// Gets the well-known color name.
- ///
- public string Name { get; }
-
- ///
- /// Gets the red byte.
- ///
- public byte R => RGB[0];
-
- ///
- /// Gets the green byte.
- ///
- public byte G => RGB[1];
-
- ///
- /// Gets the blue byte.
- ///
- public byte B => RGB[2];
-
- ///
- /// Gets the RGB byte array (3 bytes).
- ///
- public byte[] RGB { get; }
-
- ///
- /// Gets the YUV byte array (3 bytes).
- ///
- public byte[] YUV { get; }
-
- ///
- /// Returns a hexadecimal representation of the RGB byte array.
- /// Preceded by 0x and all in lowercase.
- ///
- /// if set to true [reverse].
- /// A string.
- public string ToRgbHex(bool reverse)
- {
- var data = RGB.ToArray();
- if (reverse) Array.Reverse(data);
- return ToHex(data);
- }
-
- ///
- /// Returns a hexadecimal representation of the YUV byte array.
- /// Preceded by 0x and all in lowercase.
- ///
- /// if set to true [reverse].
- /// A string.
- public string ToYuvHex(bool reverse)
- {
- var data = YUV.ToArray();
- if (reverse) Array.Reverse(data);
- return ToHex(data);
- }
-
- ///
- /// Returns a hexadecimal representation of the data byte array.
- ///
- /// The data.
- /// A string.
- private static string ToHex(byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", string.Empty).ToLowerInvariant()}";
- }
+ /// The red.
+ /// The green.
+ /// The blue.
+ public CameraColor(Int32 r, Int32 g, Int32 b) : this(r, g, b, String.Empty) {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The red.
+ /// The green.
+ /// The blue.
+ /// The well-known color name.
+ public CameraColor(Int32 r, Int32 g, Int32 b, String name) {
+ this.RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) };
+
+ Single y = this.R * .299000f + this.G * .587000f + this.B * .114000f;
+ Single u = this.R * -.168736f + this.G * -.331264f + this.B * .500000f + 128f;
+ Single v = this.R * .500000f + this.G * -.418688f + this.B * -.081312f + 128f;
+
+ this.YUV = new[] { (Byte)y.Clamp(0, 255), (Byte)u.Clamp(0, 255), (Byte)v.Clamp(0, 255) };
+ this.Name = name;
+ }
+
+ #region Static Definitions
+
+ ///
+ /// Gets the predefined white color.
+ ///
+ public static CameraColor White => new CameraColor(255, 255, 255, nameof(White));
+
+ ///
+ /// Gets the predefined red color.
+ ///
+ public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red));
+
+ ///
+ /// Gets the predefined green color.
+ ///
+ public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green));
+
+ ///
+ /// Gets the predefined blue color.
+ ///
+ public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue));
+
+ ///
+ /// Gets the predefined black color.
+ ///
+ public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black));
+
+ #endregion
+
+ ///
+ /// Gets the well-known color name.
+ ///
+ public String Name {
+ get;
+ }
+
+ ///
+ /// Gets the red byte.
+ ///
+ public Byte R => this.RGB[0];
+
+ ///
+ /// Gets the green byte.
+ ///
+ public Byte G => this.RGB[1];
+
+ ///
+ /// Gets the blue byte.
+ ///
+ public Byte B => this.RGB[2];
+
+ ///
+ /// Gets the RGB byte array (3 bytes).
+ ///
+ public Byte[] RGB {
+ get;
+ }
+
+ ///
+ /// Gets the YUV byte array (3 bytes).
+ ///
+ public Byte[] YUV {
+ get;
+ }
+
+ ///
+ /// Returns a hexadecimal representation of the RGB byte array.
+ /// Preceded by 0x and all in lowercase.
+ ///
+ /// if set to true [reverse].
+ /// A string.
+ public String ToRgbHex(Boolean reverse) {
+ Byte[] data = this.RGB.ToArray();
+ if(reverse) {
+ Array.Reverse(data);
+ }
+
+ return ToHex(data);
+ }
+
+ ///
+ /// Returns a hexadecimal representation of the YUV byte array.
+ /// Preceded by 0x and all in lowercase.
+ ///
+ /// if set to true [reverse].
+ /// A string.
+ public String ToYuvHex(Boolean reverse) {
+ Byte[] data = this.YUV.ToArray();
+ if(reverse) {
+ Array.Reverse(data);
+ }
+
+ return ToHex(data);
+ }
+
+ ///
+ /// Returns a hexadecimal representation of the data byte array.
+ ///
+ /// The data.
+ /// A string.
+ private static String ToHex(Byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", String.Empty).ToLowerInvariant()}";
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Camera/CameraController.cs b/Unosquare.RaspberryIO/Camera/CameraController.cs
index 191535d..dad55b2 100644
--- a/Unosquare.RaspberryIO/Camera/CameraController.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraController.cs
@@ -1,209 +1,177 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using Swan;
- using System;
- using System.IO;
- using System.Threading;
- using System.Threading.Tasks;
-
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs.
+ /// This class is a singleton.
+ ///
+ public class CameraController : SingletonBase {
+ #region Private Declarations
+
+ private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true);
+ private static readonly Object SyncRoot = new Object();
+ private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
+ private static Task? _videoStreamTask;
+
+ #endregion
+
+ #region Properties
+
///
- /// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs.
- /// This class is a singleton.
+ /// Gets a value indicating whether the camera module is busy.
///
- public class CameraController : SingletonBase
- {
- #region Private Declarations
-
- private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true);
- private static readonly object SyncRoot = new object();
- private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
- private static Task? _videoStreamTask;
-
- #endregion
-
- #region Properties
-
- ///
- /// Gets a value indicating whether the camera module is busy.
- ///
- ///
- /// true if this instance is busy; otherwise, false.
- ///
- public bool IsBusy => OperationDone.IsSet == false;
-
- #endregion
-
- #region Image Capture Methods
-
- ///
- /// Captures an image asynchronously.
- ///
- /// The settings.
- /// The ct.
- /// The image bytes.
- /// Cannot use camera module because it is currently busy.
- public async Task CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default)
- {
- if (Instance.IsBusy)
- throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
-
- if (settings.CaptureTimeoutMilliseconds <= 0)
- throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0");
-
- try
- {
- OperationDone.Reset();
-
- using var output = new MemoryStream();
- var exitCode = await ProcessRunner.RunProcessAsync(
- settings.CommandName,
- settings.CreateProcessArguments(),
- (data, proc) => output.Write(data, 0, data.Length),
- null,
- true,
- ct).ConfigureAwait(false);
-
- return exitCode != 0 ? Array.Empty() : output.ToArray();
- }
- finally
- {
- OperationDone.Set();
- }
- }
-
- ///
- /// Captures an image.
- ///
- /// The settings.
- /// The image bytes.
- public byte[] CaptureImage(CameraStillSettings settings) => CaptureImageAsync(settings).GetAwaiter().GetResult();
-
- ///
- /// Captures a JPEG encoded image asynchronously at 90% quality.
- ///
- /// The width.
- /// The height.
- /// The ct.
- /// The image bytes.
- public Task CaptureImageJpegAsync(int width, int height, CancellationToken ct = default)
- {
- var settings = new CameraStillSettings
- {
- CaptureWidth = width,
- CaptureHeight = height,
- CaptureJpegQuality = 90,
- CaptureTimeoutMilliseconds = 300,
- };
-
- return CaptureImageAsync(settings, ct);
- }
-
- ///
- /// Captures a JPEG encoded image at 90% quality.
- ///
- /// The width.
- /// The height.
- /// The image bytes.
- public byte[] CaptureImageJpeg(int width, int height) => CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
-
- #endregion
-
- #region Video Capture Methods
-
- ///
- /// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS.
- /// No preview is shown.
- ///
- /// The on data callback.
- /// The on exit callback.
- public void OpenVideoStream(Action onDataCallback, Action? onExitCallback = null)
- {
- var settings = new CameraVideoSettings
- {
- CaptureTimeoutMilliseconds = 0,
- CaptureDisplayPreview = false,
- CaptureWidth = 1920,
- CaptureHeight = 1080,
- };
-
- OpenVideoStream(settings, onDataCallback, onExitCallback);
- }
-
- ///
- /// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater.
- ///
- /// The settings.
- /// The on data callback.
- /// The on exit callback.
- /// Cannot use camera module because it is currently busy.
- /// CaptureTimeoutMilliseconds.
- public void OpenVideoStream(CameraVideoSettings settings, Action onDataCallback, Action? onExitCallback = null)
- {
- if (Instance.IsBusy)
- throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
-
- if (settings.CaptureTimeoutMilliseconds < 0)
- throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0");
-
- try
- {
- OperationDone.Reset();
- _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token);
- }
- catch
- {
- OperationDone.Set();
- throw;
- }
- }
-
- ///
- /// Closes the video stream of a video stream is open.
- ///
- public void CloseVideoStream()
- {
- lock (SyncRoot)
- {
- if (IsBusy == false)
- return;
- }
-
- if (_videoTokenSource.IsCancellationRequested == false)
- {
- _videoTokenSource.Cancel();
- _videoStreamTask?.Wait();
- }
-
- _videoTokenSource = new CancellationTokenSource();
- }
-
- private static async Task VideoWorkerDoWork(
- CameraVideoSettings settings,
- Action onDataCallback,
- Action onExitCallback)
- {
- try
- {
- await ProcessRunner.RunProcessAsync(
- settings.CommandName,
- settings.CreateProcessArguments(),
- (data, proc) => onDataCallback?.Invoke(data),
- null,
- true,
- _videoTokenSource.Token).ConfigureAwait(false);
-
- onExitCallback?.Invoke();
- }
- catch
- {
- // swallow
- }
- finally
- {
- Instance.CloseVideoStream();
- OperationDone.Set();
- }
- }
- #endregion
- }
+ ///
+ /// true if this instance is busy; otherwise, false.
+ ///
+ public Boolean IsBusy => OperationDone.IsSet == false;
+
+ #endregion
+
+ #region Image Capture Methods
+
+ ///
+ /// Captures an image asynchronously.
+ ///
+ /// The settings.
+ /// The ct.
+ /// The image bytes.
+ /// Cannot use camera module because it is currently busy.
+ public async Task CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) {
+ if(Instance.IsBusy) {
+ throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
+ }
+
+ if(settings.CaptureTimeoutMilliseconds <= 0) {
+ throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0");
+ }
+
+ try {
+ OperationDone.Reset();
+
+ using MemoryStream output = new MemoryStream();
+ Int32 exitCode = await ProcessRunner.RunProcessAsync(settings.CommandName, settings.CreateProcessArguments(), (data, proc) => output.Write(data, 0, data.Length), null, true, ct).ConfigureAwait(false);
+
+ return exitCode != 0 ? Array.Empty() : output.ToArray();
+ } finally {
+ OperationDone.Set();
+ }
+ }
+
+ ///
+ /// Captures an image.
+ ///
+ /// The settings.
+ /// The image bytes.
+ public Byte[] CaptureImage(CameraStillSettings settings) => this.CaptureImageAsync(settings).GetAwaiter().GetResult();
+
+ ///
+ /// Captures a JPEG encoded image asynchronously at 90% quality.
+ ///
+ /// The width.
+ /// The height.
+ /// The ct.
+ /// The image bytes.
+ public Task CaptureImageJpegAsync(Int32 width, Int32 height, CancellationToken ct = default) {
+ CameraStillSettings settings = new CameraStillSettings {
+ CaptureWidth = width,
+ CaptureHeight = height,
+ CaptureJpegQuality = 90,
+ CaptureTimeoutMilliseconds = 300,
+ };
+
+ return this.CaptureImageAsync(settings, ct);
+ }
+
+ ///
+ /// Captures a JPEG encoded image at 90% quality.
+ ///
+ /// The width.
+ /// The height.
+ /// The image bytes.
+ public Byte[] CaptureImageJpeg(Int32 width, Int32 height) => this.CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
+
+ #endregion
+
+ #region Video Capture Methods
+
+ ///
+ /// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS.
+ /// No preview is shown.
+ ///
+ /// The on data callback.
+ /// The on exit callback.
+ public void OpenVideoStream(Action onDataCallback, Action? onExitCallback = null) {
+ CameraVideoSettings settings = new CameraVideoSettings {
+ CaptureTimeoutMilliseconds = 0,
+ CaptureDisplayPreview = false,
+ CaptureWidth = 1920,
+ CaptureHeight = 1080,
+ };
+
+ this.OpenVideoStream(settings, onDataCallback, onExitCallback);
+ }
+
+ ///
+ /// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater.
+ ///
+ /// The settings.
+ /// The on data callback.
+ /// The on exit callback.
+ /// Cannot use camera module because it is currently busy.
+ /// CaptureTimeoutMilliseconds.
+ public void OpenVideoStream(CameraVideoSettings settings, Action onDataCallback, Action? onExitCallback = null) {
+ if(Instance.IsBusy) {
+ throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
+ }
+
+ if(settings.CaptureTimeoutMilliseconds < 0) {
+ throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0");
+ }
+
+ try {
+ OperationDone.Reset();
+ _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token);
+ } catch {
+ OperationDone.Set();
+ throw;
+ }
+ }
+
+ ///
+ /// Closes the video stream of a video stream is open.
+ ///
+ public void CloseVideoStream() {
+ lock(SyncRoot) {
+ if(this.IsBusy == false) {
+ return;
+ }
+ }
+
+ if(_videoTokenSource.IsCancellationRequested == false) {
+ _videoTokenSource.Cancel();
+ _videoStreamTask?.Wait();
+ }
+
+ _videoTokenSource = new CancellationTokenSource();
+ }
+
+ private static async Task VideoWorkerDoWork(CameraVideoSettings settings, Action onDataCallback, Action? onExitCallback) {
+ try {
+ _ = await ProcessRunner.RunProcessAsync(settings.CommandName, settings.CreateProcessArguments(), (data, proc) => onDataCallback?.Invoke(data), null, true, _videoTokenSource.Token).ConfigureAwait(false);
+
+ onExitCallback?.Invoke();
+ } catch {
+ // swallow
+ } finally {
+ Instance.CloseVideoStream();
+ OperationDone.Set();
+ }
+ }
+ #endregion
+ }
}
diff --git a/Unosquare.RaspberryIO/Camera/CameraRect.cs b/Unosquare.RaspberryIO/Camera/CameraRect.cs
index 505cdd8..55a3479 100644
--- a/Unosquare.RaspberryIO/Camera/CameraRect.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraRect.cs
@@ -1,82 +1,87 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System.Globalization;
- using Swan;
-
+using System;
+using System.Globalization;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// Defines the Raspberry Pi camera's sensor ROI (Region of Interest).
+ ///
+ public struct CameraRect {
///
- /// Defines the Raspberry Pi camera's sensor ROI (Region of Interest).
+ /// The default ROI which is the entire area.
///
- public struct CameraRect
- {
- ///
- /// The default ROI which is the entire area.
- ///
- public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M };
-
- ///
- /// Gets or sets the x in relative coordinates. (0.0 to 1.0).
- ///
- ///
- /// The x.
- ///
- public decimal X { get; set; }
-
- ///
- /// Gets or sets the y location in relative coordinates. (0.0 to 1.0).
- ///
- ///
- /// The y.
- ///
- public decimal Y { get; set; }
-
- ///
- /// Gets or sets the width in relative coordinates. (0.0 to 1.0).
- ///
- ///
- /// The w.
- ///
- public decimal W { get; set; }
-
- ///
- /// Gets or sets the height in relative coordinates. (0.0 to 1.0).
- ///
- ///
- /// The h.
- ///
- public decimal H { get; set; }
-
- ///
- /// Gets a value indicating whether this instance is equal to the default (The entire area).
- ///
- ///
- /// true if this instance is default; otherwise, false.
- ///
- public bool IsDefault
- {
- get
- {
- Clamp();
- return X == Default.X && Y == Default.Y && W == Default.W && H == Default.H;
- }
- }
-
- ///
- /// Clamps the members of this ROI to their minimum and maximum values.
- ///
- public void Clamp()
- {
- X = X.Clamp(0M, 1M);
- Y = Y.Clamp(0M, 1M);
- W = W.Clamp(0M, 1M - X);
- H = H.Clamp(0M, 1M - Y);
- }
-
- ///
- /// Returns a that represents this instance.
- ///
- ///
- /// A that represents this instance.
- ///
- public override string ToString() => $"{X.ToString(CultureInfo.InvariantCulture)},{Y.ToString(CultureInfo.InvariantCulture)},{W.ToString(CultureInfo.InvariantCulture)},{H.ToString(CultureInfo.InvariantCulture)}";
- }
+ public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M };
+
+ ///
+ /// Gets or sets the x in relative coordinates. (0.0 to 1.0).
+ ///
+ ///
+ /// The x.
+ ///
+ public Decimal X {
+ get; set;
+ }
+
+ ///
+ /// Gets or sets the y location in relative coordinates. (0.0 to 1.0).
+ ///
+ ///
+ /// The y.
+ ///
+ public Decimal Y {
+ get; set;
+ }
+
+ ///
+ /// Gets or sets the width in relative coordinates. (0.0 to 1.0).
+ ///
+ ///
+ /// The w.
+ ///
+ public Decimal W {
+ get; set;
+ }
+
+ ///
+ /// Gets or sets the height in relative coordinates. (0.0 to 1.0).
+ ///
+ ///
+ /// The h.
+ ///
+ public Decimal H {
+ get; set;
+ }
+
+ ///
+ /// Gets a value indicating whether this instance is equal to the default (The entire area).
+ ///
+ ///
+ /// true if this instance is default; otherwise, false.
+ ///
+ public Boolean IsDefault {
+ get {
+ this.Clamp();
+ return this.X == Default.X && this.Y == Default.Y && this.W == Default.W && this.H == Default.H;
+ }
+ }
+
+ ///
+ /// Clamps the members of this ROI to their minimum and maximum values.
+ ///
+ public void Clamp() {
+ this.X = this.X.Clamp(0M, 1M);
+ this.Y = this.Y.Clamp(0M, 1M);
+ this.W = this.W.Clamp(0M, 1M - this.X);
+ this.H = this.H.Clamp(0M, 1M - this.Y);
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override String ToString() => $"{this.X.ToString(CultureInfo.InvariantCulture)},{this.Y.ToString(CultureInfo.InvariantCulture)},{this.W.ToString(CultureInfo.InvariantCulture)},{this.H.ToString(CultureInfo.InvariantCulture)}";
+ }
}
diff --git a/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs b/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs
index 0a5c456..40ecab5 100644
--- a/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs
@@ -1,340 +1,361 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System.Globalization;
- using System.Text;
- using Swan;
-
+using System;
+using System.Globalization;
+using System.Text;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// A base class to implement raspistill and raspivid wrappers
+ /// Full documentation available at
+ /// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md.
+ ///
+ public abstract class CameraSettingsBase {
///
- /// A base class to implement raspistill and raspivid wrappers
- /// Full documentation available at
- /// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md.
+ /// The Invariant Culture shorthand.
///
- public abstract class CameraSettingsBase
- {
- ///
- /// The Invariant Culture shorthand.
- ///
- protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture;
-
- #region Capture Settings
-
- ///
- /// Gets or sets the timeout milliseconds.
- /// Default value is 5000
- /// Recommended value is at least 300 in order to let the light collectors open.
- ///
- public int CaptureTimeoutMilliseconds { get; set; } = 5000;
-
- ///
- /// Gets or sets a value indicating whether or not to show a preview window on the screen.
- ///
- public bool CaptureDisplayPreview { get; set; } = false;
-
- ///
- /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled.
- ///
- public bool CaptureDisplayPreviewInFullScreen { get; set; } = true;
-
- ///
- /// Gets or sets a value indicating whether video stabilization should be enabled.
- ///
- public bool CaptureVideoStabilizationEnabled { get; set; } = false;
-
- ///
- /// Gets or sets the display preview opacity only if the display preview property is enabled.
- ///
- public byte CaptureDisplayPreviewOpacity { get; set; } = 255;
-
- ///
- /// Gets or sets the capture sensor region of interest in relative coordinates.
- ///
- public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default;
-
- ///
- /// Gets or sets the capture shutter speed in microseconds.
- /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds).
- ///
- public int CaptureShutterSpeedMicroseconds { get; set; } = -1;
-
- ///
- /// Gets or sets the exposure mode.
- ///
- public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto;
-
- ///
- /// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10
- /// Camera exposure compensation is commonly stated in terms of EV units;
- /// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure.
- /// Exposure can be adjusted by changing either the lens f-number or the exposure time;
- /// which one is changed usually depends on the camera's exposure mode.
- ///
- public int CaptureExposureCompensation { get; set; } = 0;
-
- ///
- /// Gets or sets the capture metering mode.
- ///
- public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average;
-
- ///
- /// Gets or sets the automatic white balance mode. By default it is set to Auto.
- ///
- public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto;
-
- ///
- /// Gets or sets the capture white balance gain on the blue channel. Example: 1.25
- /// Only takes effect if White balance control is set to off.
- /// Default is 0.
- ///
- public decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
-
- ///
- /// Gets or sets the capture white balance gain on the red channel. Example: 1.75
- /// Only takes effect if White balance control is set to off.
- /// Default is 0.
- ///
- public decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
-
- ///
- /// Gets or sets the dynamic range compensation.
- /// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas.
- ///
- public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { get; set; } =
- CameraDynamicRangeCompensation.Off;
-
- #endregion
-
- #region Image Properties
-
- ///
- /// Gets or sets the width of the picture to take.
- /// Less than or equal to 0 in either width or height means maximum resolution available.
- ///
- public int CaptureWidth { get; set; } = 640;
-
- ///
- /// Gets or sets the height of the picture to take.
- /// Less than or equal to 0 in either width or height means maximum resolution available.
- ///
- public int CaptureHeight { get; set; } = 480;
-
- ///
- /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100.
- ///
- public int ImageSharpness { get; set; } = 0;
-
- ///
- /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100.
- ///
- public int ImageContrast { get; set; } = 0;
-
- ///
- /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100.
- ///
- public int ImageBrightness { get; set; } = 50; // from 0 to 100
-
- ///
- /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100.
- ///
- public int ImageSaturation { get; set; } = 0;
-
- ///
- /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800
- /// The higher the value, the more light the sensor absorbs.
- ///
- public int ImageIso { get; set; } = -1;
-
- ///
- /// Gets or sets the image capture effect to be applied.
- ///
- public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None;
-
- ///
- /// Gets or sets the color effect U coordinates.
- /// Default is -1, Range is 0 to 255
- /// 128:128 should be effectively a monochrome image.
- ///
- public int ImageColorEffectU { get; set; } = -1; // 0 to 255
-
- ///
- /// Gets or sets the color effect V coordinates.
- /// Default is -1, Range is 0 to 255
- /// 128:128 should be effectively a monochrome image.
- ///
- public int ImageColorEffectV { get; set; } = -1; // 0 to 255
-
- ///
- /// Gets or sets the image rotation. Default is no rotation.
- ///
- public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None;
-
- ///
- /// Gets or sets a value indicating whether the image should be flipped horizontally.
- ///
- public bool ImageFlipHorizontally { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the image should be flipped vertically.
- ///
- public bool ImageFlipVertically { get; set; }
-
- ///
- /// Gets or sets the image annotations using a bitmask (or flags) notation.
- /// Apply a bitwise OR to the enumeration to include multiple annotations.
- ///
- public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None;
-
- ///
- /// Gets or sets the image annotations text.
- /// Text may include date/time placeholders by using the '%' character, as used by strftime.
- /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33.
- ///
- public string ImageAnnotationsText { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the font size of the text annotations
- /// Default is -1, range is 6 to 160.
- ///
- public int ImageAnnotationFontSize { get; set; } = -1;
-
- ///
- /// Gets or sets the color of the text annotations.
- ///
- ///
- /// The color of the image annotation font.
- ///
- public CameraColor? ImageAnnotationFontColor { get; set; } = null;
-
- ///
- /// Gets or sets the background color for text annotations.
- ///
- ///
- /// The image annotation background.
- ///
- public CameraColor? ImageAnnotationBackground { get; set; } = null;
-
- #endregion
-
- #region Interface
-
- ///
- /// Gets the command file executable.
- ///
- public abstract string CommandName { get; }
-
- ///
- /// Creates the process arguments.
- ///
- /// The string that represents the process arguments.
- public virtual string CreateProcessArguments()
- {
- var sb = new StringBuilder();
- sb.Append("-o -"); // output to standard output as opposed to a file.
- sb.Append($" -t {(CaptureTimeoutMilliseconds < 0 ? "0" : CaptureTimeoutMilliseconds.ToString(Ci))}");
-
- // Basic Width and height
- if (CaptureWidth > 0 && CaptureHeight > 0)
- {
- sb.Append($" -w {CaptureWidth.ToString(Ci)}");
- sb.Append($" -h {CaptureHeight.ToString(Ci)}");
- }
-
- // Display Preview
- if (CaptureDisplayPreview)
- {
- if (CaptureDisplayPreviewInFullScreen)
- sb.Append(" -f");
-
- if (CaptureDisplayPreviewOpacity != byte.MaxValue)
- sb.Append($" -op {CaptureDisplayPreviewOpacity.ToString(Ci)}");
- }
- else
- {
- sb.Append(" -n"); // no preview
- }
-
- // Picture Settings
- if (ImageSharpness != 0)
- sb.Append($" -sh {ImageSharpness.Clamp(-100, 100).ToString(Ci)}");
-
- if (ImageContrast != 0)
- sb.Append($" -co {ImageContrast.Clamp(-100, 100).ToString(Ci)}");
-
- if (ImageBrightness != 50)
- sb.Append($" -br {ImageBrightness.Clamp(0, 100).ToString(Ci)}");
-
- if (ImageSaturation != 0)
- sb.Append($" -sa {ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
-
- if (ImageIso >= 100)
- sb.Append($" -ISO {ImageIso.Clamp(100, 800).ToString(Ci)}");
-
- if (CaptureVideoStabilizationEnabled)
- sb.Append(" -vs");
-
- if (CaptureExposureCompensation != 0)
- sb.Append($" -ev {CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
-
- if (CaptureExposure != CameraExposureMode.Auto)
- sb.Append($" -ex {CaptureExposure.ToString().ToLowerInvariant()}");
-
- if (CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto)
- sb.Append($" -awb {CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
-
- if (ImageEffect != CameraImageEffect.None)
- sb.Append($" -ifx {ImageEffect.ToString().ToLowerInvariant()}");
-
- if (ImageColorEffectU >= 0 && ImageColorEffectV >= 0)
- {
- sb.Append(
- $" -cfx {ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
- }
-
- if (CaptureMeteringMode != CameraMeteringMode.Average)
- sb.Append($" -mm {CaptureMeteringMode.ToString().ToLowerInvariant()}");
-
- if (ImageRotation != CameraImageRotation.None)
- sb.Append($" -rot {((int)ImageRotation).ToString(Ci)}");
-
- if (ImageFlipHorizontally)
- sb.Append(" -hf");
-
- if (ImageFlipVertically)
- sb.Append(" -vf");
-
- if (CaptureSensorRoi.IsDefault == false)
- sb.Append($" -roi {CaptureSensorRoi}");
-
- if (CaptureShutterSpeedMicroseconds > 0)
- sb.Append($" -ss {CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
-
- if (CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off)
- sb.Append($" -drc {CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}");
-
- if (CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off &&
- (CaptureWhiteBalanceGainBlue != 0M || CaptureWhiteBalanceGainRed != 0M))
- sb.Append($" -awbg {CaptureWhiteBalanceGainBlue.ToString(Ci)},{CaptureWhiteBalanceGainRed.ToString(Ci)}");
-
- if (ImageAnnotationFontSize > 0)
- {
- sb.Append($" -ae {ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}");
- sb.Append($",{(ImageAnnotationFontColor == null ? "0xff" : ImageAnnotationFontColor.ToYuvHex(true))}");
-
- if (ImageAnnotationBackground != null)
- {
- ImageAnnotations |= CameraAnnotation.SolidBackground;
- sb.Append($",{ImageAnnotationBackground.ToYuvHex(true)}");
- }
- }
-
- if (ImageAnnotations != CameraAnnotation.None)
- sb.Append($" -a {((int)ImageAnnotations).ToString(Ci)}");
-
- if (string.IsNullOrWhiteSpace(ImageAnnotationsText) == false)
- sb.Append($" -a \"{ImageAnnotationsText.Replace("\"", "'")}\"");
-
- return sb.ToString();
- }
-
- #endregion
- }
+ protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture;
+
+ #region Capture Settings
+
+ ///
+ /// Gets or sets the timeout milliseconds.
+ /// Default value is 5000
+ /// Recommended value is at least 300 in order to let the light collectors open.
+ ///
+ public Int32 CaptureTimeoutMilliseconds { get; set; } = 5000;
+
+ ///
+ /// Gets or sets a value indicating whether or not to show a preview window on the screen.
+ ///
+ public Boolean CaptureDisplayPreview { get; set; } = false;
+
+ ///
+ /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled.
+ ///
+ public Boolean CaptureDisplayPreviewInFullScreen { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether video stabilization should be enabled.
+ ///
+ public Boolean CaptureVideoStabilizationEnabled { get; set; } = false;
+
+ ///
+ /// Gets or sets the display preview opacity only if the display preview property is enabled.
+ ///
+ public Byte CaptureDisplayPreviewOpacity { get; set; } = 255;
+
+ ///
+ /// Gets or sets the capture sensor region of interest in relative coordinates.
+ ///
+ public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default;
+
+ ///
+ /// Gets or sets the capture shutter speed in microseconds.
+ /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds).
+ ///
+ public Int32 CaptureShutterSpeedMicroseconds { get; set; } = -1;
+
+ ///
+ /// Gets or sets the exposure mode.
+ ///
+ public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto;
+
+ ///
+ /// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10
+ /// Camera exposure compensation is commonly stated in terms of EV units;
+ /// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure.
+ /// Exposure can be adjusted by changing either the lens f-number or the exposure time;
+ /// which one is changed usually depends on the camera's exposure mode.
+ ///
+ public Int32 CaptureExposureCompensation { get; set; } = 0;
+
+ ///
+ /// Gets or sets the capture metering mode.
+ ///
+ public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average;
+
+ ///
+ /// Gets or sets the automatic white balance mode. By default it is set to Auto.
+ ///
+ public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto;
+
+ ///
+ /// Gets or sets the capture white balance gain on the blue channel. Example: 1.25
+ /// Only takes effect if White balance control is set to off.
+ /// Default is 0.
+ ///
+ public Decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
+
+ ///
+ /// Gets or sets the capture white balance gain on the red channel. Example: 1.75
+ /// Only takes effect if White balance control is set to off.
+ /// Default is 0.
+ ///
+ public Decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
+
+ ///
+ /// Gets or sets the dynamic range compensation.
+ /// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas.
+ ///
+ public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation {
+ get; set;
+ } = CameraDynamicRangeCompensation.Off;
+
+ #endregion
+
+ #region Image Properties
+
+ ///
+ /// Gets or sets the width of the picture to take.
+ /// Less than or equal to 0 in either width or height means maximum resolution available.
+ ///
+ public Int32 CaptureWidth { get; set; } = 640;
+
+ ///
+ /// Gets or sets the height of the picture to take.
+ /// Less than or equal to 0 in either width or height means maximum resolution available.
+ ///
+ public Int32 CaptureHeight { get; set; } = 480;
+
+ ///
+ /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100.
+ ///
+ public Int32 ImageSharpness { get; set; } = 0;
+
+ ///
+ /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100.
+ ///
+ public Int32 ImageContrast { get; set; } = 0;
+
+ ///
+ /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100.
+ ///
+ public Int32 ImageBrightness { get; set; } = 50; // from 0 to 100
+
+ ///
+ /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100.
+ ///
+ public Int32 ImageSaturation { get; set; } = 0;
+
+ ///
+ /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800
+ /// The higher the value, the more light the sensor absorbs.
+ ///
+ public Int32 ImageIso { get; set; } = -1;
+
+ ///
+ /// Gets or sets the image capture effect to be applied.
+ ///
+ public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None;
+
+ ///
+ /// Gets or sets the color effect U coordinates.
+ /// Default is -1, Range is 0 to 255
+ /// 128:128 should be effectively a monochrome image.
+ ///
+ public Int32 ImageColorEffectU { get; set; } = -1; // 0 to 255
+
+ ///
+ /// Gets or sets the color effect V coordinates.
+ /// Default is -1, Range is 0 to 255
+ /// 128:128 should be effectively a monochrome image.
+ ///
+ public Int32 ImageColorEffectV { get; set; } = -1; // 0 to 255
+
+ ///
+ /// Gets or sets the image rotation. Default is no rotation.
+ ///
+ public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None;
+
+ ///
+ /// Gets or sets a value indicating whether the image should be flipped horizontally.
+ ///
+ public Boolean ImageFlipHorizontally {
+ get; set;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the image should be flipped vertically.
+ ///
+ public Boolean ImageFlipVertically {
+ get; set;
+ }
+
+ ///
+ /// Gets or sets the image annotations using a bitmask (or flags) notation.
+ /// Apply a bitwise OR to the enumeration to include multiple annotations.
+ ///
+ public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None;
+
+ ///
+ /// Gets or sets the image annotations text.
+ /// Text may include date/time placeholders by using the '%' character, as used by strftime.
+ /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33.
+ ///
+ public String ImageAnnotationsText { get; set; } = String.Empty;
+
+ ///
+ /// Gets or sets the font size of the text annotations
+ /// Default is -1, range is 6 to 160.
+ ///
+ public Int32 ImageAnnotationFontSize { get; set; } = -1;
+
+ ///
+ /// Gets or sets the color of the text annotations.
+ ///
+ ///
+ /// The color of the image annotation font.
+ ///
+ public CameraColor? ImageAnnotationFontColor { get; set; } = null;
+
+ ///
+ /// Gets or sets the background color for text annotations.
+ ///
+ ///
+ /// The image annotation background.
+ ///
+ public CameraColor? ImageAnnotationBackground { get; set; } = null;
+
+ #endregion
+
+ #region Interface
+
+ ///
+ /// Gets the command file executable.
+ ///
+ public abstract String CommandName {
+ get;
+ }
+
+ ///
+ /// Creates the process arguments.
+ ///
+ /// The string that represents the process arguments.
+ public virtual String CreateProcessArguments() {
+ StringBuilder sb = new StringBuilder();
+ _ = sb.Append("-o -"); // output to standard output as opposed to a file.
+ _ = sb.Append($" -t {(this.CaptureTimeoutMilliseconds < 0 ? "0" : this.CaptureTimeoutMilliseconds.ToString(Ci))}");
+
+ // Basic Width and height
+ if(this.CaptureWidth > 0 && this.CaptureHeight > 0) {
+ _ = sb.Append($" -w {this.CaptureWidth.ToString(Ci)}");
+ _ = sb.Append($" -h {this.CaptureHeight.ToString(Ci)}");
+ }
+
+ // Display Preview
+ if(this.CaptureDisplayPreview) {
+ if(this.CaptureDisplayPreviewInFullScreen) {
+ _ = sb.Append(" -f");
+ }
+
+ if(this.CaptureDisplayPreviewOpacity != Byte.MaxValue) {
+ _ = sb.Append($" -op {this.CaptureDisplayPreviewOpacity.ToString(Ci)}");
+ }
+ } else {
+ _ = sb.Append(" -n"); // no preview
+ }
+
+ // Picture Settings
+ if(this.ImageSharpness != 0) {
+ _ = sb.Append($" -sh {this.ImageSharpness.Clamp(-100, 100).ToString(Ci)}");
+ }
+
+ if(this.ImageContrast != 0) {
+ _ = sb.Append($" -co {this.ImageContrast.Clamp(-100, 100).ToString(Ci)}");
+ }
+
+ if(this.ImageBrightness != 50) {
+ _ = sb.Append($" -br {this.ImageBrightness.Clamp(0, 100).ToString(Ci)}");
+ }
+
+ if(this.ImageSaturation != 0) {
+ _ = sb.Append($" -sa {this.ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
+ }
+
+ if(this.ImageIso >= 100) {
+ _ = sb.Append($" -ISO {this.ImageIso.Clamp(100, 800).ToString(Ci)}");
+ }
+
+ if(this.CaptureVideoStabilizationEnabled) {
+ _ = sb.Append(" -vs");
+ }
+
+ if(this.CaptureExposureCompensation != 0) {
+ _ = sb.Append($" -ev {this.CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
+ }
+
+ if(this.CaptureExposure != CameraExposureMode.Auto) {
+ _ = sb.Append($" -ex {this.CaptureExposure.ToString().ToLowerInvariant()}");
+ }
+
+ if(this.CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) {
+ _ = sb.Append($" -awb {this.CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
+ }
+
+ if(this.ImageEffect != CameraImageEffect.None) {
+ _ = sb.Append($" -ifx {this.ImageEffect.ToString().ToLowerInvariant()}");
+ }
+
+ if(this.ImageColorEffectU >= 0 && this.ImageColorEffectV >= 0) {
+ _ = sb.Append(
+ $" -cfx {this.ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{this.ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
+ }
+
+ if(this.CaptureMeteringMode != CameraMeteringMode.Average) {
+ _ = sb.Append($" -mm {this.CaptureMeteringMode.ToString().ToLowerInvariant()}");
+ }
+
+ if(this.ImageRotation != CameraImageRotation.None) {
+ _ = sb.Append($" -rot {((Int32)this.ImageRotation).ToString(Ci)}");
+ }
+
+ if(this.ImageFlipHorizontally) {
+ _ = sb.Append(" -hf");
+ }
+
+ if(this.ImageFlipVertically) {
+ _ = sb.Append(" -vf");
+ }
+
+ if(this.CaptureSensorRoi.IsDefault == false) {
+ _ = sb.Append($" -roi {this.CaptureSensorRoi}");
+ }
+
+ if(this.CaptureShutterSpeedMicroseconds > 0) {
+ _ = sb.Append($" -ss {this.CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
+ }
+
+ if(this.CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) {
+ _ = sb.Append($" -drc {this.CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}");
+ }
+
+ if(this.CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off &&
+ (this.CaptureWhiteBalanceGainBlue != 0M || this.CaptureWhiteBalanceGainRed != 0M)) {
+ _ = sb.Append($" -awbg {this.CaptureWhiteBalanceGainBlue.ToString(Ci)},{this.CaptureWhiteBalanceGainRed.ToString(Ci)}");
+ }
+
+ if(this.ImageAnnotationFontSize > 0) {
+ _ = sb.Append($" -ae {this.ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}");
+ _ = sb.Append($",{(this.ImageAnnotationFontColor == null ? "0xff" : this.ImageAnnotationFontColor.ToYuvHex(true))}");
+
+ if(this.ImageAnnotationBackground != null) {
+ this.ImageAnnotations |= CameraAnnotation.SolidBackground;
+ _ = sb.Append($",{this.ImageAnnotationBackground.ToYuvHex(true)}");
+ }
+ }
+
+ if(this.ImageAnnotations != CameraAnnotation.None) {
+ _ = sb.Append($" -a {((Int32)this.ImageAnnotations).ToString(Ci)}");
+ }
+
+ if(String.IsNullOrWhiteSpace(this.ImageAnnotationsText) == false) {
+ _ = sb.Append($" -a \"{this.ImageAnnotationsText.Replace("\"", "'")}\"");
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+ }
}
diff --git a/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs b/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs
index f384831..582c9e6 100644
--- a/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs
@@ -1,120 +1,122 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Swan;
-
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// Defines a wrapper for the raspistill program and its settings (command-line arguments).
+ ///
+ ///
+ public class CameraStillSettings : CameraSettingsBase {
+ private Int32 _rotate;
+
+ ///
+ public override String CommandName => "raspistill";
+
///
- /// Defines a wrapper for the raspistill program and its settings (command-line arguments).
+ /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution
+ /// This may slow down preview FPS.
///
- ///
- public class CameraStillSettings : CameraSettingsBase
- {
- private int _rotate;
-
- ///
- public override string CommandName => "raspistill";
-
- ///
- /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution
- /// This may slow down preview FPS.
- ///
- public bool CaptureDisplayPreviewAtResolution { get; set; } = false;
-
- ///
- /// Gets or sets the encoding format the hardware will use for the output.
- ///
- public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg;
-
- ///
- /// Gets or sets the quality for JPEG only encoding mode.
- /// Value ranges from 0 to 100.
- ///
- public int CaptureJpegQuality { get; set; } = 90;
-
- ///
- /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata.
- ///
- public bool CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
-
- ///
- /// JPEG EXIF data
- /// Keys and values must be already properly escaped. Otherwise the command will fail.
- ///
- public Dictionary CaptureJpegExtendedInfo { get; } = new Dictionary();
-
- ///
- /// Gets or sets a value indicating whether [horizontal flip].
- ///
- ///
- /// true if [horizontal flip]; otherwise, false.
- ///
- public bool HorizontalFlip { get; set; } = false;
-
- ///
- /// Gets or sets a value indicating whether [vertical flip].
- ///
- ///
- /// true if [vertical flip]; otherwise, false.
- ///
- public bool VerticalFlip { get; set; } = false;
-
- ///
- /// Gets or sets the rotation.
- ///
- /// Valid range 0-359.
- public int Rotation
- {
- get => _rotate;
- set
- {
- if (value < 0 || value > 359)
- {
- throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
- }
-
- _rotate = value;
- }
- }
-
- ///
- public override string CreateProcessArguments()
- {
- var sb = new StringBuilder(base.CreateProcessArguments());
- sb.Append($" -e {CaptureEncoding.ToString().ToLowerInvariant()}");
-
- // JPEG Encoder specific arguments
- if (CaptureEncoding == CameraImageEncodingFormat.Jpg)
- {
- sb.Append($" -q {CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
-
- if (CaptureJpegIncludeRawBayerMetadata)
- sb.Append(" -r");
-
- // JPEG EXIF data
- if (CaptureJpegExtendedInfo.Count > 0)
- {
- foreach (var kvp in CaptureJpegExtendedInfo)
- {
- if (string.IsNullOrWhiteSpace(kvp.Key) || string.IsNullOrWhiteSpace(kvp.Value))
- continue;
-
- sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
- }
- }
- }
-
- // Display preview settings
- if (CaptureDisplayPreview && CaptureDisplayPreviewAtResolution) sb.Append(" -fp");
-
- if (Rotation != 0) sb.Append($" -rot {Rotation}");
-
- if (HorizontalFlip) sb.Append(" -hf");
-
- if (VerticalFlip) sb.Append(" -vf");
-
- return sb.ToString();
- }
- }
+ public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false;
+
+ ///
+ /// Gets or sets the encoding format the hardware will use for the output.
+ ///
+ public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg;
+
+ ///
+ /// Gets or sets the quality for JPEG only encoding mode.
+ /// Value ranges from 0 to 100.
+ ///
+ public Int32 CaptureJpegQuality { get; set; } = 90;
+
+ ///
+ /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata.
+ ///
+ public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
+
+ ///
+ /// JPEG EXIF data
+ /// Keys and values must be already properly escaped. Otherwise the command will fail.
+ ///
+ public Dictionary CaptureJpegExtendedInfo { get; } = new Dictionary();
+
+ ///
+ /// Gets or sets a value indicating whether [horizontal flip].
+ ///
+ ///
+ /// true if [horizontal flip]; otherwise, false.
+ ///
+ public Boolean HorizontalFlip { get; set; } = false;
+
+ ///
+ /// Gets or sets a value indicating whether [vertical flip].
+ ///
+ ///
+ /// true if [vertical flip]; otherwise, false.
+ ///
+ public Boolean VerticalFlip { get; set; } = false;
+
+ ///
+ /// Gets or sets the rotation.
+ ///
+ /// Valid range 0-359.
+ public Int32 Rotation {
+ get => this._rotate;
+ set {
+ if(value < 0 || value > 359) {
+ throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
+ }
+
+ this._rotate = value;
+ }
+ }
+
+ ///
+ public override String CreateProcessArguments() {
+ StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
+ _ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}");
+
+ // JPEG Encoder specific arguments
+ if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) {
+ _ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
+
+ if(this.CaptureJpegIncludeRawBayerMetadata) {
+ _ = sb.Append(" -r");
+ }
+
+ // JPEG EXIF data
+ if(this.CaptureJpegExtendedInfo.Count > 0) {
+ foreach(KeyValuePair kvp in this.CaptureJpegExtendedInfo) {
+ if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) {
+ continue;
+ }
+
+ _ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
+ }
+ }
+ }
+
+ // Display preview settings
+ if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) {
+ _ = sb.Append(" -fp");
+ }
+
+ if(this.Rotation != 0) {
+ _ = sb.Append($" -rot {this.Rotation}");
+ }
+
+ if(this.HorizontalFlip) {
+ _ = sb.Append(" -hf");
+ }
+
+ if(this.VerticalFlip) {
+ _ = sb.Append(" -vf");
+ }
+
+ return sb.ToString();
+ }
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs b/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs
index c80c3ee..6a80899 100644
--- a/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs
+++ b/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs
@@ -1,104 +1,106 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System.Text;
-
+using System;
+using System.Text;
+
+namespace Unosquare.RaspberryIO.Camera {
+ ///
+ /// Represents the raspivid camera settings for video capture functionality.
+ ///
+ ///
+ public class CameraVideoSettings : CameraSettingsBase {
+ private Int32 _length;
+
+ ///
+ public override String CommandName => "raspivid";
+
///
- /// Represents the raspivid camera settings for video capture functionality.
+ /// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more.
+ /// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30.
+ /// Default -1.
///
- ///
- public class CameraVideoSettings : CameraSettingsBase
- {
- private int _length;
-
- ///
- public override string CommandName => "raspivid";
-
- ///
- /// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more.
- /// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30.
- /// Default -1.
- ///
- public int CaptureBitrate { get; set; } = -1;
-
- ///
- /// Gets or sets the framerate.
- /// Default 25, range 2 to 30.
- ///
- public int CaptureFramerate { get; set; } = 25;
-
- ///
- /// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra
- /// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame.
- /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone.
- ///
- public int CaptureKeyframeRate { get; set; } = 25;
-
- ///
- /// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect
- /// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a
- /// bitrate of 0 to set a completely variable bitrate.
- ///
- public int CaptureQuantisation { get; set; } = 23;
-
- ///
- /// Gets or sets the profile.
- /// Sets the H264 profile to be used for the encoding.
- /// Default is Main mode.
- ///
- public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main;
-
- ///
- /// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases
- /// e.g. Apple HLS. These headers are small, so don't greatly increase the file size.
- ///
- ///
- /// true if [interleave headers]; otherwise, false.
- ///
- public bool CaptureInterleaveHeaders { get; set; } = true;
-
- ///
- /// Toggle fullscreen mode for video preview.
- ///
- public bool Fullscreen { get; set; } = false;
-
- ///
- /// Specifies the path to save video files.
- ///
- public string VideoFileName { get; set; }
-
- ///
- /// Video stream length in seconds.
- ///
- public int LengthInSeconds
- {
- get => _length;
- set => _length = value * 1000;
- }
-
- ///
- /// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
- /// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases.
- ///
- ///
- /// true if [capture display preview encoded]; otherwise, false.
- ///
- public bool CaptureDisplayPreviewEncoded { get; set; } = false;
-
- ///
- public override string CreateProcessArguments()
- {
- var sb = new StringBuilder(base.CreateProcessArguments());
-
- if (Fullscreen)
- sb.Append(" -f");
-
- if (LengthInSeconds != 0)
- sb.Append($" -t {LengthInSeconds}");
-
- if (!string.IsNullOrEmpty(VideoFileName))
- sb.Append($" -o {VideoFileName}");
-
- return sb.ToString();
- }
- }
+ public Int32 CaptureBitrate { get; set; } = -1;
+
+ ///
+ /// Gets or sets the framerate.
+ /// Default 25, range 2 to 30.
+ ///
+ public Int32 CaptureFramerate { get; set; } = 25;
+
+ ///
+ /// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra
+ /// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame.
+ /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone.
+ ///
+ public Int32 CaptureKeyframeRate { get; set; } = 25;
+
+ ///
+ /// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect
+ /// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a
+ /// bitrate of 0 to set a completely variable bitrate.
+ ///
+ public Int32 CaptureQuantisation { get; set; } = 23;
+
+ ///
+ /// Gets or sets the profile.
+ /// Sets the H264 profile to be used for the encoding.
+ /// Default is Main mode.
+ ///
+ public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main;
+
+ ///
+ /// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases
+ /// e.g. Apple HLS. These headers are small, so don't greatly increase the file size.
+ ///
+ ///
+ /// true if [interleave headers]; otherwise, false.
+ ///
+ public Boolean CaptureInterleaveHeaders { get; set; } = true;
+
+ ///
+ /// Toggle fullscreen mode for video preview.
+ ///
+ public Boolean Fullscreen { get; set; } = false;
+
+ ///
+ /// Specifies the path to save video files.
+ ///
+ public String? VideoFileName {
+ get; set;
+ }
+
+ ///
+ /// Video stream length in seconds.
+ ///
+ public Int32 LengthInSeconds {
+ get => this._length;
+ set => this._length = value * 1000;
+ }
+
+ ///
+ /// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
+ /// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases.
+ ///
+ ///
+ /// true if [capture display preview encoded]; otherwise, false.
+ ///
+ public Boolean CaptureDisplayPreviewEncoded { get; set; } = false;
+
+ ///
+ public override String CreateProcessArguments() {
+ StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
+
+ if(this.Fullscreen) {
+ _ = sb.Append(" -f");
+ }
+
+ if(this.LengthInSeconds != 0) {
+ _ = sb.Append($" -t {this.LengthInSeconds}");
+ }
+
+ if(!String.IsNullOrEmpty(this.VideoFileName)) {
+ _ = sb.Append($" -o {this.VideoFileName}");
+ }
+
+ return sb.ToString();
+ }
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Camera/Enums.cs b/Unosquare.RaspberryIO/Camera/Enums.cs
index f77ca6e..0c49868 100644
--- a/Unosquare.RaspberryIO/Camera/Enums.cs
+++ b/Unosquare.RaspberryIO/Camera/Enums.cs
@@ -1,423 +1,413 @@
-namespace Unosquare.RaspberryIO.Camera
-{
- using System;
-
+namespace Unosquare.RaspberryIO.Camera {
+ using System;
+
+ ///
+ /// Defines the available encoding formats for the Raspberry Pi camera module.
+ ///
+ public enum CameraImageEncodingFormat {
///
- /// Defines the available encoding formats for the Raspberry Pi camera module.
+ /// The JPG
///
- public enum CameraImageEncodingFormat
- {
- ///
- /// The JPG
- ///
- Jpg,
-
- ///
- /// The BMP
- ///
- Bmp,
-
- ///
- /// The GIF
- ///
- Gif,
-
- ///
- /// The PNG
- ///
- Png
- }
-
+ Jpg,
+
///
- /// Defines the different exposure modes for the Raspberry Pi's camera module.
+ /// The BMP
///
- public enum CameraExposureMode
- {
- ///
- /// The automatic
- ///
- Auto,
-
- ///
- /// The night
- ///
- Night,
-
- ///
- /// The night preview
- ///
- NightPreview,
-
- ///
- /// The backlight
- ///
- Backlight,
-
- ///
- /// The spotlight
- ///
- Spotlight,
-
- ///
- /// The sports
- ///
- Sports,
-
- ///
- /// The snow
- ///
- Snow,
-
- ///
- /// The beach
- ///
- Beach,
-
- ///
- /// The very long
- ///
- VeryLong,
-
- ///
- /// The fixed FPS
- ///
- FixedFps,
-
- ///
- /// The anti shake
- ///
- AntiShake,
-
- ///
- /// The fireworks
- ///
- Fireworks
- }
-
+ Bmp,
+
///
- /// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module.
+ /// The GIF
///
- public enum CameraWhiteBalanceMode
- {
- ///
- /// No white balance
- ///
- Off,
-
- ///
- /// The automatic
- ///
- Auto,
-
- ///
- /// The sun
- ///
- Sun,
-
- ///
- /// The cloud
- ///
- Cloud,
-
- ///
- /// The shade
- ///
- Shade,
-
- ///
- /// The tungsten
- ///
- Tungsten,
-
- ///
- /// The fluorescent
- ///
- Fluorescent,
-
- ///
- /// The incandescent
- ///
- Incandescent,
-
- ///
- /// The flash
- ///
- Flash,
-
- ///
- /// The horizon
- ///
- Horizon
- }
-
+ Gif,
+
///
- /// Defines the available image effects for the Raspberry Pi's camera module.
+ /// The PNG
///
- public enum CameraImageEffect
- {
- ///
- /// No effect
- ///
- None,
-
- ///
- /// The negative
- ///
- Negative,
-
- ///
- /// The solarise
- ///
- Solarise,
-
- ///
- /// The whiteboard
- ///
- Whiteboard,
-
- ///
- /// The blackboard
- ///
- Blackboard,
-
- ///
- /// The sketch
- ///
- Sketch,
-
- ///
- /// The denoise
- ///
- Denoise,
-
- ///
- /// The emboss
- ///
- Emboss,
-
- ///
- /// The oil paint
- ///
- OilPaint,
-
- ///
- /// The hatch
- ///
- Hatch,
-
- ///
- /// Graphite Pen
- ///
- GPen,
-
- ///
- /// The pastel
- ///
- Pastel,
-
- ///
- /// The water colour
- ///
- WaterColour,
-
- ///
- /// The film
- ///
- Film,
-
- ///
- /// The blur
- ///
- Blur,
-
- ///
- /// The saturation
- ///
- Saturation,
-
- ///
- /// The solour swap
- ///
- SolourSwap,
-
- ///
- /// The washed out
- ///
- WashedOut,
-
- ///
- /// The colour point
- ///
- ColourPoint,
-
- ///
- /// The colour balance
- ///
- ColourBalance,
-
- ///
- /// The cartoon
- ///
- Cartoon
- }
-
+ Png
+ }
+
+ ///
+ /// Defines the different exposure modes for the Raspberry Pi's camera module.
+ ///
+ public enum CameraExposureMode {
///
- /// Defines the different metering modes for the Raspberry Pi's camera module.
+ /// The automatic
///
- public enum CameraMeteringMode
- {
- ///
- /// The average
- ///
- Average,
-
- ///
- /// The spot
- ///
- Spot,
-
- ///
- /// The backlit
- ///
- Backlit,
-
- ///
- /// The matrix
- ///
- Matrix
- }
-
+ Auto,
+
///
- /// Defines the different image rotation modes for the Raspberry Pi's camera module.
+ /// The night
///
- public enum CameraImageRotation
- {
- ///
- /// No rerotation
- ///
- None = 0,
-
- ///
- /// 90 Degrees
- ///
- Degrees90 = 90,
-
- ///
- /// 180 Degrees
- ///
- Degrees180 = 180,
-
- ///
- /// 270 degrees
- ///
- Degrees270 = 270
- }
-
+ Night,
+
///
- /// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module
- /// Helpful for low light photos.
+ /// The night preview
///
- public enum CameraDynamicRangeCompensation
- {
- ///
- /// The off setting
- ///
- Off,
-
- ///
- /// The low
- ///
- Low,
-
- ///
- /// The medium
- ///
- Medium,
-
- ///
- /// The high
- ///
- High
- }
-
+ NightPreview,
+
///
- /// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module.
+ /// The backlight
///
- [Flags]
- public enum CameraAnnotation
- {
- ///
- /// The none
- ///
- None = 0,
-
- ///
- /// The time
- ///
- Time = 4,
-
- ///
- /// The date
- ///
- Date = 8,
-
- ///
- /// The shutter settings
- ///
- ShutterSettings = 16,
-
- ///
- /// The caf settings
- ///
- CafSettings = 32,
-
- ///
- /// The gain settings
- ///
- GainSettings = 64,
-
- ///
- /// The lens settings
- ///
- LensSettings = 128,
-
- ///
- /// The motion settings
- ///
- MotionSettings = 256,
-
- ///
- /// The frame number
- ///
- FrameNumber = 512,
-
- ///
- /// The solid background
- ///
- SolidBackground = 1024
- }
-
+ Backlight,
+
///
- /// Defines the different H.264 encoding profiles to be used when capturing video.
+ /// The spotlight
///
- public enum CameraH264Profile
- {
- ///
- /// BP: Primarily for lower-cost applications with limited computing resources,
- /// this profile is used widely in videoconferencing and mobile applications.
- ///
- Baseline,
-
- ///
- /// MP: Originally intended as the mainstream consumer profile for broadcast
- /// and storage applications, the importance of this profile faded when the High profile was developed for those applications.
- ///
- Main,
-
- ///
- /// HiP: The primary profile for broadcast and disc storage applications, particularly
- /// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example).
- ///
- High
- }
+ Spotlight,
+
+ ///
+ /// The sports
+ ///
+ Sports,
+
+ ///
+ /// The snow
+ ///
+ Snow,
+
+ ///
+ /// The beach
+ ///
+ Beach,
+
+ ///
+ /// The very long
+ ///
+ VeryLong,
+
+ ///
+ /// The fixed FPS
+ ///
+ FixedFps,
+
+ ///
+ /// The anti shake
+ ///
+ AntiShake,
+
+ ///
+ /// The fireworks
+ ///
+ Fireworks
+ }
+
+ ///
+ /// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module.
+ ///
+ public enum CameraWhiteBalanceMode {
+ ///
+ /// No white balance
+ ///
+ Off,
+
+ ///
+ /// The automatic
+ ///
+ Auto,
+
+ ///
+ /// The sun
+ ///
+ Sun,
+
+ ///
+ /// The cloud
+ ///
+ Cloud,
+
+ ///
+ /// The shade
+ ///
+ Shade,
+
+ ///
+ /// The tungsten
+ ///
+ Tungsten,
+
+ ///
+ /// The fluorescent
+ ///
+ Fluorescent,
+
+ ///
+ /// The incandescent
+ ///
+ Incandescent,
+
+ ///
+ /// The flash
+ ///
+ Flash,
+
+ ///
+ /// The horizon
+ ///
+ Horizon
+ }
+
+ ///
+ /// Defines the available image effects for the Raspberry Pi's camera module.
+ ///
+ public enum CameraImageEffect {
+ ///
+ /// No effect
+ ///
+ None,
+
+ ///
+ /// The negative
+ ///
+ Negative,
+
+ ///
+ /// The solarise
+ ///
+ Solarise,
+
+ ///
+ /// The whiteboard
+ ///
+ Whiteboard,
+
+ ///
+ /// The blackboard
+ ///
+ Blackboard,
+
+ ///
+ /// The sketch
+ ///
+ Sketch,
+
+ ///
+ /// The denoise
+ ///
+ Denoise,
+
+ ///
+ /// The emboss
+ ///
+ Emboss,
+
+ ///
+ /// The oil paint
+ ///
+ OilPaint,
+
+ ///
+ /// The hatch
+ ///
+ Hatch,
+
+ ///
+ /// Graphite Pen
+ ///
+ GPen,
+
+ ///
+ /// The pastel
+ ///
+ Pastel,
+
+ ///
+ /// The water colour
+ ///
+ WaterColour,
+
+ ///
+ /// The film
+ ///
+ Film,
+
+ ///
+ /// The blur
+ ///
+ Blur,
+
+ ///
+ /// The saturation
+ ///
+ Saturation,
+
+ ///
+ /// The solour swap
+ ///
+ SolourSwap,
+
+ ///
+ /// The washed out
+ ///
+ WashedOut,
+
+ ///
+ /// The colour point
+ ///
+ ColourPoint,
+
+ ///
+ /// The colour balance
+ ///
+ ColourBalance,
+
+ ///
+ /// The cartoon
+ ///
+ Cartoon
+ }
+
+ ///
+ /// Defines the different metering modes for the Raspberry Pi's camera module.
+ ///
+ public enum CameraMeteringMode {
+ ///
+ /// The average
+ ///
+ Average,
+
+ ///
+ /// The spot
+ ///
+ Spot,
+
+ ///
+ /// The backlit
+ ///
+ Backlit,
+
+ ///
+ /// The matrix
+ ///
+ Matrix
+ }
+
+ ///
+ /// Defines the different image rotation modes for the Raspberry Pi's camera module.
+ ///
+ public enum CameraImageRotation {
+ ///
+ /// No rerotation
+ ///
+ None = 0,
+
+ ///
+ /// 90 Degrees
+ ///
+ Degrees90 = 90,
+
+ ///
+ /// 180 Degrees
+ ///
+ Degrees180 = 180,
+
+ ///
+ /// 270 degrees
+ ///
+ Degrees270 = 270
+ }
+
+ ///
+ /// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module
+ /// Helpful for low light photos.
+ ///
+ public enum CameraDynamicRangeCompensation {
+ ///
+ /// The off setting
+ ///
+ Off,
+
+ ///
+ /// The low
+ ///
+ Low,
+
+ ///
+ /// The medium
+ ///
+ Medium,
+
+ ///
+ /// The high
+ ///
+ High
+ }
+
+ ///
+ /// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module.
+ ///
+ [Flags]
+ public enum CameraAnnotation {
+ ///
+ /// The none
+ ///
+ None = 0,
+
+ ///
+ /// The time
+ ///
+ Time = 4,
+
+ ///
+ /// The date
+ ///
+ Date = 8,
+
+ ///
+ /// The shutter settings
+ ///
+ ShutterSettings = 16,
+
+ ///
+ /// The caf settings
+ ///
+ CafSettings = 32,
+
+ ///
+ /// The gain settings
+ ///
+ GainSettings = 64,
+
+ ///
+ /// The lens settings
+ ///
+ LensSettings = 128,
+
+ ///
+ /// The motion settings
+ ///
+ MotionSettings = 256,
+
+ ///
+ /// The frame number
+ ///
+ FrameNumber = 512,
+
+ ///
+ /// The solid background
+ ///
+ SolidBackground = 1024
+ }
+
+ ///
+ /// Defines the different H.264 encoding profiles to be used when capturing video.
+ ///
+ public enum CameraH264Profile {
+ ///
+ /// BP: Primarily for lower-cost applications with limited computing resources,
+ /// this profile is used widely in videoconferencing and mobile applications.
+ ///
+ Baseline,
+
+ ///
+ /// MP: Originally intended as the mainstream consumer profile for broadcast
+ /// and storage applications, the importance of this profile faded when the High profile was developed for those applications.
+ ///
+ Main,
+
+ ///
+ /// HiP: The primary profile for broadcast and disc storage applications, particularly
+ /// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example).
+ ///
+ High
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/AudioSettings.cs b/Unosquare.RaspberryIO/Computer/AudioSettings.cs
index 206cd88..fb10e05 100644
--- a/Unosquare.RaspberryIO/Computer/AudioSettings.cs
+++ b/Unosquare.RaspberryIO/Computer/AudioSettings.cs
@@ -1,113 +1,104 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using Swan;
- using System;
- using System.Linq;
- using System.Threading.Tasks;
-
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Settings for audio device.
+ ///
+ public class AudioSettings : SingletonBase {
+ private const String DefaultControlName = "PCM";
+ private const Int32 DefaultCardNumber = 0;
+
+ private readonly String[] _errorMess = { "Invalid", "Unable" };
+
///
- /// Settings for audio device.
+ /// Gets the current audio state.
///
- public class AudioSettings : SingletonBase
- {
- private const string DefaultControlName = "PCM";
- private const int DefaultCardNumber = 0;
-
- private readonly string[] _errorMess = { "Invalid", "Unable" };
-
- ///
- /// Gets the current audio state.
- ///
- /// The card number.
- /// Name of the control.
- /// An object.
- /// Invalid command, card number or control name.
- public async Task GetState(int cardNumber = DefaultCardNumber, string controlName = DefaultControlName)
- {
- var volumeInfo = await ProcessRunner.GetProcessOutputAsync("amixer", $"-c {cardNumber} get {controlName}").ConfigureAwait(false);
-
- var lines = volumeInfo.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (!lines.Any())
- throw new InvalidOperationException("Invalid command.");
-
- if (_errorMess.Any(x => lines[0].Contains(x)))
- throw new InvalidOperationException(lines[0]);
-
- var volumeLine = lines
- .FirstOrDefault(x => x.Trim()
- .StartsWith("Mono:", StringComparison.OrdinalIgnoreCase));
-
- if (volumeLine == null)
- throw new InvalidOperationException("Unexpected output from 'amixer'.");
-
- var sections = volumeLine.Split(new[] { ' ' },
- StringSplitOptions.RemoveEmptyEntries);
-
- var level = int.Parse(sections[3].Substring(1, sections[3].Length - 3),
- System.Globalization.NumberFormatInfo.InvariantInfo);
-
- var decibels = float.Parse(sections[4].Substring(1, sections[4].Length - 4),
- System.Globalization.NumberFormatInfo.InvariantInfo);
-
- var isMute = sections[5].Equals("[off]",
- StringComparison.CurrentCultureIgnoreCase);
-
- return new AudioState(cardNumber, controlName, level, decibels, isMute);
- }
-
- ///
- /// Sets the volume percentage.
- ///
- /// The percentage level.
- /// The card number.
- /// Name of the control.
- /// A representing the asynchronous operation.
- /// Invalid card number or control name.
- public Task SetVolumePercentage(int level, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
- SetAudioCommand($"{level}%", cardNumber, controlName);
-
- ///
- /// Sets the volume by decibels.
- ///
- /// The decibels.
- /// The card number.
- /// Name of the control.
- /// A representing the asynchronous operation.
- /// Invalid card number or control name.
- public Task SetVolumeByDecibels(float decibels, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
- SetAudioCommand($"{decibels}dB", cardNumber, controlName);
-
- ///
- /// Increments the volume by decibels.
- ///
- /// The decibels to increment or decrement.
- /// The card number.
- /// Name of the control.
- /// A representing the asynchronous operation.
- /// Invalid card number or control name.
- public Task IncrementVolume(float decibels, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
- SetAudioCommand($"{decibels}dB{(decibels < 0 ? "-" : "+")}", cardNumber, controlName);
-
- ///
- /// Toggles the mute state.
- ///
- /// if set to true, mutes the audio.
- /// The card number.
- /// Name of the control.
- /// A representing the asynchronous operation.
- /// Invalid card number or control name.
- public Task ToggleMute(bool mute, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
- SetAudioCommand(mute ? "mute" : "unmute", cardNumber, controlName);
-
- private static async Task SetAudioCommand(string command, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName)
- {
- var taskResult = await ProcessRunner.GetProcessOutputAsync("amixer", $"-q -c {cardNumber} -- set {controlName} {command}").ConfigureAwait(false);
-
- if (!string.IsNullOrWhiteSpace(taskResult))
- throw new InvalidOperationException(taskResult.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).First());
-
- return taskResult;
- }
- }
+ /// The card number.
+ /// Name of the control.
+ /// An object.
+ /// Invalid command, card number or control name.
+ public async Task GetState(Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) {
+ String volumeInfo = await ProcessRunner.GetProcessOutputAsync("amixer", $"-c {cardNumber} get {controlName}").ConfigureAwait(false);
+
+ String[] lines = volumeInfo.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
+
+ if(!lines.Any()) {
+ throw new InvalidOperationException("Invalid command.");
+ }
+
+ if(this._errorMess.Any(x => lines[0].Contains(x))) {
+ throw new InvalidOperationException(lines[0]);
+ }
+
+ String volumeLine = lines.FirstOrDefault(x => x.Trim().StartsWith("Mono:", StringComparison.OrdinalIgnoreCase));
+
+ if(volumeLine == null) {
+ throw new InvalidOperationException("Unexpected output from 'amixer'.");
+ }
+
+ String[] sections = volumeLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+
+ Int32 level = Int32.Parse(sections[3][1..^2], System.Globalization.NumberFormatInfo.InvariantInfo);
+
+ Single decibels = Single.Parse(sections[4][1..^3], System.Globalization.NumberFormatInfo.InvariantInfo);
+
+ Boolean isMute = sections[5].Equals("[off]", StringComparison.CurrentCultureIgnoreCase);
+
+ return new AudioState(cardNumber, controlName, level, decibels, isMute);
+ }
+
+ ///
+ /// Sets the volume percentage.
+ ///
+ /// The percentage level.
+ /// The card number.
+ /// Name of the control.
+ /// A representing the asynchronous operation.
+ /// Invalid card number or control name.
+ public Task SetVolumePercentage(Int32 level, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{level}%", cardNumber, controlName);
+
+ ///
+ /// Sets the volume by decibels.
+ ///
+ /// The decibels.
+ /// The card number.
+ /// Name of the control.
+ /// A representing the asynchronous operation.
+ /// Invalid card number or control name.
+ public Task SetVolumeByDecibels(Single decibels, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{decibels}dB", cardNumber, controlName);
+
+ ///
+ /// Increments the volume by decibels.
+ ///
+ /// The decibels to increment or decrement.
+ /// The card number.
+ /// Name of the control.
+ /// A representing the asynchronous operation.
+ /// Invalid card number or control name.
+ public Task IncrementVolume(Single decibels, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{decibels}dB{(decibels < 0 ? "-" : "+")}", cardNumber, controlName);
+
+ ///
+ /// Toggles the mute state.
+ ///
+ /// if set to true, mutes the audio.
+ /// The card number.
+ /// Name of the control.
+ /// A representing the asynchronous operation.
+ /// Invalid card number or control name.
+ public Task ToggleMute(Boolean mute, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand(mute ? "mute" : "unmute", cardNumber, controlName);
+
+ private static async Task SetAudioCommand(String command, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) {
+ String taskResult = await ProcessRunner.GetProcessOutputAsync("amixer", $"-q -c {cardNumber} -- set {controlName} {command}").ConfigureAwait(false);
+
+ if(!String.IsNullOrWhiteSpace(taskResult)) {
+ throw new InvalidOperationException(taskResult.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).First());
+ }
+
+ return taskResult;
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/AudioState.cs b/Unosquare.RaspberryIO/Computer/AudioState.cs
index ab71777..62d302e 100644
--- a/Unosquare.RaspberryIO/Computer/AudioState.cs
+++ b/Unosquare.RaspberryIO/Computer/AudioState.cs
@@ -1,64 +1,72 @@
-namespace Unosquare.RaspberryIO.Computer
-{
+using System;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Manage the volume of any sound device.
+ ///
+ public readonly struct AudioState {
///
- /// Manage the volume of any sound device.
+ /// Initializes a new instance of the struct.
///
- public readonly struct AudioState
- {
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The card number.
- /// Name of the control.
- /// The volume level in percentaje.
- /// The volume level in decibels.
- /// if set to true the audio is mute.
- public AudioState(int cardNumber, string controlName, int level, float decibels, bool isMute)
- {
- CardNumber = cardNumber;
- ControlName = controlName;
- Level = level;
- Decibels = decibels;
- IsMute = isMute;
- }
-
- ///
- /// Gets the card number.
- ///
- public int CardNumber { get; }
-
- ///
- /// Gets the name of the current control.
- ///
- public string ControlName { get; }
-
- ///
- /// Gets the volume level in percentage.
- ///
- public int Level { get; }
-
- ///
- /// Gets the volume level in decibels.
- ///
- public float Decibels { get; }
-
- ///
- /// Gets a value indicating whether the audio is mute.
- ///
- public bool IsMute { get; }
-
- ///
- /// Returns a that represents the audio state.
- ///
- ///
- /// A that represents the audio state.
- ///
- public override string ToString() =>
- "Device information: \n" +
- $">> Name: {ControlName}\n" +
- $">> Card number: {CardNumber}\n" +
- $">> Volume (%): {Level}%\n" +
- $">> Volume (dB): {Decibels:0.00}dB\n" +
- $">> Mute: [{(IsMute ? "Off" : "On")}]\n\n";
- }
+ /// The card number.
+ /// Name of the control.
+ /// The volume level in percentaje.
+ /// The volume level in decibels.
+ /// if set to true the audio is mute.
+ public AudioState(Int32 cardNumber, String controlName, Int32 level, Single decibels, Boolean isMute) {
+ this.CardNumber = cardNumber;
+ this.ControlName = controlName;
+ this.Level = level;
+ this.Decibels = decibels;
+ this.IsMute = isMute;
+ }
+
+ ///
+ /// Gets the card number.
+ ///
+ public Int32 CardNumber {
+ get;
+ }
+
+ ///
+ /// Gets the name of the current control.
+ ///
+ public String ControlName {
+ get;
+ }
+
+ ///
+ /// Gets the volume level in percentage.
+ ///
+ public Int32 Level {
+ get;
+ }
+
+ ///
+ /// Gets the volume level in decibels.
+ ///
+ public Single Decibels {
+ get;
+ }
+
+ ///
+ /// Gets a value indicating whether the audio is mute.
+ ///
+ public Boolean IsMute {
+ get;
+ }
+
+ ///
+ /// Returns a that represents the audio state.
+ ///
+ ///
+ /// A that represents the audio state.
+ ///
+ public override String ToString() => "Device information: \n" +
+ $">> Name: {this.ControlName}\n" +
+ $">> Card number: {this.CardNumber}\n" +
+ $">> Volume (%): {this.Level}%\n" +
+ $">> Volume (dB): {this.Decibels:0.00}dB\n" +
+ $">> Mute: [{(this.IsMute ? "Off" : "On")}]\n\n";
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Computer/Bluetooth.cs b/Unosquare.RaspberryIO/Computer/Bluetooth.cs
index beee700..8b6023e 100644
--- a/Unosquare.RaspberryIO/Computer/Bluetooth.cs
+++ b/Unosquare.RaspberryIO/Computer/Bluetooth.cs
@@ -1,271 +1,201 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Swan;
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Represents the Bluetooth information.
+ ///
+ public class Bluetooth : SingletonBase {
+ private const String BcCommand = "bluetoothctl";
+
///
- /// Represents the Bluetooth information.
+ /// Turns on the Bluetooth adapter.
///
- public class Bluetooth : SingletonBase
- {
- private const string BcCommand = "bluetoothctl";
-
- ///
- /// Turns on the Bluetooth adapter.
- ///
- /// The cancellation token.
- ///
- /// Returns true or false depending if the controller was turned on.
- ///
- /// Failed to power on:.
- public async Task PowerOn(CancellationToken cancellationToken = default)
- {
- try
- {
- var output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power on", null, cancellationToken)
- .ConfigureAwait(false);
- return output.Contains("succeeded");
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to power on: {ex.Message}");
- }
- }
-
- ///
- /// Turns off the bluetooth adapter.
- ///
- /// The cancellation token.
- ///
- /// Returns true or false depending if the controller was turned off.
- ///
- /// Failed to power off:.
- public async Task PowerOff(CancellationToken cancellationToken = default)
- {
- try
- {
- var output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power off", null, cancellationToken)
- .ConfigureAwait(false);
- return output.Contains("succeeded");
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to power off: {ex.Message}");
- }
- }
-
- ///
- /// Gets the list of detected devices.
- ///
- /// The cancellation token.
- ///
- /// Returns the list of detected devices.
- ///
- /// Failed to retrieve devices:.
- public async Task> ListDevices(CancellationToken cancellationToken = default)
- {
- try
- {
- using var cancellationTokenSource = new CancellationTokenSource(3000);
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan on", null, cancellationTokenSource.Token)
- .ConfigureAwait(false);
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan off", null, cancellationToken)
- .ConfigureAwait(false);
- var devices = await ProcessRunner.GetProcessOutputAsync(BcCommand, "devices", null, cancellationToken)
- .ConfigureAwait(false);
- return devices.Trim().Split('\n').Select(x => x.Trim());
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to retrieve devices: {ex.Message}");
- }
- }
-
- ///
- /// Gets the list of bluetooth controllers.
- ///
- /// The cancellation token.
- ///
- /// Returns the list of bluetooth controllers.
- ///
- /// Failed to retrieve controllers:.
- public async Task> ListControllers(CancellationToken cancellationToken = default)
- {
- try
- {
- var controllers = await ProcessRunner.GetProcessOutputAsync(BcCommand, "list", null, cancellationToken)
- .ConfigureAwait(false);
- return controllers.Trim().Split('\n').Select(x => x.Trim());
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to retrieve controllers: {ex.Message}");
- }
- }
-
- ///
- /// Pairs a specific device with a specific controller.
- ///
- /// The mac address of the controller that will be used to pair.
- /// The mac address of the device that will be paired.
- /// The cancellation token.
- ///
- /// Returns true or false if the pair was successfully.
- ///
- /// Failed to Pair:.
- public async Task Pair(
- string controllerAddress,
- string deviceAddress,
- CancellationToken cancellationToken = default)
- {
- try
- {
- // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
- await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Makes the controller visible to other devices.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Pairs the device with the controller.
- var result = await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"pair {deviceAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken)
- .ConfigureAwait(false);
-
- return result.Contains("Paired: yes");
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to Pair: {ex.Message}");
- }
- }
-
- ///
- /// Performs a connection of a given controller with a given device.
- ///
- /// The mac address of the controller that will be used to make the connection.
- /// The mac address of the device that will be connected.
- /// The cancellation token.
- ///
- /// Returns true or false if the connection was successfully.
- ///
- /// Failed to connect:.
- public async Task Connect(
- string controllerAddress,
- string deviceAddress,
- CancellationToken cancellationToken = default)
- {
- try
- {
- // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
- await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Makes the controller visible to other devices.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Readies the device for pairing.
- var result = await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"connect {deviceAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken)
- .ConfigureAwait(false);
-
- return result.Contains("Connected: yes");
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to connect: {ex.Message}");
- }
- }
-
- ///
- /// Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
- ///
- /// The mac address of the controller will be used.
- /// The mac address of the device will be added to the trust list devices.
- /// The cancellation token.
- ///
- /// Returns true or false if the operation was successful.
- ///
- /// Failed to add to trust devices list:.
- public async Task Trust(
- string controllerAddress,
- string deviceAddress,
- CancellationToken cancellationToken = default)
- {
- try
- {
- // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
- await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Makes the controller visible to other devices.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
- var result = await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"trust {deviceAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
- await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken)
- .ConfigureAwait(false);
-
- return result.Contains("Trusted: yes");
- }
- catch (Exception ex)
- {
- throw new BluetoothErrorException($"Failed to add to trust devices list: {ex.Message}");
- }
- }
-
- ///
- /// Displays information about a particular device.
- ///
- /// The mac address of the device which info will be retrieved.
- /// The cancellation token.
- ///
- /// Returns the device info.
- ///
- /// Failed to retrieve info for {deviceAddress}.
- public async Task DeviceInfo(string deviceAddress, CancellationToken cancellationToken = default)
- {
- var info = await ProcessRunner
- .GetProcessOutputAsync(BcCommand, $"info {deviceAddress}", null, cancellationToken)
- .ConfigureAwait(false);
-
- return !string.IsNullOrEmpty(info)
- ? info
- : throw new BluetoothErrorException($"Failed to retrieve info for {deviceAddress}");
- }
- }
+ /// The cancellation token.
+ ///
+ /// Returns true or false depending if the controller was turned on.
+ ///
+ /// Failed to power on:.
+ public async Task PowerOn(CancellationToken cancellationToken = default) {
+ try {
+ String output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power on", null, cancellationToken).ConfigureAwait(false);
+ return output.Contains("succeeded");
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to power on: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Turns off the bluetooth adapter.
+ ///
+ /// The cancellation token.
+ ///
+ /// Returns true or false depending if the controller was turned off.
+ ///
+ /// Failed to power off:.
+ public async Task PowerOff(CancellationToken cancellationToken = default) {
+ try {
+ String output = await ProcessRunner.GetProcessOutputAsync(BcCommand, "power off", null, cancellationToken).ConfigureAwait(false);
+ return output.Contains("succeeded");
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to power off: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Gets the list of detected devices.
+ ///
+ /// The cancellation token.
+ ///
+ /// Returns the list of detected devices.
+ ///
+ /// Failed to retrieve devices:.
+ public async Task> ListDevices(CancellationToken cancellationToken = default) {
+ try {
+ using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(3000);
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan on", null, cancellationTokenSource.Token).ConfigureAwait(false);
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "scan off", null, cancellationToken).ConfigureAwait(false);
+ String devices = await ProcessRunner.GetProcessOutputAsync(BcCommand, "devices", null, cancellationToken).ConfigureAwait(false);
+ return devices.Trim().Split('\n').Select(x => x.Trim());
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to retrieve devices: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Gets the list of bluetooth controllers.
+ ///
+ /// The cancellation token.
+ ///
+ /// Returns the list of bluetooth controllers.
+ ///
+ /// Failed to retrieve controllers:.
+ public async Task> ListControllers(CancellationToken cancellationToken = default) {
+ try {
+ String controllers = await ProcessRunner.GetProcessOutputAsync(BcCommand, "list", null, cancellationToken).ConfigureAwait(false);
+ return controllers.Trim().Split('\n').Select(x => x.Trim());
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to retrieve controllers: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Pairs a specific device with a specific controller.
+ ///
+ /// The mac address of the controller that will be used to pair.
+ /// The mac address of the device that will be paired.
+ /// The cancellation token.
+ ///
+ /// Returns true or false if the pair was successfully.
+ ///
+ /// Failed to Pair:.
+ public async Task Pair(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
+ try {
+ // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Makes the controller visible to other devices.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Pairs the device with the controller.
+ String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"pair {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
+
+ return result.Contains("Paired: yes");
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to Pair: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Performs a connection of a given controller with a given device.
+ ///
+ /// The mac address of the controller that will be used to make the connection.
+ /// The mac address of the device that will be connected.
+ /// The cancellation token.
+ ///
+ /// Returns true or false if the connection was successfully.
+ ///
+ /// Failed to connect:.
+ public async Task Connect(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
+ try {
+ // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Makes the controller visible to other devices.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Readies the device for pairing.
+ String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"connect {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
+
+ return result.Contains("Connected: yes");
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to connect: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
+ ///
+ /// The mac address of the controller will be used.
+ /// The mac address of the device will be added to the trust list devices.
+ /// The cancellation token.
+ ///
+ /// Returns true or false if the operation was successful.
+ ///
+ /// Failed to add to trust devices list:.
+ public async Task Trust(String controllerAddress, String deviceAddress, CancellationToken cancellationToken = default) {
+ try {
+ // Selects the controller to pair. Once you select the controller, all controller-related commands will apply to it for three minutes.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"select {controllerAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Makes the controller visible to other devices.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Readies the controller for pairing. Remember that you have three minutes after running this command to pair.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "pairable on", null, cancellationToken).ConfigureAwait(false);
+
+ // Sets the device to re-pair automatically when it is turned on, which eliminates the need to pair all over again.
+ String result = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"trust {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ // Hides the controller from other Bluetooth devices. Otherwise, any device that can detect it has access to it, leaving a major security hole.
+ _ = await ProcessRunner.GetProcessOutputAsync(BcCommand, "discoverable off", null, cancellationToken).ConfigureAwait(false);
+
+ return result.Contains("Trusted: yes");
+ } catch(Exception ex) {
+ throw new BluetoothErrorException($"Failed to add to trust devices list: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Displays information about a particular device.
+ ///
+ /// The mac address of the device which info will be retrieved.
+ /// The cancellation token.
+ ///
+ /// Returns the device info.
+ ///
+ /// Failed to retrieve info for {deviceAddress}.
+ public async Task DeviceInfo(String deviceAddress, CancellationToken cancellationToken = default) {
+ String info = await ProcessRunner.GetProcessOutputAsync(BcCommand, $"info {deviceAddress}", null, cancellationToken).ConfigureAwait(false);
+
+ return !String.IsNullOrEmpty(info) ? info : throw new BluetoothErrorException($"Failed to retrieve info for {deviceAddress}");
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/DsiDisplay.cs b/Unosquare.RaspberryIO/Computer/DsiDisplay.cs
index cd49aaf..7583336 100644
--- a/Unosquare.RaspberryIO/Computer/DsiDisplay.cs
+++ b/Unosquare.RaspberryIO/Computer/DsiDisplay.cs
@@ -1,71 +1,63 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using System.Globalization;
- using System.IO;
- using Swan;
-
+using System;
+using System.Globalization;
+using System.IO;
+
+using Swan;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// The Official Raspberry Pi 7-inch touch display from the foundation
+ /// Some docs available here:
+ /// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959.
+ ///
+ public class DsiDisplay : SingletonBase {
+ private const String BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power";
+ private const String BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
+
///
- /// The Official Raspberry Pi 7-inch touch display from the foundation
- /// Some docs available here:
- /// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959.
+ /// Prevents a default instance of the class from being created.
///
- public class DsiDisplay : SingletonBase
- {
- private const string BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power";
- private const string BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
-
- ///
- /// Prevents a default instance of the class from being created.
- ///
- private DsiDisplay()
- {
- // placeholder
- }
-
- ///
- /// Gets a value indicating whether the Pi Foundation Display files are present.
- ///
- ///
- /// true if this instance is present; otherwise, false.
- ///
- public bool IsPresent => File.Exists(BrightnessFilename);
-
- ///
- /// Gets or sets the brightness of the DSI display via filesystem.
- ///
- ///
- /// The brightness.
- ///
- public byte Brightness
- {
- get =>
- IsPresent
- ? byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out var brightness) ? brightness : (byte)0 :
- (byte)0;
- set
- {
- if (IsPresent)
- File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture));
- }
- }
-
- ///
- /// Gets or sets a value indicating whether the backlight of the DSI display on.
- /// This operation is performed via the file system.
- ///
- ///
- /// true if this instance is backlight on; otherwise, false.
- ///
- public bool IsBacklightOn
- {
- get =>
- IsPresent && (int.TryParse(File.ReadAllText(BacklightFilename).Trim(), out var value) &&
- value == 0);
- set
- {
- if (IsPresent)
- File.WriteAllText(BacklightFilename, value ? "0" : "1");
- }
- }
- }
+ private DsiDisplay() {
+ // placeholder
+ }
+
+ ///
+ /// Gets a value indicating whether the Pi Foundation Display files are present.
+ ///
+ ///
+ /// true if this instance is present; otherwise, false.
+ ///
+ public Boolean IsPresent => File.Exists(BrightnessFilename);
+
+ ///
+ /// Gets or sets the brightness of the DSI display via filesystem.
+ ///
+ ///
+ /// The brightness.
+ ///
+ public Byte Brightness {
+ get => this.IsPresent ? System.Byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out Byte brightness) ? brightness : (Byte)0 : (Byte)0;
+ set {
+ if(this.IsPresent) {
+ File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the backlight of the DSI display on.
+ /// This operation is performed via the file system.
+ ///
+ ///
+ /// true if this instance is backlight on; otherwise, false.
+ ///
+ public Boolean IsBacklightOn {
+ get => this.IsPresent && Int32.TryParse(File.ReadAllText(BacklightFilename).Trim(), out Int32 value) && value == 0;
+ set {
+ if(this.IsPresent) {
+ File.WriteAllText(BacklightFilename, value ? "0" : "1");
+ }
+ }
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs b/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs
index 661a357..7b1c801 100644
--- a/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs
+++ b/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs
@@ -1,40 +1,51 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using System.Net;
-
+using System;
+using System.Net;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Represents a Network Adapter.
+ ///
+ public class NetworkAdapterInfo {
///
- /// Represents a Network Adapter.
+ /// Gets the name.
///
- public class NetworkAdapterInfo
- {
- ///
- /// Gets the name.
- ///
- public string Name { get; internal set; }
-
- ///
- /// Gets the IP V4 address.
- ///
- public IPAddress IPv4 { get; internal set; }
-
- ///
- /// Gets the IP V6 address.
- ///
- public IPAddress IPv6 { get; internal set; }
-
- ///
- /// Gets the name of the access point.
- ///
- public string AccessPointName { get; internal set; }
-
- ///
- /// Gets the MAC (Physical) address.
- ///
- public string MacAddress { get; internal set; }
-
- ///
- /// Gets a value indicating whether this instance is wireless.
- ///
- public bool IsWireless { get; internal set; }
- }
+ public String? Name {
+ get; internal set;
+ }
+
+ ///
+ /// Gets the IP V4 address.
+ ///
+ public IPAddress? IPv4 {
+ get; internal set;
+ }
+
+ ///
+ /// Gets the IP V6 address.
+ ///
+ public IPAddress? IPv6 {
+ get; internal set;
+ }
+
+ ///
+ /// Gets the name of the access point.
+ ///
+ public String? AccessPointName {
+ get; internal set;
+ }
+
+ ///
+ /// Gets the MAC (Physical) address.
+ ///
+ public String? MacAddress {
+ get; internal set;
+ }
+
+ ///
+ /// Gets a value indicating whether this instance is wireless.
+ ///
+ public Boolean IsWireless {
+ get; internal set;
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/NetworkSettings.cs b/Unosquare.RaspberryIO/Computer/NetworkSettings.cs
index 9d88ff7..00f4480 100644
--- a/Unosquare.RaspberryIO/Computer/NetworkSettings.cs
+++ b/Unosquare.RaspberryIO/Computer/NetworkSettings.cs
@@ -1,281 +1,271 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using Swan;
- using Swan.Logging;
- using Swan.Net;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Threading.Tasks;
-
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+using Swan;
+using Swan.Logging;
+using Swan.Net;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Represents the network information.
+ ///
+ public class NetworkSettings : SingletonBase {
+ private const String EssidTag = "ESSID:";
+
///
- /// Represents the network information.
+ /// Gets the local machine Host Name.
///
- public class NetworkSettings : SingletonBase
- {
- private const string EssidTag = "ESSID:";
-
- ///
- /// Gets the local machine Host Name.
- ///
- public string HostName => Network.HostName;
-
- ///
- /// Retrieves the wireless networks.
- ///
- /// The adapter.
- /// A list of WiFi networks.
- public Task> RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter });
-
- ///
- /// Retrieves the wireless networks.
- ///
- /// The adapters.
- /// A list of WiFi networks.
- public async Task> RetrieveWirelessNetworks(string[] adapters = null)
- {
- var result = new List();
-
- foreach (var networkAdapter in adapters ?? (await RetrieveAdapters()).Where(x => x.IsWireless).Select(x => x.Name))
- {
- var wirelessOutput = await ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").ConfigureAwait(false);
- var outputLines =
- wirelessOutput.Split('\n')
- .Select(x => x.Trim())
- .Where(x => string.IsNullOrWhiteSpace(x) == false)
- .ToArray();
-
- for (var i = 0; i < outputLines.Length; i++)
- {
- var line = outputLines[i];
-
- if (line.StartsWith(EssidTag) == false) continue;
-
- var network = new WirelessNetworkInfo
- {
- Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty)
- };
-
- while (true)
- {
- if (i + 1 >= outputLines.Length) break;
-
- // should look for two lines before the ESSID acording to the scan
- line = outputLines[i - 2];
-
- if (!line.StartsWith("Quality=")) continue;
- network.Quality = line.Replace("Quality=", string.Empty);
- break;
- }
-
- while (true)
- {
- if (i + 1 >= outputLines.Length) break;
-
- // should look for a line before the ESSID acording to the scan
- line = outputLines[i - 1];
-
- if (!line.StartsWith("Encryption key:")) continue;
- network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on";
- break;
- }
-
- if (result.Any(x => x.Name == network.Name) == false)
- result.Add(network);
- }
- }
-
- return result
- .OrderBy(x => x.Name)
- .ToList();
- }
-
- ///
- /// Setups the wireless network.
- ///
- /// Name of the adapter.
- /// The network ssid.
- /// The password (8 characters as minimum length).
- /// The 2-letter country code in uppercase. Default is US.
- /// True if successful. Otherwise, false.
- public async Task SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US")
- {
- // TODO: Get the country where the device is located to set 'country' param in payload var
- var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
-
- if (!string.IsNullOrWhiteSpace(password) && password.Length < 8)
- throw new InvalidOperationException("The password must be at least 8 characters length.");
-
- payload += string.IsNullOrEmpty(password)
- ? $"network={{\n\tssid=\"{networkSsid}\"\n\tkey_mgmt=NONE\n\t}}\n"
- : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
- try
- {
- File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
- await ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant");
- await ProcessRunner.GetProcessOutputAsync("ifdown", adapterName);
- await ProcessRunner.GetProcessOutputAsync("ifup", adapterName);
- }
- catch (Exception ex)
- {
- ex.Log(nameof(NetworkSettings));
- return false;
- }
-
- return true;
- }
-
- ///
- /// Retrieves the network adapters.
- ///
- /// A list of network adapters.
- public async Task> RetrieveAdapters()
- {
- const string hWaddr = "HWaddr ";
- const string ether = "ether ";
-
- var result = new List();
- var interfacesOutput = await ProcessRunner.GetProcessOutputAsync("ifconfig");
- var wlanOutput = (await ProcessRunner.GetProcessOutputAsync("iwconfig"))
- .Split('\n')
- .Where(x => x.Contains("no wireless extensions.") == false)
- .ToArray();
-
- var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray();
-
- for (var i = 0; i < outputLines.Length; i++)
- {
- // grab the current line
- var line = outputLines[i];
-
- // skip if the line is indented
- if (char.IsLetterOrDigit(line[0]) == false)
- continue;
-
- // Read the line as an adapter
- var adapter = new NetworkAdapterInfo
- {
- Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
- };
-
- // Parse the MAC address in old version of ifconfig; it comes in the first line
- if (line.IndexOf(hWaddr, StringComparison.Ordinal) >= 0)
- {
- var startIndexHwd = line.IndexOf(hWaddr, StringComparison.Ordinal) + hWaddr.Length;
- adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
- }
-
- // Parse the info in lines other than the first
- for (var j = i + 1; j < outputLines.Length; j++)
- {
- // Get the contents of the indented line
- var indentedLine = outputLines[j];
-
- // We have hit the next adapter info
- if (char.IsLetterOrDigit(indentedLine[0]))
- {
- i = j - 1;
- break;
- }
-
- // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
- if (indentedLine.IndexOf(ether, StringComparison.Ordinal) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress))
- {
- var startIndexHwd = indentedLine.IndexOf(ether, StringComparison.Ordinal) + ether.Length;
- adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim();
- }
-
- // Parse the IPv4 Address
- GetIPv4(indentedLine, adapter);
-
- // Parse the IPv6 Address
- GetIPv6(indentedLine, adapter);
-
- // we have hit the end of the output in an indented line
- if (j >= outputLines.Length - 1)
- i = outputLines.Length;
- }
-
- // Retrieve the wireless LAN info
- var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
-
- if (wlanInfo != null)
- {
- adapter.IsWireless = true;
- var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
- if (essidParts.Length >= 2)
- {
- adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim();
- }
- }
-
- // Add the current adapter to the result
- result.Add(adapter);
- }
-
- return result.OrderBy(x => x.Name).ToList();
- }
-
- ///
- /// Retrieves the current network adapter.
- ///
- /// The name of the current network adapter.
- public static async Task GetCurrentAdapterName()
- {
- var result = await ProcessRunner.GetProcessOutputAsync("route").ConfigureAwait(false);
- var defaultLine = result.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
- .FirstOrDefault(l => l.StartsWith("default", StringComparison.OrdinalIgnoreCase));
-
- return defaultLine?.Trim().Substring(defaultLine.LastIndexOf(" ", StringComparison.OrdinalIgnoreCase) + 1);
- }
-
- ///
- /// Retrieves current wireless connected network name.
- ///
- /// The connected network name.
- public Task GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r");
-
- private static void GetIPv4(string indentedLine, NetworkAdapterInfo adapter)
- {
- var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ??
- ParseOutputTagFromLine(indentedLine, "inet ");
-
- if (addressText == null) return;
- if (IPAddress.TryParse(addressText, out var outValue))
- adapter.IPv4 = outValue;
- }
-
- private static void GetIPv6(string indentedLine, NetworkAdapterInfo adapter)
- {
- var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ??
- ParseOutputTagFromLine(indentedLine, "inet6 ");
-
- if (addressText == null) return;
-
- if (IPAddress.TryParse(addressText, out var outValue))
- adapter.IPv6 = outValue;
- }
-
- private static string ParseOutputTagFromLine(string indentedLine, string tagName)
- {
- if (indentedLine.IndexOf(tagName, StringComparison.Ordinal) < 0)
- return null;
-
- var startIndex = indentedLine.IndexOf(tagName, StringComparison.Ordinal) + tagName.Length;
- var builder = new StringBuilder(1024);
- for (var c = startIndex; c < indentedLine.Length; c++)
- {
- var currentChar = indentedLine[c];
- if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar))
- break;
-
- builder.Append(currentChar);
- }
-
- return builder.ToString();
- }
- }
+ public String HostName => Network.HostName;
+
+ ///
+ /// Retrieves the wireless networks.
+ ///
+ /// The adapter.
+ /// A list of WiFi networks.
+ public Task> RetrieveWirelessNetworks(String adapter) => this.RetrieveWirelessNetworks(new[] { adapter });
+
+ ///
+ /// Retrieves the wireless networks.
+ ///
+ /// The adapters.
+ /// A list of WiFi networks.
+ public async Task> RetrieveWirelessNetworks(String[]? adapters = null) {
+ List result = new List();
+
+ foreach(String? networkAdapter in adapters ?? (await this.RetrieveAdapters()).Where(x => x.IsWireless).Select(x => x.Name)) {
+ String wirelessOutput = await ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").ConfigureAwait(false);
+ String[] outputLines = wirelessOutput.Split('\n').Select(x => x.Trim()).Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray();
+
+ for(Int32 i = 0; i < outputLines.Length; i++) {
+ String line = outputLines[i];
+
+ if(line.StartsWith(EssidTag) == false) {
+ continue;
+ }
+
+ WirelessNetworkInfo network = new WirelessNetworkInfo {
+ Name = line.Replace(EssidTag, String.Empty).Replace("\"", String.Empty)
+ };
+
+ while(true) {
+ if(i + 1 >= outputLines.Length) {
+ break;
+ }
+
+ // should look for two lines before the ESSID acording to the scan
+ line = outputLines[i - 2];
+
+ if(!line.StartsWith("Quality=")) {
+ continue;
+ }
+
+ network.Quality = line.Replace("Quality=", String.Empty);
+ break;
+ }
+
+ while(true) {
+ if(i + 1 >= outputLines.Length) {
+ break;
+ }
+
+ // should look for a line before the ESSID acording to the scan
+ line = outputLines[i - 1];
+
+ if(!line.StartsWith("Encryption key:")) {
+ continue;
+ }
+
+ network.IsEncrypted = line.Replace("Encryption key:", String.Empty).Trim() == "on";
+ break;
+ }
+
+ if(result.Any(x => x.Name == network.Name) == false) {
+ result.Add(network);
+ }
+ }
+ }
+
+ return result
+ .OrderBy(x => x.Name)
+ .ToList();
+ }
+
+ ///
+ /// Setups the wireless network.
+ ///
+ /// Name of the adapter.
+ /// The network ssid.
+ /// The password (8 characters as minimum length).
+ /// The 2-letter country code in uppercase. Default is US.
+ /// True if successful. Otherwise, false.
+ public async Task SetupWirelessNetwork(String adapterName, String networkSsid, String? password = null, String countryCode = "US") {
+ // TODO: Get the country where the device is located to set 'country' param in payload var
+ String payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
+
+ if(!String.IsNullOrWhiteSpace(password) && password.Length < 8) {
+ throw new InvalidOperationException("The password must be at least 8 characters length.");
+ }
+
+ payload += String.IsNullOrEmpty(password)
+ ? $"network={{\n\tssid=\"{networkSsid}\"\n\tkey_mgmt=NONE\n\t}}\n"
+ : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
+ try {
+ File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
+ _ = await ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant");
+ _ = await ProcessRunner.GetProcessOutputAsync("ifdown", adapterName);
+ _ = await ProcessRunner.GetProcessOutputAsync("ifup", adapterName);
+ } catch(Exception ex) {
+ ex.Log(nameof(NetworkSettings));
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Retrieves the network adapters.
+ ///
+ /// A list of network adapters.
+ public async Task> RetrieveAdapters() {
+ const String hWaddr = "HWaddr ";
+ const String ether = "ether ";
+
+ List result = new List();
+ String interfacesOutput = await ProcessRunner.GetProcessOutputAsync("ifconfig");
+ String[] wlanOutput = (await ProcessRunner.GetProcessOutputAsync("iwconfig")).Split('\n').Where(x => x.Contains("no wireless extensions.") == false).ToArray();
+
+ String[] outputLines = interfacesOutput.Split('\n').Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray();
+
+ for(Int32 i = 0; i < outputLines.Length; i++) {
+ // grab the current line
+ String line = outputLines[i];
+
+ // skip if the line is indented
+ if(Char.IsLetterOrDigit(line[0]) == false) {
+ continue;
+ }
+
+ // Read the line as an adapter
+ NetworkAdapterInfo adapter = new NetworkAdapterInfo {
+ Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
+ };
+
+ // Parse the MAC address in old version of ifconfig; it comes in the first line
+ if(line.IndexOf(hWaddr, StringComparison.Ordinal) >= 0) {
+ Int32 startIndexHwd = line.IndexOf(hWaddr, StringComparison.Ordinal) + hWaddr.Length;
+ adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
+ }
+
+ // Parse the info in lines other than the first
+ for(Int32 j = i + 1; j < outputLines.Length; j++) {
+ // Get the contents of the indented line
+ String indentedLine = outputLines[j];
+
+ // We have hit the next adapter info
+ if(Char.IsLetterOrDigit(indentedLine[0])) {
+ i = j - 1;
+ break;
+ }
+
+ // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
+ if(indentedLine.IndexOf(ether, StringComparison.Ordinal) >= 0 && String.IsNullOrWhiteSpace(adapter.MacAddress)) {
+ Int32 startIndexHwd = indentedLine.IndexOf(ether, StringComparison.Ordinal) + ether.Length;
+ adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim();
+ }
+
+ // Parse the IPv4 Address
+ GetIPv4(indentedLine, adapter);
+
+ // Parse the IPv6 Address
+ GetIPv6(indentedLine, adapter);
+
+ // we have hit the end of the output in an indented line
+ if(j >= outputLines.Length - 1) {
+ i = outputLines.Length;
+ }
+ }
+
+ // Retrieve the wireless LAN info
+ String wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
+
+ if(wlanInfo != null) {
+ adapter.IsWireless = true;
+ String[] essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
+ if(essidParts.Length >= 2) {
+ adapter.AccessPointName = essidParts[1].Replace("\"", String.Empty).Trim();
+ }
+ }
+
+ // Add the current adapter to the result
+ result.Add(adapter);
+ }
+
+ return result.OrderBy(x => x.Name).ToList();
+ }
+
+ ///
+ /// Retrieves the current network adapter.
+ ///
+ /// The name of the current network adapter.
+ public static async Task GetCurrentAdapterName() {
+ String result = await ProcessRunner.GetProcessOutputAsync("route").ConfigureAwait(false);
+ String defaultLine = result.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(l => l.StartsWith("default", StringComparison.OrdinalIgnoreCase));
+
+ return defaultLine?.Trim().Substring(defaultLine.LastIndexOf(" ", StringComparison.OrdinalIgnoreCase) + 1);
+ }
+
+ ///
+ /// Retrieves current wireless connected network name.
+ ///
+ /// The connected network name.
+ public Task GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r");
+
+ private static void GetIPv4(String indentedLine, NetworkAdapterInfo adapter) {
+ String? addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
+
+ if(addressText == null) {
+ return;
+ }
+
+ if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
+ adapter.IPv4 = outValue;
+ }
+ }
+
+ private static void GetIPv6(String indentedLine, NetworkAdapterInfo adapter) {
+ String? addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
+
+ if(addressText == null) {
+ return;
+ }
+
+ if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
+ adapter.IPv6 = outValue;
+ }
+ }
+
+ private static String? ParseOutputTagFromLine(String indentedLine, String tagName) {
+ if(indentedLine.IndexOf(tagName, StringComparison.Ordinal) < 0) {
+ return null;
+ }
+
+ Int32 startIndex = indentedLine.IndexOf(tagName, StringComparison.Ordinal) + tagName.Length;
+ StringBuilder builder = new StringBuilder(1024);
+ for(Int32 c = startIndex; c < indentedLine.Length; c++) {
+ Char currentChar = indentedLine[c];
+ if(!Char.IsPunctuation(currentChar) && !Char.IsLetterOrDigit(currentChar)) {
+ break;
+ }
+
+ _ = builder.Append(currentChar);
+ }
+
+ return builder.ToString();
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/OsInfo.cs b/Unosquare.RaspberryIO/Computer/OsInfo.cs
index d8d85cd..6675393 100644
--- a/Unosquare.RaspberryIO/Computer/OsInfo.cs
+++ b/Unosquare.RaspberryIO/Computer/OsInfo.cs
@@ -1,46 +1,58 @@
-namespace Unosquare.RaspberryIO.Computer
-{
+using System;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Represents the OS Information.
+ ///
+ public class OsInfo {
///
- /// Represents the OS Information.
+ /// System name.
///
- public class OsInfo
- {
- ///
- /// System name.
- ///
- public string SysName { get; set; }
-
- ///
- /// Node name.
- ///
- public string NodeName { get; set; }
-
- ///
- /// Release level.
- ///
- public string Release { get; set; }
-
- ///
- /// Version level.
- ///
- public string Version { get; set; }
-
- ///
- /// Hardware level.
- ///
- public string Machine { get; set; }
-
- ///
- /// Domain name.
- ///
- public string DomainName { get; set; }
-
- ///
- /// Returns a that represents this instance.
- ///
- ///
- /// A that represents this instance.
- ///
- public override string ToString() => $"{SysName} {Release} {Version}";
- }
+ public String? SysName {
+ get; set;
+ }
+
+ ///
+ /// Node name.
+ ///
+ public String? NodeName {
+ get; set;
+ }
+
+ ///
+ /// Release level.
+ ///
+ public String? Release {
+ get; set;
+ }
+
+ ///
+ /// Version level.
+ ///
+ public String? Version {
+ get; set;
+ }
+
+ ///
+ /// Hardware level.
+ ///
+ public String? Machine {
+ get; set;
+ }
+
+ ///
+ /// Domain name.
+ ///
+ public String? DomainName {
+ get; set;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override String ToString() => $"{this.SysName} {this.Release} {this.Version}";
+ }
}
diff --git a/Unosquare.RaspberryIO/Computer/PiVersion.cs b/Unosquare.RaspberryIO/Computer/PiVersion.cs
index c748509..7c48f50 100644
--- a/Unosquare.RaspberryIO/Computer/PiVersion.cs
+++ b/Unosquare.RaspberryIO/Computer/PiVersion.cs
@@ -1,394 +1,388 @@
-namespace Unosquare.RaspberryIO.Computer
-{
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Defines the board revision codes of the different versions of the Raspberry Pi
+ /// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/.
+ /// https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md.
+ ///
+ public enum PiVersion {
///
- /// Defines the board revision codes of the different versions of the Raspberry Pi
- /// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/.
- /// https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md.
+ /// The unknown version
///
- public enum PiVersion
- {
- ///
- /// The unknown version
- ///
- Unknown = 0,
-
- ///
- /// The model B Rev1
- ///
- ModelBRev1 = 0x0002,
-
- ///
- /// The model B Rev1 ECN0001
- ///
- ModelBRev1ECN0001 = 0x0003,
-
- ///
- /// The model B Rev2 Sony
- ///
- ModelBRev2x04 = 0x0004,
-
- ///
- /// The model B Rev2 Qisda
- ///
- ModelBRev2x05 = 0x0005,
-
- ///
- /// The model B Rev2 Egoman
- ///
- ModelBRev2x06 = 0x0006,
-
- ///
- /// The model A Rev2 Egoman
- ///
- ModelAx07 = 0x0007,
-
- ///
- /// The model A Rev2 Sony
- ///
- ModelAx08 = 0x0008,
-
- ///
- /// The model A Rev2 Qisda
- ///
- ModelAx09 = 0x0009,
-
- ///
- /// The model B Rev2 (512MB) Egoman
- ///
- ModelBRev2x0d = 0x000d,
-
- ///
- /// The model B Rev2 (512MB) Sony
- ///
- ModelBRev2x0e = 0x000e,
-
- ///
- /// The model B Rev2 (512MB) Egoman
- ///
- ModelBRev2x0f = 0x000f,
-
- ///
- /// The model B+ Rev1 Sony
- ///
- ModelBPlus0x10 = 0x0010,
-
- ///
- /// The compute module 1 Sony
- ///
- ComputeModule0x11 = 0x0011,
-
- ///
- /// The model A+ Rev1.1 Sony
- ///
- ModelAPlus0x12 = 0x0012,
-
- ///
- /// The model B+ Rev1.2 Embest
- ///
- ModelBPlus0x13 = 0x0013,
-
- ///
- /// The compute module 1 Embest
- ///
- ComputeModule0x14 = 0x0014,
-
- ///
- /// The model A+ Rev1.1 Embest
- ///
- ModelAPlus0x15 = 0x0015,
-
- ///
- /// The model A+ Rev1.1 (512MB) Sony
- ///
- ModelAPlus1v1Sony = 900021,
-
- ///
- /// The model B+ Rev1.2 sony
- ///
- ModelBPlus1v2Sony = 900032,
-
- ///
- /// The Pi Zero Rev1.2 Sony
- ///
- PiZero1v2 = 0x900092,
-
- ///
- /// The Pi Zero Rev1.3 SOny
- ///
- PiZero1v3 = 0x900093,
-
- ///
- /// The Pi Zero W Rev1.1
- ///
- PiZeroW = 0x9000c1,
-
- ///
- /// The Pi 3 model A+ Sony
- ///
- Pi3ModelAPlus = 0x9020e0,
-
- ///
- /// The Pi Zero Rev1.2 Embest
- ///
- PiZero1v2Embest = 0x920092,
-
- ///
- /// The Pi Zero Rev1.3 Embest
- ///
- PiZero1v3Embest = 0x920093,
-
- ///
- /// The Pi 2 model B Rev1.0 Sony
- ///
- Pi2ModelB1v0Sony = 0xa01040,
-
- ///
- /// The Pi 2 model B Rev1.1 Sony
- ///
- Pi2ModelB1v1Sony = 0xa01041,
-
- ///
- /// The Pi 3 model B Rev1.2 Sony
- ///
- Pi3ModelBSony = 0xa02082,
-
- ///
- /// The compute module 3 Rev1.0 Sony
- ///
- ComputeModule3Sony = 0xa020a0,
-
- ///
- /// The Pi 3 model B+ Rev1.3 Sony
- ///
- Pi3ModelBPlusSony = 0xa020d3,
-
- ///
- /// The Pi 2 model B Rev1.1 Embest
- ///
- Pi2ModelB1v1Embest = 0xa21041,
-
- ///
- /// The Pi 2 model B Rev1.2 Embest
- ///
- Pi2ModelB1v2 = 0xa22042,
-
- ///
- /// The Pi 3 model B Rev1.2 Embest
- ///
- Pi3ModelBEmbest = 0xa22082,
-
- ///
- /// The compute module 3 Rev1.0 Embest
- ///
- ComputeModule3Embest = 0xa220a0,
-
- ///
- /// The Pi 3 model B Rev1.2 Sony Japan
- ///
- Pi3ModelBSonyJapan = 0xa32082,
-
- ///
- /// The Pi 3 model B Rev1.2 Stadium
- ///
- Pi3ModelBStadium = 0xa52082,
-
- ///
- /// The compute module 3+ Rev 1.0 Sony
- ///
- ComputeModule3PlusSony = 0xa02100,
-
- ///
- /// The Pi 4 model B 1GB, Sony
- ///
- Pi4ModelB1Gb = 0xa03111,
-
- ///
- /// The Pi 4 model B 2GB, Sony
- ///
- Pi4ModelB2Gb = 0xb03111,
-
- ///
- /// The Pi 4 model B 4GB, Sony
- ///
- Pi4ModelB4Gb = 0xc03111,
- }
-
+ Unknown = 0,
+
///
- /// Defines the board model accordingly to new-style revision codes.
+ /// The model B Rev1
///
- public enum BoardModel
- {
- ///
- /// Model A
- ///
- ModelA = 0,
-
- ///
- /// Model B
- ///
- ModelB = 1,
-
- ///
- /// Model A+
- ///
- ModelAPlus = 2,
-
- ///
- /// Model B+
- ///
- ModelBPlus = 3,
-
- ///
- /// Model 2B
- ///
- Model2B = 4,
-
- ///
- /// Alpha (early prototype)
- ///
- Alpha = 5,
-
- ///
- /// Compute Module 1
- ///
- CM1 = 6,
-
- ///
- /// Model 3B
- ///
- Model3B = 8,
-
- ///
- /// Model Zero
- ///
- Zero = 9,
-
- ///
- /// Compute Module 3
- ///
- CM3 = 0xa,
-
- ///
- /// Model Zero W
- ///
- ZeroW = 0xc,
-
- ///
- /// Model 3B+
- ///
- Model3BPlus = 0xd,
-
- ///
- /// Model 3A+
- ///
- Model3APlus = 0xe,
-
- ///
- /// Reserved (Internal use only)
- ///
- InternalUse = 0xf,
-
- ///
- /// Compute Module 3+
- ///
- CM3Plus = 0x10,
-
- ///
- /// Model 4B
- ///
- Model4B = 0x11,
- }
-
+ ModelBRev1 = 0x0002,
+
///
- /// Defines the processor model accordingly to new-style revision codes.
+ /// The model B Rev1 ECN0001
///
- public enum ProcessorModel
- {
- ///
- /// The BCMM2835 processor.
- ///
- BCM2835,
-
- ///
- /// The BCMM2836 processor.
- ///
- BCM2836,
-
- ///
- /// The BCMM2837 processor.
- ///
- BCM2837,
-
- ///
- /// The BCM2711 processor.
- ///
- BCM2711,
- }
-
+ ModelBRev1ECN0001 = 0x0003,
+
///
- /// Defines the manufacturer accordingly to new-style revision codes.
+ /// The model B Rev2 Sony
///
- public enum Manufacturer
- {
- ///
- /// Sony UK
- ///
- SonyUK,
-
- ///
- /// Egoman
- ///
- Egoman,
-
- ///
- /// Embest
- ///
- Embest,
-
- ///
- /// Sony Japan
- ///
- SonyJapan,
-
- ///
- /// Embest
- ///
- Embest2,
-
- ///
- /// Stadium
- ///
- Stadium,
- }
-
+ ModelBRev2x04 = 0x0004,
+
///
- /// Defines the memory size accordingly to new-style revision codes.
+ /// The model B Rev2 Qisda
///
- public enum MemorySize
- {
- ///
- /// 256 MB
- ///
- Memory256,
-
- ///
- /// 512 MB
- ///
- Memory512,
-
- ///
- /// 1 GB
- ///
- Memory1024,
-
- ///
- /// 2 GB
- ///
- Memory2048,
-
- ///
- /// 4 GB
- ///
- Memory4096,
- }
+ ModelBRev2x05 = 0x0005,
+
+ ///
+ /// The model B Rev2 Egoman
+ ///
+ ModelBRev2x06 = 0x0006,
+
+ ///
+ /// The model A Rev2 Egoman
+ ///
+ ModelAx07 = 0x0007,
+
+ ///
+ /// The model A Rev2 Sony
+ ///
+ ModelAx08 = 0x0008,
+
+ ///
+ /// The model A Rev2 Qisda
+ ///
+ ModelAx09 = 0x0009,
+
+ ///
+ /// The model B Rev2 (512MB) Egoman
+ ///
+ ModelBRev2x0d = 0x000d,
+
+ ///
+ /// The model B Rev2 (512MB) Sony
+ ///
+ ModelBRev2x0e = 0x000e,
+
+ ///
+ /// The model B Rev2 (512MB) Egoman
+ ///
+ ModelBRev2x0f = 0x000f,
+
+ ///
+ /// The model B+ Rev1 Sony
+ ///
+ ModelBPlus0x10 = 0x0010,
+
+ ///
+ /// The compute module 1 Sony
+ ///
+ ComputeModule0x11 = 0x0011,
+
+ ///
+ /// The model A+ Rev1.1 Sony
+ ///
+ ModelAPlus0x12 = 0x0012,
+
+ ///
+ /// The model B+ Rev1.2 Embest
+ ///
+ ModelBPlus0x13 = 0x0013,
+
+ ///
+ /// The compute module 1 Embest
+ ///
+ ComputeModule0x14 = 0x0014,
+
+ ///
+ /// The model A+ Rev1.1 Embest
+ ///
+ ModelAPlus0x15 = 0x0015,
+
+ ///
+ /// The model A+ Rev1.1 (512MB) Sony
+ ///
+ ModelAPlus1v1Sony = 900021,
+
+ ///
+ /// The model B+ Rev1.2 sony
+ ///
+ ModelBPlus1v2Sony = 900032,
+
+ ///
+ /// The Pi Zero Rev1.2 Sony
+ ///
+ PiZero1v2 = 0x900092,
+
+ ///
+ /// The Pi Zero Rev1.3 SOny
+ ///
+ PiZero1v3 = 0x900093,
+
+ ///
+ /// The Pi Zero W Rev1.1
+ ///
+ PiZeroW = 0x9000c1,
+
+ ///
+ /// The Pi 3 model A+ Sony
+ ///
+ Pi3ModelAPlus = 0x9020e0,
+
+ ///
+ /// The Pi Zero Rev1.2 Embest
+ ///
+ PiZero1v2Embest = 0x920092,
+
+ ///
+ /// The Pi Zero Rev1.3 Embest
+ ///
+ PiZero1v3Embest = 0x920093,
+
+ ///
+ /// The Pi 2 model B Rev1.0 Sony
+ ///
+ Pi2ModelB1v0Sony = 0xa01040,
+
+ ///
+ /// The Pi 2 model B Rev1.1 Sony
+ ///
+ Pi2ModelB1v1Sony = 0xa01041,
+
+ ///
+ /// The Pi 3 model B Rev1.2 Sony
+ ///
+ Pi3ModelBSony = 0xa02082,
+
+ ///
+ /// The compute module 3 Rev1.0 Sony
+ ///
+ ComputeModule3Sony = 0xa020a0,
+
+ ///
+ /// The Pi 3 model B+ Rev1.3 Sony
+ ///
+ Pi3ModelBPlusSony = 0xa020d3,
+
+ ///
+ /// The Pi 2 model B Rev1.1 Embest
+ ///
+ Pi2ModelB1v1Embest = 0xa21041,
+
+ ///
+ /// The Pi 2 model B Rev1.2 Embest
+ ///
+ Pi2ModelB1v2 = 0xa22042,
+
+ ///
+ /// The Pi 3 model B Rev1.2 Embest
+ ///
+ Pi3ModelBEmbest = 0xa22082,
+
+ ///
+ /// The compute module 3 Rev1.0 Embest
+ ///
+ ComputeModule3Embest = 0xa220a0,
+
+ ///
+ /// The Pi 3 model B Rev1.2 Sony Japan
+ ///
+ Pi3ModelBSonyJapan = 0xa32082,
+
+ ///
+ /// The Pi 3 model B Rev1.2 Stadium
+ ///
+ Pi3ModelBStadium = 0xa52082,
+
+ ///
+ /// The compute module 3+ Rev 1.0 Sony
+ ///
+ ComputeModule3PlusSony = 0xa02100,
+
+ ///
+ /// The Pi 4 model B 1GB, Sony
+ ///
+ Pi4ModelB1Gb = 0xa03111,
+
+ ///
+ /// The Pi 4 model B 2GB, Sony
+ ///
+ Pi4ModelB2Gb = 0xb03111,
+
+ ///
+ /// The Pi 4 model B 4GB, Sony
+ ///
+ Pi4ModelB4Gb = 0xc03111,
+ }
+
+ ///
+ /// Defines the board model accordingly to new-style revision codes.
+ ///
+ public enum BoardModel {
+ ///
+ /// Model A
+ ///
+ ModelA = 0,
+
+ ///
+ /// Model B
+ ///
+ ModelB = 1,
+
+ ///
+ /// Model A+
+ ///
+ ModelAPlus = 2,
+
+ ///
+ /// Model B+
+ ///
+ ModelBPlus = 3,
+
+ ///
+ /// Model 2B
+ ///
+ Model2B = 4,
+
+ ///
+ /// Alpha (early prototype)
+ ///
+ Alpha = 5,
+
+ ///
+ /// Compute Module 1
+ ///
+ CM1 = 6,
+
+ ///
+ /// Model 3B
+ ///
+ Model3B = 8,
+
+ ///
+ /// Model Zero
+ ///
+ Zero = 9,
+
+ ///
+ /// Compute Module 3
+ ///
+ CM3 = 0xa,
+
+ ///
+ /// Model Zero W
+ ///
+ ZeroW = 0xc,
+
+ ///
+ /// Model 3B+
+ ///
+ Model3BPlus = 0xd,
+
+ ///
+ /// Model 3A+
+ ///
+ Model3APlus = 0xe,
+
+ ///
+ /// Reserved (Internal use only)
+ ///
+ InternalUse = 0xf,
+
+ ///
+ /// Compute Module 3+
+ ///
+ CM3Plus = 0x10,
+
+ ///
+ /// Model 4B
+ ///
+ Model4B = 0x11,
+ }
+
+ ///
+ /// Defines the processor model accordingly to new-style revision codes.
+ ///
+ public enum ProcessorModel {
+ ///
+ /// The BCMM2835 processor.
+ ///
+ BCM2835,
+
+ ///
+ /// The BCMM2836 processor.
+ ///
+ BCM2836,
+
+ ///
+ /// The BCMM2837 processor.
+ ///
+ BCM2837,
+
+ ///
+ /// The BCM2711 processor.
+ ///
+ BCM2711,
+ }
+
+ ///
+ /// Defines the manufacturer accordingly to new-style revision codes.
+ ///
+ public enum Manufacturer {
+ ///
+ /// Sony UK
+ ///
+ SonyUK,
+
+ ///
+ /// Egoman
+ ///
+ Egoman,
+
+ ///
+ /// Embest
+ ///
+ Embest,
+
+ ///
+ /// Sony Japan
+ ///
+ SonyJapan,
+
+ ///
+ /// Embest
+ ///
+ Embest2,
+
+ ///
+ /// Stadium
+ ///
+ Stadium,
+ }
+
+ ///
+ /// Defines the memory size accordingly to new-style revision codes.
+ ///
+ public enum MemorySize {
+ ///
+ /// 256 MB
+ ///
+ Memory256,
+
+ ///
+ /// 512 MB
+ ///
+ Memory512,
+
+ ///
+ /// 1 GB
+ ///
+ Memory1024,
+
+ ///
+ /// 2 GB
+ ///
+ Memory2048,
+
+ ///
+ /// 4 GB
+ ///
+ Memory4096,
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Computer/SystemInfo.cs b/Unosquare.RaspberryIO/Computer/SystemInfo.cs
index fdce8ba..0f65de5 100644
--- a/Unosquare.RaspberryIO/Computer/SystemInfo.cs
+++ b/Unosquare.RaspberryIO/Computer/SystemInfo.cs
@@ -1,390 +1,383 @@
-namespace Unosquare.RaspberryIO.Computer
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using Abstractions;
- using Native;
- using Swan;
- using Swan.DependencyInjection;
-
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+using Swan;
+using Swan.DependencyInjection;
+
+using Unosquare.RaspberryIO.Abstractions;
+using Unosquare.RaspberryIO.Native;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Retrieves the RaspberryPI System Information.
+ ///
+ /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html.
+ ///
+ public sealed class SystemInfo : SingletonBase {
+ private const String CpuInfoFilePath = "/proc/cpuinfo";
+ private const String MemInfoFilePath = "/proc/meminfo";
+ private const String UptimeFilePath = "/proc/uptime";
+
+ private const Int32 NewStyleCodesMask = 0x800000;
+
+ private BoardModel _boardModel;
+ private ProcessorModel _processorModel;
+ private Manufacturer _manufacturer;
+ private MemorySize _memorySize;
+
///
- /// Retrieves the RaspberryPI System Information.
- ///
- /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html.
+ /// Prevents a default instance of the class from being created.
///
- public sealed class SystemInfo : SingletonBase
- {
- private const string CpuInfoFilePath = "/proc/cpuinfo";
- private const string MemInfoFilePath = "/proc/meminfo";
- private const string UptimeFilePath = "/proc/uptime";
-
- private const int NewStyleCodesMask = 0x800000;
-
- private BoardModel _boardModel;
- private ProcessorModel _processorModel;
- private Manufacturer _manufacturer;
- private MemorySize _memorySize;
-
- ///
- /// Prevents a default instance of the class from being created.
- ///
- /// Could not initialize the GPIO controller.
- private SystemInfo()
- {
- #region Obtain and format a property dictionary
-
- var properties =
- typeof(SystemInfo)
- .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
- .Where(
- p =>
- p.CanWrite && p.CanRead &&
- (p.PropertyType == typeof(string) || p.PropertyType == typeof(string[])))
- .ToArray();
- var propDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- foreach (var prop in properties)
- {
- propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop;
- }
-
- #endregion
-
- #region Extract CPU information
-
- if (File.Exists(CpuInfoFilePath))
- {
- var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
-
- foreach (var line in cpuInfoLines)
- {
- var lineParts = line.Split(new[] { ':' }, 2);
- if (lineParts.Length != 2)
- continue;
-
- var propertyKey = lineParts[0].Trim().Replace(" ", string.Empty);
- var propertyStringValue = lineParts[1].Trim();
-
- if (!propDictionary.ContainsKey(propertyKey)) continue;
-
- var property = propDictionary[propertyKey];
- if (property.PropertyType == typeof(string))
- {
- property.SetValue(this, propertyStringValue);
- }
- else if (property.PropertyType == typeof(string[]))
- {
- var propertyArrayValue = propertyStringValue.Split(' ');
- property.SetValue(this, propertyArrayValue);
- }
- }
- }
-
- #endregion
-
- ExtractMemoryInfo();
- ExtractBoardVersion();
- ExtractOS();
- }
-
- ///
- /// Gets the library version.
- ///
- public Version LibraryVersion { get; private set; }
-
- ///
- /// Gets the OS information.
- ///
- ///
- /// The os information.
- ///
- public OsInfo OperatingSystem { get; set; }
-
- ///
- /// Gets the Raspberry Pi version.
- ///
- public PiVersion RaspberryPiVersion { get; set; }
-
- ///
- /// Gets the board revision (1 or 2).
- ///
- ///
- /// The wiring pi board revision.
- ///
- public int BoardRevision { get; set; }
-
- ///
- /// Gets the number of processor cores.
- ///
- public int ProcessorCount => int.TryParse(Processor, out var outIndex) ? outIndex + 1 : 0;
-
- ///
- /// Gets the installed ram in bytes.
- ///
- public int InstalledRam { get; private set; }
-
- ///
- /// Gets a value indicating whether this CPU is little endian.
- ///
- public bool IsLittleEndian => BitConverter.IsLittleEndian;
-
- ///
- /// Gets the CPU model name.
- ///
- public string ModelName { get; private set; }
-
- ///
- /// Gets a list of supported CPU features.
- ///
- public string[] Features { get; private set; }
-
- ///
- /// Gets the CPU implementer hex code.
- ///
- public string CpuImplementer { get; private set; }
-
- ///
- /// Gets the CPU architecture code.
- ///
- public string CpuArchitecture { get; private set; }
-
- ///
- /// Gets the CPU variant code.
- ///
- public string CpuVariant { get; private set; }
-
- ///
- /// Gets the CPU part code.
- ///
- public string CpuPart { get; private set; }
-
- ///
- /// Gets the CPU revision code.
- ///
- public string CpuRevision { get; private set; }
-
- ///
- /// Gets the hardware model number.
- ///
- public string Hardware { get; private set; }
-
- ///
- /// Gets the hardware revision number.
- ///
- public string Revision { get; private set; }
-
- ///
- /// Gets the revision number (accordingly to new-style revision codes).
- ///
- public int RevisionNumber { get; set; }
-
- ///
- /// Gets the board model (accordingly to new-style revision codes).
- ///
- /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
- public BoardModel BoardModel =>
- NewStyleRevisionCodes ?
- _boardModel :
- throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead.");
-
- ///
- /// Gets processor model (accordingly to new-style revision codes).
- ///
- /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
- public ProcessorModel ProcessorModel =>
- NewStyleRevisionCodes ?
- _processorModel :
- throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead.");
-
- ///
- /// Gets the manufacturer of the board (accordingly to new-style revision codes).
- ///
- /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
- public Manufacturer Manufacturer =>
- NewStyleRevisionCodes ?
- _manufacturer :
- throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead.");
-
- ///
- /// Gets the size of the memory (accordingly to new-style revision codes).
- ///
- /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
- public MemorySize MemorySize =>
- NewStyleRevisionCodes ?
- _memorySize :
- throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead.");
-
- ///
- /// Gets the serial number.
- ///
- public string Serial { get; private set; }
-
- ///
- /// Gets the system up-time (in seconds).
- ///
- public double Uptime
- {
- get
- {
- try
- {
- if (File.Exists(UptimeFilePath) == false) return 0;
- var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (parts.Length >= 1 && float.TryParse(parts[0], out var result))
- return result;
- }
- catch
- {
- /* Ignore */
- }
-
- return 0;
- }
- }
-
- ///
- /// Gets the uptime in TimeSpan.
- ///
- public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime);
-
- ///
- /// Indicates if the board uses the new-style revision codes.
- ///
- private bool NewStyleRevisionCodes { get; set; }
-
- ///
- /// Placeholder for processor index.
- ///
- private string Processor { get; set; }
-
- ///
- /// Returns a that represents this instance.
- ///
- ///
- /// A that represents this instance.
- ///
- public override string ToString()
- {
- var properties = typeof(SystemInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public)
- .Where(p => p.CanRead && (
- p.PropertyType == typeof(string) ||
- p.PropertyType == typeof(string[]) ||
- p.PropertyType == typeof(int) ||
- p.PropertyType == typeof(bool) ||
- p.PropertyType == typeof(TimeSpan)))
- .ToArray();
-
- var propertyValues2 = new List
- {
- "System Information",
- $"\t{nameof(LibraryVersion),-22}: {LibraryVersion}",
- $"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}",
- };
-
- foreach (var property in properties)
- {
- if (property.PropertyType != typeof(string[]))
- {
- propertyValues2.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
- }
- else if (property.GetValue(this) is string[] allValues)
- {
- var concatValues = string.Join(" ", allValues);
- propertyValues2.Add($"\t{property.Name,-22}: {concatValues}");
- }
- }
-
- return string.Join(Environment.NewLine, propertyValues2.ToArray());
- }
-
- private void ExtractOS()
- {
- try
- {
- Standard.Uname(out var unameInfo);
-
- OperatingSystem = new OsInfo
- {
- DomainName = unameInfo.DomainName,
- Machine = unameInfo.Machine,
- NodeName = unameInfo.NodeName,
- Release = unameInfo.Release,
- SysName = unameInfo.SysName,
- Version = unameInfo.Version,
- };
- }
- catch
- {
- OperatingSystem = new OsInfo();
- }
- }
-
- private void ExtractBoardVersion()
- {
- var hasSysInfo = DependencyContainer.Current.CanResolve();
-
- try
- {
- if (string.IsNullOrWhiteSpace(Revision) == false &&
- int.TryParse(
- Revision.ToUpperInvariant(),
- NumberStyles.HexNumber,
- CultureInfo.InvariantCulture,
- out var boardVersion))
- {
- RaspberryPiVersion = PiVersion.Unknown;
- if (Enum.IsDefined(typeof(PiVersion), boardVersion))
- RaspberryPiVersion = (PiVersion)boardVersion;
-
- if ((boardVersion & NewStyleCodesMask) == NewStyleCodesMask)
- {
- NewStyleRevisionCodes = true;
- RevisionNumber = boardVersion & 0xF;
- _boardModel = (BoardModel)((boardVersion >> 4) & 0xFF);
- _processorModel = (ProcessorModel)((boardVersion >> 12) & 0xF);
- _manufacturer = (Manufacturer)((boardVersion >> 16) & 0xF);
- _memorySize = (MemorySize)((boardVersion >> 20) & 0x7);
- }
- }
-
- if (hasSysInfo)
- BoardRevision = (int)DependencyContainer.Current.Resolve().BoardRevision;
- }
- catch
- {
- /* Ignore */
- }
-
- if (hasSysInfo)
- LibraryVersion = DependencyContainer.Current.Resolve().LibraryVersion;
- }
-
- private void ExtractMemoryInfo()
- {
- if (!File.Exists(MemInfoFilePath)) return;
-
- var memInfoLines = File.ReadAllLines(MemInfoFilePath);
-
- foreach (var line in memInfoLines)
- {
- var lineParts = line.Split(new[] { ':' }, 2);
- if (lineParts.Length != 2)
- continue;
-
- if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false)
- continue;
-
- var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim();
-
- if (!int.TryParse(memKb, out var parsedMem)) continue;
- InstalledRam = parsedMem * 1024;
- break;
- }
- }
- }
+ /// Could not initialize the GPIO controller.
+ private SystemInfo() {
+ #region Obtain and format a property dictionary
+
+ PropertyInfo[] properties = typeof(SystemInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(
+ p => p.CanWrite && p.CanRead && (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[]))).ToArray();
+ Dictionary propDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ foreach(PropertyInfo prop in properties) {
+ propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop;
+ }
+
+ #endregion
+
+ #region Extract CPU information
+
+ if(File.Exists(CpuInfoFilePath)) {
+ String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
+
+ foreach(String line in cpuInfoLines) {
+ String[] lineParts = line.Split(new[] { ':' }, 2);
+ if(lineParts.Length != 2) {
+ continue;
+ }
+
+ String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty);
+ String propertyStringValue = lineParts[1].Trim();
+
+ if(!propDictionary.ContainsKey(propertyKey)) {
+ continue;
+ }
+
+ PropertyInfo property = propDictionary[propertyKey];
+ if(property.PropertyType == typeof(String)) {
+ property.SetValue(this, propertyStringValue);
+ } else if(property.PropertyType == typeof(String[])) {
+ String[] propertyArrayValue = propertyStringValue.Split(' ');
+ property.SetValue(this, propertyArrayValue);
+ }
+ }
+ }
+
+ #endregion
+
+ this.ExtractMemoryInfo();
+ this.ExtractBoardVersion();
+ this.ExtractOS();
+ }
+
+ ///
+ /// Gets the library version.
+ ///
+ public Version? LibraryVersion {
+ get; private set;
+ }
+
+ ///
+ /// Gets the OS information.
+ ///
+ ///
+ /// The os information.
+ ///
+ public OsInfo? OperatingSystem {
+ get; set;
+ }
+
+ ///
+ /// Gets the Raspberry Pi version.
+ ///
+ public PiVersion RaspberryPiVersion {
+ get; set;
+ }
+
+ ///
+ /// Gets the board revision (1 or 2).
+ ///
+ ///
+ /// The wiring pi board revision.
+ ///
+ public Int32 BoardRevision {
+ get; set;
+ }
+
+ ///
+ /// Gets the number of processor cores.
+ ///
+ public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0;
+
+ ///
+ /// Gets the installed ram in bytes.
+ ///
+ public Int32 InstalledRam {
+ get; private set;
+ }
+
+ ///
+ /// Gets a value indicating whether this CPU is little endian.
+ ///
+ public Boolean IsLittleEndian => BitConverter.IsLittleEndian;
+
+ ///
+ /// Gets the CPU model name.
+ ///
+ public String? ModelName {
+ get; private set;
+ }
+
+ ///
+ /// Gets a list of supported CPU features.
+ ///
+ public String[]? Features {
+ get; private set;
+ }
+
+ ///
+ /// Gets the CPU implementer hex code.
+ ///
+ public String? CpuImplementer {
+ get; private set;
+ }
+
+ ///
+ /// Gets the CPU architecture code.
+ ///
+ public String? CpuArchitecture {
+ get; private set;
+ }
+
+ ///
+ /// Gets the CPU variant code.
+ ///
+ public String? CpuVariant {
+ get; private set;
+ }
+
+ ///
+ /// Gets the CPU part code.
+ ///
+ public String? CpuPart {
+ get; private set;
+ }
+
+ ///
+ /// Gets the CPU revision code.
+ ///
+ public String? CpuRevision {
+ get; private set;
+ }
+
+ ///
+ /// Gets the hardware model number.
+ ///
+ public String? Hardware {
+ get; private set;
+ }
+
+ ///
+ /// Gets the hardware revision number.
+ ///
+ public String? Revision {
+ get; private set;
+ }
+
+ ///
+ /// Gets the revision number (accordingly to new-style revision codes).
+ ///
+ public Int32 RevisionNumber {
+ get; set;
+ }
+
+ ///
+ /// Gets the board model (accordingly to new-style revision codes).
+ ///
+ /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
+ public BoardModel BoardModel => this.NewStyleRevisionCodes ? this._boardModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
+
+ ///
+ /// Gets processor model (accordingly to new-style revision codes).
+ ///
+ /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
+ public ProcessorModel ProcessorModel => this.NewStyleRevisionCodes ? this._processorModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
+
+ ///
+ /// Gets the manufacturer of the board (accordingly to new-style revision codes).
+ ///
+ /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
+ public Manufacturer Manufacturer => this.NewStyleRevisionCodes ? this._manufacturer : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
+
+ ///
+ /// Gets the size of the memory (accordingly to new-style revision codes).
+ ///
+ /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.
+ public MemorySize MemorySize => this.NewStyleRevisionCodes ? this._memorySize : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
+
+ ///
+ /// Gets the serial number.
+ ///
+ public String? Serial {
+ get; private set;
+ }
+
+ ///
+ /// Gets the system up-time (in seconds).
+ ///
+ public Double Uptime {
+ get {
+ try {
+ if(File.Exists(UptimeFilePath) == false) {
+ return 0;
+ }
+
+ String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+
+ if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) {
+ return result;
+ }
+ } catch {
+ /* Ignore */
+ }
+
+ return 0;
+ }
+ }
+
+ ///
+ /// Gets the uptime in TimeSpan.
+ ///
+ public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime);
+
+ ///
+ /// Indicates if the board uses the new-style revision codes.
+ ///
+ private Boolean NewStyleRevisionCodes {
+ get; set;
+ }
+
+ ///
+ /// Placeholder for processor index.
+ ///
+ private String? Processor {
+ get; set;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override String ToString() {
+ PropertyInfo[] properties = typeof(SystemInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(
+ p => p.CanRead && (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[]) || p.PropertyType == typeof(Int32) || p.PropertyType == typeof(Boolean) || p.PropertyType == typeof(TimeSpan))).ToArray();
+
+ List propertyValues2 = new List {
+ "System Information",
+ $"\t{nameof(this.LibraryVersion),-22}: {this.LibraryVersion}",
+ $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}"
+ };
+
+ foreach(PropertyInfo property in properties) {
+ if(property.PropertyType != typeof(String[])) {
+ propertyValues2.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
+ } else if(property.GetValue(this) is String[] allValues) {
+ String concatValues = String.Join(" ", allValues);
+ propertyValues2.Add($"\t{property.Name,-22}: {concatValues}");
+ }
+ }
+
+ return String.Join(Environment.NewLine, propertyValues2.ToArray());
+ }
+
+ private void ExtractOS() {
+ try {
+ _ = Standard.Uname(out SystemName unameInfo);
+
+ this.OperatingSystem = new OsInfo {
+ DomainName = unameInfo.DomainName,
+ Machine = unameInfo.Machine,
+ NodeName = unameInfo.NodeName,
+ Release = unameInfo.Release,
+ SysName = unameInfo.SysName,
+ Version = unameInfo.Version,
+ };
+ } catch {
+ this.OperatingSystem = new OsInfo();
+ }
+ }
+
+ private void ExtractBoardVersion() {
+ Boolean hasSysInfo = DependencyContainer.Current.CanResolve();
+
+ try {
+ if(String.IsNullOrWhiteSpace(this.Revision) == false && Int32.TryParse(this.Revision != null ? this.Revision.ToUpperInvariant() : "", NumberStyles.HexNumber, CultureInfo.InvariantCulture, out Int32 boardVersion)) {
+ this.RaspberryPiVersion = PiVersion.Unknown;
+ if(Enum.IsDefined(typeof(PiVersion), boardVersion)) {
+ this.RaspberryPiVersion = (PiVersion)boardVersion;
+ }
+
+ if((boardVersion & NewStyleCodesMask) == NewStyleCodesMask) {
+ this.NewStyleRevisionCodes = true;
+ this.RevisionNumber = boardVersion & 0xF;
+ this._boardModel = (BoardModel)((boardVersion >> 4) & 0xFF);
+ this._processorModel = (ProcessorModel)((boardVersion >> 12) & 0xF);
+ this._manufacturer = (Manufacturer)((boardVersion >> 16) & 0xF);
+ this._memorySize = (MemorySize)((boardVersion >> 20) & 0x7);
+ }
+ }
+
+ if(hasSysInfo) {
+ this.BoardRevision = (Int32)DependencyContainer.Current.Resolve().BoardRevision;
+ }
+ } catch {
+ /* Ignore */
+ }
+
+ if(hasSysInfo) {
+ this.LibraryVersion = DependencyContainer.Current.Resolve().LibraryVersion;
+ }
+ }
+
+ private void ExtractMemoryInfo() {
+ if(!File.Exists(MemInfoFilePath)) {
+ return;
+ }
+
+ String[] memInfoLines = File.ReadAllLines(MemInfoFilePath);
+
+ foreach(String line in memInfoLines) {
+ String[] lineParts = line.Split(new[] { ':' }, 2);
+ if(lineParts.Length != 2) {
+ continue;
+ }
+
+ if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) {
+ continue;
+ }
+
+ String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim();
+
+ if(!Int32.TryParse(memKb, out Int32 parsedMem)) {
+ continue;
+ }
+
+ this.InstalledRam = parsedMem * 1024;
+ break;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs b/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs
index 9e41d24..ca8659b 100644
--- a/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs
+++ b/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs
@@ -1,23 +1,29 @@
-namespace Unosquare.RaspberryIO.Computer
-{
+using System;
+
+namespace Unosquare.RaspberryIO.Computer {
+ ///
+ /// Represents a wireless network information.
+ ///
+ public class WirelessNetworkInfo {
///
- /// Represents a wireless network information.
+ /// Gets the ESSID of the Wireless network.
///
- public class WirelessNetworkInfo
- {
- ///
- /// Gets the ESSID of the Wireless network.
- ///
- public string Name { get; internal set; }
-
- ///
- /// Gets the network quality.
- ///
- public string Quality { get; internal set; }
-
- ///
- /// Gets a value indicating whether this instance is encrypted.
- ///
- public bool IsEncrypted { get; internal set; }
- }
+ public String? Name {
+ get; internal set;
+ }
+
+ ///
+ /// Gets the network quality.
+ ///
+ public String? Quality {
+ get; internal set;
+ }
+
+ ///
+ /// Gets a value indicating whether this instance is encrypted.
+ ///
+ public Boolean IsEncrypted {
+ get; internal set;
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Native/Standard.cs b/Unosquare.RaspberryIO/Native/Standard.cs
index cd15d80..359e8a0 100644
--- a/Unosquare.RaspberryIO/Native/Standard.cs
+++ b/Unosquare.RaspberryIO/Native/Standard.cs
@@ -1,17 +1,16 @@
-namespace Unosquare.RaspberryIO.Native
-{
- using System.Runtime.InteropServices;
-
- internal static class Standard
- {
- internal const string LibCLibrary = "libc";
-
- ///
- /// Fills in the structure with information about the system.
- ///
- /// The name.
- /// The result.
- [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
- public static extern int Uname(out SystemName name);
- }
+using System;
+using System.Runtime.InteropServices;
+
+namespace Unosquare.RaspberryIO.Native {
+ internal static class Standard {
+ internal const String LibCLibrary = "libc";
+
+ ///
+ /// Fills in the structure with information about the system.
+ ///
+ /// The name.
+ /// The result.
+ [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
+ public static extern Int32 Uname(out SystemName name);
+ }
}
diff --git a/Unosquare.RaspberryIO/Native/SystemName.cs b/Unosquare.RaspberryIO/Native/SystemName.cs
index b9f5903..bd11a31 100644
--- a/Unosquare.RaspberryIO/Native/SystemName.cs
+++ b/Unosquare.RaspberryIO/Native/SystemName.cs
@@ -1,47 +1,46 @@
-namespace Unosquare.RaspberryIO.Native
-{
- using System.Runtime.InteropServices;
-
+using System;
+using System.Runtime.InteropServices;
+
+namespace Unosquare.RaspberryIO.Native {
+ ///
+ /// OS uname structure.
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct SystemName {
///
- /// OS uname structure.
+ /// System name.
///
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- public struct SystemName
- {
- ///
- /// System name.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string SysName;
-
- ///
- /// Node name.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string NodeName;
-
- ///
- /// Release level.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string Release;
-
- ///
- /// Version level.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string Version;
-
- ///
- /// Hardware level.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string Machine;
-
- ///
- /// Domain name.
- ///
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
- public string DomainName;
- }
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String SysName;
+
+ ///
+ /// Node name.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String NodeName;
+
+ ///
+ /// Release level.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String Release;
+
+ ///
+ /// Version level.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String Version;
+
+ ///
+ /// Hardware level.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String Machine;
+
+ ///
+ /// Domain name.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
+ public String DomainName;
+ }
}
\ No newline at end of file
diff --git a/Unosquare.RaspberryIO/Pi.cs b/Unosquare.RaspberryIO/Pi.cs
index 2500924..11252f2 100644
--- a/Unosquare.RaspberryIO/Pi.cs
+++ b/Unosquare.RaspberryIO/Pi.cs
@@ -1,141 +1,138 @@
-namespace Unosquare.RaspberryIO
-{
- using Abstractions;
- using Camera;
- using Computer;
- using Swan;
- using Swan.DependencyInjection;
- using System;
- using System.Threading.Tasks;
-
+using System;
+using System.Threading.Tasks;
+
+using Swan;
+using Swan.DependencyInjection;
+
+using Unosquare.RaspberryIO.Abstractions;
+using Unosquare.RaspberryIO.Camera;
+using Unosquare.RaspberryIO.Computer;
+
+namespace Unosquare.RaspberryIO {
+ ///
+ /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera.
+ ///
+ public static class Pi {
+ private const String MissingDependenciesMessage = "You need to load a valid assembly (WiringPi or PiGPIO).";
+ private static readonly Object SyncLock = new Object();
+ private static Boolean _isInit;
+ private static SystemInfo? _info;
+
///
- /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera.
+ /// Initializes static members of the class.
///
- public static class Pi
- {
- private const string MissingDependenciesMessage = "You need to load a valid assembly (WiringPi or PiGPIO).";
- private static readonly object SyncLock = new object();
- private static bool _isInit;
- private static SystemInfo _info;
-
- ///
- /// Initializes static members of the class.
- ///
- static Pi()
- {
- lock (SyncLock)
- {
- Camera = CameraController.Instance;
- PiDisplay = DsiDisplay.Instance;
- Audio = AudioSettings.Instance;
- Bluetooth = Bluetooth.Instance;
- }
- }
-
- ///
- /// Provides information on this Raspberry Pi's CPU and form factor.
- ///
- public static SystemInfo Info => _info ??= SystemInfo.Instance;
-
- ///
- /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins.
- ///
- public static IGpioController Gpio =>
- ResolveDependency();
-
- ///
- /// Provides access to the 2-channel SPI bus.
- ///
- public static ISpiBus Spi =>
- ResolveDependency();
-
- ///
- /// Provides access to the functionality of the i2c bus.
- ///
- public static II2CBus I2C =>
- ResolveDependency();
-
- ///
- /// Provides access to timing functionality.
- ///
- public static ITiming Timing =>
- ResolveDependency();
-
- ///
- /// Provides access to threading functionality.
- ///
- public static IThreading Threading =>
- ResolveDependency();
-
- ///
- /// Provides access to the official Raspberry Pi Camera.
- ///
- public static CameraController Camera { get; }
-
- ///
- /// Provides access to the official Raspberry Pi 7-inch DSI Display.
- ///
- public static DsiDisplay PiDisplay { get; }
-
- ///
- /// Provides access to Raspberry Pi ALSA sound card driver.
- ///
- public static AudioSettings Audio { get; }
-
- ///
- /// Provides access to Raspberry Pi Bluetooth driver.
- ///
- public static Bluetooth Bluetooth { get; }
-
- ///
- /// Restarts the Pi. Must be running as SU.
- ///
- /// The process result.
- public static Task RestartAsync() => ProcessRunner.GetProcessResultAsync("reboot");
-
- ///
- /// Restarts the Pi. Must be running as SU.
- ///
- /// The process result.
- public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult();
-
- ///
- /// Halts the Pi. Must be running as SU.
- ///
- /// The process result.
- public static Task ShutdownAsync() => ProcessRunner.GetProcessResultAsync("halt");
-
- ///
- /// Halts the Pi. Must be running as SU.
- ///
- /// The process result.
- public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult();
-
- ///
- /// Initializes an Abstractions implementation.
- ///
- /// An implementation of .
- public static void Init()
- where T : IBootstrap
- {
- lock (SyncLock)
- {
- if (_isInit) return;
-
- Activator.CreateInstance().Bootstrap();
- _isInit = true;
- }
- }
-
- private static T ResolveDependency()
- where T : class
- {
- if (!_isInit)
- throw new InvalidOperationException($"You must first initialize {nameof(Pi)} referencing a valid {nameof(IBootstrap)} implementation.");
-
- return DependencyContainer.Current.CanResolve()
- ? DependencyContainer.Current.Resolve()
- : throw new InvalidOperationException(MissingDependenciesMessage);
- }
- }
+ static Pi() {
+ lock(SyncLock) {
+ Camera = CameraController.Instance;
+ PiDisplay = DsiDisplay.Instance;
+ Audio = AudioSettings.Instance;
+ Bluetooth = Bluetooth.Instance;
+ }
+ }
+
+ ///
+ /// Provides information on this Raspberry Pi's CPU and form factor.
+ ///
+ public static SystemInfo Info => _info ??= SystemInfo.Instance;
+
+ ///
+ /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins.
+ ///
+ public static IGpioController Gpio => ResolveDependency();
+
+ ///
+ /// Provides access to the 2-channel SPI bus.
+ ///
+ public static ISpiBus Spi => ResolveDependency();
+
+ ///
+ /// Provides access to the functionality of the i2c bus.
+ ///
+ public static II2CBus I2C => ResolveDependency();
+
+ ///
+ /// Provides access to timing functionality.
+ ///
+ public static ITiming Timing => ResolveDependency();
+
+ ///
+ /// Provides access to threading functionality.
+ ///
+ public static IThreading Threading => ResolveDependency();
+
+ ///
+ /// Provides access to the official Raspberry Pi Camera.
+ ///
+ public static CameraController Camera {
+ get;
+ }
+
+ ///
+ /// Provides access to the official Raspberry Pi 7-inch DSI Display.
+ ///
+ public static DsiDisplay PiDisplay {
+ get;
+ }
+
+ ///
+ /// Provides access to Raspberry Pi ALSA sound card driver.
+ ///
+ public static AudioSettings Audio {
+ get;
+ }
+
+ ///
+ /// Provides access to Raspberry Pi Bluetooth driver.
+ ///
+ public static Bluetooth Bluetooth {
+ get;
+ }
+
+ ///
+ /// Restarts the Pi. Must be running as SU.
+ ///
+ /// The process result.
+ public static Task RestartAsync() => ProcessRunner.GetProcessResultAsync("reboot");
+
+ ///
+ /// Restarts the Pi. Must be running as SU.
+ ///
+ /// The process result.
+ public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult();
+
+ ///
+ /// Halts the Pi. Must be running as SU.
+ ///
+ /// The process result.
+ public static Task ShutdownAsync() => ProcessRunner.GetProcessResultAsync("halt");
+
+ ///
+ /// Halts the Pi. Must be running as SU.
+ ///
+ /// The process result.
+ public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult();
+
+ ///
+ /// Initializes an Abstractions implementation.
+ ///
+ /// An implementation of .
+ public static void Init() where T : IBootstrap {
+ lock(SyncLock) {
+ if(_isInit) {
+ return;
+ }
+
+ Activator.CreateInstance().Bootstrap();
+ _isInit = true;
+ }
+ }
+
+ private static T ResolveDependency() where T : class {
+ if(!_isInit) {
+ throw new InvalidOperationException($"You must first initialize {nameof(Pi)} referencing a valid {nameof(IBootstrap)} implementation.");
+ }
+
+ return DependencyContainer.Current.CanResolve() ? DependencyContainer.Current.Resolve() : throw new InvalidOperationException(MissingDependenciesMessage);
+ }
+ }
}
diff --git a/Unosquare.RaspberryIO/Unosquare.RaspberryIO.csproj b/Unosquare.RaspberryIO/Unosquare.RaspberryIO.csproj
index 5638c85..d6d4e66 100644
--- a/Unosquare.RaspberryIO/Unosquare.RaspberryIO.csproj
+++ b/Unosquare.RaspberryIO/Unosquare.RaspberryIO.csproj
@@ -16,6 +16,7 @@ This library enables developers to use the various Raspberry Pi's hardware modul
https://raw.githubusercontent.com/unosquare/raspberryio/master/LICENSE
Raspberry Pi GPIO Camera SPI I2C Embedded IoT Mono C# .NET
8.0
+ enable