/*
 * SPDX-FileCopyrightText: 2004 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#ifndef TANGO_SERVER_DEVICEIMPL_H
#define TANGO_SERVER_DEVICEIMPL_H

#include <tango/common/defines.h>
#include <tango/common/telemetry/Interface.h>
#include <tango/server/Attr.h>
#include <tango/server/Attribute.h>
#include <tango/server/AutoTangoMonitor.h>
#include <tango/server/BlackBox.h>
#include <tango/server/DeviceClass.h>
#include <tango/server/DeviceEventSubscriptionState.h>
#include <tango/server/DevIntrThread.h>
#include <tango/server/ImageAttr.h>
#include <tango/server/MultiAttribute.h>
#include <tango/server/MultiClassAttribute.h>
#include <tango/server/NoSyncModelTangoMonitor.h>
#include <tango/server/PipeProperty.h>
#include <tango/server/PollObj.h>
#include <tango/server/SpectrumAttr.h>
#include <tango/server/TangoTimestamp.h>

#include <algorithm>
#include <map>
#include <memory>
#include <string_view>
#include <vector>

namespace Tango
{

class Command;
class DeviceClass;
class EventSupplier;
class EventSubscriptionChangeCmd;
class Util;
class FwdWrongConf;
class DbDevice;
class DevicePipeBlob;

/** @defgroup Server Server classes */

//=============================================================================
//
//            The DeviceImpl class
//
//
// description :    This class is derived directly from the Tango::Device_skel
//            class generated by CORBA. It represents the CORBA
//            servant which will be accessed by the client.
//            It implements all the methods
//            and attributes defined in the IDL interface for Device.
//
//=============================================================================

/**
 * Base class for all TANGO device.
 *
 * This class inherits from CORBA classes where all the network layer is
 * implemented.
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

class DeviceImpl : public virtual POA_Tango::Device
{
  public:
    friend class Tango::AutoTangoMonitor;
    friend class Tango::NoSyncModelTangoMonitor;
    friend class Tango::EventSupplier;
    friend class Tango::EventSubscriptionChangeCmd;

    /**@name Constructors
     * Miscellaneous constructors */
    //@{

    /**
     * Constructs a newly allocated DeviceImpl object from all its creation
     * parameters with some default values.
     *
     * The device is constructed from its name, its description, an original state
     * and status. This constructor defined default values for the description,
     * state and status parameters. The default device description is <i>A TANGO device</i>.
     * The default device state is <i>UNKNOWN</i> and the default device status
     * is <i>Not initialised</i>.
     *
     * @param     device_class    Pointer to the device class object
     * @param    dev_name    The device name
     * @param    desc    The device desc
     * @param    dev_state     The device initial state
     * @param    dev_status    The device initial status
     *
     */

    DeviceImpl(DeviceClass *device_calss,
               std::string_view dev_name,
               std::string_view desc = "A TANGO device",
               Tango::DevState dev_state = Tango::UNKNOWN,
               std::string_view dev_status = StatusNotSet);
    //@}

    /**@name Destructor
     * Only one desctructor is defined for this class */
    //@{
    /**
     * The device desctructor.
     */
    ~DeviceImpl() override;

    //@}

    /**@name Get/Set object members.
     * These methods allows the external world to get/set DeviceImpl instance
     * data members
     */
    //@{
    /**
     * Get device status.
     *
     * Return the device dev_status field. This method does the same thing than the
     * default status_cmd method.
     *
     * @return Device status
     */
    std::string &get_status()
    {
        return device_status;
    }

    /**
     * Set device status.
     *
     * @param new_status The new device status
     */
    void set_status(const std::string &new_status)
    {
        device_status = new_status;
    }

    /**
     * Appends a string to the device status.
     *
     * @param stat The string to be appened to the device status
     * @param new_line If true, appends a new line character before the string
     */
    void append_status(const std::string &stat, bool new_line = false)
    {
        if(new_line)
        {
            device_status.append(1, '\n');
        }
        device_status.append(stat);
    }

    /**
     * Get device state.
     *
     * Return the device dev_state field. This method does the same thing than the
     * default state_cmd method.
     *
     * @return Device state
     */
    Tango::DevState &get_state()
    {
        return device_state;
    }

    /**
     * Get device's previous state.
     *
     * Return the device dev_prev_state field. This method is used for the on_state_change event
     *
     * @return Device previous state
     */
    Tango::DevState &get_prev_state()
    {
        return device_prev_state;
    }

    /**
     * Set device state.
     *
     * @param new_state The new device state
     */
    void set_state(const Tango::DevState &new_state);

    /**
     * Get device name.
     *
     * Return the device name (dev_name field)
     *
     * @return Device name
     */

    std::string &get_name()
    {
        return device_name;
    }

    /**
     * Get device class singleton.
     *
     * Return the device class singleton (device_class field)
     *
     * @return Pointer to the device class singleton
     */

    DeviceClass *get_device_class()
    {
        return device_class;
    }

    /**
     * Get device multi attribute object.
     *
     * Return a pointer to the device multi attribute object
     *
     * @return Pointer to the device multi attribute object
     */

    MultiAttribute *get_device_attr()
    {
        return dev_attr;
    }

    /**
     * Set device multi attribute object.
     *
     * Set the pointer to the device multi attribute object
     *
     * @return Pointer to the device multi attribute object
     */

    void set_device_attr(MultiAttribute *ptr)
    {
        dev_attr = ptr;
    }

    /**
     * Get a pointer to the associated DbDevice object.
     *
     * Return a pointer to DbDevice object associated with the device
     *
     * @return Pointer to the DbDevice object
     */

    DbDevice *get_db_device();

    /**
     * Set the associated CORBA object reference.
     *
     * Set the associated CORBA object reference. Tango supports only a one to
     * one servant-CORBA object link.
     *
     * @param     d    The CORBA object reference
     */

    void set_d_var(Tango::Device_ptr d)
    {
        d_var = d;
    }

    /**
     * Get the associated CORBA object reference.
     *
     * Get the associated CORBA object reference. Tango supports only a one to
     * one servant-CORBA object link.
     *
     * @return The CORBA object reference
     */

    Tango::Device_var get_d_var()
    {
        return d_var;
    }

    /**
     * Set the associated CORBA object identifier.
     *
     * Set the associated CORBA object identifier.
     *
     * @param     o    The CORBA object identifier
     */

    void set_obj_id(PortableServer::ObjectId_var o)
    {
        obj_id = o;
    }

    /**
     * Get the associated CORBA object identifier.
     *
     * Return the CORBA object identifier as a _var type variable
     *
     * @return The CORBA object identifier
     */

    PortableServer::ObjectId_var &get_obj_id()
    {
        return obj_id;
    }

    /**
     * Return device POA.
     *
     * Return a pointer to the device POA. This method is necessary for the
     * CORBA object implicit activation by the _this() method.
     *
     * @return Pointer to the device POA
     */
    PortableServer::POA_ptr _default_POA() override;

    /**
     * Add version info
     *
     * Append to device info current library versions used.
     *
     */
    void add_version_info(const std::string &key, const std::string &value);

    /**
     * get version info list
     *
     * Return a DevInfoVersion list with current libraries
     * and bindings versions used.
     *
     */
    Tango::DevInfoVersionList get_version_info();

    //@}

  protected:
    /**@name Polling related methods */
    //@{
    /**
     * Check if attribute is polled.
     *
     * Returns true if attribute with name given as parameter is polled.
     *
     * @param   att_name    The attribute name
     * @return  Boolean set to true if attribute is polled
     */
    bool is_attribute_polled(const std::string &att_name);
    /**
     * Check if command is polled.
     *
     * Returns true if command with name given as parameter is polled.
     *
     * @param   cmd_name    The command name
     * @return  Boolean set to true if command is polled
     */
    bool is_command_polled(const std::string &cmd_name);
    /**
     * Get attribute polling period.
     *
     * Returns attribute polling period (in mS) or 0 if the attribute is not polled
     *
     * @param   att_name    The attribute name
     * @return The attribute polling period in mS
     */
    int get_attribute_poll_period(const std::string &att_name);
    /**
     * Get command polling period.
     *
     * Returns command polling period (in mS) or 0 if the command is not polled
     *
     * @param   cmd_name    The command name
     * @return The command polling period in mS
     */
    int get_command_poll_period(const std::string &cmd_name);
    /**
     * Start polling one attribute.
     *
     * Ask Tango polling system to poll one attribute
     *
     * @param   att_name    The attribute name
     * @param   period      The polling period (mS)
     */
    void poll_attribute(const std::string &att_name, int period);
    /**
     * Start polling a command.
     *
     * Ask Tango polling system to poll a command
     *
     * @param   cmd_name    The command name
     * @param   period      The polling period (mS)
     */
    void poll_command(const std::string &cmd_name, int period);
    /**
     * Stop polling one attribute.
     *
     * Ask Tango polling system to stop polling one attribute
     *
     * @param   att_name    The attribute name
     */
    void stop_poll_attribute(const std::string &att_name);
    /**
     * Stop polling one command.
     *
     * Ask Tango polling system to stop polling one command
     *
     * @param   cmd_name    The command name
     */
    void stop_poll_command(const std::string &cmd_name);
    //@}

  public:
    /**@name Miscellaneous methods */
    //@{
    /**
     * Intialise a device.
     *
     * In the DeviceImpl class, this method is pure abstract and must be defined
     * in sub-class. Its rule is to initialise a device. This method is called
     * during device creation by the device constructor.
     *
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */

    virtual void init_device() = 0;

    /**
     * Delete a device.
     *
     * In the DeviceImpl class, this method is virtual and can be defined
     * in sub-class. Its rule is to delete memory allocated in the init_device
     * method. This method is called by the device destructor and by the
     * device Init command.
     *
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */

    virtual void delete_device() { }

    /**
     * Hook method.
     *
     * Default method to implement an action necessary on a device before any
     * command is executed. This method can be redefined in
     * sub-classes in case of the default behaviour does not fulfill the needs
     *
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual void always_executed_hook() { }

    /**
     * Hook method.
     *
     * Default method to implement an action necessary on a device after server has been initialized.
     *
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual void server_init_hook() { }

    /**
     * Read the hardware to return attribute value(s).
     *
     * Default method to implement an action necessary on a device to read the
     * hardware involved in a a read attribute CORBA call.
     * This method must be redefined in sub-classes in order to support attribute
     * reading
     *
     * @param attr_list Reference to a vector with Integer object. Each element in
     * this vector
     * is the index in the device object attribute vector of an attribute to be read.
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual void read_attr_hardware(std::vector<long> &attr_list)
    {
        (void) attr_list;
    }

    /**
     * Set the attribute read value.
     *
     * Default method to set an attribute read value.
     * This method must be redefined in sub-classes when attributes are needed
     *
     * @param attr The attribute object
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual void read_attr(Attribute &attr)
    {
        (void) attr;
    }

    /**
     * Write the hardware for attributes.
     *
     * Default method to implement an action necessary on a device to write the
     * hardware involved in a a write attribute.
     * This method must be redefined in sub-classes in order to support writable
     * attribute
     *
     * @param attr_list Reference to a vector of Integer objects. Each element in
     * this vector
     * is the index in the main attribute vector of an attribute to be written.
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual void write_attr_hardware(std::vector<long> &attr_list)
    {
        (void) attr_list;
    }

    /** Get the device state.
     *
     * The default implementation depends on the device state.
     *
     * If the device state is ON or ALARM, it reads the attributes with an alarm
     * level defined, determines the quality factor of each of these attributes
     * by comparing the read value to the alarm levels. If any of the attributes
     * are determined to have a quality factor of ATTR_ALARM, then it sets the
     * state to ALARM, otherwise it sets the state to ON.  Finally, it returns
     * the newly determined device state.
     *
     * For all the other device states, the default implementation simply
     * returns the current state.
     *
     * This method can be overridden in sub-classes as required.
     *
     * @return The device state
     * @exception DevFailed The default implementation of this method does not
     * throw an exception and it is recommended that overrides do not either.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual Tango::DevState dev_state();

    /**
     * Get device status.
     *
     * Default method to get device status. It returns the contents of the device
     * dev_status field. If the device state is ALARM, alarm messages are
     * added to the device status. This method can be redefined in
     * sub-classes in case of the default behaviour does not fulfill the needs
     *
     * @return The device status
     * @exception DevFailed This method does not throw exception but a
     * redefined method can.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    virtual Tango::ConstDevString dev_status();

    /**
     * Add a new attribute to the device attribute list.
     *
     * Attributes are normally
     * constructed in the DeviceClass::attribute_factory() method. Nevertheless, it
     * is still possible to add a new attribute to a device with this method.
     * Please, note that if you add an attribute to a device at device creation
     * time, this attribute will
     * be added to the device class attribute list. Therefore, all devices
     * belonging to the same class created after this attribute addition
     * will also have this attribute.
     *
     * @param new_attr Pointer to the new attribute to be added to the list. This pointer
     * must point to "heap" allocated memory (or to static memory) and not to "stack"
     * allocated memory
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void add_attribute(Attr *new_attr);

    /**
     * Remove one attribute from the device attribute list.
     *
     * Attributes are normally
     * constructed in the DeviceClass::attribute_factory() method. Nevertheless, it
     * is still possible to add a new attribute to a device with the DeviceImpl::add_attribute method.
     * This remove_attribute method delete the attribute from the
     * device attribute list.
     *
     * @param rem_attr Pointer to the attribute to be removed
     * @param free_it  Boolean set to true if the object passed as first argument
     *           must be freed. Default value is false.
     * @param clean_db  Clean all attributes related information (included polling
     *         info if the attribute is polled) from database. Default value is true
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void remove_attribute(Attr *rem_attr, bool free_it = false, bool clean_db = true);

    /**
     * Remove one attribute from the device attribute list.
     *
     * Attributes are normally
     * constructed in the DeviceClass::attribute_factory() method. Nevertheless, it
     * is still possible to add a new attribute to a device with the DeviceImpl::add_attribute method.
     * This remove_attribute method delete the attribute from the
     * device attribute list.
     *
     * @param rem_attr_name The name of the attribute to be removed
     * @param free_it  Boolean set to true if the attribute object
     *           must be freed. Default value is false.
     * @param clean_db  Clean all attributes related information (included polling
     *         info if the attribute is polled) from database. Default value is true
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void remove_attribute(const std::string &rem_attr_name, bool free_it = false, bool clean_db = true);

    /**
     * Add a new command to the device command list.
     *
     * Commands are normally
     * constructed in the DeviceClass::command_factory() method. Nevertheless, it
     * is still possible to add a new command to a device with this method.
     * Please, note that if you add a command to a device at device creation
     * time, this command will
     * be added to the device class command list. Therefore, all devices
     * belonging to the same class created after this command addition
     * will also have this command.
     *
     * @param new_cmd Pointer to the new command to be added to the list. This pointer
     * must point to "heap" allocated memory (or to static memory) and not to "stack"
     * allocated memory
     * @param device Set this flag to true if the command must be added for only this device
     * Default is false (command added for the device class)
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void add_command(Command *new_cmd, bool device = false);
    /**
     * Remove one command from the device command list.
     *
     * Commands are normally
     * constructed in the DeviceClass::command_factory() method. Nevertheless, it
     * is still possible to add a new command to a device with the DeviceImpl::add_command method.
     * This remove_command method delete the command from the
     * device command list.
     *
     * @param rem_cmd Pointer to the command to be removed
     * @param free_it  Boolean set to true if the object passed as first argument
     *           must be freed. Default value is false.
     * @param clean_db  Clean command related information (included polling
     *         info if the command is polled) from database. Default value is true
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void remove_command(Command *rem_cmd, bool free_it = false, bool clean_db = true);

    /**
     * Remove one command from the device command list.
     *
     * Commands are normally
     * constructed in the DeviceClass::command_factory() method. Nevertheless, it
     * is still possible to add a new command to a device with the DeviceImpl::add_command method.
     * This remove_command method delete the command from the
     * device command list.
     *
     * @param rem_cmd_name The name of the command to be removed
     * @param free_it  Boolean set to true if the command object
     *           must be freed. Default value is false.
     * @param clean_db  Clean command related information (included polling
     *         info if the command is polled) from database. Default value is true
     * @exception DevFailed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void remove_command(const std::string &rem_cmd_name, bool free_it = false, bool clean_db = true);

    /**
     * Retrieve a polled object from the polled object list.
     *
     * Retrieve in the device polled object list, the specified polled object
     * (command or attribute).
     *
     * @param obj_type The object type (command or attribute)
     * @param obj_name The object name
     * @return An iterator pointing to the polled object in the polled object list
     * @exception DevFailed Thrown if the object is not found.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    std::vector<PollObj *>::iterator get_polled_obj_by_type_name(Tango::PollObjType obj_type,
                                                                 const std::string &obj_name);
    /**
     * Check if there is subscriber(s) listening for the event
     *
     * This method returns a boolean set to true if there are some subscriber(s) listening on the event specified
     * by the two method arguments. Be aware that there is some delay (up to 600 sec) between this method returning
     * false and the last subscriber unsubscription or crash... The device interface change event is not supported by
     * this method.
     *
     * @param att_name The attribute name
     * @param event_type The event type
     * @return A boolean set to true if there are some subscriber listening on this event
     * @exception DevFailed Thrown if the attribute is not found.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    bool is_there_subscriber(const std::string &att_name, EventType event_type);

    //@}

    /**@name Methods to build Tango array types.
     * These methods are helper methods to build Tango array types from an already
     * existing buffer (Tango array types are CORBA sequences)
     */
    //@{
    /**
     * Create a DevVarCharArray type.
     *
     * Create a DevVarCharArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarCharArray type data
     */
    Tango::DevVarCharArray *create_DevVarCharArray(unsigned char *ptr, long length)
    {
        return new Tango::DevVarCharArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarShortArray type.
     *
     * Create a DevVarShortArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarShortArray type data
     */
    Tango::DevVarShortArray *create_DevVarShortArray(short *ptr, long length)
    {
        return new Tango::DevVarShortArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarLongArray type.
     *
     * Create a DevVarLongArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in the previous buffer
     *
     * @return Pointer to the created DevVarLongArray type data
     */

    Tango::DevVarLongArray *create_DevVarLongArray(DevLong *ptr, long length)
    {
        return new Tango::DevVarLongArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarLong64Array type.
     *
     * Create a DevVarLong64Array type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in the previous buffer
     *
     * @return Pointer to the created DevVarLong64Array type data
     */

    Tango::DevVarLong64Array *create_DevVarLong64Array(DevLong64 *ptr, long length)
    {
        return new Tango::DevVarLong64Array(length, length, ptr, false);
    }

    /**
     * Create a DevVarFloatArray type.
     *
     * Create a DevVarFloatArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarFloatArray type data
     */
    Tango::DevVarFloatArray *create_DevVarFloatArray(float *ptr, long length)
    {
        return new Tango::DevVarFloatArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarDoubleArray type.
     *
     * Create a DevVarDoubleArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarDoubleArray type data
     */
    Tango::DevVarDoubleArray *create_DevVarDoubleArray(double *ptr, long length)
    {
        return new Tango::DevVarDoubleArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarUShortArray type.
     *
     * Create a DevVarUShortArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarUShortArray type data
     */
    Tango::DevVarUShortArray *create_DevVarUShortArray(unsigned short *ptr, long length)
    {
        return new Tango::DevVarUShortArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarULongArray type.
     *
     * Create a DevVarULongArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarULongArray type data
     */

    Tango::DevVarULongArray *create_DevVarULongArray(DevULong *ptr, long length)
    {
        return new Tango::DevVarULongArray(length, length, ptr, false);
    }

    /**
     * Create a DevVarULong64Array type.
     *
     * Create a DevVarULong64Array type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in the previous buffer
     *
     * @return Pointer to the created DevVarULong64Array type data
     */

    Tango::DevVarULong64Array *create_DevVarULong64Array(DevULong64 *ptr, long length)
    {
        return new Tango::DevVarULong64Array(length, length, ptr, false);
    }

    /**
     * Create a DevVarStringArray type.
     *
     * Create a DevVarStringArray type data and return a pointer to it. The array is
     * build using the input pointer with the given length
     *
     * @param ptr    Pointer to the basic type data buffer
     * @param length Number of element in  the previous buffer
     *
     * @return Pointer to the created DevVarStringArray type data
     */
    Tango::DevVarStringArray *create_DevVarStringArray(char **ptr, long length)
    {
        return new Tango::DevVarStringArray(length, length, ptr, false);
    }

    //@}

    /**@name Push change event methods.
     * These methods allow to fire change events for attributes manually,
     *  without the polling to be started.
     */

    //@{
    /**
     * Set an implemented flag for the attribute to indicate that the server fires change events manually,
     * without the polling to be started.
     * If the detect parameter is set to true, the criteria specified for the change
     * event are verified and the event is only pushed if they are fulfilled.
     * If detect is set to false the event is fired without any value checking!
     *
     * @param attr_name The name of the attribute
     * @param implemented True when the server fires change events manually.
     * @param detect Triggers the verification of the change event properties when set to true. Default value is true.
     */
    void set_change_event(const std::string &attr_name, bool implemented, bool detect = true);

    /**
     * Push a change event for a state or status attribute or return an exception as change
     * event for any attribute.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name as input.
     * For the state and status attributes the actual state and status values are pushed.
     * In case of an exception, the exception is pushed as a change event for the attribute.
     *
     * @param attr_name The name of the attribute
     * @param except Pointer to a Tango::DevFailed exception. Default value is nullptr.
     */
    void push_change_event(const std::string &attr_name, DevFailed *except = nullptr);

    /**
     * Push a change event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     * The event is triggered with or without checking of the change event criteria depending
     * on the configuration choosen with set_change_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_change_event(const std::string &attr_name, T *p_data, long x = 1, long y = 0, bool release = false);

    /**
     * Push a change event for an attribute with Tango::DevEncoded attribute data type
     * when the DevEncoded data are specified by two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     * The event is triggered with or without checking of the change event criteria depending
     * on the configuration choosen with set_change_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data string part to be pushed
     * @param p_data Pointer to the data to be pushed
     * @param size The data number (pointed to by p_data)
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_change_event(const std::string &attr_name,
                           Tango::DevString *p_str_data,
                           Tango::DevUChar *p_data,
                           long size,
                           bool release = false);

    /**
     * Push a change event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The event is triggered with or without checking of the change event criteria depending
     * on the configuration choosen with set_change_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_change_event(const std::string &attr_name,
                           T *p_data,
                           const TangoTimestamp &t,
                           Tango::AttrQuality qual,
                           long x = 1,
                           long y = 0,
                           bool release = false);
    /**
     * Push a change event for an attribute with Tango::DevEncoded attribute data type
     * when the data rea specified with two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The event is triggered with or without checking of the change event criteria depending
     * on the configuration choosen with set_change_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data string part to be pushed
     * @param p_data Pointer to the data to be pushed
     * @param size Size of the data to be ushed (pointed to be p_data
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_change_event(const std::string &attr_name,
                           Tango::DevString *p_str_data,
                           Tango::DevUChar *p_data,
                           long size,
                           const TangoTimestamp &t,
                           Tango::AttrQuality qual,
                           bool release = false);
    //@}

    /**@name Push alarm event methods.
     * These methods allow to fire alarm events for attributes manually,
     *  without the polling to be started.
     */

    //@{
    /**
     * Set an implemented flag for the attribute to indicate that the server fires alarm events manually,
     * without the polling to be started.
     * If the detect parameter is set to true, the criteria specified for the alarm
     * event are verified and the event is only pushed if they are fulfilled.
     * If detect is set to false the event is fired without any value checking!
     *
     * @param attr_name The name of the attribute
     * @param implemented True when the server fires alarm events manually.
     * @param detect Triggers the verification of the alarm event properties when set to true. Default value is true.
     */
    void set_alarm_event(const std::string &attr_name, bool implemented, bool detect = true);

    /**
     * Push a alarm event for a state or status attribute or return an exception as alarm
     * event for any attribute.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name as input.
     * For the state and status attributes the actual state and status values are pushed.
     * In case of an exception, the exception is pushed as an alarm event for the attribute.
     *
     * @param attr_name The name of the attribute
     * @param except Pointer to a Tango::DevFailed exception. Default value is nullptr.
     */
    void push_alarm_event(const std::string &attr_name, DevFailed *except = nullptr);

    /**
     * Push an alarm event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and y need to be given.
     * The time stamp of the event is set to the actual time.
     * The event is triggered with or without checking of the alarm event criteria depending
     * on the configuration choosen with set_alarm_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_alarm_event(const std::string &attr_name, T *p_data, long x = 1, long y = 0, bool release = false);

    /**
     * Push an alarm event for an attribute with Tango::DevEncoded attribute data type
     * when the DevEncoded data are specified by two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and y need to be given.
     * The time stamp of the event is set to the actual time.
     * The event is triggered with or without checking of the alarm event criteria depending
     * on the configuration choosen with set_alarm_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data string part to be pushed
     * @param p_data Pointer to the data to be pushed
     * @param size The data number (pointed to by p_data)
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_alarm_event(const std::string &attr_name,
                          Tango::DevString *p_str_data,
                          Tango::DevUChar *p_data,
                          long size,
                          bool release = false);

    /**
     * Push an alarm event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and y need to be given.
     * The event is triggered with or without checking of the alarm event criteria depending
     * on the configuration choosen with set_alarm_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_alarm_event(const std::string &attr_name,
                          T *p_data,
                          const TangoTimestamp &t,
                          Tango::AttrQuality qual,
                          long x = 1,
                          long y = 0,
                          bool release = false);
    /**
     * Push an alarm event for an attribute with Tango::DevEncoded attribute data type
     * when the data rea specified with two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and y need to be given.
     * The event is triggered with or without checking of the alarm event criteria depending
     * on the configuration choosen with set_alarm_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data string part to be pushed
     * @param p_data Pointer to the data to be pushed
     * @param size Size of the data to be pushed (pointed to be p_data
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_alarm_event(const std::string &attr_name,
                          Tango::DevString *p_str_data,
                          Tango::DevUChar *p_data,
                          long size,
                          const TangoTimestamp &t,
                          Tango::AttrQuality qual,
                          bool release = false);
    //@}

    /**@name Push archive event methods.
     * These methods allow to fire archive events for attributes manually,
     *  without the polling to be started.
     */

    //@{
    /**
     * Set an implemented flag for the attribute to indicate that the server fires archive events manually,
     * without the polling to be started.
     * If the detect parameter is set to true, the criteria specified for the archive
     * event are verified and the event is only pushed if they are fulfilled.
     * If detect is set to false the event is fired without any value checking!
     *
     * @param attr_name The name of the attribute
     * @param implemented True when the server fires archive events manually.
     * @param detect Triggers the verification of the archive event properties when set to true. Default value is true.
     */
    void set_archive_event(const std::string &attr_name, bool implemented, bool detect = true);

    /**
     * Push an archive event for state or status attribute or push an exception as archive
     * event for any attribute.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name as input.
     * For the state and status attributes the actual state and status values are pushed.
     * In case of an exception, the exception is pushed as an archive event for the attribute.
     *
     * @param attr_name The name of the attribute
     * @param except Pointer to a Tango::DevFailed exception. Default value is nullptr.
     */
    void push_archive_event(const std::string &attr_name, DevFailed *except = nullptr);

    /**
     * Push an archive event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     * The event is triggered with or without checking of the archive event criteria depending
     * on the configuration choosen with set_archive_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_archive_event(const std::string &attr_name, T *p_data, long x = 1, long y = 0, bool release = false);

    /**
     * Push an archive event for an attribute with Tango::DevEncoded attribute data type
     * when the data are specified using two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     * The event is triggered with or without checking of the archive event criteria depending
     * on the configuration choosen with set_archive_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data string part to be pushed
     * @param p_data Pointer to the data part to be pushed
     * @param size Size of the data to be pushed (Pointed to by p_data)
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_archive_event(const std::string &attr_name,
                            Tango::DevString *p_str_data,
                            Tango::DevUChar *p_data,
                            long size,
                            bool release = false);

    /**
     * Push an archive event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The event is triggered with or without checking of the archive event criteria depending
     * on the configuration choosen with set_archive_event().
     *
     * @param attr_name The name of the attribute
     * @param p_data Pointer to the data to be pushed
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_archive_event(const std::string &attr_name,
                            T *p_data,
                            const TangoTimestamp &t,
                            Tango::AttrQuality qual,
                            long x = 1,
                            long y = 0,
                            bool release = false);
    /**
     * Push an archive event for an attribute with Tango::DevEncoded attribute data type
     * when it is specified using two pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The event is triggered with or without checking of the archive event criteria depending
     * on the configuration choosen with set_archive_event().
     *
     * @param attr_name The name of the attribute
     * @param p_str_data Pointer to the data std::string part to be pushed
     * @param p_data Pointer to the data to be pushed
     * @param size Size of the data to be pushed (Pointed to by p_data)
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_archive_event(const std::string &attr_name,
                            Tango::DevString *p_str_data,
                            Tango::DevUChar *p_data,
                            long size,
                            const TangoTimestamp &t,
                            Tango::AttrQuality qual,
                            bool release = false);
    //@}

    /**@name Push user event methods.
     * These methods allow to fire user events for attributes manually,
     *  without the polling to be started.
     */

    //@{
    /**
     * Push a user event for a state or status attribute or return an exception as user
     * event for any attribute.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name as input.
     * For the state and status attributes the actual state and status values are pushed.
     * In case of an exception, the exception is pushed as a user event for the attribute.
     *
     * @param attr_name The name of the attribute
     * @param filt_names The filterable fields name
     * @param filt_vals The filterable fields value (as double)
     * @param except Pointer to a Tango::DevFailed exception. Default value is nullptr.
     */
    void push_event(const std::string &attr_name,
                    const std::vector<std::string> &filt_names,
                    const std::vector<double> &filt_vals,
                    DevFailed *except = nullptr);

    /**
     * Push a user event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     *
     * @param attr_name The name of the attribute
     * @param filt_names The filterable fields name
     * @param filt_vals The filterable fields value (as double)
     * @param p_data Pointer to the data to be pushed
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_event(const std::string &attr_name,
                    const std::vector<std::string> &filt_names,
                    const std::vector<double> &filt_vals,
                    T *p_data,
                    long x = 1,
                    long y = 0,
                    bool release = false);

    /**
     * Push a user event for an attribute with Tango::DevEncoded attribute data type
     * when the attribute data are specified with 2 pointers.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name and a pointer to the data to be pushed as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     * The time stamp of the event is set to the actual time and the attribute quality
     * is set to valid.
     *
     * @param attr_name The name of the attribute
     * @param filt_names The filterable fields name
     * @param filt_vals The filterable fields value (as double)
     * @param p_str_data Pointer to the string sent with the data
     * @param p_data Pointer to the data to be pushed
     * @param size The data number (pointed to by p_data)
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_event(const std::string &attr_name,
                    const std::vector<std::string> &filt_names,
                    const std::vector<double> &filt_vals,
                    Tango::DevString *p_str_data,
                    Tango::DevUChar *p_data,
                    long size,
                    bool release = false);

    /**
     * Push a user event for an attribute with Tango::DevShort attribute data type.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     *
     * @param attr_name The name of the attribute
     * @param filt_names The filterable fields name
     * @param filt_vals The filterable fields value (as double)
     * @param p_data Pointer to the data to be pushed
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param x The attribute x length. Default value is 1
     * @param y The attribute y length. Default value is 0
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    template <class T>
    void push_event(const std::string &attr_name,
                    const std::vector<std::string> &filt_names,
                    const std::vector<double> &filt_vals,
                    T *p_data,
                    const TangoTimestamp &t,
                    Tango::AttrQuality qual,
                    long x = 1,
                    long y = 0,
                    bool release = false);

    /**
     * Push a user event for an attribute with Tango::DevEncoded attribute data type
     * when the string part and the data part of the DevEncoded data are specified
     * separately.
     * The event is pushed to the event system.
     *
     * The method needs the attribue name, a pointer to the data to be pushed, the time stamp
     * for the data and the attribute quality factor as input.
     * Depending on the attribute type the dimensions x and why need to be given.
     *
     * @param attr_name The name of the attribute
     * @param filt_names The filterable fields name
     * @param filt_vals The filterable fields value (as double)
     * @param p_str_data Pointer to the data string part
     * @param p_data Pointer to the data to be pushed
     * @param size The data number (pointed to by p_data)
     * @param t The time stamp
     * @param qual The attribute quality factor
     * @param release The release flag. If true, memory pointed to by p_data will be
     *           freed after being send to the client. Default value is false.
     * @exception DevFailed If the attribute data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_event(const std::string &attr_name,
                    const std::vector<std::string> &filt_names,
                    const std::vector<double> &filt_vals,
                    Tango::DevString *p_str_data,
                    Tango::DevUChar *p_data,
                    long size,
                    const TangoTimestamp &t,
                    Tango::AttrQuality qual,
                    bool release = false);
    //@}

    /**@name Push data ready event methods
     * This method allows the user to push a data ready event */
    //@{
    /**
     * Set an implemented flag for the attribute to indicate that the server fires data ready event
     * for this attribute.
     *
     * @param attr_name The name of the attribute
     * @param implemented True when the server fires data ready event.
     */
    void set_data_ready_event(const std::string &attr_name, bool implemented);
    /**
     * Push a data ready event for the attribute with name specified as the first
     * parameter.
     * The event is pushed to the event system.
     *
     * The method needs only the attribue name and an optional "counter" which
     * will be passed unchanged within the event
     *
     * @param attr_name The name of the attribute
     * @param ctr The user counter
     *
     * @exception DevFailed If the attribute name is unknown.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_data_ready_event(const std::string &attr_name, Tango::DevLong ctr = 0);
    //@}

    /**@name Push pipe event methods.
     * These methods allow to fire pipe events
     */

    //@{
    /**
     * Push a pipe event with exception data as value
     * The method needs the pipe name as input.
     *
     * @param pipe_name The name of the pipe
     * @param except Pointer to a Tango::DevFailed exception.
     */
    void push_pipe_event(const std::string &pipe_name, DevFailed *except);
    /**
     * Push a pipe event.
     *
     * The method needs the pipe name and a pointer to the pipe blob to be pushed as input.
     * The time stamp of the event is set to the actual time.
     *
     * @param pipe_name The name of the pipe
     * @param p_data Pointer to the Pipe blob to be sent with the event
     * @param reuse_it Flag set to true if you don want the push_pipe_event to consume Pipe internal memory. Default
     * value is false which covers 95% of use cases
     * @exception DevFailed If the pipe data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_pipe_event(const std::string &pipe_name, Tango::DevicePipeBlob *p_data, bool reuse_it = false);
    /**
     * Push a pipe event with a specified timestamp.
     *
     * The method needs the pipe name, a pointer to the pipe blob to be pushed and the time stamp
     * for the data as input.
     *
     * @param pipe_name The name of the pipe
     * @param p_data Pointer to the data to be pushed
     * @param t The time stamp
     * @param reuse_it Flag set to true if you don want the push_pipe_event to consume Pipe internal memory. Default
     * value is false which covers 95% of use cases
     * @exception DevFailed If the pipe data type is not coherent.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void push_pipe_event(const std::string &pipe_name,
                         Tango::DevicePipeBlob *p_data,
                         const TangoTimestamp &t,
                         bool reuse_it = false);
//@}

/**@name Signal related methods
 * These methods allow a signal management at device level */
//@{
#ifndef _TG_WINDOWS_
    /**
     * Register a signal to be executed in a signal handler.
     *
     * Register this device as device to be informed when signal signo is sent to
     * to the device server process. This method is available only under Linux.
     *
     * @param signo The signal number
     * @param own_handler A boolean set to true if you want the device signal handler
     * to be executed in its own handler instead of being executed by the signal
     * thread. If this parameter is set to true, care should be taken on how the
     * handler is written. A default false value is provided
     * @exception DevFailed Thrown if the signal number is out of range or if the
     * operating system failed to register a signal for the process.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void register_signal(long signo, bool own_handler = false);
#else
    /**
     * Register a signal.
     *
     * Register this device as device to be informed when signal signo is sent to
     * to the device server process
     *
     * @param signo The signal number
     * @exception DevFailed Thrown if the signal number is out of range or if the
     * operating system failed to register a signal for the process.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void register_signal(long signo);
#endif

    /**
     * Unregister a signal.
     *
     * Unregister this device as device to be informed when signal signo is sent to
     * to the device server process
     *
     * @param signo The signal number
     * @exception DevFailed Thrown if the signal number is out of range or if the
     * operating system failed to unregister a signal for the process. Unregister
     * a device for a signal number for a device not previously registered is not
     * an error. This simply will do nothing.
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void unregister_signal(long signo);

    /**
     * Signal handler.
     *
     * The method executed when the signal arrived in the device server process.
     * This method is defined as virtual and then, can be redefined following
     * device needs.
     *
     * @param signo The signal number
     */
    virtual void signal_handler(long signo);

    //@}

    //
    // Device class data members
    //

  protected:
    /**@name Class data members */
    //@{
    /**
     * The device black box pointer
     */
    std::unique_ptr<BlackBox> blackbox_ptr;
    /**
     * The device black box depth
     */
    long blackbox_depth;
    /**
     * The device name
     */
    std::string device_name;
    /**
     * The device description
     */
    std::string desc;
    /**
     * The device status
     */
    std::string device_status;
    /**
     * The device state
     */
    Tango::DevState device_state;
    /**
     * The device version
     */
    long version;
    /**
     * Pointer to the device-class object associated with the device
     */
    DeviceClass *device_class;
    /**
     * Pointer to the multi attribute object
     */
    MultiAttribute *dev_attr;
    /**
     * Pointer to the associated DbDevice object
     */
    DbDevice *db_dev;
    /**
     * The administration device name
     */
    std::string adm_device_name;
    /**
     * The version_info list to store library versions
     */
    std::map<std::string, std::string> version_info;
    //@}

  public:
    Tango::client_addr *get_client_ident();

    /// @privatesection

    typedef struct _FwdWrongConf
    {
        std::string att_name;
        std::string full_root_att_name;
        FwdAttError fae;
    } FwdWrongConf;

    char *name() override;
    char *adm_name() override;
    char *description() override;
    char *status() override;
    Tango::DevState state() override;

    CORBA::Any *command_inout(const char *in_cmd, const CORBA::Any &in_data) override;
    Tango::DevVarStringArray *black_box(CORBA::Long n) override;
    Tango::DevCmdInfoList *command_list_query() override;
    Tango::DevCmdInfo *command_query(const char *command) override;
    Tango::DevInfo *info() override;
    void ping() override;
    Tango::AttributeConfigList *get_attribute_config(const Tango::DevVarStringArray &names) override;
    void set_attribute_config(const Tango::AttributeConfigList &new_conf) override;
    Tango::AttributeValueList *read_attributes(const Tango::DevVarStringArray &names) override;
    void write_attributes(const Tango::AttributeValueList &values) override;

    void set_exported_flag(bool exp)
    {
        exported = exp;
    }

    bool get_exported_flag()
    {
        return exported;
    }

    void set_poll_ring_depth(long depth)
    {
        poll_ring_depth = depth;
    }

    long get_poll_ring_depth()
    {
        return poll_ring_depth;
    }

    void set_poll_old_factor(long fact)
    {
        poll_old_factor = fact;
    }

    long get_poll_old_factor()
    {
        return poll_old_factor;
    }

    void is_polled(bool poll)
    {
        polled = poll;
    }

    bool is_polled()
    {
        return polled;
    }

    std::vector<std::string> &get_polled_cmd()
    {
        return polled_cmd;
    }

    std::vector<std::string> &get_polled_attr()
    {
        return polled_attr;
    }

    std::vector<std::string> &get_non_auto_polled_cmd()
    {
        return non_auto_polled_cmd;
    }

    std::vector<std::string> &get_non_auto_polled_attr()
    {
        return non_auto_polled_attr;
    }

    std::vector<PollObj *> &get_poll_obj_list()
    {
        return poll_obj_list;
    }

    void stop_polling(bool);

    void stop_polling()
    {
        stop_polling(true);
    }

    void check_command_exists(const std::string &);
    Command *get_command(const std::string &);

    std::string &get_name_lower()
    {
        return device_name_lower;
    }

    TangoMonitor &get_dev_monitor()
    {
        return only_one;
    }

    TangoMonitor &get_poll_monitor()
    {
        return poll_mon;
    }

    TangoMonitor &get_att_conf_monitor()
    {
        return att_conf_mon;
    }

    TangoMonitor &get_pipe_conf_monitor()
    {
        return pipe_conf_mon;
    }

    long get_dev_idl_version()
    {
        return idl_version;
    }

    long get_cmd_poll_ring_depth(const std::string &);
    long get_attr_poll_ring_depth(const std::string &);

    std::vector<long> &get_alarmed_not_read()
    {
        return alrmd_not_read;
    }

    void poll_lists_2_v5();

    void lock(client_addr *, int);
    void relock(client_addr *);
    Tango::DevLong unlock(bool);
    void basic_unlock(bool forced = false);
    bool valid_lock();
    Tango::DevVarLongStringArray *lock_status();

    bool is_device_locked()
    {
        return device_locked;
    }

    client_addr *get_locker()
    {
        return locker_client;
    }

    client_addr *get_old_locker()
    {
        return old_locker_client;
    }

    time_t get_locking_date()
    {
        return locking_date;
    }

    Tango::DevLong get_locking_ctr()
    {
        return lock_ctr;
    }

    Tango::DevLong get_lock_validity()
    {
        return lock_validity;
    }

    void clean_locker_ptrs()
    {
        locker_client = nullptr;
        old_locker_client = nullptr;
    }

    void set_locking_param(client_addr *, client_addr *, time_t, DevLong, DevLong);

    void set_alias_name_lower(const std::string &al)
    {
        alias_name_lower = al;
    }

    std::string &get_alias_name_lower()
    {
        return alias_name_lower;
    }

    void push_att_conf_event(Attribute *);

    void data_into_net_object(Attribute &, AttributeIdlData &, long, AttrWriteType, bool);
    void polled_data_into_net_object(AttributeIdlData &, long, long, long, PollObj *, const DevVarStringArray &);

    int get_min_poll_period()
    {
        return min_poll_period;
    }

    std::vector<std::string> &get_cmd_min_poll_period()
    {
        return cmd_min_poll_period;
    }

    std::vector<std::string> &get_attr_min_poll_period()
    {
        return attr_min_poll_period;
    }

    void init_cmd_poll_ext_trig(const std::string &cmd_name);
    void init_attr_poll_ext_trig(const std::string &cmd_name);

    void set_run_att_conf_loop(bool val)
    {
        run_att_conf_loop = val;
    }

    std::vector<std::string> &get_att_wrong_db_conf()
    {
        return att_wrong_db_conf;
    }

    void check_att_conf();

    bool is_alarm_state_forced()
    {
        return force_alarm_state;
    }

    std::vector<std::string> &get_att_mem_failed()
    {
        return att_mem_failed;
    }

    std::vector<FwdWrongConf> &get_fwd_att_wrong_conf()
    {
        return fwd_att_wrong_conf;
    }

    void rem_wrong_fwd_att(const std::string &);
    void update_wrong_conf_att(const std::string &, FwdAttError);

    void set_with_fwd_att(bool _b)
    {
        with_fwd_att = _b;
    }

    bool get_with_fwd_att()
    {
        return with_fwd_att;
    }

    void set_call_source(DevSource _s)
    {
        call_source = _s;
    }

    DevSource get_call_source()
    {
        return call_source;
    }

    std::vector<Command *> &get_local_command_list()
    {
        return command_list;
    }

    Command &get_local_cmd_by_name(const std::string &);
    void remove_local_command(const std::string &);

    void set_event_intr_change_subscription(SubscriptionClock::time_point _t)
    {
        event_intr_change_subscription = _t;
    }

    SubscriptionClock::time_point get_event_intr_change_subscription() const
    {
        return event_intr_change_subscription;
    }

    void enable_intr_change_ev()
    {
        intr_change_ev = true;
    }

    void disable_intr_change_ev()
    {
        intr_change_ev = false;
    }

    bool is_intr_change_ev_enable()
    {
        return intr_change_ev;
    }

    DeviceEventSubscriptionState get_event_subscription_state();
    void set_event_subscription_state(const DeviceEventSubscriptionState &);

    log4tango::Logger *get_logger()
    {
        return logger != nullptr ? logger : get_logger_i();
    }

    void init_logger();
    void start_logging();
    void stop_logging();

#ifdef TANGO_USE_TELEMETRY
    // initialize the telemetry interface.
    // throw an ewception is the telemetry endpoint is invalid
    virtual void initialize_telemetry_interface();

    // cleanup the telemetry interface.
    virtual void cleanup_telemetry_interface() noexcept;

    // get access to the telemetry interface.
    Tango::telemetry::InterfacePtr &telemetry()
    {
        if(!telemetry_interface)
        {
            std::stringstream msg;
            msg << "the telemetry interface is not properly initialized for device '" << get_name_lower() << "'"
                << std::ends;
            TANGO_THROW_EXCEPTION(API_ClassNotFound, msg.str());
        }
        return telemetry_interface;
    }

    // enable the telemetry interface
    virtual void enable_telemetry() noexcept;

    // disable the telemetry interface
    virtual void disable_telemetry() noexcept;

    virtual bool is_telemetry_enabled() noexcept;

    // set/get telemetry topics
    virtual void set_telemetry_topics(const std::vector<std::string> &topics);
    virtual std::vector<std::string> get_telemetry_topics() noexcept;

    // Telemetry tracing
    virtual void set_telemetry_tracing(bool enable_flag) noexcept;
    virtual bool is_telemetry_tracing_enabled() noexcept;
    virtual void set_telemetry_tracing_endpoints(std::vector<telemetry::Configuration::Collector> collectors);
    virtual std::vector<telemetry::Configuration::Collector> get_telemetry_tracing_endpoints();
    virtual void add_telemetry_tracing_endpoint(telemetry::Configuration::Collector collector);
    virtual void remove_telemetry_tracing_endpoint(telemetry::Configuration::Collector collector);

    // Telemetry doesn't have (shared) ownership of the logger, so we jump through some hoops
    virtual void update_telemetry_logging();
    virtual void set_telemetry_logging(bool enable_flag);
    virtual bool is_telemetry_logging_enabled() noexcept;
    virtual void set_telemetry_logging_endpoints(std::vector<telemetry::Configuration::Collector> collectors);
    virtual std::vector<telemetry::Configuration::Collector> get_telemetry_logging_endpoints();
    virtual void add_telemetry_logging_endpoint(telemetry::Configuration::Collector collector);
    virtual void remove_telemetry_logging_endpoint(telemetry::Configuration::Collector collector);
#endif

  private:
    PipeEventSubscriptionStates get_pipe_event_subscription_states();
    void set_pipe_event_subscription_states(const PipeEventSubscriptionStates &);

    //
    // The extension class
    //

    class DeviceImplExt
    {
      public:
        DeviceImplExt() { }

        time_t alarm_state_user{0};
        time_t alarm_state_kernel{0};
    };

  protected:
    /// @privatesection
    void check_lock(const char *, const char *cmd = nullptr);
    void throw_locked_exception(const char *meth);

    void init_cmd_poll_period();
    void init_attr_poll_period();
    void init_poll_no_db();

    std::unique_ptr<DeviceImplExt> ext; // Class extension

    DevVarEncodedArray dummy_encoded_att_value;

    //
    // Ported from the extension class
    //

    log4tango::Logger *logger{nullptr};
    log4tango::Level::Value saved_log_level{log4tango::Level::WARN};
    size_t rft;

    long poll_old_factor{0};
    long idl_version{1};

    bool exported{false};
    bool polled{false};
    long poll_ring_depth{0};
    std::vector<std::string> polled_cmd;
    std::vector<std::string> polled_attr;
    std::vector<std::string> non_auto_polled_cmd;
    std::vector<std::string> non_auto_polled_attr;
    std::vector<PollObj *> poll_obj_list;

    TangoMonitor only_one;             // Device monitor
    Tango::DevState device_prev_state; // Device previous state
    std::string device_name_lower;

    std::vector<std::string> cmd_poll_ring_depth;
    std::vector<std::string> attr_poll_ring_depth;

    bool store_in_bb{true};
    TangoMonitor poll_mon;      // Polling list monitor
    TangoMonitor att_conf_mon;  // Attribute config monitor
    TangoMonitor pipe_conf_mon; // Pipe config monitor
    bool state_from_read{false};
    std::vector<long> alrmd_not_read;

    bool py_device;
    std::string alias_name_lower; // Alias name (if any)

    bool device_locked{false};
    client_addr *locker_client{nullptr};
    client_addr *old_locker_client{nullptr};
    DevLong lock_validity;
    time_t locking_date;
    std::string lock_stat;
    DevLong lock_ctr{0};

    long min_poll_period{0};
    std::vector<std::string> cmd_min_poll_period;
    std::vector<std::string> attr_min_poll_period;

    bool run_att_conf_loop{true};
    bool force_alarm_state{false};
    std::vector<std::string> att_wrong_db_conf;
    std::vector<std::string> att_mem_failed;
    std::vector<FwdWrongConf> fwd_att_wrong_conf;
    bool with_fwd_att{false};
    DevSource call_source;

    std::vector<Command *> command_list;
    SubscriptionClock::time_point event_intr_change_subscription;
    bool intr_change_ev{false};

    TangoMonitor devintr_mon;
    ShDevIntrTh devintr_shared;
    DevIntrThread *devintr_thread{nullptr};

  private:
    //
    // Private enum
    //

    typedef enum _AttErrorType
    {
        CONF = 0,
        MEM,
        FWD
    } AttErrorType;

    typedef enum _PipePropType
    {
        LABEL = 0,
        DESCRIPTION
    } PipePropType;

    //
    // Some private methods and variables
    //

    void get_dev_system_resource();
    void black_box_create();
    void real_ctor();
    void poll_object(const std::string &, int, PollObjType);
    void stop_poll_object(const std::string &, PollObjType);
    void att_conf_loop();
    void build_att_list_in_status_mess(size_t, AttErrorType);
    void lock_root_devices(int, bool);
    void push_dev_intr(bool);
    void end_pipe_config();
    void set_pipe_prop(std::vector<PipeProperty> &, Pipe *, PipePropType);

    log4tango::Logger *get_logger_i();

    std::string alarm_status;
    Tango::Device_var d_var;
    PortableServer::ObjectId_var obj_id;

    // Push event and call the proper firing method in attribute
    // allow for factorization for any type of event
    template <class T, void (Attribute::*fire)(DevFailed *)>
    void push_event(const std::string &attr_name, T *p_data, long x, long y, bool release);
    template <class T, void (Attribute::*fire)(DevFailed *)>
    void push_event(const std::string &attr_name,
                    T *p_data,
                    const TangoTimestamp &t,
                    Tango::AttrQuality qual,
                    long x,
                    long y,
                    bool release);

#ifdef TANGO_USE_TELEMETRY
    Tango::telemetry::InterfacePtr telemetry_interface;
#endif
};

inline void DeviceImpl::set_state(const Tango::DevState &new_state)
{
    device_prev_state = device_state;
    device_state = new_state;
    if(new_state == Tango::ALARM)
    {
        ext->alarm_state_user = Tango::get_current_system_datetime();
    }
    else
    {
        ext->alarm_state_user = 0;
    }
}

template <class T, void (Attribute::*fire)(DevFailed *)>
void DeviceImpl::push_event(const std::string &attr_name,
                            T *p_data,
                            const TangoTimestamp &t,
                            Tango::AttrQuality qual,
                            long x,
                            long y,
                            bool release)
{
    // get the tango synchroisation monitor
    Tango::AutoTangoMonitor synch(this);

    // search the attribute from the attribute list
    Tango::MultiAttribute *attr_list = get_device_attr();
    Tango::Attribute &attr = attr_list->get_attr_by_name(attr_name.c_str());

    // set the attribute value
    attr.set_value_date_quality(p_data, t, qual, x, y, release);
    // push the event
    (attr.*fire)(nullptr);
}

template <class T, void (Attribute::*fire)(DevFailed *)>
inline void DeviceImpl::push_event(const std::string &attr_name, T *p_data, long x, long y, bool release)
{
    // get the tango synchronization monitor
    Tango::AutoTangoMonitor synch(this);

    // search the attribute from the attribute list
    Tango::MultiAttribute *attr_list = get_device_attr();
    Tango::Attribute &attr = attr_list->get_attr_by_name(attr_name.c_str());

    // set the attribute value
    attr.set_value(p_data, x, y, release);
    // push the event
    (attr.*fire)(nullptr);
}

template <class T>
void DeviceImpl::push_change_event(const std::string &attr_name,
                                   T *p_data,
                                   const TangoTimestamp &t,
                                   Tango::AttrQuality qual,
                                   long x,
                                   long y,
                                   bool release)
{
    push_event<T, &Attribute::fire_change_event>(attr_name, p_data, t, qual, x, y, release);
}

template <class T>
void DeviceImpl::push_alarm_event(const std::string &attr_name,
                                  T *p_data,
                                  const TangoTimestamp &t,
                                  Tango::AttrQuality qual,
                                  long x,
                                  long y,
                                  bool release)
{
    push_event<T, &Attribute::fire_alarm_event>(attr_name, p_data, t, qual, x, y, release);
}

template <class T>
void DeviceImpl::push_archive_event(const std::string &attr_name,
                                    T *p_data,
                                    const TangoTimestamp &t,
                                    Tango::AttrQuality qual,
                                    long x,
                                    long y,
                                    bool release)
{
    push_event<T, &Attribute::fire_archive_event>(attr_name, p_data, t, qual, x, y, release);
}

template <class T>
inline void DeviceImpl::push_change_event(const std::string &attr_name, T *p_data, long x, long y, bool release)
{
    push_event<T, &Attribute::fire_change_event>(attr_name, p_data, x, y, release);
}

template <class T>
inline void DeviceImpl::push_alarm_event(const std::string &attr_name, T *p_data, long x, long y, bool release)
{
    push_event<T, &Attribute::fire_alarm_event>(attr_name, p_data, x, y, release);
}

template <class T>
inline void DeviceImpl::push_archive_event(const std::string &attr_name, T *p_data, long x, long y, bool release)
{
    push_event<T, &Attribute::fire_archive_event>(attr_name, p_data, x, y, release);
}

template <class T>
inline void DeviceImpl::push_event(const std::string &attr_name,
                                   const std::vector<std::string> &filt_names,
                                   const std::vector<double> &filt_vals,
                                   T *p_data,
                                   long x,
                                   long y,
                                   bool release)
{
    // get the tango synchroisation monitor
    Tango::AutoTangoMonitor synch(this);

    // search the attribute from the attribute list
    Tango::MultiAttribute *attr_list = get_device_attr();
    Tango::Attribute &attr = attr_list->get_attr_by_name(attr_name.c_str());

    // set the attribute value
    attr.set_value(p_data, x, y, release);
    // push the event
    attr.fire_event(filt_names, filt_vals);
}

template <class T>
void DeviceImpl::push_event(const std::string &attr_name,
                            const std::vector<std::string> &filt_names,
                            const std::vector<double> &filt_vals,
                            T *p_data,
                            const TangoTimestamp &t,
                            Tango::AttrQuality qual,
                            long x,
                            long y,
                            bool release)
{
    // get the tango synchroisation monitor
    Tango::AutoTangoMonitor synch(this);

    // search the attribute from the attribute list
    Tango::MultiAttribute *attr_list = get_device_attr();
    Tango::Attribute &attr = attr_list->get_attr_by_name(attr_name.c_str());

    // set the attribute value
    attr.set_value_date_quality(p_data, t, qual, x, y, release);
    // push the event
    attr.fire_event(filt_names, filt_vals);
}

} // namespace Tango

#endif // _DEVICE_H
