383 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Globalization;
 | 
						|
using System.IO;
 | 
						|
using System.Linq;
 | 
						|
using System.Reflection;
 | 
						|
 | 
						|
using Swan;
 | 
						|
using Swan.DependencyInjection;
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
    /// <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
 | 
						|
 | 
						|
      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>
 | 
						|
    public Version LibraryVersion {
 | 
						|
      get; private set;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets the OS information.
 | 
						|
    /// </summary>
 | 
						|
    /// <value>
 | 
						|
    /// The os information.
 | 
						|
    /// </value>
 | 
						|
    public OsInfo OperatingSystem {
 | 
						|
      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>
 | 
						|
    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 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>
 | 
						|
    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.");
 | 
						|
 | 
						|
    /// <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>
 | 
						|
    public String Serial {
 | 
						|
      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>
 | 
						|
    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() {
 | 
						|
      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}",
 | 
						|
        $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}",
 | 
						|
      };
 | 
						|
 | 
						|
      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 {
 | 
						|
        if(String.IsNullOrWhiteSpace(this.Revision) == false && Int32.TryParse(this.Revision.ToUpperInvariant(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out Int32 boardVersion)) {
 | 
						|
          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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
} |