Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • stustanet/temperature-daemon
  • roman/temperature-daemon
  • 007638/temperature-daemon
3 results
Show changes
import re
import asyncio import asyncio
from prometheus_client import start_http_server, Gauge from prometheus_client import start_http_server, Gauge
from . import Plugin
def init(monitor): stats_name_re = re.compile(r'^temperature-(?P<group>\w+)-(?P<type>\w+)$')
return PluginPrometheus(monitor)
class PluginPrometheus: class Prometheus(Plugin):
def __init__(self, monitor): def __init__(self, monitor):
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
self.config = monitor.config self.config = monitor.config
...@@ -30,20 +30,17 @@ class PluginPrometheus: ...@@ -30,20 +30,17 @@ class PluginPrometheus:
addr=self.config["prometheus"].get('address', 'localhost'), addr=self.config["prometheus"].get('address', 'localhost'),
port=int(self.config["prometheus"]["port"]) port=int(self.config["prometheus"]["port"])
) )
print("started prometheus http server")
async def update_sensor_values(self, sensor):
"""
update
"""
self.sensor_metrics.labels(sensor=sensor.name).set(sensor.temperature)
async def send_stats_graph(self, graph, stattype, stattime, statval): async def send_stats_graph(self, graph, stattype, stattime, statval):
""" """
to be called as a plugin callback to export aggregated measurements to be called as a plugin callback to export aggregated measurements
""" """
label_group = stattype.split("-")[1] m = stats_name_re.match(stattype)
label_type = stattype.split("-")[2] if not m:
self.aggregated_metrics.labels(group=label_group, type=label_type).set(statval) return
self.aggregated_metrics.labels(group=m.group('group'), type=m.group('type')).set(statval)
async def sensor_update(self): async def sensor_update(self):
""" """
...@@ -51,4 +48,4 @@ class PluginPrometheus: ...@@ -51,4 +48,4 @@ class PluginPrometheus:
""" """
for sensor in self.monitor.sensors.values(): for sensor in self.monitor.sensors.values():
if sensor.valid: if sensor.valid:
await self.update_sensor_values(sensor) self.sensor_metrics.labels(sensor=sensor.name).set(sensor.temperature)
import time import time
from . import Plugin
def init(monitor):
""" Plugin interface method """
return PluginWarning(monitor)
class Warnings(Plugin):
class PluginWarning:
""" """
Generate all kind of warnings whenever needed and observe the sensor Generate all kind of warnings whenever needed and observe the sensor
if they see a problematic situation in the container if they see a problematic situation in the container
...@@ -107,7 +104,7 @@ class PluginWarning: ...@@ -107,7 +104,7 @@ class PluginWarning:
tempdiff = ceil_avg - floor_avg tempdiff = ceil_avg - floor_avg
await self.monitor.call_plugin( await self.monitor.call_plugin(
"send_stats_graph", graph="stats", "send_stats_graph", graph="stats",
stattype="temperature-floor_ceil_diff", stattime=now, statval=tempdiff) stattype="temperature-floor_ceil-diff", stattime=now, statval=tempdiff)
print("floor: min {:05.2f} max {:05.2f} avg {:05.2f} var {:05.2f}".format( print("floor: min {:05.2f} max {:05.2f} avg {:05.2f} var {:05.2f}".format(
floor_min, floor_max, floor_avg, floor_var)) floor_min, floor_max, floor_avg, floor_var))
......
...@@ -21,12 +21,12 @@ import asyncio ...@@ -21,12 +21,12 @@ import asyncio
import configparser import configparser
import sys import sys
import time import time
import importlib
from datetime import datetime from datetime import datetime
from pathlib import Path
import serial_asyncio import serial_asyncio
import serial import serial
from .plugins import PLUGINS
class Sensor: class Sensor:
""" """
...@@ -110,10 +110,16 @@ class TempMonitor: ...@@ -110,10 +110,16 @@ class TempMonitor:
""" """
Connect to the ESP chip Connect to the ESP chip
""" """
self._reader, self._writer = await serial_asyncio.open_serial_connection( try:
url=self.config['serial']['port'], self._reader, self._writer = await serial_asyncio.open_serial_connection(
baudrate=self.config['serial']['baudrate'], url=self.config['serial']['port'],
loop=self.loop) baudrate=self.config['serial']['baudrate'],
loop=self.loop)
except serial.SerialException:
print("Connection failed!")
self.loop.stop()
raise
# upon startup we only see garbage. (micropython starting up), # upon startup we only see garbage. (micropython starting up),
# also it will produce warnings if the recording is started in the middle # also it will produce warnings if the recording is started in the middle
# of a message, so wait until the end of a message block to start the game # of a message, so wait until the end of a message block to start the game
...@@ -133,17 +139,23 @@ class TempMonitor: ...@@ -133,17 +139,23 @@ class TempMonitor:
await self.reconnect() await self.reconnect()
last_valid_data_received = time.time() last_valid_data_received = time.time()
line = "" line = ""
reconnected_on_error = False
while True: while True:
# Wait for the next line # Wait for the next line
if time.time() - last_valid_data_received > 1800: if time.time() - last_valid_data_received > 10:
await self.call_plugin("err_no_valid_data", last_line=line) await self.call_plugin("err_no_valid_data", last_line=line)
if not reconnected_on_error:
reconnected_on_error = True
await self.reconnect()
try: try:
line = await asyncio.wait_for( line = await asyncio.wait_for(
self._reader.readline(), self._reader.readline(),
timeout=int(self.config['serial']['timeout'])) timeout=int(self.config['serial']['timeout']))
print("Received: ", line)
except asyncio.TimeoutError: except asyncio.TimeoutError:
print("No Data")
await self.call_plugin("err_nodata") await self.call_plugin("err_nodata")
continue continue
except serial.SerialException as exc: except serial.SerialException as exc:
...@@ -154,13 +166,17 @@ class TempMonitor: ...@@ -154,13 +166,17 @@ class TempMonitor:
try: try:
line = line.decode('ascii').strip() line = line.decode('ascii').strip()
except UnicodeError: except UnicodeError:
print("Unicode error")
continue continue
# print("recv:", line) # print("recv:", line)
if line == '': if line == '':
# Block has ended # Block has ended
print("Done block, storing sensors")
await self.store_sensors() await self.store_sensors()
print("Done")
continue continue
# Try to parse the line # Try to parse the line
try: try:
owid, temp = line.split(' ') owid, temp = line.split(' ')
...@@ -171,15 +187,18 @@ class TempMonitor: ...@@ -171,15 +187,18 @@ class TempMonitor:
## we have at least a valid line ## we have at least a valid line
last_valid_data_received = time.time() last_valid_data_received = time.time()
reconnected_on_error = False
sensor = self.sensors.get(owid, None) sensor = self.sensors.get(owid, None)
if not sensor: if not sensor:
# If the sensor is new - notify the operators # If the sensor is new - notify the operators
print("Unknown sensor")
await self.call_plugin("err_unknown_sensor", await self.call_plugin("err_unknown_sensor",
config=self._configname, config=self._configname,
owid=owid, owid=owid,
temp=temp) temp=temp)
elif temp > 1000 or temp < -1000: elif temp > 1000 or temp < -1000:
print("Sensor invalid")
sensor.valid = False sensor.valid = False
# if the sensor is giving bullshit data - notify the operators # if the sensor is giving bullshit data - notify the operators
await self.call_plugin("err_problem_sensor", await self.call_plugin("err_problem_sensor",
...@@ -235,41 +254,27 @@ class TempMonitor: ...@@ -235,41 +254,27 @@ class TempMonitor:
self._last_store = time.time() self._last_store = time.time()
def setup_plugin(filename, plugin):
"""
Setup and fix plugins
"""
if not getattr(plugin, "name", None):
plugin.name = filename
def main(): def main():
""" """
Start the tempmonitor Start the tempmonitor
""" """
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
configfile = "/etc/temperature/tempermon.ini" configfile = "/etc/tempermonitor.ini"
if len(sys.argv) == 2: if len(sys.argv) == 2:
configfile = sys.argv[1] configfile = sys.argv[1]
print("Configuring temperature monitoring system from {}.".format(configfile)) print(f"Configuring temperature monitoring system from {configfile}.")
monitor = TempMonitor(loop, configfile) monitor = TempMonitor(loop, configfile)
plugin_path = Path(__file__).resolve().parent / "plugins"
print("Loading plugins from {}".format(plugin_path))
active_plugins = monitor.config["general"]["plugins"].split(",") active_plugins = monitor.config["general"]["plugins"].split(",")
print(f"Active plugins: {active_plugins}") print(f"Active plugins: {active_plugins}")
for filename in plugin_path.glob("*.py"): for plugin in active_plugins:
if (plugin_path / filename).exists() and filename.stem in active_plugins: if plugin in PLUGINS:
print("loading {}".format(filename.name)) p = PLUGINS[plugin](monitor)
modname = "plugins." + filename.name.split('.')[0] monitor.plugins.append(p)
module = importlib.import_module(modname) print(f"Loaded plugin: {plugin}")
plugin = module.init(monitor)
setup_plugin(filename, plugin)
monitor.plugins.append(plugin)
print("Loaded: {}".format(plugin.name))
try: try:
loop.run_forever() loop.run_forever()
...@@ -278,6 +283,3 @@ def main(): ...@@ -278,6 +283,3 @@ def main():
finally: finally:
loop.run_until_complete(monitor.teardown()) loop.run_until_complete(monitor.teardown())
if __name__ == "__main__":
main()