SIENTIAPDE-988
Enhance ingestor and manager classes with notification handling - Integrated NotificationHandler into Ingestor, DataManager, IngestorManager, and OpcManager for improved error reporting and monitoring. - Updated methods to send notifications on critical events such as Kafka publishing errors, OPC connection issues, and cycle count warnings. - Refactored related tests to ensure coverage of new notification functionalities and validate integration with existing components. - Improved logging and error handling across the system to enhance traceability and operational insights.
This commit is contained in:
@@ -5,14 +5,14 @@ from typing import Callable
|
||||
from asyncua.crypto.security_policies import SecurityPolicyBasic256
|
||||
from asyncua.sync import Client
|
||||
from sientia_do.notifications.models import NotificationLevel
|
||||
|
||||
from sientia_do.notifications.handlers import NotificationHandler
|
||||
from ingestor.managers.data_manager import DataManager
|
||||
|
||||
|
||||
class OpcManager():
|
||||
def __init__(self, name: str, url: str, data_manager: DataManager,
|
||||
logger: Logger, server_uri: str, cert_path: str = None,
|
||||
private_key_path: str = None, server_cert_path: str = None):
|
||||
logger: Logger, server_uri: str, notification_handler: NotificationHandler,
|
||||
cert_path: str = None, private_key_path: str = None, server_cert_path: str = None):
|
||||
self.url = url
|
||||
self.name = name
|
||||
self.server_uri = server_uri
|
||||
@@ -27,6 +27,8 @@ class OpcManager():
|
||||
self.subscriptions = {}
|
||||
self.data_manager = data_manager
|
||||
|
||||
self.notification_handler = notification_handler
|
||||
|
||||
def __str__(self):
|
||||
return f"OpcManager(name={self.name}, url={self.url}, server_uri={self.server_uri})\n" \
|
||||
f"nodes={self.nodes}, subscriptions={self.subscriptions}"
|
||||
@@ -233,70 +235,48 @@ class OpcManager():
|
||||
[self.data_manager.publish(e, data)
|
||||
for e in self.nodes[tag]['topics']]
|
||||
|
||||
def check_cycles(self, removed_data: dict, node: str, config: dict,
|
||||
handle_listen_events: Callable[[str, str, NotificationLevel], None]):
|
||||
"""
|
||||
Checks the cycle count for a specific node and triggers a notification if the cycle count exceeds a threshold.
|
||||
Args:
|
||||
removed_data (dict): A dictionary containing data that has been removed.
|
||||
Used to check if the node is present.
|
||||
node (str): The identifier of the node being checked.
|
||||
config (dict): Configuration dictionary containing metadata such as the tag name.
|
||||
handle_listen_events (Callable[[str, str, NotificationLevel], None]):
|
||||
A callback function to handle notification events. It takes three arguments:
|
||||
- A message string describing the event.
|
||||
- A tag string identifying the event.
|
||||
- A NotificationLevel enum indicating the severity of the event.
|
||||
Behavior:
|
||||
- If the node is not in `removed_data`, the cycle count for the node is incremented.
|
||||
- If the cycle count reaches or exceeds 5, the `handle_listen_events` callback is invoked
|
||||
with a warning message, a tag, and a notification level.
|
||||
Notification Example:
|
||||
If the cycle count exceeds the threshold, a warning message is generated in the format:
|
||||
"{cycles} cycles without receive from {node}:{name}"
|
||||
where `cycles` is the current cycle count, `node` is the node identifier, and `name` is the tag name
|
||||
from the `config` dictionary.
|
||||
def check_cycles(self):
|
||||
"""
|
||||
Checks the cycle counts for all monitored nodes and sends notifications if thresholds are exceeded.
|
||||
|
||||
if node not in removed_data.keys():
|
||||
This method iterates through all monitored nodes and updates their cycle counts based on
|
||||
configured increments. If a node's cycle count exceeds a threshold (5 cycles), it triggers
|
||||
a warning notification.
|
||||
|
||||
"""
|
||||
for node, config in self.nodes.items():
|
||||
self.nodes[node]['cycle_rule']['cycle_count'] += self.nodes[node]['cycle_rule']['cycle_increment']
|
||||
if self.nodes[node]['cycle_rule']['cycle_count'] >= 5 and handle_listen_events:
|
||||
name = config['tag_name']
|
||||
cycles = self.nodes[node]['cycle_rule']['cycle_count']
|
||||
handle_listen_events(
|
||||
f'{cycles} cycles without receive from {node}:{name}',
|
||||
f'TAG_{node}:{name}_LISTENNING_STOPPED',
|
||||
NotificationLevel.WARNING
|
||||
)
|
||||
if self.nodes[node]['cycle_rule']['cycle_count'] >= 5:
|
||||
name = config['tag_name']
|
||||
cycles = self.nodes[node]['cycle_rule']['cycle_count']
|
||||
self.notification_handler.build_and_send_notification(
|
||||
notification_id=f'TAG_{node}:{name}_LISTENNING_STOPPED',
|
||||
message=f'{cycles} cycles without receive from {node}:{name}',
|
||||
block="opc_manager",
|
||||
level=NotificationLevel.WARNING
|
||||
)
|
||||
|
||||
def check_opc_listenning(self, handle_listen_events: Callable[[str, str, NotificationLevel], None]) -> None:
|
||||
def check_opc_listenning(self) -> bool:
|
||||
"""
|
||||
Monitors the OPC connection and triggers events based on the number of cycles
|
||||
without receiving data from the OPC server.
|
||||
Args:
|
||||
handle_listen_events (Callable[[str, str, NotificationLevel], None]):
|
||||
A callback function to handle notification events. It takes three arguments:
|
||||
- A message string describing the event.
|
||||
- An event code string.
|
||||
- A NotificationLevel indicating the severity of the event.
|
||||
Behavior:
|
||||
- Increments the non-receive count each time the method is called.
|
||||
- If the non-receive count reaches 5, triggers a notification event indicating
|
||||
that the OPC server has stopped sending data.
|
||||
- If the non-receive count reaches 15, triggers a notification event indicating
|
||||
a retry to connect to the OPC server, disconnects the current session, and
|
||||
reinitializes the collector with the existing configuration.
|
||||
Checks the OPC connection and triggers notifications if the connection is lost.
|
||||
Returns:
|
||||
bool: True if the connection is lost, False otherwise.
|
||||
"""
|
||||
|
||||
self.non_receive_count += 1
|
||||
if self.non_receive_count >= 5 and handle_listen_events:
|
||||
handle_listen_events(
|
||||
f'{self.non_receive_count} cycles without receive from OPC {self.name}. Tags: {json.dumps(self.nodes)}',
|
||||
f'OPC_LISTENNING_STOPPED__{self.name}', NotificationLevel.ERROR)
|
||||
if self.non_receive_count >= 15 and handle_listen_events:
|
||||
handle_listen_events(
|
||||
f'Retrying to connect to server {self.name}',
|
||||
f'OPC_CONNECTION_RETRY__{self.name}', NotificationLevel.ERROR)
|
||||
self.disconnect()
|
||||
self.init_collector(
|
||||
self.nodes, self.collect_period, self.period)
|
||||
if self.non_receive_count >= 5:
|
||||
self.notification_handler.build_and_send_notification(
|
||||
notification_id=f'OPC_LISTENNING_STOPPED__{self.name}',
|
||||
message=f'{self.non_receive_count} cycles without receive from OPC {self.name}. Tags: {json.dumps(self.nodes)}',
|
||||
block="opc_manager",
|
||||
level=NotificationLevel.ERROR
|
||||
)
|
||||
if self.non_receive_count >= 15:
|
||||
self.notification_handler.build_and_send_notification(
|
||||
notification_id=f'OPC_CONNECTION_RETRY__{self.name}',
|
||||
message=f'Retrying to connect to server {self.name}',
|
||||
block="opc_manager",
|
||||
level=NotificationLevel.ERROR
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user