diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 3b015ffc..31c3c8cf 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,4 +1,5 @@ ARG ROS_VERSION=noetic +ARG BUILD_TYPE=git #==================================================================== # Preliminary image with dependencies @@ -70,14 +71,14 @@ RUN groupadd --gid $USER_GID $USERNAME \ USER $USERNAME # Install the default ROS entrypoint -COPY --chmod=0755 ros_entrypoint.sh /home/$USERNAME/ros_entrypoint.sh +COPY --chmod=0755 .docker/ros_entrypoint.sh /home/$USERNAME/ros_entrypoint.sh ENTRYPOINT [ "/home/ros/ros_entrypoint.sh" ] CMD ["/bin/bash"] #==================================================================== -# Intermediate image with cloned Git repos +# Load cloned Git repos into image #==================================================================== -FROM ci as clone +FROM ci as clone_git ARG WS_DIR=/home/$USERNAME/ros_ws ONBUILD WORKDIR ${WS_DIR}/src @@ -90,16 +91,33 @@ ONBUILD RUN echo "Cloning from ${ARENA_CAMERA_ROS_BRANCH} branch ${ARENA_CAMERA_ ONBUILD ADD https://api.github.com/repos/apl-ocean-engineering/arena_camera_ros/git/refs/heads/${ARENA_CAMERA_ROS_BRANCH} /tmp/version.json ONBUILD RUN git clone --depth 1 -b ${ARENA_CAMERA_ROS_BRANCH} ${ARENA_CAMERA_ROS_REPO} +ONBUILD RUN vcs import --shallow --skip-existing < arena_camera_ros/arena_camera_ros.repos + + #==================================================================== -# Bring in ROS dependencies but don't buil +# Load copy of local source into image +#==================================================================== +FROM ci as clone_local + +ARG WS_DIR=/home/$USERNAME/ros_ws +ONBUILD WORKDIR ${WS_DIR}/src + +# Manually install dependencies to prevent hitting the network if only the +# local source has changed +ONBUILD RUN git clone --depth 1 -b ros1 https://github.com/apl-ocean-engineering/imaging_msgs.git + +ONBUILD RUN echo "Adding local copy of arena_camera_ros to Docker image" +ONBUILD ADD arena_camera ${WS_DIR}/src/arena_camera_ros/arena_camera +ONBUILD ADD camera_control_msgs ${WS_DIR}/src/arena_camera_ros/camera_control_msgs +ONBUILD ADD arena_camera_ros.repos ${WS_DIR}/src/arena_camera_ros/arena_camera_ros.repos + +#==================================================================== +# Bring in ROS dependencies but don't build #==================================================================== # -FROM clone as prebuild +FROM clone_${BUILD_TYPE} as prebuild ARG WS_DIR -WORKDIR ${WS_DIR}/src - -RUN vcs import --shallow --skip-existing < arena_camera_ros/arena_camera_ros.repos WORKDIR ${WS_DIR} diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index 89dadf1f..c03fee73 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -3,23 +3,34 @@ services: arena_camera: build: - dockerfile: Dockerfile - image: ghcr.io/apl-ocean-engineering/arena_camera_ros:latest + context: .. + dockerfile: .docker/Dockerfile + image: ghcr.io/apl-ocean-engineering/arena_camera_ros:${IMAGE_TAG:-latest} stdin_open: true tty: true network_mode: host - # Run with the local checkout bind-mounted into the workspace - build_local: + # Prebuild includes rosdep and vcs import + # (basically, everything before "catkin build") + prebuild: + extends: arena_camera + # Local-only image + image: arena_camera_ros:prebuild build: - dockerfile: Dockerfile target: prebuild - image: ghcr.io/apl-ocean-engineering/arena_camera_ros/build:latest - stdin_open: true - tty: true - network_mode: host command: /bin/bash + + # Prebuild with _this_ copy of the repo mounted + # + mount_local: + extends: prebuild volumes: - type: bind source: .. target: /home/ros/ros_ws/src/arena_camera_ros + + build_local: + extends: arena_camera + build: + args: + BUILD_TYPE: local diff --git a/arena_camera/cfg/ArenaCamera.cfg b/arena_camera/cfg/ArenaCamera.cfg index 34098580..4e8b526a 100644 --- a/arena_camera/cfg/ArenaCamera.cfg +++ b/arena_camera/cfg/ArenaCamera.cfg @@ -19,6 +19,6 @@ gen.add("exposure_ms", double_t, SensorLevels.RECONFIGURE_RUNNING, "Exposu gen.add("auto_gain", bool_t, SensorLevels.RECONFIGURE_RUNNING, "Use camera auto-gain.", True) gen.add("gain", double_t, SensorLevels.RECONFIGURE_RUNNING, "Camera gain as a pct [0.0, 1.0]", 0.5, 0.0, 1.0) -gen.add("gamma", double_t, SensorLevels.RECONFIGURE_RUNNING, "Camera gamma [0.0, 5.0]", 2.2, 0.0, 5.0) +gen.add("gamma", double_t, SensorLevels.RECONFIGURE_STOP, "Camera gamma [0.0, 5.0]", 2.2, 0.0, 5.0) exit(gen.generate(PACKAGE, "arena_camera", "ArenaCamera")) diff --git a/arena_camera/include/arena_camera/arena_camera_nodelet.h b/arena_camera/include/arena_camera/arena_camera_nodelet.h index efefe3d9..e25f25a4 100644 --- a/arena_camera/include/arena_camera/arena_camera_nodelet.h +++ b/arena_camera/include/arena_camera/arena_camera_nodelet.h @@ -99,6 +99,8 @@ class ArenaCameraNodeletBase : public nodelet::Nodelet { void stopStreaming(); protected: + bool tryConnect(); + /** * Creates the camera instance either by UserDeviceId, SerialNumber, * or taking the first auto-detected camera. @@ -116,7 +118,8 @@ class ArenaCameraNodeletBase : public nodelet::Nodelet { /// Virtual callback for node initialization _after_ Node::onInit() /// Only called if that initialization/configuration is successful. - virtual void onSuccessfulInit(){}; + virtual void onCameraConnect(){}; + virtual void onCameraDisconnect(){}; // === Functions to set/get ImageEncoding === @@ -248,13 +251,11 @@ class ArenaCameraNodeletBase : public nodelet::Nodelet { ArenaCameraParameter arena_camera_parameter_set_; - std::unique_ptr it_; + std::unique_ptr image_transport_; image_transport::CameraPublisher img_raw_pub_; std::shared_ptr camera_info_manager_; - std::vector sampling_indices_; - boost::recursive_mutex device_mutex_; // Internal cache for exposure and gain, updated by callback @@ -290,7 +291,7 @@ class ArenaCameraStreamingNodelet : public ArenaCameraNodeletBase { ArenaCameraStreamingNodelet(); virtual ~ArenaCameraStreamingNodelet(); - void onSuccessfulInit() override; + void onCameraConnect() override; protected: typedef std::function ImageCallback_t; @@ -320,7 +321,7 @@ class ArenaCameraPolledNodelet : public ArenaCameraNodeletBase { ArenaCameraPolledNodelet(); virtual ~ArenaCameraPolledNodelet(); - void onSuccessfulInit() override; + void onCameraConnect() override; /// Callback for the grab images action /// @param goal the goal diff --git a/arena_camera/launch/arena_camera_nodelet.launch b/arena_camera/launch/arena_camera_nodelet.launch index 0101042d..3c161353 100644 --- a/arena_camera/launch/arena_camera_nodelet.launch +++ b/arena_camera/launch/arena_camera_nodelet.launch @@ -14,6 +14,9 @@ + + + @@ -30,6 +33,9 @@ + + + diff --git a/arena_camera/launch/default_params.yaml b/arena_camera/launch/default_params.yaml index a8a89a93..3a26a16f 100644 --- a/arena_camera/launch/default_params.yaml +++ b/arena_camera/launch/default_params.yaml @@ -4,6 +4,12 @@ # The tf frame under which the images were published camera_frame: arena_camera +# Period for connection health check (in seconds) +check_period_sec: 1.0 + +# Period to retry connecting to camera, if connection fails (in seconds) +retry_period_sec: 5.0 + # The DeviceUserID of the camera. If empty, the first camera found in the # device list will be used. If specified, device_user_id takes precedence # over serial_number. If neither is specified, the system users the @@ -13,7 +19,8 @@ camera_frame: arena_camera # Serial number of camera to look for. # -#serial_number: "12345" +# If left empty, driver will user device_user_id or autodetect a camera +#serial_number: "234200113" # The CameraInfo URL (Uniform Resource Locator) where the optional intrinsic # camera calibration parameters are stored. This URL string will be parsed diff --git a/arena_camera/src/nodelet_base.cpp b/arena_camera/src/nodelet_base.cpp index 52d63c5a..b76e8bbc 100644 --- a/arena_camera/src/nodelet_base.cpp +++ b/arena_camera/src/nodelet_base.cpp @@ -64,10 +64,9 @@ ArenaCameraNodeletBase::ArenaCameraNodeletBase() pDevice_(nullptr), is_streaming_(false), arena_camera_parameter_set_(), - it_(nullptr), + image_transport_(nullptr), img_raw_pub_(), - camera_info_manager_(), - sampling_indices_() {} + camera_info_manager_() {} ArenaCameraNodeletBase::~ArenaCameraNodeletBase() { if (pDevice_ != nullptr) { @@ -87,11 +86,20 @@ void ArenaCameraNodeletBase::onInit() { ros::NodeHandle &nh = getNodeHandle(); ros::NodeHandle &pnh = getPrivateNodeHandle(); + try { + // Open the Arena SDK + pSystem_ = Arena::OpenSystem(); + } catch (GenICam::GenericException &e) { + NODELET_ERROR_STREAM( + "Error while initializing Arena SDK: " << e.GetDescription()); + return; + } + metadata_pub_ = nh.advertise("imaging_metadata", 1); - it_.reset(new image_transport::ImageTransport(nh)); - img_raw_pub_ = it_->advertiseCamera("image_raw", 1); + image_transport_.reset(new image_transport::ImageTransport(nh)); + img_raw_pub_ = image_transport_->advertiseCamera("image_raw", 1); camera_info_manager_ = std::make_shared(nh); @@ -99,40 +107,109 @@ void ArenaCameraNodeletBase::onInit() { // Initialize parameters from parameter server arena_camera_parameter_set_.readFromRosParameterServer(pnh); + _dynReconfigureServer = + std::make_shared(getPrivateNodeHandle()); + + // \todo{} Consider using rosparam helpers? + float check_period_seconds = 10, retry_period_seconds = 10; + + bool do_retry; + pnh.param("retry", do_retry, true); + + if (!pnh.getParam("check_period_sec", check_period_seconds) && do_retry) { + NODELET_FATAL("Param \"check_period_sec\" not supplied"); + return; + } + + if (!pnh.getParam("retry_period_sec", retry_period_seconds) && do_retry) { + NODELET_FATAL("Param \"retry_period_sec\" not supplied"); + return; + } + + const ros::Duration camera_check_period(check_period_seconds); + const ros::Duration camera_retry_period(retry_period_seconds); + + if (do_retry) { + // The primary try-connect-catch-error-retry-state machine + // + // \todo{} Just as a note to self. Is init() supposed to return? + // should this be in a timer loop? + bool was_connected = false; + while (ros::ok()) { + if (pDevice_ && pDevice_->IsConnected()) { + was_connected = true; + camera_check_period.sleep(); + } else { + if (was_connected) { + NODELET_WARN("!! Camera Connection dropped."); + + if (camera_poll_timer_.isValid()) { + camera_poll_timer_.stop(); + } + + onCameraDisconnect(); + + is_streaming_ = false; + pSystem_->DestroyDevice(pDevice_); + pDevice_ = nullptr; + } + + was_connected = false; + + // Camera is not connected + NODELET_DEBUG("Trying to connect to camera..."); + if (!tryConnect()) { + if (!do_retry) { + NODELET_ERROR( + "Could not connect to camera, and retry:=false, giving up"); + return; + } + + NODELET_DEBUG("Couldn't connect, sleeping..."); + camera_retry_period.sleep(); + } + } + } + } else { + if (!tryConnect()) { + NODELET_ERROR("Could not connect to camera, and retry:=false, giving up"); + } + } +} + +bool ArenaCameraNodeletBase::tryConnect() { try { // Open the Arena SDK - pSystem_ = Arena::OpenSystem(); - pSystem_->UpdateDevices(100); - if (pSystem_->GetDevices().size() == 0) { - NODELET_FATAL("Did not detect any cameras!!"); - return; + if (pSystem_->UpdateDevices(100)) { + NODELET_INFO("Device list changed!"); } - NODELET_WARN("Looking for camera!"); + if (pSystem_->GetDevices().size() == 0) { + NODELET_ERROR("!! Arena SDK did not detect any cameras"); + return false; + } if (!arena_camera_parameter_set_.deviceUserID().empty()) { if (!registerCameraByUserId(arena_camera_parameter_set_.deviceUserID())) { NODELET_FATAL_STREAM("Unable to find a camera with DeviceUserId \"" << arena_camera_parameter_set_.deviceUserID() << "\""); - return; + return false; } } else if (!arena_camera_parameter_set_.serialNumber().empty()) { if (!registerCameraBySerialNumber( arena_camera_parameter_set_.serialNumber())) { NODELET_FATAL_STREAM("Unable to find a camera with Serial Number " << arena_camera_parameter_set_.serialNumber()); - return; + return false; } } else { if (!registerCameraByAuto()) { NODELET_FATAL_STREAM("Unable to find any cameras to register"); - return; + return false; } } - NODELET_WARN("Found camera!"); - // Validate that the camera is from Lucid // (otherwise the Arena SDK will segfault) assert(pDevice_); @@ -151,18 +228,21 @@ void ArenaCameraNodeletBase::onInit() { NODELET_INFO("Configuring camera..."); if (!configureCamera()) { - NODELET_FATAL_STREAM("Unable to configure camera"); - return; + NODELET_ERROR_STREAM("Unable to configure camera"); + return false; } } catch (GenICam::GenericException &e) { NODELET_ERROR_STREAM( "Error while initializing camera: " << e.GetDescription()); - return; + return false; } - NODELET_INFO("Successfully initialized"); - onSuccessfulInit(); + // setCallback will cause the callback to be called... + _dynReconfigureServer->setCallback(boost::bind( + &ArenaCameraNodeletBase::reconfigureCallbackWrapper, this, _1, _2)); + + onCameraConnect(); diagnostics_updater_.setHardwareID("none"); diagnostics_updater_.add( @@ -178,19 +258,25 @@ void ArenaCameraNodeletBase::onInit() { // It's weird that we can subscribe to gain and exposure, but don't actually // get updates without calling "poll" const int poll_ms = 100; - camera_poll_timer_ = nh.createTimer( - ros::Duration(poll_ms * (1.0 / 1000.0)), - [&](const ros::TimerEvent &) { pDevice_->GetNodeMap()->Poll(poll_ms); }); + camera_poll_timer_ = getNodeHandle().createTimer( + ros::Duration(poll_ms * (1.0 / 1000.0)), [&](const ros::TimerEvent &) { + try { + if (pDevice_) { + pDevice_->GetNodeMap()->Poll(poll_ms); + } + } catch (GenICam::GenericException &e) { + NODELET_WARN_STREAM( + "Error while polling camera: " << e.GetDescription()); + } + }); - _dynReconfigureServer = std::make_shared(pnh); - _dynReconfigureServer->setCallback(boost::bind( - &ArenaCameraNodeletBase::reconfigureCallbackWrapper, this, _1, _2)); + return true; } //=================================================================== // // Functions to find/register a camera - +// bool ArenaCameraNodeletBase::registerCameraByUserId( const std::string &device_user_id_to_open) { ROS_ASSERT(pSystem_); @@ -212,10 +298,10 @@ bool ArenaCameraNodeletBase::registerCameraByUserId( } } - NODELET_ERROR_STREAM( - "Couldn't find the camera that matches the " - << "given DeviceUserID: \"" << device_user_id_to_open << "\"! " - << "Either the ID is wrong or the cam is not yet connected"); + // NODELET_ERROR_STREAM( + // "Couldn't find the camera that matches the " + // << "given DeviceUserID: \"" << device_user_id_to_open << "\"! " + // << "Either the ID is wrong or the cam is not yet connected"); return false; } @@ -239,10 +325,10 @@ bool ArenaCameraNodeletBase::registerCameraBySerialNumber( } } - NODELET_ERROR_STREAM( - "Couldn't find the camera that matches the " - << "given Serial Number: " << serial_number << "! " - << "Either the ID is wrong or the camera is not connected"); + // NODELET_ERROR_STREAM( + // "Couldn't find the camera that matches the " + // << "given Serial Number: " << serial_number << "! " + // << "Either the ID is wrong or the camera is not connected"); return false; } @@ -275,7 +361,6 @@ bool ArenaCameraNodeletBase::registerCameraByAuto() { //=================================================================== // // - bool ArenaCameraNodeletBase::configureCamera() { ros::NodeHandle nh = getNodeHandle(); auto pNodeMap = pDevice_->GetNodeMap(); @@ -283,8 +368,7 @@ bool ArenaCameraNodeletBase::configureCamera() { // **NOTE** This function only performs one-off configuration which is // not also accessible through dynamic_reconfigure. // - // dyn_reconfigure parameters will be handled in the callback when it - // is called for the first time at node startup. + // dyn_reconfigure parameters will be handled in the callback try { NODELET_INFO_STREAM( @@ -294,7 +378,7 @@ bool ArenaCameraNodeletBase::configureCamera() { "Device firmware: " << Arena::GetNodeValue( pDevice_->GetNodeMap(), "DeviceFirmwareVersion")); - // Parameters specific to GigER cameras + // Parameters specific to GigE cameras if (Arena::GetNodeValue( pDevice_->GetNodeMap(), "DeviceTLType") == "GigEVision") { NODELET_INFO("GigE device, performing GigE specific configuration:"); @@ -359,9 +443,6 @@ bool ArenaCameraNodeletBase::configureCamera() { "Software"); } - //!! Parameters controlled by param / dynamic reonfigure are not set here - //!! Assume there will be an immediate call from dynamic reconfigure - // LUT NODELET_INFO_STREAM( (arena_camera_parameter_set_.enable_lut_ ? "Enabling" : "Disabling") @@ -478,14 +559,15 @@ bool ArenaCameraNodeletBase::configureCamera() { // Start/stop streaming void ArenaCameraNodeletBase::startStreaming() { - if (!is_streaming_) { + if (pDevice_ && !is_streaming_) { + NODELET_WARN("Starting streaming...."); pDevice_->StartStream(); is_streaming_ = true; } } void ArenaCameraNodeletBase::stopStreaming() { - if (is_streaming_) { + if (pDevice_ && is_streaming_) { pDevice_->StopStream(); is_streaming_ = false; } @@ -518,7 +600,7 @@ void ArenaCameraNodeletBase::updateFrameRate(float frame_rate) { } // special case: // dues to inacurate float comparision we skip. If we set it it might - // throw becase it could be a lil larger than the max avoid the exception + // throw becase it could be slightly larger than the max avoid the exception // (double accuracy issue when setting the node) request frame rate very // close to device max else if (cmdlnParamFrameRate == maximumFrameRate) { @@ -546,8 +628,6 @@ void ArenaCameraNodeletBase::updateFrameRate(float frame_rate) { << Arena::GetNodeValue(pNodeMap, "AcquisitionFrameRate") << " Hz"); - // if (was_streaming) startStreaming(); - } catch (GenICam::GenericException &e) { NODELET_INFO_STREAM("Exception while changing frame rate: " << e.what()); } @@ -1180,57 +1260,71 @@ void ArenaCameraNodeletBase::enableLUT(bool enable) { void ArenaCameraNodeletBase::reconfigureCallback(ArenaCameraConfig &config, uint32_t level) { - const auto stop_level = - (uint32_t)dynamic_reconfigure::SensorLevels::RECONFIGURE_STOP; + // Any config which _doesn't_ require a camera - const bool was_streaming = is_streaming_; - if (level >= stop_level) { - ROS_DEBUG("Stopping sensor for reconfigure"); - stopStreaming(); - } + try { + if (pDevice_ && pDevice_->IsConnected()) { + // Below this requires a connected camera - // -- The following params require stopping streaming, only set if needed -- - if (config.frame_rate != previous_config_.frame_rate) { - ROS_INFO_STREAM("Setting frame rate to " << config.frame_rate); - updateFrameRate(config.frame_rate); - } + const auto stop_level = + (uint32_t)dynamic_reconfigure::SensorLevels::RECONFIGURE_STOP; - if (config.auto_exposure) { - setExposure(ArenaCameraNodeletBase::AutoExposureMode::Continuous, - config.auto_exposure_max_ms); - setAutoExposureGain(config.auto_exposure_gain); - } else { - setExposure(ArenaCameraNodeletBase::AutoExposureMode::Off, - config.exposure_ms); - } + const bool was_streaming = is_streaming_; + if (level >= stop_level) { + ROS_DEBUG("Stopping sensor for reconfigure"); + stopStreaming(); + } - if (config.auto_gain) { - setGain(ArenaCameraNodeletBase::AutoGainMode::Continuous); - } else { - setGain(ArenaCameraNodeletBase::AutoGainMode::Off, config.gain); - } + // -- The following params require stopping streaming, only set if needed + // -- + if (config.frame_rate != previous_config_.frame_rate) { + ROS_INFO_STREAM("Setting frame rate to " << config.frame_rate); + updateFrameRate(config.frame_rate); + } - if ((config.auto_gain != previous_config_.auto_gain) || - (config.auto_exposure != previous_config_.auto_exposure) || - (config.target_brightness != previous_config_.target_brightness)) { - const auto canAutoBrightness = (config.auto_gain || config.auto_exposure); - if (!canAutoBrightness) { - ROS_WARN_STREAM( - "Neither auto_gain or exposure_auto are set, so the brightness " - "target (" - << config.target_brightness << ") will be " - << "ignored!"); - } - setTargetBrightness(config.target_brightness); - } + if (config.auto_exposure) { + setExposure(ArenaCameraNodeletBase::AutoExposureMode::Continuous, + config.auto_exposure_max_ms); + setAutoExposureGain(config.auto_exposure_gain); + } else { + setExposure(ArenaCameraNodeletBase::AutoExposureMode::Off, + config.exposure_ms); + } - if (config.gamma != previous_config_.gamma) { - setGamma(config.gamma); - } + if (config.auto_gain) { + setGain(ArenaCameraNodeletBase::AutoGainMode::Continuous); + } else { + setGain(ArenaCameraNodeletBase::AutoGainMode::Off, config.gain); + } - if ((level >= stop_level) && was_streaming) { - ROS_DEBUG(" ... restarting camera after reconfigure"); - startStreaming(); + if ((config.auto_gain != previous_config_.auto_gain) || + (config.auto_exposure != previous_config_.auto_exposure) || + (config.target_brightness != previous_config_.target_brightness)) { + const auto canAutoBrightness = + (config.auto_gain || config.auto_exposure); + if (!canAutoBrightness) { + ROS_WARN_STREAM( + "Neither auto_gain or exposure_auto are set, so the brightness " + "target (" + << config.target_brightness << ") will be " + << "ignored!"); + } + setTargetBrightness(config.target_brightness); + } + + if (config.gamma != previous_config_.gamma) { + setGamma(config.gamma); + } + + if ((level >= stop_level) && was_streaming) { + ROS_DEBUG(" ... restarting camera after reconfigure"); + startStreaming(); + } + } + + } catch (const GenICam::GenericException &e) { + NODELET_ERROR_STREAM( + "Exception in reconfigure callback: " << e.GetDescription()); } // Save config diff --git a/arena_camera/src/polled_nodelet.cpp b/arena_camera/src/polled_nodelet.cpp index 66661ede..acebc5ae 100644 --- a/arena_camera/src/polled_nodelet.cpp +++ b/arena_camera/src/polled_nodelet.cpp @@ -46,7 +46,7 @@ ArenaCameraPolledNodelet::~ArenaCameraPolledNodelet() {} // // Nodelet::onInit function -void ArenaCameraPolledNodelet::onSuccessfulInit() { +void ArenaCameraPolledNodelet::onCameraConnect() { try { pDevice_->StartStream(); } catch (GenICam::GenericException &e) { diff --git a/arena_camera/src/streaming_nodelet.cpp b/arena_camera/src/streaming_nodelet.cpp index 27f45321..717bc048 100644 --- a/arena_camera/src/streaming_nodelet.cpp +++ b/arena_camera/src/streaming_nodelet.cpp @@ -53,7 +53,7 @@ ArenaCameraStreamingNodelet::~ArenaCameraStreamingNodelet() { // // Nodelet::onInit function -void ArenaCameraStreamingNodelet::onSuccessfulInit() { +void ArenaCameraStreamingNodelet::onCameraConnect() { try { startStreaming(); } catch (GenICam::GenericException &e) {