Goby v2
chat.cpp
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 //
5 //
6 // This file is part of the Goby Underwater Autonomy Project Binaries
7 // ("The Goby Binaries").
8 //
9 // The Goby Binaries are free software: you can redistribute them and/or modify
10 // them under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // The Goby Binaries are distributed in the hope that they will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
21 
22 // usage: connect two modems and then run
23 // > chat /dev/tty_modem_A 1 2 log_file_A
24 // > chat /dev/tty_modem_B 2 1 log_file_B
25 
26 // type into a window and hit enter to send a message. messages will be queued
27 // and sent on a fixed rotating cycle
28 
29 #include <iostream>
30 
31 #include "chat.pb.h"
32 #include "goby/acomms/amac.h"
33 #include "goby/acomms/bind.h"
34 #include "goby/acomms/dccl.h"
35 #include "goby/acomms/modem_driver.h"
36 #include "goby/acomms/queue.h"
37 #include "goby/common/time.h"
38 #include "goby/util/as.h"
39 
40 #include <boost/lexical_cast.hpp>
41 
42 #include "chat_curses.h"
43 
44 // Uncomment to use the Micro-Modem 2 Flexible Data Packet instead of the traditional $CCCYC data cycle
45 //#define USE_FLEXIBLE_DATA_PACKET
46 // Uncomment to use the Micro-Modem ping $CCMPC instead of sending data
47 //#define USE_TWO_WAY_PING
48 
50 using goby::util::as;
51 
52 int startup_failure();
53 void received_data(const google::protobuf::Message&);
54 void received_ack(const goby::acomms::protobuf::ModemTransmission&,
56 void monitor_mac(const goby::acomms::protobuf::ModemTransmission& mac_msg);
57 void monitor_modem_receive(const goby::acomms::protobuf::ModemTransmission& rx_msg);
58 
59 std::ofstream fout_;
62 goby::acomms::MMDriver mm_driver_;
64 ChatCurses curses_;
65 int my_id_;
66 int buddy_id_;
67 
68 int main(int argc, char* argv[])
69 {
70  //
71  // Deal with command line parameters
72  //
73 
74  if (argc != 5)
75  return startup_failure();
76 
77  std::string serial_port = argv[1];
78 
79  try
80  {
81  my_id_ = boost::lexical_cast<int>(argv[2]);
82  buddy_id_ = boost::lexical_cast<int>(argv[3]);
83  }
84  catch (boost::bad_lexical_cast&)
85  {
86  std::cerr << "bad value for my_id: " << argv[2] << " or buddy_id: " << argv[3]
87  << ". these must be unsigned integers." << std::endl;
88  return startup_failure();
89  }
90 
91  std::string log_file = argv[4];
92  fout_.open(log_file.c_str());
93  if (!fout_.is_open())
94  {
95  std::cerr << "bad value for log_file: " << log_file << std::endl;
96  return startup_failure();
97  }
98 
99  //
100  // Initialize logging
101  //
102  goby::glog.add_stream(goby::common::logger::DEBUG1, &fout_);
103  goby::glog.set_name(argv[0]);
104 
105  // bind the signals of these libraries
106  bind(mm_driver_, q_manager_, mac_);
107 
108  //
109  // Initiate DCCL (libdccl)
110  //
112 
113  dccl_->validate<ChatMessage>();
114 
115  //
116  // Initiate queue manager (libqueue)
117  //
119  q_manager_cfg.set_modem_id(my_id_);
120  goby::acomms::protobuf::QueuedMessageEntry* q_entry = q_manager_cfg.add_message_entry();
121  q_entry->set_protobuf_name("ChatMessage");
122 
123 #ifdef USE_FLEXIBLE_DATA_PACKET
124  q_entry->set_ack(false);
125 #endif
126 
127  goby::acomms::protobuf::QueuedMessageEntry::Role* src_role = q_entry->add_role();
128  src_role->set_type(goby::acomms::protobuf::QueuedMessageEntry::SOURCE_ID);
129  src_role->set_field("source");
130 
131  goby::acomms::protobuf::QueuedMessageEntry::Role* dest_role = q_entry->add_role();
132  dest_role->set_type(goby::acomms::protobuf::QueuedMessageEntry::DESTINATION_ID);
133  dest_role->set_field("destination");
134 
135  goby::acomms::connect(&q_manager_.signal_receive, &received_data);
136  goby::acomms::connect(&q_manager_.signal_ack, &received_ack);
137 
138  //
139  // Initiate modem driver (libmodemdriver)
140  //
142  driver_cfg.set_modem_id(my_id_);
143  driver_cfg.set_serial_port(serial_port);
144 
145 #ifdef USE_FLEXIBLE_DATA_PACKET
146  driver_cfg.AddExtension(micromodem::protobuf::Config::nvram_cfg,
147  "psk.packet.mod_hdr_version,1");
148 #endif
149 
150 #ifdef USE_TWO_WAY_PING
151  goby::acomms::connect(&mm_driver_.signal_receive, &monitor_modem_receive);
152 #endif
153 
154  //
155  // Initiate medium access control (libamac)
156  //
158  mac_cfg.set_type(goby::acomms::protobuf::MAC_FIXED_DECENTRALIZED);
159  mac_cfg.set_modem_id(my_id_);
161 
163  my_slot.set_src(my_id_);
164  my_slot.set_dest(buddy_id_);
165 #ifdef USE_FLEXIBLE_DATA_PACKET
166  my_slot.set_type(goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC);
167  my_slot.SetExtension(micromodem::protobuf::type,
168  micromodem::protobuf::MICROMODEM_FLEXIBLE_DATA);
169  my_slot.set_max_frame_bytes(32);
170  my_slot.set_rate(1);
171 #elif defined(USE_TWO_WAY_PING)
172  my_slot.set_type(goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC);
173  my_slot.SetExtension(micromodem::protobuf::type, micromodem::protobuf::MICROMODEM_TWO_WAY_PING);
174 #else
175  my_slot.set_type(goby::acomms::protobuf::ModemTransmission::DATA);
176  my_slot.set_rate(0);
177 #endif
178  my_slot.set_slot_seconds(12);
179 
181  buddy_slot.set_src(buddy_id_);
182  buddy_slot.set_dest(my_id_);
183  buddy_slot.set_slot_seconds(12);
184 #ifdef USE_FLEXIBLE_DATA_PACKET
185  buddy_slot.set_type(goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC);
186  buddy_slot.SetExtension(micromodem::protobuf::type,
187  micromodem::protobuf::MICROMODEM_FLEXIBLE_DATA);
188  buddy_slot.set_max_frame_bytes(32);
189  buddy_slot.set_rate(1);
190 #elif defined(USE_TWO_WAY_PING)
191  buddy_slot.set_type(goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC);
192  buddy_slot.SetExtension(micromodem::protobuf::type,
193  micromodem::protobuf::MICROMODEM_TWO_WAY_PING);
194 #else
195  buddy_slot.set_type(goby::acomms::protobuf::ModemTransmission::DATA);
196  buddy_slot.set_rate(0);
197 #endif
198 
199  if (my_id_ < buddy_id_)
200  {
201  mac_cfg.add_slot()->CopyFrom(my_slot);
202  mac_cfg.add_slot()->CopyFrom(buddy_slot);
203  }
204  else
205  {
206  mac_cfg.add_slot()->CopyFrom(buddy_slot);
207  mac_cfg.add_slot()->CopyFrom(my_slot);
208  }
209 
210  //
211  // Start up everything
212  //
213  try
214  {
215  dccl_->set_cfg(dccl_cfg);
216  q_manager_.set_cfg(q_manager_cfg);
217  mac_.startup(mac_cfg);
218  mm_driver_.startup(driver_cfg);
219  }
220  catch (std::runtime_error& e)
221  {
222  std::cerr << "exception at startup: " << e.what() << std::endl;
223  return startup_failure();
224  }
225 
226  curses_.set_modem_id(my_id_);
227  curses_.startup();
228 
229  //
230  // Loop until terminated (CTRL-C)
231  //
232  for (;;)
233  {
234  std::string line;
235  curses_.run_input(line);
236 
237  if (!line.empty())
238  {
239  ChatMessage message_out;
240  message_out.set_telegram(line);
241 
242  // send this message to my buddy!
243  message_out.set_destination(buddy_id_);
244  message_out.set_source(my_id_);
245 
246  q_manager_.push_message(message_out);
247  }
248 
249  try
250  {
251  mm_driver_.do_work();
252  mac_.do_work();
253  q_manager_.do_work();
254  }
255  catch (std::runtime_error& e)
256  {
257  curses_.cleanup();
258  std::cerr << "exception while running: " << e.what() << std::endl;
259  return 1;
260  }
261  }
262 
263  return 0;
264 }
265 
266 int startup_failure()
267 {
268  std::cerr << "usage: chat /dev/tty_modem my_id buddy_id log_file" << std::endl;
269  return 1;
270 }
271 
272 void monitor_mac(const goby::acomms::protobuf::ModemTransmission& mac_msg)
273 {
274  if (mac_msg.src() == my_id_)
275  curses_.post_message("{control} starting send to my buddy");
276  else if (mac_msg.src() == buddy_id_)
277  curses_.post_message("{control} my buddy might be sending to me now");
278 }
279 
280 void monitor_modem_receive(const goby::acomms::protobuf::ModemTransmission& rx_msg)
281 {
282  if (rx_msg.GetExtension(micromodem::protobuf::type) ==
283  micromodem::protobuf::MICROMODEM_TWO_WAY_PING &&
284  rx_msg.HasExtension(micromodem::protobuf::ranging_reply))
285  {
286  const micromodem::protobuf::RangingReply& range_reply =
287  rx_msg.GetExtension(micromodem::protobuf::ranging_reply);
288  if (range_reply.one_way_travel_time_size() > 0)
289  {
290  double owtt = range_reply.one_way_travel_time(0);
291  curses_.post_message(range_reply.ShortDebugString());
292  }
293  }
294 }
295 
296 void received_data(const google::protobuf::Message& message_in)
297 {
298  ChatMessage typed_message_in;
299  typed_message_in.CopyFrom(message_in);
300  curses_.post_message(typed_message_in.source(), typed_message_in.telegram());
301 }
302 
303 void received_ack(const goby::acomms::protobuf::ModemTransmission& ack_message,
304  const google::protobuf::Message& original_message)
305 {
306  ChatMessage typed_original_message;
307  typed_original_message.CopyFrom(original_message);
308 
309  curses_.post_message(ack_message.src(),
310  std::string("{ acknowledged receiving message starting with: " +
311  typed_original_message.telegram().substr(0, 5) + " }"));
312 }
provides an API to the goby-acomms Queuing Library.
Definition: queue_manager.h:49
boost::signals2::signal< void(const protobuf::ModemTransmission &ack_msg, const google::protobuf::Message &orig_msg)> signal_ack
Signals when acknowledgment of proper message receipt has been received. This is only sent for queues...
void set_name(const std::string &s)
Set the name of the application that the logger is serving.
Definition: flex_ostream.h:67
void run_input(std::string &line)
grab a character and if there&#39;s a line to return it will be returned in line
Definition: chat_curses.cpp:65
static DCCLCodec * get()
DCCLCodec is a singleton class; use this to get a pointer to the class.
Definition: dccl.h:124
provides a terminal GUI for a chat window (lower box to type and upper box to receive messages)...
Definition: chat_curses.h:29
void startup(const protobuf::MACConfig &cfg)
Starts the MAC with given configuration.
Definition: mac_manager.cpp:65
boost::signals2::signal< void(const google::protobuf::Message &msg)> signal_receive
Signals when a DCCL message is received.
void startup()
start the display
Definition: chat_curses.cpp:30
void do_work()
Allows the MAC timer to do its work. Does not block. If you prefer more control you can directly cont...
Definition: mac_manager.h:76
provides an API to the WHOI Micro-Modem driver
Definition: mm_driver.h:41
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
Definition: time.h:104
boost::signals2::signal< void(const protobuf::ModemTransmission &message)> signal_receive
Called when a binary data transmission is received from the modem.
Definition: driver_base.h:85
void post_message(unsigned id, const std::string &line)
add a message to the upper window (the chat log)
Definition: chat_curses.cpp:98
void do_work()
See ModemDriverBase::do_work()
Definition: mm_driver.cpp:463
provides an API to the goby-acomms MAC library. MACManager is essentially a std::list<protobuf::Modem...
Definition: mac_manager.h:51
void connect(Signal *signal, Slot slot)
connect a signal to a slot (e.g. function pointer)
Definition: connect.h:36
common::FlexOstream glog
Access the Goby logger through this object.
void set_cfg(const protobuf::QueueManagerConfig &cfg)
Set (and overwrite completely if present) the current configuration. (protobuf::QueueManagerConfig de...
void startup(const protobuf::DriverConfig &cfg)
Starts the driver.
Definition: mm_driver.cpp:76
void do_work()
Calculates which messages have expired and emits the goby::acomms::QueueManager::signal_expire as nec...
void cleanup()
end the display
Definition: chat_curses.cpp:96
boost::signals2::signal< void(const protobuf::ModemTransmission &m)> signal_initiate_transmission
Signals when it is time for this platform to begin transmission of an acoustic message at the start o...
Definition: mac_manager.h:91
void add_stream(logger::Verbosity verbosity=logger::VERBOSE, std::ostream *os=0)
Attach a stream object (e.g. std::cout, std::ofstream, ...) to the logger with desired verbosity...
Definition: flex_ostream.h:96
void bind(ModemDriverBase &driver, QueueManager &queue_manager)
binds the driver link-layer callbacks to the QueueManager
Definition: bind.h:43
void push_message(const google::protobuf::Message &new_message)
Push a message (and add the queue if it does not exist)
void set_modem_id(unsigned id)
give the modem_id so we know how to label our messages
Definition: chat_curses.h:39