shepherd-core 2025.10.1__tar.gz → 2026.2.1__tar.gz
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-2026.2.1/LICENSE +21 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/PKG-INFO +5 -3
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/eenv_generator.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/experiment_generic_var1.py +6 -6
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/experiment_generic_var2.py +4 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/simulations/vharvester.py +7 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/simulations/vstorage.py +17 -15
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/vsource_debug_sim.py +5 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/pyproject.toml +3 -12
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/config.py +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/__init__.py +4 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/cal_measurement.py +7 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/calibration.py +23 -12
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/content.py +10 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/shepherd.py +13 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/wrapper.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/__init__.py +4 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/_external_fixtures.yaml +104 -96
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_bonito.yaml +436 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_multivariate_random_walk.yaml +164 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_on_off_markov.yaml +3280 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_on_off_windows.yaml +3260 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/_metadata_eenvs_synthetic_static.yaml +450 -0
- shepherd_core-2026.2.1/shepherd_core/data_models/content/energy_environment.py +370 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/energy_environment_fixture.yaml +21 -18
- shepherd_core-2026.2.1/shepherd_core/data_models/content/enum_datatypes.py +109 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/firmware.py +44 -16
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_harvester_config.py +10 -93
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_source_config.py +21 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_config.py +7 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_creator.py +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_param_experiments.py +4 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/experiment.py +38 -13
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/observer_features.py +17 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/target_config.py +55 -7
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/__init__.py +13 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/emulation.py +9 -5
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/firmware_mod.py +3 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/harvest.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/helper_paths.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/observer_tasks.py +8 -6
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/programming.py +4 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/task/testbed_tasks.py +8 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/cape.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/gpio.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/mcu.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/observer.py +2 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target.py +7 -5
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target_fixture.old1 +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/target_fixture.yaml +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/testbed.py +17 -15
- shepherd_core-2026.2.1/shepherd_core/exit_handler.py +22 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/converter.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/validation.py +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/__init__.py +23 -21
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/system.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/logger.py +0 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/reader.py +29 -25
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/cache_path.py +3 -3
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/client_abc_fix.py +14 -3
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/client_web.py +7 -5
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/fixtures.py +7 -7
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/version.py +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_converter_model.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_harvester_model.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_harvester_simulation.py +5 -5
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_source_model.py +1 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_source_simulation.py +9 -9
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_storage_models_kibam.py +3 -3
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/writer.py +16 -9
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/PKG-INFO +5 -3
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/SOURCES.txt +8 -1
- shepherd_core-2026.2.1/tests/data_models/conftest.py +40 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_base_models.py +3 -1
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_content_fixtures.py +2 -2
- shepherd_core-2026.2.1/tests/data_models/test_content_models.py +649 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_experiment_models.py +86 -6
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_task_generation.py +4 -4
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_task_models.py +2 -2
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/vsource/test_converter.py +8 -8
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/vsource/test_harvester.py +4 -4
- shepherd_core-2025.10.1/shepherd_core/data_models/content/energy_environment.py +0 -52
- shepherd_core-2025.10.1/shepherd_core/data_models/content/firmware_datatype.py +0 -15
- shepherd_core-2025.10.1/tests/data_models/conftest.py +0 -14
- shepherd_core-2025.10.1/tests/data_models/test_content_models.py +0 -283
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/README.md +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/experiment_from_yaml.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/experiment_models.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/firmware_model.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/firmware_modification.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/inventory.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/simulations/vsource.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/uart_decode_waveform.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/examples/uart_raw2.csv +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/setup.cfg +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/calibration_hw_def.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/commons.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/timezone.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_source_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_ideal.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_lead.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_lipo.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_mlcc.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_super.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/content/virtual_storage_fixture_tantal.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/experiment/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/readme.md +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/decoder_waveform/uart.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/converter_elf.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/fw_tools/patcher.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/python.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/inventory/target.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/testbed_client/user_model.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/target_model.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_storage_model.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_storage_model_fixed_point_math.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/vsource/virtual_storage_simulator.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/dependency_links.txt +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/requires.txt +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/top_level.txt +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core.egg-info/zip-safe +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/conftest.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_data.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_data_faulty.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_emulator.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_experiment.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_harvester.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_testbed.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/example_config_virtsource.yaml +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_examples.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_testbed_fixtures.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/data_models/test_testbed_models.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/decoder_waveform/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/decoder_waveform/test_decoder.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/build_msp.elf +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/build_nrf.elf +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/conftest.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_converter.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_patcher.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/fw_tools/test_validation.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/inventory/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/inventory/test_inventory.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/test_cal_hw.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/test_examples.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/test_logger.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/test_reader.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/test_writer.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/testbed_client/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/vsource/__init__.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/vsource/conftest.py +0 -0
- {shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/tests/vsource/test_z.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2025, Networked Embedded Systems Lab, TU Dresden, Ingmar Splitt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
|
-
Name:
|
|
3
|
-
Version:
|
|
2
|
+
Name: shepherd-core
|
|
3
|
+
Version: 2026.2.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
|
+
License-Expression: MIT
|
|
7
8
|
Project-URL: Documentation, https://github.com/nes-lab/shepherd-tools/blob/main/README.md
|
|
8
9
|
Project-URL: Issues, https://github.com/nes-lab/shepherd-tools/issues
|
|
9
10
|
Project-URL: Source, https://pypi.org/project/shepherd-core/
|
|
@@ -23,11 +24,11 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
26
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
27
27
|
Classifier: Operating System :: OS Independent
|
|
28
28
|
Classifier: Natural Language :: English
|
|
29
29
|
Requires-Python: >=3.10
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
31
32
|
Requires-Dist: h5py
|
|
32
33
|
Requires-Dist: numpy
|
|
33
34
|
Requires-Dist: pyYAML
|
|
@@ -50,6 +51,7 @@ Requires-Dist: pytest; extra == "test"
|
|
|
50
51
|
Requires-Dist: coverage; extra == "test"
|
|
51
52
|
Provides-Extra: all
|
|
52
53
|
Requires-Dist: shepherd-core[dev,elf,inventory,test]; extra == "all"
|
|
54
|
+
Dynamic: license-file
|
|
53
55
|
|
|
54
56
|
# Core Library
|
|
55
57
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Create a set of static
|
|
1
|
+
"""Create a set of static synthetic energy environments."""
|
|
2
2
|
|
|
3
3
|
from itertools import product
|
|
4
4
|
from pathlib import Path
|
|
@@ -29,7 +29,7 @@ for _v, _c in product(voltages_V, currents_A):
|
|
|
29
29
|
log.info("File exists, will skip: %s", file_path.name)
|
|
30
30
|
else:
|
|
31
31
|
with ShpWriter(file_path) as file:
|
|
32
|
-
file.store_hostname("
|
|
32
|
+
file.store_hostname("synthetic")
|
|
33
33
|
# values in SI units
|
|
34
34
|
timestamp_vector = np.arange(0.0, duration_s, file.sample_interval_ns / 1e9)
|
|
35
35
|
voltage_vector = np.linspace(_v, _v, int(file.samplerate_sps * duration_s))
|
|
@@ -30,20 +30,20 @@ do_connect = False
|
|
|
30
30
|
if do_connect:
|
|
31
31
|
WebClient()
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
exp = sm.Experiment(
|
|
34
34
|
name="meaningful_TestName",
|
|
35
35
|
# time_start could be "2033-03-13 14:15:16" or "datetime.now() + timedelta(minutes=30)"
|
|
36
|
-
duration=30,
|
|
36
|
+
duration=30, # ty: ignore[invalid-argument-type]
|
|
37
37
|
target_configs=[
|
|
38
38
|
sm.TargetConfig(
|
|
39
39
|
target_IDs=range(7, 12),
|
|
40
40
|
custom_IDs=range(1, 100), # note: longer list is OK
|
|
41
|
-
energy_env=sm.EnergyEnvironment(name="
|
|
41
|
+
energy_env=sm.EnergyEnvironment(name="synthetic_static_3000mV_50mA"),
|
|
42
42
|
firmware1=sm.Firmware(
|
|
43
43
|
name="FW_TestXYZ",
|
|
44
44
|
data=Path("/var/shepherd/content/fw/nes_lab/nrf52_demo_rf/build.elf"),
|
|
45
45
|
data_type=sm.FirmwareDType.path_elf,
|
|
46
|
-
|
|
46
|
+
data_2_copy=False,
|
|
47
47
|
mcu=MCU(name="nRF52"),
|
|
48
48
|
),
|
|
49
49
|
power_tracing=None,
|
|
@@ -52,10 +52,10 @@ xp = sm.Experiment(
|
|
|
52
52
|
),
|
|
53
53
|
],
|
|
54
54
|
)
|
|
55
|
-
|
|
55
|
+
exp.to_file("experiment_generic_var1.yaml")
|
|
56
56
|
|
|
57
57
|
# Create a tasks-list for the testbed
|
|
58
|
-
tb_tasks = TestbedTasks.from_xp(
|
|
58
|
+
tb_tasks = TestbedTasks.from_xp(exp)
|
|
59
59
|
tb_tasks.to_file("experiment_generic_var1_tbt.yaml")
|
|
60
60
|
|
|
61
61
|
# next steps:
|
|
@@ -28,7 +28,7 @@ do_connect = False
|
|
|
28
28
|
if do_connect:
|
|
29
29
|
WebClient()
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
exp = sm.Experiment(
|
|
32
32
|
name="meaningful_TestName",
|
|
33
33
|
# time_start could be "2033-03-13 14:15:16" or "datetime.now() + timedelta(minutes=30)"
|
|
34
34
|
duration=30,
|
|
@@ -36,7 +36,7 @@ xp = sm.Experiment(
|
|
|
36
36
|
sm.TargetConfig(
|
|
37
37
|
target_IDs=range(7, 12),
|
|
38
38
|
custom_IDs=range(1, 100), # note: longer list is OK
|
|
39
|
-
energy_env=sm.EnergyEnvironment(name="
|
|
39
|
+
energy_env=sm.EnergyEnvironment(name="synthetic_static_3000mV_50mA"),
|
|
40
40
|
firmware1=sm.Firmware.from_firmware(
|
|
41
41
|
file=Path("./firmware_nrf.elf").absolute(),
|
|
42
42
|
),
|
|
@@ -45,10 +45,10 @@ xp = sm.Experiment(
|
|
|
45
45
|
),
|
|
46
46
|
],
|
|
47
47
|
)
|
|
48
|
-
|
|
48
|
+
exp.to_file("experiment_generic_var2.yaml")
|
|
49
49
|
|
|
50
50
|
# Create a tasks-list for the testbed
|
|
51
|
-
tb_tasks = TestbedTasks.from_xp(
|
|
51
|
+
tb_tasks = TestbedTasks.from_xp(exp)
|
|
52
52
|
tb_tasks.to_file("experiment_generic_var2_tbt.yaml")
|
|
53
53
|
|
|
54
54
|
# next steps:
|
|
@@ -15,6 +15,8 @@ E_out = 17.811 mWs -> mppt_opt
|
|
|
15
15
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
18
20
|
from pathlib import Path
|
|
19
21
|
|
|
20
22
|
from shepherd_core.data_models import VirtualHarvesterConfig
|
|
@@ -23,8 +25,12 @@ from shepherd_core.vsource import simulate_harvester
|
|
|
23
25
|
from shepherd_core import Reader
|
|
24
26
|
from shepherd_data import ivonne
|
|
25
27
|
|
|
28
|
+
DURATION_MAX = 1 if "PYTEST_CURRENT_TEST" in os.environ else sys.float_info.max
|
|
29
|
+
# ⤷ limits runtime for pytest
|
|
30
|
+
|
|
26
31
|
# config simulation
|
|
27
|
-
sim_duration = 32
|
|
32
|
+
sim_duration = min(32, DURATION_MAX)
|
|
33
|
+
# ⤷ limits runtime for pytest
|
|
28
34
|
file_ivonne = Path(__file__).parents[3] / "shepherd_data/examples/jogging_10m.iv"
|
|
29
35
|
file_ivcurve = Path(__file__).parent / "jogging_ivcurve.h5"
|
|
30
36
|
|
|
@@ -30,7 +30,7 @@ from shepherd_core import log
|
|
|
30
30
|
|
|
31
31
|
path_here = Path(__file__).parent
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
DURATION_MAX = 1 if "PYTEST_CURRENT_TEST" in os.environ else sys.float_info.max
|
|
34
34
|
# ⤷ limits runtime for pytest
|
|
35
35
|
|
|
36
36
|
|
|
@@ -98,7 +98,7 @@ def experiment_current_ramp_pos(config: VirtualStorageConfig) -> None:
|
|
|
98
98
|
"""Charge virtual storage with a positive current ramp (increasing power)."""
|
|
99
99
|
dt_s = 0.1
|
|
100
100
|
SoC_start = 0.5
|
|
101
|
-
duration_s = min(200,
|
|
101
|
+
duration_s = min(200, DURATION_MAX)
|
|
102
102
|
sim = StorageSimulator(
|
|
103
103
|
models=get_models(SoC_start, config, dt_s),
|
|
104
104
|
dt_s=dt_s,
|
|
@@ -108,14 +108,14 @@ def experiment_current_ramp_pos(config: VirtualStorageConfig) -> None:
|
|
|
108
108
|
return 0.1 + 0.15 * t_s / duration_s # pru-model can handle +- 268 mA
|
|
109
109
|
|
|
110
110
|
sim.run(fn=current_trace, duration_s=duration_s)
|
|
111
|
-
sim.plot(path_here, f"
|
|
111
|
+
sim.plot(path_here, f"Experiment {config.name}, current charge ramp (positive)")
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
def experiment_current_ramp_neg(config: VirtualStorageConfig) -> None:
|
|
115
115
|
"""Discharge virtual storage with a negative current ramp (increasing power)."""
|
|
116
116
|
dt_s = 0.1
|
|
117
117
|
SoC_start = 0.5
|
|
118
|
-
duration_s = min(200,
|
|
118
|
+
duration_s = min(200, DURATION_MAX)
|
|
119
119
|
sim = StorageSimulator(
|
|
120
120
|
models=get_models(SoC_start, config, dt_s),
|
|
121
121
|
dt_s=dt_s,
|
|
@@ -125,7 +125,7 @@ def experiment_current_ramp_neg(config: VirtualStorageConfig) -> None:
|
|
|
125
125
|
return -(0.1 + 0.14 * t_s / duration_s) # pru-model can handle +- 268 mA
|
|
126
126
|
|
|
127
127
|
sim.run(fn=current_trace, duration_s=duration_s)
|
|
128
|
-
sim.plot(path_here, f"
|
|
128
|
+
sim.plot(path_here, f"Experiment {config.name}, current discharge ramp (negative)")
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
def experiment_pulsed_discharge(config: VirtualStorageConfig) -> None:
|
|
@@ -140,8 +140,8 @@ def experiment_pulsed_discharge(config: VirtualStorageConfig) -> None:
|
|
|
140
140
|
models=get_models(SoC_start, config, dt_s),
|
|
141
141
|
dt_s=dt_s,
|
|
142
142
|
)
|
|
143
|
-
sim.run(fn=i_pulse.step, duration_s=min(1_000,
|
|
144
|
-
sim.plot(path_here, f"
|
|
143
|
+
sim.run(fn=i_pulse.step, duration_s=min(1_000, DURATION_MAX))
|
|
144
|
+
sim.plot(path_here, f"Experiment {config.name}, pulsed discharge .1A, 1000 s (figure_9a)")
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
def experiment_pulsed_charge(config: VirtualStorageConfig) -> None:
|
|
@@ -156,8 +156,8 @@ def experiment_pulsed_charge(config: VirtualStorageConfig) -> None:
|
|
|
156
156
|
models=get_models(SoC_start, config, dt_s),
|
|
157
157
|
dt_s=dt_s,
|
|
158
158
|
)
|
|
159
|
-
sim.run(fn=i_pulse.step, duration_s=min(1_000,
|
|
160
|
-
sim.plot(path_here, f"
|
|
159
|
+
sim.run(fn=i_pulse.step, duration_s=min(1_000, DURATION_MAX))
|
|
160
|
+
sim.plot(path_here, f"Experiment {config.name}, pulsed charge .1A, 1000 s (figure_9b)")
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
def experiment_pulsed_resistive_charge(config: VirtualStorageConfig) -> None:
|
|
@@ -169,8 +169,10 @@ def experiment_pulsed_resistive_charge(config: VirtualStorageConfig) -> None:
|
|
|
169
169
|
models=get_models(SoC_start, config, dt_s),
|
|
170
170
|
dt_s=dt_s,
|
|
171
171
|
)
|
|
172
|
-
sim.run(fn=i_pulse.step, duration_s=min(3_000,
|
|
173
|
-
sim.plot(
|
|
172
|
+
sim.run(fn=i_pulse.step, duration_s=min(3_000, DURATION_MAX))
|
|
173
|
+
sim.plot(
|
|
174
|
+
path_here, f"Experiment {config.name}, pulsed resistive charge 20 Ohm to 4.2 V, 3000 s"
|
|
175
|
+
)
|
|
174
176
|
|
|
175
177
|
|
|
176
178
|
def experiment_resistive_load(config: VirtualStorageConfig) -> None:
|
|
@@ -185,8 +187,8 @@ def experiment_resistive_load(config: VirtualStorageConfig) -> None:
|
|
|
185
187
|
models=get_models(SoC_start, config, dt_s),
|
|
186
188
|
dt_s=dt_s,
|
|
187
189
|
)
|
|
188
|
-
sim.run(fn=i_charge, duration_s=min(1_000,
|
|
189
|
-
sim.plot(path_here, f"
|
|
190
|
+
sim.run(fn=i_charge, duration_s=min(1_000, DURATION_MAX))
|
|
191
|
+
sim.plot(path_here, f"Experiment {config.name}, resistive load 20 Ohm from 4.2 V, 1000 s")
|
|
190
192
|
|
|
191
193
|
|
|
192
194
|
def experiment_self_discharge() -> None:
|
|
@@ -207,10 +209,10 @@ def experiment_self_discharge() -> None:
|
|
|
207
209
|
def step(_t: float, _s: float, _v: float) -> float:
|
|
208
210
|
return 0
|
|
209
211
|
|
|
210
|
-
sim.run(fn=step, duration_s=min(duration.total_seconds(),
|
|
212
|
+
sim.run(fn=step, duration_s=min(duration.total_seconds(), DURATION_MAX))
|
|
211
213
|
sim.plot(
|
|
212
214
|
path_here,
|
|
213
|
-
f"
|
|
215
|
+
f"Experiment {config.name}, self-discharge, "
|
|
214
216
|
f"SoC {SoC_start} to {SoC_target} in {duration.total_seconds()} s",
|
|
215
217
|
)
|
|
216
218
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import os
|
|
13
14
|
from itertools import product
|
|
14
15
|
|
|
15
16
|
import matplotlib.pyplot as plt
|
|
@@ -38,6 +39,10 @@ src_list = [
|
|
|
38
39
|
I_mcu_sleep_A = 200e-9
|
|
39
40
|
I_mcu_active_A = 1e-3
|
|
40
41
|
|
|
42
|
+
# limit runtime for pytest
|
|
43
|
+
if "PYTEST_CURRENT_TEST" in os.environ:
|
|
44
|
+
sample_dur_list = [10_000]
|
|
45
|
+
|
|
41
46
|
# For online-queries the lib can be connected to the testbed-server.
|
|
42
47
|
# NOTE: there are 3 states:
|
|
43
48
|
# - unconnected -> demo-fixtures are queried (locally)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
|
-
name = "
|
|
2
|
+
name = "shepherd-core"
|
|
3
3
|
description = "Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed"
|
|
4
4
|
keywords = ["testbed", "beaglebone", "pru", "batteryless", "energyharvesting", "solar"]
|
|
5
5
|
|
|
6
6
|
authors = [{name = "Ingmar Splitt", email = "ingmar.splitt@tu-dresden.de"},]
|
|
7
7
|
maintainers = [{name = "Ingmar Splitt", email = "ingmar.splitt@tu-dresden.de"},]
|
|
8
8
|
|
|
9
|
-
license =
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"] # can't be in parent dir
|
|
10
11
|
dynamic = ["version"]
|
|
11
12
|
|
|
12
13
|
classifiers = [
|
|
@@ -19,7 +20,6 @@ classifiers = [
|
|
|
19
20
|
"Programming Language :: Python :: 3.12",
|
|
20
21
|
"Programming Language :: Python :: 3.13",
|
|
21
22
|
"Programming Language :: Python :: 3.14",
|
|
22
|
-
"License :: OSI Approved :: MIT License",
|
|
23
23
|
"Operating System :: OS Independent",
|
|
24
24
|
"Natural Language :: English",
|
|
25
25
|
]
|
|
@@ -104,12 +104,3 @@ addopts = "-vvv --stepwise" # opts: verbose result for each tests
|
|
|
104
104
|
[tool.coverage.run]
|
|
105
105
|
source = ["shepherd_core"]
|
|
106
106
|
omit = ["*/shepherd_data/*"]
|
|
107
|
-
|
|
108
|
-
[tool.mypy]
|
|
109
|
-
python_version = 3.10
|
|
110
|
-
ignore_missing_imports = true
|
|
111
|
-
disable_error_code = ["call-arg", ]
|
|
112
|
-
exclude = [
|
|
113
|
-
"build/",
|
|
114
|
-
".egg-info/",
|
|
115
|
-
]
|
|
@@ -27,7 +27,7 @@ class ConfigDefault(BaseModel):
|
|
|
27
27
|
UID_SIZE: int = 2
|
|
28
28
|
"""Variable size in Byte"""
|
|
29
29
|
|
|
30
|
-
TESTBED_SERVER: HttpUrl = "https://shepherd.cfaed.tu-dresden.de:8000/"
|
|
30
|
+
TESTBED_SERVER: HttpUrl = HttpUrl("https://shepherd.cfaed.tu-dresden.de:8000/")
|
|
31
31
|
"""Server that holds up to date testbed fixtures"""
|
|
32
32
|
|
|
33
33
|
|
|
@@ -18,10 +18,11 @@ from .base.calibration import CapeData
|
|
|
18
18
|
from .base.content import ContentModel
|
|
19
19
|
from .base.shepherd import ShpModel
|
|
20
20
|
from .base.wrapper import Wrapper
|
|
21
|
-
from .content.energy_environment import EnergyDType
|
|
22
21
|
from .content.energy_environment import EnergyEnvironment
|
|
22
|
+
from .content.energy_environment import EnergyProfile
|
|
23
|
+
from .content.enum_datatypes import EnergyDType
|
|
24
|
+
from .content.enum_datatypes import FirmwareDType
|
|
23
25
|
from .content.firmware import Firmware
|
|
24
|
-
from .content.firmware import FirmwareDType
|
|
25
26
|
from .content.virtual_harvester_config import VirtualHarvesterConfig
|
|
26
27
|
from .content.virtual_source_config import VirtualSourceConfig
|
|
27
28
|
from .content.virtual_storage_config import VirtualStorageConfig
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"ContentModel",
|
|
46
47
|
"EnergyDType",
|
|
47
48
|
"EnergyEnvironment",
|
|
49
|
+
"EnergyProfile",
|
|
48
50
|
"Experiment",
|
|
49
51
|
"Firmware",
|
|
50
52
|
"FirmwareDType",
|
{shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/cal_measurement.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Models for the process of calibration a device by measurements."""
|
|
2
2
|
|
|
3
3
|
from typing import Annotated
|
|
4
|
+
from typing import final
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
6
7
|
from pydantic import Field
|
|
@@ -15,6 +16,7 @@ from .calibration import CapeData
|
|
|
15
16
|
from .shepherd import ShpModel
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
@final
|
|
18
20
|
class CalMeasurementPair(ShpModel):
|
|
19
21
|
"""Value-container for a calibration-measurement."""
|
|
20
22
|
|
|
@@ -39,8 +41,8 @@ def meas_to_cal(data: CalMeasPairs, component: str) -> CalibrationPair:
|
|
|
39
41
|
gain: float = float(model[0])
|
|
40
42
|
|
|
41
43
|
# r-squared, Pearson correlation coefficient
|
|
42
|
-
|
|
43
|
-
yhat =
|
|
44
|
+
pcoef = np.poly1d(model)
|
|
45
|
+
yhat = pcoef(x)
|
|
44
46
|
ybar: float = np.sum(y) / len(y)
|
|
45
47
|
ssreg: float = np.sum((yhat - ybar) ** 2)
|
|
46
48
|
sstot: float = np.sum((y - ybar) ** 2)
|
|
@@ -55,6 +57,7 @@ def meas_to_cal(data: CalMeasPairs, component: str) -> CalibrationPair:
|
|
|
55
57
|
return CalibrationPair(offset=offset, gain=gain)
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
@final
|
|
58
61
|
class CalMeasurementHarvester(ShpModel):
|
|
59
62
|
"""Container for the values of the calibration-measurement."""
|
|
60
63
|
|
|
@@ -71,6 +74,7 @@ class CalMeasurementHarvester(ShpModel):
|
|
|
71
74
|
return CalibrationHarvester(**dcal)
|
|
72
75
|
|
|
73
76
|
|
|
77
|
+
@final
|
|
74
78
|
class CalMeasurementEmulator(ShpModel):
|
|
75
79
|
"""Container for the values of the calibration-measurement."""
|
|
76
80
|
|
|
@@ -87,6 +91,7 @@ class CalMeasurementEmulator(ShpModel):
|
|
|
87
91
|
return CalibrationEmulator(**dcal)
|
|
88
92
|
|
|
89
93
|
|
|
94
|
+
@final
|
|
90
95
|
class CalMeasurementCape(ShpModel):
|
|
91
96
|
"""Container for the values of the calibration-measurement."""
|
|
92
97
|
|
{shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/calibration.py
RENAMED
|
@@ -5,14 +5,16 @@ from collections.abc import Callable
|
|
|
5
5
|
from collections.abc import Generator
|
|
6
6
|
from collections.abc import Mapping
|
|
7
7
|
from collections.abc import Sequence
|
|
8
|
+
from typing import Annotated
|
|
8
9
|
from typing import TypeVar
|
|
10
|
+
from typing import final
|
|
9
11
|
|
|
10
12
|
import numpy as np
|
|
11
13
|
from numpy.typing import NDArray
|
|
12
14
|
from pydantic import Field
|
|
13
15
|
from pydantic import PositiveFloat
|
|
16
|
+
from pydantic import StringConstraints
|
|
14
17
|
from pydantic import conbytes
|
|
15
|
-
from pydantic import constr
|
|
16
18
|
from pydantic import validate_call
|
|
17
19
|
from typing_extensions import Self
|
|
18
20
|
|
|
@@ -47,6 +49,7 @@ def dict_generator(
|
|
|
47
49
|
yield [*pre, in_dict]
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
@final
|
|
50
53
|
class CalibrationPair(ShpModel):
|
|
51
54
|
"""SI-value [SI-Unit] = raw-value * gain + offset."""
|
|
52
55
|
|
|
@@ -56,11 +59,11 @@ class CalibrationPair(ShpModel):
|
|
|
56
59
|
|
|
57
60
|
def raw_to_si(self, values_raw: Calc_t, *, allow_negative: bool = True) -> Calc_t:
|
|
58
61
|
"""Convert between physical units and raw unsigned integers."""
|
|
59
|
-
values_si =
|
|
62
|
+
values_si = self.gain * values_raw + self.offset
|
|
60
63
|
if not allow_negative:
|
|
61
64
|
if isinstance(values_si, np.ndarray):
|
|
62
65
|
values_si[values_si < 0.0] = 0.0
|
|
63
|
-
# if
|
|
66
|
+
# if type-checker still complains, cast with .astype(float)
|
|
64
67
|
else:
|
|
65
68
|
values_si = float(max(values_si, 0.0))
|
|
66
69
|
elif not isinstance(values_si, np.ndarray):
|
|
@@ -99,6 +102,7 @@ cal_pair_adc_V = CalibrationPair.from_fn(adc_voltage_to_raw, unit="V")
|
|
|
99
102
|
cal_pair_adc_C = CalibrationPair.from_fn(adc_current_to_raw, unit="A")
|
|
100
103
|
|
|
101
104
|
|
|
105
|
+
@final
|
|
102
106
|
class CalibrationHarvester(ShpModel):
|
|
103
107
|
"""Container for all calibration-pairs for that device."""
|
|
104
108
|
|
|
@@ -140,6 +144,7 @@ cal_emu_legacy = { # legacy translator
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
|
|
147
|
+
@final
|
|
143
148
|
class CalibrationEmulator(ShpModel):
|
|
144
149
|
"""Container for all calibration-pairs for that device.
|
|
145
150
|
|
|
@@ -177,6 +182,7 @@ class CalibrationEmulator(ShpModel):
|
|
|
177
182
|
return cal_set
|
|
178
183
|
|
|
179
184
|
|
|
185
|
+
@final
|
|
180
186
|
class CapeData(ShpModel):
|
|
181
187
|
"""Representation of Beaglebone Cape information.
|
|
182
188
|
|
|
@@ -190,15 +196,17 @@ class CapeData(ShpModel):
|
|
|
190
196
|
"""
|
|
191
197
|
|
|
192
198
|
header: conbytes(max_length=4) = b"\xaa\x55\x33\xee"
|
|
193
|
-
eeprom_revision:
|
|
194
|
-
board_name:
|
|
195
|
-
version:
|
|
196
|
-
manufacturer:
|
|
197
|
-
part_number:
|
|
199
|
+
eeprom_revision: Annotated[str, StringConstraints(max_length=2)] = "A2"
|
|
200
|
+
board_name: Annotated[str, StringConstraints(max_length=32)] = "BeagleBone SHEPHERD2 Cape"
|
|
201
|
+
version: Annotated[str, StringConstraints(max_length=4)] = "24B0"
|
|
202
|
+
manufacturer: Annotated[str, StringConstraints(max_length=16)] = "NES TU DRESDEN"
|
|
203
|
+
part_number: Annotated[str, StringConstraints(max_length=16)] = "BB-SHPRD"
|
|
198
204
|
|
|
199
|
-
serial_number:
|
|
205
|
+
serial_number: Annotated[str, StringConstraints(max_length=12)]
|
|
200
206
|
|
|
201
|
-
cal_date:
|
|
207
|
+
cal_date: Annotated[str, StringConstraints(max_length=12)] = Field(
|
|
208
|
+
default_factory=local_iso_date
|
|
209
|
+
)
|
|
202
210
|
# ⤷ produces something like '2023-01-01'
|
|
203
211
|
|
|
204
212
|
def __repr__(self) -> str: # TODO: override useful?
|
|
@@ -206,6 +214,7 @@ class CapeData(ShpModel):
|
|
|
206
214
|
return str(self.model_dump())
|
|
207
215
|
|
|
208
216
|
|
|
217
|
+
@final
|
|
209
218
|
class CalibrationCape(ShpModel):
|
|
210
219
|
"""Represents calibration data of shepherd cape.
|
|
211
220
|
|
|
@@ -231,6 +240,7 @@ class CalibrationCape(ShpModel):
|
|
|
231
240
|
----
|
|
232
241
|
data: Byte string containing calibration data.
|
|
233
242
|
cape: data can be supplied
|
|
243
|
+
|
|
234
244
|
Returns:
|
|
235
245
|
CalibrationCape object with extracted calibration data.
|
|
236
246
|
|
|
@@ -240,9 +250,9 @@ class CalibrationCape(ShpModel):
|
|
|
240
250
|
lw2 = [elem for elem in lw1 if isinstance(elem[-1], float)]
|
|
241
251
|
values = struct.unpack(">" + len(lw2) * "d", data)
|
|
242
252
|
# ⤷ X => double float, big endian
|
|
243
|
-
for
|
|
253
|
+
for i_, walk in enumerate(lw2):
|
|
244
254
|
# hardcoded fixed depth ... bad but easy
|
|
245
|
-
dv[walk[0]][walk[1]][walk[2]] = float(values[
|
|
255
|
+
dv[walk[0]][walk[1]][walk[2]] = float(values[i_])
|
|
246
256
|
dv["cape"] = cape
|
|
247
257
|
return cls(**dv)
|
|
248
258
|
|
|
@@ -260,6 +270,7 @@ class CalibrationCape(ShpModel):
|
|
|
260
270
|
return struct.pack(">" + len(values) * "d", *values)
|
|
261
271
|
|
|
262
272
|
|
|
273
|
+
@final
|
|
263
274
|
class CalibrationSeries(ShpModel):
|
|
264
275
|
"""Cal-Data for a typical recording of a testbed experiment."""
|
|
265
276
|
|
{shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/content.py
RENAMED
|
@@ -9,12 +9,15 @@ from pydantic import StringConstraints
|
|
|
9
9
|
from pydantic import model_validator
|
|
10
10
|
from typing_extensions import Self
|
|
11
11
|
|
|
12
|
+
from shepherd_core.logger import log
|
|
13
|
+
from shepherd_core.version import version as core_ver
|
|
14
|
+
|
|
12
15
|
from .shepherd import ShpModel
|
|
13
16
|
|
|
14
17
|
# constr -> to_lower=True, max_length=16, regex=r"^[\w]+$"
|
|
15
18
|
# ⤷ Regex = AlphaNum
|
|
16
19
|
IdInt = Annotated[int, Field(ge=0, lt=2**128)]
|
|
17
|
-
NameStr = Annotated[str, StringConstraints(max_length=
|
|
20
|
+
NameStr = Annotated[str, StringConstraints(max_length=50, pattern=r"^[^<>:;,?\"\*|\/\\]+$")]
|
|
18
21
|
# ⤷ Regex = FileSystem-Compatible ASCII
|
|
19
22
|
SafeStr = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
|
|
20
23
|
# ⤷ Regex = All Printable ASCII-Characters with Space
|
|
@@ -41,6 +44,8 @@ class ContentModel(ShpModel):
|
|
|
41
44
|
comment: SafeStr | None = None
|
|
42
45
|
created: datetime = Field(default_factory=datetime.now)
|
|
43
46
|
updated_last: datetime = Field(default_factory=datetime.now)
|
|
47
|
+
deprecated: str | None = None
|
|
48
|
+
""" ⤷ deprecation-comments provoke a warning during validation of model."""
|
|
44
49
|
# TODO: add dedicated 'inherit_from' field?
|
|
45
50
|
|
|
46
51
|
# Ownership & Access
|
|
@@ -50,7 +55,8 @@ class ContentModel(ShpModel):
|
|
|
50
55
|
visible2group: bool = False
|
|
51
56
|
visible2all: bool = False
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
sw_ver: Annotated[str, Field(default=core_ver)]
|
|
59
|
+
""" ⤷ store core-version with content for compatibility management."""
|
|
54
60
|
|
|
55
61
|
def __str__(self) -> str:
|
|
56
62
|
return self.name
|
|
@@ -62,4 +68,6 @@ class ContentModel(ShpModel):
|
|
|
62
68
|
raise ValueError(
|
|
63
69
|
"Public instances require a description (check visible2*- and description-field)"
|
|
64
70
|
)
|
|
71
|
+
if isinstance(self.deprecated, str) and len(self.deprecated) > 0:
|
|
72
|
+
log.warning(f"Content {self.name} is deprecated: {self.deprecated}")
|
|
65
73
|
return self
|
{shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/shepherd.py
RENAMED
|
@@ -8,6 +8,7 @@ from datetime import timedelta
|
|
|
8
8
|
from ipaddress import IPv4Address
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any
|
|
11
|
+
from typing import final
|
|
11
12
|
from uuid import UUID
|
|
12
13
|
|
|
13
14
|
import yaml
|
|
@@ -38,6 +39,7 @@ def generic2str(dumper: SafeDumper, data: Any) -> Node:
|
|
|
38
39
|
return dumper.represent_scalar("tag:yaml.org,2002:str", str(data))
|
|
39
40
|
|
|
40
41
|
|
|
42
|
+
# TODO: put in helper-file (similar to models/task/helper_paths.py) and make it callable
|
|
41
43
|
yaml.add_representer(pathlib.PosixPath, path2str, SafeDumper)
|
|
42
44
|
yaml.add_representer(pathlib.WindowsPath, path2str, SafeDumper)
|
|
43
45
|
yaml.add_representer(pathlib.Path, path2str, SafeDumper)
|
|
@@ -108,12 +110,15 @@ class ShpModel(BaseModel):
|
|
|
108
110
|
)
|
|
109
111
|
return str(content)
|
|
110
112
|
|
|
111
|
-
def __getitem__(self, key:
|
|
113
|
+
def __getitem__(self, key: Any) -> Any:
|
|
112
114
|
"""Allow dict access like model["key"].
|
|
113
115
|
|
|
114
116
|
in addition to model.key.
|
|
115
117
|
"""
|
|
116
|
-
|
|
118
|
+
if isinstance(key, str):
|
|
119
|
+
return self.__getattribute__(key)
|
|
120
|
+
msg = f"Unknown key '{key}' used when selecting from Model."
|
|
121
|
+
raise IndexError(msg)
|
|
117
122
|
|
|
118
123
|
def __contains__(self, item: str) -> bool:
|
|
119
124
|
"""Allow checks like 'x in YClass'."""
|
|
@@ -127,8 +132,9 @@ class ShpModel(BaseModel):
|
|
|
127
132
|
def items(self) -> Generator[tuple, None, None]:
|
|
128
133
|
"""Fn of dict."""
|
|
129
134
|
for key in self.keys():
|
|
130
|
-
yield key, self
|
|
135
|
+
yield key, self.__getattribute__(key)
|
|
131
136
|
|
|
137
|
+
@final
|
|
132
138
|
@classmethod
|
|
133
139
|
def schema_to_file(cls, path: str | Path) -> None:
|
|
134
140
|
"""Store schema to yaml (for frontend-generators)."""
|
|
@@ -137,6 +143,7 @@ class ShpModel(BaseModel):
|
|
|
137
143
|
with Path(path).resolve().with_suffix(".yaml").open("w") as f:
|
|
138
144
|
f.write(model_yaml)
|
|
139
145
|
|
|
146
|
+
@final
|
|
140
147
|
def to_file(
|
|
141
148
|
self,
|
|
142
149
|
path: str | Path,
|
|
@@ -178,6 +185,7 @@ class ShpModel(BaseModel):
|
|
|
178
185
|
f.write(model_serial)
|
|
179
186
|
return model_path
|
|
180
187
|
|
|
188
|
+
@final
|
|
181
189
|
@classmethod
|
|
182
190
|
def from_file(cls, path: str | Path) -> Self:
|
|
183
191
|
"""Load from YAML or pickle file."""
|
|
@@ -188,12 +196,13 @@ class ShpModel(BaseModel):
|
|
|
188
196
|
with Path(path).open("rb") as shp_file:
|
|
189
197
|
shp_dict = pickle.load(shp_file) # noqa: S301
|
|
190
198
|
else:
|
|
191
|
-
with Path(path).open() as shp_file:
|
|
199
|
+
with Path(path).open(encoding="utf-8-sig") as shp_file:
|
|
192
200
|
shp_dict = yaml.safe_load(shp_file)
|
|
193
201
|
shp_wrap = Wrapper(**shp_dict)
|
|
194
202
|
if shp_wrap.datatype != cls.__name__:
|
|
195
203
|
raise ValueError("Model in file does not match the actual Class")
|
|
196
204
|
return cls(**shp_wrap.parameters)
|
|
197
205
|
|
|
206
|
+
@final
|
|
198
207
|
def get_hash(self) -> str:
|
|
199
208
|
return hashlib.sha3_224(str(self.model_dump()).encode("utf-8")).hexdigest()
|
{shepherd_core-2025.10.1 → shepherd_core-2026.2.1}/shepherd_core/data_models/base/wrapper.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import Annotated
|
|
5
|
+
from typing import final
|
|
5
6
|
|
|
6
7
|
from pydantic import BaseModel
|
|
7
8
|
from pydantic import StringConstraints
|
|
@@ -12,6 +13,7 @@ SafeStrClone = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
|
|
|
12
13
|
# ⤷ copy avoids circular import
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
@final
|
|
15
17
|
class Wrapper(BaseModel):
|
|
16
18
|
"""Generalized web- & file-interface for all models with dynamic typecasting."""
|
|
17
19
|
|