Lely core libraries  1.9.2
io_context.cpp
Go to the documentation of this file.
1 
24 #include "coapp.hpp"
25 #include <lely/can/net.hpp>
26 #include <lely/aio/detail/timespec.hpp>
28 
29 #include <mutex>
30 
31 namespace lely {
32 
33 namespace canopen {
34 
35 using namespace aio;
36 
39  Impl_(IoContext* self, TimerBase& timer, CanBusBase& bus,
40  BasicLockable* mutex);
41 
42  void
43  lock() override {
44  if (mutex) mutex->lock();
45  }
46  void
47  unlock() override {
48  if (mutex) mutex->unlock();
49  }
50 
51  void operator()(::std::error_code ec) noexcept;
52  void operator()(::std::error_code ec, int result) noexcept;
53 
54  int OnNext(const timespec* tp) noexcept;
55  int OnSend(const can_msg* msg) noexcept;
56 
57  IoContext* self{nullptr};
58 
59  TimerBase& timer;
60  TimerBase::WaitOperation wait_op;
61 
62  CanBusBase& bus;
63  CanBusBase::Frame msg CAN_MSG_INIT;
64  CanBusBase::Info info CAN_MSG_INFO_INIT;
65  int state{0};
66  int error{0};
67  CanBusBase::ReadOperation read_op;
68 
69  BasicLockable* mutex{nullptr};
70 
71  unique_c_ptr<CANNet> net;
72 };
73 
74 IoContext::IoContext(aio::TimerBase& timer, aio::CanBusBase& bus,
75  BasicLockable* mutex)
76  : impl_(new Impl_(this, timer, bus, mutex)) {}
77 
78 IoContext::~IoContext() = default;
79 
80 aio::ExecutorBase
81 IoContext::GetExecutor() const noexcept {
82  return impl_->timer.GetExecutor();
83 }
84 
85 CANNet*
86 IoContext::net() const noexcept {
87  return impl_->net.get();
88 }
89 
90 void
92  net()->setTime(aio::detail::ToTimespec(impl_->timer.GetClock().GetTime()));
93 }
94 
95 IoContext::Impl_::Impl_(IoContext* self_, TimerBase& timer_, CanBusBase& bus_,
96  BasicLockable* mutex_)
97  : self(self_),
98  timer(timer_),
99  wait_op(::std::ref(*this)),
100  bus(bus_),
101  read_op(&msg, &info, ::std::ref(*this)),
102  mutex(mutex_),
103  net(make_unique_c<CANNet>()) {
104  // Initialize the CAN network clock with the current time.
105  net->setTime(aio::detail::ToTimespec(timer.GetClock().GetTime()));
106 
107  // Register the OnNext() member function as the function to be invoked when
108  // the time at which the next timer triggers is updated.
109  net->setNextFunc<Impl_, &Impl_::OnNext>(this);
110  // Register the OnSend() member function as the function to be invoked when a
111  // CAN frame needs to be sent.
112  net->setSendFunc<Impl_, &Impl_::OnSend>(this);
113 
114  // Start waiting for timeouts.
115  timer.SubmitWait(wait_op);
116 
117  // Start receiving CAN frames.
118  bus.SubmitRead(read_op);
119 }
120 
121 void
122 IoContext::Impl_::operator()(::std::error_code ec) noexcept {
123  if (!ec) {
124  ::std::lock_guard<Impl_> lock(*this);
125  self->SetTime();
126  }
127  timer.SubmitWait(wait_op);
128 }
129 
130 void
131 IoContext::Impl_::operator()(::std::error_code ec, int result) noexcept {
132  if (!ec) {
133  ::std::lock_guard<Impl_> lock(*this);
134  if (result == 1) {
135  net->recv(msg);
136  } else if (!result) {
137  if (info.state != state) {
138  ::std::swap(info.state, state);
139  self->OnCanState(static_cast<CanState>(state),
140  static_cast<CanState>(info.state));
141  }
142  if (info.error != error) {
143  error = info.error;
144  self->OnCanError(static_cast<CanError>(error));
145  }
146  }
147  }
148  bus.SubmitRead(read_op);
149 }
150 
151 int
152 IoContext::Impl_::OnNext(const timespec* tp) noexcept {
153  timer.SetTime(TimerBase::time_point(aio::detail::FromTimespec(*tp)));
154  return 0;
155 }
156 
157 int
158 IoContext::Impl_::OnSend(const can_msg* msg) noexcept {
159  // The CAN network interface does not support asynchronous writes, so we try a
160  // non-blocking synchronous write.
161  ::std::error_code ec;
162  if (!bus.Write(*msg, ec)) {
163  if (ec) set_errc(ec.value());
164  return -1;
165  }
166  return 0;
167 }
168 
169 } // namespace canopen
170 
171 } // namespace lely
A CAN or CAN FD format frame.
Definition: msg.h:88
unique_c_ptr< T > make_unique_c(Args &&... args)
Creates an instance of a trivial, standard layout or incomplete C type and wraps it in a lely::unique...
Definition: c_type.hpp:111
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:114
virtual void unlock()=0
Releases the lock held by the execution agent. Throws no exceptions.
This header file is part of the CAN library; it contains the C++ interface of the CAN network interfa...
STL namespace.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
CANNet * net() const noexcept
Returns a pointer to the internal CAN network interface from <lely/can/net.hpp>.
Definition: io_context.cpp:86
void SetTime()
Update the CAN network time.
Definition: io_context.cpp:91
The internal implementation of the I/O context.
Definition: io_context.cpp:38
virtual void lock()=0
Blocks until a lock can be obtained for the current execution agent (thread, process, task).
The type of objects thrown as exceptions to report a system error with an associated error code...
Definition: exception.hpp:54
This header file is part of the C++ CANopen application library; it contains the I/O context declarat...
An abstract interface conforming to the BasicLockable concept.
Definition: coapp.hpp:40
This is the internal header file of the C++ CANopen application library.
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
Definition: io_context.cpp:47
Global namespace for the Lely Industries N.V. libraries.
Definition: buf.hpp:32
An opaque CAN network interface type.
Definition: net.hpp:83
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process, task).
Definition: io_context.cpp:43
aio::ExecutorBase GetExecutor() const noexcept
Returns the executor used to process I/O events on the CAN bus.
Definition: io_context.cpp:81
IoContext(aio::TimerBase &timer, aio::CanBusBase &bus, BasicLockable *mutex=nullptr)
Creates a new I/O context.
Definition: io_context.cpp:74
The I/O context.
Definition: io_context.hpp:42