Init RaspberryIO

This commit is contained in:
2019-02-17 14:08:57 +01:00
commit e7c3f7af91
193 changed files with 36743 additions and 0 deletions
@@ -0,0 +1,80 @@
namespace Unosquare.RaspberryIO.Computer
{
using Swan.Abstractions;
using System.Globalization;
using System.IO;
/// <summary>
/// The Official Raspberry Pi 7-inch touch display from the foundation
/// Some docs available here:
/// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959
/// </summary>
public class DsiDisplay : SingletonBase<DsiDisplay>
{
private const string BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power";
private const string BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
/// <summary>
/// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created.
/// </summary>
private DsiDisplay()
{
// placeholder
}
/// <summary>
/// Gets a value indicating whether the Pi Foundation Display files are present.
/// </summary>
/// <value>
/// <c>true</c> if this instance is present; otherwise, <c>false</c>.
/// </value>
public bool IsPresent => File.Exists(BrightnessFilename);
/// <summary>
/// Gets or sets the brightness of the DSI display via filesystem.
/// </summary>
/// <value>
/// The brightness.
/// </value>
public byte Brightness
{
get
{
if (IsPresent == false) return 0;
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));
}
}
/// <summary>
/// Gets or sets a value indicating whether the backlight of the DSI display on.
/// This operation is performed via the file system
/// </summary>
/// <value>
/// <c>true</c> if this instance is backlight on; otherwise, <c>false</c>.
/// </value>
public bool IsBacklightOn
{
get
{
if (IsPresent == false) return false;
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");
}
}
}
}
@@ -0,0 +1,40 @@
namespace Unosquare.RaspberryIO.Computer
{
using System.Net;
/// <summary>
/// Represents a Network Adapter
/// </summary>
public class NetworkAdapterInfo
{
/// <summary>
/// Gets the name.
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Gets the IP V4 address.
/// </summary>
public IPAddress IPv4 { get; internal set; }
/// <summary>
/// Gets the IP V6 address.
/// </summary>
public IPAddress IPv6 { get; internal set; }
/// <summary>
/// Gets the name of the access point.
/// </summary>
public string AccessPointName { get; internal set; }
/// <summary>
/// Gets the MAC (Physical) address.
/// </summary>
public string MacAddress { get; internal set; }
/// <summary>
/// Gets a value indicating whether this instance is wireless.
/// </summary>
public bool IsWireless { get; internal set; }
}
}
@@ -0,0 +1,266 @@
namespace Unosquare.RaspberryIO.Computer
{
using Swan;
using Swan.Abstractions;
using Swan.Components;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
/// <summary>
/// Represents the network information
/// </summary>
public class NetworkSettings : SingletonBase<NetworkSettings>
{
private const string EssidTag = "ESSID:";
/// <summary>
/// Gets the local machine Host Name.
/// </summary>
public string HostName => Network.HostName;
/// <summary>
/// Retrieves the wireless networks.
/// </summary>
/// <param name="adapter">The adapter.</param>
/// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter });
/// <summary>
/// Retrieves the wireless networks.
/// </summary>
/// <param name="adapters">The adapters.</param>
/// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string[] adapters = null)
{
var result = new List<WirelessNetworkInfo>();
foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name))
{
var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
var outputLines =
wirelessOutput.Split('\n')
.Select(x => x.Trim())
.Where(x => string.IsNullOrWhiteSpace(x) == false)
.ToArray();
for (var i = 0; i < outputLines.Length; i++)
{
var line = outputLines[i];
if (line.StartsWith(EssidTag) == false) continue;
var network = new WirelessNetworkInfo()
{
Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty)
};
while (true)
{
if (i + 1 >= outputLines.Length) break;
// should look for two lines before the ESSID acording to the scan
line = outputLines[i - 2];
if (line.StartsWith("Quality="))
{
network.Quality = line.Replace("Quality=", string.Empty);
break;
}
}
while (true)
{
if (i + 1 >= outputLines.Length) break;
// should look for a line before the ESSID acording to the scan
line = outputLines[i - 1];
if (line.StartsWith("Encryption key:"))
{
network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on";
break;
}
}
if (result.Any(x => x.Name == network.Name) == false)
result.Add(network);
}
}
return result.OrderBy(x => x.Name).ToList();
}
/// <summary>
/// Setups the wireless network.
/// </summary>
/// <param name="adapterName">Name of the adapter.</param>
/// <param name="networkSsid">The network ssid.</param>
/// <param name="password">The password.</param>
/// <param name="countryCode">The 2-letter country code in uppercase. Default is US.</param>
/// <returns>True if successful. Otherwise, false.</returns>
public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US")
{
// TODO: Get the country where the device is located to set 'country' param in payload var
var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
payload += string.IsNullOrEmpty(password)
? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n"
: $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
try
{
File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait();
ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait();
ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait();
}
catch (Exception ex)
{
ex.Log(nameof(NetworkSettings));
return false;
}
return true;
}
/// <summary>
/// Retrieves the network adapters.
/// </summary>
/// <returns>A list of network adapters.</returns>
public List<NetworkAdapterInfo> RetrieveAdapters()
{
const string hWaddr = "HWaddr ";
const string ether = "ether ";
var result = new List<NetworkAdapterInfo>();
var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result;
var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig")
.Result.Split('\n')
.Where(x => x.Contains("no wireless extensions.") == false)
.ToArray();
var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray();
for (var i = 0; i < outputLines.Length; i++)
{
// grab the current line
var line = outputLines[i];
// skip if the line is indented
if (char.IsLetterOrDigit(line[0]) == false)
continue;
// Read the line as an adatper
var adapter = new NetworkAdapterInfo
{
Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
};
// Parse the MAC address in old version of ifconfig; it comes in the first line
if (line.IndexOf(hWaddr) >= 0)
{
var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
}
// Parse the info in lines other than the first
for (var j = i + 1; j < outputLines.Length; j++)
{
// Get the contents of the indented line
var indentedLine = outputLines[j];
// We have hit the next adapter info
if (char.IsLetterOrDigit(indentedLine[0]))
{
i = j - 1;
break;
}
// Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress))
{
var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim();
}
// Parse the IPv4 Address
{
var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
if (addressText != null)
{
if (IPAddress.TryParse(addressText, out var outValue))
adapter.IPv4 = outValue;
}
}
// Parse the IPv6 Address
{
var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
if (addressText != null)
{
if (IPAddress.TryParse(addressText, out var outValue))
adapter.IPv6 = outValue;
}
}
// we have hit the end of the output in an indented line
if (j >= outputLines.Length - 1)
i = outputLines.Length;
}
// Retrieve the wireless LAN info
var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
if (wlanInfo != null)
{
adapter.IsWireless = true;
var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
if (essidParts.Length >= 2)
{
adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim();
}
}
// Add the current adapter to the result
result.Add(adapter);
}
return result.OrderBy(x => x.Name).ToList();
}
/// <summary>
/// Retrieves current wireless connected network name.
/// </summary>
/// <returns>The connected network name.</returns>
public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result;
/// <summary>
/// Parses the output tag from the given line.
/// </summary>
/// <param name="indentedLine">The indented line.</param>
/// <param name="tagName">Name of the tag.</param>
/// <returns>The value after the tag identifier</returns>
private static string ParseOutputTagFromLine(string indentedLine, string tagName)
{
if (indentedLine.IndexOf(tagName) < 0)
return null;
var startIndex = indentedLine.IndexOf(tagName) + tagName.Length;
var builder = new StringBuilder(1024);
for (var c = startIndex; c < indentedLine.Length; c++)
{
var currentChar = indentedLine[c];
if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar))
break;
builder.Append(currentChar);
}
return builder.ToString();
}
}
}
+46
View File
@@ -0,0 +1,46 @@
namespace Unosquare.RaspberryIO.Computer
{
/// <summary>
/// Represents the OS Information
/// </summary>
public class OsInfo
{
/// <summary>
/// System name
/// </summary>
public string SysName { get; set; }
/// <summary>
/// Node name
/// </summary>
public string NodeName { get; set; }
/// <summary>
/// Release level
/// </summary>
public string Release { get; set; }
/// <summary>
/// Version level
/// </summary>
public string Version { get; set; }
/// <summary>
/// Hardware level
/// </summary>
public string Machine { get; set; }
/// <summary>
/// Domain name
/// </summary>
public string DomainName { get; set; }
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public override string ToString() => $"{SysName} {Release} {Version}";
}
}
+134
View File
@@ -0,0 +1,134 @@
namespace Unosquare.RaspberryIO.Computer
{
/// <summary>
/// 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/
/// </summary>
public enum PiVersion
{
/// <summary>
/// The unknown version
/// </summary>
Unknown = 0,
/// <summary>
/// The model b rev1
/// </summary>
ModelBRev1 = 0x0002,
/// <summary>
/// The model b rev1 ec N0001
/// </summary>
ModelBRev1ECN0001 = 0x0003,
/// <summary>
/// The model b rev2x04
/// </summary>
ModelBRev2x04 = 0x0004,
/// <summary>
/// The model b rev2x05
/// </summary>
ModelBRev2x05 = 0x0005,
/// <summary>
/// The model b rev2x06
/// </summary>
ModelBRev2x06 = 0x0006,
/// <summary>
/// The model ax07
/// </summary>
ModelAx07 = 0x0007,
/// <summary>
/// The model ax08
/// </summary>
ModelAx08 = 0x0008,
/// <summary>
/// The model ax09
/// </summary>
ModelAx09 = 0x0009,
/// <summary>
/// The model b rev2x0d
/// </summary>
ModelBRev2x0d,
/// <summary>
/// The model b rev2x0e
/// </summary>
ModelBRev2x0e,
/// <summary>
/// The model b rev2x0f
/// </summary>
ModelBRev2x0f = 0x000f,
/// <summary>
/// The model b plus0x10
/// </summary>
ModelBPlus0x10 = 0x0010,
/// <summary>
/// The model b plus0x13
/// </summary>
ModelBPlus0x13 = 0x0013,
/// <summary>
/// The compute module0x11
/// </summary>
ComputeModule0x11 = 0x0011,
/// <summary>
/// The compute module0x14
/// </summary>
ComputeModule0x14 = 0x0014,
/// <summary>
/// The model a plus0x12
/// </summary>
ModelAPlus0x12 = 0x0012,
/// <summary>
/// The model a plus0x15
/// </summary>
ModelAPlus0x15 = 0x0015,
/// <summary>
/// The pi2 model B1V1 sony
/// </summary>
Pi2ModelB1v1Sony = 0xa01041,
/// <summary>
/// The pi2 model B1V1 embest
/// </summary>
Pi2ModelB1v1Embest = 0xa21041,
/// <summary>
/// The pi2 model B1V2
/// </summary>
Pi2ModelB1v2 = 0xa22042,
/// <summary>
/// The pi zero1v2
/// </summary>
PiZero1v2 = 0x900092,
/// <summary>
/// The pi zero1v3
/// </summary>
PiZero1v3 = 0x900093,
/// <summary>
/// The pi3 model b sony
/// </summary>
Pi3ModelBSony = 0xa02082,
/// <summary>
/// The pi3 model b embest
/// </summary>
Pi3ModelBEmbest = 0xa22082
}
}
@@ -0,0 +1,344 @@
namespace Unosquare.RaspberryIO.Computer
{
using Native;
using Swan.Abstractions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
/// <summary>
/// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html
/// </summary>
public sealed class SystemInfo : SingletonBase<SystemInfo>
{
private const string CpuInfoFilePath = "/proc/cpuinfo";
private const string MemInfoFilePath = "/proc/meminfo";
private const string UptimeFilePath = "/proc/uptime";
private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase;
private static readonly object SyncRoot = new object();
/// <summary>
/// Prevents a default instance of the <see cref="SystemInfo"/> class from being created.
/// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
private SystemInfo()
{
#region Obtain and format a property dictionary
var properties =
typeof(SystemInfo).GetTypeInfo()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(
p =>
p.CanWrite && p.CanRead &&
(p.PropertyType == typeof(string) || p.PropertyType == typeof(string[])))
.ToArray();
var propDictionary = new Dictionary<string, PropertyInfo>(StringComparer);
foreach (var prop in properties)
{
propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop;
}
#endregion
#region Extract CPU information
if (File.Exists(CpuInfoFilePath))
{
var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
foreach (var line in cpuInfoLines)
{
var lineParts = line.Split(new[] { ':' }, 2);
if (lineParts.Length != 2)
continue;
var propertyKey = lineParts[0].Trim().Replace(" ", string.Empty);
var propertyStringValue = lineParts[1].Trim();
if (!propDictionary.ContainsKey(propertyKey)) continue;
var property = propDictionary[propertyKey];
if (property.PropertyType == typeof(string))
{
property.SetValue(this, propertyStringValue);
}
else if (property.PropertyType == typeof(string[]))
{
var propertyArrayAvalue = propertyStringValue.Split(' ');
property.SetValue(this, propertyArrayAvalue);
}
}
}
#endregion
#region Extract Memory Information
if (File.Exists(MemInfoFilePath))
{
var memInfoLines = File.ReadAllLines(MemInfoFilePath);
foreach (var line in memInfoLines)
{
var lineParts = line.Split(new[] { ':' }, 2);
if (lineParts.Length != 2)
continue;
if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false)
continue;
var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim();
if (int.TryParse(memKb, out var parsedMem))
{
InstalledRam = parsedMem * 1024;
break;
}
}
}
#endregion
#region Board Version and Form Factor
try
{
if (string.IsNullOrWhiteSpace(Revision) == false &&
int.TryParse(
Revision.ToUpperInvariant(),
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out var boardVersion))
{
RaspberryPiVersion = PiVersion.Unknown;
if (Enum.GetValues(typeof(PiVersion)).Cast<int>().Contains(boardVersion))
{
RaspberryPiVersion = (PiVersion)boardVersion;
}
}
WiringPiBoardRevision = WiringPi.PiBoardRev();
}
catch
{
/* Ignore */
}
#endregion
#region Version Information
{
var libParts = WiringPi.WiringPiLibrary.Split('.');
var major = int.Parse(libParts[libParts.Length - 2]);
var minor = int.Parse(libParts[libParts.Length - 1]);
var version = new Version(major, minor);
WiringPiVersion = version;
}
#endregion
#region Extract OS Info
try
{
Standard.Uname(out var unameInfo);
OperatingSystem = new OsInfo
{
DomainName = unameInfo.DomainName,
Machine = unameInfo.Machine,
NodeName = unameInfo.NodeName,
Release = unameInfo.Release,
SysName = unameInfo.SysName,
Version = unameInfo.Version
};
}
catch
{
OperatingSystem = new OsInfo();
}
#endregion
}
/// <summary>
/// Gets the wiring pi library version.
/// </summary>
public Version WiringPiVersion { get; }
/// <summary>
/// Gets the OS information.
/// </summary>
/// <value>
/// The os information.
/// </value>
public OsInfo OperatingSystem { get; }
/// <summary>
/// Gets the Raspberry Pi version.
/// </summary>
public PiVersion RaspberryPiVersion { get; }
/// <summary>
/// Gets the Wiring Pi board revision (1 or 2).
/// </summary>
/// <value>
/// The wiring pi board revision.
/// </value>
public int WiringPiBoardRevision { get; }
/// <summary>
/// Gets the number of processor cores.
/// </summary>
public int ProcessorCount
{
get
{
if (int.TryParse(Processor, out var outIndex))
{
return outIndex + 1;
}
return 0;
}
}
/// <summary>
/// Gets the installed ram in bytes.
/// </summary>
public int InstalledRam { get; }
/// <summary>
/// Gets a value indicating whether this CPU is little endian.
/// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary>
/// Gets the CPU model name.
/// </summary>
public string ModelName { get; private set; }
/// <summary>
/// Gets a list of supported CPU features.
/// </summary>
public string[] Features { get; private set; }
/// <summary>
/// Gets the CPU implementer hex code.
/// </summary>
public string CpuImplementer { get; private set; }
/// <summary>
/// Gets the CPU architecture code.
/// </summary>
public string CpuArchitecture { get; private set; }
/// <summary>
/// Gets the CPU variant code.
/// </summary>
public string CpuVariant { get; private set; }
/// <summary>
/// Gets the CPU part code.
/// </summary>
public string CpuPart { get; private set; }
/// <summary>
/// Gets the CPU revision code.
/// </summary>
public string CpuRevision { get; private set; }
/// <summary>
/// Gets the hardware model number.
/// </summary>
public string Hardware { get; private set; }
/// <summary>
/// Gets the hardware revision number.
/// </summary>
public string Revision { get; private set; }
/// <summary>
/// Gets the serial number.
/// </summary>
public string Serial { get; private set; }
/// <summary>
/// Gets the system uptime (in seconds).
/// </summary>
public double Uptime
{
get
{
try
{
if (File.Exists(UptimeFilePath) == false) return 0;
var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1 && float.TryParse(parts[0], out var result))
return result;
}
catch
{
/* Ignore */
}
return 0;
}
}
/// <summary>
/// Gets the uptime in TimeSpan.
/// </summary>
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime);
/// <summary>
/// Placeholder for processor index
/// </summary>
private string Processor { get; set; }
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public override string ToString()
{
var properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead && (
p.PropertyType == typeof(string) ||
p.PropertyType == typeof(string[]) ||
p.PropertyType == typeof(int) ||
p.PropertyType == typeof(bool) ||
p.PropertyType == typeof(TimeSpan)))
.ToArray();
var properyValues = new List<string>
{
"System Information",
$"\t{nameof(WiringPiVersion),-22}: {WiringPiVersion}",
$"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}"
};
foreach (var property in properties)
{
if (property.PropertyType != typeof(string[]))
{
properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
}
else if (property.GetValue(this) is string[] allValues)
{
var concatValues = string.Join(" ", allValues);
properyValues.Add($"\t{property.Name,-22}: {concatValues}");
}
}
return string.Join(Environment.NewLine, properyValues.ToArray());
}
}
}
@@ -0,0 +1,23 @@
namespace Unosquare.RaspberryIO.Computer
{
/// <summary>
/// Represents a wireless network information
/// </summary>
public class WirelessNetworkInfo
{
/// <summary>
/// Gets the ESSID of the Wireless network.
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Gets the network quality.
/// </summary>
public string Quality { get; internal set; }
/// <summary>
/// Gets a value indicating whether this instance is encrypted.
/// </summary>
public bool IsEncrypted { get; internal set; }
}
}