shepherd-core 2023.12.1__py3-none-any.whl → 2024.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shepherd_core/__init__.py +5 -4
- shepherd_core/calibration_hw_def.py +9 -1
- shepherd_core/commons.py +2 -0
- shepherd_core/data_models/__init__.py +11 -0
- shepherd_core/data_models/base/__init__.py +4 -1
- shepherd_core/data_models/base/cal_measurement.py +18 -6
- shepherd_core/data_models/base/calibration.py +41 -16
- shepherd_core/data_models/base/content.py +20 -5
- shepherd_core/data_models/base/shepherd.py +23 -12
- shepherd_core/data_models/base/timezone.py +5 -0
- shepherd_core/data_models/base/wrapper.py +3 -3
- shepherd_core/data_models/content/__init__.py +5 -4
- shepherd_core/data_models/content/_external_fixtures.yaml +32 -16
- shepherd_core/data_models/content/energy_environment.py +7 -5
- shepherd_core/data_models/content/energy_environment_fixture.yaml +3 -0
- shepherd_core/data_models/content/firmware.py +12 -5
- shepherd_core/data_models/content/firmware_datatype.py +7 -0
- shepherd_core/data_models/content/virtual_harvester.py +25 -20
- shepherd_core/data_models/content/virtual_harvester_fixture.yaml +1 -0
- shepherd_core/data_models/content/virtual_source.py +40 -23
- shepherd_core/data_models/content/virtual_source_fixture.yaml +1 -0
- shepherd_core/data_models/experiment/__init__.py +5 -4
- shepherd_core/data_models/experiment/experiment.py +16 -15
- shepherd_core/data_models/experiment/observer_features.py +18 -12
- shepherd_core/data_models/experiment/target_config.py +11 -7
- shepherd_core/data_models/readme.md +88 -0
- shepherd_core/data_models/task/__init__.py +10 -3
- shepherd_core/data_models/task/emulation.py +9 -6
- shepherd_core/data_models/task/firmware_mod.py +4 -2
- shepherd_core/data_models/task/harvest.py +5 -4
- shepherd_core/data_models/task/observer_tasks.py +4 -2
- shepherd_core/data_models/task/programming.py +3 -1
- shepherd_core/data_models/task/testbed_tasks.py +10 -4
- shepherd_core/data_models/testbed/__init__.py +5 -2
- shepherd_core/data_models/testbed/cape.py +8 -6
- shepherd_core/data_models/testbed/gpio.py +11 -9
- shepherd_core/data_models/testbed/mcu.py +10 -10
- shepherd_core/data_models/testbed/observer.py +10 -5
- shepherd_core/data_models/testbed/observer_fixture.yaml +23 -22
- shepherd_core/data_models/testbed/target.py +5 -3
- shepherd_core/data_models/testbed/target_fixture.yaml +11 -11
- shepherd_core/data_models/testbed/testbed.py +6 -3
- shepherd_core/decoder_waveform/__init__.py +2 -0
- shepherd_core/decoder_waveform/uart.py +44 -25
- shepherd_core/fw_tools/__init__.py +2 -0
- shepherd_core/fw_tools/converter.py +20 -9
- shepherd_core/fw_tools/converter_elf.py +3 -0
- shepherd_core/fw_tools/patcher.py +16 -4
- shepherd_core/fw_tools/validation.py +25 -5
- shepherd_core/inventory/__init__.py +66 -6
- shepherd_core/inventory/python.py +4 -0
- shepherd_core/inventory/system.py +13 -1
- shepherd_core/inventory/target.py +4 -0
- shepherd_core/logger.py +5 -0
- shepherd_core/reader.py +44 -26
- shepherd_core/testbed_client/__init__.py +2 -0
- shepherd_core/testbed_client/cache_path.py +17 -0
- shepherd_core/testbed_client/client.py +14 -8
- shepherd_core/testbed_client/fixtures.py +30 -11
- shepherd_core/testbed_client/user_model.py +13 -6
- shepherd_core/vsource/__init__.py +2 -0
- shepherd_core/vsource/virtual_converter_model.py +11 -4
- shepherd_core/vsource/virtual_harvester_model.py +8 -1
- shepherd_core/vsource/virtual_source_model.py +10 -5
- shepherd_core/writer.py +28 -20
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/METADATA +50 -34
- shepherd_core-2024.4.2.dist-info/RECORD +75 -0
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/WHEEL +1 -1
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/top_level.txt +0 -1
- shepherd_core-2023.12.1.dist-info/RECORD +0 -117
- tests/__init__.py +0 -0
- tests/conftest.py +0 -64
- tests/data_models/__init__.py +0 -0
- tests/data_models/conftest.py +0 -14
- tests/data_models/example_cal_data.yaml +0 -31
- tests/data_models/example_cal_data_faulty.yaml +0 -29
- tests/data_models/example_cal_meas.yaml +0 -178
- tests/data_models/example_cal_meas_faulty1.yaml +0 -142
- tests/data_models/example_cal_meas_faulty2.yaml +0 -136
- tests/data_models/example_config_emulator.yaml +0 -41
- tests/data_models/example_config_experiment.yaml +0 -16
- tests/data_models/example_config_experiment_alternative.yaml +0 -14
- tests/data_models/example_config_harvester.yaml +0 -15
- tests/data_models/example_config_testbed.yaml +0 -26
- tests/data_models/example_config_virtsource.yaml +0 -78
- tests/data_models/test_base_models.py +0 -205
- tests/data_models/test_content_fixtures.py +0 -41
- tests/data_models/test_content_models.py +0 -282
- tests/data_models/test_examples.py +0 -48
- tests/data_models/test_experiment_models.py +0 -277
- tests/data_models/test_task_generation.py +0 -52
- tests/data_models/test_task_models.py +0 -131
- tests/data_models/test_testbed_fixtures.py +0 -47
- tests/data_models/test_testbed_models.py +0 -187
- tests/decoder_waveform/__init__.py +0 -0
- tests/decoder_waveform/test_decoder.py +0 -34
- tests/fw_tools/__init__.py +0 -0
- tests/fw_tools/conftest.py +0 -5
- tests/fw_tools/test_converter.py +0 -76
- tests/fw_tools/test_patcher.py +0 -66
- tests/fw_tools/test_validation.py +0 -56
- tests/inventory/__init__.py +0 -0
- tests/inventory/test_inventory.py +0 -20
- tests/test_cal_hw.py +0 -34
- tests/test_examples.py +0 -40
- tests/test_logger.py +0 -15
- tests/test_reader.py +0 -283
- tests/test_writer.py +0 -169
- tests/testbed_client/__init__.py +0 -0
- tests/vsource/__init__.py +0 -0
- tests/vsource/conftest.py +0 -49
- tests/vsource/test_converter.py +0 -161
- tests/vsource/test_harvester.py +0 -73
- tests/vsource/test_z.py +0 -5
- /shepherd_core/data_models/{doc_virtual_source.py → virtual_source_doc.txt} +0 -0
- {shepherd_core-2023.12.1.dist-info → shepherd_core-2024.4.2.dist-info}/zip-safe +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
"""Config for testbed experiments."""
|
|
2
|
+
|
|
1
3
|
from datetime import datetime
|
|
2
4
|
from datetime import timedelta
|
|
3
5
|
from typing import List
|
|
4
6
|
from typing import Optional
|
|
7
|
+
from typing import Union
|
|
8
|
+
from uuid import uuid4
|
|
5
9
|
|
|
6
|
-
from pydantic import
|
|
10
|
+
from pydantic import UUID4
|
|
7
11
|
from pydantic import Field
|
|
8
12
|
from pydantic import model_validator
|
|
9
13
|
from typing_extensions import Annotated
|
|
@@ -12,7 +16,6 @@ from typing_extensions import Self
|
|
|
12
16
|
from ..base.content import IdInt
|
|
13
17
|
from ..base.content import NameStr
|
|
14
18
|
from ..base.content import SafeStr
|
|
15
|
-
from ..base.content import id_default
|
|
16
19
|
from ..base.shepherd import ShpModel
|
|
17
20
|
from ..testbed.target import Target
|
|
18
21
|
from ..testbed.testbed import Testbed
|
|
@@ -21,16 +24,13 @@ from .target_config import TargetConfig
|
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
class Experiment(ShpModel, title="Config of an Experiment"):
|
|
24
|
-
"""
|
|
25
|
-
emulating Energy Environments for Target Nodes
|
|
26
|
-
"""
|
|
27
|
+
"""Config for experiments on the testbed emulating energy environments for target nodes."""
|
|
27
28
|
|
|
28
29
|
# General Properties
|
|
29
|
-
id:
|
|
30
|
-
|
|
31
|
-
default_factory=id_default,
|
|
32
|
-
)
|
|
30
|
+
# id: UUID4 ... # TODO: db-migration - temp fix for documentation
|
|
31
|
+
id: Union[UUID4, int] = Field(default_factory=uuid4)
|
|
33
32
|
# ⤷ TODO: automatic ID is problematic for identification by hash
|
|
33
|
+
|
|
34
34
|
name: NameStr
|
|
35
35
|
description: Annotated[
|
|
36
36
|
Optional[SafeStr], Field(description="Required for public instances")
|
|
@@ -38,12 +38,12 @@ class Experiment(ShpModel, title="Config of an Experiment"):
|
|
|
38
38
|
comment: Optional[SafeStr] = None
|
|
39
39
|
created: datetime = Field(default_factory=datetime.now)
|
|
40
40
|
|
|
41
|
-
# Ownership & Access
|
|
42
|
-
owner_id: Optional[IdInt] =
|
|
41
|
+
# Ownership & Access
|
|
42
|
+
owner_id: Optional[IdInt] = None
|
|
43
43
|
|
|
44
44
|
# feedback
|
|
45
|
-
email_results:
|
|
46
|
-
|
|
45
|
+
email_results: bool = False
|
|
46
|
+
|
|
47
47
|
sys_logging: SystemLogging = SystemLogging(dmesg=True, ptp=True, shepherd=True)
|
|
48
48
|
|
|
49
49
|
# schedule
|
|
@@ -90,7 +90,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
|
|
|
90
90
|
obs_ids = [testbed.get_observer(_id).id for _id in target_ids]
|
|
91
91
|
if len(target_ids) > len(set(obs_ids)):
|
|
92
92
|
raise ValueError(
|
|
93
|
-
"Observer used more than once in Experiment -> only 1 target per observer!"
|
|
93
|
+
"Observer is used more than once in Experiment -> only 1 target per observer!"
|
|
94
94
|
)
|
|
95
95
|
|
|
96
96
|
def get_target_ids(self) -> list:
|
|
@@ -101,4 +101,5 @@ class Experiment(ShpModel, title="Config of an Experiment"):
|
|
|
101
101
|
if target_id in _config.target_IDs:
|
|
102
102
|
return _config
|
|
103
103
|
# gets already caught in target_config - but keep:
|
|
104
|
-
|
|
104
|
+
msg = f"Target-ID {target_id} was not found in Experiment '{self.name}'"
|
|
105
|
+
raise ValueError(msg)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Configs for observer features like gpio- & power-tracing."""
|
|
2
|
+
|
|
1
3
|
from datetime import timedelta
|
|
2
4
|
from enum import Enum
|
|
3
5
|
from typing import List
|
|
@@ -15,7 +17,8 @@ from ..testbed.gpio import GPIO
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class PowerTracing(ShpModel, title="Config for Power-Tracing"):
|
|
18
|
-
"""Configuration for recording the Power-Consumption of the Target Nodes
|
|
20
|
+
"""Configuration for recording the Power-Consumption of the Target Nodes.
|
|
21
|
+
|
|
19
22
|
TODO: postprocessing not implemented ATM
|
|
20
23
|
"""
|
|
21
24
|
|
|
@@ -45,12 +48,13 @@ class PowerTracing(ShpModel, title="Config for Power-Tracing"):
|
|
|
45
48
|
if not self.calculate_power and discard_all:
|
|
46
49
|
raise ValueError("Error in config -> tracing enabled, but output gets discarded")
|
|
47
50
|
if self.calculate_power:
|
|
48
|
-
raise
|
|
51
|
+
raise NotImplementedError("postprocessing not implemented ATM")
|
|
49
52
|
return self
|
|
50
53
|
|
|
51
54
|
|
|
52
55
|
class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
|
|
53
|
-
"""Configuration for recording the GPIO-Output of the Target Nodes
|
|
56
|
+
"""Configuration for recording the GPIO-Output of the Target Nodes.
|
|
57
|
+
|
|
54
58
|
TODO: postprocessing not implemented ATM
|
|
55
59
|
"""
|
|
56
60
|
|
|
@@ -66,7 +70,7 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
|
|
|
66
70
|
|
|
67
71
|
# post-processing,
|
|
68
72
|
uart_decode: bool = False
|
|
69
|
-
#
|
|
73
|
+
# TODO: quickfix - uart-log currently done online in userspace
|
|
70
74
|
# NOTE: gpio-tracing currently shows rather big - but rare - "blind" windows (~1-4us)
|
|
71
75
|
uart_pin: GPIO = GPIO(name="GPIO8")
|
|
72
76
|
uart_baudrate: Annotated[int, Field(ge=2_400, le=921_600)] = 115_200
|
|
@@ -84,13 +88,15 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
|
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
class GpioLevel(str, Enum):
|
|
91
|
+
"""Options for setting the gpio-level or state."""
|
|
92
|
+
|
|
87
93
|
low = "L"
|
|
88
94
|
high = "H"
|
|
89
95
|
toggle = "X" # TODO: not the smartest decision for writing a converter
|
|
90
96
|
|
|
91
97
|
|
|
92
98
|
class GpioEvent(ShpModel, title="Config for a GPIO-Event"):
|
|
93
|
-
"""Configuration for a single GPIO-Event (Actuation)"""
|
|
99
|
+
"""Configuration for a single GPIO-Event (Actuation)."""
|
|
94
100
|
|
|
95
101
|
delay: PositiveFloat
|
|
96
102
|
# ⤷ from start_time
|
|
@@ -104,7 +110,8 @@ class GpioEvent(ShpModel, title="Config for a GPIO-Event"):
|
|
|
104
110
|
@model_validator(mode="after")
|
|
105
111
|
def post_validation(self) -> Self:
|
|
106
112
|
if not self.gpio.user_controllable():
|
|
107
|
-
|
|
113
|
+
msg = f"GPIO '{self.gpio.name}' in actuation-event not controllable by user"
|
|
114
|
+
raise ValueError(msg)
|
|
108
115
|
return self
|
|
109
116
|
|
|
110
117
|
def get_events(self) -> np.ndarray:
|
|
@@ -113,11 +120,10 @@ class GpioEvent(ShpModel, title="Config for a GPIO-Event"):
|
|
|
113
120
|
|
|
114
121
|
|
|
115
122
|
class GpioActuation(ShpModel, title="Config for GPIO-Actuation"):
|
|
116
|
-
"""Configuration for a GPIO-Actuation-Sequence
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
"""
|
|
123
|
+
"""Configuration for a GPIO-Actuation-Sequence."""
|
|
124
|
+
|
|
125
|
+
# TODO: not implemented ATM - decide if pru control sys-gpio or
|
|
126
|
+
# TODO: not implemented ATM - reverses pru-gpio (preferred if possible)
|
|
121
127
|
|
|
122
128
|
events: Annotated[List[GpioEvent], Field(min_length=1, max_length=1024)]
|
|
123
129
|
|
|
@@ -126,7 +132,7 @@ class GpioActuation(ShpModel, title="Config for GPIO-Actuation"):
|
|
|
126
132
|
|
|
127
133
|
|
|
128
134
|
class SystemLogging(ShpModel, title="Config for System-Logging"):
|
|
129
|
-
"""Configuration for recording Debug-Output of the Observers System-Services"""
|
|
135
|
+
"""Configuration for recording Debug-Output of the Observers System-Services."""
|
|
130
136
|
|
|
131
137
|
dmesg: bool = True
|
|
132
138
|
ptp: bool = True
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Configuration related to Target Nodes (DuT)."""
|
|
2
|
+
|
|
1
3
|
from typing import List
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
@@ -19,7 +21,7 @@ from .observer_features import PowerTracing
|
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class TargetConfig(ShpModel, title="Target Config"):
|
|
22
|
-
"""Configuration
|
|
24
|
+
"""Configuration related to Target Nodes (DuT)."""
|
|
23
25
|
|
|
24
26
|
target_IDs: Annotated[List[IdInt], Field(min_length=1, max_length=128)]
|
|
25
27
|
custom_IDs: Optional[Annotated[List[IdInt16], Field(min_length=1, max_length=128)]] = None
|
|
@@ -44,7 +46,8 @@ class TargetConfig(ShpModel, title="Target Config"):
|
|
|
44
46
|
@model_validator(mode="after")
|
|
45
47
|
def post_validation(self) -> Self:
|
|
46
48
|
if not self.energy_env.valid:
|
|
47
|
-
|
|
49
|
+
msg = f"EnergyEnv '{self.energy_env.name}' for target must be valid"
|
|
50
|
+
raise ValueError(msg)
|
|
48
51
|
for _id in self.target_IDs:
|
|
49
52
|
target = Target(id=_id)
|
|
50
53
|
for mcu_num in [1, 2]:
|
|
@@ -56,24 +59,25 @@ class TargetConfig(ShpModel, title="Target Config"):
|
|
|
56
59
|
fw_def = Firmware(name=tgt_mcu.fw_name_default)
|
|
57
60
|
# ⤷ this will raise if default is faulty
|
|
58
61
|
if tgt_mcu.id != fw_def.mcu.id:
|
|
59
|
-
|
|
62
|
+
msg = (
|
|
60
63
|
f"Default-Firmware for MCU{mcu_num} of Target-ID '{target.id}' "
|
|
61
64
|
f"(={fw_def.mcu.name}) "
|
|
62
65
|
f"is incompatible (={tgt_mcu.name})"
|
|
63
66
|
)
|
|
67
|
+
raise ValueError(msg)
|
|
64
68
|
if has_fw and has_mcu and val_fw.mcu.id != tgt_mcu.id:
|
|
65
|
-
|
|
69
|
+
msg = (
|
|
66
70
|
f"Firmware{mcu_num} for MCU of Target-ID '{target.id}' "
|
|
67
71
|
f"(={val_fw.mcu.name}) "
|
|
68
72
|
f"is incompatible (={tgt_mcu.name})"
|
|
69
73
|
)
|
|
74
|
+
raise ValueError(msg)
|
|
70
75
|
|
|
71
76
|
c_ids = self.custom_IDs
|
|
72
77
|
t_ids = self.target_IDs
|
|
73
78
|
if c_ids is not None and (len(set(c_ids)) < len(set(t_ids))):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
79
|
+
msg = f"Provided custom IDs {c_ids} not enough to cover target range {t_ids}"
|
|
80
|
+
raise ValueError(msg)
|
|
77
81
|
# TODO: if custom ids present, firmware must be ELF
|
|
78
82
|
return self
|
|
79
83
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
## Datastructure
|
|
2
|
+
|
|
3
|
+
### experiment-structure
|
|
4
|
+
|
|
5
|
+
- basics
|
|
6
|
+
- scheduling
|
|
7
|
+
- **SystemLogger**
|
|
8
|
+
- ~~AuxPort~~
|
|
9
|
+
- TargetConfigs
|
|
10
|
+
- Programming
|
|
11
|
+
- PowerTracer
|
|
12
|
+
- GpioTracer
|
|
13
|
+
- GpioActuator
|
|
14
|
+
- vSrc
|
|
15
|
+
- vHarvester
|
|
16
|
+
- [vConverter]
|
|
17
|
+
|
|
18
|
+
## TODO
|
|
19
|
+
|
|
20
|
+
- establish internal variables ``_var``
|
|
21
|
+
- descriptions to parameters (docstring on sub-models)
|
|
22
|
+
- @kai
|
|
23
|
+
- firmwares
|
|
24
|
+
- programmer-ports determine IC
|
|
25
|
+
- memory read - family-code, or write factory
|
|
26
|
+
- when tracing v_intermediate, also this current, or output?
|
|
27
|
+
- ``objcopy -O ihex input.elf output.hex``
|
|
28
|
+
- ``-S`` will strip useless sections
|
|
29
|
+
- ``-I ihex -O elf32-littlearm`` for reversal is also possible
|
|
30
|
+
- TODO: try to find ``objdump -t [elf_file] | grep SHEPHERD_NODE_ID``
|
|
31
|
+
|
|
32
|
+
- title in class might be rubbish
|
|
33
|
+
|
|
34
|
+
- Warn about tricky syntax
|
|
35
|
+
- defining sub-data-models in an experiment in python:
|
|
36
|
+
- experiments-default: don't mention argument in init
|
|
37
|
+
- trace-default: init trace with empty argument list
|
|
38
|
+
- disable: init with "None"
|
|
39
|
+
- defining experiment in yaml:
|
|
40
|
+
- experiment-default: don't mention it
|
|
41
|
+
- trace-default: NOT POSSIBLE, right?
|
|
42
|
+
- disable: init with "null" OR just mention parameter but keep it empty
|
|
43
|
+
|
|
44
|
+
### add documentation after creation ⇾ avoid Field()
|
|
45
|
+
|
|
46
|
+
- these do not work
|
|
47
|
+
|
|
48
|
+
```Python
|
|
49
|
+
from pydantic import Field
|
|
50
|
+
from shepherd_core.data_models import ShpModel
|
|
51
|
+
|
|
52
|
+
class Experiment(ShpModel, title="Config of an Experiment"):
|
|
53
|
+
def __init__(self): # test to add doc after creation ⇾ to avoid Field()
|
|
54
|
+
super().__init__()
|
|
55
|
+
self.Config.fields["output_path"].description = "test description"
|
|
56
|
+
class Config:
|
|
57
|
+
fields: dict[str, Field] = {}
|
|
58
|
+
fields["output_path"] = Field(description="test description")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### simplify init of pydantic-class
|
|
62
|
+
|
|
63
|
+
What I want: init a fixture-class with Class("name") or Class(ID) instead of Class(name="name")
|
|
64
|
+
|
|
65
|
+
What does not work:
|
|
66
|
+
|
|
67
|
+
```Python
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from typing import Union
|
|
70
|
+
from pydantic import root_validator
|
|
71
|
+
from shepherd_core.data_models import Fixture
|
|
72
|
+
from shepherd_core.data_models import ShpModel
|
|
73
|
+
|
|
74
|
+
fixtures = Fixture(Path("fix.yaml"), "testbed.target")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Target(ShpModel, title="Target Node (DuT)"):
|
|
78
|
+
@root_validator(pre=True)
|
|
79
|
+
def query_database(cls, values: Union[dict, str, int]):
|
|
80
|
+
values = fixtures.try_completing_model(values)
|
|
81
|
+
values, chain = fixtures.try_inheritance(values)
|
|
82
|
+
return values
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
what might work:
|
|
86
|
+
|
|
87
|
+
- writing a custom __init__() and putting lookup() there
|
|
88
|
+
- wait for V2
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""Module for task-related data-modules.
|
|
2
|
+
|
|
3
|
+
These models import externally from all other model-modules!
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
from pathlib import Path
|
|
2
7
|
from typing import List
|
|
3
8
|
from typing import Optional
|
|
@@ -33,7 +38,9 @@ __all__ = [
|
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = None) -> Wrapper:
|
|
36
|
-
"""
|
|
41
|
+
"""Open file and extract tasks.
|
|
42
|
+
|
|
43
|
+
- Open file (from Path or str of Path)
|
|
37
44
|
- wraps task-model
|
|
38
45
|
- and if it's an TestbedTasks it will extract the correct ObserverTask
|
|
39
46
|
"""
|
|
@@ -50,7 +57,7 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
|
|
|
50
57
|
parameters=config.model_dump(),
|
|
51
58
|
)
|
|
52
59
|
else:
|
|
53
|
-
raise
|
|
60
|
+
raise TypeError("had unknown input: %s", type(config))
|
|
54
61
|
|
|
55
62
|
if shp_wrap.datatype == TestbedTasks.__name__:
|
|
56
63
|
if observer is None:
|
|
@@ -72,7 +79,7 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
|
|
|
72
79
|
|
|
73
80
|
|
|
74
81
|
def extract_tasks(shp_wrap: Wrapper, *, no_task_sets: bool = True) -> List[ShpModel]:
|
|
75
|
-
""" """
|
|
82
|
+
"""Make the individual task-sets usable for each observer."""
|
|
76
83
|
if shp_wrap.datatype == ObserverTasks.__name__:
|
|
77
84
|
obt = ObserverTasks(**shp_wrap.parameters)
|
|
78
85
|
content = obt.get_tasks()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Configuration for the Observer in Emulation-Mode."""
|
|
2
|
+
|
|
1
3
|
import copy
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
from datetime import timedelta
|
|
@@ -26,10 +28,10 @@ from ..testbed.cape import TargetPort
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class Compression(str, Enum):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
"""Options for choosing a dataset-compression."""
|
|
32
|
+
|
|
33
|
+
lzf = default = "lzf" # not native hdf5
|
|
34
|
+
gzip1 = gzip = 1 # higher compr & load
|
|
33
35
|
null = None
|
|
34
36
|
# NOTE: changed to lzf as BBB needs every straw it can get
|
|
35
37
|
|
|
@@ -39,7 +41,7 @@ c_translate = {"lzf": "lzf", "1": 1, "None": None, None: None}
|
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
class EmulationTask(ShpModel):
|
|
42
|
-
"""Configuration for the Observer in Emulation-Mode"""
|
|
44
|
+
"""Configuration for the Observer in Emulation-Mode."""
|
|
43
45
|
|
|
44
46
|
# General config
|
|
45
47
|
input_path: Path
|
|
@@ -111,10 +113,11 @@ class EmulationTask(ShpModel):
|
|
|
111
113
|
has_time = self.time_start is not None
|
|
112
114
|
time_now = datetime.now().astimezone()
|
|
113
115
|
if has_time and self.time_start < time_now:
|
|
114
|
-
|
|
116
|
+
msg = (
|
|
115
117
|
"Start-Time for Emulation can't be in the past "
|
|
116
118
|
f"('{self.time_start}' vs '{time_now}'."
|
|
117
119
|
)
|
|
120
|
+
raise ValueError(msg)
|
|
118
121
|
if self.duration and self.duration.total_seconds() < 0:
|
|
119
122
|
raise ValueError("Task-Duration can't be negative.")
|
|
120
123
|
if isinstance(self.voltage_aux, str) and self.voltage_aux not in {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Config for Task that adds the custom ID to the firmware & stores it into a file."""
|
|
2
|
+
|
|
1
3
|
import copy
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Optional
|
|
@@ -24,7 +26,7 @@ from ..testbed.target import MCUPort
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class FirmwareModTask(ShpModel):
|
|
27
|
-
"""Config for Task that adds the custom ID to the firmware & stores it into a file"""
|
|
29
|
+
"""Config for Task that adds the custom ID to the firmware & stores it into a file."""
|
|
28
30
|
|
|
29
31
|
data: Union[FirmwareStr, Path]
|
|
30
32
|
data_type: FirmwareDType
|
|
@@ -79,7 +81,7 @@ class FirmwareModTask(ShpModel):
|
|
|
79
81
|
**kwargs: Unpack[TypedDict],
|
|
80
82
|
) -> Self:
|
|
81
83
|
if not isinstance(fw, Firmware):
|
|
82
|
-
raise
|
|
84
|
+
raise TypeError("fw-argument must be of type Firmware")
|
|
83
85
|
kwargs["data"] = fw.data
|
|
84
86
|
kwargs["data_type"] = fw.data_type
|
|
85
87
|
fw.compare_hash()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Config for the Observer in Harvest-Mode to record IV data from a harvesting-source."""
|
|
2
|
+
|
|
1
3
|
from datetime import datetime
|
|
2
4
|
from datetime import timedelta
|
|
3
5
|
from pathlib import Path
|
|
@@ -17,9 +19,7 @@ from .emulation import Compression
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class HarvestTask(ShpModel):
|
|
20
|
-
"""
|
|
21
|
-
Record IV data from a harvest-source
|
|
22
|
-
"""
|
|
22
|
+
"""Config for the Observer in Harvest-Mode to record IV data from a harvesting-source."""
|
|
23
23
|
|
|
24
24
|
# General config
|
|
25
25
|
output_path: Path
|
|
@@ -73,10 +73,11 @@ class HarvestTask(ShpModel):
|
|
|
73
73
|
has_time = self.time_start is not None
|
|
74
74
|
time_now = datetime.now().astimezone()
|
|
75
75
|
if has_time and self.time_start < time_now:
|
|
76
|
-
|
|
76
|
+
msg = (
|
|
77
77
|
"Start-Time for Emulation can't be in the past "
|
|
78
78
|
f"('{self.time_start}' vs '{time_now}'."
|
|
79
79
|
)
|
|
80
|
+
raise ValueError(msg)
|
|
80
81
|
if self.duration and self.duration.total_seconds() < 0:
|
|
81
82
|
raise ValueError("Task-Duration can't be negative.")
|
|
82
83
|
return self
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Collection of tasks for selected observer included in experiment."""
|
|
2
|
+
|
|
1
3
|
from datetime import datetime
|
|
2
4
|
from datetime import timedelta
|
|
3
5
|
from pathlib import Path
|
|
@@ -18,10 +20,10 @@ from .programming import ProgrammingTask
|
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class ObserverTasks(ShpModel):
|
|
21
|
-
"""Collection of tasks for selected observer included in experiment"""
|
|
23
|
+
"""Collection of tasks for selected observer included in experiment."""
|
|
22
24
|
|
|
23
25
|
observer: NameStr
|
|
24
|
-
owner_id: IdInt
|
|
26
|
+
owner_id: Optional[IdInt] # TODO: set to optional for now, shouldn't be
|
|
25
27
|
|
|
26
28
|
# PRE PROCESS
|
|
27
29
|
time_prep: datetime # TODO: should be optional
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Config for a Task programming the selected target."""
|
|
2
|
+
|
|
1
3
|
import copy
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Optional
|
|
@@ -21,7 +23,7 @@ from ..testbed.testbed import Testbed
|
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
class ProgrammingTask(ShpModel):
|
|
24
|
-
"""Config for Task programming the target
|
|
26
|
+
"""Config for a Task programming the selected target."""
|
|
25
27
|
|
|
26
28
|
firmware_file: Path
|
|
27
29
|
target_port: TargetPort = TargetPort.A
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
"""Collection of tasks for all observers included in experiment."""
|
|
2
|
+
|
|
1
3
|
from typing import List
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
4
|
-
from pydantic import EmailStr
|
|
5
6
|
from pydantic import Field
|
|
6
7
|
from pydantic import validate_call
|
|
7
8
|
from typing_extensions import Annotated
|
|
8
9
|
from typing_extensions import Self
|
|
9
10
|
|
|
11
|
+
from ..base.content import IdInt
|
|
10
12
|
from ..base.content import NameStr
|
|
11
13
|
from ..base.shepherd import ShpModel
|
|
12
14
|
from ..experiment.experiment import Experiment
|
|
@@ -15,13 +17,16 @@ from .observer_tasks import ObserverTasks
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class TestbedTasks(ShpModel):
|
|
18
|
-
"""Collection of tasks for all observers included in experiment"""
|
|
20
|
+
"""Collection of tasks for all observers included in experiment."""
|
|
19
21
|
|
|
20
22
|
name: NameStr
|
|
21
23
|
observer_tasks: Annotated[List[ObserverTasks], Field(min_length=1, max_length=128)]
|
|
22
24
|
|
|
23
25
|
# POST PROCESS
|
|
24
|
-
|
|
26
|
+
email_results: bool = False
|
|
27
|
+
owner_id: Optional[IdInt]
|
|
28
|
+
# TODO: had real email previously, does it really need these at all?
|
|
29
|
+
# DB stores experiment and knows when to email
|
|
25
30
|
|
|
26
31
|
@classmethod
|
|
27
32
|
@validate_call
|
|
@@ -34,7 +39,8 @@ class TestbedTasks(ShpModel):
|
|
|
34
39
|
return cls(
|
|
35
40
|
name=xp.name,
|
|
36
41
|
observer_tasks=obs_tasks,
|
|
37
|
-
|
|
42
|
+
email_results=xp.email_results,
|
|
43
|
+
owner_id=xp.owner_id,
|
|
38
44
|
)
|
|
39
45
|
|
|
40
46
|
def get_observer_tasks(self, observer: str) -> Optional[ObserverTasks]:
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""Module for testbed-related data-models.
|
|
2
|
+
|
|
3
|
+
These models import externally from: /base
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
from .cape import Cape
|
|
2
7
|
from .cape import TargetPort
|
|
3
8
|
from .gpio import GPIO
|
|
@@ -10,8 +15,6 @@ from .target import IdInt16
|
|
|
10
15
|
from .target import Target
|
|
11
16
|
from .testbed import Testbed
|
|
12
17
|
|
|
13
|
-
# these models import externally from: /base
|
|
14
|
-
|
|
15
18
|
__all__ = [
|
|
16
19
|
"Testbed",
|
|
17
20
|
"Observer",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""meta-data representation of a testbed-component (physical object)."""
|
|
2
|
+
|
|
1
3
|
from datetime import date
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
from enum import Enum
|
|
@@ -15,16 +17,16 @@ from ..base.shepherd import ShpModel
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class TargetPort(str, Enum):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
a = "A"
|
|
21
|
-
b = "B"
|
|
20
|
+
"""Options for choosing a target-port."""
|
|
21
|
+
|
|
22
|
+
A = a = "A"
|
|
23
|
+
B = b = "B"
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class Cape(ShpModel, title="Shepherd-Cape"):
|
|
25
|
-
"""meta-data representation of a testbed-component (physical object)"""
|
|
27
|
+
"""meta-data representation of a testbed-component (physical object)."""
|
|
26
28
|
|
|
27
|
-
id: IdInt
|
|
29
|
+
id: IdInt
|
|
28
30
|
name: NameStr
|
|
29
31
|
version: NameStr
|
|
30
32
|
description: SafeStr
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""meta-data representation of a testbed-component (physical object)."""
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
@@ -15,18 +17,17 @@ from ..base.shepherd import ShpModel
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class Direction(str, Enum):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
OUT = "OUT"
|
|
22
|
-
Bidirectional = "IO"
|
|
23
|
-
IO = "IO"
|
|
20
|
+
"""Options for pin-direction."""
|
|
21
|
+
|
|
22
|
+
Input = IN = "IN"
|
|
23
|
+
Output = OUT = "OUT"
|
|
24
|
+
Bidirectional = IO = "IO"
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class GPIO(ShpModel, title="GPIO of Observer Node"):
|
|
27
|
-
"""meta-data representation of a testbed-component"""
|
|
28
|
+
"""meta-data representation of a testbed-component."""
|
|
28
29
|
|
|
29
|
-
id: IdInt
|
|
30
|
+
id: IdInt
|
|
30
31
|
name: NameStr
|
|
31
32
|
description: Optional[SafeStr] = None
|
|
32
33
|
comment: Optional[SafeStr] = None
|
|
@@ -54,10 +55,11 @@ class GPIO(ShpModel, title="GPIO of Observer Node"):
|
|
|
54
55
|
no_pru = (self.reg_pru is None) or (self.pin_pru is None)
|
|
55
56
|
no_sys = (self.reg_sys is None) or (self.pin_sys is None)
|
|
56
57
|
if no_pru and no_sys:
|
|
57
|
-
|
|
58
|
+
msg = (
|
|
58
59
|
"GPIO-Instance is faulty -> "
|
|
59
60
|
f"it needs to use pru or sys, content: {self.model_dump()}"
|
|
60
61
|
)
|
|
62
|
+
raise ValueError(msg)
|
|
61
63
|
return self
|
|
62
64
|
|
|
63
65
|
def user_controllable(self) -> bool:
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""meta-data representation of a testbed-component (physical object)."""
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
@@ -13,20 +15,18 @@ from ..base.shepherd import ShpModel
|
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class ProgrammerProtocol(str, Enum):
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
sbw = "SBW"
|
|
20
|
-
JTAG = "JTAG"
|
|
21
|
-
|
|
22
|
-
UART = "UART"
|
|
23
|
-
uart = "UART"
|
|
18
|
+
"""Options regarding the programming-protocol."""
|
|
19
|
+
|
|
20
|
+
SWD = swd = "SWD"
|
|
21
|
+
SBW = sbw = "SBW"
|
|
22
|
+
JTAG = jtag = "JTAG"
|
|
23
|
+
UART = uart = "UART"
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class MCU(ShpModel, title="Microcontroller of the Target Node"):
|
|
27
|
-
"""meta-data representation of a testbed-component (physical object)"""
|
|
27
|
+
"""meta-data representation of a testbed-component (physical object)."""
|
|
28
28
|
|
|
29
|
-
id: IdInt
|
|
29
|
+
id: IdInt
|
|
30
30
|
name: NameStr
|
|
31
31
|
description: SafeStr
|
|
32
32
|
comment: Optional[SafeStr] = None
|