PyDPEET 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. pydpeet/__init__.py +56 -0
  2. pydpeet/citations/__init__.py +7 -0
  3. pydpeet/citations/citeme.py +397 -0
  4. pydpeet/citations/html_writer.py +137 -0
  5. pydpeet/dev_utils/__init__.py +7 -0
  6. pydpeet/dev_utils/generate_inits/__init__.py +7 -0
  7. pydpeet/dev_utils/generate_inits/generate_inits.py +327 -0
  8. pydpeet/io/__init__.py +7 -0
  9. pydpeet/io/configs/__init__.py +7 -0
  10. pydpeet/io/configs/config.py +227 -0
  11. pydpeet/io/configs/const.py +1 -0
  12. pydpeet/io/convert.py +594 -0
  13. pydpeet/io/device/__init__.py +7 -0
  14. pydpeet/io/device/arbin_4_23_PV090331/__init__.py +7 -0
  15. pydpeet/io/device/arbin_4_23_PV090331/formatter.py +22 -0
  16. pydpeet/io/device/arbin_4_23_PV090331/mapper.py +18 -0
  17. pydpeet/io/device/arbin_4_23_PV090331/reader.py +68 -0
  18. pydpeet/io/device/arbin_8_00_PV221201/__init__.py +7 -0
  19. pydpeet/io/device/arbin_8_00_PV221201/formatter.py +26 -0
  20. pydpeet/io/device/arbin_8_00_PV221201/mapper.py +18 -0
  21. pydpeet/io/device/arbin_8_00_PV221201/reader.py +66 -0
  22. pydpeet/io/device/basytec_6_3_1_0/__init__.py +7 -0
  23. pydpeet/io/device/basytec_6_3_1_0/formatter.py +18 -0
  24. pydpeet/io/device/basytec_6_3_1_0/mapper.py +18 -0
  25. pydpeet/io/device/basytec_6_3_1_0/reader.py +54 -0
  26. pydpeet/io/device/digatron_4_20_6_236/__init__.py +7 -0
  27. pydpeet/io/device/digatron_4_20_6_236/formatter.py +27 -0
  28. pydpeet/io/device/digatron_4_20_6_236/mapper.py +18 -0
  29. pydpeet/io/device/digatron_4_20_6_236/reader.py +45 -0
  30. pydpeet/io/device/digatron_eis_4_20_6_236/__init__.py +7 -0
  31. pydpeet/io/device/digatron_eis_4_20_6_236/formatter.py +39 -0
  32. pydpeet/io/device/digatron_eis_4_20_6_236/mapper.py +18 -0
  33. pydpeet/io/device/digatron_eis_4_20_6_236/reader.py +42 -0
  34. pydpeet/io/device/neware_8_0_0_516/__init__.py +7 -0
  35. pydpeet/io/device/neware_8_0_0_516/formatter.py +31 -0
  36. pydpeet/io/device/neware_8_0_0_516/mapper.py +18 -0
  37. pydpeet/io/device/neware_8_0_0_516/reader.py +397 -0
  38. pydpeet/io/device/parstat_2_63_3/__init__.py +7 -0
  39. pydpeet/io/device/parstat_2_63_3/formatter.py +34 -0
  40. pydpeet/io/device/parstat_2_63_3/mapper.py +18 -0
  41. pydpeet/io/device/parstat_2_63_3/reader.py +34 -0
  42. pydpeet/io/device/safion_1_9/__init__.py +7 -0
  43. pydpeet/io/device/safion_1_9/formatter.py +25 -0
  44. pydpeet/io/device/safion_1_9/mapper.py +18 -0
  45. pydpeet/io/device/safion_1_9/reader.py +89 -0
  46. pydpeet/io/device/zahner/__init__.py +7 -0
  47. pydpeet/io/device/zahner/formatter.py +73 -0
  48. pydpeet/io/device/zahner/mapper.py +34 -0
  49. pydpeet/io/device/zahner/reader.py +46 -0
  50. pydpeet/io/device/zahner_new/__init__.py +7 -0
  51. pydpeet/io/device/zahner_new/formatter.py +72 -0
  52. pydpeet/io/device/zahner_new/mapper.py +50 -0
  53. pydpeet/io/device/zahner_new/reader.py +46 -0
  54. pydpeet/io/map.py +69 -0
  55. pydpeet/io/read.py +50 -0
  56. pydpeet/io/utils/__init__.py +7 -0
  57. pydpeet/io/utils/ext_path.py +34 -0
  58. pydpeet/io/utils/formatter_utils.py +429 -0
  59. pydpeet/io/utils/load_custom_module.py +33 -0
  60. pydpeet/io/utils/timing.py +36 -0
  61. pydpeet/io/write.py +99 -0
  62. pydpeet/process/__init__.py +7 -0
  63. pydpeet/process/analyze/__init__.py +7 -0
  64. pydpeet/process/analyze/average.py +297 -0
  65. pydpeet/process/analyze/capacity.py +208 -0
  66. pydpeet/process/analyze/configs/__init__.py +7 -0
  67. pydpeet/process/analyze/configs/battery_config.py +38 -0
  68. pydpeet/process/analyze/configs/ocv_config.py +177 -0
  69. pydpeet/process/analyze/configs/step_analyzer_config.py +178 -0
  70. pydpeet/process/analyze/cycle.py +63 -0
  71. pydpeet/process/analyze/efficiency.py +182 -0
  72. pydpeet/process/analyze/energy.py +51 -0
  73. pydpeet/process/analyze/extract/__init__.py +7 -0
  74. pydpeet/process/analyze/extract/dva_ica.py +224 -0
  75. pydpeet/process/analyze/extract/ocv.py +196 -0
  76. pydpeet/process/analyze/power.py +32 -0
  77. pydpeet/process/analyze/resistance.py +87 -0
  78. pydpeet/process/analyze/soc.py +418 -0
  79. pydpeet/process/analyze/soh.py +72 -0
  80. pydpeet/process/analyze/utils.py +122 -0
  81. pydpeet/process/merge/__init__.py +7 -0
  82. pydpeet/process/merge/series.py +274 -0
  83. pydpeet/process/sequence/__init__.py +7 -0
  84. pydpeet/process/sequence/configs/__init__.py +7 -0
  85. pydpeet/process/sequence/configs/config.py +17 -0
  86. pydpeet/process/sequence/step_analyzer.py +440 -0
  87. pydpeet/process/sequence/utils/__init__.py +7 -0
  88. pydpeet/process/sequence/utils/annotate/__init__.py +7 -0
  89. pydpeet/process/sequence/utils/annotate/annotate_primitives.py +419 -0
  90. pydpeet/process/sequence/utils/configs/CONFIG_Fallback.py +231 -0
  91. pydpeet/process/sequence/utils/configs/CONFIG_preprocessing.py +243 -0
  92. pydpeet/process/sequence/utils/configs/__init__.py +7 -0
  93. pydpeet/process/sequence/utils/console_prints/__init__.py +7 -0
  94. pydpeet/process/sequence/utils/console_prints/log_time.py +30 -0
  95. pydpeet/process/sequence/utils/postprocessing/__init__.py +7 -0
  96. pydpeet/process/sequence/utils/postprocessing/df_primitives_correction.py +166 -0
  97. pydpeet/process/sequence/utils/postprocessing/filter_df.py +168 -0
  98. pydpeet/process/sequence/utils/postprocessing/generate_instructions.py +297 -0
  99. pydpeet/process/sequence/utils/preprocessing/__init__.py +7 -0
  100. pydpeet/process/sequence/utils/preprocessing/calculate_thresholds.py +27 -0
  101. pydpeet/process/sequence/utils/processing/__init__.py +7 -0
  102. pydpeet/process/sequence/utils/processing/analyze_segments.py +368 -0
  103. pydpeet/process/sequence/utils/processing/attempt_to_merge_neighboring_segments.py +122 -0
  104. pydpeet/process/sequence/utils/processing/check_CV_results.py +226 -0
  105. pydpeet/process/sequence/utils/processing/check_power_zero_watt_segments.py +85 -0
  106. pydpeet/process/sequence/utils/processing/check_zero_length.py +117 -0
  107. pydpeet/process/sequence/utils/processing/split_in_segments.py +97 -0
  108. pydpeet/process/sequence/utils/processing/supress_smaller_segments.py +99 -0
  109. pydpeet/process/sequence/utils/processing/widen_constant_segments.py +185 -0
  110. pydpeet/process/sequence/utils/visualize/__init__.py +7 -0
  111. pydpeet/process/sequence/utils/visualize/visualize_data.py +288 -0
  112. pydpeet/res/__init__.py +7 -0
  113. pydpeet/settings.py +0 -0
  114. pydpeet/utils/__init__.py +7 -0
  115. pydpeet/utils/logging_style.py +28 -0
  116. pydpeet/version.py +0 -0
  117. pydpeet-0.2.0.dist-info/METADATA +85 -0
  118. pydpeet-0.2.0.dist-info/RECORD +122 -0
  119. pydpeet-0.2.0.dist-info/WHEEL +5 -0
  120. pydpeet-0.2.0.dist-info/licenses/AUTHORS.md +0 -0
  121. pydpeet-0.2.0.dist-info/licenses/LICENCE.md +27 -0
  122. pydpeet-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Init generator
