shepherd-core 2025.5.3__py3-none-any.whl → 2025.6.1__py3-none-any.whl

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