//==================================================================================================================
//
// DeviceProxy.h - include file for TANGO device api
//
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015
//                        European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with Tango.
// If not, see <http://www.gnu.org/licenses/>.
//
//
//===================================================================================================================

#ifndef _DEVICEPROXY_H
#define _DEVICEPROXY_H

#include <chrono>

/****************************************************************************************
 *                                                                                         *
 *                     The DeviceProxy class                                                *
 *                     --------------------                                                *
 *                                                                                         *
 ***************************************************************************************/

/**
 * High level class which provides the client with an easy-to-use interface to TANGO devices.
 *
 * The high level object which provides the client with an easy-to-use interface to TANGO devices. DeviceProxy
 * is a handle to the real Device (hence the name Proxy) and is not the real Device (of course).
 * DeviceProxy provides interfaces to all TANGO devices. The DeviceProxy manages timeouts,
 * stateless connections (new DeviceProxy() nearly always works), and reconnection if the device server is
 * restarted.
 *
 *
 *
 *
 * @ingroup Client
 * @headerfile tango.h
 */

class DeviceProxy : public Tango::Connection
{
  private:
    void real_constructor(const std::string &, bool ch_acc = true);

    Tango::DbDevice *db_dev;
    std::string device_name;
    std::string alias_name;
    DeviceInfo _info;
    bool is_alias;
    DeviceProxy *adm_device;
    std::string adm_dev_name;
    omni_mutex netcalls_mutex;
    int lock_ctr;
    int lock_valid;

    void connect_to_adm_device();

    void retrieve_read_args(const TgRequest &, std::vector<std::string> &);
    DeviceAttribute *redo_synch_read_call(TgRequest &);
    std::vector<DeviceAttribute> *redo_synch_reads_call(TgRequest &);
    void redo_synch_write_call(TgRequest &);
    void write_attribute(const AttributeValueList &);
    void write_attribute(const AttributeValueList_4 &);
    void create_locking_thread(ApiUtil *, std::chrono::seconds);
    void local_import(std::string &);
    void unsubscribe_all_events();

    enum read_attr_type
    {
        SIMPLE,
        MULTIPLE
    };

    void read_attr_except(CORBA::Request_ptr, long, read_attr_type);
    void write_attr_except(CORBA::Request_ptr, long, TgRequest::ReqType);
    void check_connect_adm_device();

    void omni420_timeout_attr(int, char *, read_attr_type);
    void omni420_except_attr(int, char *, read_attr_type);
    void omni420_timeout_wattr(int, char *);
    void omni420_except_wattr(int, char *);
    /**
     * Extracts values from Any and push it to a vector of DeviceAttributeHistory
     */
    template <class T>
    void extract_value(CORBA::Any &, std::vector<DeviceAttributeHistory> &);

    /**
     * Extracts values from Any and push it to a vector of DeviceDataHistory
     */
    template <class T>
    void extract_value(CORBA::Any &, std::vector<DeviceDataHistory> &, const Tango::AttributeDimList &ad);

    friend class AttributeProxy;

  protected:
    /// @privatesection
    std::string get_corba_name(bool) override;
    std::string build_corba_name() override;

    int get_lock_ctr() override
    {
        return lock_ctr;
    }

    void set_lock_ctr(int lo) override
    {
        lock_ctr = lo;
    }

    enum polled_object
    {
        Cmd,
        Attr
    };

    bool is_polled(polled_object, const std::string &, std::string &);
    void reconnect(bool) override;
    void get_remaining_param(AttributeInfoListEx *);
    template <typename T>
    void from_hist_2_AttHistory(const T &, std::vector<DeviceAttributeHistory> *);
    void from_hist4_2_DataHistory(const DevCmdHistory_4_var &, std::vector<DeviceDataHistory> *);
    void ask_locking_status(std::vector<std::string> &, std::vector<DevLong> &);
    void get_locker_host(const std::string &, std::string &);

    void same_att_name(const std::vector<std::string> &, const char *);

  private:
    class DeviceProxyExt
    {
      public:
        DeviceProxyExt() { }

        bool nethost_alias;
        std::string orig_tango_host;
    };

    std::unique_ptr<DeviceProxyExt> ext_proxy;

    omni_mutex lock_mutex;

  public:
    /**@name Constructors */
    //@{
    /**
     * Create a DeviceProxy instance.
     *
     * Create a DeviceProxy to a device of the specified name. The TANGO_HOST environment variable is used
     * to determine which TANGO database to connect to. The client can specify an ORB as argument if she
     * wants to. The constructor will connect to the TANGO database, query for the client’s network address and
     * build a connection to the device. If the device is defined in the TANGO database but the device server is
     * not running DeviceProxy will try to build a connection every time the client tries to access the device. If
     * the device is not defined an exception is thrown. Example :
     * \code
     * DeviceProxy *my_device = new DeviceProxy(“my/own/device”);
     * \endcode
     * See appendix on device naming in
     * <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango book</a>
     * for all details about Tango device naming syntax.
     * If an alias name is
     * defined for the device, this alias name can be used to create the DeviceProxy instance.
     *
     * @param [in] name The device name
     * @param [in] orb Pointer to the ORB. Default value is fine for 99 % of cases
     *
     * @throws WrongNameSyntax, ConnectionFailed
     *
     */
    DeviceProxy(const std::string &name, CORBA::ORB_var orb = CORBA::ORB::_nil());
    /**
     * Create a DeviceProxy instance.
     *
     * Create a DeviceProxy to a device of the specified name. The TANGO_HOST environment variable is used
     * to determine which TANGO database to connect to. The client can specify an ORB as argument if she
     * wants to. The constructor will connect to the TANGO database, query for the client’s network address and
     * build a connection to the device. If the device is defined in the TANGO database but the device server is
     * not running DeviceProxy will try to build a connection every time the client tries to access the device. If
     * the device is not defined an exception is thrown. Example :
     * \code
     * DeviceProxy *my_device = new DeviceProxy(“my/own/device”);
     * \endcode
     * See appendix on device naming in
     * <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango book</a>
     * for all details about Tango device naming syntax.
     * If an alias name is
     * defined for the device, this alias name can be used to create the DeviceProxy instance.
     *
     * @param [in] name The device name
     * @param [in] orb Pointer to the ORB. Default value is fine for 99 % of cases
     *
     * @throws WrongNameSyntax, ConnectionFailed
     *
     */
    DeviceProxy(const char *name, CORBA::ORB_var orb = CORBA::ORB::_nil());
    //@}
    /// @privatesection
    DeviceProxy(const std::string &name, bool ch_access, CORBA::ORB_var orb = CORBA::ORB::_nil());
    DeviceProxy(const char *, bool ch_access, CORBA::ORB_var orb = CORBA::ORB::_nil());

    DeviceProxy(const DeviceProxy &);
    DeviceProxy &operator=(const DeviceProxy &);
    ~DeviceProxy() override;

    DeviceProxy() :
        Connection(CORBA::ORB::_nil() != nullptr),
        db_dev(nullptr),
        adm_device(nullptr),
        lock_ctr(0),
        ext_proxy(nullptr)
    {
        dbase_used = false;
    }

    /// @publicsection

    //
    // general methods
    //

    /**@name Miscellaneous methods */
    //@{
    /**
     * Get device info.
     *
     * A method which returns information on the device in a DeviceInfo structure. Example :
     * \code
     * std::cout << " device info : " << std::endl
     * DeviceInfo dev_info = my_device->info() << std::endl;
     * std::cout << " dev_class " << dev_info.dev_class;
     * std::cout << " server_id " << dev_info.server_id;
     * std::cout << " server_host " << dev_info.server_host;
     * std::cout << " server_version " << dev_info.server_version;
     * std::cout << " doc_url " << dev_info.doc_url;
     * std::cout << " device_type " << dev_info.dev_type;
     * \endcode
     * All DeviceInfo fields are strings except server_version
     * which is a long integer.
     *
     * @return Device information structure
     *
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual DeviceInfo const &info();
    /**
     * Get device state.
     *
     * A method which returns the state of the device as a Tango::DevState type. Example :
     * \code
     * Tango::DevState dev_state = my_device->state();
     * \endcode
     *
     * @return Device state enumeration
     *
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual DevState state();
    /**
     * Get device status.
     *
     * A method which returns the status of the device as a std::string. Example :
     * \code
     * std::cout << "device status: " << my_device->status() << std::endl;
     * \endcode
     *
     * @return Device status
     *
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual std::string status();
    /**
     * Ping a device.
     *
     * A method which sends a ping to the device and returns the time elapsed as microseconds. Example :
     * @code
     * std::cout << " device ping took " << my_device->ping() << " microseconds" << std::endl;
     * @endcode
     *
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual int ping();
    /**
     * Get device black box content
     *
     * Get the last n commands executed on the device server and return a pointer to a vector of strings containing
     * the date, time, command, and from which client computer the command was executed. This method
     * allocates memory for the vector of strings returned to the caller. It is the caller responsibility to delete this
     * memory.
     *
     * @param [in] nb Number of requested records. If more records than available is requested, only the available
     * records are returned
     * @return Black box content
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<std::string> *black_box(int nb);
    /**
     * Return the device name (from the device itself)
     *
     * Return the device name (from the device itself)
     *
     * @return The device name
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual std::string name();
    /**
     * Return the administrator device name
     *
     * Returns the name of the corresponding administrator device. This is useful if you need to send an administration
     * command to the device server e.g. restart it.
     *
     * @return The administrator device name
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual std::string adm_name();

    /**
     * Return the device name as it is stored locally
     *
     * Return the device name as it is stored locally
     *
     * @return The device name
     */
    std::string dev_name() override
    {
        return device_name;
    }

    /**
     * Returns the device description as a string.
     *
     * Returns the device description as a string.
     *
     * @return The device description
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual std::string description();
    /**
     * Returns device alias
     *
     * Returns the device alias name if one is defined otherwise, throws a DevFailed exception with the reason
     * field set to Db_AliasNotDefined.
     *
     * @return The device alias
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::string alias();
    /**
     * Query the device for import info from the database.
     *
     * Query the device for import info from the database.
     *
     * @return The device import info
     * @throws ConnectionFailed, CommunicationFailed, NonDbDevice
     */
    virtual DbDevImportInfo import_info();
    /**
     * Get device Tango lib version
     *
     * Get the version of the Tango library used by the device
     *
     * @return The device Tango lib version
     */
    virtual int get_tango_lib_version();
    //@}

    /** @name Synchronous command related methods */
    //@{
    /**
     * Query the device for single command information.
     *
     * Query the device for information about a single command. This command returns a single CommandInfo
     * type.
     *
     * @param [in] cmd_name The command name
     * @return The command information structure
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual CommandInfo command_query(std::string cmd_name);
    /**
     * Query the device for all commands information.
     *
     * Query the device for info on all commands. This method returns a vector of CommandInfo types. This
     * method allocates memory for the vector of CommandInfo returned to the caller. It is the caller responsibility
     * to delete this memory
     *
     * @return The command information list: One CommandInfo structure per command
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual CommandInfoList *command_list_query();
    /**
     * Query all commands name
     *
     * Return the names of all commands implemented for this device as a vector of strings. This method allocates
     * memory for the vector of strings returned to the caller. It is the caller responsibility to delete this memory.
     *
     * @return A vector of string with one string per command
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<std::string> *get_command_list();

    /**
     * Get command information for a single command
     *
     * Return the command information for a single command.
     *
     * @param [in] cmd_name Command name
     * @return The command information
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual CommandInfo get_command_config(const std::string &cmd_name)
    {
        return command_query(cmd_name);
    }

    /**
     * Get information for a set of commands
     *
     * Return command information for the list of specified commands. This method allocates memory for the vector of
     * CommandInfo returned to the caller. It is the caller responsibility to delete this memory.
     *
     * @param [in] cmd_names Command name list
     * @return A vector of CommadnInfo srtuctures with one element per command
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual CommandInfoList *get_command_config(std::vector<std::string> &cmd_names);

    /**
     * Retrieve command history from polling buffer.
     *
     * Retrieve command history from the command polling buffer. The first argument is the command name.
     * The second argument is the wanted history depth. This method returns a vector of DeviceDataHistory
     * types. This method allocates memory for the vector of DeviceDataHistory returned to the caller. It is the
     * caller responsibility to delete this memory. See chapter "Advanced Feature"
     * <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango book</a>
     * for all details regarding polling.
     * @code
     * DeviceProxy dev = new DeviceProxy("...");
     * std::vector<DeviceDataHistory> *hist;
     *
     * hist = dev->command_history("Status",5);
     *
     * for (int i = 0;i < 5;i++)
     * {
     *     bool fail = (*hist)[i].has_failed();
     *     if (fail == false)
     *     {
     *        std::string str;
     *        (*hist)[i] >> str;
     *        std::cout << "Status = " << str << std::endl;
     *     }
     *     else
     *     {
     *        std::cout << "Command failed !" << std::endl;
     *        std::cout << "Error level 0 desc = " << ((*hist)[i].errors())[0].desc << std::endl;
     *     }
     *     std::cout << "Date = " << (*hist)[i].date().tv_sec << std::endl;
     * }
     * delete hist;
     * @endcode
     *
     * @param [in] cmd_name The command name
     * @param [in] depth The required history depth
     * @return The command information list: One CommandInfo structure per command
     * @throws NonSupportedFeature, ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceDataHistory> *command_history(const std::string &cmd_name, int depth);

    /**
     * Retrieve command history from polling buffer.
     *
     * Retrieve command history from the command polling buffer. The first argument is the command name.
     * The second argument is the wanted history depth. This method returns a vector of DeviceDataHistory
     * types. This method allocates memory for the vector of DeviceDataHistory returned to the caller. It is the
     * caller responsibility to delete this memory. See chapter "Advanced Feature"
     * <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango book</a>
     * for all details regarding polling.
     * @code
     * DeviceProxy dev = new DeviceProxy("...");
     * std::vector<DeviceDataHistory> *hist;
     *
     * hist = dev->command_history("Status",5);
     *
     * for (int i = 0;i < 5;i++)
     * {
     *     bool fail = (*hist)[i].has_failed();
     *     if (fail == false)
     *     {
     *        std::string str;
     *        (*hist)[i] >> str;
     *        std::cout << "Status = " << str << std::endl;
     *     }
     *     else
     *     {
     *        std::cout << "Command failed !" << std::endl;
     *        std::cout << "Error level 0 desc = " << ((*hist)[i].errors())[0].desc << std::endl;
     *     }
     *     std::cout << "Date = " << (*hist)[i].date().tv_sec << std::endl;
     * }
     * delete hist;
     * @endcode
     *
     * @param [in] cmd_name The command name
     * @param [in] depth The required history depth
     * @return The command information list: One CommandInfo structure per command
     * @throws NonSupportedFeature, ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceDataHistory> *command_history(const char *cmd_name, int depth)
    {
        std::string str(cmd_name);
        return command_history(str, depth);
    }

    //@}

    /** @name Synchronous attribute related methods */
    //@{
    /**
     * Query the device for attribute information
     *
     * Query the device for information about a single attribute. This command returns a single AttributeInfoEx
     * type.
     *
     * @b NOTE on compatibility between Tango V4 and Tango V5 regarding attribute properties:
     * @n Between Tango V4 and Tango V5, attribute configuration has been modified to incorporate alarm and event
     * related parameters. This explains why it exists two structure types for attribute configuration parameters.
     * All Tango V4 parameters are defined in a structure called AttributeInfo and a new structure called
     * AttributeInfoEx has been defined for all Tango V5 parameters. Nevertheless, AttributeInfoEx inherits from
     * AttributeInfo and it is always possible to call the Tango V5 DeviceProxy::attribute_query() method and to
     * store its result in one AttributeInfo structure thus allowing compatibility for client written for Tango V4
     * but linked with Tango V5. It is also possible for a client written and linked with Tango V5 to call Tango
     * V5 DeviceProxy::attribute_query() method to all kind of Tango devices. For device using Tango V4, the
     * alarm and event related parameters will be retrieved from the database instead of from the device
     *
     * @param [in] att_name The attribute name
     * @return The attribute information structure
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoEx attribute_query(std::string att_name)
    {
        return get_attribute_config(att_name);
    }

    /**
     * Query the device for information on all attributes
     *
     * Query the device for info on all attributes. This method returns a vector of AttributeInfo types.
     * This method allocates memory for the
     * vector of AttributeInfo structures returned to the caller. It is the caller responsibility to delete this memory.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @return A vector of AttributeInfo structures with one element per attribute
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoList *attribute_list_query();
    /**
     * Query the device for information on all attributes
     *
     * Query the device for info on all attributes. This method returns a vector of AttributeInfoEx types.
     * This method allocates memory for the
     * vector of AttributeInfoEx structures returned to the caller. It is the caller responsibility to delete this
     * memory.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @return A vector of AttributeInfoEx structures with one element per attribute
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoListEx *attribute_list_query_ex();
    /**
     * Query all attributes name
     *
     * Return the names of all attributes implemented for this device as a vector of strings. This method allocates
     * memory for the vector of strings returned to the caller. It is the caller responsibility to delete this memory.
     *
     * @return A vector of string with one string per attribute
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<std::string> *get_attribute_list();
    /**
     * Get attribute configuration for a list of attributes
     *
     * Return the attribute configuration for the list of specified attributes. To get all the attributes pass a vector
     * containing the string AllAttr (defined in tango_const.h). This method allocates memory for the vector of
     * AttributeInfo returned to the caller. It is the caller responsibility to delete this memory.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @param [in] att_names Attributes name list
     * @return A vector of AttributeInfo structures with one element per attribute
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoList *get_attribute_config(const std::vector<std::string> &att_names);
    /**
     * Get attribute configuration (extended) for a list of attributes
     *
     * Return the extended attribute configuration for the list of specified attributes. To get all the attributes
     * pass a vector containing the define AllAttr (defined in tango_const.h). This method allocates memory for
     * the vector of AttributeInfoEx returned to the caller. It is the caller responsibility to delete this memory.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @param [in] att_names Attributes name list
     * @return A vector of AttributeInfoEx structures with one element per attribute
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoListEx *get_attribute_config_ex(const std::vector<std::string> &att_names);
    /**
     * Get attribute configuration (extended) for a single attribute
     *
     * Return the attribute configuration for a single attribute.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @param [in] att_name Attribute name
     * @return The extended attribute information
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual AttributeInfoEx get_attribute_config(const std::string &att_name);
    /**
     * Set attribute configuration
     *
     * Change the attribute configuration for the specified attributes.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @param [in] atts New attributes configuration
     * @throws ConnectionFailed, CommunicationFailed, DevUnlocked, DevFailed from device
     */
    virtual void set_attribute_config(const AttributeInfoList &atts);
    /**
     * Set extended attribute configuration
     *
     * Change the extended attribute configuration for the specified attributes.
     * @n See DeviceProxy::attribute_query for a note about compatibility between attribute properties structure
     *
     * @param [in] atts New extended attributes configuration
     * @throws ConnectionFailed, CommunicationFailed, DevUnlocked, DevFailed from device
     */
    virtual void set_attribute_config(const AttributeInfoListEx &atts);
    /**
     * Read the list of specified attributes
     *
     * Read the list of specified attributes. To extract the value you have to use the operator of the class
     * DeviceAttribute which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion
     * from the attribute native type to user type e.g. if an attribute returns a short you cannot extract it as a
     * double, you have to extract it as a short. By default, if the server reports error for one of the attribute
     * in the list, this error will be passed to the user using exception when he (she) will try to extract the data
     * form the corresponding See sub-chapter on DeviceAttribute to learn how to change this default behaviour.
     * DeviceAttribute object. This method allocates memory for the vector of DeviceAttribute objects returned
     * to the caller. This is the caller responsibility to delete this memory. Example :
     * @code
     * std::vector<DeviceAttribute> *devattr;
     * std::vector<std::string> attr_names;
     *
     * attr_names.push_back("attribute_1");
     * attr_names.push_back("attribute_2");
     *
     * devattr = device->read_attributes(attr_names);
     *
     * short short_attr_1;
     * long long_attr_2;
     *
     * (*devattr)[0] >> short_attr_1;
     * (*devattr)[1] >> long_attr_2;
     *
     * std::cout << "my_attribute value " << short_attr;
     * delete devattr;
     * @endcode
     *
     * @param [in] att_names Attribute names
     * @return A std::vector of DeviceAttribute instances with one element for each read attribute
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual std::vector<DeviceAttribute> *read_attributes(const std::vector<std::string> &att_names);
    /**
     * Read a single attribute
     *
     * Read a single attribute. To extract the value you have to use the operator of the class DeviceAttribute
     * which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion from the
     * attribute native type to user type e.g. if an attribute returns a short you cannot extract it as a double (this
     * will return 0) you have to extract it as a short.
     * See usage eample in DeviceProxy::read_attributes
     *
     * @param [in] att_name Attribute name
     * @return The attribute value in a DeviceAttribute instance
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual DeviceAttribute read_attribute(const std::string &att_name);

    /**
     * Read the list of specified attributes
     *
     * Read a single attribute. To extract the value you have to use the operator of the class DeviceAttribute
     * which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion from the
     * attribute native type to user type e.g. if an attribute returns a short you cannot extract it as a double (this
     * will return 0) you have to extract it as a short.
     * See usage eample in DeviceProxy::read_attributes
     *
     * @param [in] att_name Attribute name
     * @return The attribute value in a DeviceAttribute instance
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual DeviceAttribute read_attribute(const char *att_name)
    {
        std::string str(att_name);
        return read_attribute(str);
    }

    /**
     * Write the specified attributes
     *
     * Write the specified attributes. To insert the values to write you have to use the operator of the DeviceAttribute
     * class which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion
     * from the user type to the attribute native type e.g. if an attribute expects a short you cannot insert it as
     * a double (this will throw an exception) you have to insert it as a short. Note that this is the only API call
     * which could throw a NamedDevFailedList exception. See @ref except to get all the details on this exception.
     * Example :
     * @code
     * std::vector<DeviceAttribute> attr_in;
     *
     * std::string att1_name("First_attr");
     * std::string att2_name("Second_attr");
     *
     * short short_attr = ...;
     * double double_attr = ...;
     *
     * attr_in.push_back(DeviceAttribute(att1_name,short_attr));
     * attr_in.push_back(DeviceAttribute(att2_name,double_attr));
     *
     * device->write_attributes(attr_in);
     * @endcode
     *
     * @param [in] attr_in Attributes name and value
     * @throws ConnectionFailed, CommunicationFailed, DeviceUnlocked, DevFailed or NamedDevFailedList from device
     */
    virtual void write_attributes(const std::vector<DeviceAttribute> &attr_in);
    /**
     * Write a single attribute
     *
     * Write a single attribute. To insert the value to write you have to use the operator of the class DeviceAttribute
     * which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion from the
     * user type to the attribute native type e.g. if an attribute expects a short you cannot insert it as a double
     * (this will throw an exception) you have to insert it as a short. See Deviceproxy::write_attributes for a usage
     * example.
     *
     * @param [in] attr_in Attribute name and value
     * @throws ConnectionFailed, CommunicationFailed, DeviceUnlocked, DevFailed from device
     */
    virtual void write_attribute(const DeviceAttribute &attr_in);
    /**
     * Write and read a single attribute
     *
     * Write then read a single attribute in a single network call. By default (serialisation by device), the execution
     * of this call in the server can’t be interrupted by other clients. To insert/extract the value to write/read you
     * have to use the operator of the class DeviceAttribute which corresponds to the data type of the attribute.
     * NOTE: There is no automatic type conversion from the user type to the attribute native type e.g. if an
     * attribute expects a short you cannot insert it as a double (this will throw an exception) you have to insert it
     * as a short.
     *
     * @param [in] attr_in Attribute name and value (to be written)
     * @return The read attribute data
     * @throws ConnectionFailed, CommunicationFailed, DeviceUnlocked, DevFailed from device
     */
    virtual DeviceAttribute write_read_attribute(const DeviceAttribute &attr_in);
    /**
     * Write and read attribute(s)
     *
     * Write then read attribute(s) in a single network call. By default (serialisation by device), the execution
     * of this call in the server can’t be interrupted by other clients. On the server side, attribute(s) are first
     * written and if no exception has been thrown during the write phase, attributes will be read.
     * To insert/extract the value to write/read you
     * have to use the operator of the class DeviceAttribute which corresponds to the data type of the attribute.
     * NOTE: There is no automatic type conversion from the user type to the attribute native type e.g. if an
     * attribute expects a short you cannot insert it as a double (this will throw an exception) you have to insert it
     * as a short.
     *
     * @param [in] attr_in The attribute(s) name and value (to be written)
     * @param [in] r_names Names of attribute to be read
     * @return The read attribute(s) data
     * @throws ConnectionFailed, CommunicationFailed, DeviceUnlocked, DevFailed from device
     */
    virtual std::vector<DeviceAttribute> *write_read_attributes(const std::vector<DeviceAttribute> &attr_in,
                                                                const std::vector<std::string> &r_names);
    /**
     * Retrieve attribute history from polling buffer
     *
     * Retrieve attribute history from the attribute polling buffer. The first argument is the attribute name. The
     * second argument is the wanted history depth. This method returns a vector of DeviceAttributeHistory
     * types. This method allocates memory for the vector of DeviceAttributeHistory returned to the caller. It is
     * the caller responsibility to delete this memory.
     * See chapter on Advanced Feature
     * in <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango
     * book</a> for all details regarding polling.
     * @code
     * DeviceProxy dev = new DeviceProxy("...");
     * std::vector<DeviceAttributeHistory> *hist;
     *
     * hist = dev->attribute_history("Current",5);
     *
     * for (int i = 0;i < 5;i++)
     * {
     *    bool fail = (*hist)[i].has_failed();
     *    if (fail == false)
     *    {
     *       std::cout << "Attribute name = " << (*hist)[i].get_name() << std::endl;
     *       std::cout << "Attribute quality factor = " << (*hist)[i].get_quality() << std::endl;
     *       long value;
     *       (*hist)[i] >> value;
     *       std::cout << "Current = " << value << std::endl;
     *    }
     *    else
     *    {
     *       std::cout << "Attribute failed !" << std::endl;
     *       std::cout << "Error level 0 desc = " << ((*hist)[i].get_err_stack())[0].desc << std::endl;
     *    }
     *    std::cout << "Date = " << (*hist)[i].get_date().tv_sec << std::endl;
     * }
     * delete hist;
     * @endcode
     *
     * @param [in] att_name Attribute name
     * @param [in] depth The required history depth
     * @return The read attribute history data
     * @throws NonSupportedFeature, ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceAttributeHistory> *attribute_history(const std::string &att_name, int depth);

    /**
     * Retrieve attribute history from polling buffer
     *
     * Retrieve attribute history from the attribute polling buffer. The first argument is the attribute name. The
     * second argument is the wanted history depth. This method returns a vector of DeviceAttributeHistory
     * types. This method allocates memory for the vector of DeviceAttributeHistory returned to the caller. It is
     * the caller responsibility to delete this memory.
     * See chapter on Advanced Feature
     * in <a href=http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/index.html target=new>Tango
     * book</a> for all details regarding polling.
     * @code
     * DeviceProxy dev = new DeviceProxy("...");
     * std::vector<DeviceAttributeHistory> *hist;
     *
     * hist = dev->attribute_history("Current",5);
     *
     * for (int i = 0;i < 5;i++)
     * {
     *    bool fail = (*hist)[i].has_failed();
     *    if (fail == false)
     *    {
     *       std::cout << "Attribute name = " << (*hist)[i].get_name() << std::endl;
     *       std::cout << "Attribute quality factor = " << (*hist)[i].get_quality() << std::endl;
     *       long value;
     *       (*hist)[i] >> value;
     *       std::cout << "Current = " << value << std::endl;
     *    }
     *    else
     *    {
     *       std::cout << "Attribute failed !" << std::endl;
     *       std::cout << "Error level 0 desc = " << ((*hist)[i].get_err_stack())[0].desc << std::endl;
     *    }
     *    std::cout << "Date = " << (*hist)[i].get_date().tv_sec << std::endl;
     * }
     * delete hist;
     * @endcode
     *
     * @param [in] att_name Attribute name
     * @param [in] depth The required history depth
     * @return The read attribute history data
     * @throws NonSupportedFeature, ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceAttributeHistory> *attribute_history(const char *att_name, int depth)
    {
        std::string str(att_name);
        return attribute_history(str, depth);
    }

    //@}

    /** @name Pipe related methods */
    //@{
    /**
     * Get pipe configuration for a list of pipes
     *
     * Return the pipe configuration for the list of specified pipes. To get all the pipes
     * pass a vector containing the define AllPipe (defined in tango_const.h). This method allocates memory for
     * the vector of PipeInfo returned to the caller. It is the caller responsibility to delete this memory.
     *
     * @param [in] pipe_names Pipes name list
     * @return A vector of PipeInfo structures with one element per pipe
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual PipeInfoList *get_pipe_config(const std::vector<std::string> &pipe_names);
    /**
     * Get pipe configuration for a single pipe
     *
     * Return the pipe configuration for a single pipe.
     *
     * @param [in] pipe_name Pipe name
     * @return The pipe information
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual PipeInfo get_pipe_config(const std::string &pipe_name);
    /**
     * Set pipe configuration
     *
     * Change the pipe configuration for the specified pipes.
     *
     * @param [in] pipes New pipes configuration
     * @throws ConnectionFailed, CommunicationFailed, DevUnlocked, DevFailed from device
     */
    virtual void set_pipe_config(const PipeInfoList &pipes);
    /**
     * Query all pipes name
     *
     * Return the names of all pipes implemented for this device as a vector of strings. This method allocates
     * memory for the vector of strings returned to the caller. It is the caller responsibility to delete this memory.
     *
     * @return A vector of string with one string per pipe
     * @throws ConnectionFailed, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<std::string> *get_pipe_list();
    /**
     * Read a pipe
     *
     * Read a pipe.
     *
     * @param [in] pipe_name Pipe name
     * @return The pipe value in a DevicePipe instance
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual DevicePipe read_pipe(const std::string &pipe_name);
    /**
     * Write a pipe
     *
     * Write a pipe.
     *
     * @param [in] pipe_data Data to be sent to the device through the pipe
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual void write_pipe(DevicePipe &pipe_data);
    /**
     * Write then read a pipe
     *
     * Write then read a pipe in a single network call. By default (serialisation by device), the execution
     * of this call in the server can’t be interrupted by other clients.
     *
     * @param [in] pipe_data Data to be sent to the device through the pipe
     * @return The pipe value in a DevicePipe instance
     * @throws ConnectionFailed, CommunicationFailed
     */
    virtual DevicePipe write_read_pipe(DevicePipe &pipe_data);
    //@}

    /** @name Asynchronous attribute related methods */
    //@{
    /**
     * Read a single attribute asynchronously
     *
     * Read asynchronously (polling model) a single attribute. This call returns an asynchronous call identifier
     * which is needed to get the attribute value.
     *
     * @param [in] att_name The attributes name
     * @return The call identifier
     * @throws ConnectionFailed
     */
    virtual long read_attribute_asynch(const std::string &att_name);

    /**
     * Read a single attribute asynchronously
     *
     * Read asynchronously (polling model) a single attribute. This call returns an asynchronous call identifier
     * which is needed to get the attribute value.
     *
     * @param [in] att_name The attributes name
     * @return The call identifier
     * @throws ConnectionFailed
     */
    virtual long read_attribute_asynch(const char *att_name)
    {
        std::string tmp(att_name);
        return read_attribute_asynch(tmp);
    }

    /**
     * Read asynchronously alist of attributes
     *
     * Read asynchronously (polling model) the list of specified attributes. This call returns an asynchronous call
     * identifier which is needed to get attributes value.
     *
     * @param [in] att_names The attributes names
     * @return The call identifier
     * @throws ConnectionFailed
     */
    virtual long read_attributes_asynch(const std::vector<std::string> &att_names);
    /**
     * Check if an asynchronous read_attributes call is arrived
     *
     * Check if the answer of an asynchronous read_attribute is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, it is returned to the caller in a
     * DeviceAttribute object. If the reply is an exception, it is re-thrown by this call. An exception is also thrown
     * in case of the reply is not yet arrived. To extract attribute value, you have to use the operator of the class
     * DeviceAttribute which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion
     * from the attribute native type to user type e.g. if an attribute returns a short you cannot extract it as a
     * double, you have to extract it as a short. Memory has been allocated for the DeviceAttribute object returned to
     * the caller. This is the caller responsibility to delete this memory.
     *
     * @param [in] id The call identifier
     * @return The attribute(s) data
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceAttribute> *read_attributes_reply(long id);
    /**
     * Check if an asynchronous read_attributes call is arrived (with timeout)
     *
     * Check if the answer of an asynchronous read_attribute is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, it is returned to the caller in a
     * DeviceAttribute object. If the reply is an exception, it is re-thrown by this call. If the reply is not yet
     * arrived, the call will wait (blocking the process) for the time specified in timeout. If after timeout
     * milliseconds, the reply is still not there, an exception is thrown. If timeout is set to 0, the call waits until
     * the reply arrived. To extract attribute value, you have to use the operator of the class DeviceAttribute which
     * corresponds to the data type of the attribute. NOTE: There is no automatic type conversion from the attribute
     * native type to user type e.g. if an attribute returns a short you cannot extract it as a double, you have to
     * extract it as a short. Memory has been allocated for the DeviceAttribute object returned to the caller. This is
     * the caller responsibility to delete this memory.
     *
     * @param [in] id The call identifier
     * @param [in] timeout The timeout value
     * @return The attribute(s) data
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual std::vector<DeviceAttribute> *read_attributes_reply(long id, long timeout);
    /**
     * Check if an asynchronous read_attribute (single attribute) call is arrived
     *
     * Check if the answer of an asynchronous read_attribute is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, it is returned to the caller in a
     * DeviceAttribute. If the reply is an exception, it is re-thrown by this call. An exception is also thrown in case
     * of the reply is not yet arrived. To extract attribute value, you have to use the operator of the class
     * DeviceAttribute which corresponds to the data type of the attribute. NOTE: There is no automatic type conversion
     * from the attribute native type to user type e.g. if an attribute returns a short you cannot extract it as a
     * double, you have to extract it as a short. Memory has been allocated for the DeviceAttribute object returned to
     * the caller. This is the caller responsibility to delete this memory.
     *
     * @param [in] id The call identifier
     * @return The attribute data
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual DeviceAttribute *read_attribute_reply(long id);
    /**
     * Check if an asynchronous read_attribute (single attribute) call is arrived (with timeout)
     *
     * Check if the answer of an asynchronous read_attributes is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, it is returned to the caller in a
     * DeviceAttribute. If the reply is an exception, it is re-thrown by this call. If the reply is not yet arrived, the
     * call will wait (blocking the process) for the time specified in timeout. If after timeout milliseconds, the reply
     * is still not there, an exception is thrown. If timeout is set to 0, the call waits until the reply arrived. To
     * extract attribute value, you have to use the operator of the class DeviceAttribute which corresponds to the data
     * type of the attribute. NOTE: There is no automatic type conversion from the attribute native type to user type
     * e.g. if an attribute returns a short you cannot extract it as a double, you have to extract it as a short. Memory
     * has been allocated for the DeviceAttribute object returned to the caller. This is the caller responsibility to
     * delete this memory.
     *
     * @param [in] id The call identifier
     * @param [in] timeout The timeout value
     * @return The attribute data
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual DeviceAttribute *read_attribute_reply(long id, long timeout);
    /**
     * Write a single attribute asynchronously
     *
     * Write asynchronously (polling model) a single attribute. To insert the value to write you have to use the
     * operator of the class DeviceAttribute which corresponds to the data type of the attribute. NOTE: There is
     * no automatic type conversion from the user type to the attribute native type e.g. if an attribute expects a
     * short you cannot insert it as a double (this will throw an exception) you have to insert it as a short. This
     * call returns an asynchronous call identifier which is needed to get the server reply.
     *
     * @param [in] argin The attribute name and value
     * @return The call identifier
     * @throws ConnectionFailed
     */
    virtual long write_attribute_asynch(const DeviceAttribute &argin);
    /**
     * Write asynchronously alist of attributes
     *
     * Write asynchronously (polling model) the specified attributes. To insert the value to write you have to use
     * the operator of the class DeviceAttribute which corresponds to the data type of the attribute. NOTE: There
     * is no automatic type conversion from the user type to the attribute native type e.g. if an attribute expects
     * a short you cannot insert it as a double (this will throw an exception) you have to insert it as a short. This
     * call returns an asynchronous call identifier which is needed to get the server reply.
     *
     * @param [in] argin The attributes name and value
     * @return The call identifier
     * @throws ConnectionFailed
     */
    virtual long write_attributes_asynch(const std::vector<DeviceAttribute> &argin);

    /**
     * Check if the answer of one asynchronous write_attribute (single attribute) call is arrived
     *
     * Check if the answer of an asynchronous write_attribute is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, the call returned. If the reply is an
     * exception, it is re-thrown by this call. An exception is also thrown in case of the reply is not yet arrived.
     *
     * @param [in] id The call identifier
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual void write_attribute_reply(long id)
    {
        write_attributes_reply(id);
    }

    /**
     * Check if the answer of one asynchronous write_attribute call (single attribute) is arrived with timeout
     *
     * Check if the answer of an asynchronous write_attribute is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, the call returned. If the reply is an
     * exception, it is re-thrown by this call. If the reply is not yet arrived, the call will wait (blocking the
     * process) for the time specified in timeout. If after timeout milliseconds, the reply is still not there, an
     * exception is thrown. If timeout is set to 0, the call waits until the reply arrived.
     *
     * @param [in] id The call identifier
     * @param [in] timeout The timeout value
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual void write_attribute_reply(long id, long timeout)
    {
        write_attributes_reply(id, timeout);
    }

    /**
     * Check if the answer of one asynchronous write_attributes call is arrived
     *
     * Check if the answer of an asynchronous write_attributes is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, the call returned. If the reply is an
     * exception, it is re-thrown by this call. An exception is also thrown in case of the reply is not yet arrived.
     *
     * @param [in] id The call identifier
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual void write_attributes_reply(long id);
    /**
     * Check if the answer of one asynchronous write_attributes call is arrived with timeout
     *
     * Check if the answer of an asynchronous write_attributes is arrived (polling model). id is the asynchronous
     * call identifier. If the reply is arrived and if it is a valid reply, the call returned. If the reply is an
     * exception, it is re-thrown by this call. If the reply is not yet arrived, the call will wait (blocking the
     * process) for the time specified in timeout. If after timeout milliseconds, the reply is still not there, an
     * exception is thrown. If timeout is set to 0, the call waits until the reply arrived.
     *
     * @param [in] id The call identifier
     * @param [in] timeout The timeout value
     * @throws AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device
     */
    virtual void write_attributes_reply(long id, long timeout);

    /**
     * Read a single attribute asynchronously in callback model
     *
     * Read asynchronously (callback model) a single attribute. The last argument is a reference to a callback
     * object. This callback object should be an instance of a user class inheriting from the Tango::CallBack class
     * with the @e attr_read() method overloaded.
     *
     * @param [in] att_name The attribute name
     * @param [in] cb The call-back object
     * @throws ConnectionFailed
     */
    virtual void read_attribute_asynch(const char *att_name, CallBack &cb)
    {
        std::string tmp(att_name);
        read_attribute_asynch(tmp, cb);
    }

    /**
     * Read a single attribute asynchronously in callback model
     *
     * Read asynchronously (callback model) a single attribute. The last argument is a reference to a callback
     * object. This callback object should be an instance of a user class inheriting from the Tango::CallBack class
     * with the @e attr_read() method overloaded.
     *
     * @param [in] att_name The attribute name
     * @param [in] cb The call-back object
     * @throws ConnectionFailed
     */
    virtual void read_attribute_asynch(const std::string &att_name, CallBack &cb);
    /**
     * Read asynchronously in callback model a list of attributes
     *
     * Read asynchronously (callback model) an attribute list. The last argument is a reference to a callback
     * object. This callback object should be an instance of a user class inheriting from the Tango::CallBack class
     * with the @e attr_read() method overloaded.
     *
     * @param [in] att_names The attribute name list
     * @param [in] cb The call-back object
     * @throws ConnectionFailed
     */
    virtual void read_attributes_asynch(const std::vector<std::string> &att_names, CallBack &cb);
    /**
     * Write asynchronously in callback model a single attribute
     *
     * Write asynchronously (callback model) a single attribute. The last argument is a reference to a callback
     * object. This callback object should be an instance of a user class inheriting from the Tango::CallBack class
     * with the @e attr_written() method overloaded.
     *
     * @param [in] argin The attribute name and value
     * @param [in] cb The call-back object
     * @throws ConnectionFailed
     */
    virtual void write_attribute_asynch(const DeviceAttribute &argin, CallBack &cb);
    /**
     * Write asynchronously in callback model a list of attributes
     *
     * Write asynchronously (callback model) an attribute list. The last argument is a reference to a callback
     * object. This callback object should be an instance of a user class inheriting from the Tango::CallBack class
     * with the @e attr_written() method overloaded.
     *
     * @param [in] argin The attribute names and values
     * @param [in] cb The call-back object
     * @throws ConnectionFailed
     */
    virtual void write_attributes_asynch(const std::vector<DeviceAttribute> &argin, CallBack &cb);

    //@}

    /** @name Asynchronous related methods */
    //@{
    /**
     * Get pending asynchronous request number
     *
     * Return number of device asynchronous pending requests. The input parameter is an enumeration with three
     * values which are:
     * @li POLLING : Returns only device polling model asynchronous request number
     * @li CALLBACK : Returns only device callback model asynchronous request number
     * @li ALL_ASYNCH : Returns device asynchronous request number
     *
     * @param [in] req The asynchronous request type
     * @return Pending asynchronous request number
     */
    virtual long pending_asynch_call(asyn_req_type req)
    {
        if(req == POLLING)
        {
            return pasyn_ctr;
        }
        else if(req == CALL_BACK)
        {
            return pasyn_cb_ctr;
        }
        else
        {
            return (pasyn_ctr + pasyn_cb_ctr);
        }
    }

    //@}

    /** @name Polling related methods */
    //@{
    /**
     * Check if a command is polled
     *
     * Returns true if the command "cmd_name" is polled. Otherwise, returns false.
     *
     * @param [in] cmd_name The command name
     * @return Flag set to true if the command is polled
     */
    virtual bool is_command_polled(const std::string &cmd_name);

    /**
     * Check if a command is polled
     *
     * Returns true if the command "cmd_name" is polled. Otherwise, returns false.
     *
     * @param [in] cmd_name The command name
     * @return Flag set to true if the command is polled
     */
    virtual bool is_command_polled(const char *cmd_name)
    {
        std::string tmp(cmd_name);
        return is_command_polled(tmp);
    }

    /**
     * Check if one attribute is polled
     *
     * Returns true if the attribute "att_name" is polled. Otherwise, returns false.
     *
     * @param [in] att_name The attribute name
     * @return Flag set to true if the attribute is polled
     */
    virtual bool is_attribute_polled(const std::string &att_name);

    /**
     * Check if one attribute is polled
     *
     * Returns true if the attribute "att_name" is polled. Otherwise, returns false.
     *
     * @param [in] att_name The attribute name
     * @return Flag set to true if the attribute is polled
     */
    virtual bool is_attribute_polled(const char *att_name)
    {
        std::string tmp(att_name);
        return is_attribute_polled(tmp);
    }

    /**
     * Get command polling period
     *
     * Returns the command "cmd_name" polling period in mS. If the command is not polled, it returns 0.
     *
     * @param [in] cmd_name The command name
     * @return The command polling period
     */
    virtual int get_command_poll_period(const std::string &cmd_name);

    /**
     * Get command polling period
     *
     * Returns the command "cmd_name" polling period in mS. If the command is not polled, it returns 0.
     *
     * @param [in] cmd_name The command name
     * @return The command polling period
     */
    virtual int get_command_poll_period(const char *cmd_name)
    {
        std::string tmp(cmd_name);
        return get_command_poll_period(tmp);
    }

    /**
     * Get attribute polling period
     *
     * Returns the attribute "att_name" polling period in mS. If the attribute is not polled, it returns 0.
     *
     * @param [in] att_name The attribute name
     * @return The attribute polling period
     */
    virtual int get_attribute_poll_period(const std::string &att_name);

    /**
     * Get attribute polling period
     *
     * Returns the attribute "att_name" polling period in mS. If the attribute is not polled, it returns 0.
     *
     * @param [in] att_name The attribute name
     * @return The attribute polling period
     */
    virtual int get_attribute_poll_period(const char *att_name)
    {
        std::string tmp(att_name);
        return get_attribute_poll_period(tmp);
    }

    /**
     * Get polling status
     *
     * Returns the device polling status. There is one string for each polled command/attribute. Each string is
     * multi-line string with :
     * @li The attribute/command name
     * @li The attribute/command polling period (in mS)
     * @li The attribute/command polling ring buffer depth
     * @li The time needed for the last command/attribute execution (in mS)
     * @li The time since data in the ring buffer has not been updated
     * @li The delta time between the last records in the ring buffer
     * @li The exception parameters in case of the last command/attribute execution failed
     *
     * This method allocates memory for the vector of string(s) returned to the caller. It is the caller responsibility
     * to delete this memory.
     *
     * @return The polling status
     */
    virtual std::vector<std::string> *polling_status();
    /**
     * Poll a command
     *
     * Add the command "cmd_name" to the list of polled command. The polling period is specified by "polling_period"
     * (in mS). If the command is already polled, this method will update the polling period according to
     * "polling_period".
     *
     * @param [in] cmd_name The command name
     * @param [in] polling_period The polling period
     */
    virtual void poll_command(const std::string &cmd_name, int polling_period);

    /**
     * Poll a command
     *
     * Add the command "cmd_name" to the list of polled command. The polling period is specified by "polling_period"
     * (in mS). If the command is already polled, this method will update the polling period according to
     * "polling_period".
     *
     * @param [in] cmd_name The command name
     * @param [in] polling_period The polling period
     */
    virtual void poll_command(const char *cmd_name, int polling_period)
    {
        std::string tmp(cmd_name);
        poll_command(tmp, polling_period);
    }

    /**
     * Poll an attribute
     *
     * Add the attribute "att_name" to the list of polled attributes. The polling period is specified by
     * "polling_period" (in mS). If the attribute is already polled, this method will update the polling period
     * according to "polling_period".
     *
     * @param [in] att_name The attribute name
     * @param [in] polling_period The polling period
     */
    virtual void poll_attribute(const std::string &att_name, int polling_period);

    /**
     * Poll an attribute
     *
     * Add the attribute "att_name" to the list of polled attributes. The polling period is specified by
     * "polling_period" (in mS). If the attribute is already polled, this method will update the polling period
     * according to "polling_period".
     *
     * @param [in] att_name The attribute name
     * @param [in] polling_period The polling period
     */
    virtual void poll_attribute(const char *att_name, int polling_period)
    {
        std::string tmp(att_name);
        poll_attribute(tmp, polling_period);
    }

    /**
     * Stop polling a command
     *
     * Remove command "cmd_name" from the list of polled command.
     *
     * @param [in] cmd_name The command name
     */
    virtual void stop_poll_command(const std::string &cmd_name);

    /**
     * Stop polling a command
     *
     * Remove command "cmd_name" from the list of polled command.
     *
     * @param [in] cmd_name The command name
     */
    virtual void stop_poll_command(const char *cmd_name)
    {
        std::string tmp(cmd_name);
        stop_poll_command(tmp);
    }

    /**
     * Stop polling an attribute
     *
     * Remove attribute "att_name" from the list of polled attributes
     *
     * @param [in] att_name The attribute name
     */
    virtual void stop_poll_attribute(const std::string &att_name);

    /**
     * Stop polling an attribute
     *
     * Remove attribute "att_name" from the list of polled attributes
     *
     * @param [in] att_name The attribute name
     */
    virtual void stop_poll_attribute(const char *att_name)
    {
        std::string tmp(att_name);
        stop_poll_attribute(tmp);
    }

    //@}

    /** @name Event related methods */
    //@{
    /**
     * Subscribe for event reception
     *
     * The client call to subscribe for event reception in the push model. The client implements a callback method
     * which is triggered when the event is received. Filtering is done based on the event
     * type. For example when reading the state and the reason specified is "change" the event will be fired only
     * when the state changes. Events consist of an attribute name and the event reason. A standard set of reasons
     * are implemented by the system, additional device specific reasons can be implemented by device servers
     * programmers.
     * The attribute parameter is the device attribute name which will be sent as an event e.g. “current”, event
     * parameter is the event reason,
     * cb is a pointer to a class inheriting from the Tango CallBack class and implementing a push_event() method.
     * The subscribe_event() call returns an event id which has to be specified when unsubscribing from this
     * event. Please, note that the cb parameter is a pointer. The lifetime of the pointed to object must at least
     * be equal to the time when events are requested because only the pointer is stored into the event machinery.
     * The same thing is true for the DeviceProxy instance on which the subscribe_event() method is called.
     *
     * @param [in] att_name The attribute name
     * @param [in] event The event type
     * @param [in] cb The callback object
     * @return The event identifier
     * @throws EventSystemFailed
     */
    virtual int subscribe_event(const std::string &att_name, EventType event, CallBack *cb);
    /**
     * Subscribe for event reception with stateless support
     *
     * The client call to subscribe for event reception in the push model. The client implements a callback method
     * which is triggered when the event is received. Filtering is done based on the event
     * type. For example when reading the state and the reason specified is "change" the event will be fired only
     * when the state changes. Events consist of an attribute name and the event reason. A standard set of reasons
     * are implemented by the system, additional device specific reasons can be implemented by device servers
     * programmers.
     * The attribute parameter is the device attribute name which will be sent as an event e.g. “current”, event
     * parameter is the event reason,
     * cb is a pointer to a class inheriting from the Tango CallBack class and implementing a push_event() method.
     * The last call parameter is named stateless. When the stateless flag is set to false, an exception will be thrown
     * when the event subscription encounters a problem. With the stateless flag set to true, the event subscription
     * will always succeed, even if the corresponding device server is not running. The keep alive thread will try every
     * 10 seconds to subscribe for the specified event. At every subscription retry, a callback is executed which
     * contains the corresponding exception. The subscribe_event() call returns an event id which has to be specified
     * when unsubscribing from this event. Please, note that the cb parameter is a pointer. The lifetime of the pointed
     * to object must at least be equal to the time when events are requested because only the pointer is stored into
     * the event machinery. The same thing is true for the DeviceProxy instance on which the subscribe_event() method is
     * called.
     *
     * @param [in] att_name The attribute name
     * @param [in] event The event type
     * @param [in] cb The callback object
     * @param [in] stateless The stateless flag
     * @return The event identifier
     * @throws EventSystemFailed
     */
    virtual int subscribe_event(const std::string &att_name, EventType event, CallBack *cb, bool stateless);
    /**
     * Subscribe for event reception with event queue
     *
     * The client call to subscribe for event reception in the pull model. Instead of a callback method the client
     * has to specify the size of the event reception buffer.
     * The event reception buffer is implemented as a round robin buffer. This way the client can set-up
     * different ways to receive events.
     * @li Event reception buffer size = 1 : The client is interested only in the value of the last event received.
     * All other events that have been received since the last reading are discarded.
     * @li Event reception buffer size > 1 : The client has chosen to keep an event history of a given size. When
     * more events arrive since the last reading, older events will be discarded.
     * @li Event reception buffer size = ALL_EVENTS : The client buffers all received events. The buffer size
     * is unlimited and only restricted by the available memory for the client.
     *
     * All other parameters (att_name, event, stateless) are similar to those described in
     * DeviceProxy::subscribe_event(const string &,EventType,CallBack *,bool)
     *
     * @param [in] att_name The attribute name
     * @param [in] event The event type
     * @param [in] event_queue_size The event queue size
     * @param [in] stateless The stateless flag
     * @return The event identifier
     * @throws EventSystemFailed
     */
    virtual int
        subscribe_event(const std::string &att_name, EventType event, int event_queue_size, bool stateless = false);
    /**
     * Subscribe for device event reception with stateless support
     *
     * The client call to subscribe for <B>device</B> event reception in the push model. The client implements a
     * callback method which is triggered when the event is received. Filtering is done based on the event type. Today,
     * only one event type is supported for <B>device</B> event. This event type is a device interface change event. cb
     * is a pointer to a class inheriting from the Tango CallBack class and implementing a push_event() method. The last
     * call parameter is named stateless. When the stateless flag is set to false, an exception will be thrown when the
     * event subscription encounters a problem. With the stateless flag set to true, the event subscription will always
     * succeed, even if the corresponding device server is not running. The keep alive thread will try every 10 seconds
     * to subscribe for the specified event. At every subscription retry, a callback is executed which contains the
     * corresponding exception. The subscribe_event() call returns an event id which has to be specified when
     * unsubscribing from this event. Please, note that the cb parameter is a pointer. The lifetime of the pointed to
     * object must at least be equal to the time when events are requested because only the pointer is stored into the
     * event machinery. The same thing is true for the DeviceProxy instance on which the subscribe_event() method is
     * called.
     *
     * @param [in] event The event type
     * @param [in] cb The callback object
     * @param [in] stateless The stateless flag
     * @return The event identifier
     * @throws EventSystemFailed
     */
    virtual int subscribe_event(EventType event, CallBack *cb, bool stateless = false);

