Rewrite: Finished initial backend code. Connection to other components missing.

This commit is contained in:
2021-07-28 11:30:23 +02:00
parent 80f036060a
commit 4827d07b6d
+41 -12
View File
@@ -29,6 +29,12 @@ class HelmholtzCageDevice:
# Indicates all the threads should be joined
self._stop_flag = Event()
# --- POLLING SUBSCRIBERS ---
# This is a list of object callbacks interested in receiving device status updates.
# This will primarily include the front-end which wants to update its display data.
# The callback functions should accept a dict as an argument of the form {'arduino':, 'axes':[{}, {}, {}]}
self._subscribers = []
# --- COMMAND QUEUEING ---
self.command_lock = RLock()
# Indicates to the command executing thread that a new command has arrived for execution
@@ -94,7 +100,7 @@ class HelmholtzCageDevice:
# Loop over axes
for i in range(3):
axis_dict = {}
for key in ["coil_const", "ambient_field", "resistance", "max_volts", "max_amps"]
for key in ["coil_const", "ambient_field", "resistance", "max_volts", "max_amps"]:
axis_dict[key] = float(config.read_from_config(g.AXIS_NAMES[i], key, config.CONFIG_OBJECT))
self.axes.append(axis_dict)
@@ -125,6 +131,11 @@ class HelmholtzCageDevice:
self.proxy_id = None
# Otherwise do nothing, this case requires no behaviour
def subscribe_status_updates(self, callback):
# List containing all interested subscribers.
# We won't check if a callback is added twice. Not our responsibility
self._subscribers.append(callback)
def queue_command(self, proxy_obj, command):
""" Queues a dict for immediate execution containing the command for the cage as a whole.
Since the newest command should always be run, it is not a real queue (just a variable)"""
@@ -172,8 +183,19 @@ class HelmholtzCageDevice:
if stop_flag_set:
return
status_data = {'axes': []}
with self.hardware_lock:
pass
# This polls all three axes at once
for i in range(3):
# Helper function to find correct psu and channel to talk to
psu, channel = self._get_psu_for_axis(i)
# This is a slow operation, watch out!
status = psu.poll_channel_state(channel)
status_data['axes'].append(status)
# Distribute status data to all interested subscribers
for subscriber in self._subscribers:
subscriber(status_data)
def _set_field_raw(self, arg):
currents = []
@@ -217,33 +239,40 @@ class HelmholtzCageDevice:
# min. 8V, max. max_volts, in-between as needed with current value (+margin to not limit current)
voltage_limit = min(max(1.3 * abs(safe_current) * self.axes[i]['resistance'], 8),
self.axes[i]['max_volts']) # limit voltage
# TODO: This kind of stuff belongs in the config and should not be hardcoded
# Determine which channel of which psu is required
if i == 0 or i == 1:
psu = self.psu1
channel = psu.valid_channels[i]
else:
psu = self.psu2
channel = psu.valid_channels[0]
# Helper function to find correct psu and channel to talk to
psu, channel = self._get_psu_for_axis(i)
# Set voltages and currents. Outputs should already be active from initializer.
psu.set_current(channel, safe_current)
psu.set_voltage(channel, voltage_limit)
def _get_psu_for_axis(self, axis_index):
"""Determine which channel of which psu is required"""
# TODO: This kind of stuff belongs in the config and should not be hardcoded
if axis_index == 0 or axis_index == 1:
psu = self.psu1
channel = psu.valid_channels[axis_index]
else:
psu = self.psu2
channel = psu.valid_channels[0]
return psu, channel
def shutdown(self):
""" Shuts down the hardware. This special command overrides the currently active proxy.
The object cannot be recovered from this state, but may be re-instantiated."""
# Send signals to kill threads:
# TODO: Handle timeout behaviour
self._stop_flag.set()
# _cmd_exec_thread:
with self.command_lock:
self.command = None
self.new_command_flag.set() # Causes the thread to unblock
self._cmd_exec_thread.join(timeout=2)
# TODO: Handle timeout behaviour
#_hw_poll_thread:
# This thread is stopped just by setting the _stop_flag
self._hw_poll_thread.join(timeout=2)
# This waiting period is not easily removed without resulting in unexpected behaviour
with self.hardware_lock: