shepherd-core 2025.4.1__py3-none-any.whl → 2025.5.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.
Files changed (61) hide show
  1. shepherd_core/calibration_hw_def.py +11 -11
  2. shepherd_core/commons.py +4 -4
  3. shepherd_core/data_models/__init__.py +2 -0
  4. shepherd_core/data_models/base/cal_measurement.py +10 -11
  5. shepherd_core/data_models/base/calibration.py +7 -6
  6. shepherd_core/data_models/base/content.py +1 -1
  7. shepherd_core/data_models/base/shepherd.py +6 -7
  8. shepherd_core/data_models/base/wrapper.py +2 -2
  9. shepherd_core/data_models/content/_external_fixtures.yaml +32 -32
  10. shepherd_core/data_models/content/energy_environment.py +6 -5
  11. shepherd_core/data_models/content/firmware.py +9 -7
  12. shepherd_core/data_models/content/virtual_harvester.py +34 -26
  13. shepherd_core/data_models/content/virtual_harvester_fixture.yaml +2 -2
  14. shepherd_core/data_models/content/virtual_source.py +20 -17
  15. shepherd_core/data_models/content/virtual_source_fixture.yaml +3 -3
  16. shepherd_core/data_models/experiment/experiment.py +15 -15
  17. shepherd_core/data_models/experiment/observer_features.py +109 -16
  18. shepherd_core/data_models/experiment/target_config.py +17 -12
  19. shepherd_core/data_models/task/__init__.py +11 -8
  20. shepherd_core/data_models/task/emulation.py +32 -17
  21. shepherd_core/data_models/task/firmware_mod.py +11 -11
  22. shepherd_core/data_models/task/harvest.py +7 -6
  23. shepherd_core/data_models/task/observer_tasks.py +7 -7
  24. shepherd_core/data_models/task/programming.py +13 -12
  25. shepherd_core/data_models/task/testbed_tasks.py +8 -8
  26. shepherd_core/data_models/testbed/cape.py +7 -6
  27. shepherd_core/data_models/testbed/gpio.py +8 -7
  28. shepherd_core/data_models/testbed/mcu.py +8 -7
  29. shepherd_core/data_models/testbed/mcu_fixture.yaml +4 -4
  30. shepherd_core/data_models/testbed/observer.py +9 -7
  31. shepherd_core/data_models/testbed/target.py +9 -7
  32. shepherd_core/data_models/testbed/testbed.py +11 -10
  33. shepherd_core/data_models/virtual_source_doc.txt +3 -3
  34. shepherd_core/decoder_waveform/uart.py +5 -5
  35. shepherd_core/fw_tools/converter.py +10 -6
  36. shepherd_core/fw_tools/patcher.py +14 -15
  37. shepherd_core/fw_tools/validation.py +11 -6
  38. shepherd_core/inventory/__init__.py +6 -6
  39. shepherd_core/inventory/python.py +1 -1
  40. shepherd_core/inventory/system.py +11 -8
  41. shepherd_core/inventory/target.py +3 -3
  42. shepherd_core/logger.py +2 -2
  43. shepherd_core/reader.py +105 -78
  44. shepherd_core/testbed_client/client_abc_fix.py +22 -16
  45. shepherd_core/testbed_client/client_web.py +18 -11
  46. shepherd_core/testbed_client/fixtures.py +21 -22
  47. shepherd_core/testbed_client/user_model.py +6 -5
  48. shepherd_core/version.py +1 -1
  49. shepherd_core/vsource/target_model.py +3 -3
  50. shepherd_core/vsource/virtual_converter_model.py +3 -3
  51. shepherd_core/vsource/virtual_harvester_model.py +7 -9
  52. shepherd_core/vsource/virtual_harvester_simulation.py +7 -6
  53. shepherd_core/vsource/virtual_source_model.py +6 -5
  54. shepherd_core/vsource/virtual_source_simulation.py +8 -7
  55. shepherd_core/writer.py +37 -39
  56. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.5.2.dist-info}/METADATA +2 -3
  57. shepherd_core-2025.5.2.dist-info/RECORD +81 -0
  58. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.5.2.dist-info}/WHEEL +1 -1
  59. shepherd_core-2025.4.1.dist-info/RECORD +0 -81
  60. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.5.2.dist-info}/top_level.txt +0 -0
  61. {shepherd_core-2025.4.1.dist-info → shepherd_core-2025.5.2.dist-info}/zip-safe +0 -0
@@ -2,12 +2,11 @@
2
2
 
3
3
  import copy
4
4
  import pickle
5
+ from collections.abc import Mapping
5
6
  from datetime import datetime
6
7
  from datetime import timedelta
7
8
  from pathlib import Path
8
9
  from typing import Any
9
- from typing import Dict
10
- from typing import Mapping
11
10
  from typing import Optional
12
11
  from typing import Union
13
12
 
@@ -15,10 +14,11 @@ import yaml
15
14
  from pydantic import validate_call
16
15
  from typing_extensions import Self
17
16
 
18
- from ..data_models.base.timezone import local_now
19
- from ..data_models.base.timezone import local_tz
20
- from ..data_models.base.wrapper import Wrapper
21
- from ..logger import logger
17
+ from shepherd_core.data_models.base.timezone import local_now
18
+ from shepherd_core.data_models.base.timezone import local_tz
19
+ from shepherd_core.data_models.base.wrapper import Wrapper
20
+ from shepherd_core.logger import logger
21
+
22
22
  from .cache_path import cache_user_path
