123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- /*
- * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
- */
- #pragma once
- #include <memory>
- #include <string>
- #include <vector>
- #include <chrono>
- #include <iostream>
- #include <sstream>
- #include <uavcan/uavcan.hpp>
- #include <uavcan/node/sub_node.hpp>
- namespace uavcan_linux
- {
- /**
- * Default log sink will dump everything into stderr.
- * It is installed by default.
- */
- class DefaultLogSink : public uavcan::ILogSink
- {
- void log(const uavcan::protocol::debug::LogMessage& message) override
- {
- 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.
- * Blocks on uavcan::Node::spin() internally until the call is complete.
- */
- template <typename DataType>
- class BlockingServiceClient : public uavcan::ServiceClient<DataType>
- {
- typedef uavcan::ServiceClient<DataType> Super;
- typename DataType::Response response_;
- bool call_was_successful_;
- void callback(const uavcan::ServiceCallResult<DataType>& res)
- {
- response_ = res.getResponse();
- 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<DataType>(node)
- , call_was_successful_(false)
- {
- setup();
- }
- /**
- * Performs a blocking service call using default timeout (see the specs).
- * Use @ref getResponse() to get the actual response.
- * Returns negative error code.
- */
- int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request)
- {
- return blockingCall(server_node_id, request, Super::getDefaultRequestTimeout());
- }
- /**
- * Performs a blocking service call using the specified timeout. Please consider using default timeout instead.
- * Use @ref getResponse() to get the actual response.
- * Returns negative error code.
- */
- int blockingCall(uavcan::NodeID server_node_id, const typename DataType::Request& request,
- uavcan::MonotonicDuration timeout)
- {
- const auto SpinDuration = uavcan::MonotonicDuration::fromMSec(2);
- setup();
- Super::setRequestTimeout(timeout);
- const int call_res = Super::call(server_node_id, request);
- if (call_res >= 0)
- {
- while (Super::hasPendingCalls())
- {
- const int spin_res = Super::getNode().spin(SpinDuration);
- if (spin_res < 0)
- {
- return spin_res;
- }
- }
- }
- return call_res;
- }
- /**
- * Whether the last blocking call was successful.
- */
- bool wasSuccessful() const { return call_was_successful_; }
- /**
- * Use this to retrieve the response of the last blocking service call.
- * This method returns default constructed response object if the last service call was unsuccessful.
- */
- const typename DataType::Response& getResponse() const { return response_; }
- };
- /**
- * Contains all drivers needed for uavcan::Node.
- */
- struct DriverPack
- {
- SystemClock clock;
- std::shared_ptr<uavcan::ICanDriver> can;
- explicit DriverPack(ClockAdjustmentMode clock_adjustment_mode,
- const std::shared_ptr<uavcan::ICanDriver>& can_driver)
- : clock(clock_adjustment_mode)
- , can(can_driver)
- { }
- explicit DriverPack(ClockAdjustmentMode clock_adjustment_mode,
- const std::vector<std::string>& iface_names)
- : clock(clock_adjustment_mode)
- {
- std::shared_ptr<SocketCanDriver> socketcan(new SocketCanDriver(clock));
- can = socketcan;
- for (auto ifn : iface_names)
- {
- if (socketcan->addIface(ifn) < 0)
- {
- throw Exception("Failed to add iface " + ifn);
- }
- }
- }
- };
- typedef std::shared_ptr<DriverPack> DriverPackPtr;
- typedef std::shared_ptr<uavcan::INode> INodePtr;
- typedef std::shared_ptr<uavcan::Timer> TimerPtr;
- template <typename T>
- using SubscriberPtr = std::shared_ptr<uavcan::Subscriber<T>>;
- template <typename T>
- using PublisherPtr = std::shared_ptr<uavcan::Publisher<T>>;
- template <typename T>
- using ServiceServerPtr = std::shared_ptr<uavcan::ServiceServer<T>>;
- template <typename T>
- using ServiceClientPtr = std::shared_ptr<uavcan::ServiceClient<T>>;
- template <typename T>
- using BlockingServiceClientPtr = std::shared_ptr<BlockingServiceClient<T>>;
- static constexpr std::size_t NodeMemPoolSize = 1024 * 512; ///< This shall be enough for any possible use case
- /**
- * Generic wrapper for node objects with some additional convenience functions.
- */
- template <typename NodeType>
- class NodeBase : public NodeType
- {
- protected:
- DriverPackPtr driver_pack_;
- static void enforce(int error, const std::string& msg)
- {
- if (error < 0)
- {
- std::ostringstream os;
- os << msg << " [" << error << "]";
- throw Exception(os.str());
- }
- }
- template <typename DataType>
- static std::string getDataTypeName()
- {
- return DataType::getDataTypeFullName();
- }
- public:
- /**
- * Simple forwarding constructor, compatible with uavcan::Node.
- */
- NodeBase(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) :
- NodeType(can_driver, clock)
- { }
- /**
- * Takes ownership of the driver container via the shared pointer.
- */
- explicit NodeBase(DriverPackPtr driver_pack)
- : NodeType(*driver_pack->can, driver_pack->clock)
- , driver_pack_(driver_pack)
- { }
- /**
- * Allocates @ref uavcan::Subscriber in the heap using shared pointer.
- * The subscriber will be started immediately.
- * @throws uavcan_linux::Exception.
- */
- template <typename DataType>
- SubscriberPtr<DataType> makeSubscriber(const typename uavcan::Subscriber<DataType>::Callback& cb)
- {
- SubscriberPtr<DataType> p(new uavcan::Subscriber<DataType>(*this));
- enforce(p->start(cb), "Subscriber start failure " + getDataTypeName<DataType>());
- return p;
- }
- /**
- * Allocates @ref uavcan::Publisher in the heap using shared pointer.
- * The publisher will be initialized immediately.
- * @throws uavcan_linux::Exception.
- */
- template <typename DataType>
- PublisherPtr<DataType> makePublisher(uavcan::MonotonicDuration tx_timeout =
- uavcan::Publisher<DataType>::getDefaultTxTimeout())
- {
- PublisherPtr<DataType> p(new uavcan::Publisher<DataType>(*this));
- enforce(p->init(), "Publisher init failure " + getDataTypeName<DataType>());
- p->setTxTimeout(tx_timeout);
- return p;
- }
- /**
- * Allocates @ref uavcan::ServiceServer in the heap using shared pointer.
- * The server will be started immediately.
- * @throws uavcan_linux::Exception.
- */
- template <typename DataType>
- ServiceServerPtr<DataType> makeServiceServer(const typename uavcan::ServiceServer<DataType>::Callback& cb)
- {
- ServiceServerPtr<DataType> p(new uavcan::ServiceServer<DataType>(*this));
- enforce(p->start(cb), "ServiceServer start failure " + getDataTypeName<DataType>());
- return p;
- }
- /**
- * Allocates @ref uavcan::ServiceClient in the heap using shared pointer.
- * The service client will be initialized immediately.
- * @throws uavcan_linux::Exception.
- */
- template <typename DataType>
- ServiceClientPtr<DataType> makeServiceClient(const typename uavcan::ServiceClient<DataType>::Callback& cb)
- {
- ServiceClientPtr<DataType> p(new uavcan::ServiceClient<DataType>(*this));
- enforce(p->init(), "ServiceClient init failure " + getDataTypeName<DataType>());
- p->setCallback(cb);
- return p;
- }
- /**
- * Allocates @ref uavcan_linux::BlockingServiceClient in the heap using shared pointer.
- * The service client will be initialized immediately.
- * @throws uavcan_linux::Exception.
- */
- template <typename DataType>
- BlockingServiceClientPtr<DataType> makeBlockingServiceClient()
- {
- BlockingServiceClientPtr<DataType> p(new BlockingServiceClient<DataType>(*this));
- enforce(p->init(), "BlockingServiceClient init failure " + getDataTypeName<DataType>());
- return p;
- }
- /**
- * Allocates @ref uavcan::Timer in the heap using shared pointer.
- * The timer will be started immediately in one-shot mode.
- */
- 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;
- }
- /**
- * Allocates @ref uavcan::Timer in the heap using shared pointer.
- * The timer will be started immediately in periodic mode.
- */
- 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_; }
- };
- /**
- * Wrapper for uavcan::Node with some additional convenience functions.
- * Note that this wrapper adds stderr log sink to @ref uavcan::Logger, which can be removed if needed.
- * Do not instantiate this class directly; instead use the factory functions defined below.
- */
- class Node : public NodeBase<uavcan::Node<NodeMemPoolSize>>
- {
- typedef NodeBase<uavcan::Node<NodeMemPoolSize>> Base;
- DefaultLogSink log_sink_;
- public:
- /**
- * Simple forwarding constructor, compatible with uavcan::Node.
- */
- Node(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) :
- Base(can_driver, clock)
- {
- getLogger().setExternalSink(&log_sink_);
- }
- /**
- * Takes ownership of the driver container via the shared pointer.
- */
- explicit Node(DriverPackPtr driver_pack) :
- Base(driver_pack)
- {
- getLogger().setExternalSink(&log_sink_);
- }
- };
- /**
- * Wrapper for uavcan::SubNode with some additional convenience functions.
- * Do not instantiate this class directly; instead use the factory functions defined below.
- */
- class SubNode : public NodeBase<uavcan::SubNode<NodeMemPoolSize>>
- {
- typedef NodeBase<uavcan::SubNode<NodeMemPoolSize>> Base;
- public:
- /**
- * Simple forwarding constructor, compatible with uavcan::Node.
- */
- SubNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock) : Base(can_driver, clock) { }
- /**
- * Takes ownership of the driver container via the shared pointer.
- */
- explicit SubNode(DriverPackPtr driver_pack) : Base(driver_pack) { }
- };
- typedef std::shared_ptr<Node> NodePtr;
- typedef std::shared_ptr<SubNode> SubNodePtr;
- /**
- * Use this function to create a node instance with default SocketCAN driver.
- * It accepts the list of interface names to use for the new node, e.g. "can1", "vcan2", "slcan0".
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- * @throws uavcan_linux::Exception.
- */
- static inline NodePtr makeNode(const std::vector<std::string>& iface_names,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- DriverPackPtr dp(new DriverPack(clock_adjustment_mode, iface_names));
- return NodePtr(new Node(dp));
- }
- /**
- * Use this function to create a node instance with a custom driver.
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- * @throws uavcan_linux::Exception.
- */
- static inline NodePtr makeNode(const std::shared_ptr<uavcan::ICanDriver>& can_driver,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- DriverPackPtr dp(new DriverPack(clock_adjustment_mode, can_driver));
- return NodePtr(new Node(dp));
- }
- /**
- * This function extends the other two overloads in such a way that it instantiates and initializes
- * the node immediately; if initialization fails, it throws.
- *
- * If NodeID is not provided, it will not be initialized, and therefore the node will be started in
- * listen-only (i.e. silent) mode. The node can be switched to normal (i.e. non-silent) mode at any
- * later time by calling setNodeID() explicitly. Read the Node class docs for more info.
- *
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- *
- * @throws uavcan_linux::Exception, uavcan_linux::LibuavcanErrorException.
- */
- template <typename DriverType>
- static inline NodePtr makeNode(const DriverType& driver,
- const uavcan::NodeStatusProvider::NodeName& name,
- const uavcan::protocol::SoftwareVersion& software_version,
- const uavcan::protocol::HardwareVersion& hardware_version,
- const uavcan::NodeID node_id = uavcan::NodeID(),
- const uavcan::TransferPriority node_status_transfer_priority =
- uavcan::TransferPriority::Default,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- NodePtr node = makeNode(driver, clock_adjustment_mode);
- node->setName(name);
- node->setSoftwareVersion(software_version);
- node->setHardwareVersion(hardware_version);
- if (node_id.isValid())
- {
- node->setNodeID(node_id);
- }
- const auto res = node->start(node_status_transfer_priority);
- if (res < 0)
- {
- throw LibuavcanErrorException(res);
- }
- return node;
- }
- /**
- * Use this function to create a sub-node instance with default SocketCAN driver.
- * It accepts the list of interface names to use for the new node, e.g. "can1", "vcan2", "slcan0".
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- * @throws uavcan_linux::Exception.
- */
- static inline SubNodePtr makeSubNode(const std::vector<std::string>& iface_names,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- DriverPackPtr dp(new DriverPack(clock_adjustment_mode, iface_names));
- return SubNodePtr(new SubNode(dp));
- }
- /**
- * Use this function to create a sub-node instance with a custom driver.
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- * @throws uavcan_linux::Exception.
- */
- static inline SubNodePtr makeSubNode(const std::shared_ptr<uavcan::ICanDriver>& can_driver,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- DriverPackPtr dp(new DriverPack(clock_adjustment_mode, can_driver));
- return SubNodePtr(new SubNode(dp));
- }
- /**
- * This function extends the other two overloads in such a way that it instantiates the node
- * and sets its Node ID immediately.
- *
- * Clock adjustment mode will be detected automatically unless provided explicitly.
- *
- * @throws uavcan_linux::Exception, uavcan_linux::LibuavcanErrorException.
- */
- template <typename DriverType>
- static inline SubNodePtr makeSubNode(const DriverType& driver,
- const uavcan::NodeID node_id,
- ClockAdjustmentMode clock_adjustment_mode =
- SystemClock::detectPreferredClockAdjustmentMode())
- {
- SubNodePtr sub_node = makeSubNode(driver, clock_adjustment_mode);
- sub_node->setNodeID(node_id);
- return sub_node;
- }
- }
|