Skip to content

Sketch implementation details of SDK data pipeline #22

@matthias-wende-frequenz

Description

@matthias-wende-frequenz

In the following we sketch an implementation of the future SDK data pipeline.
The design is originated from a users perspective, who might ask the question:
"I need to measure power on a certain point of my microgrid".

For now we only sketch the details for Meters. All other Component categories
may have analog though slightly different implementations respecting the hardware specifications
of the individual category.

class MicrogridClient:
    """
    ... this is the usual mg client, with the following new methods
    """

    ...

    def get_meter(meter_id: int, sample_frequency: float).power -> Receiver[Power]:
        """
        This method looks up if a stream of with the parameters (compenent_id, sample_frequency), is existing in
        a dictionary and if not creates a new channel and stores it to the clients internal meter dictionary.

        Usually raw data streams have timestamps that are not aligned to any equidistant time-grid.
        Thus the incoming datastream will be resampled to `sample_frequency` in order
        to simplify all following data stream operations. These are addition two datastreams
        and scalar multiplication.
        """
        ...

    # the following methods are supposed to be work analog to the previous one
    def get_meter(...).current() ...
    def get_meter(...).voltage() ...
    def get_meter(...).frequency() ...
}

@dataclass
class LogicalMeterFormulas:
    """
    Combines all formulas needed to calculate metrics from several physical meters combined
    into a virtual meter
    """
    current_formula: String
    voltage_formula: String
    frequency_formula: String

class Meter(ABC):
    @property
    @abstractmethod
    def current(self) -> Receiver[Current]:
        pass

    @property
    @abstractmethod
    def voltage(self) -> Receiver[Voltage]:
        pass

    @property
    @abstractmethod
    def power(self) -> Receiver[Power]:
        pass

class LogicalMeter(Meter): {
    """
    The Logical Meter Class represents a Meter in a microgrid
    that may or may not have a physical existence.

    To calculate an output data stream, a formula has to be provided.
    # TODO how do we define a formula? We want to be able to get the data sources purely from the formula!

    If the meter is physically represented, this meter is used as the single data source
    otherwise it will be calculated according by an user provided formula.

    For most cases it's encouraged to use the per-defined formulas by using the get_grid_formula()
    or get_load_formula() functions. These functions will in most cases auto-determine the working
    formula by utilising the component graph.

    It is recommended to use this meter if the user is interested in the grid consumption or location load,
    without having prior information if the physical existence. This makes it possible to write reusable code
    that can run across different locations, with different hardware setups.

    It may also be used to calculate the total sum of meters that are connected to individual battery Racks
    to measure the total power of the full battery system.

    Usage Example:
    ```
        # example formula string: "(c_id1 + c_id2)"
        log_meter = LogicalMeter(micro_grid_client, get_grid_formulas())
        lm_power_rx = log_meter.get_power();
    ```
    """

    def __init__(self,
        client: MicroGridClient,
        formulas: MeterFormulas
    ):
    """
    Initializes a logical meter.

    If no meter, inverter or ev_charger instance is provided a new one will be created,
    which can be returned by the `get_meters()`, `get_inverters()` or `get_ev_charger()` methods.

    Note: No data stream is set up after calling this method.
    """

    def get_power(self) -> Receiver<Power>:
    """
    This method resamples the input power data streams from all meters
    and combines the data streams according to operations defined in the user provided formula
    """

        * create a new (tx, rx) pair
        * parse the formula string into a formula tree
        * get all (resampled) data streams from the micro grid api
          that are represented as tokens in the power formula string,
          i.e. call `client.get_meter_power(component_id, self.sample_frequency)`
          for all component ids
        * create a task that
            * combines the channels according to the power_formula
              and sends the result in the channels Sender

        return rx
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    resolution:wontfixThis will not be worked ontype:enhancementNew feature or enhancement visitble to users

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions