Goby3 3.4.0
2026.04.13
Loading...
Searching...
No Matches
can.h
Go to the documentation of this file.
1// Copyright 2020-2026:
2// GobySoft, LLC (2013-)
3// Community contributors (see AUTHORS file)
4// File authors:
5// Toby Schneider <toby@gobysoft.org>
6// Shawn Dooley <shawn@shawndooley.net>
7//
8//
9// This file is part of the Goby Underwater Autonomy Project Libraries
10// ("The Goby Libraries").
11//
12// The Goby Libraries are free software: you can redistribute them and/or modify
13// them under the terms of the GNU Lesser General Public License as published by
14// the Free Software Foundation, either version 2.1 of the License, or
15// (at your option) any later version.
16//
17// The Goby Libraries are distributed in the hope that they will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public License
23// along with Goby. If not, see <http://www.gnu.org/licenses/>.
24
25#ifndef GOBY_MIDDLEWARE_IO_CAN_H
26#define GOBY_MIDDLEWARE_IO_CAN_H
27
28#include <errno.h> // for errno
29#include <linux/can.h> // for can_frame, socka...
30#include <linux/can/raw.h> // for CAN_RAW_FILTER
31#include <memory> // for make_shared, sha...
32#include <net/if.h> // for ifreq, ifr_ifindex
33#include <stdint.h> // for uint32_t, uint8_t
34#include <string.h> // for strcpy, strerror
35#include <string> // for string, operator+
36#include <sys/ioctl.h> // for ioctl, SIOCGIFINDEX
37#include <sys/socket.h> // for bind, setsockopt
38#include <tuple> // for make_tuple, tuple
39#include <vector> // for vector
40
41#include <boost/asio/buffer.hpp> // for buffer
42#include <boost/asio/posix/stream_descriptor.hpp> // for stream_descriptor
43#include <boost/asio/read.hpp> // for async_read
44#include <boost/bind/bind.hpp> // for bind
45#include <boost/core/ref.hpp> // for ref
46
47#include "goby/exception.h" // for Exception
48#include "goby/middleware/io/detail/io_interface.h" // for PubSubLayer, IOT...
49#include "goby/middleware/protobuf/can_config.pb.h" // for CanConfig, CanCo...
50#include "goby/middleware/protobuf/io.pb.h" // for IOData
52
53namespace goby
54{
55namespace middleware
56{
57class Group;
58}
59} // namespace goby
60
61namespace goby
62{
63namespace middleware
64{
65namespace io
66{
67inline std::uint32_t make_extended_format_can_id(std::uint32_t pgn, std::uint8_t priority,
68 std::uint8_t source = 0)
69{
70 return (pgn & 0x1FFFF) << 8 | (priority & 0x7) << 26 | CAN_EFF_FLAG | (source & 0xFF);
71}
72
73// tuple of pgn, priority, source
74namespace can_id
75{
76constexpr int pgn_index{0};
77constexpr int priority_index{1};
78constexpr int source_index{2};
79} // namespace can_id
80
81inline std::tuple<std::uint32_t, std::uint8_t, std::uint8_t>
82parse_extended_format_can_id(std::uint32_t can_id)
83{
84 return std::make_tuple((can_id >> 8) & 0x1FFFF, (can_id >> 26) & 0x7, can_id & 0xFF);
85}
86
87template <const goby::middleware::Group& line_in_group,
88 const goby::middleware::Group& line_out_group,
89 // by default publish all incoming traffic to interprocess for logging
91 // but only subscribe on interthread for outgoing traffic
92 PubSubLayer subscribe_layer = PubSubLayer::INTERTHREAD,
93 template <class> class ThreadType = goby::zeromq::SimpleThread,
94 bool use_indexed_groups = false>
96 : public detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
97 goby::middleware::protobuf::CanConfig,
98 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>
99{
100 using Base =
101 detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
103 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>;
104
105 public:
110 : Base(config, index, std::string("can: ") + config.interface())
111 {
113 this->interthread().template publish<line_in_group>(ready);
114 }
115
116 ~CanThread() override {}
117
118 private:
119 void async_read() override;
120 void async_write(std::shared_ptr<const goby::middleware::protobuf::IOData> io_msg) override
121 {
122 detail::basic_async_write(this, io_msg);
123 }
124
125 void open_socket() override;
126
127 void data_rec(struct can_frame& receive_frame_, boost::asio::posix::stream_descriptor& stream);
128
129 private:
130 struct can_frame receive_frame_;
131};
132} // namespace io
133} // namespace middleware
134} // namespace goby
135
136template <const goby::middleware::Group& line_in_group,
137 const goby::middleware::Group& line_out_group,
139 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
140 bool use_indexed_groups>
141void goby::middleware::io::CanThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
142 ThreadType, use_indexed_groups>::open_socket()
143{
144 int can_socket;
145
146 struct sockaddr_can addr_
147 {
148 };
149 struct can_frame receive_frame_;
150 struct ifreq ifr_;
151 can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
152
153 std::vector<struct can_filter> filters;
154
155 for (auto x : this->cfg().filter())
156 {
157 auto id = x.can_id();
158 auto mask = x.has_can_mask_custom() ? x.can_mask_custom() : x.can_mask();
159
160 filters.push_back({id, mask});
161 }
162
163 for (std::uint32_t x : this->cfg().pgn_filter())
164 {
165 constexpr std::uint32_t one_byte = 8; // bits
166 auto id = x << one_byte;
167 constexpr auto mask = protobuf::CanConfig::CanFilter::PGNOnly; // PGN mask
168 filters.push_back({id, mask});
169 }
170
171 if (filters.size())
172 {
173 setsockopt(can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, filters.data(),
174 sizeof(can_filter) * filters.size());
175 }
176 std::strcpy(ifr_.ifr_name, this->cfg().interface().c_str());
177
178 ioctl(can_socket, SIOCGIFINDEX, &ifr_);
179
180 addr_.can_family = AF_CAN;
181 addr_.can_ifindex = ifr_.ifr_ifindex;
182 if (bind(can_socket, (struct sockaddr*)&addr_, sizeof(addr_)) < 0)
183 throw(goby::Exception(std::string("Error in socket bind to interface ") +
184 this->cfg().interface() + ": " + std::strerror(errno)));
185
186 this->mutable_socket().assign(can_socket);
187
188 this->interthread().template subscribe<line_out_group, can_frame>(
189 [this](const can_frame& frame)
190 {
191 auto io_msg = std::make_shared<goby::middleware::protobuf::IOData>();
192 std::string& bytes = *io_msg->mutable_data();
193
194 const int frame_size = sizeof(can_frame);
195
196 for (int i = 0; i < frame_size; ++i)
197 {
198 bytes += *(reinterpret_cast<const char*>(&frame) + i);
199 }
200 this->write(io_msg);
201 });
202}
203
204template <const goby::middleware::Group& line_in_group,
205 const goby::middleware::Group& line_out_group,
207 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
208 bool use_indexed_groups>
209void goby::middleware::io::CanThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
210 ThreadType, use_indexed_groups>::async_read()
211{
212 boost::asio::async_read(this->mutable_socket(),
213 boost::asio::buffer(&receive_frame_, sizeof(receive_frame_)),
214 boost::bind(&CanThread::data_rec, this, boost::ref(receive_frame_),
215 boost::ref(this->mutable_socket())));
216}
217
218template <const goby::middleware::Group& line_in_group,
219 const goby::middleware::Group& line_out_group,
221 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
222 bool use_indexed_groups>
224 line_in_group, line_out_group, publish_layer, subscribe_layer, ThreadType,
225 use_indexed_groups>::data_rec(struct can_frame& receive_frame_,
226 boost::asio::posix::stream_descriptor& stream)
227{
228 // Within a process raw can frames are probably what we are looking for.
229 this->interthread().template publish<line_in_group>(receive_frame_);
230
231 std::string bytes;
232 const int frame_size = sizeof(can_frame);
233
234 for (int i = 0; i < frame_size; ++i)
235 {
236 bytes += *(reinterpret_cast<char*>(&receive_frame_) + i);
237 }
238
239 this->handle_read_success(bytes.size(), bytes);
240
241 boost::asio::async_read(
242 stream, boost::asio::buffer(&receive_frame_, sizeof(receive_frame_)),
243 boost::bind(&CanThread::data_rec, this, boost::ref(receive_frame_), boost::ref(stream)));
244}
245
246#endif
simple exception class for goby applications
Definition exception.h:35
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
Definition group.h:60
CanThread(const goby::middleware::protobuf::CanConfig &config, int index=-1)
Constructs the thread.
Definition can.h:109
void bind(ModemDriverBase &driver, QueueManager &queue_manager)
binds the driver link-layer callbacks to the QueueManager
Definition bind.h:45
constexpr int priority_index
Definition can.h:77
constexpr int pgn_index
Definition can.h:76
constexpr int source_index
Definition can.h:78
void basic_async_write(IOThreadImplementation *this_thread, std::shared_ptr< const goby::middleware::protobuf::IOData > io_msg)
std::uint32_t make_extended_format_can_id(std::uint32_t pgn, std::uint8_t priority, std::uint8_t source=0)
Definition can.h:67
std::tuple< std::uint32_t, std::uint8_t, std::uint8_t > parse_extended_format_can_id(std::uint32_t can_id)
Definition can.h:82
middleware::SimpleThread< Config, detail::InterProcessTag > SimpleThread
Zeromq-backed SimpleThread. Derives from middleware::SimpleThread using InterProcessTag.
The global namespace for the Goby project.
STL namespace.