def get_active_pvs(self): """Queries the database for active PVs. Returns: list : A list of the PVs in running IOCs """ values = [] sqlquery = "SELECT pvinfo.pvname, pvs.record_type, pvs.record_desc, pvs.iocname FROM pvinfo" sqlquery += " INNER JOIN pvs ON pvs.pvname = pvinfo.pvname" # Ensure that only active IOCs are considered sqlquery += " WHERE (pvs.iocname in (SELECT iocname FROM iocrt WHERE running=1) AND infoname='INTEREST')" try: # Get as a plain list of lists values = [list(element) for element in self._db.query(sqlquery)] # Convert any bytearrays for i, pv in enumerate(values): for j, element in enumerate(pv): if type(element) == bytearray: values[i][j] = element.decode("utf-8") except Exception as err: print_and_log("issue with getting active PVs: %s" % err, "MAJOR", "DBSVR") return values
def execute_command(self, command, is_query): """Executes a command on the database, and returns all values Args: command (string): the SQL command to run is_query (boolean): is this a query (i.e. do we expect return values) Returns: values (list): list of all rows returned. None if not is_query """ conn = None curs = None values = None try: conn = self._get_connection() curs = conn.cursor() curs.execute(command) if is_query: values = curs.fetchall() # Commit as part of the query or results won't be updated between subsequent transactions. Can lead # to values not auto-updating in the GUI. conn.commit() except Exception as err: print_and_log("Error executing command on database: %s" % err.message, "MAJOR") finally: if curs is not None: curs.close() if conn is not None: conn.close() return values
def serve_forever(ioc_name: str, pv_prefix: str, macros: Dict[str, str]): """ Server the PVs for the remote ioc server Args: ioc_name: The name of the IOC to be run, including ioc number (e.g. LSICORR_01) pv_prefix: prefix for the pvs macros: Dictionary containing IOC macros Returns: """ ioc_name_with_pv_prefix = "{pv_prefix}{ioc_name}:".format(pv_prefix=pv_prefix, ioc_name=ioc_name) print_and_log(ioc_name_with_pv_prefix) server = SimpleServer() server.createPV(ioc_name_with_pv_prefix, STATIC_PV_DATABASE) # Run heartbeat IOC, this is done with a different prefix server.createPV(prefix="{pv_prefix}CS:IOC:{ioc_name}:DEVIOS:".format(pv_prefix=pv_prefix, ioc_name=ioc_name), pvdb={"HEARTBEAT": {"type": "int", "value": 0}}) # Looks like it does nothing, but this creates *and automatically registers* the driver # (via metaclasses in pcaspy). See declaration of DriverType in pcaspy/driver.py for details # of how it achieves this. LSiCorrelatorIOC(pv_prefix, macros) register_ioc_start(ioc_name, STATIC_PV_DATABASE, ioc_name_with_pv_prefix) try: while True: server.process(0.1) except Exception: print_and_log(traceback.format_exc()) raise
def get_user_filename(self): """ Returns a filename given the current run number and title. If device is simulated do not attempt to get run number or title from channel access """ if self.simulated: filename = "LSICORR_IOC_test_user_save.dat" else: run_number = ChannelAccess.caget(RUNNUMBER_PV.format(pv_prefix=self.pv_prefix)) print_and_log("run number = {}".format(run_number)) timestamp = datetime.now().strftime("%Y-%m-%dT%H_%M_%S") experiment_name = self.get_converted_pv_value(Records.EXPERIMENTNAME.name) if experiment_name == "": # No name supplied, use run title experiment_name = ChannelAccess.caget(TITLE_PV.format(pv_prefix=self.pv_prefix)) filename = "{run_number}_{experiment_name}_{timestamp}.dat".format( run_number=run_number, experiment_name=remove_non_ascii(experiment_name), timestamp=timestamp ) # Update last used filename PV full_filename = os.path.join(self.user_filepath, filename) self.update_pv_and_write_to_device(Records.OUTPUTFILE.name, full_filename) return full_filename
def _start_config_iocs(self): # Start the IOCs, if they are available and if they are flagged for autostart # Note: autostart means the IOC is started when the config is loaded, # restart means the IOC should automatically restart if it stops for some reason (e.g. it crashes) for n, ioc in self._active_configserver.get_all_ioc_details().iteritems(): try: # IOCs are restarted if and only if auto start is True. Note that auto restart instructs proc serv to # restart an IOC if it terminates unexpectedly and does not apply here. if ioc.autostart: # Throws if IOC does not exist running = self._ioc_control.get_ioc_status(n) if running == "RUNNING": # Restart it self._ioc_control.restart_ioc(n) else: # Start it self._ioc_control.start_ioc(n) except Exception as err: print_and_log("Could not (re)start IOC %s: %s" % (n, str(err)), "MAJOR") # Give it time to start as IOC has to be running to be able to set restart property sleep(2) for n, ioc in self._active_configserver.get_all_ioc_details().iteritems(): if ioc.autostart: # Set the restart property print_and_log("Setting IOC %s's auto-restart to %s" % (n, ioc.restart)) self._ioc_control.set_autorestart(n, ioc.restart)
def _pull(self): try: self.remote.pull() except GitCommandError as e: # Most likely server issue print_and_log("Unable to pull configurations from remote repo", "MINOR") raise GitPullFailed()
def _get_iocs(self, include_running=False): # Get IOCs from DatabaseServer try: return self._db_client.get_iocs() except Exception as err: print_and_log("Could not retrieve IOC list: %s" % str(err), "MAJOR") return []
def consume_write_queue(self): """Actions any requests on the write queue. Queue items are tuples with three values: the method to call; the argument(s) to send (tuple); and, the description of the state (string)) For example: self.load_config, ("configname",), "LOADING_CONFIG") """ while True: while len(self.write_queue) > 0: if self._filewatcher is not None: self._filewatcher.pause() with self.write_lock: cmd, arg, state = self.write_queue.pop(0) self.update_server_status(state) try: if arg is not None: cmd(*arg) else: cmd() except Exception as err: print_and_log( "Error executing write queue command %s for state %s: %s" % (cmd.__name__, state, err.message), "MAJOR") self.update_server_status("") if self._filewatcher is not None: self._filewatcher.resume() sleep(1)
def what(pv_sort): """ Args: pv_sort: pv sort to determine Returns: what the pv sort does """ if pv_sort == PvSort.RBV: return "" elif pv_sort == PvSort.ACTION: return "(Do the action)" elif pv_sort == PvSort.SP_RBV: return "(Set point readback)" elif pv_sort == PvSort.SP: return "(Set point)" elif pv_sort == PvSort.SET_AND_NO_ACTION: return "(Set point with no action executed)" elif pv_sort == PvSort.CHANGED: return "(Is changed)" elif pv_sort == PvSort.IN_MODE: return "(Is in mode)" elif pv_sort == PvSort.CHANGING: return "(Is changing)" elif pv_sort == PvSort.RBV_AT_SP: return "(Tolerance between RBV and target set point)" elif pv_sort == PvSort.DEFINE_POS_AS: return "(Define the value of current position)" else: print_and_log("Unknown pv sort!! {}".format(pv_sort), severity=SEVERITY.MAJOR, src="REFL") return "(unknown)"
def _set_rc_values(self, bn, settings): for key, value in settings.iteritems(): if key.upper() in TAG_RC_DICT.keys(): try: self._channel_access.caput(self._block_prefix + bn + TAG_RC_DICT[key.upper()], value) except Exception as err: print_and_log("Problem with setting runcontrol for %s: %s" % (bn, err))
def update_iocs_status(self): """Accesses the db to get a list of IOCs and checks to see if they are currently running Returns: list : The names of running IOCs """ with self._running_iocs_lock: self._running_iocs = list() try: # Get all the iocnames and whether they are running, but ignore IOCs associated with PSCTRL sqlquery = "SELECT iocname, running FROM iocrt WHERE (iocname NOT LIKE 'PSCTRL_%')" rows = self._db.query(sqlquery) for row in rows: # Check to see if running using CA and procserv try: if self._procserve.get_ioc_status(self._prefix, row[0]).upper() == "RUNNING": self._running_iocs.append(row[0]) if row[1] == 0: # This should only get called if the IOC failed to tell the DB it started self._db.update("UPDATE iocrt SET running=1 WHERE iocname='%s'" % row[0]) else: if row[1] == 1: self._db.update("UPDATE iocrt SET running=0 WHERE iocname='%s'" % row[0]) except Exception as err: # Fail but continue - probably couldn't find procserv for the ioc print_and_log("issue with updating IOC status: %s" % err, "MAJOR", "DBSVR") except Exception as err: print_and_log("issue with updating IOC statuses: %s" % err, "MAJOR", "DBSVR") return self._running_iocs
def _upload_archive_config(self): f = os.path.abspath(self._uploader_path) if os.path.isfile(f): print_and_log("Running archiver settings uploader: %s" % f) p = Popen(f) p.wait() else: print_and_log("Could not find specified archiver uploader batch file: %s" % self._uploader_path)
def delete_pv_from_db(self, name): if name in manager.pvs[self.port]: print_and_log("Removing PV %s" % name) fullname = manager.pvs[self.port][name].name del manager.pvs[self.port][name] del manager.pvf[fullname] del self.pvDB[name] del PVDB[name]
def _clear_autosave_files(self): for fname in os.listdir(self._autosave_dir): file_path = os.path.join(self._autosave_dir, fname) try: if os.path.isfile(file_path): os.unlink(file_path) except Exception as err: print_and_log("Problem deleting autosave files for the run-control IOC: %s" % str(err), "MAJOR")
def reload_current_config(self): """ Reload the current configuration.""" current_config_name = self.get_config_name() if current_config_name == "": print_and_log("No current configuration defined. Nothing to reload.") return print_and_log("Trying to reload current configuration %s" % current_config_name) self.load_active(current_config_name)
def reload_current_config(self): """Reload the current configuration.""" try: print_and_log("Reloading current configuration") self._active_configserver.reload_current_config() # If we get this far then assume the config is okay self._initialise_config(full_init=True) except Exception as err: print_and_log(str(err), "MAJOR")
def stop_ioc(self, prefix, ioc): """Stops the specified IOC. Args: prefix (string): The prefix for the instrument ioc (string): The name of the IOC """ print_and_log("Stopping IOC %s" % ioc) ChannelAccess.caput(self.generate_prefix(prefix, ioc) + ":STOP", 1)
def restart_ioc(self, prefix, ioc): """Restarts the specified IOC. Args: prefix (string): The prefix for the instrument ioc (string): The name of the IOC """ print_and_log("Restarting IOC %s" % ioc) ChannelAccess.caput(self.generate_prefix(prefix, ioc) + ":RESTART", 1)
def start_ioc(self, prefix, ioc): """Starts the specified IOC Args: prefix (string): The prefix of the instrument the IOC is being run on ioc (string): The name of the IOC to start """ print_and_log("Starting IOC %s" % ioc) ChannelAccess.caput(self.generate_prefix(prefix, ioc) + ":START", 1)
def restart(self): """ Restarts via ProcServCtrl. """ try: if self._ioc_control.get_ioc_status(BLOCKCACHE_PSC) == "RUNNING": self._ioc_control.restart_ioc(BLOCKCACHE_PSC, force=True) else: self._ioc_control.start_ioc(BLOCKCACHE_PSC) except Exception as err: print_and_log("Problem with restarting the Block Cache: %s" % str(err), "MAJOR")
def start_ioc(self, ioc): """Start an IOC. Args: ioc (string): The name of the IOC """ try: self._proc.start_ioc(self._prefix, ioc) except Exception as err: print_and_log("Could not start IOC %s: %s" % (ioc, str(err)), "MAJOR")
def toggle_autorestart(self, prefix, ioc): """Toggles the auto-restart property. Args: prefix (string): The prefix for the instrument ioc (string): The name of the IOC """ # Check IOC is running, otherwise command is ignored print_and_log("Toggling auto-restart for IOC %s" % ioc) ChannelAccess.caput(self.generate_prefix(prefix, ioc) + ":TOGGLE", 1)
def update_error_pv_print_and_log(self, error: str, severity: str = "INFO", src: str = "LSI") -> None: """ Updates the error PV with the provided error message, then prints and logs the error Args: error: The error message severity (optional): Gives the severity of the message. Expected severities are MAJOR, MINOR and INFO. src (optional): Gives the source of the message. Default source is LSI (from this IOC). """ self.update_pv_and_write_to_device(Records.ERRORMSG.name, error) print_and_log(error, severity, src)
def _unlock(self): """ Removes index.lock if it exists, and it's not being used """ lock_file_path = os.path.join(self.repo.git_dir, "index.lock") if os.path.exists(lock_file_path): try: os.remove(lock_file_path) except Exception as err: print_and_log("Unable to remove lock from version control repository: %s" % lock_file_path, "MINOR") else: print_and_log("Lock removed from version control repository: %s" % lock_file_path, "INFO")
def handle_pv_write(self, pv, data): try: if pv == SYNOPTIC_PRE + SYNOPTIC_DELETE: self.delete(convert_from_json(data)) self.update_monitors() elif pv == SYNOPTIC_PRE + SYNOPTIC_SET_DETAILS: self.save_synoptic_xml(data) self.update_monitors() except VersionControlException as err: print_and_log(str(err),"MINOR") except Exception as err: print_and_log("Error writing to PV %s: %s" % (pv,str(err)),"MAJOR")
def load_last_config(self): """Loads the last configuration used. The information is saved in a text file. """ last = self._active_configserver.load_last_config() if last is None: print_and_log("Could not retrieve last configuration - starting blank configuration") self._active_configserver.clear_config() else: print_and_log("Loaded last configuration: %s" % last) self._initialise_config()
def get_autorestart(self, ioc): """Gets the current auto-restart setting of the specified IOC. Args: ioc (string): The name of the IOC Returns: bool : Whether auto-restart is enabled """ try: return self._proc.get_autorestart(self._prefix, ioc) except Exception as err: print_and_log("Could not get auto-restart setting for IOC %s: %s" % (ioc, str(err)), "MAJOR")
def waitfor_running(self, ioc, timeout=5): """Waits for the IOC to start running. Args: ioc (string): The name of the IOC timeout(int, optional): Maximum time to wait before returning """ if self.ioc_exists(ioc): start = time() while self.ioc_restart_pending(ioc) or self.get_ioc_status(ioc) != "RUNNING": sleep(0.5) if time() - start >= timeout: print_and_log("Gave up waiting for IOC %s to be running" % ioc, "MAJOR") return
def stop_ioc(self, ioc, force=False): """Stop an IOC. Args: ioc (string): The name of the IOC force (bool): Force it to stop even if it is an IOC not to stop """ # Check it is okay to stop it if not force and ioc.startswith(IOCS_NOT_TO_STOP): return try: self._proc.stop_ioc(self._prefix, ioc) except Exception as err: print_and_log("Could not stop IOC %s: %s" % (ioc, str(err)), "MAJOR")
def on_deleted(self, event): """" Called when a file or directory is deleted. Args: event (DirDeletedEvent): Event representing directory deletion. """ # Recover and return error try: # Recover deleted file from vc so it can be deleted properly self._manager.recover_from_version_control() except Exception as err: print_and_log("File Watcher: " + str(err), "MAJOR", "FILEWTCHR") print_and_log("File Watcher: Repository reverted after %s deleted manually. Please delete files via client" % event.src_path, "MAJOR", "FILEWTCHR")
def on_any_event(self, event): """ Catch-all event handler. Args: event (FileSystemEvent): The event object representing the file system event """ if not event.is_directory: if type(event) is not FileDeletedEvent: try: name = self._get_name(event.src_path) if type(event) is FileMovedEvent: # Renaming a file triggers this modified_path = event.dest_path self._manager.delete(name) else: modified_path = event.src_path data = self._check_valid(modified_path) # Update PVs self._update(data) # Inform user print_and_log(self._get_modified_message(name), "INFO", "FILEWTCHR") except NotConfigFileException as err: print_and_log("File Watcher: " + str(err), src="FILEWTCHR") except ConfigurationIncompleteException as err: print_and_log("File Watcher: " + str(err), src="FILEWTCHR") except Exception as err: print_and_log("File Watcher: " + str(err), "MAJOR", "FILEWTCHR")
def _start_connection_pool(self): """Initialises a connection pool """ print_and_log("Creating a new connection pool: " + self._pool_name) conn = mysql.connector.connect(user=self._user, password=self._password, host=self._host, database=self._dbid, pool_name=self._pool_name, pool_size=SQLAbstraction.POOL_SIZE) curs = conn.cursor() # Check db exists curs.execute("SHOW TABLES") if len(curs.fetchall()) == 0: # Database does not exist raise Exception("Requested Database %s does not exist" % self._dbid) curs.close() conn.close()
def _generate_archive_config(self, block_prefix, blocks): print_and_log("Generating archiver configuration file: %s" % self._settings_path) root = eTree.Element('engineconfig') group = eTree.SubElement(root, 'group') name = eTree.SubElement(group, 'name') name.text = "BLOCKS" dataweb = eTree.SubElement(root, 'group') dataweb_name = eTree.SubElement(dataweb, 'name') dataweb_name.text = "DATAWEB" for block in blocks: # Append prefix for the archiver self._generate_archive_channel(group, block_prefix, block, dataweb) with open(self._settings_path, 'w') as f: xml = minidom.parseString(eTree.tostring(root)).toprettyxml() f.write(xml)
def get_archive_filename(self): """ Returns a filename which the archive data file will be saved with """ timestamp = datetime.now().strftime("%Y-%m-%dT%H_%M_%S") run_number = ChannelAccess.caget( RUNNUMBER_PV.format(pv_prefix=self.pv_prefix)) instrument = ChannelAccess.caget( INSTNAME_PV.format(pv_prefix=self.pv_prefix)) filename = "{instrument}{run_number}_DLS_{timestamp}.txt".format( instrument=instrument, run_number=run_number, timestamp=timestamp) full_filename = os.path.join(DATA_DIR, filename) print_and_log("filename1: {}".format(full_filename)) return full_filename
def write(self, reason: str, value: Any): """ Handle write to PV Args: reason: PV to set value of value: Value to set """ print_and_log("LSiCorrelatorDriver: Processing PV write for reason {} value {}".format(reason, value)) if reason == Records.START.name: THREADPOOL.submit(self.take_data) if reason.endswith(":SP"): # Update both SP and non-SP fields THREADPOOL.submit(self.update_pv_and_write_to_device, get_base_pv(reason), value, update_setpoint=True) else: THREADPOOL.submit(self.update_pv_and_write_to_device, reason, value)
def get_options(path): """Loads the IOC options from file and converts them into IocOptions objects Args: path (string): The path to the xml file to be loaded Returns: OrderedDict : A dict of IOCs and their associated options """ iocs = OrderedDict() if os.path.isfile(path): root = parse_xml_removing_namespace(path) OptionsLoader._options_from_xml(root, iocs) else: print_and_log("Cannot find config path: " + str(path), "MINOR") return iocs
def __init__(self, pv_prefix: str, macros: Dict[str, str]): """ A class containing pcaspy and IOC elements of the LSiCorrelator IOC. Args: pv_prefix: The pv prefix for the current host macros: Dictionary of macros for this IOC """ super().__init__() try: self.user_filepath = macros["FILEPATH"] except KeyError: raise RuntimeError("No file path specified to save data to") self.simulated = macros["SIMULATE"] == "1" if self.simulated: print("WARNING! Started in simulation mode") self.driver = LSiCorrelatorVendorInterface(macros, simulated=self.simulated) self.macros = macros self.pv_prefix = pv_prefix defaults = { Records.CORRELATIONTYPE.value: LSI_Param.CorrelationType.AUTO, Records.NORMALIZATION.value: LSI_Param.Normalization.COMPENSATED, Records.MEASUREMENTDURATION.value: 10, Records.SWAPCHANNELS.value: LSI_Param.SwapChannels.ChA_ChB, Records.SAMPLINGTIMEMULTIT.value: LSI_Param.SamplingTimeMultiT.ns200, Records.TRANSFERRATE.value: LSI_Param.TransferRate.ms100, Records.OVERLOADLIMIT.value: 20, Records.OVERLOADINTERVAL.value: 400, Records.REPETITIONS.value: 2, Records.CURRENT_REPETITION.value: 0, Records.CONNECTED.value: self.driver.is_connected, Records.RUNNING.value: False, Records.SCATTERING_ANGLE.value: 110, Records.SAMPLE_TEMP.value: 298, Records.SOLVENT_VISCOSITY.value: 1, Records.SOLVENT_REFRACTIVE_INDEX.value: 1.33, Records.LASER_WAVELENGTH.value: 642, Records.OUTPUTFILE.value: "No data taken yet", Records.SIM.value: 0, Records.DISABLE.value: 0 } self.alarm_status = Alarm.NO_ALARM self.alarm_severity = Severity.NO_ALARM if not os.path.isdir(self.user_filepath): self.update_error_pv_print_and_log( "LSiCorrelatorDriver: {} is invalid file path".format(self.user_filepath), "MAJOR" ) for record, default_value in defaults.items(): # Write defaults to device print_and_log("setting {} to default {}".format(record.name, default_value)) self.update_pv_and_write_to_device(record.name, record.convert_to_pv(default_value)) self.updatePVs()
def _wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception: print_and_log(traceback.format_exc(), src="lsi ")
def create_beamline_from_configuration(macros): """ Create a beamline from a configuration file in the configuration area. Args: macros (dict): Dict of user-set IOC macros Returns: Configured beamline; on error returns blank beamline with error status. """ try: print_and_log( "Importing get_beamline function from config.py in {}".format( REFL_CONFIG_PATH), SEVERITY.INFO, src=REFL_IOC_NAME) sys.path.insert(0, REFL_CONFIG_PATH) # noinspection PyUnresolvedReferences config_to_load = _get_config_to_load(macros) print_and_log("Importing get_beamline function from {} in {}".format( config_to_load, REFL_CONFIG_PATH), SEVERITY.INFO, src=REFL_IOC_NAME) config = import_module(_get_config_to_load(macros)) beamline = config.get_beamline(macros) except ImportError as error: print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) beamline = _create_beamline_in_error("Configuration not found.") except BeamlineConfigurationParkAutosaveInvalidException as error: print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error(str(error)) except BeamlineConfigurationInvalidException as error: print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error( "Beamline configuration is invalid.") except Exception as error: print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error("Can not read configuration.") return beamline