23
23
 
24
24
  # Proposed field-name:
@@ -34,8 +34,8 @@ class Fixture:
34
34
 
35
35
  def __init__(self, model_type: str) -> None:
36
36
  self.model_type: str = model_type.lower()
37
- self.elements_by_name: Dict[str, dict] = {}
38
- self.elements_by_id: Dict[int, dict] = {}
37
+ self.elements_by_name: dict[str, dict] = {}
38
+ self.elements_by_id: dict[int, dict] = {}
39
39
  # Iterator reset
40
40
  self._iter_index: int = 0
41
41
  self._iter_list: list = list(self.elements_by_name.values())
@@ -43,25 +43,26 @@ class Fixture:
43
43
  def insert(self, data: Wrapper) -> None:
44
44
  # ⤷ TODO: could get easier
45
45
  # - when not model_name but class used
46
- # - use doubleref name->id->data (safes RAM)
46
+ # - use doubleref name->id->data (saves RAM)
47
47
  if data.datatype.lower() != self.model_type.lower():
48
48
  return
49
49
  if "name" not in data.parameters:
50
50
  return
51
51
  name = str(data.parameters["name"]).lower()
52
52
  _id = data.parameters["id"]
53
- data = data.parameters
54
- self.elements_by_name[name] = data
55
- self.elements_by_id[_id] = data
53
+ data_model = data.parameters
54
+ self.elements_by_name[name] = data_model
55
+ self.elements_by_id[_id] = data_model
56
56
  # update iterator
57
- self._iter_list: list = list(self.elements_by_name.values())
57
+ self._iter_list = list(self.elements_by_name.values())
58
58
 
59
59
  def __getitem__(self, key: Union[str, int]) -> dict:
60
60
  if isinstance(key, str):
61
61
  key = key.lower()
62
62
  if key in self.elements_by_name:
63
63
  return self.elements_by_name[key]
64
- key = int(key) if key.isdigit() else None
64
+ if key.isdigit():
65
+ key = int(key)
65
66
  if key in self.elements_by_id:
66
67
  return self.elements_by_id[int(key)]
67
68
  msg = f"{self.model_type} '{key}' not found!"
@@ -85,7 +86,9 @@ class Fixture:
85
86
  def refs(self) -> dict:
86
87
  return {_i["id"]: _i["name"] for _i in self.elements_by_id.values()}
87
88
 
88
- def inheritance(self, values: dict, chain: Optional[list] = None) -> (dict, list):
89
+ def inheritance(
90
+ self, values: dict[str, Any], chain: Optional[list[str]] = None
91
+ ) -> tuple[dict[str, Any], list[str]]:
89
92
  if chain is None:
90
93
  chain = []
91
94
  values = copy.copy(values)
@@ -98,14 +101,10 @@ class Fixture:
98
101
  base_name = values.get("name")
99
102
  if base_name in chain:
100
103
  msg = f"Inheritance-Circle detected ({base_name} already in {chain})"
101
- raise ValueError(
102
- msg,
103
- )
104
+ raise ValueError(msg)
104
105
  if base_name == fixture_name:
105
106
  msg = f"Inheritance-Circle detected ({base_name} == {fixture_name})"
106
- raise ValueError(
107
- msg,
108
- )
107
+ raise ValueError(msg)
109
108
  chain.append(base_name)
110
109
  fixture_base = copy.copy(self[fixture_name])
111
110
  logger.debug("'%s' will inherit from '%s'", self.model_type, fixture_name)
@@ -181,7 +180,7 @@ class Fixtures:
181
180
  self.file_path = Path(__file__).parent.parent.resolve() / "data_models"
182
181
  else:
183
182
  self.file_path = file_path
184
- self.components: Dict[str, Fixture] = {}
183
+ self.components: dict[str, Fixture] = {}
185
184
  cache_file = cache_user_path / "fixtures.pickle"
186
185
  sheep_detect = Path("/lib/firmware/am335x-pru0-fw").exists()
187
186
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  import secrets
4
4
  from hashlib import pbkdf2_hmac
5
+ from typing import Annotated
6
+ from typing import Any
5
7
  from typing import Optional
6
8
  from typing import Union
7
9
  from uuid import uuid4
@@ -14,11 +16,10 @@ from pydantic import SecretStr
14
16
  from pydantic import StringConstraints
15
17
  from pydantic import model_validator
16
18
  from pydantic import validate_call
17
- from typing_extensions import Annotated
18
19
 
19
- from ..data_models.base.content import NameStr
20
- from ..data_models.base.content import SafeStr
21
- from ..data_models.base.shepherd import ShpModel
20
+ from shepherd_core.data_models.base.content import NameStr
21
+ from shepherd_core.data_models.base.content import SafeStr
22
+ from shepherd_core.data_models.base.shepherd import ShpModel
22
23
 
23
24
 
24
25
  @validate_call
@@ -63,7 +64,7 @@ class User(ShpModel):
63
64
 
64
65
  @model_validator(mode="before")
65
66
  @classmethod
66
- def query_database(cls, values: dict) -> dict:
67
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
67
68
  # TODO:
68
69
 
69
70
  # post correction
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.04.1"
3
+ version: str = "2025.05.2"
@@ -79,9 +79,9 @@ class DiodeTarget(TargetABC):
79
79
 
80
80
  def step(self, voltage_uV: int, *, pwr_good: bool) -> float:
81
81
  if pwr_good or not self.ctrl:
82
- V_CC = voltage_uV * 1e-6
83
- V_D = V_CC / 2
84
- I_R = I_D = 0
82
+ V_CC: float = voltage_uV * 1e-6
83
+ V_D: float = V_CC / 2
84
+ I_R = I_D = 0.0
85
85
  # there is no direct formular, but this iteration converges fast
86
86
  for _ in range(10):
87
87
  # low voltages tend to produce log(x<0)=err
@@ -17,9 +17,9 @@ Compromises:
17
17
  import math
18
18
  from typing import Optional
19
19
 
20
- from ..data_models import CalibrationEmulator
21
- from ..data_models.content.virtual_source import LUT_SIZE
22
- from ..data_models.content.virtual_source import ConverterPRUConfig
20
+ from shepherd_core.data_models import CalibrationEmulator
21
+ from shepherd_core.data_models.content.virtual_source import LUT_SIZE
22
+ from shepherd_core.data_models.content.virtual_source import ConverterPRUConfig
23
23
 
24
24
 
25
25
  class PruCalibration:
@@ -16,10 +16,8 @@ Compromises:
16
16
 
17
17
  """
18
18
 
19
- from typing import Tuple
20
-
21
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
22
- from ..logger import logger
19
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
20
+ from shepherd_core.logger import logger
23
21
 
24
22
 
25
23
  class VirtualHarvesterModel:
@@ -94,7 +92,7 @@ class VirtualHarvesterModel:
94
92
  self.voltage_nxt: int = 0
95
93
  self.current_nxt: int = 0
96
94
 
97
- def ivcurve_sample(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
95
+ def ivcurve_sample(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
98
96
  if self._cfg.window_size <= 1:
99
97
  return _voltage_uV, _current_nA
100
98
  if self._cfg.algorithm >= self.HRV_MPPT_OPT:
@@ -108,7 +106,7 @@ class VirtualHarvesterModel:
108
106
  # next line is only implied in C
109
107
  return _voltage_uV, _current_nA
110
108
 
111
- def ivcurve_2_cv(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
109
+ def ivcurve_2_cv(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
112
110
  compare_now = _voltage_uV < self.voltage_set_uV
113
111
  step_size_now = abs(_voltage_uV - self.voltage_last)
114
112
  distance_now = abs(_voltage_uV - self.voltage_set_uV)
@@ -146,7 +144,7 @@ class VirtualHarvesterModel:
146
144
  self.compare_last = compare_now
147
145
  return self.voltage_hold, self.current_hold
148
146
 
149
- def ivcurve_2_mppt_voc(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
147
+ def ivcurve_2_mppt_voc(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
150
148
  self.interval_step = self.interval_step + 1
151
149
  if self.interval_step >= self._cfg.interval_n:
152
150
  self.interval_step = 0
@@ -176,7 +174,7 @@ class VirtualHarvesterModel:
176
174
 
177
175
  return _voltage_uV, _current_nA
178
176
 
179
- def ivcurve_2_mppt_po(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
177
+ def ivcurve_2_mppt_po(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
180
178
  self.interval_step = self.interval_step + 1
181
179
  if self.interval_step >= self._cfg.interval_n:
182
180
  self.interval_step = 0
@@ -220,7 +218,7 @@ class VirtualHarvesterModel:
220
218
 
221
219
  return _voltage_uV, _current_nA
222
220
 
223
- def ivcurve_2_mppt_opt(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
221
+ def ivcurve_2_mppt_opt(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
224
222
  self.age_now += 1
225
223
  self.age_nxt += 1
226
224
 
@@ -13,11 +13,12 @@ from typing import Optional
13
13
 
14
14
  from tqdm import tqdm
15
15
 
16
- from .. import CalibrationHarvester
17
- from .. import Reader
18
- from .. import Writer
19
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
20
- from ..data_models.content.virtual_harvester import VirtualHarvesterConfig
16
+ from shepherd_core.data_models.base.calibration import CalibrationHarvester
17
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
18
+ from shepherd_core.data_models.content.virtual_harvester import VirtualHarvesterConfig
19
+ from shepherd_core.reader import Reader
20
+ from shepherd_core.writer import Writer
21
+
21
22
  from .virtual_harvester_model import VirtualHarvesterModel
22
23
 
23
24
 
@@ -53,7 +54,7 @@ def simulate_harvester(
53
54
  e_out_Ws = 0.0
54
55
 
55
56
  for _t, v_inp, i_inp in tqdm(
56
- file_inp.read_buffers(is_raw=True), total=file_inp.buffers_n, desc="Buffers", leave=False
57
+ file_inp.read(is_raw=True), total=file_inp.chunks_n, desc="Chunk", leave=False
57
58
  ):
58
59
  v_uV = cal_inp.voltage.raw_to_si(v_inp) * 1e6
59
60
  i_nA = cal_inp.current.raw_to_si(i_inp) * 1e9
@@ -12,11 +12,12 @@ NOTE: DO NOT OPTIMIZE -> stay close to original code-base
12
12
 
13
13
  from typing import Optional
14
14
 
15
- from ..data_models import CalibrationEmulator
16
- from ..data_models import EnergyDType
17
- from ..data_models import VirtualSourceConfig
18
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
19
- from ..data_models.content.virtual_source import ConverterPRUConfig
15
+ from shepherd_core.data_models.base.calibration import CalibrationEmulator
16
+ from shepherd_core.data_models.content.energy_environment import EnergyDType
17
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
18
+ from shepherd_core.data_models.content.virtual_source import ConverterPRUConfig
19
+ from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
20
+
20
21
  from .virtual_converter_model import PruCalibration
21
22
  from .virtual_converter_model import VirtualConverterModel
22
23
  from .virtual_harvester_model import VirtualHarvesterModel
@@ -14,13 +14,14 @@ from typing import Optional
14
14
  import numpy as np
15
15
  from tqdm import tqdm
16
16
 
17
- from .. import CalibrationEmulator
18
- from .. import Reader
19
- from .. import Writer
20
- from ..data_models import VirtualSourceConfig
21
- from ..logger import logger
22
- from . import VirtualSourceModel
17
+ from shepherd_core.data_models.base.calibration import CalibrationEmulator
18
+ from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
19
+ from shepherd_core.logger import logger
20
+ from shepherd_core.reader import Reader
21
+ from shepherd_core.writer import Writer
22
+
23
23
  from .target_model import TargetABC
24
+ from .virtual_source_model import VirtualSourceModel
24
25
 
25
26
 
26
27
  def simulate_source(
@@ -73,7 +74,7 @@ def simulate_source(
73
74
  stats_internal = None
74
75
 
75
76
  for _t, v_inp, i_inp in tqdm(
76
- file_inp.read_buffers(is_raw=True), total=file_inp.buffers_n, desc="Buffers", leave=False
77
+ file_inp.read(is_raw=True), total=file_inp.chunks_n, desc="Chunk", leave=False
77
78
  ):
78
79
  v_uV = 1e6 * cal_inp.voltage.raw_to_si(v_inp)
79
80
  i_nA = 1e9 * cal_inp.current.raw_to_si(i_inp)
shepherd_core/writer.py CHANGED
@@ -3,14 +3,13 @@
3
3
  import logging
4
4
  import math
5
5
  import pathlib
6
+ from collections.abc import Mapping
6
7
  from datetime import timedelta
7
8
  from itertools import product
8
9
  from pathlib import Path
9
10
  from types import TracebackType
10
11
  from typing import Any
11
- from typing import Mapping
12
12
  from typing import Optional
13
- from typing import Type
14
13
  from typing import Union
15
14
 
16
15
  import h5py
@@ -18,11 +17,10 @@ import numpy as np
18
17
  import yaml
19
18
  from pydantic import validate_call
20
19
  from typing_extensions import Self
21
- from yaml import Dumper
20
+ from yaml import Node
22
21
  from yaml import SafeDumper
23
- from yaml import ScalarNode
24
22
 
25
- from .commons import samplerate_sps_default
23
+ from .commons import SAMPLERATE_SPS_DEFAULT
26
24
  from .data_models.base.calibration import CalibrationEmulator as CalEmu
27
25
  from .data_models.base.calibration import CalibrationHarvester as CalHrv
28
26
  from .data_models.base.calibration import CalibrationSeries as CalSeries
@@ -34,13 +32,13 @@ from .reader import Reader
34
32
 
35
33
  # copy of core/models/base/shepherd - needed also here
36
34
  def path2str(
37
- dumper: Dumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
38
- ) -> ScalarNode:
35
+ dumper: SafeDumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
36
+ ) -> Node:
39
37
  """Add a yaml-representation for a specific datatype."""
40
38
  return dumper.represent_scalar("tag:yaml.org,2002:str", str(data.as_posix()))
41
39
 
42
40
 
43
- def time2int(dumper: Dumper, data: timedelta) -> ScalarNode:
41
+ def time2int(dumper: SafeDumper, data: timedelta) -> Node:
44
42
  """Add a yaml-representation for a specific datatype."""
45
43
  return dumper.represent_scalar("tag:yaml.org,2002:int", str(int(data.total_seconds())))
46
44
 
@@ -93,11 +91,10 @@ class Writer(Reader):
93
91
 
94
92
  """
95
93
 
96
- comp_default: int = 1
97
- mode_default: str = "harvester"
98
- datatype_default: EnergyDType = EnergyDType.ivsample
94
+ MODE_DEFAULT: str = "harvester"
95
+ DATATYPE_DEFAULT: EnergyDType = EnergyDType.ivsample
99
96
 
100
- _chunk_shape: tuple = (Reader.samples_per_buffer,)
97
+ _CHUNK_SHAPE: tuple = (Reader.CHUNK_SAMPLES_N,)
101
98
 
102
99
  @validate_call
103
100
  def __init__(
@@ -111,7 +108,7 @@ class Writer(Reader):
111
108
  *,
112
109
  modify_existing: bool = False,
113
110
  force_overwrite: bool = False,
114
- verbose: Optional[bool] = True,
111
+ verbose: bool = True,
115
112
  ) -> None:
116
113
  self._modify = modify_existing
117
114
  if compression is not None:
