shepherd-core 2025.6.1__py3-none-any.whl → 2025.6.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 (29) hide show
  1. shepherd_core/__init__.py +2 -2
  2. shepherd_core/data_models/__init__.py +1 -1
  3. shepherd_core/data_models/base/shepherd.py +28 -11
  4. shepherd_core/data_models/content/firmware.py +4 -4
  5. shepherd_core/data_models/content/virtual_harvester.py +3 -3
  6. shepherd_core/data_models/content/virtual_source.py +5 -5
  7. shepherd_core/data_models/experiment/observer_features.py +2 -2
  8. shepherd_core/data_models/task/__init__.py +8 -4
  9. shepherd_core/data_models/task/emulation.py +14 -5
  10. shepherd_core/data_models/task/firmware_mod.py +14 -5
  11. shepherd_core/data_models/task/harvest.py +5 -0
  12. shepherd_core/data_models/task/helper_paths.py +15 -0
  13. shepherd_core/data_models/task/observer_tasks.py +16 -15
  14. shepherd_core/data_models/task/programming.py +8 -2
  15. shepherd_core/data_models/task/testbed_tasks.py +12 -0
  16. shepherd_core/decoder_waveform/uart.py +7 -7
  17. shepherd_core/fw_tools/patcher.py +6 -6
  18. shepherd_core/fw_tools/validation.py +2 -2
  19. shepherd_core/inventory/system.py +2 -2
  20. shepherd_core/logger.py +3 -3
  21. shepherd_core/testbed_client/fixtures.py +5 -5
  22. shepherd_core/version.py +1 -1
  23. shepherd_core/vsource/virtual_harvester_model.py +2 -2
  24. shepherd_core/vsource/virtual_source_simulation.py +2 -2
  25. {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.2.dist-info}/METADATA +1 -1
  26. {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.2.dist-info}/RECORD +29 -28
  27. {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.2.dist-info}/WHEEL +0 -0
  28. {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.2.dist-info}/top_level.txt +0 -0
  29. {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.2.dist-info}/zip-safe +0 -0
shepherd_core/__init__.py CHANGED
@@ -17,7 +17,7 @@ from .data_models.task.emulation import Compression
17
17
  from .inventory import Inventory
18
18
  from .logger import get_verbose_level
19
19
  from .logger import increase_verbose_level
20
- from .logger import logger
20
+ from .logger import log
21
21
  from .reader import Reader
22
22
  from .testbed_client.client_web import WebClient
23
23
  from .version import version
@@ -41,5 +41,5 @@ __all__ = [
41
41
  "increase_verbose_level",
42
42
  "local_now",
43
43
  "local_tz",
44
- "logger",
44
+ "log",
45
45
  ]
@@ -3,7 +3,7 @@
3
3
  Public models are directly referenced here and are usable like:
4
4
 
5
5
  '''python
6
- from shepherd-core import data_models
6
+ from shepherd_core import data_models
7
7
 
8
8
  cdata = data_models.CapeData(serial_number="A123")
9
9
  '''
@@ -2,6 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  import pathlib
5
+ import pickle
5
6
  from collections.abc import Generator
6
7
  from datetime import timedelta
7
8
  from ipaddress import IPv4Address
@@ -126,10 +127,12 @@ class ShpModel(BaseModel):
126
127
  comment: Optional[str] = None,
127
128
  *,
128
129
  minimal: bool = True,
130
+ use_pickle: bool = False,
129
131
  ) -> Path:
130
132
  """Store data to yaml in a wrapper.
131
133
 
132
134
  minimal: stores minimal set (filters out unset & default parameters)
135
+ pickle: uses pickle to serialize data, on BBB >100x faster for large files
133
136
  comment: documentation.
134
137
  """
135
138
  model_dict = self.model_dump(exclude_unset=minimal)
@@ -139,25 +142,39 @@ class ShpModel(BaseModel):
139
142
  created=local_now(),
140
143
  parameters=model_dict,
141
144
  )
142
- model_yaml = yaml.safe_dump(
143
- model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal),
144
- default_flow_style=False,
145
- sort_keys=False,
146
- )
145
+ if use_pickle:
146
+ model_serial = pickle.dumps(model_dict, fix_imports=True)
147
+ model_path = Path(path).resolve().with_suffix(".pickle")
148
+ else:
149
+ # TODO: x64 windows supports CSafeLoader/dumper,
150
+ # there are examples that replace load if avail
151
+ model_serial = yaml.safe_dump(
152
+ model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal),
153
+ default_flow_style=False,
154
+ sort_keys=False,
155
+ )
156
+ model_path = Path(path).resolve().with_suffix(".yaml")
147
157
  # TODO: handle directory
148
- model_path = Path(path).resolve().with_suffix(".yaml")
158
+
149
159
  if not model_path.parent.exists():
150
160
  model_path.parent.mkdir(parents=True)
151
161
  with model_path.open("w") as f:
152
- f.write(model_yaml)
162
+ f.write(model_serial)
153
163
  return model_path
154
164
 
155
165
  @classmethod
156
166
  def from_file(cls, path: Union[str, Path]) -> Self:
157
- """Load from yaml."""
158
- with Path(path).open() as shp_file:
159
- shp_dict = yaml.safe_load(shp_file)
160
- shp_wrap = Wrapper(**shp_dict)
167
+ """Load from YAML or pickle file."""
168
+ path: Path = Path(path)
169
+ if not Path(path).exists():
170
+ raise FileNotFoundError
171
+ if path.suffix.lower() == ".pickle":
172
+ with Path(path).open("rb") as shp_file:
173
+ shp_wrap = pickle.load(shp_file, fix_imports=True) # noqa: S301
174
+ else:
175
+ with Path(path).open() as shp_file:
176
+ shp_dict = yaml.safe_load(shp_file)
177
+ shp_wrap = Wrapper(**shp_dict)
161
178
  if shp_wrap.datatype != cls.__name__:
162
179
  raise ValueError("Model in file does not match the requirement")
163
180
  return cls(**shp_wrap.parameters)
@@ -19,7 +19,7 @@ from typing_extensions import Unpack
19
19
  from shepherd_core import fw_tools
20
20
  from shepherd_core.data_models.base.content import ContentModel
21
21
  from shepherd_core.data_models.testbed.mcu import MCU
22
- from shepherd_core.logger import logger
22
+ from shepherd_core.logger import log
23
23
  from shepherd_core.testbed_client import tb_client
24
24
 
25
25
  from .firmware_datatype import FirmwareDType
@@ -135,8 +135,8 @@ class Firmware(ContentModel, title="Firmware of Target"):
135
135
  if ("nrf52" in arch or "arm" in arch) and not fw_tools.is_elf_nrf52(file):
136
136
  raise ValueError("File is not a ELF for nRF52")
137
137
  except RuntimeError:
138
- logger.warning("ObjCopy not found -> Arch of Firmware can't be verified")
139
- logger.debug("ELF-File '%s' has arch: %s", file.name, arch)
138
+ log.warning("ObjCopy not found -> Arch of Firmware can't be verified")
139
+ log.debug("ELF-File '%s' has arch: %s", file.name, arch)
140
140
  if "mcu" not in kwargs:
141
141
  kwargs["mcu"] = arch_to_mcu[arch]
142
142
 
@@ -159,7 +159,7 @@ class Firmware(ContentModel, title="Firmware of Target"):
159
159
  match = self.data_hash == hash_new
160
160
 
161
161
  if not match:
162
- logger.warning("FW-Hash does not match with stored value!")
162
+ log.warning("FW-Hash does not match with stored value!")
163
163
  # TODO: it might be more appropriate to raise here
164
164
  return match
165
165
 
@@ -14,7 +14,7 @@ from shepherd_core.config import config
14
14
  from shepherd_core.data_models.base.calibration import CalibrationHarvester
15
15
  from shepherd_core.data_models.base.content import ContentModel
16
16
  from shepherd_core.data_models.base.shepherd import ShpModel
17
- from shepherd_core.logger import logger
17
+ from shepherd_core.logger import log
18
18
  from shepherd_core.testbed_client import tb_client
19
19
 
20
20
  from .energy_environment import EnergyDType
@@ -310,7 +310,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
310
310
  if values["name"] == "neutral":
311
311
  # TODO: same test is later done in calc_algorithm_num() again
312
312
  raise ValueError("Resulting Harvester can't be neutral")
313
- logger.debug("VHrv-Inheritances: %s", chain)
313
+ log.debug("VHrv-Inheritances: %s", chain)
314
314
 
315
315
  # post corrections -> should be in separate validator
316
316
  cal = CalibrationHarvester() # TODO: as argument?
@@ -372,7 +372,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
372
372
  duration_ms = min(max(self.duration_ms, time_min_ms), interval_ms)
373
373
  _ratio = (duration_ms / interval_ms) / (self.duration_ms / self.interval_ms)
374
374
  if (_ratio - 1) > 0.1:
