Skip to content

Datapoint

Data structures for sensor measurements and actuation events.

This module defines the datapoint classes used throughout the system to represent sensor readings and watering events. All data flowing to the database passes through these structures.

Confidence dataclass

Statistical confidence specification for a measurement.

Attributes:

Name Type Description
interval float

The uncertainty interval (e.g. 0.2 for ± 0.2).

level float

The confidence level as a fraction (e.g. 0.95 for 95%).

Source code in pt/controller_3/src/plant_controller/datapoint.py
@dataclass
class Confidence:
    """Statistical confidence specification for a measurement.

    Attributes:
        interval: The uncertainty interval (e.g. 0.2 for +/- 0.2).
        level: The confidence level as a fraction (e.g. 0.95 for 95%).
    """
    interval: float
    level: float

    def str_representation(self) -> str:
        """Return a formatted string like '+-1.0000e-01 at 95.00%'."""
        return {:.4e} at {:.2%}'.format(self.interval, self.level)

    def __str__(self):
        return self.str_representation()

str_representation()

Return a formatted string like '+-1.0000e-01 at 95.00%'.

Source code in pt/controller_3/src/plant_controller/datapoint.py
def str_representation(self) -> str:
    """Return a formatted string like '+-1.0000e-01 at 95.00%'."""
    return {:.4e} at {:.2%}'.format(self.interval, self.level)

Datapoint

Bases: ABC

Abstract base class for all data that gets written to the database.

Subclasses must implement to_point to produce a dict compatible with the InfluxDB line protocol structure used by DatabaseClient.

Source code in pt/controller_3/src/plant_controller/datapoint.py
class Datapoint(ABC):
    """Abstract base class for all data that gets written to the database.

    Subclasses must implement ``to_point`` to produce a dict compatible
    with the InfluxDB line protocol structure used by DatabaseClient.
    """

    @abstractmethod
    def to_point(self, unit: str) -> dict[str, Any]:
        """Convert this datapoint to an InfluxDB-compatible dict.

        Args:
            unit: Name of the physical unit this data belongs to.

        Returns:
            Dict with 'measurement', 'tags', 'fields', and 'time' keys.
        """
        pass

    @staticmethod
    def format_for_table_name(physical_unit: str, parameter: str) -> str:
        """Generate the database table (measurement) name.

        Args:
            physical_unit: Name of the unit (e.g. 'basil_1').
            parameter: Name of the parameter (e.g. 'temperature').

        Returns:
            Lowercase string in the form '<unit>_<parameter>'.
        """
        return f'{physical_unit}_{parameter}'.lower()

to_point(unit) abstractmethod

Convert this datapoint to an InfluxDB-compatible dict.

Parameters:

Name Type Description Default
unit str

Name of the physical unit this data belongs to.

required

Returns:

Type Description
dict[str, Any]

Dict with 'measurement', 'tags', 'fields', and 'time' keys.

Source code in pt/controller_3/src/plant_controller/datapoint.py
@abstractmethod
def to_point(self, unit: str) -> dict[str, Any]:
    """Convert this datapoint to an InfluxDB-compatible dict.

    Args:
        unit: Name of the physical unit this data belongs to.

    Returns:
        Dict with 'measurement', 'tags', 'fields', and 'time' keys.
    """
    pass

format_for_table_name(physical_unit, parameter) staticmethod

Generate the database table (measurement) name.

Parameters:

Name Type Description Default
physical_unit str

Name of the unit (e.g. 'basil_1').

required
parameter str

Name of the parameter (e.g. 'temperature').

required

Returns:

Type Description
str

Lowercase string in the form '_'.

Source code in pt/controller_3/src/plant_controller/datapoint.py
@staticmethod
def format_for_table_name(physical_unit: str, parameter: str) -> str:
    """Generate the database table (measurement) name.

    Args:
        physical_unit: Name of the unit (e.g. 'basil_1').
        parameter: Name of the parameter (e.g. 'temperature').

    Returns:
        Lowercase string in the form '<unit>_<parameter>'.
    """
    return f'{physical_unit}_{parameter}'.lower()

Measurement

Bases: Datapoint

A single sensor measurement datapoint.

Attributes:

Name Type Description
parameter

Name of the measured parameter.

value

The measured value.

units

Unit string (e.g. '°C', '%', 'photons/s').

confidence

Optional uncertainty specification.

time

Timestamp of the measurement (defaults to now).

Source code in pt/controller_3/src/plant_controller/datapoint.py
class Measurement(Datapoint):
    """A single sensor measurement datapoint.

    Attributes:
        parameter: Name of the measured parameter.
        value: The measured value.
        units: Unit string (e.g. '°C', '%', 'photons/s').
        confidence: Optional uncertainty specification.
        time: Timestamp of the measurement (defaults to now).
    """

    def __init__(
        self,
        parameter: str,
        value: Any,
        units: str,
        confidence: None | Confidence = None,
        time: None | datetime = None
    ):
        self.parameter = parameter
        self.value = value
        self.units = units
        self.confidence = confidence
        if time is None:
            time = datetime.now()
        self.time = time

    def to_point(self, unit: str):
        """Convert to InfluxDB point dict.

        Args:
            unit: Name of the physical unit this measurement belongs to.
        """
        dict = {
            "measurement": Datapoint.format_for_table_name(unit, self.parameter),
            "tags": {
                "physical_unit": unit,
                "parameter": self.parameter
            },
            "fields": {
                "value": self.value,
                "units": self.units
            },
            "time": self.time
        }
        if self.confidence is not None:
            dict["fields"]["confidence"] = str(self.confidence)
        return dict

to_point(unit)

Convert to InfluxDB point dict.

Parameters:

Name Type Description Default
unit str

Name of the physical unit this measurement belongs to.

required
Source code in pt/controller_3/src/plant_controller/datapoint.py
def to_point(self, unit: str):
    """Convert to InfluxDB point dict.

    Args:
        unit: Name of the physical unit this measurement belongs to.
    """
    dict = {
        "measurement": Datapoint.format_for_table_name(unit, self.parameter),
        "tags": {
            "physical_unit": unit,
            "parameter": self.parameter
        },
        "fields": {
            "value": self.value,
            "units": self.units
        },
        "time": self.time
    }
    if self.confidence is not None:
        dict["fields"]["confidence"] = str(self.confidence)
    return dict

WateringEvent

Bases: Datapoint

A record of a watering actuation event.

Attributes:

Name Type Description
dosage

Amount of water pumped in milliliters.

time

Timestamp of the event (defaults to now).

Source code in pt/controller_3/src/plant_controller/datapoint.py
class WateringEvent(Datapoint):
    """A record of a watering actuation event.

    Attributes:
        dosage: Amount of water pumped in milliliters.
        time: Timestamp of the event (defaults to now).
    """

    def __init__(
        self,
        dosage: int,
        time: None | datetime = None
    ):
        self.dosage = dosage
        if time is None:
            time = datetime.now()
        self.time = time

    def to_point(self, unit: str):
        """Convert to InfluxDB point dict.

        Args:
            unit: Name of the physical unit this event belongs to.
        """
        return {
            "measurement": Datapoint.format_for_table_name(unit, "watering"),
            "tags": {"physical_unit": unit},
            "fields": {
                "value": self.dosage,
                "units": "ml"
            },
            "time": self.time
        }

to_point(unit)

Convert to InfluxDB point dict.

Parameters:

Name Type Description Default
unit str

Name of the physical unit this event belongs to.

required
Source code in pt/controller_3/src/plant_controller/datapoint.py
def to_point(self, unit: str):
    """Convert to InfluxDB point dict.

    Args:
        unit: Name of the physical unit this event belongs to.
    """
    return {
        "measurement": Datapoint.format_for_table_name(unit, "watering"),
        "tags": {"physical_unit": unit},
        "fields": {
            "value": self.dosage,
            "units": "ml"
        },
        "time": self.time
    }