1- // BSD 3-Clause License
2- // Copyright (c) 2021 The Trustees of the University of Pennsylvania. All Rights Reserved
3- // Authors:
4- // Shane Rozen-Levy <[email protected] >1+ /* *
2+ * @file lcm_subscriber.h
3+ * @author Ethan Musser ([email protected] ) 4+ * @author J. Diego Caporale ([email protected] ) 5+ * @author Shane Rozen-Levy ([email protected] ) 6+ * @brief Provides LCM subscriber object for subscribing to one or multiple LCM
7+ * channels.
8+ * @date 7/7/22
9+ *
10+ * @copyright Copyright 2022 The Trustees of the University of Pennsylvania. All
11+ * rights reserved. BSD 3-Clause License
12+ *
13+ */
514
615#pragma once
16+
17+ #include < map>
18+ #include < mutex>
719#include < string>
820#include " lcm/lcm-cpp.hpp"
921#include " real_time_tools/thread.hpp"
1022#include " kodlab_mjbots_sdk/abstract_realtime_object.h"
23+ #include " kodlab_mjbots_sdk/lcm_message_handler.h"
24+ #include " kodlab_mjbots_sdk/log.h"
1125
1226namespace kodlab {
13- /* !
14- * @brief An template class for an lcm subscriber. Subscribes to just one msg of type msg_type.
15- * @tparam MessageClass the lcm msg type
27+
28+ /* *
29+ * @brief LCM subscriber capable of subscribing to multiple channels
30+ * @warning Running multiple `LcmSubscriber` objects on a single CPU can result
31+ * in concurrency issues. The authors do not endorse this usage, do so at your
32+ * own risk.
33+ * @todo Move implementation to source file once `CTRL_C_DETECTED` global
34+ * variable made available to TU.
1635 */
17- template <class MessageClass >
1836class LcmSubscriber : public AbstractRealtimeObject {
1937 public:
38+
2039 /* !
21- * @brief constructor for lcm subscriber
22- * @param realtime_priority the realtime priority, max is 99
23- * @param cpu the cpu for this process
24- * @param channel_name the string for the channel name
40+ * @brief Constructor an lcm subscriber
41+ * @param realtime_priority realtime priority in range [1, 99]
42+ * @param cpu cpu for this process
2543 */
26- LcmSubscriber (int realtime_priority, int cpu, std::string channel_name );
44+ LcmSubscriber (int realtime_priority, int cpu);
2745
28- std::mutex mutex_; // / Mutex for if m_data
29- bool new_message_ =
30- false ; // / True if there is new data, false if data is old, used to prevent user for checking too often
31- MessageClass data_; // / A copy of the most recent lcm data
32- protected:
33- /* !
34- * @brief callback function when msg is received. Copies the message to m_data
35- * @param rbuf unknown, but not used
36- * @param chan channel name
37- * @param msg ptr to the incoming message data
46+ /* *
47+ * @brief Initialize the LCM subscriber thread.
3848 */
39- void HandleMsg (const lcm::ReceiveBuffer *rbuf,
40- const std::string &chan,
41- const MessageClass *msg);
49+ void Init ();
4250
43- /* !
44- * @brief subscribes to the lcm channel using handle message and handles ctrl c detection
51+ /* *
52+ * @brief Add a new LCM channel to the the LCM object's subscriptions
53+ * @tparam Message LCM message type for channel
54+ * @param channel_name channel name
55+ * @param handler `LcmMessageHandler`-inherited message handler object
56+ * @return generated `lcm::Subscription` object
4557 */
46- void Run () override ;
58+ template <class Message >
59+ lcm::Subscription *AddSubscription (std::string channel_name,
60+ LcmMessageHandler<Message> &handler) {
61+ auto sub = lcm_.subscribe (channel_name,
62+ &LcmMessageHandler<Message>::HandleMessage,
63+ &handler);
64+ subs_.emplace (channel_name, sub);
65+ return sub;
66+ }
67+
68+ /* *
69+ * @brief Remove a subscription by channel name
70+ * @note Warns and continues if channel does not exist.
71+ * @param channel_name channel name of subscription to be removed
72+ * @return 0 if unsubscribe successful, -1 if `channel_name` is not a valid
73+ * subscription
74+ */
75+ int RemoveSubscription (const std::string &channel_name);
4776
48- std::string channel_name_;
77+ /* *
78+ * @brief Accessor for `lcm::Subscription` objects
79+ * @param channel_name channel name
80+ * @return `lcm::Subscription` object on corresponding channel if subscription
81+ * exists, `nullptr` otherwise
82+ */
83+ [[nodiscard]] const lcm::Subscription *get_subscription (const std::string &channel_name) const ;
84+
85+ private:
86+
87+ /* *
88+ * @brief LCM object
89+ */
4990 lcm::LCM lcm_;
91+
92+ /* *
93+ * @brief Map of channel names to corresponding `lcm::Subscription` objects
94+ */
95+ std::map<std::string, lcm::Subscription *> subs_;
96+
97+ /* *
98+ * @brief Waits for LCM messages and exits when ctrl+c detected
99+ */
100+ void Run () override ;
101+
50102};
51103
52- /* ***********************************Implementation********************************************************************/
53- template <class msg_type >
54- void LcmSubscriber<msg_type>::Run() {
55- lcm_.subscribe (channel_name_, &LcmSubscriber::HandleMsg, this );
56- mutex_.unlock (); // Ensures mutex is unlocked
57- std::cout << " Subscribing to " << channel_name_ << std::endl;
58- while (!CTRL_C_DETECTED) {
59- lcm_.handleTimeout (1000 );
60- }
61- }
62104
63- template <class msg_type >
64- LcmSubscriber<msg_type>::LcmSubscriber(int realtime_priority, int cpu, std::string channel_name):
65- channel_name_ (channel_name) {
105+ // //////////////////////////////////////////////////////////////////////////////
106+ // Implementation //
107+ // //////////////////////////////////////////////////////////////////////////////
108+
109+ LcmSubscriber::LcmSubscriber (int realtime_priority, int cpu) {
66110 cpu_ = cpu;
67111 realtime_priority_ = realtime_priority;
68- if (!channel_name_.empty ())
69- Start ();
70112}
71113
72- template <class msg_type >
73- void LcmSubscriber<msg_type>::HandleMsg(const lcm::ReceiveBuffer *rbuf,
74- const std::string &chan,
75- const msg_type *msg) {
76- // Lock mutex
77- mutex_.lock ();
78- // Copy data
79- data_ = *msg;
80- // Let user know new message
81- new_message_ = true ;
82- // Unlock mutex
83- mutex_.unlock ();
114+ void LcmSubscriber::Init () {
115+ Start ();
84116}
85- } // namespace kodlab
117+
118+ int LcmSubscriber::RemoveSubscription (const std::string &channel_name) {
119+ auto it = subs_.find (channel_name);
120+ if (it != subs_.end ()) {
121+ int success = lcm_.unsubscribe (subs_[channel_name]);
122+ subs_.erase (it);
123+ return success;
124+ } else {
125+ LOG_WARN (" Channel \" %s\" is not in subscription list." ,
126+ channel_name.c_str ());
127+ }
128+ return -1 ;
129+ }
130+
131+ [[nodiscard]] const lcm::Subscription *LcmSubscriber::get_subscription (const std::string &channel_name) const {
132+ if (subs_.count (channel_name) == 1 ) {
133+ return subs_.at (channel_name);
134+ } else {
135+ LOG_ERROR (" Channel \" %s\" is not in subscription list." ,
136+ channel_name.c_str ());
137+ return nullptr ;
138+ }
139+ }
140+
141+ void LcmSubscriber::Run () {
142+ while (!CTRL_C_DETECTED) {
143+ lcm_.handleTimeout (1000 );
144+ }
145+ }
146+
147+ } // kodlab
148+
0 commit comments