KaCanOpen
 All Classes Functions Variables Typedefs Enumerations Pages
core.cpp
1 /*
2  * Copyright (c) 2015, Thomas Keh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <iostream>
33 #include <chrono>
34 #include <future>
35 #include <string>
36 #include <cassert>
37 
38 #include "logger.h"
39 #include "core.h"
40 
41 namespace kaco {
42 
43 //-------------------------------------------//
44 // Linkage to the CAN driver. //
45 // Needs to be in lobal scope! //
46 // TODO: Maybe encapsulate in a driver class //
47 //-------------------------------------------//
48 
52 struct CANBoard {
53 
55  const char * busname;
56 
58  const char * baudrate;
59 
60 };
61 
64 typedef void* CANHandle;
65 
66 extern "C" uint8_t canReceive_driver(CANHandle, Message *);
67 extern "C" uint8_t canSend_driver(CANHandle, Message const *);
68 extern "C" CANHandle canOpen_driver(CANBoard *);
69 extern "C" int32_t canClose_driver(CANHandle);
70 extern "C" uint8_t canChangeBaudRate_driver(CANHandle, char *);
71 
73  : nmt(*this),
74  sdo(*this),
75  pdo(*this)
76  { }
77 
79  if (m_running) {
80  stop();
81  }
82 }
83 
84 bool Core::start(const std::string busname, const std::string& baudrate) {
85 
86  assert(!m_running);
87 
88  CANBoard board = {busname.c_str(), baudrate.c_str()};
89  m_handle = canOpen_driver(&board);
90 
91  if(!m_handle) {
92  ERROR("Cannot open the CANOpen device.");
93  return false;
94  }
95 
96  m_running = true;
97  m_loop_thread = std::thread(&Core::receive_loop, this, std::ref(m_running));
98  return true;
99 
100 }
101 
102 bool Core::start(const std::string busname, const unsigned baudrate) {
103  if (baudrate>=1000000 && baudrate%1000000==0) {
104  return start(busname, std::to_string(baudrate/1000000)+"M");
105  } else if (baudrate>=1000 && baudrate%1000==0) {
106  return start(busname, std::to_string(baudrate/1000)+"K");
107  } else {
108  return start(busname, std::to_string(baudrate));
109  }
110 }
111 
112 void Core::stop() {
113 
114  assert(m_running);
115 
116  m_running = false;
117  m_loop_thread.detach();
118 
119  DEBUG_LOG("Calling canClose.");
120  canClose_driver(m_handle);
121 
122 }
123 
124 void Core::receive_loop(std::atomic<bool>& running) {
125 
126  Message message;
127 
128  while (running) {
129 
130  canReceive_driver(m_handle, &message);
131  received_message(message);
132 
133  }
134 
135 }
136 
138  std::lock_guard<std::mutex> scoped_lock(m_receive_callbacks_mutex);
139  m_receive_callbacks.push_back(callback);
140 }
141 
142 void Core::received_message(const Message& message) {
143 
144  DEBUG_LOG(" ");
145  DEBUG_LOG("Received message:");
146 
147  // cleaning up old futures
148  if (m_cleanup_futures) {
149  std::lock_guard<std::mutex> scoped_lock(m_callback_futures_mutex);
150  m_callback_futures.remove_if([](const std::future<void>& f) {
151  // return true if callback has finished it's computation.
152  return (f.wait_for(std::chrono::steady_clock::duration::zero())==std::future_status::ready);
153  });
154  }
155 
156  // first call registered callbacks
157  {
158  std::lock_guard<std::mutex> scoped_lock(m_receive_callbacks_mutex);
159  for (const MessageReceivedCallback& callback : m_receive_callbacks) {
160  // The future returned by std::async has to be stored,
161  // otherwise the immediately called future destructor
162  // blocks until callback has finished.
163  std::lock_guard<std::mutex> scoped_lock(m_callback_futures_mutex);
164  m_callback_futures.push_front(
165  std::async(std::launch::async, callback, message)
166  );
167  }
168  }
169 
170  // sencondly process known message types
171  switch (message.get_function_code()) {
172 
173  case 0: {
174  DEBUG_LOG("NMT Module Control");
175  DEBUG(message.print();)
176  break;
177  }
178 
179  case 1: {
180  DEBUG_LOG("Sync or Emergency");
181  DEBUG(message.print();)
182  break;
183  }
184 
185  case 2: {
186  DEBUG_LOG("Time stamp");
187  DEBUG(message.print();)
188  break;
189  }
190 
191  case 3:
192  case 5:
193  case 7:
194  case 9: {
195  // TODO: This will be process_incoming_tpdo()
196  pdo.process_incoming_message(message);
197  break;
198  }
199 
200  case 4:
201  case 6:
202  case 8:
203  case 10: {
204  // TODO: Implement this for slave functionality
205  // -> delegate to pdo.process_incoming_rpdo()
206  DEBUG_LOG("PDO receive");
207  DEBUG(message.print();)
208  break;
209  }
210 
211  case 11: {
212  // TODO: This will be process_incoming_server_sdo()
213  sdo.process_incoming_message(message);
214  break;
215  }
216 
217  case 12: {
218  // TODO: Implement this for slave functionality
219  // -> delegate to sdo.process_incoming_client_sdo()
220  DEBUG_LOG("SDO (receive/client)");
221  DEBUG(message.print();)
222  break;
223  }
224 
225  case 14: {
226  // NMT Error Control
227  nmt.process_incoming_message(message);
228  break;
229  }
230 
231  default: {
232  DEBUG_LOG("Unknown message:");
233  DEBUG(message.print();)
234  break;
235  }
236 
237  }
238 
239  DEBUG_LOG(" ");
240 
241 }
242 
243 void Core::send(const Message& message) {
244 
245  if (m_lock_send) {
246  m_send_mutex.lock();
247  }
248 
249  DEBUG_LOG_EXHAUSTIVE("Sending message:");
250  DEBUG_EXHAUSTIVE(message.print();)
251  canSend_driver(m_handle, &message);
252 
253  if (m_lock_send) {
254  m_send_mutex.unlock();
255  }
256 
257 }
258 
259 } // namespace co
Core()
Constructor.
Definition: core.cpp:72
void send(const Message &message)
Sends a message.
Definition: core.cpp:243
NMT nmt
The NMT sub-protocol.
Definition: core.h:109
PDO pdo
The PDO sub-protocol.
Definition: core.h:115
uint8_t get_function_code() const
Extracts the function code from the COB-ID.
Definition: message.cpp:44
void process_incoming_message(const Message &message) const
Handler for an incoming PDO message.
Definition: pdo.cpp:45
std::function< void(const Message &) > MessageReceivedCallback
Type of a message receiver function Important: Never call register_receive_callback() from within (->...
Definition: core.h:66
void stop()
Stops the receive loop and closes the driver.
Definition: core.cpp:112
void process_incoming_message(const Message &message)
Process incoming SDO message.
Definition: sdo.cpp:156
SDO sdo
The SDO sub-protocol.
Definition: core.h:112
void process_incoming_message(const Message &message)
Process incoming NMT message.
Definition: nmt.cpp:79
bool start(const std::string busname, const std::string &baudrate)
Opens CAN driver and starts CAN message receive loop.
Definition: core.cpp:84
void register_receive_callback(const MessageReceivedCallback &callback)
Registers a callback function which is called when a message has been received.
Definition: core.cpp:137
This struct represents a CANOpen message.
Definition: message.h:39
~Core()
Destructor.
Definition: core.cpp:78
void print() const
Prints the message to command line.
Definition: message.cpp:48