counter.py 6.82 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
from bliss.common.measurement import SamplingCounter
15

Matias Guijarro's avatar
Matias Guijarro committed
16
class SamplingCounterAcquisitionDevice(AcquisitionDevice):
17
18
    SIMPLE_AVERAGE,TIME_AVERAGE,INTEGRATE = range(3)

19
    def __init__(self,counter,
Matias Guijarro's avatar
Matias Guijarro committed
20
21
                 count_time=None, npoints=1,
                 mode=SIMPLE_AVERAGE, **keys):
22
23
24
25
26
27
28
29
30
31
32
        """
        Helper to manage acquisition of a sampling counter.

        count_time -- the master integration time.
        npoints -- number of point for this acquisition
        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*.
        """
Matias Guijarro's avatar
Matias Guijarro committed
33
34
        prepare_once = keys.pop('prepare_once', npoints > 1)
        start_once = keys.pop('start_once', npoints > 1)
35
36
37
38
39
40
41
        npoints = max(1,npoints)
        AcquisitionDevice.__init__(self, counter, counter.name, "zerod",
                                   npoints=npoints,
                                   trigger_type=AcquisitionDevice.SOFTWARE,
                                   prepare_once=prepare_once,
                                   start_once=start_once,
                                   **keys)
42
        self.count_time = count_time
Matias Guijarro's avatar
Matias Guijarro committed
43
        if not isinstance(counter, SamplingCounter.GroupedReadHandler):
44
            self.channels.append(AcquisitionChannel(counter.name,numpy.double, (1,)))
45
46
47
48
49
        self._nb_acq_points = 0
        self._event = event.Event()
        self._stop_flag = False
        self._ready_event = event.Event()
        self._ready_flag = True
50
        self.__mode = mode
51
        self.__counters_list = list()
52
53
54
55
56
57
58

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

60
    def add_counter_to_read(self,counter):
61
        self.__counters_list.append(counter)
62
63
        self.channels.append(AcquisitionChannel(counter.name,numpy.double, (1,)))

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
    def prepare(self):
        self._nb_acq_points = 0
        self._stop_flag = False
        self._ready_flag = True
        self._event.clear()

    def start(self):
        pass

    def stop(self):
        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):
91
        counter_name = [x.name for x in self.channels]
92
        if isinstance(self.device, SamplingCounter.GroupedReadHandler):
93
            def read():
94
                return numpy.array(self.device.read(*self.__counters_list),
95
                                   dtype=numpy.double)
Matias Guijarro's avatar
Matias Guijarro committed
96
        else:
97
98
99
            def read():
                return numpy.array(self.device.read(),
                                   dtype=numpy.double)
100
101
102
103
104
105
106
107
108
109
110
        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
111
            acc_value = numpy.zeros((len(counter_name),),dtype=numpy.double)
112
            stop_time = trig_time + self.count_time or 0
113
114
115
            #Counter integration loop
            while not self._stop_flag:
                start_read = time.time()
116
                read_value = read()
117
                end_read = time.time()
118
119
                read_time = end_read - start_read
                
Matias Guijarro's avatar
Matias Guijarro committed
120
                if self.__mode != SamplingCounterAcquisitionDevice.TIME_AVERAGE:
121
122
123
                    acc_value += read_value
                else:
                    acc_value += read_value * (end_read - start_read)
124
125
126
127
128
129
                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
130
                gevent.sleep(0) # Be able to kill the task
131
            self._nb_acq_points += 1
Matias Guijarro's avatar
Matias Guijarro committed
132
            if self.__mode == SamplingCounterAcquisitionDevice.TIME_AVERAGE:
133
134
135
136
                data = acc_value / nb_read
            else:
                data = acc_value / acc_read_time
                
Matias Guijarro's avatar
Matias Guijarro committed
137
            if self.__mode == SamplingCounterAcquisitionDevice.INTEGRATE:
138
                data *= self.count_time
139
140
            
            channel_data = {name:data[index] for index,name in enumerate(counter_name)}
141
            dispatcher.send("new_data",self,
142
                            {"channel_data": channel_data})
143
144
            self._ready_flag = True
            self._ready_event.set()
145
            
146

Matias Guijarro's avatar
Matias Guijarro committed
147
148
149
150
class IntegratingCounterAcquisitionDevice(AcquisitionDevice):
    def __init__(self, counter, count_time=None, npoints=1, **keys):
        prepare_once = keys.pop('prepare_once', npoints > 1)
        start_once = keys.pop('start_once', npoints > 1)
151
        npoints = max(1,npoints)
Matias Guijarro's avatar
Matias Guijarro committed
152
        AcquisitionDevice.__init__(self, counter, counter.name, "zerod",
153
154
155
156
157
                                   npoints=npoints,
                                   trigger_type=AcquisitionDevice.SOFTWARE,
                                   prepare_once=prepare_once,
                                   start_once=start_once,
                                   **keys)
158
        self.count_time = count_time
Matias Guijarro's avatar
Matias Guijarro committed
159
        self.channels.append(AcquisitionChannel(counter.name,numpy.double, (1,)))
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        self._nb_acq_points = 0

    def prepare(self):
        self._nb_acq_points = 0
        self._stop_flag = False

    def start(self):
        pass

    def stop(self):
        self._stop_flag = True

    def trigger(self):
        pass
    
    def reading(self):
        from_point_index = 0
        while self._nb_acq_points < self.npoints and not self._stop_flag:
            data = self.device.get_value(from_point_index)
            if data:
                from_point_index += len(data)
                self._nb_acq_points += len(data)
                channel_data = {self.name:data}
                dispatcher.send("new_data",self,
                                {"channel_data": channel_data})
                gevent.idle()
            else:
                gevent.sleep(count_time/2.)