@@ -124,41 +121,42 @@ class Writer(Reader):
124
121
  # -> logger gets configured in reader()
125
122
 
126
123
  if self._modify or force_overwrite or not file_path.exists():
127
- self.file_path: Path = file_path.resolve()
128
- self._logger.info("Storing data to '%s'", self.file_path)
124
+ file_path = file_path.resolve()
125
+ self._logger.info("Storing data to '%s'", file_path)
129
126
  elif file_path.exists() and not file_path.is_file():
130
127
  msg = f"Path is not a file ({file_path})"
131
128
  raise TypeError(msg)
132
129
  else:
133
130
  base_dir = file_path.resolve().parents[0]
134
- self.file_path = unique_path(base_dir / file_path.stem, file_path.suffix)
131
+ file_path_new = unique_path(base_dir / file_path.stem, file_path.suffix)
135
132
  self._logger.warning(
136
133
  "File '%s' already exists -> storing under '%s' instead",
137
134
  file_path,
138
- self.file_path.name,
135
+ file_path_new.name,
139
136
  )
137
+ file_path = file_path_new
140
138
 
141
139
  # open file
142
140
  if self._modify:
143
- self.h5file = h5py.File(self.file_path, "r+") # = rw
141
+ self.h5file = h5py.File(file_path, "r+") # = rw
144
142
  else:
145
- if not self.file_path.parent.exists():
146
- self.file_path.parent.mkdir(parents=True)
147
- self.h5file = h5py.File(self.file_path, "w")
143
+ if not file_path.parent.exists():
144
+ file_path.parent.mkdir(parents=True)
145
+ self.h5file = h5py.File(file_path, "w")
148
146
  # ⤷ write, truncate if exist
149
147
  self._create_skeleton()
150
148
 
151
149
  # Handle Mode
152
- if isinstance(mode, str) and mode not in self.mode_dtype_dict:
153
- msg = f"Can't handle mode '{mode}' (choose one of {self.mode_dtype_dict})"
150
+ if isinstance(mode, str) and mode not in self.MODE_TO_DTYPE:
151
+ msg = f"Can't handle mode '{mode}' (choose one of {self.MODE_TO_DTYPE})"
154
152
  raise ValueError(msg)
155
153
 
156
154
  if mode is not None:
157
155
  self.h5file.attrs["mode"] = mode
158
156
  if "mode" not in self.h5file.attrs:
159
- self.h5file.attrs["mode"] = self.mode_default
157
+ self.h5file.attrs["mode"] = self.MODE_DEFAULT
160
158
 
161
- _dtypes = self.mode_dtype_dict[self.get_mode()]
159
+ _dtypes = self.MODE_TO_DTYPE[self.get_mode()]
162
160
 
163
161
  # Handle Datatype
164
162
  if isinstance(datatype, str):
@@ -170,7 +168,7 @@ class Writer(Reader):
170
168
  if isinstance(datatype, EnergyDType):
171
169
  self.h5file["data"].attrs["datatype"] = datatype.name
172
170
  if "datatype" not in self.h5file["data"].attrs:
173
- self.h5file["data"].attrs["datatype"] = self.datatype_default.name
171
+ self.h5file["data"].attrs["datatype"] = self.DATATYPE_DEFAULT.name
174
172
  if self.get_datatype() not in _dtypes:
175
173
  msg = (
176
174
  f"Can't handle value '{self.get_datatype()}' of datatype (choose one of {_dtypes})"
@@ -204,7 +202,7 @@ class Writer(Reader):
204
202
  settings = list(self.h5file.id.get_access_plist().get_cache())
205
203
  self._logger.debug("H5Py Cache_setting=%s (_mdc, _nslots, _nbytes, _w0)", settings)
206
204
 
207
- super().__init__(file_path=None, verbose=verbose)
205
+ super().__init__(file_path=file_path, verbose=verbose)
208
206
 
209
207
  def __enter__(self) -> Self:
210
208
  super().__enter__()
@@ -212,7 +210,7 @@ class Writer(Reader):
212
210
 
213
211
  def __exit__(
214
212
  self,
215
- typ: Optional[Type[BaseException]] = None,
213
+ typ: Optional[type[BaseException]] = None,
216
214
  exc: Optional[BaseException] = None,
217
215
  tb: Optional[TracebackType] = None,
218
216
  extra_arg: int = 0,
@@ -242,7 +240,7 @@ class Writer(Reader):
242
240
  # Store voltage and current samples in the data group,
243
241
  # both are stored as 4 Byte unsigned int
244
242
  grp_data = self.h5file.create_group("data")
245
- # the size of window_samples-attribute in harvest-data indicates ivcurves as input
243
+ # the size of window_samples-attribute in harvest-data indicates ivsurface / curves as input
246
244
  # -> emulator uses virtual-harvester, field will be adjusted by .embed_config()
247
245
  grp_data.attrs["window_samples"] = 0
248
246
 
@@ -251,7 +249,7 @@ class Writer(Reader):
251
249
  (0,),
252
250
  dtype="u8",
253
251
  maxshape=(None,),
254
- chunks=self._chunk_shape,
252
+ chunks=self._CHUNK_SHAPE,
255
253
  compression=self._compression,
256
254
  )
257
255
  grp_data["time"].attrs["unit"] = "s"
@@ -262,7 +260,7 @@ class Writer(Reader):
262
260
  (0,),
263
261
  dtype="u4",
264
262
  maxshape=(None,),
265
- chunks=self._chunk_shape,
263
+ chunks=self._CHUNK_SHAPE,
266
264
  compression=self._compression,
267
265
  )
268
266
  grp_data["current"].attrs["unit"] = "A"
@@ -273,7 +271,7 @@ class Writer(Reader):
273
271
  (0,),
274
272
  dtype="u4",
275
273
  maxshape=(None,),
276
- chunks=self._chunk_shape,
274
+ chunks=self._CHUNK_SHAPE,
277
275
  compression=self._compression,
278
276
  )
279
277
  grp_data["voltage"].attrs["unit"] = "V"
@@ -289,7 +287,7 @@ class Writer(Reader):
289
287
 
290
288
  Args:
291
289
  ----
292
- timestamp: just start of buffer or whole ndarray
290
+ timestamp: just start of chunk (1 timestamp) or whole ndarray
293
291
  voltage: ndarray as raw unsigned integers
294
292
  current: ndarray as raw unsigned integers
295
293
 
@@ -333,7 +331,7 @@ class Writer(Reader):
333
331
  Args:
334
332
  ----
335
333
  timestamp: python timestamp (time.time()) in seconds (si-unit)
336
- -> provide start of buffer or whole ndarray
334
+ -> provide start of chunk (1 timestamp) or whole ndarray
337
335
  voltage: ndarray in physical-unit V
338
336
  current: ndarray in physical-unit A
339
337
 
@@ -345,16 +343,16 @@ class Writer(Reader):
345
343
  self.append_iv_data_raw(timestamp, voltage, current)
346
344
 
347
345
  def _align(self) -> None:
348
- """Align datasets with buffer-size of shepherd."""
346
+ """Align datasets with chunk-size of shepherd."""
349
347
  self._refresh_file_stats()
350
- n_buff = self.ds_voltage.size / self.samples_per_buffer
351
- size_new = int(math.floor(n_buff) * self.samples_per_buffer)
348
+ chunks_n = self.ds_voltage.size / self.CHUNK_SAMPLES_N
349
+ size_new = int(math.floor(chunks_n) * self.CHUNK_SAMPLES_N)
352
350
  if size_new < self.ds_voltage.size:
353
- if self.samplerate_sps != samplerate_sps_default:
351
+ if self.samplerate_sps != SAMPLERATE_SPS_DEFAULT:
354
352
  self._logger.debug("skipped alignment due to altered samplerate")
355
353
  return
356
354
  self._logger.info(
357
- "aligning with buffer-size, discarding last %d entries",
355
+ "aligning with chunk-size, discarding last %d entries",
358
356
  self.ds_voltage.size - size_new,
359
357
  )
360
358
  self.ds_time.resize((size_new,))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.4.1
3
+ Version: 2025.5.2
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>
@@ -18,7 +18,6 @@ Classifier: Development Status :: 5 - Production/Stable
18
18
  Classifier: Intended Audience :: Developers
19
19
  Classifier: Intended Audience :: Information Technology
20
20
  Classifier: Intended Audience :: Science/Research
21
- Classifier: Programming Language :: Python :: 3.8
22
21
  Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
@@ -27,7 +26,7 @@ Classifier: Programming Language :: Python :: 3.13
27
26
  Classifier: License :: OSI Approved :: MIT License
28
27
  Classifier: Operating System :: OS Independent
29
28
  Classifier: Natural Language :: English
30
- Requires-Python: >=3.8
29
+ Requires-Python: >=3.9
31
30
  Description-Content-Type: text/markdown
32
31
  Requires-Dist: h5py
33
32
  Requires-Dist: numpy
