Libsockcanpp
A complete C++ wrapper around socketcan.
CanDriver.cpp
Go to the documentation of this file.
1/**
2 * @file CanDriver.cpp
3 * @author Simon Cahill (simonc@online.de)
4 * @brief
5 * @version 0.1
6 * @date 2020-07-01
7 *
8 * @copyright Copyright (c) 2020 Simon Cahill
9 *
10 *
11 * Copyright 2020 Simon Cahill
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 */
25
26//////////////////////////////
27// SYSTEM INCLUDES //
28//////////////////////////////
29
30#include <fcntl.h>
31#include <linux/can.h>
32#include <linux/can/raw.h>
33#include <net/if.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/types.h>
37#include <unistd.h>
38
39#include <cstdlib>
40#include <cstring>
41#include <mutex>
42#include <queue>
43#include <string>
44#include <thread>
45
46//////////////////////////////
47// LOCAL INCLUDES //
48//////////////////////////////
49#include "CanDriver.hpp"
50#include "CanId.hpp"
51#include "CanMessage.hpp"
52#include "exceptions/CanCloseException.hpp"
53#include "exceptions/CanException.hpp"
54#include "exceptions/CanInitException.hpp"
55#include "exceptions/InvalidSocketException.hpp"
56
57namespace sockcanpp {
58
59 using exceptions::CanCloseException;
60 using exceptions::CanException;
61 using exceptions::CanInitException;
62 using exceptions::InvalidSocketException;
63
64 using std::mutex;
65 using std::queue;
66 using std::string;
67 using std::strncpy;
68 using std::unique_lock;
69 using std::chrono::milliseconds;
70 using std::this_thread::sleep_for;
71
72 //////////////////////////////////////
73 // PUBLIC IMPLEMENTATION //
74 //////////////////////////////////////
75
76 const int32_t CanDriver::CAN_MAX_DATA_LENGTH = 8;
77 const int32_t CanDriver::CAN_SOCK_RAW = CAN_RAW;
78 const int32_t CanDriver::CAN_SOCK_SEVEN = 7;
79
80#pragma region Object Construction
81 CanDriver::CanDriver(string canInterface, int32_t canProtocol, const CanId defaultSenderId):
82 CanDriver(canInterface, canProtocol, 0 /* match all */, defaultSenderId) {}
83
84 CanDriver::CanDriver(const string canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId):
85 _defaultSenderId(defaultSenderId), _canProtocol(canProtocol), _canInterface(canInterface), _canFilterMask(filterMask), _socketFd(-1) {
87 }
88#pragma endregion
89
90#pragma region I / O
91 /**
92 * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
93 *
94 * @param timeout The time (in millis) to wait before timing out.
95 *
96 * @return true If messages are available on the bus.
97 * @return false Otherwise.
98 */
99 bool CanDriver::waitForMessages(milliseconds timeout) {
100 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
101
102 unique_lock<mutex> locky(_lock);
103
104 fd_set readFileDescriptors;
105 timeval waitTime;
106 waitTime.tv_sec = timeout.count() / 1000;
107 waitTime.tv_usec = timeout.count() * 1000;
108
109 FD_ZERO(&readFileDescriptors);
110 FD_SET(_socketFd, &readFileDescriptors);
111 _queueSize = select(_socketFd + 1, &readFileDescriptors, 0, 0, &waitTime);
112
113 return _queueSize > 0;
114 }
115
116 /**
117 * @brief Attempts to read a message from the associated CAN bus.
118 *
119 * @return CanMessage The message read from the bus.
120 */
122 return readMessageLock();
123 }
124
125 /**
126 * @brief readMessage deadlock guard, attempts to read a message from the associated CAN bus.
127 *
128 * @return CanMessage The message read from the bus.
129 */
131 std::unique_ptr<std::unique_lock<std::mutex>> _lockLck{nullptr};
132 if (lock)
133 _lockLck = std::unique_ptr<std::unique_lock<std::mutex>>{new std::unique_lock<std::mutex>{_lock}};
134 if (0 > _socketFd)
135 throw InvalidSocketException("Invalid socket!", _socketFd);
136 int32_t readBytes{0};
137 can_frame canFrame;
138 memset(&canFrame, 0, sizeof(can_frame));
139 readBytes = read(_socketFd, &canFrame, sizeof(can_frame));
140 if (0 > readBytes)
141 throw CanException(formatString("FAILED to read from CAN! Error: %d => %s", errno, strerror(errno)), _socketFd);
142 return CanMessage{canFrame};
143 }
144
145 /**
146 * @brief Attempts to send a CAN message on the associated bus.
147 *
148 * @param message The message to be sent.
149 * @param forceExtended Whether or not to force use of an extended ID.
150 *
151 * @return int32_t The amount of bytes sent on the bus.
152 */
153 int32_t CanDriver::sendMessage(const CanMessage message, bool forceExtended) {
154 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
155
156 unique_lock<mutex> locky(_lockSend);
157
158 int32_t bytesWritten = 0;
159
160 if (message.getFrameData().size() > CAN_MAX_DATA_LENGTH) {
161 throw CanException(formatString("INVALID data length! Message must be smaller than %d bytes!", CAN_MAX_DATA_LENGTH), _socketFd);
162 }
163
164 auto canFrame = message.getRawFrame();
165
166 if (forceExtended || ((uint32_t)message.getCanId() > CAN_SFF_MASK)) { canFrame.can_id |= CAN_EFF_FLAG; }
167
168 bytesWritten = write(_socketFd, (const void*)&canFrame, sizeof(canFrame));
169
170 if (bytesWritten == -1) { throw CanException(formatString("FAILED to write data to socket! Error: %d => %s", errno, strerror(errno)), _socketFd); }
171
172 return bytesWritten;
173 }
174
175 /**
176 * @brief Attempts to send a queue of messages on the associated CAN bus.
177 *
178 * @param messages A queue containing the messages to be sent.
179 * @param delay If greater than 0, will delay the sending of the next message.
180 * @param forceExtended Whether or not to force use of an extended ID.
181 *
182 * @return int32_t The total amount of bytes sent.
183 */
184 int32_t CanDriver::sendMessageQueue(queue<CanMessage> messages, milliseconds delay, bool forceExtended) {
185 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
186
187 int32_t totalBytesWritten = 0;
188
189 while (!messages.empty()) {
190 totalBytesWritten += sendMessage(messages.front(), forceExtended);
191 messages.pop();
192 }
193
194 return totalBytesWritten;
195 }
196
197 /**
198 * @brief Attempts to read all messages stored in the buffer for the associated CAN bus.
199 *
200 * @return queue<CanMessage> A queue containing the messages read from the bus buffer.
201 */
203 if (_socketFd < 0)
204 throw InvalidSocketException("Invalid socket!", _socketFd);
205 unique_lock<mutex> locky(_lock);
206 queue<CanMessage> messages;
207 for (int32_t i = _queueSize; 0 < i; --i)
208 messages.emplace(readMessageLock(false));
209 return messages;
210 }
211
212 /**
213 * @brief Attempts to set the filter mask for the associated CAN bus.
214 *
215 * @param mask The bit mask to apply.
216 */
217 void CanDriver::setCanFilterMask(const int32_t mask) {
218 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
219
220 unique_lock<mutex> locky(_lock);
221 can_filter canFilter;
222
223 canFilter.can_id = _defaultSenderId;
224 canFilter.can_mask = mask;
225
226 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_FILTER, &canFilter, sizeof(canFilter)) == -1) {
227 throw CanInitException(formatString("FAILED to set CAN filter mask %x on socket %d! Error: %d => %s", mask, _socketFd, errno, strerror(errno)));
228 }
229
230 _canFilterMask = mask;
231 }
232#pragma endregion
233
234 //////////////////////////////////////
235 // PROTECTED IMPLEMENTATION //
236 //////////////////////////////////////
237
238#pragma region Socket Management
239
240 /**
241 * @brief Initialises the underlying CAN socket.
242 */
244 // unique_lock<mutex> locky(_lock);
245
246 struct sockaddr_can address;
247 struct ifreq ifaceRequest;
248 int64_t fdOptions = 0;
249 int32_t tmpReturn;
250
251 memset(&address, 0, sizeof(sizeof(struct sockaddr_can)));
252 memset(&ifaceRequest, 0, sizeof(sizeof(struct ifreq)));
253
254 _socketFd = socket(PF_CAN, SOCK_RAW, _canProtocol);
255
256 if (_socketFd == -1) {
257 throw CanInitException(formatString("FAILED to initialise socketcan! Error: %d => %s", errno, strerror(errno)));
258 }
259
260 strcpy(ifaceRequest.ifr_name, _canInterface.c_str());
261
262 if ((tmpReturn = ioctl(_socketFd, SIOCGIFINDEX, &ifaceRequest)) == -1) {
263 throw CanInitException(formatString("FAILED to perform IO control operation on socket %s! Error: %d => %s", _canInterface.c_str(), errno,
264 strerror(errno)));
265 }
266
267 fdOptions = fcntl(_socketFd, F_GETFL);
268 fdOptions |= O_NONBLOCK;
269 tmpReturn = fcntl(_socketFd, F_SETFL, fdOptions);
270
271 address.can_family = AF_CAN;
272 address.can_ifindex = ifaceRequest.ifr_ifindex;
273
275
276 if ((tmpReturn = bind(_socketFd, (struct sockaddr*)&address, sizeof(address))) == -1) {
277 throw CanInitException(formatString("FAILED to bind to socket CAN! Error: %d => %s", errno, strerror(errno)));
278 }
279 }
280
281 /**
282 * @brief Closes the underlying CAN socket.
283 */
285 unique_lock<mutex> locky(_lock);
286
287 if (_socketFd <= 0) { throw CanCloseException("Cannot close invalid socket!"); }
288
289 if (close(_socketFd) == -1) { throw CanCloseException(formatString("FAILED to close CAN socket! Error: %d => %s", errno, strerror(errno))); }
290
291 _socketFd = -1;
292 }
293#pragma endregion
294
295} // namespace sockcanpp
CanDriver class; handles communication via CAN.
Definition: CanDriver.hpp:62
virtual CanMessage readMessage()
!< Waits for CAN messages to appear
Definition: CanDriver.cpp:121
static const int32_t CAN_SOCK_RAW
!< The maximum amount of bytes allowed in a single CAN frame
Definition: CanDriver.hpp:65
static const int32_t CAN_SOCK_SEVEN
!< The raw CAN protocol
Definition: CanDriver.hpp:66
CanId _defaultSenderId
!< readMessage deadlock guard
Definition: CanDriver.hpp:102
int32_t _queueSize
!< The CAN socket file descriptor
Definition: CanDriver.hpp:107
virtual queue< CanMessage > readQueuedMessages()
!< Attempts to send a queue of messages
Definition: CanDriver.cpp:202
int32_t _canProtocol
!< The bit mask used to filter CAN messages
Definition: CanDriver.hpp:105
virtual int32_t sendMessageQueue(queue< CanMessage > messages, milliseconds delay=milliseconds(20), bool forceExtended=false)
!< Attempts to send a single CAN message
Definition: CanDriver.cpp:184
virtual CanMessage readMessageLock(bool const lock=true)
!< Uninitialises socketcan
Definition: CanDriver.cpp:130
CanDriver(const string canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId=0)
!< Constructor
Definition: CanDriver.cpp:84
int32_t _socketFd
!< The protocol used when communicating via CAN
Definition: CanDriver.hpp:106
int32_t _canFilterMask
!< The ID to send messages with if no other ID was set.
Definition: CanDriver.hpp:104
virtual bool waitForMessages(milliseconds timeout=milliseconds(3000))
!< The socket file descriptor used by this instance.
Definition: CanDriver.cpp:99
mutex _lock
!< Mutex for thread-safety.
Definition: CanDriver.hpp:110
virtual void initialiseSocketCan()
!< Attempts to set a new CAN filter mask to the BIOS
Definition: CanDriver.cpp:243
virtual int32_t sendMessage(const CanMessage message, bool forceExtended=false)
!< Attempts to read a single message from the bus
Definition: CanDriver.cpp:153
virtual void setCanFilterMask(const int32_t mask)
!< Attempts to read all queued messages from the bus
Definition: CanDriver.cpp:217
virtual void uninitialiseSocketCan()
!< Initialises socketcan
Definition: CanDriver.cpp:284
static const int32_t CAN_MAX_DATA_LENGTH
Definition: CanDriver.hpp:64
Represents a CAN message that was received.
Definition: CanMessage.hpp:55
const string getFrameData() const
Definition: CanMessage.hpp:77
const can_frame getRawFrame() const
Definition: CanMessage.hpp:78
CanMessage(const struct can_frame frame)
Definition: CanMessage.hpp:57
const CanId getCanId() const
Definition: CanMessage.hpp:76
CanException(string message, int32_t socket)
InvalidSocketException(string message, int32_t socket)
Main library namespace.
Definition: CanDriver.cpp:57
string formatString(const string &format, Args... args)
Formats a std string object.
Definition: CanDriver.hpp:131
Represents a CAN ID in a simple and easy-to-use manner.
Definition: CanId.hpp:48
CanId(const CanId &orig)
Definition: CanId.hpp:50