shepherd-core 2023.8.6__tar.gz → 2023.8.8__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.
Files changed (121) hide show
  1. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/PKG-INFO +8 -1
  2. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/README.md +7 -0
  3. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/setup.cfg +1 -1
  4. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/__init__.py +1 -1
  5. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/__init__.py +3 -1
  6. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/cal_measurement.py +17 -14
  7. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/calibration.py +41 -8
  8. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/content.py +17 -13
  9. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/shepherd.py +29 -22
  10. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/wrapper.py +5 -4
  11. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/energy_environment.py +3 -2
  12. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware.py +10 -6
  13. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_harvester.py +42 -39
  14. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_source.py +83 -72
  15. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/doc_virtual_source.py +7 -14
  16. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/experiment.py +20 -15
  17. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/observer_features.py +33 -31
  18. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/target_config.py +24 -18
  19. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/__init__.py +13 -5
  20. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/emulation.py +35 -23
  21. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/firmware_mod.py +14 -13
  22. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/harvest.py +28 -13
  23. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/observer_tasks.py +17 -7
  24. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/programming.py +13 -13
  25. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/task/testbed_tasks.py +16 -6
  26. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/cape.py +3 -2
  27. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/gpio.py +18 -15
  28. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/mcu.py +7 -6
  29. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/observer.py +23 -19
  30. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/target.py +15 -14
  31. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/testbed.py +14 -11
  32. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/converter.py +7 -7
  33. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/converter_elf.py +2 -2
  34. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/patcher.py +7 -6
  35. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/validation.py +3 -3
  36. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/__init__.py +16 -8
  37. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/python.py +4 -3
  38. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/system.py +5 -5
  39. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/inventory/target.py +4 -4
  40. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/reader.py +3 -3
  41. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/client.py +6 -4
  42. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/user_model.py +14 -10
  43. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/writer.py +2 -2
  44. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/PKG-INFO +8 -1
  45. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/requires.txt +1 -1
  46. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_data.yaml +2 -1
  47. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas.yaml +2 -1
  48. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_base_models.py +19 -2
  49. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/inventory/test_inventory.py +1 -1
  50. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/pyproject.toml +0 -0
  51. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/calibration_hw_def.py +0 -0
  52. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/commons.py +0 -0
  53. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/base/__init__.py +0 -0
  54. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/__init__.py +0 -0
  55. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/energy_environment_fixture.yaml +0 -0
  56. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware_datatype.py +0 -0
  57. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/firmware_fixture.yaml +0 -0
  58. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
  59. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/content/virtual_source_fixture.yaml +0 -0
  60. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/experiment/__init__.py +0 -0
  61. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/__init__.py +0 -0
  62. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
  63. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
  64. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
  65. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
  66. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/target_fixture.yaml +0 -0
  67. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
  68. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/decoder_waveform/__init__.py +0 -0
  69. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/decoder_waveform/uart.py +0 -0
  70. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/fw_tools/__init__.py +0 -0
  71. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/logger.py +0 -0
  72. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/__init__.py +0 -0
  73. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/testbed_client/fixtures.py +0 -0
  74. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/__init__.py +0 -0
  75. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_converter_model.py +0 -0
  76. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_harvester_model.py +0 -0
  77. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core/vsource/virtual_source_model.py +0 -0
  78. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/SOURCES.txt +0 -0
  79. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/dependency_links.txt +0 -0
  80. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/top_level.txt +0 -0
  81. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/shepherd_core.egg-info/zip-safe +0 -0
  82. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/__init__.py +0 -0
  83. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/conftest.py +0 -0
  84. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/__init__.py +0 -0
  85. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/conftest.py +0 -0
  86. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_data_faulty.yaml +0 -0
  87. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
  88. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
  89. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_emulator.yaml +0 -0
  90. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_experiment.yaml +0 -0
  91. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
  92. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_harvester.yaml +0 -0
  93. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_testbed.yaml +0 -0
  94. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/example_config_virtsource.yaml +0 -0
  95. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_content_fixtures.py +0 -0
  96. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_content_models.py +0 -0
  97. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_examples.py +0 -0
  98. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_experiment_models.py +0 -0
  99. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_task_generation.py +0 -0
  100. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_task_models.py +0 -0
  101. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_testbed_fixtures.py +0 -0
  102. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/data_models/test_testbed_models.py +0 -0
  103. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/decoder_waveform/__init__.py +0 -0
  104. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/decoder_waveform/test_decoder.py +0 -0
  105. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/__init__.py +0 -0
  106. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/conftest.py +0 -0
  107. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_converter.py +0 -0
  108. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_patcher.py +0 -0
  109. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/fw_tools/test_validation.py +0 -0
  110. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/inventory/__init__.py +0 -0
  111. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_cal_hw.py +0 -0
  112. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_examples.py +0 -0
  113. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_logger.py +0 -0
  114. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_reader.py +0 -0
  115. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/test_writer.py +0 -0
  116. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/testbed_client/__init__.py +0 -0
  117. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/__init__.py +0 -0
  118. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/conftest.py +0 -0
  119. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_converter.py +0 -0
  120. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_harvester.py +0 -0
  121. {shepherd_core-2023.8.6 → shepherd_core-2023.8.8}/tests/vsource/test_z.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shepherd_core
