Generate events in a device server#
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.
Python: PyTango API documentation
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