diff --git a/src/dcmReader/dcm_characteristic_line.py b/src/dcmReader/dcm_characteristic_line.py deleted file mode 100644 index 0e7948f..0000000 --- a/src/dcmReader/dcm_characteristic_line.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Definition of DCM characteristic line -""" -from dataclasses import dataclass - - -@dataclass -class DcmCharacteristicLine: - """Definition of a characteristic line - - Attributes: - name (str): Name of the characteristic line - description (str): Description of the characteristic line, started by LANGNAME in DCM - display_name (str): Characteristic line name according asam-2mc, started by DISPLAYNAME in DCM - variants (dict): Variants for the characteristic line, started by VAR in DCM - function (str): Name of the assigned function, started by FUNKTION in DCM - unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM - unit_values (str): Unit of the values, started by EINHEIT_W in DCM - values (dict): Dict of values of the parameter, KEYs are retrieved from ST/X, - values are retrieved from WERT - x_dimension (int): Dimension in x direction of the parameter block - x_mapping (str): Mapping of the x axis to a distribution, if available as a comment in DCM - comment (str): Block comment - """ - - def __init__(self, name) -> None: - self.name = name - self.values = {} - self.description = None - self.display_name = None - self.variants = {} - self.function = None - self.unit_x = None - self.unit_values = None - self.x_dimension = 0 - self.x_mapping = None - self.comment = None - self._type_name = "KENNLINIE" - - def __str__(self): - value = f"{self._type_name} {self.name} {self.x_dimension}\n" - - if self.comment: - for line in self.comment.splitlines(True): - value += f"* {line}" - if self.description: - value += f' LANGNAME "{self.description}"\n' - if self.function: - value += f' FUNKTION "{self.function}"\n' - if self.display_name: - value += f" DISPLAYNAME {self.display_name}\n" - if self.unit_x: - value += f' EINHEIT_X "{self.unit_x}"\n' - if self.unit_values: - value += f' EINHEIT_W "{self.unit_values}"\n' - if self.x_mapping: - value += f'*SSTX {self.x_mapping}\n' - if self.values: - x_entries = "" - value_entries = "" - for x_entry, value_entry in self.values.items(): - x_entries += f"{str(x_entry)} " - value_entries += f"{str(value_entry)} " - value += f' ST/X {x_entries.strip()}\n' - value += f' WERT {value_entries.strip()}\n' - for var_name, var_value in self.variants.items(): - value += f" VAR {var_name}={var_value}\n" - - value += "END" - - return value - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_characteristic_map.py b/src/dcmReader/dcm_characteristic_map.py deleted file mode 100644 index 648e17c..0000000 --- a/src/dcmReader/dcm_characteristic_map.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Definition of DCM characteristic map -""" -from dataclasses import dataclass - - -@dataclass -class DcmCharacteristicMap: - """Definition of a characteristic map - - Attributes: - name (str): Name of the characteristic map - description (str): Description of the characteristic map, started by LANGNAME in DCM - display_name (str): Characteristic map name according asam-2mc, started by DISPLAYNAME in DCM - variants (dict): Variants for the characteristic map, started by VAR in DCM - function (str): Name of the assigned function, started by FUNKTION in DCM - unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM - unit_y (str): Unit of the y axis values, started by EINHEIT_Y in DCM - unit_values (str): Unit of the values, started by EINHEIT_W in DCM - values (dict): 2D Dict of values of the parameter - The inner dict contains the values from ST/X as keys and the - values retrieved from WERT as values. The keys of the outer dict - contains the values from ST/Y. - x_dimension (int): Dimension in x direction of the characteristic maps - y_dimension (int): Dimension in y direction of the characteristic maps - x_mapping (str): Mapping of the x axis to a distribution, if available as a comment in DCM - y_mapping (str): Mapping of the y axis to a distribution, if available as a comment in DCM - comment (str): Block comment - """ - - def __init__(self, name) -> None: - self.name = name - self.values = {} - self.description = None - self.display_name = None - self.variants = {} - self.function = None - self.unit_x = None - self.unit_y = None - self.unit_values = None - self.x_dimension = 0 - self.y_dimension = 0 - self.x_mapping = None - self.y_mapping = None - self.comment = None - self._type_name = "KENNFELD" - - def __str__(self): - value = f"{self._type_name} {self.name} {self.x_dimension} {self.y_dimension}\n" - - if self.comment: - for line in self.comment.splitlines(True): - value += f"* {line}" - if self.description: - value += f' LANGNAME "{self.description}"\n' - if self.function: - value += f' FUNKTION "{self.function}"\n' - if self.display_name: - value += f" DISPLAYNAME {self.display_name}\n" - if self.unit_x: - value += f' EINHEIT_X "{self.unit_x}"\n' - if self.unit_y: - value += f' EINHEIT_Y "{self.unit_y}"\n' - if self.unit_values: - value += f' EINHEIT_W "{self.unit_values}"\n' - if self.x_mapping: - value += f'*SSTX {self.x_mapping}\n' - if self.y_mapping: - value += f'*SSTY {self.y_mapping}\n' - stx_written = False - for y_entry, map_values in self.values.items(): - x_entries = "" - value_entries = "" - for x_entry, value_entry in map_values.items(): - x_entries += f"{str(x_entry)} " - value_entries += f"{str(value_entry)} " - if not stx_written: - value += f' ST/X {x_entries.strip()}\n' - stx_written = True - value += f' ST/Y {str(y_entry).strip()}\n' - value += f' WERT {value_entries.strip()}\n' - for var_name, var_value in self.variants.items(): - value += f" VAR {var_name}={var_value}\n" - - value += "END" - - return value - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_distribution.py b/src/dcmReader/dcm_distribution.py deleted file mode 100644 index 6da8fde..0000000 --- a/src/dcmReader/dcm_distribution.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Definition of DCM distribution -""" -from dataclasses import dataclass - - -@dataclass -class DcmDistribution: - """Definition of a distribution - - Attributes: - name (str): Name of the distribution - description (str): Description of the distribution, started by LANGNAME in DCM - display_name (str): Distribution name according asam-2mc, started by DISPLAYNAME in DCM - variants (dict): Variants for the distribution, started by VAR in DCM - function (str): Name of the assigned function, started by FUNKTION in DCM - unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM - values (list): List of values of the distribution, values are retrieved from WERT - x_dimension (int): Dimension in x direction of the distribution - comment (str): Block comment - """ - - def __init__(self, name) -> None: - self.name = name - self.values = [] - self.description = None - self.display_name = None - self.variants = {} - self.function = None - self.unit_x = None - self.x_dimension = 0 - self.comment = None - - def __str__(self): - value = f"STUETZSTELLENVERTEILUNG {self.name} {str(self.x_dimension)}\n" - - if self.comment: - for line in self.comment.splitlines(True): - value += f"* {line}" - if self.description: - value += f' LANGNAME "{self.description}"\n' - if self.function: - value += f' FUNKTION "{self.function}"\n' - if self.display_name: - value += f" DISPLAYNAME {self.display_name}\n" - if self.unit_x: - value += f' EINHEIT_X "{self.unit_x}"\n' - if self.values: - x_entries = "" - for x_entry in self.values: - x_entries += f"{str(x_entry)} " - value += f' ST/X {x_entries.strip()}\n' - for var_name, var_value in self.variants.items(): - value += f" VAR {var_name}={var_value}\n" - - value += "END" - - return value - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_fixed_characteristic_line.py b/src/dcmReader/dcm_fixed_characteristic_line.py deleted file mode 100644 index 64ddfa4..0000000 --- a/src/dcmReader/dcm_fixed_characteristic_line.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Definition of DCM fixed characteristic line -""" - -from dataclasses import dataclass -from dcmReader.dcm_characteristic_line import DcmCharacteristicLine - - -@dataclass -class DcmFixedCharacteristicLine(DcmCharacteristicLine): - """Definition of a fixed characteristic line, derived from characteristic line""" - - def __init__(self, name) -> None: - super().__init__(name) - self._type_name = "FESTKENNLINIE" - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_fixed_characteristic_map.py b/src/dcmReader/dcm_fixed_characteristic_map.py deleted file mode 100644 index 88423de..0000000 --- a/src/dcmReader/dcm_fixed_characteristic_map.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Definition of DCM fixed characteristic map -""" - -from dataclasses import dataclass -from dcmReader.dcm_characteristic_map import DcmCharacteristicMap - - -@dataclass -class DcmFixedCharacteristicMap(DcmCharacteristicMap): - """Definition of a fixed characteristic map, derived from characteristic map""" - - def __init__(self, name) -> None: - super().__init__(name) - self._type_name = "FESTKENNFELD" - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_function.py b/src/dcmReader/dcm_function.py deleted file mode 100644 index 08e7373..0000000 --- a/src/dcmReader/dcm_function.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Definition of DCM function""" -from dataclasses import dataclass - - -@dataclass -class DcmFunction: - """Definition of a function - - Attributes: - name (str): Name of the function - version (str): Version number of the function - description (str): Description of the function - """ - - name = None - description = None - version = None - - def __init__(self, name, version, description): - self.name = name - self.version = version - self.description = description - - def __str__(self): - return f'FKT {self.name} "{self.version}" "{self.description}"' - - def __lt__(self, other): - return self.name < other.name diff --git a/src/dcmReader/dcm_group_characteristic_line.py b/src/dcmReader/dcm_group_characteristic_line.py deleted file mode 100644 index a518187..0000000 --- a/src/dcmReader/dcm_group_characteristic_line.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Definition of DCM group characteristic line -""" -from dataclasses import dataclass -from dcmReader.dcm_characteristic_line import DcmCharacteristicLine - - -@dataclass -class DcmGroupCharacteristicLine(DcmCharacteristicLine): - """Definition of a group characteristic line, derived from characteristic line""" - - def __init__(self, name) -> None: - super().__init__(name) - self._type_name = "GRUPPENKENNLINIE" - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_group_characteristic_map.py b/src/dcmReader/dcm_group_characteristic_map.py deleted file mode 100644 index 4813dd1..0000000 --- a/src/dcmReader/dcm_group_characteristic_map.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Definition of DCM fixed characteristic map -""" -from dataclasses import dataclass -from dcmReader.dcm_characteristic_map import DcmCharacteristicMap - - -@dataclass -class DcmGroupCharacteristicMap(DcmCharacteristicMap): - """Definition of a group characteristic map, derived from characteristic map""" - - def __init__(self, name) -> None: - super().__init__(name) - self._type_name = "GRUPPENKENNFELD" - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_parameter.py b/src/dcmReader/dcm_parameter.py deleted file mode 100644 index 96a417c..0000000 --- a/src/dcmReader/dcm_parameter.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Definition of DCM parameter""" -from dataclasses import dataclass - - -@dataclass -class DcmParameter: - """Definition of a parameter - - Attributes: - name (str): Name of the parameter - description (str): Description of the parameter, started by LANGNAME in DCM - display_name (str): Parameter name according asam-2mc, started by DISPLAYNAME in DCM - variants (dict): Variants for the parameter, started by VAR in DCM - function (str): Name of the assigned function, started by FUNKTION in DCM - unit (str): Unit of the parameter, started by EINHEIT_W in DCM - value (float/int): Value of the parameter, started by WERT in DCM - text (str): Alternative text-value, started by TEXT in DCM - comment (str): Block comment - """ - - def __init__(self, name): - self.name = name - self.value = None - self.description = None - self.display_name = None - self.variants = {} - self.function = None - self.unit = None - self.text = None - self.comment = None - - def __str__(self): - value = f"FESTWERT {self.name}\n" - - if self.comment: - for line in self.comment.splitlines(True): - value += f"* {line}" - if self.description: - value += f' LANGNAME "{self.description}"\n' - if self.function: - value += f' FUNKTION "{self.function}"\n' - if self.display_name: - value += f" DISPLAYNAME {self.display_name}\n" - if self.unit: - value += f' EINHEIT_W "{self.unit}"\n' - if self.value: - value += f" WERT {self.value}\n" - if self.text: - value += f' TEXT "{self.text}"\n' - - for var_name, var_value in self.variants.items(): - if self.value: - value += f" VAR {var_name}={var_value}\n" - else: - value += f' VAR {var_name}="{var_value}"\n' - value += "END" - - return value - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_parameter_block.py b/src/dcmReader/dcm_parameter_block.py deleted file mode 100644 index 2598215..0000000 --- a/src/dcmReader/dcm_parameter_block.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Definition of DCM parameter block -""" -from dataclasses import dataclass - - -@dataclass -class DcmParameterBlock: - """Definition of a block parameter - - Attributes: - name (str): Name of the block parameter - description (str): Description of the block parameter, started by LANGNAME in DCM - display_name (str): Block parameter name according asam-2mc, started by DISPLAYNAME in DCM - variants (dict): Variants for the block parameter, started by VAR in DCM - function (str): Name of the assigned function, started by FUNKTION in DCM - unit (str): Unit of the block parameters, started by EINHEIT_W in DCM - values (list): List of values of the block parameter, started by WERT in DCM - x_dimension (int): Dimension in x direction of the block parameter - y_dimension (int): Dimension in y direction of the block parameter - comment (str): Block comment - """ - - def __init__(self, name): - self.name = name - self.values = [] - self.description = None - self.display_name = None - self.variants = {} - self.function = None - self.unit = None - self.x_dimension = 0 - self.y_dimension = 0 - self.comment = None - - def __str__(self): - value = f"FESTWERTEBLOCK {self.name} {self.x_dimension}" - if self.y_dimension > 1: - value += f" @ {self.y_dimension}\n" - else: - value += "\n" - - if self.comment: - for line in self.comment.splitlines(True): - value += f"* {line}" - if self.description: - value += f' LANGNAME "{self.description}"\n' - if self.function: - value += f' FUNKTION "{self.function}"\n' - if self.display_name: - value += f" DISPLAYNAME {self.display_name}\n" - if self.unit: - value += f' EINHEIT_W "{self.unit}"\n' - if self.values: - for entry in self.values: - value += f' WERT {" ".join([str(x) for x in entry])}\n' - - for var_name, var_value in self.variants.items(): - value += f" VAR {var_name}={var_value}\n" - - value += "END" - - return value - - def __lt__(self, other): - return self.function < other.function and self.description < other.description diff --git a/src/dcmReader/dcm_reader.py b/src/dcmReader/dcm_reader.py index 8dfeac4..10ef694 100644 --- a/src/dcmReader/dcm_reader.py +++ b/src/dcmReader/dcm_reader.py @@ -1,74 +1,134 @@ """ DCMReader which handles data parsing. """ +from __future__ import annotations import os import re import logging -from dcmReader.dcm_parameter import DcmParameter -from dcmReader.dcm_function import DcmFunction -from dcmReader.dcm_parameter_block import DcmParameterBlock -from dcmReader.dcm_characteristic_line import DcmCharacteristicLine -from dcmReader.dcm_fixed_characteristic_line import DcmFixedCharacteristicLine -from dcmReader.dcm_group_characteristic_line import DcmGroupCharacteristicLine -from dcmReader.dcm_characteristic_map import DcmCharacteristicMap -from dcmReader.dcm_fixed_characteristic_map import DcmFixedCharacteristicMap -from dcmReader.dcm_group_characteristic_map import DcmGroupCharacteristicMap -from dcmReader.dcm_distribution import DcmDistribution +from collections import defaultdict +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, TypeVar, Any, Generic, Protocol + +import numpy as np + +from dcmReader.elements import ( + DcmFunction, + # DcmVariantCoding, + # DcmModuleHeader, + DcmParameter, + DcmParameterBlock, + DcmCharacteristicLine, + DcmFixedCharacteristicLine, + DcmGroupCharacteristicLine, + DcmCharacteristicMap, + DcmFixedCharacteristicMap, + DcmGroupCharacteristicMap, + DcmDistribution, +) +from dcmReader.utils import _COMMENT_QUALIFIER, _SETTINGS + +if TYPE_CHECKING: + from io import TextIOWrapper + + from dcmReader.utils import _DcmBase + + T_Element = TypeVar("T_Element", bound=_DcmBase) logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG) logger = logging.getLogger(__name__) +@dataclass class DcmReader: """Parser for the DCM (Data Conservation Format) format used by e.g. Vector, ETAS,...""" - def __init__(self): - self._file_header = "" - self._file_header_finished = False - self._functions_list = [] - self._parameter_list = [] - self._block_parameter_list = [] - self._characteristic_line_list = [] - self._fixed_characteristic_line_list = [] - self._group_characteristic_line_list = [] - self._characteristic_map_list = [] - self._fixed_characteristic_map_list = [] - self._group_characteristic_map_list = [] - self._distribution_list = [] - - def parse_variant(self, line): - """Parses a variant field + _functions_list: list[DcmFunction] = field(repr=False, default_factory=list) + _parameter_list: list[DcmParameter] = field(repr=False, default_factory=list) + _block_parameter_list: list[DcmParameterBlock] = field(repr=False, default_factory=list) + _characteristic_line_list: list[DcmCharacteristicLine] = field(repr=False, default_factory=list) + _fixed_characteristic_line_list: list[DcmFixedCharacteristicLine] = field(repr=False, default_factory=list) + _group_characteristic_line_list: list[DcmGroupCharacteristicLine] = field(repr=False, default_factory=list) + _characteristic_map_list: list[DcmCharacteristicMap] = field(repr=False, default_factory=list) + _fixed_characteristic_map_list: list[DcmFixedCharacteristicMap] = field(repr=False, default_factory=list) + _group_characteristic_map_list: list[DcmGroupCharacteristicMap] = field(repr=False, default_factory=list) + _distribution_list: list[DcmDistribution] = field(repr=False, default_factory=list) + _data: dict[str, _DcmBase] = field(repr=False, default_factory=dict) + attrs: dict = field(default_factory=dict) + + def __post_init__(self) -> None: + self.parser_methods = _SETTINGS + + def _parse_wert(self, line: str, *, element: T_Element, **kwargs) -> None: + element.values = np.concatenate([element.values, self._parse_block_parameters(line)]) + + def _parse_text(self, line: str, *, element: T_Element, **kwargs) -> None: + parameters = line.split(None, 1)[1] + parameters_list = [s.strip("\"'") for s in parameters.split()] + element.values = np.concatenate([element.values, parameters_list]) + + def _parse_coord_x(self, line: str, *, coord_x: list[float], **kwargs) -> None: + coord_x.extend(self._parse_block_parameters(line)) + + def _parse_coord_y(self, line: str, *, coord_y: list[float], **kwargs) -> None: + self._parse_coord_x(line, coord_x=coord_y) + + def _parse_comment(self, line: str, *, element: T_Element, **kwargs) -> None: + # TODO: Should the comment remember which row? So it can be reprinted there? + + line_no_cq = line[1:].strip() + k = line_no_cq.split(None, 1)[0] + + p = self.parser_methods.get(k, None) + if p is not None: + # The comment has a known keyword, parse it accordingly: + parsed_values = p["parse_method"](self)(line_no_cq, attrs=element.attrs, **kwargs) + + # Optionally store in attrs, otherwise assume it's + # stored within the method: + if p["parse_key"]: + element.attrs[p["parse_key"]] = parsed_values + else: + cmnt_prev = element.attrs.get("comment", "") + cmnts = [cmnt_prev, line_no_cq] if cmnt_prev else [line_no_cq] + element.attrs["comment"] = "\n".join(cmnts) + + def _parse_string(self, line: str, **kwargs) -> str: + """Parses a text field Args: line (str): One line of the DCM file + Returns: + Parsed text field + """ + return line.split(None, 1)[1].strip(' "') + + def _parse_variant(self, line: str, **kwargs) -> dict: + """Parses a variant field + Args: + line (str): One line of the DCM file Returns: Parsed variant either as float/int if variant is value or as str if variant is text field """ variant = re.search(r"VAR\s+(.*?)=(.*)", line.strip()) - value = None + if variant is None: + return {} + + key = str(variant.group(1)).strip() + value_str = str(variant.group(2)).strip() + value: str | float | None = None try: - value = self.convert_value(str(variant.group(2)).strip()) + # Check if it's number + value = self._convert_value(value_str) except ValueError: - value = str(variant.group(2)).strip('" ') - return {str(variant.group(1)).strip(): value} - - @staticmethod - def parse_string(line): - """Parses a text field + value = value_str.strip('" ') - Args: - line (str): One line of the DCM file - - Returns: - Parsed text field - """ - return line.split(" ", 1)[1].strip(' "') + return {key: value} - def parse_block_parameters(self, line): + def _parse_block_parameters(self, line: str, **kwargs) -> list: """Parses a block parameters line Args: @@ -77,12 +137,11 @@ def parse_block_parameters(self, line): Returns: Parsed block parameters as list """ - parameters = line.split(" ", 1)[1] - parameters = " ".join(parameters.split()).split() - return [self.convert_value(i) for i in parameters] + parameters = line.split(None, 1)[1] + parameters_list = " ".join(parameters.split()).split() + return [self._convert_value(i) for i in parameters_list] - @staticmethod - def convert_value(value): + def _convert_value(self, value: str) -> float: """Converts a text value to the correct number Args: @@ -103,6 +162,102 @@ def convert_value(value): except ValueError as err: raise ValueError(f"Cannot convert {value} from string to number.") from err + def _parse_elements(self, DcmElement: type[T_Element], line_intro: str, dcm_file: TextIOWrapper) -> T_Element: + element_syntax, name, *shape_rev = line_intro.split() + element = DcmElement(name=name, element_syntax=element_syntax) + + # Reverse the order of x and y to match numpy: + shape_rev.reverse() + shape: tuple[int, ...] = tuple(int(v) for v in shape_rev if v != "@") + coord_y: list[float] = [] + coord_x: list[float] = [] + + while True: + line = dcm_file.readline().strip() + + # Get the first keyword: + keyword = line.split(None, 1)[0] + + if keyword.startswith("END"): + if element_syntax == "STUETZSTELLENVERTEILUNG": + element.values = np.asarray(coord_x) + element.attrs[_SETTINGS["SSTX"]["key_eng"]] = element.name + + # Handle coords and dims: + xy = ("x", "y") + k_axis = tuple(_SETTINGS[f"SST{v.upper()}"]["key_eng"] for v in xy) + crds_rev = (coord_x, coord_y) + dims_rev = [] + coords_rev = [] + for i, v in enumerate(reversed(shape)): + dims_rev.append(element.attrs.get(k_axis[i], f"{element.name}_{xy[i]}")) + if crds_rev[i]: + coords_rev.append(crds_rev[i]) + element.dims = tuple(reversed(dims_rev)) + element.coords = tuple(np.array(v) for v in reversed(coords_rev)) + + element.values = element.values.reshape(shape) + + break + + else: + # Get parser settings for this keyword: + p = self.parser_methods.get(keyword, None) + + if p is None: + # Is it a comment? + p = self.parser_methods.get(keyword[0], None) + + if p is not None: + # Parse the line: + parsed_values = p["parse_method"](self)(line, coord_x=coord_x, coord_y=coord_y, element=element) + + # Optionally store in attrs, otherwise assume it's + # stored within the method: + if p["parse_key"]: + element.attrs[p["parse_key"]] = parsed_values + else: + logger.warning(f"Unknown parameter field: {line=}{keyword=}") + + return element + + def _parse_elements_funktionen( + self, DcmElement: type[T_Element], line_intro: str, dcm_file: TextIOWrapper + ) -> list[T_Element]: + # element_syntax = line_intro.strip() + + elements = [] + while True: + line = dcm_file.readline().strip() + + # Get the first keyword: + keyword = line.split(None, 1)[0] + + if keyword.startswith("END"): + break + else: + function_match = re.search(r"FKT (.*?)(?: \"(.*?)?\"(?: \"(.*?)?\")?)?$", line.strip()) + element_syntax_short = "FKT" + + fm: dict[str, str] = {"name": "", "version": "", "description": ""} + for i, (k, v) in enumerate(fm.items()): + if function_match is not None: + fm[k] = function_match.group(i + 1) + + element = DcmElement(name=fm["name"], element_syntax=element_syntax_short) + element.attrs["version"] = fm["version"] + element.attrs["description"] = fm["description"] + element.attrs["_function"] = "FUNKTIONEN" + + # Add an empty array because you cannot init with + # np.array([]).shape=(0,) and not the correct (), np.concatenate doesn't + # work with shape=() though so can't use that as init either: + element.values = np.empty((), dtype=object) + + elements.append(element) + + return elements + def write(self, file) -> None: """Writes the current DCM object to a dcm file @@ -121,32 +276,31 @@ def read(self, file) -> None: Args: file(str): DCM file to parse """ - _dcm_format = None - - comment_qualifier = ("!", "*", ".") - + file_header_finished = False + self.attrs["filename"] = file with open(file, "r", encoding="utf-8") as dcm_file: for line in dcm_file: # Remove whitespaces line = line.strip() # Check if line is comment - if line.startswith(comment_qualifier): - if not self._file_header_finished: - self._file_header = self._file_header + line[1:].strip() + os.linesep + if line.startswith(_COMMENT_QUALIFIER): + if not file_header_finished: + self.attrs["file_header"] = f"{self.attrs.get('file_header', '')}{line[1:].strip()}\n" continue # At this point first comment block passed - self._file_header_finished = True + file_header_finished = True # Check if empty line if line == "": continue # Check if format version line - if _dcm_format is None: - if line.startswith("KONSERVIERUNG_FORMAT"): - _dcm_format = float(re.search(r"(\d\.\d)", line.strip()).group(1)) + if self.attrs.get("dcm_format", None) is None: + kf, kf_value = line.split() + if kf == "KONSERVIERUNG_FORMAT": + self.attrs["dcm_format"] = float(kf_value) continue logging.info("Found line: %s", line) @@ -154,511 +308,72 @@ def read(self, file) -> None: # Check if functions start if line.startswith("FUNKTIONEN"): - while True: - line = dcm_file.readline() - if line.startswith("END"): - break - function_match = re.search(r"FKT (.*?)(?: \"(.*?)?\"(?: \"(.*?)?\")?)?$", line.strip()) - self._functions_list.append( - DcmFunction( - function_match.group(1), - function_match.group(2), - function_match.group(3), - ) - ) + self._functions_list.extend(self._parse_elements_funktionen(DcmFunction, line, dcm_file)) # Check if parameter starts elif line.startswith("FESTWERT "): - name = self.parse_string(line) - found_parameter = DcmParameter(name) - while True: - line = dcm_file.readline().strip() - - if line.startswith("END"): - break - - if line.startswith("LANGNAME"): - found_parameter.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_parameter.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_parameter.function = self.parse_string(line) - elif line.startswith("WERT"): - found_parameter.value = self.convert_value(line.split(" ", 1)[1].strip()) - elif line.startswith("EINHEIT_W"): - found_parameter.unit = self.parse_string(line) - elif line.startswith("VAR"): - found_parameter.variants.update(self.parse_variant(line)) - elif line.startswith("TEXT"): - found_parameter.text = self.parse_string(line) - elif line.startswith(comment_qualifier): - if found_parameter.comment is None: - found_parameter.comment = line[1:].strip() + os.linesep - else: - found_parameter.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._parameter_list.append(found_parameter) + self._parameter_list.append(self._parse_elements(DcmParameter, line, dcm_file)) # Check if parameter block start elif line.startswith("FESTWERTEBLOCK"): - block_data = re.search(r"FESTWERTEBLOCK\s+(.*?)\s+(\d+)(?:\s+\@\s+(\d+))?", line.strip()) - found_block_parameter = DcmParameterBlock(block_data.group(1)) - found_block_parameter.x_dimension = self.convert_value(block_data.group(2)) - found_block_parameter.y_dimension = ( - self.convert_value(block_data.group(3)) if block_data.group(3) is not None else 1 - ) - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(found_block_parameter.values) != found_block_parameter.y_dimension: - logger.error("Y dimension in %s do not match description!", found_block_parameter.name) - break - - if line.startswith("LANGNAME"): - found_block_parameter.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_block_parameter.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_block_parameter.function = self.parse_string(line) - elif line.startswith("WERT"): - parameters = self.parse_block_parameters(line) - if len(parameters) != found_block_parameter.x_dimension: - logger.error("X dimension in %s do not match description!", found_block_parameter.name) - found_block_parameter.values.append(parameters) - elif line.startswith("EINHEIT_W"): - found_block_parameter.unit = self.parse_string(line) - elif line.startswith("VAR"): - found_block_parameter.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - if found_block_parameter.comment is None: - found_block_parameter.comment = line[1:].strip() + os.linesep - else: - found_block_parameter.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._block_parameter_list.append(found_block_parameter) + self._block_parameter_list.append(self._parse_elements(DcmParameterBlock, line, dcm_file)) # Check if characteristic line elif line.startswith("KENNLINIE"): - re_match = re.search(r"KENNLINIE\s+(.*?)\s+(\d+)", line.strip()) - found_characteristic_line = DcmCharacteristicLine(re_match.group(1)) - found_characteristic_line.x_dimension = self.convert_value(re_match.group(2)) - parameters = [] - stx = [] - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(stx) != found_characteristic_line.x_dimension: - logger.error("X dimension in %s \ - do not match description!", found_characteristic_line.name) - if len(parameters) != found_characteristic_line.x_dimension: - logger.error( - "Values dimension in %s \ - do not match description!", found_characteristic_line.name - ) - found_characteristic_line.values = dict(zip(stx, parameters)) - break - - if line.startswith("LANGNAME"): - found_characteristic_line.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_characteristic_line.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_characteristic_line.function = self.parse_string(line) - elif line.startswith("WERT"): - parameters.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("EINHEIT_W"): - found_characteristic_line.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_characteristic_line.unit_x = self.parse_string(line) - elif line.startswith("VAR"): - found_characteristic_line.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match = re.search(r"SSTX\s+(.*)", line) - if re_match: - found_characteristic_line.x_mapping = re_match.group(1) - else: - if found_characteristic_line.comment is None: - found_characteristic_line.comment = line[1:].strip() + os.linesep - else: - found_characteristic_line.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._characteristic_line_list.append(found_characteristic_line) + self._characteristic_line_list.append(self._parse_elements(DcmCharacteristicLine, line, dcm_file)) # Check if fixed characteristic line elif line.startswith("FESTKENNLINIE"): - re_match = re.search(r"FESTKENNLINIE\s+(.*?)\s+(\d+)", line.strip()) - found_fixed_characteristic_line = DcmFixedCharacteristicLine(re_match.group(1)) - found_fixed_characteristic_line.x_dimension = self.convert_value(re_match.group(2)) - parameters = [] - stx = [] - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(stx) != found_fixed_characteristic_line.x_dimension: - logger.error( - "X dimension in %s do not match description!", found_fixed_characteristic_line.name - ) - if len(parameters) != found_fixed_characteristic_line.x_dimension: - logger.error( - "Values dimension in %s \ - do not match description!", found_fixed_characteristic_line.name - ) - found_fixed_characteristic_line.values = dict(zip(stx, parameters)) - break - - if line.startswith("LANGNAME"): - found_fixed_characteristic_line.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_fixed_characteristic_line.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_fixed_characteristic_line.function = self.parse_string(line) - elif line.startswith("WERT"): - parameters.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("EINHEIT_W"): - found_fixed_characteristic_line.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_fixed_characteristic_line.unit_x = self.parse_string(line) - elif line.startswith("VAR"): - found_fixed_characteristic_line.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match = re.search(r"SSTX\s+(.*)", line) - if re_match: - found_fixed_characteristic_line.x_mapping = re_match.group(1) - else: - if found_fixed_characteristic_line.comment is None: - found_fixed_characteristic_line.comment = line[1:].strip() + os.linesep - else: - found_fixed_characteristic_line.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._fixed_characteristic_line_list.append(found_fixed_characteristic_line) + self._fixed_characteristic_line_list.append( + self._parse_elements(DcmFixedCharacteristicLine, line, dcm_file) + ) # Check if group characteristic line elif line.startswith("GRUPPENKENNLINIE"): - re_match = re.search(r"GRUPPENKENNLINIE\s+(.*?)\s+(\d+)", line.strip()) - found_group_characteristic_line = DcmGroupCharacteristicLine(re_match.group(1)) - found_group_characteristic_line.x_dimension = self.convert_value(re_match.group(2)) - parameters = [] - stx = [] - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(parameters) != found_group_characteristic_line.x_dimension: - logger.error( - "Values dimension in %s \ - do not match description!", found_group_characteristic_line.name - ) - if len(stx) != found_group_characteristic_line.x_dimension: - logger.error( - "X dimension in %s \ - do not match description!", found_group_characteristic_line.name - ) - found_group_characteristic_line.values = dict(zip(stx, parameters)) - break - - if line.startswith("LANGNAME"): - found_group_characteristic_line.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_group_characteristic_line.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_group_characteristic_line.function = self.parse_string(line) - elif line.startswith("WERT"): - parameters.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("EINHEIT_W"): - found_group_characteristic_line.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_group_characteristic_line.unit_x = self.parse_string(line) - elif line.startswith("VAR"): - found_group_characteristic_line.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match = re.search(r"SSTX\s+(.*)", line) - if re_match: - found_group_characteristic_line.x_mapping = re_match.group(1) - else: - if found_group_characteristic_line.comment is None: - found_group_characteristic_line.comment = line[1:].strip() + os.linesep - else: - found_group_characteristic_line.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._group_characteristic_line_list.append(found_group_characteristic_line) + self._group_characteristic_line_list.append( + self._parse_elements(DcmGroupCharacteristicLine, line, dcm_file) + ) # Check for characteristic map elif line.startswith("KENNFELD "): - re_match = re.search(r"KENNFELD\s+(.*?)\s+(\d+)\s+(\d+)", line.strip()) - found_characteristic_map = DcmCharacteristicMap(re_match.group(1)) - found_characteristic_map.x_dimension = self.convert_value(re_match.group(2)) - found_characteristic_map.y_dimension = self.convert_value(re_match.group(3)) - stx = [] - sty = None - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(found_characteristic_map.values) != found_characteristic_map.y_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_characteristic_map.name - ) - if len(stx) != found_characteristic_map.x_dimension: - logger.error("X dimension in %s \ - do not match description!", found_characteristic_map.name) - for name, entry in found_characteristic_map.values.items(): - if len(entry) != found_characteristic_map.x_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_characteristic_map.name - ) - else: - found_characteristic_map.values[name] = dict(zip(stx, entry)) - break - - if line.startswith("LANGNAME"): - found_characteristic_map.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_characteristic_map.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_characteristic_map.function = self.parse_string(line) - elif line.startswith("WERT"): - if stx is None or sty is None: - raise ValueError(f"Values before stx/sty in {found_characteristic_map.name}") - parameters = self.parse_block_parameters(line) - if sty not in found_characteristic_map.values: - found_characteristic_map.values[sty] = [] - found_characteristic_map.values[sty].extend(parameters) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/Y"): - sty = self.convert_value(line.split(" ", 1)[1].strip()) - elif line.startswith("EINHEIT_W"): - found_characteristic_map.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_characteristic_map.unit_x = self.parse_string(line) - elif line.startswith("EINHEIT_Y"): - found_characteristic_map.unit_y = self.parse_string(line) - elif line.startswith("VAR"): - found_characteristic_map.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match_x = re.search(r"SSTX\s+(.*)", line) - re_match_y = re.search(r"SSTY\s+(.*)", line) - if re_match_x: - found_characteristic_map.x_mapping = re_match_x.group(1) - elif re_match_y: - found_characteristic_map.y_mapping = re_match_y.group(1) - else: - if found_characteristic_map.comment is None: - found_characteristic_map.comment = line[1:].strip() + os.linesep - else: - found_characteristic_map.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._characteristic_map_list.append(found_characteristic_map) + self._characteristic_map_list.append(self._parse_elements(DcmCharacteristicMap, line, dcm_file)) # Check for fixed characteristic map elif line.startswith("FESTKENNFELD "): - re_match = re.search(r"FESTKENNFELD\s+(.*?)\s+(\d+)\s+(\d+)", line.strip()) - found_fixed_characteristic_map = DcmFixedCharacteristicMap(re_match.group(1)) - found_fixed_characteristic_map.x_dimension = self.convert_value(re_match.group(2)) - found_fixed_characteristic_map.y_dimension = self.convert_value(re_match.group(3)) - stx = [] - sty = None - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(found_fixed_characteristic_map.values) != found_fixed_characteristic_map.y_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_fixed_characteristic_map.name - ) - if len(stx) != found_fixed_characteristic_map.x_dimension: - logger.error( - "X dimension in %s do not match description!", found_fixed_characteristic_map.name - ) - for name, entry in found_fixed_characteristic_map.values.items(): - if len(entry) != found_fixed_characteristic_map.x_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_fixed_characteristic_map.name - ) - else: - found_fixed_characteristic_map.values[name] = dict(zip(stx, entry)) - break - - if line.startswith("LANGNAME"): - found_fixed_characteristic_map.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_fixed_characteristic_map.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_fixed_characteristic_map.function = self.parse_string(line) - elif line.startswith("WERT"): - if stx is None or sty is None: - raise ValueError(f"Values before stx/sty in {found_fixed_characteristic_map.name}") - parameters = self.parse_block_parameters(line) - if sty not in found_fixed_characteristic_map.values: - found_fixed_characteristic_map.values[sty] = [] - found_fixed_characteristic_map.values[sty].extend(parameters) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/Y"): - sty = self.convert_value(line.split(" ", 1)[1].strip()) - elif line.startswith("EINHEIT_W"): - found_fixed_characteristic_map.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_fixed_characteristic_map.unit_x = self.parse_string(line) - elif line.startswith("EINHEIT_Y"): - found_fixed_characteristic_map.unit_y = self.parse_string(line) - elif line.startswith("VAR"): - found_fixed_characteristic_map.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match_x = re.search(r"SSTX\s+(.*)", line) - re_match_y = re.search(r"SSTY\s+(.*)", line) - if re_match_x: - found_fixed_characteristic_map.x_mapping = re_match_x.group(1) - elif re_match_y: - found_fixed_characteristic_map.y_mapping = re_match_y.group(1) - else: - if found_fixed_characteristic_map.comment is None: - found_fixed_characteristic_map.comment = line[1:].strip() + os.linesep - else: - found_fixed_characteristic_map.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._fixed_characteristic_map_list.append(found_fixed_characteristic_map) + self._fixed_characteristic_map_list.append( + self._parse_elements(DcmFixedCharacteristicMap, line, dcm_file) + ) # Check for group characteristic map elif line.startswith("GRUPPENKENNFELD "): - re_match = re.search(r"GRUPPENKENNFELD\s+(.*?)\s+(\d+)\s+(\d+)", line.strip()) - found_group_characteristic_map = DcmGroupCharacteristicMap(re_match.group(1)) - found_group_characteristic_map.x_dimension = self.convert_value(re_match.group(2)) - found_group_characteristic_map.y_dimension = self.convert_value(re_match.group(3)) - stx = [] - sty = None - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(found_group_characteristic_map.values) != found_group_characteristic_map.y_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_group_characteristic_map.name - ) - if len(stx) != found_group_characteristic_map.x_dimension: - logger.error( - "X dimension in %s do not match description!", found_group_characteristic_map.name - ) - for name, entry in found_group_characteristic_map.values.items(): - if len(entry) != found_group_characteristic_map.x_dimension: - logger.error( - "Values dimension in %s \ - does not match description!", found_group_characteristic_map.name - ) - else: - found_group_characteristic_map.values[name] = dict(zip(stx, entry)) - break - - if line.startswith("LANGNAME"): - found_group_characteristic_map.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_group_characteristic_map.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_group_characteristic_map.function = self.parse_string(line) - elif line.startswith("WERT"): - if stx is None or sty is None: - raise ValueError(f"Values before stx/sty in {found_group_characteristic_map.name}") - parameters = self.parse_block_parameters(line) - if sty not in found_group_characteristic_map.values: - found_group_characteristic_map.values[sty] = [] - found_group_characteristic_map.values[sty].extend(parameters) - elif line.startswith("ST/X"): - stx.extend(self.parse_block_parameters(line)) - elif line.startswith("ST/Y"): - sty = self.convert_value(line.split(" ", 1)[1].strip()) - elif line.startswith("EINHEIT_W"): - found_group_characteristic_map.unit_values = self.parse_string(line) - elif line.startswith("EINHEIT_X"): - found_group_characteristic_map.unit_x = self.parse_string(line) - elif line.startswith("EINHEIT_Y"): - found_group_characteristic_map.unit_y = self.parse_string(line) - elif line.startswith("VAR"): - found_group_characteristic_map.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - re_match_x = re.search(r"SSTX\s+(.*)", line) - re_match_y = re.search(r"SSTY\s+(.*)", line) - if re_match_x: - found_group_characteristic_map.x_mapping = re_match_x.group(1) - elif re_match_y: - found_group_characteristic_map.y_mapping = re_match_y.group(1) - else: - if found_group_characteristic_map.comment is None: - found_group_characteristic_map.comment = line[1:].strip() + os.linesep - else: - found_group_characteristic_map.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._group_characteristic_map_list.append(found_group_characteristic_map) + self._group_characteristic_map_list.append( + self._parse_elements(DcmGroupCharacteristicMap, line, dcm_file) + ) # Check if distribution elif line.startswith("STUETZSTELLENVERTEILUNG"): - re_match = re.search(r"STUETZSTELLENVERTEILUNG\s+(.*?)\s+(\d+)", line.strip()) - found_distribution = DcmDistribution(re_match.group(1)) - found_distribution.x_dimension = self.convert_value(re_match.group(2)) - parameters = None - stx = None - - while True: - line = dcm_file.readline().strip() - if line.startswith("END"): - if len(found_distribution.values) != found_distribution.x_dimension: - logger.error("X dimension in %s do not match description!", found_distribution.name) - break - - if line.startswith("LANGNAME"): - found_distribution.description = self.parse_string(line) - elif line.startswith("DISPLAYNAME"): - found_distribution.display_name = self.parse_string(line) - elif line.startswith("FUNKTION"): - found_distribution.function = self.parse_string(line) - elif line.startswith("ST/X"): - found_distribution.values.extend(self.parse_block_parameters(line)) - elif line.startswith("EINHEIT_X"): - found_distribution.unit_x = self.parse_string(line) - elif line.startswith("VAR"): - found_distribution.variants.update(self.parse_variant(line)) - elif line.startswith(comment_qualifier): - if found_distribution.comment is None: - found_distribution.comment = line[1:].strip() + os.linesep - else: - found_distribution.comment += line[1:].strip() + os.linesep - else: - logger.warning("Unknown parameter field: %s", line) - - self._distribution_list.append(found_distribution) + self._distribution_list.append(self._parse_elements(DcmDistribution, line, dcm_file)) # Unknown start of line else: logger.warning("Unknown line detected\n%s", line) + _all = ( + self._parameter_list + + self._block_parameter_list + + self._characteristic_line_list + + self._fixed_characteristic_line_list + + self._group_characteristic_line_list + + self._characteristic_map_list + + self._fixed_characteristic_map_list + + self._group_characteristic_map_list + + self._distribution_list + ) + for v in _all: + if self._data.get(v.name) is not None: + raise NotImplementedError("Duplicated names not supported?") + self._data[v.name] = v + def get_functions(self) -> list: """Returns all found functions as a list""" return self._functions_list @@ -702,7 +417,7 @@ def get_distributions(self) -> list: def __str__(self) -> str: output_string = "" # Print the file header - for line in self._file_header.splitlines(True): + for line in self.attrs.get("file_header", "").splitlines(True): output_string += f"* {line}" # Print the file version @@ -715,7 +430,7 @@ def __str__(self) -> str: output_string += "END\n\n" # Print rest of DCM objects - object_list = [] + object_list: list[_DcmBase] = [] object_list.extend(self._parameter_list) object_list.extend(self._block_parameter_list) object_list.extend(self._characteristic_line_list) @@ -730,3 +445,6 @@ def __str__(self) -> str: output_string += f"\n{item}\n" return output_string + + def __getitem__(self, key): + return self._data[key] diff --git a/src/dcmReader/elements.py b/src/dcmReader/elements.py new file mode 100644 index 0000000..8cd94cd --- /dev/null +++ b/src/dcmReader/elements.py @@ -0,0 +1,190 @@ +""" +Definition of DCM characteristic line +""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from dcmReader.utils import _DcmBase + + +def _attrs_init() -> dict: + return {} + # return { + # "description": "", + # "display_name": "", + # "variants": {}, + # "function": "", + # "units_x": "", + # "units_y": "", + # "units": "", + # } + + +@dataclass +class DcmFunction(_DcmBase): + """Definition of a function + + Attributes: + name (str): Name of the function + version (str): Version number of the function + description (str): Description of the function + """ + + attrs: dict = field(default_factory=_attrs_init) + + def _print_dcm_format(self) -> str: + version = self.attrs.get("version", "") + description = self.attrs.get("description", "") + return f'{self.element_syntax} {self.name} "{version}" "{description}"' + + +@dataclass +class DcmVariantCoding(_DcmBase): + """Definition of a function + + Attributes: + name (str): Name of the function + version (str): Version number of the function + description (str): Description of the function + """ + + attrs: dict = field(default_factory=_attrs_init) + + def _print_dcm_format(self) -> str: + raise NotImplementedError + + +@dataclass +class DcmModuleHeader(_DcmBase): + """Definition of a function + + Attributes: + name (str): Name of the function + version (str): Version number of the function + description (str): Description of the function + """ + + attrs: dict = field(default_factory=_attrs_init) + + def _print_dcm_format(self) -> str: + raise NotImplementedError + + +@dataclass +class DcmParameter(_DcmBase): + """Definition of a parameter + + Attributes: + name (str): Name of the parameter + description (str): Description of the parameter, started by LANGNAME in DCM + display_name (str): Parameter name according asam-2mc, started by DISPLAYNAME in DCM + variants (dict): Variants for the parameter, started by VAR in DCM + function (str): Name of the assigned function, started by FUNKTION in DCM + unit (str): Unit of the parameter, started by EINHEIT_W in DCM + value (float/int): Value of the parameter, started by WERT in DCM + text (str): Alternative text-value, started by TEXT in DCM + comment (str): Block comment + """ + + attrs: dict = field(default_factory=_attrs_init) + + +@dataclass +class DcmParameterBlock(_DcmBase): + """Definition of a parameter + + Attributes: + name (str): Name of the parameter + description (str): Description of the parameter, started by LANGNAME in DCM + display_name (str): Parameter name according asam-2mc, started by DISPLAYNAME in DCM + variants (dict): Variants for the parameter, started by VAR in DCM + function (str): Name of the assigned function, started by FUNKTION in DCM + unit (str): Unit of the parameter, started by EINHEIT_W in DCM + value (float/int): Value of the parameter, started by WERT in DCM + text (str): Alternative text-value, started by TEXT in DCM + comment (str): Block comment + """ + + attrs: dict = field(default_factory=_attrs_init) + + +@dataclass +class DcmCharacteristicLine(_DcmBase): + """Definition of a characteristic line + + Attributes: + name (str): Name of the characteristic line + description (str): Description of the characteristic line, started by LANGNAME in DCM + display_name (str): Characteristic line name according asam-2mc, started by DISPLAYNAME in DCM + variants (dict): Variants for the characteristic line, started by VAR in DCM + function (str): Name of the assigned function, started by FUNKTION in DCM + unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM + unit_values (str): Unit of the values, started by EINHEIT_W in DCM + values (dict): Dict of values of the parameter, KEYs are retrieved from ST/X, + values are retrieved from WERT + x_dimension (int): Dimension in x direction of the parameter block + x_mapping (str): Mapping of the x axis to a distribution, if available as a comment in DCM + comment (str): Block comment + """ + + attrs: dict = field(default_factory=_attrs_init) + + +class DcmFixedCharacteristicLine(DcmCharacteristicLine): + """Definition of a fixed characteristic line, derived from characteristic line""" + + +class DcmGroupCharacteristicLine(DcmCharacteristicLine): + """Definition of a group characteristic line, derived from characteristic line""" + + +@dataclass +class DcmCharacteristicMap(_DcmBase): + """Definition of a characteristic map + + Attributes: + name (str): Name of the characteristic map + description (str): Description of the characteristic map, started by LANGNAME in DCM + display_name (str): Characteristic map name according asam-2mc, started by DISPLAYNAME in DCM + variants (dict): Variants for the characteristic map, started by VAR in DCM + function (str): Name of the assigned function, started by FUNKTION in DCM + unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM + unit_y (str): Unit of the y axis values, started by EINHEIT_Y in DCM + unit_values (str): Unit of the values, started by EINHEIT_W in DCM + values (dict): 2D Dict of values of the parameter + The inner dict contains the values from ST/X as keys and the + values retrieved from WERT as values. The keys of the outer dict + contains the values from ST/Y. + x_dimension (int): Dimension in x direction of the characteristic maps + y_dimension (int): Dimension in y direction of the characteristic maps + """ + + attrs: dict = field(default_factory=_attrs_init) + + +class DcmFixedCharacteristicMap(DcmCharacteristicMap): + """Definition of a fixed characteristic map, derived from characteristic map""" + + +class DcmGroupCharacteristicMap(DcmCharacteristicMap): + """Definition of a group characteristic map, derived from characteristic map""" + + +@dataclass +class DcmDistribution(_DcmBase): + """Definition of a distribution + + Attributes: + name (str): Name of the distribution + description (str): Description of the distribution, started by LANGNAME in DCM + display_name (str): Distribution name according asam-2mc, started by DISPLAYNAME in DCM + variants (dict): Variants for the distribution, started by VAR in DCM + function (str): Name of the assigned function, started by FUNKTION in DCM + unit_x (str): Unit of the x axis values, started by EINHEIT_X in DCM + values (list): List of values of the distribution, values are retrieved from WERT + x_dimension (int): Dimension in x direction of the distribution + comment (str): Block comment + """ + + attrs: dict = field(default_factory=_attrs_init) diff --git a/src/dcmReader/py.typed b/src/dcmReader/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/dcmReader/utils.py b/src/dcmReader/utils.py new file mode 100644 index 0000000..35c5e07 --- /dev/null +++ b/src/dcmReader/utils.py @@ -0,0 +1,325 @@ +""" +Definition of DCM characteristic map +""" +from __future__ import annotations + +import math +import os +from typing import TYPE_CHECKING, Protocol + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field + +import numpy as np + +if TYPE_CHECKING: + from typing import Union, Any, Callable, TypedDict + + from numpy.typing import ArrayLike + + T_Fmt = Callable[..., str] + + class T_Setting(TypedDict): + key_ger: str + key_eng: str + print_key: str + print_format: T_Fmt + print_kwargs: dict[str, Any] + parse_key: str + parse_method: Callable + + T_Settings = dict[str, T_Setting] + + +_COMMENT_QUALIFIER = ("!", "*", ".") + +_SETTINGS: T_Settings = { + k: { + "key_ger": k, + "key_eng": "comment", + "print_key": "*", + "print_format": lambda x: f"{x}", + "print_kwargs": {"pad": 0, "n": 1}, + "parse_key": "", # comment + "parse_method": lambda self: self._parse_comment, + } + for k in _COMMENT_QUALIFIER +} +_SETTINGS.update( + { + "LANGNAME": { + "key_ger": "LANGNAME", + "key_eng": "description", + "print_key": "LANGNAME", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "description", + "parse_method": lambda self: self._parse_string, + }, + "FUNKTION": { + "key_ger": "FUNKTION", + "key_eng": "function", + "print_key": "FUNKTION", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "function", + "parse_method": lambda self: self._parse_string, + }, + "DISPLAYNAME": { + "key_ger": "DISPLAYNAME", + "key_eng": "display_name", + "print_key": "DISPLAYNAME", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "display_name", + "parse_method": lambda self: self._parse_string, + }, + "EINHEIT_X": { + "key_ger": "EINHEIT_X", + "key_eng": "units_x", + "print_key": "EINHEIT_X", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "units_x", + "parse_method": lambda self: self._parse_string, + }, + "EINHEIT_Y": { + "key_ger": "EINHEIT_Y", + "key_eng": "units_y", + "print_key": "EINHEIT_Y", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "units_y", + "parse_method": lambda self: self._parse_string, + }, + "EINHEIT_W": { + "key_ger": "EINHEIT_W", + "key_eng": "units", + "print_key": "EINHEIT_W", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "units", + "parse_method": lambda self: self._parse_string, + }, + "ST/X": { + "key_ger": "ST/X", + "key_eng": "ST/X", + "print_key": "ST/X", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "", + "parse_method": lambda self: self._parse_coord_x, + }, + "ST/Y": { + "key_ger": "ST/Y", + "key_eng": "ST/Y", + "print_key": "ST/Y", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "", + "parse_method": lambda self: self._parse_coord_y, + }, + "SSTX": { + # From comments: + "key_ger": "SSTX", + "key_eng": "name_x", + "print_key": "*SSTX", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "name_x", + "parse_method": lambda self: self._parse_string, + }, + "SSTY": { + # From comments: + "key_ger": "SSTY", + "key_eng": "name_y", + "print_key": "*SSTY", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "name_y", + "parse_method": lambda self: self._parse_string, + }, + "WERT": { + "key_ger": "WERT", + "key_eng": "value", + "print_key": "WERT", + "print_format": lambda x: f"{x}", + "print_kwargs": {}, + "parse_key": "", + "parse_method": lambda self: self._parse_wert, + }, + "TEXT": { + "key_ger": "TEXT", + "key_eng": "text", + "print_key": "TEXT", + "print_format": lambda x: f'"{x}"', + "print_kwargs": {}, + "parse_key": "", + "parse_method": lambda self: self._parse_text, + }, + "VAR": { + "key_ger": "VAR", + "key_eng": "variants", + "print_key": "VAR", + "print_format": lambda x: ", ".join( + [f'{k}="{v}"' if isinstance(v, str) else f"{k}={v}" for k, v in x.items()] + ), + "print_kwargs": {}, + "parse_key": "variants", + "parse_method": lambda self: self._parse_variant, + }, + } +) + + +class _HasValuesProtocol(Protocol): + values: np.ndarray + + +class ShapeRelatedMixin(_HasValuesProtocol): + @property + def shape(self) -> tuple[int, ...]: + """ + Get shape of array. + + See Also + -------- + numpy.ndarray.shape + """ + return self.values.shape + + @property + def ndim(self) -> int: + """ + Get number of array dimensions. + + See Also + -------- + numpy.ndarray.ndim + """ + return self.values.ndim + + @property + def size(self) -> int: + """ + Get number of elements in the array. + + See Also + -------- + numpy.ndarray.size + """ + return self.values.size + + def __len__(self) -> int: + return len(self.values) + + +def _fmt_base(x) -> str: + return f"{x}" + + +def _to_str(val: ArrayLike, fmt: T_Fmt = _fmt_base, delimiter: str = " ") -> str: + val_list = np.atleast_1d(val) + + out = delimiter.join(map(fmt, val_list)) + return out + + +def _print_values(key: str, value: ArrayLike, fmt: T_Fmt = _fmt_base, n: int = 6, pad: int = 14) -> str: + """Print pairwise values prettily.""" + # Normalize so that value is always a list: + value_list = np.atleast_1d(value) + + # Split strings that contains a newline: + if value_list.dtype.kind in {"U", "S"}: + value_list = np.array("\n".join(value_list).split("\n")) + + # Split too long lists into chunks. + value_list_chunked = [value_list[i : i + n] for i in range(0, len(value_list), n)] + + # Return the key and value in a table-format: + pad_ = f"<{pad}" + out = "" + for v in value_list_chunked: + value_str = _to_str(v, fmt) + out += f" {key:{pad_}}{value_str}\n" + return out + + +@dataclass +class _DcmBase(ShapeRelatedMixin): + name: str + values: np.ndarray = field(default_factory=lambda: np.array([])) + coords: tuple[np.ndarray, ...] = field(default_factory=tuple) + dims: tuple[str, ...] = field(default_factory=tuple) + attrs: dict = field(default_factory=dict) + element_syntax: str = "" + + def __lt__(self, other) -> bool: + self_function = self.attrs.get("function", "") + self_description = self.attrs.get("description", "") + other_function = other.attrs.get("function", "") + other_description = other.attrs.get("description", "") + return self_function < other_function and self_description < other_description + + def _print_attrs(self, k: str, value: str) -> str: + setting = _SETTINGS[k] + key_eng = setting["key_eng"] + + attrs_value = self.attrs.get(key_eng, "") + if attrs_value: + print_key = setting["print_key"] + print_val = attrs_value + print_format = setting["print_format"] + print_kwargs = setting["print_kwargs"] + + value += _print_values(print_key, print_val, print_format, **print_kwargs) + + return value + + def _print_dcm_format(self) -> str: + """ + Print the data according to the dcm-format. + """ + + ndim = self.ndim + shape_rev: list[int | str] = list(reversed(self.shape)) + if self.element_syntax == "FESTWERTEBLOCK" and ndim == 2: + shape_rev.insert(1, "@") + coords_rev = list(reversed(self.coords)) + ncoords = len(coords_rev) + + # Header: + shape_str = _to_str(shape_rev) + value = f"{self.element_syntax} {self.name} {shape_str}\n" + + # Attributes printed before the values: + for k in _SETTINGS.keys(): + if k in ("WERT", "ST/X", "ST/Y", "VAR") + _COMMENT_QUALIFIER[:-1]: + continue + + value = self._print_attrs(k, value) + + # x-values: + if ncoords > 0: + value += _print_values("ST/X", coords_rev[0], _SETTINGS["ST/X"]["print_format"]) + + # y-value and values: + print_key_val = "TEXT" if self.values.dtype.kind in ("S", "U") else "WERT" + for i, val in enumerate(np.atleast_2d(self.values)): + if ncoords > 1: + value += _print_values("ST/Y", coords_rev[1][i], _SETTINGS["ST/Y"]["print_format"]) + + value += _print_values(print_key_val, val, _SETTINGS[print_key_val]["print_format"]) + + # # Attributes printed after the values: + for k in ("VAR",): + value = self._print_attrs(k, value) + + # Close: + value += "END" + + return value + + def __str__(self) -> str: + return self._print_dcm_format() diff --git a/tests/Sample.dcm b/tests/Sample.dcm index bda37c0..5dfaf63 100644 --- a/tests/Sample.dcm +++ b/tests/Sample.dcm @@ -36,6 +36,15 @@ FESTWERT textParameter VAR VariantA="ParameterB" END +FESTWERTEBLOCK blockTextParameter1D 2 +* Sample comment + LANGNAME "Sample block parameters" + FUNKTION "BlockParameterFunction" + DISPLAYNAME BlockParameterDisplayname + EINHEIT_W "°C" + TEXT "ParameterA" "ParameterB" +END + FESTWERTEBLOCK blockParameter1D 4 * Sample comment LANGNAME "Sample block parameters" @@ -61,7 +70,7 @@ KENNLINIE characteristicLine 8 DISPLAYNAME CharacteristicLineDisplayname EINHEIT_X "s" EINHEIT_W "°" -*SSTX DISTRIBUTION X +*SSTX DISTRIBUTION X8 ST/X 0.0 1.0 2.0 3.0 ST/X 4.0 5.0 6.0 7.0 WERT 0.0 80.0 120.0 180.0 @@ -75,9 +84,9 @@ FESTKENNLINIE fixedCharacteristicLine 6 DISPLAYNAME FixedCharacteristicLineDisplayname EINHEIT_X "s" EINHEIT_W "°" - *SSTX DISTRIBUTION X + *SSTX DISTRIBUTION X6 ST/X 0.0 1.0 2.0 - ST/X 3.0 4.0 5.0 + ST/X 3.0 4.0 5.0 WERT 45.0 90.0 135.0 WERT 180.0 225.0 270.0 END @@ -89,7 +98,7 @@ GRUPPENKENNLINIE groupCharacteristicLine 3 DISPLAYNAME GroupCharacteristicLineDisplayname EINHEIT_X "s" EINHEIT_W "°" -* SSTX DISTRIBUTION X +* SSTX DISTRIBUTION X3 ST/X 1.0 2.0 3.0 WERT -45.0 -90.0 -135.0 END @@ -102,15 +111,15 @@ KENNFELD characteristicMap 6 2 EINHEIT_X "°C" EINHEIT_Y "m/s" EINHEIT_W "bar" -*SSTX DISTRIBUTION X -*SSTY DISTRIBUTION Y +*SSTX DISTRIBUTION X6 +*SSTY DISTRIBUTION Y2 ST/X 1.0 2.0 3.0 4.0 5.0 6.0 - ST/Y 1.0 + ST/Y 1.0 WERT 0.0 0.4 0.8 1.0 1.4 1.8 - ST/Y 2.0 + ST/Y 2.0 WERT 1.0 2.0 3.0 2.0 3.0 4.0 END - + FESTKENNFELD fixedCharacteristicMap 6 2 * Sample comment LANGNAME "Sample fixed characteristic map" @@ -119,14 +128,14 @@ FESTKENNFELD fixedCharacteristicMap 6 2 EINHEIT_X "°C" EINHEIT_Y "m/s" EINHEIT_W "bar" - *SSTX DISTRIBUTION X - *SSTY DISTRIBUTION Y + *SSTX DISTRIBUTION X6 + *SSTY DISTRIBUTION Y2 ST/X 1.0 2.0 3.0 ST/X 4.0 5.0 6.0 - ST/Y 0.0 + ST/Y 0.0 WERT 0.0 0.4 0.8 WERT 1.0 1.4 1.8 - ST/Y 1.0 + ST/Y 1.0 WERT 1.0 2.0 3.0 2.0 3.0 4.0 END @@ -138,22 +147,22 @@ GRUPPENKENNFELD groupCharacteristicMap 6 3 EINHEIT_X "°C" EINHEIT_Y "m/s" EINHEIT_W "bar" -* SSTX DISTRIBUTION X -* SSTY DISTRIBUTION Y - ST/X 1.0 2.0 3.0 +* SSTX DISTRIBUTION X6 +* SSTY DISTRIBUTION Y3 + ST/X 1.0 2.0 3.0 ST/X 4.0 5.0 6.0 - ST/Y 1.0 - WERT 1.0 2.0 3.0 - WERT 2.0 3.0 4.0 - ST/Y 2.0 - WERT 2.0 4.0 6.0 - WERT 3.0 4.0 5.0 - ST/Y 3.0 + ST/Y 1.0 + WERT 1.0 2.0 3.0 + WERT 2.0 3.0 4.0 + ST/Y 2.0 + WERT 2.0 4.0 6.0 + WERT 3.0 4.0 5.0 + ST/Y 3.0 WERT 3.0 6.0 9.0 WERT 7.0 8.0 9.0 END -STUETZSTELLENVERTEILUNG distrib 3 +STUETZSTELLENVERTEILUNG distrib 3 *SST LANGNAME "Sample distribution" FUNKTION "DistributionFunction" diff --git a/tests/Tests.py b/tests/Tests.py deleted file mode 100644 index 79fbc0e..0000000 --- a/tests/Tests.py +++ /dev/null @@ -1,504 +0,0 @@ -import os -import sys -import unittest - -testdir = os.path.dirname(__file__) -srcdir = "../src" -sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir))) - -from dcmReader.dcm_reader import DcmReader - - -class TestWriteFile(unittest.TestCase): - def test_fileWriting(self): - dcm = DcmReader() - dcm.read("./Sample.dcm") - self.assertEqual(9, len(dcm.get_functions())) - dcm.write("./Sample_written") - - -class TestFunctions(unittest.TestCase): - def test_functionParsing(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - self.assertEqual(9, len(dcm.get_functions())) - self.assertEqual(9, len(dcmWritten.get_functions())) - - -class TestParameters(unittest.TestCase): - def test_foundParameter(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - self.assertEqual(2, len(dcm.get_parameters())) - self.assertEqual(2, len(dcmWritten.get_parameters())) - - def test_valueParameter(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - valueParameter = dcm.get_parameters()[0] - - self.assertEqual("valueParameter", valueParameter.name) - self.assertEqual("Sample value parameter", valueParameter.description) - self.assertEqual("ParameterFunction", valueParameter.function) - self.assertEqual("°C", valueParameter.unit) - self.assertEqual(25.0, valueParameter.value) - self.assertEqual(27.5, valueParameter.variants["VariantA"]) - self.assertEqual(None, valueParameter.text) - self.assertEqual("Sample comment\nSecond comment line\n", valueParameter.comment) - - def test_textParameter(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - valueParameter = dcm.get_parameters()[1] - - self.assertEqual("textParameter", valueParameter.name) - self.assertEqual("Sample text parameter", valueParameter.description) - self.assertEqual("ParameterFunction", valueParameter.function) - self.assertEqual("-", valueParameter.unit) - self.assertEqual(None, valueParameter.value) - self.assertEqual("ParameterB", valueParameter.variants["VariantA"]) - self.assertEqual("ParameterA", valueParameter.text) - - -class TestParameterBlock(unittest.TestCase): - def test_blockParameter1D(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - blockParameter = dcm.get_block_parameters()[0] - blockParameterWritten = dcmWritten.get_block_parameters()[0] - - self.assertEqual("blockParameter1D", blockParameter.name) - self.assertEqual("Sample block parameters", blockParameter.description) - self.assertEqual("BlockParameterFunction", blockParameter.function) - self.assertEqual("BlockParameterDisplayname", blockParameter.display_name) - self.assertEqual("°C", blockParameter.unit) - self.assertEqual(0.75, blockParameter.values[0][0]) - self.assertEqual(-0.25, blockParameter.values[0][1]) - self.assertEqual(0.5, blockParameter.values[0][2]) - self.assertEqual(1.5, blockParameter.values[0][3]) - self.assertEqual("Sample comment\n", blockParameter.comment) - - self.assertEqual("blockParameter1D", blockParameterWritten.name) - self.assertEqual("Sample block parameters", blockParameterWritten.description) - self.assertEqual("BlockParameterFunction", blockParameterWritten.function) - self.assertEqual("BlockParameterDisplayname", blockParameterWritten.display_name) - self.assertEqual("°C", blockParameterWritten.unit) - self.assertEqual(0.75, blockParameterWritten.values[0][0]) - self.assertEqual(-0.25, blockParameterWritten.values[0][1]) - self.assertEqual(0.5, blockParameterWritten.values[0][2]) - self.assertEqual(1.5, blockParameterWritten.values[0][3]) - self.assertEqual("Sample comment\n", blockParameterWritten.comment) - - def test_blockParameter2D(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - blockParameter = dcm.get_block_parameters()[1] - blockParameterWritten = dcmWritten.get_block_parameters()[1] - - self.assertEqual("blockParameter2D", blockParameter.name) - self.assertEqual("Sample block parameters", blockParameter.description) - self.assertEqual("BlockParameterFunction", blockParameter.function) - self.assertEqual("BlockParameterDisplayname", blockParameter.display_name) - self.assertEqual("°C", blockParameter.unit) - self.assertEqual(0.75, blockParameter.values[0][0]) - self.assertEqual(-0.25, blockParameter.values[0][1]) - self.assertEqual(0.5, blockParameter.values[0][2]) - self.assertEqual(1.5, blockParameter.values[0][3]) - self.assertEqual(10.75, blockParameter.values[1][0]) - self.assertEqual(-10.25, blockParameter.values[1][1]) - self.assertEqual(10.5, blockParameter.values[1][2]) - self.assertEqual(11.5, blockParameter.values[1][3]) - - self.assertEqual("blockParameter2D", blockParameterWritten.name) - self.assertEqual("Sample block parameters", blockParameterWritten.description) - self.assertEqual("BlockParameterFunction", blockParameterWritten.function) - self.assertEqual("BlockParameterDisplayname", blockParameterWritten.display_name) - self.assertEqual("°C", blockParameterWritten.unit) - self.assertEqual(0.75, blockParameterWritten.values[0][0]) - self.assertEqual(-0.25, blockParameterWritten.values[0][1]) - self.assertEqual(0.5, blockParameterWritten.values[0][2]) - self.assertEqual(1.5, blockParameterWritten.values[0][3]) - self.assertEqual(10.75, blockParameterWritten.values[1][0]) - self.assertEqual(-10.25, blockParameterWritten.values[1][1]) - self.assertEqual(10.5, blockParameterWritten.values[1][2]) - self.assertEqual(11.5, blockParameterWritten.values[1][3]) - - -class TestCharacteristicLines(unittest.TestCase): - def test_characteristicLine(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_characteristic_lines()[0] - characteristicWritten = dcmWritten.get_characteristic_lines()[0] - - self.assertEqual(1, len(dcm.get_characteristic_lines())) - - self.assertEqual("characteristicLine", characteristic.name) - self.assertEqual("Sample characteristic line", characteristic.description) - self.assertEqual("CharacteristicLineFunction", characteristic.function) - self.assertEqual("CharacteristicLineDisplayname", characteristic.display_name) - self.assertEqual("°", characteristic.unit_values) - self.assertEqual("s", characteristic.unit_x) - self.assertEqual(0.0, characteristic.values[0.0]) - self.assertEqual(80.0, characteristic.values[1.0]) - self.assertEqual(120.0, characteristic.values[2.0]) - self.assertEqual(180.0, characteristic.values[3.0]) - self.assertEqual(220.0, characteristic.values[4.0]) - self.assertEqual(260.0, characteristic.values[5.0]) - self.assertEqual(300.0, characteristic.values[6.0]) - self.assertEqual(340.0, characteristic.values[7.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_characteristic_lines())) - - self.assertEqual("characteristicLine", characteristicWritten.name) - self.assertEqual("Sample characteristic line", characteristicWritten.description) - self.assertEqual("CharacteristicLineFunction", characteristicWritten.function) - self.assertEqual("CharacteristicLineDisplayname", characteristicWritten.display_name) - self.assertEqual("°", characteristicWritten.unit_values) - self.assertEqual("s", characteristicWritten.unit_x) - self.assertEqual(0.0, characteristicWritten.values[0.0]) - self.assertEqual(80.0, characteristicWritten.values[1.0]) - self.assertEqual(120.0, characteristicWritten.values[2.0]) - self.assertEqual(180.0, characteristicWritten.values[3.0]) - self.assertEqual(220.0, characteristicWritten.values[4.0]) - self.assertEqual(260.0, characteristicWritten.values[5.0]) - self.assertEqual(300.0, characteristicWritten.values[6.0]) - self.assertEqual(340.0, characteristicWritten.values[7.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - def test_fixedCharacteristicLine(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_fixed_characteristic_lines()[0] - characteristicWritten = dcmWritten.get_fixed_characteristic_lines()[0] - - self.assertEqual(1, len(dcm.get_fixed_characteristic_lines())) - - self.assertEqual("fixedCharacteristicLine", characteristic.name) - self.assertEqual("Sample fixed characteristic line", characteristic.description) - self.assertEqual("FixedCharacteristicLineFunction", characteristic.function) - self.assertEqual( - "FixedCharacteristicLineDisplayname", characteristic.display_name - ) - self.assertEqual("°", characteristic.unit_values) - self.assertEqual("s", characteristic.unit_x) - self.assertEqual(45.0, characteristic.values[0.0]) - self.assertEqual(90.0, characteristic.values[1.0]) - self.assertEqual(135.0, characteristic.values[2.0]) - self.assertEqual(180.0, characteristic.values[3.0]) - self.assertEqual(225.0, characteristic.values[4.0]) - self.assertEqual(270.0, characteristic.values[5.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_fixed_characteristic_lines())) - - self.assertEqual("fixedCharacteristicLine", characteristicWritten.name) - self.assertEqual("Sample fixed characteristic line", characteristicWritten.description) - self.assertEqual("FixedCharacteristicLineFunction", characteristicWritten.function) - self.assertEqual( - "FixedCharacteristicLineDisplayname", characteristicWritten.display_name - ) - self.assertEqual("°", characteristicWritten.unit_values) - self.assertEqual("s", characteristicWritten.unit_x) - self.assertEqual(45.0, characteristicWritten.values[0.0]) - self.assertEqual(90.0, characteristicWritten.values[1.0]) - self.assertEqual(135.0, characteristicWritten.values[2.0]) - self.assertEqual(180.0, characteristicWritten.values[3.0]) - self.assertEqual(225.0, characteristicWritten.values[4.0]) - self.assertEqual(270.0, characteristicWritten.values[5.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - def test_groupCharacteristicLine(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_group_characteristic_lines()[0] - characteristicWritten = dcmWritten.get_group_characteristic_lines()[0] - - self.assertEqual(1, len(dcm.get_group_characteristic_lines())) - - self.assertEqual("groupCharacteristicLine", characteristic.name) - self.assertEqual("Sample group characteristic line", characteristic.description) - self.assertEqual("GroupCharacteristicLineFunction", characteristic.function) - self.assertEqual( - "GroupCharacteristicLineDisplayname", characteristic.display_name - ) - self.assertEqual("°", characteristic.unit_values) - self.assertEqual("s", characteristic.unit_x) - self.assertEqual(-45.0, characteristic.values[1.0]) - self.assertEqual(-90.0, characteristic.values[2.0]) - self.assertEqual(-135.0, characteristic.values[3.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_group_characteristic_lines())) - - self.assertEqual("groupCharacteristicLine", characteristicWritten.name) - self.assertEqual("Sample group characteristic line", characteristicWritten.description) - self.assertEqual("GroupCharacteristicLineFunction", characteristicWritten.function) - self.assertEqual( - "GroupCharacteristicLineDisplayname", characteristicWritten.display_name - ) - self.assertEqual("°", characteristicWritten.unit_values) - self.assertEqual("s", characteristicWritten.unit_x) - self.assertEqual(-45.0, characteristicWritten.values[1.0]) - self.assertEqual(-90.0, characteristicWritten.values[2.0]) - self.assertEqual(-135.0, characteristicWritten.values[3.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - -class TestCharacteristicMaps(unittest.TestCase): - def test_characteristicMap(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_characteristic_maps()[0] - characteristicWritten = dcmWritten.get_characteristic_maps()[0] - - self.assertEqual(1, len(dcm.get_characteristic_maps())) - - self.assertEqual("characteristicMap", characteristic.name) - self.assertEqual("Sample characteristic map", characteristic.description) - self.assertEqual("CharacteristicMapFunction", characteristic.function) - self.assertEqual("CharacteristicMapDisplayname", characteristic.display_name) - self.assertEqual("bar", characteristic.unit_values) - self.assertEqual("°C", characteristic.unit_x) - self.assertEqual("m/s", characteristic.unit_y) - self.assertEqual(0.0, characteristic.values[1.0][1.0]) - self.assertEqual(0.4, characteristic.values[1.0][2.0]) - self.assertEqual(0.8, characteristic.values[1.0][3.0]) - self.assertEqual(1.0, characteristic.values[1.0][4.0]) - self.assertEqual(1.4, characteristic.values[1.0][5.0]) - self.assertEqual(1.8, characteristic.values[1.0][6.0]) - self.assertEqual(1.0, characteristic.values[2.0][1.0]) - self.assertEqual(2.0, characteristic.values[2.0][2.0]) - self.assertEqual(3.0, characteristic.values[2.0][3.0]) - self.assertEqual(2.0, characteristic.values[2.0][4.0]) - self.assertEqual(3.0, characteristic.values[2.0][5.0]) - self.assertEqual(4.0, characteristic.values[2.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristic.y_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_characteristic_maps())) - - self.assertEqual("characteristicMap", characteristicWritten.name) - self.assertEqual("Sample characteristic map", characteristicWritten.description) - self.assertEqual("CharacteristicMapFunction", characteristicWritten.function) - self.assertEqual("CharacteristicMapDisplayname", characteristicWritten.display_name) - self.assertEqual("bar", characteristicWritten.unit_values) - self.assertEqual("°C", characteristicWritten.unit_x) - self.assertEqual("m/s", characteristicWritten.unit_y) - self.assertEqual(0.0, characteristicWritten.values[1.0][1.0]) - self.assertEqual(0.4, characteristicWritten.values[1.0][2.0]) - self.assertEqual(0.8, characteristicWritten.values[1.0][3.0]) - self.assertEqual(1.0, characteristicWritten.values[1.0][4.0]) - self.assertEqual(1.4, characteristicWritten.values[1.0][5.0]) - self.assertEqual(1.8, characteristicWritten.values[1.0][6.0]) - self.assertEqual(1.0, characteristicWritten.values[2.0][1.0]) - self.assertEqual(2.0, characteristicWritten.values[2.0][2.0]) - self.assertEqual(3.0, characteristicWritten.values[2.0][3.0]) - self.assertEqual(2.0, characteristicWritten.values[2.0][4.0]) - self.assertEqual(3.0, characteristicWritten.values[2.0][5.0]) - self.assertEqual(4.0, characteristicWritten.values[2.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristicWritten.y_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - def test_fixedCharacteristicMap(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_fixed_characteristic_maps()[0] - characteristicWritten = dcmWritten.get_fixed_characteristic_maps()[0] - - self.assertEqual(1, len(dcm.get_fixed_characteristic_maps())) - - self.assertEqual("fixedCharacteristicMap", characteristic.name) - self.assertEqual("Sample fixed characteristic map", characteristic.description) - self.assertEqual("FixedCharacteristicMapFunction", characteristic.function) - self.assertEqual( - "FixedCharacteristicMapDisplayname", characteristic.display_name - ) - self.assertEqual("bar", characteristic.unit_values) - self.assertEqual("°C", characteristic.unit_x) - self.assertEqual("m/s", characteristic.unit_y) - self.assertEqual(0.0, characteristic.values[0.0][1.0]) - self.assertEqual(0.4, characteristic.values[0.0][2.0]) - self.assertEqual(0.8, characteristic.values[0.0][3.0]) - self.assertEqual(1.0, characteristic.values[0.0][4.0]) - self.assertEqual(1.4, characteristic.values[0.0][5.0]) - self.assertEqual(1.8, characteristic.values[0.0][6.0]) - self.assertEqual(1.0, characteristic.values[1.0][1.0]) - self.assertEqual(2.0, characteristic.values[1.0][2.0]) - self.assertEqual(3.0, characteristic.values[1.0][3.0]) - self.assertEqual(2.0, characteristic.values[1.0][4.0]) - self.assertEqual(3.0, characteristic.values[1.0][5.0]) - self.assertEqual(4.0, characteristic.values[1.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristic.y_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_fixed_characteristic_maps())) - - self.assertEqual("fixedCharacteristicMap", characteristicWritten.name) - self.assertEqual("Sample fixed characteristic map", characteristicWritten.description) - self.assertEqual("FixedCharacteristicMapFunction", characteristicWritten.function) - self.assertEqual( - "FixedCharacteristicMapDisplayname", characteristicWritten.display_name - ) - self.assertEqual("bar", characteristicWritten.unit_values) - self.assertEqual("°C", characteristicWritten.unit_x) - self.assertEqual("m/s", characteristicWritten.unit_y) - self.assertEqual(0.0, characteristicWritten.values[0.0][1.0]) - self.assertEqual(0.4, characteristicWritten.values[0.0][2.0]) - self.assertEqual(0.8, characteristicWritten.values[0.0][3.0]) - self.assertEqual(1.0, characteristicWritten.values[0.0][4.0]) - self.assertEqual(1.4, characteristicWritten.values[0.0][5.0]) - self.assertEqual(1.8, characteristicWritten.values[0.0][6.0]) - self.assertEqual(1.0, characteristicWritten.values[1.0][1.0]) - self.assertEqual(2.0, characteristicWritten.values[1.0][2.0]) - self.assertEqual(3.0, characteristicWritten.values[1.0][3.0]) - self.assertEqual(2.0, characteristicWritten.values[1.0][4.0]) - self.assertEqual(3.0, characteristicWritten.values[1.0][5.0]) - self.assertEqual(4.0, characteristicWritten.values[1.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristicWritten.y_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - def test_groupCharacteristicMap(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - characteristic = dcm.get_group_characteristic_maps()[0] - characteristicWritten = dcmWritten.get_group_characteristic_maps()[0] - - self.assertEqual(1, len(dcm.get_group_characteristic_maps())) - - self.assertEqual("groupCharacteristicMap", characteristic.name) - self.assertEqual("Sample group characteristic map", characteristic.description) - self.assertEqual("GroupCharacteristicMapFunction", characteristic.function) - self.assertEqual( - "GroupCharacteristicMapDisplayname", characteristic.display_name - ) - self.assertEqual("bar", characteristic.unit_values) - self.assertEqual("°C", characteristic.unit_x) - self.assertEqual("m/s", characteristic.unit_y) - self.assertEqual(1.0, characteristic.values[1.0][1.0]) - self.assertEqual(2.0, characteristic.values[1.0][2.0]) - self.assertEqual(3.0, characteristic.values[1.0][3.0]) - self.assertEqual(2.0, characteristic.values[1.0][4.0]) - self.assertEqual(3.0, characteristic.values[1.0][5.0]) - self.assertEqual(4.0, characteristic.values[1.0][6.0]) - self.assertEqual(2.0, characteristic.values[2.0][1.0]) - self.assertEqual(4.0, characteristic.values[2.0][2.0]) - self.assertEqual(6.0, characteristic.values[2.0][3.0]) - self.assertEqual(3.0, characteristic.values[2.0][4.0]) - self.assertEqual(4.0, characteristic.values[2.0][5.0]) - self.assertEqual(5.0, characteristic.values[2.0][6.0]) - self.assertEqual(3.0, characteristic.values[3.0][1.0]) - self.assertEqual(6.0, characteristic.values[3.0][2.0]) - self.assertEqual(9.0, characteristic.values[3.0][3.0]) - self.assertEqual(7.0, characteristic.values[3.0][4.0]) - self.assertEqual(8.0, characteristic.values[3.0][5.0]) - self.assertEqual(9.0, characteristic.values[3.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristic.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristic.y_mapping) - self.assertEqual("Sample comment\n", characteristic.comment) - - self.assertEqual(1, len(dcmWritten.get_group_characteristic_maps())) - - self.assertEqual("groupCharacteristicMap", characteristicWritten.name) - self.assertEqual("Sample group characteristic map", characteristicWritten.description) - self.assertEqual("GroupCharacteristicMapFunction", characteristicWritten.function) - self.assertEqual( - "GroupCharacteristicMapDisplayname", characteristicWritten.display_name - ) - self.assertEqual("bar", characteristicWritten.unit_values) - self.assertEqual("°C", characteristicWritten.unit_x) - self.assertEqual("m/s", characteristicWritten.unit_y) - self.assertEqual(1.0, characteristicWritten.values[1.0][1.0]) - self.assertEqual(2.0, characteristicWritten.values[1.0][2.0]) - self.assertEqual(3.0, characteristicWritten.values[1.0][3.0]) - self.assertEqual(2.0, characteristicWritten.values[1.0][4.0]) - self.assertEqual(3.0, characteristicWritten.values[1.0][5.0]) - self.assertEqual(4.0, characteristicWritten.values[1.0][6.0]) - self.assertEqual(2.0, characteristicWritten.values[2.0][1.0]) - self.assertEqual(4.0, characteristicWritten.values[2.0][2.0]) - self.assertEqual(6.0, characteristicWritten.values[2.0][3.0]) - self.assertEqual(3.0, characteristicWritten.values[2.0][4.0]) - self.assertEqual(4.0, characteristicWritten.values[2.0][5.0]) - self.assertEqual(5.0, characteristicWritten.values[2.0][6.0]) - self.assertEqual(3.0, characteristicWritten.values[3.0][1.0]) - self.assertEqual(6.0, characteristicWritten.values[3.0][2.0]) - self.assertEqual(9.0, characteristicWritten.values[3.0][3.0]) - self.assertEqual(7.0, characteristicWritten.values[3.0][4.0]) - self.assertEqual(8.0, characteristicWritten.values[3.0][5.0]) - self.assertEqual(9.0, characteristicWritten.values[3.0][6.0]) - self.assertEqual("DISTRIBUTION X", characteristicWritten.x_mapping) - self.assertEqual("DISTRIBUTION Y", characteristicWritten.y_mapping) - self.assertEqual("Sample comment\n", characteristicWritten.comment) - - -class TestDistribution(unittest.TestCase): - def test_distribution(self): - dcm = DcmReader() - dcmWritten = DcmReader() - dcm.read("./Sample.dcm") - dcmWritten.read("./Sample_written.dcm") - distribution = dcm.get_distributions()[0] - distributionWritten = dcmWritten.get_distributions()[0] - - self.assertEqual(1, len(dcm.get_distributions())) - - self.assertEqual("distrib", distribution.name) - self.assertEqual("Sample distribution", distribution.description) - self.assertEqual("DistributionFunction", distribution.function) - self.assertEqual("DistributionDisplayname", distribution.display_name) - self.assertEqual("mm", distribution.unit_x) - self.assertEqual(1.0, distribution.values[0]) - self.assertEqual(2.0, distribution.values[1]) - self.assertEqual(3.0, distribution.values[2]) - self.assertEqual("SST\n", distribution.comment) - - self.assertEqual(1, len(dcmWritten.get_distributions())) - - self.assertEqual("distrib", distributionWritten.name) - self.assertEqual("Sample distribution", distributionWritten.description) - self.assertEqual("DistributionFunction", distributionWritten.function) - self.assertEqual("DistributionDisplayname", distributionWritten.display_name) - self.assertEqual("mm", distributionWritten.unit_x) - self.assertEqual(1.0, distributionWritten.values[0]) - self.assertEqual(2.0, distributionWritten.values[1]) - self.assertEqual(3.0, distributionWritten.values[2]) - self.assertEqual("SST\n", distributionWritten.comment) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_dcmreader.py b/tests/test_dcmreader.py new file mode 100644 index 0000000..25358b8 --- /dev/null +++ b/tests/test_dcmreader.py @@ -0,0 +1,272 @@ +import os +import sys +import unittest +import pytest + +import numpy as np + +from dcmReader.dcm_reader import DcmReader + + +testdir = os.path.dirname(__file__) +srcdir = "../src" +sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir))) + + +class TestWriteFile(unittest.TestCase): + def test_fileWriting(self): + dcm = DcmReader() + dcm.read("./Sample.dcm") + self.assertEqual(9, len(dcm.get_functions())) + dcm.write("./Sample_written") + + +class TestFunctions(unittest.TestCase): + def test_functionParsing(self): + dcm = DcmReader() + dcmWritten = DcmReader() + dcm.read("./Sample.dcm") + dcmWritten.read("./Sample_written.dcm") + self.assertEqual(9, len(dcm.get_functions())) + self.assertEqual(9, len(dcmWritten.get_functions())) + + +class TestParameters(unittest.TestCase): + def test_foundParameter(self): + dcm = DcmReader() + dcmWritten = DcmReader() + dcm.read("./Sample.dcm") + dcmWritten.read("./Sample_written.dcm") + self.assertEqual(2, len(dcm.get_parameters())) + self.assertEqual(2, len(dcmWritten.get_parameters())) + + +@pytest.mark.parametrize("path", [r"./Sample.dcm", r"./Sample_written.dcm"]) +@pytest.mark.parametrize( + "name, attrs, values, coords", + [ + ( + "valueParameter", + { + "comment": "Sample comment\nSecond comment line", + "description": "Sample value parameter", + "function": "ParameterFunction", + "display_name": "ParameterDisplayname", + "units": "°C", + "variants": {"VariantA": 27.5}, + }, + np.array(25.0), + (), + ), + ( + "textParameter", + { + "description": "Sample text parameter", + "function": "ParameterFunction", + "display_name": "ParameterDisplayname", + "units": "-", + "variants": {"VariantA": "ParameterB"}, + }, + np.array("ParameterA", dtype=np.dtype(">> # Generate expected results: + >>> name = "valueParameter" + >>> x = dcm[name] + >>> print((x.name, x.attrs, x.values, x.coords)) + """ + + def test_element( + name: str, + attrs: dict, + values: np.ndarray, + coords: tuple[np.ndarray, ...], + characteristic, + ) -> None: + # Check name + np.testing.assert_array_equal(name, characteristic.name) + + # Check attrs: + np.testing.assert_array_equal(len(attrs), len(characteristic.attrs)) + for k, expected in attrs.items(): + actual = characteristic.attrs[k] + np.testing.assert_array_equal(expected, actual) + + # Check values: + if values.dtype.kind in {"U", "S"}: + # Strings: + np.testing.assert_array_equal(values, characteristic.values) + else: + # Numbers: + np.testing.assert_allclose(values, characteristic.values) + + # Check coords: + np.testing.assert_array_equal(len(coords), len(characteristic.coords)) + [np.testing.assert_allclose(e, a) for e, a in zip(coords, characteristic.coords)] + + dcm = DcmReader() + dcmWritten = DcmReader() + # dcm.read("./Sample.dcm") + # dcmWritten.read("./Sample_written.dcm") + + dcm.read(path) + + characteristic = dcm[name] + test_element(name, attrs, values, coords, characteristic) + + # characteristicWritten = dcmWritten[name] + # test_element(name, attrs, values, coords, characteristicWritten) + + # self.assertEqual(1, len(dcm.get_group_characteristic_maps())) + # np.testing.assert_array_equal(1, len(dcmWritten.get_group_characteristic_maps())) + + +if __name__ == "__main__": + # unittest.main() + pytest.main()