Skip to content

Commit

Permalink
feature: GPU温度の取得(Nvidia)
Browse files Browse the repository at this point in the history
  • Loading branch information
shm11C3 committed Sep 23, 2024
1 parent 41c7018 commit 03cbe1d
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 16 deletions.
12 changes: 12 additions & 0 deletions src-tauri/src/commands/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@ pub async fn get_gpu_usage() -> Result<i32, String> {
}
}

///
/// ## GPU温度を取得
///
#[command]
pub async fn get_gpu_temperature() -> Result<Vec<graphic_service::GpuTemperature>, String>
{
match graphic_service::get_nvidia_gpu_temperature().await {
Ok(temps) => Ok(temps),
Err(e) => Err(format!("Failed to get GPU temperature: {:?}", 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 @@ -54,6 +54,7 @@ fn main() {
hardware::get_hardware_info,
hardware::get_memory_usage,
hardware::get_gpu_usage,
hardware::get_gpu_temperature,
hardware::get_cpu_usage_history,
hardware::get_memory_usage_history,
hardware::get_gpu_usage_history,
Expand Down
58 changes: 58 additions & 0 deletions src-tauri/src/services/graphic_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,64 @@ pub async fn get_nvidia_gpu_usage() -> Result<f32, nvapi::Status> {
})?
}

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

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

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

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

let mut temperatures = Vec::new();

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

temperatures.push(GpuTemperature {
name: gpu.full_name().unwrap_or("Unknown".to_string()),
value: thermal_settings[0].current_temperature.0 as f64, // thermal_settings の0番目の温度を f64 に変換
});
}

Ok(temperatures)
});

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

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

useEffect(() => {
if (settings?.theme) {
Expand Down
3 changes: 3 additions & 0 deletions src/atom/chart.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Temperatures } 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>([]);
21 changes: 19 additions & 2 deletions src/components/charts/DoughnutChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ const DoughnutChart = ({
chartData,
hardType,
dataType,
showTitle,
}: {
chartData: number;
hardType: ChartDataType;
dataType: HardwareDataType;
showTitle: boolean;
}) => {
const data = {
datasets: [
Expand All @@ -44,12 +46,27 @@ const DoughnutChart = ({
clock: <Speedometer className="mr-1" size={18} weight="duotone" />,
};

const dataTypeUnits: Record<HardwareDataType, string> = {
usage: "%",
temp: "℃",
clock: "MHz",
};

return (
<div className="p-2 w-36 relative">
<h3 className="text-lg font-bold">{displayHardType[hardType]}</h3>
<h3 className="text-lg font-bold">
{
showTitle
? displayHardType[hardType]
: " " /** [TODO] タイトルはコンポーネント外のほうが使いやすそう */
}
</h3>
<Doughnut data={data} options={options} />
<div className="absolute inset-0 flex flex-col items-center justify-center">
<span className="text-white text-xl font-semibold">{chartData}%</span>
<span className="text-white text-xl font-semibold">
{chartData}
{dataTypeUnits[dataType]}
</span>
</div>
<span className="flex justify-center mt-4 text-gray-400">
{dataTypeIcons[dataType]}
Expand Down
56 changes: 50 additions & 6 deletions src/hooks/useHardwareData.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import {
cpuTempAtom,
cpuUsageHistoryAtom,
gpuTempAtom,
graphicUsageHistoryAtom,
memoryUsageHistoryAtom,
} from "@/atom/chart";
import { chartConfig } from "@/consts/chart";
import {
getCpuUsage,
getGpuTemperature,
getGpuUsage,
getMemoryUsage,
} from "@/services/hardwareService";
import type { ChartDataType } from "@/types/hardwareDataType";
import type { ChartDataType, Temperatures } from "@/types/hardwareDataType";
import { type PrimitiveAtom, useSetAtom } from "jotai";
import { useEffect } from "react";

type AtomActionMapping = {
atom: PrimitiveAtom<number[]>;
action: () => Promise<number>;
};

/**
* ハードウェア使用率の履歴を更新する
*/
export const useUsageUpdater = (dataType: ChartDataType) => {
type AtomActionMapping = {
atom: PrimitiveAtom<number[]>;
action: () => Promise<number>;
};

const mapping: Record<ChartDataType, AtomActionMapping> = {
cpu: {
atom: cpuUsageHistoryAtom,
Expand Down Expand Up @@ -59,3 +62,44 @@ export const useUsageUpdater = (dataType: ChartDataType) => {
return () => clearInterval(intervalId);
}, [setHistory, getUsage]);
};

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

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

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

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

fetchData();

const intervalId = setInterval(async () => {
fetchData;
}, 10000);

return () => clearInterval(intervalId);
}, [setData, getTemp]);
};
6 changes: 5 additions & 1 deletion src/services/hardwareService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HardwareInfo } from "@/types/hardwareDataType";
import type { HardwareInfo, Temperatures } from "@/types/hardwareDataType";
import { invoke } from "@tauri-apps/api/tauri";

export const getCpuUsage = async (): Promise<number> => {
Expand Down Expand Up @@ -28,3 +28,7 @@ export const getGpuUsage = async (): Promise<number> => {
export const getGpuUsageHistory = (seconds: number): Promise<number[]> => {
return invoke("get_gpu_usage_history", { seconds: seconds });
};

export const getGpuTemperature = async (): Promise<Temperatures> => {
return await invoke("get_gpu_temperature");
};
30 changes: 25 additions & 5 deletions src/template/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
cpuUsageHistoryAtom,
gpuTempAtom,
graphicUsageHistoryAtom,
memoryUsageHistoryAtom,
} from "@/atom/chart";
Expand Down Expand Up @@ -52,6 +53,7 @@ const CPUInfo = () => {
chartData={cpuUsageHistory[cpuUsageHistory.length - 1]}
dataType={"usage"}
hardType="cpu"
showTitle={true}
/>
<InfoTable
data={{
Expand All @@ -68,16 +70,33 @@ const CPUInfo = () => {

const GPUInfo = () => {
const [graphicUsageHistory] = useAtom(graphicUsageHistoryAtom);
const [gpuTemp] = useAtom(gpuTempAtom);
const { hardwareInfo } = useHardwareInfoAtom();

const targetTemperature = gpuTemp.find(
(x) => x.name === hardwareInfo.gpus[0].name,
)?.value;

return (
hardwareInfo.gpus && (
<>
<DoughnutChart
chartData={graphicUsageHistory[graphicUsageHistory.length - 1]}
dataType={"usage"}
hardType="gpu"
/>
<div className="flex justify-around">
<DoughnutChart
chartData={graphicUsageHistory[graphicUsageHistory.length - 1]}
dataType={"usage"}
hardType="gpu"
showTitle={true}
/>
{targetTemperature && (
<DoughnutChart
chartData={targetTemperature}
dataType={"temp"}
hardType="gpu"
showTitle={false}
/>
)}
</div>

<InfoTable
data={{
Name: hardwareInfo.gpus[0].name,
Expand All @@ -102,6 +121,7 @@ const MemoryInfo = () => {
chartData={memoryUsageHistory[memoryUsageHistory.length - 1]}
dataType={"usage"}
hardType="memory"
showTitle={true}
/>
<InfoTable
data={{
Expand Down
7 changes: 6 additions & 1 deletion src/types/hardwareDataType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export type GraphicInfo = {
export type HardwareInfo = {
cpu?: CpuInfo;
memory?: MemoryInfo;
gpus?: GraphicInfo[];
gpus: GraphicInfo[];
isFetched: boolean;
};

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

0 comments on commit 03cbe1d

Please sign in to comment.