Generate events in a device server

Generate events in a device server#

audience:developers lang:c++ python

The device server is the origin of all events. It will fire events as soon as they occur or are created. For a detailed explanation of the different types of events in Tango please see the events section.

Standard events (change, periodic, alarm, data ready and archive) are detected automatically in the polling thread and fired immediately (if enabled). The periodic events can only be fired by the polling thread. The alarm, change, data ready and archive events can also be manually pushed from the device server. To allow a client to subscribe to events of attributes that are not polled, the server has to declare that events are pushed from the code. Four methods are available for this purpose:

1  Attr::set_change_event(bool implemented, bool detect = true);
2  Attr::set_archive_event(bool implemented, bool detect = true);
3  Attr::set_alarm_event(bool implemented, bool detect = true);
4  Attr::set_data_ready_event(bool implemented);

For example:

1  // Create a new read-only, short attribute
2  auto *at = new Tango::Attr("some_attribute", Tango::DEV_SHORT, Tango::READ);
3
4  // Indicate that the following events will be push manually
5  // for this attribute
6  at->set_change_event(true)
7  at->set_archive_event(true)
8  at->set_alarm_event(true)
9  at->set_data_ready_event(true)
1  Attribute.set_change_event(bool implemented, bool detect = True)
2  Attribute.set_archive_event(bool implemented, bool detect = True)
3  Attribute.set_alarm_event(bool implemented, bool detect = True)
4  Attribute.set_data_ready_event(bool implemented)

For example:

 1  # Create a new read-only, short attribute
 2  at = attribute(
 3    dtype=int, access=AttrWriteType.READ
 4  )
 5
 6  # Indicate that the following events will be push manually
 7  # for this attribute
 8  at.set_change_event(True)
 9  at.set_archive_event(True)
10  at.set_alarm_event(True)
11  at.set_data_ready_event(True)

where implemented=true indicates that events are pushed manually from the code and detect=true triggers verification of the event using the same event properties defined for the polling thread. Note that when detect=false, no value checking is done on the pushed value.

The class DeviceImpl also supports the first two methods with an additional parameter attr_name defining the attribute name:

1  DeviceImpl::set_change_event(std::string attr_name, bool implemented, bool detect = true);
2  DeviceImpl::set_archive_event(std::string attr_name, bool implemented, bool detect = true);
3  DeviceImpl::set_alarm_event(std::string attr_name, bool implemented, bool detect = true);
4  DeviceImpl::set_data_ready_event(std::string attr_name, bool implemented);

For example:

 1  // Constructor
 2  TestDevice::TestDevice(Tango::DeviceClass *cl, std::string &s)
 3    : TANGO_BASE_CLASS(cl, s.c_str())
 4  {
 5    init_device()
 6  }
 7
 8  // ...
 9
10  void TestDevice::init_device()
11  {
12    // Indicate that the following events will be push manually for the attribute
13    // named "some_attribute"
14    set_change_event("some_attribute", true);
15    set_archive_event("some_attribute", true);
16    set_alarm_event("some_attribute", true);
17    set_data_ready_event("some_attribute", true);
18  }
1  DeviceImpl.set_change_event(str attr_name, bool implemented, bool detect = True)
2  DeviceImpl.set_archive_event(str attr_name, bool implemented, bool detect = True)
3  DeviceImpl.set_alarm_event(str attr_name, bool implemented, bool detect = True)
4  DeviceImpl.set_data_ready_event(str attr_name, bool implemented)

For example:

 1  class TestDevice(Device):
 2
 3    def init_device(self):
 4
 5      super().init_device()
 6
 7      # Indicate that the following events will be pushed manually for the attribute
 8      # named "some_attribute"
 9      self.set_change_event("some_attribute", True)
10      self.set_archive_event("some_attribute", True)
11      self.set_alarm_event("some_attribute", True)
12      self.set_data_ready_event("some_attribute", True)

To push events manually from the code a set of data type dependent methods can be used:

1  DeviceImpl::push_change_event(std::string attr_name, ...);
2  DeviceImpl::push_archive_event(std::string attr_name, ...);
3  DeviceImpl::push_alarm_event(std::string attr_name, ...);
4  // ctr = Optional "counter"
5  DeviceImpl::push_data_ready_event(std::string attr_name, Tango::DevLong ctr = 0);

where the ctr is an optional counter, which will be passed within the event.

For example:

 1  void sendEventTest
 2  {
 3    Tango::DevDouble v{10};
 4    // Push an alarm event for the attribute "some_attribute"
 5    push_alarm_event("some_attribute", &v);
 6    // Push a change event for the attribute "some_attribute"
 7    push_change_event("some_attribute", &v);
 8    // Push an archive event for the attribute "some_attribute"
 9    push_archive_event("some_attribute", &v);
10    // Push a 'data ready' event for the attribute "some_attribute"
11    push_data_ready_event("some_attribute");
12  }
1  DeviceImpl.push_change_event(str attr_name, ...)
2  DeviceImpl.push_archive_event(str attr_name, ...)
3  DeviceImpl.push_alarm_event(str attr_name, ...)
4  DeviceImpl.push_data_ready_event(str attr_name, int ctr = 0)

where the ctr is an optional counter, which will be passed within the event.

For example:

1def sendEventTest(self):
2  # Push an alarm event for the attribute "some_attribute"
3  self.push_alarm_event("some_attribute", 10)
4  # Push a change event for the attribute "some_attribute"
5  self.push_change_event("some_attribute", 10)
6  # Push an archive event for the attribute "some_attribute"
7  self.push_archive_event("some_attribute", 10)
8  # Push a 'data ready' event for the attribute "some_attribute"
9  self.push_data_ready_event("some_attribute", 10)

See the appropriate API for all available interfaces.

Warning

CORBA events using notfid are deprecated and about to be removed, see this cppTango issue.

For non-standard events a single call exists for pushing the data to the CORBA Notification Service (omniNotify). Clients who are subscribed to this event have to know what data type is in the DeviceAttribute and unpack it accordingly.

To push non-standard events, the following api call is available to all device servers:

1  DeviceImpl::push_event(std::string attr_name,
2               std::vector<std::string> &filterable_names,
3               std::vector<double> &filterable_vals,
4               ...);
1  DeviceImpl.push_event(str attr_name,
2               Sequence[str] filterable_names,
3               Sequence[double] filterable_vals,
4               ...)

where attr_name is the name of the attribute and filterable_names and filterable_vals represent any data which can be used by clients to filter on.

Here is a typical example of what a server will need to do to send its own events. This attribute is readable and an event is sent if its value is positive when it is read. On top of that, this event is sent with one filterable field called value which is set to the attribute value.

 1  void MyClass::read_Sinusoide(Tango::Attribute &attr)
 2  {
 3    // ...
 4       struct timeval tv{};
 5       gettimeofday(&tv, nullptr);
 6       sinusoide = 100 * sin( 2 * 3.14 * frequency * tv.tv_sec);
 7
 8       if(sinusoide >= 0)
 9       {
10          std::vector<std::string> filterable_names;
11          std::vector<double> filterable_value;
12
13          filterable_names.push_back("value");
14          filterable_value.push_back((double)sinusoide);
15
16          push_event(attr.get_name(),filterable_names, filterable_value);
17       }
18    // ...
19 }

line 13-14 : The filter pair name/value is initialised

line 16 : The event is pushed

 1  def read_Sinusoide(self, attr):
 2    # ...
 3
 4    time_of_day = datetime.datetime.now.timestamp()
 5    sinusoide = 100 * sin( 2 * 3.14 * frequency * time_of_day)
 6
 7    if sinusoide >= 0:
 8      filterable_names = ["value"]
 9      filterable_value = [sinusoide]
10
11      push_event(attr.get_name(), filterable_names, filterable_value)
12
13    # ...

line 8-9 : The filter pair name/value is initialised

line 11: The event is pushed