def launch(**kwargs): """ Connects to Tektronix TDS2004C oscilloscope 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 tektronix_logger = kwargs['logger'] tektronix_driver = tektronix_tds2004C.Driver( gpib_address=kwargs['device_id'], logger=tektronix_logger ) # Instantiate server tektronix_service = Service() tektronix_service.assign_module(module=tektronix_driver) tektronix_service.assign_logger(logger=tektronix_logger) tektronix_server = GenericServer( service=tektronix_service, host=get_ip(), port=kwargs['port'] ) tektronix_server.start()
def launch(**kwargs): """ Connects to TP Link HS103 Smart Plug :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :device_id: (int) Location of Smart plug (e.g. Powermeter Front Smart Plug) """ logger=kwargs['logger'] config = load_device_config('tp_link_hs103', kwargs['config']) smart_plug = Driver( logger=kwargs['logger'], channels=config['channels'] ) # Instantiate Server smart_plug_service = Service() smart_plug_service.assign_module(module=smart_plug) smart_plug_service.assign_logger(logger=kwargs['logger']) smart_plug_server = GenericServer( service=smart_plug_service, host=get_ip(), port=kwargs['port'] ) smart_plug_server.start()
def launch(**kwargs): """ Connects to ANC3000 and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ device_config = load_device_config('anc300', kwargs['config'], logger=kwargs['logger']) telnet_config = device_config['telnet_config'] anc300 = ANC300(host=telnet_config['host'], port=telnet_config['port'], query_delay=device_config['query_delay'], passwd=telnet_config['passwd'], limits=device_config['limits'], logger=kwargs['logger']) anc300_service = Service() anc300_service.assign_module(module=anc300) anc300_service.assign_logger(logger=kwargs['logger']) anc300_server = GenericServer(service=anc300_service, host=get_ip(), port=kwargs['port']) anc300_server.start()
def launch(**kwargs): """ Connects to a NI DAQ as staticline :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ staticline_logger = kwargs['logger'] # Instantiate HDAWG driver. hd = zi_hdawg.Driver(dev_id, logger=staticline_logger) aom = staticline.Driver( name='AOM', logger=staticline_logger, hardware_module=hd, DIO_bit=30, ) # Instantiate Server # Staticline server staticline_service = Service() staticline_service.assign_module(module=aom) staticline_service.assign_logger(logger=staticline_logger) staticline_service_server = GenericServer(service=staticline_service, host=get_ip(), port=kwargs['port']) staticline_service_server.start()
def launch(**kwargs): """ Connects to NI-daqMX card and launches server Identical to nidaqmx, except uses "device_ai" in the config file as the device name :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ # Instantiate driver logger = kwargs['logger'] config = kwargs['config'] config = load_device_config('fw102c', config, logger=kwargs['logger']) device_name = config['device_name'] port_name = config['device_id'] filters = config['filters'] filters = {f'{i+1}': f'{filters[i]} OD' for i in range(len(filters))} filterwheel = FW102CFilterWheel(port_name=port_name, device_name=device_name, filters=filters, logger=logger) filterwheel_service = Service() filterwheel_service.assign_module(module=filterwheel) filterwheel_service.assign_logger(logger=logger) filterwheel_service_server = GenericServer(service=filterwheel_service, host=get_ip(), port=kwargs['port']) filterwheel_service_server.start()
def launch(**kwargs): """ Connects to NI-daqMX card and launches server Identical to nidaqmx, except uses "device_ai" in the config file as the device name :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ # Instantiate driver logger = kwargs['logger'] config = kwargs['config'] config = load_device_config('agc_100', config, logger=kwargs['logger']) port_name = config['device_id'] agc = AGC_100(port=port_name, logger=logger) agc_service = Service() agc_service.assign_module(module=agc) agc_service.assign_logger(logger=logger) agc__server = GenericServer( service=agc_service, host=get_ip(), port=kwargs['port'] ) agc__server.start()
def launch(**kwargs): """ Connects to a NI DAQ as staticline :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ staticline_logger = kwargs['logger'] daq = nidaqmx.Driver(device_name=NI_DEVICE_NAME, logger=staticline_logger) test_staticline = staticline.Driver( name='Green Imaging Laser', logger=kwargs['logger'], hardware_module=daq, ao_output='ao2', down_voltage=0, up_voltage=3.3, ) # Instantiate Server # Staticline server staticline_service = Service() staticline_service.assign_module(module=test_staticline) staticline_service.assign_logger(logger=staticline_logger) staticline_service_server = GenericServer(service=staticline_service, host=get_ip(), port=kwargs['port']) staticline_service_server.start()
def launch(**kwargs): """ Connects to SI TT and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ if not IMPORT_STATUS: msg_str = 'Please make sure Swabian Instruments drivers are installed on this machine.' raise ModuleNotFoundError(msg_str) TT.setTimeTaggerChannelNumberScheme(TT.TT_CHANNEL_NUMBER_SCHEME_ONE) # Connect to the device, otherwise instantiate virtual connection try: tagger = TT.createTimeTagger() except RuntimeError: kwargs['logger'].warn('Failed to connect to Swabian Instruments Time Tagger.' ' Instantiating virtual device instead') tagger = TT.createTimeTaggerVirtual() try: config = kwargs['config'] config = load_device_config('si_tt', config, logger=kwargs['logger']) except: config = None # if config is None: # try: # config = load_device_config('si_tt', logger=kwargs['logger']) # except: # config = {} for channel, trig_level in config['triggers'].items(): tagger.setTriggerLevel(int(channel), float(trig_level)) cnt_trace_wrap = Wrap( tagger=tagger, logger=kwargs['logger'] ) # Instantiate Server cnt_trace_service = Service() cnt_trace_service.assign_module(module=cnt_trace_wrap) cnt_trace_service.assign_logger(logger=kwargs['logger']) cnt_trace_server = GenericServer( service=cnt_trace_service, host=get_ip(), port=kwargs['port'] ) cnt_trace_server.start()
def launch(**kwargs): """ Connects to MCS2 and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ mcs2 = MCS2(logger=kwargs['logger']) mcs2_service = Service() mcs2_service.assign_module(module=mcs2) mcs2_service.assign_logger(logger=kwargs['logger']) mcs2_server = GenericServer(service=mcs2_service, host=get_ip(), port=kwargs['port']) mcs2_server.start()
def launch(**kwargs): """ Launches a dummy hardware driver and instantiates server """ log = kwargs['logger'] log.info(f'Launching with config {kwargs["config"]}') config = load_device_config( os.path.basename(__file__)[:-3], kwargs['config'], log) dum = Dummy() log.info(f'Created dummy object with configuration parameters {config}') dum_service = ServiceBase() dum_service.assign_module(module=dum) dum_service.assign_logger(logger=log) dum_server = GenericServer(service=dum_service, host=get_ip(), port=kwargs['port']) dum_server.start()
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 launch(**kwargs): # Instantiate driver zi_logger = kwargs['logger'] device_id = load_device_config('zi_hdawg', kwargs['config'], logger=kwargs['logger'])['device_id'] hd = Driver(device_id, zi_logger) # Instantiate server hd_service = Service() hd_service.assign_module(module=hd) hd_service.assign_logger(logger=zi_logger) hd_server = GenericServer(service=hd_service, host=get_ip(), port=kwargs['port']) hd_server.start()
def start_logger(self): """ Starts the log server """ self.log_service = LogService() if self.LOG_PORT is None and not self.master: self.log_server, self.log_port = create_server(self.log_service, host=get_ip()) else: try: self.log_server = GenericServer(service=self.log_service, host=get_ip(), port=self.log_port) except ConnectionRefusedError: print( f'Failed to insantiate Log Server at port {self.LOG_PORT}') raise self.log_server.start() self.log_service.logger.info('log service succesfully started')
def launch(**kwargs): """ Connects to HWC T2220 (microwave source) and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Hittite server """ config = kwargs['config'] config = load_device_config('HMC_T2220', config, logger=kwargs['logger']) mw_source = ht.Driver(logger=kwargs['logger'], gpib_address=config['device_id']) # Instantiate Server mw_service = Service() mw_service.assign_module(module=mw_source) mw_service.assign_logger(logger=kwargs['logger']) mw_server = GenericServer(service=mw_service, host=get_ip(), port=kwargs['port']) mw_server.start()
def launch(**kwargs): """ Connects to spectrum analyzer and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the spectrum analyzer server """ config = kwargs['config'] config = load_device_config('CLD101x', config, logger=kwargs['logger']) il = laser.Driver(gpib_address=config['device_id'], logger=kwargs['logger']) # Instantiate Server il_service = Service() il_service.assign_module(module=il) il_service.assign_logger(logger=kwargs['logger']) il_server = GenericServer(service=il_service, host=socket.gethostbyname_ex( socket.gethostname())[2][0], port=kwargs['port']) il_server.start()
def launch(**kwargs): """ Connects to HF WS7 Wavemeter 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 Logger wavemeter_logger = kwargs['logger'] # Instantiate Wavemeter object hf_wlm = Driver(logger=wavemeter_logger) # Instantiate Server wavemeter_service = Service() wavemeter_service.assign_module(module=hf_wlm) wavemeter_service.assign_logger(logger=wavemeter_logger) wavemeter_server = GenericServer(service=wavemeter_service, host=get_ip(), port=kwargs['port']) wavemeter_server.start()
def launch(**kwargs): """ Connects to MPC320 instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ settings = load_device_config('thorlabs_mpc320', kwargs['config'], logger=kwargs['logger']) dev_num = settings['device_id'] pol_paddle = Driver(device_num=int(dev_num), logger=kwargs['logger']) pol_paddle_service = Service() pol_paddle_service.assign_module(module=pol_paddle) pol_paddle_service.assign_logger(logger=kwargs['logger']) pol_paddle_service_server = GenericServer(service=pol_paddle_service, host=get_ip(), port=kwargs['port']) pol_paddle_service_server.start()
def launch(**kwargs): """ Connects to PM320E and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the Cnt Monitor server """ try: settings = load_device_config('thorlabs_pm320e', kwargs['config'], logger=kwargs['logger']) except KeyError: kwargs['logger'].warn('No config file was provided') try: pm = Driver( logger=kwargs['logger'], gpib_address=settings['device_id'], ) # Handle error of wrong GPIB address by allowing user to select # NOTE: this will fail if used from the main script launcher, since script client # will automatically try to connect (even though server isn't launched) # # TLDR: if you want to use launch-control, please fill in GPIB variable with # the correct resource string except: kwargs['logger'].error('Please check GPIB address, could not connect') pm_service = Service() pm_service.assign_module(module=pm) pm_service.assign_logger(logger=kwargs['logger']) pm_server = GenericServer(service=pm_service, host=get_ip(), port=kwargs['port']) pm_server.start()
def launch(**kwargs): """ Connects to DIO breakout and instantiates server :param kwargs: (dict) containing relevant kwargs :logger: instance of LogClient for logging purposes :port: (int) port number for the DIO breakout server :config: (str) name of config file to us """ device_config = load_device_config('dio_breakout', kwargs['config'], logger=kwargs['logger']) # Try to load settings if 'resource_name' in device_config: addr = device_config['resource_name'] else: addr = device_config['device_id'] # Try to connect try: dio = Driver(address=addr, logger=kwargs['logger']) # If it fails, prompt the user to enter GPIB address from resource list except VisaIOError: kwargs['logger'].error( f'Failed to connect to device at address {addr}') raise # Instantiate Service and server dio_service = Service() dio_service.assign_module(module=dio) dio_service.assign_logger(logger=kwargs['logger']) dio_server = GenericServer(service=dio_service, host=get_ip(), port=kwargs['port']) dio_server.start()
def launch(**kwargs): """ Connects to toptica laser and launches server :param kwargs: (dict) containing relevant kwargs """ # Instantiate driver toptica_logger = kwargs['logger'] config = load_device_config('toptica_dlc_pro', kwargs['config'], toptica_logger) dlc = DLC_Pro(host=config['host'], port=int(config['port']), logger=toptica_logger) # Instantiate server dlc_service = Service() dlc_service.assign_module(module=dlc) dlc_service.assign_logger(logger=toptica_logger) dlc_server = GenericServer(service=dlc_service, host=get_ip(), port=kwargs['port']) dlc_server.start()
def create_server(service, logger=None, host='localhost'): """ Attempts to create a server with randomly chosen port numbers :param service: service from which to launch a server :param logger: instance of LogClient for logging :param host: (optinal) IP address of host :return: (tuple) instance of server created, port number (int) created on """ timeout = 0 while timeout < 1000: try: port = np.random.randint(1024, 49151) server = GenericServer(host=host, port=port, service=service) timeout = 9999 except ConnectionRefusedError: msg_str = f'Failed to create update server with port {port}' if logger is None: print(msg_str) else: logger.warn(f'Failed to create update server with port {port}') timeout += 1 return server, port
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]
class Controller: """ Class for log system controller """ LOGGER_UI = 'logger_remote' GUI_NAME = 'logger_GUI' # When kept as None, random port numbers will be used # use these values to override and set manual port numbers if desired LOG_PORT = None GUI_PORT = 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 fill_parameters(self, params): """ Called when parameters have been entered into a popup """ self.host = params['host'] self.log_port = params['log_port'] self.gui_port = params['gui_port'] self.waiting_flag = False 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] def update_terminal(self, text): """ Updates terminal output on GUI """ self.main_window.terminal.append(text) if not self.autoscroll_off: try: self.main_window.terminal.moveCursor(QtGui.QTextCursor.End) except TypeError: pass # Update buffer terminal buffer_str = f'!~{self.update_index}~!{text}' self.main_window.buffer_terminal.append(buffer_str) self.update_index += 1 def check_disconnection(self, text): """ Checks if a client has disconnected and raises a flag if so""" if 'Client disconnected' in text or self.disconnection: self.disconnection = True def disconnect(self): """ Handles case where client has disconnected """ to_del = [ client for (client, port) in self.port_list.items() if port not in self.log_server._server.clients ] for client in to_del: print('[INFO] {} disconnected at {}'.format( client, time.strftime("%Y-%m-%d, %H:%M:%S", time.gmtime()))) self.main_window.client_list.takeItem( self.main_window.client_list.row(self.client_list[client])) del self.client_list[client] del self.port_list[client] del self.log_service.client_data[client] del self.client_data[client] self.disconnection = False def update_connection(self): """ Checks if new/updated connections have been made and updates accordingly""" # Figure out ports/clients to add port_to_add = [ port for port in self.log_server._server.clients if port not in self.port_list.values() ] client_to_add = [ client for client in self.log_service.client_data if client not in self.client_list ] # Add client and update relevant directories + GUI if len(client_to_add) > 0: client = client_to_add[0] self.client_list[client] = QtWidgets.QListWidgetItem(client) self.main_window.client_list.addItem(self.client_list[client]) try: self.main_window.client_list.moveCursor(QtGui.QTextCursor.End) except TypeError: pass self.client_list[client].setToolTip( dict_to_str(self.log_service.client_data[client])) if len(port_to_add) > 0: self.port_list[client] = port_to_add[0] self.client_data[client] = self.log_service.client_data[client] # Check for updates to client data while len(self.log_service.data_updated) > 0: self.client_list[self.log_service.data_updated[0]].setToolTip( dict_to_str(self.log_service.client_data[ self.log_service.data_updated[0]])) del self.log_service.data_updated[0] def start_logger(self): """ Starts the log server """ self.log_service = LogService() if self.LOG_PORT is None and not self.master: self.log_server, self.log_port = create_server(self.log_service, host=get_ip()) else: try: self.log_server = GenericServer(service=self.log_service, host=get_ip(), port=self.log_port) except ConnectionRefusedError: print( f'Failed to insantiate Log Server at port {self.LOG_PORT}') raise self.log_server.start() self.log_service.logger.info('log service succesfully started') def initialize_gui(self): """ Initializes basic GUI display """ ip_str, ip_str_2, log_str = '', '', '' if self.master: self.main_window.setWindowTitle('Launch Control (Master)') if self.proxy: if self.staticproxy: self.main_window.setWindowTitle('Launch Control (Staticproxy)') else: self.main_window.setWindowTitle('Launch Control (Proxy)') ip_str = 'Master (Local) ' ip_str_2 = f' ({get_ip()})' log_str = 'Master' self.main_window.ip_label.setText(f'{ip_str}IP Address: {self.host}' + ip_str_2) self.main_window.logger_label.setText( f'{log_str} Logger Port: {self.log_port}') if self.proxy: self.main_window.terminal.setText( 'Connected to master Log Server. \n') self.main_window.terminal.setText( 'Log messages will be displayed below \n') self.main_window.buffer_terminal.document().setMaximumBlockCount(1000) # Assign widgets for remote access self.main_window.assign_container('client_list', 'clients') self.main_window.assign_label('buffer_terminal', 'buffer') self.main_window.assign_event_button('debug_radio_button', 'debug') # Hide some buttons self.main_window.file_viewer.setHidden(True) self.main_window.logfile_status_button.setHidden(True) self.main_window.debug_label.setHidden(True) self.main_window.debug_comboBox.setHidden(True) self.main_window.logfile_status_button.setHidden(True) self.main_window.log_previous.setHidden(True) self.main_window.logfile_status_indicator.setEnabled(False) # Configure list of scripts to run and clicking actions self._load_scripts() self._configure_clicks() self._configure_client_search() self._configure_lab_name_select() self._configure_debug() self._configure_debug_combo_select() self._configure_logfile() self._configure_logging() self._configure_autoscroll_off() self.main_window.force_update() def update_proxy(self, new_msg): """ Updates the proxy with new content using the buffer terminal continuously""" # Remove the !~ bookmark from the message self.main_window.terminal.append(re.sub(r'!~\d+~!', '', new_msg)) if not self.autoscroll_off: try: self.main_window.terminal.moveCursor(QtGui.QTextCursor.End) except TypeError: pass # New update index is the last !~ index found in the message indices_found = re.findall(r'!~\d+~!', new_msg) if len(indices_found) != 0: self.update_index = int(re.findall(r'\d+', indices_found[-1])[0]) else: self.update_index = None def kill_servers(self): """ Kills all servers connected to the logger, including the Log GUI and Log Server""" client_data = copy.deepcopy(self.client_data) del client_data['logger_GUI'] for server_data in client_data.values(): if 'port' in server_data: stop_client = ClientBase(host=server_data['ip'], port=server_data['port']) stop_client.close_server() self.gui_server.stop() self.log_server.stop() def update(self, text): """ Runs an update when new text comes through """ self.main_window.configure_widgets() self.main_window.update_widgets() #Check for disconnection events self.check_disconnection(text) # Handle new connections self.update_connection() # Update terminal self.update_terminal(text) # Chop log file if date has changed self.chop_log_file() if self.disconnection: self.disconnect() def chop_log_file(self): """ Checks if date has changed, and chops logfile accordingly""" if self.logfile_date_str is not None: # if date has changed, move to new log file with new date if self.logfile_date_str != datetime.now().strftime("%Y_%m_%d"): self.log_service.logger.info('Starting new logging file!') self.start_stop_logging(master_log=True) if os.stat(self.filenamepath).st_size > self.MAX_LOG_FILE_SIZE: self.log_service.logger.info('Starting new logging file!') self.start_stop_logging(master_log=True) def _configure_client_search(self): self.main_window.client_search.textChanged.connect( self._update_displayed_client_list) def _configure_lab_name_select(self): self.main_window.lab_name_select.currentIndexChanged.connect( self._update_displayed_client_list) def _configure_clicks(self): """ Configures what to do upon clicks """ self.main_window.close_server.pressed.connect(self._stop_server) def _update_displayed_client_list(self): search_str = self.main_window.client_search.text() lab_name = self.main_window.lab_name_select.currentText() clients = self.gui_client.get_container_info('clients') self.main_window.client_list.clear() self.client_list.clear() if lab_name == "ALL LABS": # if ALL LABS is selected, don't filter by lab name if search_str != "": for client, info in clients.items(): self.client_list[client] = QtWidgets.QListWidgetItem( client) # look for clients that have name or ip address containing search string if search_str in client or search_str in self.client_data[ client]['ip']: self.main_window.client_list.addItem( self.client_list[client]) self.client_list[client].setToolTip(info) else: # if search string is empty, don't use it to filter clients for client, info in clients.items(): self.client_list[client] = QtWidgets.QListWidgetItem( client) self.main_window.client_list.addItem( self.client_list[client]) self.client_list[client].setToolTip(info) else: # filter by lab name if search_str != "": for client, info in clients.items(): self.client_list[client] = QtWidgets.QListWidgetItem( client) # look for clients that have name or ip address containing search string, and that have the selected lab name if (search_str in client or search_str in self.client_data[client]['ip'] ) and (self.client_data[client]['lab_name'] == lab_name): self.main_window.client_list.addItem( self.client_list[client]) self.client_list[client].setToolTip(info) else: # if search string is empty, don't use it to filter clients for client, info in clients.items(): self.client_list[client] = QtWidgets.QListWidgetItem( client) # look for clients that have the selected lab name if self.client_data[client]['lab_name'] == lab_name: self.main_window.client_list.addItem( self.client_list[client]) self.client_list[client].setToolTip(info) def _stop_server(self): """ Stops the highlighted server, if applicable """ # Retrieve all selected servers. clients_to_stop = [ client.text() for client in self.main_window.client_list.selectedItems() ] for client_to_stop in clients_to_stop: server_data = self.client_data[client_to_stop] if 'port' in server_data: try: stop_client = ClientBase(host=server_data['ip'], port=server_data['port']) stop_client.close_server() except: self.gui_logger.warn( f'Failed to shutdown server {client_to_stop}' f'on host: {server_data["ip"]}, port: {server_data["port"]}' ) self.gui_logger.info( 'Attempting to remove from LogClients manually') self._close_dangling(client_to_stop) else: self._close_dangling(client_to_stop) def _close_dangling(self, client_to_stop): # Cannot connect to the server and close, must remove. # WARNING: might result in dangling threads try: client_port_to_stop = self.port_list[client_to_stop] port_found = True except KeyError: port_found = False if port_found and client_port_to_stop in self.log_server._server.clients: c = self.port_list[client_to_stop] c.close() closing(c) self.log_server._server.clients.discard(c) self.main_window.client_list.takeItem( self.main_window.client_list.row( self.client_list[client_to_stop])) del self.port_list[client_to_stop] del self.client_list[client_to_stop] del self.log_service.client_data[client_to_stop] del self.client_data[client_to_stop] self.gui_logger.info(f'Client disconnected: {client_to_stop}') # If we can't find the client connected to the server, just remove it else: self.gui_logger.warn( f'No matching client connected to LogServer: {client_to_stop}') try: # The following two member variables don't exist for a proxy. if not self.proxy: self.main_window.client_list.takeItem( self.main_window.client_list.row( self.client_list[client_to_stop])) del self.port_list[client_to_stop] del self.log_service.client_data[client_to_stop] del self.client_list[client_to_stop] del self.client_data[client_to_stop] else: self.gui_client.remove_client_list_entry(client_to_stop) self.gui_logger.info( f'Hard kill of {client_to_stop} successfull.') except: pass def _device_clicked(self, index): """ Configures behavior for device double click :param index: (QModelIndex) index of file clicked on """ # clear the client search bar and display all clients self.main_window.client_search.setText("") #self._search_clients() filepath = self.main_window.devices.model().filePath(index) # Check if it is an actual config file if not os.path.isdir(filepath): # Find the name of the server and device config file device_server = os.path.basename(os.path.dirname(filepath)) device_config = os.path.basename(filepath)[:-5] self.gui_logger.info(f'Launching device {device_server} ' f'with configuration {device_config}') # Initial configurations: All flags down. server_debug_flag = '0' # Raise flags if selected in combobox if self.debug and self.debug_level == "pylabnet_server": server_debug_flag = '1' server_port = np.random.randint(1024, 49151) launch_device_server(server=device_server, dev_config=device_config, log_ip=self.host, log_port=self.log_port, server_port=server_port, debug=server_debug_flag, logger=self.gui_logger) def _script_clicked(self, index): """ Configures behavior for script double click :param index: (QModelIndex) index of file clicked on """ # clear the client search bar and display all clients self.main_window.client_search.setText("") #self._search_clients() filepath = self.main_window.scripts.model().filePath(index) # Check if it is an actual config file if not os.path.isdir(filepath): # Find the name of the config file script_name = os.path.basename(os.path.dirname(filepath)) script_config = os.path.basename(filepath)[:-5] self.gui_logger.info(f'Launching device {script_name} ' f'with configuration {script_config}') # Initial configurations: All flags down. debug_flag, server_debug_flag = '0', '0' # Raise flags if selected in combobox if self.debug: if self.debug_level == "launcher": debug_flag = '1' elif self.debug_level == "pylabnet_server": server_debug_flag = '1' # # Build client list cmdline arg # client_index = 1 # bash_cmd = '' # for client in self.client_list: # bash_cmd += ' --client{} {} --ip{} {}'.format( # client_index, remove_spaces(client), client_index, self.client_data[client]['ip'] # ) # # Add device ID of client's corresponding hardware, if applicable # if 'device_id' in self.client_data[client]: # bash_cmd += ' --device_id{} {}'.format(client_index, self.client_data[client]['device_id']) # # Add port of client's server, if applicable # if 'port' in self.client_data[client]: # bash_cmd += ' --port{} {}'.format(client_index, self.client_data[client]['port']) # # If this client has relevant .ui file, pass this info # if 'ui' in self.client_data[client]: # bash_cmd += ' --ui{} {}'.format(client_index, self.client_data[client]['ui']) # client_index += 1 launch_script(script=script_name, config=script_config, log_ip=self.host, log_port=self.log_port, debug_flag=debug_flag, server_debug_flag=server_debug_flag, num_clients=len(self.client_list), logger=self.gui_logger) def _load_scripts(self): """ Loads all relevant scripts/devices from filesystem""" # Load scripts with configuraitons script_dir = os.path.join(get_config_directory(), 'scripts') if os.path.isdir(script_dir): model = QtWidgets.QFileSystemModel() model.setRootPath(script_dir) self.main_window.scripts.setModel(model) self.main_window.scripts.setRootIndex(model.index(script_dir)) self.main_window.scripts.hideColumn(1) self.main_window.scripts.hideColumn(2) self.main_window.scripts.hideColumn(3) self.main_window.scripts.doubleClicked.connect(self._script_clicked) # Load device config files device_dir = os.path.join(get_config_directory(), 'devices') if os.path.isdir(device_dir): model = QtWidgets.QFileSystemModel() model.setRootPath(device_dir) self.main_window.devices.setModel(model) self.main_window.devices.setRootIndex(model.index(device_dir)) self.main_window.devices.hideColumn(1) self.main_window.devices.hideColumn(2) self.main_window.devices.hideColumn(3) self.main_window.devices.doubleClicked.connect(self._device_clicked) def _copy_master(self): """ Updates the GUI to copy the GUI of the master GUI server """ # Get a dictionary of all client names and tooltip info clients = self.gui_client.get_container_info('clients') # Update the proxy GUI to reflect the client list of the main GUI for client, info in clients.items(): self.client_list[client] = QtWidgets.QListWidgetItem(client) self.main_window.client_list.addItem(self.client_list[client]) self.client_list[client].setToolTip(info) # Add client data self.client_data[client] = {} if 'ip: ' in info: self.client_data[client]['ip'] = info.split('ip: ')[1].split( '\n')[0] if 'timestamp: ' in info: self.client_data[client]['timestamp'] = info.split( 'timestamp: ')[1].split('\n')[0] if 'ui: ' in info: self.client_data[client]['ui'] = info.split('ui: ')[1].split( '\n')[0] if 'port: ' in info: self.client_data[client]['port'] = info.split( 'port: ')[1].split('\n')[0] if 'device_id: ' in info: self.client_data[client]['device_id'] = info.split( 'device_id: ')[1].split('\n')[0] if 'lab_name: ' in clients[client]: self.client_data[client]['lab_name'] = clients[client].split( 'lab_name: ')[1].split('\n')[0] else: # if no lab name is specified self.client_data[client]['lab_name'] = "NO_LAB" def _pull_connections(self): """ Updates the proxy's client list """ # Get a dictionary of all client names and tooltip info clients = self.gui_client.get_container_info('clients') # Update the proxy GUI to reflect the client list of the main GUI add_clients = list(set(clients.keys()) - set(self.client_list.keys())) remove_clients = list( set(self.client_list.keys()) - set(clients.keys())) other_clients = list( set(clients.keys()) - set(add_clients) - set(remove_clients)) # Add clients for client in add_clients: self.client_list[client] = QtWidgets.QListWidgetItem(client) self.main_window.client_list.addItem(self.client_list[client]) self.client_list[client].setToolTip(clients[client]) # Add client data self.client_data[client] = {} print('Client: ' + client) if 'ip: ' in clients[client]: self.client_data[client]['ip'] = clients[client].split( 'ip: ')[1].split('\n')[0] if 'timestamp: ' in clients[client]: self.client_data[client]['timestamp'] = clients[client].split( 'timestamp: ')[1].split('\n')[0] if 'ui: ' in clients[client]: self.client_data[client]['ui'] = clients[client].split( 'ui: ')[1].split('\n')[0] if 'port: ' in clients[client]: self.client_data[client]['port'] = clients[client].split( 'port: ')[1].split('\n')[0] if 'device_id: ' in clients[client]: self.client_data[client]['device_id'] = clients[client].split( 'device_id: ')[1].split('\n')[0] if 'lab_name: ' in clients[client]: self.client_data[client]['lab_name'] = clients[client].split( 'lab_name: ')[1].split('\n')[0] else: # if no lab name is specified self.client_data[client]['lab_name'] = "NO_LAB" # Remove clients for client in remove_clients: self.main_window.client_list.takeItem( self.main_window.client_list.row(self.client_list[client])) del self.client_list[client] # Update any other changes for client in other_clients: if self.client_list[client].toolTip() != clients[client]: self.client_list[client].setToolTip(clients[client]) if 'ip: ' in clients[client]: self.client_data[client]['ip'] = clients[client].split( 'ip: ')[1].split('\n')[0] if 'timestamp: ' in clients[client]: self.client_data[client]['timestamp'] = clients[ client].split('timestamp: ')[1].split('\n')[0] if 'ui: ' in clients[client]: self.client_data[client]['ui'] = clients[client].split( 'ui: ')[1].split('\n')[0] if 'port: ' in clients[client]: self.client_data[client]['port'] = clients[client].split( 'port: ')[1].split('\n')[0] if 'device_id: ' in clients[client]: self.client_data[client]['device_id'] = clients[ client].split('device_id: ')[1].split('\n')[0] if 'lab_name: ' in clients[client]: self.client_data[client]['lab_name'] = clients[ client].split('lab_name: ')[1].split('\n')[0] else: # if no lab name is specified self.client_data[client]['lab_name'] = "NO_LAB" def _configure_autoscroll_off(self): self.main_window.autoscroll_off_check.toggled.connect( self._update_autoscroll_setting) # Defines what to do if debug radio button is clicked. def _configure_debug(self): self.main_window.debug_radio_button.toggled.connect( self._update_debug_settings) def _configure_logging(self): """ Defines what to do if the Start/Stop Logging button is clicked """ self.main_window.logfile_status_button.toggled.connect( lambda: self.start_stop_logging(master_log=False)) def _configure_logfile(self): """ Defines what to do if the logfile radio button is clicked """ self.main_window.log_file_button.toggled.connect( self._update_logfile_status) # Defines what to do if combobox is changed. def _configure_debug_combo_select(self): self.main_window.debug_comboBox.currentIndexChanged.connect( self._update_debug_level) def _update_debug_settings(self): if self.main_window.debug_radio_button.isChecked(): self.debug = True # Enable and show combobox. self.main_window.debug_comboBox.setEnabled(True) self.main_window.debug_label.setHidden(False) self.main_window.debug_comboBox.setHidden(False) else: self.debug = False # Disable and hide combobox. self.main_window.debug_comboBox.setEnabled(False) self.main_window.debug_label.setHidden(True) self.main_window.debug_comboBox.setHidden(True) # Update debug level. self._update_debug_level() def _update_logfile_status(self): """ Updates the status of whether or not we are using a logfile """ if self.main_window.log_file_button.isChecked(): # Enable and show file browser self.main_window.file_viewer.setEnabled(True) self.main_window.file_viewer.setHidden(False) self.main_window.logfile_status_button.setEnabled(True) self.main_window.logfile_status_button.setHidden(False) self.main_window.log_previous.setEnabled(True) self.main_window.log_previous.setHidden(False) # Assign a file system model if we're not already logging if not self.main_window.logfile_status_button.isChecked(): model = QtWidgets.QFileSystemModel() model.setRootPath(QtCore.QDir.rootPath()) self.main_window.file_viewer.setModel(model) self.main_window.file_viewer.setRootIndex( model.index(QtCore.QDir.homePath())) self.main_window.file_viewer.setColumnWidth(0, 200) else: # Disable and hide file browser self.main_window.file_viewer.setHidden(True) self.main_window.file_viewer.setEnabled(False) self.main_window.logfile_status_button.setHidden(True) self.main_window.logfile_status_button.setEnabled(False) self.main_window.log_previous.setEnabled(False) self.main_window.log_previous.setHidden(True) def _update_debug_level(self, i=0): # Set debug level according to combo-box selection. # Levels are: # pylabnet_server, pylabnet_gui, launcher self.debug_level = self.main_window.debug_comboBox.currentText() def _update_autoscroll_setting(self): if self.main_window.autoscroll_off_check.isChecked(): self.autoscroll_off = True else: self.autoscroll_off = False def _kill(self): """ Kills launch control and all child servers if master """ if not self.proxy: self.kill_servers() self.main_window.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