Containers (STL-compatible) StateMachines MessageBus and more for Embedded Systems. See www.etlcpp.com
Diff: message_bus.h
- Revision:
- 0:b47c2a7920c2
diff -r 000000000000 -r b47c2a7920c2 message_bus.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/message_bus.h Fri Mar 16 16:34:18 2018 +0000 @@ -0,0 +1,391 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_MESSAGE_BUS_ +#define __ETL_MESSAGE_BUS_ + +#include <stdint.h> +#include <algorithm> + +#include "platform.h" +#include "algorithm.h" +#include "vector.h" +#include "nullptr.h" +#include "error_handler.h" +#include "exception.h" +#include "message_types.h" +#include "message.h" +#include "message_router.h" + +#undef ETL_FILE +#define ETL_FILE "39" + +namespace etl +{ + //*************************************************************************** + /// Base exception class for message bus + //*************************************************************************** + class message_bus_exception : public etl::exception + { + public: + + message_bus_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : etl::exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// Too many subscribers. + //*************************************************************************** + class message_bus_too_many_subscribers : public etl::message_bus_exception + { + public: + + message_bus_too_many_subscribers(string_type file_name_, numeric_type line_number_) + : message_bus_exception(ETL_ERROR_TEXT("message bus:too many subscribers", ETL_FILE"A"), file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// Interface for message bus + //*************************************************************************** + class imessage_bus : public etl::imessage_router + { + private: + + typedef etl::ivector<etl::imessage_router*> router_list_t; + + public: + + using etl::imessage_router::receive; + + //******************************************* + /// Subscribe to the bus. + //******************************************* + bool subscribe(etl::imessage_router& router) + { + bool ok = true; + + // There's no point actually adding null routers. + if (!router.is_null_router()) + { + ok = !router_list.full(); + + ETL_ASSERT(ok, ETL_ERROR(etl::message_bus_too_many_subscribers)); + + if (ok) + { + if (router.is_bus()) + { + // Message busses get added to the end. + router_list.push_back(&router); + } + else + { + // Routers get added in id order. + router_list_t::iterator irouter = std::upper_bound(router_list.begin(), + router_list.end(), + router.get_message_router_id(), + compare_router_id()); + + router_list.insert(irouter, &router); + } + } + } + + return ok; + } + + //******************************************* + /// Unsubscribe from the bus. + //******************************************* + void unsubscribe(etl::message_router_id_t id) + { + if (id == etl::imessage_bus::ALL_MESSAGE_ROUTERS) + { + clear(); + } + else + { + std::pair<router_list_t::iterator, router_list_t::iterator> range = std::equal_range(router_list.begin(), + router_list.end(), + id, + compare_router_id()); + + router_list.erase(range.first, range.second); + } + } + + //******************************************* + void unsubscribe(etl::imessage_router& router) + { + router_list_t::iterator irouter = std::find(router_list.begin(), + router_list.end(), + &router); + + if (irouter != router_list.end()) + { + router_list.erase(irouter); + } + } + + //******************************************* + void receive(const etl::imessage& message) + { + etl::null_message_router nmr; + receive(nmr, etl::imessage_router::ALL_MESSAGE_ROUTERS, message); + } + + //******************************************* + void receive(etl::message_router_id_t destination_router_id, + const etl::imessage& message) + { + etl::null_message_router nmr; + receive(nmr, destination_router_id, message); + } + + //******************************************* + void receive(etl::imessage_router& source, + const etl::imessage& message) + { + receive(source, etl::imessage_router::ALL_MESSAGE_ROUTERS, message); + } + + //******************************************* + void receive(etl::imessage_router& source, + etl::message_router_id_t destination_router_id, + const etl::imessage& message) + { + switch (destination_router_id) + { + //***************************** + // Null message router. These routers can never be subscribed. + case etl::imessage_router::NULL_MESSAGE_ROUTER: + { + break; + } + + //***************************** + // Broadcast to all routers. + case etl::imessage_router::ALL_MESSAGE_ROUTERS: + { + router_list_t::iterator irouter = router_list.begin(); + + // Broadcast to everyone. + while (irouter != router_list.end()) + { + etl::imessage_router& router = **irouter; + + if (router.is_bus()) + { + // The router is actually a bus. + etl::imessage_bus& bus = static_cast<etl::imessage_bus&>(router); + + // So pass it on. + bus.receive(source, destination_router_id, message); + } + else if (router.accepts(message.message_id)) + { + router.receive(source, message); + } + + ++irouter; + } + + break; + } + + //***************************** + // Must be an addressed message. + default: + { + router_list_t::iterator irouter = router_list.begin(); + + // Find routers with the id. + std::pair<router_list_t::iterator, router_list_t::iterator> range = std::equal_range(router_list.begin(), + router_list.end(), + destination_router_id, + compare_router_id()); + + // Call all of them. + while (range.first != range.second) + { + if ((*(range.first))->accepts(message.message_id)) + { + (*(range.first))->receive(source, message); + } + + ++range.first; + } + + // Do any message buses. + // These are always at the end of the list. + irouter = std::lower_bound(router_list.begin(), + router_list.end(), + etl::imessage_bus::MESSAGE_BUS, + compare_router_id()); + + while (irouter != router_list.end()) + { + // The router is actually a bus. + etl::imessage_bus& bus = static_cast<etl::imessage_bus&>(**irouter); + + // So pass it on. + bus.receive(source, destination_router_id, message); + + ++irouter; + } + + break; + } + } + } + + using imessage_router::accepts; + + //******************************************* + /// Does this message bus accept the message id? + /// Yes!, it accepts everything! + //******************************************* + bool accepts(etl::message_id_t) const + { + return true; + } + + //******************************************* + size_t size() const + { + return router_list.size(); + } + + //******************************************* + void clear() + { + return router_list.clear(); + } + + protected: + + //******************************************* + /// Constructor. + //******************************************* + imessage_bus(router_list_t& list) + : imessage_router(etl::imessage_router::MESSAGE_BUS), + router_list(list) + { + } + + private: + + //******************************************* + // How to compare routers to router ids. + //******************************************* + struct compare_router_id + { + bool operator()(const etl::imessage_router* prouter, etl::message_router_id_t id) const + { + return prouter->get_message_router_id() < id; + } + + bool operator()(etl::message_router_id_t id, const etl::imessage_router* prouter) const + { + return id < prouter->get_message_router_id(); + } + }; + + router_list_t& router_list; + }; + + //*************************************************************************** + /// The message bus + //*************************************************************************** + template <uint_least8_t MAX_ROUTERS_> + class message_bus : public etl::imessage_bus + { + public: + + //******************************************* + /// Constructor. + //******************************************* + message_bus() + : imessage_bus(router_list) + { + } + + private: + + etl::vector<etl::imessage_router*, MAX_ROUTERS_> router_list; + }; + + //*************************************************************************** + /// Send a message to a bus. + //*************************************************************************** + inline static void send_message(etl::imessage_bus& bus, + const etl::imessage& message) + { + bus.receive(message); + } + + //*************************************************************************** + /// Send a message to a bus. + //*************************************************************************** + inline static void send_message(etl::imessage_bus& bus, + etl::message_router_id_t id, + const etl::imessage& message) + { + bus.receive(id, message); + } + + //*************************************************************************** + /// Send a message to a bus. + //*************************************************************************** + inline static void send_message(etl::imessage_router& source, + etl::imessage_bus& bus, + const etl::imessage& message) + { + bus.receive(source, message); + } + + //*************************************************************************** + /// Send a message to a bus. + //*************************************************************************** + inline static void send_message(etl::imessage_router& source, + etl::imessage_bus& bus, + etl::message_router_id_t id, + const etl::imessage& message) + { + bus.receive(source, id, message); + } +} + +#undef ETL_FILE + +#endif +