    /**
     * Subscribe for device event reception with stateless support and event queue
     *
     * The client call to subscribe for <B>device</B> event reception in the pull model.
     * Today, only one event type is supported for <B>device</B> event. This event type is a device interface
     * change event.
     * Instead of a callback method the client
     * has to specify the size of the event reception buffer.
     * The event reception buffer is implemented as a round robin buffer. This way the client can set-up
     * different ways to receive events.
     * @li Event reception buffer size = 1 : The client is interested only in the value of the last event received.
     * All other events that have been received since the last reading are discarded.
     * @li Event reception buffer size > 1 : The client has chosen to keep an event history of a given size. When
     * more events arrive since the last reading, older events will be discarded.
     * @li Event reception buffer size = ALL_EVENTS : The client buffers all received events. The buffer size
     * is unlimited and only restricted by the available memory for the client.
     * The last call parameter is named stateless. When the stateless flag is set to false, an exception will be thrown
     * when the event subscription encounters a problem.
     * With the stateless flag set to true, the event subscription will always succeed.
     *
     * @param [in] event The event type
     * @param [in] event_queue_size The event queue size
     * @param [in] stateless The stateless flag
     * @return The event identifier
     * @throws EventSystemFailed
     */
    virtual int subscribe_event(EventType event, int event_queue_size, bool stateless = false);
    /**
     * Unsubscribe for event reception
     *
     * Unsubscribe a client from receiving the event specified by event_id. event_id is the event identifier returned
     * by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @throws EventSystemFailed
     */
    virtual void unsubscribe_event(int event_id);
    /**
     * Fire event callback in event pull model
     *
     * The method extracts all waiting events from the event reception buffer and executes the callback method
     * cb for every event. During event subscription the client must have chosen the <B>pull model</B> for this event.
     * event_id is the event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [in] cb The callback object
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, CallBack *cb);
    /**
     * Get arrived events from the event queue in event pull model
     *
     * The method extracts all waiting events from the event reception buffer. The returned event_list is a vector
     * of EventData pointers. The EventData object contains the event information as for the callback methods.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [out] event_list The event(s) list
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, EventDataList &event_list);
    /**
     * Get arrived events from event queue in event pull model
     *
     * The method extracts all waiting attribute configuration events from the event reception buffer. The returned
     * event_list is a vector of AttrConfEventData pointers. The AttrConfEventData object contains the event
     * information as for the callback methods.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [out] event_list The event(s) list
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, AttrConfEventDataList &event_list);
    /**
     * Get arrived events from event queue in event pull model
     *
     * The method extracts all waiting attribute configuration events from the event reception buffer. The returned
     * event_list is a vector of DataReadyEventData pointers. The DataReadyEventData object contains the event
     * information as for the callback methods.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [out] event_list The event(s) list
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, DataReadyEventDataList &event_list);
    /**
     * Get arrived events from event queue in event pull model
     *
     * The method extracts all waiting attribute configuration events from the event reception buffer. The returned
     * event_list is a vector of DevIntrChangeEventData pointers. The DevIntrChangeEventData object contains the event
     * information as for the callback methods.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [out] event_list The event(s) list
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, DevIntrChangeEventDataList &event_list);
    /**
     * Get arrived events from event queue in event pull model
     *
     * The method extracts all waiting pipe events from the event reception buffer. The returned
     * event_list is a vector of PipeEventData pointers. The PipeEventData object contains the event
     * information as for the callback methods.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @param [out] event_list The event(s) list
     * @throws EventSystemFailed
     */
    virtual void get_events(int event_id, PipeEventDataList &event_list);
    /**
     * Get event number in event queue
     *
     * Returns the number of stored events in the event reception buffer. After every call to DeviceProxy:get_events(),
     * the event queue size is 0.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @return The event number in queue
     * @throws EventSystemFailed
     */
    virtual int event_queue_size(int event_id);
    /**
     * Get date of the last event in queue
     *
     * Returns the arrival time of the last event stored in the event reception buffer. After every call to Device-
     * Proxy:get_events(), the event reception buffer is empty. In this case an exception will be returned.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @return The last event date
     * @throws EventSystemFailed
     */
    virtual TimeVal get_last_event_date(int event_id);
    /**
     * Check if the event queue is empty
     *
     * Returns true when the event reception buffer is empty.
     * During event subscription the client must have chosen the <B>pull model</B> for this event. event_id is the
     * event identifier returned by the DeviceProxy::subscribe_event() method.
     *
     * @param [in] event_id The event identifier
     * @return true if the event queue is empty
     * @throws EventSystemFailed
     */
    virtual bool is_event_queue_empty(int event_id);
    //@}

    /** @name Property related methods */
    //@{
    /**
     * Get a single device property
     *
     * Get a single property for a device. The property to get is specified as a string. Refer to
     * DbDevice::get_property() and DbData sections below for details on the DbData type.
     *
     * @param [in] prop_name The property name
     * @param [out] db The property value
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void get_property(const std::string &prop_name, DbData &db);
    /**
     * Get a list of device properties
     *
     * Get a list of properties for a device. The properties to get are specified as a vector of strings. Refer to
     * DbDevice::get_property() and DbData sections below for details on the DbData type.
     *
     * @param [in] prop_names The property names
     * @param [out] db The properties values
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void get_property(const std::vector<std::string> &prop_names, DbData &db);
    /**
     * Get property(ies) for a device
     *
     * Get property(ies) for a device. Properties to get are specified using the DbData type. Refer to DbDevice::
     * get_property() and DbData sections below for details.
     *
     * @param [in,out] db The property(ies) names and values
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void get_property(DbData &db);
    /**
     * Put property(ies) for a device
     *
     * Put property(ies) for a device. Properties to put are specified using the DbData type. Refer to DbDevice::
     * put_property() and DbData sections below for details.
     *
     * @param [in] db The property(ies) names and values
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void put_property(const DbData &db);
    /**
     * Delete a single device property
     *
     * Delete a single property for a device. The property to delete is specified as a string.
     *
     * @param [in] prop_name The property name
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void delete_property(const std::string &prop_name);
    /**
     * Delete a list of device properties
     *
     * Delete a list of properties for a device. The properties to delete are specified as a vector of strings.
     *
     * @param [in] prop_names The property names list
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void delete_property(const std::vector<std::string> &prop_names);
    /**
     * Delete property(ies) for a device
     *
     * Delete property(ies) for a device. Properties to delete are specified using the DbData type. Refer to DbDevice::
     * get_property() and DbData sections below for details.
     *
     * @param [in] db The property names
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void delete_property(const DbData &db);
    /**
     * Get list of property names for a device
     *
     * Get the list of property names for the device. The parameter filter allows the user to filter the returned name
     * list. The wildcard character is ’*’. Only one wildcard character is allowed in the filter parameter. The name
     * list is returned in the vector of strings passed as the method second argument.
     *
     * @param [in] filter The filter
     * @param [out] prop_list The device property list
     * @throws NonDbDevice, ConnectionFailed, CommunicationFailed, DevFailed from database device
     */
    virtual void get_property_list(const std::string &filter, std::vector<std::string> &prop_list);
    //@}

    /** @name Logging related methods */
    //@{
    /**
     * Add a logging target to the device
     *
     * Adds a new logging target to the device. The target_type_name input parameter must follow the
     * format: @b target_type::target_name. Supported target types are:
     * @li console
     * @li file
     * @li device
     *
     * For a device target,
     * the target_name part of the target_type_target_name parameter must contain the name of a log consumer
     * device (as defined in A.8). For a file target, target_name is the full path to the file to log to. If omitted,
     * the device’s name is used to build the file name (which is something like domain_family_member.log). Finally, the
     * target_name part of the target_type_target_name input parameter is ignored in case of a console target and can be
     * omitted.
     *
     * @param [in] target_type_name The target type and name
     * @throws DevFailed from device
     */
    virtual void add_logging_target(const std::string &target_type_name);

    /**
     * Add a logging target to the device
     *
     * Adds a new logging target to the device. The target_type_name input parameter must follow the
     * format: @b target_type::target_name. Supported target types are:
     * @li console
     * @li file
     * @li device
     *
     * For a device target,
     * the target_name part of the target_type_target_name parameter must contain the name of a log consumer
     * device (as defined in A.8). For a file target, target_name is the full path to the file to log to. If omitted,
     * the device’s name is used to build the file name (which is something like domain_family_member.log). Finally, the
     * target_name part of the target_type_target_name input parameter is ignored in case of a console target and can be
     * omitted.
     *
     * @param [in] target_type_name The target type and name
     * @throws DevFailed from device
     */
    virtual void add_logging_target(const char *target_type_name)
    {
        add_logging_target(std::string(target_type_name));
    }

    /**
     * Remove a logging target from the device
     *
     * Removes a logging target from the device’s target list. The target_type_name input parameter must
     * follow the format: target_type::target_name. Supported target types are:
     * @li console
     * @li file
     * @li device
     *
     * For a
     * device target, the target_name part of the target_type_target_name parameter must contain the name of a
     * log consumer device (as defined in ). For a file target, target_name is the full path to the file to remove. If
     * omitted, the default log file is removed. Finally, the target_name part of the target_type_target_name input
     * parameter is ignored in case of a console target and can be omitted.
     * If target_name is set to "*", all targets of the specified target_type are removed.
     *
     * @param [in] target_type_name The target type and name
     */
    virtual void remove_logging_target(const std::string &target_type_name);

    /**
     * Remove a logging target from the device
     *
     * Removes a logging target from the device’s target list. The target_type_name input parameter must
     * follow the format: target_type::target_name. Supported target types are:
     * @li console
     * @li file
     * @li device
     *
     * For a
     * device target, the target_name part of the target_type_target_name parameter must contain the name of a
     * log consumer device (as defined in ). For a file target, target_name is the full path to the file to remove. If
     * omitted, the default log file is removed. Finally, the target_name part of the target_type_target_name input
     * parameter is ignored in case of a console target and can be omitted.
     * If target_name is set to "*", all targets of the specified target_type are removed.
     *
     * @param [in] target_type_name The target type and name
     */
    virtual void remove_logging_target(const char *target_type_name)
    {
        remove_logging_target(std::string(target_type_name));
    }

    /**
     * Get current device's logging targets
     *
     * Returns a vector of string containing the current device’s logging targets. Each vector element has the
     * following format: target_type::target_name. An empty vector is returned is the device has no logging
     * targets.
     *
     * @return List of loggin target
     */
    virtual std::vector<std::string> get_logging_target();
    /**
     * Get current device's logging level
     *
     * Returns the current device’s logging level (0=OFF, 1=FATAL, 2=ERROR, 3=WARNING, 4=INFO, 5=DEBUG).
     *
     * @return The device logging level
     */
    virtual int get_logging_level();
    /**
     * Set the  device logging level
     *
     * Changes the device’s logging level. (0=OFF, 1=FATAL, 2=ERROR, 3=WARNING, 4=INFO, 5=DEBUG).
     *
     * @param [in] level The new device logging level
     */
    virtual void set_logging_level(int level);
    //@}

    /** @name Locking related methods */
    //@{
    /**
     * Lock a device
     *
     * Lock a device. The lock_validity is the time (in seconds) the lock is kept valid after the previous lock call.
     * A default value of 10 seconds is provided and should be fine in most cases. In case it is necessary to change
     * the lock validity, it’s not possible to ask for a validity less than a minimum value set to 2 seconds. The
     * library provided an automatic system to periodically re lock the device until an unlock call. No code is
     * needed to start/stop this automatic re-locking system. The locking system is re-entrant. It is then allowed
     * to call this method on a device already locked by the same process. The locking system has the following
     * features:
     * @li It is impossible to lock the database device or any device server process admin device
     * @li Destroying a locked DeviceProxy unlocks the device
     * @li Restarting a locked device keeps the lock
     * @li It is impossible to restart a device locked by someone else
     * @li Restarting a server breaks the lock
     *
     * A locked device is protected against the following calls when executed by another client:
     * @li command_inout call except for device state and status requested via command and for the set of
     * commands defined as allowed following the definition of allowed command in the Tango control
     * access schema.
     * @li write_attribute call
     * @li write_pipe call
     * @li write_read_attribute call
     * @li write_read_pipe call
     * @li set_attribute_config call
     * @li set_pipe_config call
     *
     * @param [in] lock_validity The lock validity (in seconds)
     */
    virtual void lock(int lock_validity = DEFAULT_LOCK_VALIDITY);
    /**
     * Unlock a device
     *
     * Unlock a device. If used, the method argument provides a back door on the locking system. If this argument
     * is set to true, the device will be unlocked even if the caller is not the locker. This feature is provided for
     * administration purpose and should be used very carefully. If this feature is used, the locker will receive a
     * DeviceUnlocked exception during the next call which is normally protected by the locking Tango system.
     *
     * @param [in] force The force unlock flag
     */
    virtual void unlock(bool force = false);
    /**
     * Get device locking status
     *
     * This method returns a plain string describing the device locking status. This string can be:
     * @li "Device <device name> is not locked" in case the device is not locked
     * @li "Device <device name> is locked by CPP or Python client with PID <pid> from host <host name>"
     * in case the device is locked by a CPP client
     * @li "Device <device name> is locked by JAVA client class <main class> from host <host name>" in case
     * the device is locked by a JAVA client
     *
     * @return The device locking status
     */
    virtual std::string locking_status();
    /**
     * Check if the device is locked
     *
     * Returns true if the device is locked. Otherwise, returns false
     *
     * @return The device locked flag
     */
    virtual bool is_locked();
    /**
     * Check if the device is locked by the caller
     *
     * Returns true if the device is locked by the caller. Otherwise, returns false (device not locked or locked by
     * someone else)
     *
     * @return The device locked flag
     */
    virtual bool is_locked_by_me();
    /**
     * Get device locking information
     *
     * If the device is locked, this method returns true an set some locker process informations in the structure
     * passed as argument. If the device is not locked, the method returns false. The LockerInfo structure definition
     * is
     * @code
     * typedef union
     * {
     *    pid_t LockerPid;
     *    unsigned long UUID[4];
     * }LockerId;
     *
     * enum LockerLanguage
     * {
     *    CPP,
     *    JAVA
     * };
     *
     * struct LockerInfo
     * {
     *    LockerLanguage ll;
     *    LockerId li;
     *    std::string locker_host;
     *    std::string locker_class;
     * };
     * @endcode
     *
     * @param [out] li Device locking information
     * @return The device locked flag
     */
    virtual bool get_locker(LockerInfo &li);
    //@}

    /// @privatesection

    virtual void parse_name(const std::string &);
    virtual Database *get_device_db();

    DeviceProxy *get_adm_device()
    {
        return adm_device;
    }

    //
    // attribute methods
    //

    void read_attribute(const std::string &, AttributeValue_4 *&);
    void read_attribute(const std::string &, AttributeValue_5 *&);
    void read_attribute(const char *, DeviceAttribute &);

    void read_attribute(const std::string &at, DeviceAttribute &da)
    {
        read_attribute(at.c_str(), da);
    }

    //
    // Old event methods
    //
    virtual int subscribe_event(const std::string &attr_name,
                                EventType event,
                                CallBack *,
                                const std::vector<std::string> &filters); // For compatibility with Tango < 8
    virtual int subscribe_event(const std::string &attr_name,
                                EventType event,
                                CallBack *,
                                const std::vector<std::string> &filters,
                                bool stateless); // For compatibility with Tango < 8
    virtual int subscribe_event(const std::string &attr_name,
                                EventType event,
                                int event_queue_size,
                                const std::vector<std::string> &filters,
                                bool stateless = false); // For compatibility with Tango < 8
};

#endif /* _DEVICEPROXY_H */