4
+ - Deletes all existing __init__.py under the package root
5
+ - Creates restrictive __init__.py everywhere
6
+ - Applies config.json to expose only the listed functions at the requested package depth
7
+
8
+ expects config.json in format:
9
+ {
10
+ "src/pydpeet/convert/convert.py": {
11
+ "depth": 4,
12
+ "exports": ["convert_file"]
13
+ },
14
+ ...
15
+ }
16
+
17
+ this will enable this import:
18
+ import pydpeet as eet
19
+ eet.convert.convert_file(...)
20
+ """
21
+
22
+ import datetime
23
+ import json
24
+ import os
25
+ import sys
26
+
27
+ CONFIG_FILENAME = "config.json"
28
+ LOG_FILENAME = "generation_log.txt"
29
+
30
+
31
+ def get_script_path():
32
+ return os.path.abspath(__file__)
33
+
34
+
35
+ def get_script_dir():
36
+ return os.path.dirname(get_script_path())
37
+
38
+
39
+ def header_text():
40
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
41
+ return f'"""\nAuto-generated __init__ file.\nCreated: {timestamp}\n"""\n\n'
42
+
43
+
44
+ class Logger:
45
+ def __init__(self, filename):
46
+ self.filepath = os.path.join(get_script_dir(), filename)
47
+ with open(self.filepath, "w", encoding="utf-8") as f:
48
+ f.write(f"--- Init Generation Log: {datetime.datetime.now()} ---\n\n")
49
+
50
+ def log(self, message):
51
+ print(message)
52
+ with open(self.filepath, "a", encoding="utf-8") as f:
53
+ f.write(message + "\n")
54
+
55
+
56
+ def find_project_and_package_root(start_path, logger):
57
+ """
58
+ Walk up until we find a pyproject.toml (project root).
59
+ If a 'src' folder exists in the project root we treat project_root/src as package_root.
60
+ Otherwise package_root == project_root.
61
+ """
62
+ current = start_path
63
+ while True:
64
+ if os.path.exists(os.path.join(current, "pyproject.toml")):
65
+ project_root = current
66
+ src_path = os.path.join(project_root, "src")
67
+ package_root = src_path if os.path.isdir(src_path) else project_root
68
+ logger.log(f"Project root: {project_root}")
69
+ logger.log(f"Package root: {package_root}")
70
+ return project_root, package_root
71
+ parent = os.path.dirname(current)
72
+ if parent == current:
73
+ # reached filesystem root, fallback
74
+ logger.log("No pyproject.toml found; using script dir as project_root/package_root")
75
+ return start_path, start_path
76
+ current = parent
77
+
78
+
79
+ def remove_all_inits(root_dir, logger):
80
+ """Delete every existing __init__.py under root_dir"""
81
+ logger.log("--- PHASE 1: Deleting all existing __init__.py files ---")
82
+ removed = 0
83
+ for dirpath, _, filenames in os.walk(root_dir):
84
+ for fn in filenames:
85
+ if fn == "__init__.py":
86
+ p = os.path.join(dirpath, fn)
87
+ try:
88
+ os.remove(p)
89
+ removed += 1
90
+ except Exception as e:
91
+ logger.log(f"Failed to remove {p}: {e}")
92
+ logger.log(f"Removed {removed} existing __init__.py files.")
93
+
94
+
95
+ def create_empty_inits(root_dir, logger):
96
+ """
97
+ Create an __init__.py for every folder under root_dir with strict empty __all__.
98
+ This ensures no accidental exports by existing modules.
99
+ """
100
+ logger.log("--- PHASE 2: Creating restrictive empty __init__.py across tree ---")
101
+ created = 0
102
+ for dirpath, dirnames, _ in os.walk(root_dir):
103
+ # skip hidden dirs and __pycache__
104
+ dirnames[:] = [d for d in dirnames if not d.startswith(".") and d != "__pycache__"]
105
+ init_path = os.path.join(dirpath, "__init__.py")
106
+ try:
107
+ with open(init_path, "w", encoding="utf-8") as f:
108
+ f.write(header_text())
109
+ f.write("# Restrictive package init: start with no public API\n")
110
+ f.write("__all__ = []\n")
111
+ created += 1
112
+ except Exception as e:
113
+ logger.log(f"Failed to create {init_path}: {e}")
114
+ logger.log(f"Created {created} new restrictive __init__.py files.")
115
+
116
+
117
+ def module_dotted_from_path(abs_path, project_root, package_root):
118
+ """
119
+ Return the full dotted module path for a .py file relative to package_root.
120
+ e.g. abs_path -> /.../project/src/pydpeet/convert/directory_standardization.py
121
+ package_root -> /.../project/src
122
+ returns -> 'pydpeet.convert.directory_standardization'
123
+ """
124
+ rel = os.path.relpath(abs_path, package_root)
125
+ parts = rel.split(os.sep)
126
+ if parts[-1].endswith(".py"):
127
+ parts[-1] = parts[-1][:-3]
128
+ return ".".join(parts)
129
+
130
+
131
+ def apply_config(project_root, package_root, config_path, logger):
132
+ logger.log("--- PHASE 3: Applying config.json to build the public API ---")
133
+ with open(config_path, encoding="utf-8") as f:
134
+ config = json.load(f)
135
+
136
+ # mapping: dir_path -> { 'child_packages': set(child_name),
137
+ # 'module_imports': { full_module_dotted: set(export_names) },
138
+ # 'exports': set(function_and_child_names) }
139
+ dir_data = {}
140
+
141
+ def get_data_for_dir(dir_path):
142
+ if dir_path not in dir_data:
143
+ dir_data[dir_path] = {
144
+ "child_packages": set(),
145
+ "module_imports": {}, # module_dotted -> set(names)
146
+ "exports": set(),
147
+ }
148
+ return dir_data[dir_path]
149
+
150
+ for rel_path, settings in config.items():
151
+ exports = settings.get("exports", [])
152
+ depth = settings.get("depth")
153
+ if not isinstance(depth, int) or depth < 1:
154
+ logger.log(f"Invalid depth for {rel_path}: {depth} (must be int >=1); skipping")
155
+ continue
156
+
157
+ source_abs_path = os.path.abspath(os.path.join(project_root, rel_path))
158
+ if not os.path.exists(source_abs_path):
159
+ logger.log(f"WARNING: file not found: {source_abs_path}; skipping")
160
+ continue
161
+
162
+ # Build rel parts from project root (so depth param matches your config's notion)
163
+ rel_parts_from_project = os.path.relpath(source_abs_path, project_root).split(os.sep)
164
+ # target folder parts are first (depth - 1) entries
165
+ target_folder_parts = rel_parts_from_project[: max(1, depth - 1)]
166
+ # if package_root is project_root/src, drop that first element if it equals basename(package_root)
167
+ if os.path.normpath(package_root).endswith(os.path.sep + target_folder_parts[0]) or (
168
+ target_folder_parts[0] == os.path.basename(package_root)
169
+ ):
170
+ # remove the leading package root element if present (common case with 'src')
171
+ pkg_parts = target_folder_parts[1:]
172
+ else:
173
+ # general case: target package parts are target_folder_parts starting at 0,
174
+ # but if the first entry was 'src' (rare if package_root != project_root) handle below
175
+ if target_folder_parts[0] == "src":
176
+ pkg_parts = target_folder_parts[1:]
177
+ else:
178
+ pkg_parts = target_folder_parts
179
+
180
+ if not pkg_parts:
181
+ # exporting at the package root
182
+ target_dir = package_root
183
+ else:
184
+ target_dir = os.path.join(package_root, *pkg_parts)
185
+
186
+ # Determine full module dotted name relative to package root
187
+ module_dotted = module_dotted_from_path(source_abs_path, project_root, package_root)
188
+
189
+ # Add the function exports to the target_dir data
190
+ ddata = get_data_for_dir(target_dir)
191
+ ddata["module_imports"].setdefault(module_dotted, set()).update(exports)
192
+ ddata["exports"].update(exports)
193
+
194
+ # Bubble up: mark parent dirs to import child packages (so top-level 'pydpeet' exposes 'convert', etc.)
195
+ # walk parents until package_root (inclusive)
196
+ curr = target_dir
197
+ # if target_dir is package_root, still possibly export module names directly at package_root
198
+ while True:
199
+ parent = os.path.dirname(curr)
200
+ if parent == curr:
201
+ break
202
+ if os.path.commonpath([package_root, curr]) != os.path.normpath(package_root) and not curr.startswith(
203
+ os.path.normpath(package_root)
204
+ ):
205
+ # outside of package_root, stop
206
+ break
207
+ # compute child name (basename of curr) relative to parent
208
+ child_name = os.path.basename(curr)
209
+ if child_name == "":
210
+ break
211
+ if parent.startswith(package_root) or os.path.normpath(parent) == os.path.normpath(package_root):
212
+ # ensure parent has data
213
+ p_data = get_data_for_dir(parent)
214
+ # Parent should expose the child package name (so importers can reference pydpeet.convert)
215
+ p_data["child_packages"].add(child_name)
216
+ p_data["exports"].add(child_name)
217
+ if os.path.normpath(curr) == os.path.normpath(package_root):
218
+ break
219
+ curr = parent
220
+ if os.path.normpath(curr) == os.path.normpath(os.path.dirname(package_root)):
221
+ break
222
+
223
+ # Now write the __init__.py files for each dir in dir_data (overwrite the existing restrictive ones)
224
+ for dir_path, data in dir_data.items():
225
+ init_path = os.path.join(dir_path, "__init__.py")
226
+ # ensure the dir exists
227
+ os.makedirs(dir_path, exist_ok=True)
228
+ try:
229
+ with open(init_path, "w", encoding="utf-8") as f:
230
+ f.write(header_text())
231
+ # child package imports (so parent exposes child as attribute)
232
+ if data["child_packages"]:
233
+ f.write("# Child packages exported by this package\n")
234
+ for child in sorted(data["child_packages"]):
235
+ f.write(f"from . import {child}\n")
236
+ f.write("\n")
237
+
238
+ # module imports -> bind functions into this package WITHOUT leaving the submodule object as an attribute
239
+ # inside the loop that writes the __init__.py file for dir_path:
240
+
241
+ # --- static re-export approach ---
242
+ if data["module_imports"]:
243
+ f.write("# Re-export selected names from source modules\n\n")
244
+ # module_imports is mapping full_module_dotted -> set(names)
245
+ for mod in sorted(data["module_imports"].keys()):
246
+ names = sorted(data["module_imports"][mod])
247
+ # Write a single `from <module> import a, b, c` line per module
248
+ # Use explicit imports only for the configured names
249
+ f.write(f"from {mod} import {', '.join(names)}\n")
250
+ f.write("\n")
251
+
252
+ # Write __all__
253
+ all_entries = sorted(data["exports"])
254
+ f.write("# Public API for this package\n")
255
+ f.write("__all__ = [\n")
256
+ for ent in all_entries:
257
+ f.write(f" '{ent}',\n")
258
+ f.write("]\n")
259
+ logger.log(f"Written public API for: {os.path.relpath(dir_path, package_root)}")
260
+ except Exception as e:
261
+ logger.log(f"Failed to write {init_path}: {e}")
262
+
263
+ # TODO: Obsolete?
264
+ # Ensure package_root has an __init__ that exports top-level packages (e.g., 'pydpeet')
265
+ # root_data = get_data_for_dir(package_root)
266
+
267
+ # If package root did not get any auto exports (possible), build sensible top-level __init__
268
+ # but don't overwrite if a specific config already created explicit exports
269
+ init_root = os.path.join(package_root, "__init__.py")
270
+ if os.path.exists(init_root):
271
+ # augment: ensure __all__ includes folders with exports present under package_root
272
+ # find immediate child dirs with dir_data entries
273
+ child_packages = set()
274
+ for p in dir_data.keys():
275
+ try:
276
+ rel = os.path.relpath(p, package_root).split(os.sep)
277
+ if len(rel) >= 1 and rel[0] != ".":
278
+ child_packages.add(rel[0])
279
+ except Exception:
280
+ continue
281
+ # read the file and update __all__ if needed
282
+ try:
283
+ # TODO: Obsolete?
284
+ # with open(init_root, encoding="utf-8") as f:
285
+ # content = f.read()
286
+
287
+ # simplistic append if not present
288
+ if child_packages:
289
+ # create an augmented root __init__ that imports child packages and sets __all__
290
+ with open(init_root, "w", encoding="utf-8") as f:
291
+ f.write(header_text())
292
+ f.write("# Expose immediate child packages at package root\n")
293
+ for child in sorted(child_packages):
294
+ f.write(f"from . import {child}\n")
295
+ f.write("\n")
296
+ f.write("__all__ = [\n")
297
+ for child in sorted(child_packages):
298
+ f.write(f" '{child}',\n")
299
+ f.write("]\n")
300
+ logger.log("Updated package root __init__ to export immediate child packages.")
301
+ except Exception as e:
302
+ logger.log(f"Failed updating package root __init__: {e}")
303
+
304
+
305
+ def main():
306
+ logger = Logger(LOG_FILENAME)
307
+ script_dir = get_script_dir()
308
+ project_root, package_root = find_project_and_package_root(script_dir, logger)
309
+
310
+ # Phase 1: delete all old inits
311
+ remove_all_inits(package_root, logger)
312
+
313
+ # Phase 2: create empty restrictive inits everywhere
314
+ create_empty_inits(package_root, logger)
315
+
316
+ # Phase 3: apply config
317
+ config_path = os.path.join(script_dir, CONFIG_FILENAME)
318
+ if not os.path.exists(config_path):
319
+ logger.log(f"Config file not found at {config_path}. Aborting.")
320
+ sys.exit(1)
321
+
322
+ apply_config(project_root, package_root, config_path, logger)
323
+ logger.log("\n--- Done ---\n")
324
+
325
+
326
+ if __name__ == "__main__":
327
+ main()
pydpeet/io/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ Auto-generated __init__ file.
3
+ Created: 2026-03-06 15:11:49
4
+ """
5
+
6
+ # Restrictive package init: start with no public API
7
+ __all__ = []
@@ -0,0 +1,7 @@
1
+ """
2
+ Auto-generated __init__ file.
3
+ Created: 2026-03-06 15:11:49
4
+ """
5
+
6
+ # Restrictive package init: start with no public API
7
+ __all__ = []
@@ -0,0 +1,227 @@
1
+ from collections.abc import Callable
2
+ from enum import Enum, auto
3
+
4
+ from pandas import DataFrame
5
+
6
+ # Arbin MITS Pro
7
+ # v4.23 (090331), schedule version 3.03
8
+ import pydpeet.io.device.arbin_4_23_PV090331.formatter as arbin_4_23_PV090331_formatter
9
+ import pydpeet.io.device.arbin_4_23_PV090331.mapper as arbin_4_23_PV090331_mapper
10
+ import pydpeet.io.device.arbin_4_23_PV090331.reader as arbin_4_23_PV090331_reader
11
+
12
+ # Arbin MITS Pro
13
+ # v8.00 (PV221201), schedule version 8.00.13
14
+ import pydpeet.io.device.arbin_8_00_PV221201.formatter as arbin_8_00_PV221201_formatter
15
+ import pydpeet.io.device.arbin_8_00_PV221201.mapper as arbin_8_00_PV221201_mapper
16
+ import pydpeet.io.device.arbin_8_00_PV221201.reader as arbin_8_00_PV221201_reader
17
+
18
+ # BaSyTec Battery Test Software
19
+ # File version 6.3.1.0, product version 6.2
20
+ import pydpeet.io.device.basytec_6_3_1_0.formatter as basytec_6_3_1_0_formatter
21
+ import pydpeet.io.device.basytec_6_3_1_0.mapper as basytec_6_3_1_0_mapper
22
+ import pydpeet.io.device.basytec_6_3_1_0.reader as basytec_6_3_1_0_reader
23
+
24
+ # Digatron Battery Manager and Battery Manager Workstation
25
+ # v4.20.6.236 (2018-09-28)
26
+ import pydpeet.io.device.digatron_4_20_6_236.formatter as digatron_4_20_6_236_formatter
27
+ import pydpeet.io.device.digatron_4_20_6_236.mapper as digatron_4_20_6_236_mapper
28
+ import pydpeet.io.device.digatron_4_20_6_236.reader as digatron_4_20_6_236_reader
29
+
30
+ # Digatron (EIS) Battery Manager and Battery Manager Workstation
31
+ # v4.20.6.236 (2018-09-28)
32
+ import pydpeet.io.device.digatron_eis_4_20_6_236.formatter as digatron_eis_4_20_6_236_formatter
33
+ import pydpeet.io.device.digatron_eis_4_20_6_236.mapper as digatron_eis_4_20_6_236_mapper
34
+ import pydpeet.io.device.digatron_eis_4_20_6_236.reader as digatron_eis_4_20_6_236_reader
35
+
36
+ # Neware BTS
37
+ # Client version 8.0.0.516 (2023-05-13 R3), server version 8.0.0.323 (2023-05-13 R3)
38
+ import pydpeet.io.device.neware_8_0_0_516.formatter as neware_8_0_0_516_formatter
39
+ import pydpeet.io.device.neware_8_0_0_516.mapper as neware_8_0_0_516_mapper
40
+ import pydpeet.io.device.neware_8_0_0_516.reader as neware_8_0_0_516_reader
41
+
42
+ # Parstat VersaStudio
43
+ # v2.63.3, firmware version 2.63.1
44
+ import pydpeet.io.device.parstat_2_63_3.formatter as parstat_2_63_3_formatter
45
+ import pydpeet.io.device.parstat_2_63_3.mapper as parstat_2_63_3_mapper
46
+ import pydpeet.io.device.parstat_2_63_3.reader as parstat_2_63_3_reader
47
+
48
+ # Safion Inspectrum Suite
49
+ # v1.9 (2023-10-18)
50
+ import pydpeet.io.device.safion_1_9.formatter as safion_1_9_formatter
51
+ import pydpeet.io.device.safion_1_9.mapper as safion_1_9_mapper
52
+ import pydpeet.io.device.safion_1_9.reader as safion_1_9_reader
53
+
54
+ # Zahner
55
+ # TODO: Add specs
56
+ import pydpeet.io.device.zahner.formatter as zahner_formatter
57
+ import pydpeet.io.device.zahner.mapper as zahner_mapper
58
+ import pydpeet.io.device.zahner.reader as zahner_reader
59
+
60
+ # Zahner (new)
61
+ # TODO: Add specs
62
+ import pydpeet.io.device.zahner_new.formatter as zahner_new_formatter
63
+ import pydpeet.io.device.zahner_new.mapper as zahner_new_mapper
64
+ import pydpeet.io.device.zahner_new.reader as zahner_new_reader
65
+
66
+
67
+ class Config(Enum):
68
+ Zahner_1 = auto()
69
+ Zahner_2 = auto()
70
+ Zahner_new_1 = auto()
71
+ Zahner_new_2 = auto()
72
+ Zahner_new_3 = auto()
73
+ Safion_1_9 = auto()
74
+ Parstat_2_63_3 = auto()
75
+ Neware_8_0_0_516 = auto()
76
+ Digatron_4_20_6_236 = auto()
77
+ Digatron_EIS_4_20_6_236 = auto()
78
+ BaSyTec_6_3_1_0 = auto()
79
+ Arbin_8_00_PV221201 = auto()
80
+ Arbin_4_23_PV090331 = auto()
81
+ Custom = auto()
82
+
83
+ @classmethod
84
+ def from_string(cls, value: str) -> "Config":
85
+ if not isinstance(value, str):
86
+ raise TypeError("Config must be str or Config enum")
87
+
88
+ key = value.strip().lower()
89
+
90
+ aliases = {
91
+ "arbin_4_23_pv090331": cls.Arbin_4_23_PV090331,
92
+ "arbin_8_00_pv221201": cls.Arbin_8_00_PV221201,
93
+ "basytec_6_3_1_0": cls.BaSyTec_6_3_1_0,
94
+ "digatron_4_20_6_236": cls.Digatron_4_20_6_236,
95
+ "digatron_eis_4_20_6_236": cls.Digatron_EIS_4_20_6_236,
96
+ "neware_8_0_0_516": cls.Neware_8_0_0_516,
97
+ "parstat_2_63_3": cls.Parstat_2_63_3,
98
+ "safion_1_9": cls.Safion_1_9,
99
+ "zahner1": cls.Zahner_1,
100
+ "zahner2": cls.Zahner_2,
101
+ "zahner_new_1": cls.Zahner_new_1,
102
+ }
103
+
104
+ try:
105
+ return aliases[key]
106
+ except KeyError as k:
107
+ known = ", ".join(aliases.keys())
108
+ raise ValueError(f"Unknown config '{value}'. Known: {known}") from k
109
+
110
+ @staticmethod
111
+ def exists(maybe_config: any) -> bool:
112
+ """
113
+ Checks if a given configuration is a member of the Config enum.
114
+
115
+ Args:
116
+ maybe_config (any): The configuration to check, which can be an enum member or its value.
117
+
118
+ Returns:
119
+ bool: True if maybe_config is a member of the Config enum or its value; False otherwise.
120
+ """
121
+ try:
122
+ return maybe_config in Config.__members__ or maybe_config.value in {config.value for config in Config}
123
+ except Exception:
124
+ return False
125
+
126
+ @staticmethod
127
+ def not_exists(value: any) -> bool:
128
+ """
129
+ Checks if a given configuration does not exist.
130
+
131
+ Args:
132
+ value (any): The configuration to check, which can be an enum member or its value.
133
+
134
+ Returns:
135
+ bool: True if maybe_config is not a member of the Config enum or its value; False otherwise.
136
+ """
137
+ return not Config.exists(value)
138
+
139
+
140
+ STANDARD_COLUMNS = [
141
+ "Meta_Data",
142
+ "Step_Count",
143
+ "Voltage[V]",
144
+ "Current[A]",
145
+ "Temperature[°C]",
146
+ "Test_Time[s]",
147
+ "Date_Time",
148
+ "EIS_f[Hz]",
149
+ "EIS_Z_Real[Ohm]",
150
+ "EIS_Z_Imag[Ohm]",
151
+ "EIS_DC[A]",
152
+ ]
153
+
154
+ READER_CONFIGS: dict[Config, Callable[[str], DataFrame]] = {
155
+ # zahner readers
156
+ Config.Zahner_1: zahner_reader.to_dataframe,
157
+ Config.Zahner_2: zahner_reader.to_dataframe,
158
+ # zahner New readers
159
+ Config.Zahner_new_1: zahner_new_reader.to_dataframe,
160
+ Config.Zahner_new_2: zahner_new_reader.to_dataframe,
161
+ Config.Zahner_new_3: zahner_new_reader.to_dataframe,
162
+ # Other readers
163
+ Config.Safion_1_9: safion_1_9_reader.to_dataframe,
164
+ Config.Parstat_2_63_3: parstat_2_63_3_reader.to_dataframe,
165
+ Config.Neware_8_0_0_516: neware_8_0_0_516_reader.to_dataframe,
166
+ Config.Digatron_4_20_6_236: digatron_4_20_6_236_reader.to_dataframe,
167
+ Config.Digatron_EIS_4_20_6_236: digatron_eis_4_20_6_236_reader.to_dataframe,
168
+ Config.BaSyTec_6_3_1_0: basytec_6_3_1_0_reader.to_dataframe,
169
+ Config.Arbin_8_00_PV221201: arbin_8_00_PV221201_reader.to_dataframe,
170
+ Config.Arbin_4_23_PV090331: arbin_4_23_PV090331_reader.to_dataframe,
171
+ }
172
+
173
+ MAPPER_CONFIGS: dict[Config, tuple[dict[str, str], list[str]]] = {
174
+ # zahner mappers
175
+ Config.Zahner_1: (zahner_mapper.COLUMN_MAP_1, zahner_mapper.MISSING_REQUIRED_COLUMNS_1),
176
+ Config.Zahner_2: (zahner_mapper.COLUMN_MAP_2, zahner_mapper.MISSING_REQUIRED_COLUMNS_2),
177
+ # zahner New mappers
178
+ Config.Zahner_new_1: (zahner_new_mapper.COLUMN_MAP_1, zahner_new_mapper.MISSING_REQUIRED_COLUMNS_1),
179
+ Config.Zahner_new_2: (zahner_new_mapper.COLUMN_MAP_2, zahner_new_mapper.MISSING_REQUIRED_COLUMNS_2),
180
+ Config.Zahner_new_3: (zahner_new_mapper.COLUMN_MAP_3, zahner_new_mapper.MISSING_REQUIRED_COLUMNS_3),
181
+ # Other mappers
182
+ Config.Safion_1_9: (safion_1_9_mapper.COLUMN_MAP, safion_1_9_mapper.MISSING_REQUIRED_COLUMNS),
183
+ Config.Parstat_2_63_3: (parstat_2_63_3_mapper.COLUMN_MAP, parstat_2_63_3_mapper.MISSING_REQUIRED_COLUMNS),
184
+ Config.Neware_8_0_0_516: (neware_8_0_0_516_mapper.COLUMN_MAP, neware_8_0_0_516_mapper.MISSING_REQUIRED_COLUMNS),
185
+ Config.Digatron_4_20_6_236: (
186
+ digatron_4_20_6_236_mapper.COLUMN_MAP,
187
+ digatron_4_20_6_236_mapper.MISSING_REQUIRED_COLUMNS,
188
+ ),
189
+ Config.Digatron_EIS_4_20_6_236: (
190
+ digatron_eis_4_20_6_236_mapper.COLUMN_MAP,
191
+ digatron_eis_4_20_6_236_mapper.MISSING_REQUIRED_COLUMNS,
192
+ ),
193
+ Config.BaSyTec_6_3_1_0: (basytec_6_3_1_0_mapper.COLUMN_MAP, basytec_6_3_1_0_mapper.MISSING_REQUIRED_COLUMNS),
194
+ Config.Arbin_8_00_PV221201: (
195
+ arbin_8_00_PV221201_mapper.COLUMN_MAP,
196
+ arbin_8_00_PV221201_mapper.MISSING_REQUIRED_COLUMNS,
197
+ ),
198
+ Config.Arbin_4_23_PV090331: (
199
+ arbin_4_23_PV090331_mapper.COLUMN_MAP,
200
+ arbin_4_23_PV090331_mapper.MISSING_REQUIRED_COLUMNS,
201
+ ),
202
+ }
203
+
204
+ FORMATTER_CONFIGS: dict[Config, Callable[[DataFrame], DataFrame]] = {
205
+ # zahner formatters
206
+ Config.Zahner_1: zahner_formatter.get_data_into_format_zahner_1,
207
+ Config.Zahner_2: zahner_formatter.get_data_into_format_zahner_2,
208
+ # zahner New formatters
209
+ Config.Zahner_new_1: zahner_new_formatter.get_data_into_format_zahner_1,
210
+ Config.Zahner_new_2: zahner_new_formatter.get_data_into_format_zahner_2,
211
+ Config.Zahner_new_3: zahner_new_formatter.get_data_into_format_zahner_3,
212
+ # Other formatters
213
+ Config.Safion_1_9: safion_1_9_formatter.get_data_into_format,
214
+ Config.Parstat_2_63_3: parstat_2_63_3_formatter.get_data_into_format,
215
+ Config.Neware_8_0_0_516: neware_8_0_0_516_formatter.get_data_into_format,
216
+ Config.Digatron_4_20_6_236: digatron_4_20_6_236_formatter.get_data_into_format,
217
+ Config.Digatron_EIS_4_20_6_236: digatron_eis_4_20_6_236_formatter.get_data_into_format,
218
+ Config.BaSyTec_6_3_1_0: basytec_6_3_1_0_formatter.get_data_into_format,
219
+ Config.Arbin_8_00_PV221201: arbin_8_00_PV221201_formatter.get_data_into_format,
220
+ Config.Arbin_4_23_PV090331: arbin_4_23_PV090331_formatter.get_data_into_format,
221
+ }
222
+
223
+
224
+ class DataOutputFiletype(Enum):
225
+ parquet = auto()
226
+ csv = auto()
227
+ xlsx = auto()
@@ -0,0 +1 @@
1
+ PANDAS_EXCEL_ENGINE = "calamine"