counter.py 7.42 KB
Newer Older
1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2017 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.

import numpy
import time
Matias Guijarro's avatar
Matias Guijarro committed
10
11
import gevent
from gevent import event
12
from bliss.common.event import dispatcher
Matias Guijarro's avatar
Matias Guijarro committed
13
from ..chain import AcquisitionDevice, AcquisitionChannel
14
15
from bliss.common.measurement import GroupedReadMixin
from bliss.common.utils import all_equal
16

Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
17

18
class BaseCounterAcquisitionDevice(AcquisitionDevice):
19
    def __init__(self, counter, count_time, **keys):
20
        npoints = max(1, keys.pop('npoints', 1))
21
        prepare_once = keys.pop('prepare_once', True)
22
23
24
25
26
27
28
29
30
31
32
33
34
        start_once = keys.pop('start_once', npoints > 1)

        AcquisitionDevice.__init__(self, counter, counter.name, "zerod",
                                   npoints=npoints,
                                   trigger_type=AcquisitionDevice.SOFTWARE,
                                   prepare_once=prepare_once,
                                   start_once=start_once)

        self.__count_time = count_time
        self.__grouped_read_counters_list = list()
        self.__counter_names = list()
        self._nb_acq_points = 0

35
        if not isinstance(counter, GroupedReadMixin):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
36
37
            self.channels.append(AcquisitionChannel(
                counter.name, numpy.double, (1,)))
38
            self.__counter_names.append(counter.name)
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
39

40
41
42
    @property
    def count_time(self):
        return self.__count_time
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
43

44
45
46
    @property
    def grouped_read_counters(self):
        return self.__grouped_read_counters_list
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
47

48
49
50
51
52
53
    @property
    def counter_names(self):
        return self.__counter_names

    def add_counter(self, counter):
        if not isinstance(self.device, GroupedReadMixin):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
54
55
            raise RuntimeError(
                "Cannot add counter to single-read counter acquisition device")
56
57
58

        self.__grouped_read_counters_list.append(counter)
        self.__counter_names.append(counter.name)
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
59
60
        self.channels.append(AcquisitionChannel(
            counter.name, numpy.double, (1,)))
61
62

    def _emit_new_data(self, data):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
63
64
        channel_data = dict([(name, data[i])
                             for i, name in enumerate(self.counter_names)])
65
66
67
68
        dispatcher.send("new_data", self, {"channel_data": channel_data})


class SamplingCounterAcquisitionDevice(BaseCounterAcquisitionDevice):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
69
    SIMPLE_AVERAGE, TIME_AVERAGE, INTEGRATE = range(3)
70

71
    def __init__(self, counter, count_time=None, mode=SIMPLE_AVERAGE, **keys):
72
73
74
75
76
77
78
79
80
        """
        Helper to manage acquisition of a sampling counter.

        count_time -- the master integration time.
        mode -- three mode are available *SIMPLE_AVERAGE* (the default)
        which sum all the sampling values and divide by the number of read value.
        the *TIME_AVERAGE* which sum all integration  then divide by the sum
        of time spend to measure all values. And *INTEGRATION* which sum all integration
        and then normalize it when the *count_time*.
81
82
83
84
        Other keys are:
          * npoints -- number of point for this acquisition
          * prepare_once --
          * start_once --
85
        """
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
86
87
        BaseCounterAcquisitionDevice.__init__(
            self, counter, count_time, **keys)
88

89
90
91
92
        self._event = event.Event()
        self._stop_flag = False
        self._ready_event = event.Event()
        self._ready_flag = True
93
94
95
96
97
        self.__mode = mode

    @property
    def mode(self):
        return self.__mode
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
98

99
    @mode.setter
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
100
    def mode(self, value):
101
        self.__mode = value
102
103

    def prepare(self):
104
105
        self.device.prepare(*self.grouped_read_counters)

106
    def start(self):
107
108
109
110
111
        self._nb_acq_points = 0
        self._stop_flag = False
        self._ready_flag = True
        self._event.clear()

112
        self.device.start(*self.grouped_read_counters)
113
114

    def stop(self):
115
116
        self.device.stop(*self.grouped_read_counters)

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
        self._stop_flag = True
        self._trig_time = None
        self._event.set()

    def trigger(self):
        self._trig_time = time.time()
        self._event.set()

    def wait_ready(self):
        """
        will wait until the last triggered point is read
        """
        while not self._ready_flag:
            self._ready_event.wait()
            self._ready_event.clear()

    def reading(self):
        while self._nb_acq_points < self.npoints:
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
135
            # trigger wait
136
137
138
139
            self._event.wait()
            self._event.clear()
            self._ready_flag = False
            trig_time = self._trig_time
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
140
141
142
143
            if trig_time is None:
                continue
            if self._stop_flag:
                break
144
145
146

            nb_read = 0
            acc_read_time = 0
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
147
148
            acc_value = numpy.zeros(
                (len(self.counter_names),), dtype=numpy.double)
149
            stop_time = trig_time + self.count_time or 0
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
150
            # Counter integration loop
151
152
            while not self._stop_flag:
                start_read = time.time()
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
153
154
                read_value = numpy.array(self.device.read(
                    *self.grouped_read_counters), dtype=numpy.double)
155
                end_read = time.time()
156
                read_time = end_read - start_read
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
157

158
                if self.__mode == SamplingCounterAcquisitionDevice.TIME_AVERAGE:
159
                    acc_value += read_value * (end_read - start_read)
160
161
162
                else:
                    acc_value += read_value

163
164
165
166
167
168
                nb_read += 1
                acc_read_time += end_read - start_read

                current_time = time.time()
                if (current_time + (acc_read_time / nb_read)) > stop_time:
                    break
169
                gevent.sleep(0)  # to be able to kill the task
170
            self._nb_acq_points += 1
Matias Guijarro's avatar
Matias Guijarro committed
171
            if self.__mode == SamplingCounterAcquisitionDevice.TIME_AVERAGE:
172
                data = acc_value / acc_read_time
173
174
            else:
                data = acc_value / nb_read
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
175

Matias Guijarro's avatar
Matias Guijarro committed
176
            if self.__mode == SamplingCounterAcquisitionDevice.INTEGRATE:
177
                data *= self.count_time
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
178

179
180
            self._emit_new_data(data)

181
182
            self._ready_flag = True
            self._ready_event.set()
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
183

184

185
class IntegratingCounterAcquisitionDevice(BaseCounterAcquisitionDevice):
186
    def __init__(self, counter, count_time=None, **keys):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
187
188
        BaseCounterAcquisitionDevice.__init__(
            self, counter, count_time, **keys)
189
190

    def prepare(self):
191
        self.device.prepare(*self.grouped_read_counters)
192
193

    def start(self):
194
195
196
        self._nb_acq_points = 0
        self._stop_flag = False

197
        self.device.start(*self.grouped_read_counters)
198
199

    def stop(self):
200
        self.device.stop(*self.grouped_read_counters)
201
202
203
204
        self._stop_flag = True

    def trigger(self):
        pass
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
205

206
207
208
209
210
211
    def _read_data(self, from_index):
        if self.grouped_read_counters:
            return self.device.get_values(from_index, *self.grouped_read_counters)
        else:
            return [numpy.array(self.device.get_value(from_index), dtype=numpy.double)]

212
    def reading(self):
213
        from_index = 0
214
        while self._nb_acq_points < self.npoints and not self._stop_flag:
215
            data = self._read_data(from_index)
216
            if not all_equal([len(d) for d in data]):
217
                raise RuntimeError("Read data can't have different sizes")
218
            if len(data[0]) > 0:
219
220
221
                from_index += len(data[0])
                self._nb_acq_points += len(data[0])
                self._emit_new_data(data)
222
223
                gevent.idle()
            else:
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
224
                gevent.sleep(self.count_time / 2.)