Source code for controllab.xxsim4c

# Please use the
# http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
# as documentation format for this class
# disable some pylint checks.
# pylint: disable=broad-except, too-many-arguments
# pylint: disable=too-many-lines, too-many-public-methods
""" This module provides the XML-RPC communication with 20-sim 4C.

    Example:
        >>> from controllab import XXSim4CWrapper
"""


import collections
import math
import os.path

from .tool import ExeTool
from .wrapper import Wrapper, WrapperError
from distutils.util import strtobool


LogInfo = collections.namedtuple(
    'LogInfo',
    ['filename', 'path', 'Overwrite', 'addTimeStamp'])

ProjectInfo = collections.namedtuple(
    'ProjectInfo', ['projectname', 'identifier'])

TargetInfo = collections.namedtuple(
    'TargetInfo', ['targetname', 'identifier', 'filename'])

ModelInfo = collections.namedtuple(
    'ModelInfo', ['modelname', 'identifier', 'filename'])

TimeSettings = collections.namedtuple(
    'TimeSettings', ['starttime', 'finishtime', 'interval'])

TemplateInfo = collections.namedtuple(
    'TemplateInfo', ['identifier', 'name', 'version', 'file', 'path'])


[docs]class XXSim4CWrapper(Wrapper): """20-sim 4C scripting interface for Python. This is a Python wrapper for 20-sim 4C's XML-RPC interface. Attributes: errorlevel: The level of errors that should be raised. """ DEFAULT_PORT = 5480 def _get_default_exetool(self, version=None, **kwargs): """ Get the default ExeTool for XXSim4CWrapper. Optionally a different version of the default tool can be started. Args: version (optional) : Will be passed on to ExeTool.__init__ Default: '2.2' Returns: ExeTool : An ExeTool representing 20-sim 4C. """ return ExeTool('XXSIM4C', version or '2.2', **kwargs) def _set_xrc_server(self, xrc): # pylint: disable=arguments-differ """ Correctly sets the self.server attribute. Args: xrc (xmlrpclib.ServerProxy) : The serverproxy Example: >>> import xmlrpc.client as xmlrpclib >>> _set_xrc_server(self, ... xmlrpclib.Server_proxy('http://localhost:5480')) """ self.server = xrc.xxsim4C
[docs] def connect(self, uri='http://localhost:{}'.format(DEFAULT_PORT), version=ExeTool.DEFAULT_XXSIM4C_VERSION, **kwargs): """Create a connection with 20-sim 4C using XML-RPC Args: uri (str): The URI to connect to. Defaults to 'http://localhost:5480' for connecting to 20-sim 4C. Returns: bool: True if successful, False otherwise. Example: >>> connect('http://localhost:5480') True """ """ Check if we are running from 20-sim 4C """ try: if os.environ['RUNNING_FROM_20SIM4C'] == '1': running_from_20sim4C = True if running_from_20sim4C: ivc_HTTP_port = int(os.environ['XXSIM4C_HTTP_SCRIPT_PORT']) """ override the uri """ uri='http://localhost:{}'.format(ivc_HTTP_port) except: pass return super(XXSim4CWrapper, self).connect(uri, version=version, **kwargs)
[docs] def get_version(self): """Get the version number of the 20-sim 4C instance to which a connection was made. Returns: dict: A dictionary with the following fields: * ``major``: The major number of the version number. * ``minor``: The minor number of the version number. * ``patch``: The patch number of the version number. * ``build``: The build number of the version number. False: On failure. Example: >>> xxsim4c.get_version() {'major': 2, 'minor': 2, 'patch': 0, 'build': 4700} """ try: # Obtain the version information via XML-RPC call. version_struct = self.server.getVersion() # Obtain the version string. version_number_string = version_struct['version'] # Split the version number string. version_numbers_array = version_number_string.split(".") # Return a dictionary with all version data. return {'major':int(version_numbers_array[0]), 'minor':int(version_numbers_array[1]), 'patch':int(version_numbers_array[2]), 'build':int(version_numbers_array[3])} except Exception as error: self.errorhandler(error) return False
[docs] def open_project(self, project): """Opens a project file from its path. Args: project (str): Name of the file or project to load. Returns: int: The identifier of the project (1 or higher). It can be used to select the active project Returns False on error. The project is activated and initialized after opening. Example: >>> project_id = open_project('c:\\temp\\myproject.emx') """ try: # Check whether the provided project exists if os.path.isabs(project) and not os.path.isfile(project): raise FileNotFoundError('File not found: {}'.format(project)) # Open the project reply = self.server.project.open({'name': project}) # the actual XML-RPC call if reply is not None: return reply['identifier'] except Exception as error: self.errorhandler(error) return False
[docs] def close_project(self): """ Closes active 20-sim 4C project without saving changes Returns: bool: True when closed properly, False otherwise Example: >>> result = close_project(True) """ try: # the actual XML-RPC call return self.server.project.close() except Exception as error: self.errorhandler(error) return False
[docs] def get_project(self): """Returns a list of the opened project and their project identifier Args: None Returns: list: List of ``ProjectInfo`` """ try: # the actual XML-RPC call reply = self.server.project.getProject() projects = [] for project in reply: # Ensure identifier is int instead of string. project['identifier'] = int(project['identifier']) # Create ProjectInfo tuple from dict and append to list. projects.append(ProjectInfo(**project)) return projects except Exception as error: self.errorhandler(error) return False
# TODO: XMLRPC NOT AVAILABLE # def save_model(self, path): # """ Save the changes made to the active model. # # Args: # path (str): Save the active model at this location. # *Note*: When given an empty string, the model is saved # under its current name (if any) # Returns: # bool: True on success, False otherwise # # Example: # # >>> save_model('c:\\temp\\mymodel.emx') # True # """ # try: # return self.server.save_model({'name': fileName}) # except Exception as error: self.errorhandler(error)
[docs] def get_targets(self): """Retrieves list of all the targets in use in the open project Args: None Returns: list: List of xxsim4c.TargetInfo """ try: # the actual XML-RPC call reply = self.server.project.getTargets() targets = [] for target in reply: # Turn identifier of target into int. target['identifier'] = int(target['identifier']) # Create tuple from dict and append to targets list. targets.append(TargetInfo(**target)) return targets except Exception as error: self.errorhandler(error) return False
[docs] def get_active_target(self): """ Retrieves the active target identifier Args: None Returns: collections.namedtuple: (targetname, filename, identifier) bool: False on failure. Example: >>> targetinfo = getActive_target() """ try: # the actual XML-RPC call reply = self.server.project.getActiveTarget() # Make identifier int instead of str. reply['identifier'] = int(reply['identifier']) # Return TargetInfo tuple from reply dict. return TargetInfo(**reply) except Exception as error: self.errorhandler(error) return False
[docs] def set_active_target(self, identifier): """ Sets the active target Args: Identifier (int): The identifier of the target. Returns: bool: True on success, False on failure. Example: >>> targetinfo = set_active_target(1) """ identifier = {'identifier': int(identifier)} try: # the actual XML-RPC call return self.server.project.setActiveTarget(identifier) except Exception as error: self.errorhandler(error) return False
[docs] def is_target_online(self): """Checks whether the active target is online or not Returns: bool: True when the target is on-line. False when the target is off-line or when 20-sim 4C is still trying to connect. """ try: # the actual XML-RPC call return self.server.target.isOnline() except Exception as error: self.errorhandler(error) return False
[docs] def get_models(self): """Returns a list of all opened models and their model identifier Args: None Returns: list: List of ``xxsim4c.ModelInfo`` """ try: reply = self.server.project.getModels() # the actual XML-RPC call models = [] for model in reply: # Make identifier int instead of str. model['identifier'] = int(model['identifier']) # Make tuple from dict and append to models. models.append(ModelInfo(model)) return models except Exception as error: self.errorhandler(error) return False
[docs] def get_active_model(self): """ Returns name and unique identifier of active model Args: None Returns: xxsim4c.ModelInfo : The model information Example: >>> modelinfo = getActive_model() """ try: # the actual XML-RPC call reply = self.server.project.getActiveModel() # Make identifier int instead of str. reply['identifier'] = int(reply['identifier']) # Make ModelInfo tuple from dict and return it. return ModelInfo(reply) except Exception as error: self.errorhandler(error) return False
[docs] def set_active_model(self, identifier): """ Returns name and unique identifier of active model Args: Identifier (integer) Returns: bool: True on success. False on failure. Example: >>> set_active_model(1) True """ identifier = {'identifier': int(identifier)} try: # the actual XML-RPC call return self.server.project.setActiveModel(identifier) except Exception as error: self.errorhandler(error) return False
[docs] def start(self, log): """Start a model, model needs to be ready and target needs to be online Args: log : bool that enables or disables logging Returns: bool: True on success, False otherwise Example: >>> start = start(True) """ logging = {'enableLog': log} try: # the actual XML-RPC call return self.server.run.start(logging) except Exception as error: self.errorhandler(error) return False
[docs] def stop(self): """Stops a model, if model is not running true will be returned as well Args: None Returns: bool: True on success, False otherwise Example: >>> stopped = stop() """ try: # the actual XML-RPC call return self.server.run.stop() except Exception as error: self.errorhandler(error) return False
[docs] def get_url(self): """ Gets the URL of the target Args: None Returns: URL of the target Example: >>> URL = get_url() """ try: # the actual XML-RPC call return self.server.configure.getURL() except Exception as error: self.errorhandler(error) return False
[docs] def set_url(self, url): """ Sets the url of the target Args: url (string) Returns: bool: False on failure. Example: >>> url = set_url('192.168.1.123') """ try: # the actual XML-RPC call return self.server.configure.setURL(url) except Exception as error: self.errorhandler(error) return False
[docs] def compile(self): """Compiles the 20-sim 4C sources to a target application/binary Args: None Returns: bool: True on success, False otherwise Example: >>> compile=compile() """ try: # the actual XML-RPC call return self.server.compile() except Exception as error: self.errorhandler(error) return False
[docs] def get_settings(self, settings=None): """Queries the active 20-sim 4C model for the settings Args: settings (list, optional) : The list of names of the settings that should be retrieved. If left blank, all settings will be retrieved. When querying a single setting, the value can be passed as string. Returns: The settings. """ # Start with an empty key list settings = settings or [] if isinstance(settings, str): settings = [settings] try: # the actual XML-RPC call return self.server.getSettings({'keys': settings}) except Exception as error: self.errorhandler(error) return False
def get_setting_value(self, setting): settingsResult = self.get_settings(setting) if isinstance(settingsResult, bool): return None # Convert the returned string values to proper Python data types retval = [] for setting in settingsResult: if setting['type'] == 'double': retval.append(float(setting['value'])) elif setting['type'] == 'boolean': retval.append(bool(strtobool(setting['value']))) elif setting['type'] == 'int': retval.append(int(setting['value'])) else: retval.append(setting['value']) if len(retval) == 1: return retval[0] else: return retval
[docs] def get_log_file(self): """ Retrieves the properties of the log file. Args: None Returns: LogInfo : The information about the log file. None in case of failure. Example: >>> logfile = get_log_file() """ try: # Get dict from XMLRPC, make LogInfo tuple and return it. return LogInfo(self.server.log.getLogFile()) except Exception as error: self.errorhandler(error) return False
[docs] def set_log_file(self, logname, path, overwrite=False, timestamp=True): """ Sets the LogFile Args: logname (str): The file name of the log file. path (str): The path overwrite(bool): Overwrite existing logfile. timestamp (bool): adds time stamp to log. Returns: bool: False on failure. Example: >>> xxsim4C.set.log_file('test', r'D:\temp\', True) True """ overwrite = bool(overwrite) try: return self.server.setSettings( ['log.filename', 'log.path', 'log.overWriteFile', 'log.addTimeStamp'], [logname, path, overwrite, timestamp]) except Exception as error: self.errorhandler(error) return False
[docs] def get_run_settings(self): """ Retrieves the run settings. Args: None Returns: RunSettings : The run settings, None on failure Example: >>> run_settings = getRun_settings() """ try: reply = self.server.getSettings() # the actual XML-RPC call # Create and return tuple. return TimeSettings( starttime=float(reply['run.startTime']), finishtime=float(reply['run.finishTime']), interval=float(reply['run.discreteTimeInterval'])) except Exception as error: self.errorhandler(error) return False
[docs] def set_run_settings( self, start, finish, sampletime=None, frequency=None, omega=None): """ Sets the LogFile Args: ``Start`` , ``Finish`` and ``DiscreteTimeInterval`` Returns: bool: False on failure. Example: >>> set_run_settings('0','1','0.01') True """ if sampletime is None: if frequency is not None: sampletime = 1 / frequency elif omega is not None: sampletime = 2 * math.pi / omega else: self.errorhandler( WrapperError( 'Please specify sampletime, frequency or omega.')) try: return self.set_settings(['run.startTime', 'run.finishTime', 'run.discreteTimeInterval'], [start, finish, sampletime]) except Exception as error: self.errorhandler(error) return False
[docs] def get_log_settings(self): """ Retrieves the settings for logging. Args: None Returns: TimeSettings : The timing settings for the logging. Example: >>> logsettings = get_log_settings() """ try: reply = self.server.getSettings() # the actual XML-RPC call # Convert values in reply from string to numbers. return TimeSettings( starttime=int(reply['log.startTime']), finishtime=int(reply['log.finishTime']), interval=float(reply['log.discreteTimeInterval'])) except Exception as error: self.errorhandler(error) return False
[docs] def set_log_settings( self, start, finish, sampletime=None, frequency=None, omega=None): """ Sets the LogFile Args: start (int) : Start time for logging finish (int) : Finish time for logging sampletime (float, optional) : Sample time for logging. frequency (float, optional) : Logging frequency. omega (float, optional) : Omega for logging. One of sampletime, frequency and omega must be provided. If multiple are provided, the first one in order (sampletime, frequency, omega) will be used. Returns: bool: False on failure. Example: >>> log_settings = set_log_settings(0,1,'sampletime'=0.01) """ if sampletime is None: if frequency is not None: sampletime = 1 / frequency elif omega is not None: sampletime = 2 * math.pi / omega else: self.errorhandler( WrapperError( 'Please specify sampletime, frequency or omega.')) return False if self.get_run_sampletime() < sampletime: self.errorhandler( WrapperError( 'Unable to set log settings. Sampletime of log must be ' 'smaller than sampletime of run')) return False try: return self.set_settings(['log.startTime', 'log.finishTime', 'log.discreteTimeInterval'], [start, finish, sampletime]) except Exception as error: self.errorhandler(error) return False
[docs] def set_variable_flag(self, variables, log, monitor): # Todo write description, test """ set flags for logging and monitoring variables Args: variables (list of str) : The variable names. log (bool) : True to enable logging, False to disable. monitor (bool) : True to enable monitoring, False to disable. Returns: bool Example: >>> Flag=set_variable_flag([var1,var2], ... ['log','monitor'], ... ['True','False']) """ # check for single or list variables if isinstance(variables, str): variables = [variables] try: # the actual XML-RPC call return self.server.model.setVariableFlag( {'variablenames': variables, 'flag': [{'key': 'log', 'value': log}, {'key': 'monitor', 'value': monitor}]}) except Exception as error: self.errorhandler(error) return False
[docs] def query_templates(self): """ Queries the available Templates Args: None Returns: TemplateInfo : The information about the templates Example: >>> Templates=query_templates() """ try: # Create TemplateInfo from dict returned by XMLRPC call. return TemplateInfo(**self.server.configure.queryTemplates()) except Exception as error: self.errorhandler(error) return False
[docs] def select_template(self, target_file): """ Selects and sets a templates Args: target_file (str): the file name of the Target Configuration (TCF) of the template. Returns: bool : True indicates success. Example: >>>select_template('test') True """ try: # the actual XML-RPC call return self.server.configure.selectTemplate(target_file) except Exception as error: self.errorhandler(error) return False
[docs] def set_run_start(self, start): """ Set the run start time. Equivalent of: set_settings('run.startTime', start) """ return self.set_settings('run.startTime', start)
[docs] def set_run_stop(self, stop): """ Set the run finish time. Equivalent of: set_settings('run.finishTime', start) """ return self.set_settings('run.finishTime', stop)
[docs] def set_run_endless(self, state=False): """ Set the run endless setting. Equivalent of: set_settings('run.endless', state) Args: state (bool, optional) : Default false (run until finish time.) """ return self.set_settings('run.endless', state)
[docs] def get_run_endless(self): """ Get the run endless setting. Equivalent of: get_settings('run.endless') """ return self.get_setting_value('run.endless')
[docs] def set_run_frequency(self, frequency): """ Set the run frequency. Equivalent of: self.set_settings('run.discreteTimeInterval', 1 / frequency) Args: frequency (float) : The run frequency. """ return self.set_settings('run.discreteTimeInterval', 1 / frequency)
[docs] def set_run_omega(self, omega): """ Set the run omega. Equivalent of: self.set_settings('run.discreteTimeInterval', 2 * math.pi / omega) Args: omega (float) : The run omega. """ return self.set_settings( 'run.discreteTimeInterval', 2 * math.pi / omega)
[docs] def set_run_sampletime(self, sampletime): """ Set the run sampletime. Equivalent of: self.set_settings('run.discreteTimeInterval', sampletime) Args: sampletime (float) : The run sampletime. """ return self.set_settings('run.discreteTimeInterval', sampletime)
[docs] def set_log_start(self, start): """ Set the log start time. Equivalent of: self.set_settings('log.starttime', start) Args: start (float) : The log start time. """ return self.set_settings('log.startTime', start)
[docs] def set_log_stop(self, stop): """ Set the log finish time. Equivalent of: self.set_settings('log.finishTime', stop) Args: start (float) : The log finish time. """ return self.set_settings('log.finishTime', stop)
[docs] def set_log_frequency(self, frequency): """ Set the log frequency. Equivalent of: self.set_settings('log.discreteTimeInterval', 1 / frequency) Args: frequency (float) : The log frequency. """ return self.set_settings('log.discreteTimeInterval', 1 / frequency)
[docs] def set_log_omega(self, omega): """ Set the log omega. Equivalent of: self.set_settings('log.discreteTimeInterval', 2 * math.pi / omega) Args: omega (float) : The log omega. """ return self.set_settings( 'log.discreteTimeInterval', 2 * math.pi / omega)
[docs] def set_log_sampletime(self, sampletime): """ Set the log sampletime. Equivalent of: self.set_settings('log.discreteTimeInterval', sampletime) Args: sampletime (float) : The log sampletime. """ return self.set_settings('log.discreteTimeInterval', sampletime)
[docs] def get_run_start(self): """ Get the run start time. """ return self.get_setting_value('run.startTime')
[docs] def get_run_stop(self): """ Get the run finish time. """ return self.get_setting_value('run.finishTime')
[docs] def get_run_sampletime(self): """ Get the run sampletime """ return self.get_setting_value('run.discreteTimeInterval')
[docs] def get_run_frequency(self): """ Get the run frequency. Returns: float : Frequency as calculated from the sampletime. """ return 1 / self.get_setting_value('run.discreteTimeInterval')
[docs] def get_run_omega(self): """ Get the run omega. Returns: float : Omega as calculated from the sampletime. """ return 2 * math.pi / self.get_setting_value('run.discreteTimeInterval')
[docs] def get_log_start(self): """ Get the log start time. """ return self.get_setting_value('log.startTime')
[docs] def get_log_stop(self): """ Get the log finish time. """ return self.get_setting_value('log.finishTime')
[docs] def get_log_sampletime(self): """ Get the log sampletime. """ return self.get_setting_value('log.discreteTimeInterval')
[docs] def get_log_frequency(self): """ Get the log frequency. Returns: float : Frequency as calculated from the sampletime. """ return 1 / self.get_setting_value('log.discreteTimeInterval')
[docs] def get_log_omega(self): """ Get the log omega. Returns: float : Omega as calculated from the sampletime. """ return 2 * math.pi / self.get_setting_value('log.discreteTimeInterval')
[docs] def get_log_filename(self): """ Get the base file name of the log file. """ return self.get_setting_value('log.filename')
[docs] def get_log_last_filename(self): """ Get the file name of the last stored log file. """ return self.get_setting_value('log.lastfilename')
[docs] def set_log_filename(self, filename): """ Set the file name of the log file. Args filename (str) : The file name """ return self.set_settings('log.filename', filename)
[docs] def get_log_path(self): """ Get the path of the log file. """ return self.get_setting_value('log.path')
[docs] def set_log_path(self, path): """ Set the path of the log file. Args path (str) : The path """ return self.set_settings('log.path', path)
[docs] def get_log_overwrite(self): """ Get the overwrite setting for the log. """ return self.get_setting_value('log.overwriteFile')
[docs] def set_log_overwrite(self, overwrite=False): """ Set the overwrite setting for the log. Args: overwrite (bool, optional): True to overwrite, default is False. """ return self.set_settings('log.overwriteFile', overwrite)
[docs] def get_log_add_time_stamp(self): """ Get the addTimeStamp property for the log. Returns: bool: True if the loging is timestamped. """ return self.get_setting_value('log.addTimeStamp')
[docs] def set_log_add_time_stamp(self, timestamp=True): """ Set the addTimeStamp property for the log. Args: timestamp (bool, optional): True to add timestamps to log messages. Default is True. """ return self.set_settings('log.addTimeStamp', timestamp)
[docs] def get_log_show_plot(self): """ Get the showPlot property for the log. Returns: bool: True if the 'Show plot after run' option is enabled. """ return self.get_setting_value('log.showPlot')
[docs] def set_log_show_plot(self, show_plot=True): """ Sets the showPlot property for the log. Args: show_plot (bool, optional): True shows the plot with the log results after logging is ready. Default is True. """ return self.set_settings('log.showPlot', show_plot)
[docs] def get_log_variables(self): """ Returns a list with all logVariables.""" filters = [{'key': 'flag', 'value': 'logvariable'}] try: # the actual XML-RPC call variables = self.server.model.getVariables( {'variables': [], 'filters': filters}) return variables except Exception as error: self.errorhandler(error) return False
[docs] def get_monitor_variables(self): """ Returns a list with all monitorVariables. """ filters = [{'key': 'flag', 'value': 'monitorvariable'}] try: # the actual XML-RPC call variables = self.server.model.getVariables( {'variables': [], 'filters': filters}) return variables except Exception as error: self.errorhandler(error) return False
# Missing functions: # get/set start/finish time and frequency (in log window) # getAvailableLogFreq lists the available Freq for logging # Get List of models below a certain target (Low priority) # addNewTarget: Adds a new target to the project. (Long term) # GET and SET TEMPLATE: # Gets all possible templates / Selects a new template (Long Term) # Discover targets and set IP (Long term) # connect Inputs/Outputs (Long Term) # Get//SetActive log variables (please also allow 'all') (Long Term) # functions that should be expanded: # GetTarget -> IP (or DNS) adress should also be exported! # configure(basic) # connect (basic)