pythonfmu3 0.2.2__py3-none-any.whl → 0.2.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pythonfmu3 might be problematic. Click here for more details.

Files changed (199) hide show
  1. pythonfmu3/__init__.py +8 -8
  2. pythonfmu3/__main__.py +52 -52
  3. pythonfmu3/_version.py +1 -1
  4. pythonfmu3/builder.py +260 -260
  5. pythonfmu3/default_experiment.py +7 -7
  6. pythonfmu3/deploy.py +118 -118
  7. pythonfmu3/enums.py +48 -48
  8. pythonfmu3/fmi3slave.py +438 -438
  9. pythonfmu3/logmsg.py +14 -14
  10. pythonfmu3/osutil.py +22 -22
  11. pythonfmu3/pythonfmu-export/.clang-format +35 -35
  12. pythonfmu3/pythonfmu-export/CMakeLists.txt +59 -59
  13. pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj +184 -0
  14. pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj.filters +8 -0
  15. pythonfmu3/pythonfmu-export/build/CMakeCache.txt +369 -0
  16. pythonfmu3/pythonfmu-export/build/CMakeFiles/0ee4db0675778dfba98c7160896d5a45/generate.stamp.rule +1 -0
  17. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCCompiler.cmake +72 -0
  18. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCXXCompiler.cmake +83 -0
  19. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_C.bin +0 -0
  20. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_CXX.bin +0 -0
  21. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeRCCompiler.cmake +6 -0
  22. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeSystem.cmake +15 -0
  23. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CMakeCCompilerId.c +838 -0
  24. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.exe +0 -0
  25. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.vcxproj +71 -0
  26. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  27. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.exe.recipe +11 -0
  28. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  29. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  30. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  31. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +2 -0
  32. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  33. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  34. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  35. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CMakeCXXCompilerId.cpp +826 -0
  36. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  37. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.vcxproj +71 -0
  38. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  39. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
  40. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  41. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  42. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  43. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
  44. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  45. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  46. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  47. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.txt +1 -0
  48. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.vcxproj +31 -0
  49. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.recipe +11 -0
  50. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
  51. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCCompiler.cmake +81 -0
  52. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCXXCompiler.cmake +101 -0
  53. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_C.bin +0 -0
  54. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  55. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeRCCompiler.cmake +6 -0
  56. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeSystem.cmake +15 -0
  57. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CMakeCCompilerId.c +904 -0
  58. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.exe +0 -0
  59. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.vcxproj +72 -0
  60. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  61. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.exe.recipe +11 -0
  62. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  63. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  64. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  65. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +2 -0
  66. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  67. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  68. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  69. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +919 -0
  70. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  71. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.vcxproj +72 -0
  72. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  73. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
  74. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  75. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  76. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  77. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
  78. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  79. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  80. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  81. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.txt +1 -0
  82. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.vcxproj +31 -0
  83. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.recipe +11 -0
  84. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
  85. pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeConfigureLog.yaml +568 -0
  86. pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeOutput.log +357 -0
  87. pythonfmu3/pythonfmu-export/build/CMakeFiles/TargetDirectories.txt +3 -0
  88. pythonfmu3/pythonfmu-export/build/CMakeFiles/cmake.check_cache +1 -0
  89. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp +1 -0
  90. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.depend +28 -0
  91. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.list +2 -0
  92. pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj +174 -0
  93. pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj.filters +13 -0
  94. pythonfmu3/pythonfmu-export/build/cmake_install.cmake +50 -0
  95. pythonfmu3/pythonfmu-export/build/install_manifest.txt +0 -0
  96. pythonfmu3/pythonfmu-export/build/pythonfmu-export.sln +53 -0
  97. pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp +1 -0
  98. pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp.depend +2 -0
  99. pythonfmu3/pythonfmu-export/build/src/Debug/pythonfmu-export.pdb +0 -0
  100. pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.exp +0 -0
  101. pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.lib +0 -0
  102. pythonfmu3/pythonfmu-export/build/src/cmake_install.cmake +34 -0
  103. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/PySlaveInstance.obj +0 -0
  104. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/cppfmu_cs.obj +0 -0
  105. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/exports.def +1885 -0
  106. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/fmi_functions.obj +0 -0
  107. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/objects.txt +3 -0
  108. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
  109. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
  110. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
  111. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.command.1.tlog +10 -0
  112. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.read.1.tlog +1 -0
  113. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.write.1.tlog +2 -0
  114. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.command.1.tlog +1 -0
  115. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.1.tlog +1 -0
  116. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.3.tlog +1 -0
  117. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.write.1.tlog +1 -0
  118. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +2 -0
  119. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/unsuccessfulbuild +0 -0
  120. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/vc142.pdb +0 -0
  121. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/MinSizeRel/objects.txt +3 -0
  122. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/RelWithDebInfo/objects.txt +3 -0
  123. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/PySlaveInstance.obj +0 -0
  124. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/cppfmu_cs.obj +0 -0
  125. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/exports.def +538 -0
  126. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/fmi_functions.obj +0 -0
  127. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/objects.txt +3 -0
  128. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.dll.recipe +14 -0
  129. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
  130. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
  131. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
  132. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.command.1.tlog +10 -0
  133. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.read.1.tlog +1 -0
  134. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.write.1.tlog +2 -0
  135. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.command.1.tlog +0 -0
  136. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.delete.1.tlog +0 -0
  137. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.read.1.tlog +0 -0
  138. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.write.1.tlog +0 -0
  139. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +2 -0
  140. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.write.1u.tlog +0 -0
  141. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj +414 -0
  142. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj.filters +48 -0
  143. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
  144. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
  145. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +29 -0
  146. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +3 -0
  147. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
  148. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.recipe +17 -0
  149. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate +2 -0
  150. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog +10 -0
  151. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog +27 -0
  152. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog +2 -0
  153. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
  154. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
  155. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +29 -0
  156. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +3 -0
  157. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
  158. pythonfmu3/pythonfmu-export/build_unix.sh +4 -4
  159. pythonfmu3/pythonfmu-export/build_win.sh +4 -4
  160. pythonfmu3/pythonfmu-export/src/CMakeLists.txt +45 -45
  161. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +161 -161
  162. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +192 -192
  163. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +185 -185
  164. pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +937 -937
  165. pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +638 -638
  166. pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +343 -343
  167. pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +94 -94
  168. pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +17 -17
  169. pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +744 -744
  170. pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +111 -111
  171. pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +84 -84
  172. pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
  173. pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
  174. pythonfmu3/resources/binaries/x86_64-windows1/pythonfmu-export.dll +0 -0
  175. pythonfmu3/tests/conftest.py +5 -5
  176. pythonfmu3/tests/data/csvdemo.csv +7 -7
  177. pythonfmu3/tests/slaves/PythonSlaveWithException.py +18 -18
  178. pythonfmu3/tests/slaves/localmodule.py +8 -8
  179. pythonfmu3/tests/slaves/pythonslave.py +52 -52
  180. pythonfmu3/tests/slaves/pythonslave_read_file.py +23 -23
  181. pythonfmu3/tests/slaves/slavewithdep.py +21 -21
  182. pythonfmu3/tests/test_builder.py +195 -195
  183. pythonfmu3/tests/test_deploy.py +48 -48
  184. pythonfmu3/tests/test_fmi3slave.py +152 -152
  185. pythonfmu3/tests/test_integration.py +350 -350
  186. pythonfmu3/tests/test_logger.py +180 -180
  187. pythonfmu3/tests/test_multiple_fmus.py +141 -141
  188. pythonfmu3/tests/test_variables.py +334 -334
  189. pythonfmu3/tests/utils.py +13 -13
  190. pythonfmu3/unit.py +62 -62
  191. pythonfmu3/variable_types.py +101 -101
  192. pythonfmu3/variables.py +400 -400
  193. pythonfmu3-0.2.2.dist-info/LICENSE → pythonfmu3-0.2.2.2.dist-info/LICENSE.txt +23 -23
  194. {pythonfmu3-0.2.2.dist-info → pythonfmu3-0.2.2.2.dist-info}/METADATA +9 -6
  195. pythonfmu3-0.2.2.2.dist-info/RECORD +200 -0
  196. {pythonfmu3-0.2.2.dist-info → pythonfmu3-0.2.2.2.dist-info}/WHEEL +1 -1
  197. {pythonfmu3-0.2.2.dist-info → pythonfmu3-0.2.2.2.dist-info}/entry_points.txt +1 -0
  198. pythonfmu3-0.2.2.dist-info/RECORD +0 -54
  199. {pythonfmu3-0.2.2.dist-info → pythonfmu3-0.2.2.2.dist-info}/top_level.txt +0 -0
