Passed
Pull Request — master (#365)
by
unknown
02:53
created

EventGenerator.init()   F

Complexity

Conditions 21

Size

Total Lines 81
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 68
nop 4
dl 0
loc 81
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like asyncua.server.event_generator.EventGenerator.init() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import logging
2
from datetime import datetime
3
import time
4
import uuid
5
from typing import Optional
6
import sys
7
8
from asyncua import ua
9
from asyncua.ua.uaerrors import BadSourceNodeIdInvalid, BadTargetNodeIdInvalid
10
from ..common import events, event_objects, Node
11
12
13
class EventGenerator:
14
    """
15
    Create an event based on an event type. Per default is BaseEventType used.
16
    Object members are dynamically created from the base event type and send to
17
    client when evebt is triggered (see example code in source)
18
19
    Arguments to constructor are:
20
        server: The InternalSession object to use for query and event triggering
21
        emitting_node: The emiting source for the node, either an objectId, NodeId or a Node
22
        etype: The event type, either an objectId, a NodeId or a Node object
23
        notifier_path: the path from the Server-Object to the emitting node
24
    """
25
26
    def __init__(self, isession):
27
        self.logger = logging.getLogger(__name__)
28
        self.isession = isession
29
        self.event: Optional[event_objects.BaseEvent] = None
30
        self.emitting_node: Optional[Node] = None
31
32
    async def init(self, etype=None, emitting_node=ua.ObjectIds.Server, notifier_path=None):
33
        node = None
34
35
        if isinstance(etype, event_objects.BaseEvent):
36
            self.event = etype
37
        elif isinstance(etype, Node):
38
            node = etype
39
        elif isinstance(etype, ua.NodeId):
40
            node = Node(self.isession, etype)
41
        else:
42
            node = Node(self.isession, ua.NodeId(etype))
43
44
        if node:
45
            self.event = await events.get_event_obj_from_type_node(node)
46
            if isinstance(self.event, event_objects.Condition):
47
                # we need this that it works proper for conditions and alarms aka ConditionId
48
                if isinstance(emitting_node, Node):
49
                    condition_id = ua.NodeId(emitting_node.nodeid.Identifier, emitting_node.nodeid.NamespaceIndex)
50
                elif isinstance(emitting_node, ua.NodeId):
51
                    condition_id = emitting_node
52
                else:
53
                    condition_id = ua.NodeId(emitting_node.Identifier, emitting_node.NamespaceIndex)
54
                self.event.add_property('NodeId', condition_id, ua.VariantType.NodeId)
55
56
        if isinstance(emitting_node, Node):
57
            pass
58
        elif isinstance(emitting_node, ua.NodeId):
59
            emitting_node = Node(self.isession, emitting_node)
60
        else:
61
            emitting_node = Node(self.isession, ua.NodeId(emitting_node))
62
63
        self.event.emitting_node = emitting_node.nodeid
64
        if not self.event.SourceNode:
65
            self.event.SourceNode = emitting_node.nodeid
66
        if not self.event.SourceName:
67
            self.event.SourceName = (await Node(self.isession, self.event.SourceNode).read_browse_name()).Name
68
69
        refs = []
70
        if notifier_path is not None:
71
            for i, node in enumerate(notifier_path):
72
                if not isinstance(node, Node):
73
                    notifier_path[i] = Node(self.isession, node)
74
            if notifier_path[0] != Node(self.isession, ua.ObjectIds.Server):
75
                raise BadSourceNodeIdInvalid
76
            if notifier_path[-1] != emitting_node:
77
                raise BadTargetNodeIdInvalid
78
            for i, node in enumerate(notifier_path):
79
                if node == emitting_node:
80
                    continue
81
                ref = ua.AddReferencesItem()
82
                ref.IsForward = True
83
                ref.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasNotifier)
84
                ref.SourceNodeId = node.nodeid
85
                ref.TargetNodeClass = await node.read_node_class()
86
                ref.TargetNodeId = notifier_path[i + 1].nodeid
87
                refs.append(ref)
88
89
                await node.set_event_notifier([ua.EventNotifier.SubscribeToEvents])
90
        else:
91
            if emitting_node.nodeid.Identifier != ua.ObjectIds.Server:
92
                ref = ua.AddReferencesItem()
93
                ref.IsForward = True
94
                ref.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasNotifier)
95
                ref.SourceNodeId = ua.NodeId(ua.ObjectIds.Server)
96
                ref.TargetNodeClass = await emitting_node.read_node_class()
97
                ref.TargetNodeId = emitting_node.nodeid
98
                refs.append(ref)
99
100
        await emitting_node.set_event_notifier([ua.EventNotifier.SubscribeToEvents])
101
        ref = ua.AddReferencesItem()
102
        ref.IsForward = True
103
        ref.ReferenceTypeId = ua.NodeId(ua.ObjectIds.GeneratesEvent)
104
        ref.SourceNodeId = emitting_node.nodeid
105
        ref.TargetNodeClass = ua.NodeClass.ObjectType
106
        ref.TargetNodeId = self.event.EventType
107
        refs.append(ref)
108
        results = await self.isession.add_references(refs)
109
        for result in results:
110
            result.check()
111
112
        self.emitting_node = emitting_node
113
114
    def __str__(self):
115
        return f"EventGenerator(Type:{self.event.EventType}, Emitting Node:{self.emitting_node}, " \
116
               f"Time:{self.event.Time}, Message: {self.event.Message})"
117
118
    __repr__ = __str__
119
120
    async def trigger(self, time_attr=None, message=None):
121
        """
122
        Trigger the event. This will send a notification to all subscribed clients
123
        """
124
        self.event.EventId = ua.Variant(uuid.uuid4().hex.encode('utf-8'), ua.VariantType.ByteString)
125
        if time_attr:
126
            self.event.Time = time_attr
127
        else:
128
            self.event.Time = datetime.utcnow()
129
        self.event.ReceiveTime = datetime.utcnow()
130
131
        self.event.LocalTime = ua.uaprotocol_auto.TimeZoneDataType()
132
        if sys.version_info.major > 2:
133
            localtime = time.localtime(self.event.Time.timestamp())
134
            self.event.LocalTime.Offset = localtime.tm_gmtoff//60
135
        else:
136
            localtime = time.localtime(time.mktime(self.event.Time.timetuple()))
137
            self.event.LocalTime.Offset = -(time.altzone if localtime.tm_isdst else time.timezone)
138
        self.event.LocalTime.DaylightSavingInOffset = bool(localtime.tm_isdst != -1)
139
140
        if message:
141
            self.event.Message = ua.LocalizedText(message)
142
        elif not self.event.Message:
143
            self.event.Message = ua.LocalizedText((await Node(self.isession, self.event.SourceNode).read_browse_name()).Name).Text
144
145
        await self.isession.subscription_service.trigger_event(self.event)
146