diff --git a/.vscode/settings.json b/.vscode/settings.json index 511a1c7..7b57b38 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,7 +22,7 @@ "editor.formatOnSave": true, "editor.inlayHints.enabled": "off" }, - "cSpell.words": ["consts", "nvapi", "tauri"], + "cSpell.words": ["consts", "directx", "nvapi", "tauri"], "tailwindCSS.experimental.classRegex": [ "tv\\(([^)(]*(?:\\([^)(]*(?:\\([^)(]*(?:\\([^)(]*\\)[^)(]*)*\\)[^)(]*)*\\)[^)(]*)*)\\)" ] diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6b0bc90..0823785 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -287,7 +287,7 @@ dependencies = [ "arrayvec", "log", "nom", - "num-rational", + "num-rational 0.4.2", "v_frame", ] @@ -403,6 +403,12 @@ dependencies = [ "piper", ] +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + [[package]] name = "borsh" version = "1.3.0" @@ -1027,6 +1033,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dxgi" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1639bbfd6765e92a40267d217a7acbac5b49320b68013f39a8e4376aa8c1e091" +dependencies = [ + "boolinator", + "num", + "winapi", + "wio", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -1234,6 +1252,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -1672,8 +1696,10 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "chrono", + "dxgi", "image", "nvapi", + "regex", "rust_decimal", "serde", "serde_json", @@ -1687,6 +1713,7 @@ dependencies = [ "tracing", "tracing-subscriber", "uuid", + "winapi", "windows 0.58.0", "wmi", ] @@ -2457,6 +2484,32 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" +dependencies = [ + "num-bigint 0.1.44", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.1.42", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.4.6", + "rustc-serialize", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2467,6 +2520,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" +dependencies = [ + "num-traits", + "rustc-serialize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2493,13 +2556,36 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" +dependencies = [ + "num-bigint 0.1.44", + "num-integer", + "num-traits", + "rustc-serialize", +] + [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", ] @@ -3261,6 +3347,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -3306,6 +3405,21 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -3417,6 +3531,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -3439,9 +3562,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3607,6 +3730,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-serialize" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" + [[package]] name = "rustc_version" version = "0.4.1" @@ -5693,6 +5822,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wmi" version = "0.14.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 17913af..a47791c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -42,6 +42,9 @@ tauri-plugin-dialog = "2.0.3" tauri-plugin-store = "2.1.0" base64 = "0.22" image = "0.25.4" +dxgi = "0.1.7" +winapi = { version = "0.3", features = ["dxgi"] } +regex = "1.11.1" [dependencies.uuid] version = "1.11.0" @@ -65,6 +68,9 @@ features = [ "Win32_Security", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging", + #"Win32_Graphics_Direct3D", + #"Win32_Graphics_Direct3D11", + #"Win32_Graphics_Dxgi" ] [lib] diff --git a/src-tauri/src/commands/hardware.rs b/src-tauri/src/commands/hardware.rs index be4b2fa..e0f8ac4 100644 --- a/src-tauri/src/commands/hardware.rs +++ b/src-tauri/src/commands/hardware.rs @@ -1,5 +1,8 @@ -use crate::services::graphic_service; +use crate::services::directx_gpu_service; +use crate::services::nvidia_gpu_service; use crate::services::system_info_service; +use crate::services::wmi_service; +use crate::structs::hardware::{GraphicInfo, MemoryInfo}; use crate::{log_debug, log_error, log_info, log_internal, log_warn}; use serde::{Serialize, Serializer}; use std::collections::HashMap; @@ -121,8 +124,8 @@ pub fn get_cpu_usage(state: tauri::State<'_, AppState>) -> i32 { #[serde(rename_all = "camelCase")] pub struct SysInfo { pub cpu: Option, - pub memory: Option, - pub gpus: Option>, + pub memory: Option, + pub gpus: Option>, } /// @@ -133,13 +136,39 @@ pub async fn get_hardware_info( state: tauri::State<'_, AppState>, ) -> Result { let cpu_result = system_info_service::get_cpu_info(state.system.lock().unwrap()); - let memory_result = system_info_service::get_memory_info(); - let gpus_result = graphic_service::get_nvidia_gpu_info().await; + let memory_result = wmi_service::get_memory_info(); + let nvidia_gpus_result = nvidia_gpu_service::get_nvidia_gpu_info().await; + let amd_gpus_result = directx_gpu_service::get_amd_gpu_info().await; + let intel_gpus_result = directx_gpu_service::get_intel_gpu_info().await; + + let mut gpus_result = Vec::new(); + + // NVIDIA の結果を確認して結合 + match nvidia_gpus_result { + Ok(nvidia_gpus) => gpus_result.extend(nvidia_gpus), + Err(e) => log_error!("nvidia_error", "get_all_gpu_info", Some(e)), + } + + // AMD の結果を確認して結合 + match amd_gpus_result { + Ok(amd_gpus) => gpus_result.extend(amd_gpus), + Err(e) => log_error!("amd_error", "get_all_gpu_info", Some(e)), + } + + // Intel の結果を確認して結合 + match intel_gpus_result { + Ok(intel_gpus) => gpus_result.extend(intel_gpus), + Err(e) => log_error!("intel_error", "get_all_gpu_info", Some(e)), + } let sys_info = SysInfo { cpu: cpu_result.ok(), memory: memory_result.ok(), - gpus: gpus_result.ok(), + gpus: if gpus_result.is_empty() { + None + } else { + Some(gpus_result) + }, }; // すべての情報が失敗した場合にのみエラーメッセージを返す @@ -173,9 +202,17 @@ pub fn get_memory_usage(state: tauri::State<'_, AppState>) -> i32 { /// #[command] pub async fn get_gpu_usage() -> Result { - match graphic_service::get_nvidia_gpu_usage().await { + if let Ok(usage) = nvidia_gpu_service::get_nvidia_gpu_usage().await { + return Ok((usage * 100.0).round() as i32); + } + + // NVIDIA APIが失敗した場合、WMIから取得を試みる + match wmi_service::get_gpu_usage_by_device_and_engine("3D").await { Ok(usage) => Ok((usage * 100.0).round() as i32), - Err(e) => Err(format!("Failed to get GPU usage: {:?}", e)), + Err(e) => Err(format!( + "Failed to get GPU usage from both NVIDIA API and WMI: {:?}", + e + )), } } @@ -183,8 +220,8 @@ pub async fn get_gpu_usage() -> Result { /// ## GPU温度を取得 /// #[command] -pub async fn get_gpu_temperature() -> Result, String> { - match graphic_service::get_nvidia_gpu_temperature().await { +pub async fn get_gpu_temperature() -> Result, String> { + match nvidia_gpu_service::get_nvidia_gpu_temperature().await { Ok(temps) => Ok(temps), Err(e) => Err(format!("Failed to get GPU temperature: {:?}", e)), } @@ -194,8 +231,9 @@ pub async fn get_gpu_temperature() -> Result, St /// ## GPUのファン回転数を取得 /// #[command] -pub async fn get_nvidia_gpu_cooler() -> Result, String> { - match graphic_service::get_nvidia_gpu_cooler_stat().await { +pub async fn get_nvidia_gpu_cooler() -> Result, String> +{ + match nvidia_gpu_service::get_nvidia_gpu_cooler_stat().await { Ok(temps) => Ok(temps), Err(e) => Err(format!("Failed to get GPU cooler status: {:?}", e)), } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e182f8d..07030ae 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,6 +5,7 @@ mod commands; mod enums; mod services; +mod structs; mod utils; use commands::background_image; diff --git a/src-tauri/src/services/directx_gpu_service.rs b/src-tauri/src/services/directx_gpu_service.rs new file mode 100644 index 0000000..b3121ac --- /dev/null +++ b/src-tauri/src/services/directx_gpu_service.rs @@ -0,0 +1,92 @@ +use crate::structs::hardware::GraphicInfo; + +use crate::{log_debug, log_error, log_info, log_internal, log_warn}; +use dxgi::adapter::AdapterDesc; +use dxgi::Factory; +use tokio::task::spawn_blocking; + +/// Intel GPU情報を取得する +pub async fn get_intel_gpu_info() -> Result, String> { + let handle = spawn_blocking(|| { + log_debug!("start", "get_intel_gpu_info", None::<&str>); + + // DXGIファクトリのインスタンスを作成 + let factory = Factory::new().expect("Failed to create DXGI Factory"); + let mut gpu_info_list = Vec::new(); + + // アダプタの列挙 + for adapter in factory.adapters() { + // get_desc() の結果をアンラップしてエラーハンドリング + let desc: AdapterDesc = adapter.get_desc(); + + // Intel GPU の場合のみ追加 + let gpu_name = desc.description(); + if gpu_name.contains("Intel") { + let memory_size_dedicated = desc.dedicated_video_memory() / 1024 / 1024; + let memory_size_shared = desc.shared_system_memory() / 1024 / 1024; + + let gpu_info = GraphicInfo { + id: desc.device_id().to_string(), + name: gpu_name.trim_end_matches('\0').to_string(), + vendor_name: "Intel".to_string(), + clock: 0, // Intelのクロック周波数取得が難しいため0を設定 + memory_size: format!("{} MB", memory_size_shared), + memory_size_dedicated: format!("{} MB", memory_size_dedicated), + }; + + gpu_info_list.push(gpu_info); + } + } + + log_debug!("end", "get_intel_gpu_info", None::<&str>); + Ok(gpu_info_list) + }); + + handle.await.map_err(|e| { + log_error!("join_error", "get_intel_gpu_info", Some(e.to_string())); + "Intel GPU info retrieval failed".to_string() + })? +} + +/// AMD GPU情報を取得する +pub async fn get_amd_gpu_info() -> Result, String> { + let handle = spawn_blocking(|| { + log_debug!("start", "get_amd_gpu_info", None::<&str>); + + // DXGIファクトリのインスタンスを作成 + let factory = Factory::new().expect("Failed to create DXGI Factory"); + let mut gpu_info_list = Vec::new(); + + // アダプタの列挙 + for adapter in factory.adapters() { + // get_desc() の結果をアンラップしてエラーハンドリング + let desc: AdapterDesc = adapter.get_desc(); + + // GPU 名の取得 + let gpu_name = desc.description(); + if gpu_name.contains("AMD") || gpu_name.contains("Radeon") { + let memory_size_dedicated = desc.dedicated_video_memory() / 1024 / 1024; + let memory_size_shared = desc.shared_system_memory() / 1024 / 1024; + + let gpu_info = GraphicInfo { + id: desc.device_id().to_string(), + name: gpu_name.trim_end_matches('\0').to_string(), + vendor_name: "AMD".to_string(), + clock: 0, // クロック取得が難しいため 0 に設定 + memory_size: format!("{} MB", memory_size_shared), + memory_size_dedicated: format!("{} MB", memory_size_dedicated), + }; + + gpu_info_list.push(gpu_info); + } + } + + log_debug!("end", "get_amd_gpu_info", None::<&str>); + Ok(gpu_info_list) + }); + + handle.await.map_err(|e| { + log_error!("join_error", "get_amd_gpu_info", Some(e.to_string())); + "AMD GPU info retrieval failed".to_string() + })? +} diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs index 247d83c..b2a5523 100644 --- a/src-tauri/src/services/mod.rs +++ b/src-tauri/src/services/mod.rs @@ -1,2 +1,4 @@ -pub mod graphic_service; +pub mod directx_gpu_service; +pub mod nvidia_gpu_service; pub mod system_info_service; +pub mod wmi_service; diff --git a/src-tauri/src/services/graphic_service.rs b/src-tauri/src/services/nvidia_gpu_service.rs similarity index 96% rename from src-tauri/src/services/graphic_service.rs rename to src-tauri/src/services/nvidia_gpu_service.rs index 4a18b1d..4a95812 100644 --- a/src-tauri/src/services/graphic_service.rs +++ b/src-tauri/src/services/nvidia_gpu_service.rs @@ -1,8 +1,8 @@ +use crate::structs::hardware::GraphicInfo; use crate::utils::{self, formatter}; use crate::{log_debug, log_error, log_info, log_internal, log_warn}; use nvapi; use nvapi::UtilizationDomain; -use serde::Serialize; use tokio::task::spawn_blocking; use tokio::task::JoinError; @@ -183,21 +183,9 @@ pub async fn get_nvidia_gpu_cooler_stat() -> Result, nvapi::Statu })? } -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GraphicInfo { - name: String, - vendor_name: String, - clock: u64, - memory_size: String, - memory_size_dedicated: String, -} - /// /// GPU情報を取得する /// -/// - [TODO] AMD GPU の情報も取得する -/// pub async fn get_nvidia_gpu_info() -> Result, String> { let handle = spawn_blocking(|| { log_debug!("start", "get_nvidia_gpu_info", None::<&str>); @@ -259,7 +247,16 @@ pub async fn get_nvidia_gpu_info() -> Result, String> { } }; + let gpu_id = match gpu.gpu_id() { + Ok(id) => id.to_string(), + Err(e) => { + log_error!("gpu_id_failed", "get_nvidia_gpu_info", Some(e.to_string())); + continue; + } + }; + let gpu_info = GraphicInfo { + id: gpu_id, name, vendor_name: "NVIDIA".to_string(), clock: frequency, diff --git a/src-tauri/src/services/system_info_service.rs b/src-tauri/src/services/system_info_service.rs index 3a73517..43e6ce1 100644 --- a/src-tauri/src/services/system_info_service.rs +++ b/src-tauri/src/services/system_info_service.rs @@ -1,13 +1,8 @@ -use crate::utils::{self, formatter}; -use crate::{log_debug, log_error, log_info, log_internal}; +use crate::utils; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use std::sync::mpsc::{channel, Receiver, Sender}; +use serde::Serialize; use std::sync::MutexGuard; -use std::thread; use sysinfo::System; -use wmi::{COMLibrary, WMIConnection}; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -42,177 +37,3 @@ pub fn get_cpu_info(system: MutexGuard<'_, System>) -> Result { Ok(cpu_info) } - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct MemoryInfo { - size: String, - clock: u64, - clock_unit: String, - memory_count: usize, - total_slots: usize, - memory_type: String, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "PascalCase")] -struct Win32PhysicalMemory { - capacity: u64, - speed: u32, - memory_type: Option, - smbios_memory_type: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "PascalCase")] -struct Win32PhysicalMemoryArray { - memory_devices: Option, -} - -/// -/// ## メモリ情報を取得 -/// -pub fn get_memory_info() -> Result { - let physical_memory: Vec = get_memory_info_in_thread( - "SELECT Capacity, Speed, MemoryType, SMBIOSMemoryType FROM Win32_PhysicalMemory" - .to_string(), - )?; - - let physical_memory_array: Vec = get_memory_info_in_thread( - "SELECT MemoryDevices FROM Win32_PhysicalMemoryArray".to_string(), - )?; - - log_info!( - &format!("mem info: {:?}", physical_memory), - "get_memory_info", - None::<&str> - ); - - let memory_info = MemoryInfo { - size: formatter::format_size(physical_memory.iter().map(|mem| mem.capacity).sum(), 1), - clock: physical_memory[0].speed as u64, - clock_unit: "MHz".to_string(), - memory_count: physical_memory.len(), - total_slots: physical_memory_array[0].memory_devices.unwrap_or(0) as usize, - memory_type: get_memory_type_with_fallback( - physical_memory[0].memory_type, - physical_memory[0].smbios_memory_type, - ), - }; - - Ok(memory_info) -} - -/// -/// ## MemoryTypeの値に対応するメモリの種類を文字列で返す -/// -/// - [TODO] DDR5に対応する -/// -fn get_memory_type_description(memory_type: Option) -> String { - log_info!( - &format!("mem type: {:?}", memory_type), - "get_memory_type_description", - None::<&str> - ); - - match memory_type { - Some(0) => "Unknown or Unsupported".to_string(), - Some(1) => "Other".to_string(), - Some(2) => "DRAM".to_string(), - Some(3) => "Synchronous DRAM".to_string(), - Some(4) => "Cache DRAM".to_string(), - Some(5) => "EDO".to_string(), - Some(6) => "EDRAM".to_string(), - Some(7) => "VRAM".to_string(), - Some(8) => "SRAM".to_string(), - Some(9) => "RAM".to_string(), - Some(10) => "ROM".to_string(), - Some(11) => "Flash".to_string(), - Some(12) => "EEPROM".to_string(), - Some(13) => "FEPROM".to_string(), - Some(14) => "EPROM".to_string(), - Some(15) => "CDRAM".to_string(), - Some(16) => "3DRAM".to_string(), - Some(17) => "SDRAM".to_string(), - Some(18) => "SGRAM".to_string(), - Some(19) => "RDRAM".to_string(), - Some(20) => "DDR".to_string(), - Some(21) => "DDR2".to_string(), - Some(22) => "DDR2 FB-DIMM".to_string(), - Some(24) => "DDR3".to_string(), - Some(25) => "FBD2".to_string(), - Some(26) => "DDR4".to_string(), - Some(mt) => format!("Other or Unknown Memory Type ({})", mt), - None => "Unknown".to_string(), - } -} - -/// -/// ## MemoryType もしくは SMBIOSMemoryType からメモリの種類を取得 -/// -fn get_memory_type_with_fallback( - memory_type: Option, - smbios_memory_type: Option, -) -> String { - match memory_type { - Some(0) => match smbios_memory_type { - Some(20) => "DDR".to_string(), - Some(21) => "DDR2".to_string(), - Some(24) => "DDR3".to_string(), - Some(26) => "DDR4".to_string(), - Some(34) => "DDR5".to_string(), - Some(mt) => format!("Other SMBIOS Memory Type ({})", mt), - None => "Unknown".to_string(), - }, - Some(mt) => get_memory_type_description(Some(mt)), - None => "Unknown".to_string(), - } -} - -/// -/// ## メモリ情報を別スレッドで取得する(WMIを使用) -/// -fn get_memory_info_in_thread(query: String) -> Result, String> -where - T: DeserializeOwned + std::fmt::Debug + Send + 'static, -{ - let (tx, rx): ( - Sender, String>>, - Receiver, String>>, - ) = channel(); - - // 別スレッドを起動してWMIクエリを実行 - thread::spawn(move || { - let result = (|| { - let com_con = COMLibrary::new() - .map_err(|e| format!("Failed to initialize COM Library: {:?}", e))?; - let wmi_con = WMIConnection::new(com_con) - .map_err(|e| format!("Failed to create WMI connection: {:?}", e))?; - - // WMIクエリを実行してメモリ情報を取得 - let results: Vec = wmi_con - .raw_query(query.clone()) - .map_err(|e| format!("Failed to execute query: {:?}", e))?; - - log_info!( - &format!("mem info: {:?}", results), - "get_memory_info_in_thread", - Some(&format!("query: {}", query)) - ); - - Ok(results) - })(); - - // メインスレッドに結果を送信 - if let Err(err) = tx.send(result) { - log_error!( - "Failed to send data from thread", - "get_wmi_data_in_thread", - Some(err.to_string()) - ); - } - }); - - // メインスレッドで結果を受信 - rx.recv().map_err(|_| "Failed to receive data from thread".to_string())? -} diff --git a/src-tauri/src/services/wmi_service.rs b/src-tauri/src/services/wmi_service.rs new file mode 100644 index 0000000..0525df6 --- /dev/null +++ b/src-tauri/src/services/wmi_service.rs @@ -0,0 +1,217 @@ +use crate::structs::hardware::MemoryInfo; +use crate::utils::formatter; +use crate::{log_debug, log_error, log_info, log_internal}; + +use regex::Regex; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::thread; +use wmi::{COMLibrary, WMIConnection}; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct Win32PhysicalMemory { + capacity: u64, + speed: u32, + memory_type: Option, + smbios_memory_type: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct Win32PhysicalMemoryArray { + memory_devices: Option, +} + +/// +/// ## メモリ情報を取得 +/// +pub fn get_memory_info() -> Result { + let physical_memory: Vec = wmi_query_in_thread( + "SELECT Capacity, Speed, MemoryType, SMBIOSMemoryType FROM Win32_PhysicalMemory" + .to_string(), + )?; + + let physical_memory_array: Vec = wmi_query_in_thread( + "SELECT MemoryDevices FROM Win32_PhysicalMemoryArray".to_string(), + )?; + + log_info!( + &format!("mem info: {:?}", physical_memory), + "get_memory_info", + None::<&str> + ); + + let memory_info = MemoryInfo { + size: formatter::format_size(physical_memory.iter().map(|mem| mem.capacity).sum(), 1), + clock: physical_memory[0].speed as u64, + clock_unit: "MHz".to_string(), + memory_count: physical_memory.len(), + total_slots: physical_memory_array[0].memory_devices.unwrap_or(0) as usize, + memory_type: get_memory_type_with_fallback( + physical_memory[0].memory_type, + physical_memory[0].smbios_memory_type, + ), + }; + + Ok(memory_info) +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct GpuEngineLoadInfo { + name: String, + utilization_percentage: Option, +} + +/// +/// 指定したGPUエンジンの使用率を取得する(WMIを使用) +/// +pub async fn get_gpu_usage_by_device_and_engine( + engine_type: &str, +) -> Result> { + // GPUエンジン情報を取得 + let results: Vec = wmi_query_in_thread( + "SELECT Name, UtilizationPercentage FROM Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine".to_string(), + )?; + + log_info!( + &format!("GPU engine usage data: {:?}", results), + "get_gpu_usage_by_device_and_engine", + None::<&str> + ); + + // 正規表現で `engtype_xxx` の部分を抽出 + let re = Regex::new(r"engtype_(\w+)").unwrap(); + + for engine in results.iter() { + if let Some(captures) = re.captures(&engine.name) { + if let Some(engine_name) = captures.get(1) { + if engine_name.as_str() == engine_type { + if let Some(load) = engine.utilization_percentage { + return Ok(load as f32 / 100.0); + } else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("No usage data available for engine type: {}", engine_type), + ))); + } + } + } + } + } + + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("No GPU engine found: engine_type: {}", engine_type,), + ))) +} + +/// +/// ## WMIから別スレッドで¥クエリ実行する(WMIを使用) +/// +fn wmi_query_in_thread(query: String) -> Result, String> +where + T: DeserializeOwned + std::fmt::Debug + Send + 'static, +{ + let (tx, rx): ( + Sender, String>>, + Receiver, String>>, + ) = channel(); + + // 別スレッドを起動してWMIクエリを実行 + thread::spawn(move || { + let result = (|| { + let com_con = COMLibrary::new() + .map_err(|e| format!("Failed to initialize COM Library: {:?}", e))?; + let wmi_con = WMIConnection::new(com_con) + .map_err(|e| format!("Failed to create WMI connection: {:?}", e))?; + + // WMIクエリを実行してメモリ情報を取得 + let results: Vec = wmi_con + .raw_query(query) + .map_err(|e| format!("Failed to execute query: {:?}", e))?; + + Ok(results) + })(); + + // メインスレッドに結果を送信 + if let Err(err) = tx.send(result) { + log_error!( + "Failed to send data from thread", + "get_wmi_data_in_thread", + Some(err.to_string()) + ); + } + }); + + // メインスレッドで結果を受信 + rx.recv() + .map_err(|_| "Failed to receive data from thread".to_string())? +} + +/// +/// ## MemoryTypeの値に対応するメモリの種類を文字列で返す +/// +fn get_memory_type_description(memory_type: Option) -> String { + log_info!( + &format!("mem type: {:?}", memory_type), + "get_memory_type_description", + None::<&str> + ); + + match memory_type { + Some(0) => "Unknown or Unsupported".to_string(), + Some(1) => "Other".to_string(), + Some(2) => "DRAM".to_string(), + Some(3) => "Synchronous DRAM".to_string(), + Some(4) => "Cache DRAM".to_string(), + Some(5) => "EDO".to_string(), + Some(6) => "EDRAM".to_string(), + Some(7) => "VRAM".to_string(), + Some(8) => "SRAM".to_string(), + Some(9) => "RAM".to_string(), + Some(10) => "ROM".to_string(), + Some(11) => "Flash".to_string(), + Some(12) => "EEPROM".to_string(), + Some(13) => "FEPROM".to_string(), + Some(14) => "EPROM".to_string(), + Some(15) => "CDRAM".to_string(), + Some(16) => "3DRAM".to_string(), + Some(17) => "SDRAM".to_string(), + Some(18) => "SGRAM".to_string(), + Some(19) => "RDRAM".to_string(), + Some(20) => "DDR".to_string(), + Some(21) => "DDR2".to_string(), + Some(22) => "DDR2 FB-DIMM".to_string(), + Some(24) => "DDR3".to_string(), + Some(25) => "FBD2".to_string(), + Some(26) => "DDR4".to_string(), + Some(mt) => format!("Other or Unknown Memory Type ({})", mt), + None => "Unknown".to_string(), + } +} + +/// +/// ## MemoryType もしくは SMBIOSMemoryType からメモリの種類を取得 +/// +fn get_memory_type_with_fallback( + memory_type: Option, + smbios_memory_type: Option, +) -> String { + match memory_type { + Some(0) => match smbios_memory_type { + Some(20) => "DDR".to_string(), + Some(21) => "DDR2".to_string(), + Some(24) => "DDR3".to_string(), + Some(26) => "DDR4".to_string(), + Some(34) => "DDR5".to_string(), + Some(mt) => format!("Other SMBIOS Memory Type ({})", mt), + None => "Unknown".to_string(), + }, + Some(mt) => get_memory_type_description(Some(mt)), + None => "Unknown".to_string(), + } +} diff --git a/src-tauri/src/structs/hardware.rs b/src-tauri/src/structs/hardware.rs new file mode 100644 index 0000000..c5992e5 --- /dev/null +++ b/src-tauri/src/structs/hardware.rs @@ -0,0 +1,23 @@ +use serde::Serialize; + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MemoryInfo { + pub size: String, + pub clock: u64, + pub clock_unit: String, + pub memory_count: usize, + pub total_slots: usize, + pub memory_type: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GraphicInfo { + pub id: String, + pub name: String, + pub vendor_name: String, + pub clock: u64, + pub memory_size: String, + pub memory_size_dedicated: String, +} diff --git a/src-tauri/src/structs/mod.rs b/src-tauri/src/structs/mod.rs new file mode 100644 index 0000000..9c06cff --- /dev/null +++ b/src-tauri/src/structs/mod.rs @@ -0,0 +1 @@ +pub mod hardware; diff --git a/src/template/Dashboard.tsx b/src/template/Dashboard.tsx index 7c709a1..453ab63 100644 --- a/src/template/Dashboard.tsx +++ b/src/template/Dashboard.tsx @@ -107,14 +107,18 @@ const GPUInfo = () => { )} - + {hardwareInfo.gpus.map((gpu, index) => ( +
+ +
+ ))} ) ); diff --git a/src/types/hardwareDataType.ts b/src/types/hardwareDataType.ts index 8aae711..a70225d 100644 --- a/src/types/hardwareDataType.ts +++ b/src/types/hardwareDataType.ts @@ -30,8 +30,9 @@ export type MemoryInfo = { }; export type GraphicInfo = { - clock: number; + id: string; name: string; + clock: number; vendorName: string; memorySize: string; memorySizeDedicated: string;