namespace Unosquare.RaspberryIO.Computer { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using Abstractions; using Native; using Swan; using Swan.DependencyInjection; /// /// Retrieves the RaspberryPI System Information. /// /// 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 const int NewStyleCodesMask = 0x800000; private BoardModel _boardModel; private ProcessorModel _processorModel; private Manufacturer _manufacturer; private MemorySize _memorySize; /// /// 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) .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.OrdinalIgnoreCase); 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 propertyArrayValue = propertyStringValue.Split(' '); property.SetValue(this, propertyArrayValue); } } } #endregion ExtractMemoryInfo(); ExtractBoardVersion(); ExtractOS(); } /// /// Gets the library version. /// public Version LibraryVersion { get; private set; } /// /// Gets the OS information. /// /// /// The os information. /// public OsInfo OperatingSystem { get; set; } /// /// Gets the Raspberry Pi version. /// public PiVersion RaspberryPiVersion { get; set; } /// /// Gets the board revision (1 or 2). /// /// /// The wiring pi board revision. /// public int BoardRevision { get; set; } /// /// Gets the number of processor cores. /// public int ProcessorCount => int.TryParse(Processor, out var outIndex) ? outIndex + 1 : 0; /// /// Gets the installed ram in bytes. /// public int InstalledRam { get; private set; } /// /// 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 revision number (accordingly to new-style revision codes). /// public int RevisionNumber { get; set; } /// /// Gets the board model (accordingly to new-style revision codes). /// /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}. public BoardModel BoardModel => NewStyleRevisionCodes ? _boardModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead."); /// /// Gets processor model (accordingly to new-style revision codes). /// /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}. public ProcessorModel ProcessorModel => NewStyleRevisionCodes ? _processorModel : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead."); /// /// Gets the manufacturer of the board (accordingly to new-style revision codes). /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}. public Manufacturer Manufacturer => NewStyleRevisionCodes ? _manufacturer : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead."); /// /// Gets the size of the memory (accordingly to new-style revision codes). /// /// This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)}. public MemorySize MemorySize => NewStyleRevisionCodes ? _memorySize : throw new InvalidOperationException($"This board does not support new-style revision codes. Use {nameof(RaspberryPiVersion)} property instead."); /// /// Gets the serial number. /// public string Serial { get; private set; } /// /// Gets the system up-time (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); /// /// Indicates if the board uses the new-style revision codes. /// private bool NewStyleRevisionCodes { get; set; } /// /// 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).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 propertyValues2 = new List { "System Information", $"\t{nameof(LibraryVersion),-22}: {LibraryVersion}", $"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}", }; foreach (var 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) { var 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 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(); } } private void ExtractBoardVersion() { var hasSysInfo = DependencyContainer.Current.CanResolve(); try { if (string.IsNullOrWhiteSpace(Revision) == false && int.TryParse( Revision.ToUpperInvariant(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var boardVersion)) { RaspberryPiVersion = PiVersion.Unknown; if (Enum.IsDefined(typeof(PiVersion), boardVersion)) RaspberryPiVersion = (PiVersion)boardVersion; if ((boardVersion & NewStyleCodesMask) == NewStyleCodesMask) { NewStyleRevisionCodes = true; RevisionNumber = boardVersion & 0xF; _boardModel = (BoardModel)((boardVersion >> 4) & 0xFF); _processorModel = (ProcessorModel)((boardVersion >> 12) & 0xF); _manufacturer = (Manufacturer)((boardVersion >> 16) & 0xF); _memorySize = (MemorySize)((boardVersion >> 20) & 0x7); } } if (hasSysInfo) BoardRevision = (int)DependencyContainer.Current.Resolve().BoardRevision; } catch { /* Ignore */ } if (hasSysInfo) LibraryVersion = DependencyContainer.Current.Resolve().LibraryVersion; } private void ExtractMemoryInfo() { if (!File.Exists(MemInfoFilePath)) return; 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)) continue; InstalledRam = parsedMem * 1024; break; } } } }