File: device_attribute.h

package info (click to toggle)
pytango 10.0.2-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 10,216 kB
  • sloc: python: 28,206; cpp: 16,380; sql: 255; sh: 82; makefile: 43
file content (214 lines) | stat: -rw-r--r-- 7,119 bytes parent folder | download | duplicates (3)
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * SPDX-FileCopyrightText: All Contributors to the PyTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#pragma once

#include <boost/python.hpp>
#include <tango/tango.h>
#include <iostream>
#include <string>

#include "pyutils.h"
#include "defs.h"
#include "pytgutils.h"
#include "tango_numpy.h"
#include "fast_from_py.h"

template <long tangoTypeConst>
struct python_tangocpp
{
    typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;

    static inline void to_cpp(const bopy::object &py_value, TangoScalarType &result)
    {
        result = bopy::extract<TangoScalarType>(py_value);
    }

    static inline bopy::object to_python(const TangoScalarType &value)
    {
        return bopy::object(value);
    }
};

template <>
struct python_tangocpp<Tango::DEV_STRING>
{
    static const long tangoTypeConst = Tango::DEV_STRING;
    typedef TANGO_const2type(tangoTypeConst) TangoScalarType;

    static inline void to_cpp(const bopy::object &py_value, TangoScalarType &result)
    {
        result = from_str_to_char(py_value.ptr());
    }

    static inline bopy::object to_python(const TangoScalarType &value)
    {
        return from_char_to_boost_str(value);
    }
};

namespace PyDeviceAttribute
{

/// @name Types
/// @{
typedef unique_pointer<std::vector<Tango::DeviceAttribute>> AutoDevAttrVector;
/// @}

template <long tangoTypeConst>
static void _update_array_values_as_tuples(Tango::DeviceAttribute &self, bool isImage, bopy::object py_value);

/// Set the value of a DeviceAttribute from python (useful for write*)
void reset(Tango::DeviceAttribute &self, const Tango::AttributeInfo &attr_info, bopy::object py_value);
void reset(Tango::DeviceAttribute &self,
           const std::string &attr_name,
           Tango::DeviceProxy &dev_proxy,
           bopy::object py_value);

void update_values(Tango::DeviceAttribute &self,
                   bopy::object &py_value,
                   PyTango::ExtractAs extract_as = PyTango::ExtractAsNumpy);

template <typename TDeviceAttribute>
void update_data_format(Tango::DeviceProxy &dev_proxy, TDeviceAttribute *first, size_t nelems)
{
    // Older devices do not send arg_format. So we try to discover it from
    // dim_x and dim_y. It is not perfect, sometimes we will get SCALAR for
    // SPECTRUM with size 1 for example. In that case, we must ask tango
    // for the actual value.
    TDeviceAttribute *p = first;
    std::vector<std::string> attr_names;
    for(size_t n = 0; n < nelems; ++n, ++p)
    {
        if((p->data_format != Tango::FMT_UNKNOWN) || (p->has_failed()))
        {
            continue;
        }
        if((p->get_dim_x() == 1) && (p->get_dim_y() == 0))
        {
            attr_names.push_back(p->name);
        }
        else if(p->get_dim_y() == 0)
        {
            p->data_format = Tango::SPECTRUM;
        }
        else
        {
            p->data_format = Tango::IMAGE;
        }
    }
    if(attr_names.size())
    {
        unique_pointer<Tango::AttributeInfoListEx> attr_infos;
        {
            AutoPythonAllowThreads guard;
            p = first;
            try
            {
                attr_infos.reset(dev_proxy.get_attribute_config_ex(attr_names));
                for(size_t n = 0, m = 0; n < nelems; ++n, ++p)
                {
                    if((p->data_format == Tango::FMT_UNKNOWN) && (!p->has_failed()))
                    {
                        p->data_format = (*attr_infos)[m++].data_format;
                    }
                }
            }
            catch(Tango::DevFailed &)
            {
                // if we fail to get info about the missing attributes from
                // the server (because it as shutdown, for example) we assume
                // that they are SCALAR since dim_x is 1
                for(size_t n = 0; n < nelems; ++n, ++p)
                {
                    if((p->data_format == Tango::FMT_UNKNOWN) && (!p->has_failed()))
                    {
                        p->data_format = Tango::SCALAR;
                    }
                }
            }
        }
    }
    return;
}

/// @param self The DeviceAttribute instance that the new python object
/// will represent. It must be allocated with new. The new python object
/// will handle it's destruction. There's never any reason to delete it
/// manually after a call to this: Even if this function fails, the
/// responsibility of destroying it will already be in py_value side or
/// the object will already be destroyed.
template <typename TDeviceAttribute>
bopy::object convert_to_python(TDeviceAttribute *self, PyTango::ExtractAs extract_as)
{
    bopy::object py_value;
    try
    {
        py_value = bopy::object(
            bopy::handle<>(bopy::to_python_indirect<TDeviceAttribute *, bopy::detail::make_owning_holder>()(self)));
    }
    catch(...)
    {
        delete self;
        throw;
    }

    update_values(*self, py_value, extract_as);
    return py_value;
}

/// See the other convert_to_python version. Here we get a vector of
/// DeviceAttributes. The responsibility to deallocate it is always from
/// the caller (we will make a copy). This responsibility is unavoidable
/// as we get a reference to auto_ptr -> the caller must use an auto_ptr,
/// so the memory will finally be deleted.
template <typename TDeviceAttribute>
bopy::object convert_to_python(const unique_pointer<std::vector<TDeviceAttribute>> &dev_attr_vec,
                               Tango::DeviceProxy &dev_proxy,
                               PyTango::ExtractAs extract_as)
{
    // Sometimes CppTango returns a nullptr instead of an empty vector
    // We convert it to None in python
    if(dev_attr_vec == nullptr)
    {
        bopy::object none;
        return none;
    }

    if(dev_attr_vec->empty())
    {
        bopy::list ls;
        return ls;
    }

    update_data_format(dev_proxy, &(*dev_attr_vec)[0], dev_attr_vec->size());

    // Convert the c++ vector of DeviceAttribute into a pythonic list
    bopy::list ls;
    typename std::vector<TDeviceAttribute>::const_iterator i, e = dev_attr_vec->end();
    for(i = dev_attr_vec->begin(); i != e; ++i)
    {
        ls.append(convert_to_python(new TDeviceAttribute(*i), extract_as));
    }
    return ls;
}

/// Convert a DeviceAttribute to python (useful for read*)
/// @param dev_attr Should be a pointer allocated with new. You can forget
///                 about deallocating this object (python will do it) even
///                 if the call to convert_to_python fails.
/// @param dev_proxy Device proxy where the value was got. DeviceAttribute
///                 sent by older tango versions do not have all the
///                 necessary information to extract the values, so we
///                 may need to ask.
/// @param extract_as See ExtractAs enum.
template <typename TDeviceAttribute>
bopy::object convert_to_python(TDeviceAttribute *dev_attr, Tango::DeviceProxy &dev_proxy, PyTango::ExtractAs extract_as)
{
    update_data_format(dev_proxy, dev_attr, 1);
    return convert_to_python(dev_attr, extract_as);
}
} // namespace PyDeviceAttribute