shepherd-core 2025.5.2__py3-none-any.whl → 2025.5.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.
Files changed (27) hide show
  1. shepherd_core/data_models/__init__.py +2 -2
  2. shepherd_core/data_models/base/content.py +4 -13
  3. shepherd_core/data_models/content/_external_fixtures.yaml +11 -11
  4. shepherd_core/data_models/content/virtual_harvester.py +246 -17
  5. shepherd_core/data_models/content/virtual_source.py +1 -0
  6. shepherd_core/data_models/experiment/experiment.py +6 -6
  7. shepherd_core/data_models/experiment/observer_features.py +34 -17
  8. shepherd_core/data_models/experiment/target_config.py +2 -2
  9. shepherd_core/data_models/task/emulation.py +7 -7
  10. shepherd_core/data_models/task/harvest.py +3 -2
  11. shepherd_core/data_models/task/observer_tasks.py +5 -4
  12. shepherd_core/data_models/task/programming.py +1 -0
  13. shepherd_core/data_models/task/testbed_tasks.py +3 -2
  14. shepherd_core/data_models/testbed/cape_fixture.yaml +8 -0
  15. shepherd_core/data_models/testbed/gpio.py +7 -0
  16. shepherd_core/data_models/testbed/observer_fixture.yaml +17 -0
  17. shepherd_core/data_models/testbed/target_fixture.yaml +13 -0
  18. shepherd_core/data_models/testbed/testbed_fixture.yaml +11 -0
  19. shepherd_core/reader.py +6 -5
  20. shepherd_core/testbed_client/fixtures.py +13 -11
  21. shepherd_core/testbed_client/user_model.py +3 -6
  22. shepherd_core/version.py +1 -1
  23. {shepherd_core-2025.5.2.dist-info → shepherd_core-2025.5.3.dist-info}/METADATA +1 -1
  24. {shepherd_core-2025.5.2.dist-info → shepherd_core-2025.5.3.dist-info}/RECORD +27 -27
  25. {shepherd_core-2025.5.2.dist-info → shepherd_core-2025.5.3.dist-info}/WHEEL +1 -1
  26. {shepherd_core-2025.5.2.dist-info → shepherd_core-2025.5.3.dist-info}/top_level.txt +0 -0
  27. {shepherd_core-2025.5.2.dist-info → shepherd_core-2025.5.3.dist-info}/zip-safe +0 -0
@@ -31,7 +31,7 @@ from .experiment.observer_features import GpioLevel
31
31
  from .experiment.observer_features import GpioTracing
32
32
  from .experiment.observer_features import PowerTracing
33
33
  from .experiment.observer_features import SystemLogging
34
- from .experiment.observer_features import UartTracing
34
+ from .experiment.observer_features import UartLogging
35
35
  from .experiment.target_config import TargetConfig
36
36
 
