Power update + bar graph change
Some checks failed
Generate Power Graph / plot (push) Failing after 1m51s

This commit is contained in:
mrkmntal 2025-11-05 18:43:27 -05:00
commit 974d23a7d4
2 changed files with 90 additions and 73 deletions

View file

@ -1,8 +1,7 @@
#!/usr/bin/env python3
"""
plot_power.py
Generates a PNG graph from power_log.csv for AMD Laptop APU/GPU telemetry.
Designed for Forgejo CI artifact publishing.
Forgejo CI: visualize AMD TUF APU/GPU telemetry as a clear bar graph.
Author: MarkMental
"""
@ -10,55 +9,55 @@ import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
# --- Configuration ---
CSV_PATH = "power_log.csv"
OUTPUT_FILE = "power_graph.png"
print(f"📊 Reading {CSV_PATH}...")
df = pd.read_csv(CSV_PATH)
# --- Load data ---
try:
df = pd.read_csv(CSV_PATH)
except FileNotFoundError:
raise SystemExit(f"❌ CSV not found: {CSV_PATH}")
# --- Normalize timestamp ---
df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
df["time_fmt"] = df["timestamp"].dt.strftime("%Y-%m-%d %H:%M:%S")
# --- Sanity checks ---
required_cols = {"timestamp", "apu_w", "gpu_w", "total_w"}
if not required_cols.issubset(df.columns):
raise SystemExit(f"❌ Missing columns in {CSV_PATH}. Found: {df.columns.tolist()}")
# --- Downsample if extremely long (avoid thousands of bars) ---
if len(df) > 200:
df = df.iloc[::max(1, len(df)//200), :]
# --- Parse timestamps ---
try:
df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
except Exception as e:
print(f"⚠️ Failed to parse some timestamps: {e}")
# --- Plot setup ---
fig, ax1 = plt.subplots(figsize=(12, 6))
# --- Plot ---
plt.figure(figsize=(10, 5))
plt.plot(df["timestamp"], df["apu_w"], label="APU Power (W)", color="orange", linewidth=1.3)
plt.plot(df["timestamp"], df["gpu_w"], label="GPU Power (W)", color="green", linewidth=1.3)
plt.plot(df["timestamp"], df["total_w"], label="Total Power (W)", color="blue", linewidth=1.5)
# Power bars
width = 0.3
x = range(len(df))
ax1.bar([i - width for i in x], df["apu_w"], width, label="APU Power (W)", color="#ff9933")
ax1.bar(x, df["gpu_w"], width, label="GPU Power (W)", color="#66cc66")
ax1.bar([i + width for i in x], df["total_w"], width, label="Total Power (W)", color="#3399ff")
# Optional: plot temperatures if present
ax1.set_xlabel("Timestamp (YYYY-MM-DD HH:MM:SS)")
ax1.set_ylabel("Power (Watts)")
ax1.set_title("AMD TUF Power & Temperature Log")
ax1.set_xticks(x[::max(1, len(x)//10)])
ax1.set_xticklabels(df["time_fmt"].iloc[::max(1, len(x)//10)], rotation=45, ha="right")
# --- Secondary axis for temperature ---
if "apu_temp" in df.columns and "gpu_temp" in df.columns:
ax2 = plt.gca().twinx()
ax2.plot(df["timestamp"], df["apu_temp"], "--", color="red", alpha=0.4, label="APU Temp (°C)")
ax2.plot(df["timestamp"], df["gpu_temp"], "--", color="purple", alpha=0.4, label="GPU Temp (°C)")
ax2 = ax1.twinx()
ax2.plot(x, df["apu_temp"], "--", color="red", linewidth=1.2, label="APU Temp (°C)")
ax2.plot(x, df["gpu_temp"], "--", color="purple", linewidth=1.2, label="GPU Temp (°C)")
ax2.set_ylabel("Temperature (°C)")
lines, labels = plt.gca().get_legend_handles_labels()
ax2.set_ylim(min(df[["apu_temp","gpu_temp"]].min())-5,
max(df[["apu_temp","gpu_temp"]].max())+5)
# Combine legends
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
plt.legend(lines + lines2, labels + labels2, loc="upper left")
ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")
else:
plt.legend(loc="upper left")
ax1.legend(loc="upper left")
plt.title("AMD TUF Power Log")
plt.xlabel("Time")
plt.ylabel("Power (Watts)")
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.savefig(OUTPUT_FILE, dpi=150)
print(f"✅ Saved clean bar graph: {OUTPUT_FILE}")
# --- Save ---
plt.savefig(OUTPUT_FILE, dpi=120)
print(f"✅ Saved graph: {OUTPUT_FILE}")