blob: d3506726eec9ecbf97f07c546dcd5997fc49eaff [file] [log] [blame]
#include "frc971/can_logger/can_logger.h"
#include "absl/flags/flag.h"
ABSL_FLAG(bool, poll, false,
"If true, poll the CAN bus every 100ms. If false, wake up for "
"each frame and publish it.");
ABSL_FLAG(int32_t, priority, 10,
"If --poll is not set, set the realtime priority to this.");
ABSL_FLAG(int32_t, affinity, -1, "If positive, pin to this core.");
namespace frc971::can_logger {
CanLogger::CanLogger(aos::ShmEventLoop *event_loop,
std::string_view channel_name,
std::string_view interface_name)
: shm_event_loop_(event_loop),
fd_(socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)),
frames_sender_(shm_event_loop_->MakeSender<CanFrame>(channel_name)) {
// TODO(max): Figure out a proper priority
if (!absl::GetFlag(FLAGS_poll)) {
shm_event_loop_->SetRuntimeRealtimePriority(absl::GetFlag(FLAGS_priority));
}
if (absl::GetFlag(FLAGS_affinity) >= 0) {
shm_event_loop_->SetRuntimeAffinity(
aos::MakeCpusetFromCpus({absl::GetFlag(FLAGS_affinity)}));
}
struct ifreq ifr;
strcpy(ifr.ifr_name, interface_name.data());
PCHECK(ioctl(fd_.get(), SIOCGIFINDEX, &ifr) == 0)
<< "Failed to get index for interface " << interface_name;
int enable_canfd = true;
PCHECK(setsockopt(fd_.get(), SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd,
sizeof(enable_canfd)) == 0)
<< "Failed to enable CAN FD";
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
PCHECK(bind(fd_.get(), reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr)) == 0)
<< "Failed to bind socket to interface " << interface_name;
int recieve_buffer_size;
socklen_t opt_size = sizeof(recieve_buffer_size);
PCHECK(getsockopt(fd_.get(), SOL_SOCKET, SO_RCVBUF, &recieve_buffer_size,
&opt_size) == 0);
CHECK_EQ(opt_size, sizeof(recieve_buffer_size));
VLOG(0) << "CAN recieve bufffer is " << recieve_buffer_size << " bytes large";
if (absl::GetFlag(FLAGS_poll)) {
aos::TimerHandler *timer_handler =
shm_event_loop_->AddTimer([this]() { Poll(); });
timer_handler->set_name("CAN logging Loop");
timer_handler->Schedule(event_loop->monotonic_now(), kPollPeriod);
} else {
shm_event_loop_->epoll()->OnReadable(fd_.get(), [this]() { Poll(); });
}
}
void CanLogger::Poll() {
VLOG(2) << "Polling";
int frames_read = 0;
while (ReadFrame()) {
frames_read++;
}
VLOG(1) << "Read " << frames_read << " frames to end of buffer";
}
bool CanLogger::ReadFrame() {
errno = 0;
struct canfd_frame frame;
ssize_t bytes_read = read(fd_.get(), &frame, sizeof(struct canfd_frame));
if (bytes_read < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// There are no more frames sitting in the recieve buffer.
return false;
}
VLOG(2) << "Read " << bytes_read << " bytes";
PCHECK(bytes_read > 0);
PCHECK(bytes_read == static_cast<ssize_t>(CAN_MTU) ||
bytes_read == static_cast<ssize_t>(CANFD_MTU))
<< "Incomplete can frame";
struct timeval tv;
PCHECK(ioctl(fd_.get(), SIOCGSTAMP, &tv) == 0)
<< "Failed to get timestamp of CAN frame";
aos::Sender<CanFrame>::Builder builder = frames_sender_.MakeBuilder();
auto frame_data = builder.fbb()->CreateVector<uint8_t>(frame.data, frame.len);
CanFrame::Builder can_frame_builder = builder.MakeBuilder<CanFrame>();
can_frame_builder.add_can_id(frame.can_id);
can_frame_builder.add_flags(frame.flags);
can_frame_builder.add_data(frame_data);
can_frame_builder.add_realtime_timestamp_ns(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(tv.tv_sec) +
std::chrono::microseconds(tv.tv_usec))
.count());
builder.CheckOk(builder.Send(can_frame_builder.Finish()));
return true;
}
} // namespace frc971::can_logger