Skip to content

Commit 9ef65c5

Browse files
committed
Reorganises how Odin data adapters are configured and updated
Bug fixes preventing collecting offsets or data acquisition before system initialised Deletes RdmaUdp unit test, a relic from years gone by Removes legacy python 2 support from unit tests Some code structure, naming improvements Cuts number of flake8 warnings Amends unit tests.
1 parent e685980 commit 9ef65c5

10 files changed

+604
-689
lines changed

control/src/hexitec/HexitecDAQ.py

+55-56
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ def calculate_average_occupancy(self):
297297
return return_value
298298

299299
def prepare_odin(self):
300-
"""Ensure the odin data FP and FR are configured."""
300+
"""Ensure the odin data FP(s) and FR(s) are configured."""
301301
logging.debug("Setting up Acquisition")
302302
fr_status = self.get_adapter_status("fr")
303303
fp_status = self.get_adapter_status("fp")
@@ -513,7 +513,7 @@ def prepare_hdf_file(self):
513513
self.hdf_retry = 0
514514
self.in_progress = False
515515
self.daq_ready = True
516-
self.parent.fem.flag_error("Error reopening HDF file: %s" % e)
516+
self.parent.fem.flag_error("Reopening HDF file: %s" % e)
517517
self.parent.software_state = "Error"
518518
return
519519

@@ -959,21 +959,21 @@ def _set_bin_end(self, bin_end):
959959
if bin_end < 1:
960960
raise ParameterTreeError("bin_end must be positive!")
961961
self.bin_end = bin_end
962-
self.update_histogram_dimensions()
962+
self.update_number_histograms()
963963

964964
def _set_bin_start(self, bin_start):
965965
"""Update bin_start and datasets' histograms' dimensions."""
966966
if bin_start < 0:
967967
raise ParameterTreeError("bin_start must be positive!")
968968
self.bin_start = bin_start
969-
self.update_histogram_dimensions()
969+
self.update_number_histograms()
970970

971971
def _set_bin_width(self, bin_width):
972972
"""Update bin_width and datasets' histograms' dimensions."""
973973
if bin_width <= 0:
974974
raise ParameterTreeError("bin_width must be positive!")
975975
self.bin_width = bin_width
976-
self.update_histogram_dimensions()
976+
self.update_number_histograms()
977977

978978
def update_datasets_frame_dimensions(self):
979979
"""Update frames' datasets' dimensions."""
@@ -983,69 +983,24 @@ def update_datasets_frame_dimensions(self):
983983
request = ApiAdapterRequest(str(payload), content_type="application/json")
984984
self.adapters["fp"].put(command, request)
985985

986-
def update_histogram_dimensions(self):
987-
"""Update histograms' dimensions in the relevant datasets."""
986+
def update_number_histograms(self):
987+
"""Update number of histograms."""
988988
self.number_histograms = int((self.bin_end - self.bin_start) / self.bin_width)
989-
self.gcf = GenerateConfigFiles(self.param_tree.get(''), self.number_histograms,
990-
compression_type=self.compression_type,
991-
master_dataset=self.master_dataset,
992-
extra_datasets=self.extra_datasets,
993-
live_view_selected=False,
994-
odin_path=self.odin_path)
995-
# spectra_bins dataset
996-
payload = '{"dims": [%s], "chunks": [1, %s]}' % \
997-
(self.number_histograms, self.number_histograms)
998-
command = "config/hdf/dataset/" + "spectra_bins"
999-
request = ApiAdapterRequest(str(payload), content_type="application/json")
1000-
self.adapters["fp"].put(command, request)
1001-
1002-
# pixel_spectra dataset
1003-
ps_params = self.gcf.generate_pixel_spectra_params()
1004-
payload = '{%s}' % (ps_params)
1005-
command = "config/hdf/dataset/" + "pixel_spectra"
1006-
request = ApiAdapterRequest(str(payload), content_type="application/json")
1007-
self.adapters["fp"].put(command, request)
1008-
1009-
# summed_spectra dataset
1010-
payload = '{"dims": [%s], "chunks": [1, %s]}' % \
1011-
(self.number_histograms, self.number_histograms)
1012-
command = "config/hdf/dataset/" + "summed_spectra"
1013-
request = ApiAdapterRequest(str(payload), content_type="application/json")
1014-
self.adapters["fp"].put(command, request)
1015-
del self.gcf
1016-
self.gcf = None
1017989

