def convert_awg_pin_to_dio_board(awgPinNumber, configFN = "awg_dio_pin_mapping"): """Computes the corresponding board and channel number on the DIO breakout for the corresponding input awgPinNumber given the mapping specificed in the configuration file provided. """ #First load the mapping config file config = load_config(configFN) #Get base pin index for board 0,1 and 2,3 baseB01 = config["B01_base"] baseB23 = config["B23_base"] currBase = 0 board = 0 #Now checking to see which board corresponds with our AWG pin index if awgPinNumber >= baseB01 and awgPinNumber < baseB01 + 8: #Corresponds with boards 0, 1 board = 0 currBase = baseB01 elif awgPinNumber >= baseB23 and awgPinNumber < baseB23 + 8: #Corresponds with boards 2, 3 board = 2 currBase = baseB23 else: #Means pin number is not covered by range of those currently connected to the dio raise Exception("Invalid pin number") #Finally figure out the channel number within the pair of boards channel = awgPinNumber-currBase if (channel >= 4): #If we are beyond channel 4, we are actually on the 2nd board in the pair so update accordingly channel = channel - 4 board = board + 1 return board, channel
def save_gui(self, config_filename, folder_root=None, logger=None, scalars=[], labels=[]): """ Saves the current GUI state into a config file as a dictionary :param config_filename: (str) name of configuration file to save. Can be an existing config file with other configuration parameters :folder_root: (str) Name of folder where the config files are stored. If None, use pylabnet/config :logger: (LogClient) instance of LogClient (or LogHandler) :param scalars: [str] list of scalar labels to save :param labels: [str] list of label labels to save """ # Generate GUI dictionary gui_scalars, gui_labels = dict(), dict() for scalar in scalars: gui_scalars[scalar] = self.get_scalar(scalar) for label in labels: gui_labels[label] = self.get_text(label) data = dict(gui_scalars=gui_scalars, gui_labels=gui_labels) # Append to the configuration file if it exists, otherwise create a new one filepath = get_config_filepath(config_filename, folder_root) if os.path.exists(filepath): old_data = load_config(config_filename, folder_root, logger) else: old_data = dict() data = dict(**old_data, **data) with open(filepath, 'w') as outfile: json.dump(data, outfile, indent=4) logger.info(f'Saved GUI data to {filepath}')
def slack(self, msg_str): channel = load_config('slackbot')['logger_channel'] slackbot = PylabnetSlackBot() slackbot.subscribe_channel([channel]) message = f"Log from `{self._module_tag}`: `{msg_str}`" slackbot.broadcast_to_channels(message)
def check_aom_up(self): """Checks if a given HDAWG DIO bit attached to an AOM switch is high""" dio_config = load_config('dio_assignment_global') DIO_bit = dio_config[self.config["aom_staticline"]] current_config = self._hd.geti('dios/0/output') DIO_bit_bitshifted = (0b1 << DIO_bit) # for bit 3: 000...0001000 DIO_bitup = current_config & DIO_bit_bitshifted if DIO_bitup > 0: return True else: return False
def setup(self): ''' Setup a ZI HDAWG driver module to be used as a staticline toggle. :DIO_bit: Which bit to toggle, in decimal notation. ''' # Retrieve arguments from keyword argument dictionary. assignment_dict = load_config('dio_assignment_global') DIO_bit = assignment_dict[self.config['bit_name']] # Drive 8-bit bus containing DIO_bit to be toggled. # Note that the buses are enabled by using the decimal equivalent # of a binary number indicating which bus is driven: # 1101 = 11 corresponds to driving bus 1, 2, and 4. if DIO_bit in range(8): toggle_bit = 1 # 1000 elif DIO_bit in range(8, 16): toggle_bit = 2 # 0100 elif DIO_bit in range(16, 24): toggle_bit = 4 # 0010 elif DIO_bit in range(24, 32): toggle_bit = 8 # 0001 else: self.log.error( f"DIO_bit {DIO_bit} invalid, must be in range 0-31.") self.DIO_bit = DIO_bit self.log.info( f"DIO_bit {DIO_bit} successfully assigned to staticline {self.name}." ) # Read in current configuration of DIO-bus. current_config = self.hardware_client.geti('dios/0/drive') # Set new configuration by using the bitwise OR. new_config = current_config | toggle_bit self.hardware_client.seti('dios/0/drive', new_config) # Register up/down function. self.up = lambda: self._HDAWG_toggle(1) self.down = lambda: self._HDAWG_toggle(0) # Set correct mode to manual self.hardware_client.seti('dios/0/mode', 0)
def launch(**kwargs): """ Connects to NI-daqMX card and launches server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ # Instantiate driver ni_daqmx_logger = kwargs['logger'] try: ni_driver = nidaqmx_card.Driver( device_name=kwargs['device_id'], logger=ni_daqmx_logger ) except AttributeError: try: config = load_config(kwargs['config']) ni_driver = nidaqmx_card.Driver( device_name=config['device'], logger=ni_daqmx_logger ) except AttributeError: ni_daqmx_logger.error('Please provide valid config file') raise except OSError: ni_daqmx_logger.error(f'Did not find NI daqMX name {config["device"]}') raise except KeyError: ni_daqmx_logger.error('No device name provided. ' 'Please make sure proper config file is provided') raise # Instantiate server ni_daqmx_service = Service() ni_daqmx_service.assign_module(module=ni_driver) ni_daqmx_service.assign_logger(logger=ni_daqmx_logger) ni_daqmx_server = GenericServer( service=ni_daqmx_service, host=get_ip(), port=kwargs['port'] ) ni_daqmx_server.start()
def load_gui(self, config_filename, folder_root=None, logger=None): """ Loads and applies GUI settings from a config file :param config_filename: (str) name of configuration file to save. Can be an existing config file with other configuration parameters :folder_root: (str) Name of folder where the config files are stored. If None, use pylabnet/config :logger: (LogClient) instance of LogClient (or LogHandler) """ data = load_config(config_filename, folder_root, logger) if 'gui_scalars' in data: for scalar, value in data['gui_scalars'].items(): self.activate_scalar(scalar) self.set_scalar(value, scalar) self.deactivate_scalar(scalar) if 'gui_labels' in data: for label, text in data['gui_labels'].items(): self.set_label(text, label) logger.info( f'Loaded GUI values from {get_config_filepath(config_filename, folder_root)}' )
def __init__(self, config=None): QMainWindow.__init__(self) self.config = load_config(config) self.N_staticlines = len(self.config) #N_staticlines self.labels = [] self.all_widgets = dict() self.setStyleSheet("background-color: black;") #self.setMinimumSize(QSize(640, 480)) self.setWindowTitle("Staticline window") self.centralWidget = QWidget(self) self.setCentralWidget(self.centralWidget) self.gridLayout = QGridLayout(self) self.centralWidget.setLayout(self.gridLayout) self.unpack_config_file()
def setup(self): assignment_dict = load_config('dio_assignment_global') DIO_bit = assignment_dict[self.config['bit_name']] self.board, self.channel = convert_awg_pin_to_dio_board(DIO_bit) self.isHighVoltage = self.config['is_high_volt']
def launch(**kwargs): """ Launches the WLM monitor + lock script """ logger, loghost, logport, clients, guis, params = unpack_launcher(**kwargs) hosts = load_config(kwargs['config'], logger=kwargs['logger'])['hosts'] for host in hosts: # Initiate SSH connection hostname = f"\'{host['hostname']}\'" host_ip = host['ip'] logger.info(f"Starting SSH connection to {hostname}@{host_ip}") ssh = paramiko.SSHClient() ssh.load_system_host_keys() try: ssh.connect(host_ip, username=hostname, password=LOCALHOST_PW) except TimeoutError: logger.error(f"Failed to setup SSH connection to {hostname}@{host_ip}.") logger.info(f"Succesfully connected via SSH to {hostname}@{host_ip}.") python_path = host['python_path'] script_path = host['script_path'] venv_path = host['venv_path'] servers = host['servers'] # I fappropriate flag is set, kill all python processes on host machine. # WARNING: Make sure host machine is not running any non-pylabnet processes. if host['kill_all'] == "True": logger.warn(f"Killing all python processes on {hostname}@{host_ip}.") kill_command = "taskkill /F /IM python.exe /T" ssh.exec_command(kill_command) for server in servers: try: disable_raw = server['disable'] if disable_raw == 'False': disable = False else: disable = True except KeyError: disable = False servername = server['servername'] logger.info(f"Trying to connect to {servername} on {hostname}.") # Don't execute any ssh commands if flag is set. if disable: logger.info(f'Connection to {servername} is disabled') continue # Look for optional debug flag try: if server['debug'] == "True": debug = 1 else: debug = 0 except KeyError: debug = 0 # Look for optional config flag try: config = server['config'] except KeyError: config = None server_port = np.random.randint(1, 9999) # Activate virtual env ssh.exec_command(venv_path) cmd = '"{}" "{}" --logip {} --logport {} --serverport {} --server {} --debug {} --config {}'.format( python_path, script_path, loghost, logport, server_port, servername, debug, config ) # Look for device name and ID try: cmd += f" --device_name {server['device_name']} --device_id {server['device_id']}" except KeyError: logger.warn(f'Device name and ID not specified for {servername}') logger.info(f'Executing command on {hostname}:\n{cmd}') ssh.exec_command(cmd) ssh.close()
def start_stop_logging(self, master_log=False): """ Starts or stops logging to file depending on situation :master_log: (bool) If True, this function is called as initial setup function of filesaving for the master launch control. In this case a log path as specified in the config file is chosen. """ # check if there's already an open log file and close it if self.logfile_date_str is not None: self.log_service.stop_latest_logfile() if self.main_window.logfile_status_button.isChecked() or master_log: date_str = datetime.now().strftime("%Y_%m_%d") time_str = datetime.now().strftime("%H_%M_%S") # Actually start logging filename = f'logfile_{time_str}' # Get logging file from json. filepath = None if master_log: try: config_dict = load_config('static_proxy') filepath = config_dict['logger_path'] except: self.main_window.terminal.setText( 'Critical error: ' 'no logger_path found in static_proxy.json') self.main_window.force_update() time.sleep(10) raise # Or from filepath selector. else: filepath = self.main_window.file_viewer.model().filePath( self.main_window.file_viewer.selectionModel().currentIndex( )) try: self.log_service.add_logfile(name=filename, dir_path=filepath) except Exception as error_msg: print( f'Failed to start logging to file {os.path.join(filepath, filename)}.\n{error_msg}' ) self.log_service.logger.info( f'Started logging to file {os.path.join(filepath, filename)}.') # Change button color and text self.main_window.logfile_status_button.setStyleSheet( "background-color: red") self.main_window.logfile_status_button.setText( 'Stop logging to file') self.main_window.logfile_status_indicator.setChecked(True) # Add previous text to logfile if self.main_window.log_previous.isChecked(): self.log_service.logger.info( f'Previous log terminal content: \n{self.main_window.terminal.toPlainText()}' f'\n---------------------------') # Pass current date of logfile for day-chopping purposes self.logfile_date_str = date_str # pass log file name and path to access filesize for chopping purposes self.filenamepath = config_dict[ 'logger_path'] + '\\' + date_str[:4] + '\\' + date_str[ 5:7] + '\\' + date_str[8:] + '\\logfile_' + time_str else: # Change button color and text self.main_window.logfile_status_button.setStyleSheet( "background-color: green") self.main_window.logfile_status_button.setText( 'Start logging to file') self.main_window.logfile_status_indicator.setChecked(False) # Actually stop logging self.log_service.stop_latest_logfile() # Set date string to None so that logfile does not get updated anymore self.logfile_date_str = None
def __init__(self, proxy=False, master=False, staticproxy=False): """ Initializes launch control GUI """ self.operating_system = get_os() self.app = QtWidgets.QApplication(sys.argv) self.app.setWindowIcon( QtGui.QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'devices.ico'))) # Instantiate GUI application if self.operating_system == 'Windows': ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( 'pylabnet') self.main_window = LaunchWindow(self.app, self, gui_template=self.LOGGER_UI) self.main_window.stop_button.clicked.connect(self._kill) if self.operating_system not in ['Linux', 'Windows']: raise UnsupportedOSException try: if sys.argv[1] == '-m' or master: self.master = True else: self.master = False except IndexError: if master: self.master = True else: self.master = False try: if sys.argv[1] == '-p' or proxy: self.proxy = True else: self.proxy = False except IndexError: if proxy: self.proxy = True else: self.proxy = False try: if sys.argv[1] == '-sp' or staticproxy: self.staticproxy = True else: self.staticproxy = False except IndexError: if staticproxy: self.staticproxy = True else: self.staticproxy = False self.host = get_ip() self.update_index = 0 # Retrieve static port info. if self.master: try: static_proxy_dict = load_config('static_proxy') except: print('No config found named static_proxy.json') time.sleep(10) raise self.log_port = static_proxy_dict['master_log_port'] self.gui_port = static_proxy_dict['master_gui_port'] hide_console() elif self.proxy: popup = ParameterPopup(host=str, log_port=str, gui_port=str) self.waiting_flag = True popup.parameters.connect(self.fill_parameters) while self.waiting_flag: self.app.processEvents() elif self.staticproxy: try: static_proxy_dict = load_config('static_proxy') except: print('No config found named static_proxy.json') time.sleep(10) raise self.host = static_proxy_dict['master_ip'] self.log_port = static_proxy_dict['master_log_port'] self.gui_port = static_proxy_dict['master_gui_port'] self.proxy = True hide_console() else: self.log_port = self.LOG_PORT self.gui_port = self.GUI_PORT self.log_service = None self.log_server = None self.gui_client = None self.gui_logger = None self.gui_service = None self.gui_server = None self.client_list = {} self.port_list = {} self.script_list = {} self.client_data = {} self.disconnection = False self.debug = False self.debug_level = None self.autoscroll_off = False # date string is None if not logging to file, and gives today's date if logging to file. # For day-chopping purposes self.logfile_date_str = None self.filenamepath = None self.MAX_LOG_FILE_SIZE = 5000000 # 5MB self.last_seen_buffer = "" # setting selection mode for server list to multi-select self.main_window.client_list.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection)
def start_gui_server(self): """ Starts the launch controller GUI server, or connects to the server and updates GUI""" module_str = '' if self.proxy: module_str = '_proxy' # connect to the logger try: self.gui_logger = LogClient(host=self.host, port=self.log_port, module_tag=self.GUI_NAME + module_str, ui=self.LOGGER_UI) except ConnectionRefusedError: self.main_window.terminal.setText( 'Failed to connect to master. Shutting down') self.main_window.force_update() time.sleep(10) raise # if lab name is specified: add to gui_logger try: lab_name_dict = load_config("lab_name") lab_name = lab_name_dict['lab_name'] except: lab_name = 'NO_LAB' self.gui_logger.update_data(data=dict(lab_name=lab_name)) # Instantiate GUI server and update GUI with port details self.gui_service = Service() self.gui_service.assign_module(module=self.main_window) self.gui_service.assign_logger(logger=self.gui_logger) if self.gui_port is None: self.gui_server, self.gui_port = create_server( self.gui_service, logger=self.gui_logger, host=get_ip()) my_port = self.gui_port self.main_window.gui_label.setText(f'GUI Port: {my_port}') elif self.proxy: self.gui_server, my_port = create_server(self.gui_service, logger=self.gui_logger, host=get_ip()) self.main_window.gui_label.setText( f'Master (Local) GUI Port: {self.gui_port} ({my_port})') else: try: self.gui_server = GenericServer(service=self.gui_service, host=get_ip(), port=self.gui_port) my_port = self.gui_port self.main_window.gui_label.setText(f'GUI Port: {my_port}') except ConnectionRefusedError: self.gui_logger.error( f'Failed to instantiate GUI Server at port {self.gui_port}' ) raise self.gui_server.start() self.gui_logger.update_data(data=dict(port=my_port)) if self.proxy: # Connect to the GUI server try: self.gui_client = Client(host=self.host, port=self.gui_port) except ConnectionRefusedError: self.gui_logger.error( f'Failed to connect to GUI Server with IP address: {self.host}, ' f'Port: {self:gui_port}') raise # Now update GUI to mirror clients self._copy_master() # Get the latest update index buffer = self.gui_client.get_text('buffer') try: self.update_index = int( re.findall(r'\d+', re.findall(r'!~\d+~!', buffer)[-1])[0]) except IndexError: self.update_index = 0 self.gui_service = Service() self.gui_service.assign_module(module=self.main_window) self.gui_service.assign_logger(logger=self.gui_logger) else: # Update internal attributes and add to list of log clients self.client_list[self.GUI_NAME] = QtWidgets.QListWidgetItem( self.GUI_NAME) self.port_list[self.GUI_NAME] = [ port for port in self.log_server._server.clients ][0] self.main_window.client_list.addItem( self.client_list[self.GUI_NAME]) self.client_list[self.GUI_NAME].setToolTip( dict_to_str(self.log_service.client_data[self.GUI_NAME])) self.client_data[self.GUI_NAME + module_str] = self.log_service.client_data[ self.GUI_NAME]