webapp.graph

Widget definition module: Defines UI elements used for main dashboard tab, including controls for initiating heating runs, a graph displaying the temperature profiles during runs, as well as real-time information such as current temperature and heating run progress.

Use the following code to import and display the widgets defined in this module:

example:

>>> from webapp import graph
>>> display(graph.app)

Widget layout

Web app UI module structure

Widget objects


webapp.graph.app = ipywidgets.widgets.VBox(children=(command_box, fig, info_box))

Module top-level container widget - holds all the widgets defined in the webapp.graph module, which make up the Main tab of the dashboard app.

Allows the Main tab to be imported into the dashboard app using the code below:

Example:

>>> from webapp import graph
>>> graph.app

webapp.graph.command_box = ipywidgets.widgets.HBox(children=(b_start, b_stop, b_reset))

Button widgets, for initiating heating runs


webapp.graph.fig = bqplot.Figure(...)

Graphing widget, displays temperature profiles of temperature sensors, as well as specified temperature programme, for heating runs.


webapp.graph.info_box = ipywidgets.widgets.VBox(children=(w1, w2, w3))

Container widget, containing progress bar and temperature readout label widgets


Helper functions

async webapp.graph.work()

Refresh loop for graph and temperature readouts

This function continously updates the graph and temperature readout for the tab, depending on the current programme and data.


Code listing

""" 
Widget definition module: Defines UI elements used for main dashboard tab,
including controls for initiating heating runs, a graph displaying the
temperature profiles during runs, as well as real-time information such as
current temperature and heating run progress.

Use the following code to import and display the widgets defined in this module:

:example:

>>> from webapp import graph
>>> display(graph.app)
"""
from ipywidgets.widgets import Label, FloatProgress, Button
from ipywidgets.widgets import Layout, HBox, VBox
import numpy as np
import bqplot as bq
from asyncio import sleep as asleep

from webapp.shared import appState


# Control buttons
b_start = Button(
    description='Run',
    icon='play',
    button_style='warning',
    layout=Layout(width='28%', height='100%')
)
b_stop = Button(
    description='Stop',
    icon='stop',
    button_style='warning',
    layout=Layout(width='28%', height='100%')
)
b_reset = Button(
    description='Reset',
    icon='restart',
    button_style='warning',
    layout=Layout(width='28%', height='100%')
)

def start_click(b):
    global appState
    appState.config['RUN'] = True
    
def stop_click(b):
    global appState
    appState.config['RUN'] = False


def reset_click(b):
    global appState
    #breakpoint()
    appState.config['RUN'] = False
    appState.connected = False

b_start.on_click(start_click)
b_stop.on_click(stop_click)
b_reset.on_click(reset_click)

command_box = HBox(
    children=(b_start, b_stop, b_reset),
    layout=Layout(width='87.5%', height='10%')
) #: Button widgets, for initiating heating runs

w1 = FloatProgress(
    value=0,
    min=0,
    max=1800,
    description='Heating run:',
    style={'description_width': 'initial'},
    layout=Layout(width='56%', height='33%')
)

w2 = Label(
    value=F"Current temp: {str(np.round(appState.data['TEMPA'][-1],2))} \u2103",
    layout=Layout(width='72%', height='33%')
)

w3 = Label(
    value=F"Current \u0394T: {str(np.round(appState.data['DTEMP'][-1],2))} K",
    layout=Layout(width='72%', height='33%')
)

info_box = VBox(
    children=(w1,w2,w3),
    layout=Layout(width='75%', height='15%')
) #: Container widget, containing progress bar and temperature readout label widgets

x_sc = bq.LinearScale(min=0)
y_sc_l = bq.LinearScale()
y_sc_r = bq.LinearScale()

x_ax = bq.Axis(
    label='Time (s)',
    scale=x_sc,
    num_ticks=6
)

y_ax_left = bq.Axis(
    label='Temperature (C)',
    scale=y_sc_l,
    orientation='vertical',
    tick_format='0.0f',
    num_ticks=7,
    side='left'
)

y_ax_right = bq.Axis(
    label=F"\u0394T (K)",
    scale=y_sc_r,
    orientation='vertical',
    tick_format='0.1f',
    num_ticks=7,
    side='right'
)

MeasuredTempLine = bq.Lines(
    x=appState.data['TIME'],
    y=appState.data['TEMPA'],
    scales={'x': x_sc, 'y': y_sc_l},
    colors=['#ff000f'],
    display_legend=True,
    labels=['Measured']
)

SampleTempLine = bq.Lines(
    x=appState.data['TIME'],
    y=appState.data['TEMPB'],
    scales={'x': x_sc, 'y': y_sc_l},
    colors=['#0000ff'],
    display_legend=True,
    labels=['Sample']
)

ReferenceTempLine = bq.Lines(
    x=appState.data['TIME'],
    y=appState.data['TEMPC'],
    scales={'x': x_sc, 'y': y_sc_l},
    colors=['#3c9dd0'],
    display_legend=True,
    labels=['Reference']
)

