Goby v2
abc_modem_simulator.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 //
23 // Usage:
24 // 1. run abc_modem_simulator running on same port (as TCP server)
25 // > abc_modem_simulator 54321
26 // 2. create fake tty terminals connected to TCP as client to port 54321
27 // > socat -d -d -v pty,raw,echo=0,link=/tmp/ttyFAKE1 TCP:localhost:54321
28 // > socat -d -d -v pty,raw,echo=0,link=/tmp/ttyFAKE2 TCP:localhost:54321
29 // > ...
30 // 3. run your application connecting to /tmp/ttyFAKE1, /tmp/ttyFAKE2, etc. They will all act in the same "broadcast" pool
31 
32 #include <map>
33 #include <string>
34 
35 #include "goby/acomms/acomms_constants.h" // for BROADCAST_ID
36 #include "goby/util/as.h"
37 #include "goby/util/linebasedcomms.h"
38 
39 std::map<int, std::string> modem_id2endpoint;
40 
41 void parse_in(const std::string& in, std::map<std::string, std::string>* out);
42 
43 int main(int argc, char* argv[])
44 {
45  if (argc < 2)
46  {
47  std::cout << "usage: abc_modem_simulator [tcp listen port]" << std::endl;
48  exit(1);
49  }
50 
51  goby::util::TCPServer server(goby::util::as<unsigned>(argv[1]));
52 
53  server.start();
54  sleep(1);
55 
56  while (server.active())
57  {
59  while (server.readline(&in))
60  {
61  // clear off \r\n and other whitespace at ends
62  boost::trim(*in.mutable_data());
63 
64  std::cout << "Received: " << in.ShortDebugString() << std::endl;
65 
66  std::map<std::string, std::string> parsed;
67  try
68  {
69  parse_in(in.data(), &parsed);
70  if (parsed["KEY"] == "CONF")
71  {
72  std::cout << "Got configuration: " << in.data() << std::endl;
73 
74  // register a modem id
75  if (parsed.count("MAC"))
76  {
77  int mac = goby::util::as<int>(parsed["MAC"]);
78  std::cout << "Set MAC address " << mac << " for endpoint " << in.src()
79  << std::endl;
80  modem_id2endpoint[mac] = in.src();
81  }
82  }
83  else if (parsed["KEY"] == "SEND")
84  {
85  std::cout << "Got send: " << in.data() << std::endl;
86 
88  out.set_src(server.local_endpoint());
89 
90  if (!parsed.count("HEX"))
91  throw(std::runtime_error("No DATA in SEND message"));
92 
93  if (!parsed.count("FROM"))
94  throw(std::runtime_error("No FROM in SEND message"));
95 
96  if (!parsed.count("BITRATE"))
97  throw(std::runtime_error("No BITRATE in SEND message"));
98 
99  int src = goby::util::as<int>(parsed["FROM"]);
100 
101  if (parsed.count("TO"))
102  {
103  int dest = goby::util::as<int>(parsed["TO"]);
104 
105  std::stringstream out_ss;
106  out_ss << "RECV,FROM:" << src << ",TO:" << dest << ",HEX:" << parsed["HEX"]
107  << ",BITRATE:" << parsed["BITRATE"] << "\r\n";
108  out.set_data(out_ss.str());
109 
110  if (dest == goby::acomms::BROADCAST_ID)
111  {
112  typedef std::map<int, std::string>::const_iterator const_iterator;
113  for (const_iterator it = modem_id2endpoint.begin(),
114  n = modem_id2endpoint.end();
115  it != n; ++it)
116  {
117  // do not send it back to the originator
118  if (it->first != src)
119  {
120  out.set_dest(it->second);
121  std::cout << "Sending: " << out.ShortDebugString() << std::endl;
122  server.write(out);
123  }
124  }
125  }
126  else
127  {
128  if (!modem_id2endpoint.count(dest))
129  throw(std::runtime_error("Unknown destination ID " +
130  goby::util::as<std::string>(dest)));
131 
132  out.set_dest(modem_id2endpoint[dest]);
133  std::cout << "Sending: " << out.ShortDebugString() << std::endl;
134  server.write(out);
135 
136  if (parsed.count("ACK") && goby::util::as<bool>(parsed["ACK"]))
137  {
138  out.set_dest(in.src());
139 
140  std::stringstream out_ss;
141  out_ss << "ACKN,FROM:" << dest << ",TO:" << src << "\r\n";
142  out.set_data(out_ss.str());
143  std::cout << "Sending: " << out.ShortDebugString() << std::endl;
144 
145  server.write(out);
146  }
147  }
148  }
149  else
150  throw(std::runtime_error("No TO in SEND message"));
151  }
152  }
153  catch (std::exception& e)
154  {
155  std::cout << "Invalid line from modem: " << in.data() << std::endl;
156  std::cout << "Why: " << e.what() << std::endl;
157  }
158  }
159 
160  usleep(1000);
161  }
162 
163  std::cout << "server failed..." << std::endl;
164  exit(1);
165 }
166 
167 void parse_in(const std::string& in, std::map<std::string, std::string>* out)
168 {
169  std::vector<std::string> comma_split;
170  boost::split(comma_split, in, boost::is_any_of(","));
171  out->insert(std::make_pair("KEY", comma_split.at(0)));
172  for (int i = 1, n = comma_split.size(); i < n; ++i)
173  {
174  std::vector<std::string> colon_split;
175  boost::split(colon_split, comma_split[i], boost::is_any_of(":"));
176  out->insert(std::make_pair(colon_split.at(0), colon_split.at(1)));
177  }
178 }
provides a basic TCP server for line by line text based communications to a one or more remote TCP cl...
Definition: tcp_server.h:49
const int BROADCAST_ID
special modem id for the broadcast destination - no one is assigned this address. Analogous to 192...