Source code for controllab.xxsim

# 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 Python class that allows XML-RPC communication
    with 20-sim.

    Example:
        >>> import controllab
        >>> xxsim = controllab.XXSim()
"""
from __future__ import absolute_import, print_function

import collections
import math
import os.path

from .tool import ExeTool
from .wrapper import Wrapper, WrapperError
from .wrapper import dict2value_with_properties


# Try to import numpy (needed for linearize_model()
try:
    import numpy as np
except ImportError:
    pass

CodeTarget = collections.namedtuple(
    'CodeTarget', ['name', 'submodelselection'])
ModelInfo = collections.namedtuple('ModelInfo', ['name', 'identifier'])
ImplementationInfo = collections.namedtuple(
    'Implementation', [
        'name', 'implementation', 'implementations'])


[docs]class XXSimWrapper(Wrapper): """20-sim scripting interface for Python. This is a Python wrapper for 20-sim's XML-RPC interface. Attributes: errorlevel: The level of errors that should be raised. """ DEFAULT_PORT = 5580
[docs] class Simulator(object): """ This dummy object makes the interface more similar to the old Octave interface and the underlying XML-RPC interface. It does this by giving the XXSimwrapper a simulator attribute. """ def __init__(self, xxsim): self.xxsim = xxsim self.get_fast_mode = xxsim.simulator_get_fast_mode self.get_finish_time = xxsim.simulator_get_finish_time self.get_output_after_each = xxsim.simulator_get_output_after_each self.get_start_time = xxsim.simulator_get_start_time self.set_fast_mode = xxsim.simulator_set_fast_mode self.set_finish_time = xxsim.simulator_set_finish_time self.set_output_after_each = xxsim.simulator_set_output_after_each self.set_start_time = xxsim.simulator_set_start_time self.single_step = xxsim.simulator_single_step self.start = xxsim.simulator_start self.stop = xxsim.simulator_stop self.is_simulating = xxsim.simulator_is_simulating self.copy_states_to_initials = \
xxsim.simulator_copy_states_to_initials def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.simulator = self.Simulator(self) self.destecs = None def _get_default_exetool(self, version=None, **kwargs): """ Get the default ExeTool for XXSimWrapper. Optionally a different version of the default tool can be started. Args: version (optional) : Will be passed on to ExeTool.__init__ Default: '4.7' Add command-line options to the tool Returns: clp.ExeTool : An ExeTool representing 20-sim. """ return ExeTool('XXSIM', version or '4.7', **kwargs) def _set_xrc_server(self, xrc): """ Correctly sets the self.server attribute. Args: xrc (xmlrpclib.ServerProxy) : The serverproxy Example: >>> import xmlrpc.client as xmlrpclib >>> _set_xrc_server(self, ... xmlrpclib.ServerProxy('http://localhost:5580')) """ self.server = xrc.xxsim self.destecs = xrc.destecs
[docs] def connect(self, uri='http://localhost:{}'.format(DEFAULT_PORT), **kwargs): """Create a connection with 20-sim using XML-RPC. Args: uri (str): The URI to connect to. (default: http://localhost:5580) autostart (bool): Start an instance of the 20-sim application, if none is running. If no version is supplied, the latest version will be started. (optional, default: True) version (str, optional): Only connect if the running 20-sim instance is this version. If omitted, it will also open a scripting session against other versions of 20-sim. Combined with autostart, this version of 20-sim will be started if no 20-sim is running. Returns: bool: True if successful, False otherwise. Examples: Connect to 20-sim on the local machine: >>> xxsim.connect('http://localhost:5580') True Connect to 20-sim on a remote server. Requires that the firewall of the remote server allows incoming TCP connections on port 5580. >>> xxsim.connect('http://www.example.com:5580') True Try to connect to 20-sim version 4.7, when server is running on a different version: >>> xxsim.connect('http://localhost:5580', version='4.7') False Try to connect to 20-sim, when no instance is running: >>> xxsim.connect('http://localhost:5580', True) """
return super(XXSimWrapper, self).connect(uri, **kwargs)
[docs] def disconnect(self): """Disable the 20-sim script mode and close the connection. Returns: bool: True on success. Example: >>> xxsim.disconnect() """ self.set_scriptmode(False)
return super(XXSimWrapper, self).disconnect()
[docs] def close(self): """ Close 20-sim (without saving changes to any opened models). Returns: bool: True if successful, False otherwise. Example: >>> result = xxsim.close() """
return super(XXSimWrapper, self).close()
[docs] def new_model(self): """Opens a new empty 20-sim model Returns: bool: True on success, False otherwise Example: >>> result = xxsim.new_model() """ identifier = self.open_model('', True) if identifier == 0: return False
return True
[docs] def open_model(self, model, new_instance=False): """Open a 20-sim model file from its path. After opening, the model is activated and initialized. Args: model (str): Name of the file to load. new_instance (bool): Set to True to open a new model instance. Set to False to close the current model and open a new model. (default: False) Returns: int: The identifier of the model (1 or higher, 0=error). It can be used to select the active model. Example: >>> model_id = xxsim.open_model('C:\\\\temp\\\\my_model.emx', False) """ try: # Check whether the provided model exists if not os.path.isfile(model) and model != '': raise WrapperError("File not found: {}".format(model)) if model != '': model = os.path.abspath(model) # Open the model / project by passing over the absolute path # the actual XML-RPC call reply = self.server.openModel( {'name': model, 'newinstance': new_instance}) if reply is not None: return reply['identifier'] except Exception as error: self.errorhandler(error)
return False
[docs] def close_model(self, close_editor=False): """ Close the active 20-sim model without saving changes. Args: close_editor (bool, optional): Set to True to also close the 20-sim editor. Defaults to False which keeps the editor open. In any case this will never close the final editor window because it would close 20-sim entirely. That functionality is provided by XXSim.close() Returns: bool: True when closed properly, False otherwise. Example: >>> xxsim.close_model(True) True """ try: # the actual XML-RPC call return self.server.closeModel({'closewindow': close_editor}) except Exception as error: self.errorhandler(error)
return False
[docs] def save_model(self, filename=None): """ Saves the changes made to active 20-sim model Args: filename (str, optional): The filename that should be used to save the active model. If omitted, 20-sim will save the model under its current name if it has any. Returns: bool: True on success, False otherwise Example: >>> result = xxsim.save_model('c:\\\\temp\\\\mymodel.emx') """ try: if filename is None: filename = '' # the actual XML-RPC call return self.server.saveModel({'name': filename}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_active_model(self): """ Return the name and unique identifier of the model that is active in 20-sim. Returns: xxsim.ModelInfo: The information on the active model. Example: >>> xxsim.get_active_model() ModelInfo(name='C:\\\\temp\\\\my_model.emx', identifier=2) """ try: # the actual XML-RPC call reply = self.server.getActiveModel() return ModelInfo(**reply) except Exception as error: self.errorhandler(error)
return False
[docs] def linearize_model(self, u, y, symbolic=True, at_current_time=True, closed_loop=False, abs_error=1.0e-5, rel_error=1.0e-5, use_abs_when_needed=True): """ Linearize a part of the model (SISO and MIMO) Args: u (str -or- list of str): The fully qualified name(s) of the input variable(s). y (str -or- list of str): The fully qualified name(s) of the output variable(s). symbolic (bool, optional): True indicates that a symbolic linearisation should be done, False performs a numerical linearisation. Defaults to True at_current_time (bool, optional): Linearise at the current time if True. Linearise at the start time if False. Defaults to True. closed_loop (bool, optional): Do closed loop linearisation if True. Do open loop linearisation if False. Defaults to False. abs_error (float, optional): The absolute change in states. Defaults to 1.0e-5 rel_error (float, optional): The relative change in states. Defaults to 1.0e-5 use_abs_when_needed (bool, optional): Use the absolute error margin for states that are aproximately 0.0 Returns: A, B, C, D: Matrices """ # Check if Numpy is loaded and we've received a Numpy object. if 'np' not in globals(): raise WrapperError( "The linearize_model() function depends on numpy.\nInstall numpy first") # Initialise return dict. retval = {} try: # Make lists of single strings.s if isinstance(u, str): u = [u] if isinstance(y, str): y = [y] if not isinstance(u, list): # Should be a list raise WrapperError("u argument should be a list[str] or a str") if not isinstance(y, list): # Should be a list raise WrapperError("y argument should be list[str] or a str") k = len(u) p = len(y) first_iteration = True for k_, invar in enumerate(u): for p_, outvar in enumerate(y): retval = self.server.model.linearizeModel({ 'u': [invar], 'y': [outvar], 'symbolic': symbolic, 'atCurrentTime': at_current_time, 'closedLoop': closed_loop, 'absError': abs_error, 'relError': rel_error, 'useAbsWhenNeeded': use_abs_when_needed}) # determine the amount of states n = math.sqrt(len(retval['A'])) if first_iteration: # create the numpy arrays of the right size A = np.zeros((n, n), dtype=float) B = np.zeros((n, k), dtype=float) C = np.zeros((p, n), dtype=float) D = np.zeros((p, k), dtype=float) first_iteration = False # fill the matrices A = np.array(retval['A'], dtype=float).reshape(n, n) B[:, k_] = retval['B'] C[p_, :] = retval['C'] D[p_, k_] = retval['D'][0] return A, B, C, D except Exception as error: self.errorhandler(error)
return False
[docs] def set_active_model(self, model): """Sets one of the opened 20-sim models as active. If no active model is chosen, the model opened last is automatically set as active. if only one model is opened, it is set as active model by default. Args: model (str -or- int -or- ModelInfo): model name or the model id or a ModelInfo dict Returns: bool: True on success, False otherwise Example: >>> xxsim.set_active_model('C:\\temp\\PID.emx') True >>> xxsim.set_active_model(3) True """ try: identifier = -1 if isinstance(model, int): identifier = model elif isinstance(model, str): # search through all models to find the right identifier models = self.get_models() for selected_model in models: if selected_model.name == model: identifier = selected_model.identifier break elif isinstance(model, ModelInfo): identifier = model.identifier if identifier == -1: raise WrapperError( 'The specified model is not open in this 20-sim session.') return self.server.setActiveModel({'identifier': identifier}) except Exception as error: self.errorhandler(error)
return False
[docs] def process_model(self): """Process the active 20-sim model. Returns: dict: A dictionary with the processing results on success with the following fields: * ``errors`` *list of str*: processing errors * ``results`` *list of dict*: key, value pairs with the processing results (model characteristics) * ``warnings`` *list of str*: processing warnings False on failure. Example: >>> xxsim.process_model() {'errors': [], 'results': [{'key': 'constraints', 'value': '0'}, {'key': 'states', 'value': '0'}, {'key': 'variables', 'value': '21'}, {'key': 'algebraic loop variables', 'value': '0'}, {'key': 'equations', 'value': '4'}, {'key': 'submodels', 'value': '1'}, {'key': 'discrete states', 'value': '0'}, {'key': 'dependent states', 'value': '0'}, {'key': 'discrete systems', 'value': '0'}], 'warnings': ['[LogVarTest] warning: LogVarTest\\p1 is never used', '[LogVarTest] warning: LogVarTest\\p2 is never used']} """ try: # the actual XML-RPC call return self.server.model.process() except Exception as error: self.errorhandler(error)
return False
[docs] def get_version(self): """Get the version number of the 20-sim instance with 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: >>> xxsim.get_version() {'major': 4, 'minor': 7, 'patch': 2, 'build': 7336} """ 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 run(self, continue_simulation=False): """Run the active 20-sim model with saved settings. Args: continue_simulation (bool, optional): Set to True to attempt to continue the simulation. Set to False to start a new run. (default: False) Returns: dict: A dictionary with the following fields: * ``result``: 1 if the model runs without errors. * ``info``: a list with key-value dicts with information on. the simulation run, e.g. total simulation time, number of model calculations, number of output points. * ``warning``: warning(s) during running the model, if any * ``errors``: error(s) during running the model, if any False: On failure. Example: >>> xxsim.run(False) {'errors': [], 'info': [{'key': 'Total simulation time', 'value': '0.003'}, {'key': 'Number of Model Calculations', 'value': '1002'}, {'key': 'Number of Output Points', 'value': '1002'}, {'key': 'Average Steps per Second', 'value': '334000'}], 'result': 1, 'warnings': []} """ try: # the actual XML-RPC call return self.server.simulator.run( {'continueSimulation': continue_simulation}) except Exception as error: self.errorhandler(error)
return False
[docs] def clear_run(self, action=0): """Clear one or more runs. Args: action (int): Value indicating what action to take: * 0 = clear all runs (also default if argument is omitted) * 1 = clear last run * 2 = clear previous runs Returns: bool: True on success, False otherwise. Example: >>> xxsim.clear_run(0) True """ try: # the actual XML-RPC call return self.server.simulator.clearRun({'clear': int(action)}) except Exception as error: self.errorhandler(error)
return False
[docs] def clear_all_runs(self): """Clear all simulation runs. Shortcut for: clear_run(0) Returns: bool: True on success, False otherwise. Example: >>> xxsim.clear_all_runs() True """
return self.clear_run(0)
[docs] def clear_last_run(self): """Clear only the last simulation run. Shortcut for: clear_run(1) Returns: bool: True on success, False otherwise. Example: >>> xxsim.clear_last_run() True """
return self.clear_run(1)
[docs] def clear_previous_runs(self): """Clear all runs except the last one. Shortcut for: clear_run(2) Returns: bool: True on success, False otherwise. Example: >>> xxsim.clear_previous_runs() True """
return self.clear_run(2)
[docs] def get_models(self): """Return information on all the models that are open in 20-sim. Returns: list: A list of *ModelInfo* objects, one per model, containing the following fields: * ``name`` *str*: the file name of the model * ``identifier`` *int*: the identifier for this model in the current 20-sim session. False is returned on a failure. Example: >>> xxsim.get_models() [ModelInfo(name='C:\\temp\\my_model.emx', identifier=2), ModelInfo(name='C:\\temp\\another_model.emx', identifier=3)] """ try: return [ModelInfo(**model) for model in self.server.getModels()] except Exception as error: self.errorhandler(error)
return False
[docs] def get_parameters(self, submodel_names=None): """Returns a list with the specified parameters Args: submodel_names (str -or- list): (optional) A single name of a parameter, a list with parameter names or a list with submodel names. All parameter names should contain the full hierarchy e.g ``PID.Kp``. When passing a submodel name, all parameters of this submodel will be returned. When this argument is omitted, this function will return all parameters of the active model. Returns: list *or* None: list with a variables dict (when retrieving more than one parameter) False is returned on a failure Examples: To retrieve a list of parameters found in the submodels PID and Amplifier, you can use: >>> params = xxsim.get_parameters(['PID','Amplifier']) The output is a list of parameter dicts with the following fields: * ``name``: the name of the retrieved parameter(s) * ``size``: the size of the parameter(s) * ``values``: the values of the retrieved parameter(s) * ``properties``: the properties of the parameter(s) To retrieve a single of parameter you can use >>> xxsim.get_parameters('PID.kp') [{'name': 'PID.kp', 'size': [1], 'values': [1.0], 'properties': [{'key': 'parameter', 'value': 'true'}, {'key': 'arithmetictype', 'value': 'real'}, {'key': 'quantity', 'value': 'Magnitude'}, {'key': 'unit', 'value': 'none'}, {'key': 'unitSymbol', 'value': ''}, {'key': 'description', 'value': 'Proportional gain'}]}] """ if not submodel_names: submodel_names = []
return self.get_variables(submodel_names, 'parameter')
[docs] def get_parameters_with_properties(self, submodel_names=None): """ Identical to get_parameters, but the data from 20-sim is returned as a list of namedtuple ValueWithProperties Args: submodel_names (str -or- list): (optional) A single name of a parameter, a list with parameter names or a list with submodel names. All parameter names should contain the full hierarchy e.g ``PID.Kp``. When passing a submodel name, all parameters of this submodel will be returned. When this argument is omitted, this function will return all parameters of the active model. Returns: list *or* None: list with ValueWithProperties namedtuple Examples: To retrieve a list of parameters found in the submodels PID and Amplifier, you can use: >>> params = xxsim.get_parameters(['PID','Amplifier']) The output is a list of ValueWithPropertie namedtuples with the following fields: * ``name``: the name of the retrieved parameter * ``value``: the value(s) of the retrieved parameter * ``unit``: the unit of the parameter (e.g. 'meter') * ``quantity``: the quantity of the parameter (e.g 'Length') * ``unitSymbol``: the unit symbol (e.g. 'm') * ``arithmetictype``: the parameter type (e.g. 'real') * ``description``: the description of the parameter To retrieve a single of parameter you can use: >>> xxsim.get_parameters('PID.kp') [ValueWithProperties(name='PID.kp', value=0.2, unit='none', quantity='Magnitude', unitSymbol='', arithmetictype='real', description='Proportional gain')] """ if not submodel_names: submodel_names = [] return dict2value_with_properties(
self.get_variables(submodel_names, 'parameter'))
[docs] def set_parameters(self, names, values): """ Sets the specified parameters to a new value Args: names (str or list): string (single parameter only) or a list of strings with the names of the parameters to be set values (float or list): the new values of the parameters Returns: bool: Returns True if the parameters are set properly, False otherwise Examples: To set a single parameter, you can use: >>> xsim.set_parameters('PID.kp', 0.2) True To set multiple parameters, you can use: >>> xxsim.set_parameters(['PID.kp', 'Amplifier.Gain'], [0.3, 5]) True """
return self.set_variables(names, values)
[docs] def get_initial_values(self, variables=None): """Get the initial values of the given variables. This is just a shorthand for the equivalent call to get_variables) Args: variables (list, optional): List of variable names (str). If no variables are passed, all variables are retrieved. When getting a single variable, this value can be passed as a string. Returns: list: The output is a list of initial value dicts with the following fields: * ``name``: the name of the retrieved parameter(s) * ``size``: the size of the parameter(s) * ``values``: the values of the retrieved parameter(s) * ``properties``: the properties of the parameter(s) This function will return False in case of a failure Examples: >>> xxsim.get_initial_values('PID.pdstate_initial') [{'name': 'PID.pdstate_initial', 'values': [0.0], 'size': [1], 'properties': [{'key': 'initialValue', 'value': 'true'}, {'key': 'arithmetictype', 'value': 'real'}, {'key': 'quantity', 'value': ''}, {'key': 'unit', 'value': ''}, {'key': 'unitSymbol', 'value': ''}]}] The following are equivalent: >>> xxsim.get_initial_values(['PID.pdstate_initial']) >>> xxsim.get_variables(['PID.pdstate_initial'],['initialValue']) """
return self.get_variables(variables, 'initialValue')
[docs] def set_initial_values(self, names, values): """ Set the specified initial values. Args: names (list): a list of strings with the names of the initial values to be set (or a string for a single initial value) values (list): a list of floats with the new values of the initial values (or a float for a single new value) Returns: bool: True if the initial values are set properly, False otherwise. Example: To set a single initial value, you can use: >>> xxsim.set_initial_values('PID.pdstate_initial', 1.0) For multiple initial values, you can use: >>> xxsim.set_initial_values(['PID.pdstate_initial', 'PID.pistate_initial'], [1.0, 2.0]) True """
return self.set_variables(names, values)
[docs] def get_log_values(self, variables=None): """ Fetch the values which are logged during a simulation run of the active model. Args: variables (list): List of strings with the names of the logged variables to fetch. If empty, values of *all* logged variables are fetched. For a single variable name, a string can be given. (default: []) Returns: list: A list of dictionaries, one for each requested logged variable, with keys the *name* of the variable and its *values*. """ try: # Check input arguments if variables is None: variables = [] elif isinstance(variables, str): variables = [variables] elif not isinstance(variables, list): raise WrapperError('No list of variable names given') # the actual XML-RPC call return self.server.simulator.getLogValues( {'variables': variables}) except Exception as error: self.errorhandler(error)
return False
[docs] def set_log_variables(self, log_variables): """logs specified variables while running active model Args: log_variables (list -or- str): list of variables or a single variable name *Note*: make sure the variable name exists in active model. If not, an error message will be returned Returns: bool: True on success, False otherwise Examples: >>> xxsim.set_log_variables('PID.error') True >>> xxsim.set_log_variables(['PID.error', 'PID.output']) True """ try: # Check input arguments if isinstance(log_variables, str): log_variables = [log_variables] elif not isinstance(log_variables, list): raise WrapperError('No list of variable names given') # the actual XML-RPC call return self.server.simulator.setLogVariables( {'variables': log_variables}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_value(self, var_name): """Retrieves the value of the specified variable or parameter name. Note: For matrices, use get_variables or get_value_with_properties, otherwise you will get the values as a list, but will not get the information about the shape of the matrix. Args: var_name (str): the name of the variable or parameter Returns: float *or* None: The value of the variable. None is returned in case the variable or parameter could not be found. False is returned on a failure Example: >>> xxsim.get_value('PID.kp') 1.0 """ if not isinstance(var_name, str): raise WrapperError( 'Please pass a single variable name as a string') var = self.get_variables(var_name) # Nothing found, return nothing. if var is None: return None # get_variables has failed and printed a user error. if var is False: # Return error state. return False # Only grab the value of the single variable returned val = var[0]['values'] if len(val) == 1: return val[0]
return val
[docs] def get_value_with_properties(self, var_name): """Retrieves the value of the specified variable or parameter name Args: var_name (str): the name of the variable or parameter Returns: ValueWithProperties: The value of the variable and the variable properties stored in a *ValueWithProperties* object None is returned in case the variable or parameter could not be found. False is returned on a failure Example: >>> xxsim.get_value_with_properties('PID.pdstate') ValueWithProperties(name='PID.pdstate', value=0.0, unit='', quantity='', unitSymbol='', arithmetictype='real', description='') The output is a ValueWithProperties namedtuple with the following fields: * ``name``: the name of the retrieved parameter * ``value``: the value(s) of the retrieved parameter * ``unit``: the unit of the parameter (e.g. 'meter') * ``quantity``: the quantity of the parameter (e.g 'Length') * ``unitSymbol``: the unit symbol (e.g. 'm') * ``arithmetictype``: the parameter type (e.g. 'real') * ``description``: the description of the parameter """ try: valprops = dict2value_with_properties(self.get_variables(var_name)) if valprops is False: return False if len(valprops) == 1: return valprops[0] return valprops except Exception as error: self.errorhandler(error)
return False
[docs] def query_settings(self, settings_names=()): """Queries the active 20-sim model for the settings Args: settings_names: (optional) A string, or list of strings, containing the names of the settings that are to be queried. Defaults to an empty tuple. This will query all available settings. Returns: list: A list of dicts with the following fields: * ``value`` *str*: the value of the setting (as string) * ``type`` *str*: original type of the value * ``description`` *str*: description of the setting * ``key`` *str*: setting name * ``enumerations`` *list*: * ``properties`` *list*: list of key-value dicts False is returned on a failure Example: >>> xxsim.query_settings('model.simulator.finishtime') [{'value': '10', 'properties': [{'value': '0.0', 'key': 'lowerbound'}], 'enumerations': [], 'type': 'double', 'key': 'model.simulator.finishtime', 'description': ''}] """ # Start with an empty key list keys = [] if isinstance(settings_names, str): keys = [settings_names] else: keys = settings_names try: # the actual XML-RPC call settings = self.server.querySettings({'keys': keys}) return settings except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_get_finish_time(self): """ Get the finish time used by the simulator. Returns: float: The finish time used by the simulator. A return value of False indicates an error has occurred. Example: >>> xxsim.simulator_get_finish_time() 10.0 """ try: setting = self.query_settings('model.simulator.finishtime') if len(setting) == 1: return float(setting[0]['value']) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_set_finish_time(self, finishtime): """ Set the finish time of the simulator: Args: finishtime (float): The new finish time for the simulator. Returns: bool: True on success, False on failure. Example: >>> xxsim.simulator_set_finish_time(10.0) True """ try: return self.set_settings( 'model.simulator.finishtime', finishtime) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_get_start_time(self): """ Get the start time used by the simulator. Returns: float: The start time used by the simulator. False is returned on a failure Example: >>> xxsim.simulator_get_start_time() 2.0 """ try: setting = self.query_settings('model.simulator.starttime') if len(setting) == 1: return float(setting[0]['value']) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_set_start_time(self, starttime): """ Set the start time for the simulator: Args: starttime (float): The new start time for the simulator. Returns: bool: True on of success, False on failure. Example: >>> xxsim.simulator_set_start_time(2.0) True """ try: return self.set_settings('model.simulator.starttime', starttime) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_get_fast_mode(self): """ Check whether the simulator is in 'fast mode'. Returns: bool: True if the simulator is in 'fast mode', False otherwise. Example: >>> xxsim.simulator_get_fast_mode() True """ try: reply = self.query_settings('model.simulator.fastmode') return len(reply) == 1 and reply[0]['value'] == 'true' except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_set_fast_mode(self, fastmode=True): """ Change the simulator mode to 'fast mode' or 'debug mode' Args: fastmode (bool, optional): True = fast mode (default), False = debug mode Returns: bool: True on success, False otherwise Example: >>> xxsim.simulator_set_fast_mode(True) True """ try: return self.set_settings('model.simulator.fastmode', fastmode) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_get_output_after_each(self): """ Gets the current value of the simulator 'Output After Each' Run property Returns: float: The 'output after each' time on success. A value of -1.0 indicates an error has occurred. False is returned on a failure Example: >>> xxsim.simulator_get_output_after_each() 0.0 """ try: reply = self.query_settings('model.simulator.outputaftereach') if len(reply) == 1: return reply[0]['properties'][0]['value'] except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_set_output_after_each(self, output_time): """ Change the 'Output After Each' Run property in the simulator Args: output_time (float): Set the 'output after each' option of the simulator.to this value. Returns: bool: True on success, False on failure. Example: >>> result = xxsim.simulator_set_output_after_each() """ try: return self.set_settings( 'model.simulator.outputaftereach', output_time) except Exception as error: self.errorhandler(error)
return False
[docs] def query_implementations(self): """ Returns a list of submodels names with multiple implementations and their active implementation. Returns: list: List of ImplementationInfo objects, one for each submodel, with the following fields: * ``name`` *str*: the name of the submodel * ``implementation`` *str*: the name of the active implementation * ``implementations`` *list of str*: list of all available implementations for this submodel False is returned on a failure Example: >>> implementations = xxsim.query_implementations() """ try: # the actual XML-RPC call result = self.server.model.queryImplementations() if not result: return result # Convert the result into a list of namedtuples implist = [ImplementationInfo(**element) for element in result] return implist except Exception as error: self.errorhandler(error)
return False
[docs] def get_implementations(self, submodel_name): """ Returns the implementations of a particular submodel Args: name (string): the hierarchical name of the submodel for the implementations that should be requested Returns: dict: Dictionary with: * activeImplementation * list of all implementation names. Example: >>> implementations = xxsim.get_implementations('Submodel1') >>> implementations['activeImplementations'] """ try: if not isinstance(submodel_name, str): raise WrapperError( 'Error: submodel_name should be of string type ') # the actual XML-RPC call return self.server.model.getImplementations( {'name': submodel_name}) except Exception as error: self.errorhandler(error)
return False
[docs] def set_implementations(self, submodel_names, implementation_names=None, process_model=True): """ Change the implementation of one or more submodels Args: submodel_names (str, list or dict): a string or list of strings with the submodel names or a dict with the submodel names as key and the implementation name as value implementation_names (str, optional): string or list of strings with the corresponding implementation to set. This argument should be omitted when you pass a dict to *submodel_names*. process_model (bool, optional): boolean to indicate whether the model should be processed afterwards. Defaults to true Returns: bool: True on success, False on failure Examples: >>> xxsim.set_implementations('Submodel1', 'implementationA') >>> xxsim.set_implementations('Submodel1', 'implementationA', ... False) The following are equivalent: >>> xxsim.set_implementations(['Submodel1', 'Submodel2'], ... ['implementationA','implementationB']) >>> xxsim.set_implementations({'Submodel1': 'implementationA', ... 'Submodel2': 'implementationB'}) """ try: # Check and transform input for further processing. if isinstance(submodel_names, dict) and not implementation_names: # dict of form: {'Submodel1': 'implementationB', 'Submodel2': # turn it into double list form. my_implementation_names = list(submodel_names.values()) my_submodel_names = list(submodel_names.keys()) elif isinstance(submodel_names, str) and isinstance( implementation_names, str): # two string arguments turn into two lists my_submodel_names = [submodel_names] my_implementation_names = [implementation_names] elif isinstance(submodel_names, list) and isinstance( implementation_names, list): # two list arguments if len(submodel_names) != len(implementation_names): raise WrapperError( 'Error: Size mismatch submodel_names and ' 'implementation_names should have the same number of ' 'list elements') else: raise WrapperError( 'Error: Invalid parameters. submodel_names and ' 'implementation_names should be of the same type, ' 'alternatively only submodel_names should be passed as ' 'a dict.') # The input arguments have been reshaped into two lists. # Put them in the correct format for the XML-RPC call. implementations = [{'name': my_submodel_names[i], 'implementation': my_implementation_names[i]} for i in range(len(my_submodel_names))] data = {'implementations': implementations, 'processModel': process_model} # the actual XML-RPC call return self.server.model.changeImplementations(data) except Exception as error: self.errorhandler(error)
return False
[docs] def get_c_code_targets(self): """Retrieve a list of C-code targets Returns: list : List of CodeTarget objects, one for each available target. False in case of a failure Example: >>> xxsim.get_c_code_targets() [CodeTarget(name='20simDLL', submodelselection=True), CodeTarget(name='Arduino', submodelselection=True), CodeTarget(name='CFunction', submodelselection=True), CodeTarget(name='CppClass', submodelselection=True), CodeTarget(name='Simulink', submodelselection=True), CodeTarget(name='StandAloneC', submodelselection=False)] """ try: # the actual XML-RPC call targets = self.server.simulator.getCCodeTargets() if not targets: return [] # Convert to namedtuple result = [] for target in targets: result.append(CodeTarget(**target)) return result except Exception as error: self.errorhandler(error)
return False
[docs] def get_matlab_code_targets(self): """Retrieve a list of Matlab code targets Returns: list : list of CodeTarget objects, one for each available target. False in case of a failure. Example: >>> xxsim.get_matlab_code_targets() [CodeTarget(name='Matlab', submodelselection=True), CodeTarget(name='StandAloneMatlab', submodelselection=False), CodeTarget(name='Simulink', submodelselection=True), CodeTarget(name='StandAloneSimulink', submodelselection=False)] """ try: # the actual XML-RPC call targets = self.server.simulator.getMatlabCodeTargets() if not targets: return [] # Convert to namedtuple result = [] for target in targets: result.append(CodeTarget(**target)) return result except Exception as error: self.errorhandler(error)
return False
[docs] def set_scriptmode(self, scriptmode=True): """Turn On/Off 20-sim scripting mode. Scripting mode deactivates all buttons on simulation window except the stop button. Args: scriptmode (bool,optional): True sets 20-sim in scripting mode (default), False resets 20-sim back to normal mode Returns: bool: True on success, False otherwise Example: >>> xxsim.set_scriptmode(True) True """ try: # the actual XML-RPC call if self.server is None: return True return self.server.setScriptMode({'set': scriptmode}) except Exception as error: self.errorhandler(error)
return False
[docs] def generate_c_code( self, target_name, output_directory, submodel_name='', template_directory=''): """Generate C-Code for the active model using the specified C-code target template Args: target_name (str): The name of the code generation template to use. Use :py:meth:`.get_c_code_targets()` to fetch the list of available C-code targets. output_directory (str): The output directory where the generated C-Code should be generated submodel_name (str, optional): The name of the submodel for submodel code generation templates only. You should omit this parameter if you are using a 'model' code generation template. template_directory (str, optional): The directory where the template is located. If omitted, 20-sim will search for the template in the standard list of C-code folders. Returns: bool *or* dict: False on failure, on success a *dict* with the following fields: * ``warnings`` *list* of warning messages. * ``result`` *True* * ``errors`` *list* of error messages. Examples: >>> result = generate_c_code( target_name='CFunction', output_directory='c:\\temp\\%SUBMODEL_NAME%', submodel_name='MySubmodel') >>> result = generate_c_code( target_name='StandAloneC', output_directory='c:\\temp\\%MODEL_NAME%') """ try: # the actual XML-RPC call return self.server.simulator.generateCCode({ 'targetName': target_name, 'outputDirectory': output_directory, 'submodelName': submodel_name, 'templateDirectory': template_directory}) except Exception as error: self.errorhandler(error)
return False
[docs] def generate_matlab_code( self, target_name, output_directory, submodel_name='', template_directory=''): """Generate Matlab/Simulink code for the active model using the specified Matlab or Simulink M-code target template Args: target_name (str): The name of the code generation template to use. Use :py:meth:`.get_matlab_code_targets()` to fetch the list of available Matlab-code targets. output_directory (str): The output directory where the generated Matlab code should be generated. submodel_name (str, optional): For submodel code generation templates only: the name of the submodel. You should omit this parameter if you are using a 'model' code generation template. template_directory (str, optional): The directory where the template is located. If omitted, 20-sim will search for the template in the standard list of M-code folders. Returns: bool *or* dict: False on failure, on success a *dict* with the following fields: * ``warnings`` *list* of warning messages. * ``result`` *True* * ``errors`` *list* of error messages. Examples: >>> result = generate_matlab_code( target_name='Matlab', output_directory='c:\\\\temp\\\\%SUBMODEL_NAME%', submodel_name='MySubmodel') >>> result = generate_matlab_code( target_name='StandAloneSimulink', output_directory='c:\\\\temp\\\\%MODEL_NAME%') """ try: # the actual XML-RPC call return self.server.simulator.generateMatlabCode({ 'targetName': target_name, 'outputDirectory': output_directory, 'submodelName': submodel_name, 'templateDirectory': template_directory}) except Exception as error: self.errorhandler(error)
return False
[docs] def log2csv(self, path, variables=None): """ Write the logged variables from 20-sim to a CSV on the file system. Args: path (string): The path and file name of the csv file. variables (list, optional): The list of variables names that should be stored in this csv file. Default (=None) stores all variables marked for logging with :py:meth:`.set_log_variables()` """ if not variables: variables = [] try: return self.server.simulator.writeLogToCsv( {'name': path, 'variables': variables}) except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_start(self): """ Start the simulation. The simulation will run until it reaches its finish time, or until the *simulator_stop()* function is called. Returns: bool: True on success, False otherwise. Example: >>> xxsim.simulator_start() True """ try: return self.server.simulator.start() except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_stop(self): """ Stop the simulation. Returns: bool: True on success, False otherwise. Example: >>> xxsim.simulator_stop() True """ try: return self.server.simulator.stop() except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_single_step(self): """Execute a single simulation step for the active 20-sim model. Returns: dict : simulation results dictionary with the following fields: * ``result`` *int*: * ``info`` *list of dict*: list with key, value pairs with simulation statistics * ``warnings`` *list of str*: list of warnings that occured during the simulation step * ``errors`` *list of str*: list of warnings that occured during the simulation step False on failure. Example: >>> xxsim.simulator_single_step() {'errors': [], 'info': [{'key': 'Total simulation time', 'value': '0.02'}, {'key': 'Number of Model Calculations', 'value': '2'}, {'key': 'Number of Output Points', 'value': '1'}, {'key': 'Average Steps per Second', 'value': '100'}], 'result': 0, 'warnings': []} """ try: return self.server.simulator.singleStep() except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_is_simulating(self): """ Check whether the simulator is running. WARNING: If the function fails and the error handling is set to not raise that error (the default), the function will print an error for the user and return False. In this case, the function returns False even when the simulator is running. If you initialise XXSim with RAISEERRORS.ALL it will always raise an error that can be caught by the calling script. This is the recommended way for automation. Returns: bool: True if the simulator is running, otherwise False. Example: >>> xxsim.simulator_is_simulating() False """ try: return self.server.simulator.isSimulating() except Exception as error: self.errorhandler(error)
return False
[docs] def simulator_copy_states_to_initials(self): """ Copy current states to initial values Returns: bool: True on success, False otherwise. Example: >>> xxsim.simulator_copy_states_to_initials() True """ try: return self.server.simulator.copyStatesToInitials() except Exception as error: self.errorhandler(error)
return False
[docs] def get_memory_usage(self): """Retrieve the memory usage of 20-sim and the available system memory Returns: dict: resource usage dictionary with the following fields: * ``success`` *bool* is True on a successful call * ``USERObjects`` *int* the number of USER objects * ``GDIObjects`` *int* the number of GDI objects * ``WorkingSetMemory`` *float* the current memory usage in megabytes (MB) * ``PeakWorkingSetMemory`` *float* the peak memory usage in megabytes (MB) * ``AvailablePhysicalMemory`` *float* the available physical memory on the system Example: >>> xxsim.get_memory_usage() {'WorkingSetMemory': 95.7109375, 'PeakWorkingSetMemory': 109.328125, 'GDIObjects': 781, 'AvailablePhysicalMemory': 4251.203125, 'success': True, 'USERObjects': 71} """ try: return self.server.util.getMemoryUsage() except Exception as error: self.errorhandler(error)
return False
[docs] def open_simulator(self): """ Open the simulator window for the current model Returns: bool: True if if the simulator opened without errors, False otherwise. Example: >>> xxsim.open_simulator() True """ try: return self.server.simulator.openSimulator() except Exception as error: self.errorhandler(error)
return False
[docs] def add_plot_window(self, window_name): """ For the given name, this method will create a plotting window with that name. The return value is the window ID as assigned by 20-sim. Args: window_name (str): Name of the to be added plot window. Returns: int: a unique ID corresponding to the window that was created. Example: >>> xxsim.add_plot_window('Test') 1 """ try: return self.server.plot.addPlotWindow( {'windowName': window_name}) except Exception as error: self.errorhandler(error)
return False
[docs] def add_plot_to_window(self, window, plot_name): """ For the given plotName, this method will create a plot with that name in the window specified by window. The return value is the plot ID as assigned by 20-sim. Args: window (str, int): Name or ID of the plot window to which the plot should be added. plot_name (str): Name of the to be created plot. Returns: int: a unique ID corresponding to the plot that was created. Example: >>> xxsim.add_plot_to_window('Test','Demo') 1 >>> xxsim.add_plot_to_window(1,'Demo') 2 """ try: window_id = self.get_plot_window_id_from_name(window) return self.server.plot.addPlotToWindow( {'windowID': window_id, 'plotName': plot_name}) except Exception as error: self.errorhandler(error)
return False
[docs] def add_curve_to_plot(self, window, plot, variable, x_variable='time', label_name='', key_values=[]): """ Add a new curve to the specified plot Args: window (int or str): Name or The ID corresponding to the window to which the curve will be added. plot (int or str): Name or ID corresponding to the plot to which the curve will be added. variable (str): Path of the 20-sim variable that is plotted in the curve. x_variable (str, optional): The variable plotted on the X-axis of the curve (default: 'time'). label_name (str, optional): The label of the curve as shown in the legend of the plot. If empty, the variable name is taken. Default is variable name. key_values (structured array): Structured array that contains key-value pairs. Accepted keys: * ``colorR``: The red component of the RGB color that the curve should have. Valid range: 0-255. Default*: 0 * ``colorG``: The green component of the RGB color that the curve should have. Valid range: 0-255. Default*: 0 * ``colorB``: The blue component of the RGB color that the curve should have. Valid range: 0-255. Default*: 0 * ``thickness``: The line thickness. Valid range is 1-50. **Note:** If no color is specified, 20-sim will select the next color from its internal list. Returns: int: a unique ID corresponding to the curve that was created. Example: >>> xxsim.add_curve_to_plot('PlotWindow','Plot','time') 0 >>> xxsim.add_curve_to_plot('PlotWindow','Plot','time','SomeTime') 1 >>> xxsim.add_curve_to_plot('PlotWindow','Plot','time','Sometime', [{'key':'colorB','value':'255'}]) 2 """ if label_name == '': label_name = variable try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) # the actual XML-RPC call return self.server.plot.addCurveToPlot({ 'windowID': window_id, 'plotID': plot_id, 'xVariablePath': x_variable, 'yVariablePath': variable, 'zVariablePath': 'dummy', 'labelName': label_name, 'keyvalues': key_values}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_curves_from_plot(self, window, plot): """ Returns an array of structs that each represent a curve in the specified plot. These structs have the curve path and variable ID of each specific variable. Args: window (int or str): Name or ID from the plot window where the curve is added to. plot (int or str): Name or ID from the plot where the curve is added to. Returns: list of dict: list with all curves in the specified plot window with the following fields: * ``curveID``: *int* the ID of the curve. * ``xPath``: *str* the path of the 20-sim variable on the X-axis. * ``yPath``: *str* the path of the 20-sim variable on the Y-axis. * ``zPath``: *str* the path of the 20-sim variable on the Z-axis. * ``isHidden``: *bool* boolean that indicates if the curve is hidden. """ try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) return self.server.plot.getCurvesFromPlot( {'windowID': window_id, 'plotID': plot_id}) except Exception as error: self.errorhandler(error)
return False
[docs] def hide_curve(self, window, plot, curve, hidden): """ Hides the curve if isHidden is true, and otherwise shows it. Args: window (int or str): Name or ID corresponding to the plot window in which the curve should be hidden or shown. plot (int or str): Name or ID corresponding to the plot in which the curve should be hidden or shown. curve (int): ID corresponding to the curve that is either hidden or shown. hidden (bool): Boolean indicating if the given curve will be hidden (true) or shown (false). Returns: bool: True if if the simulator opened without errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) return self.server.plot.hideCurve({ 'windowID': window_id, 'plotID': plot_id, 'curveID': curve, 'isHidden': hidden}) except Exception as error: self.errorhandler(error)
return False
[docs] def remove_curve_from_plot(self, window, plot, curve): """ For the specified plot window name or ID and plot name or ID, remove the given curve of the plot. Args: window (int or str): Name or ID corresponding to the window to which in the specified plot the specified curve will be removed. plot (int or str): Name or ID corresponding to the plot of which the curve will be removed. curve (int): ID corresponding to the to be deleted curve. Returns: bool: True if if the simulator opened without errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) return self.server.plot.removeCurveFromPlot({ 'windowID': window_id, 'plotID': plot_id, 'curveID': curve}) except Exception as error: self.errorhandler(error)
return False
[docs] def hide_plot(self, window, plot, hidden): """ Hides the plot if hidden is true, and otherwise shows it. Args: window (int or str): Name or ID corresponding to the plot window in which the plot should be hidden or shown. plot (int or str): Name or ID corresponding to the plot that is either hidden or shown. hidden (bool): Boolean indicating if the given plot will be hidden (true) or shown (false). Returns: bool: True if if the plot was hidden without any errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) return self.server.plot.hidePlot({ 'windowID': window_id, 'plotID': plot_id, 'isHidden': hidden}) except Exception as error: self.errorhandler(error)
return False
[docs] def remove_plot_from_window(self, window, plot): """ For the specified plot window name or ID, the plot with plot name or ID will be removed. Args: window (int or str): Name or ID corresponding to the window of which the plot will be removed. plot (int or str): Name or ID corresponding to the plot that should be deleted. Returns: bool: True if if the plot was removed from the window without errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) plot_id = self.get_plot_id_from_name(window, plot) return self.server.plot.removePlotFromWindow({ 'windowID': window_id, 'plotID': plot_id}) except Exception as error: self.errorhandler(error)
return False
[docs] def hide_plot_window(self, window, hidden): """ Hides the plot window if isHidden is true, and otherwise shows it. Args: window (int or str): Name or ID corresponding to the window that is either hidden or shown. hidden (bool): Boolean indicating if the given plot window will be hidden (true) or shown (false). Returns: bool: True if if the plot window was hidden without any errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) return self.server.plot.hidePlotWindow({ 'windowID': window_id, 'isHidden': hidden}) except Exception as error: self.errorhandler(error)
return False
[docs] def remove_plot_window(self, window): """ For the given window name or ID, the corresponding plot window will be removed. Args: window (int or str): Name or ID of the to be removed window. Returns: bool: True if if the plot window was removed without any errors, False otherwise. """ try: window_id = self.get_plot_window_id_from_name(window) return self.server.plot.removePlotWindow({ 'windowID': window_id}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_plot_windows(self): """ Get all currently available plot windows that are present in the simulator by both name and ID. Returns: list of dict: list with all plot windows for the current model with the following fields: * ``windowName`` *str* the name of the plot window * ``windowID`` *int* the corresponding plot identifier Example: >>> xxsim.get_plot_windows() [{'windowName': 'Crank Rod - Plots', 'windowID': 1}, {'windowName': '3D Animation', 'windowID': 2}, {'windowName': 'Window 3', 'windowID': 3}] """ try: return self.server.plot.getPlotWindows() except Exception as error: self.errorhandler(error)
return False
[docs] def get_plots_from_window(self, window): """ Get all currently available plots that are present in the specified window by both name and ID. Args: window (str -or- int): The id or name of the plot window for which the plots should be returned Returns: list of dict: list with all plots in the specified plot window with the following fields: * ``plotName``: *str* the name of the plot * ``plotID``: *int* the id of the plot Example: >>> xxsim.get_plots_from_window(1) [{'plotName': '3D Animation', 'plotID': 6}, {'plotName': 'x (load)', 'plotID': 7}, {'plotName': 'x (sledge)', 'plotID': 8}, {'plotName': 'T (motor)', 'plotID': 9}, {'plotName': 'omega', 'plotID': 10}] """ window_id = -1 try: if isinstance(window, str): window_id = self.get_plot_window_id_from_name(window) if window_id == -1: raise WrapperError( 'The specified plot window does not exist.') elif isinstance(window, int): if window >= 0: window_id = window if window_id < 0: raise WrapperError( 'Invalid parameter.\n' 'Pass a plot window name or a plot window id (>0)') return self.server.plot.getPlotsFromWindow( {'windowID': window_id}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_plot_window_id_from_name(self, window_name): """Find the corresponding window ID for a certain window name. Args: window_name (str, int): The name of the plot window (case sensitive) or its ID. Returns: int: The ID of the window (when an ID is specified as window_name, then it returns this ID). The return value is -1 if the window does not exist. Example: >>> xxsim.get_plot_window_id_from_name('3D Animation') 2 >>> xxsim.get_plot_window_id_from_name(2) 2 """ window_id = -1 try: if isinstance(window_name, str): window_list = self.get_plot_windows() name_count = 0 for window in window_list: if window['windowName'] == window_name: name_count += 1 if name_count == 1: # We found a window with the corresponding name window_id = window['windowID'] elif name_count == 2: print('Warning: multiple plot windows found ' 'with the specified name. ' 'Selected the first plot window.') break elif isinstance(window_name, int): return window_name else: raise WrapperError('The specified plot window name is neither ' 'a string nor a number.') return window_id except Exception as error: self.errorhandler(error)
return False
[docs] def get_plot_id_from_name(self, window_name, plot_name): """ Find the corresponding plot ID for a certain window and plot name. Args: window_name (str -or- int): The name of the plot window (str, case sensitive) or the plot window id (int) plot_name (str): The name of the plot (case sensitive) Returns: int: The ID of the plot (when an ID is specified as plot_name, then it returns this ID). The return value is -1 if the plot does not exist. Example: >>> xxsim.get_plot_id_from_name('Crank Rod - Plots', 'omega') 10 """ try: # Get the window id window_id = self.get_plot_window_id_from_name(window_name) if window_id == -1: raise WrapperError('Could not find the specified plot window.') # Get the plot id, either via the name of the plot or directly plot_id = -1 # If the input is a string, we got the name of a plot if isinstance(plot_name, str): # Get all id, name pairs for all plots plot_list = self.get_plots_from_window(window_id) name_count = 0 for plot in plot_list: if plot['plotName'] == plot_name: name_count += 1 if name_count > 1: print( 'Multiple plots found with the specified name.' 'Selected the first matching plot.') break plot_id = plot['plotID'] elif isinstance(plot_name, int): # the plot name is already a number plot_id = plot_name else: raise WrapperError('The specified plot name is invalid.') return plot_id except Exception as error: self.errorhandler(error)
return False
[docs] def get_plots(self): """ Get all plots in all plot windows that are present in the simulator. Returns: list of dict: list with all plots for the current model with the following fields: * ``windowName`` *str* the name of the plot window * ``windowID`` *int* the corresponding plot identifier * ``plotName`` *str* the name of the plot * ``plotID`` *int* the id of the plot Example: >>> xxsim.get_plots() [{'windowName': 'Crank Rod - Plots', 'plotName': '3D Animation','windowID': 1,'plotID': 6}, {'windowName': 'Crank Rod - Plots', 'plotName': 'x (load)','windowID': 1,'plotID': 7}, {'windowName': 'Crank Rod - Plots', 'plotName': 'x (sledge)','windowID': 1,'plotID': 8}, {'windowName': 'Crank Rod - Plots', 'plotName': 'T (motor)','windowID': 1,'plotID': 9}, {'windowName': 'Crank Rod - Plots', 'plotName': 'omega','windowID': 1,'plotID': 10}, {'windowName': '3D Animation', 'plotName': '3D Animation','windowID': 2,'plotID': 4}, {'windowName': 'Window 3', 'plotName': '3D Animation','windowID': 3,'plotID': 5}] """ try: window_list = self.get_plot_windows() all_plots = [] # Iterate all windows for window in window_list: # to retrieve the names and id's of all plots plot_list = self.get_plots_from_window(window['windowID']) for plot in plot_list: # to add them to one list all_plots.append({'windowName': window['windowName'], 'windowID': window['windowID'], 'plotID': plot['plotID'], 'plotName': plot['plotName']}) return all_plots except Exception as error: self.errorhandler(error)
return False
[docs] def save_plot_as_bitmap( self, filename, window, plot=-1, width=-1, height=-1, dpi=-1): """Save a plot or plot window as bitmap. Supported formats: png, bmp, jpg, gif Args: filename (str): Filename of the bitmap to store. Supported file extensions: .png, .bmp, .jpg, .gif window (str -or- int): The name or id of the plot window to select plot (str -or- int, , optional): The name or id of the plot to save. Leave empty to store all plots in this plot window (default). width (int, optional): The preferred width of the plot in pixels. 0 or negative => use same size as the plot (default). height (int, optional): The preferred height of the plot in pixels. 0 or negative => use same size as the plot (default). dpi (int, optional): The dpi the bitmap should be stored in (e.g. 300). Zero or negative means: use the resolution of the screen (default). Returns: bool: True when the plot was saved successfully, False otherwise. Example: >>> xxsim.save_plot_as_bitmap('c:\\temp\\myplot.png', 'Window 1', 'plot 1', 640, 480) True """ try: window_id = -1 plot_id = -1 if isinstance(window, str): window_id = self.get_plot_window_id_from_name(window) elif isinstance(window, int): window_id = window else: raise WrapperError('Window should be a string or number.') if isinstance(plot, str): plot_id = self.get_plot_id_from_name(window_id, plot) elif isinstance(plot, int): plot_id = plot else: raise WrapperError('Plot should be a string or number.') if not isinstance(height, int): raise WrapperError('Plot height should be an integer number.') if not isinstance(width, int): raise WrapperError('Plot width should be an integer number.') if not isinstance(dpi, int): raise WrapperError('Plot dpi should be an integer number.') return self.server.plot.savePlotAsBitmap( {'fileName': filename, 'windowID': window_id, 'plotID': plot_id, 'width': width, 'height': height, 'dpi': dpi}) except Exception as error: self.errorhandler(error)
return False
[docs] def save_submodel(self, submodel, filename, save_encrypted=0): """ Save the given submodel as a separate file Args: submodel (str): The hierarchical submodel name filename (str): The filename (with full path) that should be used to save the selected submodel. save_encrypted (int, optional): 2 if the submodel should be encrypted and C-code generation is allowed, 1 if the submodel should be encrypted, but C-code generation is not allowed, 0 if the submodel should not be encrypted. Default is 0. Returns: bool: True on success, False otherwise Example: >>> result = xxsim.save_submodel('mysubmodel', 'c:\\\\temp\\\\mysubmodel.emx') """ try: if not isinstance(submodel, str): raise WrapperError('submodel should be a string.') if not isinstance(filename, str): raise WrapperError('filename should be a string.') if not isinstance(save_encrypted, int): raise WrapperError('save_encrypted should be an integer.') if save_encrypted not in range(3): raise WrapperError('save_encrypted should be between 0 and 2.') # the actual XML-RPC call return self.server.model.saveSubmodel( {'submodelName': submodel, 'fileName': filename, 'encryption': save_encrypted}) except Exception as error: self.errorhandler(error)
return False
[docs] def replace_submodel(self, submodel, filename, implementation=''): """ Replace an existing submodel with the version from the provided file Args: submodel (str): The hierarchical submodel name filename (str): The filename (with full path) that should be used to save the selected submodel. implementation (str, optional): The implementation that should be made active when the stored submodel has multiple implementations Returns: bool: True on success, False otherwise Example: >>> result = xxsim.replace_submodel('mysubmodel', 'c:\\\\temp\\\\mysubmodel.emx') >>> result = xxsim.replace_submodel('mysubmodel', 'c:\\\\temp\\\\mysubmodel.emx', 'mysubmodel implementation') """ try: if not isinstance(submodel, str): raise WrapperError('submodel should be a string.') if not isinstance(filename, str): raise WrapperError('filename should be a string.') if not isinstance(implementation, str): raise WrapperError('implementation should be a string.') # the actual XML-RPC call return self.server.model.replaceSubmodel( {'submodelName': submodel, 'fileName': filename, 'implementation': implementation}) except Exception as error: self.errorhandler(error)
return False
[docs] def goto_submodel(self, submodel): """ Select the provided submodel in the tree and show its contents Args: submodel (str): The hierarchical submodel name Returns: bool: True on success, False otherwise Example: >>> result = xxsim.goto_submodel('mysubmodel') """ try: if not isinstance(submodel, str): raise WrapperError('submodel should be a string.') # the actual XML-RPC call return self.server.gui.gotoSubmodel({'submodelName': submodel}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_submodel_properties(self, submodel_name='', properties={}): """ Get the submodel properties description fields. Args: submodel_name (str, optional): The hierarchical submodel name. If no name is given or the name is the empty string, then the top-level model is used. properties (str or dict, optional): The specific set of properties that is returned. If left empty or not specified, all properties will be returned, otherwise only the given set of properties is returned. See the return section about what keys can be queried. Returns: dict: Submodel properties description fields: * ``name`` *str* the submodel name * ``description`` *str* the description field * ``title`` *str* the title field * ``keywords`` *str* the keywords field * ``version`` *dict* dict with fields 'major', 'minor', and 'patch' * ``author`` *str* the author field * ``manager`` *str* the manager field * ``project`` *str* the project field * ``company`` *str* the company field * ``department`` *str* the string field * ``helppage`` *str* the helppage field Example: >>> result = xxsim.get_submodel_properties('CrankRod') {'name':'CrankRod', 'description':'A submodel of a crankrod mechanism.', 'title':'CrankRod', 'keywords':'mechanics', 'version':{'major':'1','minor':'0','patch':'0'}, 'author':'Dummy', 'manager':'Dummy', 'project':'Crank Rod Mechanism with Sledge', 'company':'Controllab Products B.V.', 'department':'Software', 'helppage':''} >>> result = xxsim.get_submodel_properties('CrankRod','name') {'name':'CrankRod'} """ #try: if not isinstance(submodel_name, str): raise WrapperError( 'get_submodel_properties expects a string input argument.') # If the properties section is a string... if isinstance(properties, str): # Make sure that it becomes a dictionary. properties_dict = {properties} # Otherwise keep it a dictionary. else: properties_dict = properties # The actual XML-RPC call return self.server.model.getSubmodelProperties({'submodelName': submodel_name,
'keys': properties_dict})
[docs] def set_window_size(self, left, top, width, height): """ Set the size of the last opened window Args: left (int): top (int): width (int): height (int): Returns: bool: True on success, False otherwise Example: >>> result = xxsim.set_window_size(0, 0, 640, 480) """ try: if not (isinstance(left, int) and isinstance(top, int) and isinstance(width, int) and isinstance(height, int)): raise WrapperError( 'set_window_size arguments should be integers.') # the actual XML-RPC call return self.server.util.setWindowSize({ 'left': left, 'top': top, 'width': width, 'height': height}) except Exception as error: self.errorhandler(error)
return False
[docs] def get_window_size(self): """ Returns the current size of the last opened window Args: left (int): top (int): width (int): height (int): Returns: dict: size information with the following fields: * ``left`` *int* distance from the left screen border * ``top`` *int* distance from the top screen border * ``width`` *int* the width of the window * ``height`` *int* the height of the window Example: >>> xxsim.get_window_size() {'top': 0, 'height': 480, 'left': 0, 'width': 640} """ try: # the actual XML-RPC call return self.server.util.getWindowSize() except Exception as error: self.errorhandler(error)
return False