3
- Version: 2023.8.6
3
+ Version: 2023.8.8
4
4
  Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
5
5
  Home-page: https://pypi.org/project/shepherd-core/
6
6
  Author: Ingmar Splitt, Kai Geissdoerfer
@@ -77,6 +77,13 @@ The Library is available via PyPI and can be installed with
77
77
  pip install shepherd-data
78
78
  ```
79
79
 
80
+ for bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
81
+
82
+ ```Shell
83
+ pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_core -U
84
+ ```
85
+
86
+
80
87
  If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
81
88
 
82
89
  ```shell
@@ -40,6 +40,13 @@ The Library is available via PyPI and can be installed with
40
40
  pip install shepherd-data
41
41
  ```
42
42
 
43
+ for bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
44
+
45
+ ```Shell
46
+ pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_core -U
47
+ ```
48
+
49
+
43
50
  If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
44
51
 
45
52
  ```shell
@@ -41,7 +41,7 @@ install_requires =
41
41
  numpy
42
42
  pyYAML
43
43
  chromalog
44
- pydantic[email]<2.0.0
44
+ pydantic[email]>2.0.0
45
45
  tqdm
46
46
  scipy
47
47
  intelhex
@@ -21,7 +21,7 @@ from .testbed_client.client import TestbedClient
21
21
  from .testbed_client.client import tb_client
22
22
  from .writer import BaseWriter
23
23
 
24
- __version__ = "2023.8.6"
24
+ __version__ = "2023.8.8"
25
25
 
