pythonfmu3 0.1.10__tar.gz → 0.1.16__tar.gz
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.
- {pythonfmu3-0.1.10/pythonfmu3.egg-info → pythonfmu3-0.1.16}/PKG-INFO +11 -14
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/README.md +6 -11
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/__init__.py +2 -1
- pythonfmu3-0.1.16/pythonfmu3/_version.py +1 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/fmi3slave.py +23 -5
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/osutil.py +2 -2
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp +0 -3
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp +4 -3
- pythonfmu3-0.1.16/pythonfmu3/pythonfmu-export/src/pythonfmu/IPyState.hpp +18 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp +67 -8
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.hpp +3 -1
- pythonfmu3-0.1.16/pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +85 -0
- pythonfmu3-0.1.16/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3-0.1.16/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/slaves/PythonSlaveWithException.py +4 -1
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/slaves/pythonslave.py +3 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/slaves/pythonslave_read_file.py +4 -1
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/slaves/slavewithdep.py +4 -1
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_integration.py +41 -47
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_multiple_fmus.py +12 -10
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_variables.py +3 -1
- pythonfmu3-0.1.16/pythonfmu3/unit.py +62 -0
- pythonfmu3-0.1.10/pythonfmu3/type_definitions.py → pythonfmu3-0.1.16/pythonfmu3/variable_types.py +41 -25
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/variables.py +46 -5
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16/pythonfmu3.egg-info}/PKG-INFO +11 -14
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/SOURCES.txt +3 -1
- pythonfmu3-0.1.10/pythonfmu3/_version.py +0 -1
- pythonfmu3-0.1.10/pythonfmu3/pythonfmu-export/src/pythonfmu/PyState.hpp +0 -41
- pythonfmu3-0.1.10/pythonfmu3/resources/binaries/x86_64-linux/libpythonfmu-export.so +0 -0
- pythonfmu3-0.1.10/pythonfmu3/resources/binaries/x86_64-windows/pythonfmu-export.dll +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/LICENSE +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/MANIFEST.in +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/__main__.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/builder.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/default_experiment.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/deploy.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/enums.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/logmsg.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/.clang-format +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/CMakeLists.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/build_unix.sh +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/build_win.sh +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/CMakeLists.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.cpp +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/fmi/fmi3FunctionTypes.h +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/fmi/fmi3Functions.h +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/fmi/fmi3PlatformTypes.h +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/__init__.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/conftest.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/data/csvdemo.csv +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/data/hello.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/slaves/localmodule.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_builder.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_deploy.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_fmi3slave.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/test_logger.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/tests/utils.py +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/dependency_links.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/entry_points.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/not-zip-safe +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/requires.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3.egg-info/top_level.txt +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/setup.cfg +0 -0
- {pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: pythonfmu3
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: A lightweight framework that enables the packaging of Python3.x code as co-simulation FMUs.
|
|
5
5
|
Home-page: https://github.com/stephensmith25/PythonFMU3
|
|
6
6
|
Author: Stephen Smith
|
|
@@ -19,8 +19,10 @@ Classifier: Programming Language :: Python :: 3.7
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
|
-
Provides-Extra: tests
|
|
23
22
|
License-File: LICENSE
|
|
23
|
+
Provides-Extra: tests
|
|
24
|
+
Requires-Dist: pytest; extra == "tests"
|
|
25
|
+
Requires-Dist: fmpy; extra == "tests"
|
|
24
26
|
|
|
25
27
|
# PythonFMU3
|
|
26
28
|
|
|
@@ -38,13 +40,7 @@ This project is a fork of the original PythonFMU repository available at https:/
|
|
|
38
40
|
|
|
39
41
|
### Support:
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- Float64, Int32, UInt64, String, Boolean variables
|
|
44
|
-
- Step Mode
|
|
45
|
-
- Arrays, which requires the numpy package
|
|
46
|
-
|
|
47
|
-
Please take a look at the examples bouncingBall.py and linearTransform.py for a demonstration of these features.
|
|
43
|
+
Please take a look at the examples to see the supported features.
|
|
48
44
|
|
|
49
45
|
### Future
|
|
50
46
|
|
|
@@ -53,7 +49,6 @@ In no particular order, we plan to add support for:
|
|
|
53
49
|
- Support more variable types from FMI3
|
|
54
50
|
- Improve array support
|
|
55
51
|
- Add event mode
|
|
56
|
-
- Include FMI3 related testing
|
|
57
52
|
|
|
58
53
|
### How do I build an FMU from python code?
|
|
59
54
|
|
|
@@ -136,16 +131,18 @@ from pythonfmu3 import Fmi3Causality, Fmi3Slave, Boolean, Int32, Float64, String
|
|
|
136
131
|
|
|
137
132
|
class PythonSlave(Fmi3Slave):
|
|
138
133
|
|
|
139
|
-
author = "John Doe"
|
|
140
|
-
description = "A simple description"
|
|
141
|
-
|
|
142
134
|
def __init__(self, **kwargs):
|
|
143
135
|
super().__init__(**kwargs)
|
|
144
136
|
|
|
137
|
+
self.author = "John Doe"
|
|
138
|
+
self.description = "A simple description"
|
|
139
|
+
|
|
140
|
+
self.time = 0.0
|
|
145
141
|
self.intOut = 1
|
|
146
142
|
self.realOut = 3.0
|
|
147
143
|
self.booleanVariable = True
|
|
148
144
|
self.stringVariable = "Hello World!"
|
|
145
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent))
|
|
149
146
|
self.register_variable(Int32("intOut", causality=Fmi3Causality.output))
|
|
150
147
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
151
148
|
self.register_variable(Boolean("booleanVariable", causality=Fmi3Causality.local))
|
|
@@ -14,13 +14,7 @@ This project is a fork of the original PythonFMU repository available at https:/
|
|
|
14
14
|
|
|
15
15
|
### Support:
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- Float64, Int32, UInt64, String, Boolean variables
|
|
20
|
-
- Step Mode
|
|
21
|
-
- Arrays, which requires the numpy package
|
|
22
|
-
|
|
23
|
-
Please take a look at the examples bouncingBall.py and linearTransform.py for a demonstration of these features.
|
|
17
|
+
Please take a look at the examples to see the supported features.
|
|
24
18
|
|
|
25
19
|
### Future
|
|
26
20
|
|
|
@@ -29,7 +23,6 @@ In no particular order, we plan to add support for:
|
|
|
29
23
|
- Support more variable types from FMI3
|
|
30
24
|
- Improve array support
|
|
31
25
|
- Add event mode
|
|
32
|
-
- Include FMI3 related testing
|
|
33
26
|
|
|
34
27
|
### How do I build an FMU from python code?
|
|
35
28
|
|
|
@@ -112,16 +105,18 @@ from pythonfmu3 import Fmi3Causality, Fmi3Slave, Boolean, Int32, Float64, String
|
|
|
112
105
|
|
|
113
106
|
class PythonSlave(Fmi3Slave):
|
|
114
107
|
|
|
115
|
-
author = "John Doe"
|
|
116
|
-
description = "A simple description"
|
|
117
|
-
|
|
118
108
|
def __init__(self, **kwargs):
|
|
119
109
|
super().__init__(**kwargs)
|
|
120
110
|
|
|
111
|
+
self.author = "John Doe"
|
|
112
|
+
self.description = "A simple description"
|
|
113
|
+
|
|
114
|
+
self.time = 0.0
|
|
121
115
|
self.intOut = 1
|
|
122
116
|
self.realOut = 3.0
|
|
123
117
|
self.booleanVariable = True
|
|
124
118
|
self.stringVariable = "Hello World!"
|
|
119
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent))
|
|
125
120
|
self.register_variable(Int32("intOut", causality=Fmi3Causality.output))
|
|
126
121
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
127
122
|
self.register_variable(Boolean("booleanVariable", causality=Fmi3Causality.local))
|
|
@@ -4,4 +4,5 @@ from .enums import Fmi3Causality, Fmi3Initial, Fmi3Variability
|
|
|
4
4
|
from .fmi3slave import Fmi3Slave
|
|
5
5
|
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, String, Dimension
|
|
6
6
|
from .default_experiment import DefaultExperiment
|
|
7
|
-
from .
|
|
7
|
+
from .variable_types import Float64Type, EnumerationType
|
|
8
|
+
from .unit import BaseUnit, Unit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.16"
|
|
@@ -11,10 +11,11 @@ from xml.etree.ElementTree import Element, SubElement
|
|
|
11
11
|
|
|
12
12
|
from .logmsg import LogMsg
|
|
13
13
|
from .default_experiment import DefaultExperiment
|
|
14
|
-
from .type_definitions import TypeDefinitions
|
|
15
14
|
from ._version import __version__ as VERSION
|
|
16
15
|
from .enums import Fmi3Type, Fmi3Status, Fmi3Causality, Fmi3Initial, Fmi3Variability
|
|
17
16
|
from .variables import Boolean, Enumeration, Int32, Int64, UInt64, Float64, ModelVariable, String
|
|
17
|
+
from .variable_types import VariableType
|
|
18
|
+
from .unit import Unit
|
|
18
19
|
|
|
19
20
|
ModelOptions = namedtuple("ModelOptions", ["name", "value", "cli"])
|
|
20
21
|
|
|
@@ -54,7 +55,9 @@ class Fmi3Slave(ABC):
|
|
|
54
55
|
self.modelName: Optional[str] = self.__class__.__name__
|
|
55
56
|
self.description: Optional[str] = None
|
|
56
57
|
self.default_experiment: Optional[DefaultExperiment] = None
|
|
57
|
-
|
|
58
|
+
|
|
59
|
+
self.type_definitions: Dict[str, VariableType] = {}
|
|
60
|
+
self.units: Dict[str, Unit] = {}
|
|
58
61
|
|
|
59
62
|
def to_xml(self, model_options: Dict[str, str] = dict()) -> Element:
|
|
60
63
|
"""Build the XML representation of the model.
|
|
@@ -99,8 +102,15 @@ class Fmi3Slave(ABC):
|
|
|
99
102
|
|
|
100
103
|
SubElement(root, "CoSimulation", attrib=options)
|
|
101
104
|
|
|
102
|
-
if self.
|
|
103
|
-
root
|
|
105
|
+
if self.units:
|
|
106
|
+
unit_defs = SubElement(root, "UnitDefinitions")
|
|
107
|
+
for _, unit in self.units.items():
|
|
108
|
+
unit_defs.append(unit.to_xml())
|
|
109
|
+
|
|
110
|
+
if self.type_definitions:
|
|
111
|
+
type_defs = SubElement(root, "TypeDefinitions")
|
|
112
|
+
for _, val in self.type_definitions.items():
|
|
113
|
+
type_defs.append(val.to_xml())
|
|
104
114
|
|
|
105
115
|
if len(self.log_categories) > 0:
|
|
106
116
|
categories = SubElement(root, "LogCategories")
|
|
@@ -178,7 +188,7 @@ class Fmi3Slave(ABC):
|
|
|
178
188
|
raise Exception(f"Unsupported type {type(var)}!")
|
|
179
189
|
var.start = refs if len(getattr(var, "dimensions", [])) > 0 else refs[0]
|
|
180
190
|
|
|
181
|
-
def register_variable(self, var: ModelVariable, nested: bool = True):
|
|
191
|
+
def register_variable(self, var: ModelVariable, nested: bool = True, var_type: Any = None):
|
|
182
192
|
"""Register a variable as FMU interface.
|
|
183
193
|
|
|
184
194
|
Args:
|
|
@@ -206,10 +216,18 @@ class Fmi3Slave(ABC):
|
|
|
206
216
|
var.setter = lambda v: setattr(owner, var.local_name, np.reshape(v, newshape=getattr(owner, var.local_name).shape))
|
|
207
217
|
else:
|
|
208
218
|
var.setter = lambda v: setattr(owner, var.local_name, v)
|
|
219
|
+
|
|
220
|
+
if var_type:
|
|
221
|
+
self.type_definitions[var_type.name] = var_type
|
|
222
|
+
var.declared_type = var_type.name
|
|
209
223
|
|
|
210
224
|
def setup_experiment(self, start_time: float):
|
|
211
225
|
pass
|
|
212
226
|
|
|
227
|
+
def register_units(self, units: List[Unit]):
|
|
228
|
+
for unit in units:
|
|
229
|
+
self.units[unit.name] = unit
|
|
230
|
+
|
|
213
231
|
def enter_initialization_mode(self):
|
|
214
232
|
pass
|
|
215
233
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
import platform
|
|
3
2
|
|
|
4
3
|
import platform
|
|
5
4
|
|
|
@@ -8,7 +7,8 @@ def get_fmu_arch():
|
|
|
8
7
|
system = platform.system()
|
|
9
8
|
is_x86 = platform.machine() in ["i386", "AMD64", "x86_64"]
|
|
10
9
|
platforms = {"Windows": "windows", "Linux": "linux", "Darwin": "darwin"}
|
|
11
|
-
|
|
10
|
+
arch = "x86_64" if is_x86 else "x86"
|
|
11
|
+
return arch + "-" + platforms.get(system, "unknown")
|
|
12
12
|
|
|
13
13
|
def get_platform() -> str:
|
|
14
14
|
"""Get FMU binary platform folder name."""
|
{pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_common.hpp
RENAMED
|
@@ -105,11 +105,9 @@ public:
|
|
|
105
105
|
|
|
106
106
|
Logger(
|
|
107
107
|
FMIComponentEnvironment component,
|
|
108
|
-
String instanceName,
|
|
109
108
|
fmi3LogMessageCallback logCallback,
|
|
110
109
|
std::shared_ptr<Settings> settings)
|
|
111
110
|
: m_component{component}
|
|
112
|
-
, m_instanceName(std::move(instanceName))
|
|
113
111
|
, m_fmiLogger{logCallback}
|
|
114
112
|
, m_settings{settings}
|
|
115
113
|
{
|
|
@@ -154,7 +152,6 @@ public:
|
|
|
154
152
|
|
|
155
153
|
private:
|
|
156
154
|
const FMIComponentEnvironment m_component;
|
|
157
|
-
const String m_instanceName;
|
|
158
155
|
const FMICallbackLogger m_fmiLogger;
|
|
159
156
|
std::shared_ptr<Settings> m_settings;
|
|
160
157
|
};
|
{pythonfmu3-0.1.10 → pythonfmu3-0.1.16}/pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp
RENAMED
|
@@ -47,10 +47,10 @@ struct Component
|
|
|
47
47
|
modelError = 1 << 12,
|
|
48
48
|
modelFatal = 1 << 13,
|
|
49
49
|
};
|
|
50
|
-
Component(cppfmu::
|
|
50
|
+
Component(cppfmu::FMIComponentEnvironment instanceEnvironment,
|
|
51
51
|
cppfmu::FMICallbackLogger logCallback,
|
|
52
52
|
cppfmu::FMIBoolean loggingOn) : loggerSettings{std::make_shared<cppfmu::Logger::Settings>()},
|
|
53
|
-
logger{
|
|
53
|
+
logger{instanceEnvironment, logCallback, loggerSettings}, lastSuccessfulTime{std::numeric_limits<cppfmu::FMIFloat64>::quiet_NaN()}
|
|
54
54
|
{
|
|
55
55
|
loggerSettings->debugLoggingEnabled = (loggingOn == cppfmu::FMITrue);
|
|
56
56
|
}
|
|
@@ -96,7 +96,8 @@ fmi3Instance fmi3InstantiateCoSimulation(
|
|
|
96
96
|
fmi3IntermediateUpdateCallback intermediateUpdate )
|
|
97
97
|
{
|
|
98
98
|
try {
|
|
99
|
-
auto component = new Component(
|
|
99
|
+
auto component = new Component(
|
|
100
|
+
environment,
|
|
100
101
|
logMessage,
|
|
101
102
|
loggingOn);
|
|
102
103
|
component->slave = CppfmuInstantiateSlave(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#ifndef PYTHONFMU_IPYTHONSTATE_HPP
|
|
2
|
+
#define PYTHONFMU_IPYTHONSTATE_HPP
|
|
3
|
+
|
|
4
|
+
namespace pythonfmu
|
|
5
|
+
{
|
|
6
|
+
class IPyState
|
|
7
|
+
{
|
|
8
|
+
public:
|
|
9
|
+
IPyState() = default;
|
|
10
|
+
IPyState(IPyState const& other) = delete;
|
|
11
|
+
IPyState(IPyState&& other) = delete;
|
|
12
|
+
IPyState& operator=(IPyState const& other) = delete;
|
|
13
|
+
IPyState& operator=(IPyState&& other) = delete;
|
|
14
|
+
virtual ~IPyState() = default;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#endif //PYTHONFMU_IPYTHONSTATE_HPP
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
|
|
2
|
+
#include "pythonfmu/IPyState.hpp"
|
|
2
3
|
#include "pythonfmu/PySlaveInstance.hpp"
|
|
3
4
|
|
|
4
5
|
#include "pythonfmu/PyState.hpp"
|
|
@@ -7,6 +8,7 @@
|
|
|
7
8
|
|
|
8
9
|
#include <fstream>
|
|
9
10
|
#include <functional>
|
|
11
|
+
#include <mutex>
|
|
10
12
|
#include <regex>
|
|
11
13
|
#include <sstream>
|
|
12
14
|
#include <utility>
|
|
@@ -44,8 +46,9 @@ inline void py_safe_run(const std::function<void(PyGILState_STATE gilState)>& f)
|
|
|
44
46
|
PyGILState_Release(gil_state);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
PySlaveInstance::PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, const bool visible)
|
|
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))
|
|
49
52
|
, resources_(std::move(resources))
|
|
50
53
|
, logger_(logger)
|
|
51
54
|
, visible_(visible)
|
|
@@ -441,9 +444,14 @@ void PySlaveInstance::GetUInt64(const cppfmu::FMIValueReference* vr, std::size_t
|
|
|
441
444
|
}
|
|
442
445
|
|
|
443
446
|
for (int i = 0; i < nvr; i++) {
|
|
444
|
-
PyObject*
|
|
445
|
-
|
|
447
|
+
PyObject* item = PyList_GetItem(refs, i);
|
|
448
|
+
PyObject* value = PyObject_GetAttrString(item, "value");
|
|
449
|
+
if (value && PyLong_Check(value))
|
|
450
|
+
{
|
|
451
|
+
values[i] = static_cast<cppfmu::FMIUInt64>(PyLong_AsUnsignedLongLong(value));
|
|
452
|
+
}
|
|
446
453
|
}
|
|
454
|
+
|
|
447
455
|
Py_DECREF(refs);
|
|
448
456
|
clearLogBuffer();
|
|
449
457
|
});
|
|
@@ -625,7 +633,10 @@ PySlaveInstance::~PySlaveInstance()
|
|
|
625
633
|
|
|
626
634
|
} // namespace pythonfmu
|
|
627
635
|
|
|
628
|
-
|
|
636
|
+
namespace {
|
|
637
|
+
std::mutex pyStateMutex{};
|
|
638
|
+
std::shared_ptr<pythonfmu::PyState> pyState{};
|
|
639
|
+
}
|
|
629
640
|
|
|
630
641
|
std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
|
|
631
642
|
cppfmu::FMIString instanceName,
|
|
@@ -650,9 +661,57 @@ std::unique_ptr<cppfmu::SlaveInstance> CppfmuInstantiateSlave(
|
|
|
650
661
|
#endif
|
|
651
662
|
}
|
|
652
663
|
|
|
653
|
-
|
|
654
|
-
|
|
664
|
+
{
|
|
665
|
+
auto const ensurePyStateAlive = [&]() {
|
|
666
|
+
auto const lock = std::lock_guard{pyStateMutex};
|
|
667
|
+
if (nullptr == pyState) pyState = std::make_shared<pythonfmu::PyState>();
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
ensurePyStateAlive();
|
|
671
|
+
return std::make_unique<pythonfmu::PySlaveInstance>(
|
|
672
|
+
instanceName, resources, logger, visible, pyState);
|
|
655
673
|
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
extern "C" {
|
|
656
677
|
|
|
657
|
-
|
|
678
|
+
// The PyState instance owns it's own thread for constructing and destroying the Py* from the same thread.
|
|
679
|
+
// Creation of an std::thread increments ref counter of a shared library. So, when the client unloads the library
|
|
680
|
+
// the library won't be freed, as std::thread is alive, and the std::thread itself waits for de-initialization request.
|
|
681
|
+
// Thus, use DllMain on Windows and __attribute__((destructor)) on Linux for signaling to the PyState about de-initialization.
|
|
682
|
+
void finalizePythonInterpreter()
|
|
683
|
+
{
|
|
684
|
+
pyState = nullptr;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
namespace
|
|
689
|
+
{
|
|
690
|
+
#ifdef _WIN32
|
|
691
|
+
#include <windows.h>
|
|
692
|
+
|
|
693
|
+
BOOL APIENTRY DllMain(HMODULE hModule,
|
|
694
|
+
DWORD ul_reason_for_call,
|
|
695
|
+
LPVOID lpReserved)
|
|
696
|
+
{
|
|
697
|
+
switch (ul_reason_for_call) {
|
|
698
|
+
case DLL_PROCESS_ATTACH:
|
|
699
|
+
break;
|
|
700
|
+
case DLL_THREAD_ATTACH:
|
|
701
|
+
case DLL_THREAD_DETACH:
|
|
702
|
+
break;
|
|
703
|
+
case DLL_PROCESS_DETACH:
|
|
704
|
+
finalizePythonInterpreter();
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
return TRUE;
|
|
708
|
+
}
|
|
709
|
+
#elif defined(__linux__)
|
|
710
|
+
__attribute__((destructor)) void onLibraryUnload()
|
|
711
|
+
{
|
|
712
|
+
finalizePythonInterpreter();
|
|
713
|
+
}
|
|
714
|
+
#else
|
|
715
|
+
#error port the code
|
|
716
|
+
#endif
|
|
658
717
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#define PYTHONFMU_SLAVEINSTANCE_HPP
|
|
4
4
|
|
|
5
5
|
#include "cppfmu/cppfmu_cs.hpp"
|
|
6
|
+
#include "pythonfmu/IPyState.hpp"
|
|
6
7
|
|
|
7
8
|
#include <Python.h>
|
|
8
9
|
#include <string>
|
|
@@ -15,7 +16,7 @@ class PySlaveInstance : public cppfmu::SlaveInstance
|
|
|
15
16
|
{
|
|
16
17
|
|
|
17
18
|
public:
|
|
18
|
-
PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, bool visible);
|
|
19
|
+
PySlaveInstance(std::string instanceName, std::string resources, const cppfmu::Logger& logger, bool visible, std::shared_ptr<IPyState> pyState);
|
|
19
20
|
|
|
20
21
|
void initialize(PyGILState_STATE gilState);
|
|
21
22
|
|
|
@@ -53,6 +54,7 @@ public:
|
|
|
53
54
|
~PySlaveInstance() override;
|
|
54
55
|
|
|
55
56
|
private:
|
|
57
|
+
std::shared_ptr<IPyState> pyState_;
|
|
56
58
|
PyObject* pClass_;
|
|
57
59
|
PyObject* pInstance_{};
|
|
58
60
|
PyObject* pMessages_{};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
#ifndef PYTHONFMU_PYTHONSTATE_HPP
|
|
3
|
+
#define PYTHONFMU_PYTHONSTATE_HPP
|
|
4
|
+
|
|
5
|
+
#include "IPyState.hpp"
|
|
6
|
+
#include <Python.h>
|
|
7
|
+
#include <condition_variable>
|
|
8
|
+
#include <iostream>
|
|
9
|
+
#include <mutex>
|
|
10
|
+
#include <thread>
|
|
11
|
+
|
|
12
|
+
namespace pythonfmu
|
|
13
|
+
{
|
|
14
|
+
class PyState : public IPyState
|
|
15
|
+
{
|
|
16
|
+
public:
|
|
17
|
+
PyState()
|
|
18
|
+
: _initDeinitPyThread_{ &PyState::Worker, this }
|
|
19
|
+
{
|
|
20
|
+
auto lock = std::unique_lock{ mutex_ };
|
|
21
|
+
conditionalVariable_.wait(lock, [&] { return consructed_; });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
~PyState() override
|
|
25
|
+
{
|
|
26
|
+
{
|
|
27
|
+
auto const lock = std::lock_guard{ mutex_ };
|
|
28
|
+
destroyRequested_ = true;
|
|
29
|
+
}
|
|
30
|
+
conditionalVariable_.notify_one();
|
|
31
|
+
if (_initDeinitPyThread_.joinable()) _initDeinitPyThread_.join();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
|
|
36
|
+
// In accordance to the documentation https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx
|
|
37
|
+
// the Py_Initialize/Py_Finalize should be called from the same
|
|
38
|
+
// thread. The FMI standard allows to call fmi functions from different threads, also different threads
|
|
39
|
+
// could be used for loading unloading the FMU libraries. There is a deadlock, when the simulation tool
|
|
40
|
+
// unloads the FMU library from the thread which is differs from the thread where the Py_Initialize was called.
|
|
41
|
+
// Create a new thread which is used for calling Py_Initialize and Py_Deinitialize. The thread waits for
|
|
42
|
+
// notification from destructor and then calls Py_Deinitialize.
|
|
43
|
+
|
|
44
|
+
void Worker()
|
|
45
|
+
{
|
|
46
|
+
// It will be nullptr in case when some other tool already called Py_IsInitialized.
|
|
47
|
+
// There is no need to call Py_Finalize thus, this thread can exit ASAP.
|
|
48
|
+
auto const mainPyThread = []() -> PyThreadState* {
|
|
49
|
+
auto const justInitialized = !Py_IsInitialized();
|
|
50
|
+
if (justInitialized) {
|
|
51
|
+
Py_SetProgramName(L"./PythonFMU");
|
|
52
|
+
Py_Initialize();
|
|
53
|
+
#if PY_VERSION_HEX < 0x03070000
|
|
54
|
+
PyEval_InitThreads();
|
|
55
|
+
#endif
|
|
56
|
+
return PyEval_SaveThread();
|
|
57
|
+
}
|
|
58
|
+
return nullptr;
|
|
59
|
+
}();
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
auto const lock = std::lock_guard{ mutex_ };
|
|
63
|
+
consructed_ = true;
|
|
64
|
+
}
|
|
65
|
+
conditionalVariable_.notify_one();
|
|
66
|
+
|
|
67
|
+
if (nullptr != mainPyThread) {
|
|
68
|
+
auto lock = std::unique_lock{ mutex_ };
|
|
69
|
+
conditionalVariable_.wait(lock, [&] { return destroyRequested_; });
|
|
70
|
+
|
|
71
|
+
PyEval_RestoreThread(mainPyThread);
|
|
72
|
+
Py_Finalize();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
bool consructed_ = false;
|
|
77
|
+
bool destroyRequested_ = false;
|
|
78
|
+
std::condition_variable conditionalVariable_;
|
|
79
|
+
std::mutex mutex_;
|
|
80
|
+
std::thread _initDeinitPyThread_;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
} // namespace pythonfmu
|
|
84
|
+
|
|
85
|
+
#endif //PYTHONFMU_PYTHONSTATE_HPP
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64
|
|
1
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64, Fmi3Variability
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class PythonSlaveWithException(Fmi3Slave):
|
|
@@ -8,6 +8,9 @@ class PythonSlaveWithException(Fmi3Slave):
|
|
|
8
8
|
|
|
9
9
|
self.realIn = 22.0
|
|
10
10
|
self.realOut = 0.0
|
|
11
|
+
self.time = 0
|
|
12
|
+
|
|
13
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
11
14
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
12
15
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
13
16
|
|
|
@@ -21,6 +21,9 @@ class PythonSlave(Fmi3Slave):
|
|
|
21
21
|
self.realIn = 2. / 3.
|
|
22
22
|
self.booleanParameter = False
|
|
23
23
|
self.stringParameter = "dog"
|
|
24
|
+
self.time = 0
|
|
25
|
+
|
|
26
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
24
27
|
self.register_variable(
|
|
25
28
|
Int32("intParam", causality=Fmi3Causality.parameter, variability=Fmi3Variability.tunable))
|
|
26
29
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Fmi3Variability, String, DefaultExperiment
|
|
1
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Fmi3Variability, String, DefaultExperiment, Float64
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class PythonSlaveReadFile(Fmi3Slave):
|
|
@@ -11,6 +11,9 @@ class PythonSlaveReadFile(Fmi3Slave):
|
|
|
11
11
|
with (open(f'{self.resources}/hello.txt', 'r')) as file:
|
|
12
12
|
data = file.read()
|
|
13
13
|
|
|
14
|
+
self.time = 0
|
|
15
|
+
|
|
16
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
14
17
|
self.register_variable(
|
|
15
18
|
String("file_content", getter=lambda: data,
|
|
16
19
|
causality=Fmi3Causality.output,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import math
|
|
2
|
-
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64
|
|
2
|
+
from pythonfmu3.fmi3slave import Fmi3Slave, Fmi3Causality, Float64, Fmi3Variability
|
|
3
3
|
from localmodule import get_amplitude, get_time_constant
|
|
4
4
|
|
|
5
5
|
|
|
@@ -10,6 +10,9 @@ class PythonSlaveWithDep(Fmi3Slave):
|
|
|
10
10
|
|
|
11
11
|
self.realIn = 22.0
|
|
12
12
|
self.realOut = 0.0
|
|
13
|
+
self.time = 0
|
|
14
|
+
|
|
15
|
+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
|
|
13
16
|
self.register_variable(Float64("realIn", causality=Fmi3Causality.input))
|
|
14
17
|
self.register_variable(Float64("realOut", causality=Fmi3Causality.output))
|
|
15
18
|
|