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; /// /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html /// public sealed class SystemInfo : SingletonBase { private const string CpuInfoFilePath = "/proc/cpuinfo"; private const string MemInfoFilePath = "/proc/meminfo"; private const string UptimeFilePath = "/proc/uptime"; private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase; private static readonly object SyncRoot = new object(); /// /// Prevents a default instance of the class from being created. /// /// Could not initialize the GPIO controller private SystemInfo() { #region Obtain and format a property dictionary var properties = typeof(SystemInfo).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(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().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 } /// /// Gets the wiring pi library version. /// public Version WiringPiVersion { get; } /// /// Gets the OS information. /// /// /// The os information. /// public OsInfo OperatingSystem { get; } /// /// Gets the Raspberry Pi version. /// public PiVersion RaspberryPiVersion { get; } /// /// Gets the Wiring Pi board revision (1 or 2). /// /// /// The wiring pi board revision. /// public int WiringPiBoardRevision { get; } /// /// Gets the number of processor cores. /// public int ProcessorCount { get { if (int.TryParse(Processor, out var outIndex)) { return outIndex + 1; } return 0; } } /// /// Gets the installed ram in bytes. /// public int InstalledRam { get; } /// /// Gets a value indicating whether this CPU is little endian. /// public bool IsLittleEndian => BitConverter.IsLittleEndian; /// /// Gets the CPU model name. /// public string ModelName { get; private set; } /// /// Gets a list of supported CPU features. /// public string[] Features { get; private set; } /// /// Gets the CPU implementer hex code. /// public string CpuImplementer { get; private set; } /// /// Gets the CPU architecture code. /// public string CpuArchitecture { get; private set; } /// /// Gets the CPU variant code. /// public string CpuVariant { get; private set; } /// /// Gets the CPU part code. /// public string CpuPart { get; private set; } /// /// Gets the CPU revision code. /// public string CpuRevision { get; private set; } /// /// Gets the hardware model number. /// public string Hardware { get; private set; } /// /// Gets the hardware revision number. /// public string Revision { get; private set; } /// /// Gets the serial number. /// public string Serial { get; private set; } /// /// Gets the system uptime (in seconds). /// public double Uptime { get { try { if (File.Exists(UptimeFilePath) == false) return 0; var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length >= 1 && float.TryParse(parts[0], out var result)) return result; } catch { /* Ignore */ } return 0; } } /// /// Gets the uptime in TimeSpan. /// public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime); /// /// Placeholder for processor index /// private string Processor { get; set; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { var properties = typeof(SystemInfo).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 { "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()); } } }