26
26
  __all__ = [
27
27
  "BaseReader",
@@ -3,6 +3,7 @@ from .base.calibration import CalibrationEmulator
3
3
  from .base.calibration import CalibrationHarvester
4
4
  from .base.calibration import CalibrationPair
5
5
  from .base.calibration import CalibrationSeries
6
+ from .base.calibration import CapeData
6
7
  from .base.content import ContentModel
7
8
  from .base.shepherd import ShpModel
8
9
  from .base.wrapper import Wrapper
@@ -23,6 +24,7 @@ from .experiment.target_config import TargetConfig
23
24
 
24
25
  __all__ = [
25
26
  # Core
27
+ "CapeData",
26
28
  "CalibrationCape",
27
29
  "CalibrationEmulator",
28
30
  "CalibrationHarvester",
@@ -46,4 +48,4 @@ __all__ = [
46
48
  "EnergyDType",
47
49
  "VirtualSourceConfig",
48
50
  "VirtualHarvesterConfig",
49
- ] # TODO: for pydantic 2 add @classmethod after @root_validator for cleaner type-checking
51
+ ]
@@ -1,14 +1,17 @@
1
+ from typing import List
1
2
  from typing import Optional
2
3
 
3
4
  import numpy as np
5
+ from pydantic import Field
4
6
  from pydantic import PositiveFloat
5
- from pydantic import conlist
6
- from pydantic import validate_arguments
7
+ from pydantic import validate_call
8
+ from typing_extensions import Annotated
7
9
 
8
10
  from .. import CalibrationCape
9
11
  from .. import CalibrationEmulator
10
12
  from .. import CalibrationHarvester
11
13
  from .. import CalibrationPair
14
+ from .calibration import CapeData
12
15
  from .shepherd import ShpModel
13
16
 
14
17
  # TODO: move to shepherd_data to remove scipy-dependency from _core
@@ -19,10 +22,10 @@ class CalMeasurementPair(ShpModel):
19
22
  reference_si: float = 0
20
23
 
21
24
 
22
- CalMeasPairs = conlist(item_type=CalMeasurementPair, min_items=2)
25
+ CalMeasPairs = Annotated[List[CalMeasurementPair], Field(min_length=2)]
23
26
 
24
27
 
25
- @validate_arguments
28
+ @validate_call
26
29
  def meas_to_cal(data: CalMeasPairs, component: str) -> CalibrationPair:
27
30
  from scipy import stats # here due to massive delay
28
31
 
@@ -51,8 +54,8 @@ class CalMeasurementHarvester(ShpModel):
51
54
  adc_C_Hrv: CalMeasPairs
52
55
 
53
56
  def to_cal(self) -> CalibrationHarvester:
54
- dv = self.dict()
55
- dcal = CalibrationHarvester().dict()
57
+ dv = self.model_dump()
58
+ dcal = CalibrationHarvester().model_dump()
56
59
  for key in dv.keys():
57
60
  dcal[key] = meas_to_cal(self[key], f"hrv_{key}")
58
61
  return CalibrationHarvester(**dcal)
@@ -65,23 +68,23 @@ class CalMeasurementEmulator(ShpModel):
65
68
  adc_C_B: CalMeasPairs
66
69
 
67
70
  def to_cal(self) -> CalibrationEmulator:
68
- dv = self.dict()
69
- dcal = CalibrationEmulator().dict()
71
+ dv = self.model_dump()
72
+ dcal = CalibrationEmulator().model_dump()
70
73
  for key in dv.keys():
71
74
  dcal[key] = meas_to_cal(self[key], f"emu_{key}")
72
75
  return CalibrationEmulator(**dcal)
73
76
 
74
77
 
75
78
  class CalMeasurementCape(ShpModel):
76
- harvester: Optional[CalMeasurementHarvester]
77
- emulator: Optional[CalMeasurementEmulator]
78
-
79
- cape: Optional[str] = None
79
+ cape: Optional[CapeData] = None
80
80
  host: Optional[str] = None
81
81
 
82
+ harvester: Optional[CalMeasurementHarvester] = None
83
+ emulator: Optional[CalMeasurementEmulator] = None
84
+
82
85
  def to_cal(self) -> CalibrationCape:
83
- dv = self.dict()
84
- dcal = CalibrationCape().dict()
86
+ dv = self.model_dump()
87
+ dcal = CalibrationCape().model_dump()
85
88
  # TODO: is it helpful to default wrong / missing values?
86
89
  for key, value in dv.items():
87
90
  if key in ["harvester", "emulator"]:
@@ -1,4 +1,5 @@
1
1
  import struct
2
+ from datetime import date
2
3
  from typing import Callable
3
4
  from typing import Generator
4
5
  from typing import Optional
@@ -7,8 +8,11 @@ from typing import Union
7
8
 
8
9
  import numpy as np
9
10
  from numpy.typing import NDArray
11
+ from pydantic import Field
10
12
  from pydantic import PositiveFloat
11
- from pydantic import validate_arguments
13
+ from pydantic import conbytes
14
+ from pydantic import constr
15
+ from pydantic import validate_call
12
16
 
13
17
  from ...calibration_hw_def import adc_current_to_raw
14
18
  from ...calibration_hw_def import adc_voltage_to_raw
@@ -146,6 +150,33 @@ class CalibrationEmulator(ShpModel):
146
150
  return cal_set
147
151
 
148
152
 
153
+ class CapeData(ShpModel):
154
+ """Representation of Beaglebone Cape information
155
+ -> just provide serial-number on creation
156
+
157
+ According to BeagleBone specifications, each cape should host an EEPROM
158
+ that contains some standardized information about the type of cape,
159
+ manufacturer, version etc.
160
+
161
+ `See<https://github.com/beagleboard/beaglebone-black/wiki/System-Reference-Manual#824_EEPROM_Data_Format>`_
162
+ """
163
+
164
+ header: conbytes(max_length=4) = b"\xAA\x55\x33\xEE"
165
+ eeprom_revision: constr(max_length=2) = "A2"
166
+ board_name: constr(max_length=32) = "BeagleBone SHEPHERD2 Cape"
167
+ version: constr(max_length=4) = "24B0"
168
+ manufacturer: constr(max_length=16) = "NES TU DRESDEN"
169
+ part_number: constr(max_length=16) = "BB-SHPRD"
170
+
171
+ serial_number: constr(max_length=12)
172
+
173
+ cal_date: constr(max_length=12) = Field(default_factory=date.today().isoformat)
174
+
175
+ def __repr__(self) -> str:
176
+ """string-representation allows print(model)"""
177
+ return str(self.model_dump())
178
+
179
+
149
180
  class CalibrationCape(ShpModel):
150
181
  """Represents calibration data of shepherd cape.
151
182
  Defines the format of calibration data and provides convenient functions
@@ -154,29 +185,31 @@ class CalibrationCape(ShpModel):
154
185
  YAML: .to_file() and .from_file() already in ShpModel
155
186
  """
156
187
 
188
+ cape: Optional[CapeData] = None
189
+ host: Optional[str] = None
190
+
157
191
  harvester: CalibrationHarvester = CalibrationHarvester()
158
192
  emulator: CalibrationEmulator = CalibrationEmulator()
159
193
 
160
- cape: Optional[str] = None
161
- host: Optional[str] = None
162
-
163
194
  @classmethod
164
- def from_bytestr(cls, data: bytes):
195
+ def from_bytestr(cls, data: bytes, cape: Optional[CapeData] = None):
165
196
  """Instantiates calibration data based on byte string.
166
197
  This is mainly used to deserialize data read from an EEPROM memory.
167
198
 
168
199
  Args:
169
200
  data: Byte string containing calibration data.
201
+ cape: data can be supplied
170
202
  Returns:
171
203
  CalibrationCape object with extracted calibration data.
172
204
  """
173
- dv = cls().dict(include={"harvester", "emulator"})
205
+ dv = cls().model_dump(include={"harvester", "emulator"})
174
206
  lw = list(dict_generator(dv))
175
207
  values = struct.unpack(">" + len(lw) * "d", data)
176
208
  # ⤷ X => double float, big endian
177
209
  for _i, walk in enumerate(lw):
178
210
  # hardcoded fixed depth ... bad but easy
179
211
  dv[walk[0]][walk[1]][walk[2]] = float(values[_i])
212
+ dv["cape"] = cape
180
213
  return cls(**dv)
181
214
 
182
215
  def to_bytestr(self) -> bytes:
@@ -186,7 +219,7 @@ class CalibrationCape(ShpModel):
186
219
  Returns:
187
220
  Byte string representation of calibration values.
188
221
  """
189
- lw = list(dict_generator(self.dict(include={"harvester", "emulator"})))
222
+ lw = list(dict_generator(self.model_dump(include={"harvester", "emulator"})))
190
223
  values = [walk[-1] for walk in lw]
191
224
  return struct.pack(">" + len(lw) * "d", *values)
192
225
 
@@ -200,7 +233,7 @@ class CalibrationSeries(ShpModel):
200
233
  # ⤷ default = nanoseconds
201
234
 
202
235
  @classmethod
203
- @validate_arguments
236
+ @validate_call
204
237
  def from_cal(
205
238
  cls,
206
239
  cal: Union[CalibrationHarvester, CalibrationEmulator],
@@ -3,18 +3,20 @@ from datetime import datetime
3
3
  from typing import Optional
4
4
 
5
5
  from pydantic import Field
6
- from pydantic import conint
7
- from pydantic import constr
8
- from pydantic import root_validator
6
+ from pydantic import StringConstraints
7
+ from pydantic import model_validator
8
+ from typing_extensions import Annotated
9
9
 
10
10
  from .shepherd import ShpModel
11
11
 
12
12
  # constr -> to_lower=True, max_length=16, regex=r"^[\w]+$"
13
13
  # ⤷ Regex = AlphaNum
14
- IdInt = conint(ge=0, lt=2**128)
15
- NameStr = constr(max_length=32, regex=r'^[^<>:;,?"*|\/\\]+$')
14
+ IdInt = Annotated[int, Field(ge=0, lt=2**128)]
15
+ NameStr = Annotated[
16
+ str, StringConstraints(max_length=32, pattern=r'^[^<>:;,?"*|\/\\]+$')
17
+ ]
16
18
  # ⤷ Regex = FileSystem-Compatible ASCII
17
- SafeStr = constr(regex=r"^[ -~]+$")
19
+ SafeStr = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
18
20
  # ⤷ Regex = All Printable ASCII-Characters with Space
19
21
 
20
22
 
@@ -31,25 +33,27 @@ class ContentModel(ShpModel):
31
33
  default_factory=id_default,
32
34
  )
33
35
  name: NameStr
34
- description: Optional[SafeStr] = Field(description="Required when public")
36
+ description: Annotated[
37
+ Optional[SafeStr], Field(description="Required when public")
38
+ ] = None
35
39
  comment: Optional[SafeStr] = None
36
40
  created: datetime = Field(default_factory=datetime.now)
37
41
 
38
42
  # Ownership & Access
39
43
  owner: NameStr
40
- group: NameStr = Field(description="University or Subgroup")
44
+ group: Annotated[NameStr, Field(description="University or Subgroup")]
41
45
  visible2group: bool = False
42
46
  visible2all: bool = False
43
47
 
44
48
  def __str__(self):
45
49
  return self.name
46
50
 
47
- @root_validator(pre=False)
48
- def content_validation(cls, values: dict) -> dict:
49
- is_visible = values.get("visible2group") or values.get("visible2all")
50
- if is_visible and values.get("description") is None:
51
+ @model_validator(mode="after")
52
+ def content_validation(self):
53
+ is_visible = self.visible2group or self.visible2all
54
+ if is_visible and self.description is None:
51
55
  raise ValueError(
52
56
  "Public instances require a description "
53
57
  "(check visible2*- and description-field)"
54
58
  )
55
- return values
59
+ return self
@@ -8,7 +8,7 @@ from typing import Union
8
8
 
9
9
  import yaml
10
10
  from pydantic import BaseModel
11
- from pydantic import Extra
11
+ from pydantic import ConfigDict
12
12
  from yaml import SafeDumper
13
13
 
14
14
  from .wrapper import Wrapper
@@ -43,36 +43,43 @@ class ShpModel(BaseModel):
43
43
  - schema cls.schema() can also be stored to yaml with .schema_to_file()
44
44
  """
45
45
 
46
- class Config:
47
- allow_mutation = False # const after creation
48
- frozen = True # -> hashable! but currently manually with .get_hash()
49
- extra = Extra.forbid # no unnamed attributes allowed
50
- validate_all = True # also checks defaults
51
- validate_assignment = True # not relevant for the frozen model
52
- min_anystr_length = 4 # force more meaningful descriptors
53
- max_anystr_length = 512
46
+ model_config = ConfigDict(
47
+ frozen=True, # -> const after creation, hashable! but currently manually with .get_hash()
48
+ extra="forbid", # no unnamed attributes allowed
49
+ validate_default=True,
50
+ validate_assignment=True, # not relevant for the frozen model
51
+ str_min_length=1, # force more meaningful descriptors,
52
+ # TODO: was 4 but localizing constraints works different with pydantic2
53
+ # - might be solvable with "use_enum_values=True"
54
+ str_max_length=512,
54
55
  # ⤷ local str-length constraints overrule global ones!
55
- anystr_strip_whitespace = True # strip leading & trailing whitespaces
56
- use_enum_values = True # cleaner export of enum-parameters
57
- allow_inf_nan = False # float without +-inf or NaN
58
- underscore_attrs_are_private = True # allows using them
59
- # Options:
60
- # - https://docs.pydantic.dev/usage/schema/#field-customization
61
- # - https://docs.pydantic.dev/usage/model_config/
62
- # "fields["name"].description = ... should be usable to modify model
56
+ str_strip_whitespace=True, # strip leading & trailing whitespaces
57
+ use_enum_values=True, # cleaner export of enum-parameters
58
+ allow_inf_nan=False, # float without +-inf or NaN
59
+ # defer_build, possible speedup -> but it triggers a bug
60
+ )
63
61
 
64
62
  def __repr__(self) -> str:
65
63
  """string-representation allows print(model)"""
66
- return str(self.dict(exclude_unset=True, exclude_defaults=True))
64
+ return str(self.model_dump(exclude_unset=True, exclude_defaults=True))
67
65
 
68
66
  def __getitem__(self, key):
69
67
  """allows dict access -> model["key"], in addition to model.key"""
70
68
  return self.__getattribute__(key)
71
69
 
70
+ def keys(self):
71
+ """Fn of dict"""
72
+ return self.model_dump().keys()
73
+
74
+ def items(self):
75
+ """Fn of dict"""
76
+ for key in self.keys():
77
+ yield key, self[key]
78
+
72
79
  @classmethod
73
80
  def schema_to_file(cls, path: Union[str, Path]) -> None:
74
81
  """store schema to yaml (for frontend-generators)"""
75
- model_dict = cls.schema()
82
+ model_dict = cls.model_json_schema()
76
83
  model_yaml = yaml.safe_dump(
77
84
  model_dict, default_flow_style=False, sort_keys=False
78
85
  )
@@ -89,7 +96,7 @@ class ShpModel(BaseModel):
89
96
  minimal: stores minimal set (filters out unset & default parameters)
90
97
  comment: documentation
91
98
  """
92
- model_dict = self.dict(exclude_unset=minimal, exclude_defaults=minimal)
99
+ model_dict = self.model_dump(exclude_unset=minimal, exclude_defaults=minimal)
93
100
  model_wrap = Wrapper(
94
101
  datatype=type(self).__name__,
95
102
  comment=comment,
@@ -97,7 +104,7 @@ class ShpModel(BaseModel):
97
104
  parameters=model_dict,
98
105
  )
99
106
  model_yaml = yaml.safe_dump(
100
- model_wrap.dict(exclude_unset=minimal, exclude_defaults=minimal),
107
+ model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal),
101
108
  default_flow_style=False,
102
109
  sort_keys=False,
103
110
  )
@@ -118,4 +125,4 @@ class ShpModel(BaseModel):
118
125
  return cls(**shp_wrap.parameters)
119
126
 
120
127
  def get_hash(self):
121
- return hashlib.sha3_224(str(self.dict()).encode("utf-8")).hexdigest()
128
+ return hashlib.sha3_224(str(self.model_dump()).encode("utf-8")).hexdigest()
@@ -2,9 +2,10 @@ from datetime import datetime
2
2
  from typing import Optional
3
3
 
4
4
  from pydantic import BaseModel
5
- from pydantic import constr
5
+ from pydantic import StringConstraints
6
+ from typing_extensions import Annotated
6
7
 
7
- SafeStrClone = constr(regex=r"^[ -~]+$")
8
+ SafeStrClone = Annotated[str, StringConstraints(pattern=r"^[ -~]+$")]
8
9
  # ⤷ copy avoids circular import
9
10
 
10
11
 
@@ -15,8 +16,8 @@ class Wrapper(BaseModel):
15
16
 
16
17
  datatype: str
17
18
  # ⤷ model-name
18
- comment: Optional[SafeStrClone]
19
- created: Optional[datetime]
19
+ comment: Optional[SafeStrClone] = None
20
+ created: Optional[datetime] = None
20
21
  # ⤷ Optional metadata
21
22
  parameters: dict
22
23
  # ⤷ ShpModel
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from typing import Optional
4
4
 
5
5
  from pydantic import PositiveFloat
6
- from pydantic import root_validator
6
+ from pydantic import model_validator
7
7
 
8
8
  from ...testbed_client import tb_client
9
9
  from ..base.content import ContentModel
@@ -37,7 +37,8 @@ class EnergyEnvironment(ContentModel):
37
37
  indoor: Optional[bool] = None
38
38
  location: Optional[str] = None
39
39
 
40
- @root_validator(pre=True)
40
+ @model_validator(mode="before")
41
+ @classmethod
41
42
  def query_database(cls, values: dict) -> dict:
42
43
  values, _ = tb_client.try_completing_model(cls.__name__, values)
43
44
  return tb_client.fill_in_user_data(values)
@@ -2,9 +2,10 @@ from pathlib import Path
2
2
  from typing import Optional
3
3
  from typing import Union
4
4
 
5
- from pydantic import constr
6
- from pydantic import root_validator
7
- from pydantic import validate_arguments
5
+ from pydantic import StringConstraints
6
+ from pydantic import model_validator
7
+ from pydantic import validate_call
8
+ from typing_extensions import Annotated
8
9
 
9
10
  from ... import fw_tools
10
11
  from ... import logger
@@ -32,6 +33,8 @@ arch_to_mcu: dict = {
32
33
  "nrf52": {"name": "nrf52"},
33
34
  }
34
35
 
36
+ FirmwareStr = Annotated[str, StringConstraints(min_length=3, max_length=8_000_000)]
37
+
35
38
 
36
39
  class Firmware(ContentModel, title="Firmware of Target"):
37
40
  """meta-data representation of a data-component"""
@@ -40,13 +43,14 @@ class Firmware(ContentModel, title="Firmware of Target"):
40
43
 
41
44
  mcu: MCU
42
45
 
43
- data: Union[constr(min_length=3, max_length=8_000_000), Path]
46
+ data: Union[FirmwareStr, Path]
44
47
  data_type: FirmwareDType
45
48
  data_hash: Optional[str] = None
46
49
 
47
50
  # TODO: a data-hash would be awesome
48
51
 
49
- @root_validator(pre=True)
52
+ @model_validator(mode="before")
53
+ @classmethod
50
54
  def query_database(cls, values: dict) -> dict:
51
55
  values, _ = tb_client.try_completing_model(cls.__name__, values)
52
56
  return tb_client.fill_in_user_data(values)
@@ -101,7 +105,7 @@ class Firmware(ContentModel, title="Firmware of Target"):
101
105
  logger.warning("FW-Hash does not match with stored value!")
102
106
  return match
103
107
 
104
- @validate_arguments
108
+ @validate_call
105
109
  def extract_firmware(self, file: Path) -> Path:
106
110
  """stores embedded data in file
107
111
  - file-suffix is derived from data-type and adapted
@@ -2,9 +2,9 @@ from enum import Enum
2
2
  from typing import Optional
3
3
  from typing import Tuple
4
4
 
5
- from pydantic import confloat
6
- from pydantic import conint
7
- from pydantic import root_validator
5
+ from pydantic import Field
6
+ from pydantic import model_validator
7
+ from typing_extensions import Annotated
8
8
 
9
9
  from ...commons import samplerate_sps_default
10
10
  from ...logger import logger
@@ -39,32 +39,33 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
39
39
  algorithm: AlgorithmDType
40
40
  # ⤷ used to harvest energy
41
41
 
42
- samples_n: conint(ge=8, le=2_000) = 8
42
+ samples_n: Annotated[int, Field(ge=8, le=2_000)] = 8
43
43
  # ⤷ for & of ivcurve (and more?`)
44
44
 
45
- voltage_mV: confloat(ge=0, le=5_000) = 2_500
45
+ voltage_mV: Annotated[float, Field(ge=0, le=5_000)] = 2_500
46
46
  # ⤷ starting-point for some algorithms (mppt_po)
47
- voltage_min_mV: confloat(ge=0, le=5_000) = 0
48
- voltage_max_mV: confloat(ge=0, le=5_000) = 5_000
49
- current_limit_uA: confloat(ge=1, le=50_000) = 50_000
47
+ voltage_min_mV: Annotated[float, Field(ge=0, le=5_000)] = 0
48
+ voltage_max_mV: Annotated[float, Field(ge=0, le=5_000)] = 5_000
49
+ current_limit_uA: Annotated[float, Field(ge=1, le=50_000)] = 50_000
50
50
  # ⤷ allows to keep trajectory in special region (or constant current tracking)
51
51
  # ⤷ boundary for detecting open circuit in emulated version (working on IV-Curves)
52
- voltage_step_mV: Optional[confloat(ge=1, le=1_000_000)] = None
52
+ voltage_step_mV: Optional[Annotated[float, Field(ge=1, le=1_000_000)]] = None
53
53
 
54
- setpoint_n: confloat(ge=0, le=1.0) = 0.70
54
+ setpoint_n: Annotated[float, Field(ge=0, le=1.0)] = 0.70
55
55
  # ⤷ ie. for mppt_voc
56
- interval_ms: confloat(ge=0.01, le=1_000_000) = 100
56
+ interval_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 100
57
57
  # ⤷ between start of measurements (ie. V_OC)
58
- duration_ms: confloat(ge=0.01, le=1_000_000) = 0.1
58
+ duration_ms: Annotated[float, Field(ge=0.01, le=1_000_000)] = 0.1
59
59
  # ⤷ of (open voltage) measurement
60
60
  rising: bool = True
61
61
  # ⤷ direction of sawtooth
62
62
 
63
63
  # Underlying recorder
64
- wait_cycles: conint(ge=0, le=100) = 1
64
+ wait_cycles: Annotated[int, Field(ge=0, le=100)] = 1
65
65
  # ⤷ first cycle: ADC-Sampling & DAC-Writing, further steps: waiting
66
66
 
67
- @root_validator(pre=True)
67
+ @model_validator(mode="before")
68
+ @classmethod
68
69
  def query_database(cls, values: dict) -> dict:
69
70
  values, chain = tb_client.try_completing_model(cls.__name__, values)
70
71
  values = tb_client.fill_in_user_data(values)
@@ -72,27 +73,18 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
72
73
  # TODO: same test is later done in calc_algorithm_num() again
73
74
  raise ValueError("Resulting Harvester can't be neutral")
74
75
  logger.debug("VHrv-Inheritances: %s", chain)
75
- return values
76
-
77
- @root_validator(pre=False)
78
- def post_validation(cls, values: dict) -> dict:
79
- if values.get("voltage_min_mV") > values.get("voltage_max_mV"):
80
- raise ValueError("Voltage min > max")
81
- if values.get("voltage_mV") < values.get("voltage_min_mV"):
82
- raise ValueError("Voltage below min")
83
- if values.get("voltage_mV") > values.get("voltage_max_mV"):
84
- raise ValueError("Voltage above max")
85
76
 
77
+ # post corrections -> should be in separate validator
86
78
  cal = CalibrationHarvester() # todo: as argument?
87
- values["current_limit_uA"] = max(
88
- 10**6 * cal.adc_C_Hrv.raw_to_si(4), values["current_limit_uA"]
89
- )
79
+ c_limit = values.get("current_limit_uA", 50_000) # cls.current_limit_uA)
80
+ values["current_limit_uA"] = max(10**6 * cal.adc_C_Hrv.raw_to_si(4), c_limit)
90
81
 
91
82
  if values.get("voltage_step_mV") is None:
92
83
  # algo includes min & max!
93
- values["voltage_step_mV"] = abs(
94
- values["voltage_max_mV"] - values["voltage_min_mV"]
95
- ) / (values["samples_n"] - 1)
84
+ v_max = values.get("voltage_max_mV", 5_000) # cls.voltage_max_mV)
85
+ v_min = values.get("voltage_min_mV", 0) # cls.voltage_min_mV)
86
+ samples_n = values.get("samples_n", 8) # cls.samples_n) TODO
87
+ values["voltage_step_mV"] = abs(v_max - v_min) / (samples_n - 1)
96
88
 
97
89
  values["voltage_step_mV"] = max(
98
90
  10**3 * cal.dac_V_Hrv.raw_to_si(4), values["voltage_step_mV"]
@@ -100,6 +92,17 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
100
92
 
101
93
  return values
102
94
 
95
+ @model_validator(mode="after")
96
+ def post_validation(self):
97
+ if self.voltage_min_mV > self.voltage_max_mV:
98
+ raise ValueError("Voltage min > max")
99
+ if self.voltage_mV < self.voltage_min_mV:
100
+ raise ValueError("Voltage below min")
101
+ if self.voltage_mV > self.voltage_max_mV:
102
+ raise ValueError("Voltage above max")
103
+
104
+ return self
105
+
103
106
  def calc_hrv_mode(self, for_emu: bool) -> int:
104
107
  return 1 * int(for_emu) + 2 * self.rising
105
108
 
@@ -154,7 +157,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
154
157
  return self.samples_n
155
158
 
156
159
 
157
- u32 = conint(ge=0, lt=2**32)
160
+ u32 = Annotated[int, Field(ge=0, lt=2**32)]
158
161
 
159
162
 
160
163
  # Currently implemented harvesters
@@ -230,13 +233,13 @@ class HarvesterPRUConfig(ShpModel):
230
233
  window_size=window_size
231
234
  if window_size is not None
232
235
  else data.calc_window_size(for_emu, dtype_in),
233
- voltage_uV=data.voltage_mV * 10**3,
234
- voltage_min_uV=data.voltage_min_mV * 10**3,
235
- voltage_max_uV=data.voltage_max_mV * 10**3,
236
- voltage_step_uV=data.voltage_step_mV * 10**3,
237
- current_limit_nA=data.current_limit_uA * 10**3,
238
- setpoint_n8=min(255, data.setpoint_n * 2**8),
239
- interval_n=interval_ms * samplerate_sps_default * 1e-3,
240
- duration_n=duration_ms * samplerate_sps_default * 1e-3,
236
+ voltage_uV=round(data.voltage_mV * 10**3),
237
+ voltage_min_uV=round(data.voltage_min_mV * 10**3),
238
+ voltage_max_uV=round(data.voltage_max_mV * 10**3),
239
+ voltage_step_uV=round(data.voltage_step_mV * 10**3),
240
+ current_limit_nA=round(data.current_limit_uA * 10**3),
241
+ setpoint_n8=round(min(255, data.setpoint_n * 2**8)),
242
+ interval_n=round(interval_ms * samplerate_sps_default * 1e-3),
243
+ duration_n=round(duration_ms * samplerate_sps_default * 1e-3),
241
244
  wait_cycles_n=data.wait_cycles,
242
245
  )