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

Merge branch 'Threaded_SensorReader' into 'master'

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

See merge request exphys/quantum-circuits/python-repo!10
parents 55a7a5d9 a4d9f33f
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 = 60 # The temperature are update every 10s
timeout = 600 # 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()
# If it is the first reading and if nothing was send back by the thread,
# we generate an error.
try:
data_temp = self.data_queue.get(block=True, timeout=1.)
except Empty:
if self.data is None:
raise Exception('It was not possible to get the temperature')
else:
return
else:
# 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.
try:
data_temp = self.data_queue.get(block=False)
except Empty:
return
self.data = data_temp
def sensor_list(self):
"""List available sensors. You can query the value and unit or
......@@ -38,6 +100,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 +117,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