/* * Copyright (C) 2014 Pavel Kirienko */ #pragma once #include #include #include #include #include #include #include #include #include "../../clock.hpp" class CanIfaceMock : public uavcan::ICanIface { public: struct FrameWithTime { uavcan::CanFrame frame; uavcan::MonotonicTime time; FrameWithTime(const uavcan::CanFrame& frame, uavcan::MonotonicTime time) : frame(frame) , time(time) { } FrameWithTime(const uavcan::CanFrame& frame, uint64_t time_usec) : frame(frame) , time(uavcan::MonotonicTime::fromUSec(time_usec)) { } }; std::queue tx; ///< Queue of outgoing frames (bus <-- library) std::queue rx; ///< Queue of incoming frames (bus --> library) std::queue loopback; ///< Loopback bool writeable; bool tx_failure; bool rx_failure; uint64_t num_errors; uavcan::ISystemClock& iclock; bool enable_utc_timestamping; CanIfaceMock(uavcan::ISystemClock& iclock) : writeable(true) , tx_failure(false) , rx_failure(false) , num_errors(0) , iclock(iclock) , enable_utc_timestamping(false) { } void pushRx(const uavcan::CanFrame& frame) { rx.push(FrameWithTime(frame, iclock.getMonotonic())); } void pushRx(const uavcan::RxFrame& frame) { uavcan::CanFrame can_frame; EXPECT_TRUE(frame.compile(can_frame)); rx.push(FrameWithTime(can_frame, frame.getMonotonicTimestamp())); } bool matchAndPopTx(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline) { if (tx.empty()) { std::cout << "Tx buffer is empty" << std::endl; return false; } const FrameWithTime frame_time = tx.front(); tx.pop(); return (frame_time.frame == frame) && (frame_time.time == tx_deadline); } bool matchAndPopTx(const uavcan::CanFrame& frame, uint64_t tx_deadline_usec) { return matchAndPopTx(frame, uavcan::MonotonicTime::fromUSec(tx_deadline_usec)); } uavcan::CanFrame popTxFrame() { if (tx.empty()) { std::cout << "Tx buffer is empty" << std::endl; std::abort(); } const FrameWithTime frame_time = tx.front(); tx.pop(); return frame_time.frame; } virtual uavcan::int16_t send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline, uavcan::CanIOFlags flags) { assert(this); EXPECT_TRUE(writeable); // Shall never be called when not writeable if (tx_failure) { return -1; } if (!writeable) { return 0; } tx.push(FrameWithTime(frame, tx_deadline)); if (flags & uavcan::CanIOFlagLoopback) { loopback.push(FrameWithTime(frame, iclock.getMonotonic())); } return 1; } virtual uavcan::int16_t receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags) { assert(this); out_flags = uavcan::CanIOFlags(); if (loopback.empty()) { EXPECT_TRUE(rx.size()); // Shall never be called when not readable if (rx_failure) { return -1; } if (rx.empty()) { return 0; } const FrameWithTime frame = rx.front(); rx.pop(); out_frame = frame.frame; out_ts_monotonic = frame.time; } else { out_flags |= uavcan::CanIOFlagLoopback; const FrameWithTime frame = loopback.front(); loopback.pop(); out_frame = frame.frame; out_ts_monotonic = frame.time; } out_ts_utc = enable_utc_timestamping ? iclock.getUtc() : uavcan::UtcTime(); return 1; } // cppcheck-suppress unusedFunction // cppcheck-suppress functionConst virtual uavcan::int16_t configureFilters(const uavcan::CanFilterConfig*, uavcan::uint16_t) { return -1; } // cppcheck-suppress unusedFunction virtual uavcan::uint16_t getNumFilters() const { return 0; } virtual uavcan::uint64_t getErrorCount() const { return num_errors; } }; class CanDriverMock : public uavcan::ICanDriver { public: std::vector ifaces; uavcan::ISystemClock& iclock; bool select_failure; CanDriverMock(unsigned num_ifaces, uavcan::ISystemClock& iclock) : ifaces(num_ifaces, CanIfaceMock(iclock)) , iclock(iclock) , select_failure(false) { } virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks, uavcan::MonotonicTime deadline) { assert(this); //std::cout << "Write/read masks: " << inout_write_iface_mask << "/" << inout_read_iface_mask << std::endl; if (select_failure) { return -1; } const uavcan::uint8_t valid_iface_mask = uavcan::uint8_t((1 << getNumIfaces()) - 1); EXPECT_FALSE(inout_masks.write & ~valid_iface_mask); EXPECT_FALSE(inout_masks.read & ~valid_iface_mask); uavcan::uint8_t out_write_mask = 0; uavcan::uint8_t out_read_mask = 0; for (unsigned i = 0; i < getNumIfaces(); i++) { const uavcan::uint8_t mask = uavcan::uint8_t(1 << i); if ((inout_masks.write & mask) && ifaces.at(i).writeable) { out_write_mask |= mask; } if ((inout_masks.read & mask) && (ifaces.at(i).rx.size() || ifaces.at(i).loopback.size())) { out_read_mask |= mask; } } inout_masks.write = out_write_mask; inout_masks.read = out_read_mask; if ((out_write_mask | out_read_mask) == 0) { const uavcan::MonotonicTime ts = iclock.getMonotonic(); const uavcan::MonotonicDuration diff = deadline - ts; SystemClockMock* const mock = dynamic_cast(&iclock); if (mock) { if (diff.isPositive()) { mock->advance(uint64_t(diff.toUSec())); // Emulating timeout } } else { if (diff.isPositive()) { usleep(unsigned(diff.toUSec())); } } return 0; } return 1; // This value is not being checked anyway, it just has to be greater than zero } virtual uavcan::ICanIface* getIface(uavcan::uint8_t iface_index) { return &ifaces.at(iface_index); } virtual uavcan::uint8_t getNumIfaces() const { return uavcan::uint8_t(ifaces.size()); } }; enum FrameType { STD, EXT }; inline uavcan::CanFrame makeCanFrame(uint32_t id, const std::string& str_data, FrameType type) { id |= (type == EXT) ? uavcan::CanFrame::FlagEFF : 0; return uavcan::CanFrame(id, reinterpret_cast(str_data.c_str()), uavcan::uint8_t(str_data.length())); }