1018990
def _set_max_frames_received(self, max_frames_received):
1019991
self.max_frames_received = max_frames_received
1020992

1021993
def _set_pass_processed(self, pass_processed=None):
994+
"""Toggle passing processed dataset on/off."""
1022995
if pass_processed is not None:
1023996
self.pass_processed = pass_processed
1024-
self.commit_config_before_acquire = True
1025-
command = "config/histogram"
1026-
formatted_string = ('{"pass_processed": %s}' % self.pass_processed).lower()
1027-
request = ApiAdapterRequest(formatted_string, content_type="application/json")
1028-
1029-
response = self.adapters["fp"].put(command, request)
1030-
status_code = response.status_code
1031-
if (status_code != 200):
1032-
error = "Error {} updating histogram's processed dataset".format(status_code)
1033-
self.parent.fem.flag_error(error)
997+
self.commit_config_before_acquire = True
1034998

1035999
def _set_pass_raw(self, pass_raw=None):
1036-
"""Change pass_raw if provided, then update FP setting."""
1000+
"""Toggle passing raw dataset on/off."""
10371001
if pass_raw is not None:
10381002
self.pass_raw = pass_raw
10391003
self.commit_config_before_acquire = True
1040-
command = "config/histogram"
1041-
formatted_string = ('{"pass_raw": %s}' % self.pass_raw).lower()
1042-
request = ApiAdapterRequest(formatted_string, content_type="application/json")
1043-
1044-
response = self.adapters["fp"].put(command, request)
1045-
status_code = response.status_code
1046-
if (status_code != 200):
1047-
error = "Error {} updating fp histogram's raw dataset".format(status_code)
1048-
self.parent.fem.flag_error(error)
10491004

10501005
def _set_threshold_filename(self, threshold_filename):
10511006
threshold_filename = self.data_config_path + threshold_filename
@@ -1102,7 +1057,7 @@ def _set_sensors_layout(self, layout):
11021057

11031058
self.update_rows_columns_pixels()
11041059
self.update_datasets_frame_dimensions()
1105-
self.update_histogram_dimensions()
1060+
self.update_number_histograms()
11061061

