pythonfmu3 0.2.2.2__py3-none-any.whl → 0.2.3__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.
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_unix.sh +4 -4
  14. pythonfmu3/pythonfmu-export/build_win.sh +4 -4
  15. pythonfmu3/pythonfmu-export/src/CMakeLists.txt +45 -45
  16. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +161 -161
  17. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +192 -192
  18. pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +185 -185
  19. pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +937 -937
  20. pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +638 -638
  21. pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +343 -343
  22. pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +94 -94
  23. pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +17 -17
  24. pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +744 -744
  25. pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +111 -111
  26. pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +84 -84
  27. pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
  28. pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
  29. pythonfmu3/tests/conftest.py +5 -5
  30. pythonfmu3/tests/data/csvdemo.csv +7 -7
  31. pythonfmu3/tests/slaves/PythonSlaveWithException.py +18 -18
  32. pythonfmu3/tests/slaves/localmodule.py +8 -8
  33. pythonfmu3/tests/slaves/pythonslave.py +52 -52
  34. pythonfmu3/tests/slaves/pythonslave_read_file.py +23 -23
  35. pythonfmu3/tests/slaves/slavewithdep.py +21 -21
  36. pythonfmu3/tests/test_builder.py +195 -195
  37. pythonfmu3/tests/test_deploy.py +48 -48
  38. pythonfmu3/tests/test_fmi3slave.py +152 -152
  39. pythonfmu3/tests/test_integration.py +350 -350
  40. pythonfmu3/tests/test_logger.py +180 -180
  41. pythonfmu3/tests/test_multiple_fmus.py +141 -141
  42. pythonfmu3/tests/test_variables.py +334 -334
  43. pythonfmu3/tests/utils.py +13 -13
  44. pythonfmu3/unit.py +62 -62
  45. pythonfmu3/variable_types.py +101 -101
  46. pythonfmu3/variables.py +400 -400
  47. {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/METADATA +7 -9
  48. pythonfmu3-0.2.3.dist-info/RECORD +54 -0
  49. {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/WHEEL +1 -1
  50. {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.dist-info}/entry_points.txt +0 -1
  51. pythonfmu3-0.2.2.2.dist-info/LICENSE.txt → pythonfmu3-0.2.3.dist-info/licenses/LICENSE +23 -23
  52. pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj +0 -184
  53. pythonfmu3/pythonfmu-export/build/ALL_BUILD.vcxproj.filters +0 -8
  54. pythonfmu3/pythonfmu-export/build/CMakeCache.txt +0 -369
  55. pythonfmu3/pythonfmu-export/build/CMakeFiles/0ee4db0675778dfba98c7160896d5a45/generate.stamp.rule +0 -1
  56. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCCompiler.cmake +0 -72
  57. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeCXXCompiler.cmake +0 -83
  58. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_C.bin +0 -0
  59. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeDetermineCompilerABI_CXX.bin +0 -0
  60. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeRCCompiler.cmake +0 -6
  61. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CMakeSystem.cmake +0 -15
  62. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CMakeCCompilerId.c +0 -838
  63. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.exe +0 -0
  64. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/CompilerIdC.vcxproj +0 -71
  65. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  66. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.exe.recipe +0 -11
  67. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  68. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  69. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  70. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +0 -2
  71. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  72. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  73. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  74. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -826
  75. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  76. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/CompilerIdCXX.vcxproj +0 -71
  77. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  78. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +0 -11
  79. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  80. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  81. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  82. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +0 -2
  83. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  84. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  85. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  86. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.txt +0 -1
  87. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/VCTargetsPath.vcxproj +0 -31
  88. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.recipe +0 -11
  89. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.24.0/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +0 -2
  90. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCCompiler.cmake +0 -81
  91. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeCXXCompiler.cmake +0 -101
  92. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_C.bin +0 -0
  93. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  94. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeRCCompiler.cmake +0 -6
  95. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CMakeSystem.cmake +0 -15
  96. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CMakeCCompilerId.c +0 -904
  97. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.exe +0 -0
  98. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/CompilerIdC.vcxproj +0 -72
  99. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  100. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.exe.recipe +0 -11
  101. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  102. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  103. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  104. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +0 -2
  105. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  106. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  107. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  108. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -919
  109. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  110. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/CompilerIdCXX.vcxproj +0 -72
  111. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  112. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +0 -11
  113. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  114. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  115. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  116. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +0 -2
  117. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  118. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  119. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  120. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.txt +0 -1
  121. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/VCTargetsPath.vcxproj +0 -31
  122. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.recipe +0 -11
  123. pythonfmu3/pythonfmu-export/build/CMakeFiles/3.30.2/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +0 -2
  124. pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeConfigureLog.yaml +0 -568
  125. pythonfmu3/pythonfmu-export/build/CMakeFiles/CMakeOutput.log +0 -357
  126. pythonfmu3/pythonfmu-export/build/CMakeFiles/TargetDirectories.txt +0 -3
  127. pythonfmu3/pythonfmu-export/build/CMakeFiles/cmake.check_cache +0 -1
  128. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp +0 -1
  129. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.depend +0 -28
  130. pythonfmu3/pythonfmu-export/build/CMakeFiles/generate.stamp.list +0 -2
  131. pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj +0 -174
  132. pythonfmu3/pythonfmu-export/build/ZERO_CHECK.vcxproj.filters +0 -13
  133. pythonfmu3/pythonfmu-export/build/cmake_install.cmake +0 -50
  134. pythonfmu3/pythonfmu-export/build/install_manifest.txt +0 -0
  135. pythonfmu3/pythonfmu-export/build/pythonfmu-export.sln +0 -53
  136. pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp +0 -1
  137. pythonfmu3/pythonfmu-export/build/src/CMakeFiles/generate.stamp.depend +0 -2
  138. pythonfmu3/pythonfmu-export/build/src/Debug/pythonfmu-export.pdb +0 -0
  139. pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.exp +0 -0
  140. pythonfmu3/pythonfmu-export/build/src/Release/pythonfmu-export.lib +0 -0
  141. pythonfmu3/pythonfmu-export/build/src/cmake_install.cmake +0 -34
  142. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/PySlaveInstance.obj +0 -0
  143. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/cppfmu_cs.obj +0 -0
  144. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/exports.def +0 -1885
  145. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/fmi_functions.obj +0 -0
  146. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/objects.txt +0 -3
  147. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
  148. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
  149. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
  150. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.command.1.tlog +0 -10
  151. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.read.1.tlog +0 -1
  152. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/CustomBuild.write.1.tlog +0 -2
  153. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.command.1.tlog +0 -1
  154. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.1.tlog +0 -1
  155. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.read.3.tlog +0 -1
  156. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/link.write.1.tlog +0 -1
  157. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +0 -2
  158. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/pythonfmu-export.tlog/unsuccessfulbuild +0 -0
  159. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Debug/vc142.pdb +0 -0
  160. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/MinSizeRel/objects.txt +0 -3
  161. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/RelWithDebInfo/objects.txt +0 -3
  162. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/PySlaveInstance.obj +0 -0
  163. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/cppfmu_cs.obj +0 -0
  164. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/exports.def +0 -538
  165. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/fmi_functions.obj +0 -0
  166. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/objects.txt +0 -3
  167. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.dll.recipe +0 -14
  168. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.command.1.tlog +0 -0
  169. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.read.1.tlog +0 -0
  170. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CL.write.1.tlog +0 -0
  171. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.command.1.tlog +0 -10
  172. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.read.1.tlog +0 -1
  173. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/CustomBuild.write.1.tlog +0 -2
  174. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.command.1.tlog +0 -0
  175. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.delete.1.tlog +0 -0
  176. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.read.1.tlog +0 -0
  177. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/link.write.1.tlog +0 -0
  178. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.lastbuildstate +0 -2
  179. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.dir/Release/pythonfmu-export.tlog/pythonfmu-export.write.1u.tlog +0 -0
  180. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj +0 -414
  181. pythonfmu3/pythonfmu-export/build/src/pythonfmu-export.vcxproj.filters +0 -48
  182. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.recipe +0 -11
  183. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +0 -10
  184. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +0 -29
  185. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +0 -3
  186. pythonfmu3/pythonfmu-export/build/x64/Debug/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +0 -2
  187. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.recipe +0 -17
  188. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate +0 -2
  189. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog +0 -10
  190. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog +0 -27
  191. pythonfmu3/pythonfmu-export/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog +0 -2
  192. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +0 -11
  193. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +0 -10
  194. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +0 -29
  195. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +0 -3
  196. pythonfmu3/pythonfmu-export/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +0 -2
  197. pythonfmu3/resources/binaries/x86_64-windows1/pythonfmu-export.dll +0 -0
  198. pythonfmu3-0.2.2.2.dist-info/RECORD +0 -200
  199. {pythonfmu3-0.2.2.2.dist-info → pythonfmu3-0.2.3.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
+ }