Repository: NTNU-IHB/PythonFMU (python)

Issue:
Pass instanceName to slave

Pull request description:
Pass instanceName to slave
This PR passes the `instanceName` to the Python slave. This requires the slave ctor to take a string argument that is passes to the `Fmi2Slave` super class.

```python
class PythonSlave(Fmi2Slave):

    def __init__(self, instance_name):
        super().__init__(instance_name)

```

Closes #18 

Patch:
diff --git a/README.md b/README.md
index a900c61..f9dfe19 100644
--- a/README.md
+++ b/README.md
@@ -53,8 +53,8 @@ class PythonSlave(Fmi2Slave):
     author = "John Doe"
     description = "A simple description"
 
-    def __init__(self):
-        super().__init__()
+    def __init__(self, instance_name):
+        super().__init__(instance_name)
 
         self.intOut = 1
         self.realOut = 3.0
diff --git a/pythonfmu/builder.py b/pythonfmu/builder.py
index 87bfae0..19d8286 100644
--- a/pythonfmu/builder.py
+++ b/pythonfmu/builder.py
@@ -46,7 +46,7 @@ class ModelDescriptionFetcher:
             spec.loader.exec_module(fmu_interface)
             # Instantiate the interface
             class_name = getattr(fmu_interface, "slave_class")
-            instance = getattr(fmu_interface, class_name)()
+            instance = getattr(fmu_interface, class_name)("dummyInstance")
         finally:
             sys.path.remove(str(filepath.parent))  # remove inserted temporary path
         
diff --git a/pythonfmu/fmi2slave.py b/pythonfmu/fmi2slave.py
index 738713f..416b3cf 100644
--- a/pythonfmu/fmi2slave.py
+++ b/pythonfmu/fmi2slave.py
@@ -32,8 +32,9 @@ class Fmi2Slave(ABC):
     modelName: ClassVar[Optional[str]] = None
     description: ClassVar[Optional[str]] = None
 
-    def __init__(self):
+    def __init__(self, instance_name: str):
         self.vars = dict()
+        self.instance_name = instance_name
         if self.modelName is None:
             self.modelName = self.__class__.__name__
 
diff --git a/pythonfmu/pythonfmu-export/cpp/PyObjectWrapper.cpp b/pythonfmu/pythonfmu-export/cpp/PyObjectWrapper.cpp
index cc64f5f..1a187c7 100644
--- a/pythonfmu/pythonfmu-export/cpp/PyObjectWrapper.cpp
+++ b/pythonfmu/pythonfmu-export/cpp/PyObjectWrapper.cpp
@@ -1,13 +1,14 @@
 
+#include "pythonfmu/PyObjectWrapper.hpp"
+
+#include "pythonfmu/PyException.hpp"
+
+#include "cppfmu/cppfmu_common.hpp"
 
 #include <fstream>
 #include <iostream>
 #include <sstream>
 #include <utility>
-#include "pythonfmu/PyException.hpp"
-#include "pythonfmu/PyObjectWrapper.hpp"
-
-#include "cppfmu/cppfmu_common.hpp"
 
 namespace
 {
@@ -25,7 +26,8 @@ inline std::string getline(const std::string& fileName)
 namespace pythonfmu
 {
 
-PyObjectWrapper::PyObjectWrapper(const std::string& resources)
+PyObjectWrapper::PyObjectWrapper(const std::string& instanceName, const std::string& resources)
+    : instanceName_(instanceName)
 {
     // Append resources path to python sys path
     PyObject* sys_module = PyImport_ImportModule("sys");
@@ -48,7 +50,7 @@ PyObjectWrapper::PyObjectWrapper(const std::string& resources)
     if (pModule == nullptr) {
         handle_py_exception("[ctor] PyImport_ImportModule");
     }
-    
+
     PyObject* className = PyObject_GetAttrString(pModule, "slave_class");
     if (className == nullptr) {
         handle_py_exception("[ctor] PyObject_GetAttrString");
@@ -60,7 +62,9 @@ PyObjectWrapper::PyObjectWrapper(const std::string& resources)
     if (pClass == nullptr) {
         handle_py_exception("[ctor] PyObject_GetAttr");
     }
-    pInstance_ = PyObject_CallFunctionObjArgs(pClass, nullptr);
+    PyObject *argList = Py_BuildValue("s", instanceName.c_str());
+    pInstance_ = PyObject_CallFunctionObjArgs(pClass, argList, nullptr);
+    Py_DECREF(argList);
     Py_DECREF(pClass);
     if (pInstance_ == nullptr) {
         handle_py_exception("[ctor] PyObject_CallFunctionObjArgs");
diff --git a/pythonfmu/pythonfmu-export/cpp/SlaveInstance.cpp b/pythonfmu/pythonfmu-export/cpp/SlaveInstance.cpp
index ada6499..726e33e 100644
--- a/pythonfmu/pythonfmu-export/cpp/SlaveInstance.cpp
+++ b/pythonfmu/pythonfmu-export/cpp/SlaveInstance.cpp
@@ -8,8 +8,8 @@
 namespace pythonfmu
 {
 
-SlaveInstance::SlaveInstance(const std::string& resources)
-    : instance_(PyObjectWrapper(resources))
+SlaveInstance::SlaveInstance(const std::string& instanceName, const std::string& resources)
+    : instance_(PyObjectWrapper(instanceName, resources))
 {}
 
 void SlaveInstance::SetupExperiment(cppfmu::FMIBoolean, cppfmu::FMIReal, cppfmu::FMIReal tStart, cppfmu::FMIBoolean, cppfmu::FMIReal)
@@ -90,7 +90,7 @@ SlaveInstance::~SlaveInstance() = default;
 std::unique_ptr<pythonfmu::PyState> pyState = nullptr;
 
 cppfmu::UniquePtr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
-    cppfmu::FMIString,
+    cppfmu::FMIString instanceName,
     cppfmu::FMIString,
     cppfmu::FMIString fmuResourceLocation,
     cppfmu::FMIString,
@@ -98,7 +98,7 @@ cppfmu::UniquePtr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
     cppfmu::FMIBoolean,
     cppfmu::FMIBoolean,
     cppfmu::Memory memory,
-    cppfmu::Logger)
+    const cppfmu::Logger&)
 {
 
     auto resources = std::string(fmuResourceLocation);
@@ -117,5 +117,5 @@ cppfmu::UniquePtr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
     }
 
     return cppfmu::AllocateUnique<pythonfmu::SlaveInstance>(
-        memory, resources);
+        memory, instanceName, resources);
 }
diff --git a/pythonfmu/pythonfmu-export/headers/cppfmu/cppfmu_cs.hpp b/pythonfmu/pythonfmu-export/headers/cppfmu/cppfmu_cs.hpp
index d003c84..84a3d5d 100644
--- a/pythonfmu/pythonfmu-export/headers/cppfmu/cppfmu_cs.hpp
+++ b/pythonfmu/pythonfmu-export/headers/cppfmu/cppfmu_cs.hpp
@@ -149,7 +149,7 @@ cppfmu::UniquePtr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
     cppfmu::FMIBoolean visible,
     cppfmu::FMIBoolean interactive,
     cppfmu::Memory memory,
-    cppfmu::Logger logger);
+    const cppfmu::Logger& logger);
 
 
 #endif // header guard
diff --git a/pythonfmu/pythonfmu-export/headers/pythonfmu/PyObjectWrapper.hpp b/pythonfmu/pythonfmu-export/headers/pythonfmu/PyObjectWrapper.hpp
index 19f6dfc..905d374 100644
--- a/pythonfmu/pythonfmu-export/headers/pythonfmu/PyObjectWrapper.hpp
+++ b/pythonfmu/pythonfmu-export/headers/pythonfmu/PyObjectWrapper.hpp
@@ -13,7 +13,7 @@ class PyObjectWrapper
 {
 
 public:
-    explicit PyObjectWrapper(const std::string& resources);
+    PyObjectWrapper(const std::string& instanceName, const std::string& resources);
 
     void setupExperiment(double startTime);
 
@@ -47,6 +47,7 @@ public:
 
 private:
     PyObject* pInstance_;
+    const std::string& instanceName_;
 };
 
 } // namespace pythonfmu
diff --git a/pythonfmu/pythonfmu-export/headers/pythonfmu/SlaveInstance.hpp b/pythonfmu/pythonfmu-export/headers/pythonfmu/SlaveInstance.hpp
index c4e1ce2..a074715 100644
--- a/pythonfmu/pythonfmu-export/headers/pythonfmu/SlaveInstance.hpp
+++ b/pythonfmu/pythonfmu-export/headers/pythonfmu/SlaveInstance.hpp
@@ -16,7 +16,7 @@ class SlaveInstance : public cppfmu::SlaveInstance
 {
 
 public:
-    explicit SlaveInstance(const std::string& resources);
+    SlaveInstance(const std::string& instanceName, const std::string& resources);
 
     void SetupExperiment(cppfmu::FMIBoolean toleranceDefined, cppfmu::FMIReal tolerance, cppfmu::FMIReal tStart, cppfmu::FMIBoolean stopTimeDefined, cppfmu::FMIReal tStop) override;
     void EnterInitializationMode() override;


Test patch:
diff --git a/pythonfmu/tests/pythonslave.py b/pythonfmu/tests/pythonslave.py
index 034c108..c5aea48 100644
--- a/pythonfmu/tests/pythonslave.py
+++ b/pythonfmu/tests/pythonslave.py
@@ -8,8 +8,8 @@ class PythonSlave(Fmi2Slave):
     author = "John Doe"
     description = "A simple description"
 
-    def __init__(self):
-        super().__init__()
+    def __init__(self, instance_name):
+        super().__init__(instance_name)
 
         self.intParam = 42
         self.realOut = 3.0
diff --git a/pythonfmu/tests/test_integration.py b/pythonfmu/tests/test_integration.py
index 78765e7..83a8979 100644
--- a/pythonfmu/tests/test_integration.py
+++ b/pythonfmu/tests/test_integration.py
@@ -58,8 +58,8 @@ slave_class = "PythonSlaveWithDep"
 
 class PythonSlaveWithDep(Fmi2Slave):
 
-    def __init__(self):
-        super().__init__()
+    def __init__(self, instance_name):
+        super().__init__(instance_name)
 
         self.realIn = 22.0
         self.realOut = 0.0
diff --git a/pythonfmu/tests/test_variables.py b/pythonfmu/tests/test_variables.py
index 2597348..b4898f3 100644
--- a/pythonfmu/tests/test_variables.py
+++ b/pythonfmu/tests/test_variables.py
@@ -73,17 +73,15 @@ def test_ScalarVariable_start(var_type, value, causality, initial, variability):
 
     class PySlave(Fmi2Slave):
 
-        modelName = "PySlave"
-
-        def __init__(self):
-            super().__init__()
+        def __init__(self, instance_name):
+            super().__init__(instance_name)
             setattr(self, "var", value)
             self.register_variable(var_obj)
 
         def do_step(self, current_time: float, step_size: float):
             return True
 
-    slave = PySlave()
+    slave = PySlave("testInstance")
 
     xml = slave.to_xml()
     var_node = xml.find(".//ScalarVariable[@name='var']")