Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Committer:
RuslanUrya
Date:
Sat Apr 14 10:25:32 2018 +0000
Revision:
0:dfe6edabb8ec
Initial commit

Who changed what in which revision?

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