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