shepherd-core 2025.6.3__py3-none-any.whl → 2025.6.4__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/data_models/base/cal_measurement.py +4 -5
- shepherd_core/data_models/base/calibration.py +8 -10
- shepherd_core/data_models/base/content.py +2 -3
- shepherd_core/data_models/base/shepherd.py +6 -8
- shepherd_core/data_models/base/wrapper.py +3 -4
- shepherd_core/data_models/content/energy_environment.py +4 -5
- shepherd_core/data_models/content/firmware.py +3 -5
- shepherd_core/data_models/content/virtual_harvester.py +5 -6
- shepherd_core/data_models/experiment/experiment.py +9 -17
- shepherd_core/data_models/experiment/observer_features.py +15 -37
- shepherd_core/data_models/experiment/target_config.py +10 -11
- shepherd_core/data_models/task/__init__.py +1 -3
- shepherd_core/data_models/task/emulation.py +10 -14
- shepherd_core/data_models/task/firmware_mod.py +2 -4
- shepherd_core/data_models/task/harvest.py +4 -7
- shepherd_core/data_models/task/observer_tasks.py +7 -8
- shepherd_core/data_models/task/programming.py +1 -2
- shepherd_core/data_models/task/testbed_tasks.py +2 -9
- shepherd_core/data_models/testbed/cape.py +3 -5
- shepherd_core/data_models/testbed/gpio.py +7 -8
- shepherd_core/data_models/testbed/mcu.py +1 -2
- shepherd_core/data_models/testbed/observer.py +5 -6
- shepherd_core/data_models/testbed/target.py +4 -6
- shepherd_core/data_models/testbed/testbed.py +2 -3
- shepherd_core/decoder_waveform/uart.py +11 -13
- shepherd_core/fw_tools/converter.py +1 -2
- shepherd_core/fw_tools/converter_elf.py +1 -2
- shepherd_core/fw_tools/patcher.py +5 -6
- shepherd_core/inventory/python.py +8 -9
- shepherd_core/inventory/system.py +1 -2
- shepherd_core/inventory/target.py +1 -2
- shepherd_core/logger.py +1 -2
- shepherd_core/reader.py +18 -23
- shepherd_core/testbed_client/client_abc_fix.py +2 -7
- shepherd_core/testbed_client/client_web.py +5 -9
- shepherd_core/testbed_client/fixtures.py +3 -5
- shepherd_core/testbed_client/user_model.py +4 -5
- shepherd_core/version.py +1 -1
- shepherd_core/vsource/virtual_converter_model.py +1 -2
- shepherd_core/vsource/virtual_harvester_simulation.py +1 -2
- shepherd_core/vsource/virtual_source_model.py +3 -5
- shepherd_core/vsource/virtual_source_simulation.py +1 -2
- shepherd_core/writer.py +12 -14
- {shepherd_core-2025.6.3.dist-info → shepherd_core-2025.6.4.dist-info}/METADATA +2 -3
- shepherd_core-2025.6.4.dist-info/RECORD +83 -0
- shepherd_core-2025.6.3.dist-info/RECORD +0 -83
- {shepherd_core-2025.6.3.dist-info → shepherd_core-2025.6.4.dist-info}/WHEEL +0 -0
- {shepherd_core-2025.6.3.dist-info → shepherd_core-2025.6.4.dist-info}/top_level.txt +0 -0
- {shepherd_core-2025.6.3.dist-info → shepherd_core-2025.6.4.dist-info}/zip-safe +0 -0
|
@@ -5,7 +5,6 @@ from datetime import datetime
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from pathlib import PurePosixPath
|
|
7
7
|
from typing import Annotated
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
from pydantic import validate_call
|
|
11
10
|
from typing_extensions import Self
|
|
@@ -29,20 +28,20 @@ class ObserverTasks(ShpModel):
|
|
|
29
28
|
observer: NameStr
|
|
30
29
|
|
|
31
30
|
# PRE PROCESS
|
|
32
|
-
time_prep:
|
|
31
|
+
time_prep: datetime | None = None # TODO: currently not used
|
|
33
32
|
root_path: Path
|
|
34
33
|
|
|
35
34
|
# fw mod, store as hex-file and program
|
|
36
|
-
fw1_mod:
|
|
37
|
-
fw2_mod:
|
|
38
|
-
fw1_prog:
|
|
39
|
-
fw2_prog:
|
|
35
|
+
fw1_mod: FirmwareModTask | None = None
|
|
36
|
+
fw2_mod: FirmwareModTask | None = None
|
|
37
|
+
fw1_prog: ProgrammingTask | None = None
|
|
38
|
+
fw2_prog: ProgrammingTask | None = None
|
|
40
39
|
|
|
41
40
|
# MAIN PROCESS
|
|
42
|
-
emulation:
|
|
41
|
+
emulation: EmulationTask | None = None
|
|
43
42
|
|
|
44
43
|
# deprecations, TODO: remove before public release
|
|
45
|
-
owner_id: Annotated[
|
|
44
|
+
owner_id: Annotated[IdInt | None, deprecated("not needed anymore")] = None
|
|
46
45
|
abort_on_error: Annotated[bool, deprecated("has no effect")] = False
|
|
47
46
|
|
|
48
47
|
# post_copy / cleanup, Todo: could also just intake emuTask
|
|
@@ -4,7 +4,6 @@ from collections.abc import Set as AbstractSet
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from pathlib import PurePosixPath
|
|
6
6
|
from typing import Annotated
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from pydantic import Field
|
|
10
9
|
from pydantic import model_validator
|
|
@@ -60,7 +59,7 @@ class ProgrammingTask(ShpModel):
|
|
|
60
59
|
tgt_id: IdInt,
|
|
61
60
|
mcu_port: MCUPort,
|
|
62
61
|
fw_path: Path,
|
|
63
|
-
) ->
|
|
62
|
+
) -> Self | None:
|
|
64
63
|
obs = tb.get_observer(tgt_id)
|
|
65
64
|
tgt_cfg = xp.get_target_config(tgt_id)
|
|
66
65
|
|
|
@@ -4,14 +4,11 @@ from pathlib import Path
|
|
|
4
4
|
from pathlib import PurePosixPath
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
from typing import Annotated
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from pydantic import Field
|
|
10
9
|
from pydantic import validate_call
|
|
11
10
|
from typing_extensions import Self
|
|
12
|
-
from typing_extensions import deprecated
|
|
13
11
|
|
|
14
|
-
from shepherd_core.data_models.base.content import IdInt
|
|
15
12
|
from shepherd_core.data_models.base.content import NameStr
|
|
16
13
|
from shepherd_core.data_models.base.shepherd import ShpModel
|
|
17
14
|
from shepherd_core.data_models.experiment.experiment import Experiment
|
|
@@ -29,13 +26,9 @@ class TestbedTasks(ShpModel):
|
|
|
29
26
|
name: NameStr
|
|
30
27
|
observer_tasks: Annotated[list[ObserverTasks], Field(min_length=1, max_length=128)]
|
|
31
28
|
|
|
32
|
-
# deprecated, TODO: remove before public release
|
|
33
|
-
email_results: Annotated[Optional[bool], deprecated("not needed anymore")] = False
|
|
34
|
-
owner_id: Annotated[Optional[IdInt], deprecated("not needed anymore")] = None
|
|
35
|
-
|
|
36
29
|
@classmethod
|
|
37
30
|
@validate_call
|
|
38
|
-
def from_xp(cls, xp: Experiment, tb:
|
|
31
|
+
def from_xp(cls, xp: Experiment, tb: Testbed | None = None) -> Self:
|
|
39
32
|
if tb is None:
|
|
40
33
|
# TODO: is tb-argument really needed? prob. not
|
|
41
34
|
tb = Testbed() # this will query the first (and only) entry of client
|
|
@@ -47,7 +40,7 @@ class TestbedTasks(ShpModel):
|
|
|
47
40
|
observer_tasks=obs_tasks,
|
|
48
41
|
)
|
|
49
42
|
|
|
50
|
-
def get_observer_tasks(self, observer: str) ->
|
|
43
|
+
def get_observer_tasks(self, observer: str) -> ObserverTasks | None:
|
|
51
44
|
for tasks in self.observer_tasks:
|
|
52
45
|
if observer == tasks.observer:
|
|
53
46
|
return tasks
|
|
@@ -4,8 +4,6 @@ from datetime import date
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from typing import Any
|
|
7
|
-
from typing import Optional
|
|
8
|
-
from typing import Union
|
|
9
7
|
|
|
10
8
|
from pydantic import Field
|
|
11
9
|
from pydantic import model_validator
|
|
@@ -31,12 +29,12 @@ class Cape(ShpModel, title="Shepherd-Cape"):
|
|
|
31
29
|
name: NameStr
|
|
32
30
|
version: NameStr
|
|
33
31
|
description: SafeStr
|
|
34
|
-
comment:
|
|
32
|
+
comment: SafeStr | None = None
|
|
35
33
|
# TODO: wake_interval, calibration
|
|
36
34
|
|
|
37
35
|
active: bool = True
|
|
38
|
-
created:
|
|
39
|
-
calibrated:
|
|
36
|
+
created: date | datetime = Field(default_factory=datetime.now)
|
|
37
|
+
calibrated: date | datetime | None = None
|
|
40
38
|
|
|
41
39
|
def __str__(self) -> str:
|
|
42
40
|
return self.name
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Any
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from pydantic import Field
|
|
9
8
|
from pydantic import StringConstraints
|
|
@@ -30,16 +29,16 @@ class GPIO(ShpModel, title="GPIO of Observer Node"):
|
|
|
30
29
|
|
|
31
30
|
id: IdInt
|
|
32
31
|
name: NameStr
|
|
33
|
-
description:
|
|
34
|
-
comment:
|
|
32
|
+
description: SafeStr | None = None
|
|
33
|
+
comment: SafeStr | None = None
|
|
35
34
|
|
|
36
35
|
direction: Direction = Direction.Input
|
|
37
|
-
dir_switch:
|
|
36
|
+
dir_switch: Annotated[str, StringConstraints(max_length=32)] | None = None
|
|
38
37
|
|
|
39
|
-
reg_pru:
|
|
40
|
-
pin_pru:
|
|
41
|
-
reg_sys:
|
|
42
|
-
pin_sys:
|
|
38
|
+
reg_pru: Annotated[str, StringConstraints(max_length=10)] | None = None
|
|
39
|
+
pin_pru: Annotated[str, StringConstraints(max_length=10)] | None = None
|
|
40
|
+
reg_sys: Annotated[int, Field(ge=0)] | None = None
|
|
41
|
+
pin_sys: Annotated[str, StringConstraints(max_length=10)] | None = None
|
|
43
42
|
|
|
44
43
|
def __str__(self) -> str:
|
|
45
44
|
return self.name
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Any
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from pydantic import Field
|
|
9
8
|
from pydantic import model_validator
|
|
@@ -30,7 +29,7 @@ class MCU(ShpModel, title="Microcontroller of the Target Node"):
|
|
|
30
29
|
id: IdInt
|
|
31
30
|
name: NameStr
|
|
32
31
|
description: SafeStr
|
|
33
|
-
comment:
|
|
32
|
+
comment: SafeStr | None = None
|
|
34
33
|
|
|
35
34
|
platform: NameStr
|
|
36
35
|
core: NameStr
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Any
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from pydantic import Field
|
|
9
8
|
from pydantic import IPvAnyAddress
|
|
@@ -33,7 +32,7 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
|
|
|
33
32
|
id: IdInt
|
|
34
33
|
name: NameStr
|
|
35
34
|
description: SafeStr
|
|
36
|
-
comment:
|
|
35
|
+
comment: SafeStr | None = None
|
|
37
36
|
|
|
38
37
|
ip: IPvAnyAddress
|
|
39
38
|
mac: MACStr
|
|
@@ -46,12 +45,12 @@ class Observer(ShpModel, title="Shepherd-Sheep"):
|
|
|
46
45
|
""" ⤷ cfaed-floor"""
|
|
47
46
|
|
|
48
47
|
active: bool = True
|
|
49
|
-
cape:
|
|
50
|
-
target_a:
|
|
51
|
-
target_b:
|
|
48
|
+
cape: Cape | None = None
|
|
49
|
+
target_a: Target | None = None
|
|
50
|
+
target_b: Target | None = None
|
|
52
51
|
|
|
53
52
|
created: datetime = Field(default_factory=datetime.now)
|
|
54
|
-
alive_last:
|
|
53
|
+
alive_last: datetime | None = None
|
|
55
54
|
|
|
56
55
|
def __str__(self) -> str:
|
|
57
56
|
return self.name
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Any
|
|
6
|
-
from typing import Optional
|
|
7
|
-
from typing import Union
|
|
8
6
|
|
|
9
7
|
from pydantic import Field
|
|
10
8
|
from pydantic import model_validator
|
|
@@ -30,15 +28,15 @@ class Target(ShpModel, title="Target Node (DuT)"):
|
|
|
30
28
|
version: NameStr
|
|
31
29
|
description: SafeStr
|
|
32
30
|
|
|
33
|
-
comment:
|
|
31
|
+
comment: SafeStr | None = None
|
|
34
32
|
|
|
35
33
|
active: bool = True
|
|
36
34
|
created: datetime = Field(default_factory=datetime.now)
|
|
37
35
|
|
|
38
|
-
testbed_id:
|
|
36
|
+
testbed_id: IdInt16 | None = None
|
|
39
37
|
""" ⤷ is derived from ID (targets are still selected by id!)"""
|
|
40
|
-
mcu1:
|
|
41
|
-
mcu2:
|
|
38
|
+
mcu1: MCU | NameStr
|
|
39
|
+
mcu2: MCU | NameStr | None = None
|
|
42
40
|
|
|
43
41
|
# TODO: programming pins per mcu should be here (or better in Cape)
|
|
44
42
|
|
|
@@ -4,7 +4,6 @@ from datetime import timedelta
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
from typing import Any
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from pydantic import Field
|
|
10
9
|
from pydantic import HttpUrl
|
|
@@ -29,9 +28,9 @@ class Testbed(ShpModel):
|
|
|
29
28
|
id: IdInt
|
|
30
29
|
name: NameStr
|
|
31
30
|
description: SafeStr
|
|
32
|
-
comment:
|
|
31
|
+
comment: SafeStr | None = None
|
|
33
32
|
|
|
34
|
-
url:
|
|
33
|
+
url: HttpUrl | None = None
|
|
35
34
|
|
|
36
35
|
observers: Annotated[list[Observer], Field(min_length=1, max_length=128)]
|
|
37
36
|
|
|
@@ -23,8 +23,6 @@ https://sigrok.org/wiki/Protocol_decoder:Uart
|
|
|
23
23
|
|
|
24
24
|
from enum import Enum
|
|
25
25
|
from pathlib import Path
|
|
26
|
-
from typing import Optional
|
|
27
|
-
from typing import Union
|
|
28
26
|
|
|
29
27
|
import numpy as np
|
|
30
28
|
|
|
@@ -51,12 +49,12 @@ class Uart:
|
|
|
51
49
|
|
|
52
50
|
def __init__(
|
|
53
51
|
self,
|
|
54
|
-
content:
|
|
55
|
-
baud_rate:
|
|
56
|
-
frame_length:
|
|
57
|
-
inversion:
|
|
58
|
-
parity:
|
|
59
|
-
bit_order:
|
|
52
|
+
content: Path | np.ndarray,
|
|
53
|
+
baud_rate: int | None = None,
|
|
54
|
+
frame_length: int | None = 8,
|
|
55
|
+
inversion: bool | None = None,
|
|
56
|
+
parity: Parity | None = Parity.no,
|
|
57
|
+
bit_order: BitOrder | None = BitOrder.lsb_first,
|
|
60
58
|
) -> None:
|
|
61
59
|
"""Provide a file with two columns: TS & Signal.
|
|
62
60
|
|
|
@@ -115,9 +113,9 @@ class Uart:
|
|
|
115
113
|
log.error("Signal still inverted?!? Check parameters and input")
|
|
116
114
|
|
|
117
115
|
# results
|
|
118
|
-
self.events_symbols:
|
|
119
|
-
self.events_lines:
|
|
120
|
-
self.text:
|
|
116
|
+
self.events_symbols: np.ndarray | None = None
|
|
117
|
+
self.events_lines: np.ndarray | None = None
|
|
118
|
+
self.text: str | None = None
|
|
121
119
|
|
|
122
120
|
def _convert_analog2digital(self, *, invert: bool = False) -> None:
|
|
123
121
|
"""Divide dimension in two, divided by mean-value."""
|
|
@@ -208,9 +206,9 @@ class Uart:
|
|
|
208
206
|
if self.events_symbols is not None:
|
|
209
207
|
return self.events_symbols
|
|
210
208
|
|
|
211
|
-
pos_df:
|
|
209
|
+
pos_df: int | None = None
|
|
212
210
|
symbol: int = 0
|
|
213
|
-
t_start:
|
|
211
|
+
t_start: float | None = None
|
|
214
212
|
content: list = []
|
|
215
213
|
|
|
216
214
|
for time, value, steps in self.events_sig:
|
|
@@ -4,7 +4,6 @@ import base64
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import shutil
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Union
|
|
8
7
|
|
|
9
8
|
import zstandard as zstd
|
|
10
9
|
from pydantic import validate_call
|
|
@@ -81,7 +80,7 @@ def base64_to_hash(content: str) -> str:
|
|
|
81
80
|
|
|
82
81
|
|
|
83
82
|
@validate_call
|
|
84
|
-
def extract_firmware(data:
|
|
83
|
+
def extract_firmware(data: str | Path, data_type: FirmwareDType, file_path: Path) -> Path:
|
|
85
84
|
"""Make embedded firmware-data usable in filesystem.
|
|
86
85
|
|
|
87
86
|
- base64-string will be transformed to file
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from pydantic import validate_call
|
|
8
7
|
|
|
@@ -10,7 +9,7 @@ from pydantic import validate_call
|
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
@validate_call
|
|
13
|
-
def elf_to_hex(file_elf: Path, file_hex:
|
|
12
|
+
def elf_to_hex(file_elf: Path, file_hex: Path | None = None) -> Path:
|
|
14
13
|
"""Convert ELF to hex file using objcopy."""
|
|
15
14
|
if not file_elf.is_file():
|
|
16
15
|
raise ValueError("Fn needs an existing file as input")
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Annotated
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from pydantic import Field
|
|
8
7
|
from pydantic import validate_call
|
|
@@ -49,7 +48,7 @@ def find_symbol(file_elf: Path, symbol: str) -> bool:
|
|
|
49
48
|
|
|
50
49
|
|
|
51
50
|
@validate_call
|
|
52
|
-
def read_symbol(file_elf: Path, symbol: str, length: int) ->
|
|
51
|
+
def read_symbol(file_elf: Path, symbol: str, length: int) -> int | None:
|
|
53
52
|
"""Read value of symbol in ELF-File.
|
|
54
53
|
|
|
55
54
|
Will be interpreted as int.
|
|
@@ -65,12 +64,12 @@ def read_symbol(file_elf: Path, symbol: str, length: int) -> Optional[int]:
|
|
|
65
64
|
return int.from_bytes(bytes=value_raw, byteorder=elf.endian, signed=False)
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
def read_uid(file_elf: Path) ->
|
|
67
|
+
def read_uid(file_elf: Path) -> int | None:
|
|
69
68
|
"""Read value of UID-symbol for shepherd testbed."""
|
|
70
69
|
return read_symbol(file_elf, symbol=config.UID_NAME, length=config.UID_SIZE)
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
def read_arch(file_elf: Path) ->
|
|
72
|
+
def read_arch(file_elf: Path) -> str | None:
|
|
74
73
|
"""Determine chip-architecture from elf-metadata."""
|
|
75
74
|
if not is_elf(file_elf):
|
|
76
75
|
return None
|
|
@@ -90,7 +89,7 @@ def modify_symbol_value(
|
|
|
90
89
|
value: Annotated[int, Field(ge=0, lt=2 ** (8 * config.UID_SIZE))],
|
|
91
90
|
*,
|
|
92
91
|
overwrite: bool = False,
|
|
93
|
-
) ->
|
|
92
|
+
) -> Path | None:
|
|
94
93
|
"""Replace value of uint16-symbol in ELF-File.
|
|
95
94
|
|
|
96
95
|
Hardcoded for uint16_t (2 byte).
|
|
@@ -129,6 +128,6 @@ def modify_symbol_value(
|
|
|
129
128
|
return file_new
|
|
130
129
|
|
|
131
130
|
|
|
132
|
-
def modify_uid(file_elf: Path, value: int) ->
|
|
131
|
+
def modify_uid(file_elf: Path, value: int) -> Path | None:
|
|
133
132
|
"""Replace value of UID-symbol for shepherd testbed."""
|
|
134
133
|
return modify_symbol_value(file_elf, symbol=config.UID_NAME, value=value, overwrite=True)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import platform
|
|
4
4
|
from contextlib import suppress
|
|
5
5
|
from importlib import import_module
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from pydantic import ConfigDict
|
|
9
8
|
from typing_extensions import Self
|
|
@@ -15,14 +14,14 @@ class PythonInventory(ShpModel):
|
|
|
15
14
|
"""Python related inventory model."""
|
|
16
15
|
|
|
17
16
|
# program versions
|
|
18
|
-
h5py:
|
|
19
|
-
numpy:
|
|
20
|
-
pydantic:
|
|
21
|
-
python:
|
|
22
|
-
shepherd_core:
|
|
23
|
-
shepherd_sheep:
|
|
24
|
-
yaml:
|
|
25
|
-
zstandard:
|
|
17
|
+
h5py: str | None = None
|
|
18
|
+
numpy: str | None = None
|
|
19
|
+
pydantic: str | None = None
|
|
20
|
+
python: str | None = None
|
|
21
|
+
shepherd_core: str | None = None
|
|
22
|
+
shepherd_sheep: str | None = None
|
|
23
|
+
yaml: str | None = None
|
|
24
|
+
zstandard: str | None = None
|
|
26
25
|
|
|
27
26
|
model_config = ConfigDict(str_min_length=0)
|
|
28
27
|
|
|
@@ -9,7 +9,6 @@ from datetime import datetime
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from types import MappingProxyType
|
|
11
11
|
from typing import Any
|
|
12
|
-
from typing import Optional
|
|
13
12
|
|
|
14
13
|
from typing_extensions import Self
|
|
15
14
|
|
|
@@ -41,7 +40,7 @@ class SystemInventory(ShpModel):
|
|
|
41
40
|
machine: str
|
|
42
41
|
processor: str
|
|
43
42
|
|
|
44
|
-
ptp:
|
|
43
|
+
ptp: str | None = None
|
|
45
44
|
|
|
46
45
|
hostname: str
|
|
47
46
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Hardware related inventory model."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from pydantic import ConfigDict
|
|
7
6
|
from typing_extensions import Self
|
|
@@ -12,7 +11,7 @@ from shepherd_core.data_models import ShpModel
|
|
|
12
11
|
class TargetInventory(ShpModel):
|
|
13
12
|
"""Hardware related inventory model."""
|
|
14
13
|
|
|
15
|
-
cape:
|
|
14
|
+
cape: str | None = None
|
|
16
15
|
targets: Sequence[str] = ()
|
|
17
16
|
|
|
18
17
|
model_config = ConfigDict(str_min_length=0)
|
shepherd_core/logger.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import logging.handlers
|
|
5
|
-
from typing import Union
|
|
6
5
|
|
|
7
6
|
import chromalog
|
|
8
7
|
|
|
@@ -18,7 +17,7 @@ def get_verbose_level() -> int:
|
|
|
18
17
|
return verbose_level
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
def set_log_verbose_level(log_:
|
|
20
|
+
def set_log_verbose_level(log_: logging.Logger | logging.Handler, verbose: int) -> None:
|
|
22
21
|
"""Set log level of shepherd."""
|
|
23
22
|
if verbose == 0:
|
|
24
23
|
log_.setLevel(logging.ERROR)
|
shepherd_core/reader.py
CHANGED
|
@@ -12,10 +12,7 @@ from itertools import product
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from types import MappingProxyType
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
|
-
from typing import Annotated
|
|
16
15
|
from typing import Any
|
|
17
|
-
from typing import Optional
|
|
18
|
-
from typing import Union
|
|
19
16
|
|
|
20
17
|
import h5py
|
|
21
18
|
import numpy as np
|
|
@@ -91,8 +88,6 @@ class Reader:
|
|
|
91
88
|
self.file_size: int = 0
|
|
92
89
|
self.data_rate: float = 0
|
|
93
90
|
|
|
94
|
-
self.buffers_n: Annotated[int, deprecated("use .chunk_n instead")] = 0
|
|
95
|
-
|
|
96
91
|
# open file (if not already done by writer)
|
|
97
92
|
self._reader_opened: bool = False
|
|
98
93
|
if not hasattr(self, "h5file"):
|
|
@@ -161,9 +156,9 @@ class Reader:
|
|
|
161
156
|
|
|
162
157
|
def __exit__(
|
|
163
158
|
self,
|
|
164
|
-
typ:
|
|
165
|
-
exc:
|
|
166
|
-
tb:
|
|
159
|
+
typ: type[BaseException] | None = None,
|
|
160
|
+
exc: BaseException | None = None,
|
|
161
|
+
tb: TracebackType | None = None,
|
|
167
162
|
extra_arg: int = 0,
|
|
168
163
|
) -> None:
|
|
169
164
|
if self._reader_opened:
|
|
@@ -193,7 +188,7 @@ class Reader:
|
|
|
193
188
|
self.sample_interval_ns = round(10**9 * self.sample_interval_s)
|
|
194
189
|
self.samplerate_sps = max(round((self.samples_n - 1) / duration_s), 1)
|
|
195
190
|
self.runtime_s = round(self.samples_n / self.samplerate_sps, 1)
|
|
196
|
-
self.chunks_n =
|
|
191
|
+
self.chunks_n = int(self.samples_n // self.CHUNK_SAMPLES_N)
|
|
197
192
|
if isinstance(self.file_path, Path):
|
|
198
193
|
self.file_size = self.file_path.stat().st_size
|
|
199
194
|
else:
|
|
@@ -203,8 +198,8 @@ class Reader:
|
|
|
203
198
|
def read(
|
|
204
199
|
self,
|
|
205
200
|
start_n: int = 0,
|
|
206
|
-
end_n:
|
|
207
|
-
n_samples_per_chunk:
|
|
201
|
+
end_n: int | None = None,
|
|
202
|
+
n_samples_per_chunk: int | None = None,
|
|
208
203
|
*,
|
|
209
204
|
is_raw: bool = False,
|
|
210
205
|
omit_timestamps: bool = False,
|
|
@@ -251,8 +246,8 @@ class Reader:
|
|
|
251
246
|
def read_buffers(
|
|
252
247
|
self,
|
|
253
248
|
start_n: int = 0,
|
|
254
|
-
end_n:
|
|
255
|
-
n_samples_per_buffer:
|
|
249
|
+
end_n: int | None = None,
|
|
250
|
+
n_samples_per_buffer: int | None = None,
|
|
256
251
|
*,
|
|
257
252
|
is_raw: bool = False,
|
|
258
253
|
omit_ts: bool = False,
|
|
@@ -265,7 +260,7 @@ class Reader:
|
|
|
265
260
|
omit_timestamps=omit_ts,
|
|
266
261
|
)
|
|
267
262
|
|
|
268
|
-
def get_time_start(self) ->
|
|
263
|
+
def get_time_start(self) -> datetime | None:
|
|
269
264
|
if self.samples_n < 1:
|
|
270
265
|
return None
|
|
271
266
|
return datetime.fromtimestamp(self._cal.time.raw_to_si(self.ds_time[0]), tz=local_tz())
|
|
@@ -297,7 +292,7 @@ class Reader:
|
|
|
297
292
|
return self.h5file.attrs["hostname"]
|
|
298
293
|
return "unknown"
|
|
299
294
|
|
|
300
|
-
def get_datatype(self) ->
|
|
295
|
+
def get_datatype(self) -> EnergyDType | None:
|
|
301
296
|
try:
|
|
302
297
|
if "datatype" in self.h5file["data"].attrs:
|
|
303
298
|
return EnergyDType[self.h5file["data"].attrs["datatype"]]
|
|
@@ -308,7 +303,7 @@ class Reader:
|
|
|
308
303
|
else:
|
|
309
304
|
return None
|
|
310
305
|
|
|
311
|
-
def get_voltage_step(self) ->
|
|
306
|
+
def get_voltage_step(self) -> float | None:
|
|
312
307
|
"""Informs about the voltage step (in volts) used during harvesting the ivcurve.
|
|
313
308
|
|
|
314
309
|
Options for figuring out the real step:
|
|
@@ -316,7 +311,7 @@ class Reader:
|
|
|
316
311
|
- analyze recorded data for most often used delta
|
|
317
312
|
- calculate with 'steps_n * (1 + wait_cycles)' (done for calculating window_size)
|
|
318
313
|
"""
|
|
319
|
-
voltage_step:
|
|
314
|
+
voltage_step: float | None = (
|
|
320
315
|
self.get_config().get("virtual_harvester", {}).get("voltage_step_mV", None)
|
|
321
316
|
)
|
|
322
317
|
if voltage_step is None:
|
|
@@ -510,7 +505,7 @@ class Reader:
|
|
|
510
505
|
return float(sum(energy_ws))
|
|
511
506
|
|
|
512
507
|
def _dset_statistics(
|
|
513
|
-
self, dset: h5py.Dataset, cal:
|
|
508
|
+
self, dset: h5py.Dataset, cal: CalibrationPair | None = None
|
|
514
509
|
) -> dict[str, float]:
|
|
515
510
|
"""Create basic stats for a provided dataset.
|
|
516
511
|
|
|
@@ -615,7 +610,7 @@ class Reader:
|
|
|
615
610
|
|
|
616
611
|
def get_metadata(
|
|
617
612
|
self,
|
|
618
|
-
node:
|
|
613
|
+
node: h5py.Dataset | h5py.Group | None = None,
|
|
619
614
|
*,
|
|
620
615
|
minimal: bool = False,
|
|
621
616
|
) -> dict[str, dict]:
|
|
@@ -670,7 +665,7 @@ class Reader:
|
|
|
670
665
|
|
|
671
666
|
return metadata
|
|
672
667
|
|
|
673
|
-
def save_metadata(self, node:
|
|
668
|
+
def save_metadata(self, node: h5py.Dataset | h5py.Group | None = None) -> dict:
|
|
674
669
|
"""Get structure of file and dump content to yaml-file with same name as original.
|
|
675
670
|
|
|
676
671
|
:param node: starting node, leave free to go through whole file
|
|
@@ -688,7 +683,7 @@ class Reader:
|
|
|
688
683
|
metadata = {}
|
|
689
684
|
return metadata
|
|
690
685
|
|
|
691
|
-
def get_gpio_pin_num(self, name: str) ->
|
|
686
|
+
def get_gpio_pin_num(self, name: str) -> int | None:
|
|
692
687
|
# reverse lookup in a 2D-dict: key1 are pin_num, key2 are descriptor-names
|
|
693
688
|
if "gpio" not in self.h5file:
|
|
694
689
|
return None
|
|
@@ -709,7 +704,7 @@ class Reader:
|
|
|
709
704
|
data_1 = np.concatenate(([not data[0]], data[:-1]))
|
|
710
705
|
return data != data_1
|
|
711
706
|
|
|
712
|
-
def gpio_to_waveforms(self, name:
|
|
707
|
+
def gpio_to_waveforms(self, name: str | None = None) -> dict:
|
|
713
708
|
waveforms: dict[str, np.ndarray] = {}
|
|
714
709
|
if "gpio" not in self.h5file:
|
|
715
710
|
return waveforms
|
|
@@ -744,7 +739,7 @@ class Reader:
|
|
|
744
739
|
for row in pin_wf:
|
|
745
740
|
csv.write(f"{row[0] / 1e9}{separator}{int(row[1])}\n")
|
|
746
741
|
|
|
747
|
-
def gpio_to_uart(self) ->
|
|
742
|
+
def gpio_to_uart(self) -> np.ndarray | None:
|
|
748
743
|
wfs = self.gpio_to_waveforms("uart")
|
|
749
744
|
if len(wfs) < 1:
|
|
750
745
|
return None
|
|
@@ -18,7 +18,6 @@ TODO: Comfort functions missing
|
|
|
18
18
|
from abc import ABC
|
|
19
19
|
from abc import abstractmethod
|
|
20
20
|
from typing import Any
|
|
21
|
-
from typing import Optional
|
|
22
21
|
|
|
23
22
|
from shepherd_core.data_models.base.shepherd import ShpModel
|
|
24
23
|
from shepherd_core.data_models.base.wrapper import Wrapper
|
|
@@ -49,9 +48,7 @@ class AbcClient(ABC):
|
|
|
49
48
|
pass
|
|
50
49
|
|
|
51
50
|
@abstractmethod
|
|
52
|
-
def query_item(
|
|
53
|
-
self, model_type: str, uid: Optional[int] = None, name: Optional[str] = None
|
|
54
|
-
) -> dict:
|
|
51
|
+
def query_item(self, model_type: str, uid: int | None = None, name: str | None = None) -> dict:
|
|
55
52
|
pass
|
|
56
53
|
|
|
57
54
|
@abstractmethod
|
|
@@ -103,9 +100,7 @@ class FixturesClient(AbcClient):
|
|
|
103
100
|
def query_names(self, model_type: str) -> list[str]:
|
|
104
101
|
return list(self._fixtures[model_type].elements_by_name.keys())
|
|
105
102
|
|
|
106
|
-
def query_item(
|
|
107
|
-
self, model_type: str, uid: Optional[int] = None, name: Optional[str] = None
|
|
108
|
-
) -> dict:
|
|
103
|
+
def query_item(self, model_type: str, uid: int | None = None, name: str | None = None) -> dict:
|
|
109
104
|
if uid is not None:
|
|
110
105
|
return self._fixtures[model_type].query_id(uid)
|
|
111
106
|
if name is not None:
|