From 1d45da591cb83ff55f0a5ab34fd94382e3e90bb2 Mon Sep 17 00:00:00 2001 From: Sven Augustin Date: Mon, 9 Mar 2020 11:13:43 +0000 Subject: [PATCH] fixed BS Counter --- slic/daq/__init__.py | 4 +- slic/daq/{utilities.py => acquisition.py} | 2 +- slic/daq/ask_yes_no.py | 73 ++++++++++ slic/daq/bs.py | 109 --------------- slic/daq/bscounter.py | 159 ++++++++++++++++++++++ slic/daq/dia_new.py | 2 +- slic/daq/{dia.py => diacounter.py} | 2 +- slic/daq/epics.py | 2 +- slic/devices/general/detectors.py | 2 +- slic/utils/channels.py | 6 +- 10 files changed, 243 insertions(+), 118 deletions(-) rename slic/daq/{utilities.py => acquisition.py} (92%) create mode 100644 slic/daq/ask_yes_no.py delete mode 100644 slic/daq/bs.py create mode 100644 slic/daq/bscounter.py rename slic/daq/{dia.py => diacounter.py} (99%) diff --git a/slic/daq/__init__.py b/slic/daq/__init__.py index 014751fc7..5d4fd58f5 100644 --- a/slic/daq/__init__.py +++ b/slic/daq/__init__.py @@ -1,5 +1,5 @@ -from .bs import BSCounter -from .dia import DIACounter +from .bscounter import BSCounter +from .diacounter import DIACounter diff --git a/slic/daq/utilities.py b/slic/daq/acquisition.py similarity index 92% rename from slic/daq/utilities.py rename to slic/daq/acquisition.py index 78caafe27..f886f3c82 100644 --- a/slic/daq/utilities.py +++ b/slic/daq/acquisition.py @@ -30,7 +30,7 @@ class Acquisition: return "done" def __repr__(self): - return "Acquisition {}".format(self.status) + return "Acquisition: {}".format(self.status) diff --git a/slic/daq/ask_yes_no.py b/slic/daq/ask_yes_no.py new file mode 100644 index 000000000..de5653b1e --- /dev/null +++ b/slic/daq/ask_yes_no.py @@ -0,0 +1,73 @@ + +try: + read_input = input # py3 +except NameError: + read_input = raw_input # py2 + + + +ANSWERS = { + "y": True, + "n": False, + "yes": True, + "no": False +} + +OPTION_PROMPTS = { + None: "y/n", + "y": "Y/n", + "n": "y/N" +} + + + +def ask_Yes_no(question, **kwargs): + """ + See ask_yes_no() with default="y". + """ + return ask_yes_no(question, default="y", **kwargs) + + +def ask_yes_No(question, **kwargs): + """ + See ask_yes_no() with default="n". + """ + return ask_yes_no(question, default="n", **kwargs) + + +def ask_yes_no(question, default=None, ctrl_c="n", ctrl_d=None): + """ + Asks "question? [y/n]" and returns the answer as boolean. + + Valid answers are: y/n/yes/no (match is not case sensitive). + For invalid answers the question is repeated. + + default (defaults to None) is used if the user input is empty, i.e., only Enter was pressed. + ctrl_c (defaults to "n") is used if the user presses Ctrl-C. + ctrl_d (defaults to default) is used if the user presses Ctrl-D. + + The default is capitalized in the prompt. + """ + + ctrl_d = default if ctrl_d is None else ctrl_d + + option_prompt = OPTION_PROMPTS[default] + prompt = question + "? [{}] ".format(option_prompt) + + ans = None + while ans not in ANSWERS: + try: + ans = read_input(prompt).lower() + if not ans: # response was an empty string + ans = default + except KeyboardInterrupt: + print() + ans = ctrl_c + except EOFError: + print() + ans = ctrl_d + + return ANSWERS[ans] + + + diff --git a/slic/daq/bs.py b/slic/daq/bs.py deleted file mode 100644 index fee95c050..000000000 --- a/slic/daq/bs.py +++ /dev/null @@ -1,109 +0,0 @@ -import datetime -import os -import zmq - -import data_api as dapi -from bsread.h5 import receive -from bsread.avail import dispatcher - -from .utilities import Acquisition -from .basecounter import BaseCounter - - -class BSCounter(BaseCounter): - - def __init__(self, - default_channel_list={'listname':[]}, - default_file_path='%s' - ): - self._default_file_path = default_file_path - self._default_channel_list = default_channel_list - - def avail(self, *args, **kwargs): - return dispatcher.get_current_channels(*args, **kwargs) - - def check_channel_list(self, printResult=True, printOnlineChannels=False): - all_available = set([i['name'] for i in self.avail()]) - status = {} - for listname in self._default_channel_list.keys(): - tch = set(self._default_channel_list[listname]) - status[listname] = {} - status[listname]['online'] = tch.intersection(all_available) - status[listname]['offline'] = tch.difference(all_available) - if printResult: - for listname in status.keys(): - if printOnlineChannels: - print('#### Online Channels in {} ####'.format(listname)) - print('\n'.join(status[listname]['online'])) - print('\n') - print('#### Offline Channels in {} ####'.format(listname)) - print('\n'.join(status[listname]['offline'])) - else: - return status - - def cleanup_channel_list(self,listname): - status = self.check_channel_list(printResult=False) - self._default_channel_list[listname] = \ - list(set(self._default_channel_list[listname]).difference(set(status[listname]['offline']))) - print('#### Temporarily removed Offline Channels in {} ####'.format(listname)) - print('\n'.join(status[listname]['offline'])) - print('NB: The channels will be back after restart if they originate from a config file.') - - - - def h5(self,fina=None,channel_list=None,N_pulses=None,default_path=True,queue_size=100): -# if default_path: -# fina = self._default_file_path%fina - - if os.path.isfile(fina): - print('!!! File %s already exists, would you like to delete it?'%fina) - if input('(y/n)')=='y': - print('Deleting %s .'%fina) - os.remove(fina) - else: - return - if not channel_list: - print('No channels specified, using default list \'%s\' instead.'%list(self._default_channel_list.keys())[0]) - channel_list = self._default_channel_list[list(self._default_channel_list.keys())[0]] - - source = dispatcher.request_stream(channel_list) - mode = zmq.SUB - receive(source, fina, queue_size=queue_size, mode=mode, n_messages=N_pulses) - - def db(self,channel_list=None,start_time_delta=dict(),end_time_delta=dict(),default_path=True): - if not channel_list: - print('No channels specified, using default list \'%s\' instead.'%list(self._default_channel_list.keys())[0]) - channel_list = self._default_channel_list[list(self._default_channel_list.keys())[0]] - now = datetime.datetime.now() - end = now-datetime.timedelta(**end_time_delta) - start = end-datetime.timedelta(**start_time_delta) - return dapi.get_data(channels=channel_list, start=start, end=end) - - def h5_db(self,fina,channel_list=None,start_time_delta=dict(),end_time_delta=dict(),default_path=True): - data = self.db(channel_list=None,start_time_delta=start_time_delta,end_time_delta=end_time_delta,default_path=True) - if default_path: - fina = self._default_file_path%fina - - if os.path.isfile(fina): - print('!!! File %s already exists, would you like to delete it?'%fina) - if input('(y/n)')=='y': - print('Deleting %s .'%fina) - os.remove(fina) - else: - return - - data.to_hdf(fina,"/data") - - - def acquire(self,file_name=None,Npulses=100): - file_name += '.h5' - def acquire(): - self.h5(fina=file_name,N_pulses=Npulses) - return Acquisition(acquire=acquire, hold=False) - - def wait_done(self): - self.check_running() - self.check_still_running() - - - diff --git a/slic/daq/bscounter.py b/slic/daq/bscounter.py new file mode 100644 index 000000000..4d5b362fb --- /dev/null +++ b/slic/daq/bscounter.py @@ -0,0 +1,159 @@ +from datetime import datetime, timedelta +import os +import zmq + +import data_api as dapi +from bsread.h5 import receive +from bsread.avail import dispatcher + +from .acquisition import Acquisition +from .basecounter import BaseCounter +from .ask_yes_no import ask_yes_No + + + +def format_header(msg): + msg += ":" + line = "-" * len(msg) + msg += "\n" + line + return msg + + + +def can_create_file(filename): + if not os.path.isfile(filename): + return True + + delete = ask_yes_No("File \"{}\" exists already. Would you like to delete it".format(filename)) + if delete: + print("Deleting \"{}\".".format(filename)) + os.remove(filename) + return True + + return False + + + +def fix_filename(filename): + if filename: + if not filename.endswith(".h5"): + filename += ".h5" + else: + filename = "/dev/null" + return filename + + + + + +class BSCounter(BaseCounter): + + def __init__(self, default_channels=None, default_path="."): + self.default_channels = default_channels + self.default_path = default_path + + + def cleanupdefault_channels(self): + status = self.channels_status() + online = status["online"] + offline = status["offline"] + + if offline: + self.default_channels = online + print("Removed offline channels from default channel list:") + print("\n".join(offline)) + print("(Note: The channels have not been deleted from the respective config file.)") + + + def check_channels(self, channels=None, print_online=False, print_offline=True): + status = self.channels_status(channels) + + if print_online: + online = status["online"] + print(format_header("Online Channels")) + print("\n".join(online)) + print() + + if print_offline: + offline = status["offline"] + print(format_header("Offline Channels")) + print("\n".join(offline)) + print() + + + def channels_status(self, channels=None): + channels = self.default_channels if channels is None else channels + channels = set(channels) + + available = self.avail() + + online = channels.intersection(available) + offline = channels.difference(available) + + online = sorted(online) + offline = sorted(offline) + + status = dict(online=online, offline=offline) + return status + + + def avail(self): + available_channels = dispatcher.get_current_channels() + available_channels_names = set(i['name'] for i in available_channels) + return available_channels_names + + + def acquire(self, filename=None, n_pulses=100, **kwargs): + acq = lambda: self.h5(filename=filename, n_pulses=n_pulses, **kwargs) + return Acquisition(acq, hold=False) + + + def h5(self, filename=None, channels=None, n_pulses=None, use_default_path=True, queue_size=100, mode=zmq.SUB): + if filename and use_default_path: + filename = os.path.join(self.default_path, filename) + + filename = fix_filename(filename) + + if not can_create_file(filename): + return + + if not channels: + print("No channels specified, using default channel list.") + channels = self.default_channels + + source = dispatcher.request_stream(channels) + receive(source, filename, queue_size=queue_size, mode=mode, n_messages=n_pulses) + + + def h5_db(self, filename=None, use_default_path=True, **kwargs): + if filename and use_default_path: + filename = os.path.join(self.default_path, filename) + + filename = fix_filename(filename) + + if not can_create_file(filename): + return + + data = self.db(**kwargs) + data.to_hdf(filename, "/data") + + + def db(self, channels=None, start_time_delta=None, end_time_delta=None): + if not channels: + print("No channels specified, using default channel list.") + channels = self.default_channels + + start_time_delta = start_time_delta if start_time_delta is not None else dict(seconds=0) + end_time_delta = end_time_delta if end_time_delta is not None else dict(seconds=1) + + start_time_delta = timedelta(**start_time_delta) + end_time_delta = timedelta(**end_time_delta) + + now = datetime.now() + end = now - end_time_delta + start = end - start_time_delta + + return dapi.get_data(channels=channels, start=start, end=end) + + + diff --git a/slic/daq/dia_new.py b/slic/daq/dia_new.py index fd8c5d230..0b86e5612 100644 --- a/slic/daq/dia_new.py +++ b/slic/daq/dia_new.py @@ -1,6 +1,6 @@ from time import sleep from datetime import datetime -from .utilities import Acquisition +from .acquisition import Acquisition from detector_integration_api import DetectorIntegrationClient import os from pathlib import Path diff --git a/slic/daq/dia.py b/slic/daq/diacounter.py similarity index 99% rename from slic/daq/dia.py rename to slic/daq/diacounter.py index 2060f9339..d6a38ad3d 100644 --- a/slic/daq/dia.py +++ b/slic/daq/diacounter.py @@ -2,7 +2,7 @@ import os import datetime from time import sleep from detector_integration_api import DetectorIntegrationClient -from .utilities import Acquisition +from .acquisition import Acquisition from .basecounter import BaseCounter diff --git a/slic/daq/epics.py b/slic/daq/epics.py index 8a3e9f18b..757fd4898 100644 --- a/slic/daq/epics.py +++ b/slic/daq/epics.py @@ -7,7 +7,7 @@ import datetime from threading import Thread from time import sleep from pathlib import Path -from .utilities import Acquisition +from .acquisition import Acquisition class Epics: diff --git a/slic/devices/general/detectors.py b/slic/devices/general/detectors.py index 1f4f4ff09..b402fea7d 100755 --- a/slic/devices/general/detectors.py +++ b/slic/devices/general/detectors.py @@ -12,7 +12,7 @@ from time import sleep from threading import Thread from datetime import datetime -from slic.daq.utilities import Acquisition +from slic.daq.acquisition import Acquisition try: import sys, os diff --git a/slic/utils/channels.py b/slic/utils/channels.py index fbf5dd56b..6bd4460d5 100644 --- a/slic/utils/channels.py +++ b/slic/utils/channels.py @@ -25,9 +25,11 @@ def parseChannelListFile(fname): return sorted(out) -class Channels(dict): +class Channels(list): def __init__(self, fname): - self["alvra_channel_list"] = parseChannelListFile(fname) + chs = parseChannelListFile(fname) + self.extend(chs) +