From 5f84e396bcfc1414f375c0db5df93e0ab5ab28de Mon Sep 17 00:00:00 2001 From: Torsten Sommer Date: Wed, 16 May 2018 17:09:10 +0200 Subject: [PATCH] Add remaining FMI functions - Add getTypesPlatform(), getVersion(), setDebugLogging(), get*Status(), cancelStep(), getDirectionalDerivative(), getNominalContinuousStates() and getStateValueReferences() - re-order functions and add comments (align with C header files) - update doc strings --- docs/changelog.md | 2 +- fmpy/fmi1.py | 271 ++++++++++++------ fmpy/fmi2.py | 250 ++++++++++------ ...nd_setters.py => test_common_functions.py} | 39 ++- 4 files changed, 380 insertions(+), 182 deletions(-) rename tests/{test_getters_and_setters.py => test_common_functions.py} (63%) diff --git a/docs/changelog.md b/docs/changelog.md index b3bb8deb..e6d5b61f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ ## v0.2.4 (unreleased) -- `NEW` getDirectionalDerivative() +- `NEW` Remaining FMI functions added ## v0.2.3 (2018-04-11) diff --git a/fmpy/fmi1.py b/fmpy/fmi1.py index 29c12737..8281f10c 100644 --- a/fmpy/fmi1.py +++ b/fmpy/fmi1.py @@ -33,6 +33,12 @@ # fmi1StepFinishedTYPE = CFUNCTYPE(None, fmi1Component, fmi1Status) fmi1StepFinishedTYPE = c_void_p +fmi1StatusKind = c_int + +fmi1DoStepStatus = 0 +fmi1PendingStatus = 1 +fmi1LastSuccessfulTime = 2 + class fmi1CallbackFunctions(Structure): @@ -142,7 +148,8 @@ def freeLibrary(self): # unload the shared library freeLibrary(self.dll._handle) - def _print_fmi_args(self, fname, argnames, argtypes, args, restype, res): + def _log_fmi_args(self, fname, argnames, argtypes, args, restype, res): + """ Format FMI arguments and pass them to the logger """ f = fname + '(' @@ -212,70 +219,91 @@ def __init__(self, **kwargs): super(_FMU1, self).__init__(**kwargs) - # common FMI 1.0 functions + # Inquire version numbers of header files + self._fmi1Function('GetVersion', [], [], fmi1String) + + self._fmi1Function('SetDebugLogging', ['component', 'loggingOn'], [fmi1Component, fmi1Boolean], fmi1Status) + # Data Exchange Functions self._fmi1Function('GetReal', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Real)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Real)]) self._fmi1Function('GetInteger', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer)]) self._fmi1Function('GetBoolean', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Boolean)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Boolean)]) self._fmi1Function('GetString', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1String)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1String)]) self._fmi1Function('SetReal', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Real)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Real)]) self._fmi1Function('SetInteger', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer)]) self._fmi1Function('SetBoolean', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Boolean)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Boolean)]) self._fmi1Function('SetString', ['component', 'vr', 'nvr', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1String)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1String)]) - def _fmi1Function(self, name, argnames, argtypes, restype): + def _fmi1Function(self, name, argnames, argtypes, restype=fmi1Status): + """ Add an FMI 1.0 function to this instance and add a wrapper that allows + logging and checks the return code if the return type if fmi1Status + + Parameters: + fname the name of the function (without 'fmi' prefix) + argnames names of the arguments + argtypes types of the arguments + restype return type + """ + # get the exported function form the shared library f = getattr(self.dll, self.modelIdentifier + '_fmi' + name) f.argtypes = argtypes f.restype = restype - def w(*args, **kwargs): + def w(*args): + """ Wrapper function for the FMI call """ - res = f(*args, **kwargs) + # call the FMI function + res = f(*args) if self.fmiCallLogger is not None: - self._print_fmi_args('fmi' + name, argnames, argtypes, args, restype, res) + # log the call + self._log_fmi_args('fmi' + name, argnames, argtypes, args, restype, res) if restype == fmi1Status: # check the status code - if res > 1: + if res > fmi1Warning: raise Exception("FMI call failed with status %d." % res) return res + # add the function to the instance setattr(self, 'fmi1' + name, w) + # Inquire version numbers of header files + + def getVersion(self): + version = self.fmi1GetVersion() + return version.decode('utf-8') + + def setDebugLogging(self, loggingOn): + self.fmi1SetDebugLogging(self.component, fmi1True if loggingOn else fmi1False) + + # Data Exchange Functions + def getReal(self, vr): vr = (fmi1ValueReference * len(vr))(*vr) value = (fmi1Real * len(vr))() @@ -328,13 +356,16 @@ def setString(self, vr, value): class FMU1Slave(_FMU1): - """ Base class for FMI 1.0 co-simulation FMUs """ + """ An FMI 1.0 Co-Simulation FMU """ def __init__(self, **kwargs): super(FMU1Slave, self).__init__(**kwargs) - # FMI 1.0 Co-Simulation functions + # Inquire version numbers of header files + self._fmi1Function('GetTypesPlatform', [], [], fmi1String) + + # Creation and destruction of slave instances and setting debug status self._fmi1Function('InstantiateSlave', ['instanceName', 'guid', 'fmuLocation', 'mimeType', 'timeout', 'visible', 'interactive', 'functions', 'loggingOn'], [fmi1String, fmi1String, fmi1String, fmi1String, fmi1Real, fmi1Boolean, fmi1Boolean, fmi1CallbackFunctions, fmi1Boolean], @@ -342,13 +373,7 @@ def __init__(self, **kwargs): self._fmi1Function('InitializeSlave', ['component', 'tStart', 'stopTimeDefined', 'tStop'], - [fmi1Component, fmi1Real, fmi1Boolean, fmi1Real], - fmi1Status) - - self._fmi1Function('DoStep', - ['component', 'currentCommunicationPoint', 'communicationStepSize', 'newStep'], - [fmi1Component, fmi1Real, fmi1Real, fmi1Boolean], - fmi1Status) + [fmi1Component, fmi1Real, fmi1Boolean, fmi1Real]) self._fmi1Function('TerminateSlave', ['component'], [fmi1Component], fmi1Status) @@ -358,13 +383,39 @@ def __init__(self, **kwargs): self._fmi1Function('SetRealInputDerivatives', ['c', 'vr', 'nvr', 'order', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer), POINTER(fmi1Real)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer), POINTER(fmi1Real)]) self._fmi1Function('GetRealOutputDerivatives', ['c', 'vr', 'nvr', 'order', 'value'], - [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer), POINTER(fmi1Real)], - fmi1Status) + [fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1Integer), POINTER(fmi1Real)]) + + self._fmi1Function('CancelStep', ['component'], [fmi1Component]) + + self._fmi1Function('DoStep', + ['component', 'currentCommunicationPoint', 'communicationStepSize', 'newStep'], + [fmi1Component, fmi1Real, fmi1Real, fmi1Boolean]) + + self._fmi1Function('GetStatus', + ['component', 'kind', 'value'], + [fmi1Component, fmi1StatusKind, POINTER(fmi1Status)]) + + self._fmi1Function('GetRealStatus', + ['component', 'kind', 'value'], + [fmi1Component, fmi1StatusKind, POINTER(fmi1Real)]) + + self._fmi1Function('GetIntegerStatus', + ['component', 'kind', 'value'], + [fmi1Component, fmi1StatusKind, POINTER(fmi1Integer)]) + + self._fmi1Function('GetBooleanStatus', + ['component', 'kind', 'value'], + [fmi1Component, fmi1StatusKind, POINTER(fmi1Boolean)]) + + self._fmi1Function('GetStringStatus', + ['component', 'kind', 'value'], + [fmi1Component, fmi1StatusKind, POINTER(fmi1String)]) + + # Creation and destruction of slave instances and setting debug status def instantiate(self, mimeType='application/x-fmu-sharedlibrary', timeout=0, visible=fmi1False, interactive=fmi1False, functions=None, loggingOn=False): @@ -390,6 +441,14 @@ def instantiate(self, mimeType='application/x-fmu-sharedlibrary', timeout=0, vis functions, fmi1True if loggingOn else fmi1False) + # Inquire version numbers of header files + + def getTypesPlatform(self): + types_platform = self.fmi1GetTypesPlatform() + return types_platform.decode('utf-8') + + # Creation and destruction of slave instances and setting debug status + def initialize(self, tStart=0.0, stopTime=None): stopTimeDefined = fmi1True if stopTime is not None else fmi1False tStop = stopTime if stopTime is not None else 0.0 @@ -418,12 +477,40 @@ def getRealOutputDerivatives(self, vr, order): self.fmi1GetRealOutputDerivatives(self.component, vr, len(vr), order, value) return list(value) + def cancelStep(self): + return self.fmi1CancelStep(self.component) + def doStep(self, currentCommunicationPoint, communicationStepSize, newStep=fmi1True): return self.fmi1DoStep(self.component, currentCommunicationPoint, communicationStepSize, newStep) + def getStatus(self, kind): + value = fmi1Status(fmi1OK) + self.fmi1GetStatus(self.component, kind, byref(value)) + return value + + def getRealStatus(self, kind): + value = fmi1Real(0.0) + self.fmi1GetRealStatus(self.component, kind, byref(value)) + return value + + def getIntegerStatus(self, kind): + value = fmi1Integer(0) + self.fmi1GetIntegerStatus(self.component, kind, byref(value)) + return value + + def getBooleanStatus(self, kind): + value = fmi1Boolean(fmi1False) + self.fmi1GetBooleanStatus(self.component, kind, byref(value)) + return value + + def getStringStatus(self, kind): + value = fmi1String(b'') + self.fmi1GetStringStatus(self.component, kind, byref(value)) + return value + class FMU1Model(_FMU1): - """ Base class for FMI 1.0 model exchange FMUs """ + """ An FMI 1.0 Model Exchange FMU """ def __init__(self, **kwargs): @@ -431,60 +518,66 @@ def __init__(self, **kwargs): self.eventInfo = fmi1EventInfo() + # Inquire version numbers of header files + self._fmi1Function('GetModelTypesPlatform', [], [], fmi1String) + + # Creation and destruction of model instances and setting debug status self._fmi1Function('InstantiateModel', ['instanceName', 'guid', 'functions', 'loggingOn'], [fmi1String, fmi1String, fmi1CallbackFunctions, fmi1Boolean], fmi1Component) - self._fmi1Function('SetTime', - ['component', 'time'], - [fmi1Component, fmi1Real], - fmi1Status) - - self._fmi1Function('Initialize', - ['component', 'toleranceControlled', 'relativeTolerance', 'eventInfo'], - [fmi1Component, fmi1Boolean, fmi1Real, POINTER(fmi1EventInfo)], - fmi1Status) - - self._fmi1Function('GetContinuousStates', - ['component', 'states', 'nx'], - [fmi1Component, POINTER(fmi1Real), c_size_t], - fmi1Status) + self._fmi1Function('FreeModelInstance', ['component'], [fmi1Component], None) - self._fmi1Function('GetDerivatives', - ['component', 'derivatives', 'nx'], - [fmi1Component, POINTER(fmi1Real), c_size_t], - fmi1Status) + # Providing independent variables and re-initialization of caching + self._fmi1Function('SetTime', ['component', 'time'], [fmi1Component, fmi1Real]) self._fmi1Function('SetContinuousStates', ['component', 'x', 'nx'], - [fmi1Component, POINTER(fmi1Real), c_size_t], - fmi1Status) + [fmi1Component, POINTER(fmi1Real), c_size_t]) self._fmi1Function('CompletedIntegratorStep', ['component', 'callEventUpdate'], - [fmi1Component, POINTER(fmi1Boolean)], - fmi1Status) + [fmi1Component, POINTER(fmi1Boolean)]) + + # Evaluation of the model equations + self._fmi1Function('Initialize', + ['component', 'toleranceControlled', 'relativeTolerance', 'eventInfo'], + [fmi1Component, fmi1Boolean, fmi1Real, POINTER(fmi1EventInfo)]) + + self._fmi1Function('GetDerivatives', + ['component', 'derivatives', 'nx'], + [fmi1Component, POINTER(fmi1Real), c_size_t]) self._fmi1Function('GetEventIndicators', ['component', 'eventIndicators', 'ni'], - [fmi1Component, POINTER(fmi1Real), c_size_t], - fmi1Status) + [fmi1Component, POINTER(fmi1Real), c_size_t]) self._fmi1Function('EventUpdate', ['component', 'intermediateResults', 'eventInfo'], - [fmi1Component, fmi1Boolean, POINTER(fmi1EventInfo)], - fmi1Status) + [fmi1Component, fmi1Boolean, POINTER(fmi1EventInfo)]) + + self._fmi1Function('GetContinuousStates', + ['component', 'states', 'nx'], + [fmi1Component, POINTER(fmi1Real), c_size_t]) + + self._fmi1Function('GetNominalContinuousStates', + ['component', 'x_nominal', 'nx'], + [fmi1Component, POINTER(fmi1Real), c_size_t]) + + self._fmi1Function('GetStateValueReferences', + ['component', 'x_nominal', 'nx'], + [fmi1Component, POINTER(fmi1ValueReference), c_size_t]) + + self._fmi1Function('Terminate', ['component'], [fmi1Component]) - self._fmi1Function('Terminate', - ['component'], - [fmi1Component], - fmi1Status) + # Inquire version numbers of header files - self._fmi1Function('FreeModelInstance', - ['component'], - [fmi1Component], - None) + def getTypesPlatform(self): + types_platform = self.fmi1GetModelTypesPlatform() + return types_platform.decode('utf-8') + + # Creation and destruction of model instances and setting debug status def instantiate(self, functions=None, loggingOn=False): @@ -502,35 +595,45 @@ def instantiate(self, functions=None, loggingOn=False): self.callbacks, fmi1True if loggingOn else fmi1False) - def setTime(self, time): - return self.fmi1SetTime(self.component, time) + def freeInstance(self): + self.fmi1FreeModelInstance(self.component) + self.freeLibrary() - def initialize(self, toleranceControlled=fmi1False, relativeTolerance=0.0): - return self.fmi1Initialize(self.component, toleranceControlled, relativeTolerance, byref(self.eventInfo)) + # Providing independent variables and re-initialization of caching - def getContinuousStates(self, states, size): - return self.fmi1GetContinuousStates(self.component, states, size) + def setTime(self, time): + return self.fmi1SetTime(self.component, time) def setContinuousStates(self, states, size): return self.fmi1SetContinuousStates(self.component, states, size) - def getDerivatives(self, derivatives, size): - return self.fmi1GetDerivatives(self.component, derivatives, size) - def completedIntegratorStep(self): stepEvent = fmi1Boolean() self.fmi1CompletedIntegratorStep(self.component, byref(stepEvent)) return stepEvent != fmi1False + # Evaluation of the model equations + + def initialize(self, toleranceControlled=fmi1False, relativeTolerance=0.0): + return self.fmi1Initialize(self.component, toleranceControlled, relativeTolerance, byref(self.eventInfo)) + + def getDerivatives(self, derivatives, size): + return self.fmi1GetDerivatives(self.component, derivatives, size) + def getEventIndicators(self, eventIndicators, size): return self.fmi1GetEventIndicators(self.component, eventIndicators, size) def eventUpdate(self, intermediateResults=fmi1False): return self.fmi1EventUpdate(self.component, intermediateResults, byref(self.eventInfo)) + def getContinuousStates(self, states, size): + return self.fmi1GetContinuousStates(self.component, states, size) + + def getNominalContinuousStates(self, x_nominal, size): + return self.fmi1GetNominalContinuousStates(self.component, x_nominal, size) + + def getStateValueReferences(self, vrx, size): + return self.fmi1GetStateValueReferences(self.component, vrx, size) + def terminate(self): return self.fmi1Terminate(self.component) - - def freeInstance(self): - self.fmi1FreeModelInstance(self.component) - self.freeLibrary() diff --git a/fmpy/fmi2.py b/fmpy/fmi2.py index 332781f3..110a2361 100644 --- a/fmpy/fmi2.py +++ b/fmpy/fmi2.py @@ -18,7 +18,7 @@ fmi2Type = c_int fmi2Byte = c_char -fmi2Status = c_int +fmi2Status = c_int fmi2OK = 0 fmi2Warning = 1 @@ -39,6 +39,7 @@ fmi2False = 0 fmi2StatusKind = c_int + fmi2DoStepStatus = 0 fmi2PendingStatus = 1 fmi2LastSuccessfulTime = 2 @@ -84,6 +85,15 @@ def __init__(self, **kwargs): super(_FMU2, self).__init__(**kwargs) # Inquire version numbers of header files and setting logging status + self._fmi2Function('fmi2GetTypesPlatform', [], [], fmi2String) + + self._fmi2Function('fmi2GetVersion', [], [], fmi2String) + + self._fmi2Function('fmi2SetDebugLogging', + ['component', 'loggingOn', 'nCategories', 'categories'], + [fmi2Component, fmi2Boolean, c_size_t, POINTER(fmi2String)]) + + # Creation and destruction of FMU instances and setting debug status self._fmi2Function('fmi2Instantiate', ['instanceName', 'fmuType', 'guid', 'resourceLocation', 'callbacks', 'visible', 'loggingOn'], [fmi2String, fmi2Type, fmi2String, fmi2String, POINTER(fmi2CallbackFunctions), fmi2Boolean, fmi2Boolean], @@ -94,8 +104,7 @@ def __init__(self, **kwargs): # Enter and exit initialization mode, terminate and reset self._fmi2Function('fmi2SetupExperiment', ['component', 'toleranceDefined', 'tolerance', 'startTime', 'stopTimeDefined', 'stopTime'], - [fmi2Component, fmi2Boolean, fmi2Real, fmi2Real, fmi2Boolean, fmi2Real], - fmi2Status) + [fmi2Component, fmi2Boolean, fmi2Real, fmi2Real, fmi2Boolean, fmi2Real]) self._fmi2Function('fmi2EnterInitializationMode', ['component'], [fmi2Component], fmi2Status) @@ -108,104 +117,119 @@ def __init__(self, **kwargs): # Getting and setting variable values self._fmi2Function('fmi2GetReal', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real)]) self._fmi2Function('fmi2GetInteger', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer)]) self._fmi2Function('fmi2GetBoolean', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Boolean)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Boolean)]) self._fmi2Function('fmi2GetString', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2String)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2String)]) self._fmi2Function('fmi2SetReal', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real)]) self._fmi2Function('fmi2SetInteger', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer)]) self._fmi2Function('fmi2SetBoolean', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Boolean)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Boolean)]) self._fmi2Function('fmi2SetString', ['component', 'vr', 'nvr', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2String)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2String)]) # Getting and setting the internal FMU state self._fmi2Function('fmi2GetFMUstate', ['component', 'FMUstate'], - [fmi2Component, POINTER(fmi2FMUstate)], - fmi2Status) + [fmi2Component, POINTER(fmi2FMUstate)]) self._fmi2Function('fmi2SetFMUstate', ['component', 'FMUstate'], - [fmi2Component, fmi2FMUstate], - fmi2Status) + [fmi2Component, fmi2FMUstate]) self._fmi2Function('fmi2FreeFMUstate', ['component', 'FMUstate'], - [fmi2Component, POINTER(fmi2FMUstate)], - fmi2Status) + [fmi2Component, POINTER(fmi2FMUstate)]) self._fmi2Function('fmi2SerializedFMUstateSize', ['component', 'FMUstate', 'size'], - [fmi2Component, fmi2FMUstate, POINTER(c_size_t)], - fmi2Status) + [fmi2Component, fmi2FMUstate, POINTER(c_size_t)]) self._fmi2Function('fmi2SerializeFMUstate', ['component', 'FMUstate', 'serializedState', 'size'], - [fmi2Component, fmi2FMUstate, POINTER(fmi2Byte), c_size_t], - fmi2Status) + [fmi2Component, fmi2FMUstate, POINTER(fmi2Byte), c_size_t]) self._fmi2Function('fmi2DeSerializeFMUstate', ['component', 'FMUstate', 'serializedState', 'size'], - [fmi2Component, POINTER(fmi2Byte), c_size_t, POINTER(fmi2FMUstate)], - fmi2Status) + [fmi2Component, POINTER(fmi2Byte), c_size_t, POINTER(fmi2FMUstate)]) # Getting partial derivatives self._fmi2Function('fmi2GetDirectionalDerivative', ['component', 'vUnknown_ref', 'nUnknown', 'vKnown_ref', 'nKnown', 'dvKnown', 'dvUnknown'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real), POINTER(fmi2Real)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real), POINTER(fmi2Real)]) - def _fmi2Function(self, fname, argnames, argtypes, restype): + def _fmi2Function(self, fname, argnames, argtypes, restype=fmi2Status): + """ Add an FMI 2.0 function to this instance and add a wrapper that allows + logging and checks the return code if the return type is fmi2Status + + Parameters: + fname the name of the function + argnames names of the arguments + argtypes types of the arguments + restype return type + """ if not hasattr(self.dll, fname): setattr(self, fname, None) return + # get the exported function form the shared library f = getattr(self.dll, fname) f.argtypes = argtypes f.restype = restype - def w(*args, **kwargs): + def w(*args): + """ Wrapper function for the FMI call """ - res = f(*args, **kwargs) + # call the FMI function + res = f(*args) if self.fmiCallLogger is not None: - self._print_fmi_args(fname, argnames, argtypes, args, restype, res) + # log the call + self._log_fmi_args(fname, argnames, argtypes, args, restype, res) if restype == fmi2Status: # status code # check the status code - if res > 1: + if res > fmi2Warning: raise Exception("FMI call failed with status %d." % res) return res setattr(self, fname, w) + # Inquire version numbers of header files and setting logging status + + def getTypesPlatform(self): + types_platform = self.fmi2GetTypesPlatform() + return types_platform.decode('utf-8') + + def getVersion(self): + version = self.fmi2GetVersion() + return version.decode('utf-8') + + def setDebugLogging(self, loggingOn, categories): + categories_ = (fmi2String * len(categories))() + categories_[:] = [c.encode('utf-8') for c in categories] + self.fmi2SetDebugLogging(self.component, fmi2True if loggingOn else fmi2False, len(categories), categories_) + + # Creation and destruction of FMU instances and setting debug status + def instantiate(self, visible=False, callbacks=None, loggingOn=False): kind = fmi2ModelExchange if isinstance(self, FMU2Model) else fmi2CoSimulation @@ -227,6 +251,12 @@ def instantiate(self, visible=False, callbacks=None, loggingOn=False): fmi2True if visible else fmi2False, fmi2True if loggingOn else fmi2False) + def freeInstance(self): + self.fmi2FreeInstance(self.component) + self.freeLibrary() + + # Enter and exit initialization mode, terminate and reset + def setupExperiment(self, tolerance=None, startTime=0.0, stopTime=None): toleranceDefined = fmi2True if tolerance is not None else fmi2False @@ -247,11 +277,13 @@ def enterInitializationMode(self): def exitInitializationMode(self): return self.fmi2ExitInitializationMode(self.component) + def terminate(self): + return self.fmi2Terminate(self.component) + def reset(self): return self.fmi2Reset(self.component) - def terminate(self): - return self.fmi2Terminate(self.component) + # Getting and setting variable values def getReal(self, vr): vr = (fmi2ValueReference * len(vr))(*vr) @@ -299,6 +331,7 @@ def setString(self, vr, value): self.fmi2SetString(self.component, vr, len(vr), value) # Getting and setting the internal FMU state + def getFMUstate(self): state = fmi2FMUstate() self.fmi2GetFMUstate(self.component, byref(state)) @@ -310,10 +343,6 @@ def setFMUstate(self, state): def freeFMUstate(self, state): self.fmi2FreeFMUstate(self.component, byref(state)) - def freeInstance(self): - self.fmi2FreeInstance(self.component) - self.freeLibrary() - def serializeFMUstate(self, state): """ Serialize an FMU state @@ -341,8 +370,10 @@ def deSerializeFMUstate(self, serializedState, state): buffer = create_string_buffer(serializedState) self.fmi2DeSerializeFMUstate(self.component, buffer, len(buffer), byref(state)) + # Getting partial derivatives + def getDirectionalDerivative(self, vUnknown_ref, vKnown_ref, dvKnown): - """ Get the partial derivative + """ Get partial derivatives Parameters: vUnknown_ref a list of value references of the unknowns @@ -364,7 +395,7 @@ def getDirectionalDerivative(self, vUnknown_ref, vKnown_ref, dvKnown): class FMU2Model(_FMU2): - """ Base class for FMI 2.0 model exchange FMUs """ + """ An FMI 2.0 Model Exchange FMU """ def __init__(self, **kwargs): @@ -374,48 +405,44 @@ def __init__(self, **kwargs): self._fmi2Function('fmi2NewDiscreteStates', ['component', 'eventInfo'], - [fmi2Component, POINTER(fmi2EventInfo)], - fmi2Status) + [fmi2Component, POINTER(fmi2EventInfo)]) self._fmi2Function('fmi2EnterContinuousTimeMode', ['component'], - [fmi2Component], - fmi2Status) + [fmi2Component]) self._fmi2Function('fmi2EnterEventMode', ['component'], - [fmi2Component], - fmi2Status) + [fmi2Component]) self._fmi2Function('fmi2GetContinuousStates', ['component', 'x', 'nx'], - [fmi2Component, POINTER(fmi2Real), c_size_t], - fmi2Status) + [fmi2Component, POINTER(fmi2Real), c_size_t]) self._fmi2Function('fmi2SetContinuousStates', ['component', 'x', 'nx'], - [fmi2Component, POINTER(fmi2Real), c_size_t], - fmi2Status) + [fmi2Component, POINTER(fmi2Real), c_size_t]) self._fmi2Function('fmi2GetDerivatives', ['component', 'derivatives', 'nx'], - [fmi2Component, POINTER(fmi2Real), c_size_t], - fmi2Status) + [fmi2Component, POINTER(fmi2Real), c_size_t]) self._fmi2Function('fmi2GetEventIndicators', ['component', 'eventIndicators', 'ni'], - [fmi2Component, POINTER(fmi2Real), c_size_t], - fmi2Status) + [fmi2Component, POINTER(fmi2Real), c_size_t]) self._fmi2Function('fmi2SetTime', ['component', 'time'], - [fmi2Component, fmi2Real], - fmi2Status) + [fmi2Component, fmi2Real]) self._fmi2Function('fmi2CompletedIntegratorStep', ['component', 'noSetFMUStatePriorToCurrentPoint', 'enterEventMode', 'terminateSimulation'], - [fmi2Component, fmi2Boolean, POINTER(fmi2Boolean), POINTER(fmi2Boolean)], - fmi2Status) + [fmi2Component, fmi2Boolean, POINTER(fmi2Boolean), POINTER(fmi2Boolean)]) + + # Enter and exit the different modes + + def enterEventMode(self): + return self.fmi2EnterEventMode(self.component) def newDiscreteStates(self): return self.fmi2NewDiscreteStates(self.component, byref(self.eventInfo)) @@ -423,33 +450,37 @@ def newDiscreteStates(self): def enterContinuousTimeMode(self): return self.fmi2EnterContinuousTimeMode(self.component) - def enterEventMode(self): - return self.fmi2EnterEventMode(self.component) + def completedIntegratorStep(self, noSetFMUStatePriorToCurrentPoint=fmi2True): + enterEventMode = fmi2Boolean() + terminateSimulation = fmi2Boolean() + self.fmi2CompletedIntegratorStep(self.component, noSetFMUStatePriorToCurrentPoint, byref(enterEventMode), byref(terminateSimulation)) + return enterEventMode.value, terminateSimulation.value - def getContinuousStates(self, x, nx): - return self.fmi2GetContinuousStates(self.component, x, nx) + # Providing independent variables and re-initialization of caching + + def setTime(self, time): + return self.fmi2SetTime(self.component, time) def setContinuousStates(self, x, nx): return self.fmi2SetContinuousStates(self.component, x, nx) + # Evaluation of the model equations + def getDerivatives(self, dx, nx): return self.fmi2GetDerivatives(self.component, dx, nx) def getEventIndicators(self, z, nz): return self.fmi2GetEventIndicators(self.component, z, nz) - def setTime(self, time): - return self.fmi2SetTime(self.component, time) + def getContinuousStates(self, x, nx): + return self.fmi2GetContinuousStates(self.component, x, nx) - def completedIntegratorStep(self, noSetFMUStatePriorToCurrentPoint=fmi2True): - enterEventMode = fmi2Boolean() - terminateSimulation = fmi2Boolean() - self.fmi2CompletedIntegratorStep(self.component, noSetFMUStatePriorToCurrentPoint, byref(enterEventMode), byref(terminateSimulation)) - return enterEventMode.value, terminateSimulation.value + def getNominalsOfContinuousStatesTYPE(self): + pass class FMU2Slave(_FMU2): - """ Base class for FMI 2.0 co-simulation FMUs """ + """ An FMI 2.0 Co-Simulation FMU """ def __init__(self, instanceName=None, **kwargs): @@ -457,26 +488,45 @@ def __init__(self, instanceName=None, **kwargs): super(FMU2Slave, self).__init__(**kwargs) + # Simulating the slave + self._fmi2Function('fmi2SetRealInputDerivatives', ['c', 'vr', 'nvr', 'order', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer), POINTER(fmi2Real)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer), POINTER(fmi2Real)]) self._fmi2Function('fmi2GetRealOutputDerivatives', ['c', 'vr', 'nvr', 'order', 'value'], - [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer), POINTER(fmi2Real)], - fmi2Status) + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Integer), POINTER(fmi2Real)]) self._fmi2Function('fmi2DoStep', - ['component', 'currentCommunicationPoint', 'communicationStepSize', - 'noSetFMUStatePriorToCurrentPoint'], - [fmi2Component, fmi2Real, fmi2Real, fmi2Boolean], - fmi2Status) + ['component', 'currentCommunicationPoint', 'communicationStepSize', 'noSetFMUStatePriorToCurrentPoint'], + [fmi2Component, fmi2Real, fmi2Real, fmi2Boolean]) + + self._fmi2Function('fmi2CancelStep', ['component'], [fmi2Component]) + + # Inquire slave status + + self._fmi2Function('fmi2GetStatus', + ['component', 'kind', 'value'], + [fmi2Component, fmi2StatusKind, POINTER(fmi2Status)]) + + self._fmi2Function('fmi2GetRealStatus', + ['component', 'kind', 'value'], + [fmi2Component, fmi2StatusKind, POINTER(fmi2Real)]) + + self._fmi2Function('fmi2GetIntegerStatus', + ['component', 'kind', 'value'], + [fmi2Component, fmi2StatusKind, POINTER(fmi2Integer)]) self._fmi2Function('fmi2GetBooleanStatus', ['component', 'kind', 'value'], - [fmi2Component, fmi2StatusKind, POINTER(fmi2Boolean)], - fmi2Status) + [fmi2Component, fmi2StatusKind, POINTER(fmi2Boolean)]) + + self._fmi2Function('fmi2GetStringStatus', + ['component', 'kind', 'value'], + [fmi2Component, fmi2StatusKind, POINTER(fmi2String)]) + + # Simulating the slave def setRealInputDerivatives(self, vr, order, value): vr = (fmi2ValueReference * len(vr))(*vr) @@ -494,6 +544,32 @@ def getRealOutputDerivatives(self, vr, order): def doStep(self, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint=fmi2True): return self.fmi2DoStep(self.component, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint) + def cancelStep(self): + self.fmi2CancelStep(self.component) + + # Inquire slave status + + def getStatus(self, kind): + value = fmi2Status(fmi2OK) + self.fmi2GetStatus(self.component, kind, byref(value)) + return value + + def getRealStatus(self, kind): + value = fmi2Real(0.0) + self.fmi2GetRealStatus(self.component, kind, byref(value)) + return value + + def getIntegerStatus(self, kind): + value = fmi2Integer(0) + self.fmi2GetIntegerStatus(self.component, kind, byref(value)) + return value + def getBooleanStatus(self, kind): value = fmi2Boolean(fmi2False) - return self.fmi2GetBooleanStatus(self.component, kind, byref(value)) + self.fmi2GetBooleanStatus(self.component, kind, byref(value)) + return value + + def getStringStatus(self, kind): + value = fmi2String(b'') + self.fmi2GetStringStatus(self.component, kind, byref(value)) + return value diff --git a/tests/test_getters_and_setters.py b/tests/test_common_functions.py similarity index 63% rename from tests/test_getters_and_setters.py rename to tests/test_common_functions.py index d5d0641e..04af224d 100644 --- a/tests/test_getters_and_setters.py +++ b/tests/test_common_functions.py @@ -6,9 +6,9 @@ import shutil -class GettersAndSettersTest(unittest.TestCase): +class CommonFunctionsTest(unittest.TestCase): - def test_getters_and_setters(self): + def test_common_functions(self): if platform.startswith('win'): fmi_versions = ['1.0', '2.0'] @@ -22,21 +22,22 @@ def test_getters_and_setters(self): download_test_file(fmi_version, 'CoSimulation', 'Dymola', '2017', model_name, filename) - modelDescription = read_model_description(filename) + model_description = read_model_description(filename) unzipdir = extract(filename) - guid = modelDescription.guid - modelIdentifier = modelDescription.coSimulation.modelIdentifier + guid = model_description.guid variables = {} - for v in modelDescription.modelVariables: + for v in model_description.modelVariables: variables[v.name] = v - args = {'guid': guid, - 'modelIdentifier': modelIdentifier, - 'unzipDirectory': unzipdir, - 'instanceName': None} + args = { + 'guid': guid, + 'modelIdentifier': model_description.coSimulation.modelIdentifier, + 'unzipDirectory': unzipdir, + 'instanceName': None + } if fmi_version == '1.0': fmu = FMU1Slave(**args) @@ -49,6 +50,24 @@ def test_getters_and_setters(self): fmu.enterInitializationMode() fmu.exitInitializationMode() + # get types platform + types_platform = fmu.getTypesPlatform() + + if fmi_version == '1.0': + self.assertEqual('standard32', types_platform) + else: + self.assertEqual('default', types_platform) + + # get FMI version + version = fmu.getVersion() + self.assertEqual(fmi_version, version) + + # set debug logging + if fmi_version == '1.0': + fmu.setDebugLogging(True) + else: + fmu.setDebugLogging(True, ['logAll']) + # set and get Real vr = [variables['booleanPulse1.width'].valueReference] value = [30.0]