Commit dedc24ac authored by Romain Baptiste Dominique Albert's avatar Romain Baptiste Dominique Albert
Browse files

Threaded version of the SensorReader. It does handle too losing the network by...

Threaded version of the SensorReader. It does handle too losing the network by giving the last measurement of th temperature
parent c62e1e37
import urllib.request, json
import urllib.request, json
class SensorReader(object):
from threading import Thread
from queue import LifoQueue, Queue, Empty
import time
update_rate = 10 # The temperature are update every 10s
timeout = 60 # If no temperature is request by the code during 60s, the thread pooling
# the temperature shutdown
api_path = 'https://kirchmair.iqoqi.at/lab/api/cryostat/overview'
# Notice that I (Romain) choose to use a singleton to keep the compatibility with the
# existing code. It is often a bad idea to use a singleton so don't reuse this structure
# without knowing what you are doing.
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
else:
cls._instances[cls].__init__(*args, **kwargs)
return cls._instances[cls]
class SensorReader(metaclass=Singleton):
"""
Notice that this class is instanced as a Singleton so you only one
instance is created of this object. If you try to instance it again,
you will simply get the first instance of the object.
"""
def __init__(self, CryoID, BaseSensor='T_MC_RuOx', *args, **kwargs):
"""Class to read out current cryostat state. Requires connection to
internet.
"""
self.api_path = 'https://kirchmair.iqoqi.at/lab/api/cryostat/overview'
self.cryoid = CryoID
self.baseSensorId = BaseSensor
self.data = None
self.update()
if not CryoID is None and self.cryoid != CryoID:
self.cryoid = CryoID
self.baseSensorId = BaseSensor
self.data = None
self.request_queue = Queue()
self.data_queue = LifoQueue() # Last in, first out, so we always get the last pulling
self.reader = Reader(self.cryoid, self.data_queue, self.request_queue)
self.thread = None
self.update()
elif CryoID is None:
self.cryoid = None
def update(self):
"""Fetches current cryostat values"""
# Acquire data
with urllib.request.urlopen(self.api_path) as url:
tmp = json.loads(url.read().decode())
self.data = tmp['cryostats'][self.cryoid]
"""Fetches current cryostat values"""
# If the thread which read the temperature from the server is not running,
# we start it and wait a bit for it to get the temperature
# Note that this code is working because 'or' is a short-circuit operator.
if self.thread is None or not self.thread.is_alive():
self.thread = Thread(target=self.reader.run, daemon=True)
self.thread.start()
time.sleep(0.1)
# We warn the thread that a request was made to keep it alive
self.request_queue.put(1)
# We try to get last reading from the thread, and update the data structure if there is
# new data.
# If it is the first reading and nothing was send back by the thread, we generate an error.
try:
data_temp = self.data_queue.get(block=False)
except Empty:
if self.data is None:
raise Exception('It was not possible to get the temperature')
else:
return
self.data = data_temp
def sensor_list(self):
"""List available sensors. You can query the value and unit or
......@@ -38,6 +93,7 @@ class SensorReader(object):
def get_sensor_value(self, key):
"""Returns current value for sensor <key>"""
self.update()
try:
if 'state' in self.data[key]:
return self.data[key]['state']
......@@ -54,4 +110,57 @@ class SensorReader(object):
def last_update(self):
"""Returns the timestamp for last update of the temperature sensor"""
return self.data['time']
\ No newline at end of file
return self.data['time']
class Reader():
def __init__(self, CryoID, data, request):
self.cryoid = CryoID
self.data = data
self.request = request
self.last_request = 0
self.number_error = 0
def run(self):
"""
Start the reading thread, it finish when either:
- he does not arrive to reach the server three times in a row
- if the last request is older than the timeout
"""
self.last_request = time.time()
while (time.time() - self.last_request) < timeout:
try:
# Acquire data
with urllib.request.urlopen(api_path) as url:
tmp = json.loads(url.read().decode())
data = tmp['cryostats'][self.cryoid]
self.data.put(data)
self.number_error = 0
# Check the request queue. If they were a request, the life
# of the thread is increase. If several requests were made between two
# updates, the request queue is emptied.
while True:
try:
self.request.get(block=False)
self.last_request = time.time()
except Empty:
break
time.sleep(update_rate)
except (urllib.error.URLError, urllib.error.HTTPError) as e:
self.number_error += 1
if self.number_error > 2:
return
pass
return ## Finish the thread after the time without a request
# To keep the object alive, we link it with the module (which is fine because module are only
# imported one time in python). This instance will prevent the object to be destroyed by the
# garbage collector.
connection = SensorReader(None)
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment