uavcan_nodetool.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
  3. */
  4. #include <iostream>
  5. #include <iomanip>
  6. #include <thread>
  7. #include <mutex>
  8. #include <map>
  9. #include <algorithm>
  10. #include <iterator>
  11. #include <uavcan_linux/uavcan_linux.hpp>
  12. #include <uavcan/protocol/node_status_monitor.hpp>
  13. #include "debug.hpp"
  14. #include <uavcan/protocol/param/GetSet.hpp>
  15. #include <uavcan/protocol/param/ExecuteOpcode.hpp>
  16. #include <uavcan/equipment/hardpoint/Command.hpp>
  17. namespace
  18. {
  19. class StdinLineReader
  20. {
  21. mutable std::mutex mutex_;
  22. std::thread thread_;
  23. std::queue<std::string> queue_;
  24. void worker()
  25. {
  26. while (true)
  27. {
  28. std::string input;
  29. std::getline(std::cin, input);
  30. std::lock_guard<std::mutex> lock(mutex_);
  31. queue_.push(input);
  32. }
  33. }
  34. public:
  35. StdinLineReader()
  36. : thread_(&StdinLineReader::worker, this)
  37. {
  38. thread_.detach();
  39. }
  40. bool hasPendingInput() const
  41. {
  42. std::lock_guard<std::mutex> lock(mutex_);
  43. return !queue_.empty();
  44. }
  45. std::string getLine()
  46. {
  47. std::lock_guard<std::mutex> lock(mutex_);
  48. if (queue_.empty())
  49. {
  50. throw std::runtime_error("Input queue is empty");
  51. }
  52. auto ret = queue_.front();
  53. queue_.pop();
  54. return ret;
  55. }
  56. std::vector<std::string> getSplitLine()
  57. {
  58. std::istringstream iss(getLine());
  59. std::vector<std::string> out;
  60. std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(),
  61. std::back_inserter(out));
  62. return out;
  63. }
  64. };
  65. uavcan_linux::NodePtr initNode(const std::vector<std::string>& ifaces, uavcan::NodeID nid, const std::string& name)
  66. {
  67. auto node = uavcan_linux::makeNode(ifaces, name.c_str(),
  68. uavcan::protocol::SoftwareVersion(), uavcan::protocol::HardwareVersion(),
  69. nid);
  70. node->setModeOperational();
  71. return node;
  72. }
  73. template <typename DataType>
  74. typename DataType::Response call(uavcan_linux::BlockingServiceClient<DataType>& client,
  75. uavcan::NodeID server_node_id, const typename DataType::Request& request)
  76. {
  77. const int res = client.blockingCall(server_node_id, request, uavcan::MonotonicDuration::fromMSec(100));
  78. ENFORCE(res >= 0);
  79. ENFORCE(client.wasSuccessful());
  80. return client.getResponse();
  81. }
  82. /*
  83. * Command table.
  84. * The structure is:
  85. * command_name : (command_usage_info, command_entry_point)
  86. * This code was written while listening to some bad dubstep so I'm not sure about its quality.
  87. */
  88. const std::map<std::string,
  89. std::pair<std::string,
  90. std::function<void(const uavcan_linux::NodePtr&, const uavcan::NodeID,
  91. const std::vector<std::string>&)>
  92. >
  93. > commands =
  94. {
  95. {
  96. "param",
  97. {
  98. "No arguments supplied - requests all params from a remote node\n"
  99. "<param_name> <param_value> - assigns parameter <param_name> to value <param_value>",
  100. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>& args)
  101. {
  102. auto client = node->makeBlockingServiceClient<uavcan::protocol::param::GetSet>();
  103. uavcan::protocol::param::GetSet::Request request;
  104. if (args.empty())
  105. {
  106. while (true)
  107. {
  108. auto response = call(*client, node_id, request);
  109. if (response.name.empty())
  110. {
  111. break;
  112. }
  113. std::cout
  114. << response
  115. << "\n" << std::string(80, '-')
  116. << std::endl;
  117. request.index++;
  118. }
  119. }
  120. else
  121. {
  122. request.name = args.at(0).c_str();
  123. // TODO: add support for string parameters
  124. request.value.to<uavcan::protocol::param::Value::Tag::real_value>() = std::stof(args.at(1));
  125. std::cout << call(*client, node_id, request) << std::endl;
  126. }
  127. }
  128. }
  129. },
  130. {
  131. "param_save",
  132. {
  133. "Calls uavcan.protocol.param.ExecuteOpcode on a remote node with OPCODE_SAVE",
  134. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>&)
  135. {
  136. auto client = node->makeBlockingServiceClient<uavcan::protocol::param::ExecuteOpcode>();
  137. uavcan::protocol::param::ExecuteOpcode::Request request;
  138. request.opcode = request.OPCODE_SAVE;
  139. std::cout << call(*client, node_id, request) << std::endl;
  140. }
  141. }
  142. },
  143. {
  144. "param_erase",
  145. {
  146. "Calls uavcan.protocol.param.ExecuteOpcode on a remote node with OPCODE_ERASE",
  147. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>&)
  148. {
  149. auto client = node->makeBlockingServiceClient<uavcan::protocol::param::ExecuteOpcode>();
  150. uavcan::protocol::param::ExecuteOpcode::Request request;
  151. request.opcode = request.OPCODE_ERASE;
  152. std::cout << call(*client, node_id, request) << std::endl;
  153. }
  154. }
  155. },
  156. {
  157. "restart",
  158. {
  159. "Restarts a remote node using uavcan.protocol.RestartNode",
  160. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>&)
  161. {
  162. auto client = node->makeBlockingServiceClient<uavcan::protocol::RestartNode>();
  163. uavcan::protocol::RestartNode::Request request;
  164. request.magic_number = request.MAGIC_NUMBER;
  165. (void)client->blockingCall(node_id, request);
  166. if (client->wasSuccessful())
  167. {
  168. std::cout << client->getResponse() << std::endl;
  169. }
  170. else
  171. {
  172. std::cout << "<NO RESPONSE>" << std::endl;
  173. }
  174. }
  175. }
  176. },
  177. {
  178. "info",
  179. {
  180. "Calls uavcan.protocol.GetNodeInfo on a remote node",
  181. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>&)
  182. {
  183. auto client = node->makeBlockingServiceClient<uavcan::protocol::GetNodeInfo>();
  184. std::cout << call(*client, node_id, uavcan::protocol::GetNodeInfo::Request()) << std::endl;
  185. }
  186. }
  187. },
  188. {
  189. "transport_stats",
  190. {
  191. "Calls uavcan.protocol.GetTransportStats on a remote node",
  192. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID node_id, const std::vector<std::string>&)
  193. {
  194. auto client = node->makeBlockingServiceClient<uavcan::protocol::GetTransportStats>();
  195. std::cout << call(*client, node_id, uavcan::protocol::GetTransportStats::Request()) << std::endl;
  196. }
  197. }
  198. },
  199. {
  200. "hardpoint",
  201. {
  202. "Publishes uavcan.equipment.hardpoint.Command\n"
  203. "Expected argument: command",
  204. [](const uavcan_linux::NodePtr& node, const uavcan::NodeID, const std::vector<std::string>& args)
  205. {
  206. uavcan::equipment::hardpoint::Command msg;
  207. msg.command = std::stoi(args.at(0));
  208. auto pub = node->makePublisher<uavcan::equipment::hardpoint::Command>();
  209. (void)pub->broadcast(msg);
  210. }
  211. }
  212. }
  213. };
  214. void runForever(const uavcan_linux::NodePtr& node)
  215. {
  216. StdinLineReader stdin_reader;
  217. std::cout << "> " << std::flush;
  218. while (true)
  219. {
  220. ENFORCE(node->spin(uavcan::MonotonicDuration::fromMSec(10)) >= 0);
  221. if (!stdin_reader.hasPendingInput())
  222. {
  223. continue;
  224. }
  225. const auto words = stdin_reader.getSplitLine();
  226. bool command_is_known = false;
  227. try
  228. {
  229. if (words.size() >= 2)
  230. {
  231. const auto cmd = words.at(0);
  232. const uavcan::NodeID node_id(std::stoi(words.at(1)));
  233. auto it = commands.find(cmd);
  234. if (it != std::end(commands))
  235. {
  236. command_is_known = true;
  237. it->second.second(node, node_id, std::vector<std::string>(words.begin() + 2, words.end()));
  238. }
  239. }
  240. }
  241. catch (std::exception& ex)
  242. {
  243. std::cout << "FAILURE\n" << ex.what() << std::endl;
  244. }
  245. if (!command_is_known)
  246. {
  247. std::cout << "<command> <remote node id> [args...]\n";
  248. std::cout << "Say 'help' to get help.\n"; // I'll show myself out.
  249. if (!words.empty() && words.at(0) == "help")
  250. {
  251. std::cout << "Usage:\n\n";
  252. for (auto& cmd : commands)
  253. {
  254. std::cout << cmd.first << "\n" << cmd.second.first << "\n\n";
  255. }
  256. }
  257. }
  258. std::cout << "> " << std::flush;
  259. }
  260. }
  261. }
  262. int main(int argc, const char** argv)
  263. {
  264. try
  265. {
  266. if (argc < 3)
  267. {
  268. std::cerr << "Usage:\n\t" << argv[0] << " <node-id> <can-iface-name-1> [can-iface-name-N...]" << std::endl;
  269. return 1;
  270. }
  271. const int self_node_id = std::stoi(argv[1]);
  272. const std::vector<std::string> iface_names(argv + 2, argv + argc);
  273. uavcan_linux::NodePtr node = initNode(iface_names, self_node_id, "org.uavcan.linux_app.nodetool");
  274. runForever(node);
  275. return 0;
  276. }
  277. catch (const std::exception& ex)
  278. {
  279. std::cerr << "Error: " << ex.what() << std::endl;
  280. return 1;
  281. }
  282. }