Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion library/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@

import library.config as config
import library.stats as stats
from library.log import logger

STOPPING = False

# Some jobs run every millisecond, so a job failing at every run would flood the log file:
# only log the same failing job once per this delay (in seconds).
ERROR_LOG_MIN_INTERVAL = 1.0
_last_error_log = {}


def async_job(threadname=None):
""" wrapper to handle asynchronous threads """
Expand Down Expand Up @@ -62,7 +68,16 @@ def periodic(scheduler, periodic_interval, action, actionargs=()):
# If the program is not stopping: re-schedule the task for future execution
scheduler.enter(periodic_interval, 1, periodic,
(scheduler, periodic_interval, action, actionargs))
action(*actionargs)
# A failing sensor read must not kill this stat's thread: the next run is already
# scheduled above, so log the error and keep going.
try:
action(*actionargs)
except Exception:
name = getattr(action, "__name__", str(action))
now = time.monotonic()
if now - _last_error_log.get(name, 0.0) >= ERROR_LOG_MIN_INTERVAL:
_last_error_log[name] = now
logger.exception("Scheduled job '%s' failed, skipping this cycle", name)

@wraps(func)
def wrap(
Expand Down
55 changes: 40 additions & 15 deletions library/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import os
import platform
import sys
from typing import List
from typing import List, Optional

import babel.dates
import requests
Expand Down Expand Up @@ -121,6 +121,11 @@ def display_themed_value(theme_data, value, min_size=0, unit=''):


def display_themed_percent_value(theme_data, value):
# A sensor can transiently return nan: int(nan) raises ValueError, which would kill this
# stat's thread. Note this happens before the SHOW check below, i.e. even when hidden.
if value is None or math.isnan(value):
return

display_themed_value(
theme_data=theme_data,
value=int(value),
Expand All @@ -130,6 +135,9 @@ def display_themed_percent_value(theme_data, value):


def display_themed_temperature_value(theme_data, value):
if value is None or math.isnan(value):
return

display_themed_value(
theme_data=theme_data,
value=int(value),
Expand All @@ -142,6 +150,9 @@ def display_themed_progress_bar(theme_data, value):
if not theme_data.get("SHOW", False):
return

if value is None or math.isnan(value):
return

display.lcd.DisplayProgressBar(
x=theme_data.get("X", 0),
y=theme_data.get("Y", 0),
Expand Down Expand Up @@ -201,6 +212,9 @@ def display_themed_radial_bar(theme_data, value, min_size=0, unit='', custom_tex


def display_themed_percent_radial_bar(theme_data, value):
if value is None or math.isnan(value):
return

display_themed_radial_bar(
theme_data=theme_data,
value=int(value),
Expand All @@ -210,6 +224,9 @@ def display_themed_percent_radial_bar(theme_data, value):


def display_themed_temperature_radial_bar(theme_data, value):
if value is None or math.isnan(value):
return

display_themed_radial_bar(
theme_data=theme_data,
value=int(value),
Expand Down Expand Up @@ -244,10 +261,14 @@ def display_themed_line_graph(theme_data, values):
)


def save_last_value(value: float, last_values: List[float], history_size: int):
def save_last_value(value: Optional[float], last_values: List[float], history_size: int):
# Initialize last values list the first time with given size
if len(last_values) != history_size:
last_values[:] = last_values_list(size=history_size)
# A sensor may transiently return None: store it as the nan "no data" marker, else
# math.isnan() in DisplayLineGraph raises TypeError on every later redraw of this graph.
if value is None:
value = math.nan
# Store the value to the list that can then be used for line graph
last_values.append(value)
# Also remove the oldest value from list
Expand Down Expand Up @@ -925,22 +946,26 @@ def stats(cls):
theme_data = config.THEME_DATA['STATS']['PING']

delay = ping(dest_addr=PING_DEST, unit="ms")
if delay is None or delay is False:
# ping3 returns None on timeout and False on unknown host: no delay to display
delay = math.nan

save_last_value(delay, cls.last_values_ping,
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
# logger.debug(f"Ping delay: {delay}ms")

display_themed_progress_bar(theme_data['GRAPH'], delay)
display_themed_radial_bar(
theme_data=theme_data['RADIAL'],
value=int(delay),
unit="ms",
min_size=6
)
display_themed_value(
theme_data=theme_data['TEXT'],
value=int(delay),
unit="ms",
min_size=6
)
if not math.isnan(delay):
display_themed_progress_bar(theme_data['GRAPH'], delay)
display_themed_radial_bar(
theme_data=theme_data['RADIAL'],
value=int(delay),
unit="ms",
min_size=6
)
display_themed_value(
theme_data=theme_data['TEXT'],
value=int(delay),
unit="ms",
min_size=6
)
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_ping)