Skip to content

Commit

Permalink
Add keywords and tools for power measurement
Browse files Browse the repository at this point in the history
Prevent power measurement errors failing any tests.

Monitor also drops in measurement frequency:
A warning of unusually low measurement frequency
will be shown in the power plot in case of drop in
the measurement frequency. This can help in detecting
any problems in the measurement hardware.

Signed-off-by: Samuli Leivo <[email protected]>
  • Loading branch information
leivos-unikie committed Dec 13, 2024
1 parent b7b4977 commit 55fb73f
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Robot-Framework/config/variables.robot
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ Set Variables
${result} Run Process sh -c cat /run/secrets/wifi-password shell=true
Set Global Variable ${TEST_WIFI_PSWD} ${result.stdout}

Run Keyword And Ignore Error Set Global Variable ${RPI_IP_ADDRESS} ${config['addresses']['measurement_agent']['device_ip_address']}
${result} Run Process sh -c cat /run/secrets/pi-login shell=true
Set Global Variable ${LOGIN_PI} ${result.stdout}
${result} Run Process sh -c cat /run/secrets/pi-pass shell=true
Set Global Variable ${PASSWORD_PI} ${result.stdout}

Set Log Level INFO

IF $BUILD_ID != '${EMPTY}'
Expand Down
70 changes: 70 additions & 0 deletions Robot-Framework/lib/parse_power_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: 2022-2024 Technology Innovation Institute (TII)
# SPDX-License-Identifier: Apache-2.0

import pandas as pd
import logging
import matplotlib.pyplot as plt
import csv


def extract_time_interval(csv_file, start_time, end_time):
columns = ['time', 'meas_counter', 'power']
data = pd.read_csv(csv_file, names=columns)
interval = data.query("{} < time < {}".format(start_time, end_time))
interval.to_csv('power_interval.csv', index=False)

# Check if measurement frequency was within normal limits
# Reset the flag file
with open("low_frequency_flag", 'w'):
pass
normal_meas_frequency = 312
tolerance = 50
low_frequency = data[data['meas_counter'] < normal_meas_frequency - tolerance]
if not low_frequency.empty:
logging.info("Low measurement frequency detected:")
logging.info(low_frequency)
with open("low_frequency_timestamps.csv", 'a', newline='') as csvfile:
csvwriter = csv.writer(csvfile)
for index, row in low_frequency.iterrows():
csvwriter.writerow(row)
with open("low_frequency_flag", 'w', newline='') as flag:
flag.write("Warning: unusually low measurement frequency detected")
return False
return True

def generate_graph(csv_file, test_name):
data = pd.read_csv(csv_file)
start_time = data['time'].values[0]
end_time = data['time'].values[data.index.max()]
plt.figure(figsize=(20, 10))
plt.set_loglevel('WARNING')

# Show only hh-mm-ss part of the time at x-axis ticks
data['time'] = data['time'].str[11:19]

plt.ticklabel_format(axis='y', style='plain')
plt.plot(data['time'], data['power'], marker='o', linestyle='-', color='b')
plt.yticks(fontsize=14)

# Show full timestamps of the beginning and the end of the plotted time interval
plt.suptitle(f'Device power consumption {start_time} - {end_time}', fontsize=18, fontweight='bold')

# Add note to plot in case of issues in measurement frequency
with open("low_frequency_flag", 'r') as flag:
low_frequency_note = flag.readline()
plt.title(f'During "{test_name}"\n{low_frequency_note}', loc='center', fontweight="bold", fontsize=16)
plt.ylabel('Power (mW)', fontsize=16)
plt.grid(True)
plt.xticks(data['time'], rotation=45, fontsize=14)

# Set maximum for tick number
plt.locator_params(axis='x', nbins=40)

plt.savefig(f'../test-suites/power_test.png')
return

def mean_power(csv_file):
columns = ['time', 'meas_counter', 'power']
data = pd.read_csv(csv_file, names=columns)
mean_value = data['power'].mean()
return mean_value
130 changes: 130 additions & 0 deletions Robot-Framework/resources/power_meas_keywords.resource
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# SPDX-FileCopyrightText: 2022-2024 Technology Innovation Institute (TII)
# SPDX-License-Identifier: Apache-2.0

*** Settings ***
Resource ../config/variables.robot
Library ../lib/parse_power_data.py
Library SSHLibrary
Library DateTime

*** Variables ***
${SSH_MEASUREMENT} ${EMPTY}
${start_timestamp} ${EMPTY}
${RPI_IP_ADDRESS} ${EMPTY}


*** Keywords ***

Check variable availability
${value}= Get Variable Value ${RPI_IP_ADDRESS}
IF $value!='${EMPTY}'
RETURN ${True}
ELSE
RETURN ${False}
END

Start power measurement
[Documentation] Connect to the measurement agent and run script to start collecting measurement results
[Arguments] ${id}=power_data ${timeout}=200
${availability} Check variable availability
IF ${availability}==False
Log To Console Power measurement agent IP address not defined. Ignoring all power measurement related keywords.
Set Global Variable ${SSH_MEASUREMENT} ${EMPTY}
RETURN
END
${status} ${connection} Run Keyword And Ignore Error Connect to measurement agent
IF '${status}'!='PASS'
Set Global Variable ${SSH_MEASUREMENT} ${EMPTY}
Log To Console Power measurement agent not found. Ignoring all power measurement related keywords.
RETURN
END
# Multiple logging processes not allowed (for now)
Stop recording power
Start recording power ${id} ${timeout}

Connect to measurement agent
[Documentation] Set up SSH connection to the measurement agent
[Arguments] ${IP}=${RPI_IP_ADDRESS} ${PORT}=22 ${target_output}=ghaf@raspberrypi
# Use existing connection if available
${status} ${output} Run Keyword And Ignore Error Switch Connection ${SSH_MEASUREMENT}
IF '${status}'=='PASS'
Log To Console Switched connection to measurement agent.
Set Global Variable ${SSH_MEASUREMENT} ${SSH_MEASUREMENT}
RETURN ${SSH_MEASUREMENT}
END
Log To Console Connecting to measurement agent
${connection}= Open Connection ${IP} port=${PORT} prompt=\$ timeout=15
${output}= Login username=${LOGIN_PI} password=${PASSWORD_PI}
Should Contain ${output} ${target_output}
Set Global Variable ${SSH_MEASUREMENT} ${connection}
RETURN ${SSH_MEASUREMENT}

Start recording power
[Arguments] ${file_name} ${timeout}
Log To Console Starting to record power measurements
Run Keyword And Ignore Error Execute Command nohup python /home/ghaf/ghaf/ghaf-power-measurement/measure_power.py ${file_name}.csv ${timeout} > output.log 2>&1 & timeout=3

Stop recording power
IF $SSH_MEASUREMENT=='${EMPTY}'
Log To Console No connection to power measurement device. Ignoring all power measurement related keywords.
RETURN
END
Log To Console Stopping power recording
Run Keyword And Ignore Error Execute Command pkill python timeout=3

Get power record
[Arguments] ${file_name}=power_data.csv
IF $SSH_MEASUREMENT=='${EMPTY}'
Log To Console No connection to power measurement device. Ignoring all power measurement related keywords.
RETURN
END
Run Keyword And Ignore Error Connect to measurement agent
Run Keyword And Ignore Error SSHLibrary.Get File /home/ghaf/ghaf/power_data/${file_name} ../../../power_measurements/

Save power measurement interval
[Documentation] Extract measurement data within given time interval
[Arguments] ${file_name} ${start_time} ${end_time}
IF $SSH_MEASUREMENT=='${EMPTY}'
Log To Console No connection to power measurement device. Ignoring all power measurement related keywords.
RETURN
END
Log To Console Extract power data from given time interval
${time_interval} DateTime.Subtract Date From Date ${end_time} ${start_time} exclude_millis=True
IF ${time_interval} < 0
Log To Console Invalid timestamp critera for extracting power data
RETURN
END
Run Keyword And Ignore Error Extract time interval ../../../power_measurements/${file_name} ${start_time} ${end_time}

Generate power plot
[Documentation] Extract power data from start_timestamp to current time.
... Plot power vs time and save to png file.
[Arguments] ${id} ${test_name}
IF $SSH_MEASUREMENT=='${EMPTY}'
Log To Console No connection to power measurement device. Ignoring all power measurement related keywords.
RETURN
END
${end_timestamp} Get current timestamp
Run Keyword And Ignore Error Connect to measurement agent
Run Keyword And Ignore Error Get power record ${id}.csv
Run Keyword And Ignore Error Save power measurement interval ${id}.csv '${start_timestamp}' '${end_timestamp}'
Run Keyword And Ignore Error Generate graph power_interval.csv ${test_name}
Run Keyword And Ignore Error Log <img src="power_test.png" alt="Power plot" width="1200"> HTML

Set start timestamp
${current_time} DateTime.Get Current Date UTC exclude_millis=yes
Set Global Variable ${start_timestamp} ${current_time}
Log To Console ${start_timestamp}

Get current timestamp
${current_time} DateTime.Get Current Date UTC exclude_millis=yes
RETURN ${current_time}

Log average power
[Arguments] ${file_name}
IF $SSH_MEASUREMENT=='${EMPTY}'
Log To Console No connection to power measurement device. Ignoring all power measurement related keywords.
RETURN
END
${keyword_status} ${mean_P} Run Keyword And Ignore Error Mean power ${file_name}
# TODO: With the statistics tools of performance testing also average power values could be plotted and monitored
2 changes: 1 addition & 1 deletion flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 55fb73f

Please sign in to comment.