counter.py 7.41 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

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
class BaseCounterAcquisitionDevice(AcquisitionDevice):
    def __init__(self, counter, count_time, auto_add_channel, **keys):
        npoints = max(1, keys.pop('npoints', 1))
        prepare_once = keys.pop('prepare_once', npoints > 1)
        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

        if auto_add_channel:
            self.channels.append(AcquisitionChannel(counter.name, numpy.double, (1,)))
            self.__counter_names.append(counter.name)
        
    @property
    def count_time(self):
        return self.__count_time
       
    @property
    def grouped_read_counters(self):
        return self.__grouped_read_counters_list
 
    @property
    def counter_names(self):
        return self.__counter_names

    def add_counter(self, counter):
        if not isinstance(self.device, GroupedReadMixin):
            raise RuntimeError("Cannot add counter to single-read counter acquisition device")

        self.__grouped_read_counters_list.append(counter)
        self.__counter_names.append(counter.name)
        self.channels.append(AcquisitionChannel(counter.name, numpy.double, (1,)))

    def _emit_new_data(self, data):
        channel_data = dict([ (name, data[i]) for i, name in enumerate(self.counter_names) ])
        dispatcher.send("new_data", self, {"channel_data": channel_data})


class SamplingCounterAcquisitionDevice(BaseCounterAcquisitionDevice):
64
65
    SIMPLE_AVERAGE,TIME_AVERAGE,INTEGRATE = range(3)

66
    def __init__(self, counter, count_time=None, mode=SIMPLE_AVERAGE, auto_add_channel=True, **keys):
67
68
69
70
71
72
73
74
75
        """
        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*.
76
77
78
79
80
        auto_add_channel -- specify if channel is directly added from counter (default: True)
        Other keys are:
          * npoints -- number of point for this acquisition
          * prepare_once --
          * start_once --
81
        """
82
83
        BaseCounterAcquisitionDevice.__init__(self, counter, count_time, auto_add_channel, **keys)

84
85
86
87
        self._event = event.Event()
        self._stop_flag = False
        self._ready_event = event.Event()
        self._ready_flag = True
88
89
90
91
92
93
94
95
        self.__mode = mode

    @property
    def mode(self):
        return self.__mode
    @mode.setter
    def mode(self,value):
        self.__mode = value
96
97

    def prepare(self):
98
99
        self.device.prepare(*self.grouped_read_counters)

100
101
102
103
104
105
        self._nb_acq_points = 0
        self._stop_flag = False
        self._ready_flag = True
        self._event.clear()

    def start(self):
106
        self.device.start(*self.grouped_read_counters)
107
108

    def stop(self):
109
110
        self.device.stop(*self.grouped_read_counters)

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
        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:
            #trigger wait
            self._event.wait()
            self._event.clear()
            self._ready_flag = False
            trig_time = self._trig_time
            if trig_time is None: continue
            if self._stop_flag: break

            nb_read = 0
            acc_read_time = 0
139
            acc_value = numpy.zeros((len(self.counter_names),), dtype=numpy.double)
140
            stop_time = trig_time + self.count_time or 0
141
142
143
            #Counter integration loop
            while not self._stop_flag:
                start_read = time.time()
144
                read_value = numpy.array(self.device.read(*self.grouped_read_counters), dtype=numpy.double)
145
                end_read = time.time()
146
147
                read_time = end_read - start_read
                
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
Matias Guijarro's avatar
Matias Guijarro committed
159
                gevent.sleep(0) # 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
165
                
Matias Guijarro's avatar
Matias Guijarro committed
166
            if self.__mode == SamplingCounterAcquisitionDevice.INTEGRATE:
167
                data *= self.count_time
168
            
169
170
            self._emit_new_data(data)

171
172
            self._ready_flag = True
            self._ready_event.set()
173
            
174

175
176
177
class IntegratingCounterAcquisitionDevice(BaseCounterAcquisitionDevice):
    def __init__(self, counter, count_time=None, auto_add_channel=True, **keys):
        BaseCounterAcquisitionDevice.__init__(self, counter, count_time, auto_add_channel, **keys)
178
179

    def prepare(self):
180
        self.device.prepare(*self.grouped_read_counters)
181
182
183
184
        self._nb_acq_points = 0
        self._stop_flag = False

    def start(self):
185
        self.device.start(*self.grouped_read_counters)
186
187

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

    def trigger(self):
        pass
    
194
195
196
197
198
199
    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)]

200
    def reading(self):
201
        from_index = 0
202
        while self._nb_acq_points < self.npoints and not self._stop_flag:
203
204
205
206
207
            data = self._read_data(from_index)
            if all_equal([len(d) for d in data]) and len(data[0]) > 0:
                from_index += len(data[0])
                self._nb_acq_points += len(data[0])
                self._emit_new_data(data)
208
209
210
211
                gevent.idle()
            else:
                gevent.sleep(count_time/2.)