ProgrammeTempLine = bq.Lines(
    x=appState.programme.x,
    y=appState.programme.y_temp,
    scales={'x': x_sc, 'y': y_sc_l},
    line_style='dashed',
    colors=['#ff000f'],
    display_legend=True,
    labels=['Programme']
)

HeatLine = bq.Lines(
    x=appState.data['TIME'][1:],
    y=np.diff(appState.data['TEMPA'], axis=0),
    scales={'x': x_sc, 'y': y_sc_r},
    colors=['#f7a500'],
    opacities=[0.6]
)

ProgrammeHeatLine = bq.Lines(
    x=appState.programme.x,
    y=appState.programme.y_heat,
    scales={'x': x_sc, 'y': y_sc_r},
    colors=['#46ff33'],
    line_style='dashed'
)

DeltaTLine = bq.Lines(
    x=appState.data['TIME'],
    y=appState.data['DTEMP'],
    scales={'x': x_sc, 'y': y_sc_r},
    colors=['#f7a500'],
    display_legend=True,
    labels=[F"\u0394T"]
)

fig = bq.Figure(
    layout=Layout(width='100%', height='25em'),
    axes=[x_ax, y_ax_left, y_ax_right],
    marks=[MeasuredTempLine, ProgrammeTempLine,ProgrammeHeatLine, DeltaTLine],
    fig_margin=dict(top=25, bottom=50, left=45, right=70),
    legend_location='top-right'
)
"""
Graphing widget, displays temperature profiles of temperature sensors, as well as
specified temperature programme, for heating runs.
"""

app = VBox(
    children=(command_box, fig, info_box),
    layout=Layout(width='100%', height='100%', margin='0 0 0 0')
)
"""
Module top-level container widget - holds all the widgets defined in the
:mod:`webapp.graph` module, which make up the Main tab of the dashboard app.

Allows the Main tab to be imported into the dashboard app using the code
below:

:example:

>>> from webapp import graph
>>> graph.app
"""


async def work():
    """ 
    Refresh loop for graph and temperature readouts

    This function continously updates the graph and temperature readout for the tab, depending on
    the current programme and data. 
    """
    global appState
    while True:
        try:

            # Values to be updated
            TempA = appState.data['TEMPA']  # RTD 1 - measuring reference temperature
            TempB = appState.data['TEMPB']  # RTD 2 - measuring sample temperature
            TempC = appState.data['TEMPC']  # TC 1 - measuring plate temperature
            dtemps = appState.data['DTEMP'] # TC 2 - measuring sample-reference delta
            times = appState.data['TIME']

            # Update labels
            w1.value = 0 # TODO
            w2.value = F"Current temp: {str(np.round(TempC[-1],2))} \u2103"
            w3.value = F"Current \u0394T: {str(np.round(dtemps[-1],2))} K"

            # Update plot lines
            MeasuredTempLine.x = times
            MeasuredTempLine.y = TempC
            SampleTempLine.x = times
            SampleTempLine.y = TempB
            ReferenceTempLine.x = times
            ReferenceTempLine.y = TempA
            DeltaTLine.x = times
            DeltaTLine.y = dtemps
            ProgrammeTempLine.x = appState.programme.x
            ProgrammeTempLine.y = appState.programme.y_temp
            ProgrammeHeatLine.x = appState.programme.x
            ProgrammeHeatLine.y = 60*appState.programme.y_heat


            HeatLine.x = times[1:]
            if len(TempC) > 10:
                TempA = np.array(TempA)[np.array(times) > 0.0]
                TempB = np.array(TempB)[np.array(times) > 0.0]
                TempC = np.array(TempC)[np.array(times) > 0.0]
                times = np.array(times)[np.array(times) > 0.0]
                #HeatLine.y = 60*((TempA-TempA[0])/times)
                y_sc_r.max = max(min(np.max(HeatLine.y),30), 1)
                y_sc_r.min = min(max(np.min(HeatLine.y),-30), -1)
            else:
                y_sc_r.max = 30
                y_sc_r.min = -30
            x_sc.max = float(max(max(MeasuredTempLine.x), max(ProgrammeTempLine.x)))
            x_sc.min = 0.
            y_sc_l.max = float(max(max(MeasuredTempLine.y), max(ProgrammeTempLine.y)))
            y_sc_l.min = float(min(min(MeasuredTempLine.y), min(ProgrammeTempLine.y)))
            fig.marks = [MeasuredTempLine, SampleTempLine, ReferenceTempLine, ProgrammeTempLine, ProgrammeHeatLine, DeltaTLine]
            fig.axes = [x_ax, y_ax_left, y_ax_right]
            await asleep(0.2)
        except ValueError:
            await asleep(0.2)
        except Exception as e:
            print('Graph work error')
            print(e)
            await asleep(0.2)