diff --git a/python/lib/sift_py/ingestion/channel.py b/python/lib/sift_py/ingestion/channel.py index ff08e77c..a1bd3914 100644 --- a/python/lib/sift_py/ingestion/channel.py +++ b/python/lib/sift_py/ingestion/channel.py @@ -89,7 +89,7 @@ def from_pb(cls, message: ChannelConfigPb) -> Self: def fqn(self) -> str: """ - The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The + The fully-qualified channel name of a channel called 'voltage' is simply `voltage`. The fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. """ return channel_fqn(self) diff --git a/python/lib/sift_py/ingestion/config/telemetry.py b/python/lib/sift_py/ingestion/config/telemetry.py index 70bcde4d..94bc1ad2 100644 --- a/python/lib/sift_py/ingestion/config/telemetry.py +++ b/python/lib/sift_py/ingestion/config/telemetry.py @@ -17,6 +17,7 @@ from sift_py.ingestion.flow import FlowConfig from sift_py.ingestion.rule.config import ( ExpressionChannelReference, + ExpressionChannelReferenceChannelConfig, RuleAction, RuleActionAnnotationKind, RuleActionCreateDataReviewAnnotation, @@ -191,7 +192,9 @@ def _from_yaml( tags=tags, ) - channel_references: List[ExpressionChannelReference] = [] + channel_references: List[ + ExpressionChannelReference | ExpressionChannelReferenceChannelConfig + ] = [] for channel_reference in rule.get("channel_references", []): for ref, val in channel_reference.items(): diff --git a/python/lib/sift_py/ingestion/rule/config.py b/python/lib/sift_py/ingestion/rule/config.py index 613d1d70..3597501e 100644 --- a/python/lib/sift_py/ingestion/rule/config.py +++ b/python/lib/sift_py/ingestion/rule/config.py @@ -2,12 +2,13 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import Any, Dict, List, Optional, TypedDict +from typing import Any, Dict, List, Optional, TypedDict, cast from sift.annotations.v1.annotations_pb2 import AnnotationType from sift.rules.v1.rules_pb2 import ActionKind from sift_py._internal.convert.json import AsJson +from sift_py.ingestion.channel import ChannelConfig class RuleConfig(AsJson): @@ -40,13 +41,38 @@ def __init__( description: str, expression: str, action: RuleAction, - channel_references: List[ExpressionChannelReference], + channel_references: List[ + ExpressionChannelReference | ExpressionChannelReferenceChannelConfig + ], sub_expressions: Dict[str, Any] = {}, ): + self.channel_references = [] + + for channel_reference in channel_references: + config = channel_reference.get("channel_config") + + if config is not None: + config = cast(ChannelConfig, config) + + self.channel_references.append( + { + "channel_reference": channel_reference["channel_reference"], + "channel_identifier": config.fqn(), + } + ) + else: + channel_ref = cast(ExpressionChannelReference, channel_reference) + + self.channel_references.append( + { + "channel_reference": channel_ref["channel_reference"], + "channel_identifier": channel_ref["channel_identifier"], + } + ) + self.name = name self.description = description self.action = action - self.channel_references = channel_references self.expression = self.__class__.interpolate_sub_expressions(expression, sub_expressions) def as_json(self) -> Any: @@ -182,9 +208,19 @@ class RuleActionKindStrRep(Enum): class ExpressionChannelReference(TypedDict): """ - `reference`: The channel reference (e.g. '$1') used in the expression. - `identifier`: The fully qualified channel name. See `sift_py.ingestion.channel.channel_fqn`. + `channel_reference`: The channel reference (e.g. '$1') used in the expression. + `channel_identifier`: The fully qualified channel name. See `sift_py.ingestion.channel.channel_fqn`. """ channel_reference: str channel_identifier: str + + +class ExpressionChannelReferenceChannelConfig(TypedDict): + """ + `channel_reference`: The channel reference (e.g. '$1') used in the expression. + `channel_config`: Instance of `sift_py.ingestion.channel.ChannelConfig`. + """ + + channel_reference: str + channel_config: ChannelConfig diff --git a/python/lib/sift_py/ingestion/rule/config_test.py b/python/lib/sift_py/ingestion/rule/config_test.py index 0c7ebfad..8ebc6cca 100644 --- a/python/lib/sift_py/ingestion/rule/config_test.py +++ b/python/lib/sift_py/ingestion/rule/config_test.py @@ -14,12 +14,15 @@ def test_rule_config_json(): description="Rock & Roll", expression=voltage_rule_expression, action=RuleActionCreatePhaseAnnotation(), - channel_references={ - "$1": ChannelConfig( - name="voltage", - data_type=ChannelDataType.DOUBLE, - ), - }, + channel_references=[ + { + "channel_reference": "$1", + "channel_config": ChannelConfig( + name="voltage", + data_type=ChannelDataType.DOUBLE, + ), + } + ], ) assert voltage_rule_config.expression == voltage_rule_expression @@ -32,18 +35,24 @@ def test_rule_config_json(): tags=["foo", "bar"], assignee="foobar@baz.com", ), - channel_references={ - "$1": ChannelConfig( - name="vehicle_state", - component="motor", - data_type=ChannelDataType.INT_32, - ), - "$2": ChannelConfig( - name="temperature", - component="motor", - data_type=ChannelDataType.INT_32, - ), - }, + channel_references=[ + { + "channel_reference": "$1", + "channel_config": ChannelConfig( + name="vehicle_state", + component="motor", + data_type=ChannelDataType.INT_32, + ), + }, + { + "channel_reference": "$2", + "channel_config": ChannelConfig( + name="temperature", + component="motor", + data_type=ChannelDataType.INT_32, + ), + }, + ], sub_expressions={ "$3": 80, }, @@ -59,12 +68,15 @@ def test_rule_config_json(): tags=["foo", "bar"], assignee="foobar@baz.com", ), - channel_references={ - "$1": ChannelConfig( - name="log", - data_type=ChannelDataType.INT_32, - ), - }, + channel_references=[ + { + "channel_reference": "$1", + "channel_config": ChannelConfig( + name="log", + data_type=ChannelDataType.INT_32, + ), + }, + ], sub_expressions={ "$2": "Error", }, @@ -80,12 +92,15 @@ def test_rule_named_expressions(): description="checks high periods of energy output", expression=kinetic_energy_gt_expression, action=RuleActionCreatePhaseAnnotation(), - channel_references={ - "$1": ChannelConfig( - name="log", - data_type=ChannelDataType.INT_32, - ), - }, + channel_references=[ + { + "channel_reference": "$1", + "channel_config": ChannelConfig( + name="velocity", + data_type=ChannelDataType.INT_32, + ), + }, + ], sub_expressions={ "$mass": 10, "$threshold": 35,