shepherd-core 2025.6.1__py3-none-any.whl → 2025.6.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shepherd_core/__init__.py +2 -2
- shepherd_core/data_models/__init__.py +1 -1
- shepherd_core/data_models/base/shepherd.py +48 -13
- shepherd_core/data_models/content/firmware.py +4 -4
- shepherd_core/data_models/content/virtual_harvester.py +3 -3
- shepherd_core/data_models/content/virtual_source.py +5 -5
- shepherd_core/data_models/experiment/observer_features.py +2 -2
- shepherd_core/data_models/task/__init__.py +9 -4
- shepherd_core/data_models/task/emulation.py +14 -5
- shepherd_core/data_models/task/firmware_mod.py +14 -5
- shepherd_core/data_models/task/harvest.py +5 -0
- shepherd_core/data_models/task/helper_paths.py +15 -0
- shepherd_core/data_models/task/observer_tasks.py +16 -15
- shepherd_core/data_models/task/programming.py +8 -2
- shepherd_core/data_models/task/testbed_tasks.py +12 -0
- shepherd_core/decoder_waveform/uart.py +7 -7
- shepherd_core/fw_tools/patcher.py +6 -6
- shepherd_core/fw_tools/validation.py +2 -2
- shepherd_core/inventory/system.py +2 -2
- shepherd_core/logger.py +3 -3
- shepherd_core/testbed_client/fixtures.py +5 -5
- shepherd_core/version.py +1 -1
- shepherd_core/vsource/virtual_harvester_model.py +2 -2
- shepherd_core/vsource/virtual_source_simulation.py +2 -2
- {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.3.dist-info}/METADATA +1 -1
- {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.3.dist-info}/RECORD +29 -28
- {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.3.dist-info}/WHEEL +0 -0
- {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.3.dist-info}/top_level.txt +0 -0
- {shepherd_core-2025.6.1.dist-info → shepherd_core-2025.6.3.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
|
|
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
|
-
"
|
|
44
|
+
"log",
|
|
45
45
|
]
|
|
@@ -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
|
|
@@ -47,6 +48,24 @@ yaml.add_representer(IPv4Address, generic2str, SafeDumper)
|
|
|
47
48
|
yaml.add_representer(UUID, generic2str, SafeDumper)
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
def path_to_str(old: dict) -> dict:
|
|
52
|
+
r"""Allow platform-independent pickling of ShpModel.
|
|
53
|
+
|
|
54
|
+
Helper Fn
|
|
55
|
+
Posix-Paths (/xyz/abc) in WindowsPath gets converted to \\xyz\\abc when exported
|
|
56
|
+
intended usage: pickle.dump(path_to_str(model.model_dump()))
|
|
57
|
+
"""
|
|
58
|
+
new: dict = {}
|
|
59
|
+
for key, value in old.items():
|
|
60
|
+
if isinstance(value, Path):
|
|
61
|
+
new[key] = value.as_posix().replace("\\", "/")
|
|
62
|
+
elif isinstance(value, dict):
|
|
63
|
+
new[key] = path_to_str(value)
|
|
64
|
+
else:
|
|
65
|
+
new[key] = value
|
|
66
|
+
return new
|
|
67
|
+
|
|
68
|
+
|
|
50
69
|
class ShpModel(BaseModel):
|
|
51
70
|
"""Pre-configured Pydantic Base-Model (specifically for shepherd).
|
|
52
71
|
|
|
@@ -126,40 +145,56 @@ class ShpModel(BaseModel):
|
|
|
126
145
|
comment: Optional[str] = None,
|
|
127
146
|
*,
|
|
128
147
|
minimal: bool = True,
|
|
148
|
+
use_pickle: bool = False,
|
|
129
149
|
) -> Path:
|
|
130
150
|
"""Store data to yaml in a wrapper.
|
|
131
151
|
|
|
132
152
|
minimal: stores minimal set (filters out unset & default parameters)
|
|
153
|
+
pickle: uses pickle to serialize data, on BBB >100x faster for large files
|
|
133
154
|
comment: documentation.
|
|
134
155
|
"""
|
|
135
|
-
model_dict = self.model_dump(exclude_unset=minimal)
|
|
136
156
|
model_wrap = Wrapper(
|
|
137
157
|
datatype=type(self).__name__,
|
|
138
158
|
comment=comment,
|
|
139
159
|
created=local_now(),
|
|
140
|
-
parameters=
|
|
141
|
-
)
|
|
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,
|
|
160
|
+
parameters=self.model_dump(exclude_unset=minimal),
|
|
146
161
|
)
|
|
162
|
+
model_dict = model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal)
|
|
163
|
+
if use_pickle:
|
|
164
|
+
model_serial = pickle.dumps(path_to_str(model_dict))
|
|
165
|
+
model_path = Path(path).resolve().with_suffix(".pickle")
|
|
166
|
+
else:
|
|
167
|
+
# TODO: x64 windows supports CSafeLoader/dumper,
|
|
168
|
+
# there are examples that replace load if avail
|
|
169
|
+
model_serial = yaml.safe_dump(
|
|
170
|
+
model_dict,
|
|
171
|
+
default_flow_style=False,
|
|
172
|
+
sort_keys=False,
|
|
173
|
+
)
|
|
174
|
+
model_path = Path(path).resolve().with_suffix(".yaml")
|
|
147
175
|
# TODO: handle directory
|
|
148
|
-
|
|
176
|
+
|
|
149
177
|
if not model_path.parent.exists():
|
|
150
178
|
model_path.parent.mkdir(parents=True)
|
|
151
179
|
with model_path.open("w") as f:
|
|
152
|
-
f.write(
|
|
180
|
+
f.write(model_serial)
|
|
153
181
|
return model_path
|
|
154
182
|
|
|
155
183
|
@classmethod
|
|
156
184
|
def from_file(cls, path: Union[str, Path]) -> Self:
|
|
157
|
-
"""Load from
|
|
158
|
-
|
|
159
|
-
|
|
185
|
+
"""Load from YAML or pickle file."""
|
|
186
|
+
path: Path = Path(path)
|
|
187
|
+
if not Path(path).exists():
|
|
188
|
+
raise FileNotFoundError
|
|
189
|
+
if path.suffix.lower() == ".pickle":
|
|
190
|
+
with Path(path).open("rb") as shp_file:
|
|
191
|
+
shp_dict = pickle.load(shp_file) # noqa: S301
|
|
192
|
+
else:
|
|
193
|
+
with Path(path).open() as shp_file:
|
|
194
|
+
shp_dict = yaml.safe_load(shp_file)
|
|
160
195
|
shp_wrap = Wrapper(**shp_dict)
|
|
161
196
|
if shp_wrap.datatype != cls.__name__:
|
|
162
|
-
raise ValueError("Model in file does not match the
|
|
197
|
+
raise ValueError("Model in file does not match the actual Class")
|
|
163
198
|
return cls(**shp_wrap.parameters)
|
|
164
199
|
|
|
165
200
|
def get_hash(self) -> str:
|
|
@@ -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
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15
|
+
from shepherd_core.logger import log
|
|
15
16
|
|
|
16
17
|
from .emulation import Compression
|
|
17
18
|
from .emulation import EmulationTask
|
|
@@ -44,7 +45,11 @@ 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_dict = pickle.load(shp_file) # noqa: S301
|
|
51
|
+
shp_wrap = Wrapper(**shp_dict)
|
|
52
|
+
elif isinstance(config, Path) and config.exists() and config.suffix.lower() == ".yaml":
|
|
48
53
|
with config.resolve().open() as shp_file:
|
|
49
54
|
shp_dict = yaml.safe_load(shp_file)
|
|
50
55
|
shp_wrap = Wrapper(**shp_dict)
|
|
@@ -59,12 +64,12 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
|
|
|
59
64
|
|
|
60
65
|
if shp_wrap.datatype == TestbedTasks.__name__:
|
|
61
66
|
if observer is None:
|
|
62
|
-
|
|
67
|
+
log.debug(
|
|
63
68
|
"Task-Set contained TestbedTasks & no observer was provided -> will return TB-Tasks"
|
|
64
69
|
)
|
|
65
70
|
return shp_wrap
|
|
66
71
|
tbt = TestbedTasks(**shp_wrap.parameters)
|
|
67
|
-
|
|
72
|
+
log.debug("Loading Testbed-Tasks %s for %s", tbt.name, observer)
|
|
68
73
|
obt = tbt.get_observer_tasks(observer)
|
|
69
74
|
if obt is None:
|
|
70
75
|
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
|
|
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
|
-
|
|
156
|
+
log.warning("Target IO enabled, but no feature requested IO")
|
|
153
157
|
if not self.enable_io and io_requested:
|
|
154
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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=
|
|
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:
|
|
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
|
-
|
|
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=
|
|
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
|
|
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=
|
|
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
|
|
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
|
-
|
|
112
|
+
log.debug("inversion was detected / issued -> will invert signal")
|
|
113
113
|
self._convert_analog2digital(invert=True)
|
|
114
114
|
if self.detect_inversion():
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
38
|
+
log.debug("Symbol '%s' not found in ELF-File %s", symbol, file_elf.name)
|
|
39
39
|
return False
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
3
|
+
Version: 2025.6.3
|
|
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=
|
|
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=
|
|
5
|
+
shepherd_core/logger.py,sha256=se8uBmlOKhU2ODs_cx1L0Nxkj4n7nlk67BpCFYgcHns,1803
|
|
6
6
|
shepherd_core/reader.py,sha256=oTh42tCDyLRGxO505v3etBPpy9BhqfOrWeVfn0SXeJQ,29480
|
|
7
|
-
shepherd_core/version.py,sha256=
|
|
7
|
+
shepherd_core/version.py,sha256=vYW_zqQqbw9dtRcdMGPw5ZffUDJau2hQDsEwAT2IvCA,76
|
|
8
8
|
shepherd_core/writer.py,sha256=VRrM7cIv3RcX3JlYs4xq5DUK_3kD-6YH4Ie96tQ6Hcw,14591
|
|
9
|
-
shepherd_core/data_models/__init__.py,sha256=
|
|
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=
|
|
16
|
+
shepherd_core/data_models/base/shepherd.py,sha256=JOrSPweiMDR8Ry3X1nqf0PIUijpSMldsrmk7zjtRAf8,7400
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
34
|
-
shepherd_core/data_models/task/emulation.py,sha256=
|
|
35
|
-
shepherd_core/data_models/task/firmware_mod.py,sha256
|
|
36
|
-
shepherd_core/data_models/task/harvest.py,sha256=
|
|
37
|
-
shepherd_core/data_models/task/
|
|
38
|
-
shepherd_core/data_models/task/
|
|
39
|
-
shepherd_core/data_models/task/
|
|
33
|
+
shepherd_core/data_models/task/__init__.py,sha256=IeXlmnP0NQpW9r_EuEW0f55-pCXtinMqosEvmvom8Uw,3694
|
|
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=
|
|
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=
|
|
60
|
-
shepherd_core/fw_tools/validation.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
78
|
-
shepherd_core-2025.6.
|
|
79
|
-
shepherd_core-2025.6.
|
|
80
|
-
shepherd_core-2025.6.
|
|
81
|
-
shepherd_core-2025.6.
|
|
82
|
-
shepherd_core-2025.6.
|
|
78
|
+
shepherd_core/vsource/virtual_source_simulation.py,sha256=qfe4dQMaifpF54lCb_Vv_7imebdhqeKIhXj5xLPtWMM,5267
|
|
79
|
+
shepherd_core-2025.6.3.dist-info/METADATA,sha256=0-DmTMOVOu4h1vnbCY4XCl7ghEteZNGoR3KAUICJxLA,7778
|
|
80
|
+
shepherd_core-2025.6.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
81
|
+
shepherd_core-2025.6.3.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
|
|
82
|
+
shepherd_core-2025.6.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
83
|
+
shepherd_core-2025.6.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|