diff --git a/CMakeLists.txt b/CMakeLists.txt index 76184672e..b82555e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ project(OMSimulator) # Options to enable/disable specific components option(OMS_ENABLE_OMSimulator "Enable OMSimulator component" ON) -option(OMS_ENABLE_OMSimulatorGui "Enable OMSimulator GUI component" ON) -option(OMS_ENABLE_PIP "Enable pip component" ON) +option(OMS_ENABLE_OMSimulatorGui "Enable OMSimulator GUI component" OFF) +option(OMS_ENABLE_PIP "Enable pip component" OFF) option(OMS_ENABLE_TESTSUITE "Enable the OMSimulator testsuite" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/config.cmake/") diff --git a/include/OMSimulator/OMSimulator.h b/include/OMSimulator/OMSimulator.h index 6a55662e7..632675181 100644 --- a/include/OMSimulator/OMSimulator.h +++ b/include/OMSimulator/OMSimulator.h @@ -159,6 +159,12 @@ OMSAPI oms_status_enu_t OMSCALL oms_simulate(const char* cref); OMSAPI oms_status_enu_t OMSCALL oms_stepUntil(const char* cref, double stopTime); OMSAPI oms_status_enu_t OMSCALL oms_terminate(const char* cref); +OMSAPI oms_status_enu_t OMSCALL oms3_instantiateFromJson(char* model_json_desc, void** out_model_ptr); +OMSAPI oms_status_enu_t OMSCALL oms3_initialize(void* model); +OMSAPI oms_status_enu_t OMSCALL oms3_simulate(void* model); +OMSAPI oms_status_enu_t OMSCALL oms3_stepUntil(void* model, double stopTime); +OMSAPI oms_status_enu_t OMSCALL oms3_terminate(void* model); + #ifdef __cplusplus } #endif diff --git a/src/OMSimulatorLib/OMSimulator.cpp b/src/OMSimulatorLib/OMSimulator.cpp index 6484405b4..38bab7724 100644 --- a/src/OMSimulatorLib/OMSimulator.cpp +++ b/src/OMSimulatorLib/OMSimulator.cpp @@ -1691,3 +1691,31 @@ oms_status_enu_t oms_setTolerance(const char* cref, double relativeTolerance) return logError_SystemNotInModel(model->getCref(), front); } + + +oms_status_enu_t oms3_instantiateFromJson(char* model_json_desc, void** out_model_ptr) +{ + *out_model_ptr = nullptr; + logDebug(model_json_desc); + return oms_status_ok; +} + +oms_status_enu_t oms3_initialize(void* model) +{ + return oms_status_error; +} + +oms_status_enu_t oms3_simulate(void* model) +{ + return oms_status_error; +} + +oms_status_enu_t oms3_stepUntil(void* model, double stopTime) +{ + return oms_status_error; +} + +oms_status_enu_t oms3_terminate(void* model) +{ + return oms_status_error; +} diff --git a/src/OMSimulatorPython/CMakeLists.txt b/src/OMSimulatorPython/CMakeLists.txt index 04db30bb8..d86556c27 100644 --- a/src/OMSimulatorPython/CMakeLists.txt +++ b/src/OMSimulatorPython/CMakeLists.txt @@ -28,12 +28,13 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/OMSimulatorPython3.in" "${CMAKE_CURR install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/capi.py" + "${CMAKE_CURRENT_SOURCE_DIR}/component.py" "${CMAKE_CURRENT_SOURCE_DIR}/connection.py" "${CMAKE_CURRENT_SOURCE_DIR}/connector.py" - "${CMAKE_CURRENT_SOURCE_DIR}/component.py" "${CMAKE_CURRENT_SOURCE_DIR}/cref.py" "${CMAKE_CURRENT_SOURCE_DIR}/elementgeometry.py" "${CMAKE_CURRENT_SOURCE_DIR}/fmu.py" + "${CMAKE_CURRENT_SOURCE_DIR}/instantiated_model.py" "${CMAKE_CURRENT_SOURCE_DIR}/namespace.py" "${CMAKE_CURRENT_SOURCE_DIR}/settings.py" "${CMAKE_CURRENT_SOURCE_DIR}/ssd.py" diff --git a/src/OMSimulatorPython/__init__.py b/src/OMSimulatorPython/__init__.py index 5e1da1f72..c9af017bb 100644 --- a/src/OMSimulatorPython/__init__.py +++ b/src/OMSimulatorPython/__init__.py @@ -4,6 +4,7 @@ OpenModelica FMI & SSP based simulator. ''' +from OMSimulator.capi import Capi from OMSimulator.connection import Connection from OMSimulator.connector import Connector from OMSimulator.cref import CRef @@ -14,11 +15,25 @@ from OMSimulator.ssv import SSV from OMSimulator.system import System from OMSimulator.variable import Causality, SignalType, Variable - -from OMSimulator import capi +from OMSimulator.instantiated_model import InstantiatedModel # Define public API -__all__ = ['Connection', 'Connector', 'CRef', 'FMU', 'Settings', 'SSD', 'SSP', 'SSV', 'System', 'Causality', 'SignalType', 'Variable'] +__all__ = [ + 'Capi', + 'Causality', + 'Connection', + 'Connector', + 'CRef', + 'FMU', + 'InstantiatedModel', + 'Settings', + 'SignalType', + 'SSD', + 'SSP', + 'SSV', + 'System', + 'Variable', + ] __version__ = '@OMS_SHORT_VERSION_STRING@' __author__ = 'Open Source Modelica Consortium (OSMC)' @@ -28,4 +43,4 @@ c/o Linköpings universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden.''' -version = capi.capi().getVersion() +version = Capi.getVersion() diff --git a/src/OMSimulatorPython/capi.py b/src/OMSimulatorPython/capi.py index 12ebcb8b5..d503fe2d8 100644 --- a/src/OMSimulatorPython/capi.py +++ b/src/OMSimulatorPython/capi.py @@ -1,5 +1,17 @@ import ctypes import os +from enum import Enum + + +class Status(Enum): + '''Enumeration for status codes (oms_status_enu_t).''' + ok = 0 + warning = 1 + discard = 2 + error = 3 + fatal = 4 + pending = 5 + class capi: def __init__(self): @@ -24,379 +36,50 @@ def __init__(self): if os.name == 'nt' and dllSearchPath: # Windows dllDir.close() - self.obj.oms_activateVariant.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_activateVariant.restype = ctypes.c_int - self.obj.oms_addBus.argtypes = [ctypes.c_char_p] - self.obj.oms_addBus.restype = ctypes.c_int - self.obj.oms_addConnection.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool] - self.obj.oms_addConnection.restype = ctypes.c_int - self.obj.oms_addConnector.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_int] - self.obj.oms_addConnector.restype = ctypes.c_int - self.obj.oms_addConnectorToBus.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_addConnectorToBus.restype = ctypes.c_int - self.obj.oms_addResources.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_addResources.restype = ctypes.c_int - self.obj.oms_addSignalsToResults.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_addSignalsToResults.restype = ctypes.c_int - self.obj.oms_addSubModel.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_addSubModel.restype = ctypes.c_int - self.obj.oms_addSystem.argtypes = [ctypes.c_char_p, ctypes.c_int] - self.obj.oms_addSystem.restype = ctypes.c_int - self.obj.oms_compareSimulationResults.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_double, ctypes.c_double] - self.obj.oms_compareSimulationResults.restype = ctypes.c_int - self.obj.oms_copySystem.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_copySystem.restype = ctypes.c_int - self.obj.oms_delete.argtypes = [ctypes.c_char_p] - self.obj.oms_delete.restype = ctypes.c_int - self.obj.oms_deleteConnection.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_deleteConnection.restype = ctypes.c_int - self.obj.oms_deleteConnectorFromBus.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_deleteConnectorFromBus.restype = ctypes.c_int - self.obj.oms_deleteResources.argtypes = [ctypes.c_char_p] - self.obj.oms_deleteResources.restype = ctypes.c_int - self.obj.oms_doStep.argtypes = [ctypes.c_char_p] - self.obj.oms_doStep.restype = ctypes.c_int - self.obj.oms_duplicateVariant.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_duplicateVariant.restype = ctypes.c_int - self.obj.oms_export.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_export.restype = ctypes.c_int - self.obj.oms_exportDependencyGraphs.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_exportDependencyGraphs.restype = ctypes.c_int - self.obj.oms_exportSnapshot.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_exportSnapshot.restype = ctypes.c_int - self.obj.oms_exportSSMTemplate.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_exportSSMTemplate.restype = ctypes.c_int - self.obj.oms_exportSSVTemplate.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_exportSSVTemplate.restype = ctypes.c_int - self.obj.oms_freeMemory.argtypes = [ctypes.c_void_p] - self.obj.oms_freeMemory.restype = None - self.obj.oms_getBoolean.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_bool)] - self.obj.oms_getBoolean.restype = ctypes.c_int - self.obj.oms_getDirectionalDerivative.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)] - self.obj.oms_getDirectionalDerivative.restype = ctypes.c_int - self.obj.oms_getFixedStepSize.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)] - self.obj.oms_getFixedStepSize.restype = ctypes.c_int - self.obj.oms_getInteger.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int)] - self.obj.oms_getInteger.restype = ctypes.c_int - self.obj.oms_getModelState.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int)] - self.obj.oms_getModelState.restype = ctypes.c_int - self.obj.oms_getReal.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)] - self.obj.oms_getReal.restype = ctypes.c_int - self.obj.oms_getResultFile.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_int)] - self.obj.oms_getResultFile.restype = ctypes.c_int - self.obj.oms_getSolver.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_int)] - self.obj.oms_getSolver.restype = ctypes.c_int - self.obj.oms_getStartTime.argtypes = [ctypes.c_char_p] - self.obj.oms_getStartTime.restype = ctypes.c_int - self.obj.oms_getStopTime.argtypes = [ctypes.c_char_p] - self.obj.oms_getStopTime.restype = ctypes.c_int - self.obj.oms_getString.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_getString.restype = ctypes.c_int - self.obj.oms_getSystemType.argtypes = [ctypes.c_char_p] - self.obj.oms_getSystemType.restype = ctypes.c_int - self.obj.oms_getState.argtypes = [ctypes.c_char_p] - self.obj.oms_getState.restype = ctypes.c_int - self.obj.oms_setState.argtypes = [ctypes.c_char_p] - self.obj.oms_setState.restype = ctypes.c_int - self.obj.oms_freeState.argtypes = [ctypes.c_char_p] - self.obj.oms_freeState.restype = ctypes.c_int - self.obj.oms_getTime.argtypes = [ctypes.c_char_p] - self.obj.oms_getTime.restype = ctypes.c_int - self.obj.oms_getVariableStepSize.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)] - self.obj.oms_getVariableStepSize.restype = ctypes.c_int self.obj.oms_getVersion.argtypes = None self.obj.oms_getVersion.restype = ctypes.c_char_p - self.obj.oms_importFile.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_importFile.restype = ctypes.c_int - self.obj.oms_importSnapshot.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_importSnapshot.restype = ctypes.c_int - self.obj.oms_initialize.argtypes = [ctypes.c_char_p] - self.obj.oms_initialize.restype = ctypes.c_int - self.obj.oms_instantiate.argtypes = [ctypes.c_char_p] - self.obj.oms_instantiate.restype = ctypes.c_int - self.obj.oms_list.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_list.restype = ctypes.c_int - self.obj.oms_listVariants.argtypes = [ctypes.c_char_p] - self.obj.oms_listVariants.restype = ctypes.c_int - self.obj.oms_listUnconnectedConnectors.argtypes = [ctypes.c_char_p] - self.obj.oms_listUnconnectedConnectors.restype = ctypes.c_int - self.obj.oms_loadSnapshot.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] - self.obj.oms_loadSnapshot.restype = ctypes.c_int - self.obj.oms_newModel.argtypes = [ctypes.c_char_p] - self.obj.oms_newModel.restype = ctypes.c_int - self.obj.oms_newResources.argtypes = [ctypes.c_char_p] - self.obj.oms_newResources.restype = ctypes.c_int - self.obj.oms_reduceSSV.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_reduceSSV.restype = ctypes.c_int - self.obj.oms_referenceResources.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_referenceResources.restype = ctypes.c_int - self.obj.oms_removeSignalsFromResults.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_removeSignalsFromResults.restype = ctypes.c_int - self.obj.oms_rename.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_rename.restype = ctypes.c_int - self.obj.oms_replaceSubModel.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool, ctypes.POINTER(ctypes.c_int)] - self.obj.oms_replaceSubModel.restype = ctypes.c_int - self.obj.oms_reset.argtypes = [ctypes.c_char_p] - self.obj.oms_reset.restype = ctypes.c_int - self.obj.oms_setBoolean.argtypes = [ctypes.c_char_p, ctypes.c_bool] - self.obj.oms_setBoolean.restype = ctypes.c_int - self.obj.oms_setCommandLineOption.argtypes = [ctypes.c_char_p] - self.obj.oms_setCommandLineOption.restype = ctypes.c_int - self.obj.oms_setFixedStepSize.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setFixedStepSize.restype = ctypes.c_int - self.obj.oms_setInteger.argtypes = [ctypes.c_char_p, ctypes.c_int] - self.obj.oms_setInteger.restype = ctypes.c_int - self.obj.oms_setLogFile.argtypes = [ctypes.c_char_p] - self.obj.oms_setLogFile.restype = ctypes.c_int - self.obj.oms_setLoggingInterval.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setLoggingInterval.restype = ctypes.c_int - self.obj.oms_setLoggingLevel.argtypes = [ctypes.c_int] - self.obj.oms_setLoggingLevel.restype = ctypes.c_int - self.obj.oms_setMaxLogFileSize.argtypes = [ctypes.c_ulong] - self.obj.oms_setMaxLogFileSize.restype = None - self.obj.oms_setReal.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setReal.restype = ctypes.c_int - self.obj.oms_setRealInputDerivative.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setRealInputDerivative.restype = ctypes.c_int - self.obj.oms_setResultFile.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int] - self.obj.oms_setResultFile.restype = ctypes.c_int - self.obj.oms_setSolver.argtypes = [ctypes.c_char_p, ctypes.c_int] - self.obj.oms_setSolver.restype = ctypes.c_int - self.obj.oms_setStartTime.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setStartTime.restype = ctypes.c_int - self.obj.oms_setStopTime.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_setStopTime.restype = ctypes.c_int - self.obj.oms_setString.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_setString.restype = ctypes.c_int - self.obj.oms_setTempDirectory.argtypes = [ctypes.c_char_p] - self.obj.oms_setTempDirectory.restype = ctypes.c_int - self.obj.oms_setTolerance.argtypes = [ctypes.c_char_p, ctypes.c_double, ctypes.c_double] - self.obj.oms_setTolerance.restype = ctypes.c_int - self.obj.oms_setUnit.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.obj.oms_setUnit.restype = ctypes.c_int - self.obj.oms_setVariableStepSize.argtypes = [ctypes.c_char_p, ctypes.c_double, ctypes.c_double, ctypes.c_double] - self.obj.oms_setVariableStepSize.restype = ctypes.c_int - self.obj.oms_setWorkingDirectory.argtypes = [ctypes.c_char_p] - self.obj.oms_setWorkingDirectory.restype = ctypes.c_int - self.obj.oms_simulate.argtypes = [ctypes.c_char_p] - self.obj.oms_simulate.restype = ctypes.c_int - self.obj.oms_stepUntil.argtypes = [ctypes.c_char_p, ctypes.c_double] - self.obj.oms_stepUntil.restype = ctypes.c_int - self.obj.oms_terminate.argtypes = [ctypes.c_char_p] - self.obj.oms_terminate.restype = ctypes.c_int + self.obj.oms3_instantiateFromJson.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] + self.obj.oms3_instantiateFromJson.restype = ctypes.c_int + self.obj.oms3_initialize.argtypes = [ctypes.c_void_p] + self.obj.oms3_initialize.restype = ctypes.c_int + self.obj.oms3_simulate.argtypes = [ctypes.c_void_p] + self.obj.oms3_simulate.restype = ctypes.c_int + self.obj.oms3_stepUntil.argtypes = [ctypes.c_void_p, ctypes.c_double] + self.obj.oms3_stepUntil.restype = ctypes.c_int + self.obj.oms3_terminate.argtypes = [ctypes.c_void_p] + self.obj.oms3_terminate.restype = ctypes.c_int - def activateVariant(self, crefA, crefB): - return self.obj.oms_activateVariant(crefA.encode(), crefB.encode()) - def addBus(self, crefA): - return self.obj.oms_addBus(crefA.encode()) - def addConnection(self, crefA, crefB, suppressUnit=False): - return self.obj.oms_addConnection(crefA.encode(), crefB.encode(), suppressUnit) - def addConnector(self, cref, causality, type_): - return self.obj.oms_addConnector(cref.encode(), causality, type_) - def addConnectorToBus(self, busCref, connectorCref): - return self.obj.oms_addConnectorToBus(busCref.encode(), connectorCref.encode()) - def addResources(self, cref, path): - return self.obj.oms_addResources(cref.encode(), path.encode()) - def addSignalsToResults(self, cref, regex): - return self.obj.oms_addSignalsToResults(cref.encode(), regex.encode()) - def addSubModel(self, cref, fmuPath): - return self.obj.oms_addSubModel(cref.encode(), fmuPath.encode()) - def addSystem(self, ident, type_): - return self.obj.oms_addSystem(ident.encode(), type_) - def compareSimulationResults(self, filenameA, filenameB, var, relTol, absTol): - return self.obj.oms_compareSimulationResults(filenameA.encode(), filenameB.encode(), var.encode(), relTol, absTol) - def copySystem(self, source, target): - return self.obj.oms_copySystem(source.encode(), target.encode()) - def delete(self, cref): - return self.obj.oms_delete(cref.encode()) - def deleteConnection(self, crefA, crefB): - return self.obj.oms_deleteConnection(crefA.encode(), crefB.encode()) - def deleteConnectorFromBus(self, busCref, connectorCref): - return self.obj.oms_deleteConnectorFromBus(busCref.encode(), connectorCref.encode()) - def deleteResources(self, crefA): - return self.obj.oms_deleteResources(crefA.encode()) - def doStep(self, cref): - return self.obj.oms_doStep(cref.encode()) - def duplicateVariant(self, crefA, crefB): - return self.obj.oms_duplicateVariant(crefA.encode(), crefB.encode()) - def export(self, cref, filename): - return self.obj.oms_export(cref.encode(), filename.encode()) - def exportDependencyGraphs(self, cref, initialization, event, simulation): - return self.obj.oms_exportDependencyGraphs(cref.encode(), initialization.encode(), event.encode(), simulation.encode()) - def exportSnapshot(self, ident): - contents = ctypes.c_char_p() - status = self.obj.oms_exportSnapshot(ident.encode(), ctypes.byref(contents)) - contents_ = contents.value.decode('utf-8') if contents.value else None - self.obj.oms_freeMemory(contents) - return [contents_, status] - def exportSSMTemplate(self, ident, filename): - return self.obj.oms_exportSSMTemplate(ident.encode(), filename.encode()) - def exportSSVTemplate(self, ident, filename): - return self.obj.oms_exportSSVTemplate(ident.encode(), filename.encode()) - def getBoolean(self, cref): - value = ctypes.c_bool() - status = self.obj.oms_getBoolean(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getDirectionalDerivative(self, crefA, crefB=""): - value = ctypes.c_double() - status = self.obj.oms_getDirectionalDerivative(crefA.encode(), crefB.encode(), ctypes.byref(value)) - return [value.value, status] - def getFixedStepSize(self, cref): - value = ctypes.c_double() - status = self.obj.oms_getFixedStepSize(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getInteger(self, cref): - value = ctypes.c_int() - status = self.obj.oms_getInteger(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getModelState(self, cref): - value = ctypes.c_int() - status = self.obj.oms_getModelState(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getReal(self, cref): - value = ctypes.c_double() - status = self.obj.oms_getReal(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getResultFile(self, cref): - filename = ctypes.c_char_p() - bufferSize = ctypes.c_int() - status = self.obj.oms_getResultFile(cref.encode(), ctypes.byref(filename), ctypes.byref(bufferSize)) - return [filename.value.decode('utf-8') if filename.value else None, bufferSize.value, status] - def getSolver(self, cref): - value = ctypes.c_int() - status = self.obj.oms_getSolver(cref.encode(), ctypes.byref(value)) - return [value.value, status] - def getStartTime(self, cref): - startTime = ctypes.c_double() - status = self.obj.oms_getStartTime(cref.encode(), ctypes.byref(startTime)) - return [startTime.value, status] - def getStopTime(self, cref): - stopTime = ctypes.c_double() - status = self.obj.oms_getStopTime(cref.encode(), ctypes.byref(stopTime)) - return [stopTime.value, status] - def getString(self, cref): - value = ctypes.c_char_p() - status = self.obj.oms_getString(cref.encode(), ctypes.byref(value)) - value_ = value.value.decode('utf-8') if value.value else None - self.obj.oms_freeMemory(value) - return [value_, status] - def getSystemType(self, cref): - type_ = ctypes.c_int() - status = self.obj.oms_getSystemType(cref.encode(), ctypes.byref(type_)) - return [type_.value, status] - def getState(self, cref): - return self.obj.oms_getState(cref.encode()) - def setState(self, cref): - return self.obj.oms_setState(cref.encode()) - def freeState(self, cref): - return self.obj.oms_freeState(cref.encode()) - def getTime(self, cref): - time = ctypes.c_double() - status = self.obj.oms_getTime(cref.encode(), ctypes.byref(time)) - return [time.value, status] - def getVariableStepSize(self, cref): - initialStepSize = ctypes.c_double() - minimumStepSize = ctypes.c_double() - maximumStepSize = ctypes.c_double() - status = self.obj.oms_getVariableStepSize(cref.encode(), ctypes.byref(initialStepSize), ctypes.byref(minimumStepSize), ctypes.byref(maximumStepSize)) - return [initialStepSize.value, minimumStepSize.value, maximumStepSize.value, status] def getVersion(self): return self.obj.oms_getVersion().decode('utf-8') - def importFile(self, filename): - cref = ctypes.c_char_p() - status = self.obj.oms_importFile(filename.encode(), ctypes.byref(cref)) - return [cref.value.decode('utf-8') if cref.value else None, status] - def importSnapshot(self, ident, snapshot): - newCref = ctypes.c_char_p() - status = self.obj.oms_importSnapshot(ident.encode(), snapshot.encode(), ctypes.byref(newCref)) - return [newCref.value.decode('utf-8') if newCref.value else None, status] - def initialize(self, cref): - return self.obj.oms_initialize(cref.encode()) - def instantiate(self, cref): - return self.obj.oms_instantiate(cref.encode()) - def list(self, ident): - contents = ctypes.c_char_p() - status = self.obj.oms_list(ident.encode(), ctypes.byref(contents)) - contents_ = contents.value.decode('utf-8') if contents.value else None - self.obj.oms_freeMemory(contents) - return [contents_, status] - def listVariants(self, ident): - contents = ctypes.c_char_p() - status = self.obj.oms_listVariants(ident.encode(), ctypes.byref(contents)) - contents_ = contents.value.decode('utf-8') if contents.value else None - self.obj.oms_freeMemory(contents) - return [contents_, status] - def listUnconnectedConnectors(self, ident): - contents = ctypes.c_char_p() - status = self.obj.oms_listUnconnectedConnectors(ident.encode(), ctypes.byref(contents)) - contents_ = contents.value.decode('utf-8') if contents.value else None - self.obj.oms_freeMemory(contents) - return [contents_, status] - def loadSnapshot(self, ident, snapshot): - newCref = ctypes.c_char_p() - status = self.obj.oms_loadSnapshot(ident.encode(), snapshot.encode(), ctypes.byref(newCref)) - return [newCref.value.decode('utf-8') if newCref.value else None, status] - def newModel(self, cref): - return self.obj.oms_newModel(cref.encode()) - def newResources(self, crefA): - return self.obj.oms_newResources(crefA.encode()) - def reduceSSV(self, crefA, crefB, crefC, crefD=""): - return self.obj.oms_reduceSSV(crefA.encode(), crefB.encode(), crefC.encode(), crefD.encode()) - def referenceResources(self, crefA, crefB=""): - return self.obj.oms_referenceResources(crefA.encode(), crefB.encode()) - def removeSignalsFromResults(self, cref, regex): - return self.obj.oms_removeSignalsFromResults(cref.encode(), regex.encode()) - def rename(self, cref, newcref): - return self.obj.oms_rename(cref.encode(), newcref.encode()) - def replaceSubModel(self, cref, fmuPath, dryRun): - value = ctypes.c_int() - status = self.obj.oms_replaceSubModel(cref.encode(), fmuPath.encode(), dryRun, ctypes.byref(value)) - return [value.value, status] - def reset(self, cref): - return self.obj.oms_reset(cref.encode()) - def setBoolean(self, signal, value): - return self.obj.oms_setBoolean(signal.encode(), value) - def setCommandLineOption(self, cmd): - return self.obj.oms_setCommandLineOption(cmd.encode()) - def setFixedStepSize(self, cref, stepSize): - return self.obj.oms_setFixedStepSize(cref.encode(), stepSize) - def setInteger(self, signal, value): - return self.obj.oms_setInteger(signal.encode(), value) - def setLogFile(self, filename): - return self.obj.oms_setLogFile(filename.encode()) - def setLoggingInterval(self, cref, loggingInterval): - return self.obj.oms_setLoggingInterval(cref.encode(), loggingInterval) - def setLoggingLevel(self, level): - return self.obj.oms_setLoggingLevel(level) - def setMaxLogFileSize(self, size): - return self.obj.oms_setMaxLogFileSize(size) - def setReal(self, signal, value): - return self.obj.oms_setReal(signal.encode(), value) - def setRealInputDerivative(self, signal, value): - return self.obj.oms_setRealInputDerivative(signal.encode(), value) - def setResultFile(self, cref, filename, bufferSize=1): - return self.obj.oms_setResultFile(cref.encode(), filename.encode(), bufferSize) - def setSolver(self, cref, solver): - return self.obj.oms_setSolver(cref.encode(), solver) - def setStartTime(self, cref, startTime): - return self.obj.oms_setStartTime(cref.encode(), startTime) - def setStopTime(self, cref, stopTime): - return self.obj.oms_setStopTime(cref.encode(), stopTime) - def setString(self, signal, value): - return self.obj.oms_setString(signal.encode(), value.encode()) - def setTempDirectory(self, newTempDir): - return self.obj.oms_setTempDirectory(newTempDir.encode()) - def setTolerance(self, cref, relativeTolerance): - return self.obj.oms_setTolerance(cref.encode(), relativeTolerance) - def setUnit(self, signal, value): - return self.obj.oms_setUnit(signal.encode(), value.encode()) - def setVariableStepSize(self, cref, initialStepSize, minimumStepSize, maximumStepSize): - return self.obj.oms_setVariableStepSize(cref.encode(), initialStepSize, minimumStepSize, maximumStepSize) - def setWorkingDirectory(self, path): - return self.obj.oms_setWorkingDirectory(path.encode()) - def simulate(self, cref): - return self.obj.oms_simulate(cref.encode()) - def stepUntil(self, cref, stopTime): - return self.obj.oms_stepUntil(cref.encode(), stopTime) - def terminate(self, cref): - return self.obj.oms_terminate(cref.encode()) + + def instantiateFromJson(self, model_json_desc) -> tuple: + '''Instantiate a model from a JSON description.''' + model_ptr = ctypes.c_void_p() + status = self.obj.oms3_instantiateFromJson(model_json_desc.encode('utf-8'), ctypes.byref(model_ptr)) + if status != Status.ok: + return None, Status(status) + return model_ptr, Status(status) + + def initialize(self, model) -> Status: + '''Enters initialization mode.''' + status = self.obj.oms3_initialize(model) + return Status(status) + + def simulate(self, model) -> Status: + '''Exits initialization mode and runs the simulation until stopTime is reached.''' + status = self.obj.oms3_simulate(model) + return Status(status) + + def stepUntil(self, model, stopTime) -> Status: + '''Step the simulation until the specified stop time. + Note: If the model is in initialization mode, this will exit initialization mode.''' + status = self.obj.oms3_stepUntil(model, stopTime) + return Status(status) + + def terminate(self, model) -> Status: + '''Terminate the simulation and free resources.''' + status = self.obj.oms3_terminate(model) + return Status(status) + + +Capi = capi() diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py new file mode 100644 index 000000000..4c8323cc1 --- /dev/null +++ b/src/OMSimulatorPython/instantiated_model.py @@ -0,0 +1,34 @@ +from OMSimulator.capi import Capi, Status + +class InstantiatedModel: + def __init__(self, json_description): + print(json_description) + self.model, status = Capi.instantiateFromJson(json_description) + if status != Status.ok: + raise RuntimeError(f"Failed to instantiate model: {status}") + + def setValues(self): + pass + + def getValues(self): + pass + + def initialize(self): + status = Capi.initialize(self.model) + if status != Status.ok: + raise RuntimeError(f"Failed to initialize model: {status}") + + def simulate(self): + status = Capi.simulate(self.model) + if status != Status.ok: + raise RuntimeError(f"Failed to simulate model: {status}") + + def stepUntil(self, stopTime): + status = Capi.stepUntil(self.model, stopTime) + if status != Status.ok: + raise RuntimeError(f"Failed to step until {stopTime}: {status}") + + def terminate(self): + status = Capi.terminate(self.model) + if status != Status.ok: + raise RuntimeError(f"Failed to terminate model: {status}") diff --git a/src/OMSimulatorPython/system.py b/src/OMSimulatorPython/system.py index 76d599962..0c9d6fc8e 100644 --- a/src/OMSimulatorPython/system.py +++ b/src/OMSimulatorPython/system.py @@ -1,18 +1,18 @@ +import json import logging +from collections import defaultdict from lxml import etree as ET from OMSimulator.component import Component -from OMSimulator.connector import Connector from OMSimulator.connection import Connection +from OMSimulator.connector import Connector +from OMSimulator.elementgeometry import ElementGeometry from OMSimulator.fmu import FMU -from OMSimulator.values import Values +from OMSimulator.instantiated_model import InstantiatedModel from OMSimulator.ssv import SSV -from OMSimulator.elementgeometry import ElementGeometry - -from OMSimulator import CRef, namespace, utils +from OMSimulator.values import Values -from collections import defaultdict -import json +from OMSimulator import Capi, CRef, namespace, utils logger = logging.getLogger(__name__) @@ -268,7 +268,7 @@ def setSolver(self, cref: CRef, name: str): raise ValueError(f"Component '{first}' not found in {self.name}") self.elements[first].setSolver(name) - def instantiate(self): + def instantiate(self) -> InstantiatedModel: """Instantiates the system and its components.""" data = { "simulation units": [] @@ -304,8 +304,7 @@ def instantiate(self): # Dump JSON json_string = json.dumps(data, indent=2) - print(json_string) - + return InstantiatedModel(json_string) def processElements(self, elements_dict: dict, connections: list, data: dict, solver_groups : defaultdict, componentSolver : dict, solver_connections : defaultdict, systemName = None): """Processes the elements and connections in the system.""" diff --git a/testsuite/Makefile b/testsuite/Makefile index 6e4a6c9e4..98e94fa3d 100644 --- a/testsuite/Makefile +++ b/testsuite/Makefile @@ -2,7 +2,7 @@ all: difftool resources test -test: api.log parker.log dcmotor.log +test: api.log parker.log dcmotor.log simulation.log partest: difftool resources cd partest && time ./runtests.pl -nocolour -with-xml @@ -23,4 +23,8 @@ parker.log: difftool dcmotor.log: difftool @$(MAKE) -C dcmotor -f Makefile test > $@ - @grep == dcmotor.log \ No newline at end of file + @grep == dcmotor.log + +simulation.log: difftool + @$(MAKE) -C simulation -f Makefile test > $@ + @grep == simulation.log diff --git a/testsuite/api/Makefile b/testsuite/api/Makefile index 562ba03af..d2771609d 100644 --- a/testsuite/api/Makefile +++ b/testsuite/api/Makefile @@ -3,9 +3,6 @@ TEST = ../rtest -v TESTFILES = \ create_ssp_file.py \ cref.py \ -exportJson1.py \ -exportJson2.py \ -exportJson4.py \ fmuinfo.py \ invalidSSD.py \ invalidSSV.py \ diff --git a/testsuite/resources/sim.json b/testsuite/resources/sim.json new file mode 100644 index 000000000..ed463b81c --- /dev/null +++ b/testsuite/resources/sim.json @@ -0,0 +1,67 @@ +{ + "simulation units": [ + { + "components": [ + { + "name": "Controller", + "type": "FMI 2 CS", + "path": "resources/Controller.fmu", + "variables": ["output"] + } + ], + "solver": { + "type": "co-simulation" + }, + "connections": [] + }, + { + "components": [ + { + "name": "Plant", + "type": "model exchange", + "path": "resources/Plant.fmu", + "variables": ["input", "output"] + }, + { + "name": "Sensor", + "type": "model exchange", + "path": "resources/Sensor.fmu", + "variables": ["output"] + } + ], + "solver": { + "type": "CVODE", + "stepSize": 0.01, + "tolerance": 1e-5 + }, + "connections": [ + { + "start element": "Sensor", + "start connector": "output", + "end element": "Plant", + "end connector": "input" + } + ] + } + ], + "connections": [ + { + "start element": "Controller", + "start connector": "output", + "end element": "Plant", + "end connector": "input" + }, + { + "start element": "Sensor", + "start connector": "output", + "end element": "Controller", + "end connector": "input" + } + ], + "result file": "simulation_result.csv", + "simulation settings": { + "start time": 0.0, + "stop time": 10.0, + "tolerance": 1e-6 + } +} diff --git a/testsuite/resources/sim_singleFMU.json b/testsuite/resources/sim_singleFMU.json new file mode 100644 index 000000000..03907f429 --- /dev/null +++ b/testsuite/resources/sim_singleFMU.json @@ -0,0 +1,33 @@ +{ + "temp dir": "temp", + "working dir": "working", + "simulation units": [ + { + "components": [ + { + "name": "", + "type": "FMI 2 ME", + "path": "resources/Controller.fmu", + "variables": { + "output": 15, + "state1": 0 + } + } + ], + "solver": { + "type": "model exchange", + "solver": "CVODE", + "stepSize": 0.01, + "tolerance": 1e-5 + }, + "connections": [] + } + ], + "connections": [], + "result file": "simulation_result.csv", + "simulation settings": { + "start time": 0.0, + "stop time": 10.0, + "tolerance": 1e-6 + } +} diff --git a/testsuite/simulation/Makefile b/testsuite/simulation/Makefile new file mode 100644 index 000000000..a1ce7a5e7 --- /dev/null +++ b/testsuite/simulation/Makefile @@ -0,0 +1,55 @@ +TEST = ../rtest -v + +TESTFILES = \ +exportJson1.py \ +exportJson2.py \ +exportJson3.py \ +exportJson4.py \ +SimpleSimulation.py \ + +# Run make failingtest +FAILINGTESTFILES = \ + +# Dependency files that are not .lua or Makefile +# Add them here or they will be cleaned. +DEPENDENCIES = \ +*.lua \ +*.py \ +Makefile \ + +CLEAN = `ls | grep -w -v -f deps.tmp` + +.PHONY : test clean getdeps failingtest + +test: + @echo + @echo Running tests... + @$(TEST) $(TESTFILES) + +baseline: + @echo + @echo Updating badelines... + @$(TEST) -b $(TESTFILES) + +# Cleans all files that are not listed as dependencies +clean: + @echo $(DEPENDENCIES) | sed 's/ /\\\|/g' > deps.tmp + @rm -rf $(CLEAN) + +# Run this if you want to list out the files (dependencies). +# do it after cleaning and updating the folder +# then you can get a list of file names (which must be dependencies +# since you got them from repository + your own new files) +# then add them to the DEPENDENCIES. You can find the +# list in deps.txt +getdeps: + @echo $(DEPENDENCIES) | sed 's/ /\\\|/g' > deps.tmp + @echo $(CLEAN) | sed -r 's/deps.txt|deps.tmp//g' | sed 's/ / \\\n/g' > deps.txt + @echo Dependency list saved in deps.txt. + @echo Copy the list from deps.txt and add it to the Makefile @DEPENDENCIES + +failingtest: + @echo + @echo Running failing tests... + @echo + @$(TEST) $(FAILINGTESTFILES) diff --git a/testsuite/simulation/SimpleSimulation.py b/testsuite/simulation/SimpleSimulation.py new file mode 100644 index 000000000..67629d3e5 --- /dev/null +++ b/testsuite/simulation/SimpleSimulation.py @@ -0,0 +1,30 @@ +## status: correct +## teardown_command: +## linux: yes +## ucrt64: yes +## win: yes +## mac: yes + +from OMSimulator import Capi, Settings + +Settings.suppressPath = True + +# read json object from file +with open('../resources/sim.json', 'r') as file: + json_data = file.read() + +model, status = Capi.instantiateFromJson(json_data) +print(f'instantiateFromJson: {status}') +status = Capi.initialize(model) +print(f'initialize: {status}') +status = Capi.simulate(model) +print(f'simulate: {status}') +status = Capi.terminate(model) +print(f'terminate: {status}') + +## Result: +## instantiateFromJson: Status.ok +## initialize: Status.ok +## simulate: Status.ok +## terminate: Status.ok +## endResult diff --git a/testsuite/api/exportJson1.py b/testsuite/simulation/exportJson1.py similarity index 100% rename from testsuite/api/exportJson1.py rename to testsuite/simulation/exportJson1.py diff --git a/testsuite/api/exportJson2.py b/testsuite/simulation/exportJson2.py similarity index 100% rename from testsuite/api/exportJson2.py rename to testsuite/simulation/exportJson2.py diff --git a/testsuite/api/exportJson3.py b/testsuite/simulation/exportJson3.py similarity index 100% rename from testsuite/api/exportJson3.py rename to testsuite/simulation/exportJson3.py diff --git a/testsuite/api/exportJson4.py b/testsuite/simulation/exportJson4.py similarity index 100% rename from testsuite/api/exportJson4.py rename to testsuite/simulation/exportJson4.py