Apollo application and source code analysis: Monitor monitoring-hardware monitoring-CAN monitoring

Table of contents

basic concept

CAN Card

CAN - raw sockets

ESD CAN Monitoring Analysis

Socket Can Monitoring Analysis


basic concept

CAN Card

First of all, some basic concepts of CAN are required.

CAN is the abbreviation of Controller Area Network (hereinafter referred to as CAN), which is an ISO international standardized serial communication protocol. In the automobile industry, due to the requirements of safety, comfort, convenience, low power consumption, and low cost, various electronic control systems have been developed.

The CAN bus data transmission and reception of the CAN card is completed by the CAN controller and the CAN transceiver. This interface card is widely used in the automotive industry, and is developing rapidly in the fields of industrial control, robotics, medical equipment, and sensors.

The German esd can card ESD CAN-PCIe/402-1 currently used by apollo, and this can card used by apollo1.0-1.5.

The can card will have its own driver, which is generally provided by the equipment manufacturer, so the CAN monitoring here, one of the monitoring is to see if the can driver can output the correct signal.

CAN - raw sockets

If our business software wants to know the device status of the car, it needs to read the status from can. Here, socket can is mainly used, and Socket CAN uses the original socket. This interface allows operations on lower-level protocols, such as IP, ICMP, and so on. Raw sockets are often used to test out new protocol implementations or to access new devices configured in existing services.

The socket workflow is as follows:

Start the server first, create a socket by calling the socket() function, then call the bind() function to associate the socket with the local network address, and then call the listen() function to make the socket ready for listening Prepare and specify the length of its request queue, and then call the accept() function to receive the connection. After the client establishes the socket, it can call connect() to establish a connection with the server. Once the connection is established, the client and the server can send and receive data by calling the recv()/recvfrom() function and send()/sendto function. Finally, after the data transmission is over, both parties call the close() function to close the socket.

Reference Link: Linux SocketCAN programming (C++, enable multi-threaded reception)

Therefore, another monitoring in Apollo's can monitoring is for the link-can's socket communication link.


ESD CAN Monitoring Analysis

class EsdCanMonitor : public RecurrentRunner {
 public:
  EsdCanMonitor();
  void RunOnce(const double current_time) override;
};
void EsdCanMonitor::RunOnce(const double current_time) {
  Component* component = apollo::common::util::FindOrNull(
      *MonitorManager::Instance()->GetStatus()->mutable_components(),
      FLAGS_esdcan_component_name);
  if (component == nullptr) {
    // Canbus is not monitored in current mode, skip.
    return;
  }

  auto* status = component->mutable_other_status();
  status->clear_status();
  EsdCanTest(FLAGS_esdcan_id, status);
}

It can be seen that first of all, it is necessary to judge whether the configuration file is configured with this content. If this monitoring is not configured, return directly.

Then use the function EsdCanTest to check the status of the Can card, so the core is in EsdCanTest.

NTCAN_RESULT EsdCanTest(const int can_id, NTCAN_HANDLE* handle) {
  NTCAN_RESULT ret = canOpen(can_id, 0, 1, 1, 0, 0, handle);
  if (ret == NTCAN_SUCCESS) {
    AINFO << "Successfully opened ESD-CAN device " << can_id;
  } else {
    AERROR << "Failed to open ESD-CAN device " << can_id << ", error: " << ret
           << " (" << StatusString(ret) << ")";
    return ret;
  }

  CAN_IF_STATUS if_status;
  ret = canStatus(*handle, &if_status);
  if (ret != NTCAN_SUCCESS) {
    AERROR << "Cannot get status of ESD-CAN, ret=" << ret << " ("
           << StatusString(ret) << ")";
    return ret;
  }

  NTCAN_BUS_STATISTIC stats;
  ret = canIoctl(*handle, NTCAN_IOCTL_GET_BUS_STATISTIC, &stats);
  if (ret != NTCAN_SUCCESS) {
    AERROR << "NTCAN_IOCTL_GET_BUS_STATISTIC failed for device with error: "
           << ret << " (" << StatusString(ret) << ")";
    return ret;
  }

  NTCAN_CTRL_STATE ctrl_state;
  ret = canIoctl(*handle, NTCAN_IOCTL_GET_CTRL_STATUS, &ctrl_state);
  if (ret != NTCAN_SUCCESS) {
    AERROR << "NTCAN_IOCTL_GET_CTRL_STATUS failed for device with error: "
           << ret << " (" << StatusString(ret) << ")";
    return ret;
  }

  NTCAN_BITRATE bitrate;
  ret = canIoctl(*handle, NTCAN_IOCTL_GET_BITRATE_DETAILS, &bitrate);
  if (ret != NTCAN_SUCCESS) {
    AERROR << "NTCAN_IOCTL_GET_BITRATE_ for device with error: " << ret << " ("
           << StatusString(ret) << ")";
    return ret;
  }
  return ret;
}

void EsdCanTest(const int can_id, ComponentStatus* status) {
  NTCAN_HANDLE handle;
  const NTCAN_RESULT ret = EsdCanTest(can_id, &handle);
  canClose(handle);

  SummaryMonitor::EscalateStatus(
      ret == NTCAN_SUCCESS ? ComponentStatus::OK : ComponentStatus::ERROR,
      StatusString(ret), status);
}
#else
// USE_ESD_CAN is not set, do dummy check.
void EsdCanTest(const int can_id, ComponentStatus* status) {
  SummaryMonitor::EscalateStatus(ComponentStatus::ERROR,
                                 "USE_ESD_CAN is not defined during compiling",
                                 status);
}
#endif

It can be seen here that if it is found that the CAN card is not installed, an error will be reported directly. If it is installed, it will call canopen to open a can device, and then use canstatus and canIoctl to judge the current status of the can device, and finally return the status to ret.

If ret is not NTCAN_SUCCESS, report ERROR, otherwise report OK.

Socket Can Monitoring Analysis

class SocketCanMonitor : public RecurrentRunner {
 public:
  SocketCanMonitor();
  void RunOnce(const double current_time) override;
};

void SocketCanMonitor::RunOnce(const double current_time) {
  auto manager = MonitorManager::Instance();
  Component* component = apollo::common::util::FindOrNull(
      *manager->GetStatus()->mutable_components(),
      FLAGS_socket_can_component_name);
  if (component == nullptr) {
    // Canbus is not monitored in current mode, skip.
    return;
  }
  auto* status = component->mutable_other_status();
  status->clear_status();

  std::string message;
  const bool ret = SocketCanTest(&message);
  SummaryMonitor::EscalateStatus(
      ret ? ComponentStatus::OK : ComponentStatus::ERROR, message, status);
}

The same routine, the first step is to check whether the socket can is configured as a monitoring object, if so, call SocketCanTest, check the socket can, and return ret.

If ret is false, it will report ERROR failure, otherwise it will report OK status.

bool SocketCanTest(std::string* message) {
  const int dev_handler = socket(PF_CAN, SOCK_RAW, CAN_RAW);
  if (dev_handler < 0) {
    *message = "Open can device failed";
    return false;
  }
  const bool ret = SocketCanHandlerTest(dev_handler, message);
  close(dev_handler);
  return ret;
}
  1. socket(PF_CAN, SOCK_RAW, CAN_RAW); is to create a can native socket
  2. Hand over to SocketCanHandlerTest for inspection
// Test Socket CAN on an open handler.
bool SocketCanHandlerTest(const int dev_handler, std::string* message) {
  // init config and state
  // 1. set receive message_id filter, ie white list
  struct can_filter filter[1];
  filter[0].can_id = 0x000;
  filter[0].can_mask = CAN_SFF_MASK;

  int ret = setsockopt(dev_handler, SOL_CAN_RAW, CAN_RAW_FILTER, &filter,
                       sizeof(filter));
  if (ret < 0) {
    *message = "set message filter failed";
    return false;
  }

  // 2. enable reception of can frames.
  const int enable = 1;
  ret = setsockopt(dev_handler, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable,
                   sizeof(enable));
  if (ret < 0) {
    *message = "Enable reception of can frames failed";
    return false;
  }

  struct ifreq ifr;
  std::strncpy(ifr.ifr_name, "can0", IFNAMSIZ);
  if (ioctl(dev_handler, SIOCGIFINDEX, &ifr) < 0) {
    *message = "ioctl failed";
    return false;
  }

  // bind socket to network interface
  struct sockaddr_can addr;
  addr.can_family = AF_CAN;
  addr.can_ifindex = ifr.ifr_ifindex;
  ret = bind(dev_handler, reinterpret_cast<struct sockaddr*>(&addr),
             sizeof(addr));

  if (ret < 0) {
    *message = "bind socket can failed";
    return false;
  }

  return true;
}

 

  1. Set the filter, here only receive the message with the identifier equal to 0x00, if the setting fails, return false, and send out the exception log
  2. Start the reception of the can frame, if the startup fails, return false, and send out the exception log
  3. Map the actual can interface, if it fails, feedback false, and send out the exception log
  4. Bind the socket to the network interface. If the binding fails, return false and send an exception log
  5. Returns true if all of the above are successful

Reference link: Detailed explanation of can port communication_Time fee blog-CSDN blog_can port

Guess you like

Origin blog.csdn.net/qq_32378713/article/details/128074899
Recommended