Stefan Scholz / ETL
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers message_bus.h Source File

message_bus.h

00001 /******************************************************************************
00002 The MIT License(MIT)
00003 
00004 Embedded Template Library.
00005 https://github.com/ETLCPP/etl
00006 https://www.etlcpp.com
00007 
00008 Copyright(c) 2017 jwellbelove
00009 
00010 Permission is hereby granted, free of charge, to any person obtaining a copy
00011 of this software and associated documentation files(the "Software"), to deal
00012 in the Software without restriction, including without limitation the rights
00013 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
00014 copies of the Software, and to permit persons to whom the Software is
00015 furnished to do so, subject to the following conditions :
00016 
00017 The above copyright notice and this permission notice shall be included in all
00018 copies or substantial portions of the Software.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
00023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026 SOFTWARE.
00027 ******************************************************************************/
00028 
00029 #ifndef __ETL_MESSAGE_BUS_
00030 #define __ETL_MESSAGE_BUS_
00031 
00032 #include <stdint.h>
00033 #include <algorithm>
00034 
00035 #include "platform.h "
00036 #include "algorithm.h "
00037 #include "vector.h "
00038 #include "nullptr.h "
00039 #include "error_handler.h "
00040 #include "exception.h "
00041 #include "message_types.h"
00042 #include "message.h"
00043 #include "message_router.h"
00044 
00045 #undef ETL_FILE
00046 #define ETL_FILE "39"
00047 
00048 namespace etl
00049 {
00050   //***************************************************************************
00051   /// Base exception class for message bus
00052   //***************************************************************************
00053   class message_bus_exception : public etl::exception
00054   {
00055   public:
00056 
00057     message_bus_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
00058       : etl::exception(reason_, file_name_, line_number_)
00059     {
00060     }
00061   };
00062 
00063   //***************************************************************************
00064   /// Too many subscribers.
00065   //***************************************************************************
00066   class message_bus_too_many_subscribers : public etl::message_bus_exception
00067   {
00068   public:
00069 
00070     message_bus_too_many_subscribers(string_type file_name_, numeric_type line_number_)
00071       : message_bus_exception(ETL_ERROR_TEXT("message bus:too many subscribers", ETL_FILE"A"), file_name_, line_number_)
00072     {
00073     }
00074   };
00075 
00076   //***************************************************************************
00077   /// Interface for message bus
00078   //***************************************************************************
00079   class imessage_bus : public etl::imessage_router
00080   {
00081   private:
00082 
00083     typedef etl::ivector<etl::imessage_router*>  router_list_t ;
00084 
00085   public:
00086 
00087     using etl::imessage_router::receive;
00088 
00089     //*******************************************
00090     /// Subscribe to the bus.
00091     //*******************************************
00092     bool subscribe(etl::imessage_router& router)
00093     {
00094       bool ok = true;
00095 
00096       // There's no point actually adding null routers.
00097       if (!router.is_null_router())
00098       {
00099         ok = !router_list.full();
00100 
00101         ETL_ASSERT(ok, ETL_ERROR(etl::message_bus_too_many_subscribers));
00102 
00103         if (ok)
00104         {
00105           if (router.is_bus())
00106           {
00107             // Message busses get added to the end.
00108             router_list.push_back(&router);
00109           }
00110           else
00111           {
00112             // Routers get added in id order.
00113             router_list_t::iterator irouter = std::upper_bound(router_list.begin(),
00114                                                                router_list.end(),
00115                                                                router.get_message_router_id(),
00116                                                                compare_router_id());
00117 
00118             router_list.insert(irouter, &router);
00119           }
00120         }
00121       }
00122 
00123       return ok;
00124     }
00125 
00126     //*******************************************
00127     /// Unsubscribe from the bus.
00128     //*******************************************
00129     void unsubscribe(etl::message_router_id_t id)
00130     {
00131       if (id == etl::imessage_bus::ALL_MESSAGE_ROUTERS)
00132       {
00133         clear();
00134       }
00135       else
00136       {
00137         std::pair<router_list_t::iterator, router_list_t::iterator> range = std::equal_range(router_list.begin(),
00138                                                                                              router_list.end(),
00139                                                                                              id,
00140                                                                                              compare_router_id());
00141 
00142         router_list.erase(range.first, range.second);
00143       }
00144     }
00145 
00146     //*******************************************
00147     void unsubscribe(etl::imessage_router& router)
00148     {
00149       router_list_t::iterator irouter = std::find(router_list.begin(),
00150                                                   router_list.end(),
00151                                                   &router);
00152 
00153       if (irouter != router_list.end())
00154       {
00155         router_list.erase(irouter);
00156       }
00157     }
00158 
00159     //*******************************************
00160     void receive(const etl::imessage& message)
00161     {
00162       etl::null_message_router nmr;
00163       receive(nmr, etl::imessage_router::ALL_MESSAGE_ROUTERS, message);
00164     }
00165 
00166     //*******************************************
00167     void receive(etl::message_router_id_t destination_router_id,
00168                  const etl::imessage&     message)
00169     {
00170       etl::null_message_router nmr;
00171       receive(nmr, destination_router_id, message);
00172     }
00173 
00174     //*******************************************
00175     void receive(etl::imessage_router& source,
00176                  const etl::imessage&  message)
00177     {
00178       receive(source, etl::imessage_router::ALL_MESSAGE_ROUTERS, message);
00179     }
00180 
00181     //*******************************************
00182     void receive(etl::imessage_router&    source,
00183                  etl::message_router_id_t destination_router_id,
00184                  const etl::imessage&     message)
00185     {
00186       switch (destination_router_id)
00187       {
00188         //*****************************
00189         // Null message router. These routers can never be subscribed.
00190         case etl::imessage_router::NULL_MESSAGE_ROUTER:
00191         {
00192           break;
00193         }
00194 
00195         //*****************************
00196         // Broadcast to all routers.
00197         case etl::imessage_router::ALL_MESSAGE_ROUTERS:
00198         {
00199           router_list_t::iterator irouter = router_list.begin();
00200 
00201           // Broadcast to everyone.
00202           while (irouter != router_list.end())
00203           {
00204             etl::imessage_router& router = **irouter;
00205 
00206             if (router.is_bus())
00207             {
00208               // The router is actually a bus.
00209               etl::imessage_bus& bus = static_cast<etl::imessage_bus&>(router);
00210 
00211               // So pass it on.
00212               bus.receive(source, destination_router_id, message);
00213             }
00214             else if (router.accepts(message.message_id))
00215             {
00216               router.receive(source, message);
00217             }
00218 
00219             ++irouter;
00220           }
00221 
00222           break;
00223         }
00224 
00225         //*****************************
00226         // Must be an addressed message.
00227         default:
00228         {
00229           router_list_t::iterator irouter = router_list.begin();
00230 
00231           // Find routers with the id.
00232           std::pair<router_list_t::iterator, router_list_t::iterator> range = std::equal_range(router_list.begin(),
00233                                                                                                router_list.end(),
00234                                                                                                destination_router_id,
00235                                                                                                compare_router_id());
00236 
00237           // Call all of them.
00238           while (range.first != range.second)
00239           {
00240             if ((*(range.first))->accepts(message.message_id))
00241             {
00242               (*(range.first))->receive(source, message);
00243             }
00244 
00245             ++range.first;
00246           }
00247 
00248           // Do any message buses.
00249           // These are always at the end of the list.
00250           irouter = std::lower_bound(router_list.begin(),
00251                                      router_list.end(),
00252                                      etl::imessage_bus::MESSAGE_BUS,
00253                                      compare_router_id());
00254 
00255           while (irouter != router_list.end())
00256           {
00257             // The router is actually a bus.
00258             etl::imessage_bus& bus = static_cast<etl::imessage_bus&>(**irouter);
00259 
00260             // So pass it on.
00261             bus.receive(source, destination_router_id, message);
00262 
00263             ++irouter;
00264           }
00265 
00266           break;
00267         }
00268       }
00269     }
00270 
00271     using imessage_router::accepts;
00272 
00273     //*******************************************
00274     /// Does this message bus accept the message id?
00275     /// Yes!, it accepts everything!
00276     //*******************************************
00277     bool accepts(etl::message_id_t) const
00278     {
00279       return true;
00280     }
00281 
00282     //*******************************************
00283     size_t size() const
00284     {
00285       return router_list.size();
00286     }
00287 
00288     //*******************************************
00289     void clear()
00290     {
00291       return router_list.clear();
00292     }
00293 
00294   protected:
00295 
00296     //*******************************************
00297     /// Constructor.
00298     //*******************************************
00299     imessage_bus(router_list_t & list)
00300       : imessage_router(etl::imessage_router::MESSAGE_BUS),
00301         router_list(list)
00302     {
00303     }
00304 
00305   private:
00306 
00307     //*******************************************
00308     // How to compare routers to router ids.
00309     //*******************************************
00310     struct compare_router_id
00311     {
00312       bool operator()(const etl::imessage_router* prouter, etl::message_router_id_t id) const
00313       {
00314         return prouter->get_message_router_id() < id;
00315       }
00316 
00317       bool operator()(etl::message_router_id_t id, const etl::imessage_router* prouter) const
00318       {
00319         return id < prouter->get_message_router_id();
00320       }
00321     };
00322 
00323     router_list_t& router_list;
00324   };
00325 
00326   //***************************************************************************
00327   /// The message bus
00328   //***************************************************************************
00329   template <uint_least8_t MAX_ROUTERS_>
00330   class message_bus : public etl::imessage_bus
00331   {
00332   public:
00333 
00334     //*******************************************
00335     /// Constructor.
00336     //*******************************************
00337     message_bus()
00338       : imessage_bus(router_list)
00339     {
00340     }
00341 
00342   private:
00343 
00344     etl::vector<etl::imessage_router*, MAX_ROUTERS_>  router_list;
00345   };
00346 
00347   //***************************************************************************
00348   /// Send a message to a bus.
00349   //***************************************************************************
00350   inline static void send_message(etl::imessage_bus&   bus,
00351                                   const etl::imessage& message)
00352   {
00353     bus.receive(message);
00354   }
00355 
00356   //***************************************************************************
00357   /// Send a message to a bus.
00358   //***************************************************************************
00359   inline static void send_message(etl::imessage_bus&       bus,
00360                                   etl::message_router_id_t id,
00361                                   const etl::imessage&     message)
00362   {
00363     bus.receive(id, message);
00364   }
00365 
00366   //***************************************************************************
00367   /// Send a message to a bus.
00368   //***************************************************************************
00369   inline static void send_message(etl::imessage_router& source,
00370                                   etl::imessage_bus&    bus,
00371                                   const etl::imessage&  message)
00372   {
00373     bus.receive(source, message);
00374   }
00375 
00376   //***************************************************************************
00377   /// Send a message to a bus.
00378   //***************************************************************************
00379   inline static void send_message(etl::imessage_router&    source,
00380                                   etl::imessage_bus&       bus,
00381                                   etl::message_router_id_t id,
00382                                   const etl::imessage&     message)
00383   {
00384     bus.receive(source, id, message);
00385   }
00386 }
00387 
00388 #undef ETL_FILE
00389 
00390 #endif
00391