Goby3 3.2.3
2025.05.13
Loading...
Searching...
No Matches
can.h
Go to the documentation of this file.
1// Copyright 2020-2023:
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
51namespace goby
52{
53namespace middleware
54{
55class Group;
56}
57} // namespace goby
58
59namespace goby
60{
61namespace middleware
62{
63namespace io
64{
65inline std::uint32_t make_extended_format_can_id(std::uint32_t pgn, std::uint8_t priority,
66 std::uint8_t source = 0)
67{
68 return (pgn & 0x1FFFF) << 8 | (priority & 0x7) << 26 | CAN_EFF_FLAG | (source & 0xFF);
69}
70
71// tuple of pgn, priority, source
72namespace can_id
73{
74constexpr int pgn_index{0};
75constexpr int priority_index{1};
76constexpr int source_index{2};
77} // namespace can_id
78
79inline std::tuple<std::uint32_t, std::uint8_t, std::uint8_t>
80parse_extended_format_can_id(std::uint32_t can_id)
81{
82 return std::make_tuple((can_id >> 8) & 0x1FFFF, (can_id >> 26) & 0x7, can_id & 0xFF);
83}
84
85template <const goby::middleware::Group& line_in_group,
86 const goby::middleware::Group& line_out_group,
87 // by default publish all incoming traffic to interprocess for logging
89 // but only subscribe on interthread for outgoing traffic
90 PubSubLayer subscribe_layer = PubSubLayer::INTERTHREAD,
91 template <class> class ThreadType = goby::middleware::SimpleThread,
92 bool use_indexed_groups = false>
94 : public detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
95 goby::middleware::protobuf::CanConfig,
96 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>
97{
98 using Base =
99 detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
101 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>;
102
103 public:
108 : Base(config, index, std::string("can: ") + config.interface())
109 {
111 this->interthread().template publish<line_in_group>(ready);
112 }
113
114 ~CanThread() override {}
115
116 private:
117 void async_read() override;
118 void async_write(std::shared_ptr<const goby::middleware::protobuf::IOData> io_msg) override
119 {
120 detail::basic_async_write(this, io_msg);
121 }
122
123 void open_socket() override;
124
125 void data_rec(struct can_frame& receive_frame_, boost::asio::posix::stream_descriptor& stream);
126
127 private:
128 struct can_frame receive_frame_;
129};
130} // namespace io
131} // namespace middleware
132} // namespace goby
133
134template <const goby::middleware::Group& line_in_group,
135 const goby::middleware::Group& line_out_group,
137 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
138 bool use_indexed_groups>
139void goby::middleware::io::CanThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
140 ThreadType, use_indexed_groups>::open_socket()
141{
142 int can_socket;
143
144 struct sockaddr_can addr_
145 {
146 };
147 struct can_frame receive_frame_;
148 struct ifreq ifr_;
149 can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
150
151 std::vector<struct can_filter> filters;
152
153 for (auto x : this->cfg().filter())
154 {
155 auto id = x.can_id();
156 auto mask = x.has_can_mask_custom() ? x.can_mask_custom() : x.can_mask();
157
158 filters.push_back({id, mask});
159 }
160
161 for (std::uint32_t x : this->cfg().pgn_filter())
162 {
163 constexpr std::uint32_t one_byte = 8; // bits
164 auto id = x << one_byte;
165 constexpr auto mask = protobuf::CanConfig::CanFilter::PGNOnly; // PGN mask
166 filters.push_back({id, mask});
167 }
168
169 if (filters.size())
170 {
171 setsockopt(can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, filters.data(),
172 sizeof(can_filter) * filters.size());
173 }
174 std::strcpy(ifr_.ifr_name, this->cfg().interface().c_str());
175
176 ioctl(can_socket, SIOCGIFINDEX, &ifr_);
177
178 addr_.can_family = AF_CAN;
179 addr_.can_ifindex = ifr_.ifr_ifindex;
180 if (bind(can_socket, (struct sockaddr*)&addr_, sizeof(addr_)) < 0)
181 throw(goby::Exception(std::string("Error in socket bind to interface ") +
182 this->cfg().interface() + ": " + std::strerror(errno)));
183
184 this->mutable_socket().assign(can_socket);
185
186 this->interthread().template subscribe<line_out_group, can_frame>(
187 [this](const can_frame& frame)
188 {
189 auto io_msg = std::make_shared<goby::middleware::protobuf::IOData>();
190 std::string& bytes = *io_msg->mutable_data();
191
192 const int frame_size = sizeof(can_frame);
193
194 for (int i = 0; i < frame_size; ++i)
195 {
196 bytes += *(reinterpret_cast<const char*>(&frame) + i);
197 }
198 this->write(io_msg);
199 });
200}
201
202template <const goby::middleware::Group& line_in_group,
203 const goby::middleware::Group& line_out_group,
205 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
206 bool use_indexed_groups>
207void goby::middleware::io::CanThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
208 ThreadType, use_indexed_groups>::async_read()
209{
210 boost::asio::async_read(this->mutable_socket(),
211 boost::asio::buffer(&receive_frame_, sizeof(receive_frame_)),
212 boost::bind(&CanThread::data_rec, this, boost::ref(receive_frame_),
213 boost::ref(this->mutable_socket())));
214}
215
216template <const goby::middleware::Group& line_in_group,
217 const goby::middleware::Group& line_out_group,
219 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
220 bool use_indexed_groups>
222 line_in_group, line_out_group, publish_layer, subscribe_layer, ThreadType,
223 use_indexed_groups>::data_rec(struct can_frame& receive_frame_,
224 boost::asio::posix::stream_descriptor& stream)
225{
226 // Within a process raw can frames are probably what we are looking for.
227 this->interthread().template publish<line_in_group>(receive_frame_);
228
229 std::string bytes;
230 const int frame_size = sizeof(can_frame);
231
232 for (int i = 0; i < frame_size; ++i)
233 {
234 bytes += *(reinterpret_cast<char*>(&receive_frame_) + i);
235 }
236
237 this->handle_read_success(bytes.size(), bytes);
238
239 boost::asio::async_read(
240 stream, boost::asio::buffer(&receive_frame_, sizeof(receive_frame_)),
241 boost::bind(&CanThread::data_rec, this, boost::ref(receive_frame_), boost::ref(stream)));
242}
243
244#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
Implements Thread for a three layer middleware setup ([ intervehicle [ interprocess [ interthread ] ]...
CanThread(const goby::middleware::protobuf::CanConfig &config, int index=-1)
Constructs the thread.
Definition can.h:107
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:75
constexpr int pgn_index
Definition can.h:74
constexpr int source_index
Definition can.h:76
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:65
std::tuple< std::uint32_t, std::uint8_t, std::uint8_t > parse_extended_format_can_id(std::uint32_t can_id)
Definition can.h:80
The global namespace for the Goby project.
STL namespace.