/* * Copyright (C) 2014 Pavel Kirienko */ #pragma once #include #include #include #include #include #include #include namespace uavcan_linux { /** * Default log sink will dump everything into stderr */ class DefaultLogSink : public uavcan::ILogSink { virtual void log(const uavcan::protocol::debug::LogMessage& message) { const auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); const auto tstr = std::ctime(&tt); std::cerr << "### UAVCAN " << tstr << message << std::endl; } }; /** * Wrapper over uavcan::ServiceClient<> for blocking calls. * Calls spin() internally. */ template class BlockingServiceClient : public uavcan::ServiceClient { typedef uavcan::ServiceClient Super; typename DataType::Response response_; bool call_was_successful_; void callback(const uavcan::ServiceCallResult& res) { response_ = res.response; call_was_successful_ = res.isSuccessful(); } void setup() { Super::setCallback(std::bind(&BlockingServiceClient::callback, this, std::placeholders::_1)); call_was_successful_ = false; response_ = typename DataType::Response(); } public: BlockingServiceClient(uavcan::INode& node) : uavcan::ServiceClient(node) , call_was_successful_(false) { setup(); } int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request) { const auto SpinDuration = uavcan::MonotonicDuration::fromMSec(2); setup(); const int call_res = Super::call(server_node_id, request); if (call_res >= 0) { while (Super::isPending()) { const int spin_res = Super::getNode().spin(SpinDuration); if (spin_res < 0) { return spin_res; } } } return call_res; } int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request, uavcan::MonotonicDuration timeout) { Super::setRequestTimeout(timeout); return blockingCall(server_node_id, request); } bool wasSuccessful() const { return call_was_successful_; } const typename DataType::Response& getResponse() const { return response_; } }; /** * Contains all drivers needed for uavcan::Node. */ struct DriverPack { SystemClock clock; SocketCanDriver can; explicit DriverPack(ClockAdjustmentMode clock_adjustment_mode) : clock(clock_adjustment_mode) , can(clock) { } }; typedef std::shared_ptr DriverPackPtr; typedef std::shared_ptr TimerPtr; static constexpr std::size_t NodeMemPoolSize = 1024 * 512; // One size fits all /** * Wrapper for uavcan::Node with some additional convenience functions. */ class Node : public uavcan::Node { DriverPackPtr driver_pack_; DefaultLogSink log_sink_; static void enforce(int error, const std::string& msg) { if (error < 0) { std::ostringstream os; os << msg << " [" << error << "]"; throw Exception(os.str()); } } template static std::string getDataTypeName() { return DataType::getDataTypeFullName(); } public: /** * Simple forwarding constructor, compatible with uavcan::Node */ Node(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) : uavcan::Node(can_driver, clock) { getLogger().setExternalSink(&log_sink_); } /** * Takes ownership of the driver container. */ explicit Node(DriverPackPtr driver_pack) : uavcan::Node(driver_pack->can, driver_pack->clock) , driver_pack_(driver_pack) { getLogger().setExternalSink(&log_sink_); } template std::shared_ptr> makeSubscriber(const typename uavcan::Subscriber::Callback& cb) { std::shared_ptr> p(new uavcan::Subscriber(*this)); enforce(p->start(cb), "Subscriber start failure " + getDataTypeName()); return p; } template std::shared_ptr> makePublisher(uavcan::MonotonicDuration tx_timeout = uavcan::Publisher::getDefaultTxTimeout()) { std::shared_ptr> p(new uavcan::Publisher(*this)); enforce(p->init(), "Publisher init failure " + getDataTypeName()); p->setTxTimeout(tx_timeout); return p; } template std::shared_ptr> makeServiceServer(const typename uavcan::ServiceServer::Callback& cb) { std::shared_ptr> p(new uavcan::ServiceServer(*this)); enforce(p->start(cb), "ServiceServer start failure " + getDataTypeName()); return p; } template std::shared_ptr> makeServiceClient(const typename uavcan::ServiceClient::Callback& cb) { std::shared_ptr> p(new uavcan::ServiceClient(*this)); enforce(p->init(), "ServiceClient init failure " + getDataTypeName()); p->setCallback(cb); return p; } template std::shared_ptr> makeBlockingServiceClient() { std::shared_ptr> p(new BlockingServiceClient(*this)); enforce(p->init(), "BlockingServiceClient init failure " + getDataTypeName()); return p; } TimerPtr makeTimer(uavcan::MonotonicTime deadline, const typename uavcan::Timer::Callback& cb) { TimerPtr p(new uavcan::Timer(*this)); p->setCallback(cb); p->startOneShotWithDeadline(deadline); return p; } TimerPtr makeTimer(uavcan::MonotonicDuration period, const typename uavcan::Timer::Callback& cb) { TimerPtr p(new uavcan::Timer(*this)); p->setCallback(cb); p->startPeriodic(period); return p; } const DriverPackPtr& getDriverPack() const { return driver_pack_; } DriverPackPtr& getDriverPack() { return driver_pack_; } }; typedef std::shared_ptr NodePtr; /** * Constructs Node with explicitly specified ClockAdjustmentMode. */ static inline NodePtr makeNode(const std::vector& iface_names, ClockAdjustmentMode clock_adjustment_mode) { DriverPackPtr dp(new DriverPack(clock_adjustment_mode)); for (auto ifn : iface_names) { if (dp->can.addIface(ifn) < 0) { throw Exception("Failed to add iface " + ifn); } } return NodePtr(new Node(dp)); } /** * This is the preferred way to make Node. */ static inline NodePtr makeNode(const std::vector& iface_names) { return makeNode(iface_names, SystemClock::detectPreferredClockAdjustmentMode()); } }