RaspberryIO_26/Unosquare.RaspberryIO/Computer/SystemInfo.cs

383 lines
12 KiB
C#
Raw Normal View History

2019-12-06 23:12:34 +01:00
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Swan;
2019-12-08 12:34:43 +01:00
using Swan.DependencyInjection;
2019-12-06 23:12:34 +01:00
using Unosquare.RaspberryIO.Abstractions;
using Unosquare.RaspberryIO.Native;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Retrieves the RaspberryPI System Information.
///
/// 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 const Int32 NewStyleCodesMask = 0x800000;
private BoardModel _boardModel;
private ProcessorModel _processorModel;
private Manufacturer _manufacturer;
private MemorySize _memorySize;
2019-12-04 18:57:18 +01:00
/// <summary>
2019-12-06 23:12:34 +01:00
/// Prevents a default instance of the <see cref="SystemInfo"/> class from being created.
2019-12-04 18:57:18 +01:00
/// </summary>
2019-12-06 23:12:34 +01:00
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller.</exception>
private SystemInfo() {
#region Obtain and format a property dictionary
PropertyInfo[] properties = typeof(SystemInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(
p => p.CanWrite && p.CanRead && (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[]))).ToArray();
Dictionary<String, PropertyInfo> propDictionary = new Dictionary<String, PropertyInfo>(StringComparer.OrdinalIgnoreCase);
foreach(PropertyInfo prop in properties) {
propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop;
}
#endregion
#region Extract CPU information
if(File.Exists(CpuInfoFilePath)) {
String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
foreach(String line in cpuInfoLines) {
String[] lineParts = line.Split(new[] { ':' }, 2);
if(lineParts.Length != 2) {
continue;
}
String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty);
String propertyStringValue = lineParts[1].Trim();
if(!propDictionary.ContainsKey(propertyKey)) {
continue;
}
PropertyInfo property = propDictionary[propertyKey];
if(property.PropertyType == typeof(String)) {
property.SetValue(this, propertyStringValue);
} else if(property.PropertyType == typeof(String[])) {
String[] propertyArrayValue = propertyStringValue.Split(' ');
property.SetValue(this, propertyArrayValue);
}
}
}
#endregion
this.ExtractMemoryInfo();
this.ExtractBoardVersion();
this.ExtractOS();
}
/// <summary>
/// Gets the library version.
/// </summary>
2019-12-08 12:34:43 +01:00
public Version LibraryVersion {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the OS information.
/// </summary>
/// <value>
/// The os information.
/// </value>
2019-12-08 12:34:43 +01:00
public OsInfo OperatingSystem {
2019-12-06 23:12:34 +01:00
get; set;
}
/// <summary>
/// Gets the Raspberry Pi version.
/// </summary>
public PiVersion RaspberryPiVersion {
get; set;
}
/// <summary>
/// Gets the board revision (1 or 2).
/// </summary>
/// <value>
/// The wiring pi board revision.
/// </value>
public Int32 BoardRevision {
get; set;
}
/// <summary>
/// Gets the number of processor cores.
/// </summary>
public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0;
/// <summary>
/// Gets the installed ram in bytes.
/// </summary>
public Int32 InstalledRam {
get; private set;
}
/// <summary>
/// Gets a value indicating whether this CPU is little endian.
/// </summary>
public Boolean IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary>
/// Gets the CPU model name.
/// </summary>
2019-12-08 12:34:43 +01:00
public String ModelName {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets a list of supported CPU features.
/// </summary>
2019-12-08 12:34:43 +01:00
public String[] Features {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the CPU implementer hex code.
/// </summary>
2019-12-08 12:34:43 +01:00
public String CpuImplementer {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the CPU architecture code.
/// </summary>
2019-12-08 12:34:43 +01:00
public String CpuArchitecture {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the CPU variant code.
/// </summary>
2019-12-08 12:34:43 +01:00
public String CpuVariant {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the CPU part code.
/// </summary>
2019-12-08 12:34:43 +01:00
public String CpuPart {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the CPU revision code.
/// </summary>
2019-12-08 12:34:43 +01:00
public String CpuRevision {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the hardware model number.
/// </summary>
2019-12-08 12:34:43 +01:00
public String Hardware {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the hardware revision number.
/// </summary>
2019-12-08 12:34:43 +01:00
public String Revision {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the revision number (accordingly to new-style revision codes).
/// </summary>
public Int32 RevisionNumber {
get; set;
}
/// <summary>
/// Gets the board model (accordingly to new-style revision codes).
/// </summary>
/// /// <exception cref="InvalidOperationException">This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.</exception>
2019-12-08 12:34:43 +01:00
public BoardModel BoardModel => this.NewStyleRevisionCodes ? this._boardModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
2019-12-06 23:12:34 +01:00
/// <summary>
/// Gets processor model (accordingly to new-style revision codes).
/// </summary>
/// /// <exception cref="InvalidOperationException">This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.</exception>
public ProcessorModel ProcessorModel => this.NewStyleRevisionCodes ? this._processorModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
/// <summary>
/// Gets the manufacturer of the board (accordingly to new-style revision codes).
/// </summary>
/// <exception cref="InvalidOperationException">This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.</exception>
public Manufacturer Manufacturer => this.NewStyleRevisionCodes ? this._manufacturer : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
/// <summary>
/// Gets the size of the memory (accordingly to new-style revision codes).
/// </summary>
/// <exception cref="InvalidOperationException">This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}.</exception>
public MemorySize MemorySize => this.NewStyleRevisionCodes ? this._memorySize : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(this.RaspberryPiVersion)} property instead.");
/// <summary>
/// Gets the serial number.
/// </summary>
2019-12-08 12:34:43 +01:00
public String Serial {
2019-12-06 23:12:34 +01:00
get; private set;
}
/// <summary>
/// Gets the system up-time (in seconds).
/// </summary>
public Double Uptime {
get {
try {
if(File.Exists(UptimeFilePath) == false) {
return 0;
}
String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) {
return result;
}
} catch {
/* Ignore */
}
return 0;
}
}
/// <summary>
/// Gets the uptime in TimeSpan.
/// </summary>
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime);
/// <summary>
/// Indicates if the board uses the new-style revision codes.
/// </summary>
private Boolean NewStyleRevisionCodes {
get; set;
}
/// <summary>
/// Placeholder for processor index.
/// </summary>
2019-12-08 12:34:43 +01:00
private String Processor {
2019-12-06 23:12:34 +01:00
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() {
PropertyInfo[] properties = typeof(SystemInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(
p => p.CanRead && (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[]) || p.PropertyType == typeof(Int32) || p.PropertyType == typeof(Boolean) || p.PropertyType == typeof(TimeSpan))).ToArray();
List<String> propertyValues2 = new List<String> {
"System Information",
$"\t{nameof(this.LibraryVersion),-22}: {this.LibraryVersion}",
2019-12-08 12:34:43 +01:00
$"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}",
2019-12-06 23:12:34 +01:00
};
foreach(PropertyInfo property in properties) {
if(property.PropertyType != typeof(String[])) {
propertyValues2.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
} else if(property.GetValue(this) is String[] allValues) {
String concatValues = String.Join(" ", allValues);
propertyValues2.Add($"\t{property.Name,-22}: {concatValues}");
}
}
return String.Join(Environment.NewLine, propertyValues2.ToArray());
}
private void ExtractOS() {
try {
_ = Standard.Uname(out SystemName unameInfo);
this.OperatingSystem = new OsInfo {
DomainName = unameInfo.DomainName,
Machine = unameInfo.Machine,
NodeName = unameInfo.NodeName,
Release = unameInfo.Release,
SysName = unameInfo.SysName,
Version = unameInfo.Version,
};
} catch {
this.OperatingSystem = new OsInfo();
}
}
private void ExtractBoardVersion() {
Boolean hasSysInfo = DependencyContainer.Current.CanResolve<ISystemInfo>();
try {
2019-12-08 12:34:43 +01:00
if(String.IsNullOrWhiteSpace(this.Revision) == false && Int32.TryParse(this.Revision.ToUpperInvariant(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out Int32 boardVersion)) {
2019-12-06 23:12:34 +01:00
this.RaspberryPiVersion = PiVersion.Unknown;
if(Enum.IsDefined(typeof(PiVersion), boardVersion)) {
this.RaspberryPiVersion = (PiVersion)boardVersion;
}
if((boardVersion & NewStyleCodesMask) == NewStyleCodesMask) {
this.NewStyleRevisionCodes = true;
this.RevisionNumber = boardVersion & 0xF;
this._boardModel = (BoardModel)((boardVersion >> 4) & 0xFF);
this._processorModel = (ProcessorModel)((boardVersion >> 12) & 0xF);
this._manufacturer = (Manufacturer)((boardVersion >> 16) & 0xF);
this._memorySize = (MemorySize)((boardVersion >> 20) & 0x7);
}
}
if(hasSysInfo) {
this.BoardRevision = (Int32)DependencyContainer.Current.Resolve<ISystemInfo>().BoardRevision;
}
} catch {
/* Ignore */
}
if(hasSysInfo) {
this.LibraryVersion = DependencyContainer.Current.Resolve<ISystemInfo>().LibraryVersion;
}
}
private void ExtractMemoryInfo() {
if(!File.Exists(MemInfoFilePath)) {
return;
}
String[] memInfoLines = File.ReadAllLines(MemInfoFilePath);
foreach(String line in memInfoLines) {
String[] lineParts = line.Split(new[] { ':' }, 2);
if(lineParts.Length != 2) {
continue;
}
if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) {
continue;
}
String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim();
if(!Int32.TryParse(memKb, out Int32 parsedMem)) {
continue;
}
this.InstalledRam = parsedMem * 1024;
break;
}
}
}
2019-12-04 18:57:18 +01:00
}