MESSAGES.adoc 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # Handling Communications Messages in Plugins
  2. Since TBD, opencpn has a new system for handling messages in plugins. The new
  3. model is based on that a plugin subscribes to the messages it is interested
  4. in rather then the old model where plugins get all messages.
  5. The new system is the only way to handle the new NMEA2000 devices. For N0183
  6. devices a compatiblity layer is installed which makes things work as earlier.
  7. However, the new system can be used also for nmea0183 where it offers more
  8. flexibility and performance.
  9. Handling of messages consists of six steps:
  10. 1. Define the type of message to work with
  11. 2. Declare a listening object.
  12. 3. Declare an event which will carry the message.
  13. 4. Declare a method which handles the message.
  14. 5. Subscribe to this message i. e., start listening to it
  15. 6. Handle the messages as they arrives.
  16. All in all, an example could be like this:
  17. First, declare a listener on the class level, here with the shipdriver
  18. plugin _ShipDriver_pi.h_ header as an example:
  19. class ShipDriver_pi : public opencpn_plugin_118 {
  20. public:
  21. ShipDriver_pi(void* ppimgr);
  22. ~ShipDriver_pi(void);
  23. ...
  24. private:
  25. ② std::shared_ptr<ObservableListener> listener;
  26. ...
  27. Then declare a method which handles the message, something like this:
  28. ④ void HandleGPGA(ObservedEvt ev) {
  29. ① NMEA0183 id("GPGGA");
  30. std::string payload = GetN0183Payload(id, ev)
  31. ...
  32. Then, in the constructor set up the listening:
  33. ShipDriver_pi::ShipDriver_pi(void* ppimgr)
  34. : opencpn_plugin_116(ppimgr)
  35. {
  36. ...
  37. ① NMEA0183Id id("GPGGA");
  38. ③ wxDEFINE_EVENT(EVT_SHIPDRIVER, ObservedEvt);
  39. ⑤ listener.Listen(id.key(), EVT_SHIPDRIVER, this);
  40. ⑥ Bind(EVT_SHIPDRIVER [](ObservedEvt ev) { HandleGPGA(ev); });
  41. Notes:
  42. ①:: This is the type of message we are listening to. There are similar types
  43. for NMEA2000 , SignalK and decoded navigation data (lat, long, etc.)
  44. ②:: Boiler-plate code, makes sure the listener is destroyed together with
  45. the plugin. It is important that the listener is declared in the class,
  46. a variable defined in a method will not work.
  47. ③:: This the event generated for our message which we will listen to. It should
  48. have an unique name, including the plugin name is a good habit. However
  49. the name, here _EVT_SHIPDRIVER_, could be anything.
  50. ④:: The `GetN0183Payload()` method gets the payload of the message into the
  51. `payload` string. From here it can be processed as required.
  52. ⑤:: This activates the listener. From this points system sends us an
  53. _EVT_SHIPDRIVER_ for each received GPGGA message.
  54. ⑥:: This is the final piece in the puzzle. It defines what should happen when
  55. the _EVT_SHIPDRIVER_ arrives. From this point all incoming GPGGA
  56. messages will land in the `HandleGPGGA()` method and be processed.
  57. ## Epilog: Using the lambda
  58. The last step above was
  59. `Bind(EVT_SHIPDRIVER [](wxCommandEvent ev) { HandleGPGA(ev); })`. The part
  60. between the curly braces is actually a C++ lambda expression. There is no
  61. need to dive into this to get something running, but it offers far larger
  62. possibilities than to just call a function. Actually, if written like
  63. `Bind(EVT_SHIPDRIVER [&](wxCommandEvent ev) { ... })` any code could
  64. be written between the braces.
  65. The interesting part here is that the `[&]` prefix makes this code "see"
  66. anything defined in the plugin. This is a convenient way to access plugin
  67. variables in the handler, something which otherwise is a problem.
  68. To get the feeling one need to experiment. But then again, C++ lambdas
  69. is a complex step which is not necessary to get something running.