37
37
  __all__ = [
@@ -55,7 +55,7 @@ __all__ = [
55
55
  "ShpModel",
56
56
  "SystemLogging",
57
57
  "TargetConfig",
58
- "UartTracing",
58
+ "UartLogging",
59
59
  "VirtualHarvesterConfig",
60
60
  "VirtualSourceConfig",
61
61
  "Wrapper",
@@ -1,21 +1,16 @@
1
1
  """Base-Model for all content."""
2
2
 
3
- import hashlib
4
3
  from datetime import datetime
5
4
  from typing import Annotated
6
5
  from typing import Optional
7
- from typing import Union
8
6
  from uuid import uuid4
9
7
 
10
- from pydantic import UUID4
11
8
  from pydantic import Field
12
9
  from pydantic import StringConstraints
13
10
  from pydantic import model_validator
14
11
  from typing_extensions import Self
15
- from typing_extensions import deprecated
16
12
 
17
13
  from .shepherd import ShpModel
18
- from .timezone import local_now
19
14
 
20
15
  # constr -> to_lower=True, max_length=16, regex=r"^[\w]+$"
21
16
  # ⤷ Regex = AlphaNum
@@ -26,24 +21,20 @@ SafeStr = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
26
21
  # ⤷ Regex = All Printable ASCII-Characters with Space
27
22
 
28
23
 
29
- @deprecated("use UUID4 instead")
30
24
  def id_default() -> int:
31
25
  """Generate a unique ID - usable as default value.
32
26
 
33
- Note: IdInt has space for 128 bit, so 128/4 = 32 hex-chars
27
+ Note: IdInt in mongoDB can currently handle 8 bytes (16 hex-values)
34
28
  """
35
- time_stamp = str(local_now()).encode("utf-8")
36
- time_hash = hashlib.sha3_224(time_stamp).hexdigest()[-32:]
37
- return int(time_hash, 16)
29
+ return int(uuid4().hex[-15:], 16)
38
30
 
39
31
 
40
32
  class ContentModel(ShpModel):
41
33
  """Base-Model for content with generalized properties."""
42
34
 
43
- # id: UUID4 = Field( # TODO: db-migration - temp fix for documentation
44
- id: Union[UUID4, int] = Field(
35
+ id: int = Field(
45
36
  description="Unique ID",
46
- default_factory=uuid4,
37
+ default_factory=id_default,
47
38
  )
48
39
  name: NameStr
49
40
  description: Annotated[Optional[SafeStr], Field(description="Required when public")] = None
@@ -1,7 +1,7 @@
1
1
  - datatype: EnergyEnvironment
2
2
  created: 2025-05-12 17:28:39.756999+02:00
3
3
  parameters:
4
- id: 2639560972524229652
4
+ id: 263956097252422
5
5
  name: eenv_static_2000mV_10mA_3600s
6
6
  description: Artificial static Energy Environment, 2000mV, 10mA, 3600s
7
7
  comment: null
@@ -24,7 +24,7 @@
24
24
  - datatype: EnergyEnvironment
25
25
  created: 2025-05-12 17:28:39.764595+02:00
26
26
  parameters:
27
- id: 9823394105967169626
27
+ id: 98233941059
28
28
  name: eenv_static_2000mV_1mA_3600s
29
29
  description: Artificial static Energy Environment, 2000mV, 1mA, 3600s
30
30
  comment: null
@@ -47,7 +47,7 @@
47
47
  - datatype: EnergyEnvironment
48
48
  created: 2025-05-12 17:28:39.772144+02:00
49
49
  parameters:
50
- id: 3900615675169501222
50
+ id: 39006156751
51
51
  name: eenv_static_2000mV_50mA_3600s
52
52
  description: Artificial static Energy Environment, 2000mV, 50mA, 3600s
53
53
  comment: null
@@ -70,7 +70,7 @@
70
70
  - datatype: EnergyEnvironment
71
71
  created: 2025-05-12 17:28:39.779737+02:00
72
72
  parameters:
73
- id: 14796673729431137386
73
+ id: 1479667372943113
74
74
  name: eenv_static_2000mV_5mA_3600s
75
75
  description: Artificial static Energy Environment, 2000mV, 5mA, 3600s
76
76
  comment: null
@@ -93,7 +93,7 @@
93
93
  - datatype: EnergyEnvironment
94
94
  created: 2025-05-12 17:28:39.787329+02:00
95
95
  parameters:
96
- id: 6648482606607441403
96
+ id: 664848260660744
97
97
  name: eenv_static_3000mV_10mA_3600s
98
98
  description: Artificial static Energy Environment, 3000mV, 10mA, 3600s
99
99
  comment: null
@@ -116,7 +116,7 @@
116
116
  - datatype: EnergyEnvironment
117
117
  created: 2025-05-12 17:28:39.794922+02:00
118
118
  parameters:
119
- id: 13566000951043177991
119
+ id: 1356600095104317
120
120
  name: eenv_static_3000mV_1mA_3600s
121
121
  description: Artificial static Energy Environment, 3000mV, 1mA, 3600s
122
122
  comment: null
@@ -139,7 +139,7 @@
139
139
  - datatype: EnergyEnvironment
140
140
  created: 2025-05-12 17:28:39.802410+02:00
141
141
  parameters:
142
- id: 7977778327156610158
142
+ id: 797777832715661
143
143
  name: eenv_static_3000mV_50mA_3600s
144
144
  description: Artificial static Energy Environment, 3000mV, 50mA, 3600s
145
145
  comment: null
@@ -162,7 +162,7 @@
162
162
  - datatype: EnergyEnvironment
163
163
  created: 2025-05-12 17:28:39.810147+02:00
164
164
  parameters:
165
- id: 4900162978999238419
165
+ id: 490016297899923
166
166
  name: eenv_static_3000mV_5mA_3600s
167
167
  description: Artificial static Energy Environment, 3000mV, 5mA, 3600s
168
168
  comment: null
@@ -269,7 +269,7 @@
269
269
  - datatype: Firmware
270
270
  created: 2025-05-12 17:28:39.851412+02:00
271
271
  parameters:
272
- id: 7163917825449888392
272
+ id: 716391782544988
273
273
  name: nrf52_deep_sleep
274
274
  description: practically turned off MCU with the lowest possible consumption
275
275
  comment: null
@@ -325,7 +325,7 @@
325
325
  - datatype: Firmware
326
326
  created: 2025-05-12 17:28:39.868340+02:00
327
327
  parameters:
328
- id: 3174430733058172825
328
+ id: 317443073305817
329
329
  name: nrf52_rf_survey
330
330
  description: Link Matrix Generator - TX-Unit - sends packet with every possible
331
331
  P_TX, loops until stopped
@@ -354,7 +354,7 @@
354
354
  - datatype: Firmware
355
355
  created: 2025-05-12 17:28:39.876819+02:00
356
356
  parameters:
357
- id: 16381936580724580968
357
+ id: 1638193658072458
358
358
  name: nrf52_rf_test
359
359
  description: sends out 1 BLE-Packet per second (verify with an app like 'RaMBLE')
360
360
  comment: null
@@ -23,54 +23,283 @@ from .energy_environment import EnergyDType
23
23
  class AlgorithmDType(str, Enum):
24
24
  """Options for choosing a harvesting algorithm."""
25
25
 
26
- direct = disable = neutral = "neutral" # for just using IVTrace / samples
27
- isc_voc = "isc_voc" # only recordable ATM
26
+ direct = disable = neutral = "neutral"
27
+ """
28
+ Reads an energy environment as is without selecting a harvesting
29
+ voltage.
30
+
31
+ Used to play "constant-power" energy environments or simple
32
+ "on-off-patterns". Generally, not useful for virtual source
33
+ emulation.
34
+
35
+ Not applicable to real harvesting, only emulation with IVTrace / samples.
36
+ """
37
+
38
+ isc_voc = "isc_voc"
39
+ """
40
+ Short Circuit Current, Open Circuit Voltage.
41
+
42
+ This is not relevant for emulation, but used to configure recording of
43
+ energy environments.
44
+
45
+ This mode samples the two extremes of an IV curve, which may be
46
+ interesting to characterize a transducer/energy environment.
47
+
48
+ Not applicable to emulation - only recordable during harvest-recording ATM.
49
+ """
50
+
28
51
  ivcurve = ivcurves = ivsurface = "ivcurve"
52
+ """
53
+ Used during harvesting to record the full IV surface.
54
+
55
+ When configuring the energy environment recording, this algorithm
56
+ records the IV surface by repeatedly recording voltage and current
57
+ while ramping the voltage.
58
+
59
+ Cannot be used as output of emulation.
60
+ """
61
+
29
62
  constant = cv = "cv"
63
+ """
64
+ Harvest energy at a fixed predefined voltage ('voltage_mV').
65
+
66
+ For harvesting, this records the IV samples at the specified voltage.
67
+ For emulation, this virtually harvests the IV surface at the specified voltage.
68
+
69
+ In addition to constant voltage harvesting, this can be used together
70
+ with the 'feedback_to_hrv' flag to implement a "Capacitor and Diode"
71
+ topology, where the harvesting voltage depends dynamically on the
72
+ capacitor voltage.
73
+ """
74
+
30
75
  # ci .. constant current -> is this desired?
76
+
31
77
  mppt_voc = "mppt_voc"
78
+ """
79
+ Emulate a harvester with maximum power point (MPP) tracking based on
80
+ open circuit voltage measurements.
81
+
82
+ This MPPT heuristic estimates the MPP as a constant ratio of the open
83
+ circuit voltage.
84
+
85
+ Used in conjunction with 'setpoint_n', 'interval_ms', and 'duration_ms'.
86
+ """
87
+
32
88
  mppt_po = perturb_observe = "mppt_po"
89
+ """
90
+ Emulate a harvester with perturb and observe maximum power point
91
+ tracking.
92
+
93
+ This MPPT heuristic adjusts the harvesting voltage by small amounts and
94
+ checks if the power increases. Eventually, the tracking changes the
95
+ direction of adjustments and oscillates around the MPP.
96
+ """
97
+
33
98
  mppt_opt = optimal = "mppt_opt"
99
+ """
100
+ A theoretical harvester that identifies the MPP by reading it from the
101
+ IV curve during emulation.
34
102
 
103
+ Note that this is not possible for real-world harvesting as the system would
104
+ not know the entire IV curve. In that case a very fast and detailed mppt_po is
105
+ used.
106
+ """
35
107
 
36
- class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
37
- """A vHrv makes a source-characterization (i.e. ivcurve) usable for the vSrc.
38
108
 
39
- Mostly used when the file-based energy environment of the virtual source
40
- is not already supplied as pre-harvested ivtrace.
109
+ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
110
+ """The virtual harvester configuration characterizes usage of an energy environment.
111
+
112
+ It is used to both harvesting during emulation and to record
113
+ energy environments (sometimes referred to as "harvesting traces").
114
+
115
+ For emulation:
116
+
117
+ The virtual harvester configuration describes how energy from a recorded
118
+ energy environment is harvested. Typically, the energy environment provides
119
+ an IV-surface, which is a continuous function in three dimensions: voltage,
120
+ current, and time. Based on this surface, the emulation can derive the
121
+ available IV-curve at each point in time. The harvester looks up the current
122
+ that is available (according to the energy environment) from a given
123
+ harvesting voltage. The harvesting voltage may be dynamically chosen by the
124
+ harvester based on the implemented harvesting algorithm, which models
125
+ different real-world harvesters. For example, a maximum power point tracking
126
+ harvester may choose a harvesting voltage as a ratio of the open circuit
127
+ voltage available from the energy environment (or transducer in practice).
128
+
129
+ The energy environments are encoded not as a three-dimensional function, but
130
+ as IV tuples over time (sampled at a constant frequency). This originates
131
+ from the technical implementation when recording the IV-surface, where the
132
+ recorder provides the IV-curve by measuring the current for a given voltage
133
+ and ramping the voltage from minimal to maximum.
134
+
135
+ For harvest-recordings:
136
+
137
+ An energy environment is fully described by the IV surface, which are IV
138
+ curves over time. Shepherd approximates this by sampling the current at
139
+ equidistant steps of a voltage ramp. The VirtualHarvesterConfig is also used
140
+ to parameterize the recording process, typically, it should be configured to
141
+ record a full IV surface, as this contains the full information of the energy
142
+ environment. The postponed harvesting is then performed during emulation.
143
+
144
+ However, it is also possible to record a "pre-harvested" energy environment
145
+ by performing the harvesting during recording. This results in a recording
146
+ containing IV samples over time that represent the harvesting voltage
147
+ (chosen by the virtual harvester during recording) and the current available
148
+ from the energy environment for that voltage. Together, these represent the
149
+ power available for harvesting at the time, and during emulation, this power
150
+ can be converted by the input stage (boost converter) to charge the energy
151
+ storage.
41
152
  """
42
153
 
43
154
  # General Metadata & Ownership -> ContentModel
44
155
 
45
156
  algorithm: AlgorithmDType
46
- # used to harvest energy
157
+ """The algorithm determines how the harvester chooses the harvesting voltage.
158
+ """
47
159
 
48
160
  samples_n: Annotated[int, Field(ge=8, le=2_000)] = 8
49
- # for & of ivcurve (and more?`)
161
+ """How many IV samples are measured for one IV curve.
162
+
163
+ The curve is recorded by measuring the el. current available from the
164
+ transducer at equidistant voltage intervals. These voltages are
165
+ probed by ramping between `voltage_min_mV` and `voltage_max_mV` at
166
+ `samples_n` points equally distributed over the voltage range. After
167
+ setting the voltage, the recorder waits for a short period - allowing
168
+ the analog frontend and transducer to settle - before recording the
169
+ harvesting current. This wait duration is influenced by
170
+ `wait_cycles`.
171
+
172
+ Selecting all these parameters is a tradeoff between accuracy of the IV
173
+ curve (density of IV samples) and measurement duration, hence the time
174
+ accuracy (density of points) of the IV-surface.
175
+
176
+ Only applicable to recording, not used in emulation.
177
+
178
+ Used together with `voltage_min_mV`, `voltage_max_mV`, `rising`, and
179
+ `wait_cycles`.
180
+ """
50
181
 
51
182
  voltage_mV: Annotated[float, Field(ge=0, le=5_000)] = 2_500
52
- # starting-point for some algorithms (mppt_po)
183
+ """The harvesting voltage for constant voltage harvesting.
184
+
185
+ Additionally, for Perturb-and-Observe MPPT, this defines the voltage at
186
+ startup.
187
+ """
188
+
53
189
  voltage_min_mV: Annotated[float, Field(ge=0, le=5_000)] = 0
190
+ """Minimum voltage recorded for the IV curve.
191
+
192
+ See `samples_n` for further details.
193
+
194
+ In emulation, this can be used to "block" parts of the recorded IV curve
195
+ and not utilize them in the virtual source. However, this is generally
196
+ discouraged as it can result in discontinuities in the curve and is not
197
+ well tested.
198
+ For emulation, this value ideally corresponds to the value of the
199
+ recorded energy environment.
200
+ """
201
+
54
202
  voltage_max_mV: Annotated[float, Field(ge=0, le=5_000)] = 5_000
203
+ """Maximum voltage sampled for the curve.
204
+
205
+ See `voltage_min_mV` and `samples_n`.
206
+ """
207
+
55
208
  current_limit_uA: Annotated[float, Field(ge=1, le=50_000)] = 50_000
56
- # ⤷ allows to keep trajectory in special region (or constant current tracking)
57
- # boundary for detecting open circuit in emulated version (working on IV-Curves)
209
+ """
210
+ For MPPT VOC, the open circuit voltage is identified as the el. current
211
+ crosses below this threshold.
212
+
213
+ During recording it allows to keep trajectory in special region
214
+ (constant current tracking).
215
+ """
216
+
58
217
  voltage_step_mV: Optional[Annotated[float, Field(ge=1, le=1_000_000)]] = None
218
+ """The difference between two adjacent voltage samples.
219
+
220
+ This value is implicitly derived from the other ramp parameters:
221
+ (voltage_max_mV - voltage_min_mV) / (samples_n - 1)
222
+ """
59
223
 
60
224
  setpoint_n: Annotated[float, Field(ge=0, le=1.0)] = 0.70
61
- # ⤷ ie. for mppt_voc
225
+ """
226
+ The "Open Circuit Voltage Maximum Power Point Tracker" estimates the MPP
227
+ by taking a constant fraction defined by this parameter of the open
228
+ circuit voltage. For example, if the IV curve shows an open circuit
229
+ voltage of 2V and the setpoint is 0.75, then the harvester selects
230
+ 1.5 volts as the harvesting voltage.
231
+
232
+ This value is only relevant when 'algorithm == mppt_voc'.
233
+ """
234
+
62
235
  interval_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 100
63
- # between start of measurements (ie. V_OC)
236
+ """The MPP is repeatedly estimated at fixed intervals defined by this duration.
237
+
238
+ Note that the energy environment can still change in between MPP
239
+ estimations, but the harvesting voltage is not updated in between.
240
+
241
+ This value is relevant for all MPP algorithms.
242
+ For Perturb and Observe, this value is the wait interval between steps.
243
+
244
+ When an energy environment is recorded with `mppt_opt`, the optimal
245
+ harvester is approximated with a very fast Perturb-Observe algorithm,
246
+ where this interval should be set to a very small value.
247
+ When emulating with `mppt_opt`, this value is not relevant as the
248
+ emulation simply picks the maximum power point from the IV-curve.
249
+ """
250
+
64
251
  duration_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 0.1
65
- # of (open voltage) measurement
252
+ """The duration of MPP sampling.
253
+
254
+ While performing an MPP sampling every 'interval_ms', the input is
255
+ disconnected to accurately measure the open circuit voltage.
256
+
257
+ This value is only relevant for `mppt_voc`.
258
+ """
259
+
66
260
  rising: bool = True
67
- # direction of sawtooth
261
+ """Ramp direction for sampling the IV curve.
262
+
263
+ When set to true, sampling starts at the minimum voltage and ramps up to
264
+ the maximum.
265
+
266
+ See `samples_n` for further details.
267
+ Not relevant for emulation.
268
+ """
269
+
68
270
  enable_linear_extrapolation: bool = True
69
- # ⤷ improves slow cv-algo that is base of most ivcurve-harvesters
70
- # (update-freq dependent on window-size)
271
+ """
272
+ Because the IV curve is not stored fully in PRU memory but streamed
273
+ sequentially to the PRU, looking up any IV value at any time is not
274
+ possible. However, because the precision of the emulation degrades
275
+ until the relevant IV sample passes by, it can extrapolate the available data
276
+ to estimate the required IV sample.
277
+
278
+ Enabling extrapolation can yield a better numeric simulation, especially
279
+ if the harvesting voltage changes rapidly or the IV surface is steep in
280
+ relevant regions. For example, when emulating a capacitor diode
281
+ setup and the current falls at high voltages.
282
+
283
+ This value is only relevant for emulation.
284
+ """
71
285
 
72
286
  # Underlying recorder
73
287
  wait_cycles: Annotated[int, Field(ge=0, le=100)] = 1
288
+ """
289
+ The wait duration to let the analog frontend settle before taking a
290
+ measurement.
291
+
292
+ When recording the energy environment, the voltage is set by the
293
+ digital-to-analog-converter. This parameter delays the current
294
+ measurement performed by the analog-to-digital converter to allow the
295
+ harvesting transducer to settle at the defined voltage.
296
+
297
+ When recording with `IscVoc`, wait cycles should be added as the analog
298
+ changes are more significant.
299
+
300
+ Not relevant for emulation.
301
+ """
302
+
74
303
  # ⤷ first cycle: ADC-Sampling & DAC-Writing, further steps: waiting
75
304
 
76
305
  @model_validator(mode="before")
@@ -70,6 +70,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
70
70
  # ⤷ target gets disconnected
71
71
  interval_check_thresholds_ms: Annotated[float, Field(ge=0, le=4.29e3)] = 0
72
72
  # ⤷ some ICs (BQ) check every 64 ms if output should be disconnected
73
+ # TODO: add intervals for input-disable, output-disable & power-good-signal
73
74
 
74
75
  # pwr-good: target is informed on output-pin (hysteresis) -> for intermediate voltage
75
76
  V_pwr_good_enable_threshold_mV: Annotated[float, Field(ge=0, le=10_000)] = 2_800
@@ -5,17 +5,16 @@ from datetime import datetime
5
5
  from datetime import timedelta
6
6
  from typing import Annotated
7
7
  from typing import Optional
8
- from typing import Union
9
- from uuid import uuid4
10
8
 
11
- from pydantic import UUID4
12
9
  from pydantic import Field
13
10
  from pydantic import model_validator
14
11
  from typing_extensions import Self
12
+ from typing_extensions import deprecated
15
13
 
16
14
  from shepherd_core.data_models.base.content import IdInt
17
15
  from shepherd_core.data_models.base.content import NameStr
18
16
  from shepherd_core.data_models.base.content import SafeStr
17
+ from shepherd_core.data_models.base.content import id_default
19
18
  from shepherd_core.data_models.base.shepherd import ShpModel
20
19
  from shepherd_core.data_models.testbed.target import Target
21
20
  from shepherd_core.data_models.testbed.testbed import Testbed
@@ -29,8 +28,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
29
28
  """Config for experiments on the testbed emulating energy environments for target nodes."""
30
29
 
31
30
  # General Properties
32
- # id: UUID4 ... # TODO: db-migration - temp fix for documentation
33
- id: Union[UUID4, int] = Field(default_factory=uuid4)
31
+ id: int = Field(description="Unique ID", default_factory=id_default)
34
32
  # ⤷ TODO: automatic ID is problematic for identification by hash
35
33
 
36
34
  name: NameStr
@@ -51,7 +49,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
51
49
  # schedule
52
50
  time_start: Optional[datetime] = None # = ASAP
53
51
  duration: Optional[timedelta] = None # = till EOF
54
- abort_on_error: bool = False
52
+ abort_on_error: Annotated[bool, deprecated("has no effect")] = False
55
53
 
56
54
  # targets
57
55
  target_configs: Annotated[list[TargetConfig], Field(min_length=1, max_length=128)]
@@ -61,6 +59,8 @@ class Experiment(ShpModel, title="Config of an Experiment"):
61
59
 
62
60
  @model_validator(mode="after")
63
61
  def post_validation(self) -> Self:
62
+ # TODO: only do deep validation with active connection to TB-client
63
+ # or with cached fixtures
64
64
  testbed = Testbed() # this will query the first (and only) entry of client
65
65
  self._validate_targets(self.target_configs)
66
66
  self._validate_observers(self.target_configs, testbed)
@@ -6,12 +6,14 @@ from typing import Annotated
6
6
  from typing import Optional
7
7
 
8
8
  import numpy as np
9
+ from annotated_types import Interval
9
10
  from pydantic import Field
10
11
  from pydantic import PositiveFloat
11
12
  from pydantic import model_validator
12
13
  from typing_extensions import Self
13
14
  from typing_extensions import deprecated
14
15
 
16
+ from shepherd_core import logger
15
17
  from shepherd_core.data_models.base.shepherd import ShpModel
16
18
  from shepherd_core.data_models.testbed.gpio import GPIO
17
19
 
@@ -105,7 +107,7 @@ STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
105
107
  STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
106
108
 
107
109
 
108
- class UartTracing(ShpModel, title="Config for UART Tracing"):
110
+ class UartLogging(ShpModel, title="Config for UART Logging"):
109
111
  """Configuration for recording UART-Output of the Target Nodes.
110
112
 
111
113
  Note that the Communication has to be on a specific port that
@@ -132,17 +134,33 @@ class UartTracing(ShpModel, title="Config for UART Tracing"):
132
134
  return self
133
135
 
134
136
 
137
+ GpioInt = Annotated[int, Interval(ge=0, le=17)]
138
+ GpioList = Annotated[list[GpioInt], Field(min_length=1, max_length=18)]
139
+ all_gpio = list(range(18))
140
+
141
+
135
142
  class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
136
143
  """Configuration for recording the GPIO-Output of the Target Nodes.
137
144
 
138
145
  TODO: postprocessing not implemented ATM
139
146
  """
140
147
 
141
- # initial recording
142
- mask: Annotated[int, Field(ge=0, lt=2**10)] = 0b11_1111_1111 # all
143
- # ⤷ TODO: custom mask not implemented in PRU, ATM
144
- gpios: Optional[Annotated[list[GPIO], Field(min_length=1, max_length=10)]] = None # = all
145
- # TODO: list of GPIO to build mask, one of both should be internal / computed field
148
+ gpios: GpioList = all_gpio
149
+ """List of GPIO to record.
150
+
151
+ This feature allows to remove unwanted pins from recording,
152
+ i.e. for chatty pins with separate UART Logging enabled.
153
+ Numbering is based on the Target-Port and its 16x GPIO and two PwrGood-Signals.
154
+ See doc for nRF_FRAM_Target_v1.3+ to see mapping of target port.
155
+
156
+ Example for skipping UART (pin 0 & 1):
157
+ .gpio = range(2,18)
158
+
159
+ Note:
160
+ - Cape 2.4 (2023) and lower only has 9x GPIO + 1x PwrGood
161
+ - Cape 2.5 (2025) has first 12 GPIO & both PwrGood
162
+ - this will be mapped accordingly by the observer
163
+ """
146
164
 
147
165
  # time
148
166
  delay: timedelta = timedelta(seconds=0)
@@ -150,31 +168,30 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
150
168
 
151
169
  # post-processing,
152
170
  uart_decode: bool = False
153
- # TODO: quickfix - uart-log currently done online in userspace
154
- # NOTE: gpio-tracing currently shows rather big - but rare - "blind" windows (~1-4us)
155
171
  uart_pin: GPIO = GPIO(name="GPIO8")
156
172
  uart_baudrate: Annotated[int, Field(ge=2_400, le=1_152_000)] = 115_200
157
- # TODO: add a "discard_gpio" (if only uart is wanted)
158
173
 
159
174
  @model_validator(mode="after")
160
175
  def post_validation(self) -> Self:
161
- if self.mask == 0:
162
- raise ValueError("Error in config -> tracing enabled but mask is 0")
163
176
  if self.delay and self.delay.total_seconds() < 0:
164
177
  raise ValueError("Delay can't be negative.")
165
178
  if self.duration and self.duration.total_seconds() < 0:
166
179
  raise ValueError("Duration can't be negative.")
167
- if self.mask != 0b11_1111_1111: # GpioTracing.mask
168
- raise NotImplementedError("Feature GpioTracing.mask reserved for future use.")
169
- if self.gpios is not None:
170
- raise NotImplementedError("Feature GpioTracing.gpios reserved for future use.")
171
180
  if self.uart_decode:
172
- raise NotImplementedError(
181
+ logger.error(
173
182
  "Feature GpioTracing.uart_decode reserved for future use. "
174
- "Use UartTracing or manually decode serial with the provided waveform decoder."
183
+ "Use UartLogging or manually decode serial with the provided waveform decoder."
175
184
  )
176
185
  return self
177
186
 
187
+ @property
188
+ def gpio_mask(self) -> int:
189
+ # valid for cape v2.5
190
+ mask = 0
191
+ for gpio in set(self.gpios):
192
+ mask |= 2**gpio
193
+ return mask
194
+
178
195
 
179
196
  class GpioLevel(str, Enum):
180
197
  """Options for setting the gpio-level or state."""
@@ -18,7 +18,7 @@ from shepherd_core.data_models.testbed.target import Target
18
18
  from .observer_features import GpioActuation
19
19
  from .observer_features import GpioTracing
20
20
  from .observer_features import PowerTracing
21
- from .observer_features import UartTracing
21
+ from .observer_features import UartLogging
22
22
 
23
23
 
24
24
  class TargetConfig(ShpModel, title="Target Config"):
@@ -43,8 +43,8 @@ class TargetConfig(ShpModel, title="Target Config"):
43
43
 
44
44
  power_tracing: Optional[PowerTracing] = None
45
45
  gpio_tracing: Optional[GpioTracing] = None
46
- uart_tracing: Optional[UartTracing] = None
47
46
  gpio_actuation: Optional[GpioActuation] = None
47
+ uart_logging: Optional[UartLogging] = None
48
48
 
49
49
  @model_validator(mode="after")
50
50
  def post_validation(self) -> Self:
@@ -13,6 +13,7 @@ from pydantic import Field
13
13
  from pydantic import model_validator
14
14
  from pydantic import validate_call
15
15
  from typing_extensions import Self
16
+ from typing_extensions import deprecated
16
17
 
17
18
  from shepherd_core.data_models.base.content import IdInt
18
19
  from shepherd_core.data_models.base.shepherd import ShpModel
@@ -23,7 +24,7 @@ from shepherd_core.data_models.experiment.observer_features import GpioActuation
23
24
  from shepherd_core.data_models.experiment.observer_features import GpioTracing
24
25
  from shepherd_core.data_models.experiment.observer_features import PowerTracing
25
26
  from shepherd_core.data_models.experiment.observer_features import SystemLogging
26
- from shepherd_core.data_models.experiment.observer_features import UartTracing
27
+ from shepherd_core.data_models.experiment.observer_features import UartLogging
27
28
  from shepherd_core.data_models.testbed import Testbed
28
29
  from shepherd_core.data_models.testbed.cape import TargetPort
29
30
  from shepherd_core.logger import logger
@@ -63,7 +64,7 @@ class EmulationTask(ShpModel):
63
64
  # timestamp or unix epoch time, None = ASAP
64
65
  duration: Optional[timedelta] = None
65
66
  # ⤷ Duration of recording in seconds, None = till EOF
66
- abort_on_error: bool = False # TODO: remove, should not exist
67
+ abort_on_error: Annotated[bool, deprecated("has no effect")] = False
67
68
 
68
69
  # emulation-specific
69
70
  use_cal_default: bool = False
@@ -91,7 +92,7 @@ class EmulationTask(ShpModel):
91
92
 
92
93
  power_tracing: Optional[PowerTracing] = PowerTracing()
93
94
  gpio_tracing: Optional[GpioTracing] = GpioTracing()
94
- uart_tracing: Optional[UartTracing] = UartTracing()
95
+ uart_logging: Optional[UartLogging] = UartLogging()
95
96
  gpio_actuation: Optional[GpioActuation] = None
96
97
  sys_logging: Optional[SystemLogging] = SystemLogging()
97
98
 
@@ -132,7 +133,7 @@ class EmulationTask(ShpModel):
132
133
  raise ValueError("GPIO Actuation not yet implemented!")
133
134
 
134
135
  io_requested = any(
135
- _io is not None for _io in (self.gpio_actuation, self.gpio_tracing, self.uart_tracing)
136
+ _io is not None for _io in (self.gpio_actuation, self.gpio_tracing, self.uart_logging)
136
137
  )
137
138
  if self.enable_io and not io_requested:
138
139
  logger.warning("Target IO enabled, but no feature requested IO")
@@ -147,7 +148,7 @@ class EmulationTask(ShpModel):
147
148
  tgt_cfg = xp.get_target_config(tgt_id)
148
149
  io_requested = any(
149
150
  _io is not None
150
- for _io in (tgt_cfg.gpio_actuation, tgt_cfg.gpio_tracing, tgt_cfg.uart_tracing)
151
+ for _io in (tgt_cfg.gpio_actuation, tgt_cfg.gpio_tracing, tgt_cfg.uart_logging)
151
152
  )
152
153
 
153
154
  return cls(
@@ -155,14 +156,13 @@ class EmulationTask(ShpModel):
155
156
  output_path=root_path / f"emu_{obs.name}.h5",
156
157
  time_start=copy.copy(xp.time_start),
157
158
  duration=xp.duration,
158
- abort_on_error=xp.abort_on_error,
159
159
  enable_io=io_requested,
160
160
  io_port=obs.get_target_port(tgt_id),
161
161
  pwr_port=obs.get_target_port(tgt_id),
162
162
  virtual_source=tgt_cfg.virtual_source,
163
163
  power_tracing=tgt_cfg.power_tracing,
164
164
  gpio_tracing=tgt_cfg.gpio_tracing,
165
- uart_tracing=tgt_cfg.uart_tracing,
165
+ uart_logging=tgt_cfg.uart_logging,
166
166
  gpio_actuation=tgt_cfg.gpio_actuation,
167
167
  sys_logging=xp.sys_logging,
168
168
  )
@@ -9,6 +9,7 @@ from typing import Optional
9
9
  from pydantic import Field
10
10
  from pydantic import model_validator
11
11
  from typing_extensions import Self
12
+ from typing_extensions import deprecated
12
13
 
13
14
  from shepherd_core.data_models.base.shepherd import ShpModel
14
15
  from shepherd_core.data_models.base.timezone import local_tz
@@ -36,8 +37,8 @@ class HarvestTask(ShpModel):
36
37
  time_start: Optional[datetime] = None
37
38
  # timestamp or unix epoch time, None = ASAP
38
39
  duration: Optional[timedelta] = None
39
- # ⤷ Duration of recording in seconds, None = till EOF
40
- abort_on_error: bool = False
40
+ # ⤷ Duration of recording in seconds, None = till EOFSys
41
+ abort_on_error: Annotated[bool, deprecated("has no effect")] = False
41
42
 
42
43
  # emulation-specific
43
44
  use_cal_default: bool = False
@@ -3,10 +3,12 @@
3
3
  from datetime import datetime
4
4
  from datetime import timedelta
5
5
  from pathlib import Path
6
+ from typing import Annotated
6
7
  from typing import Optional
7
8
 
8
9
  from pydantic import validate_call
9
10
  from typing_extensions import Self
11
+ from typing_extensions import deprecated
10
12
 
11
13
  from shepherd_core.data_models.base.content import IdInt
12
14
  from shepherd_core.data_models.base.content import NameStr
@@ -28,7 +30,7 @@ class ObserverTasks(ShpModel):
28
30
  # PRE PROCESS
29
31
  time_prep: datetime # TODO: should be optional
30
32
  root_path: Path
31
- abort_on_error: bool
33
+ abort_on_error: Annotated[bool, deprecated("has no effect")] = False
32
34
 
33
35
  # fw mod, store as hex-file and program
34
36
  fw1_mod: Optional[FirmwareModTask] = None
@@ -70,7 +72,6 @@ class ObserverTasks(ShpModel):
70
72
  owner_id=xp.owner_id,
71
73
  time_prep=t_start - tb.prep_duration,
72
74
  root_path=root_path,
73
- abort_on_error=xp.abort_on_error,
74
75
  fw1_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
75
76
  fw2_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 2, fw_paths[1]),
76
77
  fw1_prog=ProgrammingTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
@@ -91,8 +92,8 @@ class ObserverTasks(ShpModel):
91
92
  tasks.append(task)
92
93
  return tasks
93
94
 
94
- def get_output_paths(self) -> dict:
95
- values = {}
95
+ def get_output_paths(self) -> dict[str, Path]:
96
+ values: dict[str, Path] = {}
96
97
  if isinstance(self.emulation, EmulationTask):
97
98
  if self.emulation.output_path is None:
98
99
  raise ValueError("Emu-Task should have a valid output-path")
@@ -33,6 +33,7 @@ class ProgrammingTask(ShpModel):
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
36
+ # TODO: eradicate - should not exist. derive protocol from mcu_type
36
37
 
37
38
  simulate: bool = False
38
39
 
@@ -1,5 +1,6 @@
1
1
  """Collection of tasks for all observers included in experiment."""
2
2
 
3
+ from pathlib import Path
3
4
  from typing import Annotated
4
5
  from typing import Optional
5
6
 
@@ -50,11 +51,11 @@ class TestbedTasks(ShpModel):
50
51
  return tasks
51
52
  return None
52
53
 
53
- def get_output_paths(self) -> dict:
54
+ def get_output_paths(self) -> dict[str, Path]:
54
55
  # TODO: computed field preferred, but they don't work here, as
55
56
  # - they are always stored in yaml despite "repr=False"
56
57
  # - solution will shift to some kind of "result"-datatype that is combinable
57
- values = {}
58
+ values: dict[str, Path] = {}
58
59
  for obt in self.observer_tasks:
59
60
  values = {**values, **obt.get_output_paths()}
60
61
  return values
@@ -92,3 +92,11 @@
92
92
  id: 1270065
93
93
  name: cape65
94
94
  comment: only 220u/440uF on 5V
95
+ - datatype: cape
96
+ parameters:
97
+ id: 42000
98
+ name: unit_testing_cape
99
+ version: none
100
+ description: for unit testing
101
+ created: 2025-04-29
102
+ calibrated: 2025-04-29
@@ -65,3 +65,10 @@ class GPIO(ShpModel, title="GPIO of Observer Node"):
65
65
 
66
66
  def user_controllable(self) -> bool:
67
67
  return ("gpio" in self.name.lower()) and (self.direction in {"IO", "OUT"})
68
+
69
+ def user_recordable(self) -> bool:
70
+ return (
71
+ ("gpio" in self.name.lower())
72
+ and (self.direction in {"IO", "IN"})
73
+ and (self.pin_pru is not None)
74
+ )
@@ -221,3 +221,20 @@
221
221
  target_a:
222
222
  name: nRF52_FRAM_1392_390
223
223
  created: 2023-09-22 12:12:12
224
+ - datatype: observer
225
+ parameters:
226
+ id: 42
227
+ name: unit_testing_sheep
228
+ ip: 0.0.0.0
229
+ mac: 00:00:00:00:00:00
230
+ room: none
231
+ eth_port: none
232
+ description: unit testing
233
+ longitude: 0
234
+ latitude: 0
235
+ cape:
236
+ name: unit_testing_cape
237
+ target_a:
238
+ name: unit_testing_target
239
+ created: 2025-04-29 13:07:00
240
+ active: true
@@ -161,3 +161,16 @@
161
161
  name: nRF52
162
162
  mcu2: null
163
163
  created: 2022-12-12 12:12:12
164
+
165
+ - datatype: target
166
+ parameters:
167
+ id: 42
168
+ name: unit_testing_target
169
+ version: v0
170
+ description: for unit testing
171
+ comment: no comment
172
+ created: 2025-04-29
173
+ mcu1:
174
+ name: nRF52
175
+ mcu2:
176
+ name: MSP430FR
@@ -23,3 +23,14 @@
23
23
  - name: sheep12
24
24
  - name: sheep13
25
25
  - name: sheep14
26
+
27
+ - datatype: testbed
28
+ parameters:
29
+ id: 42
30
+ name: unit_testing_testbed
31
+ description: "Artificial Testbed used in unit testing"
32
+ shared_storage: true
33
+ data_on_server: /tmp/
34
+ data_on_observer: /tmp/
35
+ observers:
36
+ - name: unit_testing_sheep
shepherd_core/reader.py CHANGED
@@ -412,19 +412,20 @@ class Reader:
412
412
  self.file_path.name,
413
413
  )
414
414
  # same length of datasets:
415
- for dset in ["current", "time"]:
415
+ samples_n = self.h5file["data"]["time"].shape[0]
416
+ for dset in ["voltage", "current"]:
416
417
  ds_size = self.h5file["data"][dset].shape[0]
417
- if ds_size != self.samples_n:
418
+ if ds_size != samples_n:
418
419
  self._logger.warning(
419
420
  "[FileValidation] dataset '%s' has different size (=%d), "
420
- "compared to smallest set (=%d), in '%s'",
421
+ "compared to time (=%d), in '%s'",
421
422
  dset,
422
423
  ds_size,
423
- self.samples_n,
424
+ samples_n,
424
425
  self.file_path.name,
425
426
  )
426
427
  # dataset-length should be multiple of chunk-size
427
- remaining_size = self.samples_n % self.CHUNK_SAMPLES_N
428
+ remaining_size = samples_n % self.CHUNK_SAMPLES_N
428
429
  if remaining_size != 0:
429
430
  self._logger.warning(
430
431
  "[FileValidation] datasets are not aligned with chunk-size in '%s'",
@@ -2,6 +2,7 @@
2
2
 
3
3
  import copy
4
4
  import pickle
5
+ from collections.abc import Iterable
5
6
  from collections.abc import Mapping
6
7
  from datetime import datetime
7
8
  from datetime import timedelta
@@ -34,11 +35,11 @@ class Fixture:
34
35
 
35
36
  def __init__(self, model_type: str) -> None:
36
37
  self.model_type: str = model_type.lower()
37
- self.elements_by_name: dict[str, dict] = {}
38
- self.elements_by_id: dict[int, dict] = {}
38
+ self.elements_by_name: dict[str, dict[str, Any]] = {}
39
+ self.elements_by_id: dict[int, dict[str, Any]] = {}
39
40
  # Iterator reset
40
41
  self._iter_index: int = 0
41
- self._iter_list: list = list(self.elements_by_name.values())
42
+ self._iter_list: list[dict[str, Any]] = list(self.elements_by_name.values())
42
43
 
43
44
  def insert(self, data: Wrapper) -> None:
44
45
  # ⤷ TODO: could get easier
@@ -54,9 +55,10 @@ class Fixture:
54
55
  self.elements_by_name[name] = data_model
55
56
  self.elements_by_id[_id] = data_model
56
57
  # update iterator
57
- self._iter_list = list(self.elements_by_name.values())
58
+ self._iter_list: list[dict[str, Any]] = list(self.elements_by_name.values())
58
59
 
59
- def __getitem__(self, key: Union[str, int]) -> dict:
60
+ def __getitem__(self, key: Union[str, int]) -> dict[str, Any]:
61
+ original_key = key
60
62
  if isinstance(key, str):
61
63
  key = key.lower()
62
64
  if key in self.elements_by_name:
@@ -65,7 +67,7 @@ class Fixture:
65
67
  key = int(key)
66
68
  if key in self.elements_by_id:
67
69
  return self.elements_by_id[int(key)]
68
- msg = f"{self.model_type} '{key}' not found!"
70
+ msg = f"{self.model_type} '{original_key}' not found!"
69
71
  raise ValueError(msg)
70
72
 
71
73
  def __iter__(self) -> Self:
@@ -73,14 +75,14 @@ class Fixture:
73
75
  self._iter_list = list(self.elements_by_name.values())
74
76
  return self
75
77
 
76
- def __next__(self) -> Any:
78
+ def __next__(self) -> dict[str, Any]:
77
79
  if self._iter_index < len(self._iter_list):
78
80
  member = self._iter_list[self._iter_index]
79
81
  self._iter_index += 1
80
82
  return member
81
83
  raise StopIteration
82
84
 
83
- def keys(self): # noqa: ANN201
85
+ def keys(self) -> Iterable[str]:
84
86
  return self.elements_by_name.keys()
85
87
 
86
88
  def refs(self) -> dict:
@@ -149,13 +151,13 @@ class Fixture:
149
151
  base[key] = value
150
152
  return base
151
153
 
152
- def query_id(self, _id: int) -> dict:
154
+ def query_id(self, _id: int) -> dict[str, Any]:
153
155
  if isinstance(_id, int) and _id in self.elements_by_id:
154
156
  return self.elements_by_id[_id]
155
157
  msg = f"Initialization of {self.model_type} by ID failed - {_id} is unknown!"
156
158
  raise ValueError(msg)
157
159
 
158
- def query_name(self, name: str) -> dict:
160
+ def query_name(self, name: str) -> dict[str, Any]:
159
161
  if isinstance(name, str) and name.lower() in self.elements_by_name:
160
162
  return self.elements_by_name[name.lower()]
161
163
  msg = f"Initialization of {self.model_type} by name failed - {name} is unknown!"
@@ -239,7 +241,7 @@ class Fixtures:
239
241
  msg = f"Component '{key}' not found!"
240
242
  raise ValueError(msg)
241
243
 
242
- def keys(self): # noqa: ANN201
244
+ def keys(self) -> Iterable[str]:
243
245
  return self.components.keys()
244
246
 
245
247
  @staticmethod
@@ -5,10 +5,7 @@ from hashlib import pbkdf2_hmac
5
5
  from typing import Annotated
6
6
  from typing import Any
7
7
  from typing import Optional
8
- from typing import Union
9
- from uuid import uuid4
10
8
 
11
- from pydantic import UUID4
12
9
  from pydantic import EmailStr
13
10
  from pydantic import Field
14
11
  from pydantic import SecretBytes
@@ -19,6 +16,7 @@ from pydantic import validate_call
19
16
 
20
17
  from shepherd_core.data_models.base.content import NameStr
21
18
  from shepherd_core.data_models.base.content import SafeStr
19
+ from shepherd_core.data_models.base.content import id_default
22
20
  from shepherd_core.data_models.base.shepherd import ShpModel
23
21
 
24
22
 
@@ -41,10 +39,9 @@ def hash_password(pw: Annotated[str, StringConstraints(min_length=20, max_length
41
39
  class User(ShpModel):
42
40
  """meta-data representation of a testbed-component (physical object)."""
43
41
 
44
- # id: UUID4 = Field( # TODO: db-migration - temp fix for documentation
45
- id: Union[UUID4, int] = Field(
42
+ id: int = Field(
46
43
  description="Unique ID",
47
- default_factory=uuid4,
44
+ default_factory=id_default,
48
45
  )
49
46
  name: NameStr
50
47
  description: Optional[SafeStr] = None
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.05.2"
3
+ version: str = "2025.05.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.5.2
3
+ Version: 2025.5.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>
@@ -2,54 +2,54 @@ shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,127
2
2
  shepherd_core/calibration_hw_def.py,sha256=aL94bA1Sf14L5A3PLdVvQVYtGi28S4NUWA65wbim8bw,2895
3
3
  shepherd_core/commons.py,sha256=rTxtndtiJ4cOHYRPRbdZdqp6T90CKFFN-I-YAFzhm4Q,200
4
4
  shepherd_core/logger.py,sha256=i8j3alm8icAx_h1_IZ6SgVgC9W5J7S-bDc31mPDWl-w,1812
5
- shepherd_core/reader.py,sha256=EYgpL09DMrACiDHoG3LbgSJrci4pCPY3r13X0XVNVLI,29179
6
- shepherd_core/version.py,sha256=p9ZnvPFg9wu9KOtST8r_-f6-9CC_FMIG7IhKqfjDez4,76
5
+ shepherd_core/reader.py,sha256=fwRGGALpw6nK4dMBznbopTRQMriHvD8u_03OcFQfQI8,29216
6
+ shepherd_core/version.py,sha256=eD5tv5xJDDTtAuWGItss0DqfnV6QAY0bpqdT1wn4kvE,76
7
7
  shepherd_core/writer.py,sha256=_ji3X2bb4JKZZm4uz8DO9uG_WoHL5Zpg6_Na36WAF5o,14609
8
- shepherd_core/data_models/__init__.py,sha256=Fl9Lhdo32mrdF232ZjOnkawQYeFsUbTS4FOUrvmSzlM,1909
8
+ shepherd_core/data_models/__init__.py,sha256=tbELRPbWx6100IHjDia85LZGb66A0ymD1bbfBWGuGJs,1909
9
9
  shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
10
10
  shepherd_core/data_models/virtual_source_doc.txt,sha256=OK_7zYbLvr6cEj3KaUcWwZJ9naoFB2KwAaXudbhzouQ,6076
11
11
  shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
12
  shepherd_core/data_models/base/cal_measurement.py,sha256=c-vjACNxsQY8LU2Msw0COrsTY-8pToJ5ZWkOztJ9tVY,3380
13
13
  shepherd_core/data_models/base/calibration.py,sha256=oUTfY6iUWUbBbOw5aMCRkdEfHzIV8aUrhwqek0QzPJM,10849
14
- shepherd_core/data_models/base/content.py,sha256=t3hw5Aes4-tVrWGOMSBGpcD642VM1RAWIo9jtbXifd0,2452
14
+ shepherd_core/data_models/base/content.py,sha256=e8-UWjF7QW4mYMqnDSnbhZTY3ODxGyx50A_K4SgDnNs,2098
15
15
  shepherd_core/data_models/base/shepherd.py,sha256=P1xW10_vIbGNyQ3Ary67vyovw9kmDtXhN-yPFYR-0Is,6085
16
16
  shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
17
17
  shepherd_core/data_models/base/wrapper.py,sha256=7QwvI30GuORH7WmyGLnRMsZ3xkRRkXIAvZ-pYRAL-WI,755
18
18
  shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKsKyMK8o6Kfaby5vlY,525
19
- shepherd_core/data_models/content/_external_fixtures.yaml,sha256=Iv0T5cN8BFVELuLtmxml2-xwcMDMZWF7LOu_TM3RIJ8,12184
19
+ shepherd_core/data_models/content/_external_fixtures.yaml,sha256=BsHW5UP1UtrEkcI-efCHq4gFtnsuOvoCPv1ri-f6JOI,12132
20
20
  shepherd_core/data_models/content/energy_environment.py,sha256=DSTB3CE3XEBXaL16z4FVaHSyLnp0bfxUgw3SnQrUpf0,1628
21
21
  shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
22
22
  shepherd_core/data_models/content/firmware.py,sha256=KLByo1GuUMJ8ZbM6WccB8IJArorNetBxqOpuUb2_QIE,5926
23
23
  shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
- shepherd_core/data_models/content/virtual_harvester.py,sha256=QLyOLH0Q1E8NKWgWyMd4e65iEA13CVHmdf7kLf4SqFk,11287
24
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=_b31Ji_N2NS92FXt1UkgzesaAi2M6T8b2cZrVzM3lw0,20313
25
25
  shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=4eNQyFI9LozpButOTlBQ07T0MFCaPEYIxwtedMjUf3U,4575
26
- shepherd_core/data_models/content/virtual_source.py,sha256=4TAk7jgXCQh6MMnT0Ijw_AdE6YPgrbSwFfb8F1V6RVE,15565
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=GLMBhhp7RPoaieZTPuYVF5q0XRw8D3RzoOaJWGeIkbg,15645
27
27
  shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=tsSJ1mM4OSPSeNkqVoZlDQ8Az3N8KeTXd0oA2JKd-hk,11201
28
28
  shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
29
- shepherd_core/data_models/experiment/experiment.py,sha256=oFOpyxHmwCZmbJAejyoqAAjarA2ZgnNYEx6Qf7TPjqU,4178
30
- shepherd_core/data_models/experiment/observer_features.py,sha256=xCaN2wymG-ygEy19jTIvY9YyitdjPWepDH5UjXdq8Y8,8315
31
- shepherd_core/data_models/experiment/target_config.py,sha256=nlOZvLC5C6Pjn-ZEXLMr5WZlC1edixL_l3rWi93UPZE,4082
29
+ shepherd_core/data_models/experiment/experiment.py,sha256=K7eENfZmUg4-Nl3jHyZfsWjgkFwLX3k6JRFbwQ3JMfY,4308
30
+ shepherd_core/data_models/experiment/observer_features.py,sha256=i3JGXK4xGhQhuqKrnUHpA-YAoUsTY-RKZ4pPeWWkPlI,8359
31
+ shepherd_core/data_models/experiment/target_config.py,sha256=FzKdYVO0ynN4-lwM5Is-ts9uQoCv52U-KZ--GHvl7dY,4082
32
32
  shepherd_core/data_models/task/__init__.py,sha256=m7_OK05djkoDt-XwKQ6gPT7qIcCUoyBxZgSXvD3HmzQ,3386
33
- shepherd_core/data_models/task/emulation.py,sha256=-JzRm-xRp421Xl4lBkT52AEhTkb8ZqH9C1Z8CeHPHF8,7380
33
+ shepherd_core/data_models/task/emulation.py,sha256=FZOG36OZCBjS3MkWu8oHCiXv3ljPMx7h7EqMjl6f_xM,7381
34
34
  shepherd_core/data_models/task/firmware_mod.py,sha256=WLmevU9-Q5QnyANbkrSCdFbnVuSi8N8fXdQ2l_Um9e8,3034
35
- shepherd_core/data_models/task/harvest.py,sha256=rQ5Aq40m4WHpEFfiwn309GjLIPu4aKzF4Fvbo4j7PLs,3470
36
- shepherd_core/data_models/task/observer_tasks.py,sha256=icUPtoIqic8IpE0U9Fc7btMj4CpYx1PoAu_wAqqZkU8,3608
37
- shepherd_core/data_models/task/programming.py,sha256=jX_5biD6BGkBdIS652CajXWm0KOgY3w1Z3AOJIFgTws,2573
38
- shepherd_core/data_models/task/testbed_tasks.py,sha256=J3aOSVVG1KvEI_83j0TK0gc5rN_v_T-URFRhrEs8czY,2176
35
+ shepherd_core/data_models/task/harvest.py,sha256=BrT4kcLniSS_d-5RSdiCPtojVrA5st0P0-FOHiXCOR4,3554
36
+ shepherd_core/data_models/task/observer_tasks.py,sha256=TKGWhQ2-KU0j5wBg48U-0nVAfUL1QZeMUCrlkeLZdz8,3708
37
+ shepherd_core/data_models/task/programming.py,sha256=Bo_q6dDmg4dnX19QblBWkMQKpUJcLYRuVRXc96POWyo,2645
38
+ shepherd_core/data_models/task/testbed_tasks.py,sha256=hOAKEvABIeiiKk4W4IfqNMP2N95NwqyKRqOaKT-WeBg,2229
39
39
  shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
40
40
  shepherd_core/data_models/testbed/cape.py,sha256=vfS05D0rC1-_wMiHeLw69VE9PxXC6PHl9ndtrv219_k,1396
41
- shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=uwZxe6hsqvofn5tzg4sffjbVtTVUkextL1GCri_z2A4,2197
42
- shepherd_core/data_models/testbed/gpio.py,sha256=pOY_7Zs32sRGuqJZrZDrjvr69QkFXC8iXgav7kk2heY,2334
41
+ shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=-N_iinnz-Ea_6qJ6C288XM06M53Ig7oA70cj4CdmC-c,2373
42
+ shepherd_core/data_models/testbed/gpio.py,sha256=eYAcVbjUlECqs1caTIiN-4HdEvXxxkEXYsghIY4yOqk,2535
43
43
  shepherd_core/data_models/testbed/gpio_fixture.yaml,sha256=yXvoXAau2hancKi2yg1xIkErPWQa6gIxNUG3y8JuF9Y,3076
44
44
  shepherd_core/data_models/testbed/mcu.py,sha256=fuq2AWbVFbbzPRPCgIeMNFhJhVNCIsmpjFagnOXkjbY,1514
45
45
  shepherd_core/data_models/testbed/mcu_fixture.yaml,sha256=bOYXdQY-6JYesxOkZAT8WvuGsdUc_MW4dkAmopLL8RM,507
46
46
  shepherd_core/data_models/testbed/observer.py,sha256=RO7i9TmHcxx69P-EHN59M9MAJetoHZg5qJUD5SEtrcg,3386
47
- shepherd_core/data_models/testbed/observer_fixture.yaml,sha256=jqAatTebWrShXBlhqkCUQIrtVqEjl7RVDR9mosS2LJQ,4807
47
+ shepherd_core/data_models/testbed/observer_fixture.yaml,sha256=bilVGLxo8iWkm_vwDWpIUG3DZgQ8fSZqHDdHgsN1afg,5158
48
48
  shepherd_core/data_models/testbed/target.py,sha256=W7U9nCz_xWMRCCgaGuqOV4dGi4ZeQ1SAp7W2FqCkanU,1978
49
49
  shepherd_core/data_models/testbed/target_fixture.old1,sha256=ivH9uTgC2Z4L_J4KDHAyIHZnB7iy9EUu1yM3M0s1lQQ,3675
50
- shepherd_core/data_models/testbed/target_fixture.yaml,sha256=LyOJa7yH17tHIGC25jlkLJ_DKnbFSoGhD-6Uh6q_HqQ,4132
50
+ shepherd_core/data_models/testbed/target_fixture.yaml,sha256=g6BWyV38b8b2UaM98EOytZIFPPrBOUXcrOXrcCj4XWI,4364
51
51
  shepherd_core/data_models/testbed/testbed.py,sha256=K6llqMTn-AU18XXRXdhzibIx-n4CQjtrXnLwqxOQesI,3828
52
- shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=LaaU8mXLQboYWYNPpA3CWmMPy2w6T6cve6gLgDaA3l0,717
52
+ shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=ca5LI-fWoc3I9m2QScVAh84Bv-ftkSGAizR3ZR0lkC8,980
53
53
  shepherd_core/decoder_waveform/__init__.py,sha256=-ohGz0fA2tKxUJk4FAQXKtI93d6YGdy0CrkdhOod1QU,120
54
54
  shepherd_core/decoder_waveform/uart.py,sha256=vuw9fKwZb_1mMtQ5fdiZN8Cr1YWSgYKar9FIMK8Bogo,11084
55
55
  shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5ReVAA,2090
@@ -65,8 +65,8 @@ shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1
65
65
  shepherd_core/testbed_client/cache_path.py,sha256=tS0er9on5fw8wddMCt1jkc2uyYOdSTvX_UmfmYJf6tY,445
66
66
  shepherd_core/testbed_client/client_abc_fix.py,sha256=xMch4y6vMMLRiqFEry2zSnYuLM6Ay-fY0dunXMz9Hws,4250
67
67
  shepherd_core/testbed_client/client_web.py,sha256=zqY4MGMWfTl2_0T1qrQl5Vz9SPjl-wMj8O5yMMQyo9I,6044
68
- shepherd_core/testbed_client/fixtures.py,sha256=ftPJwiOn-USroIee2Al3evR3WxsA1SxERusoZ-jbkVk,9223
69
- shepherd_core/testbed_client/user_model.py,sha256=f4WZ8IvSCt3s1RG_Bhi43ojiJQsZYyolJ3Ft8HNRYas,2175
68
+ shepherd_core/testbed_client/fixtures.py,sha256=gKTRt_44u42-1OMuONho34WqRvoelDbChAGA5rdbbXQ,9397
69
+ shepherd_core/testbed_client/user_model.py,sha256=F6ibcvqZOYp7Tw92FoWERuCMp9LFsTL-0ACWhSzZs2A,2077
70
70
  shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
71
71
  shepherd_core/vsource/target_model.py,sha256=BjOlwX_gIOJ91e4OOLB4_OsCpuhq9vm57ERjM-iBhAM,5129
72
72
  shepherd_core/vsource/virtual_converter_model.py,sha256=jUnJwP-FFDMtXm1NCLUJfZTvImYH4_A9rc_lXVAZ33I,11628
@@ -74,8 +74,8 @@ shepherd_core/vsource/virtual_harvester_model.py,sha256=8uPTMDth4KI5XarkX9EJtMRZ
74
74
  shepherd_core/vsource/virtual_harvester_simulation.py,sha256=eK9uCn-j_8xSTa7BQhG879ep0oZDeatlnF31LeWEDiM,2547
75
75
  shepherd_core/vsource/virtual_source_model.py,sha256=-8RwBrkIdO0g4zpo7XHnqv8F_qNh_qf5hxEUJoIuAmg,3164
76
76
  shepherd_core/vsource/virtual_source_simulation.py,sha256=6voU1TAB2P0xuVu1bnyKa-xW9rwYXUC6f06gYtUDHTs,5273
77
- shepherd_core-2025.5.2.dist-info/METADATA,sha256=nACqRWB12RTUhFeUPiKcnw7UvP2YEV4ZNg-kKRGkWSw,7756
78
- shepherd_core-2025.5.2.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
79
- shepherd_core-2025.5.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
- shepherd_core-2025.5.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
- shepherd_core-2025.5.2.dist-info/RECORD,,
77
+ shepherd_core-2025.5.3.dist-info/METADATA,sha256=X_NXYCMWCsu8RwQX9Vm40CMQbV4eUtsKyMl9P4yJsbA,7756
78
+ shepherd_core-2025.5.3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
79
+ shepherd_core-2025.5.3.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
+ shepherd_core-2025.5.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
+ shepherd_core-2025.5.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5