KaCanOpen
 All Classes Functions Variables Typedefs Enumerations Pages
nmt.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 "nmt.h"
33 #include "core.h"
34 #include "logger.h"
35 
36 #include <iostream>
37 #include <cstdint>
38 #include <future>
39 #include <chrono>
40 
41 namespace kaco {
42 
43 NMT::NMT(Core& core)
44  : m_core(core)
45  { }
46 
47 void NMT::send_nmt_message(uint8_t node_id, Command cmd) {
48  DEBUG_LOG("Set NMT state of "<<(unsigned)node_id<<" to "<<static_cast<uint32_t>(cmd));
49  const Message message = { 0x0000, false, 2, {static_cast<uint8_t>(cmd),node_id,0,0,0,0,0,0} };
50  m_core.send(message);
51 }
52 
54  send_nmt_message(0, cmd);
55 }
56 
58  //broadcast_nmt_message(Command::reset_node);
59  // TODO check node_id range
60  const auto pause = std::chrono::milliseconds(CONSECUTIVE_SEND_PAUSE_MS);
61  for (size_t node_id = 1; node_id < 239; ++node_id) {
62  send_nmt_message(node_id, Command::reset_node);
63  std::this_thread::sleep_for(pause);
64  }
65 }
66 
68  // TODO check node_id range
69  const auto pause = std::chrono::milliseconds(CONSECUTIVE_SEND_PAUSE_MS);
70  for (size_t node_id = 1; node_id < 239; ++node_id) {
71  // Protocol node guarding. See CiA 301. All devices will answer with their state via NMT.
72  uint16_t cob_id = 0x700+node_id;
73  const Message message = { cob_id, true, 0, {0,0,0,0,0,0,0,0} };
74  m_core.send(message);
75  std::this_thread::sleep_for(pause);
76  }
77 }
78 
79 void NMT::process_incoming_message(const Message& message) {
80 
81  DEBUG_LOG("NMT Error Control message from node "
82  <<(unsigned)message.get_node_id()<<".");
83 
84  uint8_t data = message.data[0];
85  uint8_t state = data&0x7F;
86 
87  if (message.rtr) {
88  DEBUG_LOG("NMT: Ignoring remote transmission request.");
89  DEBUG_DUMP(message.cob_id);
90  return;
91  }
92 
93  //bool toggle_bit = data>>7;
94  //DEBUG_DUMP(toggle_bit);
95 
96  switch (state) {
97 
98  case 0:
99  case 2:
100  case 3:
101  case 5:
102  case 127: {
103  // device is alive
104  // cleaning up old futures
105  if (m_cleanup_futures) {
106  std::lock_guard<std::mutex> scoped_lock(m_callback_futures_mutex);
107  m_callback_futures.remove_if([](const std::future<void>& f) {
108  // return true if callback has finished it's computation.
109  return (f.wait_for(std::chrono::steady_clock::duration::zero())==std::future_status::ready);
110  });
111  }
112  // TODO: this should be device_alive callback
113  {
114  std::lock_guard<std::mutex> scoped_lock(m_device_alive_callbacks_mutex);
115  for (const auto& callback : m_device_alive_callbacks) {
116  DEBUG_LOG("Calling new device callback (async)");
117  // The future returned by std::async has to be stored,
118  // otherwise the immediately called future destructor
119  // blocks until callback has finished.
120  std::lock_guard<std::mutex> scoped_lock(m_callback_futures_mutex);
121  m_callback_futures.push_front(
122  std::async(std::launch::async, callback, message.get_node_id())
123  );
124  }
125  }
126  break;
127  }
128 
129  default: {
130  // TODO disconnect device
131  }
132 
133  }
134 
135  switch (state) {
136 
137  case 0: {
138  DEBUG_LOG("New state is Initialising");
139  break;
140  }
141 
142  case 1: {
143  DEBUG_LOG("New state is Disconnected");
144  break;
145  }
146 
147  case 2: {
148  DEBUG_LOG("New state is Connecting");
149  break;
150  }
151 
152  case 3: {
153  DEBUG_LOG("New state is Preparing");
154  break;
155  }
156 
157  case 4: {
158  DEBUG_LOG("New state is Stopped");
159  break;
160  }
161 
162  case 5: {
163  DEBUG_LOG("New state is Operational");
164  break;
165  }
166 
167  case 127: {
168  DEBUG_LOG("New state is Pre-operational");
169  break;
170  }
171 
172  default: {
173  DEBUG_LOG("New state is unknown: "<<(unsigned)state);
174  break;
175  }
176 
177  }
178 
179 }
180 
182  std::lock_guard<std::mutex> scoped_lock(m_device_alive_callbacks_mutex);
183  m_device_alive_callbacks.push_back(callback);
184 }
185 
188 }
189 
190 } // end namespace kaco
uint16_t cob_id
Message ID aka COB-ID.
Definition: message.h:42
void send_nmt_message(uint8_t node_id, Command cmd)
Sends a NMT message to a given device.
Definition: nmt.cpp:47
void broadcast_nmt_message(Command cmd)
Sends a broadcast NMT message.
Definition: nmt.cpp:53
void send(const Message &message)
Sends a message.
Definition: core.cpp:243
void register_device_alive_callback(const DeviceAliveCallback &callback)
Registers a callback which will be called when a slave sends it's state via NMT and the state indicat...
Definition: nmt.cpp:181
Command
NMT commands.
Definition: nmt.h:64
DeviceAliveCallback NewDeviceCallback
Type of a new device callback function.
Definition: nmt.h:61
This class implements the Core of KaCanOpen It communicates with the CAN driver, sends CAN messages a...
Definition: core.h:59
void discover_nodes()
Discovers nodes in the network via node guard protocol.
Definition: nmt.cpp:67
void register_new_device_callback(const NewDeviceCallback &callback)
Registers a callback which will be called when a new slave device is discovered.
Definition: nmt.cpp:186
void process_incoming_message(const Message &message)
Process incoming NMT message.
Definition: nmt.cpp:79
uint8_t get_node_id() const
Extracts the node id from the COB-ID.
Definition: message.cpp:40
NMT(Core &core)
Constructor.
Definition: nmt.cpp:43
uint8_t rtr
Remote transmission request (0 if it's not an RTR message, 1 if it is an RTR message) ...
Definition: message.h:45
std::function< void(const uint8_t node_id) > DeviceAliveCallback
Type of a device alive callback function Important: Never call register_device_alive_callback() from ...
Definition: nmt.h:57
This struct represents a CANOpen message.
Definition: message.h:39
uint8_t data[8]
Data bytes.
Definition: message.h:51
void reset_all_nodes()
Resets all nodes in the network.
Definition: nmt.cpp:57