shepherd-core 2025.5.3__py3-none-any.whl → 2025.6.1__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/commons.py +3 -5
- shepherd_core/config.py +34 -0
- shepherd_core/data_models/base/calibration.py +13 -8
- shepherd_core/data_models/base/wrapper.py +4 -4
- shepherd_core/data_models/content/energy_environment.py +1 -1
- shepherd_core/data_models/content/firmware.py +10 -5
- shepherd_core/data_models/content/virtual_harvester.py +10 -10
- shepherd_core/data_models/content/virtual_source.py +36 -28
- shepherd_core/data_models/content/virtual_source_fixture.yaml +1 -1
- shepherd_core/data_models/experiment/experiment.py +28 -18
- shepherd_core/data_models/experiment/observer_features.py +30 -11
- shepherd_core/data_models/experiment/target_config.py +17 -7
- shepherd_core/data_models/task/emulation.py +38 -25
- shepherd_core/data_models/task/firmware_mod.py +1 -1
- shepherd_core/data_models/task/harvest.py +14 -13
- shepherd_core/data_models/task/observer_tasks.py +4 -3
- shepherd_core/data_models/task/programming.py +2 -2
- shepherd_core/data_models/task/testbed_tasks.py +4 -7
- shepherd_core/data_models/testbed/cape_fixture.yaml +1 -1
- shepherd_core/data_models/testbed/observer.py +1 -1
- shepherd_core/data_models/testbed/observer_fixture.yaml +2 -2
- shepherd_core/data_models/testbed/target.py +1 -1
- shepherd_core/data_models/testbed/target_fixture.old1 +1 -1
- shepherd_core/data_models/testbed/target_fixture.yaml +1 -1
- shepherd_core/data_models/testbed/testbed.py +8 -9
- shepherd_core/fw_tools/patcher.py +7 -8
- shepherd_core/inventory/system.py +1 -3
- shepherd_core/reader.py +9 -2
- shepherd_core/testbed_client/cache_path.py +1 -1
- shepherd_core/testbed_client/client_web.py +2 -2
- shepherd_core/version.py +1 -1
- shepherd_core/writer.py +2 -2
- {shepherd_core-2025.5.3.dist-info → shepherd_core-2025.6.1.dist-info}/METADATA +12 -12
- {shepherd_core-2025.5.3.dist-info → shepherd_core-2025.6.1.dist-info}/RECORD +37 -36
- {shepherd_core-2025.5.3.dist-info → shepherd_core-2025.6.1.dist-info}/WHEEL +1 -1
- {shepherd_core-2025.5.3.dist-info → shepherd_core-2025.6.1.dist-info}/top_level.txt +0 -0
- {shepherd_core-2025.5.3.dist-info → shepherd_core-2025.6.1.dist-info}/zip-safe +0 -0
|
@@ -25,6 +25,7 @@ from shepherd_core.data_models.experiment.observer_features import GpioTracing
|
|
|
25
25
|
from shepherd_core.data_models.experiment.observer_features import PowerTracing
|
|
26
26
|
from shepherd_core.data_models.experiment.observer_features import SystemLogging
|
|
27
27
|
from shepherd_core.data_models.experiment.observer_features import UartLogging
|
|
28
|
+
from shepherd_core.data_models.experiment.target_config import vsrc_neutral
|
|
28
29
|
from shepherd_core.data_models.testbed import Testbed
|
|
29
30
|
from shepherd_core.data_models.testbed.cape import TargetPort
|
|
30
31
|
from shepherd_core.logger import logger
|
|
@@ -49,46 +50,55 @@ class EmulationTask(ShpModel):
|
|
|
49
50
|
|
|
50
51
|
# General config
|
|
51
52
|
input_path: Path
|
|
52
|
-
|
|
53
|
+
""" ⤷ hdf5 file containing harvesting data"""
|
|
53
54
|
output_path: Optional[Path] = None
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
""" ⤷ dir- or file-path for storing the recorded data:
|
|
56
|
+
|
|
57
|
+
- providing a directory -> file is named emu_timestamp.h5
|
|
58
|
+
- for a complete path the filename is not changed except it exists and
|
|
59
|
+
overwrite is disabled -> emu#num.h5
|
|
60
|
+
TODO: should the output-path be mandatory?
|
|
61
|
+
"""
|
|
59
62
|
force_overwrite: bool = False
|
|
60
|
-
|
|
63
|
+
""" ⤷ Overwrite existing file"""
|
|
61
64
|
output_compression: Optional[Compression] = Compression.default
|
|
62
|
-
|
|
65
|
+
""" ⤷ should be lzf, 1 (gzip level 1) or None (order of recommendation)"""
|
|
63
66
|
time_start: Optional[datetime] = None
|
|
64
|
-
|
|
67
|
+
""" timestamp or unix epoch time, None = ASAP"""
|
|
65
68
|
duration: Optional[timedelta] = None
|
|
66
|
-
|
|
69
|
+
""" ⤷ Duration of recording in seconds, None = till EOF"""
|
|
67
70
|
abort_on_error: Annotated[bool, deprecated("has no effect")] = False
|
|
68
71
|
|
|
69
72
|
# emulation-specific
|
|
70
73
|
use_cal_default: bool = False
|
|
71
|
-
|
|
74
|
+
""" ⤷ Use default calibration values, skip loading from EEPROM"""
|
|
72
75
|
|
|
73
76
|
enable_io: bool = True
|
|
74
77
|
# TODO: add direction of pins! also it seems error-prone when only setting _tracing
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
""" ⤷ Switch the GPIO level converter to targets on/off
|
|
79
|
+
|
|
80
|
+
pre-req for sampling gpio / uart,
|
|
81
|
+
"""
|
|
77
82
|
io_port: TargetPort = TargetPort.A
|
|
78
|
-
|
|
83
|
+
""" ⤷ Either Port A or B that gets connected to IO"""
|
|
79
84
|
pwr_port: TargetPort = TargetPort.A
|
|
80
|
-
|
|
81
|
-
# the other port is aux
|
|
82
|
-
voltage_aux: Union[Annotated[float, Field(ge=0, le=4.5)], str] = 0
|
|
83
|
-
# ⤷ aux_voltage options:
|
|
84
|
-
# - 0-4.5 for specific const Voltage (0 V = disabled),
|
|
85
|
-
# - "buffer" will output intermediate voltage (storage cap of vsource),
|
|
86
|
-
# - "main" will mirror main target voltage
|
|
85
|
+
""" ⤷ selected port will be current-monitored
|
|
87
86
|
|
|
87
|
+
- main channel is nnected to virtual Source
|
|
88
|
+
- the other port is aux
|
|
89
|
+
"""
|
|
90
|
+
voltage_aux: Union[Annotated[float, Field(ge=0, le=4.5)], str] = 0
|
|
91
|
+
""" ⤷ aux_voltage options
|
|
92
|
+
- 0-4.5 for specific const Voltage (0 V = disabled),
|
|
93
|
+
- "buffer" will output intermediate voltage (storage cap of vsource),
|
|
94
|
+
- "main" will mirror main target voltage
|
|
95
|
+
"""
|
|
88
96
|
# sub-elements, could be partly moved to emulation
|
|
89
|
-
virtual_source: VirtualSourceConfig =
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
virtual_source: VirtualSourceConfig = vsrc_neutral
|
|
98
|
+
""" ⤷ Use the desired setting for the virtual source,
|
|
99
|
+
|
|
100
|
+
provide parameters or name like BQ25570
|
|
101
|
+
"""
|
|
92
102
|
|
|
93
103
|
power_tracing: Optional[PowerTracing] = PowerTracing()
|
|
94
104
|
gpio_tracing: Optional[GpioTracing] = GpioTracing()
|
|
@@ -97,7 +107,10 @@ class EmulationTask(ShpModel):
|
|
|
97
107
|
sys_logging: Optional[SystemLogging] = SystemLogging()
|
|
98
108
|
|
|
99
109
|
verbose: Annotated[int, Field(ge=0, le=4)] = 2
|
|
100
|
-
|
|
110
|
+
""" ⤷ 0=Errors, 1=Warnings, 2=Info, 3=Debug,
|
|
111
|
+
|
|
112
|
+
TODO: just bool now, systemwide
|
|
113
|
+
"""
|
|
101
114
|
|
|
102
115
|
@model_validator(mode="before")
|
|
103
116
|
@classmethod
|
|
@@ -34,7 +34,7 @@ class FirmwareModTask(ShpModel):
|
|
|
34
34
|
firmware_file: Path
|
|
35
35
|
|
|
36
36
|
verbose: Annotated[int, Field(ge=0, le=4)] = 2
|
|
37
|
-
|
|
37
|
+
""" ⤷ 0=Errors, 1=Warnings, 2=Info, 3=Debug"""
|
|
38
38
|
|
|
39
39
|
@model_validator(mode="after")
|
|
40
40
|
def post_validation(self) -> Self:
|
|
@@ -25,34 +25,35 @@ class HarvestTask(ShpModel):
|
|
|
25
25
|
|
|
26
26
|
# General config
|
|
27
27
|
output_path: Path
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
""" ⤷ dir- or file-path for storing the recorded data:
|
|
29
|
+
|
|
30
|
+
- providing a directory -> file is named hrv_timestamp.h5
|
|
31
|
+
- for a complete path the filename is not changed except it exists and
|
|
32
|
+
overwrite is disabled -> name#num.h5
|
|
33
|
+
"""
|
|
32
34
|
force_overwrite: bool = False
|
|
33
|
-
|
|
35
|
+
""" ⤷ Overwrite existing file"""
|
|
34
36
|
output_compression: Optional[Compression] = Compression.default
|
|
35
|
-
|
|
37
|
+
""" ⤷ should be 1 (level 1 gzip), lzf, or None (order of recommendation)"""
|
|
36
38
|
|
|
37
39
|
time_start: Optional[datetime] = None
|
|
38
|
-
|
|
40
|
+
""" timestamp or unix epoch time, None = ASAP"""
|
|
39
41
|
duration: Optional[timedelta] = None
|
|
40
|
-
|
|
42
|
+
""" ⤷ Duration of recording in seconds, None = till EOFSys"""
|
|
41
43
|
abort_on_error: Annotated[bool, deprecated("has no effect")] = False
|
|
42
44
|
|
|
43
45
|
# emulation-specific
|
|
44
46
|
use_cal_default: bool = False
|
|
45
|
-
|
|
47
|
+
""" ⤷ Use default calibration values, skip loading from EEPROM"""
|
|
46
48
|
|
|
47
49
|
virtual_harvester: VirtualHarvesterConfig = VirtualHarvesterConfig(name="mppt_opt")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
""" ⤷ Choose one of the predefined virtual harvesters or configure a new one
|
|
51
|
+
"""
|
|
51
52
|
power_tracing: PowerTracing = PowerTracing()
|
|
52
53
|
sys_logging: Optional[SystemLogging] = SystemLogging()
|
|
53
54
|
|
|
54
55
|
verbose: Annotated[int, Field(ge=0, le=4)] = 2
|
|
55
|
-
|
|
56
|
+
""" ⤷ 0=Errors, 1=Warnings, 2=Info, 3=Debug"""
|
|
56
57
|
|
|
57
58
|
# TODO: there is an unused DAC-Output patched to the harvesting-port
|
|
58
59
|
|
|
@@ -25,12 +25,10 @@ class ObserverTasks(ShpModel):
|
|
|
25
25
|
"""Collection of tasks for selected observer included in experiment."""
|
|
26
26
|
|
|
27
27
|
observer: NameStr
|
|
28
|
-
owner_id: Optional[IdInt] # TODO: set to optional for now, shouldn't be
|
|
29
28
|
|
|
30
29
|
# PRE PROCESS
|
|
31
30
|
time_prep: datetime # TODO: should be optional
|
|
32
31
|
root_path: Path
|
|
33
|
-
abort_on_error: Annotated[bool, deprecated("has no effect")] = False
|
|
34
32
|
|
|
35
33
|
# fw mod, store as hex-file and program
|
|
36
34
|
fw1_mod: Optional[FirmwareModTask] = None
|
|
@@ -41,6 +39,10 @@ class ObserverTasks(ShpModel):
|
|
|
41
39
|
# MAIN PROCESS
|
|
42
40
|
emulation: Optional[EmulationTask] = None
|
|
43
41
|
|
|
42
|
+
# deprecations, TODO: remove before public release
|
|
43
|
+
owner_id: Annotated[Optional[IdInt], deprecated("not needed anymore")] = None
|
|
44
|
+
abort_on_error: Annotated[bool, deprecated("has no effect")] = False
|
|
45
|
+
|
|
44
46
|
# post_copy / cleanup, Todo: could also just intake emuTask
|
|
45
47
|
# - delete firmwares
|
|
46
48
|
# - decode uart
|
|
@@ -69,7 +71,6 @@ class ObserverTasks(ShpModel):
|
|
|
69
71
|
|
|
70
72
|
return cls(
|
|
71
73
|
observer=obs.name,
|
|
72
|
-
owner_id=xp.owner_id,
|
|
73
74
|
time_prep=t_start - tb.prep_duration,
|
|
74
75
|
root_path=root_path,
|
|
75
76
|
fw1_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
|
|
@@ -29,7 +29,7 @@ class ProgrammingTask(ShpModel):
|
|
|
29
29
|
target_port: TargetPort = TargetPort.A
|
|
30
30
|
mcu_port: MCUPort = 1
|
|
31
31
|
mcu_type: SafeStr
|
|
32
|
-
|
|
32
|
+
""" ⤷ must be either "nrf52" or "msp430" ATM, TODO: clean xp to tasks"""
|
|
33
33
|
voltage: Annotated[float, Field(ge=1, lt=5)] = 3
|
|
34
34
|
datarate: Annotated[int, Field(gt=0, le=1_000_000)] = 200_000
|
|
35
35
|
protocol: ProgrammerProtocol
|
|
@@ -38,7 +38,7 @@ class ProgrammingTask(ShpModel):
|
|
|
38
38
|
simulate: bool = False
|
|
39
39
|
|
|
40
40
|
verbose: Annotated[int, Field(ge=0, le=4)] = 2
|
|
41
|
-
|
|
41
|
+
""" ⤷ 0=Errors, 1=Warnings, 2=Info, 3=Debug"""
|
|
42
42
|
|
|
43
43
|
@model_validator(mode="after")
|
|
44
44
|
def post_validation(self) -> Self:
|
|
@@ -7,6 +7,7 @@ from typing import Optional
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from pydantic import validate_call
|
|
9
9
|
from typing_extensions import Self
|
|
10
|
+
from typing_extensions import deprecated
|
|
10
11
|
|
|
11
12
|
from shepherd_core.data_models.base.content import IdInt
|
|
12
13
|
from shepherd_core.data_models.base.content import NameStr
|
|
@@ -23,11 +24,9 @@ class TestbedTasks(ShpModel):
|
|
|
23
24
|
name: NameStr
|
|
24
25
|
observer_tasks: Annotated[list[ObserverTasks], Field(min_length=1, max_length=128)]
|
|
25
26
|
|
|
26
|
-
#
|
|
27
|
-
email_results: bool = False
|
|
28
|
-
owner_id: Optional[IdInt]
|
|
29
|
-
# TODO: had real email previously, does it really need these at all?
|
|
30
|
-
# DB stores experiment and knows when to email
|
|
27
|
+
# deprecated, TODO: remove before public release
|
|
28
|
+
email_results: Annotated[Optional[bool], deprecated("not needed anymore")] = False
|
|
29
|
+
owner_id: Annotated[Optional[IdInt], deprecated("not needed anymore")] = None
|
|
31
30
|
|
|
32
31
|
@classmethod
|
|
33
32
|
@validate_call
|
|
@@ -41,8 +40,6 @@ class TestbedTasks(ShpModel):
|
|
|
41
40
|
return cls(
|
|
42
41
|
name=xp.name,
|
|
43
42
|
observer_tasks=obs_tasks,
|
|
44
|
-
email_results=xp.email_results,
|
|
45
|
-
owner_id=xp.owner_id,
|
|
46
43
|
)
|
|
47
44
|
|
|
48
45
|
def get_observer_tasks(self, observer: str) -> Optional[ObserverTasks]:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
|
|
3
3
|
# more human-readable test-protocol @
|
|
4
|
-
# https://github.com/orgua/
|
|
4
|
+
# https://github.com/orgua/shepherd-v2-planning/blob/main/doc_testbed/Cape_pre-deployment-tests.xlsx
|
|
5
5
|
- datatype: cape
|
|
6
6
|
parameters:
|
|
7
7
|
id: 1270051
|
|
@@ -43,7 +43,7 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
|
|
|
43
43
|
|
|
44
44
|
latitude: Annotated[float, Field(ge=-90, le=90)] = 51.026573
|
|
45
45
|
longitude: Annotated[float, Field(ge=-180, le=180)] = 13.723291
|
|
46
|
-
|
|
46
|
+
""" ⤷ cfaed-floor"""
|
|
47
47
|
|
|
48
48
|
active: bool = True
|
|
49
49
|
cape: Optional[Cape] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
# observer-cape-target-relation: https://github.com/orgua/
|
|
3
|
-
# network data: https://github.com/orgua/
|
|
2
|
+
# observer-cape-target-relation: https://github.com/orgua/shepherd-v2-planning/blob/main/doc_testbed/Cape_pre-deployment-tests.xlsx
|
|
3
|
+
# network data: https://github.com/orgua/shepherd-v2-planning/blob/main/doc_testbed/ethernet_MAC_addresses_bbones.ods
|
|
4
4
|
- datatype: observer
|
|
5
5
|
parameters:
|
|
6
6
|
id: 0
|
|
@@ -36,7 +36,7 @@ class Target(ShpModel, title="Target Node (DuT)"):
|
|
|
36
36
|
created: datetime = Field(default_factory=datetime.now)
|
|
37
37
|
|
|
38
38
|
testbed_id: Optional[IdInt16] = None
|
|
39
|
-
|
|
39
|
+
""" ⤷ is derived from ID (targets are still selected by id!)"""
|
|
40
40
|
mcu1: Union[MCU, NameStr]
|
|
41
41
|
mcu2: Union[MCU, NameStr, None] = None
|
|
42
42
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
# more human-readable test-protocol @
|
|
3
|
-
# https://github.com/orgua/
|
|
3
|
+
# https://github.com/orgua/shepherd-v2-planning/blob/main/doc_testbed/Target_pre-deployment-tests.xlsx
|
|
4
4
|
- datatype: target
|
|
5
5
|
parameters:
|
|
6
6
|
id: 6 # Outer ID - selected by user for XP - can be rearranged
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
# more human-readable test-protocol @
|
|
3
|
-
# https://github.com/orgua/
|
|
3
|
+
# https://github.com/orgua/shepherd-v2-planning/blob/main/doc_testbed/Target_pre-deployment-tests.xlsx
|
|
4
4
|
- datatype: target
|
|
5
5
|
parameters:
|
|
6
6
|
id: 2 # Outer ID - selected by user for XP - can be rearranged
|
|
@@ -11,15 +11,17 @@ from pydantic import HttpUrl
|
|
|
11
11
|
from pydantic import model_validator
|
|
12
12
|
from typing_extensions import Self
|
|
13
13
|
|
|
14
|
+
from shepherd_core.config import config
|
|
14
15
|
from shepherd_core.data_models.base.content import IdInt
|
|
15
16
|
from shepherd_core.data_models.base.content import NameStr
|
|
16
17
|
from shepherd_core.data_models.base.content import SafeStr
|
|
17
18
|
from shepherd_core.data_models.base.shepherd import ShpModel
|
|
18
|
-
from shepherd_core.logger import logger
|
|
19
19
|
from shepherd_core.testbed_client import tb_client
|
|
20
20
|
|
|
21
21
|
from .observer import Observer
|
|
22
22
|
|
|
23
|
+
duration_5min = timedelta(minutes=5)
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
class Testbed(ShpModel):
|
|
25
27
|
"""meta-data representation of a testbed-component (physical object)."""
|
|
@@ -36,21 +38,18 @@ class Testbed(ShpModel):
|
|
|
36
38
|
shared_storage: bool = True
|
|
37
39
|
data_on_server: Path
|
|
38
40
|
data_on_observer: Path
|
|
39
|
-
|
|
41
|
+
""" ⤷ storage layout: root_path/content_type/group/owner/[object]"""
|
|
42
|
+
# TODO: we might need individual paths for experiments & content
|
|
40
43
|
|
|
41
|
-
prep_duration: timedelta =
|
|
44
|
+
prep_duration: timedelta = duration_5min
|
|
42
45
|
# TODO: one BBone is currently time-keeper
|
|
43
46
|
|
|
44
47
|
@model_validator(mode="before")
|
|
45
48
|
@classmethod
|
|
46
49
|
def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
47
|
-
# allow instantiating an empty Testbed
|
|
48
|
-
# -> query the first (and only) entry of client
|
|
50
|
+
# allow instantiating an empty Testbed, take default in config
|
|
49
51
|
if len(values) == 0:
|
|
50
|
-
|
|
51
|
-
if len(ids) > 1:
|
|
52
|
-
logger.warning("More than one testbed defined?!?")
|
|
53
|
-
values = {"id": ids[0]}
|
|
52
|
+
values = {"name": config.TESTBED}
|
|
54
53
|
|
|
55
54
|
values, _ = tb_client.try_completing_model(cls.__name__, values)
|
|
56
55
|
return values
|
|
@@ -7,8 +7,7 @@ from typing import Optional
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from pydantic import validate_call
|
|
9
9
|
|
|
10
|
-
from shepherd_core.
|
|
11
|
-
from shepherd_core.commons import UID_SIZE
|
|
10
|
+
from shepherd_core.config import config
|
|
12
11
|
from shepherd_core.logger import logger
|
|
13
12
|
|
|
14
13
|
from .validation import is_elf
|
|
@@ -50,7 +49,7 @@ def find_symbol(file_elf: Path, symbol: str) -> bool:
|
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
@validate_call
|
|
53
|
-
def read_symbol(file_elf: Path, symbol: str, length: int
|
|
52
|
+
def read_symbol(file_elf: Path, symbol: str, length: int) -> Optional[int]:
|
|
54
53
|
"""Read value of symbol in ELF-File.
|
|
55
54
|
|
|
56
55
|
Will be interpreted as int.
|
|
@@ -68,7 +67,7 @@ def read_symbol(file_elf: Path, symbol: str, length: int = UID_SIZE) -> Optional
|
|
|
68
67
|
|
|
69
68
|
def read_uid(file_elf: Path) -> Optional[int]:
|
|
70
69
|
"""Read value of UID-symbol for shepherd testbed."""
|
|
71
|
-
return read_symbol(file_elf, symbol=UID_NAME, length=UID_SIZE)
|
|
70
|
+
return read_symbol(file_elf, symbol=config.UID_NAME, length=config.UID_SIZE)
|
|
72
71
|
|
|
73
72
|
|
|
74
73
|
def read_arch(file_elf: Path) -> Optional[str]:
|
|
@@ -88,7 +87,7 @@ def read_arch(file_elf: Path) -> Optional[str]:
|
|
|
88
87
|
def modify_symbol_value(
|
|
89
88
|
file_elf: Path,
|
|
90
89
|
symbol: str,
|
|
91
|
-
value: Annotated[int, Field(ge=0, lt=2 ** (8 * UID_SIZE))],
|
|
90
|
+
value: Annotated[int, Field(ge=0, lt=2 ** (8 * config.UID_SIZE))],
|
|
92
91
|
*,
|
|
93
92
|
overwrite: bool = False,
|
|
94
93
|
) -> Optional[Path]:
|
|
@@ -106,10 +105,10 @@ def modify_symbol_value(
|
|
|
106
105
|
raise RuntimeError(elf_error_text)
|
|
107
106
|
elf = ELF(path=file_elf)
|
|
108
107
|
addr = elf.symbols[symbol]
|
|
109
|
-
value_raw = elf.read(address=addr, count=UID_SIZE)[-UID_SIZE:]
|
|
108
|
+
value_raw = elf.read(address=addr, count=config.UID_SIZE)[-config.UID_SIZE :]
|
|
110
109
|
# ⤷ cutting needed -> msp produces 4b instead of 2
|
|
111
110
|
value_old = int.from_bytes(bytes=value_raw, byteorder=elf.endian, signed=False)
|
|
112
|
-
value_raw = value.to_bytes(length=UID_SIZE, byteorder=elf.endian, signed=False)
|
|
111
|
+
value_raw = value.to_bytes(length=config.UID_SIZE, byteorder=elf.endian, signed=False)
|
|
113
112
|
|
|
114
113
|
try:
|
|
115
114
|
elf.write(address=addr, data=value_raw)
|
|
@@ -132,4 +131,4 @@ def modify_symbol_value(
|
|
|
132
131
|
|
|
133
132
|
def modify_uid(file_elf: Path, value: int) -> Optional[Path]:
|
|
134
133
|
"""Replace value of UID-symbol for shepherd testbed."""
|
|
135
|
-
return modify_symbol_value(file_elf, symbol=UID_NAME, value=value, overwrite=True)
|
|
134
|
+
return modify_symbol_value(file_elf, symbol=config.UID_NAME, value=value, overwrite=True)
|
|
@@ -31,10 +31,8 @@ class SystemInventory(ShpModel):
|
|
|
31
31
|
"""System / OS related inventory model."""
|
|
32
32
|
|
|
33
33
|
uptime: PositiveInt
|
|
34
|
-
|
|
34
|
+
""" ⤷ seconds"""
|
|
35
35
|
timestamp: datetime
|
|
36
|
-
# time_delta: timedelta = timedelta(seconds=0) # noqa: ERA001
|
|
37
|
-
# ⤷ lag behind earliest observer, TODO: wrong place
|
|
38
36
|
|
|
39
37
|
system: str
|
|
40
38
|
release: str
|
shepherd_core/reader.py
CHANGED
|
@@ -7,6 +7,7 @@ import errno
|
|
|
7
7
|
import logging
|
|
8
8
|
import math
|
|
9
9
|
import os
|
|
10
|
+
from datetime import datetime
|
|
10
11
|
from itertools import product
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from types import MappingProxyType
|
|
@@ -24,9 +25,10 @@ from tqdm import trange
|
|
|
24
25
|
from typing_extensions import Self
|
|
25
26
|
from typing_extensions import deprecated
|
|
26
27
|
|
|
27
|
-
from .
|
|
28
|
+
from .config import config
|
|
28
29
|
from .data_models.base.calibration import CalibrationPair
|
|
29
30
|
from .data_models.base.calibration import CalibrationSeries
|
|
31
|
+
from .data_models.base.timezone import local_tz
|
|
30
32
|
from .data_models.content.energy_environment import EnergyDType
|
|
31
33
|
from .decoder_waveform import Uart
|
|
32
34
|
|
|
@@ -75,7 +77,7 @@ class Reader:
|
|
|
75
77
|
self._logger.setLevel(logging.DEBUG if verbose else logging.INFO)
|
|
76
78
|
|
|
77
79
|
if not hasattr(self, "samplerate_sps"):
|
|
78
|
-
self.samplerate_sps: int =
|
|
80
|
+
self.samplerate_sps: int = config.SAMPLERATE_SPS
|
|
79
81
|
self.sample_interval_ns: int = round(10**9 // self.samplerate_sps)
|
|
80
82
|
self.sample_interval_s: float = 1 / self.samplerate_sps
|
|
81
83
|
|
|
@@ -263,6 +265,11 @@ class Reader:
|
|
|
263
265
|
omit_timestamps=omit_ts,
|
|
264
266
|
)
|
|
265
267
|
|
|
268
|
+
def get_time_start(self) -> Optional[datetime]:
|
|
269
|
+
if self.samples_n < 1:
|
|
270
|
+
return None
|
|
271
|
+
return datetime.fromtimestamp(self._cal.time.raw_to_si(self.ds_time[0]), tz=local_tz())
|
|
272
|
+
|
|
266
273
|
def get_calibration_data(self) -> CalibrationSeries:
|
|
267
274
|
"""Read calibration-data from hdf5 file.
|
|
268
275
|
|
|
@@ -14,4 +14,4 @@ def _get_xdg_path(variable_name: str, default_path: Path) -> Path:
|
|
|
14
14
|
user_path = Path("~").expanduser()
|
|
15
15
|
|
|
16
16
|
cache_xdg_path = _get_xdg_path("XDG_CACHE_HOME", user_path / ".cache")
|
|
17
|
-
cache_user_path = cache_xdg_path / "
|
|
17
|
+
cache_user_path = cache_xdg_path / "shepherd"
|
|
@@ -8,7 +8,7 @@ from typing import Union
|
|
|
8
8
|
|
|
9
9
|
from pydantic import validate_call
|
|
10
10
|
|
|
11
|
-
from shepherd_core.
|
|
11
|
+
from shepherd_core.config import config
|
|
12
12
|
from shepherd_core.data_models.base.shepherd import ShpModel
|
|
13
13
|
from shepherd_core.data_models.base.wrapper import Wrapper
|
|
14
14
|
|
|
@@ -38,7 +38,7 @@ class WebClient(AbcClient):
|
|
|
38
38
|
if not hasattr(self, "_token"):
|
|
39
39
|
# add default values
|
|
40
40
|
self._token: str = "basic_public_access" # noqa: S105
|
|
41
|
-
self._server: str =
|
|
41
|
+
self._server: str = config.TESTBED_SERVER
|
|
42
42
|
self._user: Optional[User] = None
|
|
43
43
|
self._key: Optional[str] = None
|
|
44
44
|
self._connected: bool = False
|
shepherd_core/version.py
CHANGED
shepherd_core/writer.py
CHANGED
|
@@ -20,7 +20,7 @@ from typing_extensions import Self
|
|
|
20
20
|
from yaml import Node
|
|
21
21
|
from yaml import SafeDumper
|
|
22
22
|
|
|
23
|
-
from .
|
|
23
|
+
from .config import config
|
|
24
24
|
from .data_models.base.calibration import CalibrationEmulator as CalEmu
|
|
25
25
|
from .data_models.base.calibration import CalibrationHarvester as CalHrv
|
|
26
26
|
from .data_models.base.calibration import CalibrationSeries as CalSeries
|
|
@@ -348,7 +348,7 @@ class Writer(Reader):
|
|
|
348
348
|
chunks_n = self.ds_voltage.size / self.CHUNK_SAMPLES_N
|
|
349
349
|
size_new = int(math.floor(chunks_n) * self.CHUNK_SAMPLES_N)
|
|
350
350
|
if size_new < self.ds_voltage.size:
|
|
351
|
-
if self.samplerate_sps !=
|
|
351
|
+
if self.samplerate_sps != config.SAMPLERATE_SPS:
|
|
352
352
|
self._logger.debug("skipped alignment due to altered samplerate")
|
|
353
353
|
return
|
|
354
354
|
self._logger.info(
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shepherd_core
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.6.1
|
|
4
4
|
Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
|
|
5
5
|
Author-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
|
|
6
6
|
Maintainer-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
|
|
7
|
-
Project-URL: Documentation, https://github.com/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
7
|
+
Project-URL: Documentation, https://github.com/nes-lab/shepherd-tools/blob/main/README.md
|
|
8
|
+
Project-URL: Issues, https://github.com/nes-lab/shepherd-tools/issues
|
|
9
9
|
Project-URL: Source, https://pypi.org/project/shepherd-core/
|
|
10
10
|
Keywords: testbed,beaglebone,pru,batteryless,energyharvesting,solar
|
|
11
11
|
Platform: unix
|
|
@@ -56,16 +56,16 @@ Requires-Dist: coverage; extra == "test"
|
|
|
56
56
|
|
|
57
57
|
# Core Library
|
|
58
58
|
|
|
59
|
-
[](https://pypi.org/project/shepherd_core)
|
|
60
60
|
[](https://pypi.python.org/pypi/shepherd-core)
|
|
61
|
-
[](https://github.com/nes-lab/shepherd-tools/actions/workflows/quality_assurance.yaml)
|
|
62
62
|
[](https://github.com/astral-sh/ruff)
|
|
63
63
|
|
|
64
|
-
**Main Documentation**: <https://
|
|
64
|
+
**Main Documentation**: <https://nes-lab.github.io/shepherd>
|
|
65
65
|
|
|
66
|
-
**Source Code**: <https://github.com/
|
|
66
|
+
**Source Code**: <https://github.com/nes-lab/shepherd-tools>
|
|
67
67
|
|
|
68
|
-
**Main Project**: <https://github.com/
|
|
68
|
+
**Main Project**: <https://github.com/nes-lab/shepherd>
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
@@ -87,7 +87,7 @@ For postprocessing shepherds .h5-files usage of [shepherd_data](https://pypi.org
|
|
|
87
87
|
- decode waveforms (gpio-state & timestamp) to UART
|
|
88
88
|
- create an inventory (for deployed versions of software, hardware)
|
|
89
89
|
|
|
90
|
-
See [official documentation](https://
|
|
90
|
+
See [official documentation](https://nes-lab.github.io/shepherd) or [example scripts](https://github.com/nes-lab/shepherd-tools/tree/main/shepherd_core/examples) for more details and usage. Most functionality is showcased in both. The [extra](https://github.com/nes-lab/shepherd-tools/tree/main/shepherd_core/extra)-directory holds data-generators relevant for the testbed. Notably is a [trafficbench](https://github.com/nes-lab/TrafficBench)-experiment that's used to derive the link-matrix of the testbed-nodes.
|
|
91
91
|
|
|
92
92
|
## Config-Models in Detail
|
|
93
93
|
|
|
@@ -145,9 +145,9 @@ pip install shepherd-data -U
|
|
|
145
145
|
For bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
|
|
146
146
|
|
|
147
147
|
```Shell
|
|
148
|
-
pip install git+https://github.com/
|
|
148
|
+
pip install git+https://github.com/nes-lab/shepherd-tools.git@dev#subdirectory=shepherd_core -U
|
|
149
149
|
# and on sheep with newer debian
|
|
150
|
-
sudo pip install git+https://github.com/
|
|
150
|
+
sudo pip install git+https://github.com/nes-lab/shepherd-tools.git@dev#subdirectory=shepherd_core -U --break-system-packages
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
|
|
@@ -179,7 +179,7 @@ To run the testbench, follow these steps:
|
|
|
179
179
|
3. run the testbench (~ 320 tests):
|
|
180
180
|
|
|
181
181
|
```Shell
|
|
182
|
-
cd shepherd-
|
|
182
|
+
cd shepherd-tools/shepherd_core
|
|
183
183
|
pip3 install ./[tests]
|
|
184
184
|
pytest
|
|
185
185
|
```
|