import json from abc import ABC, abstractmethod import numpy as np from ria_toolkit_oss.signal.block_generator.data_types import DataType class Block(ABC): """ Abstract base class for signal processing blocks. This class defines the interface for all signal processing blocks, including input and output data types and the call method for processing. """ @property @abstractmethod def input_type(self) -> DataType: """ Get the input data type for the block. :return: The input data type. :rtype: DataType """ pass @property @abstractmethod def output_type(self) -> DataType: """ Get the output data type for the block. :return: The output data type. :rtype: DataType """ pass @abstractmethod def get_samples(self, num_samples) -> np.ndarray: """ Process the input data and produce output. :param args: Positional arguments for the processing method. :param kwargs: Keyword arguments for the processing method. :return: The processed output data. :rtype: numpy array """ pass def _get_metadata(self): metadata = {} for key, value in vars(self).items(): try: # Try to serialize the value to check if it's JSON serializable json.dumps(value) metadata[f"BlockGenerator:{self.__class__.__name__}:{key}"] = value except (TypeError, ValueError): # If the value is not JSON serializable, skip it continue for block in self.input: metadata = self._combine_dicts_and_handle_double_keys(block._get_metadata(), metadata) return metadata # TODO improve this def _combine_dicts_and_handle_double_keys(self, source_dict, other_dict): for key, value in source_dict.items(): # Find the last colon in the key last_colon_index = key.rfind(":") # Ensure there's at least one colon in the key if last_colon_index == -1: # If no colon, just append "(1)" new_key = f"{key}(1)" else: # Extract the prefix and the part after the last colon prefix = key[:last_colon_index] suffix = key[last_colon_index + 1 :] # Check if the suffix has a number inside parentheses if suffix.startswith("(") and suffix.endswith(")") and suffix[1:-1].isdigit(): # Extract the number inside the parentheses and increment it number = int(suffix[1:-1]) + 1 new_key = f"{prefix}({number})" else: # No number at the end, so just append "(1)" new_key = f"{key}(1)" # Ensure the new key is unique in both dictionaries while new_key in other_dict: # Find the last parentheses to extract the current number last_paren_index = new_key.rfind(")") prefix = new_key[:last_paren_index] suffix = new_key[last_paren_index + 1 :] # Extract the number in parentheses and increment it if suffix.startswith("(") and suffix.endswith(")") and suffix[1:-1].isdigit(): number = int(suffix[1:-1]) + 1 else: number = 1 # Default to 1 if no number in parentheses # Create the new key with the incremented number new_key = f"{prefix}({number})" # Update the other dictionary with the new key other_dict[new_key] = value return other_dict @abstractmethod def __call__(self, *args, **kwargs) -> np.ndarray: """ Process the input data and produce output. :param args: Positional arguments for the processing method. :param kwargs: Keyword arguments for the processing method. :return: The processed output data. :rtype: numpy array """ pass