Compare commits

..

4 Commits

Author SHA1 Message Date
2f74732924 Coding style 2019-12-04 17:10:06 +01:00
c1e8637516 Coding Styles 2019-12-03 18:44:25 +01:00
186792fde8 Coding styles 2019-12-03 18:43:54 +01:00
d75c3bc73f Add LICENSE and README.md 2019-05-29 19:25:41 +02:00
183 changed files with 33474 additions and 35028 deletions

31
LICENSE Normal file
View File

@ -0,0 +1,31 @@
MIT License
Copyright (c) 2016 Unosquare
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This software contains a compiled, unmodified version of the WiringPi library
WiringPi is a GPIO access library written in C for the BCM2835 used in the
Raspberry Pi. Its released under the GNU LGPLv3 license and is usable from C
and C++ and many other languages with suitable wrappers. A program that contains
no derivative of any portion of the Library, but is designed to work with
the Library by being compiled or linked with it, is called a "work that uses
the Library". Such a work, in isolation, is not a derivative work of the Library,
and therefore falls outside the scope of this License. Raspberry IO is then,
by definition, "work that uses the Library"

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# UnoSquare
## RaspberryIO
Based on https://github.com/unosquare/raspberryio
## SWAN
Based on https://github.com/unosquare/swan

View File

@ -1,23 +1,20 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{
using Swan;
using System; using System;
using System.Linq; using System.Linq;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// A simple RGB color class to represent colors in RGB and YUV colorspaces. /// A simple RGB color class to represent colors in RGB and YUV colorspaces.
/// </summary> /// </summary>
public class CameraColor public class CameraColor {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CameraColor"/> class. /// Initializes a new instance of the <see cref="CameraColor"/> class.
/// </summary> /// </summary>
/// <param name="r">The red.</param> /// <param name="r">The red.</param>
/// <param name="g">The green.</param> /// <param name="g">The green.</param>
/// <param name="b">The blue.</param> /// <param name="b">The blue.</param>
public CameraColor(int r, int g, int b) public CameraColor(Int32 r, Int32 g, Int32 b)
: this(r, g, b, string.Empty) : this(r, g, b, String.Empty) {
{
} }
/// <summary> /// <summary>
@ -27,16 +24,15 @@
/// <param name="g">The green.</param> /// <param name="g">The green.</param>
/// <param name="b">The blue.</param> /// <param name="b">The blue.</param>
/// <param name="name">The well-known color name.</param> /// <param name="name">The well-known color name.</param>
public CameraColor(int r, int g, int b, string 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)) };
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); Single y = this.R * .299000f + this.G * .587000f + this.B * .114000f;
var u = (R * -.168736f) + (G * -.331264f) + (B * .500000f) + 128f; Single u = this.R * -.168736f + this.G * -.331264f + this.B * .500000f + 128f;
var v = (R * .500000f) + (G * -.418688f) + (B * -.081312f) + 128f; Single v = this.R * .500000f + this.G * -.418688f + this.B * -.081312f + 128f;
YUV = new byte[] { (byte)y.Clamp(0, 255), (byte)u.Clamp(0, 255), (byte)v.Clamp(0, 255) }; this.YUV = new Byte[] { (Byte)y.Clamp(0, 255), (Byte)u.Clamp(0, 255), (Byte)v.Clamp(0, 255) };
Name = name; this.Name = name;
} }
#region Static Definitions #region Static Definitions
@ -71,32 +67,38 @@
/// <summary> /// <summary>
/// Gets the well-known color name. /// Gets the well-known color name.
/// </summary> /// </summary>
public string Name { get; } public String Name {
get;
}
/// <summary> /// <summary>
/// Gets the red byte. /// Gets the red byte.
/// </summary> /// </summary>
public byte R => RGB[0]; public Byte R => this.RGB[0];
/// <summary> /// <summary>
/// Gets the green byte. /// Gets the green byte.
/// </summary> /// </summary>
public byte G => RGB[1]; public Byte G => this.RGB[1];
/// <summary> /// <summary>
/// Gets the blue byte. /// Gets the blue byte.
/// </summary> /// </summary>
public byte B => RGB[2]; public Byte B => this.RGB[2];
/// <summary> /// <summary>
/// Gets the RGB byte array (3 bytes). /// Gets the RGB byte array (3 bytes).
/// </summary> /// </summary>
public byte[] RGB { get; } public Byte[] RGB {
get;
}
/// <summary> /// <summary>
/// Gets the YUV byte array (3 bytes). /// Gets the YUV byte array (3 bytes).
/// </summary> /// </summary>
public byte[] YUV { get; } public Byte[] YUV {
get;
}
/// <summary> /// <summary>
/// Returns a hexadecimal representation of the RGB byte array. /// Returns a hexadecimal representation of the RGB byte array.
@ -104,10 +106,12 @@
/// </summary> /// </summary>
/// <param name="reverse">if set to <c>true</c> [reverse].</param> /// <param name="reverse">if set to <c>true</c> [reverse].</param>
/// <returns>A string</returns> /// <returns>A string</returns>
public string ToRgbHex(bool reverse) public String ToRgbHex(Boolean reverse) {
{ Byte[] data = this.RGB.ToArray();
var data = RGB.ToArray(); if(reverse) {
if (reverse) Array.Reverse(data); Array.Reverse(data);
}
return ToHex(data); return ToHex(data);
} }
@ -117,10 +121,12 @@
/// </summary> /// </summary>
/// <param name="reverse">if set to <c>true</c> [reverse].</param> /// <param name="reverse">if set to <c>true</c> [reverse].</param>
/// <returns>A string</returns> /// <returns>A string</returns>
public string ToYuvHex(bool reverse) public String ToYuvHex(Boolean reverse) {
{ Byte[] data = this.YUV.ToArray();
var data = YUV.ToArray(); if(reverse) {
if (reverse) Array.Reverse(data); Array.Reverse(data);
}
return ToHex(data); return ToHex(data);
} }
@ -129,6 +135,6 @@
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>A string</returns> /// <returns>A string</returns>
private static string ToHex(byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", string.Empty).ToLowerInvariant()}"; private static String ToHex(Byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", String.Empty).ToLowerInvariant()}";
} }
} }

View File

@ -1,22 +1,20 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan.Abstractions;
{
using Swan.Abstractions;
using System; using System;
using Swan.Components; using Unosquare.Swan.Components;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs. /// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs.
/// This class is a singleton /// This class is a singleton
/// </summary> /// </summary>
public class CameraController : SingletonBase<CameraController> public class CameraController : SingletonBase<CameraController> {
{
#region Private Declarations #region Private Declarations
private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true); private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true);
private static readonly object SyncRoot = new object(); private static readonly Object SyncRoot = new Object();
private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource(); private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
private static Task<Task> _videoStreamTask; private static Task<Task> _videoStreamTask;
@ -30,7 +28,7 @@
/// <value> /// <value>
/// <c>true</c> if this instance is busy; otherwise, <c>false</c>. /// <c>true</c> if this instance is busy; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsBusy => OperationDone.IsSet == false; public Boolean IsBusy => OperationDone.IsSet == false;
#endregion #endregion
@ -43,34 +41,30 @@
/// <param name="ct">The ct.</param> /// <param name="ct">The ct.</param>
/// <returns>The image bytes</returns> /// <returns>The image bytes</returns>
/// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception> /// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception>
public async Task<byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
{ public async Task<Byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) {
if (Instance.IsBusy) if(Instance.IsBusy) {
throw new InvalidOperationException("Cannot use camera module because it is currently busy."); throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
}
if (settings.CaptureTimeoutMilliseconds <= 0) if(settings.CaptureTimeoutMilliseconds <= 0) {
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0"); throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0");
}
try try {
{
OperationDone.Reset(); OperationDone.Reset();
var output = new MemoryStream(); MemoryStream output = new MemoryStream();
var exitCode = await ProcessRunner.RunProcessAsync( Int32 exitCode = await ProcessRunner.RunProcessAsync(
settings.CommandName, settings.CommandName,
settings.CreateProcessArguments(), settings.CreateProcessArguments(),
(data, proc) => (data, proc) => output.Write(data, 0, data.Length),
{
output.Write(data, 0, data.Length);
},
null, null,
true, true,
ct); ct);
return exitCode != 0 ? new byte[] { } : output.ToArray(); return exitCode != 0 ? new Byte[] { } : output.ToArray();
} } finally {
finally
{
OperationDone.Set(); OperationDone.Set();
} }
} }
@ -80,10 +74,7 @@
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns>The image bytes</returns> /// <returns>The image bytes</returns>
public byte[] CaptureImage(CameraStillSettings settings) public Byte[] CaptureImage(CameraStillSettings settings) => this.CaptureImageAsync(settings).GetAwaiter().GetResult();
{
return CaptureImageAsync(settings).GetAwaiter().GetResult();
}
/// <summary> /// <summary>
/// Captures a JPEG encoded image asynchronously at 90% quality. /// Captures a JPEG encoded image asynchronously at 90% quality.
@ -92,17 +83,15 @@
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <param name="ct">The ct.</param> /// <param name="ct">The ct.</param>
/// <returns>The image bytes</returns> /// <returns>The image bytes</returns>
public Task<byte[]> CaptureImageJpegAsync(int width, int height, CancellationToken ct = default) public Task<Byte[]> CaptureImageJpegAsync(Int32 width, Int32 height, CancellationToken ct = default) {
{ CameraStillSettings settings = new CameraStillSettings {
var settings = new CameraStillSettings
{
CaptureWidth = width, CaptureWidth = width,
CaptureHeight = height, CaptureHeight = height,
CaptureJpegQuality = 90, CaptureJpegQuality = 90,
CaptureTimeoutMilliseconds = 300, CaptureTimeoutMilliseconds = 300,
}; };
return CaptureImageAsync(settings, ct); return this.CaptureImageAsync(settings, ct);
} }
/// <summary> /// <summary>
@ -111,7 +100,7 @@
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <returns>The image bytes</returns> /// <returns>The image bytes</returns>
public byte[] CaptureImageJpeg(int width, int height) => CaptureImageJpegAsync(width, height).GetAwaiter().GetResult(); public Byte[] CaptureImageJpeg(Int32 width, Int32 height) => this.CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
#endregion #endregion
@ -123,17 +112,15 @@
/// </summary> /// </summary>
/// <param name="onDataCallback">The on data callback.</param> /// <param name="onDataCallback">The on data callback.</param>
/// <param name="onExitCallback">The on exit callback.</param> /// <param name="onExitCallback">The on exit callback.</param>
public void OpenVideoStream(Action<byte[]> onDataCallback, Action onExitCallback = null) public void OpenVideoStream(Action<Byte[]> onDataCallback, Action onExitCallback = null) {
{ CameraVideoSettings settings = new CameraVideoSettings {
var settings = new CameraVideoSettings
{
CaptureTimeoutMilliseconds = 0, CaptureTimeoutMilliseconds = 0,
CaptureDisplayPreview = false, CaptureDisplayPreview = false,
CaptureWidth = 1920, CaptureWidth = 1920,
CaptureHeight = 1080 CaptureHeight = 1080
}; };
OpenVideoStream(settings, onDataCallback, onExitCallback); this.OpenVideoStream(settings, onDataCallback, onExitCallback);
} }
/// <summary> /// <summary>
@ -144,21 +131,19 @@
/// <param name="onExitCallback">The on exit callback.</param> /// <param name="onExitCallback">The on exit callback.</param>
/// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception> /// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception>
/// <exception cref="ArgumentException">CaptureTimeoutMilliseconds</exception> /// <exception cref="ArgumentException">CaptureTimeoutMilliseconds</exception>
public void OpenVideoStream(CameraVideoSettings settings, Action<byte[]> onDataCallback, Action onExitCallback) public void OpenVideoStream(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
{ if(Instance.IsBusy) {
if (Instance.IsBusy)
throw new InvalidOperationException("Cannot use camera module because it is currently busy."); throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
}
if (settings.CaptureTimeoutMilliseconds < 0) if(settings.CaptureTimeoutMilliseconds < 0) {
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0"); throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0");
}
try try {
{
OperationDone.Reset(); OperationDone.Reset();
_videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token); _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token);
} } catch {
catch
{
OperationDone.Set(); OperationDone.Set();
throw; throw;
} }
@ -167,16 +152,14 @@
/// <summary> /// <summary>
/// Closes the video stream of a video stream is open. /// Closes the video stream of a video stream is open.
/// </summary> /// </summary>
public void CloseVideoStream() public void CloseVideoStream() {
{ lock(SyncRoot) {
lock (SyncRoot) if(this.IsBusy == false) {
{
if (IsBusy == false)
return; return;
} }
}
if (_videoTokenSource.IsCancellationRequested == false) if(_videoTokenSource.IsCancellationRequested == false) {
{
_videoTokenSource.Cancel(); _videoTokenSource.Cancel();
_videoStreamTask.Wait(); _videoStreamTask.Wait();
} }
@ -184,13 +167,8 @@
_videoTokenSource = new CancellationTokenSource(); _videoTokenSource = new CancellationTokenSource();
} }
private static async Task VideoWorkerDoWork( private static async Task VideoWorkerDoWork(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
CameraVideoSettings settings, try {
Action<byte[]> onDataCallback,
Action onExitCallback)
{
try
{
await ProcessRunner.RunProcessAsync( await ProcessRunner.RunProcessAsync(
settings.CommandName, settings.CommandName,
settings.CreateProcessArguments(), settings.CreateProcessArguments(),
@ -200,13 +178,9 @@
_videoTokenSource.Token); _videoTokenSource.Token);
onExitCallback?.Invoke(); onExitCallback?.Invoke();
} } catch {
catch
{
// swallow // swallow
} } finally {
finally
{
Instance.CloseVideoStream(); Instance.CloseVideoStream();
OperationDone.Set(); OperationDone.Set();
} }

View File

@ -1,13 +1,12 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan;
using System.Globalization; using System.Globalization;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// Defines the Raspberry Pi camera's sensor ROI (Region of Interest) /// Defines the Raspberry Pi camera's sensor ROI (Region of Interest)
/// </summary> /// </summary>
public struct CameraRect public struct CameraRect {
{
/// <summary> /// <summary>
/// The default ROI which is the entire area. /// The default ROI which is the entire area.
/// </summary> /// </summary>
@ -19,7 +18,9 @@
/// <value> /// <value>
/// The x. /// The x.
/// </value> /// </value>
public decimal X { get; set; } public Decimal X {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the y location in relative coordinates. (0.0 to 1.0) /// Gets or sets the y location in relative coordinates. (0.0 to 1.0)
@ -27,7 +28,9 @@
/// <value> /// <value>
/// The y. /// The y.
/// </value> /// </value>
public decimal Y { get; set; } public Decimal Y {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the width in relative coordinates. (0.0 to 1.0) /// Gets or sets the width in relative coordinates. (0.0 to 1.0)
@ -35,7 +38,9 @@
/// <value> /// <value>
/// The w. /// The w.
/// </value> /// </value>
public decimal W { get; set; } public Decimal W {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the height in relative coordinates. (0.0 to 1.0) /// Gets or sets the height in relative coordinates. (0.0 to 1.0)
@ -43,7 +48,9 @@
/// <value> /// <value>
/// The h. /// The h.
/// </value> /// </value>
public decimal H { get; set; } public Decimal H {
get; set;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to the default (The entire area). /// Gets a value indicating whether this instance is equal to the default (The entire area).
@ -51,32 +58,29 @@
/// <value> /// <value>
/// <c>true</c> if this instance is default; otherwise, <c>false</c>. /// <c>true</c> if this instance is default; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsDefault public Boolean IsDefault {
{ get {
get this.Clamp();
{ return this.X == Default.X && this.Y == Default.Y && this.W == Default.W && this.H == Default.H;
Clamp();
return X == Default.X && Y == Default.Y && W == Default.W && H == Default.H;
} }
} }
/// <summary> /// <summary>
/// Clamps the members of this ROI to their minimum and maximum values /// Clamps the members of this ROI to their minimum and maximum values
/// </summary> /// </summary>
public void Clamp() public void Clamp() {
{ this.X = this.X.Clamp(0M, 1M);
X = X.Clamp(0M, 1M); this.Y = this.Y.Clamp(0M, 1M);
Y = Y.Clamp(0M, 1M); this.W = this.W.Clamp(0M, 1M - this.X);
W = W.Clamp(0M, 1M - X); this.H = this.H.Clamp(0M, 1M - this.Y);
H = H.Clamp(0M, 1M - Y);
} }
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Returns a <see cref="String" /> that represents this instance.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A <see cref="string" /> that represents this instance. /// A <see cref="String" /> that represents this instance.
/// </returns> /// </returns>
public override string ToString() => $"{X.ToString(CultureInfo.InvariantCulture)},{Y.ToString(CultureInfo.InvariantCulture)},{W.ToString(CultureInfo.InvariantCulture)},{H.ToString(CultureInfo.InvariantCulture)}"; public override String ToString() => $"{this.X.ToString(CultureInfo.InvariantCulture)},{this.Y.ToString(CultureInfo.InvariantCulture)},{this.W.ToString(CultureInfo.InvariantCulture)},{this.H.ToString(CultureInfo.InvariantCulture)}";
} }
} }

View File

@ -1,16 +1,15 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{
using Swan;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using System;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// A base class to implement raspistill and raspivid wrappers /// A base class to implement raspistill and raspivid wrappers
/// Full documentation available at /// Full documentation available at
/// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md /// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md
/// </summary> /// </summary>
public abstract class CameraSettingsBase public abstract class CameraSettingsBase {
{
/// <summary> /// <summary>
/// The Invariant Culture shorthand /// The Invariant Culture shorthand
/// </summary> /// </summary>
@ -23,27 +22,27 @@
/// Default value is 5000 /// Default value is 5000
/// Recommended value is at least 300 in order to let the light collectors open /// Recommended value is at least 300 in order to let the light collectors open
/// </summary> /// </summary>
public int CaptureTimeoutMilliseconds { get; set; } = 5000; public Int32 CaptureTimeoutMilliseconds { get; set; } = 5000;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not to show a preview window on the screen /// Gets or sets a value indicating whether or not to show a preview window on the screen
/// </summary> /// </summary>
public bool CaptureDisplayPreview { get; set; } = false; public Boolean CaptureDisplayPreview { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled
/// </summary> /// </summary>
public bool CaptureDisplayPreviewInFullScreen { get; set; } = true; public Boolean CaptureDisplayPreviewInFullScreen { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether video stabilization should be enabled. /// Gets or sets a value indicating whether video stabilization should be enabled.
/// </summary> /// </summary>
public bool CaptureVideoStabilizationEnabled { get; set; } = false; public Boolean CaptureVideoStabilizationEnabled { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets the display preview opacity only if the display preview property is enabled. /// Gets or sets the display preview opacity only if the display preview property is enabled.
/// </summary> /// </summary>
public byte CaptureDisplayPreviewOpacity { get; set; } = 255; public Byte CaptureDisplayPreviewOpacity { get; set; } = 255;
/// <summary> /// <summary>
/// Gets or sets the capture sensor region of interest in relative coordinates. /// Gets or sets the capture sensor region of interest in relative coordinates.
@ -54,7 +53,7 @@
/// Gets or sets the capture shutter speed in microseconds. /// Gets or sets the capture shutter speed in microseconds.
/// Default -1, Range 0 to 6000000 (equivalent to 6 seconds) /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds)
/// </summary> /// </summary>
public int CaptureShutterSpeedMicroseconds { get; set; } = -1; public Int32 CaptureShutterSpeedMicroseconds { get; set; } = -1;
/// <summary> /// <summary>
/// Gets or sets the exposure mode. /// Gets or sets the exposure mode.
@ -68,7 +67,7 @@
/// Exposure can be adjusted by changing either the lens f-number or the exposure time; /// 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. /// which one is changed usually depends on the camera's exposure mode.
/// </summary> /// </summary>
public int CaptureExposureCompensation { get; set; } = 0; public Int32 CaptureExposureCompensation { get; set; } = 0;
/// <summary> /// <summary>
/// Gets or sets the capture metering mode. /// Gets or sets the capture metering mode.
@ -85,20 +84,22 @@
/// Only takes effect if White balance control is set to off. /// Only takes effect if White balance control is set to off.
/// Default is 0 /// Default is 0
/// </summary> /// </summary>
public decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M; public Decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
/// <summary> /// <summary>
/// Gets or sets the capture white balance gain on the red channel. Example: 1.75 /// 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. /// Only takes effect if White balance control is set to off.
/// Default is 0 /// Default is 0
/// </summary> /// </summary>
public decimal CaptureWhiteBalanceGainRed { get; set; } = 0M; public Decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
/// <summary> /// <summary>
/// Gets or sets the dynamic range compensation. /// 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. /// 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.
/// </summary> /// </summary>
public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { get; set; } = public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation {
get; set;
} =
CameraDynamicRangeCompensation.Off; CameraDynamicRangeCompensation.Off;
#endregion #endregion
@ -109,39 +110,39 @@
/// Gets or sets the width of the picture to take. /// 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. /// Less than or equal to 0 in either width or height means maximum resolution available.
/// </summary> /// </summary>
public int CaptureWidth { get; set; } = 640; public Int32 CaptureWidth { get; set; } = 640;
/// <summary> /// <summary>
/// Gets or sets the height of the picture to take. /// 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. /// Less than or equal to 0 in either width or height means maximum resolution available.
/// </summary> /// </summary>
public int CaptureHeight { get; set; } = 480; public Int32 CaptureHeight { get; set; } = 480;
/// <summary> /// <summary>
/// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100 /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100
/// </summary> /// </summary>
public int ImageSharpness { get; set; } = 0; public Int32 ImageSharpness { get; set; } = 0;
/// <summary> /// <summary>
/// Gets or sets the picture contrast. Default is 0, Range form -100 to 100 /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100
/// </summary> /// </summary>
public int ImageContrast { get; set; } = 0; public Int32 ImageContrast { get; set; } = 0;
/// <summary> /// <summary>
/// Gets or sets the picture brightness. Default is 50, Range form 0 to 100 /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100
/// </summary> /// </summary>
public int ImageBrightness { get; set; } = 50; // from 0 to 100 public Int32 ImageBrightness { get; set; } = 50; // from 0 to 100
/// <summary> /// <summary>
/// Gets or sets the picture saturation. Default is 0, Range form -100 to 100 /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100
/// </summary> /// </summary>
public int ImageSaturation { get; set; } = 0; public Int32 ImageSaturation { get; set; } = 0;
/// <summary> /// <summary>
/// Gets or sets the picture ISO. Default is -1 Range is 100 to 800 /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800
/// The higher the value, the more light the sensor absorbs /// The higher the value, the more light the sensor absorbs
/// </summary> /// </summary>
public int ImageIso { get; set; } = -1; public Int32 ImageIso { get; set; } = -1;
/// <summary> /// <summary>
/// Gets or sets the image capture effect to be applied. /// Gets or sets the image capture effect to be applied.
@ -153,14 +154,14 @@
/// Default is -1, Range is 0 to 255 /// Default is -1, Range is 0 to 255
/// 128:128 should be effectively a monochrome image. /// 128:128 should be effectively a monochrome image.
/// </summary> /// </summary>
public int ImageColorEffectU { get; set; } = -1; // 0 to 255 public Int32 ImageColorEffectU { get; set; } = -1; // 0 to 255
/// <summary> /// <summary>
/// Gets or sets the color effect V coordinates. /// Gets or sets the color effect V coordinates.
/// Default is -1, Range is 0 to 255 /// Default is -1, Range is 0 to 255
/// 128:128 should be effectively a monochrome image. /// 128:128 should be effectively a monochrome image.
/// </summary> /// </summary>
public int ImageColorEffectV { get; set; } = -1; // 0 to 255 public Int32 ImageColorEffectV { get; set; } = -1; // 0 to 255
/// <summary> /// <summary>
/// Gets or sets the image rotation. Default is no rotation /// Gets or sets the image rotation. Default is no rotation
@ -170,12 +171,16 @@
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the image should be flipped horizontally. /// Gets or sets a value indicating whether the image should be flipped horizontally.
/// </summary> /// </summary>
public bool ImageFlipHorizontally { get; set; } public Boolean ImageFlipHorizontally {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the image should be flipped vertically. /// Gets or sets a value indicating whether the image should be flipped vertically.
/// </summary> /// </summary>
public bool ImageFlipVertically { get; set; } public Boolean ImageFlipVertically {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the image annotations using a bitmask (or flags) notation. /// Gets or sets the image annotations using a bitmask (or flags) notation.
@ -188,13 +193,13 @@
/// Text may include date/time placeholders by using the '%' character, as used by strftime. /// 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 /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33
/// </summary> /// </summary>
public string ImageAnnotationsText { get; set; } = string.Empty; public String ImageAnnotationsText { get; set; } = String.Empty;
/// <summary> /// <summary>
/// Gets or sets the font size of the text annotations /// Gets or sets the font size of the text annotations
/// Default is -1, range is 6 to 160 /// Default is -1, range is 6 to 160
/// </summary> /// </summary>
public int ImageAnnotationFontSize { get; set; } = -1; public Int32 ImageAnnotationFontSize { get; set; } = -1;
/// <summary> /// <summary>
/// Gets or sets the color of the text annotations. /// Gets or sets the color of the text annotations.
@ -219,118 +224,134 @@
/// <summary> /// <summary>
/// Gets the command file executable. /// Gets the command file executable.
/// </summary> /// </summary>
public abstract string CommandName { get; } public abstract String CommandName {
get;
}
/// <summary> /// <summary>
/// Creates the process arguments. /// Creates the process arguments.
/// </summary> /// </summary>
/// <returns>The string that represents the process arguments</returns> /// <returns>The string that represents the process arguments</returns>
public virtual string CreateProcessArguments() public virtual String CreateProcessArguments() {
{ StringBuilder sb = new StringBuilder();
var sb = new StringBuilder(); _ = sb.Append("-o -"); // output to standard output as opposed to a file.
sb.Append("-o -"); // output to standard output as opposed to a file. _ = sb.Append($" -t {(this.CaptureTimeoutMilliseconds < 0 ? "0" : this.CaptureTimeoutMilliseconds.ToString(Ci))}");
sb.Append($" -t {(CaptureTimeoutMilliseconds < 0 ? "0" : CaptureTimeoutMilliseconds.ToString(Ci))}");
// Basic Width and height // Basic Width and height
if (CaptureWidth > 0 && CaptureHeight > 0) if(this.CaptureWidth > 0 && this.CaptureHeight > 0) {
{ _ = sb.Append($" -w {this.CaptureWidth.ToString(Ci)}");
sb.Append($" -w {CaptureWidth.ToString(Ci)}"); _ = sb.Append($" -h {this.CaptureHeight.ToString(Ci)}");
sb.Append($" -h {CaptureHeight.ToString(Ci)}");
} }
// Display Preview // Display Preview
if (CaptureDisplayPreview) if(this.CaptureDisplayPreview) {
{ if(this.CaptureDisplayPreviewInFullScreen) {
if (CaptureDisplayPreviewInFullScreen) _ = sb.Append(" -f");
sb.Append(" -f");
if (CaptureDisplayPreviewOpacity != byte.MaxValue)
sb.Append($" -op {CaptureDisplayPreviewOpacity.ToString(Ci)}");
} }
else
{ if(this.CaptureDisplayPreviewOpacity != Byte.MaxValue) {
sb.Append(" -n"); // no preview _ = sb.Append($" -op {this.CaptureDisplayPreviewOpacity.ToString(Ci)}");
}
} else {
_ = sb.Append(" -n"); // no preview
} }
// Picture Settings // Picture Settings
if (ImageSharpness != 0) if(this.ImageSharpness != 0) {
sb.Append($" -sh {ImageSharpness.Clamp(-100, 100).ToString(Ci)}"); _ = sb.Append($" -sh {this.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) if(this.ImageContrast != 0) {
sb.Append($" -mm {CaptureMeteringMode.ToString().ToLowerInvariant()}"); _ = sb.Append($" -co {this.ImageContrast.Clamp(-100, 100).ToString(Ci)}");
}
if (ImageRotation != CameraImageRotation.None) if(this.ImageBrightness != 50) {
sb.Append($" -rot {((int)ImageRotation).ToString(Ci)}"); _ = sb.Append($" -br {this.ImageBrightness.Clamp(0, 100).ToString(Ci)}");
}
if (ImageFlipHorizontally) if(this.ImageSaturation != 0) {
sb.Append(" -hf"); _ = sb.Append($" -sa {this.ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
}
if (ImageFlipVertically) if(this.ImageIso >= 100) {
sb.Append(" -vf"); _ = sb.Append($" -ISO {this.ImageIso.Clamp(100, 800).ToString(Ci)}");
}
if (CaptureSensorRoi.IsDefault == false) if(this.CaptureVideoStabilizationEnabled) {
sb.Append($" -roi {CaptureSensorRoi}"); _ = sb.Append(" -vs");
}
if (CaptureShutterSpeedMicroseconds > 0) if(this.CaptureExposureCompensation != 0) {
sb.Append($" -ss {CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}"); _ = sb.Append($" -ev {this.CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
}
if (CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) if(this.CaptureExposure != CameraExposureMode.Auto) {
sb.Append($" -drc {CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}"); _ = sb.Append($" -ex {this.CaptureExposure.ToString().ToLowerInvariant()}");
}
if (CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off && if(this.CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) {
(CaptureWhiteBalanceGainBlue != 0M || CaptureWhiteBalanceGainRed != 0M)) _ = sb.Append($" -awb {this.CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
sb.Append($" -awbg {CaptureWhiteBalanceGainBlue.ToString(Ci)},{CaptureWhiteBalanceGainRed.ToString(Ci)}"); }
if (ImageAnnotationFontSize > 0) if(this.ImageEffect != CameraImageEffect.None) {
{ _ = sb.Append($" -ifx {this.ImageEffect.ToString().ToLowerInvariant()}");
sb.Append($" -ae {ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}"); }
sb.Append($",{(ImageAnnotationFontColor == null ? "0xff" : ImageAnnotationFontColor.ToYuvHex(true))}");
if (ImageAnnotationBackground != null) if(this.ImageColorEffectU >= 0 && this.ImageColorEffectV >= 0) {
{ _ = sb.Append(
ImageAnnotations |= CameraAnnotation.SolidBackground; $" -cfx {this.ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{this.ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
sb.Append($",{ImageAnnotationBackground.ToYuvHex(true)}"); }
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 (ImageAnnotations != CameraAnnotation.None) if(this.ImageAnnotations != CameraAnnotation.None) {
sb.Append($" -a {((int)ImageAnnotations).ToString(Ci)}"); _ = sb.Append($" -a {((Int32)this.ImageAnnotations).ToString(Ci)}");
}
if (string.IsNullOrWhiteSpace(ImageAnnotationsText) == false) if(String.IsNullOrWhiteSpace(this.ImageAnnotationsText) == false) {
sb.Append($" -a \"{ImageAnnotationsText.Replace("\"", "'")}\""); _ = sb.Append($" -a \"{this.ImageAnnotationsText.Replace("\"", "'")}\"");
}
return sb.ToString(); return sb.ToString();
} }

View File

@ -1,26 +1,24 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{
using Swan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// Defines a wrapper for the raspistill program and its settings (command-line arguments) /// Defines a wrapper for the raspistill program and its settings (command-line arguments)
/// </summary> /// </summary>
/// <seealso cref="CameraSettingsBase" /> /// <seealso cref="CameraSettingsBase" />
public class CameraStillSettings : CameraSettingsBase public class CameraStillSettings : CameraSettingsBase {
{ private Int32 _rotate;
private int _rotate;
/// <inheritdoc /> /// <inheritdoc />
public override string CommandName => "raspistill"; public override String CommandName => "raspistill";
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution
/// This may slow down preview FPS /// This may slow down preview FPS
/// </summary> /// </summary>
public bool CaptureDisplayPreviewAtResolution { get; set; } = false; public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets the encoding format the hardware will use for the output. /// Gets or sets the encoding format the hardware will use for the output.
@ -31,18 +29,18 @@
/// Gets or sets the quality for JPEG only encoding mode. /// Gets or sets the quality for JPEG only encoding mode.
/// Value ranges from 0 to 100 /// Value ranges from 0 to 100
/// </summary> /// </summary>
public int CaptureJpegQuality { get; set; } = 90; public Int32 CaptureJpegQuality { get; set; } = 90;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata. /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata.
/// </summary> /// </summary>
public bool CaptureJpegIncludeRawBayerMetadata { get; set; } = false; public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
/// <summary> /// <summary>
/// JPEG EXIF data /// JPEG EXIF data
/// Keys and values must be already properly escaped. Otherwise the command will fail. /// Keys and values must be already properly escaped. Otherwise the command will fail.
/// </summary> /// </summary>
public Dictionary<string, string> CaptureJpegExtendedInfo { get; } = new Dictionary<string, string>(); public Dictionary<String, String> CaptureJpegExtendedInfo { get; } = new Dictionary<String, String>();
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [horizontal flip]. /// Gets or sets a value indicating whether [horizontal flip].
@ -50,7 +48,7 @@
/// <value> /// <value>
/// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>. /// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool HorizontalFlip { get; set; } = false; public Boolean HorizontalFlip { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [vertical flip]. /// Gets or sets a value indicating whether [vertical flip].
@ -58,61 +56,64 @@
/// <value> /// <value>
/// <c>true</c> if [vertical flip]; otherwise, <c>false</c>. /// <c>true</c> if [vertical flip]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool VerticalFlip { get; set; } = false; public Boolean VerticalFlip { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets the rotation. /// Gets or sets the rotation.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Valid range 0-359</exception> /// <exception cref="ArgumentOutOfRangeException">Valid range 0-359</exception>
public int Rotation public Int32 Rotation {
{ get => this._rotate;
get => _rotate; set {
set if(value < 0 || value > 359) {
{
if (value < 0 || value > 359)
{
throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359"); throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
} }
_rotate = value; this._rotate = value;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public override string CreateProcessArguments() public override String CreateProcessArguments() {
{ StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
var sb = new StringBuilder(base.CreateProcessArguments()); _ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}");
sb.Append($" -e {CaptureEncoding.ToString().ToLowerInvariant()}");
// JPEG Encoder specific arguments // JPEG Encoder specific arguments
if (CaptureEncoding == CameraImageEncodingFormat.Jpg) if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) {
{ _ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
sb.Append($" -q {CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
if (CaptureJpegIncludeRawBayerMetadata) if(this.CaptureJpegIncludeRawBayerMetadata) {
sb.Append(" -r"); _ = sb.Append(" -r");
}
// JPEG EXIF data // JPEG EXIF data
if (CaptureJpegExtendedInfo.Count > 0) if(this.CaptureJpegExtendedInfo.Count > 0) {
{ foreach(KeyValuePair<String, String> kvp in this.CaptureJpegExtendedInfo) {
foreach (var kvp in CaptureJpegExtendedInfo) if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) {
{
if (string.IsNullOrWhiteSpace(kvp.Key) || string.IsNullOrWhiteSpace(kvp.Value))
continue; continue;
}
sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\""); _ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
} }
} }
} }
// Display preview settings // Display preview settings
if (CaptureDisplayPreview && CaptureDisplayPreviewAtResolution) sb.Append(" -fp"); if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) {
_ = sb.Append(" -fp");
}
if (Rotation != 0) sb.Append($" -rot {Rotation}"); if(this.Rotation != 0) {
_ = sb.Append($" -rot {this.Rotation}");
}
if (HorizontalFlip) sb.Append(" -hf"); if(this.HorizontalFlip) {
_ = sb.Append(" -hf");
}
if (VerticalFlip) sb.Append(" -vf"); if(this.VerticalFlip) {
_ = sb.Append(" -vf");
}
return sb.ToString(); return sb.ToString();
} }

View File

@ -1,43 +1,42 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan;
using System.Text; using System.Text;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// Represents the raspivid camera settings for video capture functionality /// Represents the raspivid camera settings for video capture functionality
/// </summary> /// </summary>
/// <seealso cref="CameraSettingsBase" /> /// <seealso cref="CameraSettingsBase" />
public class CameraVideoSettings : CameraSettingsBase public class CameraVideoSettings : CameraSettingsBase {
{
/// <inheritdoc /> /// <inheritdoc />
public override string CommandName => "raspivid"; public override String CommandName => "raspivid";
/// <summary> /// <summary>
/// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more. /// 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. /// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30.
/// Default -1 /// Default -1
/// </summary> /// </summary>
public int CaptureBitrate { get; set; } = -1; public Int32 CaptureBitrate { get; set; } = -1;
/// <summary> /// <summary>
/// Gets or sets the framerate. /// Gets or sets the framerate.
/// Default 25, range 2 to 30 /// Default 25, range 2 to 30
/// </summary> /// </summary>
public int CaptureFramerate { get; set; } = 25; public Int32 CaptureFramerate { get; set; } = 25;
/// <summary> /// <summary>
/// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra /// 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. /// 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. /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone.
/// </summary> /// </summary>
public int CaptureKeyframeRate { get; set; } = 25; public Int32 CaptureKeyframeRate { get; set; } = 25;
/// <summary> /// <summary>
/// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect /// 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 /// 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. /// bitrate of 0 to set a completely variable bitrate.
/// </summary> /// </summary>
public int CaptureQuantisation { get; set; } = 23; public Int32 CaptureQuantisation { get; set; } = 23;
/// <summary> /// <summary>
/// Gets or sets the profile. /// Gets or sets the profile.
@ -53,7 +52,7 @@
/// <value> /// <value>
/// <c>true</c> if [interleave headers]; otherwise, <c>false</c>. /// <c>true</c> if [interleave headers]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool CaptureInterleaveHeaders { get; set; } = true; public Boolean CaptureInterleaveHeaders { get; set; } = true;
/// <summary> /// <summary>
/// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation, /// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
@ -62,31 +61,36 @@
/// <value> /// <value>
/// <c>true</c> if [capture display preview encoded]; otherwise, <c>false</c>. /// <c>true</c> if [capture display preview encoded]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool CaptureDisplayPreviewEncoded { get; set; } = false; public Boolean CaptureDisplayPreviewEncoded { get; set; } = false;
/// <inheritdoc /> /// <inheritdoc />
public override string CreateProcessArguments() public override String CreateProcessArguments() {
{ StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
var sb = new StringBuilder(base.CreateProcessArguments());
sb.Append($" -pf {CaptureProfile.ToString().ToLowerInvariant()}"); _ = sb.Append($" -pf {this.CaptureProfile.ToString().ToLowerInvariant()}");
if (CaptureBitrate < 0) if(this.CaptureBitrate < 0) {
sb.Append($" -b {CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}"); _ = sb.Append($" -b {this.CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}");
}
if (CaptureFramerate >= 2) if(this.CaptureFramerate >= 2) {
sb.Append($" -fps {CaptureFramerate.Clamp(2, 30).ToString(Ci)}"); _ = sb.Append($" -fps {this.CaptureFramerate.Clamp(2, 30).ToString(Ci)}");
}
if (CaptureDisplayPreview && CaptureDisplayPreviewEncoded) if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewEncoded) {
sb.Append(" -e"); _ = sb.Append(" -e");
}
if (CaptureKeyframeRate > 0) if(this.CaptureKeyframeRate > 0) {
sb.Append($" -g {CaptureKeyframeRate.ToString(Ci)}"); _ = sb.Append($" -g {this.CaptureKeyframeRate.ToString(Ci)}");
}
if (CaptureQuantisation >= 0) if(this.CaptureQuantisation >= 0) {
sb.Append($" -qp {CaptureQuantisation.Clamp(0, 40).ToString(Ci)}"); _ = sb.Append($" -qp {this.CaptureQuantisation.Clamp(0, 40).ToString(Ci)}");
}
if (CaptureInterleaveHeaders) if(this.CaptureInterleaveHeaders) {
sb.Append(" -ih"); _ = sb.Append(" -ih");
}
return sb.ToString(); return sb.ToString();
} }

View File

@ -1,12 +1,10 @@
namespace Unosquare.RaspberryIO.Camera using System;
{
using System;
namespace Unosquare.RaspberryIO.Camera {
/// <summary> /// <summary>
/// Defines the available encoding formats for the Raspberry Pi camera module /// Defines the available encoding formats for the Raspberry Pi camera module
/// </summary> /// </summary>
public enum CameraImageEncodingFormat public enum CameraImageEncodingFormat {
{
/// <summary> /// <summary>
/// The JPG /// The JPG
/// </summary> /// </summary>
@ -31,8 +29,7 @@
/// <summary> /// <summary>
/// Defines the different exposure modes for the Raspberry Pi's camera module /// Defines the different exposure modes for the Raspberry Pi's camera module
/// </summary> /// </summary>
public enum CameraExposureMode public enum CameraExposureMode {
{
/// <summary> /// <summary>
/// The automatic /// The automatic
/// </summary> /// </summary>
@ -97,8 +94,7 @@
/// <summary> /// <summary>
/// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module /// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module
/// </summary> /// </summary>
public enum CameraWhiteBalanceMode public enum CameraWhiteBalanceMode {
{
/// <summary> /// <summary>
/// No white balance /// No white balance
/// </summary> /// </summary>
@ -153,8 +149,7 @@
/// <summary> /// <summary>
/// Defines the available image effects for the Raspberry Pi's camera module /// Defines the available image effects for the Raspberry Pi's camera module
/// </summary> /// </summary>
public enum CameraImageEffect public enum CameraImageEffect {
{
/// <summary> /// <summary>
/// No effect /// No effect
/// </summary> /// </summary>
@ -264,8 +259,7 @@
/// <summary> /// <summary>
/// Defines the different metering modes for the Raspberry Pi's camera module /// Defines the different metering modes for the Raspberry Pi's camera module
/// </summary> /// </summary>
public enum CameraMeteringMode public enum CameraMeteringMode {
{
/// <summary> /// <summary>
/// The average /// The average
/// </summary> /// </summary>
@ -290,8 +284,7 @@
/// <summary> /// <summary>
/// Defines the different image rotation modes for the Raspberry Pi's camera module /// Defines the different image rotation modes for the Raspberry Pi's camera module
/// </summary> /// </summary>
public enum CameraImageRotation public enum CameraImageRotation {
{
/// <summary> /// <summary>
/// No rerotation /// No rerotation
/// </summary> /// </summary>
@ -317,8 +310,7 @@
/// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module /// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module
/// Helpful for low light photos /// Helpful for low light photos
/// </summary> /// </summary>
public enum CameraDynamicRangeCompensation public enum CameraDynamicRangeCompensation {
{
/// <summary> /// <summary>
/// The off setting /// The off setting
/// </summary> /// </summary>
@ -344,8 +336,7 @@
/// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module /// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module
/// </summary> /// </summary>
[Flags] [Flags]
public enum CameraAnnotation public enum CameraAnnotation {
{
/// <summary> /// <summary>
/// The none /// The none
/// </summary> /// </summary>
@ -400,8 +391,7 @@
/// <summary> /// <summary>
/// Defines the different H.264 encoding profiles to be used when capturing video. /// Defines the different H.264 encoding profiles to be used when capturing video.
/// </summary> /// </summary>
public enum CameraH264Profile public enum CameraH264Profile {
{
/// <summary> /// <summary>
/// BP: Primarily for lower-cost applications with limited computing resources, /// BP: Primarily for lower-cost applications with limited computing resources,
/// this profile is used widely in videoconferencing and mobile applications. /// this profile is used widely in videoconferencing and mobile applications.

View File

@ -1,24 +1,22 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.Swan.Abstractions;
{
using Swan.Abstractions;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System;
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// The Official Raspberry Pi 7-inch touch display from the foundation /// The Official Raspberry Pi 7-inch touch display from the foundation
/// Some docs available here: /// Some docs available here:
/// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959 /// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959
/// </summary> /// </summary>
public class DsiDisplay : SingletonBase<DsiDisplay> public class DsiDisplay : SingletonBase<DsiDisplay> {
{ private const String BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power";
private const string BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power"; private const String BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
private const string BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created. /// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created.
/// </summary> /// </summary>
private DsiDisplay() private DsiDisplay() {
{
// placeholder // placeholder
} }
@ -28,7 +26,7 @@
/// <value> /// <value>
/// <c>true</c> if this instance is present; otherwise, <c>false</c>. /// <c>true</c> if this instance is present; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsPresent => File.Exists(BrightnessFilename); public Boolean IsPresent => File.Exists(BrightnessFilename);
/// <summary> /// <summary>
/// Gets or sets the brightness of the DSI display via filesystem. /// Gets or sets the brightness of the DSI display via filesystem.
@ -36,17 +34,13 @@
/// <value> /// <value>
/// The brightness. /// The brightness.
/// </value> /// </value>
public byte Brightness public Byte Brightness {
{ get => this.IsPresent == false ? (Byte)0 : Byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out Byte brightness) ? brightness : (Byte)0;
get set {
{ if(this.IsPresent == false) {
if (IsPresent == false) return 0; return;
return byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out var brightness) ? brightness : (byte)0;
} }
set
{
if (IsPresent == false) return;
File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture)); File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture));
} }
} }
@ -58,20 +52,12 @@
/// <value> /// <value>
/// <c>true</c> if this instance is backlight on; otherwise, <c>false</c>. /// <c>true</c> if this instance is backlight on; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsBacklightOn public Boolean IsBacklightOn {
{ get => this.IsPresent == false ? false : Int32.TryParse(File.ReadAllText(BacklightFilename).Trim(), out Int32 backlight) ? backlight == 0 : false;
get set {
{ if(this.IsPresent == false) {
if (IsPresent == false) return false; return;
if (int.TryParse(File.ReadAllText(BacklightFilename).Trim(), out var backlight))
return backlight == 0;
return false;
} }
set
{
if (IsPresent == false) return;
File.WriteAllText(BacklightFilename, value ? "0" : "1"); File.WriteAllText(BacklightFilename, value ? "0" : "1");
} }

View File

@ -1,40 +1,51 @@
namespace Unosquare.RaspberryIO.Computer using System;
{
using System.Net; using System.Net;
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// Represents a Network Adapter /// Represents a Network Adapter
/// </summary> /// </summary>
public class NetworkAdapterInfo public class NetworkAdapterInfo {
{
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>
public string Name { get; internal set; } public String Name {
get; internal set;
}
/// <summary> /// <summary>
/// Gets the IP V4 address. /// Gets the IP V4 address.
/// </summary> /// </summary>
public IPAddress IPv4 { get; internal set; } public IPAddress IPv4 {
get; internal set;
}
/// <summary> /// <summary>
/// Gets the IP V6 address. /// Gets the IP V6 address.
/// </summary> /// </summary>
public IPAddress IPv6 { get; internal set; } public IPAddress IPv6 {
get; internal set;
}
/// <summary> /// <summary>
/// Gets the name of the access point. /// Gets the name of the access point.
/// </summary> /// </summary>
public string AccessPointName { get; internal set; } public String AccessPointName {
get; internal set;
}
/// <summary> /// <summary>
/// Gets the MAC (Physical) address. /// Gets the MAC (Physical) address.
/// </summary> /// </summary>
public string MacAddress { get; internal set; } public String MacAddress {
get; internal set;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is wireless. /// Gets a value indicating whether this instance is wireless.
/// </summary> /// </summary>
public bool IsWireless { get; internal set; } public Boolean IsWireless {
get; internal set;
}
} }
} }

View File

@ -1,8 +1,6 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.Swan;
{ using Unosquare.Swan.Abstractions;
using Swan; using Unosquare.Swan.Components;
using Swan.Abstractions;
using Swan.Components;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -10,86 +8,85 @@
using System.Net; using System.Net;
using System.Text; using System.Text;
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// Represents the network information /// Represents the network information
/// </summary> /// </summary>
public class NetworkSettings : SingletonBase<NetworkSettings> public class NetworkSettings : SingletonBase<NetworkSettings> {
{ private const String EssidTag = "ESSID:";
private const string EssidTag = "ESSID:";
/// <summary> /// <summary>
/// Gets the local machine Host Name. /// Gets the local machine Host Name.
/// </summary> /// </summary>
public string HostName => Network.HostName; public String HostName => Network.HostName;
/// <summary> /// <summary>
/// Retrieves the wireless networks. /// Retrieves the wireless networks.
/// </summary> /// </summary>
/// <param name="adapter">The adapter.</param> /// <param name="adapter">The adapter.</param>
/// <returns>A list of WiFi networks</returns> /// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter }); public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String adapter) => this.RetrieveWirelessNetworks(new[] { adapter });
/// <summary> /// <summary>
/// Retrieves the wireless networks. /// Retrieves the wireless networks.
/// </summary> /// </summary>
/// <param name="adapters">The adapters.</param> /// <param name="adapters">The adapters.</param>
/// <returns>A list of WiFi networks</returns> /// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string[] adapters = null) public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String[] adapters = null) {
{ List<WirelessNetworkInfo> result = new List<WirelessNetworkInfo>();
var result = new List<WirelessNetworkInfo>();
foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) foreach(String networkAdapter in adapters ?? this.RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) {
{ String wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result; String[] outputLines =
var outputLines =
wirelessOutput.Split('\n') wirelessOutput.Split('\n')
.Select(x => x.Trim()) .Select(x => x.Trim())
.Where(x => string.IsNullOrWhiteSpace(x) == false) .Where(x => String.IsNullOrWhiteSpace(x) == false)
.ToArray(); .ToArray();
for (var i = 0; i < outputLines.Length; i++) for(Int32 i = 0; i < outputLines.Length; i++) {
{ String line = outputLines[i];
var line = outputLines[i];
if (line.StartsWith(EssidTag) == false) continue; if(line.StartsWith(EssidTag) == false) {
continue;
}
var network = new WirelessNetworkInfo() WirelessNetworkInfo network = new WirelessNetworkInfo() {
{ Name = line.Replace(EssidTag, String.Empty).Replace("\"", String.Empty)
Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty)
}; };
while (true) while(true) {
{ if(i + 1 >= outputLines.Length) {
if (i + 1 >= outputLines.Length) break; break;
}
// should look for two lines before the ESSID acording to the scan // should look for two lines before the ESSID acording to the scan
line = outputLines[i - 2]; line = outputLines[i - 2];
if (line.StartsWith("Quality=")) if(line.StartsWith("Quality=")) {
{ network.Quality = line.Replace("Quality=", String.Empty);
network.Quality = line.Replace("Quality=", string.Empty);
break; break;
} }
} }
while (true) while(true) {
{ if(i + 1 >= outputLines.Length) {
if (i + 1 >= outputLines.Length) break; break;
}
// should look for a line before the ESSID acording to the scan // should look for a line before the ESSID acording to the scan
line = outputLines[i - 1]; line = outputLines[i - 1];
if (line.StartsWith("Encryption key:")) if(line.StartsWith("Encryption key:")) {
{ network.IsEncrypted = line.Replace("Encryption key:", String.Empty).Trim() == "on";
network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on";
break; break;
} }
} }
if (result.Any(x => x.Name == network.Name) == false) if(result.Any(x => x.Name == network.Name) == false) {
result.Add(network); result.Add(network);
} }
} }
}
return result.OrderBy(x => x.Name).ToList(); return result.OrderBy(x => x.Name).ToList();
} }
@ -102,22 +99,18 @@
/// <param name="password">The password.</param> /// <param name="password">The password.</param>
/// <param name="countryCode">The 2-letter country code in uppercase. Default is US.</param> /// <param name="countryCode">The 2-letter country code in uppercase. Default is US.</param>
/// <returns>True if successful. Otherwise, false.</returns> /// <returns>True if successful. Otherwise, false.</returns>
public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US") public Boolean 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 // 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"; String payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
payload += string.IsNullOrEmpty(password) payload += String.IsNullOrEmpty(password)
? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n" ? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n"
: $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n"; : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
try try {
{
File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload); File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait(); ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait();
ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait(); ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait();
ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait(); ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait();
} } catch(Exception ex) {
catch (Exception ex)
{
ex.Log(nameof(NetworkSettings)); ex.Log(nameof(NetworkSettings));
return false; return false;
} }
@ -129,99 +122,92 @@
/// Retrieves the network adapters. /// Retrieves the network adapters.
/// </summary> /// </summary>
/// <returns>A list of network adapters.</returns> /// <returns>A list of network adapters.</returns>
public List<NetworkAdapterInfo> RetrieveAdapters() public List<NetworkAdapterInfo> RetrieveAdapters() {
{ const String hWaddr = "HWaddr ";
const string hWaddr = "HWaddr "; const String ether = "ether ";
const string ether = "ether ";
var result = new List<NetworkAdapterInfo>(); List<NetworkAdapterInfo> result = new List<NetworkAdapterInfo>();
var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result; String interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result;
var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig") String[] wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig")
.Result.Split('\n') .Result.Split('\n')
.Where(x => x.Contains("no wireless extensions.") == false) .Where(x => x.Contains("no wireless extensions.") == false)
.ToArray(); .ToArray();
var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray(); String[] outputLines = interfacesOutput.Split('\n').Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray();
for (var i = 0; i < outputLines.Length; i++) for(Int32 i = 0; i < outputLines.Length; i++) {
{
// grab the current line // grab the current line
var line = outputLines[i]; String line = outputLines[i];
// skip if the line is indented // skip if the line is indented
if (char.IsLetterOrDigit(line[0]) == false) if(Char.IsLetterOrDigit(line[0]) == false) {
continue; continue;
}
// Read the line as an adatper // Read the line as an adatper
var adapter = new NetworkAdapterInfo NetworkAdapterInfo adapter = new NetworkAdapterInfo {
{
Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':') Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
}; };
// Parse the MAC address in old version of ifconfig; it comes in the first line // Parse the MAC address in old version of ifconfig; it comes in the first line
if (line.IndexOf(hWaddr) >= 0) if(line.IndexOf(hWaddr) >= 0) {
{ Int32 startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim(); adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
} }
// Parse the info in lines other than the first // Parse the info in lines other than the first
for (var j = i + 1; j < outputLines.Length; j++) for(Int32 j = i + 1; j < outputLines.Length; j++) {
{
// Get the contents of the indented line // Get the contents of the indented line
var indentedLine = outputLines[j]; String indentedLine = outputLines[j];
// We have hit the next adapter info // We have hit the next adapter info
if (char.IsLetterOrDigit(indentedLine[0])) if(Char.IsLetterOrDigit(indentedLine[0])) {
{
i = j - 1; i = j - 1;
break; break;
} }
// Parse the MAC address in new versions of ifconfig; it no longer comes in the first line // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress)) if(indentedLine.IndexOf(ether) >= 0 && String.IsNullOrWhiteSpace(adapter.MacAddress)) {
{ Int32 startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim(); adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim();
} }
// Parse the IPv4 Address // Parse the IPv4 Address
{ {
var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet "); String addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
if (addressText != null) if(addressText != null) {
{ if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
if (IPAddress.TryParse(addressText, out var outValue))
adapter.IPv4 = outValue; adapter.IPv4 = outValue;
} }
} }
}
// Parse the IPv6 Address // Parse the IPv6 Address
{ {
var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 "); String addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
if (addressText != null) if(addressText != null) {
{ if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
if (IPAddress.TryParse(addressText, out var outValue))
adapter.IPv6 = outValue; adapter.IPv6 = outValue;
} }
} }
}
// we have hit the end of the output in an indented line // we have hit the end of the output in an indented line
if (j >= outputLines.Length - 1) if(j >= outputLines.Length - 1) {
i = outputLines.Length; i = outputLines.Length;
} }
}
// Retrieve the wireless LAN info // Retrieve the wireless LAN info
var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name)); String wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
if (wlanInfo != null) if(wlanInfo != null) {
{
adapter.IsWireless = true; adapter.IsWireless = true;
var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries); String[] essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
if (essidParts.Length >= 2) if(essidParts.Length >= 2) {
{ adapter.AccessPointName = essidParts[1].Replace("\"", String.Empty).Trim();
adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim();
} }
} }
@ -236,7 +222,7 @@
/// Retrieves current wireless connected network name. /// Retrieves current wireless connected network name.
/// </summary> /// </summary>
/// <returns>The connected network name.</returns> /// <returns>The connected network name.</returns>
public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result; public String GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result;
/// <summary> /// <summary>
/// Parses the output tag from the given line. /// Parses the output tag from the given line.
@ -244,20 +230,20 @@
/// <param name="indentedLine">The indented line.</param> /// <param name="indentedLine">The indented line.</param>
/// <param name="tagName">Name of the tag.</param> /// <param name="tagName">Name of the tag.</param>
/// <returns>The value after the tag identifier</returns> /// <returns>The value after the tag identifier</returns>
private static string ParseOutputTagFromLine(string indentedLine, string tagName) private static String ParseOutputTagFromLine(String indentedLine, String tagName) {
{ if(indentedLine.IndexOf(tagName) < 0) {
if (indentedLine.IndexOf(tagName) < 0)
return null; return null;
}
var startIndex = indentedLine.IndexOf(tagName) + tagName.Length; Int32 startIndex = indentedLine.IndexOf(tagName) + tagName.Length;
var builder = new StringBuilder(1024); StringBuilder builder = new StringBuilder(1024);
for (var c = startIndex; c < indentedLine.Length; c++) for(Int32 c = startIndex; c < indentedLine.Length; c++) {
{ Char currentChar = indentedLine[c];
var currentChar = indentedLine[c]; if(!Char.IsPunctuation(currentChar) && !Char.IsLetterOrDigit(currentChar)) {
if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar))
break; break;
}
builder.Append(currentChar); _ = builder.Append(currentChar);
} }
return builder.ToString(); return builder.ToString();

View File

@ -1,46 +1,58 @@
namespace Unosquare.RaspberryIO.Computer using System;
{
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// Represents the OS Information /// Represents the OS Information
/// </summary> /// </summary>
public class OsInfo public class OsInfo {
{
/// <summary> /// <summary>
/// System name /// System name
/// </summary> /// </summary>
public string SysName { get; set; } public String SysName {
get; set;
}
/// <summary> /// <summary>
/// Node name /// Node name
/// </summary> /// </summary>
public string NodeName { get; set; } public String NodeName {
get; set;
}
/// <summary> /// <summary>
/// Release level /// Release level
/// </summary> /// </summary>
public string Release { get; set; } public String Release {
get; set;
}
/// <summary> /// <summary>
/// Version level /// Version level
/// </summary> /// </summary>
public string Version { get; set; } public String Version {
get; set;
}
/// <summary> /// <summary>
/// Hardware level /// Hardware level
/// </summary> /// </summary>
public string Machine { get; set; } public String Machine {
get; set;
}
/// <summary> /// <summary>
/// Domain name /// Domain name
/// </summary> /// </summary>
public string DomainName { get; set; } public String DomainName {
get; set;
}
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Returns a <see cref="String" /> that represents this instance.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A <see cref="string" /> that represents this instance. /// A <see cref="String" /> that represents this instance.
/// </returns> /// </returns>
public override string ToString() => $"{SysName} {Release} {Version}"; public override String ToString() => $"{this.SysName} {this.Release} {this.Version}";
} }
} }

View File

@ -1,11 +1,9 @@
namespace Unosquare.RaspberryIO.Computer namespace Unosquare.RaspberryIO.Computer {
{
/// <summary> /// <summary>
/// Defines the board revision codes of the different versions of the Raspberry Pi /// 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/ /// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/
/// </summary> /// </summary>
public enum PiVersion public enum PiVersion {
{
/// <summary> /// <summary>
/// The unknown version /// The unknown version
/// </summary> /// </summary>

View File

@ -1,7 +1,5 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan.Abstractions;
using Native;
using Swan.Abstractions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -9,68 +7,64 @@
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html
/// </summary> /// </summary>
public sealed class SystemInfo : SingletonBase<SystemInfo> public sealed class SystemInfo : SingletonBase<SystemInfo> {
{ private const String CpuInfoFilePath = "/proc/cpuinfo";
private const string CpuInfoFilePath = "/proc/cpuinfo"; private const String MemInfoFilePath = "/proc/meminfo";
private const string MemInfoFilePath = "/proc/meminfo"; private const String UptimeFilePath = "/proc/uptime";
private const string UptimeFilePath = "/proc/uptime";
private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase; private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
private static readonly object SyncRoot = new object(); private static readonly Object SyncRoot = new Object();
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="SystemInfo"/> class from being created. /// Prevents a default instance of the <see cref="SystemInfo"/> class from being created.
/// </summary> /// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception> /// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
private SystemInfo() private SystemInfo() {
{
#region Obtain and format a property dictionary #region Obtain and format a property dictionary
var properties = PropertyInfo[] properties =
typeof(SystemInfo).GetTypeInfo() typeof(SystemInfo).GetTypeInfo()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where( .Where(
p => p =>
p.CanWrite && p.CanRead && p.CanWrite && p.CanRead &&
(p.PropertyType == typeof(string) || p.PropertyType == typeof(string[]))) (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[])))
.ToArray(); .ToArray();
var propDictionary = new Dictionary<string, PropertyInfo>(StringComparer); Dictionary<String, PropertyInfo> propDictionary = new Dictionary<String, PropertyInfo>(StringComparer);
foreach (var prop in properties) foreach(PropertyInfo prop in properties) {
{ propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop;
propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop;
} }
#endregion #endregion
#region Extract CPU information #region Extract CPU information
if (File.Exists(CpuInfoFilePath)) if(File.Exists(CpuInfoFilePath)) {
{ String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
foreach (var line in cpuInfoLines) foreach(String line in cpuInfoLines) {
{ String[] lineParts = line.Split(new[] { ':' }, 2);
var lineParts = line.Split(new[] { ':' }, 2); if(lineParts.Length != 2) {
if (lineParts.Length != 2)
continue; 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[]))
{ String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty);
var propertyArrayAvalue = propertyStringValue.Split(' '); 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[] propertyArrayAvalue = propertyStringValue.Split(' ');
property.SetValue(this, propertyArrayAvalue); property.SetValue(this, propertyArrayAvalue);
} }
} }
@ -80,23 +74,22 @@
#region Extract Memory Information #region Extract Memory Information
if (File.Exists(MemInfoFilePath)) if(File.Exists(MemInfoFilePath)) {
{ String[] memInfoLines = File.ReadAllLines(MemInfoFilePath);
var memInfoLines = File.ReadAllLines(MemInfoFilePath); foreach(String line in memInfoLines) {
foreach (var line in memInfoLines) String[] lineParts = line.Split(new[] { ':' }, 2);
{ if(lineParts.Length != 2) {
var lineParts = line.Split(new[] { ':' }, 2);
if (lineParts.Length != 2)
continue; continue;
}
if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) {
continue; continue;
}
var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim(); String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim();
if (int.TryParse(memKb, out var parsedMem)) if(Int32.TryParse(memKb, out Int32 parsedMem)) {
{ this.InstalledRam = parsedMem * 1024;
InstalledRam = parsedMem * 1024;
break; break;
} }
} }
@ -106,26 +99,21 @@
#region Board Version and Form Factor #region Board Version and Form Factor
try try {
{ if(String.IsNullOrWhiteSpace(this.Revision) == false &&
if (string.IsNullOrWhiteSpace(Revision) == false && Int32.TryParse(
int.TryParse( this.Revision.ToUpperInvariant(),
Revision.ToUpperInvariant(),
NumberStyles.HexNumber, NumberStyles.HexNumber,
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
out var boardVersion)) out Int32 boardVersion)) {
{ this.RaspberryPiVersion = PiVersion.Unknown;
RaspberryPiVersion = PiVersion.Unknown; if(Enum.GetValues(typeof(PiVersion)).Cast<Int32>().Contains(boardVersion)) {
if (Enum.GetValues(typeof(PiVersion)).Cast<int>().Contains(boardVersion)) this.RaspberryPiVersion = (PiVersion)boardVersion;
{
RaspberryPiVersion = (PiVersion)boardVersion;
} }
} }
WiringPiBoardRevision = WiringPi.PiBoardRev(); this.WiringPiBoardRevision = WiringPi.PiBoardRev();
} } catch {
catch
{
/* Ignore */ /* Ignore */
} }
@ -134,22 +122,20 @@
#region Version Information #region Version Information
{ {
var libParts = WiringPi.WiringPiLibrary.Split('.'); String[] libParts = WiringPi.WiringPiLibrary.Split('.');
var major = int.Parse(libParts[libParts.Length - 2]); Int32 major = Int32.Parse(libParts[libParts.Length - 2]);
var minor = int.Parse(libParts[libParts.Length - 1]); Int32 minor = Int32.Parse(libParts[libParts.Length - 1]);
var version = new Version(major, minor); Version version = new Version(major, minor);
WiringPiVersion = version; this.WiringPiVersion = version;
} }
#endregion #endregion
#region Extract OS Info #region Extract OS Info
try try {
{ _ = Standard.Uname(out SystemName unameInfo);
Standard.Uname(out var unameInfo); this.OperatingSystem = new OsInfo {
OperatingSystem = new OsInfo
{
DomainName = unameInfo.DomainName, DomainName = unameInfo.DomainName,
Machine = unameInfo.Machine, Machine = unameInfo.Machine,
NodeName = unameInfo.NodeName, NodeName = unameInfo.NodeName,
@ -157,10 +143,8 @@
SysName = unameInfo.SysName, SysName = unameInfo.SysName,
Version = unameInfo.Version Version = unameInfo.Version
}; };
} } catch {
catch this.OperatingSystem = new OsInfo();
{
OperatingSystem = new OsInfo();
} }
#endregion #endregion
@ -169,7 +153,9 @@
/// <summary> /// <summary>
/// Gets the wiring pi library version. /// Gets the wiring pi library version.
/// </summary> /// </summary>
public Version WiringPiVersion { get; } public Version WiringPiVersion {
get;
}
/// <summary> /// <summary>
/// Gets the OS information. /// Gets the OS information.
@ -177,12 +163,16 @@
/// <value> /// <value>
/// The os information. /// The os information.
/// </value> /// </value>
public OsInfo OperatingSystem { get; } public OsInfo OperatingSystem {
get;
}
/// <summary> /// <summary>
/// Gets the Raspberry Pi version. /// Gets the Raspberry Pi version.
/// </summary> /// </summary>
public PiVersion RaspberryPiVersion { get; } public PiVersion RaspberryPiVersion {
get;
}
/// <summary> /// <summary>
/// Gets the Wiring Pi board revision (1 or 2). /// Gets the Wiring Pi board revision (1 or 2).
@ -190,100 +180,112 @@
/// <value> /// <value>
/// The wiring pi board revision. /// The wiring pi board revision.
/// </value> /// </value>
public int WiringPiBoardRevision { get; } public Int32 WiringPiBoardRevision {
get;
}
/// <summary> /// <summary>
/// Gets the number of processor cores. /// Gets the number of processor cores.
/// </summary> /// </summary>
public int ProcessorCount public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0;
{
get
{
if (int.TryParse(Processor, out var outIndex))
{
return outIndex + 1;
}
return 0;
}
}
/// <summary> /// <summary>
/// Gets the installed ram in bytes. /// Gets the installed ram in bytes.
/// </summary> /// </summary>
public int InstalledRam { get; } public Int32 InstalledRam {
get;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this CPU is little endian. /// Gets a value indicating whether this CPU is little endian.
/// </summary> /// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian; public Boolean IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary> /// <summary>
/// Gets the CPU model name. /// Gets the CPU model name.
/// </summary> /// </summary>
public string ModelName { get; private set; } public String ModelName {
get; private set;
}
/// <summary> /// <summary>
/// Gets a list of supported CPU features. /// Gets a list of supported CPU features.
/// </summary> /// </summary>
public string[] Features { get; private set; } public String[] Features {
get; private set;
}
/// <summary> /// <summary>
/// Gets the CPU implementer hex code. /// Gets the CPU implementer hex code.
/// </summary> /// </summary>
public string CpuImplementer { get; private set; } public String CpuImplementer {
get; private set;
}
/// <summary> /// <summary>
/// Gets the CPU architecture code. /// Gets the CPU architecture code.
/// </summary> /// </summary>
public string CpuArchitecture { get; private set; } public String CpuArchitecture {
get; private set;
}
/// <summary> /// <summary>
/// Gets the CPU variant code. /// Gets the CPU variant code.
/// </summary> /// </summary>
public string CpuVariant { get; private set; } public String CpuVariant {
get; private set;
}
/// <summary> /// <summary>
/// Gets the CPU part code. /// Gets the CPU part code.
/// </summary> /// </summary>
public string CpuPart { get; private set; } public String CpuPart {
get; private set;
}
/// <summary> /// <summary>
/// Gets the CPU revision code. /// Gets the CPU revision code.
/// </summary> /// </summary>
public string CpuRevision { get; private set; } public String CpuRevision {
get; private set;
}
/// <summary> /// <summary>
/// Gets the hardware model number. /// Gets the hardware model number.
/// </summary> /// </summary>
public string Hardware { get; private set; } public String Hardware {
get; private set;
}
/// <summary> /// <summary>
/// Gets the hardware revision number. /// Gets the hardware revision number.
/// </summary> /// </summary>
public string Revision { get; private set; } public String Revision {
get; private set;
}
/// <summary> /// <summary>
/// Gets the serial number. /// Gets the serial number.
/// </summary> /// </summary>
public string Serial { get; private set; } public String Serial {
get; private set;
}
/// <summary> /// <summary>
/// Gets the system uptime (in seconds). /// Gets the system uptime (in seconds).
/// </summary> /// </summary>
public double Uptime public Double Uptime {
{ get {
get try {
{ if(File.Exists(UptimeFilePath) == false) {
try return 0;
{ }
if (File.Exists(UptimeFilePath) == false) return 0;
var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1 && float.TryParse(parts[0], out var result)) if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) {
return result; return result;
} }
catch } catch {
{
/* Ignore */ /* Ignore */
} }
@ -294,51 +296,48 @@
/// <summary> /// <summary>
/// Gets the uptime in TimeSpan. /// Gets the uptime in TimeSpan.
/// </summary> /// </summary>
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime); public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime);
/// <summary> /// <summary>
/// Placeholder for processor index /// Placeholder for processor index
/// </summary> /// </summary>
private string Processor { get; set; } private String Processor {
get; set;
}
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Returns a <see cref="String" /> that represents this instance.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A <see cref="string" /> that represents this instance. /// A <see cref="String" /> that represents this instance.
/// </returns> /// </returns>
public override string ToString() public override String ToString() {
{ PropertyInfo[] properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
var properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead && ( .Where(p => p.CanRead && (
p.PropertyType == typeof(string) || p.PropertyType == typeof(String) ||
p.PropertyType == typeof(string[]) || p.PropertyType == typeof(String[]) ||
p.PropertyType == typeof(int) || p.PropertyType == typeof(Int32) ||
p.PropertyType == typeof(bool) || p.PropertyType == typeof(Boolean) ||
p.PropertyType == typeof(TimeSpan))) p.PropertyType == typeof(TimeSpan)))
.ToArray(); .ToArray();
var properyValues = new List<string> List<String> properyValues = new List<String>
{ {
"System Information", "System Information",
$"\t{nameof(WiringPiVersion),-22}: {WiringPiVersion}", $"\t{nameof(this.WiringPiVersion),-22}: {this.WiringPiVersion}",
$"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}" $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}"
}; };
foreach (var property in properties) foreach(PropertyInfo property in properties) {
{ if(property.PropertyType != typeof(String[])) {
if (property.PropertyType != typeof(string[]))
{
properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}"); properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
} } else if(property.GetValue(this) is String[] allValues) {
else if (property.GetValue(this) is string[] allValues) String concatValues = String.Join(" ", allValues);
{
var concatValues = string.Join(" ", allValues);
properyValues.Add($"\t{property.Name,-22}: {concatValues}"); properyValues.Add($"\t{property.Name,-22}: {concatValues}");
} }
} }
return string.Join(Environment.NewLine, properyValues.ToArray()); return String.Join(Environment.NewLine, properyValues.ToArray());
} }
} }
} }

View File

@ -1,23 +1,29 @@
namespace Unosquare.RaspberryIO.Computer using System;
{
namespace Unosquare.RaspberryIO.Computer {
/// <summary> /// <summary>
/// Represents a wireless network information /// Represents a wireless network information
/// </summary> /// </summary>
public class WirelessNetworkInfo public class WirelessNetworkInfo {
{
/// <summary> /// <summary>
/// Gets the ESSID of the Wireless network. /// Gets the ESSID of the Wireless network.
/// </summary> /// </summary>
public string Name { get; internal set; } public String Name {
get; internal set;
}
/// <summary> /// <summary>
/// Gets the network quality. /// Gets the network quality.
/// </summary> /// </summary>
public string Quality { get; internal set; } public String Quality {
get; internal set;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is encrypted. /// Gets a value indicating whether this instance is encrypted.
/// </summary> /// </summary>
public bool IsEncrypted { get; internal set; } public Boolean IsEncrypted {
get; internal set;
}
} }
} }

View File

@ -1,10 +1,8 @@
namespace Unosquare.RaspberryIO.Gpio namespace Unosquare.RaspberryIO.Gpio {
{
/// <summary> /// <summary>
/// Defines the different drive modes of a GPIO pin /// Defines the different drive modes of a GPIO pin
/// </summary> /// </summary>
public enum GpioPinDriveMode public enum GpioPinDriveMode {
{
/// <summary> /// <summary>
/// Input drive mode (perform reads) /// Input drive mode (perform reads)
/// </summary> /// </summary>
@ -30,8 +28,7 @@
/// The GPIO pin resistor mode. This is used on input pins so that their /// The GPIO pin resistor mode. This is used on input pins so that their
/// lines are not floating /// lines are not floating
/// </summary> /// </summary>
public enum GpioPinResistorPullMode public enum GpioPinResistorPullMode {
{
/// <summary> /// <summary>
/// Pull resistor not active. Line floating /// Pull resistor not active. Line floating
/// </summary> /// </summary>
@ -51,8 +48,7 @@
/// <summary> /// <summary>
/// The PWM mode. /// The PWM mode.
/// </summary> /// </summary>
public enum PwmMode public enum PwmMode {
{
/// <summary> /// <summary>
/// PWM pulses are sent using mark-sign patterns (old school) /// PWM pulses are sent using mark-sign patterns (old school)
/// </summary> /// </summary>
@ -67,8 +63,7 @@
/// <summary> /// <summary>
/// Defines the different edge detection modes for pin interrupts /// Defines the different edge detection modes for pin interrupts
/// </summary> /// </summary>
public enum EdgeDetection public enum EdgeDetection {
{
/// <summary> /// <summary>
/// Assumes edge detection was already setup externally /// Assumes edge detection was already setup externally
/// </summary> /// </summary>
@ -93,8 +88,7 @@
/// <summary> /// <summary>
/// Defines the GPIO Pin values 0 for low, 1 for High /// Defines the GPIO Pin values 0 for low, 1 for High
/// </summary> /// </summary>
public enum GpioPinValue public enum GpioPinValue {
{
/// <summary> /// <summary>
/// Digital high /// Digital high
/// </summary> /// </summary>
@ -109,8 +103,7 @@
/// <summary> /// <summary>
/// Defines the Header connectors available /// Defines the Header connectors available
/// </summary> /// </summary>
public enum GpioHeader public enum GpioHeader {
{
/// <summary> /// <summary>
/// Not defined /// Not defined
/// </summary> /// </summary>
@ -130,8 +123,7 @@
/// <summary> /// <summary>
/// Defines all the available Wiring Pi Pin Numbers /// Defines all the available Wiring Pi Pin Numbers
/// </summary> /// </summary>
public enum WiringPiPin public enum WiringPiPin {
{
/// <summary> /// <summary>
/// The unknown /// The unknown
/// </summary> /// </summary>
@ -303,8 +295,7 @@
/// as commonly referenced by Raspberry Pi Documentation. /// as commonly referenced by Raspberry Pi Documentation.
/// Enumeration values correspond to the physical pin number. /// Enumeration values correspond to the physical pin number.
/// </summary> /// </summary>
public enum P1 public enum P1 {
{
/// <summary> /// <summary>
/// Header P1, GPIO Pin 02 /// Header P1, GPIO Pin 02
/// </summary> /// </summary>
@ -441,8 +432,7 @@
/// as commonly referenced by Raspberry Pi documentation. /// as commonly referenced by Raspberry Pi documentation.
/// Enumeration values correspond to the physical pin number. /// Enumeration values correspond to the physical pin number.
/// </summary> /// </summary>
public enum P5 public enum P5 {
{
/// <summary> /// <summary>
/// Header P5, GPIO Pin 28 /// Header P5, GPIO Pin 28
/// </summary> /// </summary>
@ -467,8 +457,7 @@
/// <summary> /// <summary>
/// Defines the different pin capabilities /// Defines the different pin capabilities
/// </summary> /// </summary>
public enum PinCapability public enum PinCapability {
{
/// <summary> /// <summary>
/// General Purpose capability: Digital and Analog Read/Write /// General Purpose capability: Digital and Analog Read/Write
/// </summary> /// </summary>
@ -533,8 +522,7 @@
/// <summary> /// <summary>
/// Defines the SPI channel numbers /// Defines the SPI channel numbers
/// </summary> /// </summary>
internal enum SpiChannelNumber internal enum SpiChannelNumber {
{
/// <summary> /// <summary>
/// The channel 0 /// The channel 0
/// </summary> /// </summary>
@ -549,8 +537,7 @@
/// <summary> /// <summary>
/// Defines GPIO controller initialization modes /// Defines GPIO controller initialization modes
/// </summary> /// </summary>
internal enum ControllerMode internal enum ControllerMode {
{
/// <summary> /// <summary>
/// The not initialized /// The not initialized
/// </summary> /// </summary>

View File

@ -1,8 +1,6 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native; using Unosquare.Swan.Abstractions;
using Swan;
using Swan.Abstractions;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -10,21 +8,18 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// Represents a singleton of the Raspberry Pi GPIO controller /// Represents a singleton of the Raspberry Pi GPIO controller
/// as an IReadOnlyCollection of GpioPins /// as an IReadOnlyCollection of GpioPins
/// Low level operations are accomplished by using the Wiring Pi library. /// Low level operations are accomplished by using the Wiring Pi library.
/// Use the Instance property to access the singleton's instance /// Use the Instance property to access the singleton's instance
/// </summary> /// </summary>
public sealed class GpioController : SingletonBase<GpioController>, IReadOnlyCollection<GpioPin> public sealed class GpioController : SingletonBase<GpioController>, IReadOnlyCollection<GpioPin> {
{
#region Private Declarations #region Private Declarations
private const string WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES"; private const String WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES";
private static readonly object SyncRoot = new object(); private static readonly Object SyncRoot = new Object();
private readonly ReadOnlyCollection<GpioPin> _pinCollection;
private readonly ReadOnlyDictionary<int, GpioPin> _headerP1Pins;
private readonly ReadOnlyDictionary<int, GpioPin> _headerP5Pins;
private readonly Dictionary<WiringPiPin, GpioPin> _pinsByWiringPiPinNumber = new Dictionary<WiringPiPin, GpioPin>(); private readonly Dictionary<WiringPiPin, GpioPin> _pinsByWiringPiPinNumber = new Dictionary<WiringPiPin, GpioPin>();
#endregion #endregion
@ -36,66 +31,65 @@
/// It in turn initializes the controller and registers the pin -- in that order. /// It in turn initializes the controller and registers the pin -- in that order.
/// </summary> /// </summary>
/// <exception cref="Exception">Unable to initialize the GPIO controller.</exception> /// <exception cref="Exception">Unable to initialize the GPIO controller.</exception>
private GpioController() private GpioController() {
{ if(this.Pins != null) {
if (_pinCollection != null)
return; return;
}
if (IsInitialized == false) if(IsInitialized == false) {
{ Boolean initResult = this.Initialize(ControllerMode.DirectWithWiringPiPins);
var initResult = Initialize(ControllerMode.DirectWithWiringPiPins); if(initResult == false) {
if (initResult == false)
throw new Exception("Unable to initialize the GPIO controller."); throw new Exception("Unable to initialize the GPIO controller.");
} }
}
#region Pin Registration (32 WiringPi Pins) #region Pin Registration (32 WiringPi Pins)
RegisterPin(GpioPin.Pin00.Value); this.RegisterPin(GpioPin.Pin00.Value);
RegisterPin(GpioPin.Pin01.Value); this.RegisterPin(GpioPin.Pin01.Value);
RegisterPin(GpioPin.Pin02.Value); this.RegisterPin(GpioPin.Pin02.Value);
RegisterPin(GpioPin.Pin03.Value); this.RegisterPin(GpioPin.Pin03.Value);
RegisterPin(GpioPin.Pin04.Value); this.RegisterPin(GpioPin.Pin04.Value);
RegisterPin(GpioPin.Pin05.Value); this.RegisterPin(GpioPin.Pin05.Value);
RegisterPin(GpioPin.Pin06.Value); this.RegisterPin(GpioPin.Pin06.Value);
RegisterPin(GpioPin.Pin07.Value); this.RegisterPin(GpioPin.Pin07.Value);
RegisterPin(GpioPin.Pin08.Value); this.RegisterPin(GpioPin.Pin08.Value);
RegisterPin(GpioPin.Pin09.Value); this.RegisterPin(GpioPin.Pin09.Value);
RegisterPin(GpioPin.Pin10.Value); this.RegisterPin(GpioPin.Pin10.Value);
RegisterPin(GpioPin.Pin11.Value); this.RegisterPin(GpioPin.Pin11.Value);
RegisterPin(GpioPin.Pin12.Value); this.RegisterPin(GpioPin.Pin12.Value);
RegisterPin(GpioPin.Pin13.Value); this.RegisterPin(GpioPin.Pin13.Value);
RegisterPin(GpioPin.Pin14.Value); this.RegisterPin(GpioPin.Pin14.Value);
RegisterPin(GpioPin.Pin15.Value); this.RegisterPin(GpioPin.Pin15.Value);
RegisterPin(GpioPin.Pin16.Value); this.RegisterPin(GpioPin.Pin16.Value);
RegisterPin(GpioPin.Pin17.Value); this.RegisterPin(GpioPin.Pin17.Value);
RegisterPin(GpioPin.Pin18.Value); this.RegisterPin(GpioPin.Pin18.Value);
RegisterPin(GpioPin.Pin19.Value); this.RegisterPin(GpioPin.Pin19.Value);
RegisterPin(GpioPin.Pin20.Value); this.RegisterPin(GpioPin.Pin20.Value);
RegisterPin(GpioPin.Pin21.Value); this.RegisterPin(GpioPin.Pin21.Value);
RegisterPin(GpioPin.Pin22.Value); this.RegisterPin(GpioPin.Pin22.Value);
RegisterPin(GpioPin.Pin23.Value); this.RegisterPin(GpioPin.Pin23.Value);
RegisterPin(GpioPin.Pin24.Value); this.RegisterPin(GpioPin.Pin24.Value);
RegisterPin(GpioPin.Pin25.Value); this.RegisterPin(GpioPin.Pin25.Value);
RegisterPin(GpioPin.Pin26.Value); this.RegisterPin(GpioPin.Pin26.Value);
RegisterPin(GpioPin.Pin27.Value); this.RegisterPin(GpioPin.Pin27.Value);
RegisterPin(GpioPin.Pin28.Value); this.RegisterPin(GpioPin.Pin28.Value);
RegisterPin(GpioPin.Pin29.Value); this.RegisterPin(GpioPin.Pin29.Value);
RegisterPin(GpioPin.Pin30.Value); this.RegisterPin(GpioPin.Pin30.Value);
RegisterPin(GpioPin.Pin31.Value); this.RegisterPin(GpioPin.Pin31.Value);
#endregion #endregion
_pinCollection = new ReadOnlyCollection<GpioPin>(_pinsByWiringPiPinNumber.Values.ToArray()); this.Pins = new ReadOnlyCollection<GpioPin>(this._pinsByWiringPiPinNumber.Values.ToArray());
var headerP1 = new Dictionary<int, GpioPin>(_pinCollection.Count); Dictionary<Int32, GpioPin> headerP1 = new Dictionary<Int32, GpioPin>(this.Pins.Count);
var headerP5 = new Dictionary<int, GpioPin>(_pinCollection.Count); Dictionary<Int32, GpioPin> headerP5 = new Dictionary<Int32, GpioPin>(this.Pins.Count);
foreach (var pin in _pinCollection) foreach(GpioPin pin in this.Pins) {
{ Dictionary<Int32, GpioPin> target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
var target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
target[pin.HeaderPinNumber] = pin; target[pin.HeaderPinNumber] = pin;
} }
_headerP1Pins = new ReadOnlyDictionary<int, GpioPin>(headerP1); this.HeaderP1 = new ReadOnlyDictionary<Int32, GpioPin>(headerP1);
_headerP5Pins = new ReadOnlyDictionary<int, GpioPin>(headerP5); this.HeaderP5 = new ReadOnlyDictionary<Int32, GpioPin>(headerP5);
} }
/// <summary> /// <summary>
@ -104,12 +98,9 @@
/// <value> /// <value>
/// <c>true</c> if the controller is properly initialized; otherwise, <c>false</c>. /// <c>true</c> if the controller is properly initialized; otherwise, <c>false</c>.
/// </value> /// </value>
public static bool IsInitialized public static Boolean IsInitialized {
{ get {
get lock(SyncRoot) {
{
lock (SyncRoot)
{
return Mode != ControllerMode.NotInitialized; return Mode != ControllerMode.NotInitialized;
} }
} }
@ -118,7 +109,7 @@
/// <summary> /// <summary>
/// Gets the number of registered pins in the controller. /// Gets the number of registered pins in the controller.
/// </summary> /// </summary>
public int Count => _pinCollection.Count; public Int32 Count => this.Pins.Count;
#endregion #endregion
@ -127,24 +118,30 @@
/// <summary> /// <summary>
/// Gets the PWM base frequency (in Hz). /// Gets the PWM base frequency (in Hz).
/// </summary> /// </summary>
public int PwmBaseFrequency => 19200000; public Int32 PwmBaseFrequency => 19200000;
/// <summary> /// <summary>
/// Gets a red-only collection of all registered pins. /// Gets a red-only collection of all registered pins.
/// </summary> /// </summary>
public ReadOnlyCollection<GpioPin> Pins => _pinCollection; public ReadOnlyCollection<GpioPin> Pins {
get;
}
/// <summary> /// <summary>
/// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number. /// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number.
/// This header is the main header and it is the one commonly used. /// This header is the main header and it is the one commonly used.
/// </summary> /// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP1 => _headerP1Pins; public ReadOnlyDictionary<Int32, GpioPin> HeaderP1 {
get;
}
/// <summary> /// <summary>
/// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number. /// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number.
/// This header is the secondary header and it is rarely used. /// This header is the secondary header and it is rarely used.
/// </summary> /// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP5 => _headerP5Pins; public ReadOnlyDictionary<Int32, GpioPin> HeaderP5 {
get;
}
#endregion #endregion
@ -327,7 +324,7 @@
/// </value> /// </value>
/// <param name="pinNumber">The pin number.</param> /// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns> /// <returns>A reference to the GPIO pin</returns>
public GpioPin this[WiringPiPin pinNumber] => _pinsByWiringPiPinNumber[pinNumber]; public GpioPin this[WiringPiPin pinNumber] => this._pinsByWiringPiPinNumber[pinNumber];
/// <summary> /// <summary>
/// Gets the <see cref="GpioPin"/> with the specified pin number. /// Gets the <see cref="GpioPin"/> with the specified pin number.
@ -337,7 +334,7 @@
/// </value> /// </value>
/// <param name="pinNumber">The pin number.</param> /// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns> /// <returns>A reference to the GPIO pin</returns>
public GpioPin this[P1 pinNumber] => HeaderP1[(int)pinNumber]; public GpioPin this[P1 pinNumber] => this.HeaderP1[(Int32)pinNumber];
/// <summary> /// <summary>
/// Gets the <see cref="GpioPin"/> with the specified pin number. /// Gets the <see cref="GpioPin"/> with the specified pin number.
@ -347,7 +344,7 @@
/// </value> /// </value>
/// <param name="pinNumber">The pin number.</param> /// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns> /// <returns>A reference to the GPIO pin</returns>
public GpioPin this[P5 pinNumber] => HeaderP5[(int)pinNumber]; public GpioPin this[P5 pinNumber] => this.HeaderP5[(Int32)pinNumber];
/// <summary> /// <summary>
/// Gets the <see cref="GpioPin"/> with the specified Wiring Pi pin number. /// Gets the <see cref="GpioPin"/> with the specified Wiring Pi pin number.
@ -359,14 +356,13 @@
/// <param name="wiringPiPinNumber">The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating.</param> /// <param name="wiringPiPinNumber">The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating.</param>
/// <returns>A reference to the GPIO pin</returns> /// <returns>A reference to the GPIO pin</returns>
/// <exception cref="IndexOutOfRangeException">When the pin index is not found</exception> /// <exception cref="IndexOutOfRangeException">When the pin index is not found</exception>
public GpioPin this[int wiringPiPinNumber] public GpioPin this[Int32 wiringPiPinNumber] {
{ get {
get if(Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false) {
{
if (Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false)
throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller."); throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller.");
}
return _pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber]; return this._pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber];
} }
} }
@ -381,11 +377,9 @@
/// </summary> /// </summary>
/// <param name="group">The group.</param> /// <param name="group">The group.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void SetPadDrive(int group, int value) public void SetPadDrive(Int32 group, Int32 value) {
{ lock(SyncRoot) {
lock (SyncRoot) _ = WiringPi.SetPadDrive(group, value);
{
WiringPi.SetPadDrive(group, value);
} }
} }
@ -397,7 +391,7 @@
/// <param name="group">The group.</param> /// <param name="group">The group.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task SetPadDriveAsync(int group, int value) => Task.Run(() => { SetPadDrive(group, value); }); public Task SetPadDriveAsync(Int32 group, Int32 value) => Task.Run(() => this.SetPadDrive(group, value));
/// <summary> /// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins. /// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@ -406,12 +400,9 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <exception cref="InvalidOperationException">PinMode</exception> /// <exception cref="InvalidOperationException">PinMode</exception>
public void WriteByte(byte value) public void WriteByte(Byte value) {
{ lock(SyncRoot) {
lock (SyncRoot) if(this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output)) {
{
if (this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output))
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}"); $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}");
} }
@ -427,7 +418,7 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteByteAsync(byte value) => Task.Run(() => { WriteByte(value); }); public Task WriteByteAsync(Byte value) => Task.Run(() => this.WriteByte(value));
/// <summary> /// <summary>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins. /// This reads the 8-bit byte supplied to the first 8 GPIO pins.
@ -436,18 +427,15 @@
/// </summary> /// </summary>
/// <returns>A byte from the GPIO</returns> /// <returns>A byte from the GPIO</returns>
/// <exception cref="InvalidOperationException">PinMode</exception> /// <exception cref="InvalidOperationException">PinMode</exception>
public byte ReadByte() public Byte ReadByte() {
{ lock(SyncRoot) {
lock (SyncRoot)
{
if(this.Skip(0).Take(8).Any(p => if(this.Skip(0).Take(8).Any(p =>
p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output)) p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output)) {
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
} }
return (byte)WiringPi.DigitalReadByte(); return (Byte)WiringPi.DigitalReadByte();
} }
} }
@ -457,7 +445,7 @@
/// Please note this function is undocumented and unsopported /// Please note this function is undocumented and unsopported
/// </summary> /// </summary>
/// <returns>A byte from the GPIO</returns> /// <returns>A byte from the GPIO</returns>
public Task<byte> ReadByteAsync() => Task.Run(() => ReadByte()); public Task<Byte> ReadByteAsync() => Task.Run(() => this.ReadByte());
#endregion #endregion
@ -469,7 +457,7 @@
/// <returns> /// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection. /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
/// </returns> /// </returns>
public IEnumerator<GpioPin> GetEnumerator() => _pinCollection.GetEnumerator(); public IEnumerator<GpioPin> GetEnumerator() => this.Pins.GetEnumerator();
/// <summary> /// <summary>
/// Returns an enumerator that iterates through the collection. /// Returns an enumerator that iterates through the collection.
@ -477,7 +465,7 @@
/// <returns> /// <returns>
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection. /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
/// </returns> /// </returns>
IEnumerator IEnumerable.GetEnumerator() => _pinCollection.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => this.Pins.GetEnumerator();
#endregion #endregion
@ -488,17 +476,15 @@
/// </summary> /// </summary>
/// <param name="bcmPinNumber">The BCM pin number.</param> /// <param name="bcmPinNumber">The BCM pin number.</param>
/// <returns>The GPIO pin</returns> /// <returns>The GPIO pin</returns>
public GpioPin GetGpioPinByBcmPinNumber(int bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber); public GpioPin GetGpioPinByBcmPinNumber(Int32 bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber);
/// <summary> /// <summary>
/// Converts the Wirings Pi pin number to the BCM pin number. /// Converts the Wirings Pi pin number to the BCM pin number.
/// </summary> /// </summary>
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param> /// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
/// <returns>The converted pin</returns> /// <returns>The converted pin</returns>
internal static int WiringPiToBcmPinNumber(int wiringPiPinNumber) internal static Int32 WiringPiToBcmPinNumber(Int32 wiringPiPinNumber) {
{ lock(SyncRoot) {
lock (SyncRoot)
{
return WiringPi.WpiPinToGpio(wiringPiPinNumber); return WiringPi.WpiPinToGpio(wiringPiPinNumber);
} }
} }
@ -508,10 +494,8 @@
/// </summary> /// </summary>
/// <param name="headerPinNumber">The header pin number.</param> /// <param name="headerPinNumber">The header pin number.</param>
/// <returns>The converted pin</returns> /// <returns>The converted pin</returns>
internal static int HaderToBcmPinNumber(int headerPinNumber) internal static Int32 HaderToBcmPinNumber(Int32 headerPinNumber) {
{ lock(SyncRoot) {
lock (SyncRoot)
{
return WiringPi.PhysPinToGpio(headerPinNumber); return WiringPi.PhysPinToGpio(headerPinNumber);
} }
} }
@ -520,13 +504,13 @@
/// Short-hand method of registering pins /// Short-hand method of registering pins
/// </summary> /// </summary>
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
private void RegisterPin(GpioPin pin) private void RegisterPin(GpioPin pin) {
{ if(this._pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false) {
if (_pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false) this._pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin;
_pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin; } else {
else
throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered"); throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered");
} }
}
/// <summary> /// <summary>
/// Initializes the controller given the initialization mode and pin numbering scheme /// Initializes the controller given the initialization mode and pin numbering scheme
@ -538,47 +522,41 @@
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException">Library was already Initialized</exception> /// <exception cref="InvalidOperationException">Library was already Initialized</exception>
/// <exception cref="ArgumentException">The init mode is invalid</exception> /// <exception cref="ArgumentException">The init mode is invalid</exception>
private bool Initialize(ControllerMode mode) private Boolean Initialize(ControllerMode mode) {
{ if(Runtime.OS != Swan.OperatingSystem.Unix) {
if (Runtime.OS != Swan.OperatingSystem.Unix)
throw new PlatformNotSupportedException("This library does not support the platform"); throw new PlatformNotSupportedException("This library does not support the platform");
}
lock (SyncRoot) lock(SyncRoot) {
{ if(IsInitialized) {
if (IsInitialized)
throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once."); throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once.");
}
Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1", EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1", EnvironmentVariableTarget.Process);
int setpuResult; Int32 setpuResult;
switch (mode) switch(mode) {
{ case ControllerMode.DirectWithWiringPiPins: {
case ControllerMode.DirectWithWiringPiPins:
{
setpuResult = WiringPi.WiringPiSetup(); setpuResult = WiringPi.WiringPiSetup();
break; break;
} }
case ControllerMode.DirectWithBcmPins: case ControllerMode.DirectWithBcmPins: {
{
setpuResult = WiringPi.WiringPiSetupGpio(); setpuResult = WiringPi.WiringPiSetupGpio();
break; break;
} }
case ControllerMode.DirectWithHeaderPins: case ControllerMode.DirectWithHeaderPins: {
{
setpuResult = WiringPi.WiringPiSetupPhys(); setpuResult = WiringPi.WiringPiSetupPhys();
break; break;
} }
case ControllerMode.FileStreamWithHardwarePins: case ControllerMode.FileStreamWithHardwarePins: {
{
setpuResult = WiringPi.WiringPiSetupSys(); setpuResult = WiringPi.WiringPiSetupSys();
break; break;
} }
default: default: {
{
throw new ArgumentException($"'{mode}' is not a valid initialization mode."); throw new ArgumentException($"'{mode}' is not a valid initialization mode.");
} }
} }

View File

@ -1,197 +1,163 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{
using System;
public partial class GpioPin namespace Unosquare.RaspberryIO.Gpio {
{ public partial class GpioPin {
#region Static Pin Definitions #region Static Pin Definitions
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3) internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
Name = "BCM 2 (SDA)" Name = "BCM 2 (SDA)"
}); });
internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5) internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
Name = "BCM 3 (SCL)" Name = "BCM 3 (SCL)"
}); });
internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7) internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK }, Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK },
Name = "BCM 4 (GPCLK0)" Name = "BCM 4 (GPCLK0)"
}); });
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11) internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS }, Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS },
Name = "BCM 17" Name = "BCM 17"
}); });
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13) internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 27" Name = "BCM 27"
}); });
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15) internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 22" Name = "BCM 22"
}); });
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19) internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
Name = "BCM 10 (MOSI)" Name = "BCM 10 (MOSI)"
}); });
internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21) internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
Name = "BCM 9 (MISO)" Name = "BCM 9 (MISO)"
}); });
internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23) internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
Name = "BCM 11 (SCLCK)" Name = "BCM 11 (SCLCK)"
}); });
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27) internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27) {
{
Capabilities = new[] { PinCapability.I2CSDA }, Capabilities = new[] { PinCapability.I2CSDA },
Name = "BCM 0 (ID_SD)" Name = "BCM 0 (ID_SD)"
}); });
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28) internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28) {
{
Capabilities = new[] { PinCapability.I2CSCL }, Capabilities = new[] { PinCapability.I2CSCL },
Name = "BCM 1 (ID_SC)" Name = "BCM 1 (ID_SC)"
}); });
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26) internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
Name = "BCM 7 (CE1)" Name = "BCM 7 (CE1)"
}); });
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24) internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
Name = "BCM 8 (CE0)" Name = "BCM 8 (CE0)"
}); });
internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22) internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 25" Name = "BCM 25"
}); });
internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18) internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 24" Name = "BCM 24"
}); });
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16) internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 23" Name = "BCM 23"
}); });
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12) internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
Name = "BCM 18 (PWM0)" Name = "BCM 18 (PWM0)"
}); });
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10) internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10) {
{
Capabilities = new[] { PinCapability.UARTRXD }, Capabilities = new[] { PinCapability.UARTRXD },
Name = "BCM 15 (RXD)" Name = "BCM 15 (RXD)"
}); });
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8) internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8) {
{
Capabilities = new[] { PinCapability.UARTTXD }, Capabilities = new[] { PinCapability.UARTTXD },
Name = "BCM 14 (TXD)" Name = "BCM 14 (TXD)"
}); });
internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29) internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 5" Name = "BCM 5"
}); });
internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31) internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 6" Name = "BCM 6"
}); });
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33) internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
Name = "BCM 13 (PWM1)" Name = "BCM 13 (PWM1)"
}); });
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35) internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
Name = "BCM 19 (MISO)" Name = "BCM 19 (MISO)"
}); });
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37) internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 26" Name = "BCM 26"
}); });
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40) internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
Name = "BCM 21 (SCLK)" Name = "BCM 21 (SCLK)"
}); });
internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38) internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
Name = "BCM 20 (MOSI)" Name = "BCM 20 (MOSI)"
}); });
internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36) internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 16" Name = "BCM 16"
}); });
internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32) internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 12 (PWM0)" Name = "BCM 12 (PWM0)"
}); });
internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin17, 3) internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin17, 3) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
Name = "BCM 28 (SDA)" Name = "BCM 28 (SDA)"
}); });
internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin18, 4) internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin18, 4) {
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
Name = "BCM 29 (SCL)" Name = "BCM 29 (SCL)"
}); });
internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin19, 5) internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin19, 5) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 30" Name = "BCM 30"
}); });
internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6) internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6) {
{
Capabilities = new[] { PinCapability.GP }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 31" Name = "BCM 31"
}); });

View File

@ -1,29 +1,27 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native;
using Swan;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// Represents a GPIO Pin, its location and its capabilities. /// Represents a GPIO Pin, its location and its capabilities.
/// Full pin reference available here: /// Full pin reference available here:
/// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/ /// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/
/// </summary> /// </summary>
public sealed partial class GpioPin public sealed partial class GpioPin {
{
#region Property Backing #region Property Backing
private readonly object _syncLock = new object(); private readonly Object _syncLock = new Object();
private GpioPinDriveMode m_PinMode; private GpioPinDriveMode m_PinMode;
private GpioPinResistorPullMode m_ResistorPullMode; private GpioPinResistorPullMode m_ResistorPullMode;
private int m_PwmRegister; private Int32 m_PwmRegister;
private PwmMode m_PwmMode = PwmMode.Balanced; private PwmMode m_PwmMode = PwmMode.Balanced;
private uint m_PwmRange = 1024; private UInt32 m_PwmRange = 1024;
private int m_PwmClockDivisor = 1; private Int32 m_PwmClockDivisor = 1;
private int m_SoftPwmValue = -1; private Int32 m_SoftPwmValue = -1;
private int m_SoftToneFrequency = -1; private Int32 m_SoftToneFrequency = -1;
#endregion #endregion
@ -34,13 +32,12 @@
/// </summary> /// </summary>
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param> /// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
/// <param name="headerPinNumber">The header pin number.</param> /// <param name="headerPinNumber">The header pin number.</param>
private GpioPin(WiringPiPin wiringPiPinNumber, int headerPinNumber) private GpioPin(WiringPiPin wiringPiPinNumber, Int32 headerPinNumber) {
{ this.PinNumber = (Int32)wiringPiPinNumber;
PinNumber = (int)wiringPiPinNumber; this.WiringPiPinNumber = wiringPiPinNumber;
WiringPiPinNumber = wiringPiPinNumber; this.BcmPinNumber = GpioController.WiringPiToBcmPinNumber((Int32)wiringPiPinNumber);
BcmPinNumber = GpioController.WiringPiToBcmPinNumber((int)wiringPiPinNumber); this.HeaderPinNumber = headerPinNumber;
HeaderPinNumber = headerPinNumber; this.Header = (this.PinNumber >= 17 && this.PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
Header = (PinNumber >= 17 && PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
} }
#endregion #endregion
@ -50,37 +47,51 @@
/// <summary> /// <summary>
/// Gets or sets the Wiring Pi pin number as an integer. /// Gets or sets the Wiring Pi pin number as an integer.
/// </summary> /// </summary>
public int PinNumber { get; } public Int32 PinNumber {
get;
}
/// <summary> /// <summary>
/// Gets the WiringPi Pin number /// Gets the WiringPi Pin number
/// </summary> /// </summary>
public WiringPiPin WiringPiPinNumber { get; } public WiringPiPin WiringPiPinNumber {
get;
}
/// <summary> /// <summary>
/// Gets the BCM chip (hardware) pin number. /// Gets the BCM chip (hardware) pin number.
/// </summary> /// </summary>
public int BcmPinNumber { get; } public Int32 BcmPinNumber {
get;
}
/// <summary> /// <summary>
/// Gets or the physical header (physical board) pin number. /// Gets or the physical header (physical board) pin number.
/// </summary> /// </summary>
public int HeaderPinNumber { get; } public Int32 HeaderPinNumber {
get;
}
/// <summary> /// <summary>
/// Gets the pin's header (physical board) location. /// Gets the pin's header (physical board) location.
/// </summary> /// </summary>
public GpioHeader Header { get; } public GpioHeader Header {
get;
}
/// <summary> /// <summary>
/// Gets the friendly name of the pin. /// Gets the friendly name of the pin.
/// </summary> /// </summary>
public string Name { get; private set; } public String Name {
get; private set;
}
/// <summary> /// <summary>
/// Gets the hardware mode capabilities of this pin. /// Gets the hardware mode capabilities of this pin.
/// </summary> /// </summary>
public PinCapability[] Capabilities { get; private set; } public PinCapability[] Capabilities {
get; private set;
}
#endregion #endregion
@ -93,26 +104,22 @@
/// The pin mode. /// The pin mode.
/// </value> /// </value>
/// <exception cref="NotSupportedException">Thrown when a pin does not support the given operation mode.</exception> /// <exception cref="NotSupportedException">Thrown when a pin does not support the given operation mode.</exception>
public GpioPinDriveMode PinMode public GpioPinDriveMode PinMode {
{ get => this.m_PinMode;
get => m_PinMode;
set set {
{ lock(this._syncLock) {
lock (_syncLock) GpioPinDriveMode mode = value;
{ if(mode == GpioPinDriveMode.GpioClock && this.Capabilities.Contains(PinCapability.GPCLK) == false ||
var mode = value; mode == GpioPinDriveMode.PwmOutput && this.Capabilities.Contains(PinCapability.PWM) == false ||
if ((mode == GpioPinDriveMode.GpioClock && Capabilities.Contains(PinCapability.GPCLK) == false) || mode == GpioPinDriveMode.Input && this.Capabilities.Contains(PinCapability.GP) == false ||
(mode == GpioPinDriveMode.PwmOutput && Capabilities.Contains(PinCapability.PWM) == false) || mode == GpioPinDriveMode.Output && this.Capabilities.Contains(PinCapability.GP) == false) {
(mode == GpioPinDriveMode.Input && Capabilities.Contains(PinCapability.GP) == false) ||
(mode == GpioPinDriveMode.Output && Capabilities.Contains(PinCapability.GP) == false))
{
throw new NotSupportedException( throw new NotSupportedException(
$"Pin {WiringPiPinNumber} '{Name}' does not support mode '{mode}'. Pin capabilities are limited to: {string.Join(", ", Capabilities)}"); $"Pin {this.WiringPiPinNumber} '{this.Name}' does not support mode '{mode}'. Pin capabilities are limited to: {String.Join(", ", this.Capabilities)}");
} }
WiringPi.PinMode(PinNumber, (int)mode); WiringPi.PinMode(this.PinNumber, (Int32)mode);
m_PinMode = mode; this.m_PinMode = mode;
} }
} }
} }
@ -121,7 +128,9 @@
/// Gets the interrupt callback. Returns null if no interrupt /// Gets the interrupt callback. Returns null if no interrupt
/// has been registered. /// has been registered.
/// </summary> /// </summary>
public InterruptServiceRoutineCallback InterruptCallback { get; private set; } public InterruptServiceRoutineCallback InterruptCallback {
get; private set;
}
/// <summary> /// <summary>
/// Gets the interrupt edge detection mode. /// Gets the interrupt edge detection mode.
@ -138,24 +147,20 @@
/// The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) /// The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v)
/// The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi. /// The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
/// </summary> /// </summary>
public GpioPinResistorPullMode InputPullMode public GpioPinResistorPullMode InputPullMode {
{ get => this.PinMode == GpioPinDriveMode.Input ? this.m_ResistorPullMode : GpioPinResistorPullMode.Off;
get => PinMode == GpioPinDriveMode.Input ? m_ResistorPullMode : GpioPinResistorPullMode.Off;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.Input) {
{ this.m_ResistorPullMode = GpioPinResistorPullMode.Off;
if (PinMode != GpioPinDriveMode.Input)
{
m_ResistorPullMode = GpioPinResistorPullMode.Off;
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to set the {nameof(InputPullMode)} for pin {PinNumber} because operating mode is {PinMode}." $"Unable to set the {nameof(this.InputPullMode)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the {nameof(InputPullMode)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); + $" Setting the {nameof(this.InputPullMode)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
} }
WiringPi.PullUpDnControl(PinNumber, (int)value); WiringPi.PullUpDnControl(this.PinNumber, (Int32)value);
m_ResistorPullMode = value; this.m_ResistorPullMode = value;
} }
} }
} }
@ -166,27 +171,23 @@
/// <value> /// <value>
/// The PWM register. /// The PWM register.
/// </value> /// </value>
public int PwmRegister public Int32 PwmRegister {
{ get => this.m_PwmRegister;
get => m_PwmRegister;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.PwmOutput) {
{ this.m_PwmRegister = 0;
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmRegister = 0;
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to write PWM register for pin {PinNumber} because operating mode is {PinMode}." $"Unable to write PWM register for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writing the PWM register is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + $" Writing the PWM register is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
} }
var val = value.Clamp(0, 1024); Int32 val = value.Clamp(0, 1024);
WiringPi.PwmWrite(PinNumber, val); WiringPi.PwmWrite(this.PinNumber, val);
m_PwmRegister = val; this.m_PwmRegister = val;
} }
} }
} }
@ -199,25 +200,21 @@
/// The PWM mode. /// The PWM mode.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set a Pwn output</exception> /// <exception cref="InvalidOperationException">When pin mode is not set a Pwn output</exception>
public PwmMode PwmMode public PwmMode PwmMode {
{ get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmMode : PwmMode.Balanced;
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmMode : PwmMode.Balanced;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.PwmOutput) {
{ this.m_PwmMode = PwmMode.Balanced;
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmMode = PwmMode.Balanced;
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to set PWM mode for pin {PinNumber} because operating mode is {PinMode}." $"Unable to set PWM mode for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM mode is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + $" Setting the PWM mode is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
} }
WiringPi.PwmSetMode((int)value); WiringPi.PwmSetMode((Int32)value);
m_PwmMode = value; this.m_PwmMode = value;
} }
} }
} }
@ -229,25 +226,21 @@
/// The PWM range. /// The PWM range.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception> /// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception>
public uint PwmRange public UInt32 PwmRange {
{ get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmRange : 0;
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmRange : 0;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.PwmOutput) {
{ this.m_PwmRange = 1024;
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmRange = 1024;
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}." $"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
} }
WiringPi.PwmSetRange(value); WiringPi.PwmSetRange(value);
m_PwmRange = value; this.m_PwmRange = value;
} }
} }
} }
@ -259,25 +252,21 @@
/// The PWM clock divisor. /// The PWM clock divisor.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception> /// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception>
public int PwmClockDivisor public Int32 PwmClockDivisor {
{ get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmClockDivisor : 0;
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmClockDivisor : 0;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.PwmOutput) {
{ this.m_PwmClockDivisor = 1;
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmClockDivisor = 1;
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}." $"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
} }
WiringPi.PwmSetClock(value); WiringPi.PwmSetClock(value);
m_PwmClockDivisor = value; this.m_PwmClockDivisor = value;
} }
} }
} }
@ -292,7 +281,7 @@
/// <value> /// <value>
/// <c>true</c> if this instance is in soft tone mode; otherwise, <c>false</c>. /// <c>true</c> if this instance is in soft tone mode; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsInSoftToneMode => m_SoftToneFrequency >= 0; public Boolean IsInSoftToneMode => this.m_SoftToneFrequency >= 0;
/// <summary> /// <summary>
/// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical /// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical
@ -301,26 +290,21 @@
/// The soft tone frequency. /// The soft tone frequency.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">When soft tones cannot be initialized on the pin</exception> /// <exception cref="InvalidOperationException">When soft tones cannot be initialized on the pin</exception>
public int SoftToneFrequency public Int32 SoftToneFrequency {
{ get => this.m_SoftToneFrequency;
get => m_SoftToneFrequency;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.IsInSoftToneMode == false) {
{ Int32 setupResult = WiringPi.SoftToneCreate(this.PinNumber);
if (IsInSoftToneMode == false) if(setupResult != 0) {
{
var setupResult = WiringPi.SoftToneCreate(PinNumber);
if (setupResult != 0)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to initialize soft tone on pin {PinNumber}. Error Code: {setupResult}"); $"Unable to initialize soft tone on pin {this.PinNumber}. Error Code: {setupResult}");
} }
} }
WiringPi.SoftToneWrite(PinNumber, value); WiringPi.SoftToneWrite(this.PinNumber, value);
m_SoftToneFrequency = value; this.m_SoftToneFrequency = value;
} }
} }
} }
@ -335,7 +319,7 @@
/// <value> /// <value>
/// <c>true</c> if this instance is in soft PWM mode; otherwise, <c>false</c>. /// <c>true</c> if this instance is in soft PWM mode; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsInSoftPwmMode => m_SoftPwmValue >= 0; public Boolean IsInSoftPwmMode => this.m_SoftPwmValue >= 0;
/// <summary> /// <summary>
/// Gets or sets the software PWM value on the pin. /// Gets or sets the software PWM value on the pin.
@ -344,21 +328,15 @@
/// The soft PWM value. /// The soft PWM value.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">StartSoftPwm</exception> /// <exception cref="InvalidOperationException">StartSoftPwm</exception>
public int SoftPwmValue public Int32 SoftPwmValue {
{ get => this.m_SoftPwmValue;
get => m_SoftPwmValue;
set set {
{ lock(this._syncLock) {
lock (_syncLock) if(this.IsInSoftPwmMode && value >= 0) {
{ WiringPi.SoftPwmWrite(this.PinNumber, value);
if (IsInSoftPwmMode && value >= 0) this.m_SoftPwmValue = value;
{ } else {
WiringPi.SoftPwmWrite(PinNumber, value);
m_SoftPwmValue = value;
}
else
{
throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}."); throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}.");
} }
} }
@ -368,7 +346,7 @@
/// <summary> /// <summary>
/// Gets the software PWM range used upon starting the PWM. /// Gets the software PWM range used upon starting the PWM.
/// </summary> /// </summary>
public int SoftPwmRange { get; private set; } = -1; public Int32 SoftPwmRange { get; private set; } = -1;
/// <summary> /// <summary>
/// Starts the software based PWM on this pin. /// Starts the software based PWM on this pin.
@ -378,27 +356,24 @@
/// <exception cref="NotSupportedException">When the pin does not suppoert PWM</exception> /// <exception cref="NotSupportedException">When the pin does not suppoert PWM</exception>
/// <exception cref="InvalidOperationException">StartSoftPwm /// <exception cref="InvalidOperationException">StartSoftPwm
/// or</exception> /// or</exception>
public void StartSoftPwm(int value, int range) public void StartSoftPwm(Int32 value, Int32 range) {
{ lock(this._syncLock) {
lock (_syncLock) if(this.Capabilities.Contains(PinCapability.GP) == false) {
{ throw new NotSupportedException($"Pin {this.PinNumber} does not support software PWM");
if (Capabilities.Contains(PinCapability.GP) == false)
throw new NotSupportedException($"Pin {PinNumber} does not support software PWM");
if (IsInSoftPwmMode)
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
var startResult = WiringPi.SoftPwmCreate(PinNumber, value, range);
if (startResult == 0)
{
m_SoftPwmValue = value;
SoftPwmRange = range;
} }
else
{ if(this.IsInSoftPwmMode) {
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
}
Int32 startResult = WiringPi.SoftPwmCreate(this.PinNumber, value, range);
if(startResult == 0) {
this.m_SoftPwmValue = value;
this.SoftPwmRange = range;
} else {
throw new InvalidOperationException( throw new InvalidOperationException(
$"Could not start software based PWM on pin {PinNumber}. Error code: {startResult}"); $"Could not start software based PWM on pin {this.PinNumber}. Error code: {startResult}");
} }
} }
} }
@ -412,18 +387,15 @@
/// This method performs a digital write /// This method performs a digital write
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void Write(GpioPinValue value) public void Write(GpioPinValue value) {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.Output) {
{
if (PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to write to pin {PinNumber} because operating mode is {PinMode}." $"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}"); + $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
} }
WiringPi.DigitalWrite(PinNumber, (int)value); WiringPi.DigitalWrite(this.PinNumber, (Int32)value);
} }
} }
@ -432,15 +404,15 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteAsync(GpioPinValue value) => Task.Run(() => { Write(value); }); public Task WriteAsync(GpioPinValue value) => Task.Run(() => this.Write(value));
/// <summary> /// <summary>
/// Writes the specified bit value. /// Writes the specified bit value.
/// This method performs a digital write /// This method performs a digital write
/// </summary> /// </summary>
/// <param name="value">if set to <c>true</c> [value].</param> /// <param name="value">if set to <c>true</c> [value].</param>
public void Write(bool value) public void Write(Boolean value)
=> Write(value ? GpioPinValue.High : GpioPinValue.Low); => this.Write(value ? GpioPinValue.High : GpioPinValue.Low);
/// <summary> /// <summary>
/// Writes the specified bit value. /// Writes the specified bit value.
@ -450,14 +422,14 @@
/// <returns> /// <returns>
/// The awaitable task /// The awaitable task
/// </returns> /// </returns>
public Task WriteAsync(bool value) => Task.Run(() => { Write(value); }); public Task WriteAsync(Boolean value) => Task.Run(() => this.Write(value));
/// <summary> /// <summary>
/// Writes the specified value. 0 for low, any other value for high /// Writes the specified value. 0 for low, any other value for high
/// This method performs a digital write /// This method performs a digital write
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void Write(int value) => Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low); public void Write(Int32 value) => this.Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low);
/// <summary> /// <summary>
/// Writes the specified value. 0 for low, any other value for high /// Writes the specified value. 0 for low, any other value for high
@ -465,25 +437,22 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteAsync(int value) => Task.Run(() => { Write(value); }); public Task WriteAsync(Int32 value) => Task.Run(() => this.Write(value));
/// <summary> /// <summary>
/// Writes the specified value as an analog level. /// Writes the specified value as an analog level.
/// You will need to register additional analog modules to enable this function for devices such as the Gertboard. /// You will need to register additional analog modules to enable this function for devices such as the Gertboard.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void WriteLevel(int value) public void WriteLevel(Int32 value) {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.Output) {
{
if (PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to write to pin {PinNumber} because operating mode is {PinMode}." $"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}"); + $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
} }
WiringPi.AnalogWrite(PinNumber, value); WiringPi.AnalogWrite(this.PinNumber, value);
} }
} }
@ -493,7 +462,7 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteLevelAsync(int value) => Task.Run(() => { WriteLevel(value); }); public Task WriteLevelAsync(Int32 value) => Task.Run(() => this.WriteLevel(value));
#endregion #endregion
@ -505,21 +474,19 @@
/// <param name="status">status to check</param> /// <param name="status">status to check</param>
/// <param name="timeOutMillisecond">timeout to reach status</param> /// <param name="timeOutMillisecond">timeout to reach status</param>
/// <returns>true/false</returns> /// <returns>true/false</returns>
public bool WaitForValue(GpioPinValue status, int timeOutMillisecond) public Boolean WaitForValue(GpioPinValue status, Int32 timeOutMillisecond) {
{ if(this.PinMode != GpioPinDriveMode.Input) {
if (PinMode != GpioPinDriveMode.Input)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}." $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
} }
var hrt = new HighResolutionTimer(); HighResolutionTimer hrt = new HighResolutionTimer();
hrt.Start(); hrt.Start();
do do {
{ if(this.ReadValue() == status) {
if (ReadValue() == status)
return true; return true;
}
Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop. Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop.
} }
@ -532,18 +499,15 @@
/// Reads the digital value on the pin as a boolean value. /// Reads the digital value on the pin as a boolean value.
/// </summary> /// </summary>
/// <returns>The state of the pin</returns> /// <returns>The state of the pin</returns>
public bool Read() public Boolean Read() {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.Input && this.PinMode != GpioPinDriveMode.Output) {
{
if (PinMode != GpioPinDriveMode.Input && PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}." $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
} }
return WiringPi.DigitalRead(PinNumber) != 0; return WiringPi.DigitalRead(this.PinNumber) != 0;
} }
} }
@ -551,20 +515,20 @@
/// Reads the digital value on the pin as a boolean value. /// Reads the digital value on the pin as a boolean value.
/// </summary> /// </summary>
/// <returns>The state of the pin</returns> /// <returns>The state of the pin</returns>
public Task<bool> ReadAsync() => Task.Run(() => Read()); public Task<Boolean> ReadAsync() => Task.Run(() => this.Read());
/// <summary> /// <summary>
/// Reads the digital value on the pin as a High or Low value. /// Reads the digital value on the pin as a High or Low value.
/// </summary> /// </summary>
/// <returns>The state of the pin</returns> /// <returns>The state of the pin</returns>
public GpioPinValue ReadValue() public GpioPinValue ReadValue()
=> Read() ? GpioPinValue.High : GpioPinValue.Low; => this.Read() ? GpioPinValue.High : GpioPinValue.Low;
/// <summary> /// <summary>
/// Reads the digital value on the pin as a High or Low value. /// Reads the digital value on the pin as a High or Low value.
/// </summary> /// </summary>
/// <returns>The state of the pin</returns> /// <returns>The state of the pin</returns>
public Task<GpioPinValue> ReadValueAsync() => Task.Run(() => ReadValue()); public Task<GpioPinValue> ReadValueAsync() => Task.Run(() => this.ReadValue());
/// <summary> /// <summary>
/// Reads the analog value on the pin. /// Reads the analog value on the pin.
@ -574,18 +538,15 @@
/// </summary> /// </summary>
/// <returns>The analog level</returns> /// <returns>The analog level</returns>
/// <exception cref="InvalidOperationException">When the pin mode is not configured as an input.</exception> /// <exception cref="InvalidOperationException">When the pin mode is not configured as an input.</exception>
public int ReadLevel() public Int32 ReadLevel() {
{ lock(this._syncLock) {
lock (_syncLock) if(this.PinMode != GpioPinDriveMode.Input) {
{
if (PinMode != GpioPinDriveMode.Input)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}." $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
} }
return WiringPi.AnalogRead(PinNumber); return WiringPi.AnalogRead(this.PinNumber);
} }
} }
@ -596,7 +557,7 @@
/// quick2Wire analog board, etc. /// quick2Wire analog board, etc.
/// </summary> /// </summary>
/// <returns>The analog level</returns> /// <returns>The analog level</returns>
public Task<int> ReadLevelAsync() => Task.Run(() => ReadLevel()); public Task<Int32> ReadLevelAsync() => Task.Run(() => this.ReadLevel());
#endregion #endregion
@ -613,31 +574,27 @@
/// or /// or
/// RegisterInterruptCallback /// RegisterInterruptCallback
/// </exception> /// </exception>
public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback) public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback) {
{ if(callback == null) {
if (callback == null)
throw new ArgumentException($"{nameof(callback)} cannot be null"); throw new ArgumentException($"{nameof(callback)} cannot be null");
}
if (InterruptCallback != null) if(this.InterruptCallback != null) {
throw new InvalidOperationException("An interrupt callback was already registered."); throw new InvalidOperationException("An interrupt callback was already registered.");
}
if (PinMode != GpioPinDriveMode.Input) if(this.PinMode != GpioPinDriveMode.Input) {
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"Unable to {nameof(RegisterInterruptCallback)} for pin {PinNumber} because operating mode is {PinMode}." $"Unable to {nameof(RegisterInterruptCallback)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); + $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
} }
lock (_syncLock) lock(this._syncLock) {
{ Int32 registerResult = WiringPi.WiringPiISR(this.PinNumber, (Int32)edgeDetection, callback);
var registerResult = WiringPi.WiringPiISR(PinNumber, (int)edgeDetection, callback); if(registerResult == 0) {
if (registerResult == 0) this.InterruptEdgeDetection = edgeDetection;
{ this.InterruptCallback = callback;
InterruptEdgeDetection = edgeDetection; } else {
InterruptCallback = callback;
}
else
{
HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback)); HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback));
} }
} }

View File

@ -1,32 +1,30 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan.Abstractions;
using Native;
using Swan.Abstractions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// A simple wrapper for the I2c bus on the Raspberry Pi /// A simple wrapper for the I2c bus on the Raspberry Pi
/// </summary> /// </summary>
public class I2CBus : SingletonBase<I2CBus> public class I2CBus : SingletonBase<I2CBus> {
{
// TODO: It would be nice to integrate i2c device detection. // TODO: It would be nice to integrate i2c device detection.
private static readonly object SyncRoot = new object(); private static readonly Object SyncRoot = new Object();
private readonly Dictionary<int, I2CDevice> _devices = new Dictionary<int, I2CDevice>(); private readonly Dictionary<Int32, I2CDevice> _devices = new Dictionary<Int32, I2CDevice>();
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="I2CBus"/> class from being created. /// Prevents a default instance of the <see cref="I2CBus"/> class from being created.
/// </summary> /// </summary>
private I2CBus() private I2CBus() {
{
// placeholder // placeholder
} }
/// <summary> /// <summary>
/// Gets the registered devices as a read only collection. /// Gets the registered devices as a read only collection.
/// </summary> /// </summary>
public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(_devices.Values.ToArray()); public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(this._devices.Values.ToArray());
/// <summary> /// <summary>
/// Gets the <see cref="I2CDevice"/> with the specified device identifier. /// Gets the <see cref="I2CDevice"/> with the specified device identifier.
@ -36,18 +34,16 @@
/// </value> /// </value>
/// <param name="deviceId">The device identifier.</param> /// <param name="deviceId">The device identifier.</param>
/// <returns>A reference to an I2C device</returns> /// <returns>A reference to an I2C device</returns>
public I2CDevice this[int deviceId] => GetDeviceById(deviceId); public I2CDevice this[Int32 deviceId] => this.GetDeviceById(deviceId);
/// <summary> /// <summary>
/// Gets the device by identifier. /// Gets the device by identifier.
/// </summary> /// </summary>
/// <param name="deviceId">The device identifier.</param> /// <param name="deviceId">The device identifier.</param>
/// <returns>The device reference</returns> /// <returns>The device reference</returns>
public I2CDevice GetDeviceById(int deviceId) public I2CDevice GetDeviceById(Int32 deviceId) {
{ lock(SyncRoot) {
lock (SyncRoot) return this._devices[deviceId];
{
return _devices[deviceId];
} }
} }
@ -57,19 +53,19 @@
/// <param name="deviceId">The device identifier.</param> /// <param name="deviceId">The device identifier.</param>
/// <returns>The device reference</returns> /// <returns>The device reference</returns>
/// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception> /// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception>
public I2CDevice AddDevice(int deviceId) public I2CDevice AddDevice(Int32 deviceId) {
{ lock(SyncRoot) {
lock (SyncRoot) if(this._devices.ContainsKey(deviceId)) {
{ return this._devices[deviceId];
if (_devices.ContainsKey(deviceId)) }
return _devices[deviceId];
var fileDescriptor = SetupFileDescriptor(deviceId); Int32 fileDescriptor = SetupFileDescriptor(deviceId);
if (fileDescriptor < 0) if(fileDescriptor < 0) {
throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}."); throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}.");
}
var device = new I2CDevice(deviceId, fileDescriptor); I2CDevice device = new I2CDevice(deviceId, fileDescriptor);
_devices[deviceId] = device; this._devices[deviceId] = device;
return device; return device;
} }
} }
@ -82,10 +78,8 @@
/// </summary> /// </summary>
/// <param name="deviceId">The device identifier.</param> /// <param name="deviceId">The device identifier.</param>
/// <returns>The Linux file handle</returns> /// <returns>The Linux file handle</returns>
private static int SetupFileDescriptor(int deviceId) private static Int32 SetupFileDescriptor(Int32 deviceId) {
{ lock(SyncRoot) {
lock (SyncRoot)
{
return WiringPi.WiringPiI2CSetup(deviceId); return WiringPi.WiringPiI2CSetup(deviceId);
} }
} }

View File

@ -1,25 +1,22 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Native; using Unosquare.RaspberryIO.Native;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// Represents a device on the I2C Bus /// Represents a device on the I2C Bus
/// </summary> /// </summary>
public class I2CDevice public class I2CDevice {
{ private readonly Object _syncLock = new Object();
private readonly object _syncLock = new object();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="I2CDevice"/> class. /// Initializes a new instance of the <see cref="I2CDevice"/> class.
/// </summary> /// </summary>
/// <param name="deviceId">The device identifier.</param> /// <param name="deviceId">The device identifier.</param>
/// <param name="fileDescriptor">The file descriptor.</param> /// <param name="fileDescriptor">The file descriptor.</param>
internal I2CDevice(int deviceId, int fileDescriptor) internal I2CDevice(Int32 deviceId, Int32 fileDescriptor) {
{ this.DeviceId = deviceId;
DeviceId = deviceId; this.FileDescriptor = fileDescriptor;
FileDescriptor = fileDescriptor;
} }
/// <summary> /// <summary>
@ -28,7 +25,9 @@
/// <value> /// <value>
/// The device identifier. /// The device identifier.
/// </value> /// </value>
public int DeviceId { get; } public Int32 DeviceId {
get;
}
/// <summary> /// <summary>
/// Gets the standard POSIX file descriptor. /// Gets the standard POSIX file descriptor.
@ -36,19 +35,22 @@
/// <value> /// <value>
/// The file descriptor. /// The file descriptor.
/// </value> /// </value>
public int FileDescriptor { get; } public Int32 FileDescriptor {
get;
}
/// <summary> /// <summary>
/// Reads a byte from the specified file descriptor /// Reads a byte from the specified file descriptor
/// </summary> /// </summary>
/// <returns>The byte from device</returns> /// <returns>The byte from device</returns>
public byte Read() public Byte Read() {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CRead(FileDescriptor); HardwareException.Throw(nameof(I2CDevice), nameof(Read));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read)); }
return (byte)result;
return (Byte)result;
} }
} }
@ -56,23 +58,23 @@
/// Reads a byte from the specified file descriptor /// Reads a byte from the specified file descriptor
/// </summary> /// </summary>
/// <returns>The byte from device</returns> /// <returns>The byte from device</returns>
public Task<byte> ReadAsync() => Task.Run(() => Read()); public Task<Byte> ReadAsync() => Task.Run(() => this.Read());
/// <summary> /// <summary>
/// Reads a buffer of the specified length, one byte at a time /// Reads a buffer of the specified length, one byte at a time
/// </summary> /// </summary>
/// <param name="length">The length.</param> /// <param name="length">The length.</param>
/// <returns>The byte array from device</returns> /// <returns>The byte array from device</returns>
public byte[] Read(int length) public Byte[] Read(Int32 length) {
{ lock(this._syncLock) {
lock (_syncLock) Byte[] buffer = new Byte[length];
{ for(Int32 i = 0; i < length; i++) {
var buffer = new byte[length]; Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
for (var i = 0; i < length; i++) if(result < 0) {
{ HardwareException.Throw(nameof(I2CDevice), nameof(Read));
var result = WiringPi.WiringPiI2CRead(FileDescriptor); }
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read));
buffer[i] = (byte)result; buffer[i] = (Byte)result;
} }
return buffer; return buffer;
@ -84,18 +86,18 @@
/// </summary> /// </summary>
/// <param name="length">The length.</param> /// <param name="length">The length.</param>
/// <returns>The byte array from device</returns> /// <returns>The byte array from device</returns>
public Task<byte[]> ReadAsync(int length) => Task.Run(() => Read(length)); public Task<Byte[]> ReadAsync(Int32 length) => Task.Run(() => this.Read(length));
/// <summary> /// <summary>
/// Writes a byte of data the specified file descriptor. /// Writes a byte of data the specified file descriptor.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
public void Write(byte data) public void Write(Byte data) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, data);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, data); HardwareException.Throw(nameof(I2CDevice), nameof(Write));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write)); }
} }
} }
@ -104,20 +106,19 @@
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteAsync(byte data) => Task.Run(() => { Write(data); }); public Task WriteAsync(Byte data) => Task.Run(() => this.Write(data));
/// <summary> /// <summary>
/// Writes a set of bytes to the specified file descriptor. /// Writes a set of bytes to the specified file descriptor.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
public void Write(byte[] data) public void Write(Byte[] data) {
{ lock(this._syncLock) {
lock (_syncLock) foreach(Byte b in data) {
{ Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, b);
foreach (var b in data) if(result < 0) {
{ HardwareException.Throw(nameof(I2CDevice), nameof(Write));
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, b); }
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write));
} }
} }
} }
@ -127,22 +128,19 @@
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteAsync(byte[] data) public Task WriteAsync(Byte[] data) => Task.Run(() => this.Write(data));
{
return Task.Run(() => { Write(data); });
}
/// <summary> /// <summary>
/// These write an 8 or 16-bit data value into the device register indicated. /// These write an 8 or 16-bit data value into the device register indicated.
/// </summary> /// </summary>
/// <param name="address">The register.</param> /// <param name="address">The register.</param>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
public void WriteAddressByte(int address, byte data) public void WriteAddressByte(Int32 address, Byte data) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CWriteReg8(this.FileDescriptor, address, data);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CWriteReg8(FileDescriptor, address, data); HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte)); }
} }
} }
@ -151,12 +149,12 @@
/// </summary> /// </summary>
/// <param name="address">The register.</param> /// <param name="address">The register.</param>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
public void WriteAddressWord(int address, ushort data) public void WriteAddressWord(Int32 address, UInt16 data) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CWriteReg16(this.FileDescriptor, address, data);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CWriteReg16(FileDescriptor, address, data); HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord)); }
} }
} }
@ -165,14 +163,14 @@
/// </summary> /// </summary>
/// <param name="address">The register.</param> /// <param name="address">The register.</param>
/// <returns>The address byte from device</returns> /// <returns>The address byte from device</returns>
public byte ReadAddressByte(int address) public Byte ReadAddressByte(Int32 address) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CReadReg8(this.FileDescriptor, address);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CReadReg8(FileDescriptor, address); HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte)); }
return (byte)result; return (Byte)result;
} }
} }
@ -181,12 +179,12 @@
/// </summary> /// </summary>
/// <param name="address">The register.</param> /// <param name="address">The register.</param>
/// <returns>The address word from device</returns> /// <returns>The address word from device</returns>
public ushort ReadAddressWord(int address) public UInt16 ReadAddressWord(Int32 address) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = WiringPi.WiringPiI2CReadReg16(this.FileDescriptor, address);
{ if(result < 0) {
var result = WiringPi.WiringPiI2CReadReg16(FileDescriptor, address); HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord));
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord)); }
return Convert.ToUInt16(result); return Convert.ToUInt16(result);
} }

View File

@ -1,17 +1,15 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{ using Unosquare.Swan.Abstractions;
using Swan.Abstractions;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// The SPI Bus containing the 2 SPI channels /// The SPI Bus containing the 2 SPI channels
/// </summary> /// </summary>
public class SpiBus : SingletonBase<SpiBus> public class SpiBus : SingletonBase<SpiBus> {
{
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="SpiBus"/> class from being created. /// Prevents a default instance of the <see cref="SpiBus"/> class from being created.
/// </summary> /// </summary>
private SpiBus() private SpiBus() {
{
// placeholder // placeholder
} }
@ -23,7 +21,9 @@
/// <value> /// <value>
/// The channel0 frequency. /// The channel0 frequency.
/// </value> /// </value>
public int Channel0Frequency { get; set; } public Int32 Channel0Frequency {
get; set;
}
/// <summary> /// <summary>
/// Gets the SPI bus on channel 1. /// Gets the SPI bus on channel 1.
@ -31,14 +31,13 @@
/// <value> /// <value>
/// The channel0. /// The channel0.
/// </value> /// </value>
public SpiChannel Channel0 public SpiChannel Channel0 {
{ get {
get if(this.Channel0Frequency == 0) {
{ this.Channel0Frequency = SpiChannel.DefaultFrequency;
if (Channel0Frequency == 0) }
Channel0Frequency = SpiChannel.DefaultFrequency;
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency); return SpiChannel.Retrieve(SpiChannelNumber.Channel0, this.Channel0Frequency);
} }
} }
@ -48,7 +47,9 @@
/// <value> /// <value>
/// The channel1 frequency. /// The channel1 frequency.
/// </value> /// </value>
public int Channel1Frequency { get; set; } public Int32 Channel1Frequency {
get; set;
}
/// <summary> /// <summary>
/// Gets the SPI bus on channel 1. /// Gets the SPI bus on channel 1.
@ -56,14 +57,13 @@
/// <value> /// <value>
/// The channel1. /// The channel1.
/// </value> /// </value>
public SpiChannel Channel1 public SpiChannel Channel1 {
{ get {
get if(this.Channel1Frequency == 0) {
{ this.Channel1Frequency = SpiChannel.DefaultFrequency;
if (Channel1Frequency == 0) }
Channel1Frequency = SpiChannel.DefaultFrequency;
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency); return SpiChannel.Retrieve(SpiChannelNumber.Channel1, this.Channel1Frequency);
} }
} }

View File

@ -1,53 +1,48 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native;
using Swan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// Provides access to using the SPI buses on the GPIO. /// Provides access to using the SPI buses on the GPIO.
/// SPI is a bus that works like a ring shift register /// SPI is a bus that works like a ring shift register
/// The number of bytes pushed is equal to the number of bytes received. /// The number of bytes pushed is equal to the number of bytes received.
/// </summary> /// </summary>
public sealed class SpiChannel public sealed class SpiChannel {
{
/// <summary> /// <summary>
/// The minimum frequency of an SPI Channel /// The minimum frequency of an SPI Channel
/// </summary> /// </summary>
public const int MinFrequency = 500000; public const Int32 MinFrequency = 500000;
/// <summary> /// <summary>
/// The maximum frequency of an SPI channel /// The maximum frequency of an SPI channel
/// </summary> /// </summary>
public const int MaxFrequency = 32000000; public const Int32 MaxFrequency = 32000000;
/// <summary> /// <summary>
/// The default frequency of SPI channels /// The default frequency of SPI channels
/// This is set to 8 Mhz wich is typical in modern hardware. /// This is set to 8 Mhz wich is typical in modern hardware.
/// </summary> /// </summary>
public const int DefaultFrequency = 8000000; public const Int32 DefaultFrequency = 8000000;
private static readonly object SyncRoot = new object(); private static readonly Object SyncRoot = new Object();
private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>(); private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>();
private readonly object _syncLock = new object(); private readonly Object _syncLock = new Object();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SpiChannel"/> class. /// Initializes a new instance of the <see cref="SpiChannel"/> class.
/// </summary> /// </summary>
/// <param name="channel">The channel.</param> /// <param name="channel">The channel.</param>
/// <param name="frequency">The frequency.</param> /// <param name="frequency">The frequency.</param>
private SpiChannel(SpiChannelNumber channel, int frequency) private SpiChannel(SpiChannelNumber channel, Int32 frequency) {
{ lock(SyncRoot) {
lock (SyncRoot) this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency);
{ this.Channel = (Int32)channel;
Frequency = frequency.Clamp(MinFrequency, MaxFrequency); this.FileDescriptor = WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency);
Channel = (int)channel;
FileDescriptor = WiringPi.WiringPiSPISetup((int)channel, Frequency);
if (FileDescriptor < 0) if(this.FileDescriptor < 0) {
{
HardwareException.Throw(nameof(SpiChannel), channel.ToString()); HardwareException.Throw(nameof(SpiChannel), channel.ToString());
} }
} }
@ -60,35 +55,42 @@
/// <value> /// <value>
/// The file descriptor. /// The file descriptor.
/// </value> /// </value>
public int FileDescriptor { get; } public Int32 FileDescriptor {
get;
}
/// <summary> /// <summary>
/// Gets the channel. /// Gets the channel.
/// </summary> /// </summary>
public int Channel { get; } public Int32 Channel {
get;
}
/// <summary> /// <summary>
/// Gets the frequency. /// Gets the frequency.
/// </summary> /// </summary>
public int Frequency { get; } public Int32 Frequency {
get;
}
/// <summary> /// <summary>
/// Sends data and simultaneously receives the data in the return buffer /// Sends data and simultaneously receives the data in the return buffer
/// </summary> /// </summary>
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <returns>The read bytes from the ring-style bus</returns> /// <returns>The read bytes from the ring-style bus</returns>
public byte[] SendReceive(byte[] buffer) public Byte[] SendReceive(Byte[] buffer) {
{ if(buffer == null || buffer.Length == 0) {
if (buffer == null || buffer.Length == 0)
return null; return null;
}
lock (_syncLock) lock(this._syncLock) {
{ Byte[] spiBuffer = new Byte[buffer.Length];
var spiBuffer = new byte[buffer.Length];
Array.Copy(buffer, spiBuffer, buffer.Length); Array.Copy(buffer, spiBuffer, buffer.Length);
var result = WiringPi.WiringPiSPIDataRW(Channel, spiBuffer, spiBuffer.Length); Int32 result = WiringPi.WiringPiSPIDataRW(this.Channel, spiBuffer, spiBuffer.Length);
if (result < 0) HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive)); if(result < 0) {
HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive));
}
return spiBuffer; return spiBuffer;
} }
@ -101,7 +103,7 @@
/// <returns> /// <returns>
/// The read bytes from the ring-style bus /// The read bytes from the ring-style bus
/// </returns> /// </returns>
public Task<byte[]> SendReceiveAsync(byte[] buffer) => Task.Run(() => SendReceive(buffer)); public Task<Byte[]> SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer));
/// <summary> /// <summary>
/// Writes the specified buffer the the underlying FileDescriptor. /// Writes the specified buffer the the underlying FileDescriptor.
@ -110,16 +112,15 @@
/// like sending data over to those long RGB LED strips /// like sending data over to those long RGB LED strips
/// </summary> /// </summary>
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
public void Write(byte[] buffer) public void Write(Byte[] buffer) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 result = Standard.Write(this.FileDescriptor, buffer, buffer.Length);
{
var result = Standard.Write(FileDescriptor, buffer, buffer.Length);
if (result < 0) if(result < 0) {
HardwareException.Throw(nameof(SpiChannel), nameof(Write)); HardwareException.Throw(nameof(SpiChannel), nameof(Write));
} }
} }
}
/// <summary> /// <summary>
/// Writes the specified buffer the the underlying FileDescriptor. /// Writes the specified buffer the the underlying FileDescriptor.
@ -129,7 +130,7 @@
/// </summary> /// </summary>
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <returns>The awaitable task</returns> /// <returns>The awaitable task</returns>
public Task WriteAsync(byte[] buffer) => Task.Run(() => { Write(buffer); }); public Task WriteAsync(Byte[] buffer) => Task.Run(() => this.Write(buffer));
/// <summary> /// <summary>
/// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically. /// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically.
@ -138,14 +139,13 @@
/// <param name="channel">The channel.</param> /// <param name="channel">The channel.</param>
/// <param name="frequency">The frequency.</param> /// <param name="frequency">The frequency.</param>
/// <returns>The usable SPI channel</returns> /// <returns>The usable SPI channel</returns>
internal static SpiChannel Retrieve(SpiChannelNumber channel, int frequency) internal static SpiChannel Retrieve(SpiChannelNumber channel, Int32 frequency) {
{ lock(SyncRoot) {
lock (SyncRoot) if(Buses.ContainsKey(channel)) {
{
if (Buses.ContainsKey(channel))
return Buses[channel]; return Buses[channel];
}
var newBus = new SpiChannel(channel, frequency); SpiChannel newBus = new SpiChannel(channel, frequency);
Buses[channel] = newBus; Buses[channel] = newBus;
return newBus; return newBus;
} }

View File

@ -1,5 +1,4 @@
namespace Unosquare.RaspberryIO.Native namespace Unosquare.RaspberryIO.Native {
{
/// <summary> /// <summary>
/// A delegate defining a callback for an Interrupt Service Routine /// A delegate defining a callback for an Interrupt Service Routine
/// </summary> /// </summary>

View File

@ -1,38 +1,32 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{
using Swan;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// Represents a low-level exception, typically thrown when return codes from a /// Represents a low-level exception, typically thrown when return codes from a
/// low-level operation is non-zero or in some cases when it is less than zero. /// low-level operation is non-zero or in some cases when it is less than zero.
/// </summary> /// </summary>
/// <seealso cref="Exception" /> /// <seealso cref="Exception" />
public class HardwareException : Exception public class HardwareException : Exception {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HardwareException" /> class. /// Initializes a new instance of the <see cref="HardwareException" /> class.
/// </summary> /// </summary>
/// <param name="errorCode">The error code.</param> /// <param name="errorCode">The error code.</param>
/// <param name="component">The component.</param> /// <param name="component">The component.</param>
public HardwareException(int errorCode, string component) public HardwareException(Int32 errorCode, String component)
: base($"A hardware exception occurred. Error Code: {errorCode}") : base($"A hardware exception occurred. Error Code: {errorCode}") {
{ this.ExtendedMessage = null;
ExtendedMessage = null;
try try {
{ this.ExtendedMessage = Standard.Strerror(errorCode);
ExtendedMessage = Standard.Strerror(errorCode); } catch {
}
catch
{
// TODO: strerror not working great... // TODO: strerror not working great...
$"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource); $"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource);
} }
ErrorCode = errorCode; this.ErrorCode = errorCode;
Component = component; this.Component = component;
} }
/// <summary> /// <summary>
@ -41,7 +35,9 @@
/// <value> /// <value>
/// The error code. /// The error code.
/// </value> /// </value>
public int ErrorCode { get; } public Int32 ErrorCode {
get;
}
/// <summary> /// <summary>
/// Gets the component. /// Gets the component.
@ -49,7 +45,9 @@
/// <value> /// <value>
/// The component. /// The component.
/// </value> /// </value>
public string Component { get; } public String Component {
get;
}
/// <summary> /// <summary>
/// Gets the extended message (could be null). /// Gets the extended message (could be null).
@ -57,7 +55,9 @@
/// <value> /// <value>
/// The extended message. /// The extended message.
/// </value> /// </value>
public string ExtendedMessage { get; } public String ExtendedMessage {
get;
}
/// <summary> /// <summary>
/// Throws a new instance of a hardware error by retrieving the last error number (errno). /// Throws a new instance of a hardware error by retrieving the last error number (errno).
@ -65,9 +65,9 @@
/// <param name="className">Name of the class.</param> /// <param name="className">Name of the class.</param>
/// <param name="methodName">Name of the method.</param> /// <param name="methodName">Name of the method.</param>
/// <exception cref="HardwareException">When an error thrown by an API call occurs</exception> /// <exception cref="HardwareException">When an error thrown by an API call occurs</exception>
public static void Throw(string className, string methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}"); public static void Throw(String className, String methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}");
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() => $"{GetType()}{(string.IsNullOrWhiteSpace(Component) ? string.Empty : $" on {Component}")}: ({ErrorCode}) - {Message}"; public override String ToString() => $"{this.GetType()}{(String.IsNullOrWhiteSpace(this.Component) ? String.Empty : $" on {this.Component}")}: ({this.ErrorCode}) - {this.Message}";
} }
} }

View File

@ -1,32 +1,30 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// Provides access to a high- esolution, time measuring device. /// Provides access to a high- esolution, time measuring device.
/// </summary> /// </summary>
/// <seealso cref="Stopwatch" /> /// <seealso cref="Stopwatch" />
public class HighResolutionTimer : Stopwatch public class HighResolutionTimer : Stopwatch {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HighResolutionTimer"/> class. /// Initializes a new instance of the <see cref="HighResolutionTimer"/> class.
/// </summary> /// </summary>
/// <exception cref="NotSupportedException">High-resolution timer not available</exception> /// <exception cref="NotSupportedException">High-resolution timer not available</exception>
public HighResolutionTimer() public HighResolutionTimer() {
{ if(!IsHighResolution) {
if (!IsHighResolution)
throw new NotSupportedException("High-resolution timer not available"); throw new NotSupportedException("High-resolution timer not available");
} }
}
/// <summary> /// <summary>
/// Gets the numer of microseconds per timer tick. /// Gets the numer of microseconds per timer tick.
/// </summary> /// </summary>
public static double MicrosecondsPerTick { get; } = 1000000d / Frequency; public static Double MicrosecondsPerTick { get; } = 1000000d / Frequency;
/// <summary> /// <summary>
/// Gets the elapsed microseconds. /// Gets the elapsed microseconds.
/// </summary> /// </summary>
public long ElapsedMicroseconds => (long)(ElapsedTicks * MicrosecondsPerTick); public Int64 ElapsedMicroseconds => (Int64)(this.ElapsedTicks * MicrosecondsPerTick);
} }
} }

View File

@ -1,16 +1,14 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{
using Swan;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// Provides standard libc calls using platform-invoke /// Provides standard libc calls using platform-invoke
/// </summary> /// </summary>
internal static class Standard internal static class Standard {
{ internal const String LibCLibrary = "libc";
internal const string LibCLibrary = "libc";
#region LibC Calls #region LibC Calls
@ -19,18 +17,16 @@
/// </summary> /// </summary>
/// <param name="error">The error.</param> /// <param name="error">The error.</param>
/// <returns></returns> /// <returns></returns>
public static string Strerror(int error) public static String Strerror(Int32 error) {
{ if(!Runtime.IsUsingMonoRuntime) {
if (!Runtime.IsUsingMonoRuntime) return StrError(error); return StrError(error);
try
{
var buffer = new StringBuilder(256);
var result = Strerror(error, buffer, (ulong)buffer.Capacity);
return (result != -1) ? buffer.ToString() : null;
} }
catch (EntryPointNotFoundException)
{ try {
StringBuilder buffer = new StringBuilder(256);
Int32 result = Strerror(error, buffer, (UInt64)buffer.Capacity);
return (result != -1) ? buffer.ToString() : null;
} catch(EntryPointNotFoundException) {
return null; return null;
} }
} }
@ -42,7 +38,7 @@
/// <param name="mode">The mode.</param> /// <param name="mode">The mode.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)]
public static extern int Chmod(string filename, uint mode); public static extern Int32 Chmod(String filename, UInt32 mode);
/// <summary> /// <summary>
/// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero /// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero
@ -52,7 +48,7 @@
/// <param name="numberBase">The number base.</param> /// <param name="numberBase">The number base.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)]
public static extern int StringToInteger(string numberString, IntPtr endPointer, int numberBase); public static extern Int32 StringToInteger(String numberString, IntPtr endPointer, Int32 numberBase);
/// <summary> /// <summary>
/// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF. /// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF.
@ -63,7 +59,7 @@
/// <param name="count">The count.</param> /// <param name="count">The count.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)]
public static extern int Write(int fd, byte[] buffer, int count); public static extern Int32 Write(Int32 fd, Byte[] buffer, Int32 count);
/// <summary> /// <summary>
/// Fills in the structure with information about the system. /// Fills in the structure with information about the system.
@ -71,13 +67,13 @@
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
public static extern int Uname(out SystemName name); public static extern Int32 Uname(out SystemName name);
[DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)]
private static extern string StrError(int errnum); private static extern String StrError(Int32 errnum);
[DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)] [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)]
private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length); private static extern Int32 Strerror(Int32 error, [Out] StringBuilder buffer, UInt64 length);
#endregion #endregion
} }

View File

@ -1,47 +1,46 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// OS uname structure /// OS uname structure
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct SystemName internal struct SystemName {
{
/// <summary> /// <summary>
/// System name /// System name
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string SysName; public String SysName;
/// <summary> /// <summary>
/// Node name /// Node name
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string NodeName; public String NodeName;
/// <summary> /// <summary>
/// Release level /// Release level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Release; public String Release;
/// <summary> /// <summary>
/// Version level /// Version level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Version; public String Version;
/// <summary> /// <summary>
/// Hardware level /// Hardware level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Machine; public String Machine;
/// <summary> /// <summary>
/// Domain name /// Domain name
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string DomainName; public String DomainName;
} }
} }

View File

@ -1,10 +1,8 @@
namespace Unosquare.RaspberryIO.Native namespace Unosquare.RaspberryIO.Native {
{
/// <summary> /// <summary>
/// Defines the different threading locking keys /// Defines the different threading locking keys
/// </summary> /// </summary>
public enum ThreadLockKey public enum ThreadLockKey {
{
/// <summary> /// <summary>
/// The lock 0 /// The lock 0
/// </summary> /// </summary>

View File

@ -1,20 +1,17 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{ using Unosquare.Swan.Abstractions;
using Swan;
using Swan.Abstractions;
using System; using System;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// Provides access to timing and threading properties and methods /// Provides access to timing and threading properties and methods
/// </summary> /// </summary>
public class Timing : SingletonBase<Timing> public class Timing : SingletonBase<Timing> {
{
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="Timing"/> class from being created. /// Prevents a default instance of the <see cref="Timing"/> class from being created.
/// </summary> /// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception> /// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
private Timing() private Timing() {
{
// placeholder // placeholder
} }
@ -26,7 +23,7 @@
/// <value> /// <value>
/// The milliseconds since setup. /// The milliseconds since setup.
/// </value> /// </value>
public uint MillisecondsSinceSetup => WiringPi.Millis(); public UInt32 MillisecondsSinceSetup => WiringPi.Millis();
/// <summary> /// <summary>
/// This returns a number representing the number of microseconds since your /// This returns a number representing the number of microseconds since your
@ -36,7 +33,7 @@
/// <value> /// <value>
/// The microseconds since setup. /// The microseconds since setup.
/// </value> /// </value>
public uint MicrosecondsSinceSetup => WiringPi.Micros(); public UInt32 MicrosecondsSinceSetup => WiringPi.Micros();
/// <summary> /// <summary>
/// This causes program execution to pause for at least howLong milliseconds. /// This causes program execution to pause for at least howLong milliseconds.
@ -44,7 +41,7 @@
/// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public static void SleepMilliseconds(uint value) => WiringPi.Delay(value); public static void SleepMilliseconds(UInt32 value) => WiringPi.Delay(value);
/// <summary> /// <summary>
/// This causes program execution to pause for at least howLong microseconds. /// This causes program execution to pause for at least howLong microseconds.
@ -56,7 +53,7 @@
/// especially if using threads. /// especially if using threads.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public void SleepMicroseconds(uint value) => WiringPi.DelayMicroseconds(value); public void SleepMicroseconds(UInt32 value) => WiringPi.DelayMicroseconds(value);
/// <summary> /// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and
@ -67,11 +64,12 @@
/// (as long as no other programs are running with elevated priorities) /// (as long as no other programs are running with elevated priorities)
/// </summary> /// </summary>
/// <param name="priority">The priority.</param> /// <param name="priority">The priority.</param>
public void SetThreadPriority(int priority) public void SetThreadPriority(Int32 priority) {
{
priority = priority.Clamp(0, 99); priority = priority.Clamp(0, 99);
var result = WiringPi.PiHiPri(priority); Int32 result = WiringPi.PiHiPri(priority);
if (result < 0) HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority)); if(result < 0) {
HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority));
}
} }
/// <summary> /// <summary>
@ -80,13 +78,15 @@
/// </summary> /// </summary>
/// <param name="worker">The worker.</param> /// <param name="worker">The worker.</param>
/// <exception cref="ArgumentNullException">worker</exception> /// <exception cref="ArgumentNullException">worker</exception>
public void CreateThread(ThreadWorker worker) public void CreateThread(ThreadWorker worker) {
{ if(worker == null) {
if (worker == null)
throw new ArgumentNullException(nameof(worker)); throw new ArgumentNullException(nameof(worker));
}
var result = WiringPi.PiThreadCreate(worker); Int32 result = WiringPi.PiThreadCreate(worker);
if (result != 0) HardwareException.Throw(nameof(Timing), nameof(CreateThread)); if(result != 0) {
HardwareException.Throw(nameof(Timing), nameof(CreateThread));
}
} }
/// <summary> /// <summary>
@ -95,7 +95,7 @@
/// it will be stalled until the first process has unlocked the same key. /// it will be stalled until the first process has unlocked the same key.
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
public void Lock(ThreadLockKey key) => WiringPi.PiLock((int)key); public void Lock(ThreadLockKey key) => WiringPi.PiLock((Int32)key);
/// <summary> /// <summary>
/// These allow you to synchronize variable updates from your main program to any threads running in your program. /// These allow you to synchronize variable updates from your main program to any threads running in your program.
@ -103,6 +103,6 @@
/// it will be stalled until the first process has unlocked the same key. /// it will be stalled until the first process has unlocked the same key.
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((int)key); public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((Int32)key);
} }
} }

View File

@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public partial class WiringPi namespace Unosquare.RaspberryIO.Native {
{ public partial class WiringPi {
#region WiringPi - I2C Library Calls #region WiringPi - I2C Library Calls
/// <summary> /// <summary>
@ -12,7 +11,7 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)]
public static extern int WiringPiI2CRead(int fd); public static extern Int32 WiringPiI2CRead(Int32 fd);
/// <summary> /// <summary>
/// These read an 8-bit value from the device register indicated. /// These read an 8-bit value from the device register indicated.
@ -21,7 +20,7 @@
/// <param name="reg">The reg.</param> /// <param name="reg">The reg.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)]
public static extern int WiringPiI2CReadReg8(int fd, int reg); public static extern Int32 WiringPiI2CReadReg8(Int32 fd, Int32 reg);
/// <summary> /// <summary>
/// These read a 16-bit value from the device register indicated. /// These read a 16-bit value from the device register indicated.
@ -30,7 +29,7 @@
/// <param name="reg">The reg.</param> /// <param name="reg">The reg.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)]
public static extern int WiringPiI2CReadReg16(int fd, int reg); public static extern Int32 WiringPiI2CReadReg16(Int32 fd, Int32 reg);
/// <summary> /// <summary>
/// Simple device write. Some devices accept data this way without needing to access any internal registers. /// Simple device write. Some devices accept data this way without needing to access any internal registers.
@ -39,7 +38,7 @@
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)]
public static extern int WiringPiI2CWrite(int fd, int data); public static extern Int32 WiringPiI2CWrite(Int32 fd, Int32 data);
/// <summary> /// <summary>
/// These write an 8-bit data value into the device register indicated. /// These write an 8-bit data value into the device register indicated.
@ -49,7 +48,7 @@
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)]
public static extern int WiringPiI2CWriteReg8(int fd, int reg, int data); public static extern Int32 WiringPiI2CWriteReg8(Int32 fd, Int32 reg, Int32 data);
/// <summary> /// <summary>
/// These write a 16-bit data value into the device register indicated. /// These write a 16-bit data value into the device register indicated.
@ -59,7 +58,7 @@
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)]
public static extern int WiringPiI2CWriteReg16(int fd, int reg, int data); public static extern Int32 WiringPiI2CWriteReg16(Int32 fd, Int32 reg, Int32 data);
/// <summary> /// <summary>
/// This initialises the I2C system with your given device identifier. /// This initialises the I2C system with your given device identifier.
@ -71,7 +70,7 @@
/// <param name="devId">The dev identifier.</param> /// <param name="devId">The dev identifier.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)]
public static extern int WiringPiI2CSetup(int devId); public static extern Int32 WiringPiI2CSetup(Int32 devId);
#endregion #endregion

View File

@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public partial class WiringPi namespace Unosquare.RaspberryIO.Native {
{ public partial class WiringPi {
#region WiringPi - Serial Port #region WiringPi - Serial Port
/// <summary> /// <summary>
@ -17,7 +16,7 @@
/// <param name="baud">The baud.</param> /// <param name="baud">The baud.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)]
public static extern int SerialOpen(string device, int baud); public static extern Int32 SerialOpen(String device, Int32 baud);
/// <summary> /// <summary>
/// Closes the device identified by the file descriptor given. /// Closes the device identified by the file descriptor given.
@ -25,7 +24,7 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)]
public static extern int SerialClose(int fd); public static extern Int32 SerialClose(Int32 fd);
/// <summary> /// <summary>
/// Sends the single byte to the serial device identified by the given file descriptor. /// Sends the single byte to the serial device identified by the given file descriptor.
@ -33,7 +32,7 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <param name="c">The c.</param> /// <param name="c">The c.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)]
public static extern void SerialPutchar(int fd, byte c); public static extern void SerialPutchar(Int32 fd, Byte c);
/// <summary> /// <summary>
/// Sends the nul-terminated string to the serial device identified by the given file descriptor. /// Sends the nul-terminated string to the serial device identified by the given file descriptor.
@ -41,7 +40,7 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <param name="s">The s.</param> /// <param name="s">The s.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)]
public static extern void SerialPuts(int fd, string s); public static extern void SerialPuts(Int32 fd, String s);
/// <summary> /// <summary>
/// Returns the number of characters available for reading, or -1 for any error condition, /// Returns the number of characters available for reading, or -1 for any error condition,
@ -50,7 +49,7 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)]
public static extern int SerialDataAvail(int fd); public static extern Int32 SerialDataAvail(Int32 fd);
/// <summary> /// <summary>
/// Returns the next character available on the serial device. /// Returns the next character available on the serial device.
@ -59,14 +58,14 @@
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)]
public static extern int SerialGetchar(int fd); public static extern Int32 SerialGetchar(Int32 fd);
/// <summary> /// <summary>
/// This discards all data received, or waiting to be send down the given device. /// This discards all data received, or waiting to be send down the given device.
/// </summary> /// </summary>
/// <param name="fd">The fd.</param> /// <param name="fd">The fd.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)]
public static extern void SerialFlush(int fd); public static extern void SerialFlush(Int32 fd);
#endregion #endregion
} }

View File

@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public partial class WiringPi namespace Unosquare.RaspberryIO.Native {
{ public partial class WiringPi {
#region WiringPi - Shift Library #region WiringPi - Shift Library
/// <summary> /// <summary>
@ -16,7 +15,7 @@
/// <param name="order">The order.</param> /// <param name="order">The order.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)]
public static extern byte ShiftIn(byte dPin, byte cPin, byte order); public static extern Byte ShiftIn(Byte dPin, Byte cPin, Byte order);
/// <summary> /// <summary>
/// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin. /// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin.
@ -28,7 +27,7 @@
/// <param name="order">The order.</param> /// <param name="order">The order.</param>
/// <param name="val">The value.</param> /// <param name="val">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)]
public static extern void ShiftOut(byte dPin, byte cPin, byte order, byte val); public static extern void ShiftOut(Byte dPin, Byte cPin, Byte order, Byte val);
#endregion #endregion

View File

@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public partial class WiringPi namespace Unosquare.RaspberryIO.Native {
{ public partial class WiringPi {
#region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h) #region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h)
/// <summary> /// <summary>
@ -16,7 +15,7 @@
/// <param name="pwmRange">The PWM range.</param> /// <param name="pwmRange">The PWM range.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)]
public static extern int SoftPwmCreate(int pin, int initialValue, int pwmRange); public static extern Int32 SoftPwmCreate(Int32 pin, Int32 initialValue, Int32 pwmRange);
/// <summary> /// <summary>
/// This updates the PWM value on the given pin. The value is checked to be in-range and pins that havent previously /// This updates the PWM value on the given pin. The value is checked to be in-range and pins that havent previously
@ -25,14 +24,14 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)]
public static extern void SoftPwmWrite(int pin, int value); public static extern void SoftPwmWrite(Int32 pin, Int32 value);
/// <summary> /// <summary>
/// This function is undocumented /// This function is undocumented
/// </summary> /// </summary>
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)]
public static extern void SoftPwmStop(int pin); public static extern void SoftPwmStop(Int32 pin);
/// <summary> /// <summary>
/// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used. /// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used.
@ -41,14 +40,14 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)]
public static extern int SoftToneCreate(int pin); public static extern Int32 SoftToneCreate(Int32 pin);
/// <summary> /// <summary>
/// This function is undocumented /// This function is undocumented
/// </summary> /// </summary>
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)]
public static extern void SoftToneStop(int pin); public static extern void SoftToneStop(Int32 pin);
/// <summary> /// <summary>
/// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0. /// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0.
@ -56,7 +55,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param> /// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)]
public static extern void SoftToneWrite(int pin, int freq); public static extern void SoftToneWrite(Int32 pin, Int32 freq);
#endregion #endregion

View File

@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public partial class WiringPi namespace Unosquare.RaspberryIO.Native {
{ public partial class WiringPi {
#region WiringPi - SPI Library Calls #region WiringPi - SPI Library Calls
/// <summary> /// <summary>
@ -12,7 +11,7 @@
/// <param name="channel">The channel.</param> /// <param name="channel">The channel.</param>
/// <returns>Unknown</returns> /// <returns>Unknown</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)]
public static extern int WiringPiSPIGetFd(int channel); public static extern Int32 WiringPiSPIGetFd(Int32 channel);
/// <summary> /// <summary>
/// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus. /// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus.
@ -25,7 +24,7 @@
/// <param name="len">The length.</param> /// <param name="len">The length.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)]
public static extern int WiringPiSPIDataRW(int channel, byte[] data, int len); public static extern Int32 WiringPiSPIDataRW(Int32 channel, Byte[] data, Int32 len);
/// <summary> /// <summary>
/// This function is undocumented /// This function is undocumented
@ -35,7 +34,7 @@
/// <param name="mode">The mode.</param> /// <param name="mode">The mode.</param>
/// <returns>Unkown</returns> /// <returns>Unkown</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)]
public static extern int WiringPiSPISetupMode(int channel, int speed, int mode); public static extern Int32 WiringPiSPISetupMode(Int32 channel, Int32 speed, Int32 mode);
/// <summary> /// <summary>
/// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer /// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer
@ -46,7 +45,7 @@
/// <param name="speed">The speed.</param> /// <param name="speed">The speed.</param>
/// <returns>The Linux file descriptor for the device or -1 for error</returns> /// <returns>The Linux file descriptor for the device or -1 for error</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)]
public static extern int WiringPiSPISetup(int channel, int speed); public static extern Int32 WiringPiSPISetup(Int32 channel, Int32 speed);
#endregion #endregion
} }

View File

@ -1,16 +1,14 @@
namespace Unosquare.RaspberryIO.Native using System;
{
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
/// <summary> /// <summary>
/// Provides native C WiringPi Library function call wrappers /// Provides native C WiringPi Library function call wrappers
/// All credit for the native library goes to the author of http://wiringpi.com/ /// All credit for the native library goes to the author of http://wiringpi.com/
/// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h /// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h
/// </summary> /// </summary>
public partial class WiringPi public partial class WiringPi {
{ internal const String WiringPiLibrary = "libwiringPi.so.2.46";
internal const string WiringPiLibrary = "libwiringPi.so.2.46";
#region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h) #region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h)
@ -22,7 +20,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)]
public static extern int WiringPiSetup(); public static extern Int32 WiringPiSetup();
/// <summary> /// <summary>
/// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. /// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
@ -37,7 +35,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)]
public static extern int WiringPiSetupSys(); public static extern Int32 WiringPiSetupSys();
/// <summary> /// <summary>
/// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO /// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO
@ -47,7 +45,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)]
public static extern int WiringPiSetupGpio(); public static extern Int32 WiringPiSetupGpio();
/// <summary> /// <summary>
/// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only. /// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
@ -55,7 +53,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)]
public static extern int WiringPiSetupPhys(); public static extern Int32 WiringPiSetupPhys();
/// <summary> /// <summary>
/// This function is undocumented /// This function is undocumented
@ -63,7 +61,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param> /// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)]
public static extern void PinModeAlt(int pin, int mode); public static extern void PinModeAlt(Int32 pin, Int32 mode);
/// <summary> /// <summary>
/// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. /// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK.
@ -76,7 +74,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param> /// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)]
public static extern void PinMode(int pin, int mode); public static extern void PinMode(Int32 pin, Int32 mode);
/// <summary> /// <summary>
/// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. /// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input.
@ -90,7 +88,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="pud">The pud.</param> /// <param name="pud">The pud.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)]
public static extern void PullUpDnControl(int pin, int pud); public static extern void PullUpDnControl(Int32 pin, Int32 pud);
/// <summary> /// <summary>
/// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin. /// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin.
@ -98,7 +96,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)]
public static extern int DigitalRead(int pin); public static extern Int32 DigitalRead(Int32 pin);
/// <summary> /// <summary>
/// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output. /// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
@ -107,7 +105,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)]
public static extern void DigitalWrite(int pin, int value); public static extern void DigitalWrite(Int32 pin, Int32 value);
/// <summary> /// <summary>
/// Writes the value to the PWM register for the given pin. The Raspberry Pi has one /// Writes the value to the PWM register for the given pin. The Raspberry Pi has one
@ -118,7 +116,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)]
public static extern void PwmWrite(int pin, int value); public static extern void PwmWrite(Int32 pin, Int32 value);
/// <summary> /// <summary>
/// This returns the value read on the supplied analog input pin. You will need to /// This returns the value read on the supplied analog input pin. You will need to
@ -127,7 +125,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)]
public static extern int AnalogRead(int pin); public static extern Int32 AnalogRead(Int32 pin);
/// <summary> /// <summary>
/// This writes the given value to the supplied analog pin. You will need to register additional /// This writes the given value to the supplied analog pin. You will need to register additional
@ -136,7 +134,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)]
public static extern void AnalogWrite(int pin, int value); public static extern void AnalogWrite(Int32 pin, Int32 value);
/// <summary> /// <summary>
/// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and /// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and
@ -144,7 +142,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)]
public static extern int PiBoardRev(); public static extern Int32 PiBoardRev();
/// <summary> /// <summary>
/// This function is undocumented /// This function is undocumented
@ -155,7 +153,7 @@
/// <param name="overVolted">The over volted.</param> /// <param name="overVolted">The over volted.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)]
public static extern int PiBoardId(ref int model, ref int mem, ref int maker, ref int overVolted); public static extern Int32 PiBoardId(ref Int32 model, ref Int32 mem, ref Int32 maker, ref Int32 overVolted);
/// <summary> /// <summary>
/// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account. /// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account.
@ -163,7 +161,7 @@
/// <param name="wPiPin">The w pi pin.</param> /// <param name="wPiPin">The w pi pin.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)]
public static extern int WpiPinToGpio(int wPiPin); public static extern Int32 WpiPinToGpio(Int32 wPiPin);
/// <summary> /// <summary>
/// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector. /// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector.
@ -171,7 +169,7 @@
/// <param name="physPin">The physical pin.</param> /// <param name="physPin">The physical pin.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)]
public static extern int PhysPinToGpio(int physPin); public static extern Int32 PhysPinToGpio(Int32 physPin);
/// <summary> /// <summary>
/// This sets the “strength” of the pad drivers for a particular group of pins. /// This sets the “strength” of the pad drivers for a particular group of pins.
@ -181,7 +179,7 @@
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)]
public static extern int SetPadDrive(int group, int value); public static extern Int32 SetPadDrive(Int32 group, Int32 value);
/// <summary> /// <summary>
/// Undocumented function /// Undocumented function
@ -189,7 +187,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)]
public static extern int GetAlt(int pin); public static extern Int32 GetAlt(Int32 pin);
/// <summary> /// <summary>
/// Undocumented function /// Undocumented function
@ -198,7 +196,7 @@
/// <param name="freq">The freq.</param> /// <param name="freq">The freq.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)]
public static extern int PwmToneWrite(int pin, int freq); public static extern Int32 PwmToneWrite(Int32 pin, Int32 freq);
/// <summary> /// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins. /// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@ -206,7 +204,7 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)]
public static extern void DigitalWriteByte(int value); public static extern void DigitalWriteByte(Int32 value);
/// <summary> /// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins. /// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@ -214,7 +212,7 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)]
public static extern void DigitalWriteByte2(int value); public static extern void DigitalWriteByte2(Int32 value);
/// <summary> /// <summary>
/// Undocumented function /// Undocumented function
@ -223,7 +221,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)]
public static extern uint DigitalReadByte(); public static extern UInt32 DigitalReadByte();
/// <summary> /// <summary>
/// Undocumented function /// Undocumented function
@ -232,7 +230,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)]
public static extern uint DigitalReadByte2(); public static extern UInt32 DigitalReadByte2();
/// <summary> /// <summary>
/// The PWM generator can run in 2 modes “balanced” and “mark:space”. The mark:space mode is traditional, /// The PWM generator can run in 2 modes “balanced” and “mark:space”. The mark:space mode is traditional,
@ -240,14 +238,14 @@
/// </summary> /// </summary>
/// <param name="mode">The mode.</param> /// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)]
public static extern void PwmSetMode(int mode); public static extern void PwmSetMode(Int32 mode);
/// <summary> /// <summary>
/// This sets the range register in the PWM generator. The default is 1024. /// This sets the range register in the PWM generator. The default is 1024.
/// </summary> /// </summary>
/// <param name="range">The range.</param> /// <param name="range">The range.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)]
public static extern void PwmSetRange(uint range); public static extern void PwmSetRange(UInt32 range);
/// <summary> /// <summary>
/// This sets the divisor for the PWM clock. /// This sets the divisor for the PWM clock.
@ -256,7 +254,7 @@
/// </summary> /// </summary>
/// <param name="divisor">The divisor.</param> /// <param name="divisor">The divisor.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)]
public static extern void PwmSetClock(int divisor); public static extern void PwmSetClock(Int32 divisor);
/// <summary> /// <summary>
/// Undocumented function /// Undocumented function
@ -264,7 +262,7 @@
/// <param name="pin">The pin.</param> /// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param> /// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)]
public static extern void GpioClockSet(int pin, int freq); public static extern void GpioClockSet(Int32 pin, Int32 freq);
/// <summary> /// <summary>
/// Note: Jan 2013: The waitForInterrupt() function is deprecated you should use the newer and easier to use wiringPiISR() function below. /// Note: Jan 2013: The waitForInterrupt() function is deprecated you should use the newer and easier to use wiringPiISR() function below.
@ -281,7 +279,7 @@
/// <returns>The result code</returns> /// <returns>The result code</returns>
[Obsolete] [Obsolete]
[DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)]
public static extern int WaitForInterrupt(int pin, int timeout); public static extern Int32 WaitForInterrupt(Int32 pin, Int32 timeout);
/// <summary> /// <summary>
/// This function registers a function to received interrupts on the specified pin. /// This function registers a function to received interrupts on the specified pin.
@ -302,7 +300,7 @@
/// <param name="method">The method.</param> /// <param name="method">The method.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)]
public static extern int WiringPiISR(int pin, int mode, InterruptServiceRoutineCallback method); public static extern Int32 WiringPiISR(Int32 pin, Int32 mode, InterruptServiceRoutineCallback method);
/// <summary> /// <summary>
/// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration. /// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration.
@ -313,7 +311,7 @@
/// <param name="method">The method.</param> /// <param name="method">The method.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)]
public static extern int PiThreadCreate(ThreadWorker method); public static extern Int32 PiThreadCreate(ThreadWorker method);
/// <summary> /// <summary>
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
@ -324,7 +322,7 @@
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)]
public static extern void PiLock(int key); public static extern void PiLock(Int32 key);
/// <summary> /// <summary>
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
@ -335,7 +333,7 @@
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)]
public static extern void PiUnlock(int key); public static extern void PiUnlock(Int32 key);
/// <summary> /// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority
@ -350,7 +348,7 @@
/// <param name="priority">The priority.</param> /// <param name="priority">The priority.</param>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)]
public static extern int PiHiPri(int priority); public static extern Int32 PiHiPri(Int32 priority);
/// <summary> /// <summary>
/// This causes program execution to pause for at least howLong milliseconds. /// This causes program execution to pause for at least howLong milliseconds.
@ -359,7 +357,7 @@
/// </summary> /// </summary>
/// <param name="howLong">The how long.</param> /// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)]
public static extern void Delay(uint howLong); public static extern void Delay(UInt32 howLong);
/// <summary> /// <summary>
/// This causes program execution to pause for at least howLong microseconds. /// This causes program execution to pause for at least howLong microseconds.
@ -371,7 +369,7 @@
/// </summary> /// </summary>
/// <param name="howLong">The how long.</param> /// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)]
public static extern void DelayMicroseconds(uint howLong); public static extern void DelayMicroseconds(UInt32 howLong);
/// <summary> /// <summary>
/// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions. /// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions.
@ -379,7 +377,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)]
public static extern uint Millis(); public static extern UInt32 Millis();
/// <summary> /// <summary>
/// This returns a number representing the number of microseconds since your program called one of /// This returns a number representing the number of microseconds since your program called one of
@ -387,7 +385,7 @@
/// </summary> /// </summary>
/// <returns>The result code</returns> /// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)]
public static extern uint Micros(); public static extern UInt32 Micros();
#endregion #endregion
} }

View File

@ -1,26 +1,23 @@
namespace Unosquare.RaspberryIO using Unosquare.RaspberryIO.Camera;
{ using Unosquare.RaspberryIO.Computer;
using Camera; using Unosquare.RaspberryIO.Gpio;
using Computer; using Unosquare.RaspberryIO.Native;
using Gpio;
using Native;
using System.Threading.Tasks; using System.Threading.Tasks;
using Swan.Components; using Unosquare.Swan.Components;
using System;
namespace Unosquare.RaspberryIO {
/// <summary> /// <summary>
/// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera
/// </summary> /// </summary>
public static class Pi public static class Pi {
{ private static readonly Object SyncLock = new Object();
private static readonly object SyncLock = new object();
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Pi" /> class. /// Initializes static members of the <see cref="Pi" /> class.
/// </summary> /// </summary>
static Pi() static Pi() {
{ lock(SyncLock) {
lock (SyncLock)
{
// Extraction of embedded resources // Extraction of embedded resources
Resources.EmbeddedResources.ExtractAll(); Resources.EmbeddedResources.ExtractAll();
@ -40,42 +37,56 @@
/// <summary> /// <summary>
/// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins. /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins.
/// </summary> /// </summary>
public static GpioController Gpio { get; } public static GpioController Gpio {
get;
}
/// <summary> /// <summary>
/// Provides information on this Raspberry Pi's CPU and form factor. /// Provides information on this Raspberry Pi's CPU and form factor.
/// </summary> /// </summary>
public static SystemInfo Info { get; } public static SystemInfo Info {
get;
}
/// <summary> /// <summary>
/// Provides access to The PI's Timing and threading API /// Provides access to The PI's Timing and threading API
/// </summary> /// </summary>
public static Timing Timing { get; } public static Timing Timing {
get;
}
/// <summary> /// <summary>
/// Provides access to the 2-channel SPI bus /// Provides access to the 2-channel SPI bus
/// </summary> /// </summary>
public static SpiBus Spi { get; } public static SpiBus Spi {
get;
}
/// <summary> /// <summary>
/// Provides access to the functionality of the i2c bus. /// Provides access to the functionality of the i2c bus.
/// </summary> /// </summary>
public static I2CBus I2C { get; } public static I2CBus I2C {
get;
}
/// <summary> /// <summary>
/// Provides access to the official Raspberry Pi Camera /// Provides access to the official Raspberry Pi Camera
/// </summary> /// </summary>
public static CameraController Camera { get; } public static CameraController Camera {
get;
}
/// <summary> /// <summary>
/// Provides access to the official Raspberry Pi 7-inch DSI Display /// Provides access to the official Raspberry Pi 7-inch DSI Display
/// </summary> /// </summary>
public static DsiDisplay PiDisplay { get; } public static DsiDisplay PiDisplay {
get;
}
/// <summary> /// <summary>
/// Gets the logger source name. /// Gets the logger source name.
/// </summary> /// </summary>
internal static string LoggerSource => typeof(Pi).Namespace; internal static String LoggerSource => typeof(Pi).Namespace;
#endregion #endregion

View File

@ -1,24 +1,18 @@
namespace Unosquare.RaspberryIO.Resources using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native;
using Swan;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
namespace Unosquare.RaspberryIO.Resources {
/// <summary> /// <summary>
/// Provides access to embedded assembly files /// Provides access to embedded assembly files
/// </summary> /// </summary>
internal static class EmbeddedResources internal static class EmbeddedResources {
{
/// <summary> /// <summary>
/// Initializes static members of the <see cref="EmbeddedResources"/> class. /// Initializes static members of the <see cref="EmbeddedResources"/> class.
/// </summary> /// </summary>
static EmbeddedResources() static EmbeddedResources() => ResourceNames = new ReadOnlyCollection<String>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames());
{
ResourceNames =
new ReadOnlyCollection<string>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames());
}
/// <summary> /// <summary>
/// Gets the resource names. /// Gets the resource names.
@ -26,36 +20,32 @@
/// <value> /// <value>
/// The resource names. /// The resource names.
/// </value> /// </value>
public static ReadOnlyCollection<string> ResourceNames { get; } public static ReadOnlyCollection<String> ResourceNames {
get;
}
/// <summary> /// <summary>
/// Extracts all the file resources to the specified base path. /// Extracts all the file resources to the specified base path.
/// </summary> /// </summary>
public static void ExtractAll() public static void ExtractAll() {
{ String basePath = Runtime.EntryAssemblyDirectory;
var basePath = Runtime.EntryAssemblyDirectory; Int32 executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
var executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
foreach (var resourceName in ResourceNames) foreach(String resourceName in ResourceNames) {
{ String filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length);
var filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length); String targetPath = Path.Combine(basePath, filename);
var targetPath = Path.Combine(basePath, filename); if(File.Exists(targetPath)) {
if (File.Exists(targetPath)) return; return;
}
using (var stream = typeof(EmbeddedResources).Assembly() using(Stream stream = typeof(EmbeddedResources).Assembly().GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) {
.GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) using(FileStream outputStream = File.OpenWrite(targetPath)) {
{
using (var outputStream = File.OpenWrite(targetPath))
{
stream?.CopyTo(outputStream); stream?.CopyTo(outputStream);
} }
try try {
{ _ = Standard.Chmod(targetPath, (UInt32)executablePermissions);
Standard.Chmod(targetPath, (uint)executablePermissions); } catch {
}
catch
{
/* Ignore */ /* Ignore */
} }
} }

View File

@ -1,8 +1,7 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
using System.Threading; using System.Threading;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Provides a generic implementation of an Atomic (interlocked) type /// Provides a generic implementation of an Atomic (interlocked) type
/// ///
@ -11,35 +10,29 @@
/// </summary> /// </summary>
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam> /// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam>
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>> public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>>
where T : struct, IComparable, IComparable<T>, IEquatable<T> where T : struct, IComparable, IComparable<T>, IEquatable<T> {
{ private Int64 _backingValue;
private long _backingValue;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class. /// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
/// </summary> /// </summary>
/// <param name="initialValue">The initial value.</param> /// <param name="initialValue">The initial value.</param>
protected AtomicTypeBase(long initialValue) protected AtomicTypeBase(Int64 initialValue) => this.BackingValue = initialValue;
{
BackingValue = initialValue;
}
/// <summary> /// <summary>
/// Gets or sets the value. /// Gets or sets the value.
/// </summary> /// </summary>
public T Value public T Value {
{ get => this.FromLong(this.BackingValue);
get => FromLong(BackingValue); set => this.BackingValue = this.ToLong(value);
set => BackingValue = ToLong(value);
} }
/// <summary> /// <summary>
/// Gets or sets the backing value. /// Gets or sets the backing value.
/// </summary> /// </summary>
protected long BackingValue protected Int64 BackingValue {
{ get => Interlocked.Read(ref this._backingValue);
get => Interlocked.Read(ref _backingValue); set => Interlocked.Exchange(ref this._backingValue, value);
set => Interlocked.Exchange(ref _backingValue, value);
} }
/// <summary> /// <summary>
@ -50,7 +43,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true; public static Boolean operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
/// <summary> /// <summary>
/// Implements the operator !=. /// Implements the operator !=.
@ -60,7 +53,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false; public static Boolean operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
/// <summary> /// <summary>
/// Implements the operator &gt;. /// Implements the operator &gt;.
@ -70,7 +63,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0; public static Boolean operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
/// <summary> /// <summary>
/// Implements the operator &lt;. /// Implements the operator &lt;.
@ -80,7 +73,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0; public static Boolean operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
/// <summary> /// <summary>
/// Implements the operator &gt;=. /// Implements the operator &gt;=.
@ -90,7 +83,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0; public static Boolean operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
/// <summary> /// <summary>
/// Implements the operator &lt;=. /// Implements the operator &lt;=.
@ -100,7 +93,7 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0; public static Boolean operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
/// <summary> /// <summary>
/// Implements the operator ++. /// Implements the operator ++.
@ -109,9 +102,8 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) {
{ _ = Interlocked.Increment(ref instance._backingValue);
Interlocked.Increment(ref instance._backingValue);
return instance; return instance;
} }
@ -122,9 +114,8 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) {
{ _ = Interlocked.Decrement(ref instance._backingValue);
Interlocked.Decrement(ref instance._backingValue);
return instance; return instance;
} }
@ -136,9 +127,8 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand) public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, Int64 operand) {
{ instance.BackingValue += operand;
instance.BackingValue = instance.BackingValue + operand;
return instance; return instance;
} }
@ -150,9 +140,8 @@
/// <returns> /// <returns>
/// The result of the operator. /// The result of the operator.
/// </returns> /// </returns>
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand) public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, Int64 operand) {
{ instance.BackingValue -= operand;
instance.BackingValue = instance.BackingValue - operand;
return instance; return instance;
} }
@ -162,16 +151,14 @@
/// <param name="other">The other instance.</param> /// <param name="other">The other instance.</param>
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns> /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
/// <exception cref="ArgumentException">When types are incompatible.</exception> /// <exception cref="ArgumentException">When types are incompatible.</exception>
public int CompareTo(object other) public Int32 CompareTo(Object other) {
{ switch(other) {
switch (other)
{
case null: case null:
return 1; return 1;
case AtomicTypeBase<T> atomic: case AtomicTypeBase<T> atomic:
return BackingValue.CompareTo(atomic.BackingValue); return this.BackingValue.CompareTo(atomic.BackingValue);
case T variable: case T variable:
return Value.CompareTo(variable); return this.Value.CompareTo(variable);
} }
throw new ArgumentException("Incompatible comparison types"); throw new ArgumentException("Incompatible comparison types");
@ -182,30 +169,28 @@
/// </summary> /// </summary>
/// <param name="other">The other instance.</param> /// <param name="other">The other instance.</param>
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns> /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
public int CompareTo(T other) => Value.CompareTo(other); public Int32 CompareTo(T other) => this.Value.CompareTo(other);
/// <summary> /// <summary>
/// Compares the value to the other instance. /// Compares the value to the other instance.
/// </summary> /// </summary>
/// <param name="other">The other instance.</param> /// <param name="other">The other instance.</param>
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns> /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
public int CompareTo(AtomicTypeBase<T> other) => BackingValue.CompareTo(other?.BackingValue ?? default); public Int32 CompareTo(AtomicTypeBase<T> other) => this.BackingValue.CompareTo(other?.BackingValue ?? default);
/// <summary> /// <summary>
/// Determines whether the specified <see cref="object" />, is equal to this instance. /// Determines whether the specified <see cref="Object" />, is equal to this instance.
/// </summary> /// </summary>
/// <param name="other">The <see cref="object" /> to compare with this instance.</param> /// <param name="other">The <see cref="Object" /> to compare with this instance.</param>
/// <returns> /// <returns>
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>. /// <c>true</c> if the specified <see cref="Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns> /// </returns>
public override bool Equals(object other) public override Boolean Equals(Object other) {
{ switch(other) {
switch (other)
{
case AtomicTypeBase<T> atomic: case AtomicTypeBase<T> atomic:
return Equals(atomic); return this.Equals(atomic);
case T variable: case T variable:
return Equals(variable); return this.Equals(variable);
} }
return false; return false;
@ -217,27 +202,27 @@
/// <returns> /// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns> /// </returns>
public override int GetHashCode() => BackingValue.GetHashCode(); public override Int32 GetHashCode() => this.BackingValue.GetHashCode();
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(AtomicTypeBase<T> other) => public Boolean Equals(AtomicTypeBase<T> other) =>
BackingValue == (other?.BackingValue ?? default); this.BackingValue == (other?.BackingValue ?? default);
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(T other) => Equals(Value, other); public Boolean Equals(T other) => Equals(this.Value, other);
/// <summary> /// <summary>
/// Converts from a long value to the target type. /// Converts from a long value to the target type.
/// </summary> /// </summary>
/// <param name="backingValue">The backing value.</param> /// <param name="backingValue">The backing value.</param>
/// <returns>The value converted form a long value.</returns> /// <returns>The value converted form a long value.</returns>
protected abstract T FromLong(long backingValue); protected abstract T FromLong(Int64 backingValue);
/// <summary> /// <summary>
/// Converts from the target type to a long value. /// Converts from the target type to a long value.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>The value converted to a long value.</returns> /// <returns>The value converted to a long value.</returns>
protected abstract long ToLong(T value); protected abstract Int64 ToLong(T value);
} }
} }

View File

@ -1,22 +1,20 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
using System.Threading; using System.Threading;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time /// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time
/// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out /// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out
/// on the same <see cref="ThreadPool"/> thread every time the timer fires. /// on the same <see cref="ThreadPool"/> thread every time the timer fires.
/// </summary> /// </summary>
public sealed class ExclusiveTimer : IDisposable public sealed class ExclusiveTimer : IDisposable {
{ private readonly Object _syncLock = new Object();
private readonly object _syncLock = new object();
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true); private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
private readonly Timer _backingTimer; private readonly Timer _backingTimer;
private readonly TimerCallback _userCallback; private readonly TimerCallback _userCallback;
private readonly AtomicBoolean _isDisposing = new AtomicBoolean(); private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
private readonly AtomicBoolean _isDisposed = new AtomicBoolean(); private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
private int _period; private Int32 _period;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
@ -25,11 +23,10 @@
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public ExclusiveTimer(TimerCallback timerCallback, object state, int dueTime, int period) public ExclusiveTimer(TimerCallback timerCallback, Object state, Int32 dueTime, Int32 period) {
{ this._period = period;
_period = period; this._userCallback = timerCallback;
_userCallback = timerCallback; this._backingTimer = new Timer(this.InternalCallback, state ?? this, dueTime, Timeout.Infinite);
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
} }
/// <summary> /// <summary>
@ -39,9 +36,8 @@
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public ExclusiveTimer(TimerCallback timerCallback, object state, TimeSpan dueTime, TimeSpan period) public ExclusiveTimer(TimerCallback timerCallback, Object state, TimeSpan dueTime, TimeSpan period)
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) : this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) {
{
// placeholder // placeholder
} }
@ -50,8 +46,7 @@
/// </summary> /// </summary>
/// <param name="timerCallback">The timer callback.</param> /// <param name="timerCallback">The timer callback.</param>
public ExclusiveTimer(TimerCallback timerCallback) public ExclusiveTimer(TimerCallback timerCallback)
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) : this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) {
{
// placholder // placholder
} }
@ -61,9 +56,8 @@
/// <param name="timerCallback">The timer callback.</param> /// <param name="timerCallback">The timer callback.</param>
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public ExclusiveTimer(Action timerCallback, int dueTime, int period) public ExclusiveTimer(Action timerCallback, Int32 dueTime, Int32 period)
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period) : this(s => timerCallback?.Invoke(), null, dueTime, period) {
{
// placeholder // placeholder
} }
@ -74,8 +68,7 @@
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period) public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period) : this(s => timerCallback?.Invoke(), null, dueTime, period) {
{
// placeholder // placeholder
} }
@ -84,8 +77,7 @@
/// </summary> /// </summary>
/// <param name="timerCallback">The timer callback.</param> /// <param name="timerCallback">The timer callback.</param>
public ExclusiveTimer(Action timerCallback) public ExclusiveTimer(Action timerCallback)
: this(timerCallback, Timeout.Infinite, Timeout.Infinite) : this(timerCallback, Timeout.Infinite, Timeout.Infinite) {
{
// placeholder // placeholder
} }
@ -95,7 +87,7 @@
/// <value> /// <value>
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>. /// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsDisposing => _isDisposing.Value; public Boolean IsDisposing => this._isDisposing.Value;
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is disposed. /// Gets a value indicating whether this instance is disposed.
@ -103,18 +95,17 @@
/// <value> /// <value>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>. /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsDisposed => _isDisposed.Value; public Boolean IsDisposed => this._isDisposed.Value;
/// <summary> /// <summary>
/// Changes the start time and the interval between method invocations for the internal timer. /// Changes the start time and the interval between method invocations for the internal timer.
/// </summary> /// </summary>
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public void Change(int dueTime, int period) public void Change(Int32 dueTime, Int32 period) {
{ this._period = period;
_period = period;
_backingTimer.Change(dueTime, Timeout.Infinite); _ = this._backingTimer.Change(dueTime, Timeout.Infinite);
} }
/// <summary> /// <summary>
@ -123,46 +114,42 @@
/// <param name="dueTime">The due time.</param> /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public void Change(TimeSpan dueTime, TimeSpan period) public void Change(TimeSpan dueTime, TimeSpan period)
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)); => this.Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
/// <summary> /// <summary>
/// Changes the interval between method invocations for the internal timer. /// Changes the interval between method invocations for the internal timer.
/// </summary> /// </summary>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public void Resume(int period) => Change(0, period); public void Resume(Int32 period) => this.Change(0, period);
/// <summary> /// <summary>
/// Changes the interval between method invocations for the internal timer. /// Changes the interval between method invocations for the internal timer.
/// </summary> /// </summary>
/// <param name="period">The period.</param> /// <param name="period">The period.</param>
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period); public void Resume(TimeSpan period) => this.Change(TimeSpan.Zero, period);
/// <summary> /// <summary>
/// Pauses this instance. /// Pauses this instance.
/// </summary> /// </summary>
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite); public void Pause() => this.Change(Timeout.Infinite, Timeout.Infinite);
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose() {
{ lock(this._syncLock) {
lock (_syncLock) if(this._isDisposed == true || this._isDisposing == true) {
{
if (_isDisposed == true || _isDisposing == true)
return; return;
_isDisposing.Value = true;
} }
try this._isDisposing.Value = true;
{
_backingTimer.Dispose();
_cycleDoneEvent.Wait();
_cycleDoneEvent.Dispose();
} }
finally
{ try {
_isDisposed.Value = true; this._backingTimer.Dispose();
_isDisposing.Value = false; this._cycleDoneEvent.Wait();
this._cycleDoneEvent.Dispose();
} finally {
this._isDisposed.Value = true;
this._isDisposing.Value = false;
} }
} }
@ -170,27 +157,24 @@
/// Logic that runs every time the timer hits the due time. /// Logic that runs every time the timer hits the due time.
/// </summary> /// </summary>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
private void InternalCallback(object state) private void InternalCallback(Object state) {
{ lock(this._syncLock) {
lock (_syncLock) if(this.IsDisposed || this.IsDisposing) {
{ return;
if (IsDisposed || IsDisposing) }
}
if(this._cycleDoneEvent.IsSet == false) {
return; return;
} }
if (_cycleDoneEvent.IsSet == false) this._cycleDoneEvent.Reset();
return;
_cycleDoneEvent.Reset(); try {
this._userCallback(state);
try } finally {
{ this._cycleDoneEvent?.Set();
_userCallback(state); _ = this._backingTimer?.Change(this._period, Timeout.Infinite);
}
finally
{
_cycleDoneEvent?.Set();
_backingTimer?.Change(_period, Timeout.Infinite);
} }
} }
} }

View File

@ -1,24 +1,21 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Represents a generic expression parser. /// Represents a generic expression parser.
/// </summary> /// </summary>
public abstract class ExpressionParser public abstract class ExpressionParser {
{
/// <summary> /// <summary>
/// Resolves the expression. /// Resolves the expression.
/// </summary> /// </summary>
/// <typeparam name="T">The type of expression result.</typeparam> /// <typeparam name="T">The type of expression result.</typeparam>
/// <param name="tokens">The tokens.</param> /// <param name="tokens">The tokens.</param>
/// <returns>The representation of the expression parsed.</returns> /// <returns>The representation of the expression parsed.</returns>
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) {
{ UnaryExpression conversion = Expression.Convert(this.Parse(tokens), typeof(T));
var conversion = Expression.Convert(Parse(tokens), typeof(T));
return Expression.Lambda<Func<T>>(conversion).Compile()(); return Expression.Lambda<Func<T>>(conversion).Compile()();
} }
@ -27,17 +24,15 @@
/// </summary> /// </summary>
/// <param name="tokens">The tokens.</param> /// <param name="tokens">The tokens.</param>
/// <returns>The final expression.</returns> /// <returns>The final expression.</returns>
public virtual Expression Parse(IEnumerable<Token> tokens) public virtual Expression Parse(IEnumerable<Token> tokens) {
{ List<Stack<Expression>> expressionStack = new List<Stack<Expression>>();
var expressionStack = new List<Stack<Expression>>();
foreach (var token in tokens) foreach(Token token in tokens) {
{ if(expressionStack.Any() == false) {
if (expressionStack.Any() == false)
expressionStack.Add(new Stack<Expression>()); expressionStack.Add(new Stack<Expression>());
}
switch (token.Type) switch(token.Type) {
{
case TokenType.Wall: case TokenType.Wall:
expressionStack.Add(new Stack<Expression>()); expressionStack.Add(new Stack<Expression>());
break; break;
@ -45,21 +40,20 @@
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value))); expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
break; break;
case TokenType.Variable: case TokenType.Variable:
ResolveVariable(token.Value, expressionStack.Last()); this.ResolveVariable(token.Value, expressionStack.Last());
break; break;
case TokenType.String: case TokenType.String:
expressionStack.Last().Push(Expression.Constant(token.Value)); expressionStack.Last().Push(Expression.Constant(token.Value));
break; break;
case TokenType.Operator: case TokenType.Operator:
ResolveOperator(token.Value, expressionStack.Last()); this.ResolveOperator(token.Value, expressionStack.Last());
break; break;
case TokenType.Function: case TokenType.Function:
ResolveFunction(token.Value, expressionStack.Last()); this.ResolveFunction(token.Value, expressionStack.Last());
if (expressionStack.Count > 1 && expressionStack.Last().Count == 1) if(expressionStack.Count > 1 && expressionStack.Last().Count == 1) {
{ Expression lastValue = expressionStack.Last().Pop();
var lastValue = expressionStack.Last().Pop(); _ = expressionStack.Remove(expressionStack.Last());
expressionStack.Remove(expressionStack.Last());
expressionStack.Last().Push(lastValue); expressionStack.Last().Push(lastValue);
} }
@ -75,20 +69,20 @@
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param> /// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveVariable(string value, Stack<Expression> expressionStack); public abstract void ResolveVariable(String value, Stack<Expression> expressionStack);
/// <summary> /// <summary>
/// Resolves the operator. /// Resolves the operator.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param> /// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveOperator(string value, Stack<Expression> expressionStack); public abstract void ResolveOperator(String value, Stack<Expression> expressionStack);
/// <summary> /// <summary>
/// Resolves the function. /// Resolves the function.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param> /// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveFunction(string value, Stack<Expression> expressionStack); public abstract void ResolveFunction(String value, Stack<Expression> expressionStack);
} }
} }

View File

@ -1,27 +1,31 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Interface object map. /// Interface object map.
/// </summary> /// </summary>
public interface IObjectMap public interface IObjectMap {
{
/// <summary> /// <summary>
/// Gets or sets the map. /// Gets or sets the map.
/// </summary> /// </summary>
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; } Dictionary<PropertyInfo, List<PropertyInfo>> Map {
get;
}
/// <summary> /// <summary>
/// Gets or sets the type of the source. /// Gets or sets the type of the source.
/// </summary> /// </summary>
Type SourceType { get; } Type SourceType {
get;
}
/// <summary> /// <summary>
/// Gets or sets the type of the destination. /// Gets or sets the type of the destination.
/// </summary> /// </summary>
Type DestinationType { get; } Type DestinationType {
get;
}
} }
} }

View File

@ -1,12 +1,10 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Defines a generic interface for synchronized locking mechanisms. /// Defines a generic interface for synchronized locking mechanisms.
/// </summary> /// </summary>
public interface ISyncLocker : IDisposable public interface ISyncLocker : IDisposable {
{
/// <summary> /// <summary>
/// Acquires a writer lock. /// Acquires a writer lock.
/// The lock is released when the returned locking object is disposed. /// The lock is released when the returned locking object is disposed.

View File

@ -1,14 +1,16 @@
namespace Unosquare.Swan.Abstractions using System;
{
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// A simple Validator interface. /// A simple Validator interface.
/// </summary> /// </summary>
public interface IValidator public interface IValidator {
{
/// <summary> /// <summary>
/// The error message. /// The error message.
/// </summary> /// </summary>
string ErrorMessage { get; } String ErrorMessage {
get;
}
/// <summary> /// <summary>
/// Checks if a value is valid. /// Checks if a value is valid.
@ -16,6 +18,6 @@
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type.</typeparam>
/// <param name="value"> The value.</param> /// <param name="value"> The value.</param>
/// <returns>True if it is valid.False if it is not.</returns> /// <returns>True if it is valid.False if it is not.</returns>
bool IsValid<T>(T value); Boolean IsValid<T>(T value);
} }
} }

View File

@ -1,33 +1,39 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim. /// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
/// </summary> /// </summary>
/// <seealso cref="IDisposable" /> /// <seealso cref="IDisposable" />
public interface IWaitEvent : IDisposable public interface IWaitEvent : IDisposable {
{
/// <summary> /// <summary>
/// Gets a value indicating whether the event is in the completed state. /// Gets a value indicating whether the event is in the completed state.
/// </summary> /// </summary>
bool IsCompleted { get; } Boolean IsCompleted {
get;
}
/// <summary> /// <summary>
/// Gets a value indicating whether the Begin method has been called. /// Gets a value indicating whether the Begin method has been called.
/// It returns false after the Complete method is called. /// It returns false after the Complete method is called.
/// </summary> /// </summary>
bool IsInProgress { get; } Boolean IsInProgress {
get;
}
/// <summary> /// <summary>
/// Returns true if the underlying handle is not closed and it is still valid. /// Returns true if the underlying handle is not closed and it is still valid.
/// </summary> /// </summary>
bool IsValid { get; } Boolean IsValid {
get;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is disposed. /// Gets a value indicating whether this instance is disposed.
/// </summary> /// </summary>
bool IsDisposed { get; } Boolean IsDisposed {
get;
}
/// <summary> /// <summary>
/// Enters the state in which waiters need to wait. /// Enters the state in which waiters need to wait.
@ -52,6 +58,6 @@
/// </summary> /// </summary>
/// <param name="timeout">The maximum amount of time to wait for.</param> /// <param name="timeout">The maximum amount of time to wait for.</param>
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns> /// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns>
bool Wait(TimeSpan timeout); Boolean Wait(TimeSpan timeout);
} }
} }

View File

@ -1,10 +1,8 @@
namespace Unosquare.Swan.Abstractions namespace Unosquare.Swan.Abstractions {
{
/// <summary> /// <summary>
/// A simple interface for application workers. /// A simple interface for application workers.
/// </summary> /// </summary>
public interface IWorker public interface IWorker {
{
/// <summary> /// <summary>
/// Should start the task immediately and asynchronously. /// Should start the task immediately and asynchronously.
/// </summary> /// </summary>

View File

@ -1,16 +1,12 @@
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
namespace Unosquare.Swan.Abstractions
{
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Swan; namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Represents an background worker abstraction with a life cycle and running at a independent thread. /// Represents an background worker abstraction with a life cycle and running at a independent thread.
/// </summary> /// </summary>
public abstract class RunnerBase public abstract class RunnerBase {
{
private Thread _worker; private Thread _worker;
private CancellationTokenSource _cancelTokenSource; private CancellationTokenSource _cancelTokenSource;
private ManualResetEvent _workFinished; private ManualResetEvent _workFinished;
@ -19,10 +15,9 @@ namespace Unosquare.Swan.Abstractions
/// Initializes a new instance of the <see cref="RunnerBase"/> class. /// Initializes a new instance of the <see cref="RunnerBase"/> class.
/// </summary> /// </summary>
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param> /// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
protected RunnerBase(bool isEnabled) protected RunnerBase(Boolean isEnabled) {
{ this.Name = this.GetType().Name;
Name = GetType().Name; this.IsEnabled = isEnabled;
IsEnabled = isEnabled;
} }
/// <summary> /// <summary>
@ -31,7 +26,7 @@ namespace Unosquare.Swan.Abstractions
/// <value> /// <value>
/// The error messages. /// The error messages.
/// </value> /// </value>
public List<string> ErrorMessages { get; } = new List<string>(); public List<String> ErrorMessages { get; } = new List<String>();
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
@ -39,7 +34,9 @@ namespace Unosquare.Swan.Abstractions
/// <value> /// <value>
/// The name. /// The name.
/// </value> /// </value>
public string Name { get; } public String Name {
get;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is running. /// Gets a value indicating whether this instance is running.
@ -47,7 +44,9 @@ namespace Unosquare.Swan.Abstractions
/// <value> /// <value>
/// <c>true</c> if this instance is running; otherwise, <c>false</c>. /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsRunning { get; private set; } public Boolean IsRunning {
get; private set;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is enabled. /// Gets a value indicating whether this instance is enabled.
@ -55,68 +54,59 @@ namespace Unosquare.Swan.Abstractions
/// <value> /// <value>
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>. /// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
/// </value> /// </value>
public bool IsEnabled { get; } public Boolean IsEnabled {
get;
}
/// <summary> /// <summary>
/// Starts this instance. /// Starts this instance.
/// </summary> /// </summary>
public virtual void Start() public virtual void Start() {
{ if(this.IsEnabled == false) {
if (IsEnabled == false)
return; return;
}
$"Start Requested".Debug(Name); $"Start Requested".Debug(this.Name);
_cancelTokenSource = new CancellationTokenSource(); this._cancelTokenSource = new CancellationTokenSource();
_workFinished = new ManualResetEvent(false); this._workFinished = new ManualResetEvent(false);
_worker = new Thread(() => this._worker = new Thread(() => {
{ _ = this._workFinished.Reset();
_workFinished.Reset(); this.IsRunning = true;
IsRunning = true; try {
try this.Setup();
{ this.DoBackgroundWork(this._cancelTokenSource.Token);
Setup(); } catch(ThreadAbortException) {
DoBackgroundWork(_cancelTokenSource.Token); $"{nameof(ThreadAbortException)} caught.".Warn(this.Name);
} catch(Exception ex) {
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(this.Name);
} finally {
this.Cleanup();
_ = this._workFinished?.Set();
this.IsRunning = false;
"Stopped Completely".Debug(this.Name);
} }
catch (ThreadAbortException) }) {
{
$"{nameof(ThreadAbortException)} caught.".Warn(Name);
}
catch (Exception ex)
{
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(Name);
}
finally
{
Cleanup();
_workFinished?.Set();
IsRunning = false;
"Stopped Completely".Debug(Name);
}
})
{
IsBackground = true, IsBackground = true,
Name = $"{Name}Thread", Name = $"{this.Name}Thread",
}; };
_worker.Start(); this._worker.Start();
} }
/// <summary> /// <summary>
/// Stops this instance. /// Stops this instance.
/// </summary> /// </summary>
public virtual void Stop() public virtual void Stop() {
{ if(this.IsEnabled == false || this.IsRunning == false) {
if (IsEnabled == false || IsRunning == false)
return; return;
}
$"Stop Requested".Debug(Name); $"Stop Requested".Debug(this.Name);
_cancelTokenSource.Cancel(); this._cancelTokenSource.Cancel();
var waitRetries = 5; Int32 waitRetries = 5;
while (waitRetries >= 1) while(waitRetries >= 1) {
{ if(this._workFinished.WaitOne(250)) {
if (_workFinished.WaitOne(250))
{
waitRetries = -1; waitRetries = -1;
break; break;
} }
@ -124,38 +114,34 @@ namespace Unosquare.Swan.Abstractions
waitRetries--; waitRetries--;
} }
if (waitRetries < 0) if(waitRetries < 0) {
{ "Workbench stopped gracefully".Debug(this.Name);
"Workbench stopped gracefully".Debug(Name); } else {
} "Did not respond to stop request. Aborting thread and waiting . . .".Warn(this.Name);
else this._worker.Abort();
{
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(Name);
_worker.Abort();
if (_workFinished.WaitOne(5000) == false) if(this._workFinished.WaitOne(5000) == false) {
"Waited and no response. Worker might have been left in an inconsistent state.".Error(Name); "Waited and no response. Worker might have been left in an inconsistent state.".Error(this.Name);
else } else {
"Waited for worker and it finally responded (OK).".Debug(Name); "Waited for worker and it finally responded (OK).".Debug(this.Name);
}
} }
_workFinished.Dispose(); this._workFinished.Dispose();
_workFinished = null; this._workFinished = null;
} }
/// <summary> /// <summary>
/// Setups this instance. /// Setups this instance.
/// </summary> /// </summary>
protected virtual void Setup() protected virtual void Setup() {
{
// empty // empty
} }
/// <summary> /// <summary>
/// Cleanups this instance. /// Cleanups this instance.
/// </summary> /// </summary>
protected virtual void Cleanup() protected virtual void Cleanup() {
{
// empty // empty
} }

View File

@ -1,7 +1,5 @@
namespace Unosquare.Swan.Abstractions using Unosquare.Swan.Formatters;
{ using Unosquare.Swan.Reflection;
using Formatters;
using Reflection;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -9,6 +7,7 @@
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Represents a provider to save and load settings using a plain JSON file. /// Represents a provider to save and load settings using a plain JSON file.
/// </summary> /// </summary>
@ -42,9 +41,8 @@
/// </example> /// </example>
/// <typeparam name="T">The type of settings model.</typeparam> /// <typeparam name="T">The type of settings model.</typeparam>
public sealed class SettingsProvider<T> public sealed class SettingsProvider<T>
: SingletonBase<SettingsProvider<T>> : SingletonBase<SettingsProvider<T>> {
{ private readonly Object _syncRoot = new Object();
private readonly object _syncRoot = new object();
private T _global; private T _global;
@ -55,7 +53,9 @@
/// <value> /// <value>
/// The configuration file path. /// The configuration file path.
/// </value> /// </value>
public string ConfigurationFilePath { get; set; } = public String ConfigurationFilePath {
get; set;
} =
#if NETSTANDARD1_3 #if NETSTANDARD1_3
Path.Combine(Runtime.LocalStoragePath, "appsettings.json"); Path.Combine(Runtime.LocalStoragePath, "appsettings.json");
#else #else
@ -68,16 +68,14 @@
/// <value> /// <value>
/// The global settings object. /// The global settings object.
/// </value> /// </value>
public T Global public T Global {
{ get {
get lock(this._syncRoot) {
{ if(Equals(this._global, default(T))) {
lock (_syncRoot) this.ReloadGlobalSettings();
{ }
if (Equals(_global, default(T)))
ReloadGlobalSettings();
return _global; return this._global;
} }
} }
} }
@ -85,22 +83,21 @@
/// <summary> /// <summary>
/// Reloads the global settings. /// Reloads the global settings.
/// </summary> /// </summary>
public void ReloadGlobalSettings() public void ReloadGlobalSettings() {
{ if(File.Exists(this.ConfigurationFilePath) == false || File.ReadAllText(this.ConfigurationFilePath).Length == 0) {
if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0) this.ResetGlobalSettings();
{
ResetGlobalSettings();
return; return;
} }
lock (_syncRoot) lock(this._syncRoot) {
_global = Json.Deserialize<T>(File.ReadAllText(ConfigurationFilePath)); this._global = Json.Deserialize<T>(File.ReadAllText(this.ConfigurationFilePath));
}
} }
/// <summary> /// <summary>
/// Persists the global settings. /// Persists the global settings.
/// </summary> /// </summary>
public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true)); public void PersistGlobalSettings() => File.WriteAllText(this.ConfigurationFilePath, Json.Serialize(this.Global, true));
/// <summary> /// <summary>
/// Updates settings from list. /// Updates settings from list.
@ -110,29 +107,32 @@
/// A list of settings of type ref="ExtendedPropertyInfo". /// A list of settings of type ref="ExtendedPropertyInfo".
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">propertyList.</exception> /// <exception cref="ArgumentNullException">propertyList.</exception>
public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) public List<String> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) {
{ if(propertyList == null) {
if (propertyList == null)
throw new ArgumentNullException(nameof(propertyList)); throw new ArgumentNullException(nameof(propertyList));
}
var changedSettings = new List<string>(); List<String> changedSettings = new List<String>();
var globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(); IEnumerable<PropertyInfo> globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
foreach (var property in propertyList) foreach(ExtendedPropertyInfo<T> property in propertyList) {
{ PropertyInfo propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
var propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
if (propertyInfo == null) continue; if(propertyInfo == null) {
continue;
}
var originalValue = propertyInfo.GetValue(Global); Object originalValue = propertyInfo.GetValue(this.Global);
var isChanged = propertyInfo.PropertyType.IsArray Boolean isChanged = propertyInfo.PropertyType.IsArray
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<object>(), Global) ? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<Object>(), this.Global)
: SetValue(property.Value, originalValue, propertyInfo); : this.SetValue(property.Value, originalValue, propertyInfo);
if (!isChanged) continue; if(!isChanged) {
continue;
}
changedSettings.Add(property.Property); changedSettings.Add(property.Property);
PersistGlobalSettings(); this.PersistGlobalSettings();
} }
return changedSettings; return changedSettings;
@ -142,9 +142,8 @@
/// Gets the list. /// Gets the list.
/// </summary> /// </summary>
/// <returns>A List of ExtendedPropertyInfo of the type T.</returns> /// <returns>A List of ExtendedPropertyInfo of the type T.</returns>
public List<ExtendedPropertyInfo<T>> GetList() public List<ExtendedPropertyInfo<T>> GetList() {
{ Dictionary<String, Object> jsonData = Json.Deserialize(Json.Serialize(this.Global)) as Dictionary<String, Object>;
var jsonData = Json.Deserialize(Json.Serialize(Global)) as Dictionary<string, object>;
return jsonData?.Keys return jsonData?.Keys
.Select(p => new ExtendedPropertyInfo<T>(p) { Value = jsonData[p] }) .Select(p => new ExtendedPropertyInfo<T>(p) { Value = jsonData[p] })
@ -154,28 +153,25 @@
/// <summary> /// <summary>
/// Resets the global settings. /// Resets the global settings.
/// </summary> /// </summary>
public void ResetGlobalSettings() public void ResetGlobalSettings() {
{ lock(this._syncRoot) {
lock (_syncRoot) this._global = Activator.CreateInstance<T>();
_global = Activator.CreateInstance<T>();
PersistGlobalSettings();
} }
private bool SetValue(object property, object originalValue, PropertyInfo propertyInfo) this.PersistGlobalSettings();
{ }
switch (property)
{ private Boolean SetValue(Object property, Object originalValue, PropertyInfo propertyInfo) {
switch(property) {
case null when originalValue == null: case null when originalValue == null:
break; break;
case null: case null:
propertyInfo.SetValue(Global, null); propertyInfo.SetValue(this.Global, null);
return true; return true;
default: default:
if (propertyInfo.PropertyType.TryParseBasicType(property, out var propertyValue) && if(propertyInfo.PropertyType.TryParseBasicType(property, out Object propertyValue) &&
!propertyValue.Equals(originalValue)) !propertyValue.Equals(originalValue)) {
{ propertyInfo.SetValue(this.Global, propertyValue);
propertyInfo.SetValue(Global, propertyValue);
return true; return true;
} }

View File

@ -1,14 +1,12 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Represents a singleton pattern abstract class. /// Represents a singleton pattern abstract class.
/// </summary> /// </summary>
/// <typeparam name="T">The type of class.</typeparam> /// <typeparam name="T">The type of class.</typeparam>
public abstract class SingletonBase<T> : IDisposable public abstract class SingletonBase<T> : IDisposable
where T : class where T : class {
{
/// <summary> /// <summary>
/// The static, singleton instance reference. /// The static, singleton instance reference.
/// </summary> /// </summary>
@ -16,7 +14,7 @@
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T, valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
isThreadSafe: true); isThreadSafe: true);
private bool _isDisposing; // To detect redundant calls private Boolean _isDisposing; // To detect redundant calls
/// <summary> /// <summary>
/// Gets the instance that this singleton represents. /// Gets the instance that this singleton represents.
@ -28,7 +26,7 @@
public static T Instance => LazyInstance.Value; public static T Instance => LazyInstance.Value;
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() => Dispose(true); public void Dispose() => this.Dispose(true);
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
@ -36,22 +34,22 @@
/// a non-default class finalizer (destructor). /// a non-default class finalizer (destructor).
/// </summary> /// </summary>
/// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposeManaged) protected virtual void Dispose(Boolean disposeManaged) {
{ if(this._isDisposing) {
if (_isDisposing) return; return;
}
_isDisposing = true; this._isDisposing = true;
// free managed resources // free managed resources
if (LazyInstance == null) return; if(LazyInstance == null) {
return;
try
{
var disposableInstance = LazyInstance.Value as IDisposable;
disposableInstance?.Dispose();
} }
catch
{ try {
IDisposable disposableInstance = LazyInstance.Value as IDisposable;
disposableInstance?.Dispose();
} catch {
// swallow // swallow
} }
} }

View File

@ -1,22 +1,20 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Unosquare.Swan.Abstractions {
/// <summary> /// <summary>
/// Represents a generic tokenizer. /// Represents a generic tokenizer.
/// </summary> /// </summary>
public abstract class Tokenizer public abstract class Tokenizer {
{ private const Char PeriodChar = '.';
private const char PeriodChar = '.'; private const Char CommaChar = ',';
private const char CommaChar = ','; private const Char StringQuotedChar = '"';
private const char StringQuotedChar = '"'; private const Char OpenFuncChar = '(';
private const char OpenFuncChar = '('; private const Char CloseFuncChar = ')';
private const char CloseFuncChar = ')'; private const Char NegativeChar = '-';
private const char NegativeChar = '-';
private const string OpenFuncStr = "("; private const String OpenFuncStr = "(";
private readonly List<Operator> _operators = new List<Operator>(); private readonly List<Operator> _operators = new List<Operator>();
@ -84,10 +82,9 @@
/// </list> /// </list>
/// </summary> /// </summary>
/// <param name="input">The input.</param> /// <param name="input">The input.</param>
protected Tokenizer(string input) protected Tokenizer(String input) {
{ this._operators.AddRange(this.GetDefaultOperators());
_operators.AddRange(GetDefaultOperators()); this.Tokenize(input);
Tokenize(input);
} }
/// <summary> /// <summary>
@ -95,10 +92,9 @@
/// </summary> /// </summary>
/// <param name="input">The input.</param> /// <param name="input">The input.</param>
/// <param name="operators">The operators to use.</param> /// <param name="operators">The operators to use.</param>
protected Tokenizer(string input, IEnumerable<Operator> operators) protected Tokenizer(String input, IEnumerable<Operator> operators) {
{ this._operators.AddRange(operators);
_operators.AddRange(operators); this.Tokenize(input);
Tokenize(input);
} }
/// <summary> /// <summary>
@ -115,14 +111,14 @@
/// <param name="input">The input.</param> /// <param name="input">The input.</param>
/// <param name="startIndex">The start index.</param> /// <param name="startIndex">The start index.</param>
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns> /// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
public abstract bool ValidateInput(string input, out int startIndex); public abstract Boolean ValidateInput(String input, out Int32 startIndex);
/// <summary> /// <summary>
/// Resolves the type of the function or member. /// Resolves the type of the function or member.
/// </summary> /// </summary>
/// <param name="input">The input.</param> /// <param name="input">The input.</param>
/// <returns>The token type.</returns> /// <returns>The token type.</returns>
public abstract TokenType ResolveFunctionOrMemberType(string input); public abstract TokenType ResolveFunctionOrMemberType(String input);
/// <summary> /// <summary>
/// Evaluates the function or member. /// Evaluates the function or member.
@ -130,7 +126,7 @@
/// <param name="input">The input.</param> /// <param name="input">The input.</param>
/// <param name="position">The position.</param> /// <param name="position">The position.</param>
/// <returns><c>true</c> if the input is a valid function or variable, otherwise <c>false</c>.</returns> /// <returns><c>true</c> if the input is a valid function or variable, otherwise <c>false</c>.</returns>
public virtual bool EvaluateFunctionOrMember(string input, int position) => false; public virtual Boolean EvaluateFunctionOrMember(String input, Int32 position) => false;
/// <summary> /// <summary>
/// Gets the default operators. /// Gets the default operators.
@ -165,14 +161,11 @@
/// or /// or
/// Mismatched parenthesis. /// Mismatched parenthesis.
/// </exception> /// </exception>
public virtual IEnumerable<Token> ShuntingYard(bool includeFunctionStopper = true) public virtual IEnumerable<Token> ShuntingYard(Boolean includeFunctionStopper = true) {
{ Stack<Token> stack = new Stack<Token>();
var stack = new Stack<Token>();
foreach (var tok in Tokens) foreach(Token tok in this.Tokens) {
{ switch(tok.Type) {
switch (tok.Type)
{
case TokenType.Number: case TokenType.Number:
case TokenType.Variable: case TokenType.Variable:
case TokenType.String: case TokenType.String:
@ -183,37 +176,36 @@
break; break;
case TokenType.Operator: case TokenType.Operator:
while(stack.Any() && stack.Peek().Type == TokenType.Operator && while(stack.Any() && stack.Peek().Type == TokenType.Operator &&
CompareOperators(tok.Value, stack.Peek().Value)) this.CompareOperators(tok.Value, stack.Peek().Value)) {
yield return stack.Pop(); yield return stack.Pop();
}
stack.Push(tok); stack.Push(tok);
break; break;
case TokenType.Comma: case TokenType.Comma:
while (stack.Any() && (stack.Peek().Type != TokenType.Comma && while(stack.Any() && stack.Peek().Type != TokenType.Comma &&
stack.Peek().Type != TokenType.Parenthesis)) stack.Peek().Type != TokenType.Parenthesis) {
yield return stack.Pop(); yield return stack.Pop();
}
break; break;
case TokenType.Parenthesis: case TokenType.Parenthesis:
if (tok.Value == OpenFuncStr) if(tok.Value == OpenFuncStr) {
{ if(stack.Any() && stack.Peek().Type == TokenType.Function) {
if (stack.Any() && stack.Peek().Type == TokenType.Function) if(includeFunctionStopper) {
{
if (includeFunctionStopper)
yield return new Token(TokenType.Wall, tok.Value); yield return new Token(TokenType.Wall, tok.Value);
} }
}
stack.Push(tok); stack.Push(tok);
} } else {
else while(stack.Peek().Value != OpenFuncStr) {
{
while (stack.Peek().Value != OpenFuncStr)
yield return stack.Pop(); yield return stack.Pop();
}
stack.Pop(); _ = stack.Pop();
if (stack.Any() && stack.Peek().Type == TokenType.Function) if(stack.Any() && stack.Peek().Type == TokenType.Function) {
{
yield return stack.Pop(); yield return stack.Pop();
} }
} }
@ -224,142 +216,136 @@
} }
} }
while (stack.Any()) while(stack.Any()) {
{ Token tok = stack.Pop();
var tok = stack.Pop(); if(tok.Type == TokenType.Parenthesis) {
if (tok.Type == TokenType.Parenthesis)
throw new InvalidOperationException("Mismatched parenthesis"); throw new InvalidOperationException("Mismatched parenthesis");
}
yield return tok; yield return tok;
} }
} }
private static bool CompareOperators(Operator op1, Operator op2) => op1.RightAssociative private static Boolean CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
? op1.Precedence < op2.Precedence ? op1.Precedence < op2.Precedence
: op1.Precedence <= op2.Precedence; : op1.Precedence <= op2.Precedence;
private void Tokenize(string input) private void Tokenize(String input) {
{ if(!this.ValidateInput(input, out Int32 startIndex)) {
if (!ValidateInput(input, out var startIndex))
{
return; return;
} }
for (var i = startIndex; i < input.Length; i++) for(Int32 i = startIndex; i < input.Length; i++) {
{ if(Char.IsWhiteSpace(input, i)) {
if (char.IsWhiteSpace(input, i)) continue;
if (input[i] == CommaChar)
{
Tokens.Add(new Token(TokenType.Comma, new string(new[] { input[i] })));
continue; continue;
} }
if (input[i] == StringQuotedChar) if(input[i] == CommaChar) {
{ this.Tokens.Add(new Token(TokenType.Comma, new String(new[] { input[i] })));
i = ExtractString(input, i);
continue; continue;
} }
if (char.IsLetter(input, i) || EvaluateFunctionOrMember(input, i)) if(input[i] == StringQuotedChar) {
{ i = this.ExtractString(input, i);
i = ExtractFunctionOrMember(input, i); continue;
}
if(Char.IsLetter(input, i) || this.EvaluateFunctionOrMember(input, i)) {
i = this.ExtractFunctionOrMember(input, i);
continue; continue;
} }
if (char.IsNumber(input, i) || ( if(Char.IsNumber(input, i) ||
input[i] == NegativeChar && input[i] == NegativeChar &&
((Tokens.Any() && Tokens.Last().Type != TokenType.Number) || !Tokens.Any()))) (this.Tokens.Any() && this.Tokens.Last().Type != TokenType.Number || !this.Tokens.Any())) {
{ i = this.ExtractNumber(input, i);
i = ExtractNumber(input, i);
continue; continue;
} }
if(input[i] == OpenFuncChar || if(input[i] == OpenFuncChar ||
input[i] == CloseFuncChar) input[i] == CloseFuncChar) {
{ this.Tokens.Add(new Token(TokenType.Parenthesis, new String(new[] { input[i] })));
Tokens.Add(new Token(TokenType.Parenthesis, new string(new[] { input[i] })));
continue; continue;
} }
i = ExtractOperator(input, i); i = this.ExtractOperator(input, i);
} }
} }
private int ExtractData( private Int32 ExtractData(
string input, String input,
int i, Int32 i,
Func<string, TokenType> tokenTypeEvaluation, Func<String, TokenType> tokenTypeEvaluation,
Func<char, bool> evaluation, Func<Char, Boolean> evaluation,
int right = 0, Int32 right = 0,
int left = -1) Int32 left = -1) {
{ Int32 charCount = 0;
var charCount = 0; for(Int32 j = i + right; j < input.Length; j++) {
for (var j = i + right; j < input.Length; j++) if(evaluation(input[j])) {
{
if (evaluation(input[j]))
break; break;
}
charCount++; charCount++;
} }
// Extract and set the value // Extract and set the value
var value = input.SliceLength(i + right, charCount); String value = input.SliceLength(i + right, charCount);
Tokens.Add(new Token(tokenTypeEvaluation(value), value)); this.Tokens.Add(new Token(tokenTypeEvaluation(value), value));
i += charCount + left; i += charCount + left;
return i; return i;
} }
private int ExtractOperator(string input, int i) => private Int32 ExtractOperator(String input, Int32 i) =>
ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar || this.ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar ||
x == CommaChar || x == CommaChar ||
x == PeriodChar || x == PeriodChar ||
x == StringQuotedChar || x == StringQuotedChar ||
char.IsWhiteSpace(x) || Char.IsWhiteSpace(x) ||
char.IsNumber(x)); Char.IsNumber(x));
private int ExtractFunctionOrMember(string input, int i) => private Int32 ExtractFunctionOrMember(String input, Int32 i) =>
ExtractData(input, i, ResolveFunctionOrMemberType, x => x == OpenFuncChar || this.ExtractData(input, i, this.ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
x == CloseFuncChar || x == CloseFuncChar ||
x == CommaChar || x == CommaChar ||
char.IsWhiteSpace(x)); Char.IsWhiteSpace(x));
private int ExtractNumber(string input, int i) => private Int32 ExtractNumber(String input, Int32 i) =>
ExtractData(input, i, x => TokenType.Number, this.ExtractData(input, i, x => TokenType.Number,
x => !char.IsNumber(x) && x != PeriodChar && x != NegativeChar); x => !Char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
private int ExtractString(string input, int i) private Int32 ExtractString(String input, Int32 i) {
{ Int32 length = this.ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
var length = ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
// open string, report issue // open string, report issue
if (length == input.Length && input[length - 1] != StringQuotedChar) if(length == input.Length && input[length - 1] != StringQuotedChar) {
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'."); throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
}
return length; return length;
} }
private bool CompareOperators(string op1, string op2) private Boolean CompareOperators(String op1, String op2)
=> CompareOperators(GetOperatorOrDefault(op1), GetOperatorOrDefault(op2)); => CompareOperators(this.GetOperatorOrDefault(op1), this.GetOperatorOrDefault(op2));
private Operator GetOperatorOrDefault(string op) private Operator GetOperatorOrDefault(String op)
=> _operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 }; => this._operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
} }
/// <summary> /// <summary>
/// Represents an operator with precedence. /// Represents an operator with precedence.
/// </summary> /// </summary>
public class Operator public class Operator {
{
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
/// </summary> /// </summary>
/// <value> /// <value>
/// The name. /// The name.
/// </value> /// </value>
public string Name { get; set; } public String Name {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the precedence. /// Gets or sets the precedence.
@ -367,7 +353,9 @@
/// <value> /// <value>
/// The precedence. /// The precedence.
/// </value> /// </value>
public int Precedence { get; set; } public Int32 Precedence {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [right associative]. /// Gets or sets a value indicating whether [right associative].
@ -375,23 +363,23 @@
/// <value> /// <value>
/// <c>true</c> if [right associative]; otherwise, <c>false</c>. /// <c>true</c> if [right associative]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool RightAssociative { get; set; } public Boolean RightAssociative {
get; set;
}
} }
/// <summary> /// <summary>
/// Represents a Token structure. /// Represents a Token structure.
/// </summary> /// </summary>
public struct Token public struct Token {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Token"/> struct. /// Initializes a new instance of the <see cref="Token"/> struct.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public Token(TokenType type, string value) public Token(TokenType type, String value) {
{ this.Type = type;
Type = type; this.Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
} }
/// <summary> /// <summary>
@ -400,7 +388,9 @@
/// <value> /// <value>
/// The type. /// The type.
/// </value> /// </value>
public TokenType Type { get; set; } public TokenType Type {
get; set;
}
/// <summary> /// <summary>
/// Gets the value. /// Gets the value.
@ -408,14 +398,15 @@
/// <value> /// <value>
/// The value. /// The value.
/// </value> /// </value>
public string Value { get; } public String Value {
get;
}
} }
/// <summary> /// <summary>
/// Enums the token types. /// Enums the token types.
/// </summary> /// </summary>
public enum TokenType public enum TokenType {
{
/// <summary> /// <summary>
/// The number /// The number
/// </summary> /// </summary>

View File

@ -1,5 +1,4 @@
namespace Unosquare.Swan.Lite.Abstractions using System;
{
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -7,31 +6,27 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.Swan.Lite.Abstractions {
/// <summary> /// <summary>
/// A base class for implementing models that fire notifications when their properties change. /// A base class for implementing models that fire notifications when their properties change.
/// This class is ideal for implementing MVVM driven UIs. /// This class is ideal for implementing MVVM driven UIs.
/// </summary> /// </summary>
/// <seealso cref="INotifyPropertyChanged" /> /// <seealso cref="INotifyPropertyChanged" />
public abstract class ViewModelBase : INotifyPropertyChanged public abstract class ViewModelBase : INotifyPropertyChanged {
{ private readonly ConcurrentDictionary<String, Boolean> QueuedNotifications = new ConcurrentDictionary<String, Boolean>();
private readonly ConcurrentDictionary<string, bool> QueuedNotifications = new ConcurrentDictionary<string, bool>(); private readonly Boolean UseDeferredNotifications;
private readonly bool UseDeferredNotifications;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class. /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary> /// </summary>
/// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param> /// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param>
protected ViewModelBase(bool useDeferredNotifications) protected ViewModelBase(Boolean useDeferredNotifications) => this.UseDeferredNotifications = useDeferredNotifications;
{
UseDeferredNotifications = useDeferredNotifications;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class. /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary> /// </summary>
protected ViewModelBase() protected ViewModelBase()
: this(false) : this(false) {
{
// placeholder // placeholder
} }
@ -52,13 +47,13 @@
/// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param> /// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param>
/// <returns>True if the value was changed, false if the existing value matched the /// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns> /// desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "", string[] notifyAlso = null) protected Boolean SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = "", String[] notifyAlso = null) {
{ if(EqualityComparer<T>.Default.Equals(storage, value)) {
if (EqualityComparer<T>.Default.Equals(storage, value))
return false; return false;
}
storage = value; storage = value;
NotifyPropertyChanged(propertyName, notifyAlso); this.NotifyPropertyChanged(propertyName, notifyAlso);
return true; return true;
} }
@ -66,54 +61,55 @@
/// Notifies one or more properties changed. /// Notifies one or more properties changed.
/// </summary> /// </summary>
/// <param name="propertyNames">The property names.</param> /// <param name="propertyNames">The property names.</param>
protected void NotifyPropertyChanged(params string[] propertyNames) => NotifyPropertyChanged(null, propertyNames); protected void NotifyPropertyChanged(params String[] propertyNames) => this.NotifyPropertyChanged(null, propertyNames);
/// <summary> /// <summary>
/// Notifies one or more properties changed. /// Notifies one or more properties changed.
/// </summary> /// </summary>
/// <param name="mainProperty">The main property.</param> /// <param name="mainProperty">The main property.</param>
/// <param name="auxiliaryProperties">The auxiliary properties.</param> /// <param name="auxiliaryProperties">The auxiliary properties.</param>
private void NotifyPropertyChanged(string mainProperty, string[] auxiliaryProperties) private void NotifyPropertyChanged(String mainProperty, String[] auxiliaryProperties) {
{
// Queue property notification // Queue property notification
if (string.IsNullOrWhiteSpace(mainProperty) == false) if(String.IsNullOrWhiteSpace(mainProperty) == false) {
QueuedNotifications[mainProperty] = true; this.QueuedNotifications[mainProperty] = true;
}
// Set the state for notification properties // Set the state for notification properties
if (auxiliaryProperties != null) if(auxiliaryProperties != null) {
{ foreach(String property in auxiliaryProperties) {
foreach (var property in auxiliaryProperties) if(String.IsNullOrWhiteSpace(property) == false) {
{ this.QueuedNotifications[property] = true;
if (string.IsNullOrWhiteSpace(property) == false) }
QueuedNotifications[property] = true;
} }
} }
// Depending on operation mode, either fire the notifications in the background // Depending on operation mode, either fire the notifications in the background
// or fire them immediately // or fire them immediately
if (UseDeferredNotifications) if(this.UseDeferredNotifications) {
Task.Run(() => NotifyQueuedProperties()); _ = Task.Run(() => this.NotifyQueuedProperties());
else } else {
NotifyQueuedProperties(); this.NotifyQueuedProperties();
}
} }
/// <summary> /// <summary>
/// Notifies the queued properties and resets the property name to a non-queued stated. /// Notifies the queued properties and resets the property name to a non-queued stated.
/// </summary> /// </summary>
private void NotifyQueuedProperties() private void NotifyQueuedProperties() {
{
// get a snapshot of property names. // get a snapshot of property names.
var propertyNames = QueuedNotifications.Keys.ToArray(); String[] propertyNames = this.QueuedNotifications.Keys.ToArray();
// Iterate through the properties // Iterate through the properties
foreach (var property in propertyNames) foreach(String property in propertyNames) {
{
// don't notify if we don't have a change // don't notify if we don't have a change
if (!QueuedNotifications[property]) continue; if(!this.QueuedNotifications[property]) {
continue;
}
// notify and reset queued state to false // notify and reset queued state to false
try { OnPropertyChanged(property); } try {
finally { QueuedNotifications[property] = false; } this.OnPropertyChanged(property);
} finally { this.QueuedNotifications[property] = false; }
} }
} }
@ -121,7 +117,7 @@
/// Called when a property changes its backing value. /// Called when a property changes its backing value.
/// </summary> /// </summary>
/// <param name="propertyName">Name of the property.</param> /// <param name="propertyName">Name of the property.</param>
private void OnPropertyChanged(string propertyName) => private void OnPropertyChanged(String propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? string.Empty)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? String.Empty));
} }
} }

View File

@ -1,26 +1,24 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using Abstractions;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values. /// Fast, atomic boolean combining interlocked to write value and volatile to read values.
/// </summary> /// </summary>
public sealed class AtomicBoolean : AtomicTypeBase<bool> public sealed class AtomicBoolean : AtomicTypeBase<Boolean> {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class. /// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
/// </summary> /// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicBoolean(bool initialValue = default) public AtomicBoolean(Boolean initialValue = default)
: base(initialValue ? 1 : 0) : base(initialValue ? 1 : 0) {
{
// placeholder // placeholder
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override bool FromLong(long backingValue) => backingValue != 0; protected override Boolean FromLong(Int64 backingValue) => backingValue != 0;
/// <inheritdoc/> /// <inheritdoc/>
protected override long ToLong(bool value) => value ? 1 : 0; protected override Int64 ToLong(Boolean value) => value ? 1 : 0;
} }
} }

View File

@ -1,29 +1,26 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using System;
using Abstractions;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Fast, atomic double combining interlocked to write value and volatile to read values. /// Fast, atomic double combining interlocked to write value and volatile to read values.
/// </summary> /// </summary>
public sealed class AtomicDouble : AtomicTypeBase<double> public sealed class AtomicDouble : AtomicTypeBase<Double> {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AtomicDouble"/> class. /// Initializes a new instance of the <see cref="AtomicDouble"/> class.
/// </summary> /// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicDouble(double initialValue = default) public AtomicDouble(Double initialValue = default)
: base(BitConverter.DoubleToInt64Bits(initialValue)) : base(BitConverter.DoubleToInt64Bits(initialValue)) {
{
// placeholder // placeholder
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override double FromLong(long backingValue) => protected override Double FromLong(Int64 backingValue) =>
BitConverter.Int64BitsToDouble(backingValue); BitConverter.Int64BitsToDouble(backingValue);
/// <inheritdoc/> /// <inheritdoc/>
protected override long ToLong(double value) => protected override Int64 ToLong(Double value) =>
BitConverter.DoubleToInt64Bits(value); BitConverter.DoubleToInt64Bits(value);
} }
} }

View File

@ -1,29 +1,26 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using System;
using Abstractions;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Represents an atomically readable or writable integer. /// Represents an atomically readable or writable integer.
/// </summary> /// </summary>
public class AtomicInteger : AtomicTypeBase<int> public class AtomicInteger : AtomicTypeBase<Int32> {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AtomicInteger"/> class. /// Initializes a new instance of the <see cref="AtomicInteger"/> class.
/// </summary> /// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicInteger(int initialValue = default) public AtomicInteger(Int32 initialValue = default)
: base(Convert.ToInt64(initialValue)) : base(Convert.ToInt64(initialValue)) {
{
// placeholder // placeholder
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override int FromLong(long backingValue) => protected override Int32 FromLong(Int64 backingValue) =>
Convert.ToInt32(backingValue); Convert.ToInt32(backingValue);
/// <inheritdoc/> /// <inheritdoc/>
protected override long ToLong(int value) => protected override Int64 ToLong(Int32 value) =>
Convert.ToInt64(value); Convert.ToInt64(value);
} }
} }

View File

@ -1,26 +1,24 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using Abstractions;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Fast, atomioc long combining interlocked to write value and volatile to read values. /// Fast, atomioc long combining interlocked to write value and volatile to read values.
/// </summary> /// </summary>
public sealed class AtomicLong : AtomicTypeBase<long> public sealed class AtomicLong : AtomicTypeBase<Int64> {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AtomicLong"/> class. /// Initializes a new instance of the <see cref="AtomicLong"/> class.
/// </summary> /// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicLong(long initialValue = default) public AtomicLong(Int64 initialValue = default)
: base(initialValue) : base(initialValue) {
{
// placeholder // placeholder
} }
/// <inheritdoc /> /// <inheritdoc />
protected override long FromLong(long backingValue) => backingValue; protected override Int64 FromLong(Int64 backingValue) => backingValue;
/// <inheritdoc /> /// <inheritdoc />
protected override long ToLong(long value) => value; protected override Int64 ToLong(Int64 value) => value;
} }
} }

View File

@ -1,31 +1,27 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// Models an option specification. /// Models an option specification.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public sealed class ArgumentOptionAttribute public sealed class ArgumentOptionAttribute
: Attribute : Attribute {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class. /// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// The default long name will be inferred from target property. /// The default long name will be inferred from target property.
/// </summary> /// </summary>
public ArgumentOptionAttribute() public ArgumentOptionAttribute()
: this(string.Empty, string.Empty) : this(String.Empty, String.Empty) {
{
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class. /// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// </summary> /// </summary>
/// <param name="longName">The long name of the option.</param> /// <param name="longName">The long name of the option.</param>
public ArgumentOptionAttribute(string longName) public ArgumentOptionAttribute(String longName)
: this(string.Empty, longName) : this(String.Empty, longName) {
{
} }
/// <summary> /// <summary>
@ -33,24 +29,21 @@
/// </summary> /// </summary>
/// <param name="shortName">The short name of the option.</param> /// <param name="shortName">The short name of the option.</param>
/// <param name="longName">The long name of the option or null if not used.</param> /// <param name="longName">The long name of the option or null if not used.</param>
public ArgumentOptionAttribute(char shortName, string longName) public ArgumentOptionAttribute(Char shortName, String longName)
: this(new string(shortName, 1), longName) : this(new String(shortName, 1), longName) {
{
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class. /// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// </summary> /// </summary>
/// <param name="shortName">The short name of the option..</param> /// <param name="shortName">The short name of the option..</param>
public ArgumentOptionAttribute(char shortName) public ArgumentOptionAttribute(Char shortName)
: this(new string(shortName, 1), string.Empty) : this(new String(shortName, 1), String.Empty) {
{
} }
private ArgumentOptionAttribute(string shortName, string longName) private ArgumentOptionAttribute(String shortName, String longName) {
{ this.ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName)); this.LongName = longName ?? throw new ArgumentNullException(nameof(longName));
LongName = longName ?? throw new ArgumentNullException(nameof(longName));
} }
/// <summary> /// <summary>
@ -59,7 +52,9 @@
/// <value> /// <value>
/// The long name. /// The long name.
/// </value> /// </value>
public string LongName { get; } public String LongName {
get;
}
/// <summary> /// <summary>
/// Gets a short name of this command line option, made of one character. /// Gets a short name of this command line option, made of one character.
@ -67,13 +62,15 @@
/// <value> /// <value>
/// The short name. /// The short name.
/// </value> /// </value>
public string ShortName { get; } public String ShortName {
get;
}
/// <summary> /// <summary>
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties, /// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
/// it allows you to split an argument and consume its content as a sequence. /// it allows you to split an argument and consume its content as a sequence.
/// </summary> /// </summary>
public char Separator { get; set; } = '\0'; public Char Separator { get; set; } = '\0';
/// <summary> /// <summary>
/// Gets or sets mapped property default value. /// Gets or sets mapped property default value.
@ -81,7 +78,9 @@
/// <value> /// <value>
/// The default value. /// The default value.
/// </value> /// </value>
public object DefaultValue { get; set; } public Object DefaultValue {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a command line option is required. /// Gets or sets a value indicating whether a command line option is required.
@ -89,7 +88,9 @@
/// <value> /// <value>
/// <c>true</c> if required; otherwise, <c>false</c>. /// <c>true</c> if required; otherwise, <c>false</c>.
/// </value> /// </value>
public bool Required { get; set; } public Boolean Required {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets a short description of this command line option. Usually a sentence summary. /// Gets or sets a short description of this command line option. Usually a sentence summary.
@ -97,6 +98,8 @@
/// <value> /// <value>
/// The help text. /// The help text.
/// </value> /// </value>
public string HelpText { get; set; } public String HelpText {
get; set;
}
} }
} }

View File

@ -1,13 +1,11 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// Represents an attribute to select which properties are copyable between objects. /// Represents an attribute to select which properties are copyable between objects.
/// </summary> /// </summary>
/// <seealso cref="Attribute" /> /// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class CopyableAttribute : Attribute public class CopyableAttribute : Attribute {
{
} }
} }

View File

@ -1,23 +1,20 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// An attribute used to help setup a property behavior when serialize/deserialize JSON. /// An attribute used to help setup a property behavior when serialize/deserialize JSON.
/// </summary> /// </summary>
/// <seealso cref="Attribute" /> /// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public sealed class JsonPropertyAttribute : Attribute public sealed class JsonPropertyAttribute : Attribute {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class. /// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
/// </summary> /// </summary>
/// <param name="propertyName">Name of the property.</param> /// <param name="propertyName">Name of the property.</param>
/// <param name="ignored">if set to <c>true</c> [ignored].</param> /// <param name="ignored">if set to <c>true</c> [ignored].</param>
public JsonPropertyAttribute(string propertyName, bool ignored = false) public JsonPropertyAttribute(String propertyName, Boolean ignored = false) {
{ this.PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); this.Ignored = ignored;
Ignored = ignored;
} }
/// <summary> /// <summary>
@ -26,7 +23,9 @@
/// <value> /// <value>
/// The name of the property. /// The name of the property.
/// </value> /// </value>
public string PropertyName { get; } public String PropertyName {
get;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored. /// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
@ -34,6 +33,8 @@
/// <value> /// <value>
/// <c>true</c> if ignored; otherwise, <c>false</c>. /// <c>true</c> if ignored; otherwise, <c>false</c>.
/// </value> /// </value>
public bool Ignored { get; } public Boolean Ignored {
get;
}
} }
} }

View File

@ -1,7 +1,6 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// An attribute used to include additional information to a Property for serialization. /// An attribute used to include additional information to a Property for serialization.
/// ///
@ -9,15 +8,16 @@
/// </summary> /// </summary>
/// <seealso cref="System.Attribute" /> /// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public sealed class PropertyDisplayAttribute : Attribute public sealed class PropertyDisplayAttribute : Attribute {
{
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
/// </summary> /// </summary>
/// <value> /// <value>
/// The name. /// The name.
/// </value> /// </value>
public string Name { get; set; } public String Name {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the description. /// Gets or sets the description.
@ -25,7 +25,9 @@
/// <value> /// <value>
/// The description. /// The description.
/// </value> /// </value>
public string Description { get; set; } public String Description {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the name of the group. /// Gets or sets the name of the group.
@ -33,7 +35,9 @@
/// <value> /// <value>
/// The name of the group. /// The name of the group.
/// </value> /// </value>
public string GroupName { get; set; } public String GroupName {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the default value. /// Gets or sets the default value.
@ -41,7 +45,9 @@
/// <value> /// <value>
/// The default value. /// The default value.
/// </value> /// </value>
public object DefaultValue { get; set; } public Object DefaultValue {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the format string to call with method <c>ToString</c>. /// Gets or sets the format string to call with method <c>ToString</c>.
@ -49,6 +55,8 @@
/// <value> /// <value>
/// The format. /// The format.
/// </value> /// </value>
public string Format { get; set; } public String Format {
get; set;
}
} }
} }

View File

@ -1,23 +1,18 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// An attribute used to help conversion structs back and forth into arrays of bytes via /// An attribute used to help conversion structs back and forth into arrays of bytes via
/// extension methods included in this library ToStruct and ToBytes. /// extension methods included in this library ToStruct and ToBytes.
/// </summary> /// </summary>
/// <seealso cref="Attribute" /> /// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)]
public class StructEndiannessAttribute : Attribute public class StructEndiannessAttribute : Attribute {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class. /// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
/// </summary> /// </summary>
/// <param name="endianness">The endianness.</param> /// <param name="endianness">The endianness.</param>
public StructEndiannessAttribute(Endianness endianness) public StructEndiannessAttribute(Endianness endianness) => this.Endianness = endianness;
{
Endianness = endianness;
}
/// <summary> /// <summary>
/// Gets the endianness. /// Gets the endianness.
@ -25,6 +20,8 @@
/// <value> /// <value>
/// The endianness. /// The endianness.
/// </value> /// </value>
public Endianness Endianness { get; } public Endianness Endianness {
get;
}
} }
} }

View File

@ -1,54 +1,50 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// Regex validator. /// Regex validator.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class MatchAttribute : Attribute, IValidator public class MatchAttribute : Attribute, IValidator {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MatchAttribute" /> class. /// Initializes a new instance of the <see cref="MatchAttribute" /> class.
/// </summary> /// </summary>
/// <param name="regex">A regex string.</param> /// <param name="regex">A regex string.</param>
/// <param name="errorMessage">The error message.</param> /// <param name="errorMessage">The error message.</param>
/// <exception cref="ArgumentNullException">Expression.</exception> /// <exception cref="ArgumentNullException">Expression.</exception>
public MatchAttribute(string regex, string errorMessage = null) public MatchAttribute(String regex, String errorMessage = null) {
{ this.Expression = regex ?? throw new ArgumentNullException(nameof(this.Expression));
Expression = regex ?? throw new ArgumentNullException(nameof(Expression)); this.ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
} }
/// <summary> /// <summary>
/// The string regex used to find a match. /// The string regex used to find a match.
/// </summary> /// </summary>
public string Expression { get; } public String Expression {
get;
/// <inheritdoc/>
public string ErrorMessage { get; internal set; }
/// <inheritdoc/>
public bool IsValid<T>(T value)
{
if (Equals(value, default(T)))
return false;
return !(value is string)
? throw new ArgumentException("Property is not a string")
: Regex.IsMatch(value.ToString(), Expression);
} }
/// <inheritdoc/>
public String ErrorMessage {
get; internal set;
}
/// <inheritdoc/>
public Boolean IsValid<T>(T value) => Equals(value, default(T))
? false
: !(value is String)
? throw new ArgumentException("Property is not a string")
: Regex.IsMatch(value.ToString(), this.Expression);
} }
/// <summary> /// <summary>
/// Email validator. /// Email validator.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class EmailAttribute : MatchAttribute public class EmailAttribute : MatchAttribute {
{ private const String EmailRegExp =
private const string EmailRegExp =
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" + @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$"; @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
@ -56,9 +52,8 @@
/// Initializes a new instance of the <see cref="EmailAttribute"/> class. /// Initializes a new instance of the <see cref="EmailAttribute"/> class.
/// </summary> /// </summary>
/// <param name="errorMessage">The error message.</param> /// <param name="errorMessage">The error message.</param>
public EmailAttribute(string errorMessage = null) public EmailAttribute(String errorMessage = null)
: base(EmailRegExp, errorMessage ?? "String is not an email") : base(EmailRegExp, errorMessage ?? "String is not an email") {
{
} }
} }
@ -66,34 +61,32 @@
/// A not null validator. /// A not null validator.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class NotNullAttribute : Attribute, IValidator public class NotNullAttribute : Attribute, IValidator {
{
/// <inheritdoc/> /// <inheritdoc/>
public string ErrorMessage => "Value is null"; public String ErrorMessage => "Value is null";
/// <inheritdoc/> /// <inheritdoc/>
public bool IsValid<T>(T value) => !Equals(default(T), value); public Boolean IsValid<T>(T value) => !Equals(default(T), value);
} }
/// <summary> /// <summary>
/// A range constraint validator. /// A range constraint validator.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : Attribute, IValidator public class RangeAttribute : Attribute, IValidator {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RangeAttribute"/> class. /// Initializes a new instance of the <see cref="RangeAttribute"/> class.
/// Constructor that takes integer minimum and maximum values. /// Constructor that takes integer minimum and maximum values.
/// </summary> /// </summary>
/// <param name="min">The minimum value.</param> /// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param> /// <param name="max">The maximum value.</param>
public RangeAttribute(int min, int max) public RangeAttribute(Int32 min, Int32 max) {
{ if(min >= max) {
if (min >= max)
throw new InvalidOperationException("Maximum value must be greater than minimum"); throw new InvalidOperationException("Maximum value must be greater than minimum");
}
Maximum = max; this.Maximum = max;
Minimum = min; this.Minimum = min;
} }
/// <summary> /// <summary>
@ -102,32 +95,36 @@
/// </summary> /// </summary>
/// <param name="min">The minimum value.</param> /// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param> /// <param name="max">The maximum value.</param>
public RangeAttribute(double min, double max) public RangeAttribute(Double min, Double max) {
{ if(min >= max) {
if (min >= max)
throw new InvalidOperationException("Maximum value must be greater than minimum"); throw new InvalidOperationException("Maximum value must be greater than minimum");
}
Maximum = max; this.Maximum = max;
Minimum = min; this.Minimum = min;
} }
/// <inheritdoc/> /// <inheritdoc/>
public string ErrorMessage => "Value is not within the specified range"; public String ErrorMessage => "Value is not within the specified range";
/// <summary> /// <summary>
/// Maximum value for the range. /// Maximum value for the range.
/// </summary> /// </summary>
public IComparable Maximum { get; } public IComparable Maximum {
get;
}
/// <summary> /// <summary>
/// Minimum value for the range. /// Minimum value for the range.
/// </summary> /// </summary>
public IComparable Minimum { get; } public IComparable Minimum {
get;
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsValid<T>(T value) public Boolean IsValid<T>(T value)
=> value is IComparable comparable => value is IComparable comparable
? comparable.CompareTo(Minimum) >= 0 && comparable.CompareTo(Maximum) <= 0 ? comparable.CompareTo(this.Minimum) >= 0 && comparable.CompareTo(this.Maximum) <= 0
: throw new ArgumentException(nameof(value)); : throw new ArgumentException(nameof(value));
} }
} }

View File

@ -1,22 +1,17 @@
namespace Unosquare.Swan.Attributes using System;
{
using System;
namespace Unosquare.Swan.Attributes {
/// <summary> /// <summary>
/// Models a verb option. /// Models a verb option.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public sealed class VerbOptionAttribute : Attribute public sealed class VerbOptionAttribute : Attribute {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class. /// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <exception cref="ArgumentNullException">name.</exception> /// <exception cref="ArgumentNullException">name.</exception>
public VerbOptionAttribute(string name) public VerbOptionAttribute(String name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
/// <summary> /// <summary>
/// Gets the name of the verb option. /// Gets the name of the verb option.
@ -24,7 +19,9 @@
/// <value> /// <value>
/// Name. /// Name.
/// </value> /// </value>
public string Name { get; } public String Name {
get;
}
/// <summary> /// <summary>
/// Gets or sets a short description of this command line verb. Usually a sentence summary. /// Gets or sets a short description of this command line verb. Usually a sentence summary.
@ -32,9 +29,11 @@
/// <value> /// <value>
/// The help text. /// The help text.
/// </value> /// </value>
public string HelpText { get; set; } public String HelpText {
get; set;
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() => $" {Name}\t\t{HelpText}"; public override String ToString() => $" {this.Name}\t\t{this.HelpText}";
} }
} }

View File

@ -1,21 +1,18 @@
namespace Unosquare.Swan.Components using System.Linq;
{
using System.Linq;
using System.Reflection; using System.Reflection;
using Attributes; using Unosquare.Swan.Attributes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides methods to parse command line arguments. /// Provides methods to parse command line arguments.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary> /// </summary>
public partial class ArgumentParser public partial class ArgumentParser {
{ private sealed class Validator {
private sealed class Validator private readonly Object _instance;
{ private readonly IEnumerable<String> _args;
private readonly object _instance;
private readonly IEnumerable<string> _args;
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>(); private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
private readonly ArgumentParserSettings _settings; private readonly ArgumentParserSettings _settings;
@ -23,122 +20,112 @@
public Validator( public Validator(
PropertyInfo[] properties, PropertyInfo[] properties,
IEnumerable<string> args, IEnumerable<String> args,
object instance, Object instance,
ArgumentParserSettings settings) ArgumentParserSettings settings) {
{ this._args = args;
_args = args; this._instance = instance;
_instance = instance; this._settings = settings;
_settings = settings; this._properties = properties;
_properties = properties;
PopulateInstance(); this.PopulateInstance();
SetDefaultValues(); this.SetDefaultValues();
GetRequiredList(); this.GetRequiredList();
} }
public List<string> UnknownList { get; } = new List<string>(); public List<String> UnknownList { get; } = new List<String>();
public List<string> RequiredList { get; } = new List<string>(); public List<String> RequiredList { get; } = new List<String>();
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any(); public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any();
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions() public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
=> _properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)) => this._properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
.Where(x => x != null); .Where(x => x != null);
private void GetRequiredList() private void GetRequiredList() {
{ foreach(PropertyInfo targetProperty in this._properties) {
foreach (var targetProperty in _properties) ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
if (optionAttr == null || optionAttr.Required == false) if(optionAttr == null || optionAttr.Required == false) {
continue; continue;
}
if (targetProperty.GetValue(_instance) == null) if(targetProperty.GetValue(this._instance) == null) {
{ this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
} }
} }
} }
private void SetDefaultValues() private void SetDefaultValues() {
{ foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) {
foreach (var targetProperty in _properties.Except(_updatedList)) ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
var defaultValue = optionAttr?.DefaultValue; Object defaultValue = optionAttr?.DefaultValue;
if (defaultValue == null) if(defaultValue == null) {
continue; continue;
}
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr)) if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) {
_updatedList.Add(targetProperty); this._updatedList.Add(targetProperty);
}
} }
} }
private void PopulateInstance() private void PopulateInstance() {
{ const Char dash = '-';
const char dash = '-'; String propertyName = String.Empty;
var propertyName = string.Empty;
foreach (var arg in _args) foreach(String arg in this._args) {
{ Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName);
var ignoreSetValue = string.IsNullOrWhiteSpace(propertyName);
if (ignoreSetValue) if(ignoreSetValue) {
{ if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) {
if (string.IsNullOrWhiteSpace(arg) || arg[0] != dash) continue; continue;
}
propertyName = arg.Substring(1); propertyName = arg.Substring(1);
if (!string.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) {
propertyName = propertyName.Substring(1); propertyName = propertyName.Substring(1);
} }
}
var targetProperty = TryGetProperty(propertyName); PropertyInfo targetProperty = this.TryGetProperty(propertyName);
if (targetProperty == null) if(targetProperty == null) {
{
// Skip if the property is not found // Skip if the property is not found
UnknownList.Add(propertyName); this.UnknownList.Add(propertyName);
continue; continue;
} }
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance)) if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) {
{ this._updatedList.Add(targetProperty);
_updatedList.Add(targetProperty); propertyName = String.Empty;
propertyName = string.Empty; } else if(targetProperty.PropertyType == typeof(Boolean)) {
}
else if (targetProperty.PropertyType == typeof(bool))
{
// If the arg is a boolean property set it to true. // If the arg is a boolean property set it to true.
targetProperty.SetValue(_instance, true); targetProperty.SetValue(this._instance, true);
_updatedList.Add(targetProperty); this._updatedList.Add(targetProperty);
propertyName = string.Empty; propertyName = String.Empty;
} }
} }
if (!string.IsNullOrEmpty(propertyName)) if(!String.IsNullOrEmpty(propertyName)) {
{ this.UnknownList.Add(propertyName);
UnknownList.Add(propertyName);
} }
} }
private bool SetPropertyValue( private Boolean SetPropertyValue(
PropertyInfo targetProperty, PropertyInfo targetProperty,
string propertyValueString, String propertyValueString,
object result, Object result,
ArgumentOptionAttribute optionAttr = null) ArgumentOptionAttribute optionAttr = null) {
{ if(targetProperty.PropertyType.GetTypeInfo().IsEnum) {
if (targetProperty.PropertyType.GetTypeInfo().IsEnum) Object parsedValue = Enum.Parse(
{
var parsedValue = Enum.Parse(
targetProperty.PropertyType, targetProperty.PropertyType,
propertyValueString, propertyValueString,
_settings.CaseInsensitiveEnumValues); this._settings.CaseInsensitiveEnumValues);
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue)); targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
@ -150,10 +137,10 @@
: targetProperty.TrySetBasicType(propertyValueString, result); : targetProperty.TrySetBasicType(propertyValueString, result);
} }
private PropertyInfo TryGetProperty(string propertyName) private PropertyInfo TryGetProperty(String propertyName)
=> _properties.FirstOrDefault(p => => this._properties.FirstOrDefault(p =>
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, _settings.NameComparer) || String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, this._settings.NameComparer) ||
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, _settings.NameComparer)); String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, this._settings.NameComparer));
} }
} }
} }

View File

@ -1,53 +1,48 @@
namespace Unosquare.Swan.Components using System.Linq;
{
using System.Linq;
using System.Reflection; using System.Reflection;
using Attributes; using Unosquare.Swan.Attributes;
using System; using System;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides methods to parse command line arguments. /// Provides methods to parse command line arguments.
/// </summary> /// </summary>
public partial class ArgumentParser public partial class ArgumentParser {
{ private sealed class TypeResolver<T> {
private sealed class TypeResolver<T> private readonly String _selectedVerb;
{
private readonly string _selectedVerb;
private PropertyInfo[] _properties; private PropertyInfo[] _properties;
public TypeResolver(string selectedVerb) public TypeResolver(String selectedVerb) => this._selectedVerb = selectedVerb;
{
_selectedVerb = selectedVerb; public PropertyInfo[] GetProperties() => this._properties?.Any() == true ? this._properties : null;
public Object GetOptionsObject(T instance) {
this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
if(!this._properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any())) {
return instance;
} }
public PropertyInfo[] GetProperties() => _properties?.Any() == true ? _properties : null; PropertyInfo selectedVerb = String.IsNullOrWhiteSpace(this._selectedVerb)
public object GetOptionsObject(T instance)
{
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
if (!_properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any()))
return instance;
var selectedVerb = string.IsNullOrWhiteSpace(_selectedVerb)
? null ? null
: _properties.FirstOrDefault(x => : this._properties.FirstOrDefault(x =>
Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(_selectedVerb)); Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(this._selectedVerb));
if (selectedVerb == null) return null; if(selectedVerb == null) {
return null;
}
var type = instance.GetType(); Type type = instance.GetType();
var verbProperty = type.GetProperty(selectedVerb.Name); PropertyInfo verbProperty = type.GetProperty(selectedVerb.Name);
if (verbProperty?.GetValue(instance) == null) if(verbProperty?.GetValue(instance) == null) {
{ Object propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
var propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
verbProperty?.SetValue(instance, propertyInstance); verbProperty?.SetValue(instance, propertyInstance);
} }
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true) this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
.ToArray(); .ToArray();
return verbProperty?.GetValue(instance); return verbProperty?.GetValue(instance);

View File

@ -1,10 +1,9 @@
namespace Unosquare.Swan.Components
{
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Attributes; using Unosquare.Swan.Attributes;
using System.Linq; using System.Linq;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides methods to parse command line arguments. /// Provides methods to parse command line arguments.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
@ -106,14 +105,12 @@ namespace Unosquare.Swan.Components
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public partial class ArgumentParser public partial class ArgumentParser {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentParser"/> class. /// Initializes a new instance of the <see cref="ArgumentParser"/> class.
/// </summary> /// </summary>
public ArgumentParser() public ArgumentParser()
: this(new ArgumentParserSettings()) : this(new ArgumentParserSettings()) {
{
} }
/// <summary> /// <summary>
@ -121,10 +118,7 @@ namespace Unosquare.Swan.Components
/// configurable with <see cref="ArgumentParserSettings" /> using a delegate. /// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
/// </summary> /// </summary>
/// <param name="parseSettings">The parse settings.</param> /// <param name="parseSettings">The parse settings.</param>
public ArgumentParser(ArgumentParserSettings parseSettings) public ArgumentParser(ArgumentParserSettings parseSettings) => this.Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
{
Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
}
/// <summary> /// <summary>
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use. /// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
@ -132,7 +126,9 @@ namespace Unosquare.Swan.Components
/// <value> /// <value>
/// The settings. /// The settings.
/// </value> /// </value>
public ArgumentParserSettings Settings { get; } public ArgumentParserSettings Settings {
get;
}
/// <summary> /// <summary>
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />. /// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
@ -150,39 +146,40 @@ namespace Unosquare.Swan.Components
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The exception that is thrown when a method call is invalid for the object's current state. /// The exception that is thrown when a method call is invalid for the object's current state.
/// </exception> /// </exception>
public bool ParseArguments<T>(IEnumerable<string> args, T instance) public Boolean ParseArguments<T>(IEnumerable<String> args, T instance) {
{ if(args == null) {
if (args == null)
throw new ArgumentNullException(nameof(args)); throw new ArgumentNullException(nameof(args));
}
if (Equals(instance, default(T))) if(Equals(instance, default(T))) {
throw new ArgumentNullException(nameof(instance)); throw new ArgumentNullException(nameof(instance));
}
var typeResolver = new TypeResolver<T>(args.FirstOrDefault()); TypeResolver<T> typeResolver = new TypeResolver<T>(args.FirstOrDefault());
var options = typeResolver.GetOptionsObject(instance); Object options = typeResolver.GetOptionsObject(instance);
if (options == null) if(options == null) {
{
ReportUnknownVerb<T>(); ReportUnknownVerb<T>();
return false; return false;
} }
var properties = typeResolver.GetProperties(); System.Reflection.PropertyInfo[] properties = typeResolver.GetProperties();
if (properties == null) if(properties == null) {
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid"); throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
}
var validator = new Validator(properties, args, options, Settings); Validator validator = new Validator(properties, args, options, this.Settings);
if (validator.IsValid()) if(validator.IsValid()) {
return true; return true;
}
ReportIssues(validator); this.ReportIssues(validator);
return false; return false;
} }
private static void ReportUnknownVerb<T>() private static void ReportUnknownVerb<T>() {
{
"No verb was specified".WriteLine(ConsoleColor.Red); "No verb was specified".WriteLine(ConsoleColor.Red);
"Valid verbs:".WriteLine(ConsoleColor.Cyan); "Valid verbs:".WriteLine(ConsoleColor.Cyan);
@ -193,38 +190,39 @@ namespace Unosquare.Swan.Components
.ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan)); .ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
} }
private void ReportIssues(Validator validator) private void ReportIssues(Validator validator) {
{
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
if (Settings.WriteBanner) if(this.Settings.WriteBanner) {
Runtime.WriteWelcomeBanner(); Runtime.WriteWelcomeBanner();
}
#endif #endif
var options = validator.GetPropertiesOptions(); IEnumerable<ArgumentOptionAttribute> options = validator.GetPropertiesOptions();
foreach (var option in options) foreach(ArgumentOptionAttribute option in options) {
{ String.Empty.WriteLine();
string.Empty.WriteLine();
// TODO: If Enum list values // TODO: If Enum list values
var shortName = string.IsNullOrWhiteSpace(option.ShortName) ? string.Empty : $"-{option.ShortName}"; String shortName = String.IsNullOrWhiteSpace(option.ShortName) ? String.Empty : $"-{option.ShortName}";
var longName = string.IsNullOrWhiteSpace(option.LongName) ? string.Empty : $"--{option.LongName}"; String longName = String.IsNullOrWhiteSpace(option.LongName) ? String.Empty : $"--{option.LongName}";
var comma = string.IsNullOrWhiteSpace(shortName) || string.IsNullOrWhiteSpace(longName) String comma = String.IsNullOrWhiteSpace(shortName) || String.IsNullOrWhiteSpace(longName)
? string.Empty ? String.Empty
: ", "; : ", ";
var defaultValue = option.DefaultValue == null ? string.Empty : $"(Default: {option.DefaultValue}) "; String defaultValue = option.DefaultValue == null ? String.Empty : $"(Default: {option.DefaultValue}) ";
$" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}".WriteLine(ConsoleColor.Cyan); $" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}".WriteLine(ConsoleColor.Cyan);
} }
string.Empty.WriteLine(); String.Empty.WriteLine();
" --help\t\tDisplay this help screen.".WriteLine(ConsoleColor.Cyan); " --help\t\tDisplay this help screen.".WriteLine(ConsoleColor.Cyan);
if (validator.UnknownList.Any()) if(validator.UnknownList.Any()) {
$"Unknown arguments: {string.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red); $"Unknown arguments: {String.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red);
}
if (validator.RequiredList.Any()) if(validator.RequiredList.Any()) {
$"Required arguments: {string.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red); $"Required arguments: {String.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red);
}
} }
} }
} }

View File

@ -1,20 +1,18 @@
namespace Unosquare.Swan.Components using System;
{
using System;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides settings for <see cref="ArgumentParser"/>. /// Provides settings for <see cref="ArgumentParser"/>.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary> /// </summary>
public class ArgumentParserSettings public class ArgumentParserSettings {
{
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [write banner]. /// Gets or sets a value indicating whether [write banner].
/// </summary> /// </summary>
/// <value> /// <value>
/// <c>true</c> if [write banner]; otherwise, <c>false</c>. /// <c>true</c> if [write banner]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool WriteBanner { get; set; } = true; public Boolean WriteBanner { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether perform case sensitive comparisons. /// Gets or sets a value indicating whether perform case sensitive comparisons.
@ -24,7 +22,7 @@
/// <value> /// <value>
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>. /// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool CaseSensitive { get; set; } = false; public Boolean CaseSensitive { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>. /// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
@ -33,7 +31,7 @@
/// <value> /// <value>
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>. /// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool CaseInsensitiveEnumValues { get; set; } = true; public Boolean CaseInsensitiveEnumValues { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it /// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
@ -46,8 +44,8 @@
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but /// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
/// when these are unknown by the main program at build time. /// when these are unknown by the main program at build time.
/// </remarks> /// </remarks>
public bool IgnoreUnknownArguments { get; set; } = true; public Boolean IgnoreUnknownArguments { get; set; } = true;
internal StringComparison NameComparer => CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; internal StringComparison NameComparer => this.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
} }
} }

View File

@ -1,11 +1,10 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// A simple benchmarking class. /// A simple benchmarking class.
/// </summary> /// </summary>
@ -33,31 +32,27 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static class Benchmark public static class Benchmark {
{ private static readonly Object SyncLock = new Object();
private static readonly object SyncLock = new object(); private static readonly Dictionary<String, List<TimeSpan>> Measures = new Dictionary<String, List<TimeSpan>>();
private static readonly Dictionary<string, List<TimeSpan>> Measures = new Dictionary<string, List<TimeSpan>>();
/// <summary> /// <summary>
/// Starts measuring with the given identifier. /// Starts measuring with the given identifier.
/// </summary> /// </summary>
/// <param name="identifier">The identifier.</param> /// <param name="identifier">The identifier.</param>
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns> /// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
public static IDisposable Start(string identifier) => new BenchmarkUnit(identifier); public static IDisposable Start(String identifier) => new BenchmarkUnit(identifier);
/// <summary> /// <summary>
/// Outputs the benchmark statistics. /// Outputs the benchmark statistics.
/// </summary> /// </summary>
/// <returns>A string containing human-readable statistics.</returns> /// <returns>A string containing human-readable statistics.</returns>
public static string Dump() public static String Dump() {
{ StringBuilder builder = new StringBuilder();
var builder = new StringBuilder();
lock (SyncLock) lock(SyncLock) {
{ foreach(KeyValuePair<String, List<TimeSpan>> kvp in Measures) {
foreach (var kvp in Measures) _ = builder.Append($"BID: {kvp.Key,-30} | ")
{
builder.Append($"BID: {kvp.Key,-30} | ")
.Append($"CNT: {kvp.Value.Count,6} | ") .Append($"CNT: {kvp.Value.Count,6} | ")
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ") .Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ") .Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
@ -74,12 +69,11 @@
/// </summary> /// </summary>
/// <param name="identifier">The identifier.</param> /// <param name="identifier">The identifier.</param>
/// <param name="elapsed">The elapsed.</param> /// <param name="elapsed">The elapsed.</param>
private static void Add(string identifier, TimeSpan elapsed) private static void Add(String identifier, TimeSpan elapsed) {
{ lock(SyncLock) {
lock (SyncLock) if(Measures.ContainsKey(identifier) == false) {
{
if (Measures.ContainsKey(identifier) == false)
Measures[identifier] = new List<TimeSpan>(1024 * 1024); Measures[identifier] = new List<TimeSpan>(1024 * 1024);
}
Measures[identifier].Add(elapsed); Measures[identifier].Add(elapsed);
} }
@ -89,41 +83,39 @@
/// Represents a disposable benchmark unit. /// Represents a disposable benchmark unit.
/// </summary> /// </summary>
/// <seealso cref="IDisposable" /> /// <seealso cref="IDisposable" />
private sealed class BenchmarkUnit : IDisposable private sealed class BenchmarkUnit : IDisposable {
{ private readonly String _identifier;
private readonly string _identifier; private Boolean _isDisposed; // To detect redundant calls
private bool _isDisposed; // To detect redundant calls
private Stopwatch _stopwatch = new Stopwatch(); private Stopwatch _stopwatch = new Stopwatch();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BenchmarkUnit" /> class. /// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
/// </summary> /// </summary>
/// <param name="identifier">The identifier.</param> /// <param name="identifier">The identifier.</param>
public BenchmarkUnit(string identifier) public BenchmarkUnit(String identifier) {
{ this._identifier = identifier;
_identifier = identifier; this._stopwatch.Start();
_stopwatch.Start();
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() => Dispose(true); public void Dispose() => this.Dispose(true);
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
private void Dispose(bool alsoManaged) private void Dispose(Boolean alsoManaged) {
{ if(this._isDisposed) {
if (_isDisposed) return; return;
if (alsoManaged)
{
Add(_identifier, _stopwatch.Elapsed);
_stopwatch?.Stop();
} }
_stopwatch = null; if(alsoManaged) {
_isDisposed = true; Add(this._identifier, this._stopwatch.Elapsed);
this._stopwatch?.Stop();
}
this._stopwatch = null;
this._isDisposed = true;
} }
} }
} }

View File

@ -1,16 +1,14 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// A thread-safe collection cache repository for types. /// A thread-safe collection cache repository for types.
/// </summary> /// </summary>
/// <typeparam name="TValue">The type of member to cache.</typeparam> /// <typeparam name="TValue">The type of member to cache.</typeparam>
public class CollectionCacheRepository<TValue> public class CollectionCacheRepository<TValue> {
{
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data = private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() => new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true); new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
@ -20,7 +18,7 @@
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns> /// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
public bool ContainsKey(Type key) => _data.Value.ContainsKey(key); public Boolean ContainsKey(Type key) => this._data.Value.ContainsKey(key);
/// <summary> /// <summary>
/// Retrieves the properties stored for the specified type. /// Retrieves the properties stored for the specified type.
@ -33,12 +31,12 @@
/// An array of the properties stored for the specified type. /// An array of the properties stored for the specified type.
/// </returns> /// </returns>
/// <exception cref="System.ArgumentNullException">type.</exception> /// <exception cref="System.ArgumentNullException">type.</exception>
public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory) public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory) {
{ if(factory == null) {
if (factory == null)
throw new ArgumentNullException(nameof(factory)); throw new ArgumentNullException(nameof(factory));
}
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null)); return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
} }
} }
} }

View File

@ -1,28 +1,23 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provide Enumerations helpers with internal cache. /// Provide Enumerations helpers with internal cache.
/// </summary> /// </summary>
public class EnumHelper public class EnumHelper
: SingletonBase<CollectionCacheRepository<Tuple<string, object>>> : SingletonBase<CollectionCacheRepository<Tuple<String, Object>>> {
{
/// <summary> /// <summary>
/// Gets all the names and enumerators from a specific Enum type. /// Gets all the names and enumerators from a specific Enum type.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam> /// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
/// <returns>A tuple of enumerator names and their value stored for the specified type.</returns> /// <returns>A tuple of enumerator names and their value stored for the specified type.</returns>
public static IEnumerable<Tuple<string, object>> Retrieve<T>() public static IEnumerable<Tuple<String, Object>> Retrieve<T>()
where T : struct, IConvertible where T : struct, IConvertible => Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
{ .Cast<Object>()
return Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
.Cast<object>()
.Select(item => Tuple.Create(Enum.GetName(t, item), item))); .Select(item => Tuple.Create(Enum.GetName(t, item), item)));
}
/// <summary> /// <summary>
/// Gets the cached items with the enum item value. /// Gets the cached items with the enum item value.
@ -33,12 +28,9 @@
/// A collection of Type/Tuple pairs /// A collection of Type/Tuple pairs
/// that represents items with the enum item value. /// that represents items with the enum item value.
/// </returns> /// </returns>
public static IEnumerable<Tuple<int, string>> GetItemsWithValue<T>(bool humanize = true) public static IEnumerable<Tuple<Int32, String>> GetItemsWithValue<T>(Boolean humanize = true)
where T : struct, IConvertible where T : struct, IConvertible => Retrieve<T>()
{ .Select(x => Tuple.Create((Int32)x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
return Retrieve<T>()
.Select(x => Tuple.Create((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
}
/// <summary> /// <summary>
/// Gets the flag values. /// Gets the flag values.
@ -49,14 +41,11 @@
/// <returns> /// <returns>
/// A list of values in the flag. /// A list of values in the flag.
/// </returns> /// </returns>
public static IEnumerable<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false) public static IEnumerable<Int32> GetFlagValues<TEnum>(Int32 value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .Select(x => (Int32)x.Item2)
return Retrieve<TEnum>()
.Select(x => (int) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0)) .When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x); .Where(x => (x & value) == x);
}
/// <summary> /// <summary>
/// Gets the flag values. /// Gets the flag values.
@ -67,14 +56,11 @@
/// <returns> /// <returns>
/// A list of values in the flag. /// A list of values in the flag.
/// </returns> /// </returns>
public static IEnumerable<long> GetFlagValues<TEnum>(long value, bool ignoreZero = false) public static IEnumerable<Int64> GetFlagValues<TEnum>(Int64 value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .Select(x => (Int64)x.Item2)
return Retrieve<TEnum>()
.Select(x => (long) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0)) .When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x); .Where(x => (x & value) == x);
}
/// <summary> /// <summary>
/// Gets the flag values. /// Gets the flag values.
@ -85,14 +71,11 @@
/// <returns> /// <returns>
/// A list of values in the flag. /// A list of values in the flag.
/// </returns> /// </returns>
public static IEnumerable<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false) public static IEnumerable<Byte> GetFlagValues<TEnum>(Byte value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .Select(x => (Byte)x.Item2)
return Retrieve<TEnum>()
.Select(x => (byte) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0)) .When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x); .Where(x => (x & value) == x);
}
/// <summary> /// <summary>
/// Gets the flag names. /// Gets the flag names.
@ -104,14 +87,11 @@
/// <returns> /// <returns>
/// A list of flag names. /// A list of flag names.
/// </returns> /// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(int value, bool ignoreZero = false, bool humanize = true) public static IEnumerable<String> GetFlagNames<TEnum>(Int32 value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .When(() => ignoreZero, q => q.Where(f => (Int32)f.Item2 != 0))
return Retrieve<TEnum>() .Where(x => ((Int32)x.Item2 & value) == (Int32)x.Item2)
.When(() => ignoreZero, q => q.Where(f => (int) f.Item2 != 0))
.Where(x => ((int) x.Item2 & value) == (int) x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1); .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
}
/// <summary> /// <summary>
/// Gets the flag names. /// Gets the flag names.
@ -123,14 +103,11 @@
/// <returns> /// <returns>
/// A list of flag names. /// A list of flag names.
/// </returns> /// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true) public static IEnumerable<String> GetFlagNames<TEnum>(Int64 value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .When(() => ignoreZero, q => q.Where(f => (Int64)f.Item2 != 0))
return Retrieve<TEnum>() .Where(x => ((Int64)x.Item2 & value) == (Int64)x.Item2)
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0))
.Where(x => ((long) x.Item2 & value) == (long) x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1); .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
}
/// <summary> /// <summary>
/// Gets the flag names. /// Gets the flag names.
@ -142,14 +119,11 @@
/// <returns> /// <returns>
/// A list of flag names. /// A list of flag names.
/// </returns> /// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(byte value, bool ignoreZero = false, bool humanize = true) public static IEnumerable<String> GetFlagNames<TEnum>(Byte value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible where TEnum : struct, IConvertible => Retrieve<TEnum>()
{ .When(() => ignoreZero, q => q.Where(f => (Byte)f.Item2 != 0))
return Retrieve<TEnum>() .Where(x => ((Byte)x.Item2 & value) == (Byte)x.Item2)
.When(() => ignoreZero, q => q.Where(f => (byte) f.Item2 != 0))
.Where(x => ((byte) x.Item2 & value) == (byte) x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1); .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
}
/// <summary> /// <summary>
/// Gets the cached items with the enum item index. /// Gets the cached items with the enum item index.
@ -159,10 +133,9 @@
/// <returns> /// <returns>
/// A collection of Type/Tuple pairs that represents items with the enum item value. /// A collection of Type/Tuple pairs that represents items with the enum item value.
/// </returns> /// </returns>
public static IEnumerable<Tuple<int, string>> GetItemsWithIndex<T>(bool humanize = true) public static IEnumerable<Tuple<Int32, String>> GetItemsWithIndex<T>(Boolean humanize = true)
where T : struct, IConvertible where T : struct, IConvertible {
{ Int32 i = 0;
var i = 0;
return Retrieve<T>() return Retrieve<T>()
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1)); .Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));

View File

@ -1,17 +1,15 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Represents a quick object comparer using the public properties of an object /// Represents a quick object comparer using the public properties of an object
/// or the public members in a structure. /// or the public members in a structure.
/// </summary> /// </summary>
public static class ObjectComparer public static class ObjectComparer {
{
/// <summary> /// <summary>
/// Compare if two variables of the same type are equal. /// Compare if two variables of the same type are equal.
/// </summary> /// </summary>
@ -19,7 +17,7 @@
/// <param name="left">The left.</param> /// <param name="left">The left.</param>
/// <param name="right">The right.</param> /// <param name="right">The right.</param>
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
public static bool AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T)); public static Boolean AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
/// <summary> /// <summary>
/// Compare if two variables of the same type are equal. /// Compare if two variables of the same type are equal.
@ -31,18 +29,16 @@
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>. /// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception> /// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreEqual(object left, object right, Type targetType) public static Boolean AreEqual(Object left, Object right, Type targetType) {
{ if(targetType == null) {
if (targetType == null)
throw new ArgumentNullException(nameof(targetType)); throw new ArgumentNullException(nameof(targetType));
}
if (Definitions.BasicTypesInfo.ContainsKey(targetType)) return Definitions.BasicTypesInfo.ContainsKey(targetType)
return Equals(left, right); ? Equals(left, right)
: targetType.IsValueType() || targetType.IsArray
if (targetType.IsValueType() || targetType.IsArray) ? AreStructsEqual(left, right, targetType)
return AreStructsEqual(left, right, targetType); : AreObjectsEqual(left, right, targetType);
return AreObjectsEqual(left, right, targetType);
} }
/// <summary> /// <summary>
@ -52,11 +48,8 @@
/// <param name="left">The left.</param> /// <param name="left">The left.</param>
/// <param name="right">The right.</param> /// <param name="right">The right.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
public static bool AreObjectsEqual<T>(T left, T right) public static Boolean AreObjectsEqual<T>(T left, T right)
where T : class where T : class => AreObjectsEqual(left, right, typeof(T));
{
return AreObjectsEqual(left, right, typeof(T));
}
/// <summary> /// <summary>
/// Compare if two objects of the same type are equal. /// Compare if two objects of the same type are equal.
@ -66,31 +59,29 @@
/// <param name="targetType">Type of the target.</param> /// <param name="targetType">Type of the target.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">targetType.</exception> /// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreObjectsEqual(object left, object right, Type targetType) public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) {
{ if(targetType == null) {
if (targetType == null)
throw new ArgumentNullException(nameof(targetType)); throw new ArgumentNullException(nameof(targetType));
}
var properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray(); PropertyInfo[] properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
foreach (var propertyTarget in properties) foreach(PropertyInfo propertyTarget in properties) {
{ Func<Object, Object> targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
var targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
if (propertyTarget.PropertyType.IsArray) if(propertyTarget.PropertyType.IsArray) {
{ IEnumerable leftObj = targetPropertyGetMethod(left) as IEnumerable;
var leftObj = targetPropertyGetMethod(left) as IEnumerable; IEnumerable rightObj = targetPropertyGetMethod(right) as IEnumerable;
var rightObj = targetPropertyGetMethod(right) as IEnumerable;
if (!AreEnumerationsEquals(leftObj, rightObj)) if(!AreEnumerationsEquals(leftObj, rightObj)) {
return false; return false;
} }
else } else {
{ if(!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
if (!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right)))
return false; return false;
} }
} }
}
return true; return true;
} }
@ -102,11 +93,8 @@
/// <param name="left">The left.</param> /// <param name="left">The left.</param>
/// <param name="right">The right.</param> /// <param name="right">The right.</param>
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
public static bool AreStructsEqual<T>(T left, T right) public static Boolean AreStructsEqual<T>(T left, T right)
where T : struct where T : struct => AreStructsEqual(left, right, typeof(T));
{
return AreStructsEqual(left, right, typeof(T));
}
/// <summary> /// <summary>
/// Compare if two structures of the same type are equal. /// Compare if two structures of the same type are equal.
@ -118,28 +106,30 @@
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>. /// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception> /// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreStructsEqual(object left, object right, Type targetType) public static Boolean AreStructsEqual(Object left, Object right, Type targetType) {
{ if(targetType == null) {
if (targetType == null)
throw new ArgumentNullException(nameof(targetType)); throw new ArgumentNullException(nameof(targetType));
}
var fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType)) IEnumerable<MemberInfo> fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
.Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType)); .Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType));
foreach (var targetMember in fields) foreach(MemberInfo targetMember in fields) {
{ switch(targetMember) {
switch (targetMember)
{
case FieldInfo field: case FieldInfo field:
if (Equals(field.GetValue(left), field.GetValue(right)) == false) if(Equals(field.GetValue(left), field.GetValue(right)) == false) {
return false; return false;
}
break; break;
case PropertyInfo property: case PropertyInfo property:
var targetPropertyGetMethod = property.GetCacheGetMethod(); Func<Object, Object> targetPropertyGetMethod = property.GetCacheGetMethod();
if(targetPropertyGetMethod != null && if(targetPropertyGetMethod != null &&
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) !Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
return false; return false;
}
break; break;
} }
} }
@ -161,28 +151,28 @@
/// or /// or
/// right. /// right.
/// </exception> /// </exception>
public static bool AreEnumerationsEquals<T>(T left, T right) public static Boolean AreEnumerationsEquals<T>(T left, T right)
where T : IEnumerable where T : IEnumerable {
{ if(Equals(left, default(T))) {
if (Equals(left, default(T)))
throw new ArgumentNullException(nameof(left)); throw new ArgumentNullException(nameof(left));
}
if (Equals(right, default(T))) if(Equals(right, default(T))) {
throw new ArgumentNullException(nameof(right)); throw new ArgumentNullException(nameof(right));
}
var leftEnumerable = left.Cast<object>().ToArray(); Object[] leftEnumerable = left.Cast<Object>().ToArray();
var rightEnumerable = right.Cast<object>().ToArray(); Object[] rightEnumerable = right.Cast<Object>().ToArray();
if (leftEnumerable.Length != rightEnumerable.Length) if(leftEnumerable.Length != rightEnumerable.Length) {
return false; return false;
}
for (var i = 0; i < leftEnumerable.Length; i++) for(Int32 i = 0; i < leftEnumerable.Length; i++) {
{ Object leftEl = leftEnumerable[i];
var leftEl = leftEnumerable[i]; Object rightEl = rightEnumerable[i];
var rightEl = rightEnumerable[i];
if (!AreEqual(leftEl, rightEl, leftEl.GetType())) if(!AreEqual(leftEl, rightEl, leftEl.GetType())) {
{
return false; return false;
} }
} }

View File

@ -1,37 +1,40 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Represents an object map. /// Represents an object map.
/// </summary> /// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam> /// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TDestination">The type of the destination.</typeparam> /// <typeparam name="TDestination">The type of the destination.</typeparam>
/// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" /> /// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" />
public class ObjectMap<TSource, TDestination> : IObjectMap public class ObjectMap<TSource, TDestination> : IObjectMap {
{ internal ObjectMap(IEnumerable<PropertyInfo> intersect) {
internal ObjectMap(IEnumerable<PropertyInfo> intersect) this.SourceType = typeof(TSource);
{ this.DestinationType = typeof(TDestination);
SourceType = typeof(TSource); this.Map = intersect.ToDictionary(
DestinationType = typeof(TDestination); property => this.DestinationType.GetProperty(property.Name),
Map = intersect.ToDictionary( property => new List<PropertyInfo> { this.SourceType.GetProperty(property.Name) });
property => DestinationType.GetProperty(property.Name),
property => new List<PropertyInfo> {SourceType.GetProperty(property.Name)});
} }
/// <inheritdoc/> /// <inheritdoc/>
public Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; } public Dictionary<PropertyInfo, List<PropertyInfo>> Map {
get;
}
/// <inheritdoc/> /// <inheritdoc/>
public Type SourceType { get; } public Type SourceType {
get;
}
/// <inheritdoc/> /// <inheritdoc/>
public Type DestinationType { get; } public Type DestinationType {
get;
}
/// <summary> /// <summary>
/// Maps the property. /// Maps the property.
@ -47,25 +50,22 @@
public ObjectMap<TSource, TDestination> MapProperty public ObjectMap<TSource, TDestination> MapProperty
<TDestinationProperty, TSourceProperty>( <TDestinationProperty, TSourceProperty>(
Expression<Func<TDestination, TDestinationProperty>> destinationProperty, Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
Expression<Func<TSource, TSourceProperty>> sourceProperty) Expression<Func<TSource, TSourceProperty>> sourceProperty) {
{ PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
if (propertyDestinationInfo == null) if(propertyDestinationInfo == null) {
{
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty)); throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
} }
var sourceMembers = GetSourceMembers(sourceProperty); List<PropertyInfo> sourceMembers = GetSourceMembers(sourceProperty);
if (sourceMembers.Any() == false) if(sourceMembers.Any() == false) {
{
throw new ArgumentException("Invalid source expression", nameof(sourceProperty)); throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
} }
// reverse order // reverse order
sourceMembers.Reverse(); sourceMembers.Reverse();
Map[propertyDestinationInfo] = sourceMembers; this.Map[propertyDestinationInfo] = sourceMembers;
return this; return this;
} }
@ -81,31 +81,31 @@
/// </returns> /// </returns>
/// <exception cref="System.Exception">Invalid destination expression.</exception> /// <exception cref="System.Exception">Invalid destination expression.</exception>
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>( public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
Expression<Func<TDestination, TDestinationProperty>> destinationProperty) Expression<Func<TDestination, TDestinationProperty>> destinationProperty) {
{ PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
if (propertyDestinationInfo == null) if(propertyDestinationInfo == null) {
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty)); throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
}
if (Map.ContainsKey(propertyDestinationInfo)) if(this.Map.ContainsKey(propertyDestinationInfo)) {
{ _ = this.Map.Remove(propertyDestinationInfo);
Map.Remove(propertyDestinationInfo);
} }
return this; return this;
} }
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty) private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty) {
{ List<PropertyInfo> sourceMembers = new List<PropertyInfo>();
var sourceMembers = new List<PropertyInfo>(); MemberExpression initialExpression = sourceProperty.Body as MemberExpression;
var initialExpression = sourceProperty.Body as MemberExpression;
while (true) while(true) {
{ PropertyInfo propertySourceInfo = initialExpression?.Member as PropertyInfo;
var propertySourceInfo = initialExpression?.Member as PropertyInfo;
if(propertySourceInfo == null) {
break;
}
if (propertySourceInfo == null) break;
sourceMembers.Add(propertySourceInfo); sourceMembers.Add(propertySourceInfo);
initialExpression = initialExpression.Expression as MemberExpression; initialExpression = initialExpression.Expression as MemberExpression;
} }

View File

@ -1,12 +1,11 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Represents an AutoMapper-like object to map from one object type /// Represents an AutoMapper-like object to map from one object type
/// to another using defined properties map or using the default behaviour /// to another using defined properties map or using the default behaviour
@ -77,8 +76,7 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public class ObjectMapper public class ObjectMapper {
{
private readonly List<IObjectMap> _maps = new List<IObjectMap>(); private readonly List<IObjectMap> _maps = new List<IObjectMap>();
/// <summary> /// <summary>
@ -96,17 +94,18 @@
/// or /// or
/// target. /// target.
/// </exception> /// </exception>
public static int Copy( public static Int32 Copy(
object source, Object source,
object target, Object target,
string[] propertiesToCopy = null, String[] propertiesToCopy = null,
string[] ignoreProperties = null) String[] ignoreProperties = null) {
{ if(source == null) {
if (source == null)
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
}
if (target == null) if(target == null) {
throw new ArgumentNullException(nameof(target)); throw new ArgumentNullException(nameof(target));
}
return Copy( return Copy(
target, target,
@ -130,17 +129,18 @@
/// or /// or
/// target. /// target.
/// </exception> /// </exception>
public static int Copy( public static Int32 Copy(
IDictionary<string, object> source, IDictionary<String, Object> source,
object target, Object target,
string[] propertiesToCopy = null, String[] propertiesToCopy = null,
string[] ignoreProperties = null) String[] ignoreProperties = null) {
{ if(source == null) {
if (source == null)
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
}
if (target == null) if(target == null) {
throw new ArgumentNullException(nameof(target)); throw new ArgumentNullException(nameof(target));
}
return Copy( return Copy(
target, target,
@ -148,7 +148,7 @@
ignoreProperties, ignoreProperties,
source.ToDictionary( source.ToDictionary(
x => x.Key.ToLowerInvariant(), x => x.Key.ToLowerInvariant(),
x => new TypeValuePair(typeof(object), x.Value))); x => new TypeValuePair(typeof(Object), x.Value)));
} }
/// <summary> /// <summary>
@ -165,26 +165,23 @@
/// or /// or
/// Types doesn't match. /// Types doesn't match.
/// </exception> /// </exception>
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>() public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>() {
{ if(this._maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination))) {
if (_maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination)))
{
throw new InvalidOperationException("You can't create an existing map"); throw new InvalidOperationException("You can't create an existing map");
} }
var sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true); IEnumerable<PropertyInfo> sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
var destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true); IEnumerable<PropertyInfo> destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
var intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray(); PropertyInfo[] intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
if (intersect.Any() == false) if(intersect.Any() == false) {
{
throw new InvalidOperationException("Types doesn't match"); throw new InvalidOperationException("Types doesn't match");
} }
var map = new ObjectMap<TSource, TDestination>(intersect); ObjectMap<TSource, TDestination> map = new ObjectMap<TSource, TDestination>(intersect);
_maps.Add(map); this._maps.Add(map);
return map; return map;
} }
@ -200,58 +197,50 @@
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">source.</exception> /// <exception cref="ArgumentNullException">source.</exception>
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception> /// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
public TDestination Map<TDestination>(object source, bool autoResolve = true) public TDestination Map<TDestination>(Object source, Boolean autoResolve = true) {
{ if(source == null) {
if (source == null)
{
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
} }
var destination = Activator.CreateInstance<TDestination>(); TDestination destination = Activator.CreateInstance<TDestination>();
var map = _maps IObjectMap map = this._maps
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination)); .FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
if (map != null) if(map != null) {
{ foreach(KeyValuePair<PropertyInfo, List<PropertyInfo>> property in map.Map) {
foreach (var property in map.Map) Object finalSource = property.Value.Aggregate(source,
{
var finalSource = property.Value.Aggregate(source,
(current, sourceProperty) => sourceProperty.GetValue(current)); (current, sourceProperty) => sourceProperty.GetValue(current));
property.Key.SetValue(destination, finalSource); property.Key.SetValue(destination, finalSource);
} }
} } else {
else if(!autoResolve) {
{
if (!autoResolve)
{
throw new InvalidOperationException( throw new InvalidOperationException(
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}"); $"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
} }
// Missing mapping, try to use default behavior // Missing mapping, try to use default behavior
Copy(source, destination); _ = Copy(source, destination);
} }
return destination; return destination;
} }
private static int Copy( private static Int32 Copy(
object target, Object target,
IEnumerable<string> propertiesToCopy, IEnumerable<String> propertiesToCopy,
IEnumerable<string> ignoreProperties, IEnumerable<String> ignoreProperties,
Dictionary<string, TypeValuePair> sourceProperties) Dictionary<String, TypeValuePair> sourceProperties) {
{
// Filter properties // Filter properties
var requiredProperties = propertiesToCopy? IEnumerable<String> requiredProperties = propertiesToCopy?
.Where(p => !string.IsNullOrWhiteSpace(p)) .Where(p => !String.IsNullOrWhiteSpace(p))
.Select(p => p.ToLowerInvariant()); .Select(p => p.ToLowerInvariant());
var ignoredProperties = ignoreProperties? IEnumerable<String> ignoredProperties = ignoreProperties?
.Where(p => !string.IsNullOrWhiteSpace(p)) .Where(p => !String.IsNullOrWhiteSpace(p))
.Select(p => p.ToLowerInvariant()); .Select(p => p.ToLowerInvariant());
var properties = Runtime.PropertyTypeCache IEnumerable<PropertyInfo> properties = Runtime.PropertyTypeCache
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite); .RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
return properties return properties
@ -265,115 +254,99 @@
.Sum(x => TrySetValue(x, target) ? 1 : 0); .Sum(x => TrySetValue(x, target) ? 1 : 0);
} }
private static bool TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target) private static Boolean TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
{ try {
try
{
SetValue(property, target); SetValue(property, target);
return true; return true;
} } catch {
catch
{
// swallow // swallow
} }
return false; return false;
} }
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target) private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
{ if(property.Value.Type.GetTypeInfo().IsEnum) {
if (property.Value.Type.GetTypeInfo().IsEnum)
{
property.Key.SetValue(target, property.Key.SetValue(target,
Enum.ToObject(property.Key.PropertyType, property.Value.Value)); Enum.ToObject(property.Key.PropertyType, property.Value.Value));
return; return;
} }
if (!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) if(!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) {
{
property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType)); property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType));
return; return;
} }
if (property.Key.PropertyType == typeof(bool)) if(property.Key.PropertyType == typeof(Boolean)) {
{
property.Key.SetValue(target, property.Key.SetValue(target,
Convert.ToBoolean(property.Value.Value)); Convert.ToBoolean(property.Value.Value));
return; return;
} }
property.Key.TrySetBasicType(property.Value.Value, target); _ = property.Key.TrySetBasicType(property.Value.Value, target);
} }
private static object GetValue(object source, Type targetType) private static Object GetValue(Object source, Type targetType) {
{ if(source == null) {
if (source == null)
return null; return null;
}
object target = null; Object target = null;
source.CreateTarget(targetType, false, ref target); source.CreateTarget(targetType, false, ref target);
switch (source) switch(source) {
{ case String _:
case string _:
target = source; target = source;
break; break;
case IList sourceList when target is Array targetArray: case IList sourceList when target is Array targetArray:
for (var i = 0; i < sourceList.Count; i++) for(Int32 i = 0; i < sourceList.Count; i++) {
{ try {
try
{
targetArray.SetValue( targetArray.SetValue(
sourceList[i].GetType().IsValueType() sourceList[i].GetType().IsValueType()
? sourceList[i] ? sourceList[i]
: sourceList[i].CopyPropertiesToNew<object>(), i); : sourceList[i].CopyPropertiesToNew<Object>(), i);
} } catch {
catch
{
// ignored // ignored
} }
} }
break; break;
case IList sourceList when target is IList targetList: case IList sourceList when target is IList targetList:
var addMethod = targetType.GetMethods() MethodInfo addMethod = targetType.GetMethods()
.FirstOrDefault( .FirstOrDefault(
m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic && m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic &&
m.GetParameters().Length == 1); m.GetParameters().Length == 1);
if (addMethod == null) return target; if(addMethod == null) {
return target;
foreach (var item in sourceList)
{
try
{
targetList.Add(item.GetType().IsValueType()
? item
: item.CopyPropertiesToNew<object>());
} }
catch
{ foreach(Object item in sourceList) {
try {
_ = targetList.Add(item.GetType().IsValueType()
? item
: item.CopyPropertiesToNew<Object>());
} catch {
// ignored // ignored
} }
} }
break; break;
default: default:
source.CopyPropertiesTo(target); _ = source.CopyPropertiesTo(target);
break; break;
} }
return target; return target;
} }
private static Dictionary<string, TypeValuePair> GetSourceMap(object source) private static Dictionary<String, TypeValuePair> GetSourceMap(Object source) {
{
// select distinct properties because they can be duplicated by inheritance // select distinct properties because they can be duplicated by inheritance
var sourceProperties = Runtime.PropertyTypeCache PropertyInfo[] sourceProperties = Runtime.PropertyTypeCache
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead) .RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
.ToArray(); .ToArray();
@ -386,25 +359,26 @@
sourceProperties.First(y => y.Name == x).GetValue(source))); sourceProperties.First(y => y.Name == x).GetValue(source)));
} }
internal class TypeValuePair internal class TypeValuePair {
{ public TypeValuePair(Type type, Object value) {
public TypeValuePair(Type type, object value) this.Type = type;
{ this.Value = value;
Type = type;
Value = value;
} }
public Type Type { get; } public Type Type {
get;
public object Value { get; }
} }
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo> public Object Value {
{ get;
public bool Equals(PropertyInfo x, PropertyInfo y) }
}
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo> {
public Boolean Equals(PropertyInfo x, PropertyInfo y)
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType; => x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
public int GetHashCode(PropertyInfo obj) public Int32 GetHashCode(PropertyInfo obj)
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode(); => obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
} }
} }

View File

@ -1,11 +1,10 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Linq; using System.Linq;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Represents an object validator. /// Represents an object validator.
/// </summary> /// </summary>
@ -68,10 +67,9 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public class ObjectValidator public class ObjectValidator {
{ private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, String>>> _predicates =
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, string>>> _predicates = new ConcurrentDictionary<Type, List<Tuple<Delegate, String>>>();
new ConcurrentDictionary<Type, List<Tuple<Delegate, string>>>();
/// <summary> /// <summary>
/// Validates an object given the specified validators and attributes. /// Validates an object given the specified validators and attributes.
@ -79,10 +77,9 @@
/// <typeparam name="T">The type of the object.</typeparam> /// <typeparam name="T">The type of the object.</typeparam>
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <returns cref="ObjectValidationResult">A validation result. </returns> /// <returns cref="ObjectValidationResult">A validation result. </returns>
public ObjectValidationResult Validate<T>(T obj) public ObjectValidationResult Validate<T>(T obj) {
{ ObjectValidationResult errorList = new ObjectValidationResult();
var errorList = new ObjectValidationResult(); _ = this.ValidateObject(obj, false, errorList.Add);
ValidateObject(obj, false, errorList.Add);
return errorList; return errorList;
} }
@ -96,7 +93,7 @@
/// <c>true</c> if the specified object is valid; otherwise, <c>false</c>. /// <c>true</c> if the specified object is valid; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">obj.</exception> /// <exception cref="ArgumentNullException">obj.</exception>
public bool IsValid<T>(T obj) => ValidateObject(obj); public Boolean IsValid<T>(T obj) => this.ValidateObject(obj);
/// <summary> /// <summary>
/// Adds a validator to a specific class. /// Adds a validator to a specific class.
@ -109,52 +106,56 @@
/// or /// or
/// message. /// message.
/// </exception> /// </exception>
public void AddValidator<T>(Predicate<T> predicate, string message) public void AddValidator<T>(Predicate<T> predicate, String message)
where T : class where T : class {
{ if(predicate == null) {
if (predicate == null)
throw new ArgumentNullException(nameof(predicate)); throw new ArgumentNullException(nameof(predicate));
}
if (string.IsNullOrEmpty(message)) if(String.IsNullOrEmpty(message)) {
throw new ArgumentNullException(message); throw new ArgumentNullException(message);
}
if (!_predicates.TryGetValue(typeof(T), out var existing)) if(!this._predicates.TryGetValue(typeof(T), out List<Tuple<Delegate, String>> existing)) {
{ existing = new List<Tuple<Delegate, String>>();
existing = new List<Tuple<Delegate, string>>(); this._predicates[typeof(T)] = existing;
_predicates[typeof(T)] = existing;
} }
existing.Add(Tuple.Create((Delegate)predicate, message)); existing.Add(Tuple.Create((Delegate)predicate, message));
} }
private bool ValidateObject<T>(T obj, bool returnOnError = true, Action<string, string> action = null) private Boolean ValidateObject<T>(T obj, Boolean returnOnError = true, Action<String, String> action = null) {
{ if(Equals(obj, null)) {
if (Equals(obj, null))
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
}
if (_predicates.ContainsKey(typeof(T))) if(this._predicates.ContainsKey(typeof(T))) {
{ foreach(Tuple<Delegate, String> validation in this._predicates[typeof(T)]) {
foreach (var validation in _predicates[typeof(T)]) if((Boolean)validation.Item1.DynamicInvoke(obj)) {
{ continue;
if ((bool) validation.Item1.DynamicInvoke(obj)) continue; }
action?.Invoke(string.Empty, validation.Item2); action?.Invoke(String.Empty, validation.Item2);
if (returnOnError) return false; if(returnOnError) {
return false;
}
} }
} }
var properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator)); Dictionary<System.Reflection.PropertyInfo, IEnumerable<Object>> properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
foreach (var prop in properties) foreach(KeyValuePair<System.Reflection.PropertyInfo, IEnumerable<Object>> prop in properties) {
{ foreach(Object attribute in prop.Value) {
foreach (var attribute in prop.Value) IValidator val = (IValidator)attribute;
{
var val = (IValidator) attribute;
if (val.IsValid(prop.Key.GetValue(obj, null))) continue; if(val.IsValid(prop.Key.GetValue(obj, null))) {
continue;
}
action?.Invoke(prop.Key.Name, val.ErrorMessage); action?.Invoke(prop.Key.Name, val.ErrorMessage);
if (returnOnError) return false; if(returnOnError) {
return false;
}
} }
} }
@ -165,8 +166,7 @@
/// <summary> /// <summary>
/// Defines a validation result containing all validation errors and their properties. /// Defines a validation result containing all validation errors and their properties.
/// </summary> /// </summary>
public class ObjectValidationResult public class ObjectValidationResult {
{
/// <summary> /// <summary>
/// A list of errors. /// A list of errors.
/// </summary> /// </summary>
@ -175,30 +175,33 @@
/// <summary> /// <summary>
/// <c>true</c> if there are no errors; otherwise, <c>false</c>. /// <c>true</c> if there are no errors; otherwise, <c>false</c>.
/// </summary> /// </summary>
public bool IsValid => !Errors.Any(); public Boolean IsValid => !this.Errors.Any();
/// <summary> /// <summary>
/// Adds an error with a specified property name. /// Adds an error with a specified property name.
/// </summary> /// </summary>
/// <param name="propertyName">The property name.</param> /// <param name="propertyName">The property name.</param>
/// <param name="errorMessage">The error message.</param> /// <param name="errorMessage">The error message.</param>
public void Add(string propertyName, string errorMessage) => public void Add(String propertyName, String errorMessage) =>
Errors.Add(new ValidationError {ErrorMessage = errorMessage, PropertyName = propertyName}); this.Errors.Add(new ValidationError { ErrorMessage = errorMessage, PropertyName = propertyName });
/// <summary> /// <summary>
/// Defines a validation error. /// Defines a validation error.
/// </summary> /// </summary>
public class ValidationError public class ValidationError {
{
/// <summary> /// <summary>
/// The property name. /// The property name.
/// </summary> /// </summary>
public string PropertyName { get; set; } public String PropertyName {
get; set;
}
/// <summary> /// <summary>
/// The message error. /// The message error.
/// </summary> /// </summary>
public string ErrorMessage { get; set; } public String ErrorMessage {
get; set;
}
} }
} }
} }

View File

@ -1,22 +1,19 @@
namespace Unosquare.Swan.Components using System;
{
using System;
using System.Threading; using System.Threading;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides factory methods to create synchronized reader-writer locks /// Provides factory methods to create synchronized reader-writer locks
/// that support a generalized locking and releasing api and syntax. /// that support a generalized locking and releasing api and syntax.
/// </summary> /// </summary>
public static class SyncLockerFactory public static class SyncLockerFactory {
{
#region Enums and Interfaces #region Enums and Interfaces
/// <summary> /// <summary>
/// Enumerates the locking operations. /// Enumerates the locking operations.
/// </summary> /// </summary>
private enum LockHolderType private enum LockHolderType {
{
Read, Read,
Write, Write,
} }
@ -24,8 +21,7 @@
/// <summary> /// <summary>
/// Defines methods for releasing locks. /// Defines methods for releasing locks.
/// </summary> /// </summary>
private interface ISyncReleasable private interface ISyncReleasable {
{
/// <summary> /// <summary>
/// Releases the writer lock. /// Releases the writer lock.
/// </summary> /// </summary>
@ -67,7 +63,7 @@
/// </summary> /// </summary>
/// <param name="useSlim">if set to <c>true</c> it uses the Slim version of a reader-writer lock.</param> /// <param name="useSlim">if set to <c>true</c> it uses the Slim version of a reader-writer lock.</param>
/// <returns>The Sync Locker.</returns> /// <returns>The Sync Locker.</returns>
public static ISyncLocker Create(bool useSlim) => useSlim ? CreateSlim() : Create(); public static ISyncLocker Create(Boolean useSlim) => useSlim ? CreateSlim() : Create();
#endregion #endregion
@ -77,34 +73,35 @@
/// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker. /// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker.
/// </summary> /// </summary>
/// <seealso cref="System.IDisposable" /> /// <seealso cref="System.IDisposable" />
private sealed class SyncLockReleaser : IDisposable private sealed class SyncLockReleaser : IDisposable {
{
private readonly ISyncReleasable _parent; private readonly ISyncReleasable _parent;
private readonly LockHolderType _operation; private readonly LockHolderType _operation;
private bool _isDisposed; private Boolean _isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SyncLockReleaser"/> class. /// Initializes a new instance of the <see cref="SyncLockReleaser"/> class.
/// </summary> /// </summary>
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
/// <param name="operation">The operation.</param> /// <param name="operation">The operation.</param>
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation) public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation) {
{ this._parent = parent;
_parent = parent; this._operation = operation;
_operation = operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose() {
{ if(this._isDisposed) {
if (_isDisposed) return; return;
_isDisposed = true; }
if (_operation == LockHolderType.Read) this._isDisposed = true;
_parent.ReleaseReaderLock();
else if(this._operation == LockHolderType.Read) {
_parent.ReleaseWriterLock(); this._parent.ReleaseReaderLock();
} else {
this._parent.ReleaseWriterLock();
}
} }
} }
@ -114,38 +111,37 @@
/// </summary> /// </summary>
/// <seealso cref="ISyncLocker" /> /// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" /> /// <seealso cref="ISyncReleasable" />
private sealed class SyncLocker : ISyncLocker, ISyncReleasable private sealed class SyncLocker : ISyncLocker, ISyncReleasable {
{ private Boolean _isDisposed;
private bool _isDisposed;
private ReaderWriterLock _locker = new ReaderWriterLock(); private ReaderWriterLock _locker = new ReaderWriterLock();
/// <inheritdoc /> /// <inheritdoc />
public IDisposable AcquireReaderLock() public IDisposable AcquireReaderLock() {
{ this._locker?.AcquireReaderLock(Timeout.Infinite);
_locker?.AcquireReaderLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Read); return new SyncLockReleaser(this, LockHolderType.Read);
} }
/// <inheritdoc /> /// <inheritdoc />
public IDisposable AcquireWriterLock() public IDisposable AcquireWriterLock() {
{ this._locker?.AcquireWriterLock(Timeout.Infinite);
_locker?.AcquireWriterLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Write); return new SyncLockReleaser(this, LockHolderType.Write);
} }
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ReleaseWriterLock(); public void ReleaseWriterLock() => this._locker?.ReleaseWriterLock();
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ReleaseReaderLock(); public void ReleaseReaderLock() => this._locker?.ReleaseReaderLock();
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose() {
{ if(this._isDisposed) {
if (_isDisposed) return; return;
_isDisposed = true; }
_locker?.ReleaseLock();
_locker = null; this._isDisposed = true;
_ = this._locker?.ReleaseLock();
this._locker = null;
} }
} }
#endif #endif
@ -155,40 +151,39 @@
/// </summary> /// </summary>
/// <seealso cref="ISyncLocker" /> /// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" /> /// <seealso cref="ISyncReleasable" />
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable {
{ private Boolean _isDisposed;
private bool _isDisposed;
private ReaderWriterLockSlim _locker private ReaderWriterLockSlim _locker
= new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <inheritdoc /> /// <inheritdoc />
public IDisposable AcquireReaderLock() public IDisposable AcquireReaderLock() {
{ this._locker?.EnterReadLock();
_locker?.EnterReadLock();
return new SyncLockReleaser(this, LockHolderType.Read); return new SyncLockReleaser(this, LockHolderType.Read);
} }
/// <inheritdoc /> /// <inheritdoc />
public IDisposable AcquireWriterLock() public IDisposable AcquireWriterLock() {
{ this._locker?.EnterWriteLock();
_locker?.EnterWriteLock();
return new SyncLockReleaser(this, LockHolderType.Write); return new SyncLockReleaser(this, LockHolderType.Write);
} }
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ExitWriteLock(); public void ReleaseWriterLock() => this._locker?.ExitWriteLock();
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ExitReadLock(); public void ReleaseReaderLock() => this._locker?.ExitReadLock();
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose() {
{ if(this._isDisposed) {
if (_isDisposed) return; return;
_isDisposed = true; }
_locker?.Dispose();
_locker = null; this._isDisposed = true;
this._locker?.Dispose();
this._locker = null;
} }
} }

View File

@ -1,10 +1,9 @@
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
namespace Unosquare.Swan.Components
{
using System; using System;
using System.Threading; using System.Threading;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Use this singleton to wait for a specific <c>TimeSpan</c> or time. /// Use this singleton to wait for a specific <c>TimeSpan</c> or time.
/// ///
@ -12,43 +11,36 @@ namespace Unosquare.Swan.Components
/// the time condition is satisfied. /// the time condition is satisfied.
/// </summary> /// </summary>
/// <seealso cref="SingletonBase{TimerControl}" /> /// <seealso cref="SingletonBase{TimerControl}" />
public class TimerControl : SingletonBase<TimerControl> public class TimerControl : SingletonBase<TimerControl> {
{ [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
private readonly Timer _innerTimer; private readonly Timer _innerTimer;
private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true); private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true);
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TimerControl"/> class. /// Initializes a new instance of the <see cref="TimerControl"/> class.
/// </summary> /// </summary>
protected TimerControl() protected TimerControl() => this._innerTimer = new Timer(
{ x => {
_innerTimer = new Timer( try {
x => this._delayLock.Complete();
{ this._delayLock.Begin();
try } catch {
{
_delayLock.Complete();
_delayLock.Begin();
}
catch
{
// ignore // ignore
} }
}, },
null, null,
0, 0,
15); 15);
}
/// <summary> /// <summary>
/// Waits until the time is elapsed. /// Waits until the time is elapsed.
/// </summary> /// </summary>
/// <param name="untilDate">The until date.</param> /// <param name="untilDate">The until date.</param>
/// <param name="ct">The cancellation token.</param> /// <param name="ct">The cancellation token.</param>
public void WaitUntil(DateTime untilDate, CancellationToken ct = default) public void WaitUntil(DateTime untilDate, CancellationToken ct = default) {
{ while(!ct.IsCancellationRequested && DateTime.UtcNow < untilDate) {
while (!ct.IsCancellationRequested && DateTime.UtcNow < untilDate) this._delayLock.Wait();
_delayLock.Wait(); }
} }
/// <summary> /// <summary>
@ -57,7 +49,7 @@ namespace Unosquare.Swan.Components
/// <param name="waitTime">The wait time.</param> /// <param name="waitTime">The wait time.</param>
/// <param name="ct">The cancellation token.</param> /// <param name="ct">The cancellation token.</param>
public void Wait(TimeSpan waitTime, CancellationToken ct = default) => public void Wait(TimeSpan waitTime, CancellationToken ct = default) =>
WaitUntil(DateTime.UtcNow.Add(waitTime), ct); this.WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
} }
} }
#endif #endif

View File

@ -1,10 +1,10 @@
#if !NETSTANDARD1_3 
namespace Unosquare.Swan.Components
{ #if !NETSTANDARD1_3
using System; using System;
using System.Threading; using System.Threading;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary> /// <summary>
/// Provides a Manual Reset Event factory with a unified API. /// Provides a Manual Reset Event factory with a unified API.
/// </summary> /// </summary>
@ -53,8 +53,7 @@ namespace Unosquare.Swan.Components
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static class WaitEventFactory public static class WaitEventFactory {
{
#region Factory Methods #region Factory Methods
/// <summary> /// <summary>
@ -62,14 +61,14 @@ namespace Unosquare.Swan.Components
/// </summary> /// </summary>
/// <param name="isCompleted">if initially set to completed. Generally true.</param> /// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <returns>The Wait Event.</returns> /// <returns>The Wait Event.</returns>
public static IWaitEvent Create(bool isCompleted) => new WaitEvent(isCompleted); public static IWaitEvent Create(Boolean isCompleted) => new WaitEvent(isCompleted);
/// <summary> /// <summary>
/// Creates a Wait Event backed by a ManualResetEventSlim. /// Creates a Wait Event backed by a ManualResetEventSlim.
/// </summary> /// </summary>
/// <param name="isCompleted">if initially set to completed. Generally true.</param> /// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <returns>The Wait Event.</returns> /// <returns>The Wait Event.</returns>
public static IWaitEvent CreateSlim(bool isCompleted) => new WaitEventSlim(isCompleted); public static IWaitEvent CreateSlim(Boolean isCompleted) => new WaitEventSlim(isCompleted);
/// <summary> /// <summary>
/// Creates a Wait Event backed by a ManualResetEventSlim. /// Creates a Wait Event backed by a ManualResetEventSlim.
@ -77,7 +76,7 @@ namespace Unosquare.Swan.Components
/// <param name="isCompleted">if initially set to completed. Generally true.</param> /// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param> /// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param>
/// <returns>The Wait Event.</returns> /// <returns>The Wait Event.</returns>
public static IWaitEvent Create(bool isCompleted, bool useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted); public static IWaitEvent Create(Boolean isCompleted, Boolean useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
#endregion #endregion
@ -86,134 +85,109 @@ namespace Unosquare.Swan.Components
/// <summary> /// <summary>
/// Defines a WaitEvent backed by a ManualResetEvent. /// Defines a WaitEvent backed by a ManualResetEvent.
/// </summary> /// </summary>
private class WaitEvent : IWaitEvent private class WaitEvent : IWaitEvent {
{
private ManualResetEvent _event; private ManualResetEvent _event;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WaitEvent"/> class. /// Initializes a new instance of the <see cref="WaitEvent"/> class.
/// </summary> /// </summary>
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param> /// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
public WaitEvent(bool isCompleted) public WaitEvent(Boolean isCompleted) => this._event = new ManualResetEvent(isCompleted);
{
_event = new ManualResetEvent(isCompleted); /// <inheritdoc />
public Boolean IsDisposed {
get; private set;
} }
/// <inheritdoc /> /// <inheritdoc />
public bool IsDisposed { get; private set; } public Boolean IsValid => this.IsDisposed || this._event == null
? false
: this._event?.SafeWaitHandle?.IsClosed ?? true ? false : !(this._event?.SafeWaitHandle?.IsInvalid ?? true);
/// <inheritdoc /> /// <inheritdoc />
public bool IsValid public Boolean IsCompleted => this.IsValid == false ? true : this._event?.WaitOne(0) ?? true;
{
get
{
if (IsDisposed || _event == null)
return false;
if (_event?.SafeWaitHandle?.IsClosed ?? true) /// <inheritdoc />
return false; public Boolean IsInProgress => !this.IsCompleted;
return !(_event?.SafeWaitHandle?.IsInvalid ?? true); /// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
} }
this.IsDisposed = true;
_ = this._event?.Set();
this._event?.Dispose();
this._event = null;
} }
/// <inheritdoc /> /// <inheritdoc />
public bool IsCompleted public void Wait() => this._event?.WaitOne();
{
get
{
if (IsValid == false)
return true;
return _event?.WaitOne(0) ?? true;
}
}
/// <inheritdoc /> /// <inheritdoc />
public bool IsInProgress => !IsCompleted; public Boolean Wait(TimeSpan timeout) => this._event?.WaitOne(timeout) ?? true;
/// <inheritdoc />
public void Begin() => _event?.Reset();
/// <inheritdoc />
public void Complete() => _event?.Set();
/// <inheritdoc />
void IDisposable.Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
_event?.Set();
_event?.Dispose();
_event = null;
}
/// <inheritdoc />
public void Wait() => _event?.WaitOne();
/// <inheritdoc />
public bool Wait(TimeSpan timeout) => _event?.WaitOne(timeout) ?? true;
} }
/// <summary> /// <summary>
/// Defines a WaitEvent backed by a ManualResetEventSlim. /// Defines a WaitEvent backed by a ManualResetEventSlim.
/// </summary> /// </summary>
private class WaitEventSlim : IWaitEvent private class WaitEventSlim : IWaitEvent {
{
private ManualResetEventSlim _event; private ManualResetEventSlim _event;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WaitEventSlim"/> class. /// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
/// </summary> /// </summary>
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param> /// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
public WaitEventSlim(bool isCompleted) public WaitEventSlim(Boolean isCompleted) => this._event = new ManualResetEventSlim(isCompleted);
{
_event = new ManualResetEventSlim(isCompleted); /// <inheritdoc />
public Boolean IsDisposed {
get; private set;
} }
/// <inheritdoc /> /// <inheritdoc />
public bool IsDisposed { get; private set; } public Boolean IsValid => this.IsDisposed || this._event?.WaitHandle?.SafeWaitHandle == null
? false
: !this._event.WaitHandle.SafeWaitHandle.IsClosed && !this._event.WaitHandle.SafeWaitHandle.IsInvalid;
/// <inheritdoc /> /// <inheritdoc />
public bool IsValid public Boolean IsCompleted => this.IsValid == false || this._event.IsSet;
{
get
{
if (IsDisposed || _event?.WaitHandle?.SafeWaitHandle == null) return false;
return !_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid; /// <inheritdoc />
public Boolean IsInProgress => !this.IsCompleted;
/// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
} }
this.IsDisposed = true;
this._event?.Set();
this._event?.Dispose();
this._event = null;
} }
/// <inheritdoc /> /// <inheritdoc />
public bool IsCompleted => IsValid == false || _event.IsSet; public void Wait() => this._event?.Wait();
/// <inheritdoc /> /// <inheritdoc />
public bool IsInProgress => !IsCompleted; public Boolean Wait(TimeSpan timeout) => this._event?.Wait(timeout) ?? true;
/// <inheritdoc />
public void Begin() => _event?.Reset();
/// <inheritdoc />
public void Complete() => _event?.Set();
/// <inheritdoc />
void IDisposable.Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
_event?.Set();
_event?.Dispose();
_event = null;
}
/// <inheritdoc />
public void Wait() => _event?.Wait();
/// <inheritdoc />
public bool Wait(TimeSpan timeout) => _event?.Wait(timeout) ?? true;
} }
#endregion #endregion

View File

@ -1,15 +1,13 @@
namespace Unosquare.Swan using System;
{
using System;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Represents a struct of DateTimeSpan to compare dates and get in /// Represents a struct of DateTimeSpan to compare dates and get in
/// separate fields the amount of time between those dates. /// separate fields the amount of time between those dates.
/// ///
/// Based on https://stackoverflow.com/a/9216404/1096693. /// Based on https://stackoverflow.com/a/9216404/1096693.
/// </summary> /// </summary>
public struct DateTimeSpan public struct DateTimeSpan {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct. /// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
/// </summary> /// </summary>
@ -20,15 +18,14 @@
/// <param name="minutes">The minutes.</param> /// <param name="minutes">The minutes.</param>
/// <param name="seconds">The seconds.</param> /// <param name="seconds">The seconds.</param>
/// <param name="milliseconds">The milliseconds.</param> /// <param name="milliseconds">The milliseconds.</param>
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) public DateTimeSpan(Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds) {
{ this.Years = years;
Years = years; this.Months = months;
Months = months; this.Days = days;
Days = days; this.Hours = hours;
Hours = hours; this.Minutes = minutes;
Minutes = minutes; this.Seconds = seconds;
Seconds = seconds; this.Milliseconds = milliseconds;
Milliseconds = milliseconds;
} }
/// <summary> /// <summary>
@ -37,7 +34,9 @@
/// <value> /// <value>
/// The years. /// The years.
/// </value> /// </value>
public int Years { get; } public Int32 Years {
get;
}
/// <summary> /// <summary>
/// Gets the months. /// Gets the months.
@ -45,7 +44,9 @@
/// <value> /// <value>
/// The months. /// The months.
/// </value> /// </value>
public int Months { get; } public Int32 Months {
get;
}
/// <summary> /// <summary>
/// Gets the days. /// Gets the days.
@ -53,7 +54,9 @@
/// <value> /// <value>
/// The days. /// The days.
/// </value> /// </value>
public int Days { get; } public Int32 Days {
get;
}
/// <summary> /// <summary>
/// Gets the hours. /// Gets the hours.
@ -61,7 +64,9 @@
/// <value> /// <value>
/// The hours. /// The hours.
/// </value> /// </value>
public int Hours { get; } public Int32 Hours {
get;
}
/// <summary> /// <summary>
/// Gets the minutes. /// Gets the minutes.
@ -69,7 +74,9 @@
/// <value> /// <value>
/// The minutes. /// The minutes.
/// </value> /// </value>
public int Minutes { get; } public Int32 Minutes {
get;
}
/// <summary> /// <summary>
/// Gets the seconds. /// Gets the seconds.
@ -77,7 +84,9 @@
/// <value> /// <value>
/// The seconds. /// The seconds.
/// </value> /// </value>
public int Seconds { get; } public Int32 Seconds {
get;
}
/// <summary> /// <summary>
/// Gets the milliseconds. /// Gets the milliseconds.
@ -85,62 +94,54 @@
/// <value> /// <value>
/// The milliseconds. /// The milliseconds.
/// </value> /// </value>
public int Milliseconds { get; } public Int32 Milliseconds {
get;
}
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
{ if(date2 < date1) {
if (date2 < date1) DateTime sub = date1;
{
var sub = date1;
date1 = date2; date1 = date2;
date2 = sub; date2 = sub;
} }
var current = date1; DateTime current = date1;
var years = 0; Int32 years = 0;
var months = 0; Int32 months = 0;
var days = 0; Int32 days = 0;
var phase = Phase.Years; Phase phase = Phase.Years;
var span = new DateTimeSpan(); DateTimeSpan span = new DateTimeSpan();
var officialDay = current.Day; Int32 officialDay = current.Day;
while (phase != Phase.Done) while(phase != Phase.Done) {
{ switch(phase) {
switch (phase)
{
case Phase.Years: case Phase.Years:
if (current.AddYears(years + 1) > date2) if(current.AddYears(years + 1) > date2) {
{
phase = Phase.Months; phase = Phase.Months;
current = current.AddYears(years); current = current.AddYears(years);
} } else {
else
{
years++; years++;
} }
break; break;
case Phase.Months: case Phase.Months:
if (current.AddMonths(months + 1) > date2) if(current.AddMonths(months + 1) > date2) {
{
phase = Phase.Days; phase = Phase.Days;
current = current.AddMonths(months); current = current.AddMonths(months);
if(current.Day < officialDay && if(current.Day < officialDay &&
officialDay <= DateTime.DaysInMonth(current.Year, current.Month)) officialDay <= DateTime.DaysInMonth(current.Year, current.Month)) {
current = current.AddDays(officialDay - current.Day); current = current.AddDays(officialDay - current.Day);
} }
else } else {
{
months++; months++;
} }
break; break;
case Phase.Days: case Phase.Days:
if (current.AddDays(days + 1) > date2) if(current.AddDays(days + 1) > date2) {
{
current = current.AddDays(days); current = current.AddDays(days);
var timespan = date2 - current; TimeSpan timespan = date2 - current;
span = new DateTimeSpan( span = new DateTimeSpan(
years, years,
months, months,
@ -150,9 +151,7 @@
timespan.Seconds, timespan.Seconds,
timespan.Milliseconds); timespan.Milliseconds);
phase = Phase.Done; phase = Phase.Done;
} } else {
else
{
days++; days++;
} }
@ -163,8 +162,7 @@
return span; return span;
} }
private enum Phase private enum Phase {
{
Years, Years,
Months, Months,
Days, Days,

View File

@ -1,16 +1,14 @@
namespace Unosquare.Swan using Unosquare.Swan.Reflection;
{
using Reflection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Contains useful constants and definitions. /// Contains useful constants and definitions.
/// </summary> /// </summary>
public static partial class Definitions public static partial class Definitions {
{
#region Main Dictionary Definition #region Main Dictionary Definition
/// <summary> /// <summary>
@ -21,39 +19,39 @@
{ {
// Non-Nullables // Non-Nullables
{typeof(DateTime), new ExtendedTypeInfo<DateTime>()}, {typeof(DateTime), new ExtendedTypeInfo<DateTime>()},
{typeof(byte), new ExtendedTypeInfo<byte>()}, {typeof(Byte), new ExtendedTypeInfo<Byte>()},
{typeof(sbyte), new ExtendedTypeInfo<sbyte>()}, {typeof(SByte), new ExtendedTypeInfo<SByte>()},
{typeof(int), new ExtendedTypeInfo<int>()}, {typeof(Int32), new ExtendedTypeInfo<Int32>()},
{typeof(uint), new ExtendedTypeInfo<uint>()}, {typeof(UInt32), new ExtendedTypeInfo<UInt32>()},
{typeof(short), new ExtendedTypeInfo<short>()}, {typeof(Int16), new ExtendedTypeInfo<Int16>()},
{typeof(ushort), new ExtendedTypeInfo<ushort>()}, {typeof(UInt16), new ExtendedTypeInfo<UInt16>()},
{typeof(long), new ExtendedTypeInfo<long>()}, {typeof(Int64), new ExtendedTypeInfo<Int64>()},
{typeof(ulong), new ExtendedTypeInfo<ulong>()}, {typeof(UInt64), new ExtendedTypeInfo<UInt64>()},
{typeof(float), new ExtendedTypeInfo<float>()}, {typeof(Single), new ExtendedTypeInfo<Single>()},
{typeof(double), new ExtendedTypeInfo<double>()}, {typeof(Double), new ExtendedTypeInfo<Double>()},
{typeof(char), new ExtendedTypeInfo<char>()}, {typeof(Char), new ExtendedTypeInfo<Char>()},
{typeof(bool), new ExtendedTypeInfo<bool>()}, {typeof(Boolean), new ExtendedTypeInfo<Boolean>()},
{typeof(decimal), new ExtendedTypeInfo<decimal>()}, {typeof(Decimal), new ExtendedTypeInfo<Decimal>()},
{typeof(Guid), new ExtendedTypeInfo<Guid>()}, {typeof(Guid), new ExtendedTypeInfo<Guid>()},
// Strings is also considered a basic type (it's the only basic reference type) // Strings is also considered a basic type (it's the only basic reference type)
{typeof(string), new ExtendedTypeInfo<string>()}, {typeof(String), new ExtendedTypeInfo<String>()},
// Nullables // Nullables
{typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()}, {typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()},
{typeof(byte?), new ExtendedTypeInfo<byte?>()}, {typeof(Byte?), new ExtendedTypeInfo<Byte?>()},
{typeof(sbyte?), new ExtendedTypeInfo<sbyte?>()}, {typeof(SByte?), new ExtendedTypeInfo<SByte?>()},
{typeof(int?), new ExtendedTypeInfo<int?>()}, {typeof(Int32?), new ExtendedTypeInfo<Int32?>()},
{typeof(uint?), new ExtendedTypeInfo<uint?>()}, {typeof(UInt32?), new ExtendedTypeInfo<UInt32?>()},
{typeof(short?), new ExtendedTypeInfo<short?>()}, {typeof(Int16?), new ExtendedTypeInfo<Int16?>()},
{typeof(ushort?), new ExtendedTypeInfo<ushort?>()}, {typeof(UInt16?), new ExtendedTypeInfo<UInt16?>()},
{typeof(long?), new ExtendedTypeInfo<long?>()}, {typeof(Int64?), new ExtendedTypeInfo<Int64?>()},
{typeof(ulong?), new ExtendedTypeInfo<ulong?>()}, {typeof(UInt64?), new ExtendedTypeInfo<UInt64?>()},
{typeof(float?), new ExtendedTypeInfo<float?>()}, {typeof(Single?), new ExtendedTypeInfo<Single?>()},
{typeof(double?), new ExtendedTypeInfo<double?>()}, {typeof(Double?), new ExtendedTypeInfo<Double?>()},
{typeof(char?), new ExtendedTypeInfo<char?>()}, {typeof(Char?), new ExtendedTypeInfo<Char?>()},
{typeof(bool?), new ExtendedTypeInfo<bool?>()}, {typeof(Boolean?), new ExtendedTypeInfo<Boolean?>()},
{typeof(decimal?), new ExtendedTypeInfo<decimal?>()}, {typeof(Decimal?), new ExtendedTypeInfo<Decimal?>()},
{typeof(Guid?), new ExtendedTypeInfo<Guid?>()}, {typeof(Guid?), new ExtendedTypeInfo<Guid?>()},
// Additional Types // Additional Types
@ -79,7 +77,9 @@
/// <value> /// <value>
/// All numeric types. /// All numeric types.
/// </value> /// </value>
public static List<Type> AllNumericTypes { get; } = new List<Type>( public static List<Type> AllNumericTypes {
get;
} = new List<Type>(
BasicTypesInfo BasicTypesInfo
.Where(kvp => kvp.Value.IsNumeric) .Where(kvp => kvp.Value.IsNumeric)
.Select(kvp => kvp.Key).ToArray()); .Select(kvp => kvp.Key).ToArray());
@ -91,7 +91,9 @@
/// <value> /// <value>
/// All numeric value types. /// All numeric value types.
/// </value> /// </value>
public static List<Type> AllNumericValueTypes { get; } = new List<Type>( public static List<Type> AllNumericValueTypes {
get;
} = new List<Type>(
BasicTypesInfo BasicTypesInfo
.Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false) .Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false)
.Select(kvp => kvp.Key).ToArray()); .Select(kvp => kvp.Key).ToArray());
@ -102,7 +104,9 @@
/// <value> /// <value>
/// All basic value types. /// All basic value types.
/// </value> /// </value>
public static List<Type> AllBasicValueTypes { get; } = new List<Type>( public static List<Type> AllBasicValueTypes {
get;
} = new List<Type>(
BasicTypesInfo BasicTypesInfo
.Where(kvp => kvp.Value.IsValueType) .Where(kvp => kvp.Value.IsValueType)
.Select(kvp => kvp.Key).ToArray()); .Select(kvp => kvp.Key).ToArray());
@ -113,9 +117,11 @@
/// <value> /// <value>
/// All basic value and string types. /// All basic value and string types.
/// </value> /// </value>
public static List<Type> AllBasicValueAndStringTypes { get; } = new List<Type>( public static List<Type> AllBasicValueAndStringTypes {
get;
} = new List<Type>(
BasicTypesInfo BasicTypesInfo
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(string)) .Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(String))
.Select(kvp => kvp.Key).ToArray()); .Select(kvp => kvp.Key).ToArray());
/// <summary> /// <summary>
@ -124,7 +130,9 @@
/// <value> /// <value>
/// All basic nullable value types. /// All basic nullable value types.
/// </value> /// </value>
public static List<Type> AllBasicNullableValueTypes { get; } = new List<Type>( public static List<Type> AllBasicNullableValueTypes {
get;
} = new List<Type>(
BasicTypesInfo BasicTypesInfo
.Where(kvp => kvp.Value.IsNullableValueType) .Where(kvp => kvp.Value.IsNullableValueType)
.Select(kvp => kvp.Key).ToArray()); .Select(kvp => kvp.Key).ToArray());

View File

@ -1,12 +1,11 @@
namespace Unosquare.Swan using System;
{
using System.Text; using System.Text;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Contains useful constants and definitions. /// Contains useful constants and definitions.
/// </summary> /// </summary>
public static partial class Definitions public static partial class Definitions {
{
/// <summary> /// <summary>
/// The MS Windows codepage 1252 encoding used in some legacy scenarios /// The MS Windows codepage 1252 encoding used in some legacy scenarios
/// such as default CSV text encoding from Excel. /// such as default CSV text encoding from Excel.
@ -22,15 +21,11 @@
/// <summary> /// <summary>
/// Initializes the <see cref="Definitions"/> class. /// Initializes the <see cref="Definitions"/> class.
/// </summary> /// </summary>
static Definitions() static Definitions() {
{ CurrentAnsiEncoding = Encoding.GetEncoding(default(Int32));
CurrentAnsiEncoding = Encoding.GetEncoding(default(int)); try {
try
{
Windows1252Encoding = Encoding.GetEncoding(1252); Windows1252Encoding = Encoding.GetEncoding(1252);
} } catch {
catch
{
// ignore, the codepage is not available use default // ignore, the codepage is not available use default
Windows1252Encoding = CurrentAnsiEncoding; Windows1252Encoding = CurrentAnsiEncoding;
} }

View File

@ -1,10 +1,8 @@
namespace Unosquare.Swan namespace Unosquare.Swan {
{
/// <summary> /// <summary>
/// Enumeration of Operating Systems. /// Enumeration of Operating Systems.
/// </summary> /// </summary>
public enum OperatingSystem public enum OperatingSystem {
{
/// <summary> /// <summary>
/// Unknown OS /// Unknown OS
/// </summary> /// </summary>
@ -29,8 +27,7 @@
/// <summary> /// <summary>
/// Enumerates the different Application Worker States. /// Enumerates the different Application Worker States.
/// </summary> /// </summary>
public enum AppWorkerState public enum AppWorkerState {
{
/// <summary> /// <summary>
/// The stopped /// The stopped
/// </summary> /// </summary>
@ -45,8 +42,7 @@
/// <summary> /// <summary>
/// Defines Endianness, big or little. /// Defines Endianness, big or little.
/// </summary> /// </summary>
public enum Endianness public enum Endianness {
{
/// <summary> /// <summary>
/// In big endian, you store the most significant byte in the smallest address. /// In big endian, you store the most significant byte in the smallest address.
/// </summary> /// </summary>

View File

@ -1,15 +1,13 @@
namespace Unosquare.Swan using System;
{
using System;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Event arguments representing the message that is logged /// Event arguments representing the message that is logged
/// on to the terminal. Use the properties to forward the data to /// on to the terminal. Use the properties to forward the data to
/// your logger of choice. /// your logger of choice.
/// </summary> /// </summary>
/// <seealso cref="System.EventArgs" /> /// <seealso cref="System.EventArgs" />
public class LogMessageReceivedEventArgs : EventArgs public class LogMessageReceivedEventArgs : EventArgs {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class. /// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
/// </summary> /// </summary>
@ -23,25 +21,24 @@
/// <param name="callerFilePath">The caller file path.</param> /// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param> /// <param name="callerLineNumber">The caller line number.</param>
public LogMessageReceivedEventArgs( public LogMessageReceivedEventArgs(
ulong sequence, UInt64 sequence,
LogMessageType messageType, LogMessageType messageType,
DateTime utcDate, DateTime utcDate,
string source, String source,
string message, String message,
object extendedData, Object extendedData,
string callerMemberName, String callerMemberName,
string callerFilePath, String callerFilePath,
int callerLineNumber) Int32 callerLineNumber) {
{ this.Sequence = sequence;
Sequence = sequence; this.MessageType = messageType;
MessageType = messageType; this.UtcDate = utcDate;
UtcDate = utcDate; this.Source = source;
Source = source; this.Message = message;
Message = message; this.CallerMemberName = callerMemberName;
CallerMemberName = callerMemberName; this.CallerFilePath = callerFilePath;
CallerFilePath = callerFilePath; this.CallerLineNumber = callerLineNumber;
CallerLineNumber = callerLineNumber; this.ExtendedData = extendedData;
ExtendedData = extendedData;
} }
/// <summary> /// <summary>
@ -50,7 +47,9 @@
/// <value> /// <value>
/// The sequence. /// The sequence.
/// </value> /// </value>
public ulong Sequence { get; } public UInt64 Sequence {
get;
}
/// <summary> /// <summary>
/// Gets the type of the message. /// Gets the type of the message.
@ -59,7 +58,9 @@
/// <value> /// <value>
/// The type of the message. /// The type of the message.
/// </value> /// </value>
public LogMessageType MessageType { get; } public LogMessageType MessageType {
get;
}
/// <summary> /// <summary>
/// Gets the UTC date at which the event at which the message was logged. /// Gets the UTC date at which the event at which the message was logged.
@ -67,7 +68,9 @@
/// <value> /// <value>
/// The UTC date. /// The UTC date.
/// </value> /// </value>
public DateTime UtcDate { get; } public DateTime UtcDate {
get;
}
/// <summary> /// <summary>
/// Gets the name of the source where the logging message /// Gets the name of the source where the logging message
@ -76,7 +79,9 @@
/// <value> /// <value>
/// The source. /// The source.
/// </value> /// </value>
public string Source { get; } public String Source {
get;
}
/// <summary> /// <summary>
/// Gets the body of the message. /// Gets the body of the message.
@ -84,7 +89,9 @@
/// <value> /// <value>
/// The message. /// The message.
/// </value> /// </value>
public string Message { get; } public String Message {
get;
}
/// <summary> /// <summary>
/// Gets the name of the caller member. /// Gets the name of the caller member.
@ -92,7 +99,9 @@
/// <value> /// <value>
/// The name of the caller member. /// The name of the caller member.
/// </value> /// </value>
public string CallerMemberName { get; } public String CallerMemberName {
get;
}
/// <summary> /// <summary>
/// Gets the caller file path. /// Gets the caller file path.
@ -100,7 +109,9 @@
/// <value> /// <value>
/// The caller file path. /// The caller file path.
/// </value> /// </value>
public string CallerFilePath { get; } public String CallerFilePath {
get;
}
/// <summary> /// <summary>
/// Gets the caller line number. /// Gets the caller line number.
@ -108,7 +119,9 @@
/// <value> /// <value>
/// The caller line number. /// The caller line number.
/// </value> /// </value>
public int CallerLineNumber { get; } public Int32 CallerLineNumber {
get;
}
/// <summary> /// <summary>
/// Gets an object representing extended data. /// Gets an object representing extended data.
@ -117,7 +130,9 @@
/// <value> /// <value>
/// The extended data. /// The extended data.
/// </value> /// </value>
public object ExtendedData { get; } public Object ExtendedData {
get;
}
/// <summary> /// <summary>
/// Gets the Extended Data properties cast as an Exception (if possible) /// Gets the Extended Data properties cast as an Exception (if possible)
@ -126,7 +141,7 @@
/// <value> /// <value>
/// The exception. /// The exception.
/// </value> /// </value>
public Exception Exception => ExtendedData as Exception; public Exception Exception => this.ExtendedData as Exception;
} }
/// <summary> /// <summary>
@ -135,8 +150,7 @@
/// event handler to prevent the terminal from displaying the message. /// event handler to prevent the terminal from displaying the message.
/// </summary> /// </summary>
/// <seealso cref="LogMessageReceivedEventArgs" /> /// <seealso cref="LogMessageReceivedEventArgs" />
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class. /// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class.
/// </summary> /// </summary>
@ -151,10 +165,7 @@
data.ExtendedData, data.ExtendedData,
data.CallerMemberName, data.CallerMemberName,
data.CallerFilePath, data.CallerFilePath,
data.CallerLineNumber) data.CallerLineNumber) => this.CancelOutput = false;
{
CancelOutput = false;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the displaying of the /// Gets or sets a value indicating whether the displaying of the
@ -163,6 +174,8 @@
/// <value> /// <value>
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>. /// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool CancelOutput { get; set; } public Boolean CancelOutput {
get; set;
}
} }
} }

View File

@ -1,6 +1,4 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -9,11 +7,11 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Provides various extension methods for byte arrays and streams. /// Provides various extension methods for byte arrays and streams.
/// </summary> /// </summary>
public static class ByteArrayExtensions public static class ByteArrayExtensions {
{
/// <summary> /// <summary>
/// Converts an array of bytes to its lower-case, hexadecimal representation. /// Converts an array of bytes to its lower-case, hexadecimal representation.
/// </summary> /// </summary>
@ -23,7 +21,7 @@
/// The specified string instance; no actual conversion is performed. /// The specified string instance; no actual conversion is performed.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">bytes.</exception> /// <exception cref="ArgumentNullException">bytes.</exception>
public static string ToLowerHex(this byte[] bytes, bool addPrefix = false) public static String ToLowerHex(this Byte[] bytes, Boolean addPrefix = false)
=> ToHex(bytes, addPrefix, "x2"); => ToHex(bytes, addPrefix, "x2");
/// <summary> /// <summary>
@ -35,7 +33,7 @@
/// The specified string instance; no actual conversion is performed. /// The specified string instance; no actual conversion is performed.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">bytes.</exception> /// <exception cref="ArgumentNullException">bytes.</exception>
public static string ToUpperHex(this byte[] bytes, bool addPrefix = false) public static String ToUpperHex(this Byte[] bytes, Boolean addPrefix = false)
=> ToHex(bytes, addPrefix, "X2"); => ToHex(bytes, addPrefix, "X2");
/// <summary> /// <summary>
@ -47,14 +45,14 @@
/// A string of hexadecimal pairs separated by hyphens, where each pair represents /// A string of hexadecimal pairs separated by hyphens, where each pair represents
/// the corresponding element in value; for example, "7F-2C-4A-00". /// the corresponding element in value; for example, "7F-2C-4A-00".
/// </returns> /// </returns>
public static string ToDashedHex(this byte[] bytes) => BitConverter.ToString(bytes); public static String ToDashedHex(this Byte[] bytes) => BitConverter.ToString(bytes);
/// <summary> /// <summary>
/// Converts an array of bytes to a base-64 encoded string. /// Converts an array of bytes to a base-64 encoded string.
/// </summary> /// </summary>
/// <param name="bytes">The bytes.</param> /// <param name="bytes">The bytes.</param>
/// <returns>A <see cref="System.String" /> converted from an array of bytes.</returns> /// <returns>A <see cref="System.String" /> converted from an array of bytes.</returns>
public static string ToBase64(this byte[] bytes) => Convert.ToBase64String(bytes); public static String ToBase64(this Byte[] bytes) => Convert.ToBase64String(bytes);
/// <summary> /// <summary>
/// Converts a set of hexadecimal characters (uppercase or lowercase) /// Converts a set of hexadecimal characters (uppercase or lowercase)
@ -66,10 +64,10 @@
/// A byte array containing the results of encoding the specified set of characters. /// A byte array containing the results of encoding the specified set of characters.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">hex.</exception> /// <exception cref="ArgumentNullException">hex.</exception>
public static byte[] ConvertHexadecimalToBytes(this string hex) public static Byte[] ConvertHexadecimalToBytes(this String hex) {
{ if(String.IsNullOrWhiteSpace(hex)) {
if (string.IsNullOrWhiteSpace(hex))
throw new ArgumentNullException(nameof(hex)); throw new ArgumentNullException(nameof(hex));
}
return Enumerable return Enumerable
.Range(0, hex.Length / 2) .Range(0, hex.Length / 2)
@ -86,7 +84,7 @@
/// <returns> /// <returns>
/// Bit value at the given offset. /// Bit value at the given offset.
/// </returns> /// </returns>
public static byte GetBitValueAt(this byte b, byte offset, byte length = 1) => (byte)((b >> offset) & ~(0xff << length)); public static Byte GetBitValueAt(this Byte b, Byte offset, Byte length = 1) => (Byte)((b >> offset) & ~(0xff << length));
/// <summary> /// <summary>
/// Sets the bit value at the given offset. /// Sets the bit value at the given offset.
@ -96,12 +94,11 @@
/// <param name="length">The length.</param> /// <param name="length">The length.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>Bit value at the given offset.</returns> /// <returns>Bit value at the given offset.</returns>
public static byte SetBitValueAt(this byte b, byte offset, byte length, byte value) public static Byte SetBitValueAt(this Byte b, Byte offset, Byte length, Byte value) {
{ Int32 mask = ~(0xff << length);
var mask = ~(0xff << length); Byte valueAt = (Byte)(value & mask);
var valueAt = (byte)(value & mask);
return (byte)((valueAt << offset) | (b & ~(mask << offset))); return (Byte)((valueAt << offset) | (b & ~(mask << offset)));
} }
/// <summary> /// <summary>
@ -111,7 +108,7 @@
/// <param name="offset">The offset.</param> /// <param name="offset">The offset.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>Bit value at the given offset.</returns> /// <returns>Bit value at the given offset.</returns>
public static byte SetBitValueAt(this byte b, byte offset, byte value) => b.SetBitValueAt(offset, 1, value); public static Byte SetBitValueAt(this Byte b, Byte offset, Byte value) => b.SetBitValueAt(offset, 1, value);
/// <summary> /// <summary>
/// Splits a byte array delimited by the specified sequence of bytes. /// Splits a byte array delimited by the specified sequence of bytes.
@ -130,32 +127,29 @@
/// or /// or
/// sequence. /// sequence.
/// </exception> /// </exception>
public static List<byte[]> Split(this byte[] buffer, int offset, params byte[] sequence) public static List<Byte[]> Split(this Byte[] buffer, Int32 offset, params Byte[] sequence) {
{ if(buffer == null) {
if (buffer == null)
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
if (sequence == null) if(sequence == null) {
throw new ArgumentNullException(nameof(sequence)); throw new ArgumentNullException(nameof(sequence));
}
var seqOffset = offset.Clamp(0, buffer.Length - 1); Int32 seqOffset = offset.Clamp(0, buffer.Length - 1);
var result = new List<byte[]>(); List<Byte[]> result = new List<Byte[]>();
while (seqOffset < buffer.Length) while(seqOffset < buffer.Length) {
{ Int32 separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
var separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
if (separatorStartIndex >= 0) if(separatorStartIndex >= 0) {
{ Byte[] item = new Byte[separatorStartIndex - seqOffset + sequence.Length];
var item = new byte[separatorStartIndex - seqOffset + sequence.Length];
Array.Copy(buffer, seqOffset, item, 0, item.Length); Array.Copy(buffer, seqOffset, item, 0, item.Length);
result.Add(item); result.Add(item);
seqOffset += item.Length; seqOffset += item.Length;
} } else {
else Byte[] item = new Byte[buffer.Length - seqOffset];
{
var item = new byte[buffer.Length - seqOffset];
Array.Copy(buffer, seqOffset, item, 0, item.Length); Array.Copy(buffer, seqOffset, item, 0, item.Length);
result.Add(item); result.Add(item);
break; break;
@ -170,12 +164,12 @@
/// </summary> /// </summary>
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns> /// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] DeepClone(this byte[] buffer) public static Byte[] DeepClone(this Byte[] buffer) {
{ if(buffer == null) {
if (buffer == null)
return null; return null;
}
var result = new byte[buffer.Length]; Byte[] result = new Byte[buffer.Length];
Array.Copy(buffer, result, buffer.Length); Array.Copy(buffer, result, buffer.Length);
return result; return result;
} }
@ -189,15 +183,16 @@
/// A new trimmed byte array. /// A new trimmed byte array.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception> /// <exception cref="ArgumentNullException">buffer.</exception>
public static byte[] TrimStart(this byte[] buffer, params byte[] sequence) public static Byte[] TrimStart(this Byte[] buffer, params Byte[] sequence) {
{ if(buffer == null) {
if (buffer == null)
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
if (buffer.StartsWith(sequence) == false) if(buffer.StartsWith(sequence) == false) {
return buffer.DeepClone(); return buffer.DeepClone();
}
var result = new byte[buffer.Length - sequence.Length]; Byte[] result = new Byte[buffer.Length - sequence.Length];
Array.Copy(buffer, sequence.Length, result, 0, result.Length); Array.Copy(buffer, sequence.Length, result, 0, result.Length);
return result; return result;
} }
@ -211,15 +206,16 @@
/// A byte array containing the results of encoding the specified set of characters. /// A byte array containing the results of encoding the specified set of characters.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception> /// <exception cref="ArgumentNullException">buffer.</exception>
public static byte[] TrimEnd(this byte[] buffer, params byte[] sequence) public static Byte[] TrimEnd(this Byte[] buffer, params Byte[] sequence) {
{ if(buffer == null) {
if (buffer == null)
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
if (buffer.EndsWith(sequence) == false) if(buffer.EndsWith(sequence) == false) {
return buffer.DeepClone(); return buffer.DeepClone();
}
var result = new byte[buffer.Length - sequence.Length]; Byte[] result = new Byte[buffer.Length - sequence.Length];
Array.Copy(buffer, 0, result, 0, result.Length); Array.Copy(buffer, 0, result, 0, result.Length);
return result; return result;
} }
@ -231,9 +227,8 @@
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <param name="sequence">The sequence.</param> /// <param name="sequence">The sequence.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns> /// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] Trim(this byte[] buffer, params byte[] sequence) public static Byte[] Trim(this Byte[] buffer, params Byte[] sequence) {
{ Byte[] trimStart = buffer.TrimStart(sequence);
var trimStart = buffer.TrimStart(sequence);
return trimStart.TrimEnd(sequence); return trimStart.TrimEnd(sequence);
} }
@ -246,12 +241,12 @@
/// True if the specified buffer is ends; otherwise, false. /// True if the specified buffer is ends; otherwise, false.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception> /// <exception cref="ArgumentNullException">buffer.</exception>
public static bool EndsWith(this byte[] buffer, params byte[] sequence) public static Boolean EndsWith(this Byte[] buffer, params Byte[] sequence) {
{ if(buffer == null) {
if (buffer == null)
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
var startIndex = buffer.Length - sequence.Length; Int32 startIndex = buffer.Length - sequence.Length;
return buffer.GetIndexOf(sequence, startIndex) == startIndex; return buffer.GetIndexOf(sequence, startIndex) == startIndex;
} }
@ -261,7 +256,7 @@
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <param name="sequence">The sequence.</param> /// <param name="sequence">The sequence.</param>
/// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns>
public static bool StartsWith(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) == 0; public static Boolean StartsWith(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
/// <summary> /// <summary>
/// Determines whether the buffer contains the specified sequence. /// Determines whether the buffer contains the specified sequence.
@ -271,7 +266,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>. /// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool Contains(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) >= 0; public static Boolean Contains(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
/// <summary> /// <summary>
/// Determines whether the buffer exactly matches, byte by byte the specified sequence. /// Determines whether the buffer exactly matches, byte by byte the specified sequence.
@ -282,13 +277,14 @@
/// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>. /// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception> /// <exception cref="ArgumentNullException">buffer.</exception>
public static bool IsEqualTo(this byte[] buffer, params byte[] sequence) public static Boolean IsEqualTo(this Byte[] buffer, params Byte[] sequence) {
{ if(ReferenceEquals(buffer, sequence)) {
if (ReferenceEquals(buffer, sequence))
return true; return true;
}
if (buffer == null) if(buffer == null) {
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0; return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0;
} }
@ -306,30 +302,37 @@
/// or /// or
/// sequence. /// sequence.
/// </exception> /// </exception>
public static int GetIndexOf(this byte[] buffer, byte[] sequence, int offset = 0) public static Int32 GetIndexOf(this Byte[] buffer, Byte[] sequence, Int32 offset = 0) {
{ if(buffer == null) {
if (buffer == null)
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
if (sequence == null) }
if(sequence == null) {
throw new ArgumentNullException(nameof(sequence)); throw new ArgumentNullException(nameof(sequence));
if (sequence.Length == 0) }
return -1;
if (sequence.Length > buffer.Length)
return -1;
var seqOffset = offset < 0 ? 0 : offset; if(sequence.Length == 0) {
return -1;
}
var matchedCount = 0; if(sequence.Length > buffer.Length) {
for (var i = seqOffset; i < buffer.Length; i++) return -1;
{ }
if (buffer[i] == sequence[matchedCount])
Int32 seqOffset = offset < 0 ? 0 : offset;
Int32 matchedCount = 0;
for(Int32 i = seqOffset; i < buffer.Length; i++) {
if(buffer[i] == sequence[matchedCount]) {
matchedCount++; matchedCount++;
else } else {
matchedCount = 0; matchedCount = 0;
}
if (matchedCount == sequence.Length) if(matchedCount == sequence.Length) {
return i - (matchedCount - 1); return i - (matchedCount - 1);
} }
}
return -1; return -1;
} }
@ -347,13 +350,14 @@
/// or /// or
/// buffer. /// buffer.
/// </exception> /// </exception>
public static MemoryStream Append(this MemoryStream stream, byte[] buffer) public static MemoryStream Append(this MemoryStream stream, Byte[] buffer) {
{ if(stream == null) {
if (stream == null)
throw new ArgumentNullException(nameof(stream)); throw new ArgumentNullException(nameof(stream));
}
if (buffer == null) if(buffer == null) {
throw new ArgumentNullException(nameof(buffer)); throw new ArgumentNullException(nameof(buffer));
}
stream.Write(buffer, 0, buffer.Length); stream.Write(buffer, 0, buffer.Length);
return stream; return stream;
@ -368,7 +372,7 @@
/// Block of bytes to the current stream using data read from a buffer. /// Block of bytes to the current stream using data read from a buffer.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception> /// <exception cref="ArgumentNullException">buffer.</exception>
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte> buffer) => Append(stream, buffer?.ToArray()); public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte> buffer) => Append(stream, buffer?.ToArray());
/// <summary> /// <summary>
/// Appends the Memory Stream with the specified set of buffers. /// Appends the Memory Stream with the specified set of buffers.
@ -379,13 +383,14 @@
/// Block of bytes to the current stream using data read from a buffer. /// Block of bytes to the current stream using data read from a buffer.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">buffers.</exception> /// <exception cref="ArgumentNullException">buffers.</exception>
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte[]> buffers) public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte[]> buffers) {
{ if(buffers == null) {
if (buffers == null)
throw new ArgumentNullException(nameof(buffers)); throw new ArgumentNullException(nameof(buffers));
}
foreach (var buffer in buffers) foreach(Byte[] buffer in buffers) {
Append(stream, buffer); _ = Append(stream, buffer);
}
return stream; return stream;
} }
@ -396,14 +401,14 @@
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns> /// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
public static string ToText(this IEnumerable<byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray()); public static String ToText(this IEnumerable<Byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray());
/// <summary> /// <summary>
/// Converts an array of bytes into text with UTF8 encoding. /// Converts an array of bytes into text with UTF8 encoding.
/// </summary> /// </summary>
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns> /// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
public static string ToText(this IEnumerable<byte> buffer) => buffer.ToText(Encoding.UTF8); public static String ToText(this IEnumerable<Byte> buffer) => buffer.ToText(Encoding.UTF8);
/// <summary> /// <summary>
/// Retrieves a sub-array from the specified <paramref name="array"/>. A sub-array starts at /// Retrieves a sub-array from the specified <paramref name="array"/>. A sub-array starts at
@ -417,28 +422,30 @@
/// An array of T from which to retrieve a sub-array. /// An array of T from which to retrieve a sub-array.
/// </param> /// </param>
/// <param name="startIndex"> /// <param name="startIndex">
/// An <see cref="int"/> that represents the zero-based starting position of /// An <see cref="Int32"/> that represents the zero-based starting position of
/// a sub-array in <paramref name="array"/>. /// a sub-array in <paramref name="array"/>.
/// </param> /// </param>
/// <param name="length"> /// <param name="length">
/// An <see cref="int"/> that represents the number of elements to retrieve. /// An <see cref="Int32"/> that represents the number of elements to retrieve.
/// </param> /// </param>
/// <typeparam name="T"> /// <typeparam name="T">
/// The type of elements in <paramref name="array"/>. /// The type of elements in <paramref name="array"/>.
/// </typeparam> /// </typeparam>
public static T[] SubArray<T>(this T[] array, int startIndex, int length) public static T[] SubArray<T>(this T[] array, Int32 startIndex, Int32 length) {
{ Int32 len;
int len; if(array == null || (len = array.Length) == 0) {
if (array == null || (len = array.Length) == 0)
return new T[0]; return new T[0];
}
if (startIndex < 0 || length <= 0 || startIndex + length > len) if(startIndex < 0 || length <= 0 || startIndex + length > len) {
return new T[0]; return new T[0];
}
if (startIndex == 0 && length == len) if(startIndex == 0 && length == len) {
return array; return array;
}
var subArray = new T[length]; T[] subArray = new T[length];
Array.Copy(array, startIndex, subArray, 0, length); Array.Copy(array, startIndex, subArray, 0, length);
return subArray; return subArray;
@ -456,16 +463,16 @@
/// An array of T from which to retrieve a sub-array. /// An array of T from which to retrieve a sub-array.
/// </param> /// </param>
/// <param name="startIndex"> /// <param name="startIndex">
/// A <see cref="long"/> that represents the zero-based starting position of /// A <see cref="Int64"/> that represents the zero-based starting position of
/// a sub-array in <paramref name="array"/>. /// a sub-array in <paramref name="array"/>.
/// </param> /// </param>
/// <param name="length"> /// <param name="length">
/// A <see cref="long"/> that represents the number of elements to retrieve. /// A <see cref="Int64"/> that represents the number of elements to retrieve.
/// </param> /// </param>
/// <typeparam name="T"> /// <typeparam name="T">
/// The type of elements in <paramref name="array"/>. /// The type of elements in <paramref name="array"/>.
/// </typeparam> /// </typeparam>
public static T[] SubArray<T>(this T[] array, long startIndex, long length) => array.SubArray((int)startIndex, (int)length); public static T[] SubArray<T>(this T[] array, Int64 startIndex, Int64 length) => array.SubArray((Int32)startIndex, (Int32)length);
/// <summary> /// <summary>
/// Reads the bytes asynchronous. /// Reads the bytes asynchronous.
@ -478,31 +485,28 @@
/// A byte array containing the results of encoding the specified set of characters. /// A byte array containing the results of encoding the specified set of characters.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">stream.</exception> /// <exception cref="ArgumentNullException">stream.</exception>
public static async Task<byte[]> ReadBytesAsync(this Stream stream, long length, int bufferLength, CancellationToken ct = default) public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int64 length, Int32 bufferLength, CancellationToken ct = default) {
{ if(stream == null) {
if (stream == null)
throw new ArgumentNullException(nameof(stream)); throw new ArgumentNullException(nameof(stream));
}
using (var dest = new MemoryStream()) using(MemoryStream dest = new MemoryStream()) {
{ try {
try Byte[] buff = new Byte[bufferLength];
{ while(length > 0) {
var buff = new byte[bufferLength]; if(length < bufferLength) {
while (length > 0) bufferLength = (Int32)length;
{ }
if (length < bufferLength)
bufferLength = (int)length;
var nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false); Int32 nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false);
if (nread == 0) if(nread == 0) {
break; break;
}
dest.Write(buff, 0, nread); dest.Write(buff, 0, nread);
length -= nread; length -= nread;
} }
} } catch {
catch
{
// ignored // ignored
} }
@ -520,27 +524,24 @@
/// A byte array containing the results of encoding the specified set of characters. /// A byte array containing the results of encoding the specified set of characters.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">stream.</exception> /// <exception cref="ArgumentNullException">stream.</exception>
public static async Task<byte[]> ReadBytesAsync(this Stream stream, int length, CancellationToken ct = default) public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int32 length, CancellationToken ct = default) {
{ if(stream == null) {
if (stream == null)
throw new ArgumentNullException(nameof(stream)); throw new ArgumentNullException(nameof(stream));
}
var buff = new byte[length]; Byte[] buff = new Byte[length];
var offset = 0; Int32 offset = 0;
try try {
{ while(length > 0) {
while (length > 0) Int32 nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
{ if(nread == 0) {
var nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
if (nread == 0)
break; break;
}
offset += nread; offset += nread;
length -= nread; length -= nread;
} }
} } catch {
catch
{
// ignored // ignored
} }
@ -555,14 +556,15 @@
/// The byte array from conversion. /// The byte array from conversion.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">sbyteArray.</exception> /// <exception cref="ArgumentNullException">sbyteArray.</exception>
public static byte[] ToByteArray(this sbyte[] sbyteArray) public static Byte[] ToByteArray(this SByte[] sbyteArray) {
{ if(sbyteArray == null) {
if (sbyteArray == null)
throw new ArgumentNullException(nameof(sbyteArray)); throw new ArgumentNullException(nameof(sbyteArray));
}
var byteArray = new byte[sbyteArray.Length]; Byte[] byteArray = new Byte[sbyteArray.Length];
for (var index = 0; index < sbyteArray.Length; index++) for(Int32 index = 0; index < sbyteArray.Length; index++) {
byteArray[index] = (byte)sbyteArray[index]; byteArray[index] = (Byte)sbyteArray[index];
}
return byteArray; return byteArray;
} }
@ -575,14 +577,16 @@
/// The sbyte array from conversion. /// The sbyte array from conversion.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">byteArray.</exception> /// <exception cref="ArgumentNullException">byteArray.</exception>
public static sbyte[] ToSByteArray(this byte[] byteArray) public static SByte[] ToSByteArray(this Byte[] byteArray) {
{ if(byteArray == null) {
if (byteArray == null)
throw new ArgumentNullException(nameof(byteArray)); throw new ArgumentNullException(nameof(byteArray));
}
SByte[] sbyteArray = new SByte[byteArray.Length];
for(Int32 index = 0; index < byteArray.Length; index++) {
sbyteArray[index] = (SByte)byteArray[index];
}
var sbyteArray = new sbyte[byteArray.Length];
for (var index = 0; index < byteArray.Length; index++)
sbyteArray[index] = (sbyte)byteArray[index];
return sbyteArray; return sbyteArray;
} }
@ -592,7 +596,7 @@
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
/// <param name="s">The s.</param> /// <param name="s">The s.</param>
/// <returns>The sbyte array from string.</returns> /// <returns>The sbyte array from string.</returns>
public static sbyte[] GetSBytes(this Encoding encoding, string s) public static SByte[] GetSBytes(this Encoding encoding, String s)
=> encoding.GetBytes(s).ToSByteArray(); => encoding.GetBytes(s).ToSByteArray();
/// <summary> /// <summary>
@ -601,7 +605,7 @@
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The string.</returns> /// <returns>The string.</returns>
public static string GetString(this Encoding encoding, sbyte[] data) public static String GetString(this Encoding encoding, SByte[] data)
=> encoding.GetString(data.ToByteArray()); => encoding.GetString(data.ToByteArray());
/// <summary> /// <summary>
@ -620,53 +624,59 @@
/// or /// or
/// target. /// target.
/// </exception> /// </exception>
public static int ReadInput(this Stream sourceStream, ref sbyte[] target, int start, int count) public static Int32 ReadInput(this Stream sourceStream, ref SByte[] target, Int32 start, Int32 count) {
{ if(sourceStream == null) {
if (sourceStream == null)
throw new ArgumentNullException(nameof(sourceStream)); throw new ArgumentNullException(nameof(sourceStream));
}
if (target == null) if(target == null) {
throw new ArgumentNullException(nameof(target)); throw new ArgumentNullException(nameof(target));
}
// Returns 0 bytes if not enough space in target // Returns 0 bytes if not enough space in target
if (target.Length == 0) if(target.Length == 0) {
return 0; return 0;
}
var receiver = new byte[target.Length]; Byte[] receiver = new Byte[target.Length];
var bytesRead = 0; Int32 bytesRead = 0;
var startIndex = start; Int32 startIndex = start;
var bytesToRead = count; Int32 bytesToRead = count;
while (bytesToRead > 0) while(bytesToRead > 0) {
{ Int32 n = sourceStream.Read(receiver, startIndex, bytesToRead);
var n = sourceStream.Read(receiver, startIndex, bytesToRead); if(n == 0) {
if (n == 0)
break; break;
}
bytesRead += n; bytesRead += n;
startIndex += n; startIndex += n;
bytesToRead -= n; bytesToRead -= n;
} }
// Returns -1 if EOF // Returns -1 if EOF
if (bytesRead == 0) if(bytesRead == 0) {
return -1; return -1;
}
for (var i = start; i < start + bytesRead; i++) for(Int32 i = start; i < start + bytesRead; i++) {
target[i] = (sbyte)receiver[i]; target[i] = (SByte)receiver[i];
}
return bytesRead; return bytesRead;
} }
private static string ToHex(byte[] bytes, bool addPrefix, string format) private static String ToHex(Byte[] bytes, Boolean addPrefix, String format) {
{ if(bytes == null) {
if (bytes == null)
throw new ArgumentNullException(nameof(bytes)); throw new ArgumentNullException(nameof(bytes));
}
var sb = new StringBuilder(bytes.Length * 2); StringBuilder sb = new StringBuilder(bytes.Length * 2);
foreach (var item in bytes) foreach(Byte item in bytes) {
sb.Append(item.ToString(format, CultureInfo.InvariantCulture)); _ = sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
}
return $"{(addPrefix ? "0x" : string.Empty)}{sb}"; return $"{(addPrefix ? "0x" : String.Empty)}{sb}";
} }
} }
} }

View File

@ -1,15 +1,13 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Provides various extension methods for dates. /// Provides various extension methods for dates.
/// </summary> /// </summary>
public static class DateExtensions public static class DateExtensions {
{ private static readonly Dictionary<String, Int32> DateRanges = new Dictionary<String, Int32>()
private static readonly Dictionary<string, int> DateRanges = new Dictionary<string, int>()
{ {
{ "minute", 59}, { "minute", 59},
{ "hour", 23}, { "hour", 23},
@ -23,7 +21,7 @@
/// </summary> /// </summary>
/// <param name="date">The date.</param> /// <param name="date">The date.</param>
/// <returns>The concatenation of date.Year, date.Month and date.Day.</returns> /// <returns>The concatenation of date.Year, date.Month and date.Day.</returns>
public static string ToSortableDate(this DateTime date) public static String ToSortableDate(this DateTime date)
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00}"; => $"{date.Year:0000}-{date.Month:00}-{date.Day:00}";
/// <summary> /// <summary>
@ -31,7 +29,7 @@
/// </summary> /// </summary>
/// <param name="date">The date.</param> /// <param name="date">The date.</param>
/// <returns>The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.</returns> /// <returns>The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.</returns>
public static string ToSortableDateTime(this DateTime date) public static String ToSortableDateTime(this DateTime date)
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00} {date.Hour:00}:{date.Minute:00}:{date.Second:00}"; => $"{date.Year:0000}-{date.Month:00}-{date.Day:00} {date.Hour:00}:{date.Minute:00}:{date.Second:00}";
/// <summary> /// <summary>
@ -49,43 +47,44 @@
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// Unable to parse sortable date and time. - sortableDate. /// Unable to parse sortable date and time. - sortableDate.
/// </exception> /// </exception>
public static DateTime ToDateTime(this string sortableDate) public static DateTime ToDateTime(this String sortableDate) {
{ if(String.IsNullOrWhiteSpace(sortableDate)) {
if (string.IsNullOrWhiteSpace(sortableDate))
throw new ArgumentNullException(nameof(sortableDate)); throw new ArgumentNullException(nameof(sortableDate));
}
var hour = 0; Int32 hour = 0;
var minute = 0; Int32 minute = 0;
var second = 0; Int32 second = 0;
var dateTimeParts = sortableDate.Split(' '); String[] dateTimeParts = sortableDate.Split(' ');
try try {
{ if(dateTimeParts.Length != 1 && dateTimeParts.Length != 2) {
if (dateTimeParts.Length != 1 && dateTimeParts.Length != 2)
throw new Exception(); throw new Exception();
}
var dateParts = dateTimeParts[0].Split('-'); String[] dateParts = dateTimeParts[0].Split('-');
if (dateParts.Length != 3) throw new Exception(); if(dateParts.Length != 3) {
throw new Exception();
}
var year = int.Parse(dateParts[0]); Int32 year = Int32.Parse(dateParts[0]);
var month = int.Parse(dateParts[1]); Int32 month = Int32.Parse(dateParts[1]);
var day = int.Parse(dateParts[2]); Int32 day = Int32.Parse(dateParts[2]);
if (dateTimeParts.Length > 1) if(dateTimeParts.Length > 1) {
{ String[] timeParts = dateTimeParts[1].Split(':');
var timeParts = dateTimeParts[1].Split(':'); if(timeParts.Length != 3) {
if (timeParts.Length != 3) throw new Exception(); throw new Exception();
}
hour = int.Parse(timeParts[0]); hour = Int32.Parse(timeParts[0]);
minute = int.Parse(timeParts[1]); minute = Int32.Parse(timeParts[1]);
second = int.Parse(timeParts[2]); second = Int32.Parse(timeParts[2]);
} }
return new DateTime(year, month, day, hour, minute, second); return new DateTime(year, month, day, hour, minute, second);
} } catch(Exception) {
catch (Exception)
{
throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate)); throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate));
} }
} }
@ -110,19 +109,18 @@
/// A new instance of the DateTime structure to the specified datetime and timespan ticks. /// A new instance of the DateTime structure to the specified datetime and timespan ticks.
/// </returns> /// </returns>
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan) public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
=> new DateTime(((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks) * timeSpan.Ticks); => new DateTime((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks * timeSpan.Ticks);
/// <summary> /// <summary>
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC). /// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
/// </summary> /// </summary>
/// <param name="date">The date to convert.</param> /// <param name="date">The date to convert.</param>
/// <returns>Seconds since Unix epoch.</returns> /// <returns>Seconds since Unix epoch.</returns>
public static long ToUnixEpochDate(this DateTime date) public static Int64 ToUnixEpochDate(this DateTime date) {
{
#if NETSTANDARD2_0 #if NETSTANDARD2_0
return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds(); return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds();
#else #else
var epochTicks = new DateTime(1970, 1, 1).Ticks; Int64 epochTicks = new DateTime(1970, 1, 1).Ticks;
return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond; return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond;
#endif #endif
@ -147,15 +145,14 @@
/// <param name="month">The month. (1-12).</param> /// <param name="month">The month. (1-12).</param>
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param> /// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns> /// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
public static bool AsCronCanRun(this DateTime date, int? minute = null, int? hour = null, int? dayOfMonth = null, int? month = null, int? dayOfWeek = null) public static Boolean AsCronCanRun(this DateTime date, Int32? minute = null, Int32? hour = null, Int32? dayOfMonth = null, Int32? month = null, Int32? dayOfWeek = null) {
{ List<Boolean?> results = new List<Boolean?>
var results = new List<bool?>
{ {
GetElementParts(minute, date.Minute), GetElementParts(minute, date.Minute),
GetElementParts(hour, date.Hour), GetElementParts(hour, date.Hour),
GetElementParts(dayOfMonth, date.Day), GetElementParts(dayOfMonth, date.Day),
GetElementParts(month, date.Month), GetElementParts(month, date.Month),
GetElementParts(dayOfWeek, (int) date.DayOfWeek), GetElementParts(dayOfWeek, (Int32) date.DayOfWeek),
}; };
return results.Any(x => x != false); return results.Any(x => x != false);
@ -171,60 +168,64 @@
/// <param name="month">The month. (1-12).</param> /// <param name="month">The month. (1-12).</param>
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param> /// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns> /// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
public static bool AsCronCanRun(this DateTime date, string minute = "*", string hour = "*", string dayOfMonth = "*", string month = "*", string dayOfWeek = "*") public static Boolean AsCronCanRun(this DateTime date, String minute = "*", String hour = "*", String dayOfMonth = "*", String month = "*", String dayOfWeek = "*") {
{ List<Boolean?> results = new List<Boolean?>
var results = new List<bool?>
{ {
GetElementParts(minute, nameof(minute), date.Minute), GetElementParts(minute, nameof(minute), date.Minute),
GetElementParts(hour, nameof(hour), date.Hour), GetElementParts(hour, nameof(hour), date.Hour),
GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day), GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day),
GetElementParts(month, nameof(month), date.Month), GetElementParts(month, nameof(month), date.Month),
GetElementParts(dayOfWeek, nameof(dayOfWeek), (int) date.DayOfWeek), GetElementParts(dayOfWeek, nameof(dayOfWeek), (Int32) date.DayOfWeek),
}; };
return results.Any(x => x != false); return results.Any(x => x != false);
} }
private static bool? GetElementParts(int? status, int value) => status.HasValue ? status.Value == value : (bool?) null; private static Boolean? GetElementParts(Int32? status, Int32 value) => status.HasValue ? status.Value == value : (Boolean?)null;
private static bool? GetElementParts(string parts, string type, int value) private static Boolean? GetElementParts(String parts, String type, Int32 value) {
{ if(String.IsNullOrWhiteSpace(parts) || parts == "*") {
if (string.IsNullOrWhiteSpace(parts) || parts == "*") return null; return null;
if (parts.Contains(","))
{
return parts.Split(',').Select(int.Parse).Contains(value);
} }
var stop = DateRanges[type]; if(parts.Contains(",")) {
return parts.Split(',').Select(Int32.Parse).Contains(value);
}
if (parts.Contains("/")) Int32 stop = DateRanges[type];
{
var multiple = int.Parse(parts.Split('/').Last());
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
for (var i = start; i <= stop; i += multiple) if(parts.Contains("/")) {
if (i == value) return true; Int32 multiple = Int32.Parse(parts.Split('/').Last());
Int32 start = type == "dayOfMonth" || type == "month" ? 1 : 0;
for(Int32 i = start; i <= stop; i += multiple) {
if(i == value) {
return true;
}
}
return false; return false;
} }
if (parts.Contains("-")) if(parts.Contains("-")) {
{ String[] range = parts.Split('-');
var range = parts.Split('-'); Int32 start = Int32.Parse(range.First());
var start = int.Parse(range.First()); stop = Math.Max(stop, Int32.Parse(range.Last()));
stop = Math.Max(stop, int.Parse(range.Last()));
if ((type == "dayOfMonth" || type == "month") && start == 0) if((type == "dayOfMonth" || type == "month") && start == 0) {
start = 1; start = 1;
}
for (var i = start; i <= stop; i++) for(Int32 i = start; i <= stop; i++) {
if (i == value) return true; if(i == value) {
return true;
}
}
return false; return false;
} }
return int.Parse(parts) == value; return Int32.Parse(parts) == value;
} }
} }
} }

View File

@ -1,13 +1,11 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Extension methods. /// Extension methods.
/// </summary> /// </summary>
public static partial class Extensions public static partial class Extensions {
{
/// <summary> /// <summary>
/// Gets the value if exists or default. /// Gets the value if exists or default.
/// </summary> /// </summary>
@ -20,10 +18,10 @@
/// The value of the provided key or default. /// The value of the provided key or default.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">dict.</exception> /// <exception cref="ArgumentNullException">dict.</exception>
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) {
{ if(dict == null) {
if (dict == null)
throw new ArgumentNullException(nameof(dict)); throw new ArgumentNullException(nameof(dict));
}
return dict.ContainsKey(key) ? dict[key] : defaultValue; return dict.ContainsKey(key) ? dict[key] : defaultValue;
} }
@ -40,15 +38,17 @@
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
/// <param name="valueFactory">The value factory.</param> /// <param name="valueFactory">The value factory.</param>
/// <returns>The value for the key.</returns> /// <returns>The value for the key.</returns>
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) {
{ if(dict == null) {
if (dict == null)
throw new ArgumentNullException(nameof(dict)); throw new ArgumentNullException(nameof(dict));
}
if(!dict.ContainsKey(key)) {
TValue value = valueFactory(key);
if(Equals(value, default)) {
return default;
}
if (!dict.ContainsKey(key))
{
var value = valueFactory(key);
if (Equals(value, default)) return default;
dict[key] = value; dict[key] = value;
} }
@ -63,13 +63,12 @@
/// <param name="dict">The dictionary.</param> /// <param name="dict">The dictionary.</param>
/// <param name="itemAction">The item action.</param> /// <param name="itemAction">The item action.</param>
/// <exception cref="ArgumentNullException">dict.</exception> /// <exception cref="ArgumentNullException">dict.</exception>
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) {
{ if(dict == null) {
if (dict == null)
throw new ArgumentNullException(nameof(dict)); throw new ArgumentNullException(nameof(dict));
}
foreach (var kvp in dict) foreach(KeyValuePair<TKey, TValue> kvp in dict) {
{
itemAction(kvp.Key, kvp.Value); itemAction(kvp.Key, kvp.Value);
} }
} }

View File

@ -1,14 +1,12 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Functional programming extension methods. /// Functional programming extension methods.
/// </summary> /// </summary>
public static class FunctionalExtensions public static class FunctionalExtensions {
{
/// <summary> /// <summary>
/// Whens the specified condition. /// Whens the specified condition.
/// </summary> /// </summary>
@ -28,17 +26,19 @@
/// </exception> /// </exception>
public static IQueryable<T> When<T>( public static IQueryable<T> When<T>(
this IQueryable<T> list, this IQueryable<T> list,
Func<bool> condition, Func<Boolean> condition,
Func<IQueryable<T>, IQueryable<T>> fn) Func<IQueryable<T>, IQueryable<T>> fn) {
{ if(list == null) {
if (list == null)
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(list));
}
if (condition == null) if(condition == null) {
throw new ArgumentNullException(nameof(condition)); throw new ArgumentNullException(nameof(condition));
}
if (fn == null) if(fn == null) {
throw new ArgumentNullException(nameof(fn)); throw new ArgumentNullException(nameof(fn));
}
return condition() ? fn(list) : list; return condition() ? fn(list) : list;
} }
@ -62,17 +62,19 @@
/// </exception> /// </exception>
public static IEnumerable<T> When<T>( public static IEnumerable<T> When<T>(
this IEnumerable<T> list, this IEnumerable<T> list,
Func<bool> condition, Func<Boolean> condition,
Func<IEnumerable<T>, IEnumerable<T>> fn) Func<IEnumerable<T>, IEnumerable<T>> fn) {
{ if(list == null) {
if (list == null)
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(list));
}
if (condition == null) if(condition == null) {
throw new ArgumentNullException(nameof(condition)); throw new ArgumentNullException(nameof(condition));
}
if (fn == null) if(fn == null) {
throw new ArgumentNullException(nameof(fn)); throw new ArgumentNullException(nameof(fn));
}
return condition() ? fn(list) : list; return condition() ? fn(list) : list;
} }
@ -96,20 +98,23 @@
/// </exception> /// </exception>
public static IList<T> AddWhen<T>( public static IList<T> AddWhen<T>(
this IList<T> list, this IList<T> list,
Func<bool> condition, Func<Boolean> condition,
Func<T> value) Func<T> value) {
{ if(list == null) {
if (list == null)
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(list));
}
if (condition == null) if(condition == null) {
throw new ArgumentNullException(nameof(condition)); throw new ArgumentNullException(nameof(condition));
}
if (value == null) if(value == null) {
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
}
if (condition()) if(condition()) {
list.Add(value()); list.Add(value());
}
return list; return list;
} }
@ -127,14 +132,15 @@
/// <exception cref="ArgumentNullException">list.</exception> /// <exception cref="ArgumentNullException">list.</exception>
public static IList<T> AddWhen<T>( public static IList<T> AddWhen<T>(
this IList<T> list, this IList<T> list,
bool condition, Boolean condition,
T value) T value) {
{ if(list == null) {
if (list == null)
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(list));
}
if (condition) if(condition) {
list.Add(value); list.Add(value);
}
return list; return list;
} }
@ -158,20 +164,23 @@
/// </exception> /// </exception>
public static List<T> AddRangeWhen<T>( public static List<T> AddRangeWhen<T>(
this List<T> list, this List<T> list,
Func<bool> condition, Func<Boolean> condition,
Func<IEnumerable<T>> value) Func<IEnumerable<T>> value) {
{ if(list == null) {
if (list == null)
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(list));
}
if (condition == null) if(condition == null) {
throw new ArgumentNullException(nameof(condition)); throw new ArgumentNullException(nameof(condition));
}
if (value == null) if(value == null) {
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
}
if (condition()) if(condition()) {
list.AddRange(value()); list.AddRange(value());
}
return list; return list;
} }

View File

@ -1,23 +1,21 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections; using System.Collections;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Provides various extension methods for Reflection and Types. /// Provides various extension methods for Reflection and Types.
/// </summary> /// </summary>
public static class ReflectionExtensions public static class ReflectionExtensions {
{ private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>> CacheGetMethods =
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>> CacheGetMethods = new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>(), true);
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>(), true);
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>> CacheSetMethods = private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>> CacheSetMethods =
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>(), true); new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>(), true);
#region Assembly Extensions #region Assembly Extensions
@ -29,17 +27,14 @@
/// Array of Type objects representing the types specified by an assembly. /// Array of Type objects representing the types specified by an assembly.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">assembly.</exception> /// <exception cref="ArgumentNullException">assembly.</exception>
public static IEnumerable<Type> GetAllTypes(this Assembly assembly) public static IEnumerable<Type> GetAllTypes(this Assembly assembly) {
{ if(assembly == null) {
if (assembly == null)
throw new ArgumentNullException(nameof(assembly)); throw new ArgumentNullException(nameof(assembly));
try
{
return assembly.GetTypes();
} }
catch (ReflectionTypeLoadException e)
{ try {
return assembly.GetTypes();
} catch(ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null); return e.Types.Where(t => t != null);
} }
} }
@ -56,10 +51,10 @@
/// Default value of this type. /// Default value of this type.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">type.</exception> /// <exception cref="ArgumentNullException">type.</exception>
public static object GetDefault(this Type type) public static Object GetDefault(this Type type) {
{ if(type == null) {
if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
}
return type.IsValueType() ? Activator.CreateInstance(type) : null; return type.IsValueType() ? Activator.CreateInstance(type) : null;
} }
@ -72,12 +67,12 @@
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>. /// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">sourceType.</exception> /// <exception cref="ArgumentNullException">sourceType.</exception>
public static bool IsCollection(this Type sourceType) public static Boolean IsCollection(this Type sourceType) {
{ if(sourceType == null) {
if (sourceType == null)
throw new ArgumentNullException(nameof(sourceType)); throw new ArgumentNullException(nameof(sourceType));
}
return sourceType != typeof(string) && return sourceType != typeof(String) &&
typeof(IEnumerable).IsAssignableFrom(sourceType); typeof(IEnumerable).IsAssignableFrom(sourceType);
} }
@ -99,25 +94,28 @@
public static MethodInfo GetMethod( public static MethodInfo GetMethod(
this Type type, this Type type,
BindingFlags bindingFlags, BindingFlags bindingFlags,
string methodName, String methodName,
Type[] genericTypes, Type[] genericTypes,
Type[] parameterTypes) Type[] parameterTypes) {
{ if(type == null) {
if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
}
if (methodName == null) if(methodName == null) {
throw new ArgumentNullException(nameof(methodName)); throw new ArgumentNullException(nameof(methodName));
}
if (genericTypes == null) if(genericTypes == null) {
throw new ArgumentNullException(nameof(genericTypes)); throw new ArgumentNullException(nameof(genericTypes));
}
if (parameterTypes == null) if(parameterTypes == null) {
throw new ArgumentNullException(nameof(parameterTypes)); throw new ArgumentNullException(nameof(parameterTypes));
}
var methods = type List<MethodInfo> methods = type
.GetMethods(bindingFlags) .GetMethods(bindingFlags)
.Where(mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)) .Where(mi => String.Equals(methodName, mi.Name, StringComparison.Ordinal))
.Where(mi => mi.ContainsGenericParameters) .Where(mi => mi.ContainsGenericParameters)
.Where(mi => mi.GetGenericArguments().Length == genericTypes.Length) .Where(mi => mi.GetGenericArguments().Length == genericTypes.Length)
.Where(mi => mi.GetParameters().Length == parameterTypes.Length) .Where(mi => mi.GetParameters().Length == parameterTypes.Length)
@ -135,7 +133,7 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified type is class; otherwise, <c>false</c>. /// <c>true</c> if the specified type is class; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsClass(this Type type) => type.GetTypeInfo().IsClass; public static Boolean IsClass(this Type type) => type.GetTypeInfo().IsClass;
/// <summary> /// <summary>
/// Determines whether this instance is abstract. /// Determines whether this instance is abstract.
@ -144,7 +142,7 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified type is abstract; otherwise, <c>false</c>. /// <c>true</c> if the specified type is abstract; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract; public static Boolean IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract;
/// <summary> /// <summary>
/// Determines whether this instance is interface. /// Determines whether this instance is interface.
@ -153,7 +151,7 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified type is interface; otherwise, <c>false</c>. /// <c>true</c> if the specified type is interface; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsInterface(this Type type) => type.GetTypeInfo().IsInterface; public static Boolean IsInterface(this Type type) => type.GetTypeInfo().IsInterface;
/// <summary> /// <summary>
/// Determines whether this instance is primitive. /// Determines whether this instance is primitive.
@ -162,7 +160,7 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified type is primitive; otherwise, <c>false</c>. /// <c>true</c> if the specified type is primitive; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive; public static Boolean IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive;
/// <summary> /// <summary>
/// Determines whether [is value type]. /// Determines whether [is value type].
@ -171,7 +169,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>. /// <c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType; public static Boolean IsValueType(this Type type) => type.GetTypeInfo().IsValueType;
/// <summary> /// <summary>
/// Determines whether [is generic type]. /// Determines whether [is generic type].
@ -180,7 +178,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [is generic type] [the specified type]; otherwise, <c>false</c>. /// <c>true</c> if [is generic type] [the specified type]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType; public static Boolean IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType;
/// <summary> /// <summary>
/// Determines whether [is generic parameter]. /// Determines whether [is generic parameter].
@ -189,7 +187,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [is generic parameter] [the specified type]; otherwise, <c>false</c>. /// <c>true</c> if [is generic parameter] [the specified type]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsGenericParameter(this Type type) => type.IsGenericParameter; public static Boolean IsGenericParameter(this Type type) => type.IsGenericParameter;
/// <summary> /// <summary>
/// Determines whether the specified attribute type is defined. /// Determines whether the specified attribute type is defined.
@ -200,7 +198,7 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified attribute type is defined; otherwise, <c>false</c>. /// <c>true</c> if the specified attribute type is defined; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsDefined(this Type type, Type attributeType, bool inherit) => public static Boolean IsDefined(this Type type, Type attributeType, Boolean inherit) =>
type.GetTypeInfo().IsDefined(attributeType, inherit); type.GetTypeInfo().IsDefined(attributeType, inherit);
/// <summary> /// <summary>
@ -212,7 +210,7 @@
/// <returns> /// <returns>
/// Attributes associated with the property represented by this PropertyInfo object. /// Attributes associated with the property represented by this PropertyInfo object.
/// </returns> /// </returns>
public static Attribute[] GetCustomAttributes(this Type type, Type attributeType, bool inherit) => public static Attribute[] GetCustomAttributes(this Type type, Type attributeType, Boolean inherit) =>
type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).Cast<Attribute>().ToArray(); type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).Cast<Attribute>().ToArray();
/// <summary> /// <summary>
@ -222,7 +220,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [is generic type definition] [the specified type]; otherwise, <c>false</c>. /// <c>true</c> if [is generic type definition] [the specified type]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition; public static Boolean IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition;
/// <summary> /// <summary>
/// Bases the type. /// Bases the type.
@ -246,7 +244,7 @@
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>. /// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">type.</exception> /// <exception cref="ArgumentNullException">type.</exception>
public static bool IsIEnumerable(this Type type) public static Boolean IsIEnumerable(this Type type)
=> type == null => type == null
? throw new ArgumentNullException(nameof(type)) ? throw new ArgumentNullException(nameof(type))
: type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IEnumerable<>); : type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
@ -262,7 +260,7 @@
/// <returns> /// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool TryParseBasicType(this Type type, object value, out object result) public static Boolean TryParseBasicType(this Type type, Object value, out Object result)
=> TryParseBasicType(type, value.ToStringInvariant(), out result); => TryParseBasicType(type, value.ToStringInvariant(), out result);
/// <summary> /// <summary>
@ -274,8 +272,7 @@
/// <returns> /// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool TryParseBasicType(this Type type, string value, out object result) public static Boolean TryParseBasicType(this Type type, String value, out Object result) {
{
result = null; result = null;
return Definitions.BasicTypesInfo.ContainsKey(type) && Definitions.BasicTypesInfo[type].TryParse(value, out result); return Definitions.BasicTypesInfo.ContainsKey(type) && Definitions.BasicTypesInfo[type].TryParse(value, out result);
@ -290,18 +287,13 @@
/// <returns> /// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool TrySetBasicType(this PropertyInfo property, object value, object obj) public static Boolean TrySetBasicType(this PropertyInfo property, Object value, Object obj) {
{ try {
try if(property.PropertyType.TryParseBasicType(value, out Object propertyValue)) {
{
if (property.PropertyType.TryParseBasicType(value, out var propertyValue))
{
property.SetValue(obj, propertyValue); property.SetValue(obj, propertyValue);
return true; return true;
} }
} } catch {
catch
{
// swallow // swallow
} }
@ -318,30 +310,23 @@
/// <returns> /// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool TrySetArrayBasicType(this Type type, object value, Array array, int index) public static Boolean TrySetArrayBasicType(this Type type, Object value, Array array, Int32 index) {
{ try {
try if(value == null) {
{
if (value == null)
{
array.SetValue(null, index); array.SetValue(null, index);
return true; return true;
} }
if (type.TryParseBasicType(value, out var propertyValue)) if(type.TryParseBasicType(value, out Object propertyValue)) {
{
array.SetValue(propertyValue, index); array.SetValue(propertyValue, index);
return true; return true;
} }
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) if(type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
{
array.SetValue(null, index); array.SetValue(null, index);
return true; return true;
} }
} } catch {
catch
{
// swallow // swallow
} }
@ -357,22 +342,23 @@
/// <returns> /// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable<object> value, object obj) public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable<Object> value, Object obj) {
{ Type elementType = propertyInfo.PropertyType.GetElementType();
var elementType = propertyInfo.PropertyType.GetElementType();
if (elementType == null) if(elementType == null) {
return false; return false;
}
var targetArray = Array.CreateInstance(elementType, value.Count()); Array targetArray = Array.CreateInstance(elementType, value.Count());
var i = 0; Int32 i = 0;
foreach (var sourceElement in value) foreach(Object sourceElement in value) {
{ Boolean result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
var result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
if (!result) return false; if(!result) {
return false;
}
} }
propertyInfo.SetValue(obj, targetArray); propertyInfo.SetValue(obj, targetArray);
@ -391,23 +377,21 @@
/// <param name="propertyInfo">The property information.</param> /// <param name="propertyInfo">The property information.</param>
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <returns>The property value or null.</returns> /// <returns>The property value or null.</returns>
public static string ToFormattedString(this PropertyInfo propertyInfo, object obj) public static String ToFormattedString(this PropertyInfo propertyInfo, Object obj) {
{ try {
try Object value = propertyInfo.GetValue(obj);
{ PropertyDisplayAttribute attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
var value = propertyInfo.GetValue(obj);
var attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
if (attr == null) return value?.ToString() ?? string.Empty; if(attr == null) {
return value?.ToString() ?? String.Empty;
var valueToFormat = value ?? attr.DefaultValue;
return string.IsNullOrEmpty(attr.Format)
? (valueToFormat?.ToString() ?? string.Empty)
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
} }
catch
{ Object valueToFormat = value ?? attr.DefaultValue;
return String.IsNullOrEmpty(attr.Format)
? (valueToFormat?.ToString() ?? String.Empty)
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
} catch {
return null; return null;
} }
} }
@ -420,9 +404,8 @@
/// <returns> /// <returns>
/// The cached MethodInfo. /// The cached MethodInfo.
/// </returns> /// </returns>
public static Func<object, object> GetCacheGetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) public static Func<Object, Object> GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
{ Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
var key = Tuple.Create(!nonPublic, propertyInfo);
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic
? null ? null
@ -439,9 +422,8 @@
/// <returns> /// <returns>
/// The cached MethodInfo. /// The cached MethodInfo.
/// </returns> /// </returns>
public static Action<object, object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) public static Action<Object, Object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
{ Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
var key = Tuple.Create(!nonPublic, propertyInfo);
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic
? null ? null
@ -450,20 +432,16 @@
x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args)); x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args));
} }
private static string ConvertObjectAndFormat(Type propertyType, object value, string format) private static String ConvertObjectAndFormat(Type propertyType, Object value, String format) => propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)
{ ? Convert.ToDateTime(value).ToString(format)
if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)) : propertyType == typeof(Int32) || propertyType == typeof(Int32?)
return Convert.ToDateTime(value).ToString(format); ? Convert.ToInt32(value).ToString(format)
if (propertyType == typeof(int) || propertyType == typeof(int?)) : propertyType == typeof(Decimal) || propertyType == typeof(Decimal?)
return Convert.ToInt32(value).ToString(format); ? Convert.ToDecimal(value).ToString(format)
if (propertyType == typeof(decimal) || propertyType == typeof(decimal?)) : propertyType == typeof(Double) || propertyType == typeof(Double?)
return Convert.ToDecimal(value).ToString(format); ? Convert.ToDouble(value).ToString(format)
if (propertyType == typeof(double) || propertyType == typeof(double?)) : propertyType == typeof(Byte) || propertyType == typeof(Byte?)
return Convert.ToDouble(value).ToString(format); ? Convert.ToByte(value).ToString(format)
if (propertyType == typeof(byte) || propertyType == typeof(byte?)) : value?.ToString() ?? String.Empty;
return Convert.ToByte(value).ToString(format);
return value?.ToString() ?? string.Empty;
}
} }
} }

View File

@ -1,6 +1,4 @@
namespace Unosquare.Swan using Unosquare.Swan.Formatters;
{
using Formatters;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -8,17 +6,17 @@
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// String related extension methods. /// String related extension methods.
/// </summary> /// </summary>
public static class StringExtensions public static class StringExtensions {
{
#region Private Declarations #region Private Declarations
private const RegexOptions StandardRegexOptions = private const RegexOptions StandardRegexOptions =
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant; RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant;
private static readonly string[] ByteSuffixes = {"B", "KB", "MB", "GB", "TB"}; private static readonly String[] ByteSuffixes = { "B", "KB", "MB", "GB", "TB" };
private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true); private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true);
private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true); private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true);
@ -39,17 +37,13 @@
new Regex(@"[a-z][A-Z]", new Regex(@"[a-z][A-Z]",
StandardRegexOptions)); StandardRegexOptions));
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() => private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() => m => {
{ String x = m.ToString();
return m =>
{
var x = m.ToString();
return x[0] + " " + x.Substring(1, x.Length - 1); return x[0] + " " + x.Substring(1, x.Length - 1);
};
}); });
private static readonly Lazy<string[]> InvalidFilenameChars = private static readonly Lazy<String[]> InvalidFilenameChars =
new Lazy<string[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray()); new Lazy<String[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray());
#endregion #endregion
@ -63,31 +57,32 @@
/// The computed hash code. /// The computed hash code.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">stream.</exception> /// <exception cref="ArgumentNullException">stream.</exception>
public static byte[] ComputeMD5(this Stream stream, bool createHasher = false) [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
{ public static Byte[] ComputeMD5(this Stream stream, Boolean createHasher = false) {
if (stream == null) if(stream == null) {
throw new ArgumentNullException(nameof(stream)); throw new ArgumentNullException(nameof(stream));
}
#if NET452 #if NET452
var md5 = MD5.Create(); MD5 md5 = MD5.Create();
const int bufferSize = 4096; const Int32 bufferSize = 4096;
var readAheadBuffer = new byte[bufferSize]; Byte[] readAheadBuffer = new Byte[bufferSize];
var readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length); Int32 readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
do do {
{ Int32 bytesRead = readAheadBytesRead;
var bytesRead = readAheadBytesRead; Byte[] buffer = readAheadBuffer;
var buffer = readAheadBuffer;
readAheadBuffer = new byte[bufferSize]; readAheadBuffer = new Byte[bufferSize];
readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length); readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
if (readAheadBytesRead == 0) if(readAheadBytesRead == 0) {
md5.TransformFinalBlock(buffer, 0, bytesRead); md5.TransformFinalBlock(buffer, 0, bytesRead);
else } else {
md5.TransformBlock(buffer, 0, bytesRead, buffer, 0); md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
} }
}
while(readAheadBytesRead != 0); while(readAheadBytesRead != 0);
return md5.Hash; return md5.Hash;
@ -108,18 +103,21 @@
/// <param name="value">The input string.</param> /// <param name="value">The input string.</param>
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param> /// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
/// <returns>The computed hash code.</returns> /// <returns>The computed hash code.</returns>
public static byte[] ComputeMD5(this string value, bool createHasher = false) => public static Byte[] ComputeMD5(this String value, Boolean createHasher = false) =>
Encoding.UTF8.GetBytes(value).ComputeMD5(createHasher); Encoding.UTF8.GetBytes(value).ComputeMD5(createHasher);
/// <summary> /// <summary>
/// Computes the MD5 hash of the given byte array. /// Computes the MD5 hash of the given byte array.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param> /// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
/// <returns>The computed hash code.</returns> /// <returns>The computed hash code.</returns>
public static byte[] ComputeMD5(this byte[] data, bool createHasher = false) => [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeMD5(this Byte[] data, Boolean createHasher = false) =>
(createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data); (createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data);
/// <summary> /// <summary>
/// Computes the SHA-1 hash of the given string using UTF8 byte encoding. /// Computes the SHA-1 hash of the given string using UTF8 byte encoding.
/// </summary> /// </summary>
@ -129,12 +127,13 @@
/// The computes a Hash-based Message Authentication Code (HMAC) /// The computes a Hash-based Message Authentication Code (HMAC)
/// using the SHA1 hash function. /// using the SHA1 hash function.
/// </returns> /// </returns>
public static byte[] ComputeSha1(this string value, bool createHasher = false) [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
{ public static Byte[] ComputeSha1(this String value, Boolean createHasher = false) {
var inputBytes = Encoding.UTF8.GetBytes(value); Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes); return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes);
} }
/// <summary> /// <summary>
/// Computes the SHA-256 hash of the given string using UTF8 byte encoding. /// Computes the SHA-256 hash of the given string using UTF8 byte encoding.
/// </summary> /// </summary>
@ -144,12 +143,13 @@
/// The computes a Hash-based Message Authentication Code (HMAC) /// The computes a Hash-based Message Authentication Code (HMAC)
/// by using the SHA256 hash function. /// by using the SHA256 hash function.
/// </returns> /// </returns>
public static byte[] ComputeSha256(this string value, bool createHasher = false) [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
{ public static Byte[] ComputeSha256(this String value, Boolean createHasher = false) {
var inputBytes = Encoding.UTF8.GetBytes(value); Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes); return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes);
} }
/// <summary> /// <summary>
/// Computes the SHA-512 hash of the given string using UTF8 byte encoding. /// Computes the SHA-512 hash of the given string using UTF8 byte encoding.
/// </summary> /// </summary>
@ -159,9 +159,9 @@
/// The computes a Hash-based Message Authentication Code (HMAC) /// The computes a Hash-based Message Authentication Code (HMAC)
/// using the SHA512 hash function. /// using the SHA512 hash function.
/// </returns> /// </returns>
public static byte[] ComputeSha512(this string value, bool createHasher = false) [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
{ public static Byte[] ComputeSha512(this String value, Boolean createHasher = false) {
var inputBytes = Encoding.UTF8.GetBytes(value); Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes); return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes);
} }
@ -172,17 +172,16 @@
/// </summary> /// </summary>
/// <param name="obj">The item.</param> /// <param name="obj">The item.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToStringInvariant(this object obj) public static String ToStringInvariant(this Object obj) {
{ if(obj == null) {
if (obj == null) return String.Empty;
return string.Empty; }
var itemType = obj.GetType(); Type itemType = obj.GetType();
if (itemType == typeof(string)) return itemType == typeof(String)
return obj as string; ? obj as String
: Definitions.BasicTypesInfo.ContainsKey(itemType)
return Definitions.BasicTypesInfo.ContainsKey(itemType)
? Definitions.BasicTypesInfo[itemType].ToStringInvariant(obj) ? Definitions.BasicTypesInfo[itemType].ToStringInvariant(obj)
: obj.ToString(); : obj.ToString();
} }
@ -195,13 +194,7 @@
/// <typeparam name="T">The type to get the string.</typeparam> /// <typeparam name="T">The type to get the string.</typeparam>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToStringInvariant<T>(this T item) public static String ToStringInvariant<T>(this T item) => typeof(String) == typeof(T) ? Equals(item, default(T)) ? String.Empty : item as String : ToStringInvariant(item as Object);
{
if (typeof(string) == typeof(T))
return Equals(item, default(T)) ? string.Empty : item as string;
return ToStringInvariant(item as object);
}
/// <summary> /// <summary>
/// Removes the control characters from a string except for those specified. /// Removes the control characters from a string except for those specified.
@ -212,16 +205,17 @@
/// A string that represents the current object. /// A string that represents the current object.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">input.</exception> /// <exception cref="ArgumentNullException">input.</exception>
public static string RemoveControlCharsExcept(this string value, params char[] excludeChars) public static String RemoveControlCharsExcept(this String value, params Char[] excludeChars) {
{ if(value == null) {
if (value == null)
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
}
if (excludeChars == null) if(excludeChars == null) {
excludeChars = new char[] { }; excludeChars = new Char[] { };
}
return new string(value return new String(value
.Where(c => char.IsControl(c) == false || excludeChars.Contains(c)) .Where(c => Char.IsControl(c) == false || excludeChars.Contains(c))
.ToArray()); .ToArray());
} }
@ -231,7 +225,7 @@
/// <param name="value">The input.</param> /// <param name="value">The input.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
/// <exception cref="ArgumentNullException">input.</exception> /// <exception cref="ArgumentNullException">input.</exception>
public static string RemoveControlChars(this string value) => value.RemoveControlCharsExcept(null); public static String RemoveControlChars(this String value) => value.RemoveControlCharsExcept(null);
/// <summary> /// <summary>
/// Outputs JSON string representing this object. /// Outputs JSON string representing this object.
@ -239,8 +233,8 @@
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <param name="format">if set to <c>true</c> format the output.</param> /// <param name="format">if set to <c>true</c> format the output.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToJson(this object obj, bool format = true) => public static String ToJson(this Object obj, Boolean format = true) =>
obj == null ? string.Empty : Json.Serialize(obj, format); obj == null ? String.Empty : Json.Serialize(obj, format);
/// <summary> /// <summary>
/// Returns text representing the properties of the specified object in a human-readable format. /// Returns text representing the properties of the specified object in a human-readable format.
@ -249,20 +243,17 @@
/// </summary> /// </summary>
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Stringify(this object obj) public static String Stringify(this Object obj) {
{ if(obj == null) {
if (obj == null)
return "(null)"; return "(null)";
}
try try {
{ String jsonText = Json.Serialize(obj, false, "$type");
var jsonText = Json.Serialize(obj, false, "$type"); Object jsonData = Json.Deserialize(jsonText);
var jsonData = Json.Deserialize(jsonText);
return new HumanizeJson(jsonData, 0).GetResult(); return new HumanizeJson(jsonData, 0).GetResult();
} } catch {
catch
{
return obj.ToStringInvariant(); return obj.ToStringInvariant();
} }
} }
@ -276,14 +267,14 @@
/// <param name="startIndex">The start index.</param> /// <param name="startIndex">The start index.</param>
/// <param name="endIndex">The end index.</param> /// <param name="endIndex">The end index.</param>
/// <returns>Retrieves a substring from this instance.</returns> /// <returns>Retrieves a substring from this instance.</returns>
public static string Slice(this string value, int startIndex, int endIndex) public static String Slice(this String value, Int32 startIndex, Int32 endIndex) {
{ if(value == null) {
if (value == null) return String.Empty;
return string.Empty; }
var end = endIndex.Clamp(startIndex, value.Length - 1); Int32 end = endIndex.Clamp(startIndex, value.Length - 1);
return startIndex >= end ? string.Empty : value.Substring(startIndex, (end - startIndex) + 1); return startIndex >= end ? String.Empty : value.Substring(startIndex, end - startIndex + 1);
} }
/// <summary> /// <summary>
@ -295,15 +286,15 @@
/// <param name="startIndex">The start index.</param> /// <param name="startIndex">The start index.</param>
/// <param name="length">The length.</param> /// <param name="length">The length.</param>
/// <returns>Retrieves a substring from this instance.</returns> /// <returns>Retrieves a substring from this instance.</returns>
public static string SliceLength(this string str, int startIndex, int length) public static String SliceLength(this String str, Int32 startIndex, Int32 length) {
{ if(str == null) {
if (str == null) return String.Empty;
return string.Empty; }
var start = startIndex.Clamp(0, str.Length - 1); Int32 start = startIndex.Clamp(0, str.Length - 1);
var len = length.Clamp(0, str.Length - start); Int32 len = length.Clamp(0, str.Length - start);
return len == 0 ? string.Empty : str.Substring(start, len); return len == 0 ? String.Empty : str.Substring(start, len);
} }
/// <summary> /// <summary>
@ -314,8 +305,8 @@
/// An array whose elements contain the substrings from this instance /// An array whose elements contain the substrings from this instance
/// that are delimited by one or more characters in separator. /// that are delimited by one or more characters in separator.
/// </returns> /// </returns>
public static string[] ToLines(this string value) => public static String[] ToLines(this String value) =>
value == null ? new string[] { } : SplitLinesRegex.Value.Split(value); value == null ? new String[] { } : SplitLinesRegex.Value.Split(value);
/// <summary> /// <summary>
/// Humanizes (make more human-readable) an identifier-style string /// Humanizes (make more human-readable) an identifier-style string
@ -324,12 +315,12 @@
/// </summary> /// </summary>
/// <param name="value">The identifier-style string.</param> /// <param name="value">The identifier-style string.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Humanize(this string value) public static String Humanize(this String value) {
{ if(value == null) {
if (value == null) return String.Empty;
return string.Empty; }
var returnValue = UnderscoreRegex.Value.Replace(value, " "); String returnValue = UnderscoreRegex.Value.Replace(value, " ");
returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value); returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value);
return returnValue; return returnValue;
} }
@ -341,18 +332,21 @@
/// <param name="value">The text.</param> /// <param name="value">The text.</param>
/// <param name="spaces">The spaces.</param> /// <param name="spaces">The spaces.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Indent(this string value, int spaces = 4) public static String Indent(this String value, Int32 spaces = 4) {
{ if(value == null) {
if (value == null) value = string.Empty; value = String.Empty;
if (spaces <= 0) return value; }
var lines = value.ToLines(); if(spaces <= 0) {
var builder = new StringBuilder(); return value;
var indentStr = new string(' ', spaces); }
foreach (var line in lines) String[] lines = value.ToLines();
{ StringBuilder builder = new StringBuilder();
builder.AppendLine($"{indentStr}{line}"); String indentStr = new String(' ', spaces);
foreach(String line in lines) {
_ = builder.AppendLine($"{indentStr}{line}");
} }
return builder.ToString().TrimEnd(); return builder.ToString().TrimEnd();
@ -368,28 +362,27 @@
/// <param name="value">The string.</param> /// <param name="value">The string.</param>
/// <param name="charIndex">Index of the character.</param> /// <param name="charIndex">Index of the character.</param>
/// <returns>A 2-tuple whose value is (item1, item2).</returns> /// <returns>A 2-tuple whose value is (item1, item2).</returns>
public static Tuple<int, int> TextPositionAt(this string value, int charIndex) public static Tuple<Int32, Int32> TextPositionAt(this String value, Int32 charIndex) {
{ if(value == null) {
if (value == null)
return Tuple.Create(0, 0); return Tuple.Create(0, 0);
}
var index = charIndex.Clamp(0, value.Length - 1); Int32 index = charIndex.Clamp(0, value.Length - 1);
var lineIndex = 0; Int32 lineIndex = 0;
var colNumber = 0; Int32 colNumber = 0;
for (var i = 0; i <= index; i++) for(Int32 i = 0; i <= index; i++) {
{ if(value[i] == '\n') {
if (value[i] == '\n')
{
lineIndex++; lineIndex++;
colNumber = 0; colNumber = 0;
continue; continue;
} }
if (value[i] != '\r') if(value[i] != '\r') {
colNumber++; colNumber++;
} }
}
return Tuple.Create(lineIndex + 1, colNumber); return Tuple.Create(lineIndex + 1, colNumber);
} }
@ -402,14 +395,11 @@
/// A string with a safe file name. /// A string with a safe file name.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">s.</exception> /// <exception cref="ArgumentNullException">s.</exception>
public static string ToSafeFilename(this string value) public static String ToSafeFilename(this String value) => value == null
{
return value == null
? throw new ArgumentNullException(nameof(value)) ? throw new ArgumentNullException(nameof(value))
: InvalidFilenameChars.Value : InvalidFilenameChars.Value
.Aggregate(value, (current, c) => current.Replace(c, string.Empty)) .Aggregate(value, (current, c) => current.Replace(c, String.Empty))
.Slice(0, 220); .Slice(0, 220);
}
/// <summary> /// <summary>
/// Formats a long into the closest bytes string. /// Formats a long into the closest bytes string.
@ -418,7 +408,7 @@
/// <returns> /// <returns>
/// The string representation of the current Byte object, formatted as specified by the format parameter. /// The string representation of the current Byte object, formatted as specified by the format parameter.
/// </returns> /// </returns>
public static string FormatBytes(this long bytes) => ((ulong) bytes).FormatBytes(); public static String FormatBytes(this Int64 bytes) => ((UInt64)bytes).FormatBytes();
/// <summary> /// <summary>
/// Formats a long into the closest bytes string. /// Formats a long into the closest bytes string.
@ -428,13 +418,11 @@
/// A copy of format in which the format items have been replaced by the string /// A copy of format in which the format items have been replaced by the string
/// representations of the corresponding arguments. /// representations of the corresponding arguments.
/// </returns> /// </returns>
public static string FormatBytes(this ulong bytes) public static String FormatBytes(this UInt64 bytes) {
{ Int32 i;
int i; Double dblSByte = bytes;
double dblSByte = bytes;
for (i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024) for(i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024) {
{
dblSByte = bytes / 1024.0; dblSByte = bytes / 1024.0;
} }
@ -450,8 +438,8 @@
/// Retrieves a substring from this instance. /// Retrieves a substring from this instance.
/// The substring starts at a specified character position and has a specified length. /// The substring starts at a specified character position and has a specified length.
/// </returns> /// </returns>
public static string Truncate(this string value, int maximumLength) => public static String Truncate(this String value, Int32 maximumLength) =>
Truncate(value, maximumLength, string.Empty); Truncate(value, maximumLength, String.Empty);
/// <summary> /// <summary>
/// Truncates the specified value and append the omission last. /// Truncates the specified value and append the omission last.
@ -463,32 +451,28 @@
/// Retrieves a substring from this instance. /// Retrieves a substring from this instance.
/// The substring starts at a specified character position and has a specified length. /// The substring starts at a specified character position and has a specified length.
/// </returns> /// </returns>
public static string Truncate(this string value, int maximumLength, string omission) public static String Truncate(this String value, Int32 maximumLength, String omission) => value == null
{ ? null
if (value == null) : value.Length > maximumLength
return null; ? value.Substring(0, maximumLength) + (omission ?? String.Empty)
return value.Length > maximumLength
? value.Substring(0, maximumLength) + (omission ?? string.Empty)
: value; : value;
}
/// <summary> /// <summary>
/// Determines whether the specified <see cref="string"/> contains any of characters in /// Determines whether the specified <see cref="String"/> contains any of characters in
/// the specified array of <see cref="char"/>. /// the specified array of <see cref="Char"/>.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>; /// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
/// otherwise, <c>false</c>. /// otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <param name="value"> /// <param name="value">
/// A <see cref="string"/> to test. /// A <see cref="String"/> to test.
/// </param> /// </param>
/// <param name="chars"> /// <param name="chars">
/// An array of <see cref="char"/> that contains characters to find. /// An array of <see cref="Char"/> that contains characters to find.
/// </param> /// </param>
public static bool Contains(this string value, params char[] chars) => public static Boolean Contains(this String value, params Char[] chars) =>
chars?.Length == 0 || (!string.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1); chars?.Length == 0 || !String.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1;
/// <summary> /// <summary>
/// Replaces all chars in a string. /// Replaces all chars in a string.
@ -497,8 +481,8 @@
/// <param name="replaceValue">The replace value.</param> /// <param name="replaceValue">The replace value.</param>
/// <param name="chars">The chars.</param> /// <param name="chars">The chars.</param>
/// <returns>The string with the characters replaced.</returns> /// <returns>The string with the characters replaced.</returns>
public static string ReplaceAll(this string value, string replaceValue, params char[] chars) => public static String ReplaceAll(this String value, String replaceValue, params Char[] chars) =>
chars.Aggregate(value, (current, c) => current.Replace(new string(new[] {c}), replaceValue)); chars.Aggregate(value, (current, c) => current.Replace(new String(new[] { c }), replaceValue));
/// <summary> /// <summary>
/// Convert hex character to an integer. Return -1 if char is something /// Convert hex character to an integer. Return -1 if char is something
@ -506,9 +490,7 @@
/// </summary> /// </summary>
/// <param name="value">The c.</param> /// <param name="value">The c.</param>
/// <returns>Converted integer.</returns> /// <returns>Converted integer.</returns>
public static int Hex2Int(this char value) public static Int32 Hex2Int(this Char value) => value >= '0' && value <= '9'
{
return value >= '0' && value <= '9'
? value - '0' ? value - '0'
: value >= 'A' && value <= 'F' : value >= 'A' && value <= 'F'
? value - 'A' + 10 ? value - 'A' + 10
@ -517,4 +499,3 @@
: -1; : -1;
} }
} }
}

View File

@ -1,15 +1,13 @@
namespace Unosquare.Swan using System;
{
using System;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Provides various extension methods for value types and structs. /// Provides various extension methods for value types and structs.
/// </summary> /// </summary>
public static class ValueTypeExtensions public static class ValueTypeExtensions {
{
/// <summary> /// <summary>
/// Clamps the specified value between the minimum and the maximum. /// Clamps the specified value between the minimum and the maximum.
/// </summary> /// </summary>
@ -19,12 +17,7 @@
/// <param name="max">The maximum.</param> /// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns> /// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static T Clamp<T>(this T value, T min, T max) public static T Clamp<T>(this T value, T min, T max)
where T : struct, IComparable where T : struct, IComparable => value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
{
if (value.CompareTo(min) < 0) return min;
return value.CompareTo(max) > 0 ? max : value;
}
/// <summary> /// <summary>
/// Clamps the specified value between the minimum and the maximum. /// Clamps the specified value between the minimum and the maximum.
@ -33,7 +26,7 @@
/// <param name="min">The minimum.</param> /// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param> /// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns> /// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static int Clamp(this int value, int min, int max) public static Int32 Clamp(this Int32 value, Int32 min, Int32 max)
=> value < min ? min : (value > max ? max : value); => value < min ? min : (value > max ? max : value);
/// <summary> /// <summary>
@ -46,11 +39,8 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>. /// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsBetween<T>(this T value, T min, T max) public static Boolean IsBetween<T>(this T value, T min, T max)
where T : struct, IComparable where T : struct, IComparable => value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
{
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
}
/// <summary> /// <summary>
/// Converts an array of bytes into the given struct type. /// Converts an array of bytes into the given struct type.
@ -58,11 +48,8 @@
/// <typeparam name="T">The type of structure to convert.</typeparam> /// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns> /// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
public static T ToStruct<T>(this byte[] data) public static T ToStruct<T>(this Byte[] data)
where T : struct where T : struct => ToStruct<T>(data, 0, data.Length);
{
return ToStruct<T>(data, 0, data.Length);
}
/// <summary> /// <summary>
/// Converts an array of bytes into the given struct type. /// Converts an array of bytes into the given struct type.
@ -75,22 +62,19 @@
/// A managed object containing the data pointed to by the ptr parameter. /// A managed object containing the data pointed to by the ptr parameter.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">data.</exception> /// <exception cref="ArgumentNullException">data.</exception>
public static T ToStruct<T>(this byte[] data, int offset, int length) public static T ToStruct<T>(this Byte[] data, Int32 offset, Int32 length)
where T : struct where T : struct {
{ if(data == null) {
if (data == null)
throw new ArgumentNullException(nameof(data)); throw new ArgumentNullException(nameof(data));
var buffer = new byte[length];
Array.Copy(data, offset, buffer, 0, buffer.Length);
var handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
try
{
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
} }
finally
{ Byte[] buffer = new Byte[length];
Array.Copy(data, offset, buffer, 0, buffer.Length);
GCHandle handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
try {
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
} finally {
handle.Free(); handle.Free();
} }
} }
@ -101,19 +85,15 @@
/// <typeparam name="T">The type of structure to convert.</typeparam> /// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns> /// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] ToBytes<T>(this T obj) public static Byte[] ToBytes<T>(this T obj)
where T : struct where T : struct {
{ Byte[] data = new Byte[Marshal.SizeOf(obj)];
var data = new byte[Marshal.SizeOf(obj)]; GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try try {
{
Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false); Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
return GetStructBytes<T>(data); return GetStructBytes<T>(data);
} } finally {
finally
{
handle.Free(); handle.Free();
} }
} }
@ -126,38 +106,37 @@
/// A 32-bit unsigned integer equivalent to the ulong /// A 32-bit unsigned integer equivalent to the ulong
/// contained in longBytes. /// contained in longBytes.
/// </returns> /// </returns>
public static uint SwapEndianness(this ulong longBytes) public static UInt32 SwapEndianness(this UInt64 longBytes)
=> (uint) (((longBytes & 0x000000ff) << 24) + => (UInt32)(((longBytes & 0x000000ff) << 24) +
((longBytes & 0x0000ff00) << 8) + ((longBytes & 0x0000ff00) << 8) +
((longBytes & 0x00ff0000) >> 8) + ((longBytes & 0x00ff0000) >> 8) +
((longBytes & 0xff000000) >> 24)); ((longBytes & 0xff000000) >> 24));
private static byte[] GetStructBytes<T>(byte[] data) private static Byte[] GetStructBytes<T>(Byte[] data) {
{ if(data == null) {
if (data == null)
throw new ArgumentNullException(nameof(data)); throw new ArgumentNullException(nameof(data));
}
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
var fields = typeof(T).GetTypeInfo() FieldInfo[] fields = typeof(T).GetTypeInfo()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
#else #else
var fields = typeof(T).GetTypeInfo().DeclaredFields; var fields = typeof(T).GetTypeInfo().DeclaredFields;
#endif #endif
var endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>(); StructEndiannessAttribute endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>();
foreach (var field in fields) foreach(FieldInfo field in fields) {
{ if(endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false)) {
if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false))
continue; continue;
}
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32(); Int32 offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
var length = Marshal.SizeOf(field.FieldType); Int32 length = Marshal.SizeOf(field.FieldType);
endian = endian ?? Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute>(field); endian = endian ?? Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute>(field);
if(endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian || if(endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian)) endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian)) {
{
Array.Reverse(data, offset, length); Array.Reverse(data, offset, length);
} }
} }

View File

@ -1,6 +1,4 @@
namespace Unosquare.Swan using Unosquare.Swan.Attributes;
{
using Attributes;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -9,11 +7,11 @@
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Unosquare.Swan {
/// <summary> /// <summary>
/// Extension methods. /// Extension methods.
/// </summary> /// </summary>
public static partial class Extensions public static partial class Extensions {
{
/// <summary> /// <summary>
/// Iterates over the public, instance, readable properties of the source and /// Iterates over the public, instance, readable properties of the source and
/// tries to write a compatible value to a public, instance, writable property in the destination. /// tries to write a compatible value to a public, instance, writable property in the destination.
@ -22,10 +20,9 @@
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <returns>Number of properties that was copied successful.</returns> /// <returns>Number of properties that was copied successful.</returns>
public static int CopyPropertiesTo<T>(this T source, object target) public static Int32 CopyPropertiesTo<T>(this T source, Object target)
where T : class where T : class {
{ IEnumerable<String> copyable = GetCopyableProperties(target);
var copyable = GetCopyableProperties(target);
return copyable.Any() return copyable.Any()
? CopyOnlyPropertiesTo(source, target, copyable.ToArray()) ? CopyOnlyPropertiesTo(source, target, copyable.ToArray())
: CopyPropertiesTo(source, target, null); : CopyPropertiesTo(source, target, null);
@ -41,7 +38,7 @@
/// <returns> /// <returns>
/// Number of properties that were successfully copied. /// Number of properties that were successfully copied.
/// </returns> /// </returns>
public static int CopyPropertiesTo(this object source, object target, string[] ignoreProperties = null) public static Int32 CopyPropertiesTo(this Object source, Object target, String[] ignoreProperties = null)
=> Components.ObjectMapper.Copy(source, target, null, ignoreProperties); => Components.ObjectMapper.Copy(source, target, null, ignoreProperties);
/// <summary> /// <summary>
@ -52,11 +49,8 @@
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <returns>Number of properties that was copied successful.</returns> /// <returns>Number of properties that was copied successful.</returns>
public static int CopyOnlyPropertiesTo<T>(this T source, object target) public static Int32 CopyOnlyPropertiesTo<T>(this T source, Object target)
where T : class where T : class => CopyOnlyPropertiesTo(source, target, null);
{
return CopyOnlyPropertiesTo(source, target, null);
}
/// <summary> /// <summary>
/// Iterates over the public, instance, readable properties of the source and /// Iterates over the public, instance, readable properties of the source and
@ -68,10 +62,7 @@
/// <returns> /// <returns>
/// Number of properties that were successfully copied. /// Number of properties that were successfully copied.
/// </returns> /// </returns>
public static int CopyOnlyPropertiesTo(this object source, object target, string[] propertiesToCopy) public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, String[] propertiesToCopy) => Components.ObjectMapper.Copy(source, target, propertiesToCopy);
{
return Components.ObjectMapper.Copy(source, target, propertiesToCopy);
}
/// <summary> /// <summary>
/// Copies the properties to new instance of T. /// Copies the properties to new instance of T.
@ -83,11 +74,8 @@
/// The specified type with properties copied. /// The specified type with properties copied.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">source.</exception> /// <exception cref="ArgumentNullException">source.</exception>
public static T DeepClone<T>(this T source, string[] ignoreProperties = null) public static T DeepClone<T>(this T source, String[] ignoreProperties = null)
where T : class where T : class => source.CopyPropertiesToNew<T>(ignoreProperties);
{
return source.CopyPropertiesToNew<T>(ignoreProperties);
}
/// <summary> /// <summary>
/// Copies the properties to new instance of T. /// Copies the properties to new instance of T.
@ -99,19 +87,16 @@
/// The specified type with properties copied. /// The specified type with properties copied.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">source.</exception> /// <exception cref="ArgumentNullException">source.</exception>
public static T CopyPropertiesToNew<T>(this object source, string[] ignoreProperties = null) public static T CopyPropertiesToNew<T>(this Object source, String[] ignoreProperties = null)
where T : class where T : class {
{ if(source == null) {
if (source == null)
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
}
var target = Activator.CreateInstance<T>(); T target = Activator.CreateInstance<T>();
var copyable = target.GetCopyableProperties(); IEnumerable<String> copyable = target.GetCopyableProperties();
if (copyable.Any()) _ = copyable.Any() ? source.CopyOnlyPropertiesTo(target, copyable.ToArray()) : source.CopyPropertiesTo(target, ignoreProperties);
source.CopyOnlyPropertiesTo(target, copyable.ToArray());
else
source.CopyPropertiesTo(target, ignoreProperties);
return target; return target;
} }
@ -126,14 +111,14 @@
/// The specified type with properties copied. /// The specified type with properties copied.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">source.</exception> /// <exception cref="ArgumentNullException">source.</exception>
public static T CopyOnlyPropertiesToNew<T>(this object source, string[] propertiesToCopy) public static T CopyOnlyPropertiesToNew<T>(this Object source, String[] propertiesToCopy)
where T : class where T : class {
{ if(source == null) {
if (source == null)
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
}
var target = Activator.CreateInstance<T>(); T target = Activator.CreateInstance<T>();
source.CopyOnlyPropertiesTo(target, propertiesToCopy); _ = source.CopyOnlyPropertiesTo(target, propertiesToCopy);
return target; return target;
} }
@ -145,13 +130,10 @@
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <param name="ignoreKeys">The ignore keys.</param> /// <param name="ignoreKeys">The ignore keys.</param>
/// <returns>Number of properties that was copied successful.</returns> /// <returns>Number of properties that was copied successful.</returns>
public static int CopyKeyValuePairTo( public static Int32 CopyKeyValuePairTo(
this IDictionary<string, object> source, this IDictionary<String, Object> source,
object target, Object target,
string[] ignoreKeys = null) String[] ignoreKeys = null) => Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
{
return Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
}
/// <summary> /// <summary>
/// Measures the elapsed time of the given action as a TimeSpan /// Measures the elapsed time of the given action as a TimeSpan
@ -162,24 +144,19 @@
/// A time interval that represents a specified time, where the specification is in units of ticks. /// A time interval that represents a specified time, where the specification is in units of ticks.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">target.</exception> /// <exception cref="ArgumentNullException">target.</exception>
public static TimeSpan Benchmark(this Action target) public static TimeSpan Benchmark(this Action target) {
{ if(target == null) {
if (target == null)
throw new ArgumentNullException(nameof(target)); throw new ArgumentNullException(nameof(target));
}
var sw = new Stopwatch(); Stopwatch sw = new Stopwatch();
try try {
{
sw.Start(); sw.Start();
target.Invoke(); target.Invoke();
} } catch {
catch
{
// swallow // swallow
} } finally {
finally
{
sw.Stop(); sw.Stop();
} }
@ -195,13 +172,12 @@
public static void Retry( public static void Retry(
this Action action, this Action action,
TimeSpan retryInterval = default, TimeSpan retryInterval = default,
int retryCount = 3) Int32 retryCount = 3) {
{ if(action == null) {
if (action == null)
throw new ArgumentNullException(nameof(action)); throw new ArgumentNullException(nameof(action));
}
Retry<object>(() => _ = Retry<Object>(() => {
{
action(); action();
return null; return null;
}, },
@ -224,27 +200,25 @@
public static T Retry<T>( public static T Retry<T>(
this Func<T> action, this Func<T> action,
TimeSpan retryInterval = default, TimeSpan retryInterval = default,
int retryCount = 3) Int32 retryCount = 3) {
{ if(action == null) {
if (action == null)
throw new ArgumentNullException(nameof(action)); throw new ArgumentNullException(nameof(action));
}
if (retryInterval == default) if(retryInterval == default) {
retryInterval = TimeSpan.FromSeconds(1); retryInterval = TimeSpan.FromSeconds(1);
}
var exceptions = new List<Exception>(); List<Exception> exceptions = new List<Exception>();
for (var retry = 0; retry < retryCount; retry++) for(Int32 retry = 0; retry < retryCount; retry++) {
{ try {
try if(retry > 0) {
{
if (retry > 0)
Task.Delay(retryInterval).Wait(); Task.Delay(retryInterval).Wait();
}
return action(); return action();
} } catch(Exception ex) {
catch (Exception ex)
{
exceptions.Add(ex); exceptions.Add(ex);
} }
} }
@ -258,19 +232,19 @@
/// <param name="ex">The ex.</param> /// <param name="ex">The ex.</param>
/// <param name="priorMessage">The prior message.</param> /// <param name="priorMessage">The prior message.</param>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns> /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public static string ExceptionMessage(this Exception ex, string priorMessage = "") public static String ExceptionMessage(this Exception ex, String priorMessage = "") {
{ while(true) {
while (true) if(ex == null) {
{
if (ex == null)
throw new ArgumentNullException(nameof(ex)); throw new ArgumentNullException(nameof(ex));
}
var fullMessage = string.IsNullOrWhiteSpace(priorMessage) String fullMessage = String.IsNullOrWhiteSpace(priorMessage)
? ex.Message ? ex.Message
: priorMessage + "\r\n" + ex.Message; : priorMessage + "\r\n" + ex.Message;
if (string.IsNullOrWhiteSpace(ex.InnerException?.Message)) if(String.IsNullOrWhiteSpace(ex.InnerException?.Message)) {
return fullMessage; return fullMessage;
}
ex = ex.InnerException; ex = ex.InnerException;
priorMessage = fullMessage; priorMessage = fullMessage;
@ -285,10 +259,10 @@
/// Array of properties. /// Array of properties.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">model.</exception> /// <exception cref="ArgumentNullException">model.</exception>
public static IEnumerable<string> GetCopyableProperties(this object obj) public static IEnumerable<String> GetCopyableProperties(this Object obj) {
{ if(obj == null) {
if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
}
return Runtime.PropertyTypeCache return Runtime.PropertyTypeCache
.RetrieveAllProperties(obj.GetType(), true) .RetrieveAllProperties(obj.GetType(), true)
@ -304,23 +278,23 @@
/// <returns> /// <returns>
/// <c>true</c> if the specified model is valid; otherwise, <c>false</c>. /// <c>true</c> if the specified model is valid; otherwise, <c>false</c>.
/// </returns> /// </returns>
public static bool IsValid(this object obj) => Runtime.ObjectValidator.IsValid(obj); public static Boolean IsValid(this Object obj) => Runtime.ObjectValidator.IsValid(obj);
internal static void CreateTarget( internal static void CreateTarget(
this object source, this Object source,
Type targetType, Type targetType,
bool includeNonPublic, Boolean includeNonPublic,
ref object target) ref Object target) {
{ switch(source) {
switch (source) case String _:
{
case string _:
break; // do nothing. Simply skip creation break; // do nothing. Simply skip creation
case IList sourceObjectList when targetType.IsArray: // When using arrays, there is no default constructor, attempt to build a compatible array case IList sourceObjectList when targetType.IsArray: // When using arrays, there is no default constructor, attempt to build a compatible array
var elementType = targetType.GetElementType(); Type elementType = targetType.GetElementType();
if (elementType != null) if(elementType != null) {
target = Array.CreateInstance(elementType, sourceObjectList.Count); target = Array.CreateInstance(elementType, sourceObjectList.Count);
}
break; break;
default: default:
target = Activator.CreateInstance(targetType, includeNonPublic); target = Activator.CreateInstance(targetType, includeNonPublic);

View File

@ -1,6 +1,4 @@
namespace Unosquare.Swan.Formatters using Unosquare.Swan.Reflection;
{
using Reflection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -8,6 +6,7 @@
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// Represents a reader designed for CSV text. /// Represents a reader designed for CSV text.
/// It is capable of deserializing objects from individual lines of CSV text, /// It is capable of deserializing objects from individual lines of CSV text,
@ -66,19 +65,18 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public class CsvReader : IDisposable public class CsvReader : IDisposable {
{
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache(); private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
private readonly object _syncLock = new object(); private readonly Object _syncLock = new Object();
private ulong _count; private UInt64 _count;
private char _escapeCharacter = '"'; private Char _escapeCharacter = '"';
private char _separatorCharacter = ','; private Char _separatorCharacter = ',';
private bool _hasDisposed; // To detect redundant calls private Boolean _hasDisposed; // To detect redundant calls
private string[] _headings; private String[] _headings;
private Dictionary<string, string> _defaultMap; private Dictionary<String, String> _defaultMap;
private StreamReader _reader; private StreamReader _reader;
#region Constructors #region Constructors
@ -89,15 +87,16 @@
/// <param name="inputStream">The stream.</param> /// <param name="inputStream">The stream.</param>
/// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param> /// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param>
/// <param name="textEncoding">The text encoding.</param> /// <param name="textEncoding">The text encoding.</param>
public CsvReader(Stream inputStream, bool leaveOpen, Encoding textEncoding) public CsvReader(Stream inputStream, Boolean leaveOpen, Encoding textEncoding) {
{ if(inputStream == null) {
if (inputStream == null)
throw new ArgumentNullException(nameof(inputStream)); throw new ArgumentNullException(nameof(inputStream));
}
if (textEncoding == null) if(textEncoding == null) {
throw new ArgumentNullException(nameof(textEncoding)); throw new ArgumentNullException(nameof(textEncoding));
}
_reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen); this._reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
} }
/// <summary> /// <summary>
@ -107,8 +106,7 @@
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
/// <param name="textEncoding">The text encoding.</param> /// <param name="textEncoding">The text encoding.</param>
public CsvReader(Stream stream, Encoding textEncoding) public CsvReader(Stream stream, Encoding textEncoding)
: this(stream, false, textEncoding) : this(stream, false, textEncoding) {
{
// placeholder // placeholder
} }
@ -119,8 +117,7 @@
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
public CsvReader(Stream stream) public CsvReader(Stream stream)
: this(stream, false, Definitions.Windows1252Encoding) : this(stream, false, Definitions.Windows1252Encoding) {
{
} }
/// <summary> /// <summary>
@ -129,9 +126,8 @@
/// when this reader is disposed of. /// when this reader is disposed of.
/// </summary> /// </summary>
/// <param name="filename">The filename.</param> /// <param name="filename">The filename.</param>
public CsvReader(string filename) public CsvReader(String filename)
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding) : this(File.OpenRead(filename), false, Definitions.Windows1252Encoding) {
{
// placeholder // placeholder
} }
@ -141,9 +137,8 @@
/// </summary> /// </summary>
/// <param name="filename">The filename.</param> /// <param name="filename">The filename.</param>
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
public CsvReader(string filename, Encoding encoding) public CsvReader(String filename, Encoding encoding)
: this(File.OpenRead(filename), false, encoding) : this(File.OpenRead(filename), false, encoding) {
{
// placeholder // placeholder
} }
@ -157,13 +152,10 @@
/// <value> /// <value>
/// The count. /// The count.
/// </value> /// </value>
public ulong Count public UInt64 Count {
{ get {
get lock(this._syncLock) {
{ return this._count;
lock (_syncLock)
{
return _count;
} }
} }
} }
@ -175,14 +167,11 @@
/// <value> /// <value>
/// The escape character. /// The escape character.
/// </value> /// </value>
public char EscapeCharacter public Char EscapeCharacter {
{ get => this._escapeCharacter;
get => _escapeCharacter; set {
set lock(this._syncLock) {
{ this._escapeCharacter = value;
lock (_syncLock)
{
_escapeCharacter = value;
} }
} }
} }
@ -194,14 +183,11 @@
/// <value> /// <value>
/// The separator character. /// The separator character.
/// </value> /// </value>
public char SeparatorCharacter public Char SeparatorCharacter {
{ get => this._separatorCharacter;
get => _separatorCharacter; set {
set lock(this._syncLock) {
{ this._separatorCharacter = value;
lock (_syncLock)
{
_separatorCharacter = value;
} }
} }
} }
@ -213,13 +199,10 @@
/// <value> /// <value>
/// <c>true</c> if [end of stream]; otherwise, <c>false</c>. /// <c>true</c> if [end of stream]; otherwise, <c>false</c>.
/// </value> /// </value>
public bool EndOfStream public Boolean EndOfStream {
{ get {
get lock(this._syncLock) {
{ return this._reader.EndOfStream;
lock (_syncLock)
{
return _reader.EndOfStream;
} }
} }
} }
@ -233,15 +216,14 @@
/// </summary> /// </summary>
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns> /// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public string[] ReadLine() public String[] ReadLine() {
{ lock(this._syncLock) {
lock (_syncLock) if(this._reader.EndOfStream) {
{
if (_reader.EndOfStream)
throw new EndOfStreamException("Cannot read past the end of the stream"); throw new EndOfStreamException("Cannot read past the end of the stream");
}
var values = ParseRecord(_reader, _escapeCharacter, _separatorCharacter); String[] values = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
_count++; this._count++;
return values; return values;
} }
} }
@ -257,14 +239,13 @@
/// as the first read operation (i.e. while count is still 0). /// as the first read operation (i.e. while count is still 0).
/// </summary> /// </summary>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public void SkipRecord() public void SkipRecord() {
{ lock(this._syncLock) {
lock (_syncLock) if(this._reader.EndOfStream) {
{
if (_reader.EndOfStream)
throw new EndOfStreamException("Cannot read past the end of the stream"); throw new EndOfStreamException("Cannot read past the end of the stream");
}
ParseRecord(_reader, _escapeCharacter, _separatorCharacter); _ = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
} }
} }
@ -279,20 +260,20 @@
/// ReadHeadings. /// ReadHeadings.
/// </exception> /// </exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public string[] ReadHeadings() public String[] ReadHeadings() {
{ lock(this._syncLock) {
lock (_syncLock) if(this._headings != null) {
{
if (_headings != null)
throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called."); throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called.");
}
if (_count != 0) if(this._count != 0) {
throw new InvalidOperationException("Reading headings is only supported as the first read operation."); throw new InvalidOperationException("Reading headings is only supported as the first read operation.");
}
_headings = ReadLine(); this._headings = this.ReadLine();
_defaultMap = _headings.ToDictionary(x => x, x => x); this._defaultMap = this._headings.ToDictionary(x => x, x => x);
return _headings.ToArray(); return this._headings.ToArray();
} }
} }
@ -304,25 +285,25 @@
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception> /// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
/// <exception cref="System.ArgumentNullException">map.</exception> /// <exception cref="System.ArgumentNullException">map.</exception>
public IDictionary<string, object> ReadObject(IDictionary<string, string> map) public IDictionary<String, Object> ReadObject(IDictionary<String, String> map) {
{ lock(this._syncLock) {
lock (_syncLock) if(this._headings == null) {
{
if (_headings == null)
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object."); throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
}
if (map == null) if(map == null) {
throw new ArgumentNullException(nameof(map)); throw new ArgumentNullException(nameof(map));
}
var result = new Dictionary<string, object>(); Dictionary<String, Object> result = new Dictionary<String, Object>();
var values = ReadLine(); String[] values = this.ReadLine();
for (var i = 0; i < _headings.Length; i++) for(Int32 i = 0; i < this._headings.Length; i++) {
{ if(i > values.Length - 1) {
if (i > values.Length - 1)
break; break;
}
result[_headings[i]] = values[i]; result[this._headings[i]] = values[i];
} }
return result; return result;
@ -334,7 +315,7 @@
/// The property names correspond to the names of the CSV headings. /// The property names correspond to the names of the CSV headings.
/// </summary> /// </summary>
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns> /// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
public IDictionary<string, object> ReadObject() => ReadObject(_defaultMap); public IDictionary<String, Object> ReadObject() => this.ReadObject(this._defaultMap);
/// <summary> /// <summary>
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary) /// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
@ -349,49 +330,52 @@
/// result.</exception> /// result.</exception>
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception> /// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public void ReadObject<T>(IDictionary<string, string> map, ref T result) public void ReadObject<T>(IDictionary<String, String> map, ref T result) {
{ lock(this._syncLock) {
lock (_syncLock)
{
// Check arguments // Check arguments
{ {
if (map == null) if(map == null) {
throw new ArgumentNullException(nameof(map)); throw new ArgumentNullException(nameof(map));
}
if (_reader.EndOfStream) if(this._reader.EndOfStream) {
throw new EndOfStreamException("Cannot read past the end of the stream"); throw new EndOfStreamException("Cannot read past the end of the stream");
}
if (_headings == null) if(this._headings == null) {
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object."); throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
}
if (Equals(result, default(T))) if(Equals(result, default(T))) {
throw new ArgumentNullException(nameof(result)); throw new ArgumentNullException(nameof(result));
} }
}
// Read line and extract values // Read line and extract values
var values = ReadLine(); String[] values = this.ReadLine();
// Extract properties from cache // Extract properties from cache
var properties = TypeCache IEnumerable<PropertyInfo> properties = TypeCache
.RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.ContainsKey(x.PropertyType)); .RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.ContainsKey(x.PropertyType));
// Assign property values for each heading // Assign property values for each heading
for (var i = 0; i < _headings.Length; i++) for(Int32 i = 0; i < this._headings.Length; i++) {
{
// break if no more headings are matched // break if no more headings are matched
if (i > values.Length - 1) if(i > values.Length - 1) {
break; break;
}
// skip if no heading is available or the heading is empty // skip if no heading is available or the heading is empty
if (map.ContainsKey(_headings[i]) == false && if(map.ContainsKey(this._headings[i]) == false &&
string.IsNullOrWhiteSpace(map[_headings[i]]) == false) String.IsNullOrWhiteSpace(map[this._headings[i]]) == false) {
continue; continue;
}
// Prepare the target property // Prepare the target property
var propertyName = map[_headings[i]]; String propertyName = map[this._headings[i]];
// Parse and assign the basic type value to the property if exists // Parse and assign the basic type value to the property if exists
properties _ = properties
.FirstOrDefault(p => p.Name.Equals(propertyName))? .FirstOrDefault(p => p.Name.Equals(propertyName))?
.TrySetBasicType(values[i], result); .TrySetBasicType(values[i], result);
} }
@ -409,11 +393,10 @@
/// <exception cref="System.ArgumentNullException">map.</exception> /// <exception cref="System.ArgumentNullException">map.</exception>
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception> /// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception> /// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public T ReadObject<T>(IDictionary<string, string> map) public T ReadObject<T>(IDictionary<String, String> map)
where T : new() where T : new() {
{ T result = Activator.CreateInstance<T>();
var result = Activator.CreateInstance<T>(); this.ReadObject(map, ref result);
ReadObject(map, ref result);
return result; return result;
} }
@ -424,10 +407,7 @@
/// <typeparam name="T">The type of object.</typeparam> /// <typeparam name="T">The type of object.</typeparam>
/// <returns>The conversion of specific type of object.</returns> /// <returns>The conversion of specific type of object.</returns>
public T ReadObject<T>() public T ReadObject<T>()
where T : new() where T : new() => this.ReadObject<T>(this._defaultMap);
{
return ReadObject<T>(_defaultMap);
}
#endregion #endregion
@ -441,89 +421,76 @@
/// <param name="escapeCharacter">The escape character.</param> /// <param name="escapeCharacter">The escape character.</param>
/// <param name="separatorCharacter">The separator character.</param> /// <param name="separatorCharacter">The separator character.</param>
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns> /// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
private static string[] ParseRecord(StreamReader reader, char escapeCharacter = '"', char separatorCharacter = ',') private static String[] ParseRecord(StreamReader reader, Char escapeCharacter = '"', Char separatorCharacter = ',') {
{ List<String> values = new List<String>();
var values = new List<string>(); StringBuilder currentValue = new StringBuilder(1024);
var currentValue = new StringBuilder(1024); ReadState currentState = ReadState.WaitingForNewField;
var currentState = ReadState.WaitingForNewField; String line;
string line;
while ((line = reader.ReadLine()) != null) while((line = reader.ReadLine()) != null) {
{ for(Int32 charIndex = 0; charIndex < line.Length; charIndex++) {
for (var charIndex = 0; charIndex < line.Length; charIndex++)
{
// Get the current and next character // Get the current and next character
var currentChar = line[charIndex]; Char currentChar = line[charIndex];
var nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new char?(); Char? nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new global::System.Char?();
// Perform logic based on state and decide on next state // Perform logic based on state and decide on next state
switch (currentState) switch(currentState) {
{ case ReadState.WaitingForNewField: {
case ReadState.WaitingForNewField: _ = currentValue.Clear();
{
currentValue.Clear();
if (currentChar == escapeCharacter) if(currentChar == escapeCharacter) {
{
currentState = ReadState.PushingQuoted; currentState = ReadState.PushingQuoted;
continue; continue;
} }
if (currentChar == separatorCharacter) if(currentChar == separatorCharacter) {
{
values.Add(currentValue.ToString()); values.Add(currentValue.ToString());
currentState = ReadState.WaitingForNewField; currentState = ReadState.WaitingForNewField;
continue; continue;
} }
currentValue.Append(currentChar); _ = currentValue.Append(currentChar);
currentState = ReadState.PushingNormal; currentState = ReadState.PushingNormal;
continue; continue;
} }
case ReadState.PushingNormal: case ReadState.PushingNormal: {
{
// Handle field content delimiter by comma // Handle field content delimiter by comma
if (currentChar == separatorCharacter) if(currentChar == separatorCharacter) {
{
currentState = ReadState.WaitingForNewField; currentState = ReadState.WaitingForNewField;
values.Add(currentValue.ToString()); values.Add(currentValue.ToString());
currentValue.Clear(); _ = currentValue.Clear();
continue; continue;
} }
// Handle double quote escaping // Handle double quote escaping
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
{
// advance 1 character now. The loop will advance one more. // advance 1 character now. The loop will advance one more.
currentValue.Append(currentChar); _ = currentValue.Append(currentChar);
charIndex++; charIndex++;
continue; continue;
} }
currentValue.Append(currentChar); _ = currentValue.Append(currentChar);
break; break;
} }
case ReadState.PushingQuoted: case ReadState.PushingQuoted: {
{
// Handle field content delimiter by ending double quotes // Handle field content delimiter by ending double quotes
if (currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter)) if(currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter)) {
{
currentState = ReadState.PushingNormal; currentState = ReadState.PushingNormal;
continue; continue;
} }
// Handle double quote escaping // Handle double quote escaping
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
{
// advance 1 character now. The loop will advance one more. // advance 1 character now. The loop will advance one more.
currentValue.Append(currentChar); _ = currentValue.Append(currentChar);
charIndex++; charIndex++;
continue; continue;
} }
currentValue.Append(currentChar); _ = currentValue.Append(currentChar);
break; break;
} }
} }
@ -531,17 +498,14 @@
// determine if we need to continue reading a new line if it is part of the quoted // determine if we need to continue reading a new line if it is part of the quoted
// field value // field value
if (currentState == ReadState.PushingQuoted) if(currentState == ReadState.PushingQuoted) {
{
// we need to add the new line sequence to the output of the field // we need to add the new line sequence to the output of the field
// because we were pushing a quoted value // because we were pushing a quoted value
currentValue.Append(Environment.NewLine); _ = currentValue.Append(Environment.NewLine);
} } else {
else
{
// push anything that has not been pushed (flush) into a last value // push anything that has not been pushed (flush) into a last value
values.Add(currentValue.ToString()); values.Add(currentValue.ToString());
currentValue.Clear(); _ = currentValue.Clear();
// stop reading more lines we have reached the end of the CSV record // stop reading more lines we have reached the end of the CSV record
break; break;
@ -550,8 +514,7 @@
// If we ended up pushing quoted and no closing quotes we might // If we ended up pushing quoted and no closing quotes we might
// have additional text in yt // have additional text in yt
if (currentValue.Length > 0) if(currentValue.Length > 0) {
{
values.Add(currentValue.ToString()); values.Add(currentValue.ToString());
} }
@ -570,15 +533,12 @@
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
/// <returns>A generic collection of objects that can be individually accessed by index.</returns> /// <returns>A generic collection of objects that can be individually accessed by index.</returns>
public static IList<T> LoadRecords<T>(Stream stream) public static IList<T> LoadRecords<T>(Stream stream)
where T : new() where T : new() {
{ List<T> result = new List<T>();
var result = new List<T>();
using (var reader = new CsvReader(stream)) using(CsvReader reader = new CsvReader(stream)) {
{ _ = reader.ReadHeadings();
reader.ReadHeadings(); while(!reader.EndOfStream) {
while (!reader.EndOfStream)
{
result.Add(reader.ReadObject<T>()); result.Add(reader.ReadObject<T>());
} }
} }
@ -593,11 +553,8 @@
/// <typeparam name="T">The type of IList items to load.</typeparam> /// <typeparam name="T">The type of IList items to load.</typeparam>
/// <param name="filePath">The file path.</param> /// <param name="filePath">The file path.</param>
/// <returns>A generic collection of objects that can be individually accessed by index.</returns> /// <returns>A generic collection of objects that can be individually accessed by index.</returns>
public static IList<T> LoadRecords<T>(string filePath) public static IList<T> LoadRecords<T>(String filePath)
where T : new() where T : new() => LoadRecords<T>(File.OpenRead(filePath));
{
return LoadRecords<T>(File.OpenRead(filePath));
}
#endregion #endregion
@ -607,32 +564,26 @@
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(Boolean disposing) {
{ if(this._hasDisposed) {
if (_hasDisposed) return; return;
if (disposing)
{
try
{
_reader.Dispose();
} }
finally
{ if(disposing) {
_reader = null; try {
this._reader.Dispose();
} finally {
this._reader = null;
} }
} }
_hasDisposed = true; this._hasDisposed = true;
} }
/// <summary> /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// </summary>
public void Dispose() public void Dispose() => this.Dispose(true);
{
Dispose(true);
}
#endregion #endregion
@ -640,8 +591,7 @@
/// Defines the 3 different read states /// Defines the 3 different read states
/// for the parsing state machine. /// for the parsing state machine.
/// </summary> /// </summary>
private enum ReadState private enum ReadState {
{
WaitingForNewField, WaitingForNewField,
PushingNormal, PushingNormal,
PushingQuoted, PushingQuoted,

View File

@ -1,6 +1,4 @@
namespace Unosquare.Swan.Formatters using Unosquare.Swan.Reflection;
{
using Reflection;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -9,6 +7,7 @@
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A CSV writer useful for exporting a set of objects. /// A CSV writer useful for exporting a set of objects.
/// </summary> /// </summary>
@ -46,16 +45,15 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public class CsvWriter : IDisposable public class CsvWriter : IDisposable {
{
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache(); private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
private readonly object _syncLock = new object(); private readonly Object _syncLock = new Object();
private readonly Stream _outputStream; private readonly Stream _outputStream;
private readonly Encoding _encoding; private readonly Encoding _encoding;
private readonly bool _leaveStreamOpen; private readonly Boolean _leaveStreamOpen;
private bool _isDisposing; private Boolean _isDisposing;
private ulong _mCount; private UInt64 _mCount;
#region Constructors #region Constructors
@ -65,11 +63,10 @@
/// <param name="outputStream">The output stream.</param> /// <param name="outputStream">The output stream.</param>
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param> /// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
public CsvWriter(Stream outputStream, bool leaveOpen, Encoding encoding) public CsvWriter(Stream outputStream, Boolean leaveOpen, Encoding encoding) {
{ this._outputStream = outputStream;
_outputStream = outputStream; this._encoding = encoding;
_encoding = encoding; this._leaveStreamOpen = leaveOpen;
_leaveStreamOpen = leaveOpen;
} }
/// <summary> /// <summary>
@ -79,8 +76,7 @@
/// <param name="outputStream">The output stream.</param> /// <param name="outputStream">The output stream.</param>
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
public CsvWriter(Stream outputStream, Encoding encoding) public CsvWriter(Stream outputStream, Encoding encoding)
: this(outputStream, false, encoding) : this(outputStream, false, encoding) {
{
// placeholder // placeholder
} }
@ -91,8 +87,7 @@
/// </summary> /// </summary>
/// <param name="outputStream">The output stream.</param> /// <param name="outputStream">The output stream.</param>
public CsvWriter(Stream outputStream) public CsvWriter(Stream outputStream)
: this(outputStream, false, Definitions.Windows1252Encoding) : this(outputStream, false, Definitions.Windows1252Encoding) {
{
// placeholder // placeholder
} }
@ -102,9 +97,8 @@
/// disposing of this writer, and uses the Windows 1252 encoding. /// disposing of this writer, and uses the Windows 1252 encoding.
/// </summary> /// </summary>
/// <param name="filename">The filename.</param> /// <param name="filename">The filename.</param>
public CsvWriter(string filename) public CsvWriter(String filename)
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) : this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) {
{
// placeholder // placeholder
} }
@ -115,9 +109,8 @@
/// </summary> /// </summary>
/// <param name="filename">The filename.</param> /// <param name="filename">The filename.</param>
/// <param name="encoding">The encoding.</param> /// <param name="encoding">The encoding.</param>
public CsvWriter(string filename, Encoding encoding) public CsvWriter(String filename, Encoding encoding)
: this(File.OpenWrite(filename), false, encoding) : this(File.OpenWrite(filename), false, encoding) {
{
// placeholder // placeholder
} }
@ -131,7 +124,7 @@
/// <value> /// <value>
/// The separator character. /// The separator character.
/// </value> /// </value>
public char SeparatorCharacter { get; set; } = ','; public Char SeparatorCharacter { get; set; } = ',';
/// <summary> /// <summary>
/// Gets or sets the escape character to use to escape field values. /// Gets or sets the escape character to use to escape field values.
@ -139,7 +132,7 @@
/// <value> /// <value>
/// The escape character. /// The escape character.
/// </value> /// </value>
public char EscapeCharacter { get; set; } = '"'; public Char EscapeCharacter { get; set; } = '"';
/// <summary> /// <summary>
/// Gets or sets the new line character sequence to use when writing a line. /// Gets or sets the new line character sequence to use when writing a line.
@ -147,7 +140,7 @@
/// <value> /// <value>
/// The new line sequence. /// The new line sequence.
/// </value> /// </value>
public string NewLineSequence { get; set; } = Environment.NewLine; public String NewLineSequence { get; set; } = Environment.NewLine;
/// <summary> /// <summary>
/// Defines a list of properties to ignore when outputting CSV lines. /// Defines a list of properties to ignore when outputting CSV lines.
@ -155,7 +148,7 @@
/// <value> /// <value>
/// The ignore property names. /// The ignore property names.
/// </value> /// </value>
public List<string> IgnorePropertyNames { get; } = new List<string>(); public List<String> IgnorePropertyNames { get; } = new List<String>();
/// <summary> /// <summary>
/// Gets number of lines that have been written, including the headings line. /// Gets number of lines that have been written, including the headings line.
@ -163,13 +156,10 @@
/// <value> /// <value>
/// The count. /// The count.
/// </value> /// </value>
public ulong Count public UInt64 Count {
{ get {
get lock(this._syncLock) {
{ return this._mCount;
lock (_syncLock)
{
return _mCount;
} }
} }
} }
@ -187,17 +177,16 @@
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param> /// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
/// <returns>Number of item saved.</returns> /// <returns>Number of item saved.</returns>
public static int SaveRecords<T>(IEnumerable<T> items, Stream stream, bool truncateData = false) public static Int32 SaveRecords<T>(IEnumerable<T> items, Stream stream, Boolean truncateData = false) {
{
// truncate the file if it had data // truncate the file if it had data
if (truncateData && stream.Length > 0) if(truncateData && stream.Length > 0) {
stream.SetLength(0); stream.SetLength(0);
}
using (var writer = new CsvWriter(stream)) using(CsvWriter writer = new CsvWriter(stream)) {
{
writer.WriteHeadings<T>(); writer.WriteHeadings<T>();
writer.WriteObjects(items); writer.WriteObjects(items);
return (int)writer.Count; return (Int32)writer.Count;
} }
} }
@ -210,7 +199,7 @@
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
/// <param name="filePath">The file path.</param> /// <param name="filePath">The file path.</param>
/// <returns>Number of item saved.</returns> /// <returns>Number of item saved.</returns>
public static int SaveRecords<T>(IEnumerable<T> items, string filePath) => SaveRecords(items, File.OpenWrite(filePath), true); public static Int32 SaveRecords<T>(IEnumerable<T> items, String filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
#endregion #endregion
@ -222,8 +211,8 @@
/// If items are not string, the ToStringInvariant() method is called on them. /// If items are not string, the ToStringInvariant() method is called on them.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
public void WriteLine(params object[] items) public void WriteLine(params Object[] items)
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant())); => this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
/// <summary> /// <summary>
/// Writes a line of CSV text. Items are converted to strings. /// Writes a line of CSV text. Items are converted to strings.
@ -231,67 +220,66 @@
/// If items are not string, the ToStringInvariant() method is called on them. /// If items are not string, the ToStringInvariant() method is called on them.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
public void WriteLine(IEnumerable<object> items) public void WriteLine(IEnumerable<Object> items)
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant())); => this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
/// <summary> /// <summary>
/// Writes a line of CSV text. /// Writes a line of CSV text.
/// If items are found to be null, empty strings are written out. /// If items are found to be null, empty strings are written out.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
public void WriteLine(params string[] items) => WriteLine((IEnumerable<string>) items); public void WriteLine(params String[] items) => this.WriteLine((IEnumerable<String>)items);
/// <summary> /// <summary>
/// Writes a line of CSV text. /// Writes a line of CSV text.
/// If items are found to be null, empty strings are written out. /// If items are found to be null, empty strings are written out.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
public void WriteLine(IEnumerable<string> items) public void WriteLine(IEnumerable<String> items) {
{ lock(this._syncLock) {
lock (_syncLock) Int32 length = items.Count();
{ Byte[] separatorBytes = this._encoding.GetBytes(new[] { this.SeparatorCharacter });
var length = items.Count(); Byte[] endOfLineBytes = this._encoding.GetBytes(this.NewLineSequence);
var separatorBytes = _encoding.GetBytes(new[] { SeparatorCharacter });
var endOfLineBytes = _encoding.GetBytes(NewLineSequence);
// Declare state variables here to avoid recreation, allocation and // Declare state variables here to avoid recreation, allocation and
// reassignment in every loop // reassignment in every loop
bool needsEnclosing; Boolean needsEnclosing;
string textValue; String textValue;
byte[] output; Byte[] output;
for (var i = 0; i < length; i++) for(Int32 i = 0; i < length; i++) {
{
textValue = items.ElementAt(i); textValue = items.ElementAt(i);
// Determine if we need the string to be enclosed // Determine if we need the string to be enclosed
// (it either contains an escape, new line, or separator char) // (it either contains an escape, new line, or separator char)
needsEnclosing = textValue.IndexOf(SeparatorCharacter) >= 0 needsEnclosing = textValue.IndexOf(this.SeparatorCharacter) >= 0
|| textValue.IndexOf(EscapeCharacter) >= 0 || textValue.IndexOf(this.EscapeCharacter) >= 0
|| textValue.IndexOf('\r') >= 0 || textValue.IndexOf('\r') >= 0
|| textValue.IndexOf('\n') >= 0; || textValue.IndexOf('\n') >= 0;
// Escape the escape characters by repeating them twice for every instance // Escape the escape characters by repeating them twice for every instance
textValue = textValue.Replace($"{EscapeCharacter}", textValue = textValue.Replace($"{this.EscapeCharacter}",
$"{EscapeCharacter}{EscapeCharacter}"); $"{this.EscapeCharacter}{this.EscapeCharacter}");
// Enclose the text value if we need to // Enclose the text value if we need to
if (needsEnclosing) if(needsEnclosing) {
textValue = string.Format($"{EscapeCharacter}{textValue}{EscapeCharacter}", textValue); textValue = String.Format($"{this.EscapeCharacter}{textValue}{this.EscapeCharacter}", textValue);
}
// Get the bytes to write to the stream and write them // Get the bytes to write to the stream and write them
output = _encoding.GetBytes(textValue); output = this._encoding.GetBytes(textValue);
_outputStream.Write(output, 0, output.Length); this._outputStream.Write(output, 0, output.Length);
// only write a separator if we are moving in between values. // only write a separator if we are moving in between values.
// the last value should not be written. // the last value should not be written.
if (i < length - 1) if(i < length - 1) {
_outputStream.Write(separatorBytes, 0, separatorBytes.Length); this._outputStream.Write(separatorBytes, 0, separatorBytes.Length);
}
} }
// output the newline sequence // output the newline sequence
_outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length); this._outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
_mCount += 1; this._mCount += 1;
} }
} }
@ -307,23 +295,21 @@
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <exception cref="System.ArgumentNullException">item.</exception> /// <exception cref="System.ArgumentNullException">item.</exception>
public void WriteObject(object item) public void WriteObject(Object item) {
{ if(item == null) {
if (item == null)
throw new ArgumentNullException(nameof(item)); throw new ArgumentNullException(nameof(item));
}
lock (_syncLock) lock(this._syncLock) {
{ switch(item) {
switch (item)
{
case IDictionary typedItem: case IDictionary typedItem:
WriteLine(GetFilteredDictionary(typedItem)); this.WriteLine(this.GetFilteredDictionary(typedItem));
return; return;
case ICollection typedItem: case ICollection typedItem:
WriteLine(typedItem.Cast<object>()); this.WriteLine(typedItem.Cast<Object>());
return; return;
default: default:
WriteLine(GetFilteredTypeProperties(item.GetType()) this.WriteLine(this.GetFilteredTypeProperties(item.GetType())
.Select(x => x.ToFormattedString(item))); .Select(x => x.ToFormattedString(item)));
break; break;
} }
@ -338,7 +324,7 @@
/// </summary> /// </summary>
/// <typeparam name="T">The type of object to write.</typeparam> /// <typeparam name="T">The type of object to write.</typeparam>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
public void WriteObject<T>(T item) => WriteObject(item as object); public void WriteObject<T>(T item) => this.WriteObject(item as Object);
/// <summary> /// <summary>
/// Writes a set of items, one per line and atomically by repeatedly calling the /// Writes a set of items, one per line and atomically by repeatedly calling the
@ -347,12 +333,11 @@
/// </summary> /// </summary>
/// <typeparam name="T">The type of object to write.</typeparam> /// <typeparam name="T">The type of object to write.</typeparam>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
public void WriteObjects<T>(IEnumerable<T> items) public void WriteObjects<T>(IEnumerable<T> items) {
{ lock(this._syncLock) {
lock (_syncLock) foreach(T item in items) {
{ this.WriteObject(item);
foreach (var item in items) }
WriteObject(item);
} }
} }
@ -365,32 +350,32 @@
/// </summary> /// </summary>
/// <param name="type">The type of object to extract headings.</param> /// <param name="type">The type of object to extract headings.</param>
/// <exception cref="System.ArgumentNullException">type.</exception> /// <exception cref="System.ArgumentNullException">type.</exception>
public void WriteHeadings(Type type) public void WriteHeadings(Type type) {
{ if(type == null) {
if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
}
var properties = GetFilteredTypeProperties(type).Select(p => p.Name).Cast<object>(); IEnumerable<Object> properties = this.GetFilteredTypeProperties(type).Select(p => p.Name).Cast<Object>();
WriteLine(properties); this.WriteLine(properties);
} }
/// <summary> /// <summary>
/// Writes the headings. /// Writes the headings.
/// </summary> /// </summary>
/// <typeparam name="T">The type of object to extract headings.</typeparam> /// <typeparam name="T">The type of object to extract headings.</typeparam>
public void WriteHeadings<T>() => WriteHeadings(typeof(T)); public void WriteHeadings<T>() => this.WriteHeadings(typeof(T));
/// <summary> /// <summary>
/// Writes the headings. /// Writes the headings.
/// </summary> /// </summary>
/// <param name="dictionary">The dictionary to extract headings.</param> /// <param name="dictionary">The dictionary to extract headings.</param>
/// <exception cref="System.ArgumentNullException">dictionary.</exception> /// <exception cref="System.ArgumentNullException">dictionary.</exception>
public void WriteHeadings(IDictionary dictionary) public void WriteHeadings(IDictionary dictionary) {
{ if(dictionary == null) {
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary)); throw new ArgumentNullException(nameof(dictionary));
}
WriteLine(GetFilteredDictionary(dictionary, true)); this.WriteLine(this.GetFilteredDictionary(dictionary, true));
} }
#if NET452 #if NET452
@ -400,15 +385,16 @@
/// <param name="item">The object to extract headings.</param> /// <param name="item">The object to extract headings.</param>
/// <exception cref="ArgumentNullException">item</exception> /// <exception cref="ArgumentNullException">item</exception>
/// <exception cref="ArgumentException">Unable to cast dynamic object to a suitable dictionary - item</exception> /// <exception cref="ArgumentException">Unable to cast dynamic object to a suitable dictionary - item</exception>
public void WriteHeadings(dynamic item) public void WriteHeadings(dynamic item) {
{ if(item == null) {
if (item == null)
throw new ArgumentNullException(nameof(item)); throw new ArgumentNullException(nameof(item));
}
if (!(item is IDictionary<string, object> dictionary)) if(!(item is IDictionary<global::System.String, global::System.Object> dictionary)) {
throw new ArgumentException("Unable to cast dynamic object to a suitable dictionary", nameof(item)); throw new ArgumentException("Unable to cast dynamic object to a suitable dictionary", nameof(item));
}
WriteHeadings(dictionary); this.WriteHeadings(dictionary);
} }
#else #else
/// <summary> /// <summary>
@ -430,47 +416,46 @@
#region IDisposable Support #region IDisposable Support
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() => Dispose(true); public void Dispose() => this.Dispose(true);
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="disposeAlsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <param name="disposeAlsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposeAlsoManaged) protected virtual void Dispose(Boolean disposeAlsoManaged) {
{ if(this._isDisposing) {
if (_isDisposing) return; return;
}
if (disposeAlsoManaged) if(disposeAlsoManaged) {
{ if(this._leaveStreamOpen == false) {
if (_leaveStreamOpen == false) this._outputStream.Dispose();
{
_outputStream.Dispose();
} }
} }
_isDisposing = true; this._isDisposing = true;
} }
#endregion #endregion
#region Support Methods #region Support Methods
private IEnumerable<string> GetFilteredDictionary(IDictionary dictionary, bool filterKeys = false) private IEnumerable<String> GetFilteredDictionary(IDictionary dictionary, Boolean filterKeys = false)
=> dictionary => dictionary
.Keys .Keys
.Cast<object>() .Cast<Object>()
.Select(key => key == null ? string.Empty : key.ToStringInvariant()) .Select(key => key == null ? String.Empty : key.ToStringInvariant())
.Where(stringKey => !IgnorePropertyNames.Contains(stringKey)) .Where(stringKey => !this.IgnorePropertyNames.Contains(stringKey))
.Select(stringKey => .Select(stringKey =>
filterKeys filterKeys
? stringKey ? stringKey
: dictionary[stringKey] == null ? string.Empty : dictionary[stringKey].ToStringInvariant()); : dictionary[stringKey] == null ? String.Empty : dictionary[stringKey].ToStringInvariant());
private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type) private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type)
=> TypeCache.Retrieve(type, t => => TypeCache.Retrieve(type, t =>
t.GetProperties(BindingFlags.Public | BindingFlags.Instance) t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead)) .Where(p => p.CanRead))
.Where(p => !IgnorePropertyNames.Contains(p.Name)); .Where(p => !this.IgnorePropertyNames.Contains(p.Name));
#endregion #endregion

View File

@ -1,149 +1,133 @@
namespace Unosquare.Swan.Formatters using System;
{
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
internal class HumanizeJson namespace Unosquare.Swan.Formatters {
{ internal class HumanizeJson {
private readonly StringBuilder _builder = new StringBuilder(); private readonly StringBuilder _builder = new StringBuilder();
private readonly int _indent; private readonly Int32 _indent;
private readonly string _indentStr; private readonly String _indentStr;
private readonly object _obj; private readonly Object _obj;
public HumanizeJson(object obj, int indent) public HumanizeJson(Object obj, Int32 indent) {
{ if(obj == null) {
if (obj == null)
{
return; return;
} }
_indent = indent; this._indent = indent;
_indentStr = new string(' ', indent * 4); this._indentStr = new String(' ', indent * 4);
_obj = obj; this._obj = obj;
ParseObject(); this.ParseObject();
} }
public string GetResult() => _builder == null ? string.Empty : _builder.ToString().TrimEnd(); public String GetResult() => this._builder == null ? String.Empty : this._builder.ToString().TrimEnd();
private void ParseObject() private void ParseObject() {
{ switch(this._obj) {
switch (_obj) case Dictionary<String, Object> dictionary:
{ this.AppendDictionary(dictionary);
case Dictionary<string, object> dictionary:
AppendDictionary(dictionary);
break; break;
case List<object> list: case List<Object> list:
AppendList(list); this.AppendList(list);
break; break;
default: default:
AppendString(); this.AppendString();
break; break;
} }
} }
private void AppendDictionary(Dictionary<string, object> objects) private void AppendDictionary(Dictionary<String, Object> objects) {
{ foreach(KeyValuePair<String, Object> kvp in objects) {
foreach (var kvp in objects) if(kvp.Value == null) {
{ continue;
if (kvp.Value == null) continue; }
var writeOutput = false; Boolean writeOutput = false;
switch (kvp.Value) switch(kvp.Value) {
{ case Dictionary<String, Object> valueDictionary:
case Dictionary<string, object> valueDictionary: if(valueDictionary.Count > 0) {
if (valueDictionary.Count > 0)
{
writeOutput = true; writeOutput = true;
_builder _ = this._builder
.Append($"{_indentStr}{kvp.Key,-16}: object") .Append($"{this._indentStr}{kvp.Key,-16}: object")
.AppendLine(); .AppendLine();
} }
break; break;
case List<object> valueList: case List<Object> valueList:
if (valueList.Count > 0) if(valueList.Count > 0) {
{
writeOutput = true; writeOutput = true;
_builder _ = this._builder
.Append($"{_indentStr}{kvp.Key,-16}: array[{valueList.Count}]") .Append($"{this._indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
.AppendLine(); .AppendLine();
} }
break; break;
default: default:
writeOutput = true; writeOutput = true;
_builder.Append($"{_indentStr}{kvp.Key,-16}: "); _ = this._builder.Append($"{this._indentStr}{kvp.Key,-16}: ");
break; break;
} }
if (writeOutput) if(writeOutput) {
_builder.AppendLine(new HumanizeJson(kvp.Value, _indent + 1).GetResult()); _ = this._builder.AppendLine(new HumanizeJson(kvp.Value, this._indent + 1).GetResult());
}
} }
} }
private void AppendList(List<object> objects) private void AppendList(List<Object> objects) {
{ Int32 index = 0;
var index = 0; foreach(Object value in objects) {
foreach (var value in objects) Boolean writeOutput = false;
{
var writeOutput = false;
switch (value) switch(value) {
{ case Dictionary<String, Object> valueDictionary:
case Dictionary<string, object> valueDictionary: if(valueDictionary.Count > 0) {
if (valueDictionary.Count > 0)
{
writeOutput = true; writeOutput = true;
_builder _ = this._builder
.Append($"{_indentStr}[{index}]: object") .Append($"{this._indentStr}[{index}]: object")
.AppendLine(); .AppendLine();
} }
break; break;
case List<object> valueList: case List<Object> valueList:
if (valueList.Count > 0) if(valueList.Count > 0) {
{
writeOutput = true; writeOutput = true;
_builder _ = this._builder
.Append($"{_indentStr}[{index}]: array[{valueList.Count}]") .Append($"{this._indentStr}[{index}]: array[{valueList.Count}]")
.AppendLine(); .AppendLine();
} }
break; break;
default: default:
writeOutput = true; writeOutput = true;
_builder.Append($"{_indentStr}[{index}]: "); _ = this._builder.Append($"{this._indentStr}[{index}]: ");
break; break;
} }
index++; index++;
if (writeOutput) if(writeOutput) {
_builder.AppendLine(new HumanizeJson(value, _indent + 1).GetResult()); _ = this._builder.AppendLine(new HumanizeJson(value, this._indent + 1).GetResult());
}
} }
} }
private void AppendString() private void AppendString() {
{ String stringValue = this._obj.ToString();
var stringValue = _obj.ToString();
if (stringValue.Length + _indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 || if(stringValue.Length + this._indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 ||
stringValue.IndexOf('\n') >= 0) stringValue.IndexOf('\n') >= 0) {
{ _ = this._builder.AppendLine();
_builder.AppendLine(); IEnumerable<String> stringLines = stringValue.ToLines().Select(l => l.Trim());
var stringLines = stringValue.ToLines().Select(l => l.Trim());
foreach (var line in stringLines) foreach(String line in stringLines) {
{ _ = this._builder.AppendLine($"{this._indentStr}{line}");
_builder.AppendLine($"{_indentStr}{line}");
} }
} } else {
else _ = this._builder.Append($"{stringValue}");
{
_builder.Append($"{stringValue}");
} }
} }
} }

View File

@ -1,14 +1,13 @@
namespace Unosquare.Swan.Formatters using System;
{
using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done /// to teach Geo how things are done
@ -16,46 +15,45 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured /// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET. /// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public static partial class Json public static partial class Json {
{ private class Converter {
private class Converter private static readonly ConcurrentDictionary<MemberInfo, String> MemberInfoNameCache =
{ new ConcurrentDictionary<MemberInfo, String>();
private static readonly ConcurrentDictionary<MemberInfo, string> MemberInfoNameCache =
new ConcurrentDictionary<MemberInfo, string>();
private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>(); private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>();
private readonly object _target; private readonly Object _target;
private readonly Type _targetType; private readonly Type _targetType;
private readonly bool _includeNonPublic; private readonly Boolean _includeNonPublic;
private Converter( private Converter(
object source, Object source,
Type targetType, Type targetType,
ref object targetInstance, ref Object targetInstance,
bool includeNonPublic) Boolean includeNonPublic) {
{ this._targetType = targetInstance != null ? targetInstance.GetType() : targetType;
_targetType = targetInstance != null ? targetInstance.GetType() : targetType; this._includeNonPublic = includeNonPublic;
_includeNonPublic = includeNonPublic;
if (source == null) if(source == null) {
{
return; return;
} }
var sourceType = source.GetType(); Type sourceType = source.GetType();
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType; if(this._targetType == null || this._targetType == typeof(Object)) {
if (sourceType == _targetType) this._targetType = sourceType;
{ }
_target = source;
if(sourceType == this._targetType) {
this._target = source;
return; return;
} }
if (!TrySetInstance(targetInstance, source, ref _target)) if(!this.TrySetInstance(targetInstance, source, ref this._target)) {
return; return;
}
ResolveObject(source, ref _target); this.ResolveObject(source, ref this._target);
} }
/// <summary> /// <summary>
@ -65,21 +63,17 @@
/// <param name="targetType">Type of the target.</param> /// <param name="targetType">Type of the target.</param>
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param> /// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
/// <returns>The target object.</returns> /// <returns>The target object.</returns>
internal static object FromJsonResult(object source, internal static Object FromJsonResult(Object source,
Type targetType, Type targetType,
bool includeNonPublic) Boolean includeNonPublic) {
{ Object nullRef = null;
object nullRef = null;
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult(); return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
} }
private static object FromJsonResult(object source, private static Object FromJsonResult(Object source,
Type targetType, Type targetType,
ref object targetInstance, ref Object targetInstance,
bool includeNonPublic) Boolean includeNonPublic) => new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
{
return new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
}
private static Type GetAddMethodParameterType(Type targetType) private static Type GetAddMethodParameterType(Type targetType)
=> ListAddMethodCache.GetOrAdd(targetType, => ListAddMethodCache.GetOrAdd(targetType,
@ -89,243 +83,216 @@
.GetParameters()[0] .GetParameters()[0]
.ParameterType); .ParameterType);
private static void GetByteArray(string sourceString, ref object target) private static void GetByteArray(String sourceString, ref Object target) {
{ try {
try
{
target = Convert.FromBase64String(sourceString); target = Convert.FromBase64String(sourceString);
} // Try conversion from Base 64 } // Try conversion from Base 64
catch catch {
{
target = Encoding.UTF8.GetBytes(sourceString); target = Encoding.UTF8.GetBytes(sourceString);
} // Get the string bytes in UTF8 } // Get the string bytes in UTF8
} }
private static object GetSourcePropertyValue(IDictionary<string, object> sourceProperties, private static Object GetSourcePropertyValue(IDictionary<String, Object> sourceProperties,
MemberInfo targetProperty) MemberInfo targetProperty) {
{ String targetPropertyName = MemberInfoNameCache.GetOrAdd(
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
targetProperty, targetProperty,
x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name); x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name);
return sourceProperties.GetValueOrDefault(targetPropertyName); return sourceProperties.GetValueOrDefault(targetPropertyName);
} }
private bool TrySetInstance(object targetInstance, object source, ref object target) private Boolean TrySetInstance(Object targetInstance, Object source, ref Object target) {
{ if(targetInstance == null) {
if (targetInstance == null)
{
// Try to create a default instance // Try to create a default instance
try try {
{ source.CreateTarget(this._targetType, this._includeNonPublic, ref target);
source.CreateTarget(_targetType, _includeNonPublic, ref target); } catch {
}
catch
{
return false; return false;
} }
} } else {
else
{
target = targetInstance; target = targetInstance;
} }
return true; return true;
} }
private object GetResult() => _target ?? _targetType.GetDefault(); private Object GetResult() => this._target ?? this._targetType.GetDefault();
private void ResolveObject(object source, ref object target) private void ResolveObject(Object source, ref Object target) {
{ switch(source) {
switch (source)
{
// Case 0: Special Cases Handling (Source and Target are of specific convertible types) // Case 0: Special Cases Handling (Source and Target are of specific convertible types)
// Case 0.1: Source is string, Target is byte[] // Case 0.1: Source is string, Target is byte[]
case string sourceString when _targetType == typeof(byte[]): case String sourceString when this._targetType == typeof(Byte[]):
GetByteArray(sourceString, ref target); GetByteArray(sourceString, ref target);
break; break;
// Case 1.1: Source is Dictionary, Target is IDictionary // Case 1.1: Source is Dictionary, Target is IDictionary
case Dictionary<string, object> sourceProperties when target is IDictionary targetDictionary: case Dictionary<String, Object> sourceProperties when target is IDictionary targetDictionary:
PopulateDictionary(sourceProperties, targetDictionary); this.PopulateDictionary(sourceProperties, targetDictionary);
break; break;
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type) // Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
case Dictionary<string, object> sourceProperties: case Dictionary<String, Object> sourceProperties:
PopulateObject(sourceProperties); this.PopulateObject(sourceProperties);
break; break;
// Case 2.1: Source is List, Target is Array // Case 2.1: Source is List, Target is Array
case List<object> sourceList when target is Array targetArray: case List<Object> sourceList when target is Array targetArray:
PopulateArray(sourceList, targetArray); this.PopulateArray(sourceList, targetArray);
break; break;
// Case 2.2: Source is List, Target is IList // Case 2.2: Source is List, Target is IList
case List<object> sourceList when target is IList targetList: case List<Object> sourceList when target is IList targetList:
PopulateIList(sourceList, targetList); this.PopulateIList(sourceList, targetList);
break; break;
// Case 3: Source is a simple type; Attempt conversion // Case 3: Source is a simple type; Attempt conversion
default: default:
var sourceStringValue = source.ToStringInvariant(); String sourceStringValue = source.ToStringInvariant();
// Handle basic types or enumerations if not // Handle basic types or enumerations if not
if (!_targetType.TryParseBasicType(sourceStringValue, out target)) if(!this._targetType.TryParseBasicType(sourceStringValue, out target)) {
GetEnumValue(sourceStringValue, ref target); this.GetEnumValue(sourceStringValue, ref target);
}
break; break;
} }
} }
private void PopulateIList(IList<object> objects, IList list) private void PopulateIList(IList<Object> objects, IList list) {
{ Type parameterType = GetAddMethodParameterType(this._targetType);
var parameterType = GetAddMethodParameterType(_targetType); if(parameterType == null) {
if (parameterType == null) return; return;
}
foreach (var item in objects) foreach(Object item in objects) {
{ try {
try _ = list.Add(FromJsonResult(
{
list.Add(FromJsonResult(
item, item,
parameterType, parameterType,
_includeNonPublic)); this._includeNonPublic));
} } catch {
catch
{
// ignored // ignored
} }
} }
} }
private void PopulateArray(IList<object> objects, Array array) private void PopulateArray(IList<Object> objects, Array array) {
{ Type elementType = this._targetType.GetElementType();
var elementType = _targetType.GetElementType();
for (var i = 0; i < objects.Count; i++) for(Int32 i = 0; i < objects.Count; i++) {
{ try {
try Object targetItem = FromJsonResult(
{
var targetItem = FromJsonResult(
objects[i], objects[i],
elementType, elementType,
_includeNonPublic); this._includeNonPublic);
array.SetValue(targetItem, i); array.SetValue(targetItem, i);
} } catch {
catch
{
// ignored // ignored
} }
} }
} }
private void GetEnumValue(string sourceStringValue, ref object target) private void GetEnumValue(String sourceStringValue, ref Object target) {
{ Type enumType = Nullable.GetUnderlyingType(this._targetType);
var enumType = Nullable.GetUnderlyingType(_targetType); if(enumType == null && this._targetType.GetTypeInfo().IsEnum) {
if (enumType == null && _targetType.GetTypeInfo().IsEnum) enumType = _targetType; enumType = this._targetType;
if (enumType == null) return; }
try if(enumType == null) {
{ return;
}
try {
target = Enum.Parse(enumType, sourceStringValue); target = Enum.Parse(enumType, sourceStringValue);
} } catch {
catch
{
// ignored // ignored
} }
} }
private void PopulateDictionary(IDictionary<string, object> sourceProperties, IDictionary targetDictionary) private void PopulateDictionary(IDictionary<String, Object> sourceProperties, IDictionary targetDictionary) {
{
// find the add method of the target dictionary // find the add method of the target dictionary
var addMethod = _targetType.GetMethods() MethodInfo addMethod = this._targetType.GetMethods()
.FirstOrDefault( .FirstOrDefault(
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2); m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2);
// skip if we don't have a compatible add method // skip if we don't have a compatible add method
if (addMethod == null) return; if(addMethod == null) {
var addMethodParameters = addMethod.GetParameters(); return;
if (addMethodParameters[0].ParameterType != typeof(string)) return; }
ParameterInfo[] addMethodParameters = addMethod.GetParameters();
if(addMethodParameters[0].ParameterType != typeof(String)) {
return;
}
// Retrieve the target entry type // Retrieve the target entry type
var targetEntryType = addMethodParameters[1].ParameterType; Type targetEntryType = addMethodParameters[1].ParameterType;
// Add the items to the target dictionary // Add the items to the target dictionary
foreach (var sourceProperty in sourceProperties) foreach(KeyValuePair<String, Object> sourceProperty in sourceProperties) {
{ try {
try Object targetEntryValue = FromJsonResult(
{
var targetEntryValue = FromJsonResult(
sourceProperty.Value, sourceProperty.Value,
targetEntryType, targetEntryType,
_includeNonPublic); this._includeNonPublic);
targetDictionary.Add(sourceProperty.Key, targetEntryValue); targetDictionary.Add(sourceProperty.Key, targetEntryValue);
} } catch {
catch
{
// ignored // ignored
} }
} }
} }
private void PopulateObject(IDictionary<string, object> sourceProperties) private void PopulateObject(IDictionary<String, Object> sourceProperties) {
{ if(this._targetType.IsValueType()) {
if (_targetType.IsValueType()) this.PopulateFields(sourceProperties);
{
PopulateFields(sourceProperties);
} }
PopulateProperties(sourceProperties); this.PopulateProperties(sourceProperties);
} }
private void PopulateProperties(IDictionary<string, object> sourceProperties) private void PopulateProperties(IDictionary<String, Object> sourceProperties) {
{ IEnumerable<PropertyInfo> properties = PropertyTypeCache.RetrieveFilteredProperties(this._targetType, false, p => p.CanWrite);
var properties = PropertyTypeCache.RetrieveFilteredProperties(_targetType, false, p => p.CanWrite);
foreach (var property in properties) foreach(PropertyInfo property in properties) {
{ Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property); if(sourcePropertyValue == null) {
if (sourcePropertyValue == null) continue; continue;
}
try try {
{ Object currentPropertyValue = !property.PropertyType.IsArray
var currentPropertyValue = !property.PropertyType.IsArray ? property.GetCacheGetMethod(this._includeNonPublic)(this._target)
? property.GetCacheGetMethod(_includeNonPublic)(_target)
: null; : null;
var targetPropertyValue = FromJsonResult( Object targetPropertyValue = FromJsonResult(
sourcePropertyValue, sourcePropertyValue,
property.PropertyType, property.PropertyType,
ref currentPropertyValue, ref currentPropertyValue,
_includeNonPublic); this._includeNonPublic);
property.GetCacheSetMethod(_includeNonPublic)(_target, new[] { targetPropertyValue }); property.GetCacheSetMethod(this._includeNonPublic)(this._target, new[] { targetPropertyValue });
} } catch {
catch
{
// ignored // ignored
} }
} }
} }
private void PopulateFields(IDictionary<string, object> sourceProperties) private void PopulateFields(IDictionary<String, Object> sourceProperties) {
{ foreach(FieldInfo field in FieldTypeCache.RetrieveAllFields(this._targetType)) {
foreach (var field in FieldTypeCache.RetrieveAllFields(_targetType)) Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
{ if(sourcePropertyValue == null) {
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field); continue;
if (sourcePropertyValue == null) continue; }
var targetPropertyValue = FromJsonResult( Object targetPropertyValue = FromJsonResult(
sourcePropertyValue, sourcePropertyValue,
field.FieldType, field.FieldType,
_includeNonPublic); this._includeNonPublic);
try try {
{ field.SetValue(this._target, targetPropertyValue);
field.SetValue(_target, targetPropertyValue); } catch {
}
catch
{
// ignored // ignored
} }
} }

View File

@ -1,9 +1,8 @@
namespace Unosquare.Swan.Formatters using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done /// to teach Geo how things are done
@ -11,80 +10,76 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured /// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET. /// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public partial class Json public partial class Json {
{
/// <summary> /// <summary>
/// A simple JSON Deserializer. /// A simple JSON Deserializer.
/// </summary> /// </summary>
private class Deserializer private class Deserializer {
{
#region State Variables #region State Variables
private readonly object _result; private readonly Object _result;
private readonly Dictionary<string, object> _resultObject; private readonly Dictionary<String, Object> _resultObject;
private readonly List<object> _resultArray; private readonly List<Object> _resultArray;
private readonly ReadState _state = ReadState.WaitingForRootOpen; private readonly ReadState _state = ReadState.WaitingForRootOpen;
private readonly string _currentFieldName; private readonly String _currentFieldName;
private readonly string _json; private readonly String _json;
private int _index; private Int32 _index;
#endregion #endregion
private Deserializer(string json, int startIndex) private Deserializer(String json, Int32 startIndex) {
{ this._json = json;
_json = json;
for (_index = startIndex; _index < _json.Length; _index++) for(this._index = startIndex; this._index < this._json.Length; this._index++) {
{
#region Wait for { or [ #region Wait for { or [
if (_state == ReadState.WaitingForRootOpen) if(this._state == ReadState.WaitingForRootOpen) {
{ if(Char.IsWhiteSpace(this._json, this._index)) {
if (char.IsWhiteSpace(_json, _index)) continue;
if (_json[_index] == OpenObjectChar)
{
_resultObject = new Dictionary<string, object>();
_state = ReadState.WaitingForField;
continue; continue;
} }
if (_json[_index] == OpenArrayChar) if(this._json[this._index] == OpenObjectChar) {
{ this._resultObject = new Dictionary<String, Object>();
_resultArray = new List<object>(); this._state = ReadState.WaitingForField;
_state = ReadState.WaitingForValue;
continue; continue;
} }
throw CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'"); if(this._json[this._index] == OpenArrayChar) {
this._resultArray = new List<Object>();
this._state = ReadState.WaitingForValue;
continue;
}
throw this.CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
} }
#endregion #endregion
#region Wait for opening field " (only applies for object results) #region Wait for opening field " (only applies for object results)
if (_state == ReadState.WaitingForField) if(this._state == ReadState.WaitingForField) {
{ if(Char.IsWhiteSpace(this._json, this._index)) {
if (char.IsWhiteSpace(_json, _index)) continue; continue;
}
// Handle empty arrays and empty objects // Handle empty arrays and empty objects
if ((_resultObject != null && _json[_index] == CloseObjectChar) if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|| (_resultArray != null && _json[_index] == CloseArrayChar)) || this._resultArray != null && this._json[this._index] == CloseArrayChar) {
{ this._result = this._resultObject ?? this._resultArray as Object;
_result = _resultObject ?? _resultArray as object;
return; return;
} }
if (_json[_index] != StringQuotedChar) if(this._json[this._index] != StringQuotedChar) {
throw CreateParserException($"'{StringQuotedChar}'"); throw this.CreateParserException($"'{StringQuotedChar}'");
}
var charCount = GetFieldNameCount(); Int32 charCount = this.GetFieldNameCount();
_currentFieldName = Unescape(_json.SliceLength(_index + 1, charCount)); this._currentFieldName = Unescape(this._json.SliceLength(this._index + 1, charCount));
_index += charCount + 1; this._index += charCount + 1;
_state = ReadState.WaitingForColon; this._state = ReadState.WaitingForColon;
continue; continue;
} }
@ -92,14 +87,16 @@
#region Wait for field-value separator : (only applies for object results #region Wait for field-value separator : (only applies for object results
if (_state == ReadState.WaitingForColon) if(this._state == ReadState.WaitingForColon) {
{ if(Char.IsWhiteSpace(this._json, this._index)) {
if (char.IsWhiteSpace(_json, _index)) continue; continue;
}
if (_json[_index] != ValueSeparatorChar) if(this._json[this._index] != ValueSeparatorChar) {
throw CreateParserException($"'{ValueSeparatorChar}'"); throw this.CreateParserException($"'{ValueSeparatorChar}'");
}
_state = ReadState.WaitingForValue; this._state = ReadState.WaitingForValue;
continue; continue;
} }
@ -107,49 +104,48 @@
#region Wait for and Parse the value #region Wait for and Parse the value
if (_state == ReadState.WaitingForValue) if(this._state == ReadState.WaitingForValue) {
{ if(Char.IsWhiteSpace(this._json, this._index)) {
if (char.IsWhiteSpace(_json, _index)) continue; continue;
}
// Handle empty arrays and empty objects // Handle empty arrays and empty objects
if ((_resultObject != null && _json[_index] == CloseObjectChar) if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|| (_resultArray != null && _json[_index] == CloseArrayChar)) || this._resultArray != null && this._json[this._index] == CloseArrayChar) {
{ this._result = this._resultObject ?? this._resultArray as Object;
_result = _resultObject ?? _resultArray as object;
return; return;
} }
// determine the value based on what it starts with // determine the value based on what it starts with
switch (_json[_index]) switch(this._json[this._index]) {
{
case StringQuotedChar: // expect a string case StringQuotedChar: // expect a string
ExtractStringQuoted(); this.ExtractStringQuoted();
break; break;
case OpenObjectChar: // expect object case OpenObjectChar: // expect object
case OpenArrayChar: // expect array case OpenArrayChar: // expect array
ExtractObject(); this.ExtractObject();
break; break;
case 't': // expect true case 't': // expect true
ExtractConstant(TrueLiteral, true); this.ExtractConstant(TrueLiteral, true);
break; break;
case 'f': // expect false case 'f': // expect false
ExtractConstant(FalseLiteral, false); this.ExtractConstant(FalseLiteral, false);
break; break;
case 'n': // expect null case 'n': // expect null
ExtractConstant(NullLiteral, null); this.ExtractConstant(NullLiteral, null);
break; break;
default: // expect number default: // expect number
ExtractNumber(); this.ExtractNumber();
break; break;
} }
_currentFieldName = null; this._currentFieldName = null;
_state = ReadState.WaitingForNextOrRootClose; this._state = ReadState.WaitingForNextOrRootClose;
continue; continue;
} }
@ -157,84 +153,83 @@
#region Wait for closing ], } or an additional field or value , #region Wait for closing ], } or an additional field or value ,
if (_state != ReadState.WaitingForNextOrRootClose) continue; if(this._state != ReadState.WaitingForNextOrRootClose) {
if (char.IsWhiteSpace(_json, _index)) continue;
if (_json[_index] == FieldSeparatorChar)
{
if (_resultObject != null)
{
_state = ReadState.WaitingForField;
_currentFieldName = null;
continue; continue;
} }
_state = ReadState.WaitingForValue; if(Char.IsWhiteSpace(this._json, this._index)) {
continue; continue;
} }
if ((_resultObject != null && _json[_index] == CloseObjectChar) || if(this._json[this._index] == FieldSeparatorChar) {
(_resultArray != null && _json[_index] == CloseArrayChar)) if(this._resultObject != null) {
{ this._state = ReadState.WaitingForField;
_result = _resultObject ?? _resultArray as object; this._currentFieldName = null;
continue;
}
this._state = ReadState.WaitingForValue;
continue;
}
if(this._resultObject != null && this._json[this._index] == CloseObjectChar ||
this._resultArray != null && this._json[this._index] == CloseArrayChar) {
this._result = this._resultObject ?? this._resultArray as Object;
return; return;
} }
throw CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'"); throw this.CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
#endregion #endregion
} }
} }
internal static object DeserializeInternal(string json) => new Deserializer(json, 0)._result; internal static Object DeserializeInternal(String json) => new Deserializer(json, 0)._result;
private static string Unescape(string str) private static String Unescape(String str) {
{
// check if we need to unescape at all // check if we need to unescape at all
if (str.IndexOf(StringEscapeChar) < 0) if(str.IndexOf(StringEscapeChar) < 0) {
return str; return str;
}
var builder = new StringBuilder(str.Length); StringBuilder builder = new StringBuilder(str.Length);
for (var i = 0; i < str.Length; i++) for(Int32 i = 0; i < str.Length; i++) {
{ if(str[i] != StringEscapeChar) {
if (str[i] != StringEscapeChar) _ = builder.Append(str[i]);
{
builder.Append(str[i]);
continue; continue;
} }
if (i + 1 > str.Length - 1) if(i + 1 > str.Length - 1) {
break; break;
}
// escape sequence begins here // escape sequence begins here
switch (str[i + 1]) switch(str[i + 1]) {
{
case 'u': case 'u':
i = ExtractEscapeSequence(str, i, builder); i = ExtractEscapeSequence(str, i, builder);
break; break;
case 'b': case 'b':
builder.Append('\b'); _ = builder.Append('\b');
i += 1; i += 1;
break; break;
case 't': case 't':
builder.Append('\t'); _ = builder.Append('\t');
i += 1; i += 1;
break; break;
case 'n': case 'n':
builder.Append('\n'); _ = builder.Append('\n');
i += 1; i += 1;
break; break;
case 'f': case 'f':
builder.Append('\f'); _ = builder.Append('\f');
i += 1; i += 1;
break; break;
case 'r': case 'r':
builder.Append('\r'); _ = builder.Append('\r');
i += 1; i += 1;
break; break;
default: default:
builder.Append(str[i + 1]); _ = builder.Append(str[i + 1]);
i += 1; i += 1;
break; break;
} }
@ -243,30 +238,27 @@
return builder.ToString(); return builder.ToString();
} }
private static int ExtractEscapeSequence(string str, int i, StringBuilder builder) private static Int32 ExtractEscapeSequence(String str, Int32 i, StringBuilder builder) {
{ Int32 startIndex = i + 2;
var startIndex = i + 2; Int32 endIndex = i + 5;
var endIndex = i + 5; if(endIndex > str.Length - 1) {
if (endIndex > str.Length - 1) _ = builder.Append(str[i + 1]);
{
builder.Append(str[i + 1]);
i += 1; i += 1;
return i; return i;
} }
var hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes(); Byte[] hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode)); _ = builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
i += 5; i += 5;
return i; return i;
} }
private int GetFieldNameCount() private Int32 GetFieldNameCount() {
{ Int32 charCount = 0;
var charCount = 0; for(Int32 j = this._index + 1; j < this._json.Length; j++) {
for (var j = _index + 1; j < _json.Length; j++) if(this._json[j] == StringQuotedChar && this._json[j - 1] != StringEscapeChar) {
{
if (_json[j] == StringQuotedChar && _json[j - 1] != StringEscapeChar)
break; break;
}
charCount++; charCount++;
} }
@ -274,95 +266,95 @@
return charCount; return charCount;
} }
private void ExtractObject() private void ExtractObject() {
{
// Extract and set the value // Extract and set the value
var deserializer = new Deserializer(_json, _index); Deserializer deserializer = new Deserializer(this._json, this._index);
if (_currentFieldName != null) if(this._currentFieldName != null) {
_resultObject[_currentFieldName] = deserializer._result; this._resultObject[this._currentFieldName] = deserializer._result;
else } else {
_resultArray.Add(deserializer._result); this._resultArray.Add(deserializer._result);
_index = deserializer._index;
} }
private void ExtractNumber() this._index = deserializer._index;
{ }
var charCount = 0;
for (var j = _index; j < _json.Length; j++) private void ExtractNumber() {
{ Int32 charCount = 0;
if (char.IsWhiteSpace(_json[j]) || _json[j] == FieldSeparatorChar for(Int32 j = this._index; j < this._json.Length; j++) {
|| (_resultObject != null && _json[j] == CloseObjectChar) if(Char.IsWhiteSpace(this._json[j]) || this._json[j] == FieldSeparatorChar
|| (_resultArray != null && _json[j] == CloseArrayChar)) || this._resultObject != null && this._json[j] == CloseObjectChar
|| this._resultArray != null && this._json[j] == CloseArrayChar) {
break; break;
}
charCount++; charCount++;
} }
// Extract and set the value // Extract and set the value
var stringValue = _json.SliceLength(_index, charCount); String stringValue = this._json.SliceLength(this._index, charCount);
if (decimal.TryParse(stringValue, out var value) == false) if(Decimal.TryParse(stringValue, out Decimal value) == false) {
throw CreateParserException("[number]"); throw this.CreateParserException("[number]");
if (_currentFieldName != null)
_resultObject[_currentFieldName] = value;
else
_resultArray.Add(value);
_index += charCount - 1;
} }
private void ExtractConstant(string boolValue, bool? value) if(this._currentFieldName != null) {
{ this._resultObject[this._currentFieldName] = value;
if (!_json.SliceLength(_index, boolValue.Length).Equals(boolValue)) } else {
throw CreateParserException($"'{ValueSeparatorChar}'"); this._resultArray.Add(value);
}
this._index += charCount - 1;
}
private void ExtractConstant(String boolValue, Boolean? value) {
if(!this._json.SliceLength(this._index, boolValue.Length).Equals(boolValue)) {
throw this.CreateParserException($"'{ValueSeparatorChar}'");
}
// Extract and set the value // Extract and set the value
if (_currentFieldName != null) if(this._currentFieldName != null) {
_resultObject[_currentFieldName] = value; this._resultObject[this._currentFieldName] = value;
else } else {
_resultArray.Add(value); this._resultArray.Add(value);
_index += boolValue.Length - 1;
} }
private void ExtractStringQuoted() this._index += boolValue.Length - 1;
{ }
var charCount = 0;
var escapeCharFound = false;
for (var j = _index + 1; j < _json.Length; j++)
{
if (_json[j] == StringQuotedChar && !escapeCharFound)
break;
escapeCharFound = _json[j] == StringEscapeChar && !escapeCharFound; private void ExtractStringQuoted() {
Int32 charCount = 0;
Boolean escapeCharFound = false;
for(Int32 j = this._index + 1; j < this._json.Length; j++) {
if(this._json[j] == StringQuotedChar && !escapeCharFound) {
break;
}
escapeCharFound = this._json[j] == StringEscapeChar && !escapeCharFound;
charCount++; charCount++;
} }
// Extract and set the value // Extract and set the value
var value = Unescape(_json.SliceLength(_index + 1, charCount)); String value = Unescape(this._json.SliceLength(this._index + 1, charCount));
if (_currentFieldName != null) if(this._currentFieldName != null) {
_resultObject[_currentFieldName] = value; this._resultObject[this._currentFieldName] = value;
else } else {
_resultArray.Add(value); this._resultArray.Add(value);
_index += charCount + 1;
} }
private FormatException CreateParserException(string expected) this._index += charCount + 1;
{ }
var textPosition = _json.TextPositionAt(_index);
private FormatException CreateParserException(String expected) {
Tuple<Int32, Int32> textPosition = this._json.TextPositionAt(this._index);
return new FormatException( return new FormatException(
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {_state}): Expected {expected} but got '{_json[_index]}'."); $"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {this._state}): Expected {expected} but got '{this._json[this._index]}'.");
} }
/// <summary> /// <summary>
/// Defines the different JSON read states. /// Defines the different JSON read states.
/// </summary> /// </summary>
private enum ReadState private enum ReadState {
{
WaitingForRootOpen, WaitingForRootOpen,
WaitingForField, WaitingForField,
WaitingForColon, WaitingForColon,

View File

@ -1,12 +1,11 @@
namespace Unosquare.Swan.Formatters using System;
{
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done /// to teach Geo how things are done
@ -14,21 +13,19 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured /// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET. /// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public partial class Json public partial class Json {
{
/// <summary> /// <summary>
/// A simple JSON serializer. /// A simple JSON serializer.
/// </summary> /// </summary>
private class Serializer private class Serializer {
{
#region Private Declarations #region Private Declarations
private static readonly Dictionary<int, string> IndentStrings = new Dictionary<int, string>(); private static readonly Dictionary<Int32, String> IndentStrings = new Dictionary<Int32, String>();
private readonly SerializerOptions _options; private readonly SerializerOptions _options;
private readonly string _result; private readonly String _result;
private readonly StringBuilder _builder; private readonly StringBuilder _builder;
private readonly string _lastCommaSearch; private readonly String _lastCommaSearch;
#endregion #endregion
@ -40,74 +37,66 @@
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <param name="depth">The depth.</param> /// <param name="depth">The depth.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
private Serializer(object obj, int depth, SerializerOptions options) private Serializer(Object obj, Int32 depth, SerializerOptions options) {
{ if(depth > 20) {
if (depth > 20)
{
throw new InvalidOperationException( throw new InvalidOperationException(
"The max depth (20) has been reached. Serializer can not continue."); "The max depth (20) has been reached. Serializer can not continue.");
} }
// Basic Type Handling (nulls, strings, number, date and bool) // Basic Type Handling (nulls, strings, number, date and bool)
_result = ResolveBasicType(obj); this._result = ResolveBasicType(obj);
if (string.IsNullOrWhiteSpace(_result) == false) if(String.IsNullOrWhiteSpace(this._result) == false) {
return; return;
}
_options = options; this._options = options;
_lastCommaSearch = FieldSeparatorChar + (_options.Format ? Environment.NewLine : string.Empty); this._lastCommaSearch = FieldSeparatorChar + (this._options.Format ? Environment.NewLine : String.Empty);
// Handle circular references correctly and avoid them // Handle circular references correctly and avoid them
if (options.IsObjectPresent(obj)) if(options.IsObjectPresent(obj)) {
{ this._result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
_result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
return; return;
} }
// At this point, we will need to construct the object with a StringBuilder. // At this point, we will need to construct the object with a StringBuilder.
_builder = new StringBuilder(); this._builder = new StringBuilder();
switch (obj) switch(obj) {
{
case IDictionary itemsZero when itemsZero.Count == 0: case IDictionary itemsZero when itemsZero.Count == 0:
_result = EmptyObjectLiteral; this._result = EmptyObjectLiteral;
break; break;
case IDictionary items: case IDictionary items:
_result = ResolveDictionary(items, depth); this._result = this.ResolveDictionary(items, depth);
break; break;
case IEnumerable enumerableZero when !enumerableZero.Cast<object>().Any(): case IEnumerable enumerableZero when !enumerableZero.Cast<Object>().Any():
_result = EmptyArrayLiteral; this._result = EmptyArrayLiteral;
break; break;
case IEnumerable enumerableBytes when enumerableBytes is byte[] bytes: case IEnumerable enumerableBytes when enumerableBytes is Byte[] bytes:
_result = Serialize(bytes.ToBase64(), depth, _options); this._result = Serialize(bytes.ToBase64(), depth, this._options);
break; break;
case IEnumerable enumerable: case IEnumerable enumerable:
_result = ResolveEnumerable(enumerable, depth); this._result = this.ResolveEnumerable(enumerable, depth);
break; break;
default: default:
_result = ResolveObject(obj, depth); this._result = this.ResolveObject(obj, depth);
break; break;
} }
} }
internal static string Serialize(object obj, int depth, SerializerOptions options) internal static String Serialize(Object obj, Int32 depth, SerializerOptions options) => new Serializer(obj, depth, options)._result;
{
return new Serializer(obj, depth, options)._result;
}
#endregion #endregion
#region Helper Methods #region Helper Methods
private static string ResolveBasicType(object obj) private static String ResolveBasicType(Object obj) {
{ switch(obj) {
switch (obj)
{
case null: case null:
return NullLiteral; return NullLiteral;
case string s: case String s:
return Escape(s, true); return Escape(s, true);
case bool b: case Boolean b:
return b ? TrueLiteral : FalseLiteral; return b ? TrueLiteral : FalseLiteral;
case Type _: case Type _:
case Assembly _: case Assembly _:
@ -118,81 +107,84 @@
case DateTime d: case DateTime d:
return $"{StringQuotedChar}{d:s}{StringQuotedChar}"; return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
default: default:
var targetType = obj.GetType(); Type targetType = obj.GetType();
if (!Definitions.BasicTypesInfo.ContainsKey(targetType)) if(!Definitions.BasicTypesInfo.ContainsKey(targetType)) {
return string.Empty; return String.Empty;
}
var escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false); String escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
return decimal.TryParse(escapedValue, out _) return Decimal.TryParse(escapedValue, out _)
? $"{escapedValue}" ? $"{escapedValue}"
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}"; : $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
} }
} }
private static bool IsNonEmptyJsonArrayOrObject(string serialized) private static Boolean IsNonEmptyJsonArrayOrObject(String serialized) {
{ if(serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) {
if (serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) return false; return false;
}
// find the first position the character is not a space // find the first position the character is not a space
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault(); return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
} }
private static string Escape(string str, bool quoted) private static String Escape(String str, Boolean quoted) {
{ if(str == null) {
if (str == null) return String.Empty;
return string.Empty; }
StringBuilder builder = new StringBuilder(str.Length * 2);
if(quoted) {
_ = builder.Append(StringQuotedChar);
}
var builder = new StringBuilder(str.Length * 2);
if (quoted) builder.Append(StringQuotedChar);
Escape(str, builder); Escape(str, builder);
if (quoted) builder.Append(StringQuotedChar); if(quoted) {
_ = builder.Append(StringQuotedChar);
}
return builder.ToString(); return builder.ToString();
} }
private static void Escape(string str, StringBuilder builder) private static void Escape(String str, StringBuilder builder) {
{ foreach(Char currentChar in str) {
foreach (var currentChar in str) switch(currentChar) {
{
switch (currentChar)
{
case '\\': case '\\':
case '"': case '"':
case '/': case '/':
builder _ = builder
.Append('\\') .Append('\\')
.Append(currentChar); .Append(currentChar);
break; break;
case '\b': case '\b':
builder.Append("\\b"); _ = builder.Append("\\b");
break; break;
case '\t': case '\t':
builder.Append("\\t"); _ = builder.Append("\\t");
break; break;
case '\n': case '\n':
builder.Append("\\n"); _ = builder.Append("\\n");
break; break;
case '\f': case '\f':
builder.Append("\\f"); _ = builder.Append("\\f");
break; break;
case '\r': case '\r':
builder.Append("\\r"); _ = builder.Append("\\r");
break; break;
default: default:
if (currentChar < ' ') if(currentChar < ' ') {
{ Byte[] escapeBytes = BitConverter.GetBytes((UInt16)currentChar);
var escapeBytes = BitConverter.GetBytes((ushort)currentChar); if(BitConverter.IsLittleEndian == false) {
if (BitConverter.IsLittleEndian == false)
Array.Reverse(escapeBytes); Array.Reverse(escapeBytes);
}
builder.Append("\\u") _ = builder.Append("\\u")
.Append(escapeBytes[1].ToString("X").PadLeft(2, '0')) .Append(escapeBytes[1].ToString("X").PadLeft(2, '0'))
.Append(escapeBytes[0].ToString("X").PadLeft(2, '0')); .Append(escapeBytes[0].ToString("X").PadLeft(2, '0'));
} } else {
else _ = builder.Append(currentChar);
{
builder.Append(currentChar);
} }
break; break;
@ -200,29 +192,25 @@
} }
} }
private Dictionary<string, object> CreateDictionary( private Dictionary<String, Object> CreateDictionary(
Dictionary<string, MemberInfo> fields, Dictionary<String, MemberInfo> fields,
string targetType, String targetType,
object target) Object target) {
{
// Create the dictionary and extract the properties // Create the dictionary and extract the properties
var objectDictionary = new Dictionary<string, object>(); Dictionary<String, Object> objectDictionary = new Dictionary<String, Object>();
if (string.IsNullOrWhiteSpace(_options.TypeSpecifier) == false) if(String.IsNullOrWhiteSpace(this._options.TypeSpecifier) == false) {
objectDictionary[_options.TypeSpecifier] = targetType; objectDictionary[this._options.TypeSpecifier] = targetType;
}
foreach (var field in fields) foreach(KeyValuePair<String, MemberInfo> field in fields) {
{
// Build the dictionary using property names and values // Build the dictionary using property names and values
// Note: used to be: property.GetValue(target); but we would be reading private properties // Note: used to be: property.GetValue(target); but we would be reading private properties
try try {
{
objectDictionary[field.Key] = field.Value is PropertyInfo property objectDictionary[field.Key] = field.Value is PropertyInfo property
? property.GetCacheGetMethod(_options.IncludeNonPublic)(target) ? property.GetCacheGetMethod(this._options.IncludeNonPublic)(target)
: (field.Value as FieldInfo)?.GetValue(target); : (field.Value as FieldInfo)?.GetValue(target);
} } catch {
catch
{
/* ignored */ /* ignored */
} }
} }
@ -230,127 +218,127 @@
return objectDictionary; return objectDictionary;
} }
private string ResolveDictionary(IDictionary items, int depth) private String ResolveDictionary(IDictionary items, Int32 depth) {
{ this.Append(OpenObjectChar, depth);
Append(OpenObjectChar, depth); this.AppendLine();
AppendLine();
// Iterate through the elements and output recursively // Iterate through the elements and output recursively
var writeCount = 0; Int32 writeCount = 0;
foreach (var key in items.Keys) foreach(Object key in items.Keys) {
{
// Serialize and append the key (first char indented) // Serialize and append the key (first char indented)
Append(StringQuotedChar, depth + 1); this.Append(StringQuotedChar, depth + 1);
Escape(key.ToString(), _builder); Escape(key.ToString(), this._builder);
_builder _ = this._builder
.Append(StringQuotedChar) .Append(StringQuotedChar)
.Append(ValueSeparatorChar) .Append(ValueSeparatorChar)
.Append(" "); .Append(" ");
// Serialize and append the value // Serialize and append the value
var serializedValue = Serialize(items[key], depth + 1, _options); String serializedValue = Serialize(items[key], depth + 1, this._options);
if (IsNonEmptyJsonArrayOrObject(serializedValue)) AppendLine(); if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
Append(serializedValue, 0); this.AppendLine();
}
this.Append(serializedValue, 0);
// Add a comma and start a new line -- We will remove the last one when we are done writing the elements // Add a comma and start a new line -- We will remove the last one when we are done writing the elements
Append(FieldSeparatorChar, 0); this.Append(FieldSeparatorChar, 0);
AppendLine(); this.AppendLine();
writeCount++; writeCount++;
} }
// Output the end of the object and set the result // Output the end of the object and set the result
RemoveLastComma(); this.RemoveLastComma();
Append(CloseObjectChar, writeCount > 0 ? depth : 0); this.Append(CloseObjectChar, writeCount > 0 ? depth : 0);
return _builder.ToString(); return this._builder.ToString();
} }
private string ResolveObject(object target, int depth) private String ResolveObject(Object target, Int32 depth) {
{ Type targetType = target.GetType();
var targetType = target.GetType(); Dictionary<String, MemberInfo> fields = this._options.GetProperties(targetType);
var fields = _options.GetProperties(targetType);
if (fields.Count == 0 && string.IsNullOrWhiteSpace(_options.TypeSpecifier)) if(fields.Count == 0 && String.IsNullOrWhiteSpace(this._options.TypeSpecifier)) {
return EmptyObjectLiteral; return EmptyObjectLiteral;
}
// If we arrive here, then we convert the object into a // If we arrive here, then we convert the object into a
// dictionary of property names and values and call the serialization // dictionary of property names and values and call the serialization
// function again // function again
var objectDictionary = CreateDictionary(fields, targetType.ToString(), target); Dictionary<String, Object> objectDictionary = this.CreateDictionary(fields, targetType.ToString(), target);
return Serialize(objectDictionary, depth, _options); return Serialize(objectDictionary, depth, this._options);
} }
private string ResolveEnumerable(IEnumerable target, int depth) private String ResolveEnumerable(IEnumerable target, Int32 depth) {
{
// Cast the items as a generic object array // Cast the items as a generic object array
var items = target.Cast<object>(); IEnumerable<Object> items = target.Cast<Object>();
Append(OpenArrayChar, depth); this.Append(OpenArrayChar, depth);
AppendLine(); this.AppendLine();
// Iterate through the elements and output recursively // Iterate through the elements and output recursively
var writeCount = 0; Int32 writeCount = 0;
foreach (var entry in items) foreach(Object entry in items) {
{ String serializedValue = Serialize(entry, depth + 1, this._options);
var serializedValue = Serialize(entry, depth + 1, _options);
if (IsNonEmptyJsonArrayOrObject(serializedValue)) if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
Append(serializedValue, 0); this.Append(serializedValue, 0);
else } else {
Append(serializedValue, depth + 1); this.Append(serializedValue, depth + 1);
}
Append(FieldSeparatorChar, 0); this.Append(FieldSeparatorChar, 0);
AppendLine(); this.AppendLine();
writeCount++; writeCount++;
} }
// Output the end of the array and set the result // Output the end of the array and set the result
RemoveLastComma(); this.RemoveLastComma();
Append(CloseArrayChar, writeCount > 0 ? depth : 0); this.Append(CloseArrayChar, writeCount > 0 ? depth : 0);
return _builder.ToString(); return this._builder.ToString();
} }
private void SetIndent(int depth) private void SetIndent(Int32 depth) {
{ if(this._options.Format == false || depth <= 0) {
if (_options.Format == false || depth <= 0) return; return;
}
_builder.Append(IndentStrings.GetOrAdd(depth, x => new string(' ', x * 4))); _ = this._builder.Append(IndentStrings.GetOrAdd(depth, x => new String(' ', x * 4)));
} }
/// <summary> /// <summary>
/// Removes the last comma in the current string builder. /// Removes the last comma in the current string builder.
/// </summary> /// </summary>
private void RemoveLastComma() private void RemoveLastComma() {
{ if(this._builder.Length < this._lastCommaSearch.Length) {
if (_builder.Length < _lastCommaSearch.Length)
return; return;
}
if (_lastCommaSearch.Where((t, i) => _builder[_builder.Length - _lastCommaSearch.Length + i] != t).Any()) if(this._lastCommaSearch.Where((t, i) => this._builder[this._builder.Length - this._lastCommaSearch.Length + i] != t).Any()) {
{
return; return;
} }
// If we got this far, we simply remove the comma character // If we got this far, we simply remove the comma character
_builder.Remove(_builder.Length - _lastCommaSearch.Length, 1); _ = this._builder.Remove(this._builder.Length - this._lastCommaSearch.Length, 1);
} }
private void Append(string text, int depth) private void Append(String text, Int32 depth) {
{ this.SetIndent(depth);
SetIndent(depth); _ = this._builder.Append(text);
_builder.Append(text);
} }
private void Append(char text, int depth) private void Append(Char text, Int32 depth) {
{ this.SetIndent(depth);
SetIndent(depth); _ = this._builder.Append(text);
_builder.Append(text);
} }
private void AppendLine() private void AppendLine() {
{ if(this._options.Format == false) {
if (_options.Format == false) return; return;
_builder.Append(Environment.NewLine); }
_ = this._builder.Append(Environment.NewLine);
} }
#endregion #endregion

View File

@ -1,12 +1,11 @@
namespace Unosquare.Swan.Formatters using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done /// to teach Geo how things are done
@ -14,91 +13,92 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured /// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET. /// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public partial class Json public partial class Json {
{ private class SerializerOptions {
private class SerializerOptions private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>
{ TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>();
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>();
private readonly string[] _includeProperties; private readonly String[] _includeProperties;
private readonly string[] _excludeProperties; private readonly String[] _excludeProperties;
private readonly Dictionary<int, List<WeakReference>> _parentReferences = new Dictionary<int, List<WeakReference>>(); private readonly Dictionary<Int32, List<WeakReference>> _parentReferences = new Dictionary<Int32, List<WeakReference>>();
public SerializerOptions( public SerializerOptions(
bool format, Boolean format,
string typeSpecifier, String typeSpecifier,
string[] includeProperties, String[] includeProperties,
string[] excludeProperties = null, String[] excludeProperties = null,
bool includeNonPublic = true, Boolean includeNonPublic = true,
IReadOnlyCollection<WeakReference> parentReferences = null) IReadOnlyCollection<WeakReference> parentReferences = null) {
{ this._includeProperties = includeProperties;
_includeProperties = includeProperties; this._excludeProperties = excludeProperties;
_excludeProperties = excludeProperties;
IncludeNonPublic = includeNonPublic; this.IncludeNonPublic = includeNonPublic;
Format = format; this.Format = format;
TypeSpecifier = typeSpecifier; this.TypeSpecifier = typeSpecifier;
if (parentReferences == null) if(parentReferences == null) {
return; return;
}
foreach (var parentReference in parentReferences.Where(x => x.IsAlive)) foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) {
{ _ = this.IsObjectPresent(parentReference.Target);
IsObjectPresent(parentReference.Target);
} }
} }
public bool Format { get; } public Boolean Format {
public string TypeSpecifier { get; } get;
public bool IncludeNonPublic { get; } }
public String TypeSpecifier {
get;
}
public Boolean IncludeNonPublic {
get;
}
internal bool IsObjectPresent(object target) internal Boolean IsObjectPresent(Object target) {
{ Int32 hashCode = target.GetHashCode();
var hashCode = target.GetHashCode();
if (_parentReferences.ContainsKey(hashCode)) if(this._parentReferences.ContainsKey(hashCode)) {
{ if(this._parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) {
if (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target)))
return true; return true;
}
_parentReferences[hashCode].Add(new WeakReference(target)); this._parentReferences[hashCode].Add(new WeakReference(target));
return false; return false;
} }
_parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) }); this._parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
return false; return false;
} }
internal Dictionary<string, MemberInfo> GetProperties(Type targetType) internal Dictionary<String, MemberInfo> GetProperties(Type targetType)
=> GetPropertiesCache(targetType) => GetPropertiesCache(targetType)
.When(() => _includeProperties?.Length > 0, .When(() => this._includeProperties?.Length > 0,
query => query.Where(p => _includeProperties.Contains(p.Key.Item1))) query => query.Where(p => this._includeProperties.Contains(p.Key.Item1)))
.When(() => _excludeProperties?.Length > 0, .When(() => this._excludeProperties?.Length > 0,
query => query.Where(p => !_excludeProperties.Contains(p.Key.Item1))) query => query.Where(p => !this._excludeProperties.Contains(p.Key.Item1)))
.ToDictionary(x => x.Key.Item2, x => x.Value); .ToDictionary(x => x.Key.Item2, x => x.Value);
private static Dictionary<Tuple<string, string>, MemberInfo> GetPropertiesCache(Type targetType) private static Dictionary<Tuple<String, String>, MemberInfo> GetPropertiesCache(Type targetType) {
{ if(TypeCache.TryGetValue(targetType, out Dictionary<Tuple<String, String>, MemberInfo> current)) {
if (TypeCache.TryGetValue(targetType, out var current))
return current; return current;
}
var fields = List<MemberInfo> fields =
new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead)); new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead));
// If the target is a struct (value type) navigate the fields. // If the target is a struct (value type) navigate the fields.
if (targetType.IsValueType()) if(targetType.IsValueType()) {
{
fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType)); fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType));
} }
var value = fields Dictionary<Tuple<String, String>, MemberInfo> value = fields
.ToDictionary( .ToDictionary(
x => Tuple.Create(x.Name, x => Tuple.Create(x.Name,
x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name), x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name),
x => x); x => x);
TypeCache.TryAdd(targetType, value); _ = TypeCache.TryAdd(targetType, value);
return value; return value;
} }

View File

@ -1,13 +1,12 @@
namespace Unosquare.Swan.Formatters using Unosquare.Swan.Reflection;
{
using Reflection;
using System; using System;
using Components; using Unosquare.Swan.Components;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done /// to teach Geo how things are done
@ -15,35 +14,34 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured /// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET. /// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public static partial class Json public static partial class Json {
{
#region Constants #region Constants
internal const string AddMethodName = "Add"; internal const String AddMethodName = "Add";
private const char OpenObjectChar = '{'; private const Char OpenObjectChar = '{';
private const char CloseObjectChar = '}'; private const Char CloseObjectChar = '}';
private const char OpenArrayChar = '['; private const Char OpenArrayChar = '[';
private const char CloseArrayChar = ']'; private const Char CloseArrayChar = ']';
private const char FieldSeparatorChar = ','; private const Char FieldSeparatorChar = ',';
private const char ValueSeparatorChar = ':'; private const Char ValueSeparatorChar = ':';
private const char StringEscapeChar = '\\'; private const Char StringEscapeChar = '\\';
private const char StringQuotedChar = '"'; private const Char StringQuotedChar = '"';
private const string EmptyObjectLiteral = "{ }"; private const String EmptyObjectLiteral = "{ }";
private const string EmptyArrayLiteral = "[ ]"; private const String EmptyArrayLiteral = "[ ]";
private const string TrueLiteral = "true"; private const String TrueLiteral = "true";
private const string FalseLiteral = "false"; private const String FalseLiteral = "false";
private const string NullLiteral = "null"; private const String NullLiteral = "null";
#endregion #endregion
private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache(); private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache();
private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache(); private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache();
private static readonly CollectionCacheRepository<string> IgnoredPropertiesCache = new CollectionCacheRepository<string>(); private static readonly CollectionCacheRepository<String> IgnoredPropertiesCache = new CollectionCacheRepository<String>();
#region Public API #region Public API
@ -100,16 +98,13 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static string Serialize( public static String Serialize(
object obj, Object obj,
bool format = false, Boolean format = false,
string typeSpecifier = null, String typeSpecifier = null,
bool includeNonPublic = false, Boolean includeNonPublic = false,
string[] includedNames = null, String[] includedNames = null,
string[] excludedNames = null) String[] excludedNames = null) => Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
{
return Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
}
/// <summary> /// <summary>
/// Serializes the specified object into a JSON string. /// Serializes the specified object into a JSON string.
@ -124,21 +119,19 @@
/// <returns> /// <returns>
/// A <see cref="System.String" /> that represents the current object. /// A <see cref="System.String" /> that represents the current object.
/// </returns> /// </returns>
public static string Serialize( public static String Serialize(
object obj, Object obj,
bool format, Boolean format,
string typeSpecifier, String typeSpecifier,
bool includeNonPublic, Boolean includeNonPublic,
string[] includedNames, String[] includedNames,
string[] excludedNames, String[] excludedNames,
List<WeakReference> parentReferences) List<WeakReference> parentReferences) {
{ if(obj != null && (obj is String || Definitions.AllBasicValueTypes.Contains(obj.GetType()))) {
if (obj != null && (obj is string || Definitions.AllBasicValueTypes.Contains(obj.GetType())))
{
return SerializePrimitiveValue(obj); return SerializePrimitiveValue(obj);
} }
var options = new SerializerOptions( SerializerOptions options = new SerializerOptions(
format, format,
typeSpecifier, typeSpecifier,
includedNames, includedNames,
@ -178,9 +171,8 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static string SerializeOnly(object obj, bool format, params string[] includeNames) public static String SerializeOnly(Object obj, Boolean format, params String[] includeNames) {
{ SerializerOptions options = new SerializerOptions(format, null, includeNames);
var options = new SerializerOptions(format, null, includeNames);
return Serializer.Serialize(obj, 0, options); return Serializer.Serialize(obj, 0, options);
} }
@ -214,9 +206,8 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static string SerializeExcluding(object obj, bool format, params string[] excludeNames) public static String SerializeExcluding(Object obj, Boolean format, params String[] excludeNames) {
{ SerializerOptions options = new SerializerOptions(format, null, null, excludeNames);
var options = new SerializerOptions(format, null, null, excludeNames);
return Serializer.Serialize(obj, 0, options); return Serializer.Serialize(obj, 0, options);
} }
@ -245,7 +236,7 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static object Deserialize(string json) => Deserializer.DeserializeInternal(json); public static Object Deserialize(String json) => Deserializer.DeserializeInternal(json);
/// <summary> /// <summary>
/// Deserializes the specified json string and converts it to the specified object type. /// Deserializes the specified json string and converts it to the specified object type.
@ -272,7 +263,7 @@
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
public static T Deserialize<T>(string json) => (T)Deserialize(json, typeof(T)); public static T Deserialize<T>(String json) => (T)Deserialize(json, typeof(T));
/// <summary> /// <summary>
/// Deserializes the specified json string and converts it to the specified object type. /// Deserializes the specified json string and converts it to the specified object type.
@ -281,7 +272,7 @@
/// <param name="json">The json.</param> /// <param name="json">The json.</param>
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param> /// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
/// <returns>The deserialized specified type object.</returns> /// <returns>The deserialized specified type object.</returns>
public static T Deserialize<T>(string json, bool includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic); public static T Deserialize<T>(String json, Boolean includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
/// <summary> /// <summary>
/// Deserializes the specified json string and converts it to the specified object type. /// Deserializes the specified json string and converts it to the specified object type.
@ -290,37 +281,34 @@
/// <param name="resultType">Type of the result.</param> /// <param name="resultType">Type of the result.</param>
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param> /// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
/// <returns>Type of the current conversion from json result.</returns> /// <returns>Type of the current conversion from json result.</returns>
public static object Deserialize(string json, Type resultType, bool includeNonPublic = false) public static Object Deserialize(String json, Type resultType, Boolean includeNonPublic = false)
=> Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic); => Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
#endregion #endregion
#region Private API #region Private API
private static string[] GetExcludedNames(Type type, string[] excludedNames) private static String[] GetExcludedNames(Type type, String[] excludedNames) {
{ if(type == null) {
if (type == null)
return excludedNames; return excludedNames;
}
var excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties() IEnumerable<String> excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
.Where(x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true) .Where(x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true)
.Select(x => x.Name)); .Select(x => x.Name));
if (excludedByAttr?.Any() != true) return excludedByAttr?.Any() != true
return excludedNames; ? excludedNames
: excludedNames == null
return excludedNames == null
? excludedByAttr.ToArray() ? excludedByAttr.ToArray()
: excludedByAttr.Intersect(excludedNames).ToArray(); : excludedByAttr.Intersect(excludedNames).ToArray();
} }
private static string SerializePrimitiveValue(object obj) private static String SerializePrimitiveValue(Object obj) {
{ switch(obj) {
switch (obj) case String stringValue:
{
case string stringValue:
return stringValue; return stringValue;
case bool boolValue: case Boolean boolValue:
return boolValue ? TrueLiteral : FalseLiteral; return boolValue ? TrueLiteral : FalseLiteral;
default: default:
return obj.ToString(); return obj.ToString();

View File

@ -1,11 +1,10 @@
namespace Unosquare.Swan.Reflection using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
namespace Unosquare.Swan.Reflection {
/// <summary> /// <summary>
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type). /// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
/// ///
@ -13,25 +12,23 @@
/// calls the retrieval process if the type is not contained /// calls the retrieval process if the type is not contained
/// in the cache. /// in the cache.
/// </summary> /// </summary>
public class AttributeCache public class AttributeCache {
{ private readonly Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>> _data =
private readonly Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>> _data = new Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>>(() =>
new Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>>(() => new ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>(), true);
new ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>(), true);
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AttributeCache"/> class. /// Initializes a new instance of the <see cref="AttributeCache"/> class.
/// </summary> /// </summary>
/// <param name="propertyCache">The property cache object.</param> /// <param name="propertyCache">The property cache object.</param>
public AttributeCache(PropertyTypeCache propertyCache = null) public AttributeCache(PropertyTypeCache propertyCache = null) => this.PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
{
PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
}
/// <summary> /// <summary>
/// A PropertyTypeCache object for caching properties and their attributes. /// A PropertyTypeCache object for caching properties and their attributes.
/// </summary> /// </summary>
public PropertyTypeCache PropertyTypeCache { get; } public PropertyTypeCache PropertyTypeCache {
get;
}
/// <summary> /// <summary>
/// Determines whether [contains] [the specified member]. /// Determines whether [contains] [the specified member].
@ -41,7 +38,7 @@
/// <returns> /// <returns>
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>. /// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
/// </returns> /// </returns>
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T))); public Boolean Contains<T>(MemberInfo member) => this._data.Value.ContainsKey(new Tuple<Object, Type>(member, typeof(T)));
/// <summary> /// <summary>
/// Gets specific attributes from a member constrained to an attribute. /// Gets specific attributes from a member constrained to an attribute.
@ -50,13 +47,13 @@
/// <param name="member">The member.</param> /// <param name="member">The member.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An array of the attributes stored for the specified type.</returns> /// <returns>An array of the attributes stored for the specified type.</returns>
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false) public IEnumerable<Object> Retrieve<T>(MemberInfo member, Boolean inherit = false)
where T : Attribute where T : Attribute {
{ if(member == null) {
if (member == null)
throw new ArgumentNullException(nameof(member)); throw new ArgumentNullException(nameof(member));
}
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit)); return this.Retrieve(new Tuple<Object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
} }
/// <summary> /// <summary>
@ -66,16 +63,17 @@
/// <param name="type">The attribute type.</param> /// <param name="type">The attribute type.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An array of the attributes stored for the specified type.</returns> /// <returns>An array of the attributes stored for the specified type.</returns>
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false) public IEnumerable<Object> Retrieve(MemberInfo member, Type type, Boolean inherit = false) {
{ if(member == null) {
if (member == null)
throw new ArgumentNullException(nameof(member)); throw new ArgumentNullException(nameof(member));
}
if (type == null) if(type == null) {
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
}
return Retrieve( return this.Retrieve(
new Tuple<object, Type>(member, type), new Tuple<Object, Type>(member, type),
t => member.GetCustomAttributes(type, inherit)); t => member.GetCustomAttributes(type, inherit));
} }
@ -86,14 +84,14 @@
/// <param name="member">The member.</param> /// <param name="member">The member.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An attribute stored for the specified type.</returns> /// <returns>An attribute stored for the specified type.</returns>
public T RetrieveOne<T>(MemberInfo member, bool inherit = false) public T RetrieveOne<T>(MemberInfo member, Boolean inherit = false)
where T : Attribute where T : Attribute {
{ if(member == null) {
if (member == null)
return default; return default;
}
var attr = Retrieve( IEnumerable<Object> attr = this.Retrieve(
new Tuple<object, Type>(member, typeof(T)), new Tuple<Object, Type>(member, typeof(T)),
t => member.GetCustomAttributes(typeof(T), inherit)); t => member.GetCustomAttributes(typeof(T), inherit));
return ConvertToAttribute<T>(attr); return ConvertToAttribute<T>(attr);
@ -106,11 +104,10 @@
/// <typeparam name="T">The type to retrieve the attribute.</typeparam> /// <typeparam name="T">The type to retrieve the attribute.</typeparam>
/// <param name="inherit">if set to <c>true</c> [inherit].</param> /// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns>An attribute stored for the specified type.</returns> /// <returns>An attribute stored for the specified type.</returns>
public TAttribute RetrieveOne<TAttribute, T>(bool inherit = false) public TAttribute RetrieveOne<TAttribute, T>(Boolean inherit = false)
where TAttribute : Attribute where TAttribute : Attribute {
{ IEnumerable<Object> attr = this.Retrieve(
var attr = Retrieve( new Tuple<Object, Type>(typeof(T), typeof(TAttribute)),
new Tuple<object, Type>(typeof(T), typeof(TAttribute)),
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit)); t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
return ConvertToAttribute<TAttribute>(attr); return ConvertToAttribute<TAttribute>(attr);
@ -123,14 +120,14 @@
/// <param name="type">The type of the object.</param> /// <param name="type">The type of the object.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns> /// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
public Dictionary<PropertyInfo, IEnumerable<object>> Retrieve<T>(Type type, bool inherit = false) public Dictionary<PropertyInfo, IEnumerable<Object>> Retrieve<T>(Type type, Boolean inherit = false)
where T : Attribute where T : Attribute {
{ if(type == null) {
if (type == null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
}
return PropertyTypeCache.RetrieveAllProperties(type, true) return this.PropertyTypeCache.RetrieveAllProperties(type, true)
.ToDictionary(x => x, x => Retrieve<T>(x, inherit)); .ToDictionary(x => x, x => this.Retrieve<T>(x, inherit));
} }
/// <summary> /// <summary>
@ -142,33 +139,34 @@
/// <returns> /// <returns>
/// A dictionary of the properties and their attributes stored for the specified type. /// A dictionary of the properties and their attributes stored for the specified type.
/// </returns> /// </returns>
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false) public Dictionary<PropertyInfo, IEnumerable<Object>> RetrieveFromType<T>(Type attributeType, Boolean inherit = false) {
{ if(attributeType == null) {
if (attributeType == null)
throw new ArgumentNullException(nameof(attributeType)); throw new ArgumentNullException(nameof(attributeType));
return PropertyTypeCache.RetrieveAllProperties<T>(true)
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
} }
private static T ConvertToAttribute<T>(IEnumerable<object> attr) return this.PropertyTypeCache.RetrieveAllProperties<T>(true)
where T : Attribute .ToDictionary(x => x, x => this.Retrieve(x, attributeType, inherit));
{ }
if (attr?.Any() != true)
return default;
if (attr.Count() == 1) private static T ConvertToAttribute<T>(IEnumerable<Object> attr)
where T : Attribute {
if(attr?.Any() != true) {
return default;
}
if(attr.Count() == 1) {
return (T)Convert.ChangeType(attr.First(), typeof(T)); return (T)Convert.ChangeType(attr.First(), typeof(T));
}
throw new AmbiguousMatchException("Multiple custom attributes of the same type found."); throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
} }
private IEnumerable<object> Retrieve(Tuple<object, Type> key, Func<Tuple<object, Type>, IEnumerable<object>> factory) private IEnumerable<Object> Retrieve(Tuple<Object, Type> key, Func<Tuple<Object, Type>, IEnumerable<Object>> factory) {
{ if(factory == null) {
if (factory == null)
throw new ArgumentNullException(nameof(factory)); throw new ArgumentNullException(nameof(factory));
}
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null)); return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
} }
} }
} }

View File

@ -1,34 +1,29 @@
namespace Unosquare.Swan.Reflection using System;
{
using System;
using System.Reflection; using System.Reflection;
using Attributes; using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Reflection {
/// <summary> /// <summary>
/// Represents a Property object from a Object Reflection Property with extended values. /// Represents a Property object from a Object Reflection Property with extended values.
/// </summary> /// </summary>
public class ExtendedPropertyInfo public class ExtendedPropertyInfo {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class. /// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class.
/// </summary> /// </summary>
/// <param name="propertyInfo">The property information.</param> /// <param name="propertyInfo">The property information.</param>
public ExtendedPropertyInfo(PropertyInfo propertyInfo) public ExtendedPropertyInfo(PropertyInfo propertyInfo) {
{ if(propertyInfo == null) {
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo)); throw new ArgumentNullException(nameof(propertyInfo));
} }
Property = propertyInfo.Name; this.Property = propertyInfo.Name;
DataType = propertyInfo.PropertyType.Name; this.DataType = propertyInfo.PropertyType.Name;
foreach (PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true)) foreach(PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true)) {
{ this.Name = display.Name;
Name = display.Name; this.Description = display.Description;
Description = display.Description; this.GroupName = display.GroupName;
GroupName = display.GroupName; this.DefaultValue = display.DefaultValue;
DefaultValue = display.DefaultValue;
} }
} }
@ -38,7 +33,9 @@
/// <value> /// <value>
/// The property. /// The property.
/// </value> /// </value>
public string Property { get; } public String Property {
get;
}
/// <summary> /// <summary>
/// Gets or sets the type of the data. /// Gets or sets the type of the data.
@ -46,7 +43,9 @@
/// <value> /// <value>
/// The type of the data. /// The type of the data.
/// </value> /// </value>
public string DataType { get; } public String DataType {
get;
}
/// <summary> /// <summary>
/// Gets or sets the value. /// Gets or sets the value.
@ -54,7 +53,9 @@
/// <value> /// <value>
/// The value. /// The value.
/// </value> /// </value>
public object Value { get; set; } public Object Value {
get; set;
}
/// <summary> /// <summary>
/// Gets or sets the default value. /// Gets or sets the default value.
@ -62,7 +63,9 @@
/// <value> /// <value>
/// The default value. /// The default value.
/// </value> /// </value>
public object DefaultValue { get; } public Object DefaultValue {
get;
}
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
@ -70,7 +73,9 @@
/// <value> /// <value>
/// The name. /// The name.
/// </value> /// </value>
public string Name { get; } public String Name {
get;
}
/// <summary> /// <summary>
/// Gets or sets the description. /// Gets or sets the description.
@ -78,7 +83,9 @@
/// <value> /// <value>
/// The description. /// The description.
/// </value> /// </value>
public string Description { get; } public String Description {
get;
}
/// <summary> /// <summary>
/// Gets or sets the name of the group. /// Gets or sets the name of the group.
@ -86,22 +93,22 @@
/// <value> /// <value>
/// The name of the group. /// The name of the group.
/// </value> /// </value>
public string GroupName { get; } public String GroupName {
get;
}
} }
/// <summary> /// <summary>
/// Represents a Property object from a Object Reflection Property with extended values. /// Represents a Property object from a Object Reflection Property with extended values.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the object.</typeparam> /// <typeparam name="T">The type of the object.</typeparam>
public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo {
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class. /// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class.
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
public ExtendedPropertyInfo(string property) public ExtendedPropertyInfo(String property)
: base(typeof(T).GetProperty(property)) : base(typeof(T).GetProperty(property)) {
{
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More