11071062
def _get_compression_type(self):
11081063
return self.compression_type
@@ -1152,6 +1107,7 @@ def commit_configuration(self):
11521107
# Enable live view for first node only
11531108
live_view_selected = True
11541109
logging.debug("Sending configuration to %s FP(s)" % self.number_nodes)
1110+
11551111
# Loop over node(s)
11561112
for index in range(self.number_nodes):
11571113
self.gcf = GenerateConfigFiles(parameter_tree, self.number_histograms,
@@ -1181,10 +1137,33 @@ def commit_configuration(self):
11811137
if (status_code != 200):
11821138
error = "Error {} loading plugins config in fp adapter".format(status_code)
11831139
self.parent.fem.flag_error(error)
1140+
pixel_spectra_params = self.gcf.generate_pixel_spectra_params()
11841141
# Delete GCF object before next iteration
11851142
del self.gcf
11861143
self.gcf = None
11871144

1145+
# Update dataset dimensions
1146+
1147+
# spectra_bins dataset
1148+
payload = '{"dims": [%s], "chunks": [1, %s]}' % \
1149+
(self.number_histograms, self.number_histograms)
1150+
command = "config/hdf/dataset/" + "spectra_bins"
1151+
request = ApiAdapterRequest(str(payload), content_type="application/json")
1152+
self.adapters["fp"].put(command, request)
1153+
1154+
# pixel_spectra dataset
1155+
payload = '{%s}' % (pixel_spectra_params)
1156+
command = "config/hdf/dataset/" + "pixel_spectra"
1157+
request = ApiAdapterRequest(str(payload), content_type="application/json")
1158+
self.adapters["fp"].put(command, request)
1159+
1160+
# summed_spectra dataset
1161+
payload = '{"dims": [%s], "chunks": [1, %s]}' % \
1162+
(self.number_histograms, self.number_histograms)
1163+
command = "config/hdf/dataset/" + "summed_spectra"
1164+
request = ApiAdapterRequest(str(payload), content_type="application/json")
1165+
self.adapters["fp"].put(command, request)
1166+
11881167
# Allow FP time to process above PUT requests before configuring plugin settings
11891168
IOLoop.instance().call_later(0.4, self.submit_configuration)
11901169

@@ -1214,6 +1193,26 @@ def submit_configuration(self):
12141193
else:
12151194
self.plugin = "histogram"
12161195

1196+
command = "config/histogram"
1197+
formatted_string = ('{"pass_processed": %s}' % self.pass_processed).lower()
1198+
request = ApiAdapterRequest(formatted_string, content_type="application/json")
1199+
1200+
response = self.adapters["fp"].put(command, request)
1201+
status_code = response.status_code
1202+
if (status_code != 200):
1203+
error = "Error {} updating histogram's processed dataset".format(status_code)
1204+
self.parent.fem.flag_error(error)
1205+
1206+
command = "config/histogram"
1207+
formatted_string = ('{"pass_raw": %s}' % self.pass_raw).lower()
1208+
request = ApiAdapterRequest(formatted_string, content_type="application/json")
1209+
1210+
response = self.adapters["fp"].put(command, request)
1211+
status_code = response.status_code
1212+
if (status_code != 200):
1213+
error = "Error {} updating fp histogram's raw dataset".format(status_code)
1214+
self.parent.fem.flag_error(error)
1215+
12171216
# Update live histogram labelling according to calibration enabled (or not)
12181217
command = ""
12191218
payload = ('{"calibration_enable": %s}' % self.calibration_enable).lower()

control/src/hexitec/HexitecFem.py

+26-18
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,6 @@ def verify_farm_mode_parameters(self, iface):
323323
self.number_nodes = num_ips
324324
self.parent.set_number_nodes(self.number_nodes)
325325

326-
def prepare_hardware(self, bDebug=False):
327-
"""Prepare hardware connection."""
328-
return self.prepare_farm_mode()
329-
330326
def configure_camera_interfaces(self):
331327
"""Configure IP, Mac and port parameters for detector's Control and Data interfaces."""
332328
Hex2x6CtrlRdma = RdmaUDP(local_ip=self.server_ctrl_ip, local_port=self.server_ctrl_port,
@@ -489,23 +485,35 @@ def connect(self):
489485
raise socket_error("Failed to setup Control connection: %s" % e)
490486
return
491487

492-
def check_hardware_status(self, action):
488+
def check_hardware_ready(self, action):
493489
"""Helper function checking hardware connected and not busy.
494490
495491
Raise Exception if hardware not connected, or hardware busy.
496492
"""
497493
if self.hardware_connected is not True:
498-
raise ParameterTreeError(f"Error: Can't {action} without a connection")
494+
raise ParameterTreeError(f"Can't {action} without a connection")
499495
if self.hardware_busy:
500-
error = f"Error: Can't {action}, Hardware busy"
496+
error = f"Can't {action}, Hardware busy"
497+
self.flag_error(error, "")
498+
raise ParameterTreeError(error)
499+
else:
500+
self._set_status_error("")
501+
502+
def check_system_initialised(self, action):
503+
"""Helper function checking system initialised.
504+
505+
Raise Exception if system not initialised.
506+
"""
507+
if self.system_initialised is not True:
508+
error = f"Can't {action}, system not initialised"
501509
self.flag_error(error, "")
502510
raise ParameterTreeError(error)
503511
else:
504512
self._set_status_error("")
505513

506514
def environs(self, msg=None):
507515
"""Readout environmental data if hardware connected and not busy."""
508-
self.check_hardware_status("read sensors")
516+
self.check_hardware_ready("read sensors")
509517
IOLoop.instance().add_callback(self.read_sensors)
510518

511519
@run_on_executor(executor='thread_executor')
@@ -605,16 +613,16 @@ def connect_hardware(self, msg=None):
605613
self.parent.software_state = "Cold"
606614
return
607615
if self.hardware_connected:
608-
raise ParameterTreeError("Error: Connection already established")
616+
error = "Connection already established"
617+
self.flag_error(error, "")
618+
raise ParameterTreeError(error)
609619
else:
610620
self._set_status_error("")
611621
self.hardware_busy = True
612622
self.hardware_connected = True
613623
# Configure control, data lines unless already configured
614624
# - Insist Control interface configured on every connect
615625
if self.parent.cold_start:
616-
# Commit configuration (again) otherwise FP(s) shutdown prematurely
617-
self.parent.daq.commit_configuration() # TODO superfluous?
618626
# Configure Control, Camera interfaces
619627
self.configure_camera_interfaces()
620628
self.parent.cold_start = True
@@ -679,7 +687,7 @@ def power_up_modules(self):
679687

680688
def initialise_hardware(self, msg=None):
681689
"""Initialise sensors, load enables, etc to initialise both VSR boards."""
682-
self.check_hardware_status("initialise hardware")
690+
self.check_hardware_ready("initialise hardware")
683691
try:
684692
self.hardware_busy = True
685693
self.parent.software_state = "Initialising"
@@ -692,10 +700,8 @@ def initialise_hardware(self, msg=None):
692700

693701
def collect_data(self, msg=None):
694702
"""Acquire data from camera."""
695-
self.check_hardware_status("collect data")
696703
try:
697704
self.hardware_busy = True
698-
# self.parent.software_state = "Acquiring"
699705
self._set_status_message("Acquiring data..")
700706
self.acquire_data()
701707
except Exception as e:
@@ -741,6 +747,7 @@ def cam_connect_completed(self):
741747
logging.debug("Modules Enabled")
742748
self._set_status_message("VSRs booted")
743749
self.hardware_busy = False
750+
self.parent.daq.commit_configuration()
744751
self.parent.software_state = "Idle"
745752

746753
def cam_disconnect(self):
@@ -751,14 +758,14 @@ def cam_disconnect(self):
751758
logging.debug("Modules Disabled")
752759
self.disconnect()
753760
logging.debug("Camera is Disconnected")
754-
self.system_initialised = False
755761
except socket_error as e:
756762
self.flag_error("Unable to disconnect camera", str(e))
757763
raise HexitecFemError(e)
758764
except AttributeError as e:
759765
error = "Unable to disconnect camera: No active connection"
760766
self.flag_error(error, str(e))
761767
raise HexitecFemError("%s; %s" % (e, "No active connection"))
768+
self.system_initialised = False
762769

763770
def acquire_data(self):
764771
"""Acquire data, poll fem for completion."""
@@ -868,7 +875,8 @@ def acquire_data_completed(self):
868875

869876
def run_collect_offsets(self):
870877
"""Run collect offsets sequence if connected and hardware not busy."""
871-
self.check_hardware_status("collect offsets")
878+
self.check_hardware_ready("collect offsets")
879+
self.check_system_initialised("collect offsets")
872880
self.collect_offsets()
873881

874882
@run_on_executor(executor='thread_executor')
@@ -1597,7 +1605,7 @@ def convert_bias_to_dac_values(self, hv):
15971605

15981606
def hv_on(self):
15991607
"""Switch HV on."""
1600-
self.check_hardware_status("switch HV on")
1608+
self.check_hardware_ready("switch HV on")
16011609
logging.debug("Going to set HV bias to -{} volts".format(self.bias_level))
16021610
hv_msb, hv_lsb = self.convert_bias_to_dac_values(self.bias_level)
16031611
# print(f" HV Bias (-{self.bias_level}) : {hv_msb[0]:X} {hv_msb[1]:X}",
@@ -1611,7 +1619,7 @@ def hv_on(self):
16111619

16121620
def hv_off(self):
16131621
"""Switch HV off."""
1614-
self.check_hardware_status("switch HV off")
1622+
self.check_hardware_ready("switch HV off")
16151623
logging.debug("Disable: [0xE2]")
16161624
# Can call hv_off function on any VSR object
16171625
self.vsr_list[0].hv_off()

0 commit comments

Comments
 (0)