selectors.py 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import logging
  2. import re
  3. import dataclasses
  4. from .common import MatchSeries
  5. from structures.measurement import Measurement24v, Measurement480v
  6. from structures.plant import S7State, CompactLogixState
  7. from structures.correlated import CorrelatedMeasurements
  8. from structures.common import BaseMeasurement
  9. logger = logging.getLogger(__name__)
  10. ALLOWED_NAMES = \
  11. [x.name for x in dataclasses.fields(Measurement24v)] + \
  12. [x.name for x in dataclasses.fields(Measurement480v)] + \
  13. [x.name for x in dataclasses.fields(CompactLogixState)] + \
  14. [x.name for x in dataclasses.fields(S7State)] + \
  15. [x.name for x in dataclasses.fields(CorrelatedMeasurements)] + \
  16. ['sum', 'min', 'max', 'avg', 'count', 'last']
  17. ALLOWED_NAMES = set([name for name in ALLOWED_NAMES if not name.startswith('_')])
  18. @dataclasses.dataclass(frozen=True)
  19. class Selection(BaseMeasurement):
  20. value: str
  21. class ComplexSelector():
  22. def __init__(self, parent, selector) -> None:
  23. self._selector = selector
  24. self._compiled = compile(selector, "<string>", "eval")
  25. # Validate allowed names
  26. for name in self._compiled.co_names:
  27. if name not in ALLOWED_NAMES:
  28. raise NameError(f"The use of '{name}' is not allowed in '{selector}'")
  29. def execute(self, values):
  30. for measurement in values:
  31. try:
  32. value = eval(self._compiled, {"__builtins__": {
  33. 'sum': sum,
  34. 'min': min,
  35. 'max': max,
  36. 'avg': lambda x: sum(x) / len(x),
  37. 'count': len,
  38. 'last': lambda x: x[-1],
  39. }}, measurement.__dict__)
  40. yield Selection(
  41. timestamp = measurement.timestamp,
  42. series = re.match(r"[\w_\.\-\(\)]+", self._selector).group(0),
  43. source = "selection",
  44. value=value
  45. )
  46. except Exception as e:
  47. logger.error(f"Error while evaluating selector '{self._selector}': {e}")