@@ -0,0 +1,81 @@
1
+ shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,1270
2
+ shepherd_core/calibration_hw_def.py,sha256=aL94bA1Sf14L5A3PLdVvQVYtGi28S4NUWA65wbim8bw,2895
3
+ shepherd_core/commons.py,sha256=rTxtndtiJ4cOHYRPRbdZdqp6T90CKFFN-I-YAFzhm4Q,200
4
+ shepherd_core/logger.py,sha256=i8j3alm8icAx_h1_IZ6SgVgC9W5J7S-bDc31mPDWl-w,1812
5
+ shepherd_core/reader.py,sha256=EYgpL09DMrACiDHoG3LbgSJrci4pCPY3r13X0XVNVLI,29179
6
+ shepherd_core/version.py,sha256=p9ZnvPFg9wu9KOtST8r_-f6-9CC_FMIG7IhKqfjDez4,76
7
+ shepherd_core/writer.py,sha256=_ji3X2bb4JKZZm4uz8DO9uG_WoHL5Zpg6_Na36WAF5o,14609
8
+ shepherd_core/data_models/__init__.py,sha256=Fl9Lhdo32mrdF232ZjOnkawQYeFsUbTS4FOUrvmSzlM,1909
9
+ shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
10
+ shepherd_core/data_models/virtual_source_doc.txt,sha256=OK_7zYbLvr6cEj3KaUcWwZJ9naoFB2KwAaXudbhzouQ,6076
11
+ shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
+ shepherd_core/data_models/base/cal_measurement.py,sha256=c-vjACNxsQY8LU2Msw0COrsTY-8pToJ5ZWkOztJ9tVY,3380
13
+ shepherd_core/data_models/base/calibration.py,sha256=oUTfY6iUWUbBbOw5aMCRkdEfHzIV8aUrhwqek0QzPJM,10849
14
+ shepherd_core/data_models/base/content.py,sha256=t3hw5Aes4-tVrWGOMSBGpcD642VM1RAWIo9jtbXifd0,2452
15
+ shepherd_core/data_models/base/shepherd.py,sha256=P1xW10_vIbGNyQ3Ary67vyovw9kmDtXhN-yPFYR-0Is,6085
16
+ shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
17
+ shepherd_core/data_models/base/wrapper.py,sha256=7QwvI30GuORH7WmyGLnRMsZ3xkRRkXIAvZ-pYRAL-WI,755
18
+ shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKsKyMK8o6Kfaby5vlY,525
19
+ shepherd_core/data_models/content/_external_fixtures.yaml,sha256=Iv0T5cN8BFVELuLtmxml2-xwcMDMZWF7LOu_TM3RIJ8,12184
20
+ shepherd_core/data_models/content/energy_environment.py,sha256=DSTB3CE3XEBXaL16z4FVaHSyLnp0bfxUgw3SnQrUpf0,1628
21
+ shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
22
+ shepherd_core/data_models/content/firmware.py,sha256=KLByo1GuUMJ8ZbM6WccB8IJArorNetBxqOpuUb2_QIE,5926
23
+ shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=QLyOLH0Q1E8NKWgWyMd4e65iEA13CVHmdf7kLf4SqFk,11287
25
+ shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=4eNQyFI9LozpButOTlBQ07T0MFCaPEYIxwtedMjUf3U,4575
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=4TAk7jgXCQh6MMnT0Ijw_AdE6YPgrbSwFfb8F1V6RVE,15565
27
+ shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=tsSJ1mM4OSPSeNkqVoZlDQ8Az3N8KeTXd0oA2JKd-hk,11201
28
+ shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
29
+ shepherd_core/data_models/experiment/experiment.py,sha256=oFOpyxHmwCZmbJAejyoqAAjarA2ZgnNYEx6Qf7TPjqU,4178
30
+ shepherd_core/data_models/experiment/observer_features.py,sha256=xCaN2wymG-ygEy19jTIvY9YyitdjPWepDH5UjXdq8Y8,8315
31
+ shepherd_core/data_models/experiment/target_config.py,sha256=nlOZvLC5C6Pjn-ZEXLMr5WZlC1edixL_l3rWi93UPZE,4082
32
+ shepherd_core/data_models/task/__init__.py,sha256=m7_OK05djkoDt-XwKQ6gPT7qIcCUoyBxZgSXvD3HmzQ,3386
33
+ shepherd_core/data_models/task/emulation.py,sha256=-JzRm-xRp421Xl4lBkT52AEhTkb8ZqH9C1Z8CeHPHF8,7380
34
+ shepherd_core/data_models/task/firmware_mod.py,sha256=WLmevU9-Q5QnyANbkrSCdFbnVuSi8N8fXdQ2l_Um9e8,3034
35
+ shepherd_core/data_models/task/harvest.py,sha256=rQ5Aq40m4WHpEFfiwn309GjLIPu4aKzF4Fvbo4j7PLs,3470
36
+ shepherd_core/data_models/task/observer_tasks.py,sha256=icUPtoIqic8IpE0U9Fc7btMj4CpYx1PoAu_wAqqZkU8,3608
37
+ shepherd_core/data_models/task/programming.py,sha256=jX_5biD6BGkBdIS652CajXWm0KOgY3w1Z3AOJIFgTws,2573
38
+ shepherd_core/data_models/task/testbed_tasks.py,sha256=J3aOSVVG1KvEI_83j0TK0gc5rN_v_T-URFRhrEs8czY,2176
39
+ shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
40
+ shepherd_core/data_models/testbed/cape.py,sha256=vfS05D0rC1-_wMiHeLw69VE9PxXC6PHl9ndtrv219_k,1396
41
+ shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=uwZxe6hsqvofn5tzg4sffjbVtTVUkextL1GCri_z2A4,2197
42
+ shepherd_core/data_models/testbed/gpio.py,sha256=pOY_7Zs32sRGuqJZrZDrjvr69QkFXC8iXgav7kk2heY,2334
43
+ shepherd_core/data_models/testbed/gpio_fixture.yaml,sha256=yXvoXAau2hancKi2yg1xIkErPWQa6gIxNUG3y8JuF9Y,3076
44
+ shepherd_core/data_models/testbed/mcu.py,sha256=fuq2AWbVFbbzPRPCgIeMNFhJhVNCIsmpjFagnOXkjbY,1514
45
+ shepherd_core/data_models/testbed/mcu_fixture.yaml,sha256=bOYXdQY-6JYesxOkZAT8WvuGsdUc_MW4dkAmopLL8RM,507
46
+ shepherd_core/data_models/testbed/observer.py,sha256=RO7i9TmHcxx69P-EHN59M9MAJetoHZg5qJUD5SEtrcg,3386
47
+ shepherd_core/data_models/testbed/observer_fixture.yaml,sha256=jqAatTebWrShXBlhqkCUQIrtVqEjl7RVDR9mosS2LJQ,4807
48
+ shepherd_core/data_models/testbed/target.py,sha256=W7U9nCz_xWMRCCgaGuqOV4dGi4ZeQ1SAp7W2FqCkanU,1978
49
+ shepherd_core/data_models/testbed/target_fixture.old1,sha256=ivH9uTgC2Z4L_J4KDHAyIHZnB7iy9EUu1yM3M0s1lQQ,3675
50
+ shepherd_core/data_models/testbed/target_fixture.yaml,sha256=LyOJa7yH17tHIGC25jlkLJ_DKnbFSoGhD-6Uh6q_HqQ,4132
51
+ shepherd_core/data_models/testbed/testbed.py,sha256=K6llqMTn-AU18XXRXdhzibIx-n4CQjtrXnLwqxOQesI,3828
52
+ shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=LaaU8mXLQboYWYNPpA3CWmMPy2w6T6cve6gLgDaA3l0,717
53
+ shepherd_core/decoder_waveform/__init__.py,sha256=-ohGz0fA2tKxUJk4FAQXKtI93d6YGdy0CrkdhOod1QU,120
54
+ shepherd_core/decoder_waveform/uart.py,sha256=vuw9fKwZb_1mMtQ5fdiZN8Cr1YWSgYKar9FIMK8Bogo,11084
55
+ shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5ReVAA,2090
56
+ shepherd_core/fw_tools/converter.py,sha256=V74V-82VVkvkZ2i4HI7K_291_FGZNjxVdX6eO7Y3Zks,3657
57
+ shepherd_core/fw_tools/converter_elf.py,sha256=GQDVqIqMW4twNMvZIV3sowFMezhs2TN-IYREjRP7Xt4,1089
58
+ shepherd_core/fw_tools/patcher.py,sha256=ISbnA2ZLTIV2JZh_b3GorNDGNrLaBN0fFnFnyh-smNU,3946
59
+ shepherd_core/fw_tools/validation.py,sha256=KxOBtAgqUemZC_Lj1_xtV6AMpGLU04_epxihMF3RX7U,4792
60
+ shepherd_core/inventory/__init__.py,sha256=yQxP55yV61xXWfZSSzekQQYopPZCspFpHSyG7VTqtpg,3819
61
+ shepherd_core/inventory/python.py,sha256=pvugNgLZaDllIXX_KiuvpcWUWlJtD2IUKYDRjcTGQss,1262
62
+ shepherd_core/inventory/system.py,sha256=qFTRVXClQZoCeV2KM8RAbst8XL7IaJwwV0pGHPZSJs4,3325
63
+ shepherd_core/inventory/target.py,sha256=zLUNQs2FE7jMDsiRtbeAwqRVcit4e2F1UUOF04xw-XY,520
64
+ shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1fwUtWW8Vg_4,234
65
+ shepherd_core/testbed_client/cache_path.py,sha256=tS0er9on5fw8wddMCt1jkc2uyYOdSTvX_UmfmYJf6tY,445
66
+ shepherd_core/testbed_client/client_abc_fix.py,sha256=xMch4y6vMMLRiqFEry2zSnYuLM6Ay-fY0dunXMz9Hws,4250
67
+ shepherd_core/testbed_client/client_web.py,sha256=zqY4MGMWfTl2_0T1qrQl5Vz9SPjl-wMj8O5yMMQyo9I,6044
68
+ shepherd_core/testbed_client/fixtures.py,sha256=ftPJwiOn-USroIee2Al3evR3WxsA1SxERusoZ-jbkVk,9223
69
+ shepherd_core/testbed_client/user_model.py,sha256=f4WZ8IvSCt3s1RG_Bhi43ojiJQsZYyolJ3Ft8HNRYas,2175
70
+ shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
71
+ shepherd_core/vsource/target_model.py,sha256=BjOlwX_gIOJ91e4OOLB4_OsCpuhq9vm57ERjM-iBhAM,5129
72
+ shepherd_core/vsource/virtual_converter_model.py,sha256=jUnJwP-FFDMtXm1NCLUJfZTvImYH4_A9rc_lXVAZ33I,11628
73
+ shepherd_core/vsource/virtual_harvester_model.py,sha256=8uPTMDth4KI5XarkX9EJtMRZ_8eWUeWCZxgmx4be3Fs,9763
74
+ shepherd_core/vsource/virtual_harvester_simulation.py,sha256=eK9uCn-j_8xSTa7BQhG879ep0oZDeatlnF31LeWEDiM,2547
75
+ shepherd_core/vsource/virtual_source_model.py,sha256=-8RwBrkIdO0g4zpo7XHnqv8F_qNh_qf5hxEUJoIuAmg,3164
76
+ shepherd_core/vsource/virtual_source_simulation.py,sha256=6voU1TAB2P0xuVu1bnyKa-xW9rwYXUC6f06gYtUDHTs,5273
77
+ shepherd_core-2025.5.2.dist-info/METADATA,sha256=nACqRWB12RTUhFeUPiKcnw7UvP2YEV4ZNg-kKRGkWSw,7756
78
+ shepherd_core-2025.5.2.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
79
+ shepherd_core-2025.5.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
+ shepherd_core-2025.5.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
+ shepherd_core-2025.5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5