375
- logger.debug(
375
+ log.debug(
376
376
  "Ratio between interval & duration has changed "
377
377
  "more than 10%% due to constraints (%.4f)",
378
378
  _ratio,
@@ -10,7 +10,7 @@ from typing_extensions import Self
10
10
  from shepherd_core.config import config
11
11
  from shepherd_core.data_models.base.content import ContentModel
12
12
  from shepherd_core.data_models.base.shepherd import ShpModel
13
- from shepherd_core.logger import logger
13
+ from shepherd_core.logger import log
14
14
  from shepherd_core.testbed_client import tb_client
15
15
 
16
16
  from .energy_environment import EnergyDType
@@ -128,7 +128,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
128
128
  def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
129
129
  values, chain = tb_client.try_completing_model(cls.__name__, values)
130
130
  values = tb_client.fill_in_user_data(values)
131
- logger.debug("VSrc-Inheritances: %s", chain)
131
+ log.debug("VSrc-Inheritances: %s", chain)
132
132
  return values
133
133
 
134
134
  @model_validator(mode="after")
@@ -187,7 +187,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
187
187
  if not (isinstance(dV_output_en_thrs_mV, (int, float)) and (dV_output_en_thrs_mV >= 0)):
188
188
  dV_output_en_thrs_mV = 0
189
189
  if not (isinstance(dV_output_imed_low_mV, (int, float)) and (dV_output_imed_low_mV >= 0)):
190
- logger.warning("VSrc: C_output shouldn't be larger than C_intermediate")
190
+ log.warning("VSrc: C_output shouldn't be larger than C_intermediate")
191
191
  dV_output_imed_low_mV = 0
192
192
 
193
193
  # decide which hysteresis-thresholds to use for buck-converter
@@ -224,7 +224,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
224
224
  enable_storage = self.C_intermediate_uF > 0
225
225
  enable_boost = self.enable_boost and enable_storage
226
226
  if enable_boost != self.enable_boost:
227
- logger.warning("VSrc - boost was disabled due to missing storage capacitor!")
227
+ log.warning("VSrc - boost was disabled due to missing storage capacitor!")
228
228
  enable_feedback = (
229
229
  self.enable_feedback_to_hrv
230
230
  and enable_storage
@@ -235,7 +235,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
235
235
  reason = "enabled boost, " if enable_boost else ""
236
236
  reason += "" if dtype_in == EnergyDType.ivcurve else "input not ivcurve, "
237
237
  reason += "" if enable_storage else "no storage capacitor"
238
- logger.warning("VSRC - feedback to harvester was disabled! Reasons: %s", reason)
238
+ log.warning("VSRC - feedback to harvester was disabled! Reasons: %s", reason)
239
239
  return (
240
240
  1 * int(enable_storage)
241
241
  + 2 * int(enable_boost)
@@ -13,9 +13,9 @@ from pydantic import model_validator
13
13
  from typing_extensions import Self
14
14
  from typing_extensions import deprecated
15
15
 
16
- from shepherd_core import logger
17
16
  from shepherd_core.data_models.base.shepherd import ShpModel
18
17
  from shepherd_core.data_models.testbed.gpio import GPIO
18
+ from shepherd_core.logger import log
19
19
 
20
20
  # defaults (pre-init complex types)
21
21
  zero_duration = timedelta(seconds=0)
@@ -190,7 +190,7 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
190
190
  if self.duration and self.duration.total_seconds() < 0:
191
191
  raise ValueError("Duration can't be negative.")
192
192
  if self.uart_decode:
193
- logger.error(
193
+ log.error(
194
194
  "Feature GpioTracing.uart_decode reserved for future use. "
195
195
  "Use UartLogging or manually decode serial with the provided waveform decoder."
196
196
  )
@@ -3,6 +3,7 @@
3
3
  These models import externally from all other model-modules!
4
4
  """
5
5
 
6
+ import pickle
6
7
  from pathlib import Path
7
8
  from typing import Optional
8
9
  from typing import Union
@@ -11,7 +12,7 @@ import yaml
11
12
 
12
13
  from shepherd_core.data_models.base.shepherd import ShpModel
13
14
  from shepherd_core.data_models.base.wrapper import Wrapper
14
- from shepherd_core.logger import logger
15
+ from shepherd_core.logger import log
15
16
 
16
17
  from .emulation import Compression
17
18
  from .emulation import EmulationTask
@@ -44,7 +45,10 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
44
45
  if isinstance(config, str):
45
46
  config = Path(config)
46
47
 
47
- if isinstance(config, Path):
48
+ if isinstance(config, Path) and config.exists() and config.suffix.lower() == ".pickle":
49
+ with config.resolve().open("rb") as shp_file:
50
+ shp_wrap = pickle.load(shp_file, fix_imports=True) # noqa: S301
51
+ elif isinstance(config, Path) and config.exists() and config.suffix.lower() == ".yaml":
48
52
  with config.resolve().open() as shp_file:
49
53
  shp_dict = yaml.safe_load(shp_file)
50
54
  shp_wrap = Wrapper(**shp_dict)
@@ -59,12 +63,12 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
59
63
 
60
64
  if shp_wrap.datatype == TestbedTasks.__name__:
61
65
  if observer is None:
62
- logger.debug(
66
+ log.debug(
63
67
  "Task-Set contained TestbedTasks & no observer was provided -> will return TB-Tasks"
64
68
  )
65
69
  return shp_wrap
66
70
  tbt = TestbedTasks(**shp_wrap.parameters)
67
- logger.debug("Loading Testbed-Tasks %s for %s", tbt.name, observer)
71
+ log.debug("Loading Testbed-Tasks %s for %s", tbt.name, observer)
68
72
  obt = tbt.get_observer_tasks(observer)
69
73
  if obt is None:
70
74
  msg = f"Observer '{observer}' is not in TestbedTask-Set"
@@ -1,10 +1,12 @@
1
1
  """Configuration for the Observer in Emulation-Mode."""
2
2
 
3
3
  import copy
4
+ from collections.abc import Set as AbstractSet
4
5
  from datetime import datetime
5
6
  from datetime import timedelta
6
7
  from enum import Enum
7
8
  from pathlib import Path
9
+ from pathlib import PurePosixPath
8
10
  from typing import Annotated
9
11
  from typing import Optional
10
12
  from typing import Union
@@ -28,7 +30,9 @@ from shepherd_core.data_models.experiment.observer_features import UartLogging
28
30
  from shepherd_core.data_models.experiment.target_config import vsrc_neutral
29
31
  from shepherd_core.data_models.testbed import Testbed
30
32
  from shepherd_core.data_models.testbed.cape import TargetPort
31
- from shepherd_core.logger import logger
33
+ from shepherd_core.logger import log
34
+
35
+ from .helper_paths import path_posix
32
36
 
33
37
 
34
38
  class Compression(str, Enum):
@@ -149,9 +153,9 @@ class EmulationTask(ShpModel):
149
153
  _io is not None for _io in (self.gpio_actuation, self.gpio_tracing, self.uart_logging)
150
154
  )
151
155
  if self.enable_io and not io_requested:
152
- logger.warning("Target IO enabled, but no feature requested IO")
156
+ log.warning("Target IO enabled, but no feature requested IO")
153
157
  if not self.enable_io and io_requested:
154
- logger.warning("Target IO not enabled, but a feature requested IO")
158
+ log.warning("Target IO not enabled, but a feature requested IO")
155
159
  return self
156
160
 
157
161
  @classmethod
@@ -165,8 +169,8 @@ class EmulationTask(ShpModel):
165
169
  )
166
170
 
167
171
  return cls(
168
- input_path=tgt_cfg.energy_env.data_path,
169
- output_path=root_path / f"emu_{obs.name}.h5",
172
+ input_path=path_posix(tgt_cfg.energy_env.data_path),
173
+ output_path=path_posix(root_path / f"emu_{obs.name}.h5"),
170
174
  time_start=copy.copy(xp.time_start),
171
175
  duration=xp.duration,
172
176
  enable_io=io_requested,
@@ -180,6 +184,11 @@ class EmulationTask(ShpModel):
180
184
  sys_logging=xp.sys_logging,
181
185
  )
182
186
 
187
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
188
+ all_ok = any(self.input_path.is_relative_to(path) for path in paths)
189
+ all_ok &= any(self.output_path.is_relative_to(path) for path in paths)
190
+ return all_ok
191
+
183
192
 
184
193
  # TODO: herdConfig
185
194
  # - store if path is remote (read & write)
@@ -1,7 +1,8 @@
1
1
  """Config for Task that adds the custom ID to the firmware & stores it into a file."""
2
2
 
3
- import copy
3
+ from collections.abc import Set as AbstractSet
4
4
  from pathlib import Path
5
+ from pathlib import PurePosixPath
5
6
  from typing import Annotated
6
7
  from typing import Optional
7
8
  from typing import TypedDict
@@ -22,7 +23,9 @@ from shepherd_core.data_models.experiment.experiment import Experiment
22
23
  from shepherd_core.data_models.testbed import Testbed
23
24
  from shepherd_core.data_models.testbed.target import IdInt16
24
25
  from shepherd_core.data_models.testbed.target import MCUPort
25
- from shepherd_core.logger import logger
26
+ from shepherd_core.logger import log
27
+
28
+ from .helper_paths import path_posix
26
29
 
27
30
 
28
31
  class FirmwareModTask(ShpModel):
@@ -42,7 +45,7 @@ class FirmwareModTask(ShpModel):
42
45
  FirmwareDType.base64_hex,
43
46
  FirmwareDType.path_hex,
44
47
  }:
45
- logger.warning("Firmware is scheduled to get custom-ID but is not in elf-format")
48
+ log.warning("Firmware is scheduled to get custom-ID but is not in elf-format")
46
49
  return self
47
50
 
48
51
  @classmethod
@@ -68,10 +71,10 @@ class FirmwareModTask(ShpModel):
68
71
  fw_id = obs.get_target(tgt_id).testbed_id
69
72
 
70
73
  return cls(
71
- data=fw.data,
74
+ data=path_posix(fw.data) if isinstance(fw.data, Path) else fw.data,
72
75
  data_type=fw.data_type,
73
76
  custom_id=fw_id,
74
- firmware_file=copy.copy(fw_path),
77
+ firmware_file=path_posix(fw_path),
75
78
  )
76
79
 
77
80
  @classmethod
@@ -90,3 +93,9 @@ class FirmwareModTask(ShpModel):
90
93
  path_new: Path = path / fw.name
91
94
  kwargs["firmware_file"] = path_new.with_suffix(".hex")
92
95
  return cls(**kwargs)
96
+
97
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
98
+ all_ok = any(self.firmware_file.is_relative_to(path) for path in paths)
99
+ if isinstance(self.data, Path):
100
+ all_ok = any(self.data.is_relative_to(path) for path in paths)
101
+ return all_ok
@@ -1,8 +1,10 @@
1
1
  """Config for the Observer in Harvest-Mode to record IV data from a harvesting-source."""
2
2
 
3
+ from collections.abc import Set as AbstractSet
3
4
  from datetime import datetime
4
5
  from datetime import timedelta
5
6
  from pathlib import Path
7
+ from pathlib import PurePosixPath
6
8
  from typing import Annotated
7
9
  from typing import Optional
8
10
 
@@ -84,3 +86,6 @@ class HarvestTask(ShpModel):
84
86
  if self.duration and self.duration.total_seconds() < 0:
85
87
  raise ValueError("Task-Duration can't be negative.")
86
88
  return self
89
+
90
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
91
+ return any(self.output_path.is_relative_to(path) for path in paths)
@@ -0,0 +1,15 @@
1
+ r"""Helper FN to avoid unwanted behavior.
2
+
3
+ On windows Path("\xyz") gets transformed to "/xyz", but not on linux.
4
+ When sending an experiment via fastapi, this bug gets triggered.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+
10
+ def path_posix(path: Path) -> Path:
11
+ r"""Help Linux to get from "\xyz" to "/xyz".
12
+
13
+ This isn't a problem on windows and gets triggered when sending XP via fastapi.
14
+ """
15
+ return Path(path.as_posix().replace("\\", "/"))
@@ -1,8 +1,9 @@
1
1
  """Collection of tasks for selected observer included in experiment."""
2
2
 
3
+ from collections.abc import Set as AbstractSet
3
4
  from datetime import datetime
4
- from datetime import timedelta
5
5
  from pathlib import Path
6
+ from pathlib import PurePosixPath
6
7
  from typing import Annotated
7
8
  from typing import Optional
8
9
 
@@ -18,6 +19,7 @@ from shepherd_core.data_models.testbed.testbed import Testbed
18
19
 
19
20
  from .emulation import EmulationTask
20
21
  from .firmware_mod import FirmwareModTask
22
+ from .helper_paths import path_posix
21
23
  from .programming import ProgrammingTask
22
24
 
23
25
 
@@ -27,7 +29,7 @@ class ObserverTasks(ShpModel):
27
29
  observer: NameStr
28
30
 
29
31
  # PRE PROCESS
30
- time_prep: datetime # TODO: should be optional
32
+ time_prep: Optional[datetime] = None # TODO: currently not used
31
33
  root_path: Path
32
34
 
33
35
  # fw mod, store as hex-file and program
@@ -55,24 +57,14 @@ class ObserverTasks(ShpModel):
55
57
  if not tb.shared_storage:
56
58
  raise ValueError("Implementation currently relies on shared storage!")
57
59
 
58
- t_start = (
59
- xp.time_start
60
- if isinstance(xp.time_start, datetime)
61
- else datetime.now().astimezone() + timedelta(minutes=3)
62
- ) # TODO: this is messed up, just a hotfix
63
-
64
60
  obs = tb.get_observer(tgt_id)
65
- xp_dir = "experiments/" + xp.name + "_" + t_start.strftime("%Y-%m-%d_%H-%M-%S")
66
- root_path = tb.data_on_observer / xp_dir
67
- # TODO: Paths should be "friendlier"
68
- # - replace whitespace with "_" and remove non-alphanum?
69
-
61
+ root_path = tb.data_on_observer / "experiments" / xp.folder_name()
70
62
  fw_paths = [root_path / f"fw{_i}_{obs.name}.hex" for _i in [1, 2]]
71
63
 
72
64
  return cls(
73
65
  observer=obs.name,
74
- time_prep=t_start - tb.prep_duration,
75
- root_path=root_path,
66
+ # time_prep=
67
+ root_path=path_posix(root_path),
76
68
  fw1_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
77
69
  fw2_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 2, fw_paths[1]),
78
70
  fw1_prog=ProgrammingTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
@@ -100,3 +92,12 @@ class ObserverTasks(ShpModel):
100
92
  raise ValueError("Emu-Task should have a valid output-path")
101
93
  values[self.observer] = self.emulation.output_path
102
94
  return values
95
+
96
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
97
+ all_ok = any(self.root_path.is_relative_to(path) for path in paths)
98
+ all_ok &= self.fw1_mod.is_contained(paths)
99
+ all_ok &= self.fw2_mod.is_contained(paths)
100
+ all_ok &= self.fw1_prog.is_contained(paths)
101
+ all_ok &= self.fw2_prog.is_contained(paths)
102
+ all_ok &= self.emulation.is_contained(paths)
103
+ return all_ok
@@ -1,7 +1,8 @@
1
1
  """Config for a Task programming the selected target."""
2
2
 
3
- import copy
3
+ from collections.abc import Set as AbstractSet
4
4
  from pathlib import Path
5
+ from pathlib import PurePosixPath
5
6
  from typing import Annotated
6
7
  from typing import Optional
7
8
 
@@ -21,6 +22,8 @@ from shepherd_core.data_models.testbed.mcu import ProgrammerProtocol
21
22
  from shepherd_core.data_models.testbed.target import MCUPort
22
23
  from shepherd_core.data_models.testbed.testbed import Testbed
23
24
 
25
+ from .helper_paths import path_posix
26
+
24
27
 
25
28
  class ProgrammingTask(ShpModel):
26
29
  """Config for a Task programming the selected target."""
@@ -66,7 +69,7 @@ class ProgrammingTask(ShpModel):
66
69
  return None
67
70
 
68
71
  return cls(
69
- firmware_file=copy.copy(fw_path),
72
+ firmware_file=path_posix(fw_path),
70
73
  target_port=obs.get_target_port(tgt_id),
71
74
  mcu_port=mcu_port,
72
75
  mcu_type=fw.mcu.name,
@@ -74,3 +77,6 @@ class ProgrammingTask(ShpModel):
74
77
  datarate=fw.mcu.prog_datarate,
75
78
  protocol=fw.mcu.prog_protocol,
76
79
  )
80
+
81
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
82
+ return any(self.firmware_file.is_relative_to(path) for path in paths)
@@ -1,6 +1,8 @@
1
1
  """Collection of tasks for all observers included in experiment."""
2
2
 
3
3
  from pathlib import Path
4
+ from pathlib import PurePosixPath
5
+ from typing import TYPE_CHECKING
4
6
  from typing import Annotated
5
7
  from typing import Optional
6
8
 
@@ -17,6 +19,9 @@ from shepherd_core.data_models.testbed.testbed import Testbed
17
19
 
18
20
  from .observer_tasks import ObserverTasks
19
21
 
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Set as AbstractSet
24
+
20
25
 
21
26
  class TestbedTasks(ShpModel):
22
27
  """Collection of tasks for all observers included in experiment."""
@@ -56,3 +61,10 @@ class TestbedTasks(ShpModel):
56
61
  for obt in self.observer_tasks:
57
62
  values = {**values, **obt.get_output_paths()}
58
63
  return values
64
+
65
+ def is_contained(self) -> bool:
66
+ paths_allowed: AbstractSet[PurePosixPath] = {
67
+ PurePosixPath("/var/shepherd/"),
68
+ PurePosixPath("/tmp/"), # noqa: S108
69
+ }
70
+ return all(obt.is_contained(paths_allowed) for obt in self.observer_tasks)
@@ -28,7 +28,7 @@ from typing import Union
28
28
 
29
29
  import numpy as np
30
30
 
31
- from shepherd_core.logger import logger
31
+ from shepherd_core.logger import log
32
32
 
33
33
 
34
34
  class Parity(str, Enum):
@@ -109,10 +109,10 @@ class Uart:
109
109
  raise ValueError("Only bit-order LSB-first is supported ATM")
110
110
 
111
111
  if self.inversion:
112
- logger.debug("inversion was detected / issued -> will invert signal")
112
+ log.debug("inversion was detected / issued -> will invert signal")
113
113
  self._convert_analog2digital(invert=True)
114
114
  if self.detect_inversion():
115
- logger.error("Signal still inverted?!? Check parameters and input")
115
+ log.error("Signal still inverted?!? Check parameters and input")
116
116
 
117
117
  # results
118
118
  self.events_symbols: Optional[np.ndarray] = None
@@ -136,7 +136,7 @@ class Uart:
136
136
  self.events_sig = self.events_sig[data_f == 1]
137
137
 
138
138
  if len(data_0) > len(self.events_sig):
139
- logger.debug(
139
+ log.debug(
140
140
  "filtered out %d/%d events (redundant)",
141
141
  len(data_0) - len(self.events_sig),
142
142
  len(data_0),
@@ -145,7 +145,7 @@ class Uart:
145
145
  def _add_duration(self) -> None:
146
146
  """Calculate third column -> duration of state in [baud-ticks]."""
147
147
  if self.events_sig.shape[1] > 2:
148
- logger.warning("Tried to add state-duration, but it seems already present")
148
+ log.warning("Tried to add state-duration, but it seems already present")
149
149
  return
150
150
  if not hasattr(self, "dur_tick"):
151
151
  raise ValueError("Make sure that baud-rate was calculated before running add_dur()")
@@ -223,7 +223,7 @@ class Uart:
223
223
  symbol = 0
224
224
  pos_df = None
225
225
  else:
226
- logger.debug("Error - Long pause - but SigLow (@%d)", time)
226
+ log.debug("Error - Long pause - but SigLow (@%d)", time)
227
227
  continue
228
228
  if pos_df is None and value == 0:
229
229
  # Start of frame (first low after pause / EOF)
@@ -245,7 +245,7 @@ class Uart:
245
245
  symbol = 0
246
246
  pos_df = None
247
247
  if off_tick and value == 0:
248
- logger.debug("Error - Off-sized step - but SigLow (@%d)", time)
248
+ log.debug("Error - Off-sized step - but SigLow (@%d)", time)
249
249
  self.events_symbols = np.concatenate(content).reshape((len(content), 2))
250
250
  # TODO: numpy is converting timestamp to string -> must be added as tuple (ts, symbol)
251
251
  # symbol_events[:, 0] = symbol_events[:, 0].astype(float) # noqa: ERA001
@@ -8,7 +8,7 @@ from pydantic import Field
8
8
  from pydantic import validate_call
9
9
 
10
10
  from shepherd_core.config import config
11
- from shepherd_core.logger import logger
11
+ from shepherd_core.logger import log
12
12
 
13
13
  from .validation import is_elf
14
14
 
@@ -35,9 +35,9 @@ def find_symbol(file_elf: Path, symbol: str) -> bool:
35
35
  except KeyError:
36
36
  addr = None
37
37
  if addr is None:
38
- logger.debug("Symbol '%s' not found in ELF-File %s", symbol, file_elf.name)
38
+ log.debug("Symbol '%s' not found in ELF-File %s", symbol, file_elf.name)
39
39
  return False
40
- logger.debug(
40
+ log.debug(
41
41
  "Symbol '%s' found in ELF-File %s, arch=%s, order=%s",
42
42
  symbol,
43
43
  file_elf.name,
@@ -79,7 +79,7 @@ def read_arch(file_elf: Path) -> Optional[str]:
79
79
  elf = ELF(path=file_elf)
80
80
  if "exec" in elf.elftype.lower():
81
81
  return elf.arch.lower()
82
- logger.error("ELF is not Executable")
82
+ log.error("ELF is not Executable")
83
83
  return None
84
84
 
85
85
 
@@ -113,13 +113,13 @@ def modify_symbol_value(
113
113
  try:
114
114
  elf.write(address=addr, data=value_raw)
115
115
  except AttributeError:
116
- logger.warning("ELF-Modifier failed @%s for symbol '%s'", f"0x{addr:X}", symbol)
116
+ log.warning("ELF-Modifier failed @%s for symbol '%s'", f"0x{addr:X}", symbol)
117
117
  return None
118
118
 
119
119
  file_new = file_elf if overwrite else file_elf.with_stem(file_elf.stem + "_" + str(value))
120
120
  elf.save(path=file_new)
121
121
  elf.close()
122
- logger.debug(
122
+ log.debug(
123
123
  "Value of Symbol '%s' modified: %s -> %s @%s",
124
124
  symbol,
125
125
  hex(value_old),
@@ -13,7 +13,7 @@ from intelhex import IntelHexError
13
13
  from pydantic import validate_call
14
14
 
15
15
  from shepherd_core.data_models.content.firmware_datatype import FirmwareDType
16
- from shepherd_core.logger import logger
16
+ from shepherd_core.logger import log
17
17
 
18
18
  from .converter_elf import elf_to_hex
19
19
 
@@ -94,7 +94,7 @@ def is_elf(file: Path) -> bool:
94
94
  try:
95
95
  _ = ELF(path=file)
96
96
  except ELFError:
97
- logger.debug("File %s is not ELF - Magic number does not match", file.name)
97
+ log.debug("File %s is not ELF - Magic number does not match", file.name)
98
98
  return False
99
99
  return True
100
100
 
@@ -14,7 +14,7 @@ from typing import Optional
14
14
  from typing_extensions import Self
15
15
 
16
16
  from shepherd_core.data_models.base.timezone import local_now
17
- from shepherd_core.logger import logger
17
+ from shepherd_core.logger import log
18
18
 
19
19
  try:
20
20
  import psutil
@@ -62,7 +62,7 @@ class SystemInventory(ShpModel):
62
62
  if psutil is None:
63
63
  ifs2 = {}
64
64
  uptime = 0
65
- logger.warning(
65
+ log.warning(
66
66
  "Inventory-Parameters will be missing. "
67
67
  "Please install functionality with "
68
68
  "'pip install shepherd_core[inventory] -U' first"
shepherd_core/logger.py CHANGED
@@ -7,8 +7,8 @@ from typing import Union
7
7
  import chromalog
8
8
 
9
9
  chromalog.basicConfig(format="%(message)s")
10
- logger = logging.getLogger("SHPCore")
11
- logger.addHandler(logging.NullHandler())
10
+ log = logging.getLogger("SHPCore")
11
+ log.addHandler(logging.NullHandler())
12
12
 
13
13
  verbose_level: int = 2
14
14
 
@@ -47,7 +47,7 @@ def increase_verbose_level(verbose: int) -> None:
47
47
  global verbose_level # noqa: PLW0603
48
48
  if verbose >= verbose_level:
49
49
  verbose_level = min(max(verbose, 0), 3)
50
- set_log_verbose_level(logger, verbose_level)
50
+ set_log_verbose_level(log, verbose_level)
51
51
 
52
52
 
53
53
  increase_verbose_level(2)
@@ -18,7 +18,7 @@ from typing_extensions import Self
18
18
  from shepherd_core.data_models.base.timezone import local_now
19
19
  from shepherd_core.data_models.base.timezone import local_tz
20
20
  from shepherd_core.data_models.base.wrapper import Wrapper
21
- from shepherd_core.logger import logger
21
+ from shepherd_core.logger import log
22
22
 
23
23
  from .cache_path import cache_user_path
24
24
 
@@ -109,7 +109,7 @@ class Fixture:
109
109
  raise ValueError(msg)
110
110
  chain.append(base_name)
111
111
  fixture_base = copy.copy(self[fixture_name])
112
- logger.debug("'%s' will inherit from '%s'", self.model_type, fixture_name)
112
+ log.debug("'%s' will inherit from '%s'", self.model_type, fixture_name)
113
113
  fixture_base["name"] = fixture_name
114
114
  chain.append(fixture_name)
115
115
  base_dict, chain = self.inheritance(values=fixture_base, chain=chain)
@@ -196,7 +196,7 @@ class Fixtures:
196
196
  # TODO: also add version as criterion
197
197
  with cache_file.open("rb", buffering=-1) as fd:
198
198
  self.components = pickle.load(fd) # noqa: S301
199
- logger.debug(" -> found & used pickled fixtures")
199
+ log.debug(" -> found & used pickled fixtures")
200
200
  else:
201
201
  if self.file_path.is_file():
202
202
  files = [self.file_path]
@@ -204,7 +204,7 @@ class Fixtures:
204
204
  files = list(
205
205
  self.file_path.glob("**/*" + self.suffix)
206
206
  ) # for py>=3.12: case_sensitive=False
207
- logger.debug(" -> got %s %s-files", len(files), self.suffix)
207
+ log.debug(" -> got %s %s-files", len(files), self.suffix)
208
208
  else:
209
209
  raise ValueError("Path must either be file or directory (or empty)")
210
210
 
@@ -212,7 +212,7 @@ class Fixtures:
212
212
  self.insert_file(file)
213
213
 
214
214
  if len(self.components) < 1:
215
- logger.error(f"No fixture-components found at {self.file_path.as_posix()}")
215
+ log.error(f"No fixture-components found at {self.file_path.as_posix()}")
216
216
  elif sheep_detect:
217
217
  cache_file.parent.mkdir(parents=True, exist_ok=True)
218
218
  with cache_file.open("wb", buffering=-1) as fd:
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.06.1"
3
+ version: str = "2025.06.2"
@@ -17,7 +17,7 @@ Compromises:
17
17
  """
18
18
 
19
19
  from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
20
- from shepherd_core.logger import logger
20
+ from shepherd_core.logger import log
21
21
 
22
22
 
23
23
  class VirtualHarvesterModel:
@@ -37,7 +37,7 @@ class VirtualHarvesterModel:
37
37
 
38
38
  self.is_emu: bool = bool(self._cfg.hrv_mode & (2**0))
39
39
  if not self.is_emu:
40
- logger.warning(
40
+ log.warning(
41
41
  "This VSrc-config is not meant for emulation-mode -> activate 'is_emu' flag."
42
42
  )
43
43
 
@@ -16,7 +16,7 @@ from tqdm import tqdm
16
16
 
17
17
  from shepherd_core.data_models.base.calibration import CalibrationEmulator
18
18
  from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
19
- from shepherd_core.logger import logger
19
+ from shepherd_core.logger import log
20
20
  from shepherd_core.reader import Reader
21
21
  from shepherd_core.writer import Writer
22
22
 
@@ -68,7 +68,7 @@ def simulate_source(
68
68
  # keep dependencies low
69
69
  from matplotlib import pyplot as plt
70
70
  except ImportError:
71
- logger.warning("Matplotlib not installed, plotting of internals disabled")
71
+ log.warning("Matplotlib not installed, plotting of internals disabled")
72
72
  stats_internal = None
73
73
  else:
74
74
  stats_internal = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.6.1
3
+ Version: 2025.6.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>
@@ -1,42 +1,43 @@
1
- shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,1270
1
+ shepherd_core/__init__.py,sha256=cwfuqvk-psFHE6iGGaGw82BMqLiLTazVBxHm97sz_E0,1264
2
2
  shepherd_core/calibration_hw_def.py,sha256=aL94bA1Sf14L5A3PLdVvQVYtGi28S4NUWA65wbim8bw,2895
3
3
  shepherd_core/commons.py,sha256=_phovuhCgLmO5gcazQ5hyykUPc907dyK9KpY2lUtoIM,205
4
4
  shepherd_core/config.py,sha256=YegFEXuBUBnbq5mb67em8ozEnSkEQSPXjqHlKA2HXCQ,967
5
- shepherd_core/logger.py,sha256=i8j3alm8icAx_h1_IZ6SgVgC9W5J7S-bDc31mPDWl-w,1812
5
+ shepherd_core/logger.py,sha256=se8uBmlOKhU2ODs_cx1L0Nxkj4n7nlk67BpCFYgcHns,1803
6
6
  shepherd_core/reader.py,sha256=oTh42tCDyLRGxO505v3etBPpy9BhqfOrWeVfn0SXeJQ,29480
7
- shepherd_core/version.py,sha256=GU8zo_E8cdlxV6BNWPOqg-44y9FHnqLBXEfn-u0bBN4,76
7
+ shepherd_core/version.py,sha256=FEJD7Y6nIjgA1GntHr4Eta4zDjWL0iegU8jCdhrGybA,76
8
8
  shepherd_core/writer.py,sha256=VRrM7cIv3RcX3JlYs4xq5DUK_3kD-6YH4Ie96tQ6Hcw,14591
9
- shepherd_core/data_models/__init__.py,sha256=tbELRPbWx6100IHjDia85LZGb66A0ymD1bbfBWGuGJs,1909
9
+ shepherd_core/data_models/__init__.py,sha256=jBsk2RpD5Gw5GNe1gql9YrWD-7Uv7F2jwRRx-CHdceQ,1909
10
10
  shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
11
11
  shepherd_core/data_models/virtual_source_doc.txt,sha256=OK_7zYbLvr6cEj3KaUcWwZJ9naoFB2KwAaXudbhzouQ,6076
12
12
  shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
13
13
  shepherd_core/data_models/base/cal_measurement.py,sha256=c-vjACNxsQY8LU2Msw0COrsTY-8pToJ5ZWkOztJ9tVY,3380
14
14
  shepherd_core/data_models/base/calibration.py,sha256=25ls5q9d5ko5X2DwbNWFuUZIji2Zwh04l_UxdRt-AFk,10787
15
15
  shepherd_core/data_models/base/content.py,sha256=e8-UWjF7QW4mYMqnDSnbhZTY3ODxGyx50A_K4SgDnNs,2098
16
- shepherd_core/data_models/base/shepherd.py,sha256=P1xW10_vIbGNyQ3Ary67vyovw9kmDtXhN-yPFYR-0Is,6085
16
+ shepherd_core/data_models/base/shepherd.py,sha256=m32eOpN_oGGVL0bJhN8IEQTQ04ZoQQoiTdRu64mtHLg,6871
17
17
  shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
18
18
  shepherd_core/data_models/base/wrapper.py,sha256=Dkvosu2gcJOUgDHhyRUmIsuWdtmtICIUmKmRU9ChtEM,784
19
19
  shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKsKyMK8o6Kfaby5vlY,525
20
20
  shepherd_core/data_models/content/_external_fixtures.yaml,sha256=BsHW5UP1UtrEkcI-efCHq4gFtnsuOvoCPv1ri-f6JOI,12132
21
21
  shepherd_core/data_models/content/energy_environment.py,sha256=GWlnd1p012Kg7OWosDuYpuRzyCd4aLdCzx7rrJ_uneI,1633
22
22
  shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
23
- shepherd_core/data_models/content/firmware.py,sha256=-sjvEkq0Wr8e2jvBCCH_pVXvUKcIJ-rQc7LTxpHuFsk,6191
23
+ shepherd_core/data_models/content/firmware.py,sha256=a_RX9gaX3hqnSH6UMUyPlXZupa0qmtnRlG15NEGNWIg,6179
24
24
  shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
25
- shepherd_core/data_models/content/virtual_harvester.py,sha256=F_A04YeA5ke51kLI0clTtQgdeX439JiknC-2lDqYuRc,20317
25
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=c79Lmr_0WYmCqa6ntT-Ab1_3kcq9bCahnWIWxOZkfg4,20308
26
26
  shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=4eNQyFI9LozpButOTlBQ07T0MFCaPEYIxwtedMjUf3U,4575
27
- shepherd_core/data_models/content/virtual_source.py,sha256=ZaqZleIDKGDA39_GhbQY7LMeOu-N9a03g_-M0BEj9pM,15865
27
+ shepherd_core/data_models/content/virtual_source.py,sha256=GbdRghRLmLEUrMo1dyd0Pc0-bm7-3LCIgVaOhCb6NxQ,15850
28
28
  shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=WWbo9ACoD-JJ-jidMFTfwSn4PR_nRPwKQ0Aa2qKVrxE,11202
29
29
  shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
30
30
  shepherd_core/data_models/experiment/experiment.py,sha256=X304nrNMv69LLIT2k9oiM7swgpf3uFr91a95vipZSow,4701
31
- shepherd_core/data_models/experiment/observer_features.py,sha256=3zhy3OdGO8SKDijNP-c_Z2_SgiQop47j8mW3pryim5k,9057
31
+ shepherd_core/data_models/experiment/observer_features.py,sha256=dxbdmAbh8a-fz2_ppTNWEnIDbzSskxvAayXNI2LCJfs,9058
32
32
  shepherd_core/data_models/experiment/target_config.py,sha256=qI72kj5_rpJXU3A2_TmfoN9vATOZPFjphpXLZfc9riM,4260
33
- shepherd_core/data_models/task/__init__.py,sha256=m7_OK05djkoDt-XwKQ6gPT7qIcCUoyBxZgSXvD3HmzQ,3386
34
- shepherd_core/data_models/task/emulation.py,sha256=kZ7CP1CC_Su15NqTm9cDvMGBmimHD7u8BZ6OS4PmhWw,7513
35
- shepherd_core/data_models/task/firmware_mod.py,sha256=-oFqo4huYNQkAdTsCWd_zjTurUMbHpy-gZogvXLM0pg,3039
36
- shepherd_core/data_models/task/harvest.py,sha256=xXNedCj4RUj1TgRMmKONWjcGEZ6gS0PAX7QQBJKdngo,3584
37
- shepherd_core/data_models/task/observer_tasks.py,sha256=OZ9rJyPxRXonqRNYi10gFNDD7DrGHupAkZF353rqBJs,3735
38
- shepherd_core/data_models/task/programming.py,sha256=lL0uOtTjuf33UC2p5eAQOvxvdJ5Pz1p1s2l7OILOaVc,2655
39
- shepherd_core/data_models/task/testbed_tasks.py,sha256=1COnAvJZVjVcF4Vw6UoCA5JKot-_twR-IwfJWwVkRbk,2208
33
+ shepherd_core/data_models/task/__init__.py,sha256=GsWyipHYbE7vg0G4Lvpuh_HpoJMsqh75F2YJhylMIUc,3673
34
+ shepherd_core/data_models/task/emulation.py,sha256=7mOpB75qwunTT_KWCteLmrQwjbPVomcbm3LqCsrDiy8,7897
35
+ shepherd_core/data_models/task/firmware_mod.py,sha256=wWfqWKvcbCXwepVXYYJhmdplDE97xsAE7ZnGFF3Pvm8,3484
36
+ shepherd_core/data_models/task/harvest.py,sha256=OJKupuZ4yBuVl-WspuXjD6p-dvmwbfRwp4PsAUVeP1w,3813
37
+ shepherd_core/data_models/task/helper_paths.py,sha256=AOfbZekT1OxH8pUV_B0S_SR7O4tcRbJalhnUBGPfvd4,440
38
+ shepherd_core/data_models/task/observer_tasks.py,sha256=0cGwR1HfAQ9y7PmRWyPiwD4pUAUKl10WxeOXHYui_4E,3861
39
+ shepherd_core/data_models/task/programming.py,sha256=R48Vv4Zyn9VGynQc82S9nFjkzxjb8973OkREzvqnZ0U,2913
40
+ shepherd_core/data_models/task/testbed_tasks.py,sha256=lGEKG8LlENWP54WDy7yGtS96cD2YxmMmKfQB5GV9EYs,2624
40
41
  shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
41
42
  shepherd_core/data_models/testbed/cape.py,sha256=vfS05D0rC1-_wMiHeLw69VE9PxXC6PHl9ndtrv219_k,1396
42
43
  shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=ZCjQSlHE3_5EQpusmRYuw-z9NlxT-8MU49RCd04PfAg,2373
@@ -52,31 +53,31 @@ shepherd_core/data_models/testbed/target_fixture.yaml,sha256=aoP7Al_sXw8nBQpIP25
52
53
  shepherd_core/data_models/testbed/testbed.py,sha256=6qPGUKkwB5cLMY0upqd80nI9ydLmzNLfc5RhuUuBvGw,3761
53
54
  shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=ca5LI-fWoc3I9m2QScVAh84Bv-ftkSGAizR3ZR0lkC8,980
54
55
  shepherd_core/decoder_waveform/__init__.py,sha256=-ohGz0fA2tKxUJk4FAQXKtI93d6YGdy0CrkdhOod1QU,120
55
- shepherd_core/decoder_waveform/uart.py,sha256=vuw9fKwZb_1mMtQ5fdiZN8Cr1YWSgYKar9FIMK8Bogo,11084
56
+ shepherd_core/decoder_waveform/uart.py,sha256=YDglnTWM88FIaBVHX8VVqEnNja39e2SYPnAkoU2u5rk,11063
56
57
  shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5ReVAA,2090
57
58
  shepherd_core/fw_tools/converter.py,sha256=V74V-82VVkvkZ2i4HI7K_291_FGZNjxVdX6eO7Y3Zks,3657
58
59
  shepherd_core/fw_tools/converter_elf.py,sha256=GQDVqIqMW4twNMvZIV3sowFMezhs2TN-IYREjRP7Xt4,1089
59
- shepherd_core/fw_tools/patcher.py,sha256=AFLg5qxkDP-T66wcX3Dbgx9x-V7-ucKLc1cOdmXHf7c,3939
60
- shepherd_core/fw_tools/validation.py,sha256=KxOBtAgqUemZC_Lj1_xtV6AMpGLU04_epxihMF3RX7U,4792
60
+ shepherd_core/fw_tools/patcher.py,sha256=UP-qoNDtxo3DKuD2PnK6MmCeSxYBHPlXzsVd5Hn0Q78,3921
61
+ shepherd_core/fw_tools/validation.py,sha256=usX-wifUrDmAh2QNhPv0qn0CFrsdjgkOEcYwcVaTc1A,4786
61
62
  shepherd_core/inventory/__init__.py,sha256=yQxP55yV61xXWfZSSzekQQYopPZCspFpHSyG7VTqtpg,3819
62
63
  shepherd_core/inventory/python.py,sha256=pvugNgLZaDllIXX_KiuvpcWUWlJtD2IUKYDRjcTGQss,1262
63
- shepherd_core/inventory/system.py,sha256=dXofIkNk8oyI_XDKscw-9-UUYc0WGDSM1Qziio0M4E4,3205
64
+ shepherd_core/inventory/system.py,sha256=SfO1RQF1j9MmnDvekD0v-cQX7IyaNH9RtQRY048c6dA,3199
64
65
  shepherd_core/inventory/target.py,sha256=zLUNQs2FE7jMDsiRtbeAwqRVcit4e2F1UUOF04xw-XY,520
65
66
  shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1fwUtWW8Vg_4,234
66
67
  shepherd_core/testbed_client/cache_path.py,sha256=BXklO72gFDhJ9i2gGlgw5MbuxexGA42two7DCdpd9dM,437
67
68
  shepherd_core/testbed_client/client_abc_fix.py,sha256=xMch4y6vMMLRiqFEry2zSnYuLM6Ay-fY0dunXMz9Hws,4250
68
69
  shepherd_core/testbed_client/client_web.py,sha256=WvQgD196MPn_f_XbVBIFCmTz1i2vfwPVFfsTMs682ik,6034
69
- shepherd_core/testbed_client/fixtures.py,sha256=gKTRt_44u42-1OMuONho34WqRvoelDbChAGA5rdbbXQ,9397
70
+ shepherd_core/testbed_client/fixtures.py,sha256=3RvDOfEuzhzG3_M2g4nDVPP1XMIK_FFGgk7nIt7uPdU,9382
70
71
  shepherd_core/testbed_client/user_model.py,sha256=F6ibcvqZOYp7Tw92FoWERuCMp9LFsTL-0ACWhSzZs2A,2077
71
72
  shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
72
73
  shepherd_core/vsource/target_model.py,sha256=BjOlwX_gIOJ91e4OOLB4_OsCpuhq9vm57ERjM-iBhAM,5129
73
74
  shepherd_core/vsource/virtual_converter_model.py,sha256=jUnJwP-FFDMtXm1NCLUJfZTvImYH4_A9rc_lXVAZ33I,11628
74
- shepherd_core/vsource/virtual_harvester_model.py,sha256=8uPTMDth4KI5XarkX9EJtMRZ_8eWUeWCZxgmx4be3Fs,9763
75
+ shepherd_core/vsource/virtual_harvester_model.py,sha256=K9RnAHCz1ibdYMte5l_U7vrY6_mVRZBg7xBL2ZYC16c,9757
75
76
  shepherd_core/vsource/virtual_harvester_simulation.py,sha256=eK9uCn-j_8xSTa7BQhG879ep0oZDeatlnF31LeWEDiM,2547
76
77
  shepherd_core/vsource/virtual_source_model.py,sha256=-8RwBrkIdO0g4zpo7XHnqv8F_qNh_qf5hxEUJoIuAmg,3164
77
- shepherd_core/vsource/virtual_source_simulation.py,sha256=6voU1TAB2P0xuVu1bnyKa-xW9rwYXUC6f06gYtUDHTs,5273
78
- shepherd_core-2025.6.1.dist-info/METADATA,sha256=6YnsOiAJAO_JDKGLqPYbrWGNW5zBao9Ua5dL1PgsMSo,7778
79
- shepherd_core-2025.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
80
- shepherd_core-2025.6.1.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
81
- shepherd_core-2025.6.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
82
- shepherd_core-2025.6.1.dist-info/RECORD,,
78
+ shepherd_core/vsource/virtual_source_simulation.py,sha256=qfe4dQMaifpF54lCb_Vv_7imebdhqeKIhXj5xLPtWMM,5267
79
+ shepherd_core-2025.6.2.dist-info/METADATA,sha256=WZcUnd9242FtTdD7Md5nnJEWYWlBP8bj5DeqVlizMFg,7778
80
+ shepherd_core-2025.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
81
+ shepherd_core-2025.6.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
82
+ shepherd_core-2025.6.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
83
+ shepherd_core-2025.6.2.dist-info/RECORD,,