@@ -1,744 +1,744 @@
1
-
2
- #include "pythonfmu/IPyState.hpp"
3
- #include "pythonfmu/PySlaveInstance.hpp"
4
-
5
- #include "pythonfmu/PyState.hpp"
6
-
7
- #include "cppfmu/cppfmu_cs.hpp"
8
-
9
- #include <fstream>
10
- #include <functional>
11
- #include <mutex>
12
- #include <regex>
13
- #include <sstream>
14
- #include <utility>
15
-
16
- namespace pythonfmu
17
- {
18
-
19
- inline std::string getline(const std::string& fileName)
20
- {
21
- std::string line;
22
- std::ifstream infile(fileName);
23
- std::getline(infile, line);
24
- return line;
25
- }
26
-
27
- inline std::string findClassName(const std::string& fileName)
28
- {
29
- std::string line;
30
- std::ifstream infile(fileName);
31
- std::string regexStr(R"(^class (\w+)\(\s*Fmi3Slave\s*\)\s*:)");
32
- while (getline(infile, line)) {
33
- std::smatch m;
34
- std::regex re(regexStr);
35
- if (std::regex_search(line, m, re)) {
36
- return m[1];
37
- }
38
- }
39
- return "";
40
- }
41
-
42
- inline void py_safe_run(const std::function<void(PyGILState_STATE gilState)>& f)
43
- {
44
- PyGILState_STATE gil_state = PyGILState_Ensure();
45
- f(gil_state);
46
- PyGILState_Release(gil_state);
47
- }
48
-
49
- PySlaveInstance::PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, const bool visible, std::shared_ptr<IPyState> pyState)
50
- : pyState_{ std::move(pyState) }
51
- , instanceName_(std::move(instanceName))
52
- , resources_(std::move(resources))
53
- , logger_(logger)
54
- , visible_(visible)
55
- {
56
- py_safe_run([this](PyGILState_STATE gilState) {
57
- // Append resources path to python sys path
58
- PyObject* sys_module = PyImport_ImportModule("sys");
59
- if (sys_module == nullptr) {
60
- handle_py_exception("[ctor] PyImport_ImportModule", gilState);
61
- }
62
- PyObject* sys_path = PyObject_GetAttrString(sys_module, "path");
63
- Py_DECREF(sys_module);
64
- if (sys_path == nullptr) {
65
- handle_py_exception("[ctor] PyObject_GetAttrString", gilState);
66
- }
67
- int success = PyList_Insert(sys_path, 0, PyUnicode_FromString(resources_.c_str()));
68
- Py_DECREF(sys_path);
69
- if (success != 0) {
70
- handle_py_exception("[ctor] PyList_Insert", gilState);
71
- }
72
-
73
- std::string moduleName = getline(resources_ + "/slavemodule.txt");
74
- PyObject* pModule = PyImport_ImportModule(moduleName.c_str());
75
- if (pModule == nullptr) {
76
- PyErr_Print();
77
- handle_py_exception("[ctor] PyImport_ImportModule", gilState);
78
- }
79
-
80
- std::string className = findClassName(resources_ + "/" + moduleName + ".py");
81
- if (className.empty()) {
82
- cleanPyObject();
83
- throw cppfmu::FatalError("Unable to find class extending Fmi3Slave!");
84
- }
85
-
86
- PyObject* pyClassName = Py_BuildValue("s", className.c_str());
87
- if (pyClassName == nullptr) {
88
- handle_py_exception("[ctor] Py_BuildValue", gilState);
89
- }
90
-
91
- pClass_ = PyObject_GetAttr(pModule, pyClassName);
92
- Py_DECREF(pyClassName);
93
- Py_DECREF(pModule);
94
- if (pClass_ == nullptr) {
95
- handle_py_exception("[ctor] PyObject_GetAttr", gilState);
96
- }
97
-
98
- initialize(gilState);
99
- });
100
- }
101
-
102
- void PySlaveInstance::clearLogBuffer() const
103
- {
104
- clearLogStrBuffer();
105
-
106
- PyObject* debugField = Py_BuildValue("s", "debug");
107
- PyObject* msgField = Py_BuildValue("s", "msg");
108
- PyObject* categoryField = Py_BuildValue("s", "category");
109
- PyObject* statusField = Py_BuildValue("s", "status");
110
-
111
- if ( pMessages_ != NULL && PyList_Check(pMessages_) ) {
112
- auto size = PyList_Size(pMessages_);
113
- for (auto i = 0; i < size; i++) {
114
- PyObject* msg = PyList_GetItem(pMessages_, i);
115
-
116
- auto debugAttr = PyObject_GetAttr(msg, debugField);
117
- auto msgAttr = PyObject_GetAttr(msg, msgField);
118
- auto categoryAttr = PyObject_GetAttr(msg, categoryField);
119
- auto statusAttr = PyObject_GetAttr(msg, statusField);
120
-
121
- auto statusValue = static_cast<cppfmu::FMIStatus>(PyLong_AsLong(statusAttr));
122
-
123
- PyObject* msgValue = PyUnicode_AsEncodedString(msgAttr, "utf-8", nullptr);
124
- char* msgStr = PyBytes_AsString(msgValue);
125
- logStrBuffer.emplace_back(msgValue);
126
-
127
- const char* categoryStr = "";
128
- if (categoryAttr != Py_None) {
129
- PyObject* categoryValue = PyUnicode_AsEncodedString(categoryAttr, "utf-8", nullptr);
130
- categoryStr = PyBytes_AsString(categoryValue);
131
- logStrBuffer.emplace_back(categoryValue);
132
- }
133
-
134
- if (PyObject_IsTrue(debugAttr)) {
135
- const_cast<cppfmu::Logger&>(logger_).DebugLog(statusValue, categoryStr, msgStr);
136
- } else {
137
- const_cast<cppfmu::Logger&>(logger_).Log(statusValue, categoryStr, msgStr);
138
- }
139
- }
140
- PyList_SetSlice(pMessages_, 0, size, nullptr);
141
- }
142
- Py_DECREF(debugField);
143
- Py_DECREF(msgField);
144
- Py_DECREF(categoryField);
145
- Py_DECREF(statusField);
146
- }
147
-
148
- void PySlaveInstance::initialize(PyGILState_STATE gilState)
149
- {
150
- Py_XDECREF(pInstance_);
151
- Py_XDECREF(pMessages_);
152
-
153
- PyObject* args = PyTuple_New(0);
154
- PyObject* kwargs = Py_BuildValue("{ss,ss,sn,si}",
155
- "instance_name", instanceName_.c_str(),
156
- "resources", resources_.c_str(),
157
- "logger", &logger_,
158
- "visible", visible_);
159
- pInstance_ = PyObject_Call(pClass_, args, kwargs);
160
- Py_DECREF(args);
161
- Py_DECREF(kwargs);
162
- if (pInstance_ == nullptr) {
163
- handle_py_exception("[initialize] PyObject_Call", gilState);
164
- }
165
- pMessages_ = PyObject_CallMethod(pInstance_, "_get_log_queue", nullptr);
166
- }
167
-
168
- void PySlaveInstance::SetupExperiment(cppfmu::FMIBoolean, cppfmu::FMIFloat64, cppfmu::FMIFloat64 startTime, cppfmu::FMIBoolean, cppfmu::FMIFloat64)
169
- {
170
- py_safe_run([this, startTime](PyGILState_STATE gilState) {
171
- auto f = PyObject_CallMethod(pInstance_, "setup_experiment", "(d)", startTime);
172
- if (f == nullptr) {
173
- handle_py_exception("[setupExperiment] PyObject_CallMethod", gilState);
174
- }
175
- Py_DECREF(f);
176
- clearLogBuffer();
177
- });
178
- }
179
-
180
- void PySlaveInstance::EnterInitializationMode()
181
- {
182
- py_safe_run([this](PyGILState_STATE gilState) {
183
- auto f = PyObject_CallMethod(pInstance_, "enter_initialization_mode", nullptr);
184
- if (f == nullptr) {
185
- handle_py_exception("[enterInitializationMode] PyObject_CallMethod", gilState);
186
- }
187
- Py_DECREF(f);
188
- clearLogBuffer();
189
- });
190
- }
191
-
192
- void PySlaveInstance::ExitInitializationMode()
193
- {
194
- py_safe_run([this](PyGILState_STATE gilState) {
195
- auto f = PyObject_CallMethod(pInstance_, "exit_initialization_mode", nullptr);
196
- if (f == nullptr) {
197
- handle_py_exception("[exitInitializationMode] PyObject_CallMethod", gilState);
198
- }
199
- Py_DECREF(f);
200
- clearLogBuffer();
201
- });
202
- }
203
-
204
- cppfmu::FMIStatus PySlaveInstance::DoStep(cppfmu::FMIFloat64 currentTime,
205
- cppfmu::FMIFloat64 stepSize,
206
- cppfmu::FMIBoolean noSetFmuStatePriorToCurrentPoint,
207
- cppfmu::FMIBoolean* eventHandlingNeeded,
208
- cppfmu::FMIBoolean* terminateSimulation,
209
- cppfmu::FMIBoolean* earlyReturn,
210
- cppfmu::FMIFloat64& endOfStep)
211
- {
212
- cppfmu::FMIStatus fmuStatus = cppfmu::FMIOK;
213
- py_safe_run([this, &fmuStatus, currentTime, stepSize, terminateSimulation](PyGILState_STATE gilState) {
214
- auto f = PyObject_CallMethod(pInstance_, "do_step", "(dd)", currentTime, stepSize);
215
- if (f == nullptr) {
216
- handle_py_exception("[doStep] PyObject_CallMethod", gilState);
217
- }
218
-
219
- if (PyObject_HasAttrString(f, "status")) {
220
- PyObject* pyStatus = PyObject_GetAttrString(f, "status");
221
- if (pyStatus) {
222
- fmuStatus = static_cast<cppfmu::FMIStatus>(PyLong_AsLong(pyStatus));
223
- Py_DECREF(pyStatus);
224
- }
225
- } else {
226
- bool status = static_cast<bool>(PyObject_IsTrue(f));
227
- if (!status) {
228
- fmuStatus = cppfmu::FMIDiscard;
229
- }
230
- }
231
-
232
- if (PyObject_HasAttrString(f, "terminateSimulation")) {
233
- PyObject* pyTerminateSimulation = PyObject_GetAttrString(f, "terminateSimulation");
234
- if (pyTerminateSimulation) {
235
- *terminateSimulation = static_cast<bool>(PyObject_IsTrue(pyTerminateSimulation));
236
- Py_DECREF(pyTerminateSimulation);
237
- }
238
- }
239
- Py_DECREF(f);
240
- clearLogBuffer();
241
- });
242
-
243
- return fmuStatus;
244
- }
245
-
246
- void PySlaveInstance::Reset()
247
- {
248
- py_safe_run([this](PyGILState_STATE gilState) {
249
- initialize(gilState);
250
- });
251
- }
252
-
253
- void PySlaveInstance::Terminate()
254
- {
255
- py_safe_run([this](PyGILState_STATE gilState) {
256
- auto f = PyObject_CallMethod(pInstance_, "terminate", nullptr);
257
- if (f == nullptr) {
258
- handle_py_exception("[terminate] PyObject_CallMethod", gilState);
259
- }
260
- Py_DECREF(f);
261
- clearLogBuffer();
262
- });
263
- }
264
-
265
- void PySlaveInstance::SetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIFloat64* values, std::size_t nValues)
266
- {
267
- py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
268
- PyObject* vrs = PyList_New(nvr);
269
- PyObject* refs = PyList_New(nValues);
270
- for (int i = 0; i < nvr; i++) {
271
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
272
- }
273
- for (int i = 0; i < nValues; i++) {
274
- PyList_SetItem(refs, i, Py_BuildValue("d", values[i]));
275
- }
276
-
277
- auto f = PyObject_CallMethod(pInstance_, "set_float64", "(OO)", vrs, refs);
278
- Py_DECREF(vrs);
279
- Py_DECREF(refs);
280
- if (f == nullptr) {
281
- handle_py_exception("[setFloat64] PyObject_CallMethod", gilState);
282
- }
283
- Py_DECREF(f);
284
- clearLogBuffer();
285
- });
286
- }
287
-
288
- void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* values)
289
- {
290
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
291
- PyObject* vrs = PyList_New(nvr);
292
- PyObject* refs = PyList_New(nvr);
293
- for (int i = 0; i < nvr; i++) {
294
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
295
- PyList_SetItem(refs, i, Py_BuildValue("i", values[i]));
296
- }
297
-
298
- auto f = PyObject_CallMethod(pInstance_, "set_int32", "(OO)", vrs, refs);
299
- Py_DECREF(vrs);
300
- Py_DECREF(refs);
301
- if (f == nullptr) {
302
- handle_py_exception("[setInt32] PyObject_CallMethod", gilState);
303
- }
304
- Py_DECREF(f);
305
- clearLogBuffer();
306
- });
307
- }
308
-
309
- void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* values)
310
- {
311
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
312
- PyObject* vrs = PyList_New(nvr);
313
- PyObject* refs = PyList_New(nvr);
314
- for (int i = 0; i < nvr; i++) {
315
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
316
- PyList_SetItem(refs, i, Py_BuildValue("L", values[i]));
317
- }
318
-
319
- auto f = PyObject_CallMethod(pInstance_, "set_int64", "(OO)", vrs, refs);
320
- Py_DECREF(vrs);
321
- Py_DECREF(refs);
322
- if (f == nullptr) {
323
- handle_py_exception("[setInt64] PyObject_CallMethod", gilState);
324
- }
325
- Py_DECREF(f);
326
- clearLogBuffer();
327
- });
328
- }
329
-
330
- void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* values)
331
- {
332
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
333
- PyObject* vrs = PyList_New(nvr);
334
- PyObject* refs = PyList_New(nvr);
335
- for (int i = 0; i < nvr; i++) {
336
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
337
- PyList_SetItem(refs, i, Py_BuildValue("K", values[i]));
338
- }
339
-
340
- auto f = PyObject_CallMethod(pInstance_, "set_uint64", "(OO)", vrs, refs);
341
- Py_DECREF(vrs);
342
- Py_DECREF(refs);
343
- if (f == nullptr) {
344
- handle_py_exception("[setUInt64] PyObject_CallMethod", gilState);
345
- }
346
- Py_DECREF(f);
347
- clearLogBuffer();
348
- });
349
- }
350
-
351
- void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* values)
352
- {
353
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
354
- PyObject* vrs = PyList_New(nvr);
355
- PyObject* refs = PyList_New(nvr);
356
- for (int i = 0; i < nvr; i++) {
357
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
358
- PyList_SetItem(refs, i, PyBool_FromLong(values[i]));
359
- }
360
-
361
- auto f = PyObject_CallMethod(pInstance_, "set_boolean", "(OO)", vrs, refs);
362
- Py_DECREF(vrs);
363
- Py_DECREF(refs);
364
- if (f == nullptr) {
365
- handle_py_exception("[setBoolean] PyObject_CallMethod", gilState);
366
- }
367
- Py_DECREF(f);
368
- clearLogBuffer();
369
- });
370
- }
371
-
372
- void PySlaveInstance::SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* values)
373
- {
374
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
375
- PyObject* vrs = PyList_New(nvr);
376
- PyObject* refs = PyList_New(nvr);
377
- for (int i = 0; i < nvr; i++) {
378
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
379
- PyList_SetItem(refs, i, Py_BuildValue("s", values[i]));
380
- }
381
-
382
- auto f = PyObject_CallMethod(pInstance_, "set_string", "(OO)", vrs, refs);
383
- Py_DECREF(vrs);
384
- Py_DECREF(refs);
385
- if (f == nullptr) {
386
- handle_py_exception("[setString] PyObject_CallMethod", gilState);
387
- }
388
- Py_DECREF(f);
389
- clearLogBuffer();
390
- });
391
- }
392
-
393
- void PySlaveInstance::GetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIFloat64* values, std::size_t nValues) const
394
- {
395
- py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
396
- PyObject* vrs = PyList_New(nvr);
397
- for (int i = 0; i < nvr; i++) {
398
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
399
- }
400
-
401
- auto refs = PyObject_CallMethod(pInstance_, "get_float64", "O", vrs);
402
- Py_DECREF(vrs);
403
- if (refs == nullptr) {
404
- handle_py_exception("[getFloat64] PyObject_CallMethod", gilState);
405
- }
406
-
407
- for (int i = 0; i < nValues; i++) {
408
- PyObject* value = PyList_GetItem(refs, i);
409
- values[i] = PyFloat_AsDouble(value);
410
- }
411
- Py_DECREF(refs);
412
- clearLogBuffer();
413
- });
414
- }
415
-
416
- void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* values) const
417
- {
418
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
419
- PyObject* vrs = PyList_New(nvr);
420
- for (int i = 0; i < nvr; i++) {
421
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
422
- }
423
- auto refs = PyObject_CallMethod(pInstance_, "get_int32", "O", vrs);
424
- Py_DECREF(vrs);
425
- if (refs == nullptr) {
426
- handle_py_exception("[getInt32] PyObject_CallMethod", gilState);
427
- }
428
-
429
- for (int i = 0; i < nvr; i++) {
430
- PyObject* value = PyList_GetItem(refs, i);
431
- values[i] = static_cast<cppfmu::FMIInt32>(PyLong_AsLong(value));
432
- }
433
- Py_DECREF(refs);
434
- clearLogBuffer();
435
- });
436
- }
437
-
438
- void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* values) const
439
- {
440
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
441
- PyObject* vrs = PyList_New(nvr);
442
- for (int i = 0; i < nvr; i++) {
443
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
444
- }
445
- auto refs = PyObject_CallMethod(pInstance_, "get_int64", "O", vrs);
446
- Py_DECREF(vrs);
447
- if (refs == nullptr) {
448
- handle_py_exception("[getInt64] PyObject_CallMethod", gilState);
449
- }
450
-
451
- for (int i = 0; i < nvr; i++) {
452
- PyObject* value = PyList_GetItem(refs, i);
453
- values[i] = static_cast<cppfmu::FMIInt64>(PyLong_AsLongLong(value));
454
- }
455
- Py_DECREF(refs);
456
- clearLogBuffer();
457
- });
458
- }
459
-
460
- void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* values) const
461
- {
462
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
463
- PyObject* vrs = PyList_New(nvr);
464
- for (int i = 0; i < nvr; i++) {
465
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
466
- }
467
- auto refs = PyObject_CallMethod(pInstance_, "get_uint64", "O", vrs);
468
- Py_DECREF(vrs);
469
- if (refs == nullptr) {
470
- handle_py_exception("[getUInt64] PyObject_CallMethod", gilState);
471
- }
472
-
473
- for (int i = 0; i < nvr; i++) {
474
- PyObject* item = PyList_GetItem(refs, i);
475
- PyObject* value = PyObject_GetAttrString(item, "value");
476
- if (value && PyLong_Check(value))
477
- {
478
- values[i] = static_cast<cppfmu::FMIUInt64>(PyLong_AsUnsignedLongLong(value));
479
- }
480
- }
481
-
482
- Py_DECREF(refs);
483
- clearLogBuffer();
484
- });
485
- }
486
-
487
- void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* values) const
488
- {
489
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
490
- PyObject* vrs = PyList_New(nvr);
491
- for (int i = 0; i < nvr; i++) {
492
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
493
- }
494
- auto refs = PyObject_CallMethod(pInstance_, "get_boolean", "O", vrs);
495
- Py_DECREF(vrs);
496
- if (refs == nullptr) {
497
- handle_py_exception("[getBoolean] PyObject_CallMethod", gilState);
498
- }
499
-
500
- for (int i = 0; i < nvr; i++) {
501
- PyObject* value = PyList_GetItem(refs, i);
502
- values[i] = PyObject_IsTrue(value);
503
- }
504
- Py_DECREF(refs);
505
- clearLogBuffer();
506
- });
507
- }
508
-
509
- void PySlaveInstance::GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* values) const
510
- {
511
- py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
512
- clearStrBuffer();
513
- PyObject* vrs = PyList_New(nvr);
514
- for (int i = 0; i < nvr; i++) {
515
- PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
516
- }
517
- auto refs = PyObject_CallMethod(pInstance_, "get_string", "O", vrs);
518
- Py_DECREF(vrs);
519
- if (refs == nullptr) {
520
- handle_py_exception("[getString] PyObject_CallMethod", gilState);
521
- }
522
-
523
- for (int i = 0; i < nvr; i++) {
524
- PyObject* value = PyUnicode_AsEncodedString(PyList_GetItem(refs, i), "utf-8", nullptr);
525
- values[i] = PyBytes_AsString(value);
526
- strBuffer.emplace_back(value);
527
- }
528
- Py_DECREF(refs);
529
- clearLogBuffer();
530
- });
531
- }
532
-
533
- void PySlaveInstance::GetFMUstate(fmi3FMUState& state)
534
- {
535
- py_safe_run([this, &state](PyGILState_STATE gilState) {
536
- auto f = PyObject_CallMethod(pInstance_, "_get_fmu_state", nullptr);
537
- if (f == nullptr) {
538
- handle_py_exception("[_get_fmu_state] PyObject_CallMethod", gilState);
539
- }
540
- state = reinterpret_cast<fmi3FMUState*>(f);
541
- clearLogBuffer();
542
- });
543
- }
544
-
545
- void PySlaveInstance::SetFMUstate(const fmi3FMUState& state)
546
- {
547
- py_safe_run([this, &state](PyGILState_STATE gilState) {
548
- auto pyState = reinterpret_cast<PyObject*>(state);
549
- auto f = PyObject_CallMethod(pInstance_, "_set_fmu_state", "(O)", pyState);
550
- if (f == nullptr) {
551
- handle_py_exception("[_set_fmu_state] PyObject_CallMethod", gilState);
552
- }
553
- clearLogBuffer();
554
- });
555
- }
556
-
557
- void PySlaveInstance::FreeFMUstate(fmi3FMUState& state)
558
- {
559
- py_safe_run([this, &state](PyGILState_STATE gilState) {
560
- auto f = reinterpret_cast<PyObject*>(state);
561
- Py_XDECREF(f);
562
- });
563
- }
564
-
565
- size_t PySlaveInstance::SerializedFMUstateSize(const fmi3FMUState& state)
566
- {
567
- size_t size;
568
- py_safe_run([this, &state, &size](PyGILState_STATE gilState) {
569
- auto pyState = reinterpret_cast<PyObject*>(state);
570
- PyObject* pyStateBytes = PyObject_CallMethod(pClass_, "_fmu_state_to_bytes", "(O)", pyState);
571
- if (pyStateBytes == nullptr) {
572
- handle_py_exception("[SerializedFMUstateSize] PyObject_CallMethod", gilState);
573
- }
574
- size = PyBytes_Size(pyStateBytes);
575
- Py_DECREF(pyStateBytes);
576
- clearLogBuffer();
577
- });
578
- return size;
579
- }
580
-
581
- void PySlaveInstance::SerializeFMUstate(const fmi3FMUState& state, fmi3Byte* bytes, size_t size)
582
- {
583
- py_safe_run([this, &state, &bytes, size](PyGILState_STATE gilState) {
584
- auto pyState = reinterpret_cast<PyObject*>(state);
585
- PyObject* pyStateBytes = PyObject_CallMethod(pClass_, "_fmu_state_to_bytes", "(O)", pyState);
586
- if (pyStateBytes == nullptr) {
587
- handle_py_exception("[SerializeFMUstate] PyObject_CallMethod", gilState);
588
- }
589
- char* c = PyBytes_AsString(pyStateBytes);
590
- if (c == nullptr) {
591
- handle_py_exception("[SerializeFMUstate] PyBytes_AsString", gilState);
592
- }
593
- for (int i = 0; i < size; i++) {
594
- bytes[i] = c[i];
595
- }
596
- Py_DECREF(pyStateBytes);
597
- clearLogBuffer();
598
- });
599
- }
600
-
601
- void PySlaveInstance::DeSerializeFMUstate(const fmi3Byte bytes[], size_t size, fmi3FMUState& state)
602
- {
603
- py_safe_run([this, &bytes, size, &state](PyGILState_STATE gilState) {
604
- char const * castedBytes = reinterpret_cast<char const*>(bytes);
605
- PyObject* pyStateBytes = PyBytes_FromStringAndSize(castedBytes, size);
606
- if (pyStateBytes == nullptr) {
607
- handle_py_exception("[DeSerializeFMUstate] PyBytes_FromStringAndSize", gilState);
608
- }
609
- PyObject* pyState = PyObject_CallMethod(pClass_, "_fmu_state_from_bytes", "(O)", pyStateBytes);
610
- if (pyState == nullptr) {
611
- handle_py_exception("[DeSerializeFMUstate] PyObject_CallMethod", gilState);
612
- }
613
- state = reinterpret_cast<fmi3FMUState*>(pyState);
614
- Py_DECREF(pyStateBytes);
615
- clearLogBuffer();
616
- });
617
- }
618
-
619
- void PySlaveInstance::handle_py_exception(const std::string& what, PyGILState_STATE gilState) const
620
- {
621
- auto err = PyErr_Occurred();
622
- if (err != nullptr) {
623
- cleanPyObject();
624
-
625
- PyObject *pExcType, *pExcValue, *pExcTraceback;
626
- PyErr_Fetch(&pExcType, &pExcValue, &pExcTraceback);
627
-
628
- std::ostringstream oss;
629
- oss << "Fatal py exception encountered: ";
630
- oss << what << "\n";
631
- if (pExcValue != nullptr) {
632
- PyObject* pRepr = PyObject_Repr(pExcValue);
633
- PyObject* pyStr = PyUnicode_AsEncodedString(pRepr, "utf-8", nullptr);
634
- oss << PyBytes_AsString(pyStr);
635
- Py_DECREF(pyStr);
636
- Py_DECREF(pRepr);
637
- } else {
638
- oss << "unknown error";
639
- }
640
-
641
- PyErr_Clear();
642
-
643
- Py_XDECREF(pExcType);
644
- Py_XDECREF(pExcValue);
645
- Py_XDECREF(pExcTraceback);
646
-
647
- PyGILState_Release(gilState);
648
-
649
- auto msg = oss.str();
650
- throw cppfmu::FatalError(msg.c_str());
651
- }
652
- }
653
-
654
- PySlaveInstance::~PySlaveInstance()
655
- {
656
- py_safe_run([this](PyGILState_STATE gilState) {
657
- cleanPyObject();
658
- });
659
- }
660
-
661
- } // namespace pythonfmu
662
-
663
- namespace {
664
- std::mutex pyStateMutex{};
665
- std::shared_ptr<pythonfmu::PyState> pyState{};
666
- }
667
-
668
- std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
669
- cppfmu::FMIString instanceName,
670
- cppfmu::FMIString,
671
- cppfmu::FMIString fmuResourceLocation,
672
- cppfmu::FMIString,
673
- cppfmu::FMIFloat64,
674
- cppfmu::FMIBoolean visible,
675
- cppfmu::FMIBoolean,
676
- const cppfmu::Logger& logger)
677
- {
678
-
679
- // Convert URI %20 to space
680
- auto resources = std::regex_replace(std::string(fmuResourceLocation), std::regex("%20"), " ");
681
- auto find = resources.find("file://");
682
-
683
- if (find != std::string::npos) {
684
- #ifdef _MSC_VER
685
- resources.replace(find, 8, "");
686
- #else
687
- resources.replace(find, 7, "");
688
- #endif
689
- }
690
-
691
- {
692
- auto const ensurePyStateAlive = [&]() {
693
- auto const lock = std::lock_guard{pyStateMutex};
694
- if (nullptr == pyState) pyState = std::make_shared<pythonfmu::PyState>();
695
- };
696
-
697
- ensurePyStateAlive();
698
- return std::make_unique<pythonfmu::PySlaveInstance>(
699
- instanceName, resources, logger, visible, pyState);
700
- }
701
- }
702
-
703
- extern "C" {
704
-
705
- // The PyState instance owns it's own thread for constructing and destroying the Py* from the same thread.
706
- // Creation of an std::thread increments ref counter of a shared library. So, when the client unloads the library
707
- // the library won't be freed, as std::thread is alive, and the std::thread itself waits for de-initialization request.
708
- // Thus, use DllMain on Windows and __attribute__((destructor)) on Linux for signaling to the PyState about de-initialization.
709
- void finalizePythonInterpreter()
710
- {
711
- pyState = nullptr;
712
- }
713
- }
714
-
715
- namespace
716
- {
717
- #ifdef _WIN32
718
- #include <windows.h>
719
-
720
- BOOL APIENTRY DllMain(HMODULE hModule,
721
- DWORD ul_reason_for_call,
722
- LPVOID lpReserved)
723
- {
724
- switch (ul_reason_for_call) {
725
- case DLL_PROCESS_ATTACH:
726
- break;
727
- case DLL_THREAD_ATTACH:
728
- case DLL_THREAD_DETACH:
729
- break;
730
- case DLL_PROCESS_DETACH:
731
- finalizePythonInterpreter();
732
- break;
733
- }
734
- return TRUE;
735
- }
736
- #elif defined(__linux__) || defined(__APPLE__)
737
- __attribute__((destructor)) void onLibraryUnload()
738
- {
739
- finalizePythonInterpreter();
740
- }
741
- #else
742
- #error port the code
743
- #endif
744
- }
1
+
2
+ #include "pythonfmu/IPyState.hpp"
3
+ #include "pythonfmu/PySlaveInstance.hpp"
4
+
5
+ #include "pythonfmu/PyState.hpp"
6
+
7
+ #include "cppfmu/cppfmu_cs.hpp"
8
+
9
+ #include <fstream>
10
+ #include <functional>
11
+ #include <mutex>
12
+ #include <regex>
13
+ #include <sstream>
14
+ #include <utility>
15
+
16
+ namespace pythonfmu
17
+ {
18
+
19
+ inline std::string getline(const std::string& fileName)
20
+ {
21
+ std::string line;
22
+ std::ifstream infile(fileName);
23
+ std::getline(infile, line);
24
+ return line;
25
+ }
26
+
27
+ inline std::string findClassName(const std::string& fileName)
28
+ {
29
+ std::string line;
30
+ std::ifstream infile(fileName);
31
+ std::string regexStr(R"(^class (\w+)\(\s*Fmi3Slave\s*\)\s*:)");
32
+ while (getline(infile, line)) {
33
+ std::smatch m;
34
+ std::regex re(regexStr);
35
+ if (std::regex_search(line, m, re)) {
36
+ return m[1];
37
+ }
38
+ }
39
+ return "";
40
+ }
41
+
42
+ inline void py_safe_run(const std::function<void(PyGILState_STATE gilState)>& f)
43
+ {
44
+ PyGILState_STATE gil_state = PyGILState_Ensure();
45
+ f(gil_state);
46
+ PyGILState_Release(gil_state);
47
+ }
48
+
49
+ PySlaveInstance::PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, const bool visible, std::shared_ptr<IPyState> pyState)
50
+ : pyState_{ std::move(pyState) }
51
+ , instanceName_(std::move(instanceName))
52
+ , resources_(std::move(resources))
53
+ , logger_(logger)
54
+ , visible_(visible)
55
+ {
56
+ py_safe_run([this](PyGILState_STATE gilState) {
57
+ // Append resources path to python sys path
58
+ PyObject* sys_module = PyImport_ImportModule("sys");
59
+ if (sys_module == nullptr) {
60
+ handle_py_exception("[ctor] PyImport_ImportModule", gilState);
61
+ }
62
+ PyObject* sys_path = PyObject_GetAttrString(sys_module, "path");
63
+ Py_DECREF(sys_module);
64
+ if (sys_path == nullptr) {
65
+ handle_py_exception("[ctor] PyObject_GetAttrString", gilState);
66
+ }
67
+ int success = PyList_Insert(sys_path, 0, PyUnicode_FromString(resources_.c_str()));
68
+ Py_DECREF(sys_path);
69
+ if (success != 0) {
70
+ handle_py_exception("[ctor] PyList_Insert", gilState);
71
+ }
72
+
73
+ std::string moduleName = getline(resources_ + "/slavemodule.txt");
74
+ PyObject* pModule = PyImport_ImportModule(moduleName.c_str());
75
+ if (pModule == nullptr) {
76
+ PyErr_Print();
77
+ handle_py_exception("[ctor] PyImport_ImportModule", gilState);
78
+ }
79
+
80
+ std::string className = findClassName(resources_ + "/" + moduleName + ".py");
81
+ if (className.empty()) {
82
+ cleanPyObject();
83
+ throw cppfmu::FatalError("Unable to find class extending Fmi3Slave!");
84
+ }
85
+
86
+ PyObject* pyClassName = Py_BuildValue("s", className.c_str());
87
+ if (pyClassName == nullptr) {
88
+ handle_py_exception("[ctor] Py_BuildValue", gilState);
89
+ }
90
+
91
+ pClass_ = PyObject_GetAttr(pModule, pyClassName);
92
+ Py_DECREF(pyClassName);
93
+ Py_DECREF(pModule);
94
+ if (pClass_ == nullptr) {
95
+ handle_py_exception("[ctor] PyObject_GetAttr", gilState);
96
+ }
97
+
98
+ initialize(gilState);
99
+ });
100
+ }
101
+
102
+ void PySlaveInstance::clearLogBuffer() const
103
+ {
104
+ clearLogStrBuffer();
105
+
106
+ PyObject* debugField = Py_BuildValue("s", "debug");
107
+ PyObject* msgField = Py_BuildValue("s", "msg");
108
+ PyObject* categoryField = Py_BuildValue("s", "category");
109
+ PyObject* statusField = Py_BuildValue("s", "status");
110
+
111
+ if ( pMessages_ != NULL && PyList_Check(pMessages_) ) {
112
+ auto size = PyList_Size(pMessages_);
113
+ for (auto i = 0; i < size; i++) {
114
+ PyObject* msg = PyList_GetItem(pMessages_, i);
115
+
116
+ auto debugAttr = PyObject_GetAttr(msg, debugField);
117
+ auto msgAttr = PyObject_GetAttr(msg, msgField);
118
+ auto categoryAttr = PyObject_GetAttr(msg, categoryField);
119
+ auto statusAttr = PyObject_GetAttr(msg, statusField);
120
+
121
+ auto statusValue = static_cast<cppfmu::FMIStatus>(PyLong_AsLong(statusAttr));
122
+
123
+ PyObject* msgValue = PyUnicode_AsEncodedString(msgAttr, "utf-8", nullptr);
124
+ char* msgStr = PyBytes_AsString(msgValue);
125
+ logStrBuffer.emplace_back(msgValue);
126
+
127
+ const char* categoryStr = "";
128
+ if (categoryAttr != Py_None) {
129
+ PyObject* categoryValue = PyUnicode_AsEncodedString(categoryAttr, "utf-8", nullptr);
130
+ categoryStr = PyBytes_AsString(categoryValue);
131
+ logStrBuffer.emplace_back(categoryValue);
132
+ }
133
+
134
+ if (PyObject_IsTrue(debugAttr)) {
135
+ const_cast<cppfmu::Logger&>(logger_).DebugLog(statusValue, categoryStr, msgStr);
136
+ } else {
137
+ const_cast<cppfmu::Logger&>(logger_).Log(statusValue, categoryStr, msgStr);
138
+ }
139
+ }
140
+ PyList_SetSlice(pMessages_, 0, size, nullptr);
141
+ }
142
+ Py_DECREF(debugField);
143
+ Py_DECREF(msgField);
144
+ Py_DECREF(categoryField);
145
+ Py_DECREF(statusField);
146
+ }
147
+
148
+ void PySlaveInstance::initialize(PyGILState_STATE gilState)
149
+ {
150
+ Py_XDECREF(pInstance_);
151
+ Py_XDECREF(pMessages_);
152
+
153
+ PyObject* args = PyTuple_New(0);
154
+ PyObject* kwargs = Py_BuildValue("{ss,ss,sn,si}",
155
+ "instance_name", instanceName_.c_str(),
156
+ "resources", resources_.c_str(),
157
+ "logger", &logger_,
158
+ "visible", visible_);
159
+ pInstance_ = PyObject_Call(pClass_, args, kwargs);
160
+ Py_DECREF(args);
161
+ Py_DECREF(kwargs);
162
+ if (pInstance_ == nullptr) {
163
+ handle_py_exception("[initialize] PyObject_Call", gilState);
164
+ }
165
+ pMessages_ = PyObject_CallMethod(pInstance_, "_get_log_queue", nullptr);
166
+ }
167
+
168
+ void PySlaveInstance::SetupExperiment(cppfmu::FMIBoolean, cppfmu::FMIFloat64, cppfmu::FMIFloat64 startTime, cppfmu::FMIBoolean, cppfmu::FMIFloat64)
169
+ {
170
+ py_safe_run([this, startTime](PyGILState_STATE gilState) {
171
+ auto f = PyObject_CallMethod(pInstance_, "setup_experiment", "(d)", startTime);
172
+ if (f == nullptr) {
173
+ handle_py_exception("[setupExperiment] PyObject_CallMethod", gilState);
174
+ }
175
+ Py_DECREF(f);
176
+ clearLogBuffer();
177
+ });
178
+ }
179
+
180
+ void PySlaveInstance::EnterInitializationMode()
181
+ {
182
+ py_safe_run([this](PyGILState_STATE gilState) {
183
+ auto f = PyObject_CallMethod(pInstance_, "enter_initialization_mode", nullptr);
184
+ if (f == nullptr) {
185
+ handle_py_exception("[enterInitializationMode] PyObject_CallMethod", gilState);
186
+ }
187
+ Py_DECREF(f);
188
+ clearLogBuffer();
189
+ });
190
+ }
191
+
192
+ void PySlaveInstance::ExitInitializationMode()
193
+ {
194
+ py_safe_run([this](PyGILState_STATE gilState) {
195
+ auto f = PyObject_CallMethod(pInstance_, "exit_initialization_mode", nullptr);
196
+ if (f == nullptr) {
197
+ handle_py_exception("[exitInitializationMode] PyObject_CallMethod", gilState);
198
+ }
199
+ Py_DECREF(f);
200
+ clearLogBuffer();
201
+ });
202
+ }
203
+
204
+ cppfmu::FMIStatus PySlaveInstance::DoStep(cppfmu::FMIFloat64 currentTime,
205
+ cppfmu::FMIFloat64 stepSize,
206
+ cppfmu::FMIBoolean noSetFmuStatePriorToCurrentPoint,
207
+ cppfmu::FMIBoolean* eventHandlingNeeded,
208
+ cppfmu::FMIBoolean* terminateSimulation,
209
+ cppfmu::FMIBoolean* earlyReturn,
210
+ cppfmu::FMIFloat64& endOfStep)
211
+ {
212
+ cppfmu::FMIStatus fmuStatus = cppfmu::FMIOK;
213
+ py_safe_run([this, &fmuStatus, currentTime, stepSize, terminateSimulation](PyGILState_STATE gilState) {
214
+ auto f = PyObject_CallMethod(pInstance_, "do_step", "(dd)", currentTime, stepSize);
215
+ if (f == nullptr) {
216
+ handle_py_exception("[doStep] PyObject_CallMethod", gilState);
217
+ }
218
+
219
+ if (PyObject_HasAttrString(f, "status")) {
220
+ PyObject* pyStatus = PyObject_GetAttrString(f, "status");
221
+ if (pyStatus) {
222
+ fmuStatus = static_cast<cppfmu::FMIStatus>(PyLong_AsLong(pyStatus));
223
+ Py_DECREF(pyStatus);
224
+ }
225
+ } else {
226
+ bool status = static_cast<bool>(PyObject_IsTrue(f));
227
+ if (!status) {
228
+ fmuStatus = cppfmu::FMIDiscard;
229
+ }
230
+ }
231
+
232
+ if (PyObject_HasAttrString(f, "terminateSimulation")) {
233
+ PyObject* pyTerminateSimulation = PyObject_GetAttrString(f, "terminateSimulation");
234
+ if (pyTerminateSimulation) {
235
+ *terminateSimulation = static_cast<bool>(PyObject_IsTrue(pyTerminateSimulation));
236
+ Py_DECREF(pyTerminateSimulation);
237
+ }
238
+ }
239
+ Py_DECREF(f);
240
+ clearLogBuffer();
241
+ });
242
+
243
+ return fmuStatus;
244
+ }
245
+
246
+ void PySlaveInstance::Reset()
247
+ {
248
+ py_safe_run([this](PyGILState_STATE gilState) {
249
+ initialize(gilState);
250
+ });
251
+ }
252
+
253
+ void PySlaveInstance::Terminate()
254
+ {
255
+ py_safe_run([this](PyGILState_STATE gilState) {
256
+ auto f = PyObject_CallMethod(pInstance_, "terminate", nullptr);
257
+ if (f == nullptr) {
258
+ handle_py_exception("[terminate] PyObject_CallMethod", gilState);
259
+ }
260
+ Py_DECREF(f);
261
+ clearLogBuffer();
262
+ });
263
+ }
264
+
265
+ void PySlaveInstance::SetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIFloat64* values, std::size_t nValues)
266
+ {
267
+ py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
268
+ PyObject* vrs = PyList_New(nvr);
269
+ PyObject* refs = PyList_New(nValues);
270
+ for (int i = 0; i < nvr; i++) {
271
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
272
+ }
273
+ for (int i = 0; i < nValues; i++) {
274
+ PyList_SetItem(refs, i, Py_BuildValue("d", values[i]));
275
+ }
276
+
277
+ auto f = PyObject_CallMethod(pInstance_, "set_float64", "(OO)", vrs, refs);
278
+ Py_DECREF(vrs);
279
+ Py_DECREF(refs);
280
+ if (f == nullptr) {
281
+ handle_py_exception("[setFloat64] PyObject_CallMethod", gilState);
282
+ }
283
+ Py_DECREF(f);
284
+ clearLogBuffer();
285
+ });
286
+ }
287
+
288
+ void PySlaveInstance::SetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt32* values)
289
+ {
290
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
291
+ PyObject* vrs = PyList_New(nvr);
292
+ PyObject* refs = PyList_New(nvr);
293
+ for (int i = 0; i < nvr; i++) {
294
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
295
+ PyList_SetItem(refs, i, Py_BuildValue("i", values[i]));
296
+ }
297
+
298
+ auto f = PyObject_CallMethod(pInstance_, "set_int32", "(OO)", vrs, refs);
299
+ Py_DECREF(vrs);
300
+ Py_DECREF(refs);
301
+ if (f == nullptr) {
302
+ handle_py_exception("[setInt32] PyObject_CallMethod", gilState);
303
+ }
304
+ Py_DECREF(f);
305
+ clearLogBuffer();
306
+ });
307
+ }
308
+
309
+ void PySlaveInstance::SetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIInt64* values)
310
+ {
311
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
312
+ PyObject* vrs = PyList_New(nvr);
313
+ PyObject* refs = PyList_New(nvr);
314
+ for (int i = 0; i < nvr; i++) {
315
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
316
+ PyList_SetItem(refs, i, Py_BuildValue("L", values[i]));
317
+ }
318
+
319
+ auto f = PyObject_CallMethod(pInstance_, "set_int64", "(OO)", vrs, refs);
320
+ Py_DECREF(vrs);
321
+ Py_DECREF(refs);
322
+ if (f == nullptr) {
323
+ handle_py_exception("[setInt64] PyObject_CallMethod", gilState);
324
+ }
325
+ Py_DECREF(f);
326
+ clearLogBuffer();
327
+ });
328
+ }
329
+
330
+ void PySlaveInstance::SetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIUInt64* values)
331
+ {
332
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
333
+ PyObject* vrs = PyList_New(nvr);
334
+ PyObject* refs = PyList_New(nvr);
335
+ for (int i = 0; i < nvr; i++) {
336
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
337
+ PyList_SetItem(refs, i, Py_BuildValue("K", values[i]));
338
+ }
339
+
340
+ auto f = PyObject_CallMethod(pInstance_, "set_uint64", "(OO)", vrs, refs);
341
+ Py_DECREF(vrs);
342
+ Py_DECREF(refs);
343
+ if (f == nullptr) {
344
+ handle_py_exception("[setUInt64] PyObject_CallMethod", gilState);
345
+ }
346
+ Py_DECREF(f);
347
+ clearLogBuffer();
348
+ });
349
+ }
350
+
351
+ void PySlaveInstance::SetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, const cppfmu::FMIBoolean* values)
352
+ {
353
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
354
+ PyObject* vrs = PyList_New(nvr);
355
+ PyObject* refs = PyList_New(nvr);
356
+ for (int i = 0; i < nvr; i++) {
357
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
358
+ PyList_SetItem(refs, i, PyBool_FromLong(values[i]));
359
+ }
360
+
361
+ auto f = PyObject_CallMethod(pInstance_, "set_boolean", "(OO)", vrs, refs);
362
+ Py_DECREF(vrs);
363
+ Py_DECREF(refs);
364
+ if (f == nullptr) {
365
+ handle_py_exception("[setBoolean] PyObject_CallMethod", gilState);
366
+ }
367
+ Py_DECREF(f);
368
+ clearLogBuffer();
369
+ });
370
+ }
371
+
372
+ void PySlaveInstance::SetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString const* values)
373
+ {
374
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
375
+ PyObject* vrs = PyList_New(nvr);
376
+ PyObject* refs = PyList_New(nvr);
377
+ for (int i = 0; i < nvr; i++) {
378
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
379
+ PyList_SetItem(refs, i, Py_BuildValue("s", values[i]));
380
+ }
381
+
382
+ auto f = PyObject_CallMethod(pInstance_, "set_string", "(OO)", vrs, refs);
383
+ Py_DECREF(vrs);
384
+ Py_DECREF(refs);
385
+ if (f == nullptr) {
386
+ handle_py_exception("[setString] PyObject_CallMethod", gilState);
387
+ }
388
+ Py_DECREF(f);
389
+ clearLogBuffer();
390
+ });
391
+ }
392
+
393
+ void PySlaveInstance::GetFloat64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIFloat64* values, std::size_t nValues) const
394
+ {
395
+ py_safe_run([this, &vr, nvr, &values, nValues](PyGILState_STATE gilState) {
396
+ PyObject* vrs = PyList_New(nvr);
397
+ for (int i = 0; i < nvr; i++) {
398
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
399
+ }
400
+
401
+ auto refs = PyObject_CallMethod(pInstance_, "get_float64", "O", vrs);
402
+ Py_DECREF(vrs);
403
+ if (refs == nullptr) {
404
+ handle_py_exception("[getFloat64] PyObject_CallMethod", gilState);
405
+ }
406
+
407
+ for (int i = 0; i < nValues; i++) {
408
+ PyObject* value = PyList_GetItem(refs, i);
409
+ values[i] = PyFloat_AsDouble(value);
410
+ }
411
+ Py_DECREF(refs);
412
+ clearLogBuffer();
413
+ });
414
+ }
415
+
416
+ void PySlaveInstance::GetInt32(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt32* values) const
417
+ {
418
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
419
+ PyObject* vrs = PyList_New(nvr);
420
+ for (int i = 0; i < nvr; i++) {
421
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
422
+ }
423
+ auto refs = PyObject_CallMethod(pInstance_, "get_int32", "O", vrs);
424
+ Py_DECREF(vrs);
425
+ if (refs == nullptr) {
426
+ handle_py_exception("[getInt32] PyObject_CallMethod", gilState);
427
+ }
428
+
429
+ for (int i = 0; i < nvr; i++) {
430
+ PyObject* value = PyList_GetItem(refs, i);
431
+ values[i] = static_cast<cppfmu::FMIInt32>(PyLong_AsLong(value));
432
+ }
433
+ Py_DECREF(refs);
434
+ clearLogBuffer();
435
+ });
436
+ }
437
+
438
+ void PySlaveInstance::GetInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIInt64* values) const
439
+ {
440
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
441
+ PyObject* vrs = PyList_New(nvr);
442
+ for (int i = 0; i < nvr; i++) {
443
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
444
+ }
445
+ auto refs = PyObject_CallMethod(pInstance_, "get_int64", "O", vrs);
446
+ Py_DECREF(vrs);
447
+ if (refs == nullptr) {
448
+ handle_py_exception("[getInt64] PyObject_CallMethod", gilState);
449
+ }
450
+
451
+ for (int i = 0; i < nvr; i++) {
452
+ PyObject* value = PyList_GetItem(refs, i);
453
+ values[i] = static_cast<cppfmu::FMIInt64>(PyLong_AsLongLong(value));
454
+ }
455
+ Py_DECREF(refs);
456
+ clearLogBuffer();
457
+ });
458
+ }
459
+
460
+ void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIUInt64* values) const
461
+ {
462
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
463
+ PyObject* vrs = PyList_New(nvr);
464
+ for (int i = 0; i < nvr; i++) {
465
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
466
+ }
467
+ auto refs = PyObject_CallMethod(pInstance_, "get_uint64", "O", vrs);
468
+ Py_DECREF(vrs);
469
+ if (refs == nullptr) {
470
+ handle_py_exception("[getUInt64] PyObject_CallMethod", gilState);
471
+ }
472
+
473
+ for (int i = 0; i < nvr; i++) {
474
+ PyObject* item = PyList_GetItem(refs, i);
475
+ PyObject* value = PyObject_GetAttrString(item, "value");
476
+ if (value && PyLong_Check(value))
477
+ {
478
+ values[i] = static_cast<cppfmu::FMIUInt64>(PyLong_AsUnsignedLongLong(value));
479
+ }
480
+ }
481
+
482
+ Py_DECREF(refs);
483
+ clearLogBuffer();
484
+ });
485
+ }
486
+
487
+ void PySlaveInstance::GetBoolean(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIBoolean* values) const
488
+ {
489
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
490
+ PyObject* vrs = PyList_New(nvr);
491
+ for (int i = 0; i < nvr; i++) {
492
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
493
+ }
494
+ auto refs = PyObject_CallMethod(pInstance_, "get_boolean", "O", vrs);
495
+ Py_DECREF(vrs);
496
+ if (refs == nullptr) {
497
+ handle_py_exception("[getBoolean] PyObject_CallMethod", gilState);
498
+ }
499
+
500
+ for (int i = 0; i < nvr; i++) {
501
+ PyObject* value = PyList_GetItem(refs, i);
502
+ values[i] = PyObject_IsTrue(value);
503
+ }
504
+ Py_DECREF(refs);
505
+ clearLogBuffer();
506
+ });
507
+ }
508
+
509
+ void PySlaveInstance::GetString(const cppfmu::FMIValueReference* vr, std::size_t nvr, cppfmu::FMIString* values) const
510
+ {
511
+ py_safe_run([this, &vr, nvr, &values](PyGILState_STATE gilState) {
512
+ clearStrBuffer();
513
+ PyObject* vrs = PyList_New(nvr);
514
+ for (int i = 0; i < nvr; i++) {
515
+ PyList_SetItem(vrs, i, Py_BuildValue("i", vr[i]));
516
+ }
517
+ auto refs = PyObject_CallMethod(pInstance_, "get_string", "O", vrs);
518
+ Py_DECREF(vrs);
519
+ if (refs == nullptr) {
520
+ handle_py_exception("[getString] PyObject_CallMethod", gilState);
521
+ }
522
+
523
+ for (int i = 0; i < nvr; i++) {
524
+ PyObject* value = PyUnicode_AsEncodedString(PyList_GetItem(refs, i), "utf-8", nullptr);
525
+ values[i] = PyBytes_AsString(value);
526
+ strBuffer.emplace_back(value);
527
+ }
528
+ Py_DECREF(refs);
529
+ clearLogBuffer();
530
+ });
531
+ }
532
+
533
+ void PySlaveInstance::GetFMUstate(fmi3FMUState& state)
534
+ {
535
+ py_safe_run([this, &state](PyGILState_STATE gilState) {
536
+ auto f = PyObject_CallMethod(pInstance_, "_get_fmu_state", nullptr);
537
+ if (f == nullptr) {
538
+ handle_py_exception("[_get_fmu_state] PyObject_CallMethod", gilState);
539
+ }
540
+ state = reinterpret_cast<fmi3FMUState*>(f);
541
+ clearLogBuffer();
542
+ });
543
+ }
544
+
545
+ void PySlaveInstance::SetFMUstate(const fmi3FMUState& state)
546
+ {
547
+ py_safe_run([this, &state](PyGILState_STATE gilState) {
548
+ auto pyState = reinterpret_cast<PyObject*>(state);
549
+ auto f = PyObject_CallMethod(pInstance_, "_set_fmu_state", "(O)", pyState);
550
+ if (f == nullptr) {
551
+ handle_py_exception("[_set_fmu_state] PyObject_CallMethod", gilState);
552
+ }
553
+ clearLogBuffer();
554
+ });
555
+ }
556
+
557
+ void PySlaveInstance::FreeFMUstate(fmi3FMUState& state)
558
+ {
559
+ py_safe_run([this, &state](PyGILState_STATE gilState) {
560
+ auto f = reinterpret_cast<PyObject*>(state);
561
+ Py_XDECREF(f);
562
+ });
563
+ }
564
+
565
+ size_t PySlaveInstance::SerializedFMUstateSize(const fmi3FMUState& state)
566
+ {
567
+ size_t size;
568
+ py_safe_run([this, &state, &size](PyGILState_STATE gilState) {
569
+ auto pyState = reinterpret_cast<PyObject*>(state);
570
+ PyObject* pyStateBytes = PyObject_CallMethod(pClass_, "_fmu_state_to_bytes", "(O)", pyState);
571
+ if (pyStateBytes == nullptr) {
572
+ handle_py_exception("[SerializedFMUstateSize] PyObject_CallMethod", gilState);
573
+ }
574
+ size = PyBytes_Size(pyStateBytes);
575
+ Py_DECREF(pyStateBytes);
576
+ clearLogBuffer();
577
+ });
578
+ return size;
579
+ }
580
+
581
+ void PySlaveInstance::SerializeFMUstate(const fmi3FMUState& state, fmi3Byte* bytes, size_t size)
582
+ {
583
+ py_safe_run([this, &state, &bytes, size](PyGILState_STATE gilState) {
584
+ auto pyState = reinterpret_cast<PyObject*>(state);
585
+ PyObject* pyStateBytes = PyObject_CallMethod(pClass_, "_fmu_state_to_bytes", "(O)", pyState);
586
+ if (pyStateBytes == nullptr) {
587
+ handle_py_exception("[SerializeFMUstate] PyObject_CallMethod", gilState);
588
+ }
589
+ char* c = PyBytes_AsString(pyStateBytes);
590
+ if (c == nullptr) {
591
+ handle_py_exception("[SerializeFMUstate] PyBytes_AsString", gilState);
592
+ }
593
+ for (int i = 0; i < size; i++) {
594
+ bytes[i] = c[i];
595
+ }
596
+ Py_DECREF(pyStateBytes);
597
+ clearLogBuffer();
598
+ });
599
+ }
600
+
601
+ void PySlaveInstance::DeSerializeFMUstate(const fmi3Byte bytes[], size_t size, fmi3FMUState& state)
602
+ {
603
+ py_safe_run([this, &bytes, size, &state](PyGILState_STATE gilState) {
604
+ char const * castedBytes = reinterpret_cast<char const*>(bytes);
605
+ PyObject* pyStateBytes = PyBytes_FromStringAndSize(castedBytes, size);
606
+ if (pyStateBytes == nullptr) {
607
+ handle_py_exception("[DeSerializeFMUstate] PyBytes_FromStringAndSize", gilState);
608
+ }
609
+ PyObject* pyState = PyObject_CallMethod(pClass_, "_fmu_state_from_bytes", "(O)", pyStateBytes);
610
+ if (pyState == nullptr) {
611
+ handle_py_exception("[DeSerializeFMUstate] PyObject_CallMethod", gilState);
612
+ }
613
+ state = reinterpret_cast<fmi3FMUState*>(pyState);
614
+ Py_DECREF(pyStateBytes);
615
+ clearLogBuffer();
616
+ });
617
+ }
618
+
619
+ void PySlaveInstance::handle_py_exception(const std::string& what, PyGILState_STATE gilState) const
620
+ {
621
+ auto err = PyErr_Occurred();
622
+ if (err != nullptr) {
623
+ cleanPyObject();
624
+
625
+ PyObject *pExcType, *pExcValue, *pExcTraceback;
626
+ PyErr_Fetch(&pExcType, &pExcValue, &pExcTraceback);
627
+
628
+ std::ostringstream oss;
629
+ oss << "Fatal py exception encountered: ";
630
+ oss << what << "\n";
631
+ if (pExcValue != nullptr) {
632
+ PyObject* pRepr = PyObject_Repr(pExcValue);
633
+ PyObject* pyStr = PyUnicode_AsEncodedString(pRepr, "utf-8", nullptr);
634
+ oss << PyBytes_AsString(pyStr);
635
+ Py_DECREF(pyStr);
636
+ Py_DECREF(pRepr);
637
+ } else {
638
+ oss << "unknown error";
639
+ }
640
+
641
+ PyErr_Clear();
642
+
643
+ Py_XDECREF(pExcType);
644
+ Py_XDECREF(pExcValue);
645
+ Py_XDECREF(pExcTraceback);
646
+
647
+ PyGILState_Release(gilState);
648
+
649
+ auto msg = oss.str();
650
+ throw cppfmu::FatalError(msg.c_str());
651
+ }
652
+ }
653
+
654
+ PySlaveInstance::~PySlaveInstance()
655
+ {
656
+ py_safe_run([this](PyGILState_STATE gilState) {
657
+ cleanPyObject();
658
+ });
659
+ }
660
+
661
+ } // namespace pythonfmu
662
+
663
+ namespace {
664
+ std::mutex pyStateMutex{};
665
+ std::shared_ptr<pythonfmu::PyState> pyState{};
666
+ }
667
+
668
+ std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
669
+ cppfmu::FMIString instanceName,
670
+ cppfmu::FMIString,
671
+ cppfmu::FMIString fmuResourceLocation,
672
+ cppfmu::FMIString,
673
+ cppfmu::FMIFloat64,
674
+ cppfmu::FMIBoolean visible,
675
+ cppfmu::FMIBoolean,
676
+ const cppfmu::Logger& logger)
677
+ {
678
+
679
+ // Convert URI %20 to space
680
+ auto resources = std::regex_replace(std::string(fmuResourceLocation), std::regex("%20"), " ");
681
+ auto find = resources.find("file://");
682
+
683
+ if (find != std::string::npos) {
684
+ #ifdef _MSC_VER
685
+ resources.replace(find, 8, "");
686
+ #else
687
+ resources.replace(find, 7, "");
688
+ #endif
689
+ }
690
+
691
+ {
692
+ auto const ensurePyStateAlive = [&]() {
693
+ auto const lock = std::lock_guard{pyStateMutex};
694
+ if (nullptr == pyState) pyState = std::make_shared<pythonfmu::PyState>();
695
+ };
696
+
697
+ ensurePyStateAlive();
698
+ return std::make_unique<pythonfmu::PySlaveInstance>(
699
+ instanceName, resources, logger, visible, pyState);
700
+ }
701
+ }
702
+
703
+ extern "C" {
704
+
705
+ // The PyState instance owns it's own thread for constructing and destroying the Py* from the same thread.
706
+ // Creation of an std::thread increments ref counter of a shared library. So, when the client unloads the library
707
+ // the library won't be freed, as std::thread is alive, and the std::thread itself waits for de-initialization request.
708
+ // Thus, use DllMain on Windows and __attribute__((destructor)) on Linux for signaling to the PyState about de-initialization.
709
+ void finalizePythonInterpreter()
710
+ {
711
+ pyState = nullptr;
712
+ }
713
+ }
714
+
715
+ namespace
716
+ {
717
+ #ifdef _WIN32
718
+ #include <windows.h>
719
+
720
+ BOOL APIENTRY DllMain(HMODULE hModule,
721
+ DWORD ul_reason_for_call,
722
+ LPVOID lpReserved)
723
+ {
724
+ switch (ul_reason_for_call) {
725
+ case DLL_PROCESS_ATTACH:
726
+ break;
727
+ case DLL_THREAD_ATTACH:
728
+ case DLL_THREAD_DETACH:
729
+ break;
730
+ case DLL_PROCESS_DETACH:
731
+ finalizePythonInterpreter();
732
+ break;
733
+ }
734
+ return TRUE;
735
+ }
736
+ #elif defined(__linux__) || defined(__APPLE__)
737
+ __attribute__((destructor)) void onLibraryUnload()
738
+ {
739
+ finalizePythonInterpreter();
740
+ }
741
+ #else
742
+ #error port the code
743
+ #endif
744
+ }