using Unosquare.RaspberryIO.Native; using Unosquare.Swan.Abstractions; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; namespace Unosquare.RaspberryIO.Computer { /// /// 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; [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "")] 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 PropertyInfo[] 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(); Dictionary propDictionary = new Dictionary(StringComparer); 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[] propertyArrayAvalue = propertyStringValue.Split(' '); property.SetValue(this, propertyArrayAvalue); } } } #endregion #region Extract Memory Information if(File.Exists(MemInfoFilePath)) { 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)) { this.InstalledRam = parsedMem * 1024; break; } } } #endregion #region Board Version and Form Factor try { if(String.IsNullOrWhiteSpace(this.Revision) == false && Int32.TryParse( this.Revision.ToUpperInvariant(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out Int32 boardVersion)) { this.RaspberryPiVersion = PiVersion.Unknown; if(Enum.GetValues(typeof(PiVersion)).Cast().Contains(boardVersion)) { this.RaspberryPiVersion = (PiVersion)boardVersion; } } this.WiringPiBoardRevision = WiringPi.PiBoardRev(); } catch { /* Ignore */ } #endregion #region Version Information { String[] libParts = WiringPi.WiringPiLibrary.Split('.'); Int32 major = Int32.Parse(libParts[libParts.Length - 2]); Int32 minor = Int32.Parse(libParts[libParts.Length - 1]); Version version = new Version(major, minor); this.WiringPiVersion = version; } #endregion #region Extract OS Info 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(); } #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 Int32 WiringPiBoardRevision { get; } /// /// Gets the number of processor cores. /// public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0; /// /// Gets the installed ram in bytes. /// public Int32 InstalledRam { get; } /// /// Gets a value indicating whether this CPU is little endian. /// public Boolean IsLittleEndian => BitConverter.IsLittleEndian; /// /// Gets the CPU model name. /// public String ModelName { get; private set; } /// /// Gets a list of supported CPU features. /// public String[] Features { get; private set; } /// /// Gets the CPU implementer hex code. /// public String CpuImplementer { get; private set; } /// /// Gets the CPU architecture code. /// public String CpuArchitecture { get; private set; } /// /// Gets the CPU variant code. /// public String CpuVariant { get; private set; } /// /// Gets the CPU part code. /// public String CpuPart { get; private set; } /// /// Gets the CPU revision code. /// public String CpuRevision { get; private set; } /// /// Gets the hardware model number. /// public String Hardware { get; private set; } /// /// Gets the hardware revision number. /// public String Revision { get; private set; } /// /// Gets the 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; } String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) { return result; } } catch { /* Ignore */ } return 0; } } /// /// Gets the uptime in TimeSpan. /// public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime); /// /// Placeholder for processor index /// private String Processor { get; set; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override String ToString() { PropertyInfo[] properties = typeof(SystemInfo).GetTypeInfo().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 properyValues = new List { "System Information", $"\t{nameof(this.WiringPiVersion),-22}: {this.WiringPiVersion}", $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}" }; foreach(PropertyInfo 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) { String concatValues = String.Join(" ", allValues); properyValues.Add($"\t{property.Name,-22}: {concatValues}"); } } return String.Join(Environment.NewLine, properyValues.ToArray()); } } }