diff --git a/nebula_common/include/nebula_common/seyond/seyond_common.hpp b/nebula_common/include/nebula_common/seyond/seyond_common.hpp index 6d6ea3cba..9e9ac20ef 100755 --- a/nebula_common/include/nebula_common/seyond/seyond_common.hpp +++ b/nebula_common/include/nebula_common/seyond/seyond_common.hpp @@ -80,6 +80,16 @@ struct SeyondCalibrationConfiguration : public SeyondCalibrationConfigurationBas inline ReturnMode ReturnModeFromStringSeyond( const std::string & return_mode, const SensorModel & sensor_model) { + switch (sensor_model) { + case SensorModel::SEYOND_FALCON_KINETIC: + case SensorModel::SEYOND_ROBIN_W: + if (return_mode == "Strongest") return ReturnMode::STRONGEST; + if (return_mode == "Dual") return ReturnMode::DUAL; + if (return_mode == "DualStrongestLast") return ReturnMode::DUAL_STRONGEST_LAST; + break; + default: + break; + } return ReturnMode::UNKNOWN; } @@ -89,6 +99,16 @@ inline ReturnMode ReturnModeFromStringSeyond( /// @return Corresponding ReturnMode inline ReturnMode ReturnModeFromIntSeyond(const int return_mode, const SensorModel & sensor_model) { + switch (sensor_model) { + case SensorModel::SEYOND_FALCON_KINETIC: + case SensorModel::SEYOND_ROBIN_W: + if (return_mode == 1) return ReturnMode::STRONGEST; + if (return_mode == 2) return ReturnMode::DUAL; + if (return_mode == 3) return ReturnMode::DUAL_STRONGEST_LAST; + break; + default: + break; + } return ReturnMode::UNKNOWN; } @@ -98,11 +118,20 @@ inline ReturnMode ReturnModeFromIntSeyond(const int return_mode, const SensorMod /// @return Corresponding return mode number for the hardware inline int IntFromReturnModeSeyond(const ReturnMode return_mode, const SensorModel & sensor_model) { - + switch (sensor_model) { + case SensorModel::SEYOND_FALCON_KINETIC: + case SensorModel::SEYOND_ROBIN_W: + if (return_mode == ReturnMode::STRONGEST) return 1; + if (return_mode == ReturnMode::DUAL) return 2; + if (return_mode == ReturnMode::DUAL_STRONGEST_LAST) return 3; + break; + default: + break; + } return -1; } } // namespace drivers } // namespace nebula -#endif // NEBULA_SEYOND_COMMON_H \ No newline at end of file +#endif // NEBULA_SEYOND_COMMON_H diff --git a/nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_seyond/seyond_hw_interface.hpp b/nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_seyond/seyond_hw_interface.hpp index 7b11b4a26..5fce7f062 100644 --- a/nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_seyond/seyond_hw_interface.hpp +++ b/nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_seyond/seyond_hw_interface.hpp @@ -27,38 +27,49 @@ #include #include -#include #include namespace nebula { namespace drivers { +constexpr uint16_t SeyondTcpCommandPort = 8001; +constexpr uint16_t SeyondHttpCommandPort = 8010; /// @brief Hardware interface of seyond driver class SeyondHwInterface { private: std::unique_ptr<::drivers::common::IoContext> cloud_io_context_; - std::shared_ptr m_owned_ctx; std::unique_ptr<::drivers::udp_driver::UdpDriver> cloud_udp_driver_; std::shared_ptr sensor_configuration_; std::function & buffer)> cloud_packet_callback_; /**This function pointer is called when the scan is complete*/ - int prev_phase_{}; - int target_model_no; + std::shared_ptr m_owned_ctx_; + std::unique_ptr<::drivers::tcp_driver::TcpDriver> command_tcp_driver_; + std::shared_ptr parent_node_logger_; /// @brief Printing the string to RCLCPP_INFO_STREAM /// @param info Target string void PrintInfo(std::string info); + /// @brief Printing the string to RCLCPP_ERROR_STREAM /// @param error Target string void PrintError(std::string error); + /// @brief Printing the string to RCLCPP_DEBUG_STREAM /// @param debug Target string void PrintDebug(std::string debug); + /// @brief Send TCP command to the device and return its response as string + /// @param commad payload of the command to be sent + /// @return string response from the device + std::string SendCommand(std::string commad); + + /// @brief Start UDP stream from the device + Status StartUdpStreaming(); + public: /// @brief Constructor SeyondHwInterface(); @@ -66,6 +77,10 @@ class SeyondHwInterface /// @brief Destructor ~SeyondHwInterface(); + /// @brief Initializing tcp_driver for TCP communication + /// @return Resulting status + Status InitializeTcpDriver(); + /// @brief Callback function to receive the Cloud Packet data from the UDP Driver /// @param buffer Buffer containing the data received from the UDP socket void ReceiveSensorPacketCallback(std::vector & buffer); @@ -93,11 +108,11 @@ class SeyondHwInterface /// @param sensor_configuration SensorConfiguration for this interface /// @return Resulting status Status SetSensorConfiguration( - std::shared_ptr sensor_configuration); + const std::shared_ptr& sensor_configuration); - /// @brief Set target model number - /// @param model Model number - void SetTargetModel(int model); + // /// @brief Set target model number + // /// @param model Model number + // void SetTargetModel(nebula::drivers::SensorModel model); /// @brief Registering callback for NebulaPackets /// @param scan_callback Callback function @@ -107,6 +122,24 @@ class SeyondHwInterface /// @brief Setting rclcpp::Logger /// @param node Logger void SetLogger(std::shared_ptr logger); + + /// @brief Display common information acquired from sensor + void DisplayCommonVersion(); + + /// @brief Setting device return mode + /// @param return_mode The mode of return + /// @return Resulting status + Status SetReturnMode(int return_mode); + + + /// @brief Setting PTP profile + /// @param profile profile to be set + /// @return Resulting status + Status SetPtpMode(PtpProfile profile); + + /// @brief validate the current settings then set them + /// @return Resulting status + Status CheckAndSetConfig(); }; } // namespace drivers } // namespace nebula diff --git a/nebula_hw_interfaces/src/nebula_seyond_hw_interfaces/seyond_hw_interface.cpp b/nebula_hw_interfaces/src/nebula_seyond_hw_interfaces/seyond_hw_interface.cpp index bd9737203..d0f70be55 100644 --- a/nebula_hw_interfaces/src/nebula_seyond_hw_interfaces/seyond_hw_interface.cpp +++ b/nebula_hw_interfaces/src/nebula_seyond_hw_interfaces/seyond_hw_interface.cpp @@ -1,21 +1,69 @@ #include "nebula_hw_interfaces/nebula_hw_interfaces_seyond/seyond_hw_interface.hpp" + +#include + namespace nebula { namespace drivers { SeyondHwInterface::SeyondHwInterface() : cloud_io_context_{new ::drivers::common::IoContext(1)}, - m_owned_ctx{new boost::asio::io_context(1)}, - cloud_udp_driver_{new ::drivers::udp_driver::UdpDriver(*cloud_io_context_)} + cloud_udp_driver_{new ::drivers::udp_driver::UdpDriver(*cloud_io_context_)}, + sensor_configuration_(std::make_shared()), + cloud_packet_callback_(nullptr), + m_owned_ctx_{new boost::asio::io_context(1)}, + command_tcp_driver_{new ::drivers::tcp_driver::TcpDriver(m_owned_ctx_)} { } +SeyondHwInterface::~SeyondHwInterface() +{ +} + +Status SeyondHwInterface::InitializeTcpDriver() +{ + // Initialize command sender which communicate with the device via TCP + try { + command_tcp_driver_->init_socket( + sensor_configuration_->sensor_ip, SeyondTcpCommandPort, sensor_configuration_->host_ip, + SeyondTcpCommandPort); + } catch (const std::exception & ex) { + std::stringstream ss; + ss << "SeyondHwInterface::SensorInterfaceStart (init TCP): " << Status::ERROR_1 + << std::endl; + PrintError(ss.str()); + command_tcp_driver_->closeSync(); + return Status::ERROR_1; + } + + DisplayCommonVersion(); + return Status::OK; +} + void SeyondHwInterface::ReceiveSensorPacketCallback(std::vector & buffer) { + cloud_packet_callback_(buffer); } Status SeyondHwInterface::SensorInterfaceStart() { + StartUdpStreaming(); + + // Initialize stream receiver which communicate with the device via UDP + try { + std::cout << "Starting UDP server on: " << *sensor_configuration_ << std::endl; + cloud_udp_driver_->init_receiver( + sensor_configuration_->host_ip, sensor_configuration_->data_port); + cloud_udp_driver_->receiver()->open(); + cloud_udp_driver_->receiver()->bind(); + cloud_udp_driver_->receiver()->asyncReceive( + std::bind(&SeyondHwInterface::ReceiveSensorPacketCallback, this, std::placeholders::_1)); + } catch (const std::exception & ex) { + Status status = Status::UDP_CONNECTION_ERROR; + std::cerr << status << sensor_configuration_->sensor_ip << ", " + << sensor_configuration_->data_port << std::endl; + return status; + } return Status::OK; } @@ -25,44 +73,71 @@ Status SeyondHwInterface::SensorInterfaceStop() } Status SeyondHwInterface::SetSensorConfiguration( - std::shared_ptr sensor_configuration) + const std::shared_ptr& sensor_configuration) { + if (!(sensor_configuration->sensor_model == SensorModel::SEYOND_FALCON_KINETIC || + sensor_configuration->sensor_model == SensorModel::SEYOND_ROBIN_W)) { + return Status::INVALID_SENSOR_MODEL; + } + + sensor_configuration_ = std::static_pointer_cast(sensor_configuration); + return Status::OK; } Status SeyondHwInterface::GetSensorConfiguration( const SensorConfigurationBase & sensor_configuration) { + std::stringstream ss; + ss << sensor_configuration; + PrintDebug(ss.str()); return Status::ERROR_1; } Status SeyondHwInterface::GetCalibrationConfiguration( const CalibrationConfigurationBase & calibration_configuration) { + PrintDebug(calibration_configuration.calibration_file); return Status::ERROR_1; } Status SeyondHwInterface::RegisterScanCallback( std::function &)> scan_callback) { + cloud_packet_callback_ = std::move(scan_callback); return Status::OK; } -void SeyondHwInterface::SetTargetModel(int model) -{ - target_model_no = model; -} +// void SeyondHwInterface::SetTargetModel(nebula::drivers::SensorModel model) +// { +// target_model_no = static_cast(model); +// } void SeyondHwInterface::PrintError(std::string error) { + if (parent_node_logger_) { + RCLCPP_ERROR_STREAM((*parent_node_logger_), error); + } else { + std::cerr << error << std::endl; + } } void SeyondHwInterface::PrintDebug(std::string debug) { + if (parent_node_logger_) { + RCLCPP_DEBUG_STREAM((*parent_node_logger_), debug); + } else { + std::cout << debug << std::endl; + } } void SeyondHwInterface::PrintInfo(std::string info) { +if (parent_node_logger_) { + RCLCPP_INFO_STREAM((*parent_node_logger_), info); + } else { + std::cout << info << std::endl; + } } void SeyondHwInterface::SetLogger(std::shared_ptr logger) @@ -70,5 +145,148 @@ void SeyondHwInterface::SetLogger(std::shared_ptr logger) parent_node_logger_ = logger; } +void SeyondHwInterface::DisplayCommonVersion() +{ + std::stringstream info; + info << "*************** Innovusion Lidar Version Info ***************" <GetIOContext()->stopped()) { + command_tcp_driver_->GetIOContext()->restart(); + } + + std::vector payload; + std::copy(command.begin(), command.end(), std::back_inserter(payload)); + payload.emplace_back('\n'); + payload.emplace_back(0); + + command_tcp_driver_->asyncSend(payload); + command_tcp_driver_->run(); + + boost::asio::streambuf rec_buf; + command_tcp_driver_->socket()->receive(rec_buf); + std::string rec_str = boost::asio::buffer_cast(rec_buf.data()); + command_tcp_driver_->closeSync(); + return rec_str; +} + +Status SeyondHwInterface::StartUdpStreaming() +{ + // XXX: This streaming-starting command need to be sent via HTTP as of now + auto stream_starter_ctx = std::make_shared(1); + auto stream_starter_http_driver = std::unique_ptr<::drivers::tcp_driver::HttpClientDriver>( + new ::drivers::tcp_driver::HttpClientDriver(stream_starter_ctx)); + + // Initialize stream starter which communicate with the device via HTTP + try { + stream_starter_http_driver->init_client(sensor_configuration_->sensor_ip, SeyondHttpCommandPort); + } catch (const std::exception & ex) { + std::stringstream ss; + ss << "SeyondHwInterface::SensorInterfaceStart (init HTTP): " << Status::HTTP_CONNECTION_ERROR + << std::endl; + PrintError(ss.str()); + return Status::HTTP_CONNECTION_ERROR; + } + + // XXX: Currently, one port is used to receive data, status, and message + uint16_t data_port = sensor_configuration_->data_port; + uint16_t status_port = sensor_configuration_->data_port; + uint16_t message_port = sensor_configuration_->data_port; + Status stat; + switch (sensor_configuration_->sensor_model) + { + case SensorModel::SEYOND_FALCON_KINETIC: { + std::string payload = std::to_string(data_port) + "," + std::to_string(message_port) + "," + + std::to_string(status_port); + std::string http_command = "/command/?set_udp_ports_ip=" + payload; + auto response = stream_starter_http_driver->get(http_command); + stat = Status::OK; + break; + } + case SensorModel::SEYOND_ROBIN_W: { + std::string payload = "{\"ip\":\"" + sensor_configuration_->sensor_ip + + "\",\"port_data\":" + std::to_string(data_port) + + ",\"port_message\":" + std::to_string(message_port) + + ",\"port_status\":" + std::to_string(status_port) + "}"; + stream_starter_http_driver->post( "/v1/lidar/force_udp_ports_ip", payload); + stat = Status::OK; + break; + } + default: { + PrintError("Invalid sensor model was specified"); + stat = Status::INVALID_SENSOR_MODEL; + } + } + stream_starter_http_driver->client()->close(); + + return stat; +} + +Status SeyondHwInterface::SetReturnMode(int return_mode) +{ + std::string command = "set_i_config manufacture multiple_return_mode " + std::to_string(return_mode); + SendCommand(command); + return Status::OK; +} + +Status SeyondHwInterface::SetPtpMode(PtpProfile profile) +{ + std::string command = "set_i_config time ntp_en 0"; // Disable NTP first just in case + SendCommand(command); + + command = "set_i_config time ptp_en 1"; + SendCommand(command); + + // Show messages regarding support status + if (profile != PtpProfile::IEEE_1588v2 && profile != PtpProfile::IEEE_802_1AS_AUTO) { + PrintInfo("Unsupported PTP profile was selected. Falling back to IEEE 1588v2"); + } else if (profile == PtpProfile::IEEE_802_1AS_AUTO) { + PrintInfo("\"automotive\" was specified as PTP profile. " + "Currently, (PTP domain | PTP transport type | PTP switch type) " + "specification is not supported."); + } + + int is_gptp = (profile == PtpProfile::IEEE_802_1AS_AUTO); + command = "set_i_config time ptp_automotive " + std::to_string(is_gptp); + SendCommand(command); + + return Status::OK; +} + +Status SeyondHwInterface::CheckAndSetConfig() +{ + Status ret = Status::ERROR_1; + // Set return mode + auto return_mode_int = nebula::drivers::IntFromReturnModeSeyond( + sensor_configuration_->return_mode, sensor_configuration_->sensor_model); + if (return_mode_int < 0) { + PrintError( + "Invalid Return Mode for this sensor. Please check your settings. Falling back to Dual " + "mode."); + return_mode_int = nebula::drivers::IntFromReturnModeSeyond(ReturnMode::DUAL, + sensor_configuration_->sensor_model); + + } + ret = SetReturnMode(return_mode_int); + if (ret != Status::OK) { + return ret; + } + + // Set PTP mode + ret = SetPtpMode(sensor_configuration_->ptp_profile); + if (ret != Status::OK) { + return ret; + } + + return Status::OK; +} + } // namespace drivers } // namespace nebula diff --git a/nebula_ros/include/nebula_ros/seyond/hw_interface_wrapper.hpp b/nebula_ros/include/nebula_ros/seyond/hw_interface_wrapper.hpp index bf5a976f4..741326706 100755 --- a/nebula_ros/include/nebula_ros/seyond/hw_interface_wrapper.hpp +++ b/nebula_ros/include/nebula_ros/seyond/hw_interface_wrapper.hpp @@ -20,6 +20,7 @@ class SeyondHwInterfaceWrapper public: SeyondHwInterfaceWrapper( rclcpp::Node * const parent_node, std::shared_ptr & config); + SeyondHwInterfaceWrapper() = default; void OnConfigChange(const std::shared_ptr & new_config); @@ -28,10 +29,10 @@ class SeyondHwInterfaceWrapper std::shared_ptr HwInterface() const; private: - std::shared_ptr hw_interface_; + std::shared_ptr hw_interface_{}; rclcpp::Logger logger_; - nebula::Status status_; - bool setup_sensor_; + nebula::Status status_{}; + bool setup_sensor_{}; }; } // namespace ros } // namespace nebula diff --git a/nebula_ros/include/nebula_ros/seyond/seyond_ros_wrapper.hpp b/nebula_ros/include/nebula_ros/seyond/seyond_ros_wrapper.hpp index b04e72083..ae19e153d 100644 --- a/nebula_ros/include/nebula_ros/seyond/seyond_ros_wrapper.hpp +++ b/nebula_ros/include/nebula_ros/seyond/seyond_ros_wrapper.hpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace nebula { @@ -57,19 +58,19 @@ class SeyondRosWrapper final : public rclcpp::Node Status ValidateAndSetConfig(std::shared_ptr & new_config); - Status wrapper_status_; + Status wrapper_status_{}; std::shared_ptr sensor_cfg_ptr_{}; - std::mutex mtx_config_; + std::mutex mtx_config_{}; mt_queue> packet_queue_; /// @brief Thread to isolate decoding from receiving - std::thread decoder_thread_; + std::thread decoder_thread_{}; rclcpp::Subscription::SharedPtr packets_sub_{}; - bool launch_hw_; + bool launch_hw_{}; std::optional hw_interface_wrapper_; std::optional hw_monitor_wrapper_; diff --git a/nebula_ros/src/seyond/hw_interface_wrapper.cpp b/nebula_ros/src/seyond/hw_interface_wrapper.cpp index 2223fcee0..01e3411fb 100644 --- a/nebula_ros/src/seyond/hw_interface_wrapper.cpp +++ b/nebula_ros/src/seyond/hw_interface_wrapper.cpp @@ -7,14 +7,16 @@ namespace ros SeyondHwInterfaceWrapper::SeyondHwInterfaceWrapper( rclcpp::Node * const parent_node, std::shared_ptr & config) -: hw_interface_(new SeyondHwInterface()), +: hw_interface_(std::make_shared()), logger_(parent_node->get_logger().get_child("HwInterface")), - status_(Status::NOT_INITIALIZED) + status_(Status::NOT_INITIALIZED), + setup_sensor_(false) { setup_sensor_ = parent_node->declare_parameter("setup_sensor", true, param_read_only()); bool retry_connect = parent_node->declare_parameter("retry_hw", true, param_read_only()); - status_ = hw_interface_->SetSensorConfiguration(config); + status_ = hw_interface_->SetSensorConfiguration( + std::static_pointer_cast(config)); if (status_ != Status::OK) { throw std::runtime_error( @@ -27,18 +29,24 @@ SeyondHwInterfaceWrapper::SeyondHwInterfaceWrapper( int retry_count = 0; // NOTE: for when TP interface is implemented - // while (true) - // { - // status_ = hw_interface_->InitializeTcpDriver(); - // if (status_ == Status::OK || !retry_connect) - // { - // break; - // } + while (true) + { + status_ = hw_interface_->InitializeTcpDriver(); + if (status_ == Status::OK || !retry_connect) + { + break; + } - // retry_count++; - // std::this_thread::sleep_for(std::chrono::milliseconds(8000)); // >5000 - // RCLCPP_WARN_STREAM(logger_, status_ << ". Retry #" << retry_count); - // } + retry_count++; + std::this_thread::sleep_for(std::chrono::milliseconds(8000)); // >5000 + RCLCPP_WARN_STREAM(logger_, status_ << ". Retry #" << retry_count); + } + + if (status_ == Status::OK) { + if (setup_sensor_) { + hw_interface_->CheckAndSetConfig(); + } + } status_ = Status::OK; } @@ -46,10 +54,11 @@ SeyondHwInterfaceWrapper::SeyondHwInterfaceWrapper( void SeyondHwInterfaceWrapper::OnConfigChange( const std::shared_ptr & new_config) { - hw_interface_->SetSensorConfiguration(new_config); - // if (setup_sensor_) { - // hw_interface_->CheckAndSetConfig(); - // } + hw_interface_->SetSensorConfiguration( + std::static_pointer_cast(new_config)); + if (setup_sensor_) { + hw_interface_->CheckAndSetConfig(); + } } Status SeyondHwInterfaceWrapper::Status() diff --git a/nebula_ros/src/seyond/seyond_ros_wrapper.cpp b/nebula_ros/src/seyond/seyond_ros_wrapper.cpp index 0eae442fb..8563e8122 100644 --- a/nebula_ros/src/seyond/seyond_ros_wrapper.cpp +++ b/nebula_ros/src/seyond/seyond_ros_wrapper.cpp @@ -88,7 +88,7 @@ nebula::Status SeyondRosWrapper::DeclareAndGetSensorConfigParams() config.sensor_model = drivers::SensorModelFromString(_sensor_model); auto _return_mode = declare_parameter("return_mode", "", param_read_write()); - config.return_mode = drivers::ReturnModeFromString(_return_mode); + config.return_mode = drivers::ReturnModeFromStringSeyond(_return_mode, config.sensor_model); config.host_ip = declare_parameter("host_ip", "255.255.255.255", param_read_only()); config.sensor_ip = @@ -276,7 +276,8 @@ rcl_interfaces::msg::SetParametersResult SeyondRosWrapper::OnParameterChange( } if (_return_mode.length() > 0) - new_cfg.return_mode = nebula::drivers::ReturnModeFromString(_return_mode); + new_cfg.return_mode = nebula::drivers::ReturnModeFromStringSeyond(_return_mode, + sensor_cfg_ptr_->sensor_model); // //////////////////////////////////////// // Validate and use new config