HAN-FUN API  1.5.3
This project provides the common implementation of ULE Alliance's HAN-FUN application protocol.
example_07.cpp
Go to the documentation of this file.
1 // =============================================================================
15 // =============================================================================
16 
17 #include <cassert>
18 #include <cmath>
19 
20 #include <iomanip>
21 
22 #include "hanfun.h"
23 #include "hanfun/debug.h"
24 
25 #include "localloop.h"
26 
27 // =============================================================================
28 // Defines
29 // =============================================================================
30 
31 /* *INDENT-OFF* */
32 #define TABLE_LOG(_X) LOG (INFO) << std::right << std::setw (15) << _X << std::left << NL
33 
34 #define TABLE_STREAM(_X) stream << "[INFO ] " << std::right << std::setw (15) << _X << std::left
35 /* *INDENT-ON* */
36 
37 // =============================================================================
38 // Implementation
39 // =============================================================================
40 
41 using namespace HF::Core::AttributeReporting;
42 
43 std::ostream &operator<<(std::ostream &stream, const Report::Attribute &attr)
44 {
45  stream << " (idx) " << attr->interface() << " (itf) " << (int) attr->uid() << " (uid)";
46  return stream;
47 }
48 
49 std::ostream &operator<<(std::ostream &stream, const Report::Periodic::Entry &entry)
50 {
51  TABLE_STREAM("ITF : " << entry.itf.id << " (uid) " << entry.itf.role << " (role)") << NL;
52  TABLE_STREAM("Unit : " << (int) entry.unit) << NL;
53 
54  TABLE_STREAM("Attributes : " << (int) entry.attributes.size() << " (#)");
55 
56  return stream;
57 }
58 
59 std::ostream &operator<<(std::ostream &stream, const Report::Event::Entry &entry)
60 {
61  TABLE_STREAM("ITF : " << entry.itf.id << " (uid) " << entry.itf.role << " (role)") << NL;
62  TABLE_STREAM("Unit : " << (int) entry.unit) << NL;
63 
64  TABLE_STREAM("Fields : " << (int) entry.fields.size() << " (#)");
65 
66  return stream;
67 }
68 
69 std::ostream &operator<<(std::ostream &stream, const Report::Event::Field &field)
70 {
71  stream << " (idx) " << field.type << " (type) ";
72  return stream;
73 }
74 
75 std::ostream &operator<<(std::ostream &stream, const Report::Abstract &report)
76 {
77  stream << (int) report.id << " (id) " << (int) report.type << " (type) ";
78  return stream;
79 }
80 
81 namespace
82 {
83  /*
84  * Custom attribute reporting client role implementation.
85  */
86  struct AttributeReporting: public HF::Core::AttributeReporting::AbstractClient
87  {
88  void report(HF::Core::AttributeReporting::Type type, const HF::Protocol::Address &address,
89  const HF::Common::ByteArray &payload, uint16_t offset)
90  {
91  UNUSED(payload);
92  UNUSED(offset);
93 
94  using namespace HF::Core::AttributeReporting;
95 
96  switch (type)
97  {
99  case PERIODIC:
100  {
101  LOG(INFO) << "Received a periodic notification from device : "
102  << address.device << NL;
103 
104  Report::Periodic report;
105 
106  report.unpack(HF::Attributes::get_factory, payload, offset);
107 
108  TABLE_LOG("Report : " << report);
109  TABLE_LOG("Entries : " << std::distance(report.entries.begin(),
110  report.entries.end()) << " (#)");
111 
112  uint8_t unit_count = 0;
113  /* *INDENT-OFF* */
114  std::for_each (report.entries.begin (), report.entries.end (),
115  [&unit_count](Report::Periodic::Entry &entry)
116  {
117  TABLE_LOG ("Entry : " << (int) unit_count++ << std::endl << entry);
118 
119  uint8_t attr_count = 0;
120  std::for_each (entry.attributes.begin (), entry.attributes.end (),
121  [&attr_count](Report::Attribute attr)
122  {
123  TABLE_LOG (" Attribute : " << (int) attr_count++ << attr);
124 
125  if (attr->interface () == HF::Interface::LEVEL_CONTROL &&
127  {
129  (HF::Interfaces::LevelControl::Level *) attr.get ();
130  int precent = HF::Common::to_percent <uint8_t>(level_attr->get ());
131  TABLE_LOG ("Attr Value : " << precent << "%");
132  }
133  });
134  });
135  /* *INDENT-ON* */
136 
137  break;
138  }
139 
142  case EVENT:
143  {
144  LOG(INFO) << "Received an event notification : " << address.device << NL;
145 
146  Report::Event report;
147 
148  report.unpack(HF::Attributes::get_factory, payload, offset);
149 
150  TABLE_LOG("Report : " << report);
151  TABLE_LOG("Entries : " << std::distance(report.entries.begin(),
152  report.entries.end()) << " (#)");
153 
154  uint8_t unit_count = 0;
155  /* *INDENT-OFF* */
156  std::for_each (report.entries.begin (), report.entries.end (),
157  [&unit_count](Report::Event::Entry &entry)
158  {
159  TABLE_LOG ("Entry : " << (int) unit_count++ << std::endl << entry);
160 
161  uint8_t field_count = 0;
162  std::for_each (entry.fields.begin (), entry.fields.end (),
163  [&field_count](Report::Event::Field &field)
164  {
165  TABLE_LOG (" Field : " << (int) field_count++ << field);
166  TABLE_LOG (" Attribute : " << field.attribute->interface () << " (itf) "
167  << (int) field.attribute->uid () << " (uid)");
168 
169  if (field.attribute->interface () == HF::Interface::LEVEL_CONTROL &&
170  field.attribute->uid () == HF::Interfaces::LevelControl::LEVEL_ATTR)
171  {
173  (HF::Interfaces::LevelControl::Level *) field.attribute.get ();
174  int precent = HF::Common::to_percent <uint8_t>(level_attr->get ());
175  TABLE_LOG ("Attr Value : " << precent << "%");
176  }
177  });
178  });
179  /* *INDENT-ON* */
180 
181  break;
182  }
184  default:
185  break;
186  }
187  }
188 
189  void created(const HF::Protocol::Address &address,
190  const HF::Core::AttributeReporting::Response &response)
191  {
192  LOG(INFO) << "Report rule created on device : " << address.device << NL;
193  LOG(INFO) << " Code : " << (int) response.code << NL;
194  LOG(INFO) << " Type : " << (int) response.report.type << NL;
195  LOG(INFO) << " Id : " << (int) response.report.id << NL;
196  }
197 
198  void added(const HF::Protocol::Address &address,
199  const HF::Core::AttributeReporting::Response &response)
200  {
201  LOG(INFO) << "Report rule entries created on device : " << address.device << NL;
202  LOG(INFO) << " Code : " << (int) response.code << NL;
203  LOG(INFO) << " Type : " << (int) response.report.type << NL;
204  LOG(INFO) << " Id : " << (int) response.report.id << NL;
205  }
206  };
207 
208  /*
209  * Unit implementing the Simple %Level Control profile.
210  */
211  struct SimpleLevelControl: public HF::Units::Unit<HF::Profiles::SimpleLevelControl,
212  AttributeReporting>
213  {
214  SimpleLevelControl(uint8_t id, HF::IDevice &device):
215  HF::Units::Unit<HF::Profiles::SimpleLevelControl, AttributeReporting>(id, device)
216  {}
217 
218  AttributeReporting *attribute_reporting() const
219  {
220  return const_cast<AttributeReporting *>(get<0>());
221  }
222  };
223 
224  /*
225  * Unit implementing the Simple Level Controllable profile.
226  */
227  struct SimpleLevelControllable: public HF::Units::Unit<HF::Profiles::SimpleLevelControllable>
228  {
230 
231  SimpleLevelControllable(uint8_t id, HF::IDevice &device):
232  _Parent(id, device)
233  {}
234 
235  void level_change(HF::Protocol::Address &source, uint8_t old_level, uint8_t new_level)
236  {
237  _Parent::level_change(source, old_level, new_level);
238  LOG(INFO) << "Level Change : "
239  << (int) HF::Common::to_percent(old_level)
240  << "\% -> "
241  << (int) HF::Common::to_percent(new_level)
242  << "\%" << NL;
243  }
244  };
245 
246  /*
247  * Example node.
248  */
249  struct Node: public HF::Devices::Node::Node
250  {};
251 
252  /*
253  * Example concentrator.
254  */
255  struct Base: public HF::Devices::Concentrator::Concentrator
256  {};
257 
258 } // namespace
259 
260 // =============================================================================
261 // MAIN
262 // =============================================================================
263 
264 int main(int argc, char **argv)
265 {
266  UNUSED(argc);
267  UNUSED(argv);
268 
269  LOG(INFO) << "Use case : Level Control interface usage" << NL;
270 
271  /*
272  * Each node variable is a remote device, i.e.,
273  * the Node variables are declared on the remote device
274  * code and are not used on the base code.
275  */
276  LOG(INFO) << "Create the node instances ..." << NL;
277 
278  Node node1;
279 
280  LOG(INFO) << "Add unit to node1 instance ..." << NL;
281  SimpleLevelControl level_control(1, node1);
282 
283  Node node2;
284 
285  LOG(INFO) << "Add unit to node2 instance ..." << NL;
286  SimpleLevelControllable level_controllable(1, node2);
287 
288  /*
289  * This instance represents the base application.
290  */
291  LOG(INFO) << "Create the base instance ..." << NL;
292  Base base;
293 
294  LOG(INFO) << "Create transport instance" << NL;
295  Localloop loop;
296 
297  /*
298  * Setup the network.
299  *
300  * This simulates the devices connecting to the base using for
301  * example a TCP/IP connection or a DECT ULE PVC.
302  */
303  LOG(INFO) << "Network setup ..." << NL;
304 
305  loop.set_base(&base);
306  loop.add_node(&node1, "node_1");
307  loop.add_node(&node2, "node_2");
308 
309  // Register the two devices.
310 
311  // Node 1 is unregistered.
312  assert(node1.address() == HF::Protocol::BROADCAST_ADDR);
313 
314  LOG(INFO) << "Registering node1 ... " << NL;
315  node1.unit0()->device_management()->register_device();
316  LOG(INFO) << "Node1 address ... " << node1.address() << NL;
317 
318  // Node 1 is registered
319  assert(node1.address() == 1);
320 
321  // Node 2 is unregistered.
322  assert(node2.address() == HF::Protocol::BROADCAST_ADDR);
323 
324  LOG(INFO) << "Registering node2 ... " << NL;
325  node2.unit0()->device_management()->register_device();
326  LOG(INFO) << "Node2 address ... " << node2.address() << NL;
327 
328  // Node 2 is registered
329  assert(node2.address() == 2);
330 
331  LOG(INFO) << "There should be two registered devices ... "
332  << base.unit0()->device_management()->entries().size() << NL;
333 
334  assert(base.unit0()->device_management()->entries().size() == 2);
335 
336  LOG(INFO) << "Set initial level value ... 15\%" << NL;
337  level_controllable.level(15.0f);
338 
339  HF::Protocol::Address addr(2, 0);
340 
341  // =============================================================================
342  // Periodic Notification
343  // =============================================================================
344 
345  using namespace HF::Core::AttributeReporting;
346 
347  LOG(INFO) << "====== Periodic Notifications ======" << NL;
348 
349  // Create a periodic rule.
350  level_control.attribute_reporting()->create(addr, 6);
351 
352  // Create another periodic rule.
353  level_control.attribute_reporting()->create(addr, 15);
354 
355  // Create the rule entries.
356  std::vector<Periodic::Entry> periodic_entries(1);
357 
358  auto &entry = periodic_entries.front();
359 
360  // Create an entry to receive all mandatory attributes.
361  entry.unit = 1;
364  entry.pack_id = HF::Attributes::Pack::MANDATORY;
365 
366  Reference report_ref(PERIODIC, 1);
367 
368  // Add entries to rule 1
369  level_control.attribute_reporting()->add(addr, report_ref, periodic_entries.begin(),
370  periodic_entries.end());
371 
372 
373  // Add entries to rule 2
374  report_ref.id = 2;
375  level_control.attribute_reporting()->add(addr, report_ref, periodic_entries.begin(),
376  periodic_entries.end());
377 
378  // Simulate remote device HAN-FUN periodic processing.
379  LOG(INFO) << ">>> Simulate node periodic processing <<<" << NL;
380 
381  for (int time = 0; time < 35; time++)
382  {
383  LOG(INFO) << "Time : " << time << "s" << NL;
384  node2.periodic(time);
385 
386  // At these times the level changes.
387  if (time == 10)
388  {
389  // Another node changes the value.
390  LOG(INFO) << "Level changes to 25 % by an another node." << NL;
391  HF::Protocol::Address addr(node2.address(), 1);
392  level_control.level(addr, 25.0f);
393  }
394  else if (time == 25)
395  {
396  // The value is changed on the actual node.
397  LOG(INFO) << "Level changes to 55 % on the actual node." << NL;
398  level_controllable.level(55.0f);
399  }
400  }
401 
402  // =============================================================================
403  // Event Notification
404  // =============================================================================
405 
406  LOG(INFO) << "====== Event Notifications ======" << NL;
407 
408  LOG(INFO) << "Set initial level value ... 15\%" << NL;
409  level_controllable.level(15.0f);
410 
411  // Create a periodic rule.
412  level_control.attribute_reporting()->create(addr);
413 
414  // Create another periodic rule.
415  level_control.attribute_reporting()->create(addr);
416 
417  // Create the rule entries.
418  std::vector<Event::Entry> event_entries;
419 
420  Event::Entry evt_entry;
421 
422  // Create an entry to receive changes on .
423  evt_entry.unit = 1;
424  evt_entry.itf.id = HF::Interface::LEVEL_CONTROL;
425  evt_entry.itf.role = HF::Interface::SERVER_ROLE;
427 
428  // Create the condition to send a report when level values is lower that to 5%
430  field.value = {HF::Common::from_percent<uint8_t>(5.0f)}; // 5%;
431 
432  evt_entry.fields.push_back(field);
433 
434  // Create the condition to send a report when level values is equal to 60%
435  field.type = Event::EQ;
436  field.value = {HF::Common::from_percent<uint8_t>(60.0f)}; // 60%;
437 
438  evt_entry.fields.push_back(field);
439 
440  // Create the condition to send a report when level values is greater that to 85%
441  field.type = Event::HT;
442  field.value = {HF::Common::from_percent<uint8_t>(85.0f)}; // 85%;
443 
444  evt_entry.fields.push_back(field);
445 
447 
448  event_entries.push_back(evt_entry);
449 
450  // Add entries to rule 1
451  level_control.attribute_reporting()->add(addr, evt_report_ref, event_entries.begin(),
452  event_entries.end());
453 
454  // Create another set of rules in second rule.
455  event_entries.clear();
456  evt_entry.fields.clear();
457 
458  // Create the condition to send a report when level values is lower that to 10%
459  field.type = Event::LT;
460  field.value = {HF::Common::from_percent<uint8_t>(10.0f)}; // 10%;
461 
462  evt_entry.fields.push_back(field);
463 
464  field.type = Event::EQ;
465  field.value = {HF::Common::from_percent<uint8_t>(40.0f)}; // 40%;
466 
467  evt_entry.fields.push_back(field);
468 
469  // Create the condition to send a report when level values is greater that to 90%
470  field.type = Event::HT;
471  field.value = {HF::Common::from_percent<uint8_t>(75.0f)}; // 75%;
472 
473  evt_entry.fields.push_back(field);
474 
475  event_entries.push_back(evt_entry);
476 
477  // Add entries to rule 2
478  evt_report_ref.id = 2;
479  level_control.attribute_reporting()->add(addr, evt_report_ref, event_entries.begin(),
480  event_entries.end());
481 
482  LOG(INFO) << ">>> Simulate node level change <<<" << NL;
483 
484  addr.unit = 1;
485 
486  LOG(INFO) << "== Change level to 30 % ..." << NL;
487  level_control.level(addr, 30.0f);
488 
489  LOG(INFO) << "== Change level to 40 % ..." << NL;
490  level_control.level(addr, 40.0f);
491 
492  LOG(INFO) << "== Change level to 50 % ..." << NL;
493  level_control.level(addr, 50.0f);
494 
495  LOG(INFO) << "== Change level to 60 % ..." << NL;
496  level_control.level(addr, 60.0f);
497 
498  LOG(INFO) << "== Change level to 70 % ..." << NL;
499  level_control.level(addr, 40.0f);
500 
501  LOG(INFO) << "== Change level to 80 % ..." << NL;
502  level_control.level(addr, 80.0f);
503 
504  LOG(INFO) << "== Change level to 90 % ..." << NL;
505  level_control.level(addr, 90.0f);
506 
507  LOG(INFO) << "== Change level to 20% ..." << NL;
508  level_control.level(addr, 20.0f);
509 
510  LOG(INFO) << "== Change level to 15% ..." << NL;
511  level_control.level(addr, 15.0f);
512 
513  LOG(INFO) << "== Change level to 10% ..." << NL;
514  level_control.level(addr, 10.0f);
515 
516  LOG(INFO) << "== Change level to 7% ..." << NL;
517  level_control.level(addr, 7.0f);
518 
519  LOG(INFO) << "== Change level to 5% ..." << NL;
520  level_control.level(addr, 5.0f);
521 
522  LOG(INFO) << "== Change level to 1% ..." << NL;
523  level_control.level(addr, 1.0f);
524 
525  return 0;
526 }
uint16_t unpack(HF::Attributes::FactoryGetter get_factory, const Common::ByteArray &array, uint16_t offset=0)
Read a message from a ByteArray.
Type
Types of reports send from the server to the client.
This namespace contains the classes that implement the Attribute Reporting service.
bool response(Message::Type type)
Check if message is a response.
This is the top level include file for the HAN-FUN library.
This file contains the prototypes of the debug functionality in HAN-FUN.
std::ostream & operator<<(std::ostream &stream, const uint8_t byte)
Convert the given byte into a string and write it to the given stream.
uint8_t pack_id
Attribute&#39;s Pack ID.
Return all mandatory attributes for the interface.
Definition: attributes.h:842
This class represents a node in the HAN-FUN network.
Definition: node.h:34
Helper class to handle the Level attribute for the Level Control interface.
Helper class for using the Attribute Reporting service.
Factory get_factory(Common::Interface itf)
Return the attribute factory associated with the given interface identifier.
constexpr uint16_t BROADCAST_ADDR
HAN-FUN Broadcast - device address.
Definition: protocol.h:45
uint16_t id
Identifier of the interface.
uint16_t unpack(HF::Attributes::FactoryGetter get_factory, const Common::ByteArray &array, uint16_t offset=0)
Read a message from a ByteArray.
uint8_t unit
Unit ID this entry to.
Helper template class to implement units.
Definition: units.h:196
uint16_t address() const _override
Return the device address on the HAN-FUN network, when the device is registered, or HF_BROADCAST_ADDR...
Definition: devices.h:456
This class represents the interface common to all HAN-FUN devices.
Definition: device.h:99
Return the attributes with the given attributes.
Definition: attributes.h:844
P to_percent(T value)
Convert a value in the [0,std::numeric_limits<T>::max()] range into a percentage. ...
Entry field for a given attribute.
Field for the entries in event notification.
std::vector< Field > fields
Vector containing the fields for the event entry.
void periodic(uint32_t time)
Handle periodic processing.
Common::Interface itf
Interface UID this notification relates to.
uint16_t role
Interface role : Server or Client.
Template for HAN-FUN concentrator devices.
Definition: devices.h:906
This class represents a byte array.
#define NL
Helper define for new-line and stream clear.
Definition: debug.h:34
std::vector< Field > fields
Vector containing the fields for this entry.
Level Control interface UID.
Definition: interface.h:80
Network Address.
Definition: protocol.h:201
Common::SimpleList< Entry > entries
Entries for the event notification.
uint8_t type
Report type : PERIODIC or EVENT.
Common::Interface itf
Interface UID.
uint8_t unit
Unit id that originated this notification.
This file contains an implementation of a HAN-FUN transport layer to be used in the example applicati...
uint16_t device
Device Address.
Definition: protocol.h:204
#define UNUSED(x)
Helper macro to remove warning about unused function/method argument.
CoreServices * unit0() const _override
Return pointer to the unit 0 for this device.
Definition: devices.h:461
Common::SimpleList< Entry > entries
Entries associated with this notification.
#define LOG(X)
Log messages with the level given by X.
Definition: debug.h:81
AttributeReporting::Event::Type type
Event type.
Template for declaring HAN-FUN node devices.
Definition: devices.h:425
Top-level namespace for the HAN-FUN library.
Definition: attributes.h:22