Skip to content

Commit

Permalink
feature: gpu fan speed
Browse files Browse the repository at this point in the history
  • Loading branch information
shm11C3 committed Sep 28, 2024
1 parent a45486e commit 598dc9e
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 51 deletions.
14 changes: 12 additions & 2 deletions src-tauri/src/commands/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,24 @@ pub async fn get_gpu_usage() -> Result<i32, String> {
/// ## GPU温度を取得
///
#[command]
pub async fn get_gpu_temperature() -> Result<Vec<graphic_service::GpuTemperature>, String>
{
pub async fn get_gpu_temperature() -> Result<Vec<graphic_service::NameValue>, String> {
match graphic_service::get_nvidia_gpu_temperature().await {
Ok(temps) => Ok(temps),
Err(e) => Err(format!("Failed to get GPU temperature: {:?}", e)),
}
}

///
/// ## GPUのファン回転数を取得
///
#[command]
pub async fn get_nvidia_gpu_cooler() -> Result<Vec<graphic_service::NameValue>, String> {
match graphic_service::get_nvidia_gpu_cooler_stat().await {
Ok(temps) => Ok(temps),
Err(e) => Err(format!("Failed to get GPU cooler status: {:?}", e)),
}
}

///
/// ## CPU使用率の履歴を取得
///
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ fn main() {
hardware::get_memory_usage,
hardware::get_gpu_usage,
hardware::get_gpu_temperature,
hardware::get_nvidia_gpu_cooler,
hardware::get_cpu_usage_history,
hardware::get_memory_usage_history,
hardware::get_gpu_usage_history,
Expand Down
61 changes: 58 additions & 3 deletions src-tauri/src/services/graphic_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ pub async fn get_nvidia_gpu_usage() -> Result<f32, nvapi::Status> {

#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuTemperature {
pub struct NameValue {
name: String,
value: f64, // 摂氏温度
}

///
/// ## GPU温度を取得する(NVAPI を使用)
///
pub async fn get_nvidia_gpu_temperature() -> Result<Vec<GpuTemperature>, nvapi::Status> {
pub async fn get_nvidia_gpu_temperature() -> Result<Vec<NameValue>, nvapi::Status> {
let handle = spawn_blocking(|| {
log_debug!("start", "get_nvidia_gpu_temperature", None::<&str>);

Expand Down Expand Up @@ -111,7 +111,7 @@ pub async fn get_nvidia_gpu_temperature() -> Result<Vec<GpuTemperature>, nvapi::
nvapi::Status::Error
})?;

temperatures.push(GpuTemperature {
temperatures.push(NameValue {
name: gpu.full_name().unwrap_or("Unknown".to_string()),
value: thermal_settings[0].current_temperature.0 as f64, // thermal_settings の0番目の温度を f64 に変換
});
Expand All @@ -130,6 +130,61 @@ pub async fn get_nvidia_gpu_temperature() -> Result<Vec<GpuTemperature>, nvapi::
})?
}

///
/// ## GPUのファン回転数を取得する(NVAPI を使用)
///
pub async fn get_nvidia_gpu_cooler_stat() -> Result<Vec<NameValue>, nvapi::Status> {
let handle = spawn_blocking(|| {
log_debug!("start", "get_nvidia_gpu_cooler_stat", None::<&str>);

let gpus = nvapi::PhysicalGpu::enumerate()?;

if gpus.is_empty() {
log_warn!(
"not found",
"get_nvidia_gpu_cooler_stat",
Some("gpu is not found")
);
tracing::warn!("gpu is not found");
return Err(nvapi::Status::Error); // GPUが見つからない場合はエラーを返す
}

let mut cooler_infos = Vec::new();

print!("{:?}", gpus);

for gpu in gpus.iter() {
// 温度情報を取得
let cooler_settings = gpu.cooler_settings(None).map_err(|e| {
log_warn!(
"cooler_settings_failed",
"get_nvidia_gpu_cooler_stat",
Some(&format!("{:?}", e))
);
nvapi::Status::Error
})?;

print!("{:?}", cooler_settings);

cooler_infos.push(NameValue {
name: gpu.full_name().unwrap_or("Unknown".to_string()),
value: cooler_settings[0].current_level.0 as f64,
});
}

Ok(cooler_infos)
});

handle.await.map_err(|e: JoinError| {
log_error!(
"join_error",
"get_nvidia_gpu_cooler_stat",
Some(e.to_string())
);
nvapi::Status::Error
})?
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GraphicInfo {
Expand Down
8 changes: 3 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { useEffect } from "react";
import Dashboard from "./template/Dashboard";
import ChartTemplate from "./template/Usage";
import "./index.css";
import {
useHardwareTempUpdater,
useUsageUpdater,
} from "@/hooks/useHardwareData";
import { useHardwareUpdater, useUsageUpdater } from "@/hooks/useHardwareData";
import {
useErrorModalListener,
useSettingsModalListener,
Expand All @@ -28,7 +25,8 @@ const Page = () => {
useUsageUpdater("cpu");
useUsageUpdater("memory");
useUsageUpdater("gpu");
useHardwareTempUpdater("gpu");
useHardwareUpdater("gpu", "temp");
useHardwareUpdater("gpu", "fan");

useEffect(() => {
if (settings?.theme) {
Expand Down
8 changes: 5 additions & 3 deletions src/atom/chart.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { Temperatures } from "@/types/hardwareDataType";
import type { NameValues } from "@/types/hardwareDataType";
import { atom } from "jotai";

export const cpuUsageHistoryAtom = atom<number[]>([]);
export const memoryUsageHistoryAtom = atom<number[]>([]);
export const graphicUsageHistoryAtom = atom<number[]>([]);
export const cpuTempAtom = atom<Temperatures>([]);
export const gpuTempAtom = atom<Temperatures>([]);
export const cpuTempAtom = atom<NameValues>([]);
export const cpuFanSpeedAtom = atom<NameValues>([]);
export const gpuTempAtom = atom<NameValues>([]);
export const gpuFanSpeedAtom = atom<NameValues>([]);
27 changes: 13 additions & 14 deletions src/atom/useHardwareInfoAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@ import { useEffect } from "react";

const hardInfoAtom = atom<HardwareInfo>({
isFetched: false,
gpus: [],
});

export const useHardwareInfoAtom = () => {
const [hardwareInfo, setHardInfo] = useAtom(hardInfoAtom);

useEffect(() => {
const init = async () => {
try {
const hardwareInfo = await getHardwareInfo();
setHardInfo(hardwareInfo);
if (!hardwareInfo.isFetched) {
const init = async () => {
try {
const fetchedHardwareInfo = await getHardwareInfo();

hardwareInfo.isFetched = true;
} catch (e) {
console.error(e);
hardwareInfo.isFetched = false;
}
};
setHardInfo({
...fetchedHardwareInfo,
isFetched: true,
});
} catch (e) {
console.error(e);
}
};

// データがなければ取得して更新
if (!hardwareInfo.isFetched) {
init();
}
}, [setHardInfo, hardwareInfo]);
}, [hardwareInfo.isFetched, setHardInfo]);

return { hardwareInfo };
};
54 changes: 38 additions & 16 deletions src/hooks/useHardwareData.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import {
cpuFanSpeedAtom,
cpuTempAtom,
cpuUsageHistoryAtom,
gpuFanSpeedAtom,
gpuTempAtom,
graphicUsageHistoryAtom,
memoryUsageHistoryAtom,
} from "@/atom/chart";
import { chartConfig } from "@/consts/chart";
import {
getCpuUsage,
getGpuFanSpeed,
getGpuTemperature,
getGpuUsage,
getMemoryUsage,
} from "@/services/hardwareService";
import type { ChartDataType, Temperatures } from "@/types/hardwareDataType";
import type { ChartDataType, NameValues } from "@/types/hardwareDataType";
import { type PrimitiveAtom, useSetAtom } from "jotai";
import { useEffect } from "react";

Expand Down Expand Up @@ -63,34 +66,53 @@ export const useUsageUpdater = (dataType: ChartDataType) => {
}, [setHistory, getUsage]);
};

export const useHardwareTempUpdater = (
dataType: Exclude<ChartDataType, "memory">,
export const useHardwareUpdater = (
hardType: Exclude<ChartDataType, "memory">,
dataType: "temp" | "fan",
) => {
type AtomActionMapping = {
atom: PrimitiveAtom<Temperatures>;
action: () => Promise<Temperatures>;
atom: PrimitiveAtom<NameValues>;
action: () => Promise<NameValues>;
};

const mapping: Record<Exclude<ChartDataType, "memory">, AtomActionMapping> = {
const mapping: Record<
Exclude<ChartDataType, "memory">,
Record<"temp" | "fan", AtomActionMapping>
> = {
cpu: {
atom: cpuTempAtom,
action: () => {
console.error("Not implemented");
return Promise.resolve([]);
temp: {
atom: cpuTempAtom,
action: () => {
console.error("Not implemented");
return Promise.resolve([]);
},
},
fan: {
atom: cpuFanSpeedAtom,
action: () => {
console.error("Not implemented");
return Promise.resolve([]);
},
},
},
gpu: {
atom: gpuTempAtom,
action: getGpuTemperature,
fan: {
atom: gpuFanSpeedAtom,
action: getGpuFanSpeed,
},
temp: {
atom: gpuTempAtom,
action: getGpuTemperature,
},
},
};

const setData = useSetAtom(mapping[dataType].atom);
const getTemp = mapping[dataType].action;
const setData = useSetAtom(mapping[hardType][dataType].atom);
const getData = mapping[hardType][dataType].action;

useEffect(() => {
const fetchData = async () => {
const temp = await getTemp();
const temp = await getData();
setData(temp);
};

Expand All @@ -101,5 +123,5 @@ export const useHardwareTempUpdater = (
}, 10000);

return () => clearInterval(intervalId);
}, [setData, getTemp]);
}, [setData, getData]);
};
12 changes: 9 additions & 3 deletions src/services/hardwareService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { HardwareInfo, Temperatures } from "@/types/hardwareDataType";
import type { HardwareInfo, NameValues } from "@/types/hardwareDataType";
import { invoke } from "@tauri-apps/api/tauri";

export const getCpuUsage = async (): Promise<number> => {
return await invoke("get_cpu_usage");
};

export const getHardwareInfo = async (): Promise<HardwareInfo> => {
export const getHardwareInfo = async (): Promise<
Exclude<HardwareInfo, "isFetched">
> => {
return await invoke("get_hardware_info");
};

Expand All @@ -29,6 +31,10 @@ export const getGpuUsageHistory = (seconds: number): Promise<number[]> => {
return invoke("get_gpu_usage_history", { seconds: seconds });
};

export const getGpuTemperature = async (): Promise<Temperatures> => {
export const getGpuTemperature = async (): Promise<NameValues> => {
return await invoke("get_gpu_temperature");
};

export const getGpuFanSpeed = async (): Promise<NameValues> => {
return await invoke("get_nvidia_gpu_cooler");
};
24 changes: 21 additions & 3 deletions src/template/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
cpuUsageHistoryAtom,
gpuFanSpeedAtom,
gpuTempAtom,
graphicUsageHistoryAtom,
memoryUsageHistoryAtom,
} from "@/atom/chart";
import { useHardwareInfoAtom } from "@/atom/useHardwareInfoAtom";
import DoughnutChart from "@/components/charts/DoughnutChart";
import type { NameValues } from "@/types/hardwareDataType";
import { useAtom } from "jotai";

const InfoTable = ({
Expand Down Expand Up @@ -71,11 +73,19 @@ const CPUInfo = () => {
const GPUInfo = () => {
const [graphicUsageHistory] = useAtom(graphicUsageHistoryAtom);
const [gpuTemp] = useAtom(gpuTempAtom);
const [gpuFan] = useAtom(gpuFanSpeedAtom);
const { hardwareInfo } = useHardwareInfoAtom();

const targetTemperature = gpuTemp.find(
(x) => x.name === hardwareInfo.gpus[0].name,
)?.value;
const getTargetInfo = (data: NameValues) => {
return data.find(
(x) => hardwareInfo.gpus && x.name === hardwareInfo.gpus[0].name,
)?.value;
};

const targetTemperature = getTargetInfo(gpuTemp);
const targetFanSpeed = getTargetInfo(gpuFan);

console.log(gpuFan);

return (
hardwareInfo.gpus && (
Expand All @@ -95,6 +105,14 @@ const GPUInfo = () => {
showTitle={false}
/>
)}
{targetFanSpeed && (
<DoughnutChart
chartData={targetFanSpeed}
dataType={"clock"}
hardType="gpu"
showTitle={false}
/>
)}
</div>

<InfoTable
Expand Down
4 changes: 2 additions & 2 deletions src/types/hardwareDataType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export type GraphicInfo = {
export type HardwareInfo = {
cpu?: CpuInfo;
memory?: MemoryInfo;
gpus: GraphicInfo[];
gpus?: GraphicInfo[];
isFetched: boolean;
};

export type Temperatures = Array<{
export type NameValues = Array<{
name: string;
value: number;
}>;

0 comments on commit 598dc9e

Please sign in to comment.