3838
3939_logger = logging .getLogger (__name__ )
4040
41+
42+ class _M :
43+ """Plain holder for the `Metric` members matched in `from_samples`.
44+
45+ `Metric` is an `Enum`, and `Enum.<MEMBER>` access goes through the slow
46+ `EnumType.__getattribute__`/`_name_map` machinery. The `from_samples`
47+ `match`/`case` blocks evaluate one such access *per case, per sample, per
48+ component, on every resampling tick* — which profiling showed to be a
49+ dominant CPU cost when consuming high-rate telemetry. Resolving the
50+ members once here, into a plain class, lets the `case _M.<MEMBER>`
51+ patterns use ordinary (fast) attribute access instead.
52+ """
53+
54+ AC_ACTIVE_POWER = Metric .AC_ACTIVE_POWER
55+ AC_ACTIVE_POWER_PHASE_1 = Metric .AC_ACTIVE_POWER_PHASE_1
56+ AC_ACTIVE_POWER_PHASE_2 = Metric .AC_ACTIVE_POWER_PHASE_2
57+ AC_ACTIVE_POWER_PHASE_3 = Metric .AC_ACTIVE_POWER_PHASE_3
58+ AC_REACTIVE_POWER = Metric .AC_REACTIVE_POWER
59+ AC_REACTIVE_POWER_PHASE_1 = Metric .AC_REACTIVE_POWER_PHASE_1
60+ AC_REACTIVE_POWER_PHASE_2 = Metric .AC_REACTIVE_POWER_PHASE_2
61+ AC_REACTIVE_POWER_PHASE_3 = Metric .AC_REACTIVE_POWER_PHASE_3
62+ AC_CURRENT_PHASE_1 = Metric .AC_CURRENT_PHASE_1
63+ AC_CURRENT_PHASE_2 = Metric .AC_CURRENT_PHASE_2
64+ AC_CURRENT_PHASE_3 = Metric .AC_CURRENT_PHASE_3
65+ AC_VOLTAGE_PHASE_1_N = Metric .AC_VOLTAGE_PHASE_1_N
66+ AC_VOLTAGE_PHASE_2_N = Metric .AC_VOLTAGE_PHASE_2_N
67+ AC_VOLTAGE_PHASE_3_N = Metric .AC_VOLTAGE_PHASE_3_N
68+ AC_FREQUENCY = Metric .AC_FREQUENCY
69+ DC_POWER = Metric .DC_POWER
70+ BATTERY_SOC_PCT = Metric .BATTERY_SOC_PCT
71+ BATTERY_CAPACITY = Metric .BATTERY_CAPACITY
72+ BATTERY_TEMPERATURE = Metric .BATTERY_TEMPERATURE
73+
74+
4175T = TypeVar ("T" , bound = "ComponentData" )
4276
4377PhaseTuple : TypeAlias = tuple [float , float , float ]
@@ -314,35 +348,35 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
314348
315349 for sample in samples .metric_samples :
316350 match sample .metric :
317- case Metric .AC_ACTIVE_POWER :
351+ case _M .AC_ACTIVE_POWER :
318352 self .active_power = sample .as_single_value () or 0.0
319- case Metric .AC_ACTIVE_POWER_PHASE_1 :
353+ case _M .AC_ACTIVE_POWER_PHASE_1 :
320354 active_power_per_phase [0 ] = sample .as_single_value () or 0.0
321- case Metric .AC_ACTIVE_POWER_PHASE_2 :
355+ case _M .AC_ACTIVE_POWER_PHASE_2 :
322356 active_power_per_phase [1 ] = sample .as_single_value () or 0.0
323- case Metric .AC_ACTIVE_POWER_PHASE_3 :
357+ case _M .AC_ACTIVE_POWER_PHASE_3 :
324358 active_power_per_phase [2 ] = sample .as_single_value () or 0.0
325- case Metric .AC_REACTIVE_POWER_PHASE_1 :
359+ case _M .AC_REACTIVE_POWER_PHASE_1 :
326360 reactive_power_per_phase [0 ] = sample .as_single_value () or 0.0
327- case Metric .AC_REACTIVE_POWER_PHASE_2 :
361+ case _M .AC_REACTIVE_POWER_PHASE_2 :
328362 reactive_power_per_phase [1 ] = sample .as_single_value () or 0.0
329- case Metric .AC_REACTIVE_POWER_PHASE_3 :
363+ case _M .AC_REACTIVE_POWER_PHASE_3 :
330364 reactive_power_per_phase [2 ] = sample .as_single_value () or 0.0
331- case Metric .AC_REACTIVE_POWER :
365+ case _M .AC_REACTIVE_POWER :
332366 self .reactive_power = sample .as_single_value () or 0.0
333- case Metric .AC_CURRENT_PHASE_1 :
367+ case _M .AC_CURRENT_PHASE_1 :
334368 current_per_phase [0 ] = sample .as_single_value () or 0.0
335- case Metric .AC_CURRENT_PHASE_2 :
369+ case _M .AC_CURRENT_PHASE_2 :
336370 current_per_phase [1 ] = sample .as_single_value () or 0.0
337- case Metric .AC_CURRENT_PHASE_3 :
371+ case _M .AC_CURRENT_PHASE_3 :
338372 current_per_phase [2 ] = sample .as_single_value () or 0.0
339- case Metric .AC_VOLTAGE_PHASE_1_N :
373+ case _M .AC_VOLTAGE_PHASE_1_N :
340374 voltage_per_phase [0 ] = sample .as_single_value () or 0.0
341- case Metric .AC_VOLTAGE_PHASE_2_N :
375+ case _M .AC_VOLTAGE_PHASE_2_N :
342376 voltage_per_phase [1 ] = sample .as_single_value () or 0.0
343- case Metric .AC_VOLTAGE_PHASE_3_N :
377+ case _M .AC_VOLTAGE_PHASE_3_N :
344378 voltage_per_phase [2 ] = sample .as_single_value () or 0.0
345- case Metric .AC_FREQUENCY :
379+ case _M .AC_FREQUENCY :
346380 self .frequency = sample .as_single_value () or 0.0
347381 case unexpected :
348382 _logger .warning (
@@ -495,7 +529,7 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
495529 for sample in samples .metric_samples :
496530 value = sample .as_single_value () or 0.0
497531 match sample .metric :
498- case Metric .BATTERY_SOC_PCT :
532+ case _M .BATTERY_SOC_PCT :
499533 self .soc = value
500534 if sample .bounds :
501535 # Update power bounds from the SOC metric bounds,
@@ -514,7 +548,7 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
514548 )
515549 self .soc_lower_bound = sample .bounds [0 ].lower or 0.0
516550 self .soc_upper_bound = sample .bounds [0 ].upper or 0.0
517- case Metric .DC_POWER :
551+ case _M .DC_POWER :
518552 (
519553 self .power_inclusion_lower_bound ,
520554 self .power_inclusion_upper_bound ,
@@ -523,9 +557,9 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
523557 ) = _bound_ranges_to_inclusion_exclusion (
524558 sample .bounds , "DC_POWER" , sample
525559 )
526- case Metric .BATTERY_CAPACITY :
560+ case _M .BATTERY_CAPACITY :
527561 self .capacity = value
528- case Metric .BATTERY_TEMPERATURE :
562+ case _M .BATTERY_TEMPERATURE :
529563 self .temperature = value
530564 case unexpected :
531565 _logger .warning (
@@ -715,7 +749,7 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
715749 for sample in samples .metric_samples :
716750 value = sample .as_single_value () or 0.0
717751 match sample .metric :
718- case Metric .AC_ACTIVE_POWER :
752+ case _M .AC_ACTIVE_POWER :
719753 self .active_power = value
720754 (
721755 self .active_power_inclusion_lower_bound ,
@@ -725,33 +759,33 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
725759 ) = _bound_ranges_to_inclusion_exclusion (
726760 sample .bounds , "AC_ACTIVE_POWER" , sample
727761 )
728- case Metric .AC_ACTIVE_POWER_PHASE_1 :
762+ case _M .AC_ACTIVE_POWER_PHASE_1 :
729763 active_power_per_phase [0 ] = value
730- case Metric .AC_ACTIVE_POWER_PHASE_2 :
764+ case _M .AC_ACTIVE_POWER_PHASE_2 :
731765 active_power_per_phase [1 ] = value
732- case Metric .AC_ACTIVE_POWER_PHASE_3 :
766+ case _M .AC_ACTIVE_POWER_PHASE_3 :
733767 active_power_per_phase [2 ] = value
734- case Metric .AC_REACTIVE_POWER :
768+ case _M .AC_REACTIVE_POWER :
735769 self .reactive_power = value
736- case Metric .AC_REACTIVE_POWER_PHASE_1 :
770+ case _M .AC_REACTIVE_POWER_PHASE_1 :
737771 reactive_power_per_phase [0 ] = value
738- case Metric .AC_REACTIVE_POWER_PHASE_2 :
772+ case _M .AC_REACTIVE_POWER_PHASE_2 :
739773 reactive_power_per_phase [1 ] = value
740- case Metric .AC_REACTIVE_POWER_PHASE_3 :
774+ case _M .AC_REACTIVE_POWER_PHASE_3 :
741775 reactive_power_per_phase [2 ] = value
742- case Metric .AC_CURRENT_PHASE_1 :
776+ case _M .AC_CURRENT_PHASE_1 :
743777 current_per_phase [0 ] = value
744- case Metric .AC_CURRENT_PHASE_2 :
778+ case _M .AC_CURRENT_PHASE_2 :
745779 current_per_phase [1 ] = value
746- case Metric .AC_CURRENT_PHASE_3 :
780+ case _M .AC_CURRENT_PHASE_3 :
747781 current_per_phase [2 ] = value
748- case Metric .AC_VOLTAGE_PHASE_1_N :
782+ case _M .AC_VOLTAGE_PHASE_1_N :
749783 voltage_per_phase [0 ] = value
750- case Metric .AC_VOLTAGE_PHASE_2_N :
784+ case _M .AC_VOLTAGE_PHASE_2_N :
751785 voltage_per_phase [1 ] = value
752- case Metric .AC_VOLTAGE_PHASE_3_N :
786+ case _M .AC_VOLTAGE_PHASE_3_N :
753787 voltage_per_phase [2 ] = value
754- case Metric .AC_FREQUENCY :
788+ case _M .AC_FREQUENCY :
755789 self .frequency = value
756790 case unexpected :
757791 _logger .warning (
@@ -974,7 +1008,7 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
9741008 for sample in samples .metric_samples :
9751009 value = sample .as_single_value () or 0.0
9761010 match sample .metric :
977- case Metric .AC_ACTIVE_POWER :
1011+ case _M .AC_ACTIVE_POWER :
9781012 self .active_power = value
9791013 (
9801014 self .active_power_inclusion_lower_bound ,
@@ -984,33 +1018,33 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
9841018 ) = _bound_ranges_to_inclusion_exclusion (
9851019 sample .bounds , "AC_ACTIVE_POWER" , sample
9861020 )
987- case Metric .AC_ACTIVE_POWER_PHASE_1 :
1021+ case _M .AC_ACTIVE_POWER_PHASE_1 :
9881022 active_power_per_phase [0 ] = value
989- case Metric .AC_ACTIVE_POWER_PHASE_2 :
1023+ case _M .AC_ACTIVE_POWER_PHASE_2 :
9901024 active_power_per_phase [1 ] = value
991- case Metric .AC_ACTIVE_POWER_PHASE_3 :
1025+ case _M .AC_ACTIVE_POWER_PHASE_3 :
9921026 active_power_per_phase [2 ] = value
993- case Metric .AC_REACTIVE_POWER :
1027+ case _M .AC_REACTIVE_POWER :
9941028 self .reactive_power = value
995- case Metric .AC_REACTIVE_POWER_PHASE_1 :
1029+ case _M .AC_REACTIVE_POWER_PHASE_1 :
9961030 reactive_power_per_phase [0 ] = value
997- case Metric .AC_REACTIVE_POWER_PHASE_2 :
1031+ case _M .AC_REACTIVE_POWER_PHASE_2 :
9981032 reactive_power_per_phase [1 ] = value
999- case Metric .AC_REACTIVE_POWER_PHASE_3 :
1033+ case _M .AC_REACTIVE_POWER_PHASE_3 :
10001034 reactive_power_per_phase [2 ] = value
1001- case Metric .AC_CURRENT_PHASE_1 :
1035+ case _M .AC_CURRENT_PHASE_1 :
10021036 current_per_phase [0 ] = value
1003- case Metric .AC_CURRENT_PHASE_2 :
1037+ case _M .AC_CURRENT_PHASE_2 :
10041038 current_per_phase [1 ] = value
1005- case Metric .AC_CURRENT_PHASE_3 :
1039+ case _M .AC_CURRENT_PHASE_3 :
10061040 current_per_phase [2 ] = value
1007- case Metric .AC_VOLTAGE_PHASE_1_N :
1041+ case _M .AC_VOLTAGE_PHASE_1_N :
10081042 voltage_per_phase [0 ] = value
1009- case Metric .AC_VOLTAGE_PHASE_2_N :
1043+ case _M .AC_VOLTAGE_PHASE_2_N :
10101044 voltage_per_phase [1 ] = value
1011- case Metric .AC_VOLTAGE_PHASE_3_N :
1045+ case _M .AC_VOLTAGE_PHASE_3_N :
10121046 voltage_per_phase [2 ] = value
1013- case Metric .AC_FREQUENCY :
1047+ case _M .AC_FREQUENCY :
10141048 self .frequency = value
10151049 case unexpected :
10161050 _logger .warning (
@@ -1234,7 +1268,7 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
12341268 for sample in samples .metric_samples :
12351269 value = sample .as_single_value () or 0.0
12361270 match sample .metric :
1237- case Metric .AC_ACTIVE_POWER :
1271+ case _M .AC_ACTIVE_POWER :
12381272 self .active_power = value
12391273 (
12401274 self .active_power_inclusion_lower_bound ,
@@ -1244,33 +1278,33 @@ def from_samples(cls, samples: ComponentDataSamples) -> Self:
12441278 ) = _bound_ranges_to_inclusion_exclusion (
12451279 sample .bounds , "AC_ACTIVE_POWER" , sample
12461280 )
1247- case Metric .AC_ACTIVE_POWER_PHASE_1 :
1281+ case _M .AC_ACTIVE_POWER_PHASE_1 :
12481282 active_power_per_phase [0 ] = value
1249- case Metric .AC_ACTIVE_POWER_PHASE_2 :
1283+ case _M .AC_ACTIVE_POWER_PHASE_2 :
12501284 active_power_per_phase [1 ] = value
1251- case Metric .AC_ACTIVE_POWER_PHASE_3 :
1285+ case _M .AC_ACTIVE_POWER_PHASE_3 :
12521286 active_power_per_phase [2 ] = value
1253- case Metric .AC_REACTIVE_POWER :
1287+ case _M .AC_REACTIVE_POWER :
12541288 self .reactive_power = value
1255- case Metric .AC_REACTIVE_POWER_PHASE_1 :
1289+ case _M .AC_REACTIVE_POWER_PHASE_1 :
12561290 reactive_power_per_phase [0 ] = value
1257- case Metric .AC_REACTIVE_POWER_PHASE_2 :
1291+ case _M .AC_REACTIVE_POWER_PHASE_2 :
12581292 reactive_power_per_phase [1 ] = value
1259- case Metric .AC_REACTIVE_POWER_PHASE_3 :
1293+ case _M .AC_REACTIVE_POWER_PHASE_3 :
12601294 reactive_power_per_phase [2 ] = value
1261- case Metric .AC_CURRENT_PHASE_1 :
1295+ case _M .AC_CURRENT_PHASE_1 :
12621296 current_per_phase [0 ] = value
1263- case Metric .AC_CURRENT_PHASE_2 :
1297+ case _M .AC_CURRENT_PHASE_2 :
12641298 current_per_phase [1 ] = value
1265- case Metric .AC_CURRENT_PHASE_3 :
1299+ case _M .AC_CURRENT_PHASE_3 :
12661300 current_per_phase [2 ] = value
1267- case Metric .AC_VOLTAGE_PHASE_1_N :
1301+ case _M .AC_VOLTAGE_PHASE_1_N :
12681302 voltage_per_phase [0 ] = value
1269- case Metric .AC_VOLTAGE_PHASE_2_N :
1303+ case _M .AC_VOLTAGE_PHASE_2_N :
12701304 voltage_per_phase [1 ] = value
1271- case Metric .AC_VOLTAGE_PHASE_3_N :
1305+ case _M .AC_VOLTAGE_PHASE_3_N :
12721306 voltage_per_phase [2 ] = value
1273- case Metric .AC_FREQUENCY :
1307+ case _M .AC_FREQUENCY :
12741308 self .frequency = value
12751309 case unexpected :
12761310 _logger .warning (
0 commit comments