summaryrefslogtreecommitdiff
path: root/lib/taurus/core/util/eventfilters.py
blob: 8dc52a1d640a2aafa2966ac80246dbe179d3faa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env python

#############################################################################
##
## This file is part of Taurus
## 
## http://taurus-scada.org
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
## 
## Taurus is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## 
## Taurus is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
## 
## You should have received a copy of the GNU Lesser General Public License
## along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################

"""event filters library to be used with 
:meth:`taurus.qt.qtgui.base.TaurusBaseComponent.setFilters`"""

import PyTango
import taurus

def IGNORE_ALL(s, t, v):
    '''Will discard all events'''
    return None

def ONLY_CHANGE(s, t, v):
    '''Only change events pass'''
    if t == taurus.core.taurusbasetypes.TaurusEventType.Change: return s,t,v
    else: return None

def IGNORE_CHANGE(s, t, v):
    '''Config events are discarded'''
    if t != taurus.core.taurusbasetypes.TaurusEventType.Change: return s,t,v
    else: return None

def ONLY_CHANGE_AND_PERIODIC(s, t, v):
    '''Only change events pass'''
    if t in [taurus.core.taurusbasetypes.TaurusEventType.Change, 
             taurus.core.taurusbasetypes.TaurusEventType.Periodic]: 
        return s,t,v
    else: return None

def IGNORE_CHANGE_AND_PERIODIC(s, t, v):
    '''Config events are discarded'''
    if t not in [taurus.core.taurusbasetypes.TaurusEventType.Change, 
                 taurus.core.taurusbasetypes.TaurusEventType.Periodic]: 
        return s,t,v
    else: return None
    
def ONLY_CONFIG(s, t, v):
    '''Only config events pass'''
    if t == taurus.core.taurusbasetypes.TaurusEventType.Config: return s,t,v
    else: return None
    
def IGNORE_CONFIG(s, t, v):
    '''Config events are discarded'''
    if t != taurus.core.taurusbasetypes.TaurusEventType.Config: return s,t,v
    else: return None
    
def ONLY_VALID(s, t, v):
    '''Only events whose quality is VALID pass'''
    if getattr(v,'quality',None) == PyTango.AttrQuality.ATTR_VALID: return s,t,v
    else: return None
    
def IGNORE_FAKE(s, t, v):
    '''Only events with actual value (!=None) pass'''
    if v is not None: return s,t,v
    else: return None


class EventValueMap(dict):
    """A filter destined to change the original value into another one according
    to a given map. Example::
           
        filter = EventValueMap({1:"OPEN", 2:"CHANGING", 3:"CLOSED"})
       
    this will create a filter that changes the integer value of the event
    into a string. The event type is changed according to the python type in
    the map value.
       
    For now it only supports simple types: str, int, long, float, bool
    """

    PYTYPE_TO_TANGO = {
        str   : PyTango.DevString,
        int   : PyTango.DevLong,
        long  : PyTango.DevLong64,
        float : PyTango.DevDouble,
        bool  : PyTango.DevBoolean,
    }

    def __call__(self, s, t, v):
        if not t in (taurus.core.taurusbasetypes.TaurusEventType.Change, 
                     taurus.core.taurusbasetypes.TaurusEventType.Periodic):
            return s, t, v
        if v is None:
            return s, t, v
        
        # make a copy
        v = PyTango.DeviceAttribute(v)
        
        v.value = self.get(v.value, v.value)
        
        v.type = EventValueMap.PYTYPE_TO_TANGO.get(type(v.value), v.type)
        return s, t, v


class RepeatedEventFilter(object):
    """
    The instances of this class will be callables that can be used as filters 
    of repeated-value events. 
    If the event type is Change or Periodic, it will only pass when its 
    evt_value.value is different from that of the last event received from the 
    same source and type. If evt_value.value is not available in the current 
    event, the whole evt_value is used for comparison and as a future reference.
    
    This is useful to avoid processing repetitive events.
    
    Note that you cannot use this class as a filter: you need to use an 
    instance of it.
    
    Note 2: Use a different instance each time you insert this filter 
    into a different widget unless you *really* know what you are doing.
    
    Example of usage::
           
        filters = [RepeatedEventFilter(), IGNORE_CONFIG]
        filterEvent(s, t, v, filters)
        
    """
    def __init__(self):
        self._lastValues = {}

    def __call__(self, s, t, v):
        # restrict this  filter only to change and periodic events. 
        if ONLY_CHANGE_AND_PERIODIC(s, t, v) is None:
            return s,t,v
        # block event if we recorded one before with same src, type and v.value
        new_value = getattr(v, 'value', v)
        try:
            if self._lastValues[(s, t)] == new_value:
                return None
        except KeyError:
            pass
        # if it was't blocked, store src, type and v.value for future checks
        self._lastValues[(s, t)] =  new_value
        return s, t, v


def filterEvent(evt_src=-1, evt_type=-1, evt_value=-1, filters=()):
    """The event is processed by each and all filters in strict order
    unless one of them returns None (in which case the event is discarded)

    :param evt_src: (object) object that triggered the event
    :param evt_type: (taurus.core.taurusbasetypes.TaurusEventType) type of event
    :param evt_value: (object) event value
    :param filters: (sequence<callable>) a sequence of callables, each returning
                    either None (to discard the event) or the tuple (with 
                    possibly transformed values) of 
                    (evt_src, evt_type, evt_value)
    
    :return: (None or tuple) The result of piping the event through the given 
             filters.
    """
    evt = evt_src, evt_type, evt_value

    for f in filters:
        evt = f(*evt)
        if evt is None:
            return None
    return evt