-1

I have 3 devices which send 8 bytes of data over CAN interface. To read the buffer from CAN I am using a while loop which looks something like this:

void CanServer::ReadFromCAN() {
     data_from_buffer_.clear();
     can_frame frame;
     read_can_port_ = read(soc_, &frame, sizeof(struct can_frame));

     if (read_can_port_ < 0) return;

     id_ = frame.can_id&0x1FFFFFFF;
     dlc_ = frame.can_dlc;

     for (const auto& byte : frame.data) 
          data_from_buffer_.push_back(byte);
}

while (ros::ok()) {
       std_msgs::Int32MultiArray tachometer_array;
       std::vector<__u8> data_from_can;

       /***
        * Read for the Radar1
        */

       this->ReadFromCAN();

       if (read_can_port_ < 0) continue;

       //ROS_INFO("Read from CAN");
       if (id_ == can_id::RadarFrame1)
           for (int i = 0; i < dlc_; i++) {
                radar1_bytes_[i] = data_from_buffer_[i];
                radar1_buffer_.push_back(data_from_buffer_[i]);
           }

           if (IsMagicWord(radar1_bytes_, 0)) {
               frame_id = "radar1_link";
               this->PulbishRadarPCL(frame_id, radar1_pub_, radar1_buffer_, 0);
               radar1_buffer_.clear();
               canFrame_.can_dlc = 0;
           }
}

    if (id_ == can_id::RadarFrame2) {
        for (int i = 0; i < dlc_; i++) {
            radar2_bytes_[i] = data_from_buffer_[i];
            radar2_buffer_.push_back(data_from_buffer_[i]);
        }

        if (IsMagicWord(radar2_bytes_, 1)) {
            frame_id = "radar2_link";
            this->PulbishRadarPCL(frame_id, radar2_pub_, radar2_buffer_, 1);
            radar2_buffer_.clear();
            canFrame_.can_dlc = 0;
        }
    }

    if (id_ == can_id::RadarFrame3) {
        for (int i = 0; i < dlc_; i++) {
            radar3_bytes_[i] = data_from_buffer_[i];
            radar3_buffer_.push_back(data_from_buffer_[i]);
        }

        if (IsMagicWord(radar3_bytes_, 2)) {
            frame_id = "radar3_link";
            this->PulbishRadarPCL(frame_id, radar3_pub_, radar3_buffer_, 2);
            radar3_buffer_.clear();
            canFrame_.can_dlc = 0;
        }
    }

    rate.sleep();
}

Where rate.sleep() is similar to sleep() function in C++.

Right now, I am running this while loop in 5 MHz however I think this is an overkill and I am getting almost 100% CPU usage on a 1 core.

I tried to play around with the delay time but I think this is highly inefficient and I wonder is there any other way to handle this?

TheArchitect
  • 1,160
  • 4
  • 15
  • 26
aikhs
  • 949
  • 2
  • 8
  • 20
  • `for (const auto& byte : frame.data) data_from_buffer_.push_back(byte);` should be replaced with `data_from_buffer_.assign(std::begin(frame.data), std::end(frame.data));` for a teeny performance boost. – Mooing Duck Aug 08 '20 at 21:42

1 Answers1

0

It turns out that poll is what you need. Here is my example.

  1. First, create a pollfd structure from <poll.h> header in Linux. I have decided to create a class member but you can create however you like:

     pollfd poll_;
     poll_.fd = soc_;
     poll_.events = POLLIN;
     poll_.revents = 0;
    

Here, soc_ is a socket and POLLIN means that you want to read from the socket.

  1. Then, in my while loop, instead of delaying I just used this function at the beginning of my while loop:

     poll_int = poll(&poll_, 1, 100);
     if (poll_int <= 0) continue;
    

So poll() function returns value of 1 if the read was succesful and I made a timeout of 100ms (just a random number, I know that the data are coming at much higher rate)

With that, you will only read the data from socket whenever poll returns a value greater that 0.

Results? 3% CPU usage and if you want to add more data into your socket flow, poll will optimize for you so this is a scalable way of reading something like CAN bus.

aikhs
  • 949
  • 2
  • 8
  • 20