counter.py 7.03 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
        start_once = keys.pop('start_once', npoints > 1)

24
        AcquisitionDevice.__init__(self, counter, counter.name,
25
26
27
28
29
30
31
32
33
                                   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._nb_acq_points = 0

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

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

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

46
47
    def add_counter(self, counter):
        if not isinstance(self.device, GroupedReadMixin):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
48
49
            raise RuntimeError(
                "Cannot add counter to single-read counter acquisition device")
50
51

        self.__grouped_read_counters_list.append(counter)
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
52
        self.channels.append(AcquisitionChannel(
53
            counter.name, numpy.double, ()))
54
55

    def _emit_new_data(self, data):
56
        self.channels.update_from_iterable(data)
57
58
59


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

62
    def __init__(self, counter, count_time=None, mode=SIMPLE_AVERAGE, **keys):
63
64
65
66
67
68
69
70
71
        """
        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*.
72
73
74
75
        Other keys are:
          * npoints -- number of point for this acquisition
          * prepare_once --
          * start_once --
76
        """
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
77
78
        BaseCounterAcquisitionDevice.__init__(
            self, counter, count_time, **keys)
79

80
81
82
83
        self._event = event.Event()
        self._stop_flag = False
        self._ready_event = event.Event()
        self._ready_flag = True
84
85
86
87
88
        self.__mode = mode

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

90
    @mode.setter
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
91
    def mode(self, value):
92
        self.__mode = value
93
94

    def prepare(self):
95
96
        self.device.prepare(*self.grouped_read_counters)

97
    def start(self):
98
99
100
101
102
        self._nb_acq_points = 0
        self._stop_flag = False
        self._ready_flag = True
        self._event.clear()

103
        self.device.start(*self.grouped_read_counters)
104
105

    def stop(self):
106
107
        self.device.stop(*self.grouped_read_counters)

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
        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
126
            # trigger wait
127
128
129
130
            self._event.wait()
            self._event.clear()
            self._ready_flag = False
            trig_time = self._trig_time
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
131
132
133
134
            if trig_time is None:
                continue
            if self._stop_flag:
                break
135
136
137

            nb_read = 0
            acc_read_time = 0
138
            acc_value = numpy.zeros((len(self.channels),), dtype=numpy.double)
139
            stop_time = trig_time + self.count_time or 0
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
140
            # Counter integration loop
141
142
            while not self._stop_flag:
                start_read = time.time()
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
143
144
                read_value = numpy.array(self.device.read(
                    *self.grouped_read_counters), dtype=numpy.double)
145
                end_read = time.time()
146
                read_time = end_read - start_read
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
147

148
                if self.__mode == SamplingCounterAcquisitionDevice.TIME_AVERAGE:
149
                    acc_value += read_value * (end_read - start_read)
150
151
152
                else:
                    acc_value += read_value

153
154
155
156
157
158
                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
159
                gevent.sleep(0)  # to be able to kill the task
160
            self._nb_acq_points += 1
Matias Guijarro's avatar
Matias Guijarro committed
161
            if self.__mode == SamplingCounterAcquisitionDevice.TIME_AVERAGE:
162
                data = acc_value / acc_read_time
163
164
            else:
                data = acc_value / nb_read
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
165

Matias Guijarro's avatar
Matias Guijarro committed
166
            if self.__mode == SamplingCounterAcquisitionDevice.INTEGRATE:
167
                data *= self.count_time
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
168

169
170
            self._emit_new_data(data)

171
172
            self._ready_flag = True
            self._ready_event.set()
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
173

174

175
class IntegratingCounterAcquisitionDevice(BaseCounterAcquisitionDevice):
176
    def __init__(self, counter, count_time=None, **keys):
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
177
178
        BaseCounterAcquisitionDevice.__init__(
            self, counter, count_time, **keys)
179
180

    def prepare(self):
181
        self.device.prepare(*self.grouped_read_counters)
182
183

    def start(self):
184
185
186
        self._nb_acq_points = 0
        self._stop_flag = False

187
        self.device.start(*self.grouped_read_counters)
188
189

    def stop(self):
190
        self.device.stop(*self.grouped_read_counters)
191
192
193
194
        self._stop_flag = True

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

196
197
198
199
200
201
    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)]

202
    def reading(self):
203
        from_index = 0
204
        while self._nb_acq_points < self.npoints and not self._stop_flag:
205
            data = self._read_data(from_index)
206
            if not all_equal([len(d) for d in data]):
207
                raise RuntimeError("Read data can't have different sizes")
208
            if len(data[0]) > 0:
209
210
211
                from_index += len(data[0])
                self._nb_acq_points += len(data[0])
                self._emit_new_data(data)
212
213
                gevent.idle()
            else:
Sebastien Petitdemange's avatar
pep8    
Sebastien Petitdemange committed
214
                gevent.sleep(self.count_time / 2.)