fameio 2.3.0__py3-none-any.whl → 3.0.0__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.
- CHANGELOG.md +28 -0
- fameio/__init__.py +4 -1
- fameio/{source/cli → cli}/__init__.py +2 -0
- fameio/{source/cli → cli}/convert_results.py +8 -8
- fameio/{source/cli → cli}/make_config.py +5 -5
- fameio/{source/cli → cli}/options.py +0 -8
- fameio/{source/cli → cli}/parser.py +26 -63
- fameio/input/__init__.py +27 -0
- fameio/input/loader/__init__.py +68 -0
- fameio/input/loader/controller.py +129 -0
- fameio/input/loader/loader.py +109 -0
- fameio/input/metadata.py +149 -0
- fameio/input/resolver.py +44 -0
- fameio/{source → input}/scenario/__init__.py +1 -2
- fameio/{source → input}/scenario/agent.py +24 -38
- fameio/input/scenario/attribute.py +203 -0
- fameio/{source → input}/scenario/contract.py +50 -61
- fameio/{source → input}/scenario/exception.py +8 -13
- fameio/{source → input}/scenario/fameiofactory.py +6 -6
- fameio/{source → input}/scenario/generalproperties.py +22 -47
- fameio/{source → input}/scenario/scenario.py +34 -31
- fameio/input/scenario/stringset.py +48 -0
- fameio/{source → input}/schema/__init__.py +2 -2
- fameio/input/schema/agenttype.py +125 -0
- fameio/input/schema/attribute.py +268 -0
- fameio/{source → input}/schema/java_packages.py +26 -22
- fameio/{source → input}/schema/schema.py +25 -22
- fameio/{source → input}/validator.py +32 -35
- fameio/{source → input}/writer.py +86 -86
- fameio/{source/logs.py → logs.py} +25 -9
- fameio/{source/results → output}/agent_type.py +21 -22
- fameio/{source/results → output}/conversion.py +34 -31
- fameio/{source/results → output}/csv_writer.py +7 -7
- fameio/{source/results → output}/data_transformer.py +24 -24
- fameio/{source/results → output}/input_dao.py +51 -49
- fameio/{source/results → output}/output_dao.py +16 -17
- fameio/{source/results → output}/reader.py +30 -31
- fameio/{source/results → output}/yaml_writer.py +2 -3
- fameio/scripts/__init__.py +2 -2
- fameio/scripts/convert_results.py +16 -15
- fameio/scripts/make_config.py +9 -9
- fameio/{source/series.py → series.py} +28 -26
- fameio/{source/time.py → time.py} +8 -8
- fameio/{source/tools.py → tools.py} +2 -2
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/METADATA +277 -72
- fameio-3.0.0.dist-info/RECORD +56 -0
- fameio/source/__init__.py +0 -8
- fameio/source/loader.py +0 -181
- fameio/source/metadata.py +0 -32
- fameio/source/path_resolver.py +0 -34
- fameio/source/scenario/attribute.py +0 -130
- fameio/source/scenario/stringset.py +0 -51
- fameio/source/schema/agenttype.py +0 -132
- fameio/source/schema/attribute.py +0 -203
- fameio/source/schema/exception.py +0 -9
- fameio-2.3.0.dist-info/RECORD +0 -55
- /fameio/{source/results → output}/__init__.py +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSE.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/WHEEL +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/entry_points.txt +0 -0
@@ -4,20 +4,17 @@
|
|
4
4
|
|
5
5
|
import math
|
6
6
|
from collections import Counter
|
7
|
-
from typing import Any
|
7
|
+
from typing import Any
|
8
8
|
|
9
|
-
from fameio.
|
10
|
-
from fameio.
|
11
|
-
from fameio.
|
12
|
-
from fameio.
|
13
|
-
from fameio.
|
14
|
-
from fameio.
|
15
|
-
from fameio.source.schema.schema import Schema
|
16
|
-
from fameio.source.series import TimeSeriesManager, TimeSeriesException
|
17
|
-
from fameio.source.time import FameTime
|
9
|
+
from fameio.input.resolver import PathResolver
|
10
|
+
from fameio.input.scenario import Agent, Attribute, Contract, Scenario, StringSet
|
11
|
+
from fameio.input.schema import Schema, AttributeSpecs, AttributeType, AgentType
|
12
|
+
from fameio.logs import log_error_and_raise, log
|
13
|
+
from fameio.series import TimeSeriesManager, TimeSeriesError
|
14
|
+
from fameio.time import FameTime
|
18
15
|
|
19
16
|
|
20
|
-
class
|
17
|
+
class ValidationError(Exception):
|
21
18
|
"""Indicates an error occurred during validation of any data with a connected schema"""
|
22
19
|
|
23
20
|
pass
|
@@ -77,12 +74,12 @@ class SchemaValidator:
|
|
77
74
|
return timeseries_manager
|
78
75
|
|
79
76
|
@staticmethod
|
80
|
-
def ensure_unique_agent_ids(agents:
|
77
|
+
def ensure_unique_agent_ids(agents: list[Agent]) -> None:
|
81
78
|
"""Raises exception if any id for given `agents` is not unique"""
|
82
79
|
list_of_ids = [agent.id for agent in agents]
|
83
80
|
non_unique_ids = [agent_id for agent_id, count in Counter(list_of_ids).items() if count > 1]
|
84
81
|
if non_unique_ids:
|
85
|
-
log_error_and_raise(
|
82
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_ID_NOT_UNIQUE.format(non_unique_ids)))
|
86
83
|
|
87
84
|
@staticmethod
|
88
85
|
def ensure_agent_and_timeseries_are_valid(agent: Agent, schema: Schema, timeseries_manager: TimeSeriesManager):
|
@@ -95,7 +92,7 @@ class SchemaValidator:
|
|
95
92
|
def ensure_agent_type_in_schema(agent: Agent, schema: Schema) -> None:
|
96
93
|
"""Raises exception if type for given `agent` is not specified in given `schema`"""
|
97
94
|
if agent.type_name not in schema.agent_types:
|
98
|
-
log_error_and_raise(
|
95
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(agent.type_name)))
|
99
96
|
|
100
97
|
@staticmethod
|
101
98
|
def ensure_is_valid_agent(agent: Agent, schema: Schema) -> None:
|
@@ -112,10 +109,10 @@ class SchemaValidator:
|
|
112
109
|
if name in schema.agent_types:
|
113
110
|
return schema.agent_types[name]
|
114
111
|
else:
|
115
|
-
log_error_and_raise(
|
112
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(name)))
|
116
113
|
|
117
114
|
@staticmethod
|
118
|
-
def _ensure_mandatory_present(attributes:
|
115
|
+
def _ensure_mandatory_present(attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs]) -> None:
|
119
116
|
"""
|
120
117
|
Raises Exception if in given list of `specifications` at least one item is mandatory,
|
121
118
|
provides no defaults and is not contained in given `attributes` dictionary
|
@@ -125,7 +122,7 @@ class SchemaValidator:
|
|
125
122
|
if specification.is_mandatory:
|
126
123
|
if not specification.has_default_value:
|
127
124
|
log_error_and_raise(
|
128
|
-
|
125
|
+
ValidationError(SchemaValidator._ATTRIBUTE_MISSING.format(specification.full_name))
|
129
126
|
)
|
130
127
|
else:
|
131
128
|
if specification.has_default_value:
|
@@ -141,11 +138,11 @@ class SchemaValidator:
|
|
141
138
|
SchemaValidator._ensure_mandatory_present(attribute.nested, specification.nested_attributes)
|
142
139
|
|
143
140
|
@staticmethod
|
144
|
-
def _ensure_attributes_exist(attributes:
|
141
|
+
def _ensure_attributes_exist(attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs]) -> None:
|
145
142
|
"""Raises exception any entry of given `attributes` has no corresponding type `specification`"""
|
146
143
|
for name, attribute in attributes.items():
|
147
144
|
if name not in specifications:
|
148
|
-
log_error_and_raise(
|
145
|
+
log_error_and_raise(ValidationError(SchemaValidator._ATTRIBUTE_UNKNOWN.format(attribute)))
|
149
146
|
if attribute.has_nested:
|
150
147
|
specification = specifications[name]
|
151
148
|
SchemaValidator._ensure_attributes_exist(attribute.nested, specification.nested_attributes)
|
@@ -156,7 +153,7 @@ class SchemaValidator:
|
|
156
153
|
|
157
154
|
@staticmethod
|
158
155
|
def _ensure_value_and_type_match(
|
159
|
-
attributes:
|
156
|
+
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs]
|
160
157
|
) -> None:
|
161
158
|
"""Raises exception if in given list of `attributes` its value does not match associated type `specification`"""
|
162
159
|
for name, attribute in attributes.items():
|
@@ -166,10 +163,10 @@ class SchemaValidator:
|
|
166
163
|
type_spec = specification.attr_type
|
167
164
|
if not SchemaValidator._is_compatible(specification, value):
|
168
165
|
message = SchemaValidator._INCOMPATIBLE.format(value, type_spec, specification.full_name)
|
169
|
-
log_error_and_raise(
|
166
|
+
log_error_and_raise(ValidationError(message))
|
170
167
|
if not SchemaValidator._is_allowed_value(specification, value):
|
171
168
|
message = SchemaValidator._DISALLOWED.format(value, specification.full_name)
|
172
|
-
log_error_and_raise(
|
169
|
+
log_error_and_raise(ValidationError(message))
|
173
170
|
if attribute.has_nested:
|
174
171
|
SchemaValidator._ensure_value_and_type_match(attribute.nested, specification.nested_attributes)
|
175
172
|
if attribute.has_nested_list:
|
@@ -210,7 +207,7 @@ class SchemaValidator:
|
|
210
207
|
elif attribute_type is AttributeType.TIME_SERIES:
|
211
208
|
return isinstance(value, (str, int)) or (isinstance(value, float) and not math.isnan(value))
|
212
209
|
else:
|
213
|
-
log_error_and_raise(
|
210
|
+
log_error_and_raise(ValidationError(SchemaValidator._TYPE_NOT_IMPLEMENTED.format(attribute_type)))
|
214
211
|
|
215
212
|
@staticmethod
|
216
213
|
def _is_allowed_value(attribute: AttributeSpecs, value) -> bool:
|
@@ -239,7 +236,7 @@ class SchemaValidator:
|
|
239
236
|
|
240
237
|
@staticmethod
|
241
238
|
def _ensure_valid_timeseries(
|
242
|
-
attributes:
|
239
|
+
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs], manager: TimeSeriesManager
|
243
240
|
) -> None:
|
244
241
|
"""Recursively searches for time_series in agent attributes and registers them at given `manager`"""
|
245
242
|
for name, attribute in attributes.items():
|
@@ -249,9 +246,9 @@ class SchemaValidator:
|
|
249
246
|
if attribute_type is AttributeType.TIME_SERIES:
|
250
247
|
try:
|
251
248
|
manager.register_and_validate(attribute.value)
|
252
|
-
except
|
249
|
+
except TimeSeriesError as e:
|
253
250
|
message = SchemaValidator._TIME_SERIES_INVALID.format(specification.full_name)
|
254
|
-
log_error_and_raise(
|
251
|
+
log_error_and_raise(ValidationError(message, e))
|
255
252
|
if attribute.has_nested:
|
256
253
|
SchemaValidator._ensure_valid_timeseries(attribute.nested, specification.nested_attributes, manager)
|
257
254
|
if attribute.has_nested_list:
|
@@ -259,7 +256,7 @@ class SchemaValidator:
|
|
259
256
|
SchemaValidator._ensure_valid_timeseries(entry, specification.nested_attributes, manager)
|
260
257
|
|
261
258
|
@staticmethod
|
262
|
-
def ensure_string_set_consistency(agent: Agent, schema: Schema, string_sets:
|
259
|
+
def ensure_string_set_consistency(agent: Agent, schema: Schema, string_sets: dict[str, StringSet]) -> None:
|
263
260
|
"""
|
264
261
|
Raises exception if
|
265
262
|
a) an agent's attribute is of type StringSet but the corresponding StringSet is not defined in the scenario
|
@@ -271,7 +268,7 @@ class SchemaValidator:
|
|
271
268
|
|
272
269
|
@staticmethod
|
273
270
|
def _ensure_string_set_consistency(
|
274
|
-
attributes:
|
271
|
+
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs], string_sets: dict[str, StringSet]
|
275
272
|
) -> None:
|
276
273
|
"""
|
277
274
|
Recursively iterates through all attributes of an agent, applying tests if attribute type is `StringSet`
|
@@ -290,10 +287,10 @@ class SchemaValidator:
|
|
290
287
|
msg = SchemaValidator._MISSING_STRING_SET_ENTRY.format(
|
291
288
|
attribute.value, str(attribute), name
|
292
289
|
)
|
293
|
-
log_error_and_raise(
|
290
|
+
log_error_and_raise(ValidationError(msg))
|
294
291
|
else:
|
295
292
|
msg = SchemaValidator._MISSING_STRING_SET.format(specification.full_name)
|
296
|
-
log_error_and_raise(
|
293
|
+
log_error_and_raise(ValidationError(msg))
|
297
294
|
if attribute.has_nested:
|
298
295
|
SchemaValidator._ensure_string_set_consistency(
|
299
296
|
attribute.nested, specification.nested_attributes, string_sets
|
@@ -303,20 +300,20 @@ class SchemaValidator:
|
|
303
300
|
SchemaValidator._ensure_string_set_consistency(entry, specification.nested_attributes, string_sets)
|
304
301
|
|
305
302
|
@staticmethod
|
306
|
-
def ensure_is_valid_contract(contract: Contract, schema: Schema, agent_types_by_id:
|
303
|
+
def ensure_is_valid_contract(contract: Contract, schema: Schema, agent_types_by_id: dict[int, str]) -> None:
|
307
304
|
"""Raises exception if given `contract` does not meet the `schema`'s requirements, using `agent_types_by_id`"""
|
308
305
|
sender_id = contract.sender_id
|
309
306
|
if sender_id not in agent_types_by_id:
|
310
|
-
log_error_and_raise(
|
307
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_MISSING.format(sender_id)))
|
311
308
|
if contract.receiver_id not in agent_types_by_id:
|
312
|
-
log_error_and_raise(
|
309
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_MISSING.format(contract.receiver_id)))
|
313
310
|
sender_type_name = agent_types_by_id[sender_id]
|
314
311
|
if sender_type_name not in schema.agent_types:
|
315
|
-
log_error_and_raise(
|
312
|
+
log_error_and_raise(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(sender_type_name)))
|
316
313
|
sender_type = schema.agent_types[sender_type_name]
|
317
314
|
product = contract.product_name
|
318
315
|
if product not in sender_type.products:
|
319
|
-
log_error_and_raise(
|
316
|
+
log_error_and_raise(ValidationError(SchemaValidator._PRODUCT_MISSING.format(product, sender_type_name)))
|
320
317
|
|
321
318
|
@staticmethod
|
322
319
|
def check_agents_have_contracts(scenario: Scenario) -> None:
|
@@ -1,36 +1,29 @@
|
|
1
1
|
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
import sys
|
5
4
|
import importlib.metadata as metadata
|
5
|
+
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Any,
|
8
|
-
|
9
|
-
from fameprotobuf.
|
10
|
-
from fameprotobuf.
|
11
|
-
from fameprotobuf.
|
12
|
-
from fameprotobuf.
|
13
|
-
from fameprotobuf.
|
14
|
-
from fameprotobuf.
|
15
|
-
|
16
|
-
|
17
|
-
from fameio.
|
18
|
-
from fameio.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
from fameio.source.schema.java_packages import JavaPackages
|
28
|
-
from fameio.source.series import TimeSeriesManager
|
29
|
-
from fameio.source.time import FameTime
|
30
|
-
from fameio.source.tools import ensure_is_list
|
31
|
-
|
32
|
-
|
33
|
-
class ProtoWriterException(Exception):
|
7
|
+
from typing import Any, Union
|
8
|
+
|
9
|
+
from fameprotobuf.contract_pb2 import ProtoContract
|
10
|
+
from fameprotobuf.data_storage_pb2 import DataStorage
|
11
|
+
from fameprotobuf.execution_data_pb2 import ExecutionData
|
12
|
+
from fameprotobuf.field_pb2 import NestedField
|
13
|
+
from fameprotobuf.input_file_pb2 import InputData
|
14
|
+
from fameprotobuf.model_pb2 import ModelData
|
15
|
+
|
16
|
+
import fameio
|
17
|
+
from fameio.input.scenario import Agent, Attribute, Contract, GeneralProperties, Scenario, StringSet
|
18
|
+
from fameio.input.schema import AttributeSpecs, AttributeType, JavaPackages, Schema
|
19
|
+
from fameio.logs import log_error_and_raise, log
|
20
|
+
from fameio.output.reader import Reader
|
21
|
+
from fameio.series import TimeSeriesManager
|
22
|
+
from fameio.time import FameTime
|
23
|
+
from fameio.tools import ensure_is_list
|
24
|
+
|
25
|
+
|
26
|
+
class ProtoWriterError(Exception):
|
34
27
|
"""Indicates an error during writing of protobuf file"""
|
35
28
|
|
36
29
|
pass
|
@@ -39,7 +32,7 @@ class ProtoWriterException(Exception):
|
|
39
32
|
class ProtoWriter:
|
40
33
|
"""Writes a given scenario to protobuf file"""
|
41
34
|
|
42
|
-
_FAME_PROTOBUF_STREAM_HEADER =
|
35
|
+
_FAME_PROTOBUF_STREAM_HEADER = fameio.FILE_HEADER_V2
|
43
36
|
|
44
37
|
_TYPE_NOT_IMPLEMENTED = "AttributeType '{}' not implemented."
|
45
38
|
_CONTRACT_UNSUPPORTED = (
|
@@ -72,43 +65,42 @@ class ProtoWriter:
|
|
72
65
|
self._add_contracts(pb_input, scenario.contracts)
|
73
66
|
self._set_time_series(pb_input)
|
74
67
|
self._set_schema(pb_input, scenario.schema)
|
68
|
+
self._set_string_sets(pb_input, scenario.string_sets)
|
75
69
|
|
76
70
|
self._set_java_package_names(pb_data_storage.model, scenario.schema.packages)
|
77
|
-
self._set_execution_versions(pb_data_storage.execution.
|
71
|
+
self._set_execution_versions(pb_data_storage.execution.version_data)
|
78
72
|
return pb_data_storage
|
79
73
|
|
80
74
|
@staticmethod
|
81
75
|
def _set_general_properties(pb_input: InputData, general_properties: GeneralProperties) -> None:
|
82
76
|
"""Saves a scenario's general properties to specified protobuf `pb_input` container"""
|
83
77
|
log().info("Adding General Properties")
|
84
|
-
pb_input.
|
85
|
-
pb_input.simulation.
|
86
|
-
pb_input.simulation.
|
87
|
-
pb_input.simulation.
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
def _add_agents(self, pb_input: InputData, agents: List[Agent], schema: Schema) -> None:
|
78
|
+
pb_input.run_id = general_properties.run_id
|
79
|
+
pb_input.simulation.start_time = general_properties.simulation_start_time
|
80
|
+
pb_input.simulation.stop_time = general_properties.simulation_stop_time
|
81
|
+
pb_input.simulation.random_seed = general_properties.simulation_random_seed
|
82
|
+
|
83
|
+
def _add_agents(self, pb_input: InputData, agents: list[Agent], schema: Schema) -> None:
|
92
84
|
"""Triggers setting of `agents` to `pb_input`"""
|
93
85
|
log().info("Adding Agents")
|
94
86
|
for agent in agents:
|
95
|
-
pb_agent = self._set_agent(pb_input.
|
87
|
+
pb_agent = self._set_agent(pb_input.agents.add(), agent)
|
96
88
|
attribute_specs = schema.agent_types[agent.type_name].attributes
|
97
89
|
self._set_attributes(pb_agent, agent.attributes, attribute_specs)
|
98
|
-
pb_agent.metadata =
|
90
|
+
pb_agent.metadata = agent.get_metadata_string()
|
99
91
|
|
100
92
|
@staticmethod
|
101
93
|
def _set_agent(pb_agent: InputData.AgentDao, agent: Agent) -> InputData.AgentDao:
|
102
94
|
"""Saves type and id of given `agent` to protobuf `pb_agent` container. Returns given `pb_agent`"""
|
103
|
-
pb_agent.
|
95
|
+
pb_agent.class_name = agent.type_name
|
104
96
|
pb_agent.id = agent.id
|
105
97
|
return pb_agent
|
106
98
|
|
107
99
|
def _set_attributes(
|
108
100
|
self,
|
109
101
|
pb_parent: Union[InputData.AgentDao, NestedField],
|
110
|
-
attributes:
|
111
|
-
specs:
|
102
|
+
attributes: dict[str, Attribute],
|
103
|
+
specs: dict[str, AttributeSpecs],
|
112
104
|
) -> None:
|
113
105
|
"""Assigns `attributes` to protobuf fields of given `pb_parent` - cascades for nested Attributes"""
|
114
106
|
values_not_set = [key for key in specs.keys()]
|
@@ -136,51 +128,51 @@ class ProtoWriter:
|
|
136
128
|
@staticmethod
|
137
129
|
def _add_field(pb_parent: Union[InputData.AgentDao, NestedField], name: str) -> NestedField:
|
138
130
|
"""Returns new field with given `name` that is added to given `pb_parent`"""
|
139
|
-
pb_field = pb_parent.
|
140
|
-
pb_field.
|
131
|
+
pb_field = pb_parent.fields.add()
|
132
|
+
pb_field.field_name = name
|
141
133
|
return pb_field
|
142
134
|
|
143
135
|
def _set_attribute(self, pb_field: NestedField, value: Any, attribute_type: AttributeType) -> None:
|
144
136
|
"""Sets given `value` to given protobuf `pb_field` depending on specified `attribute_type`"""
|
145
137
|
if attribute_type is AttributeType.INTEGER:
|
146
|
-
pb_field.
|
138
|
+
pb_field.int_values.extend(ensure_is_list(value))
|
147
139
|
elif attribute_type is AttributeType.DOUBLE:
|
148
|
-
pb_field.
|
140
|
+
pb_field.double_values.extend(ensure_is_list(value))
|
149
141
|
elif attribute_type is AttributeType.LONG:
|
150
|
-
pb_field.
|
142
|
+
pb_field.long_values.extend(ensure_is_list(value))
|
151
143
|
elif attribute_type is AttributeType.TIME_STAMP:
|
152
|
-
pb_field.
|
144
|
+
pb_field.long_values.extend(ensure_is_list(FameTime.convert_string_if_is_datetime(value)))
|
153
145
|
elif attribute_type in (AttributeType.ENUM, AttributeType.STRING, AttributeType.STRING_SET):
|
154
|
-
pb_field.
|
146
|
+
pb_field.string_values.extend(ensure_is_list(value))
|
155
147
|
elif attribute_type is AttributeType.TIME_SERIES:
|
156
|
-
pb_field.
|
148
|
+
pb_field.series_id = self._time_series_manager.get_series_id_by_identifier(value)
|
157
149
|
else:
|
158
|
-
log_error_and_raise(
|
150
|
+
log_error_and_raise(ProtoWriterError(self._TYPE_NOT_IMPLEMENTED.format(attribute_type)))
|
159
151
|
|
160
152
|
@staticmethod
|
161
|
-
def _add_contracts(pb_input: InputData, contracts:
|
153
|
+
def _add_contracts(pb_input: InputData, contracts: list[Contract]) -> None:
|
162
154
|
"""Triggers setting of `contracts` to `pb_input`"""
|
163
155
|
log().info("Adding Contracts")
|
164
156
|
for contract in contracts:
|
165
|
-
pb_contract = ProtoWriter._set_contract(pb_input.
|
157
|
+
pb_contract = ProtoWriter._set_contract(pb_input.contracts.add(), contract)
|
166
158
|
ProtoWriter._set_contract_attributes(pb_contract, contract.attributes)
|
167
|
-
pb_contract.metadata =
|
159
|
+
pb_contract.metadata = contract.get_metadata_string()
|
168
160
|
|
169
161
|
@staticmethod
|
170
162
|
def _set_contract(pb_contract: ProtoContract, contract: Contract) -> ProtoContract:
|
171
163
|
"""Saves given `contract` details to protobuf container `pb_contract`. Returns given `pb_contract`"""
|
172
|
-
pb_contract.
|
173
|
-
pb_contract.
|
174
|
-
pb_contract.
|
175
|
-
pb_contract.
|
176
|
-
pb_contract.
|
164
|
+
pb_contract.sender_id = contract.sender_id
|
165
|
+
pb_contract.receiver_id = contract.receiver_id
|
166
|
+
pb_contract.product_name = contract.product_name
|
167
|
+
pb_contract.first_delivery_time = contract.first_delivery_time
|
168
|
+
pb_contract.delivery_interval_in_steps = contract.delivery_interval
|
177
169
|
if contract.expiration_time:
|
178
|
-
pb_contract.
|
170
|
+
pb_contract.expiration_time = contract.expiration_time
|
179
171
|
return pb_contract
|
180
172
|
|
181
173
|
@staticmethod
|
182
174
|
def _set_contract_attributes(
|
183
|
-
pb_parent: Union[ProtoContract, NestedField], attributes:
|
175
|
+
pb_parent: Union[ProtoContract, NestedField], attributes: dict[str, Attribute]
|
184
176
|
) -> None:
|
185
177
|
"""Assign (nested) Attributes to given protobuf container `pb_parent`"""
|
186
178
|
for name, attribute in attributes.items():
|
@@ -190,13 +182,13 @@ class ProtoWriter:
|
|
190
182
|
if attribute.has_value:
|
191
183
|
value = attribute.value
|
192
184
|
if isinstance(value, int):
|
193
|
-
pb_field.
|
185
|
+
pb_field.int_values.extend([value])
|
194
186
|
elif isinstance(value, float):
|
195
|
-
pb_field.
|
187
|
+
pb_field.double_values.extend([value])
|
196
188
|
elif isinstance(value, str):
|
197
|
-
pb_field.
|
189
|
+
pb_field.string_values.extend([value])
|
198
190
|
else:
|
199
|
-
log_error_and_raise(
|
191
|
+
log_error_and_raise(ProtoWriterError(ProtoWriter._CONTRACT_UNSUPPORTED.format(str(attribute))))
|
200
192
|
elif attribute.has_nested:
|
201
193
|
ProtoWriter._set_contract_attributes(pb_field, attribute.nested)
|
202
194
|
|
@@ -204,17 +196,11 @@ class ProtoWriter:
|
|
204
196
|
"""Adds all time series from TimeSeriesManager to given `pb_input`"""
|
205
197
|
log().info("Adding TimeSeries")
|
206
198
|
for unique_id, identifier, data in self._time_series_manager.get_all_series():
|
207
|
-
pb_series = pb_input.
|
208
|
-
pb_series.
|
209
|
-
pb_series.
|
210
|
-
|
211
|
-
|
212
|
-
@staticmethod
|
213
|
-
def _add_rows_to_series(series: InputData.TimeSeriesDao, data_frame) -> None:
|
214
|
-
for key, value in data_frame.itertuples(index=False):
|
215
|
-
row = series.row.add()
|
216
|
-
row.timeStep = int(key)
|
217
|
-
row.value = value
|
199
|
+
pb_series = pb_input.time_series.add()
|
200
|
+
pb_series.series_id = unique_id
|
201
|
+
pb_series.series_name = identifier
|
202
|
+
pb_series.time_steps.extend(list(data[0]))
|
203
|
+
pb_series.values.extend(list(data[1]))
|
218
204
|
|
219
205
|
@staticmethod
|
220
206
|
def _set_schema(pb_input: InputData, schema: Schema) -> None:
|
@@ -222,13 +208,27 @@ class ProtoWriter:
|
|
222
208
|
log().info("Adding Schema")
|
223
209
|
pb_input.schema = schema.to_string()
|
224
210
|
|
211
|
+
@staticmethod
|
212
|
+
def _set_string_sets(pb_input: InputData, string_sets: dict[str, StringSet]) -> None:
|
213
|
+
"""Adds the given StringSets to given `pb_input`"""
|
214
|
+
for name, string_set in string_sets.items():
|
215
|
+
pb_set = pb_input.string_sets.add()
|
216
|
+
pb_set.name = name
|
217
|
+
for value, specification in string_set.values.items():
|
218
|
+
pb_value = pb_set.values.add()
|
219
|
+
pb_value.name = value
|
220
|
+
if specification.has_metadata():
|
221
|
+
pb_value.metadata = specification.get_metadata_string()
|
222
|
+
if string_set.has_metadata():
|
223
|
+
pb_set.metadata = string_set.get_metadata_string()
|
224
|
+
|
225
225
|
@staticmethod
|
226
226
|
def _set_java_package_names(pb_model: ModelData, java_packages: JavaPackages) -> None:
|
227
227
|
"""Adds given JavaPackages names to given ModelData section"""
|
228
|
-
pb_packages = pb_model.
|
229
|
-
pb_packages.
|
230
|
-
pb_packages.
|
231
|
-
pb_packages.
|
228
|
+
pb_packages = pb_model.package_definition
|
229
|
+
pb_packages.agents.extend(java_packages.agents)
|
230
|
+
pb_packages.data_items.extend(java_packages.data_items)
|
231
|
+
pb_packages.portables.extend(java_packages.portables)
|
232
232
|
|
233
233
|
def _write_protobuf_to_disk(self, pb_data_storage: DataStorage) -> None:
|
234
234
|
"""Writes given `protobuf_input_data` to disk"""
|
@@ -240,12 +240,12 @@ class ProtoWriter:
|
|
240
240
|
file.write(len(serialised_data_storage).to_bytes(Reader.BYTES_DEFINING_MESSAGE_LENGTH, byteorder="big"))
|
241
241
|
file.write(serialised_data_storage)
|
242
242
|
except OSError as e:
|
243
|
-
log_error_and_raise(
|
243
|
+
log_error_and_raise(ProtoWriterError(ProtoWriter._NO_FILE_SPECIFIED.format(self.file_path), e))
|
244
244
|
log().info(self._INFO_WRITING_COMPLETED.format(self.file_path))
|
245
245
|
|
246
246
|
@staticmethod
|
247
|
-
def _set_execution_versions(
|
247
|
+
def _set_execution_versions(pb_version_data: ExecutionData.VersionData) -> None:
|
248
248
|
"""Adds version strings for fameio, fameprotobuf, and python to the given Versions message"""
|
249
|
-
|
250
|
-
|
251
|
-
|
249
|
+
pb_version_data.fame_protobuf = metadata.version("fameprotobuf")
|
250
|
+
pb_version_data.fame_io = metadata.version("fameio")
|
251
|
+
pb_version_data.python = sys.version
|
@@ -5,7 +5,7 @@
|
|
5
5
|
import logging as pylog
|
6
6
|
from enum import Enum
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import
|
8
|
+
from typing import Optional
|
9
9
|
|
10
10
|
|
11
11
|
class LogLevel(Enum):
|
@@ -20,8 +20,8 @@ class LogLevel(Enum):
|
|
20
20
|
DEBUG = pylog.DEBUG
|
21
21
|
|
22
22
|
|
23
|
-
_loggers:
|
24
|
-
_handlers:
|
23
|
+
_loggers: list[pylog.Logger] = []
|
24
|
+
_handlers: list[pylog.Handler] = []
|
25
25
|
|
26
26
|
_FORMAT_NORMAL = "%(asctime)s — %(levelname)s — %(message)s" # noqa
|
27
27
|
_FORMAT_DETAILLED = "%(asctime)s.%(msecs)03d — %(levelname)s — %(module)s:%(funcName)s:%(lineno)d — %(message)s" # noqa
|
@@ -42,14 +42,30 @@ def log() -> pylog.Logger:
|
|
42
42
|
return _loggers[0]
|
43
43
|
|
44
44
|
|
45
|
-
def
|
46
|
-
"""
|
47
|
-
|
48
|
-
|
45
|
+
def log_critical_and_raise(exception: Exception) -> None:
|
46
|
+
"""
|
47
|
+
Raises the specified `exception` and logs a critical error with the exception's message
|
48
|
+
|
49
|
+
Args:
|
50
|
+
exception: to be raised and logged at level `critical`
|
51
|
+
|
52
|
+
Raises:
|
53
|
+
Exception: the given exception
|
54
|
+
"""
|
55
|
+
log().critical(str(exception))
|
56
|
+
raise exception
|
49
57
|
|
50
58
|
|
51
|
-
def log_error_and_raise(exception: Exception) ->
|
52
|
-
"""
|
59
|
+
def log_error_and_raise(exception: Exception) -> None:
|
60
|
+
"""
|
61
|
+
Raises the specified `exception` and logs a critical error with the exception's message
|
62
|
+
|
63
|
+
Args:
|
64
|
+
exception: to be raised and logged at level `error`
|
65
|
+
|
66
|
+
Raises:
|
67
|
+
Exception: the given exception
|
68
|
+
"""
|
53
69
|
log().error(str(exception))
|
54
70
|
raise exception
|
55
71
|
|
@@ -1,10 +1,9 @@
|
|
1
|
-
# SPDX-FileCopyrightText:
|
1
|
+
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
from typing import Union
|
4
5
|
|
5
|
-
from
|
6
|
-
|
7
|
-
from fameprotobuf.Services_pb2 import Output
|
6
|
+
from fameprotobuf.services_pb2 import Output
|
8
7
|
|
9
8
|
|
10
9
|
class AgentType:
|
@@ -13,42 +12,42 @@ class AgentType:
|
|
13
12
|
def __init__(self, agent_type: Output.AgentType) -> None:
|
14
13
|
self._agent_type = agent_type
|
15
14
|
|
16
|
-
def get_simple_column_map(self) ->
|
15
|
+
def get_simple_column_map(self) -> dict[int, str]:
|
17
16
|
"""Returns dictionary of simple column IDs mapped to their name - ignoring complex columns"""
|
18
|
-
return {field.
|
17
|
+
return {field.field_id: field.field_name for field in self._agent_type.fields if len(field.index_names) == 0}
|
19
18
|
|
20
|
-
def get_merged_column_map(self) ->
|
19
|
+
def get_merged_column_map(self) -> dict[int, str]:
|
21
20
|
"""Returns dictionary of all column IDs mapped to their name merged with names of inner complex columns"""
|
22
21
|
column_names = {}
|
23
|
-
for field in self._agent_type.
|
24
|
-
if len(field.
|
25
|
-
column_names[field.
|
22
|
+
for field in self._agent_type.fields:
|
23
|
+
if len(field.index_names) == 0:
|
24
|
+
column_names[field.field_id] = field.field_name
|
26
25
|
else:
|
27
|
-
column_names[field.
|
26
|
+
column_names[field.field_id] = f"{field.field_name}_({tuple(field.index_names)}, value)"
|
28
27
|
return column_names
|
29
28
|
|
30
|
-
def get_simple_column_mask(self) ->
|
29
|
+
def get_simple_column_mask(self) -> list[bool]:
|
31
30
|
"""Returns list of bool - where an entry is True if the output column with the same index is not complex"""
|
32
|
-
return [True if len(field.
|
31
|
+
return [True if len(field.index_names) == 0 else False for field in self._agent_type.fields]
|
33
32
|
|
34
|
-
def get_complex_column_ids(self) ->
|
33
|
+
def get_complex_column_ids(self) -> set[int]:
|
35
34
|
"""Returns set of IDs for complex columns, ignoring simple columns"""
|
36
|
-
return set([field.
|
35
|
+
return set([field.field_id for field in self._agent_type.fields if len(field.index_names) > 0])
|
37
36
|
|
38
37
|
def get_column_name_for_id(self, column_index: int) -> Union[str, None]:
|
39
38
|
"""Returns name of column by given `column_index` or None, if column is not present"""
|
40
|
-
if 0 <= column_index < len(self._agent_type.
|
41
|
-
return self._agent_type.
|
39
|
+
if 0 <= column_index < len(self._agent_type.fields):
|
40
|
+
return self._agent_type.fields[column_index].field_name
|
42
41
|
else:
|
43
42
|
return None
|
44
43
|
|
45
|
-
def get_inner_columns(self, column_index: int) ->
|
44
|
+
def get_inner_columns(self, column_index: int) -> tuple[str, ...]:
|
46
45
|
"""Returns tuple of inner column names for complex column with given `column_index`"""
|
47
|
-
return tuple(self._agent_type.
|
46
|
+
return tuple(self._agent_type.fields[column_index].index_names)
|
48
47
|
|
49
48
|
def get_class_name(self) -> str:
|
50
49
|
"""Returns name of class of wrapped agent type"""
|
51
|
-
return self._agent_type.
|
50
|
+
return self._agent_type.class_name
|
52
51
|
|
53
52
|
|
54
53
|
class AgentTypeLog:
|
@@ -57,11 +56,11 @@ class AgentTypeLog:
|
|
57
56
|
_ERR_AGENT_TYPE_MISSING = "Requested AgentType `{}` not found."
|
58
57
|
_ERR_DOUBLE_DEFINITION = "Just one definition allowed per AgentType. Found multiple for {}. File might be corrupt."
|
59
58
|
|
60
|
-
def __init__(self, requested_agents:
|
59
|
+
def __init__(self, requested_agents: list[str]) -> None:
|
61
60
|
self._requested_agents = [agent.upper() for agent in requested_agents] if requested_agents else None
|
62
61
|
self._requested_agent_types = {}
|
63
62
|
|
64
|
-
def update_agents(self, new_types:
|
63
|
+
def update_agents(self, new_types: dict[str, Output.AgentType]) -> None:
|
65
64
|
"""Saves new `agent_types` (if any) contained in given `output` if requested for extraction"""
|
66
65
|
if new_types:
|
67
66
|
if self._requested_agents:
|