Repository: CATIA-Systems/FMPy (language: python)

Issue:
Remoting: fmi2SetString failes with status 3
I have a 32 bit windows FMU with a string as an input. Executing it with fmpy in a 32bit python installation and setting the string inputs works fine. However, when I add remoting and then use fmpy in a 64 bit python installation to set the input, fmi2SetString fails with status 3 (error).


Discussion / hints:

Fix patch:
diff --git a/remoting/client_sm.cpp b/remoting/client_sm.cpp
index accf8a6e..44125e4a 100644
--- a/remoting/client_sm.cpp
+++ b/remoting/client_sm.cpp
@@ -15,14 +15,15 @@ using namespace std;
 #define ARG(IDX) (&pBuf[MAX_ARG_SIZE * IDX])
 
 
-HANDLE inputReady = INVALID_HANDLE_VALUE;
-HANDLE outputReady = INVALID_HANDLE_VALUE;
+static HANDLE inputReady = INVALID_HANDLE_VALUE;
+static HANDLE outputReady = INVALID_HANDLE_VALUE;
 
-HANDLE hMapFile = INVALID_HANDLE_VALUE;
-LPTSTR pBuf = NULL;
+static HANDLE hMapFile = INVALID_HANDLE_VALUE;
+static LPTSTR pBuf = NULL;
 
-fmi2CallbackLogger s_logger = NULL;
-fmi2ComponentEnvironment s_componentEnvironment = NULL;
+static char* s_instanceName = NULL;
+static fmi2CallbackLogger s_logger = NULL;
+static fmi2ComponentEnvironment s_componentEnvironment = NULL;
 
 static 	PROCESS_INFORMATION s_proccessInfo = { 0 };
 
@@ -96,6 +97,7 @@ fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2Str
         return NULL;
     }
 
+    s_instanceName = strdup(instanceName);
     s_logger = functions->logger;
     s_componentEnvironment = functions->componentEnvironment;
 
@@ -216,6 +218,8 @@ void fmi2FreeInstance(fmi2Component c) {
 
     fmi2Status status = makeRPC(rpc_fmi2FreeInstance);
 
+    free(s_instanceName);
+
     UnmapViewOfFile(pBuf);
 
     CloseHandle(hMapFile);
@@ -291,8 +295,21 @@ fmi2Status fmi2GetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t
     return status;
 }
 
-fmi2Status fmi2GetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String  value[]) {
-	NOT_IMPLEMENTED
+fmi2Status fmi2GetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String value[]) {
+
+    if (nvr > 1) {
+        s_logger(s_componentEnvironment, s_instanceName, fmi2Error, "fmiError", "fmi2GetString() supports only one value per call.");
+        return fmi2Error;
+    }
+
+    memcpy(ARG(1), vr, sizeof(fmi2ValueReference) * nvr);
+    memcpy(ARG(2), &nvr, sizeof(size_t));
+
+    fmi2Status status = makeRPC(rpc_fmi2GetString);
+
+    value[0] = ARG(3);
+
+    return status;
 }
 
 fmi2Status fmi2SetReal(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Real value[]) {
@@ -322,8 +339,19 @@ fmi2Status fmi2SetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t
     return makeRPC(rpc_fmi2SetBoolean);
 }
 
-fmi2Status fmi2SetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2String  value[]) {
-	NOT_IMPLEMENTED
+fmi2Status fmi2SetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2String value[]) {
+
+    if (nvr > 1) {
+        s_logger(s_componentEnvironment, s_instanceName, fmi2Error, "fmiError", "fmi2SetString() supports only one value per call.");
+        return fmi2Error;
+    }
+
+    memcpy(ARG(1), vr, sizeof(fmi2ValueReference) * nvr);
+    memcpy(ARG(2), &nvr, sizeof(size_t));
+    
+    strcpy(ARG(3), value[0]);
+    
+    return makeRPC(rpc_fmi2SetString);
 }
 
 /* Getting and setting the internal FMU state */
diff --git a/remoting/client_tcp.cpp b/remoting/client_tcp.cpp
index feaf5cdd..708c895b 100644
--- a/remoting/client_tcp.cpp
+++ b/remoting/client_tcp.cpp
@@ -386,8 +386,16 @@ fmi2Status fmi2GetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t
 	return fmi2Status(r.status);
 }
 
-fmi2Status fmi2GetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String  value[]) {
-	NOT_IMPLEMENTED
+fmi2Status fmi2GetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String value[]) {
+    vector<unsigned int> v_vr(vr, vr + nvr);
+    auto r = client->call("fmi2GetString", v_vr).as<StringReturnValue>();
+    static vector<string> s;
+    s = r.value;
+    for (size_t i = 0; i < r.value.size(); i++) {
+        value[i] = s[i].c_str();
+    }
+    forwardLogMessages(r.logMessages);
+    return fmi2Status(r.status);
 }
 
 fmi2Status fmi2SetReal(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Real value[]) {
@@ -415,7 +423,11 @@ fmi2Status fmi2SetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t
 }
 
 fmi2Status fmi2SetString(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2String  value[]) {
-	NOT_IMPLEMENTED
+    auto vr_ = static_cast<const unsigned int*>(vr);
+    vector<unsigned int> v_vr(vr_, vr_ + nvr);
+    vector<string> v_value(value, value + nvr);
+    auto r = client->call("fmi2SetString", v_vr, v_value).as<ReturnValue>();
+    return handleReturnValue(r);
 }
 
 /* Getting and setting the internal FMU state */
diff --git a/remoting/remoting_tcp.h b/remoting/remoting_tcp.h
index ae4e052b..18939aff 100644
--- a/remoting/remoting_tcp.h
+++ b/remoting/remoting_tcp.h
@@ -32,6 +32,13 @@ struct IntegerReturnValue {
 	MSGPACK_DEFINE_ARRAY(status, logMessages, value)
 };
 
+struct StringReturnValue {
+	int status;
+	std::list<LogMessage> logMessages;
+	std::vector<std::string> value;
+	MSGPACK_DEFINE_ARRAY(status, logMessages, value)
+};
+
 struct EventInfoReturnValue {
 	int status;
 	std::list<LogMessage> logMessages;
diff --git a/remoting/server_sm.cpp b/remoting/server_sm.cpp
index c4303e1e..f3470974 100644
--- a/remoting/server_sm.cpp
+++ b/remoting/server_sm.cpp
@@ -223,10 +223,19 @@ int main(int argc, char *argv[]) {
             STATUS = FMI2GetBoolean(m_instance, ARG(fmi2ValueReference*, 1), *ARG(size_t*, 2), ARG(fmi2Boolean*, 3));
             break;
         
-        case rpc_fmi2GetString:
-            STATUS = FMIError;
+        case rpc_fmi2GetString: {
+            fmi2String value;
+            STATUS = FMI2GetString(m_instance, ARG(fmi2ValueReference*, 1), *ARG(size_t*, 2), &value);
+            if (STATUS < FMIDiscard) {
+                if (strlen(value) > (MAX_ARG_SIZE - 1)) {
+                    cout << "Error: value returned from fmi2GetString() exceeds " << (MAX_ARG_SIZE - 1) << " bytes." << endl;
+                    STATUS = FMIError;
+                } else {
+                    strcpy(ARG(char*, 3), value);
+                }
+            }
             break;
-        
+        }
         case rpc_fmi2SetReal:
             STATUS = FMI2SetReal(m_instance, ARG(fmi2ValueReference*, 1), *ARG(size_t*, 2), ARG(const fmi2Real*, 3));
             break;
@@ -239,10 +248,11 @@ int main(int argc, char *argv[]) {
             STATUS = FMI2SetBoolean(m_instance, ARG(fmi2ValueReference*, 1), *ARG(size_t*, 2), ARG(const fmi2Boolean*, 3));
             break;
         
-        case rpc_fmi2SetString:
-            STATUS = FMIError;
+        case rpc_fmi2SetString: {
+            const fmi2String value = ARG(const fmi2String, 3);
+            STATUS = FMI2SetString(m_instance, ARG(fmi2ValueReference*, 1), *ARG(size_t*, 2), &value);
             break;
-
+        }
         case rpc_fmi2GetFMUstate:
         case rpc_fmi2SetFMUstate:
         case rpc_fmi2FreeFMUstate:
diff --git a/remoting/server_tcp.cpp b/remoting/server_tcp.cpp
index 6077ad9c..9af1829e 100644
--- a/remoting/server_tcp.cpp
+++ b/remoting/server_tcp.cpp
@@ -138,18 +138,24 @@ class FMU {
 		return r;
 	}
 
-	RealReturnValue createRealReturnValue(int status, const vector<double> &value) {
+	RealReturnValue createRealReturnValue(int status, const vector<double>& value) {
 		RealReturnValue r = { status, s_logMessages, value };
 		s_logMessages.clear();
 		return r;
 	}
 
-	IntegerReturnValue createIntegerReturnValue(int status, const vector<int> &value) {
+	IntegerReturnValue createIntegerReturnValue(int status, const vector<int>& value) {
 		IntegerReturnValue r = { status, s_logMessages, value };
 		s_logMessages.clear();
 		return r;
 	}
 
+	StringReturnValue createStringReturnValue(int status, const vector<string>& value) {
+		StringReturnValue r = { status, s_logMessages, value };
+		s_logMessages.clear();
+		return r;
+	}
+
 	EventInfoReturnValue createEventInfoReturnValue(int status, const fmi2EventInfo *eventInfo) {
 		EventInfoReturnValue r = {
 			status,
@@ -259,13 +265,24 @@ class FMU {
 			return createIntegerReturnValue(status, value);
 		});
 
-		srv.bind("fmi2GetBoolean", [this](const vector<unsigned int> &vr) {
+		srv.bind("fmi2GetBoolean", [this](const vector<unsigned int>& vr) {
 			resetExitTimer();
 			vector<int> value(vr.size());
 			const FMIStatus status = FMI2GetBoolean(m_instance, vr.data(), vr.size(), value.data());
 			return createIntegerReturnValue(status, value);
 		});
 
+		srv.bind("fmi2GetString", [this](const vector<unsigned int>& vr) {
+			resetExitTimer();
+			vector<fmi2String> value(vr.size());
+			const FMIStatus status = FMI2GetString(m_instance, vr.data(), vr.size(), value.data());
+			vector<string> v;
+			for (size_t i = 0; i < value.size(); i++) {
+				v.push_back(value[i]);
+			}
+			return createStringReturnValue(status, v);
+		});
+
 		srv.bind("fmi2SetReal", [this](const vector<unsigned int> &vr, const vector<double> &value) {
 			resetExitTimer();
 			const FMIStatus status = FMI2SetReal(m_instance, vr.data(), vr.size(), value.data());
@@ -278,10 +295,20 @@ class FMU {
 			return createReturnValue(status);
 		});
 
-		srv.bind("fmi2SetBoolean", [this](const vector<unsigned int> &vr, const vector<int> &value) {
+		srv.bind("fmi2SetBoolean", [this](const vector<unsigned int>& vr, const vector<int>& value) {
 			resetExitTimer();
 			const FMIStatus status = FMI2SetBoolean(m_instance, vr.data(), vr.size(), value.data());
 			return createReturnValue(status);
+			});
+
+		srv.bind("fmi2SetString", [this](const vector<unsigned int>& vr, const vector<string>& value) {
+			resetExitTimer();
+			vector<fmi2String> v_value;
+			for (size_t i = 0; i < value.size(); i++) {
+				v_value.push_back(value[i].c_str());
+			}
+			const FMIStatus status = FMI2SetString(m_instance, vr.data(), vr.size(), v_value.data());
+			return createReturnValue(status);
 		});
 
 		/* Getting and setting the internal FMU state */


Test patch:
diff --git a/remoting/client_test.cpp b/remoting/client_test.cpp
index f60dc1d7..377b8089 100644
--- a/remoting/client_test.cpp
+++ b/remoting/client_test.cpp
@@ -69,8 +69,10 @@ int main(int argc, char *argv[]) {
 	auto setupExperiment         = get<fmi2SetupExperimentTYPE>         (l, "fmi2SetupExperiment");
 	auto enterInitializationMode = get<fmi2EnterInitializationModeTYPE> (l, "fmi2EnterInitializationMode");
 	auto exitInitializationMode  = get<fmi2ExitInitializationModeTYPE>  (l, "fmi2ExitInitializationMode");
-	auto getReal                 = get<fmi2GetRealTYPE>                 (l, "fmi2GetReal");
-	auto doStep                  = get<fmi2DoStepTYPE>                  (l, "fmi2DoStep");
+    auto getReal                 = get<fmi2GetRealTYPE>                 (l, "fmi2GetReal");
+    auto getString               = get<fmi2GetStringTYPE>               (l, "fmi2GetString");
+    auto setString               = get<fmi2SetStringTYPE>               (l, "fmi2SetString");
+    auto doStep                  = get<fmi2DoStepTYPE>                  (l, "fmi2DoStep");
 	auto terminate               = get<fmi2TerminateTYPE>               (l, "fmi2Terminate");
 	auto freeInstance            = get<fmi2FreeInstanceTYPE>            (l, "fmi2FreeInstance");