Skip to content
Snippets Groups Projects
Commit 6173b86b authored by Mattia Mancini's avatar Mattia Mancini
Browse files

Use the panel library to integrate KTDashboard and the PMT sensor togheter

parent b8ae6a88
No related branches found
No related tags found
1 merge request!1Wrap into panel
**/**/__pycache__
...@@ -6,10 +6,7 @@ output. ...@@ -6,10 +6,7 @@ output.
To execute it run To execute it run
``` ```
bokeh serve pmt-view --show python3 pmt-view/main.py --port 5321
``` ```
A web browser will open with the live monitor of the Joules/Watts usage. A web browser will open with the live monitor of the Joules/Watts usage.
To configure the type of sensor to use
or the device number please edit `config.py` in `pmt-view`
File deleted
# Configuration file for PMT-VIEW # Configuration file for PMT-VIEW
# Name of the sensor see pmt documentation for more details UPDATE_INTERVAL = 1000
SENSOR = "rapl" WINDOW_IN_SECONDS = 60 * 5
# Device ID # Device ID
DEVICE_ID = 0 DEVICE_ID = 0
from argparse import ArgumentParser from argparse import ArgumentParser
import pypmt import pypmt
import time import time
from bokeh.plotting import figure from bokeh.plotting import figure as b_figure
from bokeh.io import show from bokeh.io import show
from bokeh.embed import file_html from bokeh.embed import file_html
from bokeh.io import curdoc from bokeh.io import curdoc
from bokeh.models import ColumnDataSource from bokeh.models import ColumnDataSource
import config import config
import pmt import pmt
import panel
import pandas
import os
from functools import reduce
import itertools
import logging
from bokeh.palettes import Dark2_5 as palette
linestyles = ['solid', 'dashed', 'dotted', 'dotdash']
from ktdashboard.ktdashboard import KTdashboard
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s - %(message)s')
MAX_POWERSENSOR3_PAIRS = 4
AVAILABLE_SENSORS = [
'tegra',
'powersensor2',
'powersensor3',
#'dummy',
'nvml',
'rapl',
'rocm',
'xilinx'
]
def parse_args(): def parse_args():
parser = ArgumentParser(description="PMT display sensors") parser = ArgumentParser(description="PMT display sensors")
parser.add_argument("--sensor", default=config.SENSOR) parser.add_argument("--kt_cache", help="Kernel tuner cache file", default=None)
parser.add_argument("--device", default=config.DEVICE_ID, type=int) parser.add_argument(
return parser.parse_args() "--kt-test", help="Kernel Tuner test", action="store_true")
parser.add_argument("--update-interval",
default=config.UPDATE_INTERVAL, type=int)
parser.add_argument("--window_in_seconds",
default=config.WINDOW_IN_SECONDS, type=int)
parser.add_argument("--port", default=0, type=int)
parser.add_argument("--show", default=False, action="store_true")
parser.add_argument("--host", default="0.0.0.0")
def main(bokeh=False): return parser.parse_args()
args = parse_args()
sensor = pmt.get_pmt(args.sensor, device_id=args.device)
disp = Display(sensor, args.sensor)
disp.draw()
disp.loop()
class PMTView:
def data_frame_columns(self):
sensor_pairs = [[f'{k}_joules', f'{k}_watts']
for k in AVAILABLE_SENSORS]
return list(reduce(lambda x, y: x+y, sensor_pairs, []))
class Display: def init_datasource(self, source_key):
def __init__(self, sensor, sensor_name): self.datasources[source_key] = ColumnDataSource({
self.figure = figure(width=900, height=500)
self.sensor_name = sensor_name
self.datasource = ColumnDataSource(
data={
"Joules": [0], "Joules": [0],
"Watts": [0], "Watts": [0],
"Average watts": [0],
"Average joules": [0],
"Seconds": [0], "Seconds": [0],
} })
)
self.sensor = sensor self.state[source_key] = None
self.state = None
def init_datasources(self):
for sensor in AVAILABLE_SENSORS:
if sensor == 'powersensor3':
for pair_id in self.power_sensor3_pairs:
self.init_datasource(f'{sensor}-{pair_id}')
self.init_datasource(sensor)
def __init__(self, update_interval, window_in_seconds):
self.data = pandas.DataFrame(columns=self.data_frame_columns())
self.datasources = {}
self.state = {}
self.power_sensor3_pairs = {}
self.window_in_seconds = window_in_seconds
self.cur_second = 0 self.cur_second = 0
self.update_interval = update_interval
self.sensors = self.init_sensors()
self.init_datasources()
def draw(self): self.sensor_names = panel.widgets.CheckBoxGroup(
self.figure.line( name='Sensors', options=AVAILABLE_SENSORS, size=len(AVAILABLE_SENSORS)
x="Seconds",
y="Joules",
color="red",
line_width=3.0,
source=self.datasource,
legend_label="Joules",
) )
self.figure.line( self.quantity = panel.widgets.Select(
name='Quantity', options=['Watts', 'Joules'], default='Watts')
self.scatter = panel.bind(self.draw_plot, self.sensor_names, self.quantity)
self.controls = self.draw_controls()
self.selected_sensors = panel.bind(
self.init_sensors, self.sensor_names)
self.dashboard = self.create_dashboard()
def init_sensors(self, device_id=0):
sensors = {}
for sensor_name in AVAILABLE_SENSORS.copy():
try:
if sensor_name == 'tegra' and os.system('tegrastats --help') != 0:
raise Exception("Missing sensor tegra")
elif sensor_name == 'powersensor3' and os.path.exists('/dev/ttyACM0'):
ps3 = pmt.get_pmt(
sensor_name, '/dev/ttyACM0')
for pair_id in range(MAX_POWERSENSOR3_PAIRS):
pair_name = ps3.getPairName(pair_id)
if pair_name != 'NC' and not pair_name.startswith('NotUsed'):
self.power_sensor3_pairs[pair_id] = pair_name
logging.info("found sensor pair: %s",
self.power_sensor3_pairs)
sensors[sensor_name] = ps3
else:
sensors[sensor_name] = pmt.get_pmt(
sensor_name, device_id)
logging.info("%s, loaded", sensor_name)
except Exception as e:
AVAILABLE_SENSORS.remove(sensor_name)
logging.exception(e)
logging.warning("%s, unavailable", sensor_name)
return sensors
def draw_controls(self):
return panel.WidgetBox(
panel.pane.Markdown("## PMT Configuration"),
self.sensor_names, self.quantity)
def draw_plot(self, sensor_names, quantity):
colors = itertools.cycle(palette)
lss = itertools.cycle(linestyles)
figure = b_figure(width=500, height=500, )
for sensor, color in zip(sensor_names, colors):
if sensor == 'powersensor3':
for pair_id, ls in zip(self.power_sensor3_pairs, lss):
figure.line(
x="Seconds", x="Seconds",
y="Watts", y=quantity,
color="blue",
line_width=3.0, line_width=3.0,
source=self.datasource, color=color,
legend_label="Watts", line_dash=ls,
source=self.datasources[f'{sensor}-{pair_id}'],
legend_label=f"{sensor} {self.power_sensor3_pairs[pair_id]}",
) )
self.figure.line(
else:
figure.line(
x="Seconds", x="Seconds",
y="Average watts", y=quantity,
color="lightblue",
line_width=3.0, line_width=3.0,
source=self.datasource, color=color,
legend_label="Average watts", source=self.datasources[sensor],
legend_label=f"{sensor}",
) )
self.figure.line( if sensor_names:
x="Seconds", figure.legend.location = 'top_left'
y="Average joules", pane = panel.Column(
color="orange", panel.pane.Markdown(f"## Total power usage [{quantity}]"),
line_width=3.0, panel.pane.Bokeh(figure)
source=self.datasource,
legend_label="Average joules",
) )
def callback(self): return pane
if self.state is None:
def update_sensor(self, sensor, pair_id=None):
state_key = sensor
if sensor.startswith('powersensor3') and pair_id is not None:
state_key = f'{sensor}-{pair_id}'
try:
if self.state[state_key] is None:
# first read is garbage # first read is garbage
self.state = self.sensor.read() if pair_id is not None:
self.state = self.sensor.read() self.state[state_key] = self.sensors[sensor].read(pair_id)
self.state[state_key] = self.sensors[sensor].read(pair_id)
else:
self.state[state_key] = self.sensors[sensor].read()
self.state[state_key] = self.sensors[sensor].read()
time.sleep(1) time.sleep(1)
n_state = self.sensor.read() if pair_id is not None:
self.cur_second += pypmt.PMT.seconds(self.state, n_state) n_state = self.sensors[sensor].read(pair_id)
else:
n_state = self.sensors[sensor].read()
datasource = dict(self.datasources[state_key].data)
self.cur_second += pypmt.PMT.seconds(
self.state[state_key], n_state)
row = { row = {
"Joules": [ "Joules": [
pypmt.PMT.joules(self.state, n_state), pypmt.PMT.joules(
], self.state[state_key], n_state) / pypmt.PMT.seconds(self.state[state_key], n_state),
"Watts": [pypmt.PMT.joules(self.state, n_state)],
"Average watts": [
pypmt.PMT.joules(self.state, n_state) / pypmt.PMT.seconds(self.state, n_state)
],
"Average joules": [
pypmt.PMT.watts(self.state, n_state) / pypmt.PMT.seconds(self.state, n_state)
], ],
"Watts": [pypmt.PMT.joules(self.state[state_key], n_state) / pypmt.PMT.seconds(self.state[state_key], n_state)],
"Seconds": [self.cur_second], "Seconds": [self.cur_second],
} }
self.datasource.stream(row) for value_name, value in row.items():
self.state = n_state if value_name not in datasource:
datasource[value_name] = value
elif 'Seconds' in datasource and (datasource['Seconds'][-1] - datasource['Seconds'][0] > self.window_in_seconds):
datasource[value_name].pop(0)
datasource[value_name].append(value[0])
self.datasources[state_key].data = datasource
# self.datasources[state_key].stream(row)
self.state[state_key] = n_state
except Exception as e:
logging.exception("Error in upgrading values: %s", e)
def callback(self):
for sensor in AVAILABLE_SENSORS:
if sensor == 'powersensor3':
for pair_id in self.power_sensor3_pairs:
self.update_sensor(sensor, pair_id)
else:
self.update_sensor(sensor)
def create_dashboard(self):
dash = panel.template.BootstrapTemplate(title='PMTView')
dash.sidebar.append(self.controls)
dash.main.append(self.scatter)
return dash
def loop(self): def loop(self):
curdoc().template_variables["sensor"] = self.sensor_name panel.state.add_periodic_callback(
curdoc().add_periodic_callback(lambda: self.callback(), 1000) lambda: self.callback(), self.update_interval)
curdoc().add_root(self.figure) return self.dashboard
def main():
args = parse_args()
dash = PMTView(update_interval=args.update_interval,
window_in_seconds=args.window_in_seconds)
# ktdash.dashboard.servable()
# dash.dashboard.servable()
combined = panel.template.BootstrapTemplate(title='PMTView')
main = panel.Row()
sidebar = panel.Column()
if args.kt_cache:
ktdash = KTdashboard(args.kt_cache, demo=args.kt_test)
ktdash.plot_options['height'] = 500
ktdash.plot_options['width'] = 500
sidebar.extend([
panel.pane.Markdown("## KernelTuner parameters"),
ktdash.yvariable, ktdash.xvariable, ktdash.colorvariable])
main.append(ktdash.scatter)
sidebar.append(dash.controls)
main.append(dash.scatter)
combined.main.append(main)
combined.sidebar.append(sidebar)
combined.servable()
def dashboard():
if args.kt_cache:
panel.state.add_periodic_callback(ktdash.update_data, 1000)
panel.state.add_periodic_callback(dash.callback, 1000)
return combined
server = panel.serve(dashboard, show=args.show, port=args.port, address=args.host)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
elif __name__.startswith("bokeh_app"):
main(bokeh=True)
bokeh bokeh
kernel_tuner[cuda]
git+https://github.com/KernelTuner/dashboard
panel
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment