PyAutomationIO 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. automation/__init__.py +46 -0
  2. automation/alarms/__init__.py +563 -0
  3. automation/alarms/states.py +192 -0
  4. automation/alarms/trigger.py +64 -0
  5. automation/buffer.py +132 -0
  6. automation/core.py +1792 -0
  7. automation/dbmodels/__init__.py +23 -0
  8. automation/dbmodels/alarms.py +549 -0
  9. automation/dbmodels/core.py +86 -0
  10. automation/dbmodels/events.py +178 -0
  11. automation/dbmodels/logs.py +155 -0
  12. automation/dbmodels/machines.py +181 -0
  13. automation/dbmodels/opcua.py +81 -0
  14. automation/dbmodels/opcua_server.py +174 -0
  15. automation/dbmodels/tags.py +921 -0
  16. automation/dbmodels/users.py +259 -0
  17. automation/extensions/__init__.py +15 -0
  18. automation/extensions/api.py +149 -0
  19. automation/extensions/cors.py +18 -0
  20. automation/filter/__init__.py +19 -0
  21. automation/iad/__init__.py +3 -0
  22. automation/iad/frozen_data.py +54 -0
  23. automation/iad/out_of_range.py +51 -0
  24. automation/iad/outliers.py +51 -0
  25. automation/logger/__init__.py +0 -0
  26. automation/logger/alarms.py +434 -0
  27. automation/logger/core.py +265 -0
  28. automation/logger/datalogger.py +877 -0
  29. automation/logger/events.py +202 -0
  30. automation/logger/logdict.py +53 -0
  31. automation/logger/logs.py +203 -0
  32. automation/logger/machines.py +248 -0
  33. automation/logger/opcua_server.py +130 -0
  34. automation/logger/users.py +96 -0
  35. automation/managers/__init__.py +4 -0
  36. automation/managers/alarms.py +455 -0
  37. automation/managers/db.py +328 -0
  38. automation/managers/opcua_client.py +186 -0
  39. automation/managers/state_machine.py +183 -0
  40. automation/models.py +174 -0
  41. automation/modules/__init__.py +14 -0
  42. automation/modules/alarms/__init__.py +0 -0
  43. automation/modules/alarms/resources/__init__.py +10 -0
  44. automation/modules/alarms/resources/alarms.py +280 -0
  45. automation/modules/alarms/resources/summary.py +81 -0
  46. automation/modules/events/__init__.py +0 -0
  47. automation/modules/events/resources/__init__.py +10 -0
  48. automation/modules/events/resources/events.py +85 -0
  49. automation/modules/events/resources/logs.py +109 -0
  50. automation/modules/tags/__init__.py +0 -0
  51. automation/modules/tags/resources/__init__.py +8 -0
  52. automation/modules/tags/resources/tags.py +254 -0
  53. automation/modules/users/__init__.py +2 -0
  54. automation/modules/users/resources/__init__.py +10 -0
  55. automation/modules/users/resources/models/__init__.py +2 -0
  56. automation/modules/users/resources/models/roles.py +5 -0
  57. automation/modules/users/resources/models/users.py +14 -0
  58. automation/modules/users/resources/roles.py +38 -0
  59. automation/modules/users/resources/users.py +113 -0
  60. automation/modules/users/roles.py +121 -0
  61. automation/modules/users/users.py +335 -0
  62. automation/opcua/__init__.py +1 -0
  63. automation/opcua/models.py +541 -0
  64. automation/opcua/subscription.py +259 -0
  65. automation/pages/__init__.py +0 -0
  66. automation/pages/alarms.py +34 -0
  67. automation/pages/alarms_history.py +21 -0
  68. automation/pages/assets/styles.css +7 -0
  69. automation/pages/callbacks/__init__.py +28 -0
  70. automation/pages/callbacks/alarms.py +218 -0
  71. automation/pages/callbacks/alarms_summary.py +20 -0
  72. automation/pages/callbacks/db.py +222 -0
  73. automation/pages/callbacks/filter.py +238 -0
  74. automation/pages/callbacks/machines.py +29 -0
  75. automation/pages/callbacks/machines_detailed.py +581 -0
  76. automation/pages/callbacks/opcua.py +266 -0
  77. automation/pages/callbacks/opcua_server.py +244 -0
  78. automation/pages/callbacks/tags.py +495 -0
  79. automation/pages/callbacks/trends.py +119 -0
  80. automation/pages/communications.py +129 -0
  81. automation/pages/components/__init__.py +123 -0
  82. automation/pages/components/alarms.py +151 -0
  83. automation/pages/components/alarms_summary.py +45 -0
  84. automation/pages/components/database.py +128 -0
  85. automation/pages/components/gaussian_filter.py +69 -0
  86. automation/pages/components/machines.py +396 -0
  87. automation/pages/components/opcua.py +384 -0
  88. automation/pages/components/opcua_server.py +53 -0
  89. automation/pages/components/tags.py +253 -0
  90. automation/pages/components/trends.py +66 -0
  91. automation/pages/database.py +26 -0
  92. automation/pages/filter.py +55 -0
  93. automation/pages/machines.py +20 -0
  94. automation/pages/machines_detailed.py +41 -0
  95. automation/pages/main.py +63 -0
  96. automation/pages/opcua_server.py +28 -0
  97. automation/pages/tags.py +40 -0
  98. automation/pages/trends.py +35 -0
  99. automation/singleton.py +30 -0
  100. automation/state_machine.py +1674 -0
  101. automation/tags/__init__.py +2 -0
  102. automation/tags/cvt.py +1198 -0
  103. automation/tags/filter.py +55 -0
  104. automation/tags/tag.py +418 -0
  105. automation/tests/__init__.py +10 -0
  106. automation/tests/test_alarms.py +110 -0
  107. automation/tests/test_core.py +257 -0
  108. automation/tests/test_unit.py +21 -0
  109. automation/tests/test_user.py +155 -0
  110. automation/utils/__init__.py +164 -0
  111. automation/utils/decorators.py +222 -0
  112. automation/utils/npw.py +294 -0
  113. automation/utils/observer.py +21 -0
  114. automation/utils/units.py +118 -0
  115. automation/variables/__init__.py +55 -0
  116. automation/variables/adimentional.py +30 -0
  117. automation/variables/current.py +71 -0
  118. automation/variables/density.py +115 -0
  119. automation/variables/eng_time.py +68 -0
  120. automation/variables/force.py +90 -0
  121. automation/variables/length.py +104 -0
  122. automation/variables/mass.py +80 -0
  123. automation/variables/mass_flow.py +101 -0
  124. automation/variables/percentage.py +30 -0
  125. automation/variables/power.py +113 -0
  126. automation/variables/pressure.py +93 -0
  127. automation/variables/temperature.py +168 -0
  128. automation/variables/volume.py +70 -0
  129. automation/variables/volumetric_flow.py +100 -0
  130. automation/workers/__init__.py +2 -0
  131. automation/workers/logger.py +164 -0
  132. automation/workers/state_machine.py +207 -0
  133. automation/workers/worker.py +36 -0
  134. pyautomationio-1.1.1.dist-info/METADATA +199 -0
  135. pyautomationio-1.1.1.dist-info/RECORD +138 -0
  136. pyautomationio-1.1.1.dist-info/WHEEL +5 -0
  137. pyautomationio-1.1.1.dist-info/licenses/LICENSE +21 -0
  138. pyautomationio-1.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,259 @@
1
+ import secrets
2
+ from peewee import CharField, IntegerField, ForeignKeyField
3
+ from ..dbmodels.core import BaseModel
4
+ from ..modules.users.users import Users as CVTUsers
5
+ from ..modules.users.roles import Roles as CVTRoles
6
+ from ..modules.users.roles import Role
7
+ from ..modules.users.users import User
8
+ from werkzeug.security import generate_password_hash, check_password_hash
9
+
10
+ users = CVTUsers()
11
+ roles = CVTRoles()
12
+
13
+ class Roles(BaseModel):
14
+
15
+ identifier = CharField(unique=True, max_length=16)
16
+ name = CharField(unique=True, max_length=32)
17
+ level = IntegerField()
18
+ __defaults__ = [
19
+ {"name": "sudo", "level": 0, "identifier": secrets.token_hex(4)},
20
+ {"name": "admin", "level": 1, "identifier": secrets.token_hex(4)},
21
+ {"name": "supervisor", "level": 2, "identifier": secrets.token_hex(4)},
22
+ {"name": "operator", "level": 10, "identifier": secrets.token_hex(4)},
23
+ {"name": "auditor", "level": 100, "identifier": secrets.token_hex(4)},
24
+ {"name": "guest", "level": 256, "identifier": secrets.token_hex(4)}
25
+ ]
26
+
27
+ @classmethod
28
+ def create(cls, name:str, level:int, identifier:str)->tuple:
29
+
30
+ name = name.upper()
31
+
32
+ if cls.name_exist(name):
33
+
34
+ return None, f"role {name} is already used"
35
+
36
+ if cls.identifier_exist(identifier):
37
+
38
+ return None, f"identifier {identifier} is already used"
39
+
40
+ query = cls(name=name, level=level, identifier=identifier)
41
+ query.save()
42
+
43
+ return query, f"Role creation successful"
44
+
45
+ @classmethod
46
+ def read_by_name(cls, name:str):
47
+
48
+ return cls.get_or_none(name=name.upper())
49
+
50
+ @classmethod
51
+ def read_by_identifier(cls, identifier:str):
52
+
53
+ return cls.get_or_none(identifier=identifier)
54
+
55
+ @classmethod
56
+ def name_exist(cls, name:str)->bool:
57
+
58
+ return True if cls.get_or_none(name=name.upper()) else False
59
+
60
+ @classmethod
61
+ def identifier_exist(cls, identifier:str)->bool:
62
+
63
+ return True if cls.get_or_none(identifier=identifier) else False
64
+
65
+ @classmethod
66
+ def read_names(cls)->list:
67
+
68
+ return [role.name for role in cls.select()]
69
+
70
+ @classmethod
71
+ def fill_cvt_roles(cls):
72
+ r"""
73
+ Documentation here
74
+ """
75
+ for role in cls.select():
76
+
77
+ _role = Role(
78
+ name=role.name,
79
+ level=role.level,
80
+ identifier=role.identifier
81
+ )
82
+
83
+ roles.add(role=_role)
84
+
85
+ def serialize(self)->dict:
86
+ r"""
87
+ Serialize database record to a jsonable object
88
+ """
89
+
90
+ return {
91
+ "id": self.id,
92
+ "identifier": self.identifier,
93
+ "name": self.name,
94
+ "level": self.level
95
+ }
96
+
97
+
98
+ class Users(BaseModel):
99
+
100
+ identifier = CharField(unique=True, max_length=16)
101
+ username = CharField(unique=True, max_length=64)
102
+ role = ForeignKeyField(Roles, backref='users', on_delete='CASCADE')
103
+ email = CharField(unique=True, max_length=128)
104
+ password = CharField()
105
+ token = CharField(null=True)
106
+ name = CharField(max_length=64, null=True)
107
+ lastname = CharField(max_length=64, null=True)
108
+
109
+ @classmethod
110
+ def create(cls, user:User)-> dict:
111
+
112
+ if cls.username_exist(user.username):
113
+
114
+ return None, f"username {user.username} is already used"
115
+
116
+ if cls.email_exist(user.email):
117
+
118
+ return None, f"email {user.email} is already used"
119
+
120
+ if cls.identifier_exist(user.identifier):
121
+
122
+ return None, f"identifier {user.identifier} is already used"
123
+
124
+ query = cls(
125
+ username=user.username,
126
+ role=Roles.read_by_name(name=user.role.name),
127
+ email=user.email,
128
+ password=user.password,
129
+ identifier=user.identifier,
130
+ name=user.name,
131
+ lastname=user.lastname,
132
+ token=user.token
133
+ )
134
+ query.save()
135
+
136
+ return query, f"User creation successful"
137
+
138
+ @classmethod
139
+ def login(cls, password:str, username:str="", email:str=""):
140
+ r"""
141
+ Documentation here
142
+ """
143
+ if username:
144
+
145
+ user = cls.get_or_none(username=username)
146
+
147
+ if email:
148
+
149
+ user = cls.get_or_none(email=email)
150
+
151
+ if user:
152
+
153
+ if user.decode_password(password):
154
+
155
+ if not user.token:
156
+
157
+ user.token = cls.encode(secrets.token_hex(4))
158
+ user.save()
159
+
160
+ users.login(password=password, token=user.token, username=username, email=email)
161
+
162
+ return user, f"Login successfull"
163
+
164
+ return None, f"Invalid credentials"
165
+
166
+ return None, f"Invalid Username or Email"
167
+
168
+ @classmethod
169
+ def logout(cls, token:str):
170
+ r"""
171
+ Documentation here
172
+ """
173
+ user = cls.get_or_none(token=token)
174
+
175
+ if user:
176
+
177
+ user.token = None
178
+ user.save()
179
+
180
+ return user, "Logout successfull"
181
+
182
+ return None, "Invalid Token"
183
+
184
+ @classmethod
185
+ def read_by_username(cls, username:str):
186
+
187
+ return cls.get_or_none(username=username)
188
+
189
+ @classmethod
190
+ def read_by_name(cls, name:str):
191
+
192
+ return cls.get_or_none(name=name)
193
+
194
+ @classmethod
195
+ def name_exist(cls, name:str)->bool:
196
+
197
+ return True if cls.get_or_none(name=name) else False
198
+
199
+ @classmethod
200
+ def username_exist(cls, username:str)->bool:
201
+
202
+ return True if cls.get_or_none(username=username) else False
203
+
204
+ @classmethod
205
+ def email_exist(cls, email:str)->bool:
206
+
207
+ return True if cls.get_or_none(email=email) else False
208
+
209
+ @classmethod
210
+ def identifier_exist(cls, identifier:str)->bool:
211
+
212
+ return True if cls.get_or_none(identifier=identifier) else False
213
+
214
+ @classmethod
215
+ def encode(cls, value:str)->str:
216
+
217
+ return generate_password_hash(value)
218
+
219
+
220
+ def decode_password(self, password:str)->str:
221
+
222
+ return check_password_hash(self.password, password)
223
+
224
+ def decode_token(self, token:str)->str:
225
+
226
+ return check_password_hash(self.token, token)
227
+
228
+ @classmethod
229
+ def fill_cvt_users(cls):
230
+ r"""
231
+ Documentation here
232
+ """
233
+ for user in cls.select():
234
+
235
+ users.signup(
236
+ username=user.username,
237
+ role_name=user.role.name,
238
+ email=user.email,
239
+ password=user.password,
240
+ name=user.name,
241
+ lastname=user.lastname,
242
+ identifier=user.identifier,
243
+ encode_password=False
244
+ )
245
+
246
+ def serialize(self)-> dict:
247
+ r"""
248
+ Serialize database record to a jsonable object
249
+ """
250
+
251
+ return {
252
+ "id": self.id,
253
+ "identifier": self.identifier,
254
+ "username": self.username,
255
+ "email": self.email,
256
+ "role": self.role.serialize(),
257
+ "name": self.name,
258
+ "lastname": self.lastname
259
+ }
@@ -0,0 +1,15 @@
1
+ from .api import Api
2
+ from .cors import Cors
3
+
4
+ _api = Api()
5
+ _cors = Cors()
6
+
7
+ def init_app(app):
8
+ """
9
+ Application extensions initialization.
10
+ """
11
+ extensions = ( _api, _cors)
12
+
13
+ for extension in extensions:
14
+
15
+ extension.init_app(app)
@@ -0,0 +1,149 @@
1
+ from flask import Blueprint, request
2
+ from flask_restx import abort
3
+ from flask_restx import Api as API
4
+ from ..singleton import Singleton
5
+ from functools import wraps
6
+ import logging, jwt
7
+ from ..utils.decorators import decorator
8
+ from ..dbmodels.users import Users
9
+ from ..modules.users.users import Users as CVTUsers
10
+
11
+
12
+ authorizations = {
13
+ 'apikey' : {
14
+ 'type' : 'apiKey',
15
+ 'in' : 'header',
16
+ 'name' : 'X-API-KEY'
17
+ }
18
+ }
19
+
20
+
21
+ blueprint = Blueprint('api', __name__, url_prefix='/api')
22
+
23
+ api = API(blueprint, version='1.0',
24
+ title='PyAutomation API',
25
+ description="""
26
+ This API groups all namespaces defined in every module's resources for PyAutomation App.
27
+ """,
28
+ doc='/docs',
29
+ authorizations=authorizations
30
+ )
31
+
32
+ users = CVTUsers()
33
+
34
+ class Api(Singleton):
35
+
36
+ def __init__(self):
37
+
38
+ self.app = None
39
+
40
+ def init_app(self, app):
41
+ r"""
42
+ Documentation here
43
+ """
44
+ self.app = self.create_api(app)
45
+
46
+ return app
47
+
48
+ def create_api(self, app):
49
+ r"""
50
+ Documentation here
51
+ """
52
+ app.register_blueprint(blueprint)
53
+
54
+ return api
55
+
56
+ @staticmethod
57
+ def verify_tpt(tpt:str):
58
+ r"""
59
+ Verify Third Party Token
60
+ """
61
+ from .. import server
62
+ try:
63
+
64
+ jwt.decode(tpt, server.config["TPT_TOKEN"], algorithms=["HS256"])
65
+
66
+ return True
67
+
68
+ except:
69
+
70
+ return
71
+
72
+ @classmethod
73
+ def validate_reqparser(cls, reqparser):
74
+ def _validate_reqparser(f):
75
+
76
+ @wraps(f)
77
+ def decorated(*args, **kwargs):
78
+
79
+ reqparser.parse_args()
80
+ result = f(*args, **kwargs)
81
+ return result
82
+
83
+ return decorated
84
+
85
+ return _validate_reqparser
86
+
87
+ @classmethod
88
+ def token_required(cls, auth:bool=False):
89
+
90
+ def _token_required(f):
91
+
92
+ @wraps(f)
93
+ def decorated(*args, **kwargs):
94
+ try:
95
+
96
+ if auth:
97
+
98
+ token = None
99
+
100
+ if 'X-API-KEY' in request.headers:
101
+
102
+ token = request.headers['X-API-KEY']
103
+
104
+ elif 'Authorization' in request.headers:
105
+
106
+ token = request.headers['Authorization'].split('Token ')[-1]
107
+
108
+ if not token:
109
+
110
+ return {'message' : 'Key is missing.'}, 401
111
+
112
+ user = Users.get_or_none(token=token)
113
+
114
+ if user:
115
+
116
+ return f(*args, **kwargs)
117
+
118
+ if Api.verify_tpt(tpt=token):
119
+
120
+ return f(*args, **kwargs)
121
+
122
+ return {'message' : 'Invalid token'}, 401
123
+
124
+ except Exception as err:
125
+ logger = logging.getLogger("pyautomation")
126
+ logger.error(str(err))
127
+
128
+ return decorated
129
+
130
+ return _token_required
131
+
132
+ @classmethod
133
+ def get_current_user(cls):
134
+
135
+ token = None
136
+
137
+ if 'X-API-KEY' in request.headers:
138
+
139
+ token = request.headers['X-API-KEY']
140
+
141
+ elif 'Authorization' in request.headers:
142
+
143
+ token = request.headers['Authorization'].split('Token ')[-1]
144
+
145
+ if token:
146
+
147
+ return users.get_active_user(token=token)
148
+
149
+ return None
@@ -0,0 +1,18 @@
1
+ from flask_cors import CORS
2
+ from ..singleton import Singleton
3
+
4
+
5
+ class Cors(Singleton):
6
+
7
+ def __init__(self):
8
+
9
+ self.app = None
10
+
11
+ def init_app(self, app):
12
+ r"""
13
+ Documentation here
14
+ """
15
+
16
+ self.app = CORS(app, resources={r"/*": {"origins": "*"}})
17
+
18
+ return app
@@ -0,0 +1,19 @@
1
+ from ..utils.decorators import decorator
2
+
3
+
4
+ @decorator
5
+ def filter(func, args, kwargs):
6
+ r"""
7
+ Documentation here
8
+ """
9
+ cvt = args[0]
10
+ tag_id = kwargs["id"]
11
+ value = kwargs["value"]
12
+ tag = cvt.get_tag(id=tag_id)
13
+ if tag.gaussian_filter:
14
+
15
+ kwargs["value"] = tag.filter(value, threshold=tag.gaussian_filter_threshold, r_value=tag.gaussian_filter_r_value)
16
+
17
+ return func(*args, **kwargs)
18
+
19
+ return func(*args, **kwargs)
@@ -0,0 +1,3 @@
1
+ from .frozen_data import iad_frozen_data
2
+ from .out_of_range import iad_out_of_range
3
+ from .outliers import iad_outlier
@@ -0,0 +1,54 @@
1
+ from ..utils.decorators import decorator
2
+ from ..buffer import Buffer
3
+ import math
4
+
5
+ data = dict()
6
+
7
+ def __iad(data:Buffer, tag_name:str):
8
+ r"""
9
+ Frozen Data Algorithm
10
+ """
11
+ from ..managers.alarms import AlarmManager
12
+ alarm_manager = AlarmManager()
13
+ alarm = alarm_manager.get_alarm_by_name(name=f"alarm.iad.{tag_name}")
14
+
15
+ if alarm:
16
+ if alarm.state.alarm_status.lower() == "not active":
17
+
18
+ mean = sum(data) / len(data)
19
+ variance = sum((x - mean) ** 2 for x in data) / len(data)
20
+ std_dev = math.sqrt(variance)
21
+
22
+ if abs(std_dev) < 0.001:
23
+
24
+ alarm.description = f"Frozen data anomaly"
25
+ alarm.abnormal_condition()
26
+
27
+ else:
28
+
29
+ alarm.description = ""
30
+ alarm.normal_condition()
31
+
32
+ @decorator
33
+ def iad_frozen_data(func, args, kwargs):
34
+ r"""
35
+ Documentation here
36
+ """
37
+
38
+ cvt = args[0]
39
+ tag_id = kwargs["id"]
40
+ value = kwargs["value"]
41
+ tag = cvt.get_tag(id=tag_id)
42
+ if tag.frozen_data_detection:
43
+
44
+ if tag.name not in data:
45
+
46
+ data[tag.name] = Buffer()
47
+
48
+ data[tag.name](value)
49
+ # Apply IAD logic
50
+ if len(data[tag.name]) >= data[tag.name].size:
51
+
52
+ __iad(data[tag.name], tag_name=tag.name)
53
+
54
+ return func(*args, **kwargs)
@@ -0,0 +1,51 @@
1
+ from ..utils.decorators import decorator
2
+ from ..buffer import Buffer
3
+
4
+ data = dict()
5
+
6
+ def __iad(data:Buffer, tag_name:str):
7
+ r"""
8
+ Out Of Range Algorithm
9
+ """
10
+ from ..managers.alarms import AlarmManager
11
+ alarm_manager = AlarmManager()
12
+ alarm = alarm_manager.get_alarm_by_name(name=f"alarm.iad.{tag_name}")
13
+ if alarm:
14
+ if alarm.state.alarm_status.lower() == "not active":
15
+
16
+ mean = sum(data) / len(data)
17
+ # variance = sum((x - mean) ** 2 for x in data) / len(data)
18
+ # std_dev = math.sqrt(variance)
19
+
20
+ # if abs(std_dev) < 0.01:
21
+
22
+ # alarm.description = f"Out Of Range anomaly"
23
+ # alarm.abnormal_condition()
24
+
25
+ # else:
26
+
27
+ # alarm.description = ""
28
+ # alarm.normal_condition()
29
+
30
+ @decorator
31
+ def iad_out_of_range(func, args, kwargs):
32
+ r"""
33
+ Documentation here
34
+ """
35
+ cvt = args[0]
36
+ tag_id = kwargs["id"]
37
+ value = kwargs["value"]
38
+ tag = cvt.get_tag(id=tag_id)
39
+ if tag.out_of_range_detection:
40
+
41
+ if tag.name not in data:
42
+
43
+ data[tag.name] = Buffer()
44
+
45
+ data[tag.name](value)
46
+ # Apply IAD logic
47
+ if len(data[tag.name]) >= data[tag.name].size:
48
+
49
+ __iad(data[tag.name], tag_name=tag.name)
50
+
51
+ return func(*args, **kwargs)
@@ -0,0 +1,51 @@
1
+ from ..utils.decorators import decorator
2
+ from ..buffer import Buffer
3
+
4
+ data = dict()
5
+
6
+ def __iad(data:Buffer, tag_name:str):
7
+ r"""
8
+ Outliers Algorithm
9
+ """
10
+ from ..managers.alarms import AlarmManager
11
+ alarm_manager = AlarmManager()
12
+ alarm = alarm_manager.get_alarm_by_name(name=f"alarm.iad.{tag_name}")
13
+ if alarm:
14
+ if alarm.state.alarm_status.lower() == "not active":
15
+
16
+ mean = sum(data) / len(data)
17
+ # variance = sum((x - mean) ** 2 for x in data) / len(data)
18
+ # std_dev = math.sqrt(variance)
19
+
20
+ # if abs(std_dev) < 0.01:
21
+
22
+ # alarm.description = f"Outlier anomaly"
23
+ # alarm.abnormal_condition()
24
+
25
+ # else:
26
+
27
+ # alarm.description = ""
28
+ # alarm.normal_condition()
29
+
30
+ @decorator
31
+ def iad_outlier(func, args, kwargs):
32
+ r"""
33
+ Documentation here
34
+ """
35
+ cvt = args[0]
36
+ tag_id = kwargs["id"]
37
+ value = kwargs["value"]
38
+ tag = cvt.get_tag(id=tag_id)
39
+ if tag.outlier_detection:
40
+
41
+ if tag.name not in data:
42
+
43
+ data[tag.name] = Buffer()
44
+
45
+ data[tag.name](value)
46
+ # Apply IAD logic
47
+ if len(data[tag.name]) >= data[tag.name].size:
48
+
49
+ __iad(data[tag.name], tag_name=tag.name)
50
+
51
+ return func(*args, **kwargs)
File without changes