From 1e39a0cd5fb0a5330f778e08fb066958dfc13c4f Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Sun, 24 Sep 2023 17:31:26 -0600 Subject: [PATCH] Hardware Info working properly 100% --- .gitignore | 2 + Gemfile | 10 +- Gemfile.lock | 10 +- bin/runtime_log.log | 3 + bin/system_info.log | 36 ------ lib/cpu_info.rb | 32 ++++- lib/disk_info.rb | 1 + lib/display_handler.rb | 263 ++++++++++++++++++++++++++++++++++------- lib/gpu_info.rb | 33 +++++- 9 files changed, 300 insertions(+), 90 deletions(-) delete mode 100644 bin/system_info.log diff --git a/.gitignore b/.gitignore index d6aa672..11d82fe 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ build-iPhoneSimulator/ # Used by RuboCop. Remote config files pulled in from inherit_from directive. # .rubocop-https?--* +bin/runtime_log.log +.gitignore diff --git a/Gemfile b/Gemfile index a81a539..ba45499 100755 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,13 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' # gem "rails" -gem "tty-prompt", "~> 0.23.1" +gem 'speedtest_net', '~> 0.9.2' -gem "speedtest_net", "~> 0.9.2" +gem 'reline', '~> 0.3.8' + +gem 'curses', '~> 1.4', require: false + +gem "tty-prompt", "~> 0.23.1" diff --git a/Gemfile.lock b/Gemfile.lock index 7d3779d..6b191b4 100755 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,14 +1,16 @@ GEM remote: https://rubygems.org/ specs: - byebug (11.1.3) curb (1.0.5) + curses (1.4.4) ethon (0.16.0) ffi (>= 1.15.0) ffi (1.15.5) + io-console (0.6.0) pastel (0.8.0) tty-color (~> 0.5) - rdebug (0.1) + reline (0.3.8) + io-console (~> 0.5) rexml (3.2.6) speedtest_net (0.9.2) curb (>= 0.9, < 2.0) @@ -32,8 +34,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - byebug (~> 11.1) - rdebug (~> 0.1) + curses (~> 1.4) + reline (~> 0.3.8) speedtest_net (~> 0.9.2) tty-prompt (~> 0.23.1) diff --git a/bin/runtime_log.log b/bin/runtime_log.log index 1367f13..0a6e7da 100644 --- a/bin/runtime_log.log +++ b/bin/runtime_log.log @@ -2,6 +2,9 @@ Starting system info gathering... \n Gathering CPU info \n Gathering RAM info \n Gathering Disk info \n +{:model=>"SAMSUNG MZ7PD256", :type=>"SSD", :transport=>"ata"} +{:model=>"ST2000DM006-2DM1", :type=>"HDD", :transport=>"ata"} +{:model=>"Timetec 30TT253X", :type=>"SSD", :transport=>"ata"} Gathering GPU info \n Gathering Kernel info \n Gathering Network info \n diff --git a/bin/system_info.log b/bin/system_info.log deleted file mode 100644 index 8893454..0000000 --- a/bin/system_info.log +++ /dev/null @@ -1,36 +0,0 @@ -{ - "cpu": { - "model": "Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz", - "speed": "3.36 GHz", - "cores": 4, - "threads": 4 - }, - "ram": { - "total_ram": 23, - "used_ram": 4, - "ram_type": "None", - "ram_speed": "2133 MT/s" - }, - "disks": [ - { - "model": "SAMSUNG MZ7PD256", - "type": "SSD", - "transport": "ata" - }, - { - "model": "ST2000DM006-2DM1", - "type": "HDD", - "transport": "ata" - }, - { - "model": "Timetec 30TT253X", - "type": "SSD", - "transport": "ata" - } - ], - "gpu": { - "model": "NVIDIA Corporation GP106 [GeForce GTX 1060 6GB] (rev a1)", - "opengl_driver_version": "4.6.0 NVIDIA 535.104.05", - "vulkan_driver_version": "1.3.264" - } -} diff --git a/lib/cpu_info.rb b/lib/cpu_info.rb index 8109c57..84681e9 100755 --- a/lib/cpu_info.rb +++ b/lib/cpu_info.rb @@ -5,6 +5,7 @@ class CpuInfo cpu_info = {} cores = 0 threads = 0 + manufacturer = nil File.open('/proc/cpuinfo', 'r') do |f| f.each_line do |line| @@ -18,19 +19,42 @@ class CpuInfo cores += 1 if key == 'core id' threads += 1 if key == 'processor' - # Filter out the specific information + # Determine the manufacturer + if key == 'vendor_id' + manufacturer = if value.include?('Intel') + 'Intel' + elsif value.include?('AMD') + 'AMD' + else + 'Unknown' + end + end + + # Filter out the specific information based on manufacturer case key when 'model name' - cpu_info[:model] = value + case manufacturer + when 'Intel' + cleaned_model = value.match(/Intel\(R\) Core\(TM\) (\w+-?\d+)/) + cpu_info[:model] = "Intel Core #{cleaned_model[1]}" if cleaned_model + when 'AMD' + cleaned_model = value.match(/AMD Ryzen (\d+ \w+)/) + cpu_info[:model] = "AMD Ryzen #{cleaned_model[1]}" if cleaned_model + end when 'cpu MHz' speed_ghz = (value.to_f * 1e-3).round(2) cpu_info[:speed] = "#{speed_ghz} GHz" - when 'cache size' - cpu_info[:l3_cache] = value if value.include?('L3') end end end + # Fetch L3 Cache size using lscpu + lscpu_output = `lscpu | grep "L3 cache"`.strip + if lscpu_output && !lscpu_output.empty? + l3_cache_match = lscpu_output.match(/L3 cache:\s+(.+)/) + cpu_info[:l3_cache] = l3_cache_match[1] if l3_cache_match + end + cpu_info[:cores] = cores cpu_info[:threads] = threads cpu_info diff --git a/lib/disk_info.rb b/lib/disk_info.rb index 83e9e18..8066fc5 100755 --- a/lib/disk_info.rb +++ b/lib/disk_info.rb @@ -31,6 +31,7 @@ class DiskInfo transport: transport || 'Unknown' } disk_info << disk + File.open('runtime_log.log', 'a') { |f| f.puts("#{disk}") } end disk_info diff --git a/lib/display_handler.rb b/lib/display_handler.rb index bf317b3..927eb1c 100755 --- a/lib/display_handler.rb +++ b/lib/display_handler.rb @@ -1,64 +1,247 @@ # frozen_string_literal: true -require 'tty-prompt' -require 'json' +require 'curses' +require 'readline' class DisplayHandler def self.display(system_info) - # Log the entire system_info to a log file - File.open('system_info.log', 'w') do |f| - f.puts(JSON.pretty_generate(system_info)) - end + setup_readline + Curses.init_screen + Curses.start_color + Curses.init_pair(1, Curses::COLOR_BLUE, Curses::COLOR_BLACK) + Curses.init_pair(2, Curses::COLOR_GREEN, Curses::COLOR_BLACK) + Curses.init_pair(3, Curses::COLOR_YELLOW, Curses::COLOR_BLACK) - prompt = TTY::Prompt.new + @main_win = Curses::Window.new(0, 0, 0, 0) + @menu_win = @main_win.subwin(Curses.lines - 4, 20, 1, 1) + @info_win = @main_win.subwin(Curses.lines - 4, Curses.cols - 22, 1, 22) + @menu_win.keypad(true) - choices = [ - { name: 'OS Info', value: :os }, - { name: 'Hardware Info', value: :hardware }, - { name: 'Network Info', value: :network }, - { name: 'Exit', value: :exit } - ] + exit_menu = false # Flag to indicate if the menu should exit loop do - user_choice = prompt.select('Choose an option:', choices) + draw_main_box + draw_menu_box + draw_info_box - case user_choice - when :os - display_os_info(system_info.slice(:kernel, :os, :uptime)) - when :hardware - display_hardware_info(system_info.slice(:cpu, :ram, :disk, :gpu)) - when :network - display_network_info(system_info.slice(:network, :nic)) - when :exit - break + choices = ['OS Info', 'Hardware Info', 'Network Info', 'Exit'] + selected_index = 0 + + loop do + row = 2 # Starting row + col = 3 # Starting column + choices.each_with_index do |choice, index| + @menu_win.setpos(row + index, col) # Explicitly set position for each choice + if index == selected_index + @menu_win.attron(Curses.color_pair(3)) { @menu_win.addstr(choice) } + else + @menu_win.addstr(choice) + end + @menu_win.setpos(row + index + 1, col) # Move to the next line + end + @menu_win.refresh + + input = @menu_win.getch + case input + when Curses::KEY_UP + selected_index = [selected_index - 1, 0].max + when Curses::KEY_DOWN + selected_index = [selected_index + 1, choices.length - 1].min + when 10, 13 # Enter key + case choices[selected_index] + when 'Exit' + exit_menu = true # Set flag to true + break # Break inner loop + when 'OS Info' + display_os_info(system_info.slice(:kernel, :os, :uptime)) + when 'Hardware Info' + display_hardware_info(system_info.slice(:cpu, :ram, :disks, :gpu)) + when 'Network Info' + display_network_info(system_info.slice(:network, :nic)) + end + end + + break if exit_menu # Break outer loop if flag is true end + + break if exit_menu # Break outer loop if flag is true end + + Curses.close_screen + end + + def self.draw_main_box + @main_win.box('|', '-') + @main_win.setpos(0, (Curses.cols - 11) / 2) + @main_win.addstr('System Info') + @main_win.setpos(Curses.lines - 1, Curses.cols - 4) + @main_win.addstr('V1.0') + @main_win.refresh + end + + def self.draw_menu_box + @menu_win.box('|', '-') + @menu_win.setpos(0, 5) + @menu_win.addstr('Menu') + @menu_win.refresh + end + + def self.draw_info_box + @info_win.box('|', '-') + @info_win.setpos(0, @info_win.maxx - 13) + @info_win.addstr('Information') + @info_win.refresh + end + + def self.prepare_info_window(title) + @info_win.clear + draw_info_box + @info_win.setpos(2, 2) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr(title) } + @info_win.setpos(3, 2) + @info_win.addstr('-' * (title.length + 2)) end def self.display_os_info(os_info) - # Display OS information - puts "\nOS Information:" - puts '---------------' - os_info.each do |key, value| - puts "#{key.capitalize}: #{value}" - end + @info_win.clear + draw_info_box + @info_win.setpos(2, 2) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('OS Information') } + @info_win.setpos(3, 2) + @info_win.addstr('--------------') + display_info(os_info, @info_win) + @info_win.refresh end def self.display_hardware_info(hardware_info) - # Display Hardware information (CPU, RAM, etc.) - puts "\nHardware Information:" - puts '---------------------' - hardware_info.each do |key, value| - puts "#{key.capitalize}: #{value}" - end + @info_win.clear + draw_info_box + + # CPU and RAM on the left side + left_row = 4 + left_row = display_cpu_info(hardware_info[:cpu], left_row) + display_ram_info(hardware_info[:ram], left_row) + + # Disks and GPU on the right side + right_row = 4 + right_col = 40 # Adjust this value to place it at the desired column + right_row = display_disk_info(hardware_info[:disks], right_row, right_col) + display_gpu_info(hardware_info[:gpu], right_row, right_col) + + @info_win.refresh + end + + def self.display_cpu_info(cpu_info, start_row) + @info_win.setpos(start_row, 2) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('CPU Information') } + start_row += 1 + @info_win.setpos(start_row, 2) + @info_win.addstr('--------------') + start_row += 1 + @info_win.setpos(start_row, 2) + display_info(cpu_info, @info_win, start_row, 4) + start_row += cpu_info.keys.length + 1 + start_row + end + + def self.display_ram_info(ram_info, start_row, col = 2) + @info_win.setpos(start_row, col) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('RAM Information') } + start_row += 1 + @info_win.setpos(start_row, col) + @info_win.addstr('--------------') + start_row += 1 + display_info(ram_info, @info_win, start_row, 4, col) + start_row += ram_info.keys.length + 1 + start_row + end + + def self.display_disk_info(disk_info, start_row, col = 2) + @info_win.setpos(start_row, col) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('Disk Information')} + start_row += 1 + @info_win.setpos(start_row, col) + @info_win.addstr('---------------') + start_row += 1 + display_info({ disks: disk_info }, @info_win, start_row, 0, col) + start_row += (disk_info.length * 3) + 1 + start_row + end + + def self.display_gpu_info(gpu_info, start_row, col = 2) + @info_win.setpos(start_row, col) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('GPU Information')} + start_row += 1 + @info_win.setpos(start_row, col) + @info_win.addstr('--------------') + start_row += 1 + display_info(gpu_info, @info_win, start_row, 4, col) + start_row += gpu_info.keys.length + 1 + start_row end def self.display_network_info(network_info) - # Display Network information - puts "\nNetwork Information:" - puts '--------------------' - network_info.each do |key, value| - puts "#{key.capitalize}: #{value}" + @info_win.clear + draw_info_box + @info_win.setpos(2, 2) + @info_win.attron(Curses.color_pair(1)) { @info_win.addstr('Network Information') } + @info_win.setpos(3, 2) + @info_win.addstr('-------------------') + display_info(network_info, @info_win) + @info_win.refresh + end + + def self.display_info(info_hash, win, row = 4, indent = 0, col = 2) + info_hash.each do |key, value| + row = handle_value_type(value, win, row, indent, col, key) + end + row + end + + def self.handle_value_type(value, win, row, indent, col, key) + formatted_key = key.to_s.gsub('_', ' ').split.map(&:capitalize).join(' ') + if value.is_a?(Hash) + display_hash_value(value, win, row, indent, col, formatted_key) + elsif value.is_a?(Array) + display_array_value(value, win, row, indent, col) + else + display_scalar_value(value, win, row, indent, col, formatted_key) end end + + def self.display_hash_value(value, win, row, indent, col, key) + win.setpos(row, col + indent) + win.addstr("#{key}: ") + display_info(value, win, row, indent + 4, col) + end + + def self.display_array_value(value, win, row, indent, col) + value.each do |item| + if item.is_a?(Hash) + row = display_info(item, win, row, indent + 4, col) + else + win.setpos(row, col + indent + 4) + win.addstr(item.to_s) + row += 1 + end + end + row + end + + def self.display_scalar_value(value, win, row, indent, col, key) + win.setpos(row, col + indent) + win.addstr("#{key}: #{value}") + row + 1 + end + + def self.setup_readline + # Set up Readline for proper terminal settings + Readline.emacs_editing_mode + # the above line sets Readline to emacs_editing_mode so that terminals behave like they're supposed to + Readline.completion_append_character = ' ' + # we remove Readlines thing where it adds a space to the end of tab completes because it breaks the Curses cursor + Readline.completion_proc = proc { |_s| [] } + # here we basically are disabling tab completion all together. That's because for some reason it breaks the cursor + end + private_class_method :display_info, :setup_readline end diff --git a/lib/gpu_info.rb b/lib/gpu_info.rb index 2094284..aa3c78b 100755 --- a/lib/gpu_info.rb +++ b/lib/gpu_info.rb @@ -7,10 +7,37 @@ class GpuInfo # Use `lspci` to get basic GPU information lspci_output = `lspci | grep VGA`.strip + if lspci_output && !lspci_output.empty? - # Extract the model and other details - model_match = lspci_output.match(/VGA compatible controller: (.+)$/) - gpu_info[:model] = model_match[1] if model_match + # Determine the manufacturer + manufacturer = case lspci_output + when /NVIDIA/ + 'NVIDIA' + when /Advanced Micro Devices, Inc./ + 'AMD' + when /Intel Corporation/ + 'Intel' + else + 'Unknown' + end + + # Extract the model and other details based on manufacturer + case manufacturer + when 'NVIDIA' + model_match = lspci_output.match(/NVIDIA Corporation (\w+) \[.*GTX (\d+ \d+GB)\]/) || + lspci_output.match(/NVIDIA Corporation (\w+) \[.*RTX (\d+ \d+GB)\]/) + gpu_info[:model] = "NVIDIA GTX #{model_match[2]}" if model_match + when 'AMD' + model_match = lspci_output.match(/Advanced Micro Devices, Inc. \[.*Radeon (\w+ \d+)\]/) || + lspci_output.match(/Advanced Micro Devices, Inc. \[.*RX (\d+)\]/) + gpu_info[:model] = "AMD Radeon #{model_match[1]}" if model_match + when 'Intel' + model_match = lspci_output.match(/Intel Corporation.*HD Graphics (\d+)/) || + lspci_output.match(/Intel Corporation.*Iris Xe Graphics/) + gpu_info[:model] = "Intel #{model_match[1]}" if model_match + else + gpu_info[:model] = lspci_output # Fallback to the original string if no match + end end # Fetch Video RAM amount