> For the complete documentation index, see [llms.txt](https://astroport-1.gitbook.io/astroport.one/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://astroport-1.gitbook.io/astroport.one/docs/how-to/power_monitoring.md).

# Power Consumption Monitoring System

## Overview

The Power Monitoring system provides a generic, reusable interface for measuring and reporting power consumption of processes using PowerJoular. It can be integrated into any script to track energy usage during execution.

**24/7 mode (recommended for 20H12):** PowerJoular can run as a systemd service (`powerjoular.service`) and write continuously to `/var/lib/powerjoular/power_24h.csv`. The 20H12 process then uses the **last 24 hours** of this CSV to generate the daily power report (full-day consumption). See [24/7 systemd service](#247-systemd-service-powerjoularservice) below.

**Important:** Files are stored in `/tmp/` by default (not `~/.zen/tmp/`) to avoid cleanup by scripts like `20h12.process.sh` which clean `~/.zen/tmp/` during execution. This ensures monitoring data persists throughout script execution.

## Components

### 1. `admin/monitor/power_monitor.sh` - Generic Monitoring Wrapper

Main script that provides a unified interface for power monitoring operations.

**Commands:**

* `start [csv_file] [pid_file]` - Start power monitoring (one-off)
* `stop [pid_file]` - Stop power monitoring
* `status [pid_file]` - Get monitoring status
* `report <csv_file> <output_html> [title] [log_file] [hostname] [duration]` - Generate HTML report from a CSV
* `report-from-24h <output_html> [title] [log_file] [hostname] [duration]` - Generate report from last 24h of 24/7 CSV (uses `POWER_24H_CSV`, default `/var/lib/powerjoular/power_24h.csv`)
* `trim-24h-csv [csv_path]` - Trim 24/7 CSV to last 24h only (stops powerjoular.service, overwrites CSV, restarts). Call after report-from-24h in 20H12 to avoid filling disk.

### 2. `admin/monitor/generate_powerjoular_graph.py` - Graph Generator

Python script that generates power consumption graphs from CSV data using matplotlib.

### 3. `admin/monitor/generate_power_report.sh` - HTML Report Generator

Generates comprehensive HTML reports with embedded graphs and statistics. Generic version that accepts a custom report title.

### 4. 24/7 systemd service (`powerjoular.service`)

Installed by `install/install_powerjoular.sh` from `tools/systemd/powerjoular.service`. Runs PowerJoular continuously and appends to `/var/lib/powerjoular/power_24h.csv`. The 20H12 script uses this file to report **full-day** power consumption (last 24h).

To avoid filling disk, **20H12 trims the CSV to last 24h** after generating the report (`power_monitor.sh trim-24h-csv`): it stops the service, overwrites the CSV with only the last 24h of data, then restarts the service.

* **Enable/start:** `sudo systemctl enable --now powerjoular`
* **Status:** `sudo systemctl status powerjoular`
* **CSV path:** `/var/lib/powerjoular/power_24h.csv` (override with env `POWER_24H_CSV`)

## Usage

### Basic Usage

```bash
#!/bin/bash
MY_PATH="$(dirname "$0")"
MY_PATH="$(cd "$MY_PATH" && pwd)"

# Start monitoring (PID file auto-derived from CSV)
MONITOR_OUTPUT=$("${MY_PATH}/admin/monitor/power_monitor.sh" start)
if [[ $? -eq 0 ]]; then
    PID=$(echo "$MONITOR_OUTPUT" | head -1)
    CSV=$(echo "$MONITOR_OUTPUT" | tail -1)
    echo "Monitoring started: PID=$PID, CSV=$CSV"
    
    # Your process here...
    sleep 60
    
    # Stop monitoring (using CSV file, PID file auto-detected)
    "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$CSV"
    
    # Generate report
    "${MY_PATH}/admin/monitor/power_monitor.sh" report \
        "$CSV" \
        "power_report.html" \
        "My Process" \
        "process.log" \
        "$(hostname -f)" \
        "1m"
fi
```

### Advanced Usage with Custom CSV File

```bash
#!/bin/bash
MY_PATH="$(dirname "$0")"
MY_PATH="$(cd "$MY_PATH" && pwd)"

# Custom CSV file (PID file automatically derived: my_process_power.pid)
CUSTOM_CSV="my_process_power.csv"

# Start with custom CSV (stored in /tmp/ by default to avoid cleanup)
"${MY_PATH}/admin/monitor/power_monitor.sh" start "$CUSTOM_CSV"

# Do your work...
your_script.sh

# Stop monitoring (using CSV file)
"${MY_PATH}/admin/monitor/power_monitor.sh" stop "$CUSTOM_CSV"

# Generate report
"${MY_PATH}/admin/monitor/power_monitor.sh" report \
    "$CUSTOM_CSV" \
    "my_report.html" \
    "Custom Process Report"
```

**Note:**

* PID files are automatically derived from CSV files (same path with `.pid` extension). No need to manage them manually.

### Integration in Existing Scripts

#### Example 1: Simple Integration

```bash
#!/bin/bash
MY_PATH="$(dirname "$0")"
MY_PATH="$(cd "$MY_PATH" && pwd)"

# Start monitoring at script start (PID file auto-derived)
POWER_CSV="my_script_power.csv"
"${MY_PATH}/admin/monitor/power_monitor.sh" start "$POWER_CSV" >/dev/null 2>&1

# Trap to ensure monitoring stops on exit
cleanup() {
    "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$POWER_CSV" >/dev/null 2>&1
}
trap cleanup EXIT INT TERM

# Your script logic here...
echo "Running main process..."
sleep 30

# Generate report before exit
"${MY_PATH}/admin/monitor/power_monitor.sh" report \
    "$POWER_CSV" \
    "my_script_report.html" \
    "My Script Power Report" \
    "" \
    "$(hostname -f)" \
    "30s"
```

#### Example 2: With Error Handling

```bash
#!/bin/bash
MY_PATH="$(dirname "$0")"
MY_PATH="$(cd "$MY_PATH" && pwd)"

POWER_CSV="process_power.csv"
POWER_REPORT="process_report.html"

# Start monitoring (PID file auto-derived)
if "${MY_PATH}/admin/monitor/power_monitor.sh" start "$POWER_CSV" >/dev/null 2>&1; then
    echo "Power monitoring started"
    
    # Ensure cleanup on exit
    cleanup() {
        "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$POWER_CSV" >/dev/null 2>&1
        
        # Generate report if CSV has data
        if [[ -f "/tmp/$POWER_CSV" ]] && [[ -s "/tmp/$POWER_CSV" ]]; then
            "${MY_PATH}/admin/monitor/power_monitor.sh" report \
                "$POWER_CSV" \
                "$POWER_REPORT" \
                "Process Power Report" \
                "" \
                "$(hostname -f)" \
                "" >/dev/null 2>&1
        fi
    }
    trap cleanup EXIT INT TERM
    
    # Main process
    main_process
    
    exit_code=$?
    
    # Generate report
    "${MY_PATH}/admin/monitor/power_monitor.sh" report \
        "$POWER_CSV" \
        "$POWER_REPORT" \
        "Process Power Report" \
        "" \
        "$(hostname -f)" \
        ""
    
    exit $exit_code
else
    echo "Warning: Failed to start power monitoring"
    # Continue without monitoring
    main_process
fi
```

## CSV Format

PowerJoular CSV format (with `-f` append mode):

```
timestamp,power,cpu_power,gpu_power
2026-01-24 22:40:52,0.47466666666667,8.27213500000653,8.27213500000653,0.00000000000000
2026-01-24 22:40:54,0.34316353887399,8.16117300000042,8.16117300000042,0.00000000000000
```

**Fields:**

* `timestamp`: ISO format timestamp (YYYY-MM-DD HH:MM:SS)
* `power`: Total power consumption (Watts)
* `cpu_power`: CPU power consumption (Watts)
* `gpu_power`: GPU power consumption (Watts) - may be 0 if no GPU

## Report Format

The generated HTML report includes:

1. **Power Statistics Box:**
   * Average Power (W)
   * Maximum Power (W)
   * Minimum Power (W)
   * Duration (if provided)
2. **Power Consumption Graph:**
   * Total power over time (line chart with fill)
   * CPU/GPU breakdown (if available)
   * Statistics overlay
3. **Execution Log:**
   * Process log file content (if provided)
   * Formatted for readability

## Requirements

### System Requirements

* **PowerJoular**: Must be installed and available in PATH
  * Installation: `~/.zen/Astroport.ONE/install/install_powerjoular.sh`
  * Requires: GNAT compiler, gprbuild
  * Supports: Intel RAPL, AMD Ryzen/EPYC, Raspberry Pi, Asus Tinker Board

### Python Requirements

* **Python 3** with:
  * `matplotlib` (for graph generation)
  * Standard library: `csv`, `datetime`, `os`, `sys`

Install dependencies:

```bash
pip install matplotlib
```

Or via Astroport.ONE install.sh (already included).

### Permissions

PowerJoular requires **root/sudo** access for RAPL monitoring on Linux kernels 5.10+:

```bash
sudo powerjoular
```

The `power_monitor.sh` script handles sudo automatically.

## API Reference

### `power_monitor.sh start [csv_file]`

Starts power monitoring. PID file is automatically derived from CSV file.

**Parameters:**

* `csv_file` (optional): Path to CSV output file (default: `/tmp/power_monitor_$$.csv`)
  * If relative path, stored in `/tmp/` (to avoid cleanup by scripts that clean `~/.zen/tmp/`)
  * PID file automatically created as `{csv_file%.csv}.pid`

**Returns:**

* Exit code: 0 on success, 1 on failure
* Stdout: PID on first line, CSV path on second line (if successful)

**Example:**

```bash
OUTPUT=$("${MY_PATH}/admin/monitor/power_monitor.sh" start "my_process.csv")
PID=$(echo "$OUTPUT" | head -1)
CSV=$(echo "$OUTPUT" | tail -1)
# PID file automatically created as /tmp/my_process.pid
```

### `power_monitor.sh stop [csv_file]`

Stops power monitoring. Can use CSV file (PID file auto-detected) or omit for default.

**Parameters:**

* `csv_file` (optional): CSV file path (PID file auto-derived) or omit for default

**Returns:**

* Exit code: 0 on success

**Example:**

```bash
# Stop using CSV file
"${MY_PATH}/admin/monitor/power_monitor.sh" stop "my_process.csv"

# Stop default monitoring
"${MY_PATH}/admin/monitor/power_monitor.sh" stop
```

### `power_monitor.sh status [csv_file]`

Gets monitoring status. Can use CSV file (PID file auto-detected) or omit for default.

**Parameters:**

* `csv_file` (optional): CSV file path (PID file auto-derived) or omit for default

**Returns:**

* `RUNNING:PID` if monitoring is active
* `NOT_RUNNING` if not active

**Example:**

```bash
STATUS=$("${MY_PATH}/admin/monitor/power_monitor.sh" status "my_process.csv")
if [[ "$STATUS" == RUNNING:* ]]; then
    PID="${STATUS#RUNNING:}"
    echo "Monitoring active: PID=$PID"
fi
```

### `power_monitor.sh report <csv_file> <output_html> [title] [log_file] [hostname] [duration]`

Generates HTML report with power consumption graph.

**Parameters:**

* `csv_file` (required): Path to PowerJoular CSV file
* `output_html` (required): Path to output HTML file
* `title` (optional): Report title (default: "Power Consumption Report")
* `log_file` (optional): Path to process log file to include
* `hostname` (optional): Hostname for report (default: `$(hostname -f)`)
* `duration` (optional): Process duration string (e.g., "1h 30m 15s")

**Returns:**

* Exit code: 0 on success, 1 on failure

**Example:**

```bash
"${MY_PATH}/admin/monitor/power_monitor.sh" report \
    "/tmp/power.csv" \
    "/tmp/report.html" \
    "My Process Report" \
    "/tmp/process.log" \
    "server.example.com" \
    "2h 15m"
```

## Integration Examples

### In 20h12.process.sh

The `20h12.process.sh` script uses the **24/7 PowerJoular service** to report **full-day** (last 24h) power consumption. No start/stop in the script; the systemd service writes continuously to `/var/lib/powerjoular/power_24h.csv`.

```bash
# 24/7 CSV from powerjoular.service (install: install/install_powerjoular.sh)
POWER_24H_CSV="${POWER_24H_CSV:-/var/lib/powerjoular/power_24h.csv}"

# ... script execution ...

# Generate report from last 24h of 24/7 CSV (full-day consumption)
"${MY_PATH}/admin/monitor/power_monitor.sh" report-from-24h \
    "/tmp/20h12_power_report.html" \
    "20H12 Power Consumption - Last 24h" \
    "$HOME/.zen/log/20h12_$(date +%Y%m%d).log" \
    "$(hostname -f)" \
    "24h"

# Trim 24/7 CSV to last 24h only to avoid filling disk (stops service, overwrites CSV, restarts)
"${MY_PATH}/admin/monitor/power_monitor.sh" trim-24h-csv "$POWER_24H_CSV"
```

For one-off monitoring (start/stop within a script), use `start`, `stop`, and `report` as in the [Custom Scripts](#in-custom-scripts) example below.

### In Custom Scripts

```bash
#!/bin/bash
# my_custom_script.sh

MY_PATH="$(dirname "$0")"
MY_PATH="$(cd "$MY_PATH" && pwd)"

# Setup power monitoring (PID file auto-derived)
POWER_CSV="my_script_power.csv"

if "${MY_PATH}/admin/monitor/power_monitor.sh" start "$POWER_CSV" >/dev/null 2>&1; then
    # Cleanup function
    cleanup() {
        "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$POWER_CSV" >/dev/null 2>&1
    }
    trap cleanup EXIT
    
    # Your script logic
    echo "Running process..."
    sleep 60
    
    # Generate report
    "${MY_PATH}/admin/monitor/power_monitor.sh" report \
        "$POWER_CSV" \
        "my_script_report.html" \
        "My Script Power Report"
else
    echo "Warning: Power monitoring unavailable"
fi
```

## Troubleshooting

### PowerJoular Not Found

**Error:** `PowerJoular is not available`

**Solution:**

```bash
~/.zen/Astroport.ONE/install/install_powerjoular.sh
```

### Permission Denied

**Error:** `sudo: a password is required`

**Solution:** Ensure the script has sudo access or run with appropriate permissions. PowerJoular requires root for RAPL access.

### No Data in CSV

**Issue:** CSV file exists but has only 1 line or is empty

**Possible causes:**

* Monitoring started but stopped too quickly
* PowerJoular failed to initialize
* Insufficient permissions

**Solution:** Check PowerJoular logs and ensure monitoring runs for at least a few seconds.

### Graph Generation Fails

**Error:** `Python3 or generate_powerjoular_graph.py not available`

**Solution:**

```bash
pip install matplotlib
```

Or ensure matplotlib is installed via Astroport.ONE install.sh.

### Report Generation Fails

**Error:** `generate_power_report.sh not found`

**Solution:** Ensure all scripts are in `admin/monitor/` directory and paths are correct.

## Best Practices

1. **Always use cleanup traps:**

   ```bash
   cleanup() {
       "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$POWER_CSV" >/dev/null 2>&1
   }
   trap cleanup EXIT INT TERM
   ```
2. **Use CSV file for stop/status (PID file auto-detected):**

   ```bash
   # Start
   "${MY_PATH}/admin/monitor/power_monitor.sh" start "$POWER_CSV"

   # Stop (using CSV, PID file auto-detected)
   "${MY_PATH}/admin/monitor/power_monitor.sh" stop "$POWER_CSV"
   ```
3. **Check monitoring status before generating reports:**

   ```bash
   # Files are in /tmp/ by default (not ~/.zen/tmp/)
   if [[ -f "/tmp/$POWER_CSV" ]] && [[ -s "/tmp/$POWER_CSV" ]]; then
       # Generate report
   fi
   ```
4. **Use descriptive titles for reports:**

   ```bash
   "${MY_PATH}/admin/monitor/power_monitor.sh" report \
       "$CSV" \
       "$OUTPUT" \
       "Descriptive Process Name - $(date +%Y-%m-%d)"
   ```
5. **Include log files when available:**

   ```bash
   "${MY_PATH}/admin/monitor/power_monitor.sh" report \
       "$CSV" \
       "$OUTPUT" \
       "Process Report" \
       "/var/log/process.log"
   ```
6. **Handle monitoring failures gracefully:**

   ```bash
   if ! "${MY_PATH}/admin/monitor/power_monitor.sh" start "$POWER_CSV"; then
       echo "Warning: Power monitoring unavailable, continuing without it"
   fi
   ```
7. **No need to manage PID files manually:**
   * PID files are automatically derived from CSV files
   * All files stored in `/tmp/` by default to avoid cleanup by scripts that clean `~/.zen/tmp/`
   * Just use the CSV file for all operations

## References

* **PowerJoular Documentation:** <https://www.noureddine.org/research/joular/powerjoular>
* **PowerJoular GitHub:** <https://github.com/papiche/powerjoular>
* **Installation Script:** `install/install_powerjoular.sh`
* **Example Integration:** `20h12.process.sh`

## License

AGPL-3.0 (same as Astroport.ONE)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://astroport-1.gitbook.io/astroport.one/docs/how-to/power_monitoring.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
