From 9ee8fa21cdb9f1c13f5b71621cc6843bf91c9f41 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Sun, 19 Dec 2021 16:51:32 -0500 Subject: [PATCH] PX4 ROS2 msg conformity and explicit topics - .msg files are PascalCase - topics are still either per msg or explicitly listed in TOPICS - compatible structs are still generated (eg struct msg_s), but ROS2 style px4::msg::Msg is also available --- .ci/Jenkinsfile-compile | 9 +- .github/workflows/checks.yml | 1 - .github/workflows/metadata.yml | 40 - .gitmodules | 4 - .vscode/cmake-variants.yaml | 10 +- .vscode/settings.json | 7 +- .ycm_extra_conf.py | 172 ---- CMakeLists.txt | 233 +++-- Documentation/Doxyfile.in | 1 - Jenkinsfile | 172 ---- Kconfig | 3 + Makefile | 28 +- .../px4fmu_common/init.d-posix/CMakeLists.txt | 1 - ROMFS/px4fmu_common/init.d-posix/px4-rc.rtps | 4 - ROMFS/px4fmu_common/init.d-posix/rcS | 6 - Tools/astyle/files_to_check_code_style.sh | 3 - Tools/build_micrortps_agent.sh | 15 - Tools/kconfig/cmake_kconfig_lut.txt | 2 - Tools/px4_developer.mk.example | 5 - Tools/setup/arch.sh | 4 +- Tools/setup/ubuntu.sh | 4 +- Tools/update_px4_ros2_bridge.sh | 135 --- .../bitcraze/crazyflie/syslink/CMakeLists.txt | 2 + .../cuav/nora/extras/cuav_nora_bootloader.bin | Bin 42692 -> 42908 bytes .../x7pro/extras/cuav_x7pro_bootloader.bin | Bin 42692 -> 42908 bytes .../cubepilot_cubeorange_bootloader.bin | Bin 42624 -> 42836 bytes .../extras/cubepilot_io-v2_default.bin | Bin 39456 -> 39488 bytes .../extras/cubepilot_io-v2_default.bin | Bin 39456 -> 39488 bytes .../can-rtk-gps/canbootloader.px4board | 1 - boards/freefly/can-rtk-gps/src/CMakeLists.txt | 2 + .../extras/holybro_durandal-v1_bootloader.bin | Bin 42708 -> 42928 bytes .../durandal-v1/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/holybro/pix32v5/default.px4board | 7 +- .../pix32v5/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes .../extras/matek_h743-slim_bootloader.bin | Bin 42688 -> 42904 bytes boards/modalai/fc-v1/rtps.px4board | 1 - .../fc-v2/extras/modalai_fc-v2_bootloader.bin | Bin 42712 -> 42924 bytes .../mro_ctrl-zero-h7-oem_bootloader.bin | Bin 42660 -> 42872 bytes boards/mro/ctrl-zero-h7-oem/rtps.px4board | 1 - .../extras/mro_ctrl-zero-h7_bootloader.bin | Bin 42640 -> 42852 bytes boards/mro/ctrl-zero-h7/rtps.px4board | 1 - .../extras/mro_pixracerpro_bootloader.bin | Bin 42632 -> 42852 bytes boards/mro/pixracerpro/rtps.px4board | 1 - .../mro/x21-777/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/nxp/fmuk66-e/rtps.px4board | 1 - boards/nxp/fmuk66-v3/rtps.px4board | 1 - .../px4/fmu-v2/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes .../px4/fmu-v3/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes .../fmu-v4pro/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/px4/fmu-v5/default.px4board | 4 +- .../px4/fmu-v5/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/px4/fmu-v5/rtps.px4board | 3 - boards/px4/fmu-v5/stackcheck.px4board | 4 + .../px4/fmu-v5x/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/px4/fmu-v5x/rtps.px4board | 1 - .../fmu-v6u/extras/px4_fmu-v6u_bootloader.bin | Bin 42692 -> 42904 bytes .../px4/fmu-v6x/extras/px4_io-v2_default.bin | Bin 39456 -> 39488 bytes boards/px4/ros2/default.px4board | 1 + .../px4/ros2/src}/CMakeLists.txt | 13 +- .../px4/ros2/src/board_config.h | 29 +- .../px4/ros2/src/init.cpp | 0 boards/px4/sitl/rtps.px4board | 1 - cmake/package.cmake | 5 +- cmake/px4_add_common_flags.cmake | 20 +- cmake/px4_add_library.cmake | 5 +- cmake/px4_add_module.cmake | 23 +- cmake/px4_config.cmake | 12 +- .../python_src/px4_it/mavros/mission_test.py | 12 - msg/{action_request.msg => ActionRequest.msg} | 0 msg/{actuator_armed.msg => ActuatorArmed.msg} | 0 ...ator_controls.msg => ActuatorControls.msg} | 3 - ..._status.msg => ActuatorControlsStatus.msg} | 2 - ...actuator_motors.msg => ActuatorMotors.msg} | 0 ...tuator_outputs.msg => ActuatorOutputs.msg} | 3 - ...actuator_servos.msg => ActuatorServos.msg} | 0 ...servos_trim.msg => ActuatorServosTrim.msg} | 0 msg/{actuator_test.msg => ActuatorTest.msg} | 0 msg/{adc_report.msg => AdcReport.msg} | 0 msg/{airspeed.msg => Airspeed.msg} | 0 ...ed_validated.msg => AirspeedValidated.msg} | 0 msg/{airspeed_wind.msg => AirspeedWind.msg} | 0 ....msg => AutotuneAttitudeControlStatus.msg} | 0 msg/{battery_status.msg => BatteryStatus.msg} | 0 msg/CMakeLists.txt | 423 ++++----- msg/{camera_capture.msg => CameraCapture.msg} | 0 msg/{camera_status.msg => CameraStatus.msg} | 0 msg/{camera_trigger.msg => CameraTrigger.msg} | 2 - ...cellular_status.msg => CellularStatus.msg} | 0 ...nstraints.msg => CollisionConstraints.msg} | 0 ...llision_report.msg => CollisionReport.msg} | 0 ...commander_state.msg => CommanderState.msg} | 0 ..._status.msg => ControlAllocatorStatus.msg} | 0 msg/{cpuload.msg => Cpuload.msg} | 0 msg/{debug_array.msg => DebugArray.msg} | 0 ...{debug_key_value.msg => DebugKeyValue.msg} | 0 msg/{debug_value.msg => DebugValue.msg} | 0 msg/{debug_vect.msg => DebugVect.msg} | 0 ..._pressure.msg => DifferentialPressure.msg} | 0 ...distance_sensor.msg => DistanceSensor.msg} | 0 ...ekf2_timestamps.msg => Ekf2Timestamps.msg} | 0 msg/{esc_report.msg => EscReport.msg} | 0 msg/{esc_status.msg => EscStatus.msg} | 2 +- ...or_baro_bias.msg => EstimatorBaroBias.msg} | 0 ...vent_flags.msg => EstimatorEventFlags.msg} | 0 ..._gps_status.msg => EstimatorGpsStatus.msg} | 0 ...novations.msg => EstimatorInnovations.msg} | 2 - ...ow_vel.msg => EstimatorOpticalFlowVel.msg} | 0 ...status.msg => EstimatorSelectorStatus.msg} | 0 ...ensor_bias.msg => EstimatorSensorBias.msg} | 0 ...timator_states.msg => EstimatorStates.msg} | 0 ...timator_status.msg => EstimatorStatus.msg} | 0 ...tus_flags.msg => EstimatorStatusFlags.msg} | 0 msg/{event.msg => Event.msg} | 0 ...r_status.msg => FailureDetectorStatus.msg} | 0 msg/{follow_target.msg => FollowTarget.msg} | 4 +- ...nerator_status.msg => GeneratorStatus.msg} | 0 ...geofence_result.msg => GeofenceResult.msg} | 0 ...tus.msg => GimbalDeviceAttitudeStatus.msg} | 0 ...mation.msg => GimbalDeviceInformation.msg} | 0 ...titude.msg => GimbalDeviceSetAttitude.msg} | 0 ...ation.msg => GimbalManagerInformation.msg} | 0 ...itude.msg => GimbalManagerSetAttitude.msg} | 0 ....msg => GimbalManagerSetManualControl.msg} | 0 ...ger_status.msg => GimbalManagerStatus.msg} | 0 msg/{gps_dump.msg => GpsDump.msg} | 0 ...{gps_inject_data.msg => GpsInjectData.msg} | 0 msg/{heater_status.msg => HeaterStatus.msg} | 0 msg/{home_position.msg => HomePosition.msg} | 0 ...t_estimate.msg => HoverThrustEstimate.msg} | 0 msg/{input_rc.msg => InputRc.msg} | 0 ...msg => InternalCombustionEngineStatus.msg} | 0 ...iumsbd_status.msg => IridiumsbdStatus.msg} | 0 msg/{irlock_report.msg => IrlockReport.msg} | 0 msg/{landing_gear.msg => LandingGear.msg} | 0 ...tions.msg => LandingTargetInnovations.msg} | 0 ..._target_pose.msg => LandingTargetPose.msg} | 0 msg/{led_control.msg => LedControl.msg} | 0 msg/{log_message.msg => LogMessage.msg} | 0 msg/{logger_status.msg => LoggerStatus.msg} | 0 ...{mag_worker_data.msg => MagWorkerData.msg} | 0 ...imate.msg => MagnetometerBiasEstimate.msg} | 0 ...setpoint.msg => ManualControlSetpoint.msg} | 2 - ...switches.msg => ManualControlSwitches.msg} | 0 msg/{mavlink_log.msg => MavlinkLog.msg} | 0 msg/{mavlink_tunnel.msg => MavlinkTunnel.msg} | 0 msg/{mission.msg => Mission.msg} | 0 msg/{mission_result.msg => MissionResult.msg} | 0 ...t_orientation.msg => MountOrientation.msg} | 0 ...sion_item.msg => NavigatorMissionItem.msg} | 0 msg/{npfg_status.msg => NpfgStatus.msg} | 0 ...acle_distance.msg => ObstacleDistance.msg} | 2 - ...ntrol_mode.msg => OffboardControlMode.msg} | 0 ...r_status.msg => OnboardComputerStatus.msg} | 0 msg/{optical_flow.msg => OpticalFlow.msg} | 0 msg/OrbTest.msg | 3 + msg/{orb_test_large.msg => OrbTestLarge.msg} | 0 msg/{orb_test.msg => OrbTestMedium.msg} | 2 +- msg/{orbit_status.msg => OrbitStatus.msg} | 0 ...rameter_update.msg => ParameterUpdate.msg} | 0 msg/{ping.msg => Ping.msg} | 0 ...sg => PositionControllerLandingStatus.msg} | 0 ...tatus.msg => PositionControllerStatus.msg} | 0 ...tion_setpoint.msg => PositionSetpoint.msg} | 0 ...riplet.msg => PositionSetpointTriplet.msg} | 6 +- ..._button_state.msg => PowerButtonState.msg} | 0 msg/{power_monitor.msg => PowerMonitor.msg} | 0 msg/{pps_capture.msg => PpsCapture.msg} | 0 msg/{pwm_input.msg => PwmInput.msg} | 0 msg/{px4io_status.msg => Px4ioStatus.msg} | 0 msg/{radio_status.msg => RadioStatus.msg} | 0 ...ate_ctrl_status.msg => RateCtrlStatus.msg} | 0 msg/{rc_channels.msg => RcChannels.msg} | 0 ...c_parameter_map.msg => RcParameterMap.msg} | 0 msg/{rpm.msg => Rpm.msg} | 0 ..._time_estimate.msg => RtlTimeEstimate.msg} | 0 msg/{safety.msg => Safety.msg} | 0 msg/{satellite_info.msg => SatelliteInfo.msg} | 0 msg/{sensor_accel.msg => SensorAccel.msg} | 0 ...sor_accel_fifo.msg => SensorAccelFifo.msg} | 0 msg/{sensor_baro.msg => SensorBaro.msg} | 0 ...sensor_combined.msg => SensorCombined.msg} | 0 ...or_correction.msg => SensorCorrection.msg} | 0 msg/{sensor_gps.msg => SensorGps.msg} | 0 msg/{sensor_gyro.msg => SensorGyro.msg} | 0 ...{sensor_gyro_fft.msg => SensorGyroFft.msg} | 0 ...ensor_gyro_fifo.msg => SensorGyroFifo.msg} | 0 ...or_hygrometer.msg => SensorHygrometer.msg} | 0 msg/{sensor_mag.msg => SensorMag.msg} | 0 ...eflight_mag.msg => SensorPreflightMag.msg} | 0 ...nsor_selection.msg => SensorSelection.msg} | 0 ...rs_status_imu.msg => SensorsStatusImu.msg} | 0 msg/{system_power.msg => SystemPower.msg} | 0 msg/{takeoff_status.msg => TakeoffStatus.msg} | 0 ...{task_stack_info.msg => TaskStackInfo.msg} | 0 msg/{tecs_status.msg => TecsStatus.msg} | 0 ...lemetry_status.msg => TelemetryStatus.msg} | 0 msg/{test_motor.msg => TestMotor.msg} | 0 msg/{timesync.msg => Timesync.msg} | 0 ...timesync_status.msg => TimesyncStatus.msg} | 0 ...ectory_bezier.msg => TrajectoryBezier.msg} | 0 ...ry_waypoint.msg => TrajectoryWaypoint.msg} | 0 ...onder_report.msg => TransponderReport.msg} | 0 msg/{tune_control.msg => TuneControl.msg} | 0 ...request.msg => UavcanParameterRequest.msg} | 0 ...ter_value.msg => UavcanParameterValue.msg} | 0 msg/{ulog_stream.msg => UlogStream.msg} | 0 ...{ulog_stream_ack.msg => UlogStreamAck.msg} | 0 ...celeration.msg => VehicleAcceleration.msg} | 0 ...ehicle_air_data.msg => VehicleAirData.msg} | 0 ...ion.msg => VehicleAngularAcceleration.msg} | 0 ...=> VehicleAngularAccelerationSetpoint.msg} | 0 ...elocity.msg => VehicleAngularVelocity.msg} | 2 - ...hicle_attitude.msg => VehicleAttitude.msg} | 3 - ...tpoint.msg => VehicleAttitudeSetpoint.msg} | 2 - ...vehicle_command.msg => VehicleCommand.msg} | 2 - ..._command_ack.msg => VehicleCommandAck.msg} | 0 ...constraints.msg => VehicleConstraints.msg} | 0 ...ontrol_mode.msg => VehicleControlMode.msg} | 0 ...position.msg => VehicleGlobalPosition.msg} | 3 - ...ps_position.msg => VehicleGpsPosition.msg} | 0 msg/{vehicle_imu.msg => VehicleImu.msg} | 0 ...le_imu_status.msg => VehicleImuStatus.msg} | 0 ...d_detected.msg => VehicleLandDetected.msg} | 0 ..._position.msg => VehicleLocalPosition.msg} | 3 - ...t.msg => VehicleLocalPositionSetpoint.msg} | 2 - ...gnetometer.msg => VehicleMagnetometer.msg} | 0 ...hicle_odometry.msg => VehicleOdometry.msg} | 3 - ..._setpoint.msg => VehicleRatesSetpoint.msg} | 0 msg/{vehicle_roi.msg => VehicleRoi.msg} | 0 msg/{vehicle_status.msg => VehicleStatus.msg} | 0 ...tatus_flags.msg => VehicleStatusFlags.msg} | 0 ...setpoint.msg => VehicleThrustSetpoint.msg} | 0 ...setpoint.msg => VehicleTorqueSetpoint.msg} | 0 ...bezier.msg => VehicleTrajectoryBezier.msg} | 4 +- ...oint.msg => VehicleTrajectoryWaypoint.msg} | 4 +- ...hicle_status.msg => VtolVehicleStatus.msg} | 0 msg/{wheel_encoders.msg => WheelEncoders.msg} | 0 msg/{wind.msg => Wind.msg} | 2 - ...ator_status.msg => YawEstimatorStatus.msg} | 0 msg/orb_test_medium.msg | 7 - msg/templates/uorb/msg.cpp.em | 92 -- msg/templates/uorb/msg.h.em | 87 +- msg/templates/uorb/uORBTopics.cpp.em | 42 +- msg/templates/uorb/uORBTopics.hpp.em | 35 +- .../uorb_microcdr/microRTPS_client.cpp.em | 306 ------- msg/templates/uorb_microcdr/msg.cpp.em | 156 ---- msg/templates/urtps/Publisher.cpp.em | 219 ----- msg/templates/urtps/Publisher.h.em | 120 --- msg/templates/urtps/RtpsTopics.cpp.em | 187 ---- msg/templates/urtps/RtpsTopics.h.em | 220 ----- msg/templates/urtps/Subscriber.cpp.em | 284 ------ msg/templates/urtps/Subscriber.h.em | 140 --- msg/templates/urtps/microRTPS_agent.cpp.em | 404 --------- .../urtps/microRTPS_agent_CMakeLists.txt.em | 63 -- msg/templates/urtps/microRTPS_timesync.cpp.em | 295 ------ msg/templates/urtps/microRTPS_timesync.h.em | 303 ------- msg/templates/urtps/microRTPS_transport.cpp | 801 ----------------- msg/templates/urtps/microRTPS_transport.h | 158 ---- msg/templates/urtps/msg.idl.em | 154 ---- msg/tools/generate_microRTPS_bridge.py | 444 --------- msg/tools/generate_msg_docs.py | 5 +- msg/tools/px_generate_uorb_topic_files.py | 345 +------ msg/tools/px_generate_uorb_topic_helper.py | 2 + msg/tools/uorb_rtps_classifier.py | 186 ---- msg/tools/uorb_to_ros_msgs.py | 127 --- msg/tools/uorb_to_ros_urtps_topics.py | 160 ---- msg/tools/urtps_bridge_topics.yaml | 91 -- package.xml | 69 +- platforms/common/CMakeLists.txt | 6 +- .../include/px4_platform_common/Node.hpp | 47 + .../include/px4_platform_common/workqueue.h | 1 - platforms/common/uORB/CMakeLists.txt | 14 +- platforms/common/uORB/Publication.hpp | 2 +- platforms/common/uORB/Subscription.hpp | 6 +- .../common/uORB/SubscriptionInterval.hpp | 3 + platforms/common/uORB/uORB.h | 10 +- platforms/common/uORB/uORBCommunicator.hpp | 9 - platforms/common/uORB/uORBDeviceMaster.hpp | 2 +- platforms/common/uORB/uORBManager.hpp | 2 +- .../common/uORB/uORB_tests/CMakeLists.txt | 9 + platforms/common/work_queue/CMakeLists.txt | 4 +- platforms/common/work_queue/hrt_queue.c | 26 +- platforms/common/work_queue/hrt_thread.c | 4 +- platforms/common/work_queue/work_lock.c | 1 + platforms/nuttx/CMakeLists.txt | 2 + platforms/nuttx/cmake/px4_impl_os.cmake | 15 +- .../nuttx/src/canbootloader/CMakeLists.txt | 4 +- .../arch/nxp/s32k14x/CMakeLists.txt | 5 +- .../arch/nxp/s32k14x/drivers/can/driver.c | 2 +- .../arch/stm/stm32f4/CMakeLists.txt | 4 +- .../arch/stm/stm32f4/drivers/can/driver.c | 2 +- .../arch/stm/stm32f7/CMakeLists.txt | 4 +- .../arch/stm/stm32f7/drivers/can/driver.c | 2 +- .../nuttx/src/canbootloader/protocol/uavcan.c | 2 +- .../nuttx/src/canbootloader/uavcan/main.c | 2 +- .../src/px4/nxp/imxrt/hrt/CMakeLists.txt | 5 +- .../src/px4/nxp/kinetis/hrt/CMakeLists.txt | 6 +- .../src/px4/nxp/s32k1xx/hrt/CMakeLists.txt | 5 +- .../src/px4/rpi/rpi_common/hrt/CMakeLists.txt | 9 +- .../px4/stm/stm32_common/hrt/CMakeLists.txt | 9 +- .../px4/stm/stm32f1/watchdog/CMakeLists.txt | 3 +- .../px4/stm/stm32f4/watchdog/CMakeLists.txt | 3 +- .../px4/stm/stm32f7/watchdog/CMakeLists.txt | 3 +- platforms/posix/CMakeLists.txt | 10 +- platforms/posix/include/px4_platform_types.h | 3 - platforms/ros2/CMakeLists.txt | 29 + platforms/ros2/cmake/Toolchain-native.cmake | 17 + platforms/ros2/cmake/finalize.cmake | 3 + platforms/ros2/cmake/init.cmake | 182 ++++ platforms/ros2/cmake/px4_impl_os.cmake | 148 +++ .../ros2/include/hrt_work.h | 47 +- .../ros2/include/px4_platform_common/Node.hpp | 42 + .../include/px4_platform_common/defines.h | 53 ++ .../ros2/include/px4_platform_common/log.h | 41 + .../ros2/include/px4_platform_common/module.h | 417 +++++++++ .../px4_platform_common/module_params.h | 109 ++- .../ros2/include/px4_platform_common/param.h | 452 ++++++++++ .../px4_platform_common/param_macros.h | 250 ++++++ .../ros2/include/px4_platform_common/posix.h | 18 +- .../include/px4_platform_common/px4_config.h | 18 +- .../ros2/include/px4_platform_common/sem.h | 108 +++ .../ros2/include/px4_platform_common/tasks.h | 187 ++++ .../ros2/include/px4_platform_common/time.h | 6 + .../include/px4_platform_common/workqueue.h | 140 +++ platforms/ros2/include/queue.h | 134 +++ .../ros2/include/system_config.h | 63 +- platforms/ros2/include/uORB/Publication.hpp | 148 +++ platforms/ros2/include/uORB/Subscription.hpp | 167 ++++ .../ros2/include/uORB/SubscriptionBasic.hpp | 155 ++++ .../ros2/include/uORB/uORB.h | 115 +-- platforms/ros2/src/px4/CMakeLists.txt | 37 + platforms/ros2/src/px4/common/CMakeLists.txt | 39 + platforms/ros2/src/px4/common/drv_hrt.cpp | 245 +++++ .../common/include/px4_platform/micro_hal.h | 51 ++ platforms/ros2/src/px4/common/main.cpp | 19 + platforms/ros2/src/px4/common/px4_sem.cpp | 219 +++++ platforms/ros2/src/px4/generic/CMakeLists.txt | 34 + .../src/px4/generic/generic/CMakeLists.txt | 32 + .../generic/include/px4_arch/CMakeLists.txt | 0 .../include/px4_arch/i2c_hw_description.h | 53 ++ .../generic/include/px4_arch/micro_hal.h | 36 + .../include/px4_arch/spi_hw_description.h | 59 ++ src/drivers/adc/ads1115/CMakeLists.txt | 6 +- src/drivers/adc/board_adc/CMakeLists.txt | 3 + src/drivers/barometer/bmp388/CMakeLists.txt | 2 + src/drivers/barometer/ms5611/CMakeLists.txt | 2 + src/drivers/bootloaders/CMakeLists.txt | 4 +- src/drivers/bootloaders/boot_alt_app_shared.c | 2 +- src/drivers/bootloaders/boot_app_shared.c | 2 +- src/drivers/camera_capture/CMakeLists.txt | 5 +- src/drivers/camera_trigger/CMakeLists.txt | 3 + .../differential_pressure/ets/CMakeLists.txt | 2 + .../ms4525/CMakeLists.txt | 2 + .../ms5525/CMakeLists.txt | 2 + .../sdp3x/CMakeLists.txt | 2 + .../broadcom/afbrs50/CMakeLists.txt | 2 + .../distance_sensor/cm8jl65/CMakeLists.txt | 2 + .../distance_sensor/gy_us42/CMakeLists.txt | 2 + .../distance_sensor/leddar_one/CMakeLists.txt | 2 + .../distance_sensor/mappydot/CMakeLists.txt | 2 + src/drivers/dshot/CMakeLists.txt | 7 + src/drivers/gps/CMakeLists.txt | 7 + src/drivers/heater/CMakeLists.txt | 2 + src/drivers/imu/bosch/bmi088/CMakeLists.txt | 5 + .../imu/invensense/icm20602/CMakeLists.txt | 5 + .../imu/invensense/icm42688p/CMakeLists.txt | 5 + .../imu/invensense/mpu6000/CMakeLists.txt | 5 + src/drivers/irlock/CMakeLists.txt | 2 + src/drivers/lights/rgbled/CMakeLists.txt | 2 + .../lights/rgbled_ncp5623c/CMakeLists.txt | 2 + src/drivers/lights/rgbled_pwm/CMakeLists.txt | 2 + .../magnetometer/akm/ak09916/CMakeLists.txt | 2 + .../magnetometer/akm/ak8963/CMakeLists.txt | 2 + .../magnetometer/bosch/bmm150/CMakeLists.txt | 2 + .../magnetometer/hmc5883/CMakeLists.txt | 2 + .../isentek/ist8308/CMakeLists.txt | 2 + .../isentek/ist8310/CMakeLists.txt | 2 + .../magnetometer/lis3mdl/CMakeLists.txt | 2 + .../magnetometer/lsm303agr/CMakeLists.txt | 2 + .../magnetometer/lsm9ds1_mag/CMakeLists.txt | 2 + .../magnetometer/qmc5883l/CMakeLists.txt | 2 + .../magnetometer/rm3100/CMakeLists.txt | 2 + .../vtrantech/vcm1193l/CMakeLists.txt | 2 + .../optical_flow/paw3902/CMakeLists.txt | 2 + .../optical_flow/pmw3901/CMakeLists.txt | 2 + .../optical_flow/px4flow/CMakeLists.txt | 2 + .../optical_flow/thoneflow/CMakeLists.txt | 2 + .../power_monitor/ina228/CMakeLists.txt | 2 + .../power_monitor/voxlpm/CMakeLists.txt | 3 + src/drivers/pps_capture/CMakeLists.txt | 2 + src/drivers/protocol_splitter/Kconfig | 5 - .../protocol_splitter/protocol_splitter.cpp | 845 ------------------ src/drivers/pwm_input/CMakeLists.txt | 2 + src/drivers/pwm_out/CMakeLists.txt | 19 +- src/drivers/pwm_out_sim/CMakeLists.txt | 8 +- src/drivers/px4io/CMakeLists.txt | 8 + src/drivers/rc_input/CMakeLists.txt | 2 + src/drivers/roboclaw/CMakeLists.txt | 2 + src/drivers/rpm/pcf8583/CMakeLists.txt | 2 + src/drivers/rpm/rpm_simulator/CMakeLists.txt | 2 + src/drivers/safety_button/CMakeLists.txt | 5 + src/drivers/tap_esc/CMakeLists.txt | 7 + .../telemetry/frsky_telemetry/CMakeLists.txt | 2 + .../telemetry/iridiumsbd/CMakeLists.txt | 2 + src/drivers/test_ppm/test_ppm.cpp | 1 - src/drivers/tone_alarm/CMakeLists.txt | 2 + src/drivers/uavcan/CMakeLists.txt | 6 + src/drivers/uavcan_v1/CMakeLists.txt | 12 + src/drivers/uavcannode/CMakeLists.txt | 19 + src/examples/fake_imu/CMakeLists.txt | 6 + src/examples/fixedwing_control/main.cpp | 6 +- src/examples/px4_simple_app/CMakeLists.txt | 2 +- .../{px4_simple_app.c => px4_simple_app.cpp} | 4 +- src/examples/rover_steering_control/main.cpp | 6 +- src/lib/CMakeLists.txt | 81 +- src/lib/crc/CMakeLists.txt | 39 + src/lib/{systemlib => crc}/crc.c | 0 src/lib/{systemlib => crc}/crc.h | 0 src/lib/parameters/CMakeLists.txt | 12 +- src/lib/parameters/tinybson/CMakeLists.txt | 2 +- src/lib/systemlib/CMakeLists.txt | 8 +- src/lib/systemlib/otp.c | 3 +- .../airship_att_control/CMakeLists.txt | 3 + .../airship_att_control_main.cpp | 2 +- src/modules/airspeed_selector/CMakeLists.txt | 3 + .../airspeed_selector_main.cpp | 15 +- .../CMakeLists.txt | 5 + .../attitude_estimator_q/CMakeLists.txt | 2 + src/modules/battery_status/CMakeLists.txt | 2 + src/modules/camera_feedback/CMakeLists.txt | 2 + src/modules/commander/CMakeLists.txt | 42 +- src/modules/commander/Commander.hpp | 4 +- src/modules/control_allocator/CMakeLists.txt | 12 + src/modules/ekf2/CMakeLists.txt | 31 + .../flight_mode_manager/CMakeLists.txt | 15 + src/modules/fw_att_control/CMakeLists.txt | 11 + .../CMakeLists.txt | 2 + src/modules/fw_pos_control_l1/CMakeLists.txt | 14 +- src/modules/gimbal/CMakeLists.txt | 7 + src/modules/gyro_fft/CMakeLists.txt | 2 + src/modules/land_detector/CMakeLists.txt | 7 + .../landing_target_estimator/CMakeLists.txt | 3 + src/modules/load_mon/CMakeLists.txt | 3 + src/modules/logger/CMakeLists.txt | 3 + src/modules/logger/logged_topics.cpp | 2 +- src/modules/logger/logged_topics.h | 2 +- src/modules/logger/logger.cpp | 2 +- src/modules/mag_bias_estimator/CMakeLists.txt | 2 + src/modules/manual_control/CMakeLists.txt | 5 + src/modules/mavlink/CMakeLists.txt | 75 ++ src/modules/mc_att_control/CMakeLists.txt | 6 +- .../CMakeLists.txt | 2 + .../mc_hover_thrust_estimator/CMakeLists.txt | 2 + src/modules/mc_pos_control/CMakeLists.txt | 7 +- src/modules/mc_rate_control/CMakeLists.txt | 8 + src/modules/micrortps_bridge/CMakeLists.txt | 190 ---- src/modules/micrortps_bridge/Kconfig | 12 - src/modules/micrortps_bridge/README.md | 1 - src/modules/micrortps_bridge/micro-CDR | 1 - .../micrortps_client/CMakeLists.txt | 118 --- .../micrortps_client/microRTPS_client.h | 99 -- .../microRTPS_client_main.cpp | 330 ------- .../micrortps_client/module.yaml | 38 - .../res/basic_example_flow.png | Bin 182298 -> 0 bytes src/modules/navigator/CMakeLists.txt | 12 + src/modules/rc_update/CMakeLists.txt | 5 + src/modules/replay/Replay.hpp | 2 +- src/modules/rover_pos_control/CMakeLists.txt | 6 + src/modules/sensors/CMakeLists.txt | 72 ++ src/modules/sih/CMakeLists.txt | 5 + src/modules/simulator/CMakeLists.txt | 20 + .../battery_simulator/CMakeLists.txt | 2 + .../temperature_compensation/CMakeLists.txt | 3 + src/modules/uuv_att_control/CMakeLists.txt | 3 + src/modules/vtol_att_control/CMakeLists.txt | 7 + .../topic_listener/generate_listener.py | 140 +++ .../topic_listener/listener_main.cpp | 2 +- test/mavsdk_tests/mavsdk_test_runner.py | 4 +- test/rostest_avoidance_run.sh | 20 - uORBTopics.cpp.in | 56 ++ uORBTopics.hpp.in | 72 ++ 481 files changed, 5491 insertions(+), 8399 deletions(-) delete mode 100644 .ycm_extra_conf.py delete mode 100644 ROMFS/px4fmu_common/init.d-posix/px4-rc.rtps delete mode 100755 Tools/build_micrortps_agent.sh delete mode 100644 Tools/px4_developer.mk.example delete mode 100755 Tools/update_px4_ros2_bridge.sh delete mode 100644 boards/modalai/fc-v1/rtps.px4board delete mode 100644 boards/mro/ctrl-zero-h7-oem/rtps.px4board delete mode 100644 boards/mro/ctrl-zero-h7/rtps.px4board delete mode 100644 boards/mro/pixracerpro/rtps.px4board delete mode 100644 boards/nxp/fmuk66-e/rtps.px4board delete mode 100644 boards/nxp/fmuk66-v3/rtps.px4board delete mode 100644 boards/px4/fmu-v5/rtps.px4board delete mode 100644 boards/px4/fmu-v5x/rtps.px4board create mode 100644 boards/px4/ros2/default.px4board rename {src/drivers/protocol_splitter => boards/px4/ros2/src}/CMakeLists.txt (90%) rename src/lib/systemlib/conversions.c => boards/px4/ros2/src/board_config.h (79%) rename msg/tools/__init__.py => boards/px4/ros2/src/init.cpp (100%) delete mode 100644 boards/px4/sitl/rtps.px4board rename msg/{action_request.msg => ActionRequest.msg} (100%) rename msg/{actuator_armed.msg => ActuatorArmed.msg} (100%) rename msg/{actuator_controls.msg => ActuatorControls.msg} (79%) rename msg/{actuator_controls_status.msg => ActuatorControlsStatus.msg} (66%) rename msg/{actuator_motors.msg => ActuatorMotors.msg} (100%) rename msg/{actuator_outputs.msg => ActuatorOutputs.msg} (65%) rename msg/{actuator_servos.msg => ActuatorServos.msg} (100%) rename msg/{actuator_servos_trim.msg => ActuatorServosTrim.msg} (100%) rename msg/{actuator_test.msg => ActuatorTest.msg} (100%) rename msg/{adc_report.msg => AdcReport.msg} (100%) rename msg/{airspeed.msg => Airspeed.msg} (100%) rename msg/{airspeed_validated.msg => AirspeedValidated.msg} (100%) rename msg/{airspeed_wind.msg => AirspeedWind.msg} (100%) rename msg/{autotune_attitude_control_status.msg => AutotuneAttitudeControlStatus.msg} (100%) rename msg/{battery_status.msg => BatteryStatus.msg} (100%) rename msg/{camera_capture.msg => CameraCapture.msg} (100%) rename msg/{camera_status.msg => CameraStatus.msg} (100%) rename msg/{camera_trigger.msg => CameraTrigger.msg} (89%) rename msg/{cellular_status.msg => CellularStatus.msg} (100%) rename msg/{collision_constraints.msg => CollisionConstraints.msg} (100%) rename msg/{collision_report.msg => CollisionReport.msg} (100%) rename msg/{commander_state.msg => CommanderState.msg} (100%) rename msg/{control_allocator_status.msg => ControlAllocatorStatus.msg} (100%) rename msg/{cpuload.msg => Cpuload.msg} (100%) rename msg/{debug_array.msg => DebugArray.msg} (100%) rename msg/{debug_key_value.msg => DebugKeyValue.msg} (100%) rename msg/{debug_value.msg => DebugValue.msg} (100%) rename msg/{debug_vect.msg => DebugVect.msg} (100%) rename msg/{differential_pressure.msg => DifferentialPressure.msg} (100%) rename msg/{distance_sensor.msg => DistanceSensor.msg} (100%) rename msg/{ekf2_timestamps.msg => Ekf2Timestamps.msg} (100%) rename msg/{esc_report.msg => EscReport.msg} (100%) rename msg/{esc_status.msg => EscStatus.msg} (98%) rename msg/{estimator_baro_bias.msg => EstimatorBaroBias.msg} (100%) rename msg/{estimator_event_flags.msg => EstimatorEventFlags.msg} (100%) rename msg/{estimator_gps_status.msg => EstimatorGpsStatus.msg} (100%) rename msg/{estimator_innovations.msg => EstimatorInnovations.msg} (95%) rename msg/{estimator_optical_flow_vel.msg => EstimatorOpticalFlowVel.msg} (100%) rename msg/{estimator_selector_status.msg => EstimatorSelectorStatus.msg} (100%) rename msg/{estimator_sensor_bias.msg => EstimatorSensorBias.msg} (100%) rename msg/{estimator_states.msg => EstimatorStates.msg} (100%) rename msg/{estimator_status.msg => EstimatorStatus.msg} (100%) rename msg/{estimator_status_flags.msg => EstimatorStatusFlags.msg} (100%) rename msg/{event.msg => Event.msg} (100%) rename msg/{failure_detector_status.msg => FailureDetectorStatus.msg} (100%) rename msg/{follow_target.msg => FollowTarget.msg} (75%) rename msg/{generator_status.msg => GeneratorStatus.msg} (100%) rename msg/{geofence_result.msg => GeofenceResult.msg} (100%) rename msg/{gimbal_device_attitude_status.msg => GimbalDeviceAttitudeStatus.msg} (100%) rename msg/{gimbal_device_information.msg => GimbalDeviceInformation.msg} (100%) rename msg/{gimbal_device_set_attitude.msg => GimbalDeviceSetAttitude.msg} (100%) rename msg/{gimbal_manager_information.msg => GimbalManagerInformation.msg} (100%) rename msg/{gimbal_manager_set_attitude.msg => GimbalManagerSetAttitude.msg} (100%) rename msg/{gimbal_manager_set_manual_control.msg => GimbalManagerSetManualControl.msg} (100%) rename msg/{gimbal_manager_status.msg => GimbalManagerStatus.msg} (100%) rename msg/{gps_dump.msg => GpsDump.msg} (100%) rename msg/{gps_inject_data.msg => GpsInjectData.msg} (100%) rename msg/{heater_status.msg => HeaterStatus.msg} (100%) rename msg/{home_position.msg => HomePosition.msg} (100%) rename msg/{hover_thrust_estimate.msg => HoverThrustEstimate.msg} (100%) rename msg/{input_rc.msg => InputRc.msg} (100%) rename msg/{internal_combustion_engine_status.msg => InternalCombustionEngineStatus.msg} (100%) rename msg/{iridiumsbd_status.msg => IridiumsbdStatus.msg} (100%) rename msg/{irlock_report.msg => IrlockReport.msg} (100%) rename msg/{landing_gear.msg => LandingGear.msg} (100%) rename msg/{landing_target_innovations.msg => LandingTargetInnovations.msg} (100%) rename msg/{landing_target_pose.msg => LandingTargetPose.msg} (100%) rename msg/{led_control.msg => LedControl.msg} (100%) rename msg/{log_message.msg => LogMessage.msg} (100%) rename msg/{logger_status.msg => LoggerStatus.msg} (100%) rename msg/{mag_worker_data.msg => MagWorkerData.msg} (100%) rename msg/{magnetometer_bias_estimate.msg => MagnetometerBiasEstimate.msg} (100%) rename msg/{manual_control_setpoint.msg => ManualControlSetpoint.msg} (97%) rename msg/{manual_control_switches.msg => ManualControlSwitches.msg} (100%) rename msg/{mavlink_log.msg => MavlinkLog.msg} (100%) rename msg/{mavlink_tunnel.msg => MavlinkTunnel.msg} (100%) rename msg/{mission.msg => Mission.msg} (100%) rename msg/{mission_result.msg => MissionResult.msg} (100%) rename msg/{mount_orientation.msg => MountOrientation.msg} (100%) rename msg/{navigator_mission_item.msg => NavigatorMissionItem.msg} (100%) rename msg/{npfg_status.msg => NpfgStatus.msg} (100%) rename msg/{obstacle_distance.msg => ObstacleDistance.msg} (96%) rename msg/{offboard_control_mode.msg => OffboardControlMode.msg} (100%) rename msg/{onboard_computer_status.msg => OnboardComputerStatus.msg} (100%) rename msg/{optical_flow.msg => OpticalFlow.msg} (100%) create mode 100644 msg/OrbTest.msg rename msg/{orb_test_large.msg => OrbTestLarge.msg} (100%) rename msg/{orb_test.msg => OrbTestMedium.msg} (68%) rename msg/{orbit_status.msg => OrbitStatus.msg} (100%) rename msg/{parameter_update.msg => ParameterUpdate.msg} (100%) rename msg/{ping.msg => Ping.msg} (100%) rename msg/{position_controller_landing_status.msg => PositionControllerLandingStatus.msg} (100%) rename msg/{position_controller_status.msg => PositionControllerStatus.msg} (100%) rename msg/{position_setpoint.msg => PositionSetpoint.msg} (100%) rename msg/{position_setpoint_triplet.msg => PositionSetpointTriplet.msg} (67%) rename msg/{power_button_state.msg => PowerButtonState.msg} (100%) rename msg/{power_monitor.msg => PowerMonitor.msg} (100%) rename msg/{pps_capture.msg => PpsCapture.msg} (100%) rename msg/{pwm_input.msg => PwmInput.msg} (100%) rename msg/{px4io_status.msg => Px4ioStatus.msg} (100%) rename msg/{radio_status.msg => RadioStatus.msg} (100%) rename msg/{rate_ctrl_status.msg => RateCtrlStatus.msg} (100%) rename msg/{rc_channels.msg => RcChannels.msg} (100%) rename msg/{rc_parameter_map.msg => RcParameterMap.msg} (100%) rename msg/{rpm.msg => Rpm.msg} (100%) rename msg/{rtl_time_estimate.msg => RtlTimeEstimate.msg} (100%) rename msg/{safety.msg => Safety.msg} (100%) rename msg/{satellite_info.msg => SatelliteInfo.msg} (100%) rename msg/{sensor_accel.msg => SensorAccel.msg} (100%) rename msg/{sensor_accel_fifo.msg => SensorAccelFifo.msg} (100%) rename msg/{sensor_baro.msg => SensorBaro.msg} (100%) rename msg/{sensor_combined.msg => SensorCombined.msg} (100%) rename msg/{sensor_correction.msg => SensorCorrection.msg} (100%) rename msg/{sensor_gps.msg => SensorGps.msg} (100%) rename msg/{sensor_gyro.msg => SensorGyro.msg} (100%) rename msg/{sensor_gyro_fft.msg => SensorGyroFft.msg} (100%) rename msg/{sensor_gyro_fifo.msg => SensorGyroFifo.msg} (100%) rename msg/{sensor_hygrometer.msg => SensorHygrometer.msg} (100%) mode change 100755 => 100644 rename msg/{sensor_mag.msg => SensorMag.msg} (100%) rename msg/{sensor_preflight_mag.msg => SensorPreflightMag.msg} (100%) rename msg/{sensor_selection.msg => SensorSelection.msg} (100%) rename msg/{sensors_status_imu.msg => SensorsStatusImu.msg} (100%) rename msg/{system_power.msg => SystemPower.msg} (100%) rename msg/{takeoff_status.msg => TakeoffStatus.msg} (100%) rename msg/{task_stack_info.msg => TaskStackInfo.msg} (100%) rename msg/{tecs_status.msg => TecsStatus.msg} (100%) rename msg/{telemetry_status.msg => TelemetryStatus.msg} (100%) rename msg/{test_motor.msg => TestMotor.msg} (100%) rename msg/{timesync.msg => Timesync.msg} (100%) rename msg/{timesync_status.msg => TimesyncStatus.msg} (100%) rename msg/{trajectory_bezier.msg => TrajectoryBezier.msg} (100%) rename msg/{trajectory_waypoint.msg => TrajectoryWaypoint.msg} (100%) rename msg/{transponder_report.msg => TransponderReport.msg} (100%) rename msg/{tune_control.msg => TuneControl.msg} (100%) rename msg/{uavcan_parameter_request.msg => UavcanParameterRequest.msg} (100%) rename msg/{uavcan_parameter_value.msg => UavcanParameterValue.msg} (100%) rename msg/{ulog_stream.msg => UlogStream.msg} (100%) rename msg/{ulog_stream_ack.msg => UlogStreamAck.msg} (100%) rename msg/{vehicle_acceleration.msg => VehicleAcceleration.msg} (100%) rename msg/{vehicle_air_data.msg => VehicleAirData.msg} (100%) rename msg/{vehicle_angular_acceleration.msg => VehicleAngularAcceleration.msg} (100%) rename msg/{vehicle_angular_acceleration_setpoint.msg => VehicleAngularAccelerationSetpoint.msg} (100%) rename msg/{vehicle_angular_velocity.msg => VehicleAngularVelocity.msg} (78%) rename msg/{vehicle_attitude.msg => VehicleAttitude.msg} (80%) rename msg/{vehicle_attitude_setpoint.msg => VehicleAttitudeSetpoint.msg} (92%) rename msg/{vehicle_command.msg => VehicleCommand.msg} (99%) rename msg/{vehicle_command_ack.msg => VehicleCommandAck.msg} (100%) rename msg/{vehicle_constraints.msg => VehicleConstraints.msg} (100%) rename msg/{vehicle_control_mode.msg => VehicleControlMode.msg} (100%) rename msg/{vehicle_global_position.msg => VehicleGlobalPosition.msg} (92%) rename msg/{vehicle_gps_position.msg => VehicleGpsPosition.msg} (100%) rename msg/{vehicle_imu.msg => VehicleImu.msg} (100%) rename msg/{vehicle_imu_status.msg => VehicleImuStatus.msg} (100%) rename msg/{vehicle_land_detected.msg => VehicleLandDetected.msg} (100%) rename msg/{vehicle_local_position.msg => VehicleLocalPosition.msg} (97%) rename msg/{vehicle_local_position_setpoint.msg => VehicleLocalPositionSetpoint.msg} (89%) rename msg/{vehicle_magnetometer.msg => VehicleMagnetometer.msg} (100%) rename msg/{vehicle_odometry.msg => VehicleOdometry.msg} (95%) rename msg/{vehicle_rates_setpoint.msg => VehicleRatesSetpoint.msg} (100%) rename msg/{vehicle_roi.msg => VehicleRoi.msg} (100%) rename msg/{vehicle_status.msg => VehicleStatus.msg} (100%) rename msg/{vehicle_status_flags.msg => VehicleStatusFlags.msg} (100%) rename msg/{vehicle_thrust_setpoint.msg => VehicleThrustSetpoint.msg} (100%) rename msg/{vehicle_torque_setpoint.msg => VehicleTorqueSetpoint.msg} (100%) rename msg/{vehicle_trajectory_bezier.msg => VehicleTrajectoryBezier.msg} (86%) rename msg/{vehicle_trajectory_waypoint.msg => VehicleTrajectoryWaypoint.msg} (86%) rename msg/{vtol_vehicle_status.msg => VtolVehicleStatus.msg} (100%) rename msg/{wheel_encoders.msg => WheelEncoders.msg} (100%) rename msg/{wind.msg => Wind.msg} (96%) rename msg/{yaw_estimator_status.msg => YawEstimatorStatus.msg} (100%) delete mode 100644 msg/orb_test_medium.msg delete mode 100644 msg/templates/uorb/msg.cpp.em delete mode 100644 msg/templates/uorb_microcdr/microRTPS_client.cpp.em delete mode 100644 msg/templates/uorb_microcdr/msg.cpp.em delete mode 100644 msg/templates/urtps/Publisher.cpp.em delete mode 100644 msg/templates/urtps/Publisher.h.em delete mode 100644 msg/templates/urtps/RtpsTopics.cpp.em delete mode 100644 msg/templates/urtps/RtpsTopics.h.em delete mode 100644 msg/templates/urtps/Subscriber.cpp.em delete mode 100644 msg/templates/urtps/Subscriber.h.em delete mode 100644 msg/templates/urtps/microRTPS_agent.cpp.em delete mode 100644 msg/templates/urtps/microRTPS_agent_CMakeLists.txt.em delete mode 100644 msg/templates/urtps/microRTPS_timesync.cpp.em delete mode 100644 msg/templates/urtps/microRTPS_timesync.h.em delete mode 100644 msg/templates/urtps/microRTPS_transport.cpp delete mode 100644 msg/templates/urtps/microRTPS_transport.h delete mode 100644 msg/templates/urtps/msg.idl.em delete mode 100644 msg/tools/generate_microRTPS_bridge.py delete mode 100644 msg/tools/uorb_rtps_classifier.py delete mode 100755 msg/tools/uorb_to_ros_msgs.py delete mode 100755 msg/tools/uorb_to_ros_urtps_topics.py delete mode 100644 msg/tools/urtps_bridge_topics.yaml create mode 100644 platforms/common/include/px4_platform_common/Node.hpp delete mode 100644 platforms/posix/include/px4_platform_types.h create mode 100644 platforms/ros2/CMakeLists.txt create mode 100644 platforms/ros2/cmake/Toolchain-native.cmake create mode 100644 platforms/ros2/cmake/finalize.cmake create mode 100644 platforms/ros2/cmake/init.cmake create mode 100644 platforms/ros2/cmake/px4_impl_os.cmake rename src/lib/systemlib/conversions.h => platforms/ros2/include/hrt_work.h (68%) create mode 100644 platforms/ros2/include/px4_platform_common/Node.hpp create mode 100644 platforms/ros2/include/px4_platform_common/defines.h create mode 100644 platforms/ros2/include/px4_platform_common/log.h create mode 100644 platforms/ros2/include/px4_platform_common/module.h rename msg/templates/px4/uorb/msg.h.em => platforms/ros2/include/px4_platform_common/module_params.h (53%) create mode 100644 platforms/ros2/include/px4_platform_common/param.h create mode 100644 platforms/ros2/include/px4_platform_common/param_macros.h rename msg/templates/uorb_microcdr/uORBTopics.hpp.em => platforms/ros2/include/px4_platform_common/posix.h (82%) rename msg/templates/uorb_microcdr/uORBTopics.cpp.em => platforms/ros2/include/px4_platform_common/px4_config.h (82%) create mode 100644 platforms/ros2/include/px4_platform_common/sem.h create mode 100644 platforms/ros2/include/px4_platform_common/tasks.h create mode 100644 platforms/ros2/include/px4_platform_common/time.h create mode 100644 platforms/ros2/include/px4_platform_common/workqueue.h create mode 100644 platforms/ros2/include/queue.h rename msg/templates/uorb_microcdr/msg.h.em => platforms/ros2/include/system_config.h (58%) create mode 100644 platforms/ros2/include/uORB/Publication.hpp create mode 100644 platforms/ros2/include/uORB/Subscription.hpp create mode 100644 platforms/ros2/include/uORB/SubscriptionBasic.hpp rename msg/templates/px4/ros/msg.h.em => platforms/ros2/include/uORB/uORB.h (50%) create mode 100644 platforms/ros2/src/px4/CMakeLists.txt create mode 100644 platforms/ros2/src/px4/common/CMakeLists.txt create mode 100644 platforms/ros2/src/px4/common/drv_hrt.cpp create mode 100644 platforms/ros2/src/px4/common/include/px4_platform/micro_hal.h create mode 100644 platforms/ros2/src/px4/common/main.cpp create mode 100644 platforms/ros2/src/px4/common/px4_sem.cpp create mode 100644 platforms/ros2/src/px4/generic/CMakeLists.txt create mode 100644 platforms/ros2/src/px4/generic/generic/CMakeLists.txt create mode 100644 platforms/ros2/src/px4/generic/generic/include/px4_arch/CMakeLists.txt create mode 100644 platforms/ros2/src/px4/generic/generic/include/px4_arch/i2c_hw_description.h create mode 100644 platforms/ros2/src/px4/generic/generic/include/px4_arch/micro_hal.h create mode 100644 platforms/ros2/src/px4/generic/generic/include/px4_arch/spi_hw_description.h delete mode 100644 src/drivers/protocol_splitter/Kconfig delete mode 100644 src/drivers/protocol_splitter/protocol_splitter.cpp rename src/examples/px4_simple_app/{px4_simple_app.c => px4_simple_app.cpp} (97%) create mode 100644 src/lib/crc/CMakeLists.txt rename src/lib/{systemlib => crc}/crc.c (100%) rename src/lib/{systemlib => crc}/crc.h (100%) delete mode 100644 src/modules/micrortps_bridge/CMakeLists.txt delete mode 100644 src/modules/micrortps_bridge/Kconfig delete mode 100644 src/modules/micrortps_bridge/README.md delete mode 160000 src/modules/micrortps_bridge/micro-CDR delete mode 100644 src/modules/micrortps_bridge/micrortps_client/CMakeLists.txt delete mode 100644 src/modules/micrortps_bridge/micrortps_client/microRTPS_client.h delete mode 100644 src/modules/micrortps_bridge/micrortps_client/microRTPS_client_main.cpp delete mode 100644 src/modules/micrortps_bridge/micrortps_client/module.yaml delete mode 100644 src/modules/micrortps_bridge/res/basic_example_flow.png create mode 100755 src/systemcmds/topic_listener/generate_listener.py delete mode 100755 test/rostest_avoidance_run.sh create mode 100644 uORBTopics.cpp.in create mode 100644 uORBTopics.hpp.in diff --git a/.ci/Jenkinsfile-compile b/.ci/Jenkinsfile-compile index 44936a53e8..570b7feacc 100644 --- a/.ci/Jenkinsfile-compile +++ b/.ci/Jenkinsfile-compile @@ -28,7 +28,7 @@ pipeline { ] def base_builds = [ - target: ["px4_sitl_rtps"], + target: ["px4_sitl_default"], image: docker_images.base, archive: false ] @@ -63,23 +63,17 @@ pipeline { "matek_gnss-m9n-f4_canbootloader", "matek_gnss-m9n-f4_default", "modalai_fc-v1_default", - "modalai_fc-v1_rtps", "modalai_fc-v2_default", "mro_ctrl-zero-f7_default", "mro_ctrl-zero-f7-oem_default", "mro_ctrl-zero-h7_default", - "mro_ctrl-zero-h7_rtps", "mro_ctrl-zero-h7-oem_default", - "mro_ctrl-zero-h7-oem_rtps", "mro_pixracerpro_default", - "mro_pixracerpro_rtps", "mro_x21-777_default", "mro_x21_default", "nxp_fmuk66-e_default", - "nxp_fmuk66-e_rtps", "nxp_fmuk66-e_socketcan", "nxp_fmuk66-v3_default", - "nxp_fmuk66-v3_rtps", "nxp_fmuk66-v3_socketcan", "nxp_fmurt1062-v1_default", "nxp_ucans32k146_canbootloader", @@ -95,7 +89,6 @@ pipeline { "px4_fmu-v4pro_default", "px4_fmu-v5_debug", "px4_fmu-v5_default", - "px4_fmu-v5_rtps", "px4_fmu-v5_stackcheck", "px4_fmu-v5_uavcanv0periph", "px4_fmu-v5_uavcanv1", diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index bcae79ad69..42296a43e4 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -23,7 +23,6 @@ jobs: "shellcheck_all", "NO_NINJA_BUILD=1 px4_fmu-v5_default", "NO_NINJA_BUILD=1 px4_sitl_default", - "BUILD_MICRORTPS_AGENT=1 px4_sitl_rtps", "airframe_metadata", "module_documentation", "parameters_metadata", diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index 3adb3e565e..a7a9f9faca 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -115,43 +115,3 @@ jobs: cd Tools/uorb_graph ls -ls * # TODO: deploy graph_px4_sitl.json to S3 px4-travis:Firmware/master/ - - micrortps_agent: - runs-on: ubuntu-latest - container: px4io/px4-dev-base-focal:2021-09-08 - steps: - - uses: actions/checkout@v1 - with: - token: ${{ secrets.ACCESS_TOKEN }} - - - name: microRTPS agent - run: | - make px4_sitl_rtps - git clone https://github.com/PX4/micrortps_agent.git - cp -R build/px4_sitl_rtps/src/modules/micrortps_bridge/micrortps_agent/* micrortps_agent - - ROS_msgs: - runs-on: ubuntu-latest - container: px4io/px4-dev-base-focal:2021-09-08 - steps: - - uses: actions/checkout@v1 - with: - token: ${{ secrets.ACCESS_TOKEN }} - - - name: PX4 ROS msgs - run: | - git clone https://github.com/PX4/px4_msgs.git - python3 msg/tools/uorb_to_ros_msgs.py msg/ px4_msgs/msg/ - - ROS2_bridge: - runs-on: ubuntu-latest - container: px4io/px4-dev-base-focal:2021-09-08 - steps: - - uses: actions/checkout@v1 - with: - token: ${{ secrets.ACCESS_TOKEN }} - - - name: PX4 ROS2 bridge - run: | - git clone https://github.com/PX4/px4_ros_com.git - ./msg/tools/uorb_to_ros_urtps_topics.py -i msg/tools/urtps_bridge_topics.yaml -o px4_ros_com/templates/urtps_bridge_topics.yaml diff --git a/.gitmodules b/.gitmodules index f23a7a3e78..1573bf09e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,10 +18,6 @@ path = src/drivers/gps/devices url = https://github.com/PX4/PX4-GPSDrivers.git branch = master -[submodule "src/modules/micrortps_bridge/micro-CDR"] - path = src/modules/micrortps_bridge/micro-CDR - url = https://github.com/PX4/Micro-CDR.git - branch = master [submodule "platforms/nuttx/NuttX/nuttx"] path = platforms/nuttx/NuttX/nuttx url = https://github.com/PX4/NuttX.git diff --git a/.vscode/cmake-variants.yaml b/.vscode/cmake-variants.yaml index 4c023f3580..96b3bf3276 100644 --- a/.vscode/cmake-variants.yaml +++ b/.vscode/cmake-variants.yaml @@ -6,11 +6,6 @@ CONFIG: buildType: RelWithDebInfo settings: CONFIG: px4_sitl_default - px4_sitl_rtps: - short: px4_sitl_rtps - buildType: RelWithDebInfo - settings: - CONFIG: px4_sitl_rtps px4_sitl_asan: short: px4_sitl (AddressSanitizer) buildType: AddressSanitizer @@ -31,6 +26,11 @@ CONFIG: buildType: RelWithDebInfo settings: CONFIG: px4_sitl_test + px4_ros2_default: + short: px4_ros2 + buildType: RelWithDebInfo + settings: + CONFIG: px4_ros2_default px4_io-v2_default: short: px4_io-v2 buildType: MinSizeRel diff --git a/.vscode/settings.json b/.vscode/settings.json index 1bf4e6eb2f..c917fe0a03 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "C_Cpp.autoAddFileAssociations": false, "C_Cpp.clang_format_fallbackStyle": "none", "C_Cpp.default.browse.limitSymbolsToIncludedHeaders": true, - "C_Cpp.default.cppStandard": "c++14", + "C_Cpp.default.cppStandard": "c++17", "C_Cpp.default.cStandard": "c11", "C_Cpp.formatting": "Disabled", "C_Cpp.intelliSenseEngine": "Default", @@ -121,6 +121,7 @@ "variant": "cpp", "vector": "cpp" }, + "ros.distro": "foxy", "search.exclude": { "${workspaceFolder}/build": true }, @@ -136,5 +137,7 @@ "yaml.schemas": { "${workspaceFolder}/validation/module_schema.yaml": "${workspaceFolder}/src/modules/*/module.yaml" }, - "cortex-debug.openocdPath": "${env:PICO_SDK_PATH}/../openocd/src/openocd" // Added for rp2040 + "python.autoComplete.extraPaths": [ + "/opt/ros/foxy/lib/python3.8/site-packages" + ] } diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py deleted file mode 100644 index 4f7cfc2020..0000000000 --- a/.ycm_extra_conf.py +++ /dev/null @@ -1,172 +0,0 @@ -# This file is NOT licensed under the GPLv3, which is the license for the rest -# of YouCompleteMe. -# -# Here's the license text for this file: -# -# This is free and unencumbered software released into the public domain. -# -# Anyone is free to copy, modify, publish, use, compile, sell, or -# distribute this software, either in source code form or as a compiled -# binary, for any purpose, commercial or non-commercial, and by any -# means. -# -# In jurisdictions that recognize copyright laws, the author or authors -# of this software dedicate any and all copyright interest in the -# software to the public domain. We make this dedication for the benefit -# of the public at large and to the detriment of our heirs and -# successors. We intend this dedication to be an overt act of -# relinquishment in perpetuity of all present and future rights to this -# software under copyright law. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -# For more information, please refer to - -import os -import ycm_core - -# These are the compilation flags that will be used in case there's no -# compilation database set (by default, one is not set). -# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. -flags = [ -'-Wall', -'-Wextra', -'-Werror', -#'-Wc++98-compat', -'-Wno-long-long', -'-Wno-variadic-macros', -'-fexceptions', -'-DNDEBUG', -# You 100% do NOT need -DUSE_CLANG_COMPLETER in your flags; only the YCM -# source code needs it. -#'-DUSE_CLANG_COMPLETER', -# THIS IS IMPORTANT! Without a "-std=" flag, clang won't know which -# language to use when compiling headers. So it will guess. Badly. So C++ -# headers will be compiled as C headers. You don't want that so ALWAYS specify -# a "-std=". -# For a C project, you would set this to something like 'c99' instead of -# 'c++14'. -'-std=c++14', -# ...and the same thing goes for the magic -x option which specifies the -# language that the files to be compiled are written in. This is mostly -# relevant for c++ headers. -# For a C project, you would set this to 'c' instead of 'c++'. -'-x', -'c++', -'-undef', # get rid of standard definitions to allow us to include arm math header -'-I', os.path.join(os.path.expanduser("~"),'gcc-arm-none-eabi-4_7-2013q3/arm-none-eabi/include'), -'-I', 'Build/px4_io-v2_default.build/nuttx-export/include/', -'-I', './NuttX/nuttx/arch/arm/include', -'-include', './src/include/visibility.h', -'-I', './src', -'-I', './src/modules', -'-I', './src/include', -'-I', './src/lib', -'-I', './NuttX', -] - - -# Set this to the absolute path to the folder (NOT the file!) containing the -# compile_commands.json file to use that instead of 'flags'. See here for -# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html -# -# Most projects will NOT need to set this to anything; you can just change the -# 'flags' list of compilation flags. Notice that YCM itself uses that approach. -compilation_database_folder = '' - -if os.path.exists( compilation_database_folder ): - database = ycm_core.CompilationDatabase( compilation_database_folder ) -else: - database = None - -SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] - -def DirectoryOfThisScript(): - return os.path.dirname( os.path.abspath( __file__ ) ) - - -def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): - if not working_directory: - return list( flags ) - new_flags = [] - make_next_absolute = False - path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] - for flag in flags: - new_flag = flag - - if make_next_absolute: - make_next_absolute = False - if not flag.startswith( '/' ): - new_flag = os.path.join( working_directory, flag ) - - for path_flag in path_flags: - if flag == path_flag: - make_next_absolute = True - break - - if flag.startswith( path_flag ): - path = flag[ len( path_flag ): ] - new_flag = path_flag + os.path.join( working_directory, path ) - break - - if new_flag: - new_flags.append( new_flag ) - return new_flags - - -def IsHeaderFile( filename ): - extension = os.path.splitext( filename )[ 1 ] - return extension in [ '.h', '.hxx', '.hpp', '.hh' ] - - -def GetCompilationInfoForFile( filename ): - # The compilation_commands.json file generated by CMake does not have entries - # for header files. So we do our best by asking the db for flags for a - # corresponding source file, if any. If one exists, the flags for that file - # should be good enough. - if IsHeaderFile( filename ): - basename = os.path.splitext( filename )[ 0 ] - for extension in SOURCE_EXTENSIONS: - replacement_file = basename + extension - if os.path.exists( replacement_file ): - compilation_info = database.GetCompilationInfoForFile( - replacement_file ) - if compilation_info.compiler_flags_: - return compilation_info - return None - return database.GetCompilationInfoForFile( filename ) - - -def FlagsForFile( filename, **kwargs ): - if database: - # Bear in mind that compilation_info.compiler_flags_ does NOT return a - # python list, but a "list-like" StringVec object - compilation_info = GetCompilationInfoForFile( filename ) - if not compilation_info: - return None - - final_flags = MakeRelativePathsInFlagsAbsolute( - compilation_info.compiler_flags_, - compilation_info.compiler_working_dir_ ) - - # NOTE: This is just for YouCompleteMe; it's highly likely that your project - # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR - # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. - #try: - # final_flags.remove( '-stdlib=libc++' ) - #except ValueError: - # pass - else: - relative_to = DirectoryOfThisScript() - final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) - - return { - 'flags': final_flags, - 'do_cache': True - } diff --git a/CMakeLists.txt b/CMakeLists.txt index a6288819a9..8d6006c0fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2017 - 2019 PX4 Development Team. All rights reserved. +# Copyright (c) 2017 - 2022 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -101,6 +101,8 @@ cmake_minimum_required(VERSION 3.2 FATAL_ERROR) +cmake_policy(SET CMP0058 NEW) + set(PX4_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE FILEPATH "PX4 source directory" FORCE) set(PX4_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE FILEPATH "PX4 binary directory" FORCE) @@ -138,30 +140,41 @@ define_property(GLOBAL PROPERTY PX4_SRC_FILES BRIEF_DOCS "src files from all PX4 modules & libs" FULL_DOCS "SRC files from px4_add_{module,library}" ) - +define_property(GLOBAL PROPERTY PX4_PUBLICATIONS + BRIEF_DOCS "PX4 publication topics" + FULL_DOCS "List of topics published by PX4 modules" + ) +define_property(GLOBAL PROPERTY PX4_SUBSCRIPTIONS + BRIEF_DOCS "PX4 subscription topics" + FULL_DOCS "List of topics subscribed by PX4 modules" + ) #============================================================================= # configuration # -set(CONFIG "px4_sitl_default" CACHE STRING "desired configuration") +# default to px4_ros2_default if building within a ROS2 colcon environment +if((DEFINED ENV{COLCON_PREFIX_PATH}) AND (DEFINED ENV{ROS_VERSION})) + if("$ENV{ROS_VERSION}" MATCHES "2") + message(STATUS "ROS_VERSION: $ENV{ROS_VERSION}") + set(CONFIG "px4_ros2_default" CACHE STRING "desired configuration") # TODO + endif() +endif() + +if(NOT CONFIG) + set(CONFIG "px4_sitl_default" CACHE STRING "desired configuration") +endif() include(px4_add_module) set(config_module_list) set(config_kernel_list) # Find Python -# If using catkin, Python 2 is found since it points -# to the Python libs installed with the ROS distro -if (NOT CATKIN_DEVEL_PREFIX) - find_package(PythonInterp 3) - # We have a custom error message to tell users how to install python3. - if (NOT PYTHONINTERP_FOUND) - message(FATAL_ERROR "Python 3 not found. Please install Python 3:\n" - " Ubuntu: sudo apt install python3 python3-dev python3-pip\n" - " macOS: brew install python") - endif() -else() - find_package(PythonInterp REQUIRED) +find_package(PythonInterp 3) +# We have a custom error message to tell users how to install python3. +if(NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR "Python 3 not found. Please install Python 3:\n" + " Ubuntu: sudo apt install python3 python3-dev python3-pip\n" + " macOS: brew install python") endif() option(PYTHON_COVERAGE "Python code coverage" OFF) @@ -197,10 +210,6 @@ set_property(GLOBAL PROPERTY PX4_MODULE_CONFIG_FILES) include(platforms/${PX4_PLATFORM}/cmake/px4_impl_os.cmake) list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake) -if(EXISTS "${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake/init.cmake") - include(init) -endif() - # CMake build type (Debug Release RelWithDebInfo MinSizeRel Coverage) if(NOT CMAKE_BUILD_TYPE) if(${PX4_PLATFORM} STREQUAL "nuttx") @@ -236,20 +245,18 @@ project(px4 CXX C ASM) set(package-contact "px4users@googlegroups.com") -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# For the catkin build process, unset build of dynamically-linked binaries -# and do not change CMAKE_RUNTIME_OUTPUT_DIRECTORY -if (NOT CATKIN_DEVEL_PREFIX) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PX4_BINARY_DIR}) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PX4_BINARY_DIR}) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PX4_BINARY_DIR}) -else() - SET(BUILD_SHARED_LIBS OFF) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PX4_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PX4_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PX4_BINARY_DIR}) + +if(EXISTS "${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake/init.cmake") + include(init) endif() #============================================================================= @@ -295,21 +302,6 @@ endif() include(ccache) -#============================================================================= -# find programs and packages -# - -# see if catkin was invoked to build this -if (CATKIN_DEVEL_PREFIX) - message(STATUS "catkin ENABLED") - find_package(catkin REQUIRED) - if (catkin_FOUND) - catkin_package() - else() - message(FATAL_ERROR "catkin not found") - endif() -endif() - #============================================================================= # get chip and chip manufacturer # @@ -321,13 +313,6 @@ if(NOT PX4_CHIP) message(FATAL_ERROR "px4_os_determine_build_chip() needs to set PX4_CHIP") endif() -#============================================================================= -# build flags -# -include(px4_add_common_flags) -px4_add_common_flags() -px4_os_add_flags() - #============================================================================= # board cmake init (optional) # @@ -335,6 +320,13 @@ if(EXISTS ${PX4_BOARD_DIR}/cmake/init.cmake) include(${PX4_BOARD_DIR}/cmake/init.cmake) endif() +#============================================================================= +# build flags +# +include(px4_add_common_flags) +px4_add_common_flags() +px4_os_add_flags() + #============================================================================= # message, and airframe generation # @@ -416,8 +408,13 @@ add_library(parameters_interface INTERFACE) include(px4_add_library) add_subdirectory(src/lib EXCLUDE_FROM_ALL) -add_subdirectory(platforms/${PX4_PLATFORM}/src/px4) -add_subdirectory(platforms EXCLUDE_FROM_ALL) +add_subdirectory(platforms/${PX4_PLATFORM}/src/px4 EXCLUDE_FROM_ALL) + +if(${PX4_PLATFORM} MATCHES "ros2") # TODO: fix + add_subdirectory(platforms/common/work_queue EXCLUDE_FROM_ALL) +else() + add_subdirectory(platforms EXCLUDE_FROM_ALL) +endif() if(EXISTS "${PX4_BOARD_DIR}/CMakeLists.txt") add_subdirectory(${PX4_BOARD_DIR}) @@ -439,6 +436,126 @@ target_link_libraries(parameters_interface INTERFACE parameters) # firmware added last to generate the builtin for included modules add_subdirectory(platforms/${PX4_PLATFORM}) + +set(PX4_ORB_TOPIC_COUNT 0) + +if(${PX4_PLATFORM} MATCHES "ros2") # TODO: fix + + configure_file(${CMAKE_SOURCE_DIR}/build/px4_fmu-v5x_default/uORBTopics.cpp ${CMAKE_BINARY_DIR}/uORBTopics.cpp COPYONLY) + configure_file(${CMAKE_SOURCE_DIR}/build/px4_fmu-v5x_default/uORBTopics.hpp ${CMAKE_BINARY_DIR}/uORBTopics.hpp COPYONLY) + + set(PX4_ORB_TOPIC_COUNT 1) + +else() + + get_property(publications GLOBAL PROPERTY PX4_PUBLICATIONS) + get_property(subscriptions GLOBAL PROPERTY PX4_SUBSCRIPTIONS) + + # TODO: for now combine subsriptions and publications for complete topic list + list(APPEND publications ${subscriptions}) + + list(SORT publications) + list(REMOVE_DUPLICATES publications) + + set(pub_all_topics) + set(PX4_MSG_TYPE_ID) + set(PX4_MSG_TOPIC_ID) + set(PX4_MSG_TOPIC_ID_STRING) + set(PX4_MSG_TOPIC_ORB_ID) + + set(PX4_ORB_DECLARE_STR) + set(PX4_ORB_DEFINE_STR) + set(PX4_ORB_HEADER_INCLUDE_STR) + + + foreach(pub ${publications}) + #message(STATUS "pub: ${pub}") + string(REPLACE " /" ";" pub ${pub}) + + list(GET pub 0 pub_type) + list(GET pub 1 pub_topic) + + string(REPLACE "::" ";" pub_type_split ${pub_type}) + list(GET pub_type_split 2 pub_type_simple_lower) + + # Pascal case to snake case (PubType -> pub_type) + string(REGEX REPLACE "(.)([A-Z][a-z]+)" "\\1_\\2" pub_type_simple_lower "${pub_type_simple_lower}") + string(REGEX REPLACE "([a-z0-9])([A-Z])" "\\1_\\2" pub_type_simple_lower "${pub_type_simple_lower}") + string(TOLOWER "${pub_type_simple_lower}" pub_type_simple_lower) + + #message(STATUS "pub: Type: ${pub_type}, Topic: ${pub_topic} (${pub_type_simple_lower})") + + + + + # pub_type to include path (eg px4::msg::VehicleStatus => px4/msg/vehicle_status.hpp) + # temporary create px4/msg/vehicle_status.hpp which simply includes + + + + + list(APPEND pub_all_topics ${pub_topic}) + + list(APPEND PX4_MSG_TYPE_ID ${pub_type}) + + #list(APPEND PX4_MSG_TOPIC_ID ${pub_topic}) + set(PX4_MSG_TOPIC_ID "${PX4_MSG_TOPIC_ID}\t${pub_topic},\n") + #set(PX4_MSG_TOPIC_ID_STRING "${PX4_MSG_TOPIC_ID_STRING}\t"case ORB_ID::${pub_topic}: return \"${pub_topic}\";"\n") + set(PX4_MSG_TOPIC_ORB_ID "${PX4_MSG_TOPIC_ORB_ID}\tORB_ID(${pub_topic}),\n") + + list(APPEND PX4_MSG_TOPIC_ID_STRING + "case ORB_ID::${pub_topic}: return \"${pub_topic}\";\n" + ) + + # PX4_MSG_TYPE_ID + # PX4_MSG_TOPIC_ID + + # .h ORB_DECLARE(actuator_controls_0); + # .c ORB_DEFINE(actuator_controls_0, struct actuator_controls_s, 48, __orb_actuator_controls_fields, static_cast(ORB_ID::actuator_controls_0)); + + + set(PX4_ORB_DECLARE_STR + "${PX4_ORB_DECLARE_STR}ORB_DECLARE(${pub_topic});\n") + + # ORB_DEFINE(actuator_armed, struct actuator_armed_s, 16, __orb_actuator_armed_fields, static_cast(ORB_ID::actuator_armed)); + set(PX4_ORB_DEFINE_STR + "${PX4_ORB_DEFINE_STR}ORB_DEFINE(${pub_topic}, ${pub_type}, px4_embedded::${pub_type_simple_lower}_s::SIZE_NO_PADDING, px4_embedded::${pub_type_simple_lower}_s::FIELDS, static_cast(ORB_ID::${pub_topic}));\n") + + set(PX4_ORB_HEADER_INCLUDE_STR + "${PX4_ORB_HEADER_INCLUDE_STR}#include \n") + + math(EXPR PX4_ORB_TOPIC_COUNT "${PX4_ORB_TOPIC_COUNT}+1") + + endforeach() + list(REMOVE_DUPLICATES PX4_MSG_TYPE_ID) + #list(REMOVE_DUPLICATES PX4_MSG_TOPIC_ID) + + #message(STATUS "PX4_MSG_TYPE_ID: ${PX4_MSG_TYPE_ID}") + #message(STATUS "PX4_MSG_TOPIC_ID: ${PX4_MSG_TOPIC_ID}") + + if(PX4_ORB_TOPIC_COUNT GREATER 0) + configure_file(uORBTopics.cpp.in ${CMAKE_BINARY_DIR}/uORBTopics.cpp) + configure_file(uORBTopics.hpp.in ${CMAKE_BINARY_DIR}/uORBTopics.hpp) + endif() + + # .hpp enum ORB_ID + # .cpp struct orb_metadat ORB_ID() array + + foreach(f ${msg_files}) + #message(STATUS "MSG: ${f}") + endforeach() +endif() + +if(PX4_ORB_TOPIC_COUNT GREATER 0) + add_library(uorb_msgs ${uorb_headers} ${CMAKE_BINARY_DIR}/uORBTopics.cpp ${CMAKE_BINARY_DIR}/uORBTopics.hpp) + add_dependencies(uorb_msgs prebuild_targets uorb_headers) +endif() + +if(${PX4_PLATFORM} MATCHES "ros2") # TODO: fix + ament_target_dependencies(uorb_msgs rclcpp std_msgs) + rosidl_target_interfaces(uorb_msgs ${PROJECT_NAME} "rosidl_typesupport_cpp") +endif() + #============================================================================= # uORB graph generation: add a custom target 'uorb_graph' # @@ -466,17 +583,13 @@ include(doxygen) include(metadata) include(package) -# print size -add_custom_target(size - COMMAND size $ - DEPENDS px4 - WORKING_DIRECTORY ${PX4_BINARY_DIR} - USES_TERMINAL - ) - # install python requirements using configured python add_custom_target(install_python_requirements COMMAND ${PYTHON_EXECUTABLE} -m pip install --requirement ${PX4_SOURCE_DIR}/Tools/setup/requirements.txt DEPENDS ${PX4_SOURCE_DIR}/Tools/setup/requirements.txt USES_TERMINAL ) + +if(EXISTS "${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake/finalize.cmake") + include(finalize) +endif() diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index f6d12430c7..0874d152cf 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -829,7 +829,6 @@ RECURSIVE = YES # run. EXCLUDE = @CMAKE_SOURCE_DIR@/src/modules/uavcan/libuavcan \ - @CMAKE_SOURCE_DIR@/src/modules/micrortps_bridge/micro-CDR \ @CMAKE_SOURCE_DIR@/src/examples \ @CMAKE_SOURCE_DIR@/src/templates diff --git a/Jenkinsfile b/Jenkinsfile index 22d4702879..ee953e56ef 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,76 +12,6 @@ pipeline { } } parallel { - // stage('Catkin build on ROS workspace') { - // agent { - // docker { - // image 'px4io/px4-dev-ros-melodic:2021-08-18' - // args '-e CCACHE_BASEDIR=$WORKSPACE -v ${CCACHE_DIR}:${CCACHE_DIR}:rw -e HOME=$WORKSPACE' - // } - // } - // steps { - // sh 'ls -l' - // sh '''#!/bin/bash -l - // echo $0; - // mkdir -p catkin_ws/src; - // cd catkin_ws; - // git -C ${WORKSPACE}/catkin_ws/src/Firmware submodule update --init --recursive --force Tools/sitl_gazebo - // git clone --recursive ${WORKSPACE}/catkin_ws/src/Firmware/Tools/sitl_gazebo src/mavlink_sitl_gazebo; - // git -C ${WORKSPACE}/catkin_ws/src/Firmware fetch --tags; - // source /opt/ros/melodic/setup.bash; - // export PYTHONPATH=/opt/ros/$ROS_DISTRO/lib/python2.7/dist-packages:/usr/lib/python2.7/dist-packages:/usr/local/lib/python2.7/dist-packages; - // catkin init; - // catkin build -j$(nproc) -l$(nproc); - // ''' - // // test if the binary was correctly installed and runs using 'mavros_posix_silt.launch' - // sh '''#!/bin/bash -l - // echo $0; - // source catkin_ws/devel/setup.bash; - // rostest px4 pub_test.launch; - // ''' - // } - // post { - // always { - // sh 'rm -rf catkin_ws' - // } - // failure { - // archiveArtifacts(allowEmptyArchive: false, artifacts: '.ros/**/*.xml, .ros/**/*.log') - // } - // } - // options { - // checkoutToSubdirectory('catkin_ws/src/Firmware') - // } - // } - - stage('Colcon build on ROS2 workspace') { - agent { - docker { - image 'px4io/px4-dev-ros2-foxy:2021-08-18' - args '-e CCACHE_BASEDIR=$WORKSPACE -v ${CCACHE_DIR}:${CCACHE_DIR}:rw -e HOME=$WORKSPACE' - } - } - steps { - sh 'ls -l' - sh '''#!/bin/bash -l - echo $0; - unset ROS_DISTRO; - mkdir -p colcon_ws/src; - cd colcon_ws; - git -C ${WORKSPACE}/colcon_ws/src/Firmware submodule update --init --recursive --force Tools/sitl_gazebo; - git -C ${WORKSPACE}/colcon_ws/src/Firmware fetch --tags; - source /opt/ros/foxy/setup.sh; - colcon build --event-handlers console_direct+ --symlink-install; - ''' - } - post { - always { - sh 'rm -rf colcon_ws' - } - } - options { - checkoutToSubdirectory('colcon_ws/src/Firmware') - } - } stage('Airframe') { agent { @@ -254,108 +184,6 @@ pipeline { } } - stage('microRTPS agent') { - agent { - docker { image 'px4io/px4-dev-base-focal:2021-08-18' } - } - steps { - sh('export') - sh('git fetch --all --tags') - sh('make distclean; git clean -ff -x -d .') - sh('make px4_sitl_rtps') - withCredentials([usernamePassword(credentialsId: 'px4buildbot_github_personal_token', passwordVariable: 'GIT_PASS', usernameVariable: 'GIT_USER')]) { - sh("git clone https://${GIT_USER}:${GIT_PASS}@github.com/PX4/micrortps_agent.git -b ${BRANCH_NAME}") - sh("rm -rf micrortps_agent/src micrortps_agent/idl") - sh('cp -R build/px4_sitl_rtps/src/modules/micrortps_bridge/micrortps_agent/* micrortps_agent') - sh('cd micrortps_agent; git status; git add src; git commit -a -m "Update microRTPS agent source code `date`" || true') - sh('cd micrortps_agent; git push origin ${BRANCH_NAME} || true') - sh('cd micrortps_agent; git status; git add idl; git commit -a -m "Update IDL definitions `date`" || true') - sh('cd micrortps_agent; git push origin ${BRANCH_NAME} || true') - sh('cd micrortps_agent; git status; git add CMakeLists.txt; git commit -a -m "Update CMakeLists.txt `date`" || true') - sh('cd micrortps_agent; git push origin ${BRANCH_NAME} || true') - sh('rm -rf micrortps_agent') - } - } - when { - anyOf { - branch 'master' - branch 'pr-jenkins' // for testing - } - } - } - - stage('PX4 ROS msgs') { - agent { - docker { image 'px4io/px4-dev-base-focal:2021-08-18' } - } - steps { - sh('export') - sh('make distclean; git clean -ff -x -d .') - withCredentials([usernamePassword(credentialsId: 'px4buildbot_github_personal_token', passwordVariable: 'GIT_PASS', usernameVariable: 'GIT_USER')]) { - sh("git clone https://${GIT_USER}:${GIT_PASS}@github.com/PX4/px4_msgs.git") - // 'master' branch - sh('./msg/tools/uorb_to_ros_msgs.py msg/ px4_msgs/msg/') - sh('cd px4_msgs; git status; git add .; git commit -a -m "Update message definitions `date`" || true') - sh('cd px4_msgs; git push origin master || true') - // 'ros1' branch - sh('cd px4_msgs; git checkout ros1') - sh('./msg/tools/uorb_to_ros_msgs.py msg/ px4_msgs/msg/') - sh('cd px4_msgs; git status; git add .; git commit -a -m "Update message definitions `date`" || true') - sh('cd px4_msgs; git push origin ros1 || true') - sh('rm -rf px4_msgs') - } - } - when { - anyOf { - branch 'master' - branch 'pr-jenkins' // for testing - } - } - } - - stage('PX4 ROS2 bridge') { - agent { - docker { image 'px4io/px4-dev-base-focal:2021-08-18' } - } - steps { - sh('export') - sh('make distclean; git clean -ff -x -d .') - withCredentials([usernamePassword(credentialsId: 'px4buildbot_github_personal_token', passwordVariable: 'GIT_PASS', usernameVariable: 'GIT_USER')]) { - sh("git clone https://${GIT_USER}:${GIT_PASS}@github.com/PX4/px4_ros_com.git -b ${BRANCH_NAME}") - // deploy uORB RTPS ID map - sh('./msg/tools/uorb_to_ros_urtps_topics.py -i msg/tools/urtps_bridge_topics.yaml -o px4_ros_com/templates/urtps_bridge_topics.yaml') - sh('cd px4_ros_com; git status; git add .; git commit -a -m "Update uORB RTPS ID map `date`" || true') - sh('cd px4_ros_com; git push origin ${BRANCH_NAME} || true') - // deploy uORB RTPS required tools - sh('cp msg/tools/uorb_rtps_classifier.py px4_ros_com/scripts/uorb_rtps_classifier.py') - sh('cp msg/tools/generate_microRTPS_bridge.py px4_ros_com/scripts/generate_microRTPS_bridge.py') - sh('cp msg/tools/px_generate_uorb_topic_files.py px4_ros_com/scripts/px_generate_uorb_topic_files.py') - sh('cp msg/tools/px_generate_uorb_topic_helper.py px4_ros_com/scripts/px_generate_uorb_topic_helper.py') - // deploy templates - sh('cp msg/templates/urtps/microRTPS_agent.cpp.em px4_ros_com/templates/microRTPS_agent.cpp.em') - sh('cp msg/templates/urtps/microRTPS_timesync.cpp.em px4_ros_com/templates/microRTPS_timesync.cpp.em') - sh('cp msg/templates/urtps/microRTPS_timesync.h.em px4_ros_com/templates/microRTPS_timesync.h.em') - sh('cp msg/templates/urtps/microRTPS_transport.cpp px4_ros_com/templates/microRTPS_transport.cpp') - sh('cp msg/templates/urtps/microRTPS_transport.h px4_ros_com/templates/microRTPS_transport.h') - sh('cp msg/templates/urtps/Publisher.cpp.em px4_ros_com/templates/Publisher.cpp.em') - sh('cp msg/templates/urtps/Publisher.h.em px4_ros_com/templates/Publisher.h.em') - sh('cp msg/templates/urtps/Subscriber.cpp.em px4_ros_com/templates/Subscriber.cpp.em') - sh('cp msg/templates/urtps/Subscriber.h.em px4_ros_com/templates/Subscriber.h.em') - sh('cp msg/templates/urtps/RtpsTopics.cpp.em px4_ros_com/templates/RtpsTopics.cpp.em') - sh('cp msg/templates/urtps/RtpsTopics.h.em px4_ros_com/templates/RtpsTopics.h.em') - sh('cd px4_ros_com; git status; git add .; git commit -a -m "Update uORB RTPS script tools `date`" || true') - sh('cd px4_ros_com; git push origin ${BRANCH_NAME} || true') - sh('rm -rf px4_msgs') - } - } - when { - anyOf { - branch 'master' - branch 'pr-jenkins' // for testing - } - } - } - stage('S3') { agent { docker { image 'px4io/px4-dev-base-focal:2021-08-18' } diff --git a/Kconfig b/Kconfig index 425cf9fa82..7cbd36200a 100644 --- a/Kconfig +++ b/Kconfig @@ -17,6 +17,8 @@ menu "Toolchain" bool "posix" config PLATFORM_QURT bool "qurt" + config PLATFORM_ROS2 + bool "ros2" endchoice config BOARD_PLATFORM @@ -24,6 +26,7 @@ menu "Toolchain" default "nuttx" if PLATFORM_NUTTX default "posix" if PLATFORM_POSIX default "qurt" if PLATFORM_QURT + default "ros2" if PLATFORM_ROS2 config BOARD_LOCKSTEP bool "Force enable lockstep" diff --git a/Makefile b/Makefile index 7f43b46b68..46ad216443 100644 --- a/Makefile +++ b/Makefile @@ -170,11 +170,6 @@ ifdef PYTHON_EXECUTABLE CMAKE_ARGS += -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} endif -# Check if the microRTPS agent is to be built -ifdef BUILD_MICRORTPS_AGENT - CMAKE_ARGS += -DBUILD_MICRORTPS_AGENT=ON -endif - # Functions # -------------------------------------------------------------------- # describe how to build a cmake config @@ -251,7 +246,7 @@ endef # Other targets # -------------------------------------------------------------------- -.PHONY: qgc_firmware px4fmu_firmware misc_qgc_extra_firmware check_rtps +.PHONY: qgc_firmware px4fmu_firmware misc_qgc_extra_firmware # QGroundControl flashable NuttX firmware qgc_firmware: px4fmu_firmware misc_qgc_extra_firmware @@ -276,15 +271,7 @@ misc_qgc_extra_firmware: \ check_airmind_mindpx-v2_default \ sizes -# builds with RTPS -check_rtps: \ - check_px4_fmu-v3_rtps \ - check_px4_fmu-v4_rtps \ - check_px4_fmu-v4pro_rtps \ - check_px4_sitl_rtps \ - sizes - -.PHONY: sizes check quick_check check_rtps uorb_graphs +.PHONY: sizes check quick_check uorb_graphs sizes: @-find build -name *.elf -type f | xargs size 2> /dev/null || : @@ -479,7 +466,7 @@ clang-tidy-quiet: px4_sitl_default-clang # TODO: Fix cppcheck errors then try --enable=warning,performance,portability,style,unusedFunction or --enable=all cppcheck: px4_sitl_default @mkdir -p "$(SRC_DIR)"/build/cppcheck - @cppcheck -i"$(SRC_DIR)"/src/examples --enable=performance --std=c++14 --std=c99 --std=posix --project="$(SRC_DIR)"/build/px4_sitl_default/compile_commands.json --xml-version=2 2> "$(SRC_DIR)"/build/cppcheck/cppcheck-result.xml > /dev/null + @cppcheck -i"$(SRC_DIR)"/src/examples --enable=performance --std=c++17 --std=c99 --std=posix --project="$(SRC_DIR)"/build/px4_sitl_default/compile_commands.json --xml-version=2 2> "$(SRC_DIR)"/build/cppcheck/cppcheck-result.xml > /dev/null @cppcheck-htmlreport --source-encoding=ascii --file="$(SRC_DIR)"/build/cppcheck/cppcheck-result.xml --report-dir="$(SRC_DIR)"/build/cppcheck --source-dir="$(SRC_DIR)"/src/ shellcheck_all: @@ -559,12 +546,3 @@ ifneq ($(ROS2_WS_DIR),) else ROS2_WS_DIR := ~/colcon_ws endif - -update_ros2_bridge: - @Tools/update_px4_ros2_bridge.sh --ws_dir ${ROS2_WS_DIR} --all - -update_px4_ros_com: - @Tools/update_px4_ros2_bridge.sh --ws_dir ${ROS2_WS_DIR} --px4_ros_com - -update_px4_msgs: - @Tools/update_px4_ros2_bridge.sh --ws_dir ${ROS2_WS_DIR} --px4_msgs diff --git a/ROMFS/px4fmu_common/init.d-posix/CMakeLists.txt b/ROMFS/px4fmu_common/init.d-posix/CMakeLists.txt index 419ed0ed18..1ba7ccde27 100644 --- a/ROMFS/px4fmu_common/init.d-posix/CMakeLists.txt +++ b/ROMFS/px4fmu_common/init.d-posix/CMakeLists.txt @@ -36,7 +36,6 @@ add_subdirectory(airframes) px4_add_romfs_files( px4-rc.mavlink px4-rc.params - px4-rc.rtps px4-rc.simulator rc.replay rcS diff --git a/ROMFS/px4fmu_common/init.d-posix/px4-rc.rtps b/ROMFS/px4fmu_common/init.d-posix/px4-rc.rtps deleted file mode 100644 index d3cfa4f817..0000000000 --- a/ROMFS/px4fmu_common/init.d-posix/px4-rc.rtps +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2154 - -micrortps_client start -t UDP -r $((2019+2*px4_instance)) -s $((2020+2*px4_instance)) diff --git a/ROMFS/px4fmu_common/init.d-posix/rcS b/ROMFS/px4fmu_common/init.d-posix/rcS index 13ff820f4d..e45ec6bdaa 100644 --- a/ROMFS/px4fmu_common/init.d-posix/rcS +++ b/ROMFS/px4fmu_common/init.d-posix/rcS @@ -226,12 +226,6 @@ sensors start commander start navigator start -# Try to start the micrortps_client with UDP transport if module exists -if px4-micrortps_client status > /dev/null 2>&1 -then - . px4-rc.rtps -fi - if param greater -s MNT_MODE_IN -1 then gimbal start diff --git a/Tools/astyle/files_to_check_code_style.sh b/Tools/astyle/files_to_check_code_style.sh index 963c8a51cf..834ed5a2ec 100755 --- a/Tools/astyle/files_to_check_code_style.sh +++ b/Tools/astyle/files_to_check_code_style.sh @@ -8,7 +8,6 @@ if [ $# -gt 0 ]; then fi exec find boards msg src platforms test \ - -path msg/templates/urtps -prune -o \ -path platforms/nuttx/NuttX -prune -o \ -path platforms/qurt/dspal -prune -o \ -path src/drivers/uavcan/libuavcan -prune -o \ @@ -21,8 +20,6 @@ exec find boards msg src platforms test \ -path src/modules/ekf2/EKF -prune -o \ -path src/modules/gyro_fft/CMSIS_5 -prune -o \ -path src/modules/mavlink/mavlink -prune -o \ - -path src/modules/micrortps_bridge/micro-CDR -prune -o \ - -path src/modules/micrortps_bridge/microRTPS_client -prune -o \ -path test/mavsdk_tests/catch2 -prune -o \ -path src/lib/crypto/monocypher -prune -o \ -path src/lib/crypto/libtomcrypt -prune -o \ diff --git a/Tools/build_micrortps_agent.sh b/Tools/build_micrortps_agent.sh deleted file mode 100755 index 6d45eaeb41..0000000000 --- a/Tools/build_micrortps_agent.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e - -SCRIPT_DIR=$0 -if [[ ${SCRIPT_DIR:0:1} != '/' ]]; then - SCRIPT_DIR=$(dirname $(realpath -s "$PWD/$0")) -fi - -PX4_DIR=$(cd "$(dirname $(dirname $SCRIPT_DIR))" && pwd) - -if [ -d $PX4_DIR/build/*_rtps ]; then - cd $PX4_DIR/build/*_rtps/src/modules/micrortps_bridge/micrortps_agent/ - cmake -Bbuild - cmake --build build -j$(nproc --all) -fi diff --git a/Tools/kconfig/cmake_kconfig_lut.txt b/Tools/kconfig/cmake_kconfig_lut.txt index b9298e75dc..c66646a4ef 100644 --- a/Tools/kconfig/cmake_kconfig_lut.txt +++ b/Tools/kconfig/cmake_kconfig_lut.txt @@ -93,7 +93,6 @@ pca9685_pwm_out,CONFIG_DRIVERS_PCA9685_PWM_OUT=y power_monitor/ina226,CONFIG_DRIVERS_POWER_MONITOR_INA226=y power_monitor/voxlpm,CONFIG_DRIVERS_POWER_MONITOR_VOXLPM=y pps_capture,CONFIG_DRIVERS_PPS_CAPTURE=y -protocol_splitter,CONFIG_DRIVERS_PROTOCOL_SPLITTER=y pwm_input,CONFIG_DRIVERS_PWM_INPUT=y pwm_out_sim,CONFIG_DRIVERS_PWM_OUT_SIM=y pwm_out,CONFIG_DRIVERS_PWM_OUT=y @@ -138,7 +137,6 @@ mc_att_control,CONFIG_MODULES_MC_ATT_CONTROL=y mc_hover_thrust_estimator,CONFIG_MODULES_MC_HOVER_THRUST_ESTIMATOR=y mc_pos_control,CONFIG_MODULES_MC_POS_CONTROL=y mc_rate_control,CONFIG_MODULES_MC_RATE_CONTROL=y -micrortps_bridge,CONFIG_MODULES_MICRORTPS_BRIDGE=y navigator,CONFIG_MODULES_NAVIGATOR=y px4iofirmware,CONFIG_MODULES_PX4IOFIRMWARE=y rc_update,CONFIG_MODULES_RC_UPDATE=y diff --git a/Tools/px4_developer.mk.example b/Tools/px4_developer.mk.example deleted file mode 100644 index 54c3024831..0000000000 --- a/Tools/px4_developer.mk.example +++ /dev/null @@ -1,5 +0,0 @@ -$(info px4_developer.mk inclded) -ifeq ($(UAVCAN_BL_OVERRIDE),y) -$(info ************************** UAVCAN BOOT LOADERS built for In place application Debugging ***************************************) -export EXTRAFLAGS +=-DDEBUG_APPLICATION_INPLACE -endif \ No newline at end of file diff --git a/Tools/setup/arch.sh b/Tools/setup/arch.sh index 83bc225d49..f27d7e756f 100755 --- a/Tools/setup/arch.sh +++ b/Tools/setup/arch.sh @@ -9,8 +9,6 @@ ## - jMAVSim simulator (omit with arg: --no-sim-tools) ## - Gazebo simulator (not by default, use --gazebo) ## -## Not Installs: -## - FastRTPS and FastCDR INSTALL_NUTTX="true" INSTALL_SIM="true" @@ -122,7 +120,7 @@ if [[ $INSTALL_SIM == "true" ]]; then echo echo "Installing PX4 simulation dependencies" - # java (jmavsim or fastrtps) + # java (jmavsim) sudo pacman -S --noconfirm --needed \ ant \ jdk-openjdk \ diff --git a/Tools/setup/ubuntu.sh b/Tools/setup/ubuntu.sh index 852f38c94c..abc6492724 100755 --- a/Tools/setup/ubuntu.sh +++ b/Tools/setup/ubuntu.sh @@ -10,8 +10,6 @@ set -e ## - NuttX toolchain (omit with arg: --no-nuttx) ## - jMAVSim and Gazebo9 simulator (omit with arg: --no-sim-tools) ## -## Not Installs: -## - FastRTPS and FastCDR INSTALL_NUTTX="true" INSTALL_SIM="true" @@ -208,7 +206,7 @@ if [[ $INSTALL_SIM == "true" ]]; then java_version=14 gazebo_version=11 fi - # Java (jmavsim or fastrtps) + # Java (jmavsim) sudo DEBIAN_FRONTEND=noninteractive apt-get -y --quiet --no-install-recommends install \ ant \ openjdk-$java_version-jre \ diff --git a/Tools/update_px4_ros2_bridge.sh b/Tools/update_px4_ros2_bridge.sh deleted file mode 100755 index 58ebca5b7c..0000000000 --- a/Tools/update_px4_ros2_bridge.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash -set -e - -agent_template_files_updated=0 -code_generator_files_updated=0 - -# parse help argument -if [[ $1 == "-h" ]] || [[ $1 == "--help" ]]; then - echo -e "Usage: update_px4_ros2_bridge.bash [options...] \t This script allows to update px4_ros_com and px4_msgs in a specified directory." >&2 - echo - echo -e "\t--ws_dir \t\t Location of the ament/colcon workspace. Default: $HOME/colcon_ws." - echo -e "\t--px4_ros_com \t\t Updates px4_ros_com microRTPS agent code generation and templates." - echo -e "\t--px4_msgs \t\t Updates px4_msgs messages definition files." - echo -e "\t--all \t\t Updates both px4_ros_com and px4_msgs." - echo - exit 0 -fi - -# parse the arguments -while [ $# -gt 0 ]; do - if [[ $1 == *"--"* ]]; then - v="${1/--/}" - if [ ! -z $2 ]; then - declare $v="$2" - else - declare $v=1 - fi - fi - shift -done - -# get script directory -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# get PX4-Autopilot directory -PX4_DIR=$(cd "$(dirname "$SCRIPT_DIR")" && pwd) - -function compare_and_update () { - cmp -s "$1" "$2" - if [ $? -eq 1 ]; then - cp "$1" "$2" - return 0 - fi - - return 1; -} - -# update microRTPS agent code generators / helpers -function update_agent_code { - declare -a templates=( \ - "microRTPS_agent.cpp.em" \ - "microRTPS_timesync.cpp.em" \ - "microRTPS_timesync.h.em" \ - "microRTPS_transport.cpp" \ - "microRTPS_transport.h" \ - "Publisher.cpp.em" \ - "Publisher.h.em" \ - "Subscriber.cpp.em" \ - "Subscriber.h.em" \ - "RtpsTopics.cpp.em" \ - "RtpsTopics.h.em" \ - ) - - for file in ${templates[@]}; do - compare_and_update "$PX4_DIR/msg/templates/urtps/$file" "$ws_dir/src/px4_ros_com/templates/$file" \ - && echo -e "--\t\t- '$ws_dir/src/px4_ros_com/templates/$file' updated" && ((agent_template_files_updated+=1)) - done - if [ $agent_template_files_updated -eq 0 ]; then - echo -e "--\t\t- No template files updated" - elif [ $agent_template_files_updated -eq 1 ]; then - echo -e "--\t\t - 1 template file updated" - else - echo -e "--\t\t - $agent_template_files_updated template files updated" - fi -} - -# update microRTPS agent code templates -function update_agent_templates { - declare -a code_generators=( \ - "uorb_rtps_classifier.py" \ - "generate_microRTPS_bridge.py" \ - "px_generate_uorb_topic_files.py" \ - ) - for file in ${code_generators[@]}; do - compare_and_update "$PX4_DIR/msg/tools/$file $ws_dir/src/px4_ros_com/scripts/$file" \ - && echo -e "--\t\t- '$ws_dir/src/px4_ros_com/scripts/$file' updated" && ((code_generator_files_updated+=1)) - done - if [ $code_generator_files_updated -eq 0 ]; then - echo -e "--\t\t- No code generators / helpers files updated" - elif [ $code_generator_files_updated -eq 1 ]; then - echo -e "--\t\t - 1 code generator / helper file updated" - else - echo -e "--\t\t - $code_generator_files_updated code generator / helper files updated" - fi -} - -# update px4_ros_com files -function update_px4_ros_com { - python3 $PX4_DIR/msg/tools/uorb_to_ros_urtps_topics.py -i $PX4_DIR/msg/tools/urtps_bridge_topics.yaml -o $ws_dir/src/px4_ros_com/templates/urtps_bridge_topics.yaml - echo -e "--------------- \033[1mmicroRTPS agent code generation and templates update\033[0m ----------------" - echo "-------------------------------------------------------------------------------------------------------" - update_agent_code - update_agent_templates - return 0 -} - -# function to update px4_msgs -function update_px4_msgs { - find "$ws_dir/src/px4_msgs/msg/" -maxdepth 1 -type f -delete - python3 $PX4_DIR/msg/tools/uorb_to_ros_msgs.py $PX4_DIR/msg/ $ws_dir/src/px4_msgs/msg/ -} - -# decisor -echo "-------------------------------------------------------------------------------------------------------" -if [ -d "${ws_dir}" ]; then - ws_dir=$(cd "$ws_dir" && pwd) - if [ ! -z ${all} ]; then - update_px4_ros_com - echo "-------------------------------------------------------------------------------------------------------" - update_px4_msgs - elif [ ! -z ${px4_ros_com} ]; then - update_px4_ros_com - echo "-------------------------------------------------------------------------------------------------------" - elif [ ! -z ${px4_msgs} ]; then - update_px4_msgs - fi - echo -e "-------------------------------- \033[0;32mUpdate successful!\033[0m ---------------------------------" - echo "-------------------------------------------------------------------------------------------------------" - exit 0 -else - echo -e "-- \033[0;31mWorkspace directory doesn't exist...\033[0m" - echo -e "---------------------------------- \033[0;31mUpdate failed!\033[0m -----------------------------------" - echo "-------------------------------------------------------------------------------------------------------" - exit $ERRCODE -fi diff --git a/boards/bitcraze/crazyflie/syslink/CMakeLists.txt b/boards/bitcraze/crazyflie/syslink/CMakeLists.txt index ebf6c04c08..f89269dbc8 100644 --- a/boards/bitcraze/crazyflie/syslink/CMakeLists.txt +++ b/boards/bitcraze/crazyflie/syslink/CMakeLists.txt @@ -44,4 +44,6 @@ px4_add_module( syslink.c DEPENDS battery + SUBSCRIPTIONS + "px4::msg::BatteryStatus /battery_status" ) diff --git a/boards/cuav/nora/extras/cuav_nora_bootloader.bin b/boards/cuav/nora/extras/cuav_nora_bootloader.bin index 77449064c7e8c611a2509558a354fdfa530da1bb..3ba1922daa31741dfedda815bc2749580054c7b4 100755 GIT binary patch delta 12887 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 delta 12700 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 X5dgs50>XU&P|5?m-$GXZ*M$EAw;_9G diff --git a/boards/cuav/x7pro/extras/cuav_x7pro_bootloader.bin b/boards/cuav/x7pro/extras/cuav_x7pro_bootloader.bin index a27ec0069fd9fe323c8af0a7db69fad0ae31b53d..ade6662095c9dbc62a93db00731264b051ae4ac2 100755 GIT binary patch delta 12887 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 delta 12700 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 bBj5Vl7$6mAPeTlQ59i}*^Rt$+?Pj!kKS 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 YUjb3qMdEV*mgE diff --git a/boards/cubepilot/cubeorange/extras/cubepilot_io-v2_default.bin b/boards/cubepilot/cubeorange/extras/cubepilot_io-v2_default.bin index 0179a993e62b05e6a1b9749cd20c99b5318d1afa..93a1e13448fc13e756757f2a230c48daee3199f9 100755 GIT binary patch delta 7809 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 UbnToOkDz1(fEfc|@*e>FUl#fE6#xJL diff --git a/boards/cubepilot/cubeyellow/extras/cubepilot_io-v2_default.bin b/boards/cubepilot/cubeyellow/extras/cubepilot_io-v2_default.bin index 0179a993e62b05e6a1b9749cd20c99b5318d1afa..93a1e13448fc13e756757f2a230c48daee3199f9 100755 GIT binary patch delta 7809 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 UbnToOkDz1(fEfc|@*e>FUl#fE6#xJL diff --git a/boards/freefly/can-rtk-gps/canbootloader.px4board b/boards/freefly/can-rtk-gps/canbootloader.px4board index b0fd876605..cc9f7769c1 100644 --- a/boards/freefly/can-rtk-gps/canbootloader.px4board +++ b/boards/freefly/can-rtk-gps/canbootloader.px4board @@ -3,4 +3,3 @@ CONFIG_BOARD_ARCHITECTURE="cortex-m7" CONFIG_BOARD_ROMFSROOT="" CONFIG_BOARD_CONSTRAINED_MEMORY=y CONFIG_DRIVERS_BOOTLOADERS=y -CONFIG_DRIVERS_LIGHTS_RGBLED_NCP5623C=y diff --git a/boards/freefly/can-rtk-gps/src/CMakeLists.txt b/boards/freefly/can-rtk-gps/src/CMakeLists.txt index 246b51200a..19094576ea 100644 --- a/boards/freefly/can-rtk-gps/src/CMakeLists.txt +++ b/boards/freefly/can-rtk-gps/src/CMakeLists.txt @@ -44,6 +44,8 @@ if("${PX4_BOARD_LABEL}" STREQUAL "canbootloader") nuttx_arch nuttx_drivers canbootloader + drivers__device + px4_platform ) target_include_directories(drivers_board PRIVATE ${PX4_SOURCE_DIR}/platforms/nuttx/src/canbootloader) diff --git a/boards/holybro/durandal-v1/extras/holybro_durandal-v1_bootloader.bin b/boards/holybro/durandal-v1/extras/holybro_durandal-v1_bootloader.bin index 4f66190c0711606e25666432075079a1bd3c524b..59c147b807d78514a5fdc2674db0c388af53159e 100755 GIT binary patch delta 12880 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 M0Ee5%p{Csb1Jzu{ApigX delta 12600 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%0AZzBC2y|6LIb+h7FHIhB)Do~Vr|DD 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 diff --git a/boards/holybro/durandal-v1/extras/px4_io-v2_default.bin b/boards/holybro/durandal-v1/extras/px4_io-v2_default.bin index cef251c5f50280243052c56143c4d947afe41965..a91611df6997bdcc4f6bd0ee889c97480a00acd9 100755 GIT binary patch delta 7809 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 diff --git a/boards/modalai/fc-v1/rtps.px4board b/boards/modalai/fc-v1/rtps.px4board deleted file mode 100644 index 81e62c7825..0000000000 --- a/boards/modalai/fc-v1/rtps.px4board +++ /dev/null @@ -1 +0,0 @@ -CONFIG_MODULES_MICRORTPS_BRIDGE=y diff --git a/boards/modalai/fc-v2/extras/modalai_fc-v2_bootloader.bin b/boards/modalai/fc-v2/extras/modalai_fc-v2_bootloader.bin index 6608f5916d1e0c13c205ffff78a2e9e7f8c40278..21c40365e22497478af1cc4d5326979055edde55 100755 GIT binary patch delta 12798 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 aG)U;zgMQDGCJ^rcfD!?qp@}p#W&b}u<*-!% delta 12567 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 R;_U!XCIBotLY5!N{XZ;m%DDgl delta 12527 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 LOOKMpM|1xVo)XrN delta 12524 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 delta 12544 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 F{{@aMFW>+G diff --git a/boards/px4/fmu-v6x/extras/px4_io-v2_default.bin b/boards/px4/fmu-v6x/extras/px4_io-v2_default.bin index cef251c5f50280243052c56143c4d947afe41965..a91611df6997bdcc4f6bd0ee889c97480a00acd9 100755 GIT binary patch delta 7809 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 Y_n}16wR2`Xf|3ycW( + * Copyright (c) 2021 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,25 +32,19 @@ ****************************************************************************/ /** - * @file conversions.c - * Implementation of commonly used conversions. + * @file board_config.h + * + * SITL internal definitions */ -#include -#include +#pragma once -#include "conversions.h" +#define BOARD_OVERRIDE_UUID "SIMULATIONID0000" // must be of length 16 +#define PX4_SOC_ARCH_ID PX4_SOC_ARCH_ID_SITL -int16_t -int16_t_from_bytes(uint8_t bytes[]) -{ - union { - uint8_t b[2]; - int16_t w; - } u; +#define BOARD_HAS_POWER_CONTROL 1 - u.b[1] = bytes[0]; - u.b[0] = bytes[1]; +#define BOARD_NUMBER_BRICKS 0 - return u.w; -} +#include +#include diff --git a/msg/tools/__init__.py b/boards/px4/ros2/src/init.cpp similarity index 100% rename from msg/tools/__init__.py rename to boards/px4/ros2/src/init.cpp diff --git a/boards/px4/sitl/rtps.px4board b/boards/px4/sitl/rtps.px4board deleted file mode 100644 index 81e62c7825..0000000000 --- a/boards/px4/sitl/rtps.px4board +++ /dev/null @@ -1 +0,0 @@ -CONFIG_MODULES_MICRORTPS_BRIDGE=y diff --git a/cmake/package.cmake b/cmake/package.cmake index fd71343221..69d3ab7a06 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -57,4 +57,7 @@ else() endif() include(CPack) -include(bloaty) + +if(${PX4_PLATFORM} MATCHES "nuttx") + include(bloaty) +endif() diff --git a/cmake/px4_add_common_flags.cmake b/cmake/px4_add_common_flags.cmake index 71fbbf2657..cce78801f1 100644 --- a/cmake/px4_add_common_flags.cmake +++ b/cmake/px4_add_common_flags.cmake @@ -71,11 +71,11 @@ function(px4_add_common_flags) -Werror -Warray-bounds - -Wcast-align + #-Wcast-align # TODO -Wdisabled-optimization -Wdouble-promotion -Wfatal-errors - -Wfloat-equal + #-Wfloat-equal -Wformat-security -Winit-self -Wlogical-op @@ -156,9 +156,6 @@ function(px4_add_common_flags) # CXX only flags set(cxx_flags) list(APPEND cxx_flags - -fno-exceptions - -fno-threadsafe-statics - -Wreorder # disabled warnings @@ -175,16 +172,19 @@ function(px4_add_common_flags) add_compile_options($<$:${flag}>) endforeach() + if(NOT (${PX4_PLATFORM} MATCHES "ros2")) # TODO: fix + include_directories( + ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/src/px4/${PX4_CHIP_MANUFACTURER}/${PX4_CHIP}/include + ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/src/px4/common/include + ${PX4_SOURCE_DIR}/platforms/common + ${PX4_SOURCE_DIR}/platforms/common/include + ) + endif() include_directories( ${PX4_BINARY_DIR} ${PX4_BINARY_DIR}/src/lib - ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/src/px4/${PX4_CHIP_MANUFACTURER}/${PX4_CHIP}/include - ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/src/px4/common/include - ${PX4_SOURCE_DIR}/platforms/common - ${PX4_SOURCE_DIR}/platforms/common/include - ${PX4_SOURCE_DIR}/src ${PX4_SOURCE_DIR}/src/include ${PX4_SOURCE_DIR}/src/lib diff --git a/cmake/px4_add_library.cmake b/cmake/px4_add_library.cmake index 7dc7b6cfd0..56c00263c6 100644 --- a/cmake/px4_add_library.cmake +++ b/cmake/px4_add_library.cmake @@ -39,7 +39,10 @@ include(px4_list_make_absolute) # Like add_library but with PX4 platform dependencies # function(px4_add_library target) - add_library(${target} EXCLUDE_FROM_ALL ${ARGN}) + add_library(${target} STATIC + EXCLUDE_FROM_ALL + ${ARGN} + ) target_compile_definitions(${target} PRIVATE MODULE_NAME="${target}") diff --git a/cmake/px4_add_module.cmake b/cmake/px4_add_module.cmake index ff6e1e4075..3985cebba4 100644 --- a/cmake/px4_add_module.cmake +++ b/cmake/px4_add_module.cmake @@ -48,6 +48,7 @@ include(px4_list_make_absolute) # [ DEPENDS ] # [ SRCS ] # [ MODULE_CONFIG ] +# [ PUBLICATIONS ] # [ EXTERNAL ] # [ DYNAMIC ] # ) @@ -64,6 +65,8 @@ include(px4_list_make_absolute) # MODULE_CONFIG : yaml config file(s) # INCLUDES : include directories # DEPENDS : targets which this module depends on +# PUBLICATIONS : List of publications from this module +# SUBSCRIPTIONS : List of subsriptions used by this module # EXTERNAL : flag to indicate that this module is out-of-tree # DYNAMIC : don't compile into the px4 binary, but build a separate dynamically loadable module (posix) # UNITY_BUILD : merge all source files and build this module as a single compilation unit @@ -86,7 +89,7 @@ function(px4_add_module) px4_parse_function_args( NAME px4_add_module ONE_VALUE MODULE MAIN STACK_MAIN STACK_MAX PRIORITY - MULTI_VALUE COMPILE_FLAGS LINK_FLAGS SRCS INCLUDES DEPENDS MODULE_CONFIG + MULTI_VALUE COMPILE_FLAGS LINK_FLAGS SRCS INCLUDES DEPENDS MODULE_CONFIG PUBLICATIONS SUBSCRIPTIONS OPTIONS EXTERNAL DYNAMIC UNITY_BUILD REQUIRED MODULE MAIN ARGN ${ARGN}) @@ -185,7 +188,7 @@ function(px4_add_module) # set defaults if not set set(MAIN_DEFAULT MAIN-NOTFOUND) - set(STACK_MAIN_DEFAULT 2048) + set(STACK_MAIN_DEFAULT 4096) set(PRIORITY_DEFAULT SCHED_PRIORITY_DEFAULT) foreach(property MAIN STACK_MAIN PRIORITY) @@ -238,7 +241,21 @@ function(px4_add_module) endforeach() endif() - foreach (prop LINK_FLAGS STACK_MAIN MAIN PRIORITY) + if(PUBLICATIONS) + foreach(publication ${PUBLICATIONS}) + #message(STATUS "${MODULE} PUBLICATION: ${publication}") + set_property(GLOBAL APPEND PROPERTY PX4_PUBLICATIONS ${publication}) + endforeach() + endif() + + if(SUBSCRIPTIONS) + foreach(subscription ${SUBSCRIPTIONS}) + #message(STATUS "${MODULE} SUBSCRIPTION: ${subscription}") + set_property(GLOBAL APPEND PROPERTY PX4_SUBSCRIPTIONS ${subscription}) + endforeach() + endif() + + foreach(prop LINK_FLAGS STACK_MAIN MAIN PRIORITY) if (${prop}) set_target_properties(${MODULE} PROPERTIES ${prop} ${${prop}}) endif() diff --git a/cmake/px4_config.cmake b/cmake/px4_config.cmake index 41ea7b8c07..3c8ca6eee7 100644 --- a/cmake/px4_config.cmake +++ b/cmake/px4_config.cmake @@ -63,9 +63,9 @@ if(NOT PX4_CONFIG_FILE) ) set(PX4_CONFIG_FILE "${PX4_SOURCE_DIR}/boards/${filename}" CACHE FILEPATH "path to PX4 CONFIG file" FORCE) set(PX4_BOARD_DIR "${PX4_SOURCE_DIR}/boards/${vendor}/${model}" CACHE STRING "PX4 board directory" FORCE) - set(MODEL "${model}" CACHE STRING "PX4 board model" FORCE) - set(VENDOR "${vendor}" CACHE STRING "PX4 board vendor" FORCE) - set(LABEL "${label}" CACHE STRING "PX4 board vendor" FORCE) + set(MODEL "${model}" CACHE STRING "PX4 board model" FORCE) + set(VENDOR "${vendor}" CACHE STRING "PX4 board vendor" FORCE) + set(LABEL "${label}" CACHE STRING "PX4 board vendor" FORCE) break() endif() @@ -76,9 +76,9 @@ if(NOT PX4_CONFIG_FILE) ) set(PX4_CONFIG_FILE "${PX4_SOURCE_DIR}/boards/${filename}" CACHE FILEPATH "path to PX4 CONFIG file" FORCE) set(PX4_BOARD_DIR "${PX4_SOURCE_DIR}/boards/${vendor}/${model}" CACHE STRING "PX4 board directory" FORCE) - set(MODEL "${model}" CACHE STRING "PX4 board model" FORCE) - set(VENDOR "${vendor}" CACHE STRING "PX4 board vendor" FORCE) - set(LABEL "${label}" CACHE STRING "PX4 board vendor" FORCE) + set(MODEL "${model}" CACHE STRING "PX4 board model" FORCE) + set(VENDOR "${vendor}" CACHE STRING "PX4 board vendor" FORCE) + set(LABEL "${label}" CACHE STRING "PX4 board vendor" FORCE) break() endif() endif() diff --git a/integrationtests/python_src/px4_it/mavros/mission_test.py b/integrationtests/python_src/px4_it/mavros/mission_test.py index fac011eb7d..d3900ae063 100755 --- a/integrationtests/python_src/px4_it/mavros/mission_test.py +++ b/integrationtests/python_src/px4_it/mavros/mission_test.py @@ -47,7 +47,6 @@ import glob import json import math import os -from px4tools import ulog import sys from mavros import mavlink from mavros_msgs.msg import Mavlink, Waypoint, WaypointReached @@ -295,17 +294,6 @@ class MavrosMissionTest(MavrosTestCommon): rospy.loginfo("mission done, calculating performance metrics") last_log = get_last_log() rospy.loginfo("log file {0}".format(last_log)) - data = ulog.read_ulog(last_log).concat(dt=0.1) - data = ulog.compute_data(data) - res = ulog.estimator_analysis(data, False) - - # enforce performance - self.assertTrue(abs(res['roll_error_mean']) < 5.0, str(res)) - self.assertTrue(abs(res['pitch_error_mean']) < 5.0, str(res)) - self.assertTrue(abs(res['yaw_error_mean']) < 5.0, str(res)) - self.assertTrue(res['roll_error_std'] < 5.0, str(res)) - self.assertTrue(res['pitch_error_std'] < 5.0, str(res)) - self.assertTrue(res['yaw_error_std'] < 5.0, str(res)) if __name__ == '__main__': diff --git a/msg/action_request.msg b/msg/ActionRequest.msg similarity index 100% rename from msg/action_request.msg rename to msg/ActionRequest.msg diff --git a/msg/actuator_armed.msg b/msg/ActuatorArmed.msg similarity index 100% rename from msg/actuator_armed.msg rename to msg/ActuatorArmed.msg diff --git a/msg/actuator_controls.msg b/msg/ActuatorControls.msg similarity index 79% rename from msg/actuator_controls.msg rename to msg/ActuatorControls.msg index 25ff45f432..0e9eb98dd5 100644 --- a/msg/actuator_controls.msg +++ b/msg/ActuatorControls.msg @@ -20,6 +20,3 @@ uint8 GROUP_INDEX_PAYLOAD = 6 uint64 timestamp_sample # the timestamp the data this control response is based on was sampled float32[8] control - -# TOPICS actuator_controls actuator_controls_0 actuator_controls_1 actuator_controls_2 actuator_controls_3 -# TOPICS actuator_controls_virtual_fw actuator_controls_virtual_mc diff --git a/msg/actuator_controls_status.msg b/msg/ActuatorControlsStatus.msg similarity index 66% rename from msg/actuator_controls_status.msg rename to msg/ActuatorControlsStatus.msg index 4e3bf83450..98364042ac 100644 --- a/msg/actuator_controls_status.msg +++ b/msg/ActuatorControlsStatus.msg @@ -6,5 +6,3 @@ uint8 INDEX_YAW = 2 uint8 INDEX_THROTTLE = 3 float32[4] control_power - -# TOPICS actuator_controls_status actuator_controls_status_0 actuator_controls_status_1 diff --git a/msg/actuator_motors.msg b/msg/ActuatorMotors.msg similarity index 100% rename from msg/actuator_motors.msg rename to msg/ActuatorMotors.msg diff --git a/msg/actuator_outputs.msg b/msg/ActuatorOutputs.msg similarity index 65% rename from msg/actuator_outputs.msg rename to msg/ActuatorOutputs.msg index c16b73b5d7..ff546948d8 100644 --- a/msg/actuator_outputs.msg +++ b/msg/ActuatorOutputs.msg @@ -3,6 +3,3 @@ uint8 NUM_ACTUATOR_OUTPUTS = 16 uint8 NUM_ACTUATOR_OUTPUT_GROUPS = 4 # for sanity checking uint32 noutputs # valid outputs float32[16] output # output data, in natural output units - -# actuator_outputs_sim is used for SITL, HITL & SIH (with an output range of [-1, 1]) -# TOPICS actuator_outputs actuator_outputs_sim diff --git a/msg/actuator_servos.msg b/msg/ActuatorServos.msg similarity index 100% rename from msg/actuator_servos.msg rename to msg/ActuatorServos.msg diff --git a/msg/actuator_servos_trim.msg b/msg/ActuatorServosTrim.msg similarity index 100% rename from msg/actuator_servos_trim.msg rename to msg/ActuatorServosTrim.msg diff --git a/msg/actuator_test.msg b/msg/ActuatorTest.msg similarity index 100% rename from msg/actuator_test.msg rename to msg/ActuatorTest.msg diff --git a/msg/adc_report.msg b/msg/AdcReport.msg similarity index 100% rename from msg/adc_report.msg rename to msg/AdcReport.msg diff --git a/msg/airspeed.msg b/msg/Airspeed.msg similarity index 100% rename from msg/airspeed.msg rename to msg/Airspeed.msg diff --git a/msg/airspeed_validated.msg b/msg/AirspeedValidated.msg similarity index 100% rename from msg/airspeed_validated.msg rename to msg/AirspeedValidated.msg diff --git a/msg/airspeed_wind.msg b/msg/AirspeedWind.msg similarity index 100% rename from msg/airspeed_wind.msg rename to msg/AirspeedWind.msg diff --git a/msg/autotune_attitude_control_status.msg b/msg/AutotuneAttitudeControlStatus.msg similarity index 100% rename from msg/autotune_attitude_control_status.msg rename to msg/AutotuneAttitudeControlStatus.msg diff --git a/msg/battery_status.msg b/msg/BatteryStatus.msg similarity index 100% rename from msg/battery_status.msg rename to msg/BatteryStatus.msg diff --git a/msg/CMakeLists.txt b/msg/CMakeLists.txt index 433c36b08f..b698a7718a 100644 --- a/msg/CMakeLists.txt +++ b/msg/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2016 PX4 Development Team. All rights reserved. +# Copyright (c) 2016-2022 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,213 +37,179 @@ cmake_policy(SET CMP0057 NEW) include(px4_list_make_absolute) set(msg_files - action_request.msg - actuator_armed.msg - actuator_controls.msg - actuator_controls_status.msg - actuator_motors.msg - actuator_outputs.msg - actuator_servos.msg - actuator_servos_trim.msg - actuator_test.msg - adc_report.msg - airspeed.msg - airspeed_validated.msg - airspeed_wind.msg - autotune_attitude_control_status.msg - battery_status.msg - camera_capture.msg - camera_status.msg - camera_trigger.msg - cellular_status.msg - collision_constraints.msg - collision_report.msg - commander_state.msg - control_allocator_status.msg - cpuload.msg - differential_pressure.msg - distance_sensor.msg - ekf2_timestamps.msg - estimator_gps_status.msg - esc_report.msg - esc_status.msg - estimator_baro_bias.msg - estimator_event_flags.msg - estimator_innovations.msg - estimator_optical_flow_vel.msg - estimator_selector_status.msg - estimator_sensor_bias.msg - estimator_states.msg - estimator_status.msg - estimator_status_flags.msg - event.msg - follow_target.msg - failure_detector_status.msg - generator_status.msg - geofence_result.msg - gimbal_device_attitude_status.msg - gimbal_device_information.msg - gimbal_device_set_attitude.msg - gimbal_manager_information.msg - gimbal_manager_set_attitude.msg - gimbal_manager_set_manual_control.msg - gimbal_manager_status.msg - gps_dump.msg - gps_inject_data.msg - heater_status.msg - home_position.msg - hover_thrust_estimate.msg - internal_combustion_engine_status.msg - input_rc.msg - iridiumsbd_status.msg - irlock_report.msg - landing_gear.msg - landing_target_innovations.msg - landing_target_pose.msg - led_control.msg - log_message.msg - logger_status.msg - mag_worker_data.msg - magnetometer_bias_estimate.msg - manual_control_setpoint.msg - manual_control_switches.msg - mavlink_log.msg - mavlink_tunnel.msg - mission.msg - mission_result.msg - mount_orientation.msg - navigator_mission_item.msg - npfg_status.msg - obstacle_distance.msg - offboard_control_mode.msg - onboard_computer_status.msg - optical_flow.msg - orbit_status.msg - parameter_update.msg - ping.msg - pps_capture.msg - position_controller_landing_status.msg - position_controller_status.msg - position_setpoint.msg - position_setpoint_triplet.msg - power_button_state.msg - power_monitor.msg - pwm_input.msg - px4io_status.msg - radio_status.msg - rate_ctrl_status.msg - rc_channels.msg - rc_parameter_map.msg - rpm.msg - rtl_time_estimate.msg - safety.msg - satellite_info.msg - sensor_accel.msg - sensor_accel_fifo.msg - sensor_baro.msg - sensor_combined.msg - sensor_correction.msg - sensor_gps.msg - sensor_gyro.msg - sensor_gyro_fft.msg - sensor_gyro_fifo.msg - sensor_hygrometer.msg - sensor_mag.msg - sensor_preflight_mag.msg - sensor_selection.msg - sensors_status_imu.msg - system_power.msg - takeoff_status.msg - task_stack_info.msg - tecs_status.msg - telemetry_status.msg - test_motor.msg - timesync.msg - timesync_status.msg - trajectory_bezier.msg - trajectory_waypoint.msg - transponder_report.msg - tune_control.msg - uavcan_parameter_request.msg - uavcan_parameter_value.msg - ulog_stream.msg - ulog_stream_ack.msg - vehicle_acceleration.msg - vehicle_air_data.msg - vehicle_angular_acceleration.msg - vehicle_angular_acceleration_setpoint.msg - vehicle_angular_velocity.msg - vehicle_attitude.msg - vehicle_attitude_setpoint.msg - vehicle_command.msg - vehicle_command_ack.msg - vehicle_constraints.msg - vehicle_control_mode.msg - vehicle_global_position.msg - vehicle_gps_position.msg - vehicle_imu.msg - vehicle_imu_status.msg - vehicle_land_detected.msg - vehicle_local_position.msg - vehicle_local_position_setpoint.msg - vehicle_magnetometer.msg - vehicle_odometry.msg - vehicle_rates_setpoint.msg - vehicle_roi.msg - vehicle_status.msg - vehicle_status_flags.msg - vehicle_thrust_setpoint.msg - vehicle_torque_setpoint.msg - vehicle_trajectory_bezier.msg - vehicle_trajectory_waypoint.msg - vtol_vehicle_status.msg - wheel_encoders.msg - wind.msg - yaw_estimator_status.msg + ActionRequest.msg + ActuatorArmed.msg + ActuatorControls.msg + ActuatorControlsStatus.msg + ActuatorMotors.msg + ActuatorOutputs.msg + ActuatorServos.msg + ActuatorServosTrim.msg + ActuatorTest.msg + AdcReport.msg + Airspeed.msg + AirspeedValidated.msg + AirspeedWind.msg + AutotuneAttitudeControlStatus.msg + BatteryStatus.msg + CameraCapture.msg + CameraStatus.msg + CameraTrigger.msg + CellularStatus.msg + CollisionConstraints.msg + CollisionReport.msg + CommanderState.msg + ControlAllocatorStatus.msg + Cpuload.msg + DebugArray.msg + DebugKeyValue.msg + DebugValue.msg + DebugVect.msg + DifferentialPressure.msg + DistanceSensor.msg + Ekf2Timestamps.msg + EscReport.msg + EscStatus.msg + EstimatorBaroBias.msg + EstimatorEventFlags.msg + EstimatorGpsStatus.msg + EstimatorInnovations.msg + EstimatorOpticalFlowVel.msg + EstimatorSelectorStatus.msg + EstimatorSensorBias.msg + EstimatorStates.msg + EstimatorStatus.msg + EstimatorStatusFlags.msg + Event.msg + FailureDetectorStatus.msg + FollowTarget.msg + GeneratorStatus.msg + GeofenceResult.msg + GimbalDeviceAttitudeStatus.msg + GimbalDeviceInformation.msg + GimbalDeviceSetAttitude.msg + GimbalManagerInformation.msg + GimbalManagerSetAttitude.msg + GimbalManagerSetManualControl.msg + GimbalManagerStatus.msg + GpsDump.msg + GpsInjectData.msg + HeaterStatus.msg + HomePosition.msg + HoverThrustEstimate.msg + InputRc.msg + InternalCombustionEngineStatus.msg + IridiumsbdStatus.msg + IrlockReport.msg + LandingGear.msg + LandingTargetInnovations.msg + LandingTargetPose.msg + LedControl.msg + LoggerStatus.msg + LogMessage.msg + MagnetometerBiasEstimate.msg + MagWorkerData.msg + ManualControlSetpoint.msg + ManualControlSwitches.msg + MavlinkLog.msg + MavlinkTunnel.msg + Mission.msg + MissionResult.msg + MountOrientation.msg + NavigatorMissionItem.msg + NpfgStatus.msg + ObstacleDistance.msg + OffboardControlMode.msg + OnboardComputerStatus.msg + OpticalFlow.msg + OrbitStatus.msg + OrbTest.msg + OrbTestLarge.msg + OrbTestMedium.msg + ParameterUpdate.msg + Ping.msg + PositionControllerLandingStatus.msg + PositionControllerStatus.msg + PositionSetpoint.msg + PositionSetpointTriplet.msg + PowerButtonState.msg + PowerMonitor.msg + PpsCapture.msg + PwmInput.msg + Px4ioStatus.msg + RadioStatus.msg + RateCtrlStatus.msg + RcChannels.msg + RcParameterMap.msg + Rpm.msg + RtlTimeEstimate.msg + Safety.msg + SatelliteInfo.msg + SensorAccel.msg + SensorAccelFifo.msg + SensorBaro.msg + SensorCombined.msg + SensorCorrection.msg + SensorGps.msg + SensorGyro.msg + SensorGyroFft.msg + SensorGyroFifo.msg + SensorHygrometer.msg + SensorMag.msg + SensorPreflightMag.msg + SensorSelection.msg + SensorsStatusImu.msg + SystemPower.msg + TakeoffStatus.msg + TaskStackInfo.msg + TecsStatus.msg + TelemetryStatus.msg + TestMotor.msg + Timesync.msg + TimesyncStatus.msg + TrajectoryBezier.msg + TrajectoryWaypoint.msg + TransponderReport.msg + TuneControl.msg + UavcanParameterRequest.msg + UavcanParameterValue.msg + UlogStream.msg + UlogStreamAck.msg + VehicleAcceleration.msg + VehicleAirData.msg + VehicleAngularAcceleration.msg + VehicleAngularAccelerationSetpoint.msg + VehicleAngularVelocity.msg + VehicleAttitude.msg + VehicleAttitudeSetpoint.msg + VehicleCommand.msg + VehicleCommandAck.msg + VehicleConstraints.msg + VehicleControlMode.msg + VehicleGlobalPosition.msg + VehicleGpsPosition.msg + VehicleImu.msg + VehicleImuStatus.msg + VehicleLandDetected.msg + VehicleLocalPosition.msg + VehicleLocalPositionSetpoint.msg + VehicleMagnetometer.msg + VehicleOdometry.msg + VehicleRatesSetpoint.msg + VehicleRoi.msg + VehicleStatus.msg + VehicleStatusFlags.msg + VehicleThrustSetpoint.msg + VehicleTorqueSetpoint.msg + VehicleTrajectoryBezier.msg + VehicleTrajectoryWaypoint.msg + VtolVehicleStatus.msg + WheelEncoders.msg + Wind.msg + YawEstimatorStatus.msg ) - -if(NOT px4_constrained_flash_build) - list(APPEND msg_files - debug_array.msg - debug_key_value.msg - debug_value.msg - debug_vect.msg - ) -endif() - -if(PX4_TESTING) - list(APPEND msg_files - orb_test.msg - orb_test_large.msg - orb_test_medium.msg - ) -endif() - list(SORT msg_files) -set(deprecated_msgs - ekf2_innovations.msg # 2019-11-22, Updated estimator interface and logging; replaced by 'estimator_innovations'. - ) - -foreach(msg IN LISTS deprecated_msgs) - if(msg IN_LIST msg_files) - get_filename_component(msg_we ${msg} NAME_WE) - list(APPEND invalid_msgs ${msg_we}) - endif() -endforeach() -if(invalid_msgs) - list(LENGTH invalid_msgs invalid_msgs_size) - if(${invalid_msgs_size} GREATER 1) - foreach(msg IN LISTS invalid_msgs) - string(CONCAT invalid_msgs_cs ${invalid_msgs_cs} "'${msg}', ") - endforeach() - STRING(REGEX REPLACE ", +$" "" invalid_msgs_cs ${invalid_msgs_cs}) - message(FATAL_ERROR "${invalid_msgs_cs} are listed as deprecated. Please use different names for the messages.") - else() - message(FATAL_ERROR "'${invalid_msgs}' is listed as deprecated. Please use a different name for the message.") - endif() -endif() - px4_list_make_absolute(msg_files ${CMAKE_CURRENT_SOURCE_DIR} ${msg_files}) if(NOT EXTERNAL_MODULES_LOCATION STREQUAL "") @@ -258,40 +224,40 @@ if(NOT EXTERNAL_MODULES_LOCATION STREQUAL "") endif() endif() +# set parent scope msg_files for other modules to consume (eg topic_listener) +set(msg_files ${msg_files} PARENT_SCOPE) + # headers set(msg_out_path ${PX4_BINARY_DIR}/uORB/topics) set(msg_source_out_path ${CMAKE_CURRENT_BINARY_DIR}/topics_sources) -set(uorb_headers ${msg_out_path}/uORBTopics.hpp) -set(uorb_sources ${msg_source_out_path}/uORBTopics.cpp) +set(uorb_headers) foreach(msg_file ${msg_files}) get_filename_component(msg ${msg_file} NAME_WE) + + # Pascal case to snake case (MsgFile -> msg_file) + string(REGEX REPLACE "(.)([A-Z][a-z]+)" "\\1_\\2" msg "${msg}") + string(REGEX REPLACE "([a-z0-9])([A-Z])" "\\1_\\2" msg "${msg}") + string(TOLOWER "${msg}" msg) + list(APPEND uorb_headers ${msg_out_path}/${msg}.h) - list(APPEND uorb_sources ${msg_source_out_path}/${msg}.cpp) endforeach() -if (px4_constrained_flash_build) +if(px4_constrained_flash_build) set(added_arguments --constrained-flash) endif() -# set parent scope msg_files for other modules to consume (eg topic_listener) -set(msg_files ${msg_files} PARENT_SCOPE) - # Generate uORB headers add_custom_command(OUTPUT ${uorb_headers} COMMAND ${PYTHON_EXECUTABLE} tools/px_generate_uorb_topic_files.py - --headers -f ${msg_files} -i ${CMAKE_CURRENT_SOURCE_DIR} -o ${msg_out_path} -e templates/uorb - -t ${CMAKE_CURRENT_BINARY_DIR}/tmp/headers - -q ${added_arguments} DEPENDS ${msg_files} templates/uorb/msg.h.em - templates/uorb/uORBTopics.hpp.em tools/px_generate_uorb_topic_files.py tools/px_generate_uorb_topic_helper.py COMMENT "Generating uORB topic headers" @@ -299,28 +265,3 @@ add_custom_command(OUTPUT ${uorb_headers} VERBATIM ) add_custom_target(uorb_headers DEPENDS ${uorb_headers}) - -# Generate uORB sources -add_custom_command(OUTPUT ${uorb_sources} - COMMAND ${PYTHON_EXECUTABLE} tools/px_generate_uorb_topic_files.py - --sources - -f ${msg_files} - -i ${CMAKE_CURRENT_SOURCE_DIR} - -o ${msg_source_out_path} - -e templates/uorb - -t ${CMAKE_CURRENT_BINARY_DIR}/tmp/sources - -q - ${added_arguments} - DEPENDS - ${msg_files} - templates/uorb/msg.cpp.em - templates/uorb/uORBTopics.cpp.em - tools/px_generate_uorb_topic_files.py - tools/px_generate_uorb_topic_helper.py - COMMENT "Generating uORB topic sources" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM - ) - -add_library(uorb_msgs ${uorb_sources}) -add_dependencies(uorb_msgs prebuild_targets uorb_headers) diff --git a/msg/camera_capture.msg b/msg/CameraCapture.msg similarity index 100% rename from msg/camera_capture.msg rename to msg/CameraCapture.msg diff --git a/msg/camera_status.msg b/msg/CameraStatus.msg similarity index 100% rename from msg/camera_status.msg rename to msg/CameraStatus.msg diff --git a/msg/camera_trigger.msg b/msg/CameraTrigger.msg similarity index 89% rename from msg/camera_trigger.msg rename to msg/CameraTrigger.msg index 0da1dd109d..abfdac6dda 100644 --- a/msg/camera_trigger.msg +++ b/msg/CameraTrigger.msg @@ -5,5 +5,3 @@ uint32 seq # Image sequence number bool feedback # Trigger feedback from camera uint32 ORB_QUEUE_LENGTH = 2 - -# TOPICS camera_trigger \ No newline at end of file diff --git a/msg/cellular_status.msg b/msg/CellularStatus.msg similarity index 100% rename from msg/cellular_status.msg rename to msg/CellularStatus.msg diff --git a/msg/collision_constraints.msg b/msg/CollisionConstraints.msg similarity index 100% rename from msg/collision_constraints.msg rename to msg/CollisionConstraints.msg diff --git a/msg/collision_report.msg b/msg/CollisionReport.msg similarity index 100% rename from msg/collision_report.msg rename to msg/CollisionReport.msg diff --git a/msg/commander_state.msg b/msg/CommanderState.msg similarity index 100% rename from msg/commander_state.msg rename to msg/CommanderState.msg diff --git a/msg/control_allocator_status.msg b/msg/ControlAllocatorStatus.msg similarity index 100% rename from msg/control_allocator_status.msg rename to msg/ControlAllocatorStatus.msg diff --git a/msg/cpuload.msg b/msg/Cpuload.msg similarity index 100% rename from msg/cpuload.msg rename to msg/Cpuload.msg diff --git a/msg/debug_array.msg b/msg/DebugArray.msg similarity index 100% rename from msg/debug_array.msg rename to msg/DebugArray.msg diff --git a/msg/debug_key_value.msg b/msg/DebugKeyValue.msg similarity index 100% rename from msg/debug_key_value.msg rename to msg/DebugKeyValue.msg diff --git a/msg/debug_value.msg b/msg/DebugValue.msg similarity index 100% rename from msg/debug_value.msg rename to msg/DebugValue.msg diff --git a/msg/debug_vect.msg b/msg/DebugVect.msg similarity index 100% rename from msg/debug_vect.msg rename to msg/DebugVect.msg diff --git a/msg/differential_pressure.msg b/msg/DifferentialPressure.msg similarity index 100% rename from msg/differential_pressure.msg rename to msg/DifferentialPressure.msg diff --git a/msg/distance_sensor.msg b/msg/DistanceSensor.msg similarity index 100% rename from msg/distance_sensor.msg rename to msg/DistanceSensor.msg diff --git a/msg/ekf2_timestamps.msg b/msg/Ekf2Timestamps.msg similarity index 100% rename from msg/ekf2_timestamps.msg rename to msg/Ekf2Timestamps.msg diff --git a/msg/esc_report.msg b/msg/EscReport.msg similarity index 100% rename from msg/esc_report.msg rename to msg/EscReport.msg diff --git a/msg/esc_status.msg b/msg/EscStatus.msg similarity index 98% rename from msg/esc_status.msg rename to msg/EscStatus.msg index f786f6d351..491e8c35de 100644 --- a/msg/esc_status.msg +++ b/msg/EscStatus.msg @@ -25,4 +25,4 @@ uint8 esc_online_flags # Bitmask indicating which ESC is online/offline uint8 esc_armed_flags # Bitmask indicating which ESC is armed. For ESC's where the arming state is not known (returned by the ESC), the arming bits should always be set. -esc_report[8] esc +px4/EscReport[8] esc diff --git a/msg/estimator_baro_bias.msg b/msg/EstimatorBaroBias.msg similarity index 100% rename from msg/estimator_baro_bias.msg rename to msg/EstimatorBaroBias.msg diff --git a/msg/estimator_event_flags.msg b/msg/EstimatorEventFlags.msg similarity index 100% rename from msg/estimator_event_flags.msg rename to msg/EstimatorEventFlags.msg diff --git a/msg/estimator_gps_status.msg b/msg/EstimatorGpsStatus.msg similarity index 100% rename from msg/estimator_gps_status.msg rename to msg/EstimatorGpsStatus.msg diff --git a/msg/estimator_innovations.msg b/msg/EstimatorInnovations.msg similarity index 95% rename from msg/estimator_innovations.msg rename to msg/EstimatorInnovations.msg index cb3e85a1c4..82c4d1a1aa 100644 --- a/msg/estimator_innovations.msg +++ b/msg/EstimatorInnovations.msg @@ -34,5 +34,3 @@ float32 hagl # height of ground innovation (m) and innovation variance (m**2) # The innovation test ratios are scalar values. In case the field is a vector, # the test ratio will be put in the first component of the vector. - -# TOPICS estimator_innovations estimator_innovation_variances estimator_innovation_test_ratios diff --git a/msg/estimator_optical_flow_vel.msg b/msg/EstimatorOpticalFlowVel.msg similarity index 100% rename from msg/estimator_optical_flow_vel.msg rename to msg/EstimatorOpticalFlowVel.msg diff --git a/msg/estimator_selector_status.msg b/msg/EstimatorSelectorStatus.msg similarity index 100% rename from msg/estimator_selector_status.msg rename to msg/EstimatorSelectorStatus.msg diff --git a/msg/estimator_sensor_bias.msg b/msg/EstimatorSensorBias.msg similarity index 100% rename from msg/estimator_sensor_bias.msg rename to msg/EstimatorSensorBias.msg diff --git a/msg/estimator_states.msg b/msg/EstimatorStates.msg similarity index 100% rename from msg/estimator_states.msg rename to msg/EstimatorStates.msg diff --git a/msg/estimator_status.msg b/msg/EstimatorStatus.msg similarity index 100% rename from msg/estimator_status.msg rename to msg/EstimatorStatus.msg diff --git a/msg/estimator_status_flags.msg b/msg/EstimatorStatusFlags.msg similarity index 100% rename from msg/estimator_status_flags.msg rename to msg/EstimatorStatusFlags.msg diff --git a/msg/event.msg b/msg/Event.msg similarity index 100% rename from msg/event.msg rename to msg/Event.msg diff --git a/msg/failure_detector_status.msg b/msg/FailureDetectorStatus.msg similarity index 100% rename from msg/failure_detector_status.msg rename to msg/FailureDetectorStatus.msg diff --git a/msg/follow_target.msg b/msg/FollowTarget.msg similarity index 75% rename from msg/follow_target.msg rename to msg/FollowTarget.msg index f0afe3f4b8..5999f87453 100644 --- a/msg/follow_target.msg +++ b/msg/FollowTarget.msg @@ -1,8 +1,8 @@ uint64 timestamp # time since system start (microseconds) float64 lat # target position (deg * 1e7) -float64 lon # target position (deg * 1e7) +float64 lon # target position (deg * 1e7) float32 alt # target position float32 vy # target vel in y float32 vx # target vel in x -float32 vz # target vel in z +float32 vz # target vel in z uint8 est_cap # target reporting capabilities diff --git a/msg/generator_status.msg b/msg/GeneratorStatus.msg similarity index 100% rename from msg/generator_status.msg rename to msg/GeneratorStatus.msg diff --git a/msg/geofence_result.msg b/msg/GeofenceResult.msg similarity index 100% rename from msg/geofence_result.msg rename to msg/GeofenceResult.msg diff --git a/msg/gimbal_device_attitude_status.msg b/msg/GimbalDeviceAttitudeStatus.msg similarity index 100% rename from msg/gimbal_device_attitude_status.msg rename to msg/GimbalDeviceAttitudeStatus.msg diff --git a/msg/gimbal_device_information.msg b/msg/GimbalDeviceInformation.msg similarity index 100% rename from msg/gimbal_device_information.msg rename to msg/GimbalDeviceInformation.msg diff --git a/msg/gimbal_device_set_attitude.msg b/msg/GimbalDeviceSetAttitude.msg similarity index 100% rename from msg/gimbal_device_set_attitude.msg rename to msg/GimbalDeviceSetAttitude.msg diff --git a/msg/gimbal_manager_information.msg b/msg/GimbalManagerInformation.msg similarity index 100% rename from msg/gimbal_manager_information.msg rename to msg/GimbalManagerInformation.msg diff --git a/msg/gimbal_manager_set_attitude.msg b/msg/GimbalManagerSetAttitude.msg similarity index 100% rename from msg/gimbal_manager_set_attitude.msg rename to msg/GimbalManagerSetAttitude.msg diff --git a/msg/gimbal_manager_set_manual_control.msg b/msg/GimbalManagerSetManualControl.msg similarity index 100% rename from msg/gimbal_manager_set_manual_control.msg rename to msg/GimbalManagerSetManualControl.msg diff --git a/msg/gimbal_manager_status.msg b/msg/GimbalManagerStatus.msg similarity index 100% rename from msg/gimbal_manager_status.msg rename to msg/GimbalManagerStatus.msg diff --git a/msg/gps_dump.msg b/msg/GpsDump.msg similarity index 100% rename from msg/gps_dump.msg rename to msg/GpsDump.msg diff --git a/msg/gps_inject_data.msg b/msg/GpsInjectData.msg similarity index 100% rename from msg/gps_inject_data.msg rename to msg/GpsInjectData.msg diff --git a/msg/heater_status.msg b/msg/HeaterStatus.msg similarity index 100% rename from msg/heater_status.msg rename to msg/HeaterStatus.msg diff --git a/msg/home_position.msg b/msg/HomePosition.msg similarity index 100% rename from msg/home_position.msg rename to msg/HomePosition.msg diff --git a/msg/hover_thrust_estimate.msg b/msg/HoverThrustEstimate.msg similarity index 100% rename from msg/hover_thrust_estimate.msg rename to msg/HoverThrustEstimate.msg diff --git a/msg/input_rc.msg b/msg/InputRc.msg similarity index 100% rename from msg/input_rc.msg rename to msg/InputRc.msg diff --git a/msg/internal_combustion_engine_status.msg b/msg/InternalCombustionEngineStatus.msg similarity index 100% rename from msg/internal_combustion_engine_status.msg rename to msg/InternalCombustionEngineStatus.msg diff --git a/msg/iridiumsbd_status.msg b/msg/IridiumsbdStatus.msg similarity index 100% rename from msg/iridiumsbd_status.msg rename to msg/IridiumsbdStatus.msg diff --git a/msg/irlock_report.msg b/msg/IrlockReport.msg similarity index 100% rename from msg/irlock_report.msg rename to msg/IrlockReport.msg diff --git a/msg/landing_gear.msg b/msg/LandingGear.msg similarity index 100% rename from msg/landing_gear.msg rename to msg/LandingGear.msg diff --git a/msg/landing_target_innovations.msg b/msg/LandingTargetInnovations.msg similarity index 100% rename from msg/landing_target_innovations.msg rename to msg/LandingTargetInnovations.msg diff --git a/msg/landing_target_pose.msg b/msg/LandingTargetPose.msg similarity index 100% rename from msg/landing_target_pose.msg rename to msg/LandingTargetPose.msg diff --git a/msg/led_control.msg b/msg/LedControl.msg similarity index 100% rename from msg/led_control.msg rename to msg/LedControl.msg diff --git a/msg/log_message.msg b/msg/LogMessage.msg similarity index 100% rename from msg/log_message.msg rename to msg/LogMessage.msg diff --git a/msg/logger_status.msg b/msg/LoggerStatus.msg similarity index 100% rename from msg/logger_status.msg rename to msg/LoggerStatus.msg diff --git a/msg/mag_worker_data.msg b/msg/MagWorkerData.msg similarity index 100% rename from msg/mag_worker_data.msg rename to msg/MagWorkerData.msg diff --git a/msg/magnetometer_bias_estimate.msg b/msg/MagnetometerBiasEstimate.msg similarity index 100% rename from msg/magnetometer_bias_estimate.msg rename to msg/MagnetometerBiasEstimate.msg diff --git a/msg/manual_control_setpoint.msg b/msg/ManualControlSetpoint.msg similarity index 97% rename from msg/manual_control_setpoint.msg rename to msg/ManualControlSetpoint.msg index bbbdd0bc66..68232b087c 100644 --- a/msg/manual_control_setpoint.msg +++ b/msg/ManualControlSetpoint.msg @@ -52,5 +52,3 @@ float32 aux5 float32 aux6 bool sticks_moving - -# TOPICS manual_control_setpoint manual_control_input diff --git a/msg/manual_control_switches.msg b/msg/ManualControlSwitches.msg similarity index 100% rename from msg/manual_control_switches.msg rename to msg/ManualControlSwitches.msg diff --git a/msg/mavlink_log.msg b/msg/MavlinkLog.msg similarity index 100% rename from msg/mavlink_log.msg rename to msg/MavlinkLog.msg diff --git a/msg/mavlink_tunnel.msg b/msg/MavlinkTunnel.msg similarity index 100% rename from msg/mavlink_tunnel.msg rename to msg/MavlinkTunnel.msg diff --git a/msg/mission.msg b/msg/Mission.msg similarity index 100% rename from msg/mission.msg rename to msg/Mission.msg diff --git a/msg/mission_result.msg b/msg/MissionResult.msg similarity index 100% rename from msg/mission_result.msg rename to msg/MissionResult.msg diff --git a/msg/mount_orientation.msg b/msg/MountOrientation.msg similarity index 100% rename from msg/mount_orientation.msg rename to msg/MountOrientation.msg diff --git a/msg/navigator_mission_item.msg b/msg/NavigatorMissionItem.msg similarity index 100% rename from msg/navigator_mission_item.msg rename to msg/NavigatorMissionItem.msg diff --git a/msg/npfg_status.msg b/msg/NpfgStatus.msg similarity index 100% rename from msg/npfg_status.msg rename to msg/NpfgStatus.msg diff --git a/msg/obstacle_distance.msg b/msg/ObstacleDistance.msg similarity index 96% rename from msg/obstacle_distance.msg rename to msg/ObstacleDistance.msg index e3c4963ab2..2bd8800d44 100644 --- a/msg/obstacle_distance.msg +++ b/msg/ObstacleDistance.msg @@ -20,5 +20,3 @@ uint16 min_distance # Minimum distance the sensor can measure in centimeters. uint16 max_distance # Maximum distance the sensor can measure in centimeters. float32 angle_offset # Relative angle offset of the 0-index element in the distances array. Value of 0 corresponds to forward. Positive values are offsets to the right. - -# TOPICS obstacle_distance obstacle_distance_fused diff --git a/msg/offboard_control_mode.msg b/msg/OffboardControlMode.msg similarity index 100% rename from msg/offboard_control_mode.msg rename to msg/OffboardControlMode.msg diff --git a/msg/onboard_computer_status.msg b/msg/OnboardComputerStatus.msg similarity index 100% rename from msg/onboard_computer_status.msg rename to msg/OnboardComputerStatus.msg diff --git a/msg/optical_flow.msg b/msg/OpticalFlow.msg similarity index 100% rename from msg/optical_flow.msg rename to msg/OpticalFlow.msg diff --git a/msg/OrbTest.msg b/msg/OrbTest.msg new file mode 100644 index 0000000000..77fcca6e5b --- /dev/null +++ b/msg/OrbTest.msg @@ -0,0 +1,3 @@ +uint64 timestamp # time since system start (microseconds) + +int32 val diff --git a/msg/orb_test_large.msg b/msg/OrbTestLarge.msg similarity index 100% rename from msg/orb_test_large.msg rename to msg/OrbTestLarge.msg diff --git a/msg/orb_test.msg b/msg/OrbTestMedium.msg similarity index 68% rename from msg/orb_test.msg rename to msg/OrbTestMedium.msg index bbbca412fd..fe6f989313 100644 --- a/msg/orb_test.msg +++ b/msg/OrbTestMedium.msg @@ -2,4 +2,4 @@ uint64 timestamp # time since system start (microseconds) int32 val -# TOPICS orb_test orb_multitest +uint8[64] junk diff --git a/msg/orbit_status.msg b/msg/OrbitStatus.msg similarity index 100% rename from msg/orbit_status.msg rename to msg/OrbitStatus.msg diff --git a/msg/parameter_update.msg b/msg/ParameterUpdate.msg similarity index 100% rename from msg/parameter_update.msg rename to msg/ParameterUpdate.msg diff --git a/msg/ping.msg b/msg/Ping.msg similarity index 100% rename from msg/ping.msg rename to msg/Ping.msg diff --git a/msg/position_controller_landing_status.msg b/msg/PositionControllerLandingStatus.msg similarity index 100% rename from msg/position_controller_landing_status.msg rename to msg/PositionControllerLandingStatus.msg diff --git a/msg/position_controller_status.msg b/msg/PositionControllerStatus.msg similarity index 100% rename from msg/position_controller_status.msg rename to msg/PositionControllerStatus.msg diff --git a/msg/position_setpoint.msg b/msg/PositionSetpoint.msg similarity index 100% rename from msg/position_setpoint.msg rename to msg/PositionSetpoint.msg diff --git a/msg/position_setpoint_triplet.msg b/msg/PositionSetpointTriplet.msg similarity index 67% rename from msg/position_setpoint_triplet.msg rename to msg/PositionSetpointTriplet.msg index 7c9a2ca78b..e76bc3e9db 100644 --- a/msg/position_setpoint_triplet.msg +++ b/msg/PositionSetpointTriplet.msg @@ -3,6 +3,6 @@ uint64 timestamp # time since system start (microseconds) -px4/position_setpoint previous -px4/position_setpoint current -px4/position_setpoint next +px4/PositionSetpoint previous +px4/PositionSetpoint current +px4/PositionSetpoint next diff --git a/msg/power_button_state.msg b/msg/PowerButtonState.msg similarity index 100% rename from msg/power_button_state.msg rename to msg/PowerButtonState.msg diff --git a/msg/power_monitor.msg b/msg/PowerMonitor.msg similarity index 100% rename from msg/power_monitor.msg rename to msg/PowerMonitor.msg diff --git a/msg/pps_capture.msg b/msg/PpsCapture.msg similarity index 100% rename from msg/pps_capture.msg rename to msg/PpsCapture.msg diff --git a/msg/pwm_input.msg b/msg/PwmInput.msg similarity index 100% rename from msg/pwm_input.msg rename to msg/PwmInput.msg diff --git a/msg/px4io_status.msg b/msg/Px4ioStatus.msg similarity index 100% rename from msg/px4io_status.msg rename to msg/Px4ioStatus.msg diff --git a/msg/radio_status.msg b/msg/RadioStatus.msg similarity index 100% rename from msg/radio_status.msg rename to msg/RadioStatus.msg diff --git a/msg/rate_ctrl_status.msg b/msg/RateCtrlStatus.msg similarity index 100% rename from msg/rate_ctrl_status.msg rename to msg/RateCtrlStatus.msg diff --git a/msg/rc_channels.msg b/msg/RcChannels.msg similarity index 100% rename from msg/rc_channels.msg rename to msg/RcChannels.msg diff --git a/msg/rc_parameter_map.msg b/msg/RcParameterMap.msg similarity index 100% rename from msg/rc_parameter_map.msg rename to msg/RcParameterMap.msg diff --git a/msg/rpm.msg b/msg/Rpm.msg similarity index 100% rename from msg/rpm.msg rename to msg/Rpm.msg diff --git a/msg/rtl_time_estimate.msg b/msg/RtlTimeEstimate.msg similarity index 100% rename from msg/rtl_time_estimate.msg rename to msg/RtlTimeEstimate.msg diff --git a/msg/safety.msg b/msg/Safety.msg similarity index 100% rename from msg/safety.msg rename to msg/Safety.msg diff --git a/msg/satellite_info.msg b/msg/SatelliteInfo.msg similarity index 100% rename from msg/satellite_info.msg rename to msg/SatelliteInfo.msg diff --git a/msg/sensor_accel.msg b/msg/SensorAccel.msg similarity index 100% rename from msg/sensor_accel.msg rename to msg/SensorAccel.msg diff --git a/msg/sensor_accel_fifo.msg b/msg/SensorAccelFifo.msg similarity index 100% rename from msg/sensor_accel_fifo.msg rename to msg/SensorAccelFifo.msg diff --git a/msg/sensor_baro.msg b/msg/SensorBaro.msg similarity index 100% rename from msg/sensor_baro.msg rename to msg/SensorBaro.msg diff --git a/msg/sensor_combined.msg b/msg/SensorCombined.msg similarity index 100% rename from msg/sensor_combined.msg rename to msg/SensorCombined.msg diff --git a/msg/sensor_correction.msg b/msg/SensorCorrection.msg similarity index 100% rename from msg/sensor_correction.msg rename to msg/SensorCorrection.msg diff --git a/msg/sensor_gps.msg b/msg/SensorGps.msg similarity index 100% rename from msg/sensor_gps.msg rename to msg/SensorGps.msg diff --git a/msg/sensor_gyro.msg b/msg/SensorGyro.msg similarity index 100% rename from msg/sensor_gyro.msg rename to msg/SensorGyro.msg diff --git a/msg/sensor_gyro_fft.msg b/msg/SensorGyroFft.msg similarity index 100% rename from msg/sensor_gyro_fft.msg rename to msg/SensorGyroFft.msg diff --git a/msg/sensor_gyro_fifo.msg b/msg/SensorGyroFifo.msg similarity index 100% rename from msg/sensor_gyro_fifo.msg rename to msg/SensorGyroFifo.msg diff --git a/msg/sensor_hygrometer.msg b/msg/SensorHygrometer.msg old mode 100755 new mode 100644 similarity index 100% rename from msg/sensor_hygrometer.msg rename to msg/SensorHygrometer.msg diff --git a/msg/sensor_mag.msg b/msg/SensorMag.msg similarity index 100% rename from msg/sensor_mag.msg rename to msg/SensorMag.msg diff --git a/msg/sensor_preflight_mag.msg b/msg/SensorPreflightMag.msg similarity index 100% rename from msg/sensor_preflight_mag.msg rename to msg/SensorPreflightMag.msg diff --git a/msg/sensor_selection.msg b/msg/SensorSelection.msg similarity index 100% rename from msg/sensor_selection.msg rename to msg/SensorSelection.msg diff --git a/msg/sensors_status_imu.msg b/msg/SensorsStatusImu.msg similarity index 100% rename from msg/sensors_status_imu.msg rename to msg/SensorsStatusImu.msg diff --git a/msg/system_power.msg b/msg/SystemPower.msg similarity index 100% rename from msg/system_power.msg rename to msg/SystemPower.msg diff --git a/msg/takeoff_status.msg b/msg/TakeoffStatus.msg similarity index 100% rename from msg/takeoff_status.msg rename to msg/TakeoffStatus.msg diff --git a/msg/task_stack_info.msg b/msg/TaskStackInfo.msg similarity index 100% rename from msg/task_stack_info.msg rename to msg/TaskStackInfo.msg diff --git a/msg/tecs_status.msg b/msg/TecsStatus.msg similarity index 100% rename from msg/tecs_status.msg rename to msg/TecsStatus.msg diff --git a/msg/telemetry_status.msg b/msg/TelemetryStatus.msg similarity index 100% rename from msg/telemetry_status.msg rename to msg/TelemetryStatus.msg diff --git a/msg/test_motor.msg b/msg/TestMotor.msg similarity index 100% rename from msg/test_motor.msg rename to msg/TestMotor.msg diff --git a/msg/timesync.msg b/msg/Timesync.msg similarity index 100% rename from msg/timesync.msg rename to msg/Timesync.msg diff --git a/msg/timesync_status.msg b/msg/TimesyncStatus.msg similarity index 100% rename from msg/timesync_status.msg rename to msg/TimesyncStatus.msg diff --git a/msg/trajectory_bezier.msg b/msg/TrajectoryBezier.msg similarity index 100% rename from msg/trajectory_bezier.msg rename to msg/TrajectoryBezier.msg diff --git a/msg/trajectory_waypoint.msg b/msg/TrajectoryWaypoint.msg similarity index 100% rename from msg/trajectory_waypoint.msg rename to msg/TrajectoryWaypoint.msg diff --git a/msg/transponder_report.msg b/msg/TransponderReport.msg similarity index 100% rename from msg/transponder_report.msg rename to msg/TransponderReport.msg diff --git a/msg/tune_control.msg b/msg/TuneControl.msg similarity index 100% rename from msg/tune_control.msg rename to msg/TuneControl.msg diff --git a/msg/uavcan_parameter_request.msg b/msg/UavcanParameterRequest.msg similarity index 100% rename from msg/uavcan_parameter_request.msg rename to msg/UavcanParameterRequest.msg diff --git a/msg/uavcan_parameter_value.msg b/msg/UavcanParameterValue.msg similarity index 100% rename from msg/uavcan_parameter_value.msg rename to msg/UavcanParameterValue.msg diff --git a/msg/ulog_stream.msg b/msg/UlogStream.msg similarity index 100% rename from msg/ulog_stream.msg rename to msg/UlogStream.msg diff --git a/msg/ulog_stream_ack.msg b/msg/UlogStreamAck.msg similarity index 100% rename from msg/ulog_stream_ack.msg rename to msg/UlogStreamAck.msg diff --git a/msg/vehicle_acceleration.msg b/msg/VehicleAcceleration.msg similarity index 100% rename from msg/vehicle_acceleration.msg rename to msg/VehicleAcceleration.msg diff --git a/msg/vehicle_air_data.msg b/msg/VehicleAirData.msg similarity index 100% rename from msg/vehicle_air_data.msg rename to msg/VehicleAirData.msg diff --git a/msg/vehicle_angular_acceleration.msg b/msg/VehicleAngularAcceleration.msg similarity index 100% rename from msg/vehicle_angular_acceleration.msg rename to msg/VehicleAngularAcceleration.msg diff --git a/msg/vehicle_angular_acceleration_setpoint.msg b/msg/VehicleAngularAccelerationSetpoint.msg similarity index 100% rename from msg/vehicle_angular_acceleration_setpoint.msg rename to msg/VehicleAngularAccelerationSetpoint.msg diff --git a/msg/vehicle_angular_velocity.msg b/msg/VehicleAngularVelocity.msg similarity index 78% rename from msg/vehicle_angular_velocity.msg rename to msg/VehicleAngularVelocity.msg index a8748772b7..547e124790 100644 --- a/msg/vehicle_angular_velocity.msg +++ b/msg/VehicleAngularVelocity.msg @@ -3,5 +3,3 @@ uint64 timestamp # time since system start (microseconds) uint64 timestamp_sample # timestamp of the data sample on which this message is based (microseconds) float32[3] xyz # Bias corrected angular velocity about the FRD body frame XYZ-axis in rad/s - -# TOPICS vehicle_angular_velocity vehicle_angular_velocity_groundtruth diff --git a/msg/vehicle_attitude.msg b/msg/VehicleAttitude.msg similarity index 80% rename from msg/vehicle_attitude.msg rename to msg/VehicleAttitude.msg index a57b7c5058..e60fb354ae 100644 --- a/msg/vehicle_attitude.msg +++ b/msg/VehicleAttitude.msg @@ -7,6 +7,3 @@ uint64 timestamp_sample # the timestamp of the raw data (microseconds) float32[4] q # Quaternion rotation from the FRD body frame to the NED earth frame float32[4] delta_q_reset # Amount by which quaternion has changed during last reset uint8 quat_reset_counter # Quaternion reset counter - -# TOPICS vehicle_attitude vehicle_attitude_groundtruth vehicle_vision_attitude -# TOPICS estimator_attitude diff --git a/msg/vehicle_attitude_setpoint.msg b/msg/VehicleAttitudeSetpoint.msg similarity index 92% rename from msg/vehicle_attitude_setpoint.msg rename to msg/VehicleAttitudeSetpoint.msg index 065fa12314..db79c9d17a 100644 --- a/msg/vehicle_attitude_setpoint.msg +++ b/msg/VehicleAttitudeSetpoint.msg @@ -23,5 +23,3 @@ uint8 apply_flaps # flap config specifier uint8 FLAPS_OFF = 0 # no flaps uint8 FLAPS_LAND = 1 # landing config flaps uint8 FLAPS_TAKEOFF = 2 # take-off config flaps - -# TOPICS vehicle_attitude_setpoint mc_virtual_attitude_setpoint fw_virtual_attitude_setpoint diff --git a/msg/vehicle_command.msg b/msg/VehicleCommand.msg similarity index 99% rename from msg/vehicle_command.msg rename to msg/VehicleCommand.msg index 9ddaad2a92..1979b9f9db 100644 --- a/msg/vehicle_command.msg +++ b/msg/VehicleCommand.msg @@ -175,5 +175,3 @@ uint8 source_system # System sending the command uint8 source_component # Component sending the command uint8 confirmation # 0: First transmission of this command. 1-255: Confirmation transmissions (e.g. for kill command) bool from_external - -# TOPICS vehicle_command gimbal_v1_command diff --git a/msg/vehicle_command_ack.msg b/msg/VehicleCommandAck.msg similarity index 100% rename from msg/vehicle_command_ack.msg rename to msg/VehicleCommandAck.msg diff --git a/msg/vehicle_constraints.msg b/msg/VehicleConstraints.msg similarity index 100% rename from msg/vehicle_constraints.msg rename to msg/VehicleConstraints.msg diff --git a/msg/vehicle_control_mode.msg b/msg/VehicleControlMode.msg similarity index 100% rename from msg/vehicle_control_mode.msg rename to msg/VehicleControlMode.msg diff --git a/msg/vehicle_global_position.msg b/msg/VehicleGlobalPosition.msg similarity index 92% rename from msg/vehicle_global_position.msg rename to msg/VehicleGlobalPosition.msg index ce16daecc8..49002ae796 100644 --- a/msg/vehicle_global_position.msg +++ b/msg/VehicleGlobalPosition.msg @@ -24,6 +24,3 @@ float32 terrain_alt # Terrain altitude WGS84, (metres) bool terrain_alt_valid # Terrain altitude estimate is valid bool dead_reckoning # True if this position is estimated through dead-reckoning - -# TOPICS vehicle_global_position vehicle_global_position_groundtruth -# TOPICS estimator_global_position diff --git a/msg/vehicle_gps_position.msg b/msg/VehicleGpsPosition.msg similarity index 100% rename from msg/vehicle_gps_position.msg rename to msg/VehicleGpsPosition.msg diff --git a/msg/vehicle_imu.msg b/msg/VehicleImu.msg similarity index 100% rename from msg/vehicle_imu.msg rename to msg/VehicleImu.msg diff --git a/msg/vehicle_imu_status.msg b/msg/VehicleImuStatus.msg similarity index 100% rename from msg/vehicle_imu_status.msg rename to msg/VehicleImuStatus.msg diff --git a/msg/vehicle_land_detected.msg b/msg/VehicleLandDetected.msg similarity index 100% rename from msg/vehicle_land_detected.msg rename to msg/VehicleLandDetected.msg diff --git a/msg/vehicle_local_position.msg b/msg/VehicleLocalPosition.msg similarity index 97% rename from msg/vehicle_local_position.msg rename to msg/VehicleLocalPosition.msg index 981fec4ba8..a0a23ce260 100644 --- a/msg/vehicle_local_position.msg +++ b/msg/VehicleLocalPosition.msg @@ -69,6 +69,3 @@ float32 vxy_max # maximum horizontal speed - set to 0 when limiting not requi float32 vz_max # maximum vertical speed - set to 0 when limiting not required (meters/sec) float32 hagl_min # minimum height above ground level - set to 0 when limiting not required (meters) float32 hagl_max # maximum height above ground level - set to 0 when limiting not required (meters) - -# TOPICS vehicle_local_position vehicle_local_position_groundtruth -# TOPICS estimator_local_position diff --git a/msg/vehicle_local_position_setpoint.msg b/msg/VehicleLocalPositionSetpoint.msg similarity index 89% rename from msg/vehicle_local_position_setpoint.msg rename to msg/VehicleLocalPositionSetpoint.msg index 418cf5052b..1dfe4ec6c9 100644 --- a/msg/vehicle_local_position_setpoint.msg +++ b/msg/VehicleLocalPositionSetpoint.msg @@ -14,5 +14,3 @@ float32 vz # in meters/sec float32[3] acceleration # in meters/sec^2 float32[3] jerk # in meters/sec^3 float32[3] thrust # normalized thrust vector in NED - -# TOPICS vehicle_local_position_setpoint trajectory_setpoint diff --git a/msg/vehicle_magnetometer.msg b/msg/VehicleMagnetometer.msg similarity index 100% rename from msg/vehicle_magnetometer.msg rename to msg/VehicleMagnetometer.msg diff --git a/msg/vehicle_odometry.msg b/msg/VehicleOdometry.msg similarity index 95% rename from msg/vehicle_odometry.msg rename to msg/VehicleOdometry.msg index 5f597f4d96..f3104a6664 100644 --- a/msg/vehicle_odometry.msg +++ b/msg/VehicleOdometry.msg @@ -63,6 +63,3 @@ float32 yawspeed # Angular velocity about Z body axis float32[21] velocity_covariance uint8 reset_counter - -# TOPICS vehicle_odometry vehicle_mocap_odometry vehicle_visual_odometry -# TOPICS estimator_odometry estimator_visual_odometry_aligned diff --git a/msg/vehicle_rates_setpoint.msg b/msg/VehicleRatesSetpoint.msg similarity index 100% rename from msg/vehicle_rates_setpoint.msg rename to msg/VehicleRatesSetpoint.msg diff --git a/msg/vehicle_roi.msg b/msg/VehicleRoi.msg similarity index 100% rename from msg/vehicle_roi.msg rename to msg/VehicleRoi.msg diff --git a/msg/vehicle_status.msg b/msg/VehicleStatus.msg similarity index 100% rename from msg/vehicle_status.msg rename to msg/VehicleStatus.msg diff --git a/msg/vehicle_status_flags.msg b/msg/VehicleStatusFlags.msg similarity index 100% rename from msg/vehicle_status_flags.msg rename to msg/VehicleStatusFlags.msg diff --git a/msg/vehicle_thrust_setpoint.msg b/msg/VehicleThrustSetpoint.msg similarity index 100% rename from msg/vehicle_thrust_setpoint.msg rename to msg/VehicleThrustSetpoint.msg diff --git a/msg/vehicle_torque_setpoint.msg b/msg/VehicleTorqueSetpoint.msg similarity index 100% rename from msg/vehicle_torque_setpoint.msg rename to msg/VehicleTorqueSetpoint.msg diff --git a/msg/vehicle_trajectory_bezier.msg b/msg/VehicleTrajectoryBezier.msg similarity index 86% rename from msg/vehicle_trajectory_bezier.msg rename to msg/VehicleTrajectoryBezier.msg index 421ff1a015..ae6274733e 100644 --- a/msg/vehicle_trajectory_bezier.msg +++ b/msg/VehicleTrajectoryBezier.msg @@ -12,7 +12,5 @@ uint8 POINT_4 = 4 uint8 NUMBER_POINTS = 5 -trajectory_bezier[5] control_points +px4/TrajectoryBezier[5] control_points uint8 bezier_order - -# TOPICS vehicle_trajectory_bezier diff --git a/msg/vehicle_trajectory_waypoint.msg b/msg/VehicleTrajectoryWaypoint.msg similarity index 86% rename from msg/vehicle_trajectory_waypoint.msg rename to msg/VehicleTrajectoryWaypoint.msg index 182f548d23..017f039183 100644 --- a/msg/vehicle_trajectory_waypoint.msg +++ b/msg/VehicleTrajectoryWaypoint.msg @@ -16,6 +16,4 @@ uint8 POINT_4 = 4 uint8 NUMBER_POINTS = 5 -trajectory_waypoint[5] waypoints - -# TOPICS vehicle_trajectory_waypoint vehicle_trajectory_waypoint_desired +px4/TrajectoryWaypoint[5] waypoints diff --git a/msg/vtol_vehicle_status.msg b/msg/VtolVehicleStatus.msg similarity index 100% rename from msg/vtol_vehicle_status.msg rename to msg/VtolVehicleStatus.msg diff --git a/msg/wheel_encoders.msg b/msg/WheelEncoders.msg similarity index 100% rename from msg/wheel_encoders.msg rename to msg/WheelEncoders.msg diff --git a/msg/wind.msg b/msg/Wind.msg similarity index 96% rename from msg/wind.msg rename to msg/Wind.msg index ff8b6f4535..7791159d6e 100644 --- a/msg/wind.msg +++ b/msg/Wind.msg @@ -12,5 +12,3 @@ float32 tas_innov_var # True airspeed innovation variance float32 beta_innov # Sideslip measurement innovation float32 beta_innov_var # Sideslip measurement innovation variance - -# TOPICS wind estimator_wind diff --git a/msg/yaw_estimator_status.msg b/msg/YawEstimatorStatus.msg similarity index 100% rename from msg/yaw_estimator_status.msg rename to msg/YawEstimatorStatus.msg diff --git a/msg/orb_test_medium.msg b/msg/orb_test_medium.msg deleted file mode 100644 index b25ae1c851..0000000000 --- a/msg/orb_test_medium.msg +++ /dev/null @@ -1,7 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -int32 val - -uint8[64] junk - -# TOPICS orb_test_medium orb_test_medium_multi orb_test_medium_wrap_around orb_test_medium_queue orb_test_medium_queue_poll diff --git a/msg/templates/uorb/msg.cpp.em b/msg/templates/uorb/msg.cpp.em deleted file mode 100644 index 03776549f5..0000000000 --- a/msg/templates/uorb/msg.cpp.em +++ /dev/null @@ -1,92 +0,0 @@ -@############################################### -@# -@# PX4 ROS compatible message source code -@# generation for C++ -@# -@# EmPy template for generating .h files -@# Based on the original template for ROS -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - file_name_in (String) Source file -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@# - search_path (dict) search paths for genmsg -@# - topics (List of String) multi-topic names -@# - constrained_flash set to true if flash is constrained -@############################################### -/**************************************************************************** - * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/* Auto-generated by genmsg_cpp from file @file_name_in */ - -@{ -import genmsg.msgs - -from px_generate_uorb_topic_helper import * # this is in Tools/ - -uorb_struct = '%s_s'%spec.short_name -topic_name = spec.short_name - -sorted_fields = sorted(spec.parsed_fields(), key=sizeof_field_type, reverse=True) -struct_size, padding_end_size = add_padding_bytes(sorted_fields, search_path) -topic_fields = ["%s %s" % (convert_type(field.type, True), field.name) for field in sorted_fields] -}@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -@# join all msg files in one line e.g: "float[3] position;float[3] velocity;bool armed" -@# This is used for the logger -constexpr char __orb_@(topic_name)_fields[] = "@( ";".join(topic_fields) );"; - -@[for multi_topic in topics]@ -ORB_DEFINE(@multi_topic, struct @uorb_struct, @(struct_size-padding_end_size), __orb_@(topic_name)_fields, static_cast(ORB_ID::@multi_topic)); -@[end for] - -void print_message(const orb_metadata *meta, const @uorb_struct& message) -{ - if (sizeof(message) != meta->o_size) { - printf("unexpected message size for %s: %zu != %i\n", meta->o_name, sizeof(message), meta->o_size); - return; - } - orb_print_message_internal(meta, &message, true); -} diff --git a/msg/templates/uorb/msg.h.em b/msg/templates/uorb/msg.h.em index 9854e1eaef..4bd9622a50 100644 --- a/msg/templates/uorb/msg.h.em +++ b/msg/templates/uorb/msg.h.em @@ -13,7 +13,7 @@ @# - file_name_in (String) Source file @# - spec (msggen.MsgSpec) Parsed specification of the .msg file @# - search_path (dict) search paths for genmsg -@# - topics (List of String) multi-topic names +@# - topics (List of String) topic names @############################################### /**************************************************************************** * @@ -52,12 +52,17 @@ @{ import genmsg.msgs +import re from px_generate_uorb_topic_helper import * # this is in Tools/ -uorb_struct = '%s_s'%spec.short_name -uorb_struct_upper = spec.short_name.upper() -topic_name = spec.short_name +px4_struct = '%s'%name_snake_case +uorb_struct = '%s_s'%name_snake_case +uorb_struct_upper = name_snake_case.upper() + +sorted_fields = sorted(spec.parsed_fields(), key=sizeof_field_type, reverse=True) +struct_size, padding_end_size = add_padding_bytes(sorted_fields, search_path) +topic_fields = ["%s %s" % (convert_type(field.type, True), field.name) for field in sorted_fields] }@ #pragma once @@ -67,6 +72,7 @@ topic_name = spec.short_name @############################## #include +#include @############################## @# Includes for dependencies @@ -77,9 +83,16 @@ for field in spec.parsed_fields(): if (not field.is_header): (package, name) = genmsg.names.package_resource_name(field.base_type) package = package or spec.package # convert '' to package + + name = re.sub(r'(?'%(name)) }@ + +#ifdef __PX4_ROS2 +namespace px4_embedded { +#endif // __PX4_ROS2 + @# Constants c style #ifndef __cplusplus @[for constant in spec.constants]@ @@ -124,14 +137,70 @@ for constant in spec.constants: print('\tstatic constexpr %s %s = %s;'%(type_px4, constant.name, int(constant.val))) } + + static constexpr char FIELDS[] = "@( ";".join(topic_fields) );"; + static constexpr size_t SIZE_NO_PADDING = @(struct_size-padding_end_size); + #endif }; -/* register this as object request broker structure */ -@[for multi_topic in topics]@ -ORB_DECLARE(@multi_topic); -@[end for] + +#if defined(__PX4_ROS2) +} // namespace px4_embedded + +#include "px4/msg/@(px4_struct).hpp" +using @(uorb_struct) = px4::msg::@(spec.short_name); + +inline px4::msg::@(spec.short_name) px4_embedded_to_ros2(const px4_embedded::@(uorb_struct)& msg_in) +{ + px4::msg::@(spec.short_name) msg_out; +@{ +for field in spec.parsed_fields(): + + #print(field, field.is_builtin, field.base_type, field.is_array, field.array_len) + + if field.is_builtin and not field.is_array: + print(" msg_out.%s = msg_in.%s;" % (field.name, field.name)) + elif field.is_builtin and field.is_array: + print(" std::copy(std::begin(msg_in.%s), std::end(msg_in.%s), msg_out.%s.begin());" % (field.name, field.name, field.name)) + elif not field.is_builtin: + #print(" msg_out.%s = px4_embedded_to_ros2(msg_in.%s);" % (field.name, field.name)) + print(" // %s.%s not printed" % (name_snake_case, field.name)) + else: + print(" #error %s.%s not printed" % (name_snake_case, field.name)) + +}@ + + return msg_out; +} + + +#else + +# ifdef __cplusplus +namespace px4 { + namespace msg { + using @(spec.short_name) = @(uorb_struct); + } // namespace msg +} // namespace px4 + +namespace px4_embedded { + using @(uorb_struct) = @(uorb_struct); +} + +# endif + +#endif // PX4_ROS2 #ifdef __cplusplus -void print_message(const orb_metadata *meta, const @uorb_struct& message); + +inline void print_message(const orb_metadata *meta, const @uorb_struct& message) +{ + if (sizeof(message) != meta->o_size) { + printf("unexpected message size for %s: %zu != %i\n", meta->o_name, sizeof(message), meta->o_size); + return; + } + orb_print_message_internal(meta, &message, true); +} + #endif diff --git a/msg/templates/uorb/uORBTopics.cpp.em b/msg/templates/uorb/uORBTopics.cpp.em index b08950bb19..bca3139eb0 100644 --- a/msg/templates/uorb/uORBTopics.cpp.em +++ b/msg/templates/uorb/uORBTopics.cpp.em @@ -1,18 +1,6 @@ -@############################################### -@# -@# EmPy template for generating uORBTopics.cpp file -@# for logging purposes -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - msgs (List) list of all msg files -@# - multi_topics (List) list of all multi-topic names -@############################################### /**************************************************************************** * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. + * Copyright (C) 2021 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -43,23 +31,23 @@ * ****************************************************************************/ -#include #include -@{ -msg_names = [mn.replace(".msg", "") for mn in msgs] -msgs_count = len(msg_names) -msg_names_all = list(set(msg_names + multi_topics)) # set() filters duplicates -msg_names_all.sort() -msgs_count_all = len(msg_names_all) -}@ -@[for msg_name in msg_names]@ -#include -@[end for] + +#include "uORBTopics.hpp" + +// ORB_DEFINE(action_request, px4::msg::ActionRequest, px4_embedded::action_request_s::SIZE_NO_PADDING, px4_embedded::action_request_s::FIELDS, static_cast(ORB_ID::action_request)); +// ORB_DEFINE(actuator_armed, px4::msg::ActuatorArmed, px4_embedded::actuator_armed_s::SIZE_NO_PADDING, px4_embedded::actuator_armed_s::FIELDS, static_cast(ORB_ID::actuator_armed)); + +//const constexpr struct orb_metadata *const uorb_topics_list[ORB_TOPICS_COUNT] = { +// ORB_ID(action_request), +// ORB_ID(actuator_armed), + + + +@PX4_ORB_DEFINE_STR@ const constexpr struct orb_metadata *const uorb_topics_list[ORB_TOPICS_COUNT] = { -@[for idx, msg_name in enumerate(msg_names_all, 1)]@ - ORB_ID(@(msg_name))@[if idx != msgs_count_all], @[end if] -@[end for] +@PX4_MSG_TOPIC_ORB_ID@ }; const struct orb_metadata *const *orb_get_topics() diff --git a/msg/templates/uorb/uORBTopics.hpp.em b/msg/templates/uorb/uORBTopics.hpp.em index d278f06398..49267105c6 100644 --- a/msg/templates/uorb/uORBTopics.hpp.em +++ b/msg/templates/uorb/uORBTopics.hpp.em @@ -57,14 +57,32 @@ msgs_count_all = len(msg_names_all) #include -static constexpr size_t ORB_TOPICS_COUNT{@(msgs_count_all)}; +// TODO: ros2 friendly include paths +//#include +//#include + + +@PX4_ORB_HEADER_INCLUDE_STR@ + +static constexpr size_t ORB_TOPICS_COUNT{@PX4_ORB_TOPIC_COUNT@}; static constexpr size_t orb_topics_count() { return ORB_TOPICS_COUNT; } + +// ORB_DECLARE(action_request); +// ORB_DECLARE(actuator_armed); + +@PX4_ORB_DECLARE_STR@ + /* * Returns array of topics metadata */ extern const struct orb_metadata *const *orb_get_topics() __EXPORT; + +// enum class ORB_ID : uint8_t { +// action_request, +// actuator_armed, + enum class ORB_ID : uint8_t { @[for idx, msg_name in enumerate(msg_names_all)]@ @(msg_name) = @(idx), @@ -73,3 +91,18 @@ enum class ORB_ID : uint8_t { }; const struct orb_metadata *get_orb_meta(ORB_ID id); + + +static constexpr const char *get_topic_string(ORB_ID orb_id) +{ + switch (orb_id) { + //case ORB_ID::action_request: return "action_request"; + //case ORB_ID::actuator_armed: return "actuator_armed"; + +@[for idx, msg_name in enumerate(msg_names_all)]@ + case @(msg_name): return "@(msg_name)"; +@[end for] + } + + return nullptr; +} diff --git a/msg/templates/uorb_microcdr/microRTPS_client.cpp.em b/msg/templates/uorb_microcdr/microRTPS_client.cpp.em deleted file mode 100644 index 941073b17f..0000000000 --- a/msg/templates/uorb_microcdr/microRTPS_client.cpp.em +++ /dev/null @@ -1,306 +0,0 @@ -@############################################### -@# -@# EmPy template for generating microRTPS_client.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - msgs (List) list of all RTPS messages -@# - multi_topics (List) list of all multi-topic names -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import os - -import genmsg.msgs - -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -topic_names = [s.short_name for s in spec] -send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND] -send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND] -recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE] -receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE] -}@ -/**************************************************************************** - * - * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -@[for topic in list(set(topic_names))]@ -#include -#include -@[end for]@ - -using namespace time_literals; - -@[if recv_topics]@ -// Publishers for received messages -struct RcvTopicsPubs { -@[ for idx, topic in enumerate(recv_topics)]@ - uORB::Publication <@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))}; -@[ end for]@ -}; -@[end if]@ - -@[if send_topics]@ -// Subscribers for messages to send -struct SendTopicsSubs { -@[ for idx, topic in enumerate(send_topics)]@ - uORB::Subscription @(topic)_sub{ORB_ID(@(topic))}; -@[ end for]@ -}; - -struct SendThreadArgs { - const uint32_t &datarate; - uint64_t &total_sent; - uint64_t &sent_last_sec; - uint64_t &sent; - int &sent_loop; - SendThreadArgs(const uint32_t &datarate_, uint64_t &total_sent_, - uint64_t &sent_last_sec_, uint64_t &sent_, int &sent_loop_) - : datarate(datarate_), - total_sent(total_sent_), - sent_last_sec(sent_last_sec_), - sent(sent_), - sent_loop(sent_loop_) {} -}; - -void *send(void *args) -{ - char data_buffer[BUFFER_SIZE]{}; - int read{0}; - uint32_t length{0}; - size_t header_length{0}; - uint8_t last_msg_seq{0}; - uint8_t last_remote_msg_seq{0}; - - struct SendThreadArgs *data = reinterpret_cast(args); - SendTopicsSubs *subs = new SendTopicsSubs(); - - float bandwidth_mult{0}; - float tx_interval{1.f}; - uint64_t tx_last_sec_read{0}; - hrt_abstime last_stats_update{0}; - - // ucdrBuffer to serialize using the user defined buffer - ucdrBuffer writer; - header_length = transport_node->get_header_length(); - ucdr_init_buffer(&writer, reinterpret_cast(&data_buffer[header_length]), BUFFER_SIZE - header_length); - - while (!_should_exit_task) { -@[ for idx, topic in enumerate(send_topics)]@ - { - @(send_base_types[idx])_s @(topic)_data; - - if (subs->@(topic)_sub.update(&@(topic)_data)) - { -@[ if topic == 'Timesync' or topic == 'timesync']@ - if (@(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) { - last_remote_msg_seq = @(topic)_data.seq; - - @(topic)_data.timestamp = hrt_absolute_time(); - @(topic)_data.seq = last_msg_seq; - @(topic)_data.tc1 = hrt_absolute_time() * 1000ULL; - @(topic)_data.ts1 = @(topic)_data.ts1; - - last_msg_seq++; -@[ end if]@ - // copy raw data into local buffer. Payload is shifted by header length to make room for header - serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length); - - if (0 < (read = transport_node->write(static_cast(@(msgs[0].index(topic) + 1)), data_buffer, length))) { - data->total_sent += read; - tx_last_sec_read += read; - ++data->sent; - } - -@[ if topic == 'Timesync' or topic == 'timesync']@ - } - -@[ end if]@ - } - } -@[ end for]@ - - if (hrt_absolute_time() - last_stats_update >= 1_s) { - data->sent_last_sec = tx_last_sec_read; - if (data->datarate > 0) { - bandwidth_mult = static_cast(data->datarate) / static_cast(tx_last_sec_read); - // Apply a low-pass filter to determine the new TX interval - tx_interval += 0.5f * (tx_interval / bandwidth_mult - tx_interval); - // Clamp the interval between 1 and 1000 ms - tx_interval = math::constrain(tx_interval, MIN_TX_INTERVAL_US, MAX_TX_INTERVAL_US); - } - tx_last_sec_read = 0; - last_stats_update = hrt_absolute_time(); - } - - px4_usleep(tx_interval); - - ++data->sent_loop; - } - - delete(data); - delete(subs); - - return nullptr; -} - -static int launch_send_thread(pthread_t &sender_thread, struct SendThreadArgs &args) -{ - pthread_attr_t sender_thread_attr; - pthread_attr_init(&sender_thread_attr); - pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(2250)); - struct sched_param param; - (void)pthread_attr_getschedparam(&sender_thread_attr, ¶m); - param.sched_priority = SCHED_PRIORITY_DEFAULT; - (void)pthread_attr_setschedparam(&sender_thread_attr, ¶m); - int rc = pthread_create(&sender_thread, &sender_thread_attr, &send, (void *)&args); - if (rc != 0) { - errno = rc; - PX4_ERR("Could not create send thread (%d)", errno); - return -1; - } - rc = pthread_setname_np(sender_thread, "urtpsclient_snd"); - if (pthread_setname_np(sender_thread, "urtpsclient_snd")) { - errno = rc; - PX4_ERR("Could not set pthread name for the send thread (%d)", errno); - } - pthread_attr_destroy(&sender_thread_attr); - - return 0; -} -@[end if]@ - -void micrortps_start_topics(const uint32_t &datarate, struct timespec &begin, uint64_t &total_rcvd, - uint64_t &total_sent, uint64_t &sent_last_sec, - uint64_t &rcvd_last_sec, uint64_t &received, uint64_t &sent, int &rcvd_loop, int &sent_loop) -{ - px4_clock_gettime(CLOCK_REALTIME, &begin); - _should_exit_task = false; - -@[if recv_topics]@ - char data_buffer[BUFFER_SIZE]{}; - int read{0}; - uint8_t topic_ID{255}; - - uint64_t rx_last_sec_read{0}; - hrt_abstime last_stats_update{0}; - - RcvTopicsPubs *pubs = new RcvTopicsPubs(); - - // Set the main task name to 'urtpsclient_rcv' in case there is - // data to receive - px4_prctl(PR_SET_NAME, "urtpsclient_rcv", px4_getpid()); - - // ucdrBuffer to deserialize using the user defined buffer - ucdrBuffer reader; - ucdr_init_buffer(&reader, reinterpret_cast(data_buffer), BUFFER_SIZE); -@[end if]@ - -@[if send_topics]@ - // var struct to be updated on the thread - SendThreadArgs *sender_thread_args = new SendThreadArgs(datarate, total_sent, sent_last_sec, sent, sent_loop); - - // create a thread for sending data - pthread_t sender_thread; - launch_send_thread(sender_thread, (*sender_thread_args)); -@[end if]@ - - while (!_should_exit_task) { -@[if recv_topics]@ - while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) { - total_rcvd += read; - rx_last_sec_read += read; - - uint64_t read_time = hrt_absolute_time(); - - switch (topic_ID) { -@[ for idx, topic in enumerate(recv_topics)]@ - case @(msgs[0].index(topic) + 1): { - @(receive_base_types[idx])_s @(topic)_data; - deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer); - - if (@(topic)_data.timestamp > read_time) { - // don't allow timestamps from the future - @(topic)_data.timestamp = read_time; - } - - pubs->@(topic)_pub.publish(@(topic)_data); - ++received; - } - break; -@[ end for]@ - default: - PX4_WARN("Unexpected topic ID '%hhu' to getMsg. Please make sure the client is capable of parsing the message associated to the topic ID '%hhu'", - topic_ID, topic_ID); - break; - } - } -@[end if]@ - - if (hrt_absolute_time() - last_stats_update >= 1_s) { - rcvd_last_sec = rx_last_sec_read; - rx_last_sec_read = 0; - last_stats_update = hrt_absolute_time(); - } - - // loop forever if informed loop number is negative - if (_options.loops >= 0 && rcvd_loop >= _options.loops) { break; } - - px4_usleep(_options.sleep_us); - ++rcvd_loop; - } - -@[if send_topics]@ - _should_exit_task = true; - pthread_join(sender_thread, nullptr); -@[end if]@ -@[if recv_topics]@ - delete(pubs); -@[end if]@ -} diff --git a/msg/templates/uorb_microcdr/msg.cpp.em b/msg/templates/uorb_microcdr/msg.cpp.em deleted file mode 100644 index 458f455f49..0000000000 --- a/msg/templates/uorb_microcdr/msg.cpp.em +++ /dev/null @@ -1,156 +0,0 @@ -@############################################### -@# -@# PX4 ROS compatible message source code -@# generation for C++ -@# -@# EmPy template for generating .h files -@# Based on the original template for ROS -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - file_name_in (String) Source file -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@# - search_path (dict) search paths for genmsg -@############################################### -/**************************************************************************** - * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/* Auto-generated by genmsg_cpp from file @file_name_in */ - -@{ -import genmsg.msgs - -from px_generate_uorb_topic_helper import * # this is in Tools/ - -uorb_struct = '%s_s'%spec.short_name -topic_name = spec.short_name -}@ - -#include -#include -#include -#include - -@################################################# -@# Searching for serialize function per each field -@################################################# -@{ - -def print_info(field): - print("type: ", field.type, "name: ", field.name, "base_type: ", \ - field.base_type, "field.is_array:", ('0', '1')[field.is_array], " array_len: ", field.array_len, \ - "is_builtin:", ('0', '1')[field.is_builtin], "is_header:", ('0', '1')[field.is_header]) - -def print_level_info(fields): - for field in fields: - print_info(field) - if (not field.is_builtin): - print("\n") - children_fields = get_children_fields(field.base_type, search_path) - print_level_info(children_fields) - print("\n") - -def walk_through_parsed_fields(): - print_level_info(spec.parsed_fields()) - -def get_serialization_type_name(type_name): - if type_name in type_serialize_map: - return type_serialize_map[type_name] - else: - raise Exception("Type {0} not supported, add to type_serialize_map!".format(type_name)) - -def add_serialize_functions(fields, scope_name): - for field in fields: - if (not field.is_header): - if (field.is_builtin): - if (not field.is_array): - print(" ucdr_serialize_" + str(get_serialization_type_name(field.type)) + "(writer, input->" + scope_name+str(field.name) + ");") - else: - print(" ucdr_serialize_array_" + str(get_serialization_type_name(field.base_type)) + "(writer, input->" + scope_name+str(field.name) + ", " + str(field.array_len) + ");") - else: - name = field.name - children_fields = get_children_fields(field.base_type, search_path) - if (scope_name): name = scope_name + name - if (not field.is_array): - add_serialize_functions(children_fields, name + '.') - else: - for i in range(field.array_len): - add_serialize_functions(children_fields, name + ('[%d].' %i)) - -def add_deserialize_functions(fields, scope_name): - for field in fields: - if (not field.is_header): - if (field.is_builtin): - if (not field.is_array): - print(" ucdr_deserialize_" + str(get_serialization_type_name(field.type)) + "(reader, &output->" + scope_name+str(field.name) + ");") - else: - print(" ucdr_deserialize_array_" + str(get_serialization_type_name(field.base_type)) + "(reader, output->" + scope_name+str(field.name) + ", " + str(field.array_len) + ");") - else: - name = field.name - children_fields = get_children_fields(field.base_type, search_path) - if (scope_name): name = scope_name + name - if (not field.is_array): - add_deserialize_functions(children_fields, name + '.') - else: - for i in range(field.array_len): - add_deserialize_functions(children_fields, name + ('[%d].' %i)) - -def add_code_to_serialize(): - add_serialize_functions(spec.parsed_fields(), "") - -def add_code_to_deserialize(): - add_deserialize_functions(spec.parsed_fields(), "") -}@ - -void serialize_@(topic_name)(ucdrBuffer *writer, const struct @(uorb_struct) *input, char *output, uint32_t *length) -{ - if (nullptr == writer || nullptr == input || nullptr == output || nullptr == length) - return; - - ucdr_reset_buffer(writer); - -@add_code_to_serialize() - (*length) = ucdr_buffer_length(writer); -} - -void deserialize_@(topic_name)(ucdrBuffer *reader, struct @(uorb_struct) *output, const char *input) -{ - if (nullptr == reader || nullptr == output || nullptr == input) - return; - - ucdr_reset_buffer(reader); - -@add_code_to_deserialize() -} diff --git a/msg/templates/urtps/Publisher.cpp.em b/msg/templates/urtps/Publisher.cpp.em deleted file mode 100644 index 86687f06a1..0000000000 --- a/msg/templates/urtps/Publisher.cpp.em +++ /dev/null @@ -1,219 +0,0 @@ -@############################################### -@# -@# EmPy template for generating _uRTPS_UART.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - fastrtps_version (str) FastRTPS version installed on the system -@# - ros2_distro (str) ROS2 distro name -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -from packaging import version -import re - -topic = alias if alias else spec.short_name - -try: - ros2_distro = ros2_distro.decode("utf-8") -except AttributeError: - pass - -topic_name = topic - -# For ROS, use the topic pattern convention defined in -# http://wiki.ros.org/ROS/Patterns/Conventions -if ros2_distro: - topic_name_split = re.sub( r"([A-Z])", r" \1", topic).split() - topic_name = topic_name_split[0] - for w in topic_name_split[1:]: - topic_name += "_" + w - topic_name = topic_name.lower() -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file @(topic)_Publisher.cpp - * This file contains the implementation of the publisher functions. - * - * This file was adapted from the fastrtpsgen tool. - */ - -#include "@(topic)_Publisher.h" - -#include -#include -#include -#include -#include -#include -@[if version.parse(fastrtps_version) >= version.parse('2.0')]@ -#include - -using SharedMemTransportDescriptor = eprosima::fastdds::rtps::SharedMemTransportDescriptor; -@[end if]@ - - -@(topic)_Publisher::@(topic)_Publisher() - : mp_participant(nullptr), - mp_publisher(nullptr) -{ } - -@(topic)_Publisher::~@(topic)_Publisher() -{ - Domain::removeParticipant(mp_participant); -} - -bool @(topic)_Publisher::init(const std::string &ns, std::string topic_name) -{ - // Create RTPSParticipant - ParticipantAttributes PParam; -@[if version.parse(fastrtps_version) < version.parse('2.0')]@ - PParam.rtps.builtin.domainId = 0; -@[else]@ - PParam.domainId = 0; -@[end if]@ -@[if version.parse(fastrtps_version) <= version.parse('1.8.4')]@ - PParam.rtps.builtin.leaseDuration = c_TimeInfinite; -@[else]@ - PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite; -@[end if]@ -@[if ros2_distro]@ - // ROS2 default memory management policy - PParam.rtps.builtin.writerHistoryMemoryPolicy = PREALLOCATED_WITH_REALLOC_MEMORY_MODE; -@[end if]@ - std::string nodeName = ns; - nodeName.append("@(topic)_publisher"); - PParam.rtps.setName(nodeName.c_str()); - -@[if ros2_distro]@ - // Check if ROS_LOCALHOST_ONLY is set. This means that one wants to use only - // the localhost network for data sharing. If FastRTPS/DDS >= 2.0 and - // RMW_IMPLEMENTATION is FastDDS then the Shared Memory transport is used - const char* localhost_only = std::getenv("ROS_LOCALHOST_ONLY"); - const char* rmw_implementation = std::getenv("RMW_IMPLEMENTATION"); - const char* ros_distro = std::getenv("ROS_DISTRO"); - if (localhost_only && strcmp(localhost_only, "1") == 0 - && ((rmw_implementation && ((strcmp(rmw_implementation, "rmw_fastrtps_cpp") == 0) - || (strcmp(rmw_implementation, "rmw_fastrtps_dynamic_cpp") == 0))) - || (!rmw_implementation && ros_distro && strcmp(ros_distro, "foxy") == 0))) { - // Create a custom network UDPv4 transport descriptor - // to whitelist the localhost - auto localhostUdpTransport = std::make_shared(); - localhostUdpTransport->interfaceWhiteList.emplace_back("127.0.0.1"); - - // Disable the built-in Transport Layer - PParam.rtps.useBuiltinTransports = false; - - // Add the descriptor as a custom user transport - PParam.rtps.userTransports.push_back(localhostUdpTransport); - -@[ if version.parse(fastrtps_version) >= version.parse('2.0')]@ - // Add shared memory transport when available - auto shmTransport = std::make_shared(); - PParam.rtps.userTransports.push_back(shmTransport); -@[ end if]@ - } -@[end if]@ - - mp_participant = Domain::createParticipant(PParam); - - if (mp_participant == nullptr) { - return false; - } - - // Register the type - Domain::registerType(mp_participant, static_cast(&@(topic)DataType)); - - // Create Publisher - PublisherAttributes Wparam; - Wparam.topic.topicKind = NO_KEY; - Wparam.topic.topicDataType = @(topic)DataType.getName(); -@[if ros2_distro]@ -@[ if ros2_distro == "ardent"]@ - Wparam.qos.m_partition.push_back("rt"); - std::string topicName = ns; -@[ else]@ - std::string topicName = "rt/"; - topicName.append(ns); -@[ end if]@ - // ROS2 default publish mode QoS policy - Wparam.qos.m_publishMode.kind = ASYNCHRONOUS_PUBLISH_MODE; -@[else]@ - std::string topicName = ns; -@[end if]@ - topic_name.empty() ? topicName.append("fmu/@(topic_name)/out") : topicName.append(topic_name); - Wparam.topic.topicName = topicName; - mp_publisher = Domain::createPublisher(mp_participant, Wparam, static_cast(&m_listener)); - - if (mp_publisher == nullptr) { - return false; - } - - return true; -} - -void @(topic)_Publisher::PubListener::onPublicationMatched(Publisher *pub, MatchingInfo &info) -{ - // The first 6 values of the ID guidPrefix of an entity in a DDS-RTPS Domain - // are the same for all its subcomponents (publishers, subscribers) - bool is_different_endpoint = false; - - for (size_t i = 0; i < 6; i++) { - if (pub->getGuid().guidPrefix.value[i] != info.remoteEndpointGuid.guidPrefix.value[i]) { - is_different_endpoint = true; - break; - } - } - - // If the matching happens for the same entity, do not make a match - if (is_different_endpoint) { - if (info.status == MATCHED_MATCHING) { - n_matched++; - std::cout << "\033[0;37m[ micrortps_agent ]\t@(topic) publisher matched\033[0m" << std::endl; - - } else { - n_matched--; - std::cout << "\033[0;37m[ micrortps_agent ]\t@(topic) publisher unmatched\033[0m" << std::endl; - } - } -} - -void @(topic)_Publisher::publish(@(topic)_msg_t *st) -{ - mp_publisher->write(st); -} diff --git a/msg/templates/urtps/Publisher.h.em b/msg/templates/urtps/Publisher.h.em deleted file mode 100644 index 6851497d2a..0000000000 --- a/msg/templates/urtps/Publisher.h.em +++ /dev/null @@ -1,120 +0,0 @@ -@############################################### -@# -@# EmPy template for generating _uRTPS_UART.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - ros2_distro (str) ROS2 distro name -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -from packaging import version -import re - -topic = alias if alias else spec.short_name -try: - ros2_distro = ros2_distro.decode("utf-8") -except AttributeError: - pass -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file @(topic)_Publisher.h - * This header file contains the declaration of the publisher functions. - * - * This file was adapted from the fastrtpsgen tool. - */ - - -#ifndef _@(topic)__PUBLISHER_H_ -#define _@(topic)__PUBLISHER_H_ - -#include -#include - -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -#include "@(topic)_PubSubTypes.h" -@[else]@ -#include "@(topic)PubSubTypes.h" -@[end if]@ - -using namespace eprosima::fastrtps; -using namespace eprosima::fastrtps::rtps; - -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::dds_::@(topic)_; -using @(topic)_msg_datatype = @(package)::msg::dds_::@(topic)_PubSubType; -@[ else]@ -using @(topic)_msg_t = @(topic)_; -using @(topic)_msg_datatype = @(topic)_PubSubType; -@[ end if]@ -@[else]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::@(topic); -using @(topic)_msg_datatype = @(package)::msg::@(topic)PubSubType; -@[ else]@ -using @(topic)_msg_t = @(topic); -using @(topic)_msg_datatype = @(topic)PubSubType; -@[ end if]@ -@[end if]@ - -class @(topic)_Publisher -{ -public: - @(topic)_Publisher(); - virtual ~@(topic)_Publisher(); - bool init(const std::string &ns, std::string topic_name = ""); - void run(); - void publish(@(topic)_msg_t *st); -private: - Participant *mp_participant; - Publisher *mp_publisher; - - class PubListener : public PublisherListener - { - public: - PubListener() : n_matched(0) {}; - ~PubListener() {}; - void onPublicationMatched(Publisher *pub, MatchingInfo &info); - int n_matched; - } m_listener; - @(topic)_msg_datatype @(topic)DataType; -}; - -#endif // _@(topic)__PUBLISHER_H_ diff --git a/msg/templates/urtps/RtpsTopics.cpp.em b/msg/templates/urtps/RtpsTopics.cpp.em deleted file mode 100644 index be1d8d2045..0000000000 --- a/msg/templates/urtps/RtpsTopics.cpp.em +++ /dev/null @@ -1,187 +0,0 @@ -@############################################### -@# -@# EmPy template for generating RtpsTopics.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -import os -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND] -recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE] -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include "RtpsTopics.h" - -bool RtpsTopics::init(std::condition_variable *t_send_queue_cv, std::mutex *t_send_queue_mutex, - std::queue *t_send_queue, const std::string &ns) -{ -@[if recv_topics]@ - // Initialise subscribers - std::cout << "\033[0;36m--- Subscribers ---\033[0m" << std::endl; -@[for topic in recv_topics]@ - - if (_@(topic)_sub.init(@(msgs[0].index(topic) + 1), t_send_queue_cv, t_send_queue_mutex, t_send_queue, ns)) { - std::cout << "- @(topic) subscriber started" << std::endl; - - } else { - std::cerr << "Failed starting @(topic) subscriber" << std::endl; - return false; - } - -@[end for]@ - std::cout << "\033[0;36m-----------------------\033[0m" << std::endl << std::endl; -@[end if]@ -@[if send_topics]@ - - // Initialise publishers - std::cout << "\033[0;36m---- Publishers ----\033[0m" << std::endl; -@[for topic in send_topics]@ - -@[ if topic == 'Timesync' or topic == 'timesync']@ - if (_@(topic)_pub.init(ns)) { - if (_@(topic)_fmu_in_pub.init(ns, std::string("fmu/timesync/in"))) { - _timesync->start(&_@(topic)_fmu_in_pub); - std::cout << "- @(topic) publishers started" << std::endl; - } -@[ elif topic == 'TimesyncStatus' or topic == 'timesync_status']@ - if (_@(topic)_pub.init(ns, std::string("timesync_status"))) { - _timesync->init_status_pub(&_@(topic)_pub); - std::cout << "- @(topic) publisher started" << std::endl; -@[ else]@ - if (_@(topic)_pub.init(ns)) { - std::cout << "- @(topic) publisher started" << std::endl; -@[ end if]@ - - } else { - std::cerr << "ERROR starting @(topic) publisher" << std::endl; - return false; - } - -@[end for]@ - std::cout << "\033[0;36m-----------------------\033[0m" << std::endl; -@[end if]@ - return true; -} - -@[if send_topics]@ -template -void RtpsTopics::sync_timestamp_of_incoming_data(T &msg) { - uint64_t timestamp = getMsgTimestamp(&msg); - uint64_t timestamp_sample = getMsgTimestampSample(&msg); - _timesync->subtractOffset(timestamp); - setMsgTimestamp(&msg, timestamp); - _timesync->subtractOffset(timestamp_sample); - setMsgTimestampSample(&msg, timestamp_sample); -} - -void RtpsTopics::publish(const uint8_t topic_ID, char data_buffer[], size_t len) -{ - switch (topic_ID) { -@[for topic in send_topics]@ - - case @(msgs[0].index(topic) + 1): { // @(topic) publisher - @(topic)_msg_t st; - eprosima::fastcdr::FastBuffer cdrbuffer(data_buffer, len); - eprosima::fastcdr::Cdr cdr_des(cdrbuffer); - st.deserialize(cdr_des); -@[ if topic == 'Timesync' or topic == 'timesync']@ - _timesync->processTimesyncMsg(&st, &_@(topic)_pub); -@[ end if]@ - - // apply timestamp offset - sync_timestamp_of_incoming_data(st); - - _@(topic)_pub.publish(&st); - } - break; -@[end for]@ - - default: - printf("\033[1;33m[ micrortps_agent ]\tUnexpected topic ID '%hhu' to publish. Please make sure the agent is capable of parsing the message associated to the topic ID '%hhu'\033[0m\n", - topic_ID, topic_ID); - break; - } -} -@[end if]@ -@[if recv_topics]@ -template -void RtpsTopics::sync_timestamp_of_outgoing_data(T &msg) { - uint64_t timestamp = getMsgTimestamp(&msg); - uint64_t timestamp_sample = getMsgTimestampSample(&msg); - _timesync->addOffset(timestamp); - setMsgTimestamp(&msg, timestamp); - _timesync->addOffset(timestamp_sample); - setMsgTimestampSample(&msg, timestamp_sample); -} - -bool RtpsTopics::getMsg(const uint8_t topic_ID, eprosima::fastcdr::Cdr &scdr) -{ - bool ret = false; - - switch (topic_ID) { -@[for topic in recv_topics]@ - - case @(msgs[0].index(topic) + 1): // @(topic) subscriber - if (_@(topic)_sub.hasMsg()) { - @(topic)_msg_t msg = _@(topic)_sub.getMsg(); - - // apply timestamp offset - sync_timestamp_of_outgoing_data(msg); - - msg.serialize(scdr); - ret = true; - - _@(topic)_sub.unlockMsg(); - } - - break; -@[end for]@ - - default: - printf("\033[1;33m[ micrortps_agent ]\tUnexpected topic ID '%hhu' to getMsg. Please make sure the agent is capable of parsing the message associated to the topic ID '%hhu'\033[0m\n", - topic_ID, topic_ID); - break; - } - - return ret; -} -@[end if]@ diff --git a/msg/templates/urtps/RtpsTopics.h.em b/msg/templates/urtps/RtpsTopics.h.em deleted file mode 100644 index b82c7b20c8..0000000000 --- a/msg/templates/urtps/RtpsTopics.h.em +++ /dev/null @@ -1,220 +0,0 @@ -@############################################### -@# -@# EmPy template for generating RtpsTopics.h file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - fastrtps_version (List[str]) FastRTPS version installed on the system -@# - package (List[str]) messages package name. Defaulted to 'px4' -@# - ros2_distro (List[str]) ROS2 distro name -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -import os -from packaging import version -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND] -recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE] -package = package[0] -fastrtps_version = fastrtps_version[0] -try: - ros2_distro = ros2_distro[0].decode("utf-8") -except AttributeError: - ros2_distro = ros2_distro[0] -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include -#include -#include -#include - -#include "microRTPS_timesync.h" - -@[for topic in send_topics]@ -#include "@(topic)_Publisher.h" -@[end for]@ -@[for topic in recv_topics]@ -#include "@(topic)_Subscriber.h" -@[end for]@ - - -@[for topic in (recv_topics + send_topics)]@ -@[ if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::dds_::@(topic)_; -@[ else]@ -using @(topic)_msg_t = @(topic)_; -@[ end if]@ -@[ else]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::@(topic); -@[ else]@ -using @(topic)_msg_t = @(topic); -@[ end if]@ -@[ end if]@ -@[end for]@ - -class RtpsTopics -{ -public: - bool init(std::condition_variable *t_send_queue_cv, std::mutex *t_send_queue_mutex, std::queue *t_send_queue, - const std::string &ns); - void set_timesync(const std::shared_ptr ×ync) { _timesync = timesync; }; -@[if send_topics]@ - template - void sync_timestamp_of_incoming_data(T &msg); - void publish(const uint8_t topic_ID, char data_buffer[], size_t len); -@[end if]@ -@[if recv_topics]@ - template - void sync_timestamp_of_outgoing_data(T &msg); - bool getMsg(const uint8_t topic_ID, eprosima::fastcdr::Cdr &scdr); -@[end if]@ - -private: -@[if send_topics]@ - /** Publishers **/ -@[for topic in send_topics]@ -@[ if topic == 'Timesync' or topic == 'timesync']@ - @(topic)_Publisher _@(topic)_pub; - @(topic)_Publisher _@(topic)_fmu_in_pub; -@[ else]@ - @(topic)_Publisher _@(topic)_pub; -@[ end if]@ -@[end for]@ -@[end if]@ - -@[if recv_topics]@ - /** Subscribers **/ -@[for topic in recv_topics]@ - @(topic)_Subscriber _@(topic)_sub; -@[end for]@ -@[end if]@ - - // SFINAE - template struct hasTimestampSample{ - private: - template().timestamp_sample(int64_t()))> - static std::true_type detect(int); - template - static std::false_type detect(...); - public: - static constexpr bool value = decltype(detect(0))::value; - }; - - template - inline typename std::enable_if < !hasTimestampSample::value, uint64_t >::type - getMsgTimestampSample_impl(const T *) { return 0; } - - /** Msg metada Getters **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - template - inline uint64_t getMsgTimestamp(const T *msg) { return msg->timestamp_(); } - - template - inline typename std::enable_if::value, uint64_t>::type - getMsgTimestampSample_impl(const T *msg) { return msg->timestamp_sample_(); } - - template - inline uint8_t getMsgSysID(const T *msg) { return msg->sys_id_(); } - - template - inline uint8_t getMsgSeq(const T *msg) { return msg->seq_(); } -@[elif ros2_distro]@ - template - inline uint64_t getMsgTimestamp(const T *msg) { return msg->timestamp(); } - - template - inline typename std::enable_if::value, uint64_t>::type - getMsgTimestampSample_impl(const T *msg) { return msg->timestamp_sample(); } - - template - inline uint8_t getMsgSysID(const T *msg) { return msg->sys_id(); } - - template - inline uint8_t getMsgSeq(const T *msg) { return msg->seq(); } -@[end if]@ - - template - inline uint64_t getMsgTimestampSample(const T *msg) { return getMsgTimestampSample_impl(msg); } - - template - inline typename std::enable_if ::value, void>::type - setMsgTimestampSample_impl(T *, const uint64_t &) {} - - /** Msg metadata Setters **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - template - inline void setMsgTimestamp(T *msg, const uint64_t ×tamp) { msg->timestamp_() = timestamp; } - - template - inline typename std::enable_if::value, void>::type - setMsgTimestampSample_impl(T *msg, const uint64_t ×tamp_sample) { msg->timestamp_sample_() = timestamp_sample; } - - template - inline void setMsgSysID(T *msg, const uint8_t &sys_id) { msg->sys_id_() = sys_id; } - - template - inline void setMsgSeq(T *msg, const uint8_t &seq) { msg->seq_() = seq; } -@[elif ros2_distro]@ - template - inline void setMsgTimestamp(T *msg, const uint64_t ×tamp) { msg->timestamp() = timestamp; } - - template - inline typename std::enable_if::value, void>::type - setMsgTimestampSample_impl(T *msg, const uint64_t ×tamp_sample) { msg->timestamp_sample() = timestamp_sample; } - - template - inline void setMsgSysID(T *msg, const uint8_t &sys_id) { msg->sys_id() = sys_id; } - - template - inline void setMsgSeq(T *msg, const uint8_t &seq) { msg->seq() = seq; } -@[end if]@ - - template - inline void setMsgTimestampSample(T *msg, const uint64_t ×tamp_sample) { setMsgTimestampSample_impl(msg, timestamp_sample); } - - /** - * @@brief Timesync object ptr. - * This object is used to compuyte and apply the time offsets to the - * messages timestamps. - */ - std::shared_ptr _timesync; -}; diff --git a/msg/templates/urtps/Subscriber.cpp.em b/msg/templates/urtps/Subscriber.cpp.em deleted file mode 100644 index ab0c5f2d82..0000000000 --- a/msg/templates/urtps/Subscriber.cpp.em +++ /dev/null @@ -1,284 +0,0 @@ -@############################################### -@# -@# EmPy template for generating _uRTPS_UART.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - fastrtps_version (str) FastRTPS version installed on the system -@# - ros2_distro (str) ROS2 distro name -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -from packaging import version -import re - -topic = alias if alias else spec.short_name -try: - ros2_distro = ros2_distro.decode("utf-8") -except AttributeError: - pass - -topic_name = topic - -# For ROS, use the topic pattern convention defined in -# http://wiki.ros.org/ROS/Patterns/Conventions -if ros2_distro: - topic_name_split = re.sub( r"([A-Z])", r" \1", topic).split() - topic_name = topic_name_split[0] - for w in topic_name_split[1:]: - topic_name += "_" + w - topic_name = topic_name.lower() -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file @(topic)_Subscriber.cpp - * This file contains the implementation of the subscriber functions. - * - * This file was adapted from the fastrtpsgen tool. - */ - -#include "@(topic)_Subscriber.h" - -#include -#include -#include -#include -#include -#include -@[if version.parse(fastrtps_version) >= version.parse('2.0')]@ -#include - -using SharedMemTransportDescriptor = eprosima::fastdds::rtps::SharedMemTransportDescriptor; -@[end if]@ - - -@(topic)_Subscriber::@(topic)_Subscriber() - : mp_participant(nullptr), - mp_subscriber(nullptr) -{ } - -@(topic)_Subscriber::~@(topic)_Subscriber() -{ - Domain::removeParticipant(mp_participant); -} - -bool @(topic)_Subscriber::init(uint8_t topic_ID, std::condition_variable *t_send_queue_cv, - std::mutex *t_send_queue_mutex, std::queue *t_send_queue, const std::string &ns, - std::string topic_name) -{ - m_listener.topic_ID = topic_ID; - m_listener.t_send_queue_cv = t_send_queue_cv; - m_listener.t_send_queue_mutex = t_send_queue_mutex; - m_listener.t_send_queue = t_send_queue; - - // Create RTPSParticipant - ParticipantAttributes PParam; -@[if version.parse(fastrtps_version) < version.parse('2.0')]@ - PParam.rtps.builtin.domainId = 0; -@[else]@ - PParam.domainId = 0; -@[end if]@ -@[if version.parse(fastrtps_version) <= version.parse('1.8.4')]@ - PParam.rtps.builtin.leaseDuration = c_TimeInfinite; -@[else]@ - PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite; -@[end if]@ -@[if ros2_distro]@ - // ROS2 default memory management policy - PParam.rtps.builtin.writerHistoryMemoryPolicy = PREALLOCATED_WITH_REALLOC_MEMORY_MODE; -@[end if]@ - std::string nodeName = ns; - nodeName.append("@(topic)_subscriber"); - PParam.rtps.setName(nodeName.c_str()); - -@[if ros2_distro]@ - // Check if ROS_LOCALHOST_ONLY is set. This means that one wants to use only - // the localhost network for data sharing. If FastRTPS/DDS >= 2.0 and - // RMW_IMPLEMENTATION is FastDDS then the Shared Memory transport is used - const char* localhost_only = std::getenv("ROS_LOCALHOST_ONLY"); - const char* rmw_implementation = std::getenv("RMW_IMPLEMENTATION"); - const char* ros_distro = std::getenv("ROS_DISTRO"); - if (localhost_only && strcmp(localhost_only, "1") == 0 - && ((rmw_implementation && ((strcmp(rmw_implementation, "rmw_fastrtps_cpp") == 0) - || (strcmp(rmw_implementation, "rmw_fastrtps_dynamic_cpp") == 0))) - || (!rmw_implementation && ros_distro && strcmp(ros_distro, "foxy") == 0))) { - // Create a custom network UDPv4 transport descriptor - // to whitelist the localhost - auto localhostUdpTransport = std::make_shared(); - localhostUdpTransport->interfaceWhiteList.emplace_back("127.0.0.1"); - - // Disable the built-in Transport Layer - PParam.rtps.useBuiltinTransports = false; - - // Add the descriptor as a custom user transport - PParam.rtps.userTransports.push_back(localhostUdpTransport); - -@[ if version.parse(fastrtps_version) >= version.parse('2.0')]@ - // Add shared memory transport when available - auto shmTransport = std::make_shared(); - PParam.rtps.userTransports.push_back(shmTransport); -@[ end if]@ - } -@[end if]@ - - mp_participant = Domain::createParticipant(PParam); - - if (mp_participant == nullptr) { - return false; - } - - // Register the type - Domain::registerType(mp_participant, static_cast(&@(topic)DataType)); - - // Create Subscriber - SubscriberAttributes Rparam; - Rparam.topic.topicKind = NO_KEY; - Rparam.topic.topicDataType = @(topic)DataType.getName(); -@[if ros2_distro]@ -@[ if ros2_distro == "ardent"]@ - Rparam.qos.m_partition.push_back("rt"); - std::string topicName = ns; -@[ else]@ - std::string topicName = "rt/"; - topicName.append(ns); -@[ end if]@ -@[else]@ - std::string topicName = ns; -@[end if]@ - topic_name.empty() ? topicName.append("fmu/@(topic_name)/in") : topicName.append(topic_name); - Rparam.topic.topicName = topicName; - mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, static_cast(&m_listener)); - - if (mp_subscriber == nullptr) { - return false; - } - - return true; -} - -void @(topic)_Subscriber::SubListener::onSubscriptionMatched(Subscriber *sub, MatchingInfo &info) -{ -@# Since the time sync runs on the bridge itself, it is required that there is a -@# match between two topics of the same entity -@[if topic != 'Timesync' and topic != 'timesync' and topic != 'TimesyncStatus' and topic != 'timesync_status']@ - // The first 6 values of the ID guidPrefix of an entity in a DDS-RTPS Domain - // are the same for all its subcomponents (publishers, subscribers) - bool is_different_endpoint = false; - - for (size_t i = 0; i < 6; i++) { - if (sub->getGuid().guidPrefix.value[i] != info.remoteEndpointGuid.guidPrefix.value[i]) { - is_different_endpoint = true; - break; - } - } - - // If the matching happens for the same entity, do not make a match - if (is_different_endpoint) { - if (info.status == MATCHED_MATCHING) { - n_matched++; - std::cout << "\033[0;37m[ micrortps_agent ]\t@(topic) subscriber matched\033[0m" << std::endl; - - } else { - n_matched--; - std::cout << "\033[0;37m[ micrortps_agent ]\t@(topic) subscriber unmatched\033[0m" << std::endl; - } - } - -@[else]@ - (void)sub; - - if (info.status == MATCHED_MATCHING) { - n_matched++; - - } else { - n_matched--; - } -@[end if]@ -} - -void @(topic)_Subscriber::SubListener::onNewDataMessage(Subscriber *sub) -{ - if (n_matched > 0) { - std::unique_lock has_msg_lock(has_msg_mutex); - - if (has_msg.load() == true) { // Check if msg has been fetched - has_msg_cv.wait(has_msg_lock); // Wait till msg has been fetched - } - - has_msg_lock.unlock(); - - // Take data - if (sub->takeNextData(&msg, &m_info)) { - if (m_info.sampleKind == ALIVE) { - std::unique_lock lk(*t_send_queue_mutex); - - ++n_msg; - has_msg = true; - - t_send_queue->push(topic_ID); - lk.unlock(); - t_send_queue_cv->notify_one(); - - } - } - } -} - -bool @(topic)_Subscriber::hasMsg() -{ - if (m_listener.n_matched > 0) { - return m_listener.has_msg.load(); - } - - return false; -} - -@(topic)_msg_t @(topic)_Subscriber::getMsg() -{ - return m_listener.msg; -} - -void @(topic)_Subscriber::unlockMsg() -{ - if (m_listener.n_matched > 0) { - std::unique_lock has_msg_lock(m_listener.has_msg_mutex); - m_listener.has_msg = false; - has_msg_lock.unlock(); - m_listener.has_msg_cv.notify_one(); - } -} diff --git a/msg/templates/urtps/Subscriber.h.em b/msg/templates/urtps/Subscriber.h.em deleted file mode 100644 index 703112bef3..0000000000 --- a/msg/templates/urtps/Subscriber.h.em +++ /dev/null @@ -1,140 +0,0 @@ -@############################################### -@# -@# EmPy template for generating _uRTPS_UART.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - ros2_distro (str) ROS2 distro name -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### -@{ -import genmsg.msgs -from packaging import version -import re - -topic = alias if alias else spec.short_name -try: - ros2_distro = ros2_distro.decode("utf-8") -except AttributeError: - pass -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file @(topic)_Subscriber.h - * This header file contains the declaration of the subscriber functions. - * - * This file was adapted from the fastrtpsgen tool. - */ - - -#ifndef _@(topic)__SUBSCRIBER_H_ -#define _@(topic)__SUBSCRIBER_H_ - -#include -#include -#include -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -#include "@(topic)_PubSubTypes.h" -@[else]@ -#include "@(topic)PubSubTypes.h" -@[end if]@ - -#include -#include -#include - -using namespace eprosima::fastrtps; -using namespace eprosima::fastrtps::rtps; - -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::dds_::@(topic)_; -using @(topic)_msg_datatype = @(package)::msg::dds_::@(topic)_PubSubType; -@[ else]@ -using @(topic)_msg_t = @(topic)_; -using @(topic)_msg_datatype = @(topic)_PubSubType; -@[ end if]@ -@[else]@ -@[ if ros2_distro]@ -using @(topic)_msg_t = @(package)::msg::@(topic); -using @(topic)_msg_datatype = @(package)::msg::@(topic)PubSubType; -@[ else]@ -using @(topic)_msg_t = @(topic); -using @(topic)_msg_datatype = @(topic)PubSubType; -@[ end if]@ -@[end if]@ - -class @(topic)_Subscriber -{ -public: - @(topic)_Subscriber(); - virtual ~@(topic)_Subscriber(); - bool init(uint8_t topic_ID, std::condition_variable *t_send_queue_cv, std::mutex *t_send_queue_mutex, - std::queue *t_send_queue, const std::string &ns, std::string topic_name = ""); - void run(); - bool hasMsg(); - @(topic)_msg_t getMsg(); - void unlockMsg(); - -private: - Participant *mp_participant; - Subscriber *mp_subscriber; - - class SubListener : public SubscriberListener - { - public: - SubListener() : n_matched(0), n_msg(0), has_msg(false) {}; - ~SubListener() {}; - void onSubscriptionMatched(Subscriber *sub, MatchingInfo &info); - void onNewDataMessage(Subscriber *sub); - SampleInfo_t m_info; - int n_matched; - int n_msg; - @(topic)_msg_t msg; - std::atomic_bool has_msg; - uint8_t topic_ID; - std::condition_variable *t_send_queue_cv; - std::mutex *t_send_queue_mutex; - std::queue *t_send_queue; - std::condition_variable has_msg_cv; - std::mutex has_msg_mutex; - - } m_listener; - @(topic)_msg_datatype @(topic)DataType; -}; - -#endif // _@(topic)__SUBSCRIBER_H_ diff --git a/msg/templates/urtps/microRTPS_agent.cpp.em b/msg/templates/urtps/microRTPS_agent.cpp.em deleted file mode 100644 index 07510f9174..0000000000 --- a/msg/templates/urtps/microRTPS_agent.cpp.em +++ /dev/null @@ -1,404 +0,0 @@ -@############################################### -@# -@# EmPy template for generating microRTPS_agent.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@# - ros2_distro (List[str]) ROS2 distro name -@############################################### -@{ -import genmsg.msgs -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -try: - ros2_distro = ros2_distro[0].decode("utf-8") -except AttributeError: - ros2_distro = ros2_distro[0] - -send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND] -recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE] -}@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "microRTPS_transport.h" -#include "microRTPS_timesync.h" -#include "RtpsTopics.h" - -@[if ros2_distro]@ -#include -@[end if]@ - -// Default values -#define SLEEP_US 1 -#define MAX_SLEEP_US 1000000 -#define BAUDRATE 460800 -#define MAX_DATA_RATE 10000000 -#define DEVICE "/dev/ttyACM0" -#define POLL_MS 1 -#define MAX_POLL_MS 1000 -#define DEFAULT_RECV_PORT 2020 -#define DEFAULT_SEND_PORT 2019 -#define DEFAULT_IP "127.0.0.1" - -using namespace eprosima; -using namespace eprosima::fastrtps; - -volatile sig_atomic_t running = 1; -std::unique_ptr transport_node; -std::unique_ptr topics; -uint32_t total_sent = 0, sent = 0; - -struct options { - enum class eTransports { - UART, - UDP - }; - eTransports transport = options::eTransports::UART; - char device[64] = DEVICE; - int sleep_us = SLEEP_US; - uint32_t baudrate = BAUDRATE; - int poll_ms = POLL_MS; - uint16_t recv_port = DEFAULT_RECV_PORT; - uint16_t send_port = DEFAULT_SEND_PORT; - char ip[16] = DEFAULT_IP; - bool sw_flow_control = false; - bool hw_flow_control = false; - bool verbose_debug = false; - std::string ns = ""; -} _options; - -static void usage(const char *name) -{ - printf("usage: %s [options]\n\n" - " -b UART device baudrate. Defaults to 460800\n" - " -d UART device. Defaults to /dev/ttyACM0\n" - " -f Activates UART link SW flow control\n" - " -g Activates UART link HW flow control\n" - " -i Target remote IP address for UDP. Defaults to 127.0.0.1\n" - " -n Topics namespace. Identifies the vehicle in a multi-agent network\n" - " -o UART polling timeout in milliseconds. Defaults to 1ms\n" - " -r UDP port for receiving (local). Defaults to 2020\n" - " -s UDP port for sending (remote). Defaults to 2019\n" - " -t [UART|UDP] Defaults to UART\n" - " -v Add more verbosity\n" - " -w Iteration time for data publishing to the DDS world, in microseconds.\n" - " Defaults to 1us\n" - " (ROS2 only) Allows to pass arguments to the timesync ROS2 node.\n" - " Currently used for setting the usage of simulation time by the node using\n" - " '--ros-args -p use_sim_time:=true'\n", - name); -} - -static int parse_options(int argc, char **argv) -{ - static const struct option options[] = { - {"baudrate", required_argument, NULL, 'b'}, - {"device", required_argument, NULL, 'd'}, - {"sw-flow-control", no_argument, NULL, 'f'}, - {"hw-flow-control", no_argument, NULL, 'g'}, - {"ip-address", required_argument, NULL, 'i'}, - {"namespace", required_argument, NULL, 'n'}, - {"poll-ms", required_argument, NULL, 'o'}, - {"reception-port", required_argument, NULL, 'r'}, - {"sending-port", required_argument, NULL, 's'}, - {"transport", required_argument, NULL, 't'}, - {"increase-verbosity", no_argument, NULL, 'v'}, - {"sleep-time-us", required_argument, NULL, 'w'}, - {"ros-args", required_argument, NULL, 0}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}}; - - int ch; - - while ((ch = getopt_long(argc, argv, "t:d:w:b:o:r:s:i:fghvn:", options, nullptr)) >= 0) { - switch (ch) { - case 't': _options.transport = strcmp(optarg, "UDP") == 0 ? - options::eTransports::UDP - : options::eTransports::UART; break; - - case 'd': if (nullptr != optarg) strcpy(_options.device, optarg); break; - - case 'w': _options.sleep_us = strtoul(optarg, nullptr, 10); break; - - case 'b': _options.baudrate = strtoul(optarg, nullptr, 10); break; - - case 'o': _options.poll_ms = strtol(optarg, nullptr, 10); break; - - case 'r': _options.recv_port = strtoul(optarg, nullptr, 10); break; - - case 's': _options.send_port = strtoul(optarg, nullptr, 10); break; - - case 'i': if (nullptr != optarg) strcpy(_options.ip, optarg); break; - - case 'f': _options.sw_flow_control = true; break; - - case 'g': _options.hw_flow_control = true; break; - - case 'h': usage(argv[0]); exit(0); break; - - case 'v': _options.verbose_debug = true; break; - - case 'n': if (nullptr != optarg) _options.ns = std::string(optarg) + "/"; break; - - default: -@[if ros2_distro]@ - break; -@[else]@ - usage(argv[0]); - return -1; -@[end if]@ - } - } - - if (_options.poll_ms < POLL_MS) { - _options.poll_ms = POLL_MS; - printf("\033[1;33m[ micrortps_agent ]\tPoll timeout too low. Using %d ms instead\033[0m\n", POLL_MS); - } else if (_options.poll_ms > MAX_POLL_MS) { - _options.poll_ms = MAX_POLL_MS; - printf("\033[1;33m[ micrortps_agent ]\tPoll timeout too high. Using %d ms instead\033[0m\n", MAX_POLL_MS); - } - - if (_options.sleep_us > MAX_SLEEP_US) { - _options.sleep_us = MAX_SLEEP_US; - printf("\033[1;33m[ micrortps_agent ]\tPublishing iteration cycle too slow. Using %d us instead\033[0m\n", MAX_SLEEP_US); - } - - if (_options.hw_flow_control && _options.sw_flow_control) { - printf("\033[0;31m[ micrortps_agent ]\tHW and SW flow control set. Please set only one or another\033[0m\n"); - return -1; - } - - return 0; -} - -@[if recv_topics]@ -std::atomic exit_sender_thread(false); -std::condition_variable t_send_queue_cv; -std::mutex t_send_queue_mutex; -std::queue t_send_queue; - -void t_send(void *) -{ - char data_buffer[BUFFER_SIZE] = {}; - uint32_t length = 0; - uint8_t topic_ID = 255; - - while (running && !exit_sender_thread) { - std::unique_lock lk(t_send_queue_mutex); - - while (t_send_queue.empty() && !exit_sender_thread) { - t_send_queue_cv.wait(lk); - } - - topic_ID = t_send_queue.front(); - t_send_queue.pop(); - lk.unlock(); - - size_t header_length = transport_node->get_header_length(); - /* make room for the header to fill in later */ - eprosima::fastcdr::FastBuffer cdrbuffer(&data_buffer[header_length], sizeof(data_buffer) - header_length); - eprosima::fastcdr::Cdr scdr(cdrbuffer); - - if (!exit_sender_thread) { - if (topics->getMsg(topic_ID, scdr)) { - length = scdr.getSerializedDataLength(); - - if (0 < (length = transport_node->write(topic_ID, data_buffer, length))) { - total_sent += length; - ++sent; - } - } - } - } -} -@[end if]@ - -void signal_handler(int signum) -{ - printf("\n\033[1;33m[ micrortps_agent ]\tInterrupt signal (%d) received.\033[0m\n", signum); - running = 0; - transport_node->close(); -} - -int main(int argc, char **argv) -{ - printf("\033[0;37m--- MicroRTPS Agent ---\033[0m\n"); - - if (-1 == parse_options(argc, argv)) { - printf("\033[1;33m[ micrortps_agent ]\tEXITING...\033[0m\n"); - return -1; - } - - topics = std::make_unique(); - -@[if ros2_distro]@ - // Initialize communications via the rmw implementation and set up a global signal handler. - rclcpp::init(argc, argv, rclcpp::InitOptions()); -@[end if]@ - - // register signal SIGINT and signal handler - signal(SIGINT, signal_handler); - - printf("[ micrortps_agent ]\tStarting link...\n"); - - const char* localhost_only = std::getenv("ROS_LOCALHOST_ONLY"); - - if (localhost_only && strcmp(localhost_only, "1") == 0) { - printf("[ micrortps_agent ]\tUsing only the localhost network...\n"); - } - - /** - * Set the system ID to Mission Computer, in order to identify the agent side - * - * Note: theoretically a multi-agent system is possible, but this would require - * adjustments in the way the timesync is done (would have to create a timesync - * instance per agent). Keeping it contained for a 1:1 link for now is reasonable. - */ - const uint8_t sys_id = static_cast(MicroRtps::System::MISSION_COMPUTER); - - switch (_options.transport) { - case options::eTransports::UART: { - transport_node = std::make_unique(_options.device, _options.baudrate, _options.poll_ms, - _options.sw_flow_control, _options.hw_flow_control, sys_id, _options.verbose_debug); - printf("[ micrortps_agent ]\tUART transport: device: %s; baudrate: %d; poll: %dms; flow_control: %s\n", - _options.device, _options.baudrate, _options.poll_ms, - _options.sw_flow_control ? "SW enabled" : (_options.hw_flow_control ? "HW enabled" : "No")); - } - break; - - case options::eTransports::UDP: { - transport_node = std::make_unique(_options.ip, _options.recv_port, _options.send_port, - sys_id, _options.verbose_debug); - printf("[ micrortps_agent ]\tUDP transport: ip address: %s; recv port: %u; send port: %u\n", - _options.ip, _options.recv_port, _options.send_port); - } - break; - - default: - printf("\033[0;37m[ micrortps_agent ]\tEXITING...\033[0m\n"); - return -1; - } - - if (0 > transport_node->init()) { - printf("\033[0;37m[ micrortps_agent ]\tEXITING...\033[0m\n"); - return -1; - } - - sleep(1); - -@[if send_topics]@ - char data_buffer[BUFFER_SIZE] = {}; - int received = 0, loop = 0; - int length = 0, total_read = 0; - bool receiving = false; - uint8_t topic_ID = 255; - std::chrono::time_point start, end; -@[end if]@ - - // Init timesync - topics->set_timesync(std::make_shared(_options.verbose_debug)); - -@[if recv_topics]@ - topics->init(&t_send_queue_cv, &t_send_queue_mutex, &t_send_queue, _options.ns); -@[end if]@ - - running = true; -@[if recv_topics]@ - std::thread sender_thread(t_send, nullptr); -@[end if]@ - - while (running) { -@[if send_topics]@ - ++loop; - if (!receiving) { start = std::chrono::steady_clock::now(); } - - // Publish messages received from UART - if (0 < (length = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) { - topics->publish(topic_ID, data_buffer, sizeof(data_buffer)); - ++received; - total_read += length; - receiving = true; - end = std::chrono::steady_clock::now(); - } -@[else]@ - usleep(_options.sleep_us); -@[end if]@ - } - -@[if recv_topics]@ - exit_sender_thread = true; - t_send_queue_cv.notify_one(); - sender_thread.join(); - - std::chrono::duration elapsed_secs = end - start; - if (received > 0) { - printf("[ micrortps_agent ]\tRECEIVED: %d messages - %d bytes; %d LOOPS - %.03f seconds - %.02fKB/s\n", - received, total_read, loop, elapsed_secs.count(), static_cast(total_read) / (1000 * elapsed_secs.count())); - } -@[end if]@ -@[if recv_topics]@ - if (sent > 0) { - printf("[ micrortps_agent ]\tSENT: %lu messages - %lu bytes\n", static_cast(sent), - static_cast(total_sent)); - } -@[end if]@ - -@[if ros2_distro]@ - rclcpp::shutdown(); -@[end if]@ - transport_node.reset(); - topics.reset(); - - return 0; -} diff --git a/msg/templates/urtps/microRTPS_agent_CMakeLists.txt.em b/msg/templates/urtps/microRTPS_agent_CMakeLists.txt.em deleted file mode 100644 index f75d13f5d1..0000000000 --- a/msg/templates/urtps/microRTPS_agent_CMakeLists.txt.em +++ /dev/null @@ -1,63 +0,0 @@ -################################################################################ -# -# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). -# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -################################################################################ - -cmake_minimum_required(VERSION 2.8.12) -project(micrortps_agent) - -# Find requirements -find_package(fastrtps REQUIRED) -find_package(fastcdr REQUIRED) - -# Set C++14 -include(CheckCXXCompilerFlag) -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG OR - CMAKE_CXX_COMPILER_ID MATCHES "Clang") - check_cxx_compiler_flag(--std=c++14 SUPPORTS_CXX14) - if(SUPPORTS_CXX14) - add_compile_options(--std=c++14) - else() - message(FATAL_ERROR "Compiler doesn't support C++14") - endif() -endif() - -file(GLOB MICRORTPS_AGENT_SOURCES src/*.cpp src/*.h) -add_executable(micrortps_agent ${MICRORTPS_AGENT_SOURCES}) -target_link_libraries(micrortps_agent fastrtps fastcdr) - -# Install to '/usr/local/bin' if `make install` is used -install( - TARGETS micrortps_agent - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib -) diff --git a/msg/templates/urtps/microRTPS_timesync.cpp.em b/msg/templates/urtps/microRTPS_timesync.cpp.em deleted file mode 100644 index a441339fb1..0000000000 --- a/msg/templates/urtps/microRTPS_timesync.cpp.em +++ /dev/null @@ -1,295 +0,0 @@ -@############################################### -@# -@# EmPy template for generating microRTPS_timesync.cpp file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - package (List[str]) messages package name. Defaulted to 'px4' -@# - ros2_distro (List[str]) ROS2 distro name -@############################################### -@{ -import genmsg.msgs -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -package = package[0] -fastrtps_version = fastrtps_version[0] -try: - ros2_distro = ros2_distro[0].decode("utf-8") -except AttributeError: - ros2_distro = ros2_distro[0] -}@ -/**************************************************************************** - * - * Copyright (c) 2020-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file microRTPS_timesync.cpp - * @@brief source code for time sync implementation - */ - -#include -#include -#include - -#include "microRTPS_timesync.h" - -TimeSync::TimeSync(bool debug) - : _offset_ns(-1), -@[if ros2_distro]@ - _timesync_node(std::make_shared("timesync_node")), -@[end if]@ - _skew_ns_per_sync(0.0), - _num_samples(0), - _request_reset_counter(0), - _last_msg_seq(0), - _last_remote_msg_seq(0), - _debug(debug) -{ } - -TimeSync::~TimeSync() { stop(); } - -void TimeSync::start(TimesyncPublisher *pub) -{ - stop(); - -@[if ros2_distro]@ - auto spin_node = [this]() { - rclcpp::spin(_timesync_node); - }; -@[end if]@ - - auto run = [this, pub]() { - while (!_request_stop) { - timesync_msg_t msg = newTimesyncMsg(); - - pub->publish(&msg); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - }; - _request_stop = false; -@[if ros2_distro]@ - _timesync_node_thread.reset(new std::thread(spin_node)); -@[end if]@ - _send_timesync_thread.reset(new std::thread(run)); -} - -void TimeSync::init_status_pub(TimesyncStatusPublisher *status_pub) -{ - auto run = [this, status_pub]() { - while (!_request_stop) { - timesync_status_msg_t status_msg = newTimesyncStatusMsg(); - - status_pub->publish(&status_msg); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - }; - _request_stop = false; - _send_timesync_status_thread.reset(new std::thread(run)); -} - -void TimeSync::stop() -{ - _request_stop = true; - -@[if ros2_distro]@ - if (_timesync_node_thread && _timesync_node_thread->joinable()) { _timesync_node_thread->join(); } -@[end if]@ - if (_send_timesync_thread && _send_timesync_thread->joinable()) { _send_timesync_thread->join(); } - if (_send_timesync_status_thread && _send_timesync_status_thread->joinable()) { _send_timesync_status_thread->join(); } -} - -void TimeSync::reset() -{ - _num_samples = 0; - _request_reset_counter = 0; -} - -@[if ros2_distro]@ -uint64_t TimeSync::getROSTimeNSec() const -{ - return _timesync_node->now().nanoseconds(); -} - -uint64_t TimeSync::getROSTimeUSec() const -{ - return RCL_NS_TO_US(getROSTimeNSec()); -} -@[else]@ -uint64_t TimeSync::getSteadyTimeNSec() const -{ - auto time = std::chrono::steady_clock::now(); - return std::chrono::time_point_cast(time).time_since_epoch().count(); -} - -uint64_t TimeSync::getSteadyTimeUSec() const -{ - auto time = std::chrono::steady_clock::now(); - return std::chrono::time_point_cast(time).time_since_epoch().count(); -} -@[end if]@ - -bool TimeSync::addMeasurement(int64_t local_t1_ns, int64_t remote_t2_ns, int64_t local_t3_ns) -{ - _rtti = local_t3_ns - local_t1_ns; - _remote_time_stamp = remote_t2_ns; - - // assume rtti is evenly split both directions - int64_t remote_t3_ns = remote_t2_ns + _rtti.load() / 2ll; - - int64_t measurement_offset = remote_t3_ns - local_t3_ns; - - if (_request_reset_counter > REQUEST_RESET_COUNTER_THRESHOLD) { - reset(); - - if (_debug) { std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync clock changed, resetting\033[0m" << std::endl; } - } - - if (_num_samples == 0) { - updateOffset(measurement_offset); - _skew_ns_per_sync = 0; - } - - if (_num_samples >= WINDOW_SIZE) { - if (std::abs(measurement_offset - _offset_ns.load()) > TRIGGER_RESET_THRESHOLD_NS) { - _request_reset_counter++; - - if (_debug) { std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync offset outlier, discarding\033[0m" << std::endl; } - - return false; - - } else { - _request_reset_counter = 0; - } - } - - // ignore if rtti > 50ms - if (_rtti.load() > 50ll * 1000ll * 1000ll) { - if (_debug) { std::cout << "\033[1;33m[ micrortps__timesync ]\tRTTI too high for timesync: " << _rtti.load() / (1000ll * 1000ll) << "ms\033[0m" << std::endl; } - - return false; - } - - double alpha = ALPHA_FINAL; - double beta = BETA_FINAL; - - if (_num_samples < WINDOW_SIZE) { - double schedule = (double)_num_samples / WINDOW_SIZE; - double s = 1. - exp(.5 * (1. - 1. / (1. - schedule))); - alpha = (1. - s) * ALPHA_INITIAL + s * ALPHA_FINAL; - beta = (1. - s) * BETA_INITIAL + s * BETA_FINAL; - } - - _offset_prev = _offset_ns.load(); - updateOffset(static_cast((_skew_ns_per_sync + _offset_ns.load()) * (1. - alpha) + - measurement_offset * alpha)); - _skew_ns_per_sync = - static_cast(beta * (_offset_ns.load() - _offset_prev.load()) + (1. - beta) * _skew_ns_per_sync); - - _num_samples++; - - return true; -} - -void TimeSync::processTimesyncMsg(timesync_msg_t *msg, TimesyncPublisher *pub) -{ - if (getMsgSeq(msg) != _last_remote_msg_seq) { - _last_remote_msg_seq = getMsgSeq(msg); - - if (getMsgTC1(msg) > 0) { -@[if ros2_distro]@ - if (!addMeasurement(getMsgTS1(msg), getMsgTC1(msg), getROSTimeNSec())) { -@[else]@ - if (!addMeasurement(getMsgTS1(msg), getMsgTC1(msg), getSteadyTimeNSec())) { -@[end if]@ - if (_debug) { std::cerr << "\033[1;33m[ micrortps__timesync ]\tOffset not updated\033[0m" << std::endl; } - } - - } else if (getMsgTC1(msg) == 0) { -@[if ros2_distro]@ - setMsgTimestamp(msg, getROSTimeUSec()); -@[else]@ - setMsgTimestamp(msg, getSteadyTimeUSec()); -@[end if]@ - setMsgSeq(msg, getMsgSeq(msg) + 1); -@[if ros2_distro]@ - setMsgTC1(msg, getROSTimeNSec()); -@[else]@ - setMsgTC1(msg, getSteadyTimeNSec()); -@[end if]@ - - pub->publish(msg); - } - } -} - -timesync_msg_t TimeSync::newTimesyncMsg() -{ - timesync_msg_t msg{}; - -@[if ros2_distro]@ - setMsgTimestamp(&msg, getROSTimeUSec()); -@[else]@ - setMsgTimestamp(&msg, getSteadyTimeUSec()); -@[end if]@ - setMsgSeq(&msg, _last_msg_seq); - setMsgTC1(&msg, 0); -@[if ros2_distro]@ - setMsgTS1(&msg, getROSTimeNSec()); -@[else]@ - setMsgTS1(&msg, getSteadyTimeNSec()); -@[end if]@ - - _last_msg_seq++; - - return msg; -} - -timesync_status_msg_t TimeSync::newTimesyncStatusMsg() -{ - timesync_status_msg_t msg{}; - -@[if ros2_distro]@ - setMsgTimestamp(&msg, getROSTimeUSec()); -@[else]@ - setMsgTimestamp(&msg, getSteadyTimeUSec()); -@[end if]@ - setMsgSourceProtocol(&msg, 1); // SOURCE_PROTOCOL_RTPS - setMsgRemoteTimeStamp(&msg, _remote_time_stamp.load() / 1000ULL); - setMsgObservedOffset(&msg, _offset_prev.load()); - setMsgEstimatedOffset(&msg, _offset_ns.load()); - setMsgRoundTripTime(&msg, _rtti.load() / 1000ll); - - return msg; -} diff --git a/msg/templates/urtps/microRTPS_timesync.h.em b/msg/templates/urtps/microRTPS_timesync.h.em deleted file mode 100644 index ffe4541f18..0000000000 --- a/msg/templates/urtps/microRTPS_timesync.h.em +++ /dev/null @@ -1,303 +0,0 @@ -@############################################### -@# -@# EmPy template for generating microRTPS_timesync.h file -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - package (List[str]) messages package name. Defaulted to 'px4' -@# - ros2_distro (List[str]) ROS2 distro name -@############################################### -@{ -import genmsg.msgs -from packaging import version -from px_generate_uorb_topic_files import MsgScope # this is in Tools/ - -package = package[0] -fastrtps_version = fastrtps_version[0] -try: - ros2_distro = ros2_distro[0].decode("utf-8") -except AttributeError: - ros2_distro = ros2_distro[0] -}@ -/**************************************************************************** - * - * Copyright (c) 2020-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/*! - * @@file microRTPS_timesync.h - * @@brief Adds time sync for the microRTPS bridge - * @@author Nuno Marques - * @@author Julian Kent - */ - -#pragma once - -#include -#include -#include - -@[if ros2_distro]@ -#include "Timesync_Publisher.h" -#include "TimesyncStatus_Publisher.h" - -#include -#include -#include -@[else]@ -#include "timesync_Publisher.h" -#include "timesync_status_Publisher.h" -@[end if]@ - -static constexpr double ALPHA_INITIAL = 0.05; -static constexpr double ALPHA_FINAL = 0.003; -static constexpr double BETA_INITIAL = 0.05; -static constexpr double BETA_FINAL = 0.003; -static constexpr int WINDOW_SIZE = 500; -static constexpr int64_t UNKNOWN = 0; -static constexpr int64_t TRIGGER_RESET_THRESHOLD_NS = 100ll * 1000ll * 1000ll; -static constexpr int REQUEST_RESET_COUNTER_THRESHOLD = 5; - -@# Sets the timesync DDS type according to the FastRTPS and ROS2 version -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -@[ if ros2_distro]@ -using timesync_msg_t = @(package)::msg::dds_::Timesync_; -using timesync_status_msg_t = @(package)::msg::dds_::TimesyncStatus_; -@[ else]@ -using timesync_msg_t = timesync_; -using timesync_status_msg_t = timesync_status_; -@[ end if]@ -@[else]@ -@[ if ros2_distro]@ -using timesync_msg_t = @(package)::msg::Timesync; -using timesync_status_msg_t = @(package)::msg::TimesyncStatus; -@[ else]@ -using timesync_msg_t = timesync; -using timesync_status_msg_t = timesync_status; -@[ end if]@ -@[end if]@ -@# Sets the timesync publisher entity depending on using ROS2 or not -@[if ros2_distro]@ -using TimesyncPublisher = Timesync_Publisher; -using TimesyncStatusPublisher = TimesyncStatus_Publisher; -@[else]@ -using TimesyncPublisher = timesync_Publisher; -using TimesyncStatusPublisher = timesync_status_Publisher; -@[end if]@ - -class TimeSync -{ -public: - TimeSync(bool debug); - virtual ~TimeSync(); - - /** - * @@brief Starts the timesync publishing thread - * @@param[in] pub The timesync publisher entity to use - */ - void start(TimesyncPublisher *pub); - - /** - * @@brief Init and run the timesync status publisher thread - * @@param[in] pub The timesync status publisher entity to use - */ - void init_status_pub(TimesyncStatusPublisher *status_pub); - - /** - * @@brief Resets the filter - */ - void reset(); - - /** - * @@brief Stops the timesync publishing thread - */ - void stop(); - -@[if ros2_distro]@ - /** - * @@brief Get ROS time in nanoseconds. This will match the system time, which - * corresponds to the system-wide real time since epoch. If use_sim_time - * is set, the simulation time is grabbed by the node and used instead - * More info about ROS2 clock and time in: - * https://design.ros2.org/articles/clock_and_time.html - * @@return ROS time in nanoseconds - */ - uint64_t getROSTimeNSec() const; - - /** - * @@brief Get ROS time in microseconds. Fetches the time from getROSTimeNSec() - * and converts it to microseconds - * @@return ROS time in microseconds - */ - uint64_t getROSTimeUSec() const; -@[else]@ - /** - * @@brief Get clock monotonic time (raw) in nanoseconds - * @@return Steady CLOCK_MONOTONIC time in nanoseconds - */ - uint64_t getSteadyTimeNSec() const; - - /** - * @@brief Get clock monotonic time (raw) in microseconds - * @@return Steady CLOCK_MONOTONIC time in microseconds - */ - uint64_t getSteadyTimeUSec() const; -@[end if]@ - - /** - * @@brief Adds a time offset measurement to be filtered - * @@param[in] local_t1_ns The agent CLOCK_MONOTONIC_RAW time in nanoseconds when the message was sent - * @@param[in] remote_t2_ns The (client) remote CLOCK_MONOTONIC time in nanoseconds - * @@param[in] local_t3_ns The agent current CLOCK_MONOTONIC time in nanoseconds - * @@return true or false depending if the time offset was updated - */ - bool addMeasurement(int64_t local_t1_ns, int64_t remote_t2_ns, int64_t local_t3_ns); - - /** - * @@brief Processes DDS timesync message - * @@param[in,out] msg The timestamp msg to be processed - */ - void processTimesyncMsg(timesync_msg_t *msg, TimesyncPublisher *pub); - - /** - * @@brief Creates a new timesync DDS message to be sent from the agent to the client - * @@return A new timesync message with the origin in the agent and with the agent timestamp - */ - timesync_msg_t newTimesyncMsg(); - - /** - * @@brief Creates a new timesync status DDS message to be sent from the agent to the client - * @@return A new timesync status message with the origin in the agent and with the agent timestamp - */ - timesync_status_msg_t newTimesyncStatusMsg(); - - /** - * @@brief Get the time sync offset in nanoseconds - * @@return The offset in nanoseconds - */ - inline int64_t getOffset() { return _offset_ns.load(); } - - /** - * @@brief Sums the time sync offset to the timestamp - * @@param[in,out] timestamp The timestamp to add the offset to - */ - inline void addOffset(uint64_t ×tamp) { timestamp = (timestamp * 1000LL + _offset_ns.load()) / 1000ULL; } - - /** - * @@brief Substracts the time sync offset to the timestamp - * @@param[in,out] timestamp The timestamp to subtract the offset of - */ - inline void subtractOffset(uint64_t ×tamp) { timestamp = (timestamp * 1000LL - _offset_ns.load()) / 1000ULL; } - -private: - std::atomic _offset_ns; - std::atomic _offset_prev; - std::atomic _remote_time_stamp; - std::atomic _rtti; - -@[if ros2_distro]@ - /** - * @@brief A ROS2 node to fetch the ROS time to be used for timesync - */ - std::shared_ptr _timesync_node; -@[end if]@ - - int64_t _skew_ns_per_sync; - int64_t _num_samples; - - int32_t _request_reset_counter; - uint8_t _last_msg_seq; - uint8_t _last_remote_msg_seq; - - bool _debug; - - std::unique_ptr _send_timesync_thread; - std::unique_ptr _send_timesync_status_thread; -@[if ros2_distro]@ - std::unique_ptr _timesync_node_thread; -@[end if]@ - std::atomic _request_stop{false}; - - /** - * @@brief Updates the offset of the time sync filter - * @@param[in] offset The value of the offset to update to - */ - inline void updateOffset(const uint64_t &offset) { _offset_ns.store(offset, std::memory_order_relaxed); } - - /** Timesync msg Getters **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - inline uint64_t getMsgTimestamp(const timesync_msg_t *msg) { return msg->timestamp_(); } - inline uint8_t getMsgSeq(const timesync_msg_t *msg) { return msg->seq_(); } - inline int64_t getMsgTC1(const timesync_msg_t *msg) { return msg->tc1_(); } - inline int64_t getMsgTS1(const timesync_msg_t *msg) { return msg->ts1_(); } -@[elif ros2_distro]@ - inline uint64_t getMsgTimestamp(const timesync_msg_t *msg) { return msg->timestamp(); } - inline uint8_t getMsgSeq(const timesync_msg_t *msg) { return msg->seq(); } - inline int64_t getMsgTC1(const timesync_msg_t *msg) { return msg->tc1(); } - inline int64_t getMsgTS1(const timesync_msg_t *msg) { return msg->ts1(); } - @[end if]@ - - /** Common timestamp setter **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - template - inline void setMsgTimestamp(T *msg, const uint64_t ×tamp) { msg->timestamp_() = timestamp; } -@[elif ros2_distro]@ - template - inline void setMsgTimestamp(T *msg, const uint64_t ×tamp) { msg->timestamp() = timestamp; } -@[end if]@ - - /** Timesync msg Setters **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - inline void setMsgSeq(timesync_msg_t *msg, const uint8_t &seq) { msg->seq_() = seq; } - inline void setMsgTC1(timesync_msg_t *msg, const int64_t &tc1) { msg->tc1_() = tc1; } - inline void setMsgTS1(timesync_msg_t *msg, const int64_t &ts1) { msg->ts1_() = ts1; } -@[elif ros2_distro]@ - inline void setMsgSeq(timesync_msg_t *msg, const uint8_t &seq) { msg->seq() = seq; } - inline void setMsgTC1(timesync_msg_t *msg, const int64_t &tc1) { msg->tc1() = tc1; } - inline void setMsgTS1(timesync_msg_t *msg, const int64_t &ts1) { msg->ts1() = ts1; } -@[end if]@ - - /** Timesync Status msg Setters **/ -@[if version.parse(fastrtps_version) <= version.parse('1.7.2') or not ros2_distro]@ - inline void setMsgSourceProtocol(timesync_status_msg_t *msg, const uint8_t &source_protocol) { msg->source_protocol_() = source_protocol; } - inline void setMsgRemoteTimeStamp(timesync_status_msg_t *msg, const uint64_t &remote_timestamp) { msg->remote_timestamp_() = remote_timestamp; } - inline void setMsgObservedOffset(timesync_status_msg_t *msg, const int64_t &observed_offset) { msg->observed_offset_() = observed_offset; } - inline void setMsgEstimatedOffset(timesync_status_msg_t *msg, const int64_t &estimated_offset) { msg->estimated_offset_() = estimated_offset; } - inline void setMsgRoundTripTime(timesync_status_msg_t *msg, const uint32_t &round_trip_time) { msg->round_trip_time_() = round_trip_time; } -@[elif ros2_distro]@ - inline void setMsgSourceProtocol(timesync_status_msg_t *msg, const uint8_t &source_protocol) { msg->source_protocol() = source_protocol; } - inline void setMsgRemoteTimeStamp(timesync_status_msg_t *msg, const uint64_t &remote_timestamp) { msg->remote_timestamp() = remote_timestamp; } - inline void setMsgObservedOffset(timesync_status_msg_t *msg, const int64_t &observed_offset) { msg->observed_offset() = observed_offset; } - inline void setMsgEstimatedOffset(timesync_status_msg_t *msg, const int64_t &estimated_offset) { msg->estimated_offset() = estimated_offset; } - inline void setMsgRoundTripTime(timesync_status_msg_t *msg, const uint32_t &round_trip_time) { msg->round_trip_time() = round_trip_time; } -@[end if]@ -}; diff --git a/msg/templates/urtps/microRTPS_transport.cpp b/msg/templates/urtps/microRTPS_transport.cpp deleted file mode 100644 index 9252d26eb9..0000000000 --- a/msg/templates/urtps/microRTPS_transport.cpp +++ /dev/null @@ -1,801 +0,0 @@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#if __has_include("px4_platform_common/log.h") && __has_include("px4_platform_common/time.h") -#include -#include -#endif - -#if defined(__linux__) || defined(__PX4_LINUX) -#include -#endif /* __linux__ */ - -#include "microRTPS_transport.h" - - -/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ -uint16_t const crc16_table[256] = { - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 -}; - -Transport_node::Transport_node(const uint8_t sys_id, const bool debug): - _rx_buff_pos(0), - _debug(debug), - _sys_id(sys_id) -{ -} - -Transport_node::~Transport_node() -{ -} - -uint16_t Transport_node::crc16_byte(uint16_t crc, const uint8_t data) -{ - return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; -} - -uint16_t Transport_node::crc16(uint8_t const *buffer, size_t len) -{ - uint16_t crc = 0; - - while (len--) { - crc = crc16_byte(crc, *buffer++); - } - - return crc; -} - -ssize_t Transport_node::read(uint8_t *topic_id, char out_buffer[], size_t buffer_len) -{ - if (nullptr == out_buffer || nullptr == topic_id || !fds_OK()) { - return -1; - } - - *topic_id = 255; - - ssize_t len = node_read((void *)(_rx_buffer + _rx_buff_pos), sizeof(_rx_buffer) - _rx_buff_pos); - - if (len < 0) { - int errsv = errno; - - if (errsv && EAGAIN != errsv && ETIMEDOUT != errsv) { -#ifndef PX4_DEBUG - - if (_debug) { printf("\033[0;31m[ micrortps_transport ]\tRead fail %d\033[0m\n", errsv); } - -#else - - if (_debug) { PX4_DEBUG("Read fail %d", errsv); } - -#endif /* PX4_DEBUG */ - } - - return len; - } - - _rx_buff_pos += len; - - // We read some - size_t header_size = sizeof(struct Header); - - // but not enough - if (_rx_buff_pos < header_size) { - return 0; - } - - uint32_t msg_start_pos = 0; - - for (msg_start_pos = 0; msg_start_pos <= _rx_buff_pos - header_size; ++msg_start_pos) { - if ('>' == _rx_buffer[msg_start_pos] && memcmp(_rx_buffer + msg_start_pos, ">>>", 3) == 0) { - break; - } - } - - // Start not found - if (msg_start_pos > (_rx_buff_pos - header_size)) { -#ifndef PX4_DEBUG - - if (_debug) { printf("\033[1;33m[ micrortps_transport ]\t (↓↓ %" PRIu32 ")\033[0m\n", msg_start_pos); } - -#else - - if (_debug) { PX4_DEBUG(" (↓↓ %" PRIu32 ")", msg_start_pos); } - -#endif /* PX4_DEBUG */ - - // All we've checked so far is garbage, drop it - but save unchecked bytes - memmove(_rx_buffer, _rx_buffer + msg_start_pos, _rx_buff_pos - msg_start_pos); - _rx_buff_pos -= msg_start_pos; - return -1; - } - - // [>,>,>,topic_id,sys_id,seq,payload_length_H,payload_length_L,CRCHigh,CRCLow,payloadStart, ... ,payloadEnd] - struct Header *header = (struct Header *)&_rx_buffer[msg_start_pos]; - uint32_t payload_len = ((uint32_t)header->payload_len_h << 8) | header->payload_len_l; - - // The received message comes from this system. Discard it. - // This might happen when: - // 1. The same UDP port is being used to send a rcv packets or - // 2. The same topic on the agent is being used for outgoing and incoming data - if (header->sys_id == _sys_id) { - // Drop the message and continue with the read buffer - memmove(_rx_buffer, _rx_buffer + msg_start_pos + 1, _rx_buff_pos - (msg_start_pos + 1)); - _rx_buff_pos -= (msg_start_pos + 1); - return -1; - } - - // The message won't fit the buffer. - if (buffer_len < header_size + payload_len) { - // Drop the message and continue with the read buffer - memmove(_rx_buffer, _rx_buffer + msg_start_pos + 1, _rx_buff_pos - (msg_start_pos + 1)); - _rx_buff_pos -= (msg_start_pos + 1); - return -EMSGSIZE; - } - - // We do not have a complete message yet - if (msg_start_pos + header_size + payload_len > _rx_buff_pos) { - // If there's garbage at the beginning, drop it - if (msg_start_pos > 0) { -#ifndef PX4_DEBUG - - if (_debug) { printf("\033[1;33m[ micrortps_transport ]\t (↓ %" PRIu32 ")\033[0m\n", msg_start_pos); } - -#else - - if (_debug) { PX4_DEBUG(" (↓ %" PRIu32 ")", msg_start_pos); } - -#endif /* PX4_DEBUG */ - memmove(_rx_buffer, _rx_buffer + msg_start_pos, _rx_buff_pos - msg_start_pos); - _rx_buff_pos -= msg_start_pos; - } - - return 0; - } - - uint16_t read_crc = ((uint16_t)header->crc_h << 8) | header->crc_l; - uint16_t calc_crc = crc16((uint8_t *)_rx_buffer + msg_start_pos + header_size, payload_len); - - if (read_crc != calc_crc) { -#ifndef PX4_DEBUG - - if (_debug) { printf("\033[0;31m[ micrortps_transport ]\tBad CRC %" PRIu16 " != %" PRIu16 "\t\t(↓ %lu)\033[0m\n", read_crc, calc_crc, (unsigned long)(header_size + payload_len)); } - -#else - - if (_debug) { PX4_DEBUG("Bad CRC %u != %u\t\t(↓ %lu)", read_crc, calc_crc, (unsigned long)(header_size + payload_len)); } - -#endif /* PX4_DEBUG */ - - // Drop garbage up just beyond the start of the message - memmove(_rx_buffer, _rx_buffer + (msg_start_pos + 1), _rx_buff_pos); - - // If there is a CRC error, the payload len cannot be trusted - _rx_buff_pos -= (msg_start_pos + 1); - - len = -1; - - } else { - // copy message to outbuffer and set other return values - memmove(out_buffer, _rx_buffer + msg_start_pos + header_size, payload_len); - *topic_id = header->topic_id; - len = payload_len + header_size; - - // discard message from _rx_buffer - _rx_buff_pos -= msg_start_pos + header_size + payload_len; - memmove(_rx_buffer, _rx_buffer + msg_start_pos + header_size + payload_len, _rx_buff_pos); - } - - return len; -} - -size_t Transport_node::get_header_length() -{ - return sizeof(struct Header); -} - -ssize_t Transport_node::write(const uint8_t topic_id, char buffer[], size_t length) -{ - if (!fds_OK()) { - return -1; - } - - static struct Header header = {{'>', '>', '>'}, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; - - // [>,>,>,topic_id,seq,payload_length,CRCHigh,CRCLow,payload_start, ... ,payload_end] - uint16_t crc = crc16((uint8_t *)&buffer[sizeof(header)], length); - - header.topic_id = topic_id; - header.sys_id = _sys_id; - header.seq = _seq_number++; - header.payload_len_h = (length >> 8) & 0xff; - header.payload_len_l = length & 0xff; - header.crc_h = (crc >> 8) & 0xff; - header.crc_l = crc & 0xff; - - /* Headroom for header is created in client */ - /* Fill in the header in the same payload buffer to call a single node_write */ - memcpy(buffer, &header, sizeof(header)); - ssize_t len = node_write(buffer, length + sizeof(header)); - - if (len != ssize_t(length + sizeof(header))) { - return len; - } - - return len + sizeof(header); -} - -UART_node::UART_node(const char *uart_name, const uint32_t baudrate, - const uint32_t poll_ms, const bool hw_flow_control, - const bool sw_flow_control, const uint8_t sys_id, - const bool debug): - Transport_node(sys_id, debug), - _uart_fd(-1), - _baudrate(baudrate), - _poll_ms(poll_ms), - _hw_flow_control(hw_flow_control), - _sw_flow_control(sw_flow_control) -{ - - if (nullptr != uart_name) { - strcpy(_uart_name, uart_name); - } -} - -UART_node::~UART_node() -{ - close(); -} - -int UART_node::init() -{ - // Open a serial port - _uart_fd = open(_uart_name, O_RDWR | O_NOCTTY | O_NONBLOCK); - - if (_uart_fd < 0) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUART transport: Failed to open device: %s (%d)\033[0m\n", _uart_name, errno); -#else - PX4_ERR("UART transport: Failed to open device: %s (%d)", _uart_name, errno); -#endif /* PX4_ERR */ - return -errno; - } - - // If using shared UART, no need to set it up - if (_baudrate == 0) { - _poll_fd[0].fd = _uart_fd; - _poll_fd[0].events = POLLIN; - return _uart_fd; - } - - // Try to set baud rate - struct termios uart_config; - int termios_state; - - // Back up the original uart configuration to restore it after exit - if ((termios_state = tcgetattr(_uart_fd, &uart_config)) < 0) { - int errno_bkp = errno; -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUART transport: ERR GET CONF %s: %d (%d)\n\033[0m", _uart_name, termios_state, - errno); -#else - PX4_ERR("UART transport: ERR GET CONF %s: %d (%d)", _uart_name, termios_state, errno); -#endif /* PX4_ERR */ - close(); - return -errno_bkp; - } - -#if defined(__linux__) || defined(__PX4_LINUX) - uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); - uart_config.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST); - - uart_config.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | ECHONL | ICANON | IEXTEN | ISIG); - - // never send SIGTTOU - uart_config.c_lflag &= ~(TOSTOP); - - // ignore modem control lines - uart_config.c_cflag |= CLOCAL; - - // 8 bits - uart_config.c_cflag |= CS8; -#else /* __linux__ */ - - // Clear ONLCR flag (which appends a CR for every LF) - uart_config.c_oflag &= ~ONLCR; -#endif - - // Flow control - if (_hw_flow_control) { - // HW flow control - uart_config.c_cflag |= CRTSCTS; - uart_config.c_iflag &= ~(IXON | IXOFF | IXANY); - } else if (_sw_flow_control) { - // SW flow control - uart_config.c_cflag &= ~CRTSCTS; - uart_config.c_lflag |= (IXON | IXOFF | IXANY); - } else { - uart_config.c_cflag &= ~CRTSCTS; - uart_config.c_iflag &= ~(IXON | IXOFF | IXANY); - } - - // Set baud rate - speed_t speed; - - if (!baudrate_to_speed(_baudrate, &speed)) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUART transport: ERR SET BAUD %s: Unsupported _baudrate: %d\n\tsupported examples:\n\t9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 921600, 1000000\033[0m\n", - _uart_name, _baudrate); -#else - PX4_ERR("UART transport: ERR SET BAUD %s: Unsupported baudrate: %" PRIu32 "\n\tsupported examples:\n\t9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 921600, 1000000\n", - _uart_name, _baudrate); -#endif /* PX4_ERR */ - close(); - return -EINVAL; - } - - if (cfsetispeed(&uart_config, speed) < 0 || cfsetospeed(&uart_config, speed) < 0) { - int errno_bkp = errno; -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUART transport: ERR SET BAUD %s: %d (%d)\033[0m\n", _uart_name, termios_state, - errno); -#else - PX4_ERR("ERR SET BAUD %s: %d (%d)", _uart_name, termios_state, errno); -#endif /* PX4_ERR */ - close(); - return -errno_bkp; - } - - if ((termios_state = tcsetattr(_uart_fd, TCSANOW, &uart_config)) < 0) { - int errno_bkp = errno; -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUART transport: ERR SET CONF %s (%d)\033[0m\n", _uart_name, errno); -#else - PX4_ERR("UART transport: ERR SET CONF %s (%d)", _uart_name, errno); -#endif /* PX4_ERR */ - close(); - return -errno_bkp; - } - -#if defined(__linux__) || defined(__PX4_LINUX) - // For Linux, set high speed polling at the chip level. Since this routine relies on a USB latency - // change at the chip level it may fail on certain chip sets if their driver does not support this - // configuration request - { - struct serial_struct serial_ctl; - - if (ioctl(_uart_fd, TIOCGSERIAL, &serial_ctl) < 0) { - printf("\033[0;31m[ micrortps_transport ]\tError while trying to read serial port configuration: %d\033[0m\n", errno); - - if (ioctl(_uart_fd, TCFLSH, TCIOFLUSH) == -1) { - int errno_bkp = errno; - printf("\033[0;31m[ protocol__splitter ]\tCould not flush terminal\033[0m\n"); - close(); - return -errno_bkp; - } - } - - serial_ctl.flags |= ASYNC_LOW_LATENCY; - - if (ioctl(_uart_fd, TIOCSSERIAL, &serial_ctl) < 0) { - int errno_bkp = errno; - printf("\033[0;31m[ micrortps_transport ]\tError while trying to write serial port latency: %d\033[0m\n", errno); - close(); - return -errno_bkp; - } - } -#endif /* __linux__ */ - - char aux[64]; - bool flush = false; - - while (0 < ::read(_uart_fd, (void *)&aux, 64)) { - flush = true; -#ifndef px4_usleep - usleep(1000); -#else - /* With PX4 px4_usleep() should be used. */ - px4_usleep(1000); -#endif /* px4_usleep */ - } - - if (flush) { -#ifndef PX4_DEBUG - - if (_debug) { printf("[ micrortps_transport ]\tUART transport: Flush\n"); } - -#else - - if (_debug) { PX4_DEBUG("UART transport: Flush"); } - -#endif /* PX4_DEBUG */ - - } else { -#ifndef PX4_DEBUG - - if (_debug) { printf("[ micrortps_transport ]\tUART transport: No flush\n"); } - -#else - - if (_debug) { PX4_DEBUG("UART transport: No flush"); } - -#endif /* PX4_INFO */ - } - - _poll_fd[0].fd = _uart_fd; - _poll_fd[0].events = POLLIN; - - return _uart_fd; -} - -bool UART_node::fds_OK() -{ - return (-1 != _uart_fd); -} - -uint8_t UART_node::close() -{ - if (-1 != _uart_fd) { -#ifndef PX4_WARN - printf("\033[1;33m[ micrortps_transport ]\tClosed UART.\n\033[0m"); -#else - PX4_WARN("Closed UART."); -#endif /* PX4_WARN */ - ::close(_uart_fd); - _uart_fd = -1; - memset(&_poll_fd, 0, sizeof(_poll_fd)); - } - - return 0; -} - -ssize_t UART_node::node_read(void *buffer, size_t len) -{ - if (nullptr == buffer || !fds_OK()) { - return -1; - } - - ssize_t ret = 0; - int r = poll(_poll_fd, 1, _poll_ms); - - if (r == 1 && (_poll_fd[0].revents & POLLIN)) { - ret = ::read(_uart_fd, buffer, len); - } - - return ret; -} - -ssize_t UART_node::node_write(void *buffer, size_t len) -{ - if (nullptr == buffer || !fds_OK()) { - return -1; - } - - return ::write(_uart_fd, buffer, len); -} - -bool UART_node::baudrate_to_speed(uint32_t bauds, speed_t *speed) -{ -#ifndef B460800 -#define B460800 460800 -#endif - -#ifndef B500000 -#define B500000 500000 -#endif - -#ifndef B921600 -#define B921600 921600 -#endif - -#ifndef B1000000 -#define B1000000 1000000 -#endif - -#ifndef B1500000 -#define B1500000 1500000 -#endif - -#ifndef B2000000 -#define B2000000 2000000 -#endif - - switch (bauds) { - case 0: *speed = B0; break; - - case 50: *speed = B50; break; - - case 75: *speed = B75; break; - - case 110: *speed = B110; break; - - case 134: *speed = B134; break; - - case 150: *speed = B150; break; - - case 200: *speed = B200; break; - - case 300: *speed = B300; break; - - case 600: *speed = B600; break; - - case 1200: *speed = B1200; break; - - case 1800: *speed = B1800; break; - - case 2400: *speed = B2400; break; - - case 4800: *speed = B4800; break; - - case 9600: *speed = B9600; break; - - case 19200: *speed = B19200; break; - - case 38400: *speed = B38400; break; - - case 57600: *speed = B57600; break; - - case 115200: *speed = B115200; break; - - case 230400: *speed = B230400; break; - - case 460800: *speed = B460800; break; - - case 500000: *speed = B500000; break; - - case 921600: *speed = B921600; break; - - case 1000000: *speed = B1000000; break; - - case 1500000: *speed = B1500000; break; - - case 2000000: *speed = B2000000; break; - -#ifdef B3000000 - case 3000000: *speed = B3000000; break; - -#endif -#ifdef B3500000 - case 3500000: *speed = B3500000; break; - -#endif -#ifdef B4000000 - case 4000000: *speed = B4000000; break; - -#endif - default: - return false; - } - - return true; -} - -UDP_node::UDP_node(const char *udp_ip, uint16_t udp_port_recv, - uint16_t udp_port_send, const uint8_t sys_id, const bool debug): - Transport_node(sys_id, debug), - _sender_fd(-1), - _receiver_fd(-1), - _udp_port_recv(udp_port_recv), - _udp_port_send(udp_port_send) -{ - if (nullptr != udp_ip) { - strcpy(_udp_ip, udp_ip); - } -} - -UDP_node::~UDP_node() -{ - close(); -} - -int UDP_node::init() -{ - if (0 > init_receiver(_udp_port_recv) || 0 > init_sender(_udp_port_send)) { - return -1; - } - - return 0; -} - -bool UDP_node::fds_OK() -{ - return (-1 != _sender_fd && -1 != _receiver_fd); -} - -int UDP_node::init_receiver(uint16_t udp_port) -{ -#if !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) - // udp socket data - memset((char *)&_receiver_inaddr, 0, sizeof(_receiver_inaddr)); - _receiver_inaddr.sin_family = AF_INET; - _receiver_inaddr.sin_port = htons(udp_port); - _receiver_inaddr.sin_addr.s_addr = htonl(INADDR_ANY); - - if ((_receiver_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUDP transport: Create socket failed\033[0m\n"); -#else - PX4_ERR("UDP transport: Create socket failed"); -#endif /* PX4_ERR */ - return -1; - } - -#ifndef PX4_INFO - printf("[ micrortps_transport ]\tUDP transport: Trying to connect...\n"); -#else - PX4_INFO("UDP transport: Trying to connect..."); -#endif /* PX4_INFO */ - - if (bind(_receiver_fd, (struct sockaddr *)&_receiver_inaddr, sizeof(_receiver_inaddr)) < 0) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUDP transport: Bind failed\033[0m\n"); -#else - PX4_ERR("UDP transport: Bind failed"); -#endif /* PX4_ERR */ - return -1; - } - -#ifndef PX4_INFO - printf("[ micrortps_transport ]\tUDP transport: Connected to server!\n\n"); -#else - PX4_INFO("UDP transport: Connected to server!"); -#endif /* PX4_INFO */ -#endif /* __PX4_NUTTX */ - return 0; -} - -int UDP_node::init_sender(uint16_t udp_port) -{ -#if !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) - - if ((_sender_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUDP transport: Create socket failed\033[0m\n"); -#else - PX4_ERR("UDP transport: Create socket failed"); -#endif /* PX4_ERR */ - return -1; - } - - memset((char *) &_sender_outaddr, 0, sizeof(_sender_outaddr)); - _sender_outaddr.sin_family = AF_INET; - _sender_outaddr.sin_port = htons(udp_port); - - if (inet_aton(_udp_ip, &_sender_outaddr.sin_addr) == 0) { -#ifndef PX4_ERR - printf("\033[0;31m[ micrortps_transport ]\tUDP transport: inet_aton() failed\033[0m\n"); -#else - PX4_ERR("UDP transport: inet_aton() failed"); -#endif /* PX4_ERR */ - return -1; - } - -#endif /* __PX4_NUTTX */ - - return 0; -} - -uint8_t UDP_node::close() -{ -#if !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) - - if (_sender_fd != -1) { -#ifndef PX4_WARN - printf("\033[1;33m[ micrortps_transport ]\tUDP transport: Closed sender socket!\033[0m\n"); -#else - PX4_WARN("UDP transport: Closed sender socket!"); -#endif /* PX4_WARN */ - shutdown(_sender_fd, SHUT_RDWR); - ::close(_sender_fd); - _sender_fd = -1; - } - - if (_receiver_fd != -1) { -#ifndef PX4_WARN - printf("\033[1;33m[ micrortps_transport ]\tUDP transport: Closed receiver socket!\033[0m\n"); -#else - PX4_WARN("UDP transport: Closed receiver socket!"); -#endif /* PX4_WARN */ - shutdown(_receiver_fd, SHUT_RDWR); - ::close(_receiver_fd); - _receiver_fd = -1; - } - -#endif /* __PX4_NUTTX */ - return 0; -} - -ssize_t UDP_node::node_read(void *buffer, size_t len) -{ - if (nullptr == buffer || !fds_OK()) { - return -1; - } - - ssize_t ret = 0; -#if !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) - // Blocking call - static socklen_t addrlen = sizeof(_receiver_outaddr); - ret = recvfrom(_receiver_fd, buffer, len, 0, (struct sockaddr *)&_receiver_outaddr, &addrlen); -#endif /* !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) */ - return ret; -} - -ssize_t UDP_node::node_write(void *buffer, size_t len) -{ - if (nullptr == buffer || !fds_OK()) { - return -1; - } - - ssize_t ret = 0; -#if !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) - ret = sendto(_sender_fd, buffer, len, 0, (struct sockaddr *)&_sender_outaddr, sizeof(_sender_outaddr)); -#endif /* !defined (__PX4_NUTTX) || (defined (CONFIG_NET) && defined (__PX4_NUTTX)) */ - return ret; -} diff --git a/msg/templates/urtps/microRTPS_transport.h b/msg/templates/urtps/microRTPS_transport.h deleted file mode 100644 index 3605b63f8c..0000000000 --- a/msg/templates/urtps/microRTPS_transport.h +++ /dev/null @@ -1,158 +0,0 @@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#pragma once - -#include -#include -#include -#include - -#define BUFFER_SIZE 1024 -#define DEFAULT_UART "/dev/ttyACM0" - -namespace MicroRtps { - enum class System { - FMU, - MISSION_COMPUTER - }; -} - -class Transport_node -{ -public: - Transport_node(const uint8_t sys_id, const bool debug); - virtual ~Transport_node(); - - virtual int init() {return 0;} - virtual uint8_t close() {return 0;} - ssize_t read(uint8_t *topic_id, char out_buffer[], size_t buffer_len); - - /** - * write a buffer - * @param topic_id - * @param buffer buffer to write: it must leave get_header_length() bytes free at the beginning. This will be - * filled with the header. length does not include get_header_length(). So buffer looks like this: - * ------------------------------------------------- - * | header (leave free) | payload data | - * | get_header_length() bytes | length bytes | - * ------------------------------------------------- - * @param length buffer length excluding header length - * @return length on success, <0 on error - */ - ssize_t write(const uint8_t topic_id, char buffer[], size_t length); - - /** Get the Length of struct Header to make headroom for the size of struct Header along with payload */ - size_t get_header_length(); - -private: - struct __attribute__((packed)) Header { - char marker[3]; - uint8_t topic_id; - uint8_t sys_id; - uint8_t seq; - uint8_t payload_len_h; - uint8_t payload_len_l; - uint8_t crc_h; - uint8_t crc_l; - }; - -protected: - virtual ssize_t node_read(void *buffer, size_t len) = 0; - virtual ssize_t node_write(void *buffer, size_t len) = 0; - virtual bool fds_OK() = 0; - uint16_t crc16_byte(uint16_t crc, const uint8_t data); - uint16_t crc16(uint8_t const *buffer, size_t len); - - uint32_t _rx_buff_pos; - char _rx_buffer[BUFFER_SIZE]{}; - - bool _debug; - - uint8_t _sys_id; - uint8_t _seq_number{0}; -}; - -class UART_node: public Transport_node -{ -public: - UART_node(const char *uart_name, const uint32_t baudrate, - const uint32_t poll_ms, const bool hw_flow_control, - const bool sw_flow_control, const uint8_t sys_id, - const bool debug); - virtual ~UART_node(); - - int init(); - uint8_t close(); - -protected: - ssize_t node_read(void *buffer, size_t len); - ssize_t node_write(void *buffer, size_t len); - bool fds_OK(); - bool baudrate_to_speed(uint32_t bauds, speed_t *speed); - - int _uart_fd; - char _uart_name[64]{}; - uint32_t _baudrate; - uint32_t _poll_ms; - bool _hw_flow_control{false}; - bool _sw_flow_control{false}; - struct pollfd _poll_fd[1]{}; -}; - -class UDP_node: public Transport_node -{ -public: - UDP_node(const char *udp_ip, uint16_t udp_port_recv, uint16_t udp_port_send, - const uint8_t sys_id, const bool debug); - virtual ~UDP_node(); - - int init(); - uint8_t close(); - -protected: - int init_receiver(uint16_t udp_port); - int init_sender(uint16_t udp_port); - ssize_t node_read(void *buffer, size_t len); - ssize_t node_write(void *buffer, size_t len); - bool fds_OK(); - - int _sender_fd; - int _receiver_fd; - char _udp_ip[16]{}; - uint16_t _udp_port_recv; - uint16_t _udp_port_send; - struct sockaddr_in _sender_outaddr; - struct sockaddr_in _receiver_inaddr; - struct sockaddr_in _receiver_outaddr; -}; diff --git a/msg/templates/urtps/msg.idl.em b/msg/templates/urtps/msg.idl.em deleted file mode 100644 index 1fa20e3fe6..0000000000 --- a/msg/templates/urtps/msg.idl.em +++ /dev/null @@ -1,154 +0,0 @@ -@############################################### -@# -@# ROS message to IDL converter -@# -@# EmPy template for generating .idl files -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@################################################################################ -@# -@# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). -@# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. -@# -@# Redistribution and use in source and binary forms, with or without -@# modification, are permitted provided that the following conditions are met: -@# -@# 1. Redistributions of source code must retain the above copyright notice, this -@# list of conditions and the following disclaimer. -@# -@# 2. Redistributions in binary form must reproduce the above copyright notice, -@# this list of conditions and the following disclaimer in the documentation -@# and/or other materials provided with the distribution. -@# -@# 3. Neither the name of the copyright holder nor the names of its contributors -@# may be used to endorse or promote products derived from this software without -@# specific prior written permission. -@# -@# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -@# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -@# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -@# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -@# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -@# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -@# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -@# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -@# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -@# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -@# POSSIBILITY OF SUCH DAMAGE. -@# -@################################################################################ -@{ -import genmsg.msgs -from packaging import version -from px_generate_uorb_topic_helper import * # this is in Tools/ - -builtin_types = set() -array_types = set() - -topic = alias if alias else spec.short_name -}@ -@################################################# -@# Searching for serialize function per each field -@################################################# -@{ - -def get_include_directives(spec): - include_directives = set() - for field in (spec.parsed_fields()): - if genmsg.msgs.is_valid_constant_type(genmsg.msgs.bare_msg_type(field.type)): - continue - builtin_type = str(field.base_type).replace('px4/', '') - if version.parse(fastrtps_version) <= version.parse('1.7.2'): - include_directive = '#include "%s_.idl"' % builtin_type - else: - include_directive = '#include "%s.idl"' % builtin_type - builtin_types.add(builtin_type) - include_directives.add(include_directive) - return sorted(include_directives) - - -def get_idl_type_name(field_type): - if field_type in type_idl_map: - return type_idl_map[field_type] - else: - (package, name) = genmsg.names.package_resource_name(field_type) - return name - - -def add_msg_field(field): - if (not field.is_header): - if field.is_array: - if version.parse(fastrtps_version) <= version.parse('1.7.2'): - print(' {0}__{1}_array_{2} {3}_;'.format(topic, str(get_idl_type_name(field.base_type)).replace(" ", "_"), str(field.array_len), field.name)) - else: - print(' {0}__{1}_array_{2} {3};'.format(topic, str(get_idl_type_name(field.base_type)).replace(" ", "_"), str(field.array_len), field.name)) - else: - if version.parse(fastrtps_version) <= version.parse('1.7.2'): - base_type = get_idl_type_name(field.base_type) + "_" if get_idl_type_name(field.base_type) in builtin_types else get_idl_type_name(field.base_type) - else: - base_type = get_idl_type_name(field.base_type) if get_idl_type_name(field.base_type) in builtin_types else get_idl_type_name(field.base_type) - print(' {0} {1}_;'.format(base_type, field.name)) - -def add_msg_fields(): - for field in spec.parsed_fields(): - add_msg_field(field) - - -def add_array_typedefs(): - for field in spec.parsed_fields(): - if not field.is_header and field.is_array: - if version.parse(fastrtps_version) <= version.parse('1.7.2'): - base_type = get_idl_type_name(field.base_type) + "_" if get_idl_type_name(field.base_type) in builtin_types else get_idl_type_name(field.base_type) - else: - base_type = get_idl_type_name(field.base_type) if get_idl_type_name(field.base_type) in builtin_types else get_idl_type_name(field.base_type) - array_type = 'typedef {0} {1}__{2}_array_{3}[{4}];'.format(base_type, topic, get_idl_type_name(field.base_type).replace(" ", "_"), field.array_len, field.array_len) - if array_type not in array_types: - array_types.add(array_type) - for atype in array_types: - print(atype) - - -def add_msg_constants(): - sorted_constants = sorted(spec.constants, - key=sizeof_field_type, reverse=True) - for constant in sorted_constants: - print('const {0} {1}__{2} = {3};'.format(get_idl_type_name(constant.type), topic, constant.name, constant.val)) - -} -#ifndef __@(topic)__idl__ -#define __@(topic)__idl__ - -@############################# -@# Include dependency messages -@############################# -@[for line in get_include_directives(spec)]@ -@(line)@ -@[end for]@ - - -@# Constants -@add_msg_constants() -@# Array types -@add_array_typedefs() -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -struct @(topic)_ -@[else]@ -struct @(topic) -@[end if]@ -{ -@add_msg_fields() -@[if version.parse(fastrtps_version) <= version.parse('1.7.2')]@ -}; // struct @(topic)_ - -#pragma keylist @(topic)_ -@[else]@ -}; // struct @(topic) - -#pragma keylist @(topic) -@[end if]@ - -#endif // __@(topic)__idl__ diff --git a/msg/tools/generate_microRTPS_bridge.py b/msg/tools/generate_microRTPS_bridge.py deleted file mode 100644 index 3162d635d1..0000000000 --- a/msg/tools/generate_microRTPS_bridge.py +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/env python3 -################################################################################ -# -# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). -# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -################################################################################ - -# This script can generate the client and agent code based on a set of topics -# to sent and set to receive. It uses fastrtpsgen to generate the code from the -# IDL for the topic messages. The PX4 msg definitions are used to create the IDL -# used by fastrtpsgen using templates. - -import sys -import os -import argparse -import shutil -import px_generate_uorb_topic_files -from uorb_rtps_classifier import Classifier -import subprocess -import glob -import errno -import re - -try: - from six.moves import input -except ImportError as e: - print("Failed to import six: " + e) - print("") - print("You may need to install it using:") - print(" pip3 install --user six") - print("") - sys.exit(1) - -try: - from packaging import version -except ImportError as e: - print("Failed to import packaging: " + str(e)) - print("") - print("You may need to install it using:") - print(" pip3 install --user packaging") - print("") - sys.exit(1) - - -default_client_out = "src/modules/micrortps_bridge/micrortps_client" -default_agent_out = "src/modules/micrortps_bridge/micrortps_agent" -default_uorb_templates_dir = "templates/uorb_microcdr" -default_urtps_templates_dir = "templates/urtps" -default_urtps_topics_file = "tools/urtps_bridge_topics.yaml" -default_package_name = px_generate_uorb_topic_files.PACKAGE - -parser = argparse.ArgumentParser() - -parser.add_argument("-a", "--agent", dest='agent', action="store_true", - help="Flag for generate the agent, by default is true if -c is not specified") -parser.add_argument("-c", "--client", dest='client', action="store_true", - help="Flag for generate the client, by default is true if -a is not specified") -parser.add_argument("-i", "--generate-idl", dest='gen_idl', - action="store_true", help="Flag for generate idl files for each msg") -parser.add_argument("-j", "--idl-dir", dest='idl_dir', - type=str, help="IDL files dir", default='') -parser.add_argument("-m", "--mkdir-build", dest='mkdir_build', - action="store_true", help="Flag to create 'build' dir") -parser.add_argument("-l", "--generate-cmakelists", dest='cmakelists', - action="store_true", help="Flag to generate a CMakeLists.txt file for the micro-RTPS agent") -parser.add_argument("-t", "--topic-msg-dir", dest='msgdir', type=str, - help="Topics message, by default using relative path 'msg/'", default="msg") -parser.add_argument("-b", "--uorb-templates-dir", dest='uorb_templates', type=str, - help="uORB templates, by default using relative path to msgdir 'templates/uorb_microcdr'", default=default_uorb_templates_dir) -parser.add_argument("-q", "--urtps-templates-dir", dest='urtps_templates', type=str, - help="uRTPS templates, by default using relative path to msgdir 'templates/urtps'", default=default_urtps_templates_dir) -parser.add_argument("-y", "--rtps-ids-file", dest='yaml_file', type=str, - help="Setup uRTPS bridge topics file path, by default using relative path to msgdir 'tools/urtps_bridge_topics.yaml'", default=default_urtps_topics_file) -parser.add_argument("-p", "--package", dest='package', type=str, - help="Msg package naming, by default px4", default=default_package_name) -parser.add_argument("-o", "--agent-outdir", dest='agentdir', type=str, - help="Agent output dir, by default using relative path 'src/modules/micrortps_bridge/micrortps_agent'", default=default_agent_out) -parser.add_argument("-u", "--client-outdir", dest='clientdir', type=str, - help="Client output dir, by default using relative path 'src/modules/micrortps_bridge/micrortps_client'", default=default_client_out) -parser.add_argument("-f", "--fastrtpsgen-dir", dest='fastrtpsgen', type=str, nargs='?', - help="fastrtpsgen installation dir, only needed if fastrtpsgen is not in PATH, by default empty", default="") -parser.add_argument("-g", "--fastrtpsgen-include", dest='fastrtpsgen_include', type=str, - help="directory(ies) to add to preprocessor include paths of fastrtpsgen, by default empty", default="") -parser.add_argument("-r", "--ros2-distro", dest='ros2_distro', type=str, nargs='?', - help="ROS2 distro, only required if generating the agent for usage with ROS2 nodes, by default empty", default="") -parser.add_argument("--delete-tree", dest='del_tree', - action="store_true", help="Delete dir tree output dir(s)") - - -if len(sys.argv) <= 1: - parser.print_usage() - exit(-1) - -# Parse arguments -args = parser.parse_args() -agent = args.agent -client = args.client -cmakelists = args.cmakelists -del_tree = args.del_tree -gen_idl = args.gen_idl -mkdir_build = args.mkdir_build -package = args.package - -# Msg files path -msg_dir = os.path.abspath(args.msgdir) -px_generate_uorb_topic_files.append_to_include_path( - {msg_dir}, px_generate_uorb_topic_files.INCL_DEFAULT, package) - -# Agent files output path -agent_out_dir = os.path.abspath(args.agentdir) - -# Client files output path -client_out_dir = os.path.abspath(args.clientdir) - -# IDL files path -idl_dir = args.idl_dir -if idl_dir != '': - idl_dir = os.path.abspath(args.idl_dir) -else: - idl_dir = os.path.join(agent_out_dir, "idl") - -if args.fastrtpsgen is None or args.fastrtpsgen == '': - # Assume fastrtpsgen is in PATH - fastrtpsgen_path = 'fastrtpsgen' - for dirname in os.environ['PATH'].split(':'): - candidate = os.path.join(dirname, 'fastrtpsgen') - if os.path.isfile(candidate): - fastrtpsgen_path = candidate -else: - # Path to fastrtpsgen is explicitly specified - if os.path.isdir(args.fastrtpsgen): - fastrtpsgen_path = os.path.join( - os.path.abspath(args.fastrtpsgen), 'fastrtpsgen') - else: - fastrtpsgen_path = args.fastrtpsgen - -fastrtpsgen_include = args.fastrtpsgen_include -if fastrtpsgen_include is not None and fastrtpsgen_include != '': - fastrtpsgen_include = "-I " + \ - os.path.abspath( - args.fastrtpsgen_include) + " " - -# get FastRTPSGen version -# .. note:: since Fast-RTPS 1.8.0 release, FastRTPSGen is a separated repository -# and not included in the Fast-RTPS project. -# The starting version since this separation is 1.0.0, which follows its own -# versioning -fastrtpsgen_version = version.Version("1.0.0") -if(os.path.exists(fastrtpsgen_path)): - try: - fastrtpsgen_version_out = subprocess.check_output( - [fastrtpsgen_path, "-version"]).decode("utf-8").strip()[-5:] - except OSError: - raise - - try: - fastrtpsgen_version = version.parse(fastrtpsgen_version_out) - except version.InvalidVersion: - raise Exception( - "'fastrtpsgen -version' returned None or an invalid version") -else: - raise Exception( - "FastRTPSGen not found. Specify the location of fastrtpsgen with the -f flag") - - -# get ROS 2 version, if exists -ros2_distro = '' -ros_version = os.environ.get('ROS_VERSION') -if ros_version == '2': - if args.ros2_distro != '': - ros2_distro = args.ros2_distro - else: - ros2_distro = os.environ.get('ROS_DISTRO') - -# get FastRTPS version -fastrtps_version = '' -if not ros2_distro: - # grab the version installed system wise - fastrtps_version = subprocess.check_output( - "ldconfig -v 2>/dev/null | grep libfastrtps", shell=True).decode("utf-8").strip().split('so.')[-1] -else: - # grab the version of the ros--fastrtps package - fastrtps_version = re.search(r'Version:\s*([\dd.]+)', subprocess.check_output( - "dpkg -s ros-" + ros2_distro + "-fastrtps 2>/dev/null | grep -i version", shell=True).decode("utf-8").strip()).group(1) - - -# If nothing specified it's generated both -if agent == False and client == False: - agent = True - client = True - -if del_tree: - if agent: - _continue = str(input("\nFiles in " + agent_out_dir + - " will be erased, continue?[Y/n]\n")) - if _continue == "N" or _continue == "n": - print("Aborting execution...") - exit(-1) - else: - if agent and os.path.isdir(agent_out_dir): - shutil.rmtree(agent_out_dir) - - if client: - _continue = str(input( - "\nFiles in " + client_out_dir + " will be erased, continue?[Y/n]\n")) - if _continue.strip() in ("N", "n"): - print("Aborting execution...") - exit(-1) - else: - if client and os.path.isdir(client_out_dir): - shutil.rmtree(client_out_dir) - -if agent and os.path.isdir(os.path.join(agent_out_dir, "idl")): - shutil.rmtree(os.path.join(agent_out_dir, "idl")) - -# uORB templates path -uorb_templates_dir = (args.uorb_templates if os.path.isabs(args.uorb_templates) - else os.path.join(msg_dir, args.uorb_templates)) - -# uRTPS templates path -urtps_templates_dir = (args.urtps_templates if os.path.isabs(args.urtps_templates) - else os.path.join(msg_dir, args.urtps_templates)) - -# parse yaml file into a map of ids and messages to send and receive -classifier = (Classifier(os.path.abspath(args.yaml_file), msg_dir) if os.path.isabs(args.yaml_file) - else Classifier(os.path.join(msg_dir, args.yaml_file), msg_dir)) - - -uRTPS_CLIENT_TEMPL_FILE = 'microRTPS_client.cpp.em' -uRTPS_AGENT_TOPICS_H_TEMPL_FILE = 'RtpsTopics.h.em' -uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE = 'RtpsTopics.cpp.em' -uRTPS_AGENT_TEMPL_FILE = 'microRTPS_agent.cpp.em' -uRTPS_TIMESYNC_CPP_TEMPL_FILE = 'microRTPS_timesync.cpp.em' -uRTPS_TIMESYNC_H_TEMPL_FILE = 'microRTPS_timesync.h.em' -uRTPS_AGENT_CMAKELISTS_TEMPL_FILE = 'microRTPS_agent_CMakeLists.txt.em' -uRTPS_PUBLISHER_SRC_TEMPL_FILE = 'Publisher.cpp.em' -uRTPS_PUBLISHER_H_TEMPL_FILE = 'Publisher.h.em' -uRTPS_SUBSCRIBER_SRC_TEMPL_FILE = 'Subscriber.cpp.em' -uRTPS_SUBSCRIBER_H_TEMPL_FILE = 'Subscriber.h.em' - - -def generate_agent(out_dir): - global fastrtps_version - - if classifier.msgs_to_send: - for msg_file in classifier.msgs_to_send: - if gen_idl: - if out_dir != agent_out_dir: - px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", os.path.join(out_dir, "/idl"), urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - else: - px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", idl_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_SRC_TEMPL_FILE) - px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_H_TEMPL_FILE) - - if classifier.alias_msgs_to_send: - for msg_file in classifier.alias_msgs_to_send: - msg_alias = msg_file[0] - msg_name = msg_file[1] - if gen_idl: - if out_dir != agent_out_dir: - px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, os.path.join(out_dir, "/idl"), urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - else: - px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, idl_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_SRC_TEMPL_FILE) - px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_H_TEMPL_FILE) - - if classifier.msgs_to_receive: - for msg_file in classifier.msgs_to_receive: - if gen_idl: - if out_dir != agent_out_dir: - px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", os.path.join(out_dir, "/idl"), urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - else: - px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", idl_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE) - px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_H_TEMPL_FILE) - - if classifier.alias_msgs_to_receive: - for msg_file in classifier.alias_msgs_to_receive: - msg_alias = msg_file[0] - msg_name = msg_file[1] - if gen_idl: - if out_dir != agent_out_dir: - px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, os.path.join(out_dir, "/idl"), urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - else: - px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, idl_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list) - px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE) - px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir, - package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_H_TEMPL_FILE) - - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir, - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TEMPL_FILE) - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir, - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_TIMESYNC_CPP_TEMPL_FILE) - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir, - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_TIMESYNC_H_TEMPL_FILE) - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir, - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TOPICS_H_TEMPL_FILE) - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir, - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE) - if cmakelists: - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, os.path.dirname(out_dir), - urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_CMAKELISTS_TEMPL_FILE) - - # Final steps to install agent - mkdir_p(os.path.join(out_dir, "fastrtpsgen")) - prev_cwd_path = os.getcwd() - os.chdir(os.path.join(out_dir, "fastrtpsgen")) - if not glob.glob(os.path.join(idl_dir, "*.idl")): - raise Exception("No IDL files found in %s" % idl_dir) - - # If it is generating the bridge code for interfacing with ROS2, then set - # the '-typeros2' option in fastrtpsgen. - # .. note:: This is only available in FastRTPSGen 1.0.4 and above - gen_ros2_typename = "" - if ros2_distro and ros2_distro in ['dashing', 'eloquent', 'foxy', 'galactic', 'rolling'] and fastrtpsgen_version >= version.Version("1.0.4"): - gen_ros2_typename = "-typeros2 " - - idl_files = [] - for idl_file in glob.glob(os.path.join(idl_dir, "*.idl")): - # Only run the generator for the topics that are actually marked to be - # used by the bridge - if os.path.splitext(os.path.basename(idl_file))[0] in classifier.msg_list: - idl_files.append(idl_file) - - try: - ret = subprocess.check_call(fastrtpsgen_path + " -d " + out_dir + - "/fastrtpsgen -example x64Linux2.6gcc " + gen_ros2_typename + fastrtpsgen_include + " ".join(str(idl_file) for idl_file in idl_files), shell=True) - except OSError: - raise - - rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*PubSubMain*")) - rm_wildcard(os.path.join(out_dir, "fastrtpsgen/makefile*")) - rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*Publisher*")) - rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*Subscriber*")) - for f in glob.glob(os.path.join(out_dir, "fastrtpsgen/*.cxx")): - os.rename(f, f.replace(".cxx", ".cpp")) - cp_wildcard(os.path.join(out_dir, "fastrtpsgen/*"), out_dir) - if os.path.isdir(os.path.join(out_dir, "fastrtpsgen")): - shutil.rmtree(os.path.join(out_dir, "fastrtpsgen")) - cp_wildcard(os.path.join(urtps_templates_dir, - "microRTPS_transport.*"), agent_out_dir) - if cmakelists: - os.rename(os.path.join(os.path.dirname(out_dir), "microRTPS_agent_CMakeLists.txt"), - os.path.join(os.path.dirname(out_dir), "CMakeLists.txt")) - if (mkdir_build): - mkdir_p(os.path.join(os.path.dirname(out_dir), "build")) - os.chdir(prev_cwd_path) - return 0 - - -def rm_wildcard(pattern): - for f in glob.glob(pattern): - os.remove(f) - - -def cp_wildcard(pattern, destdir): - for f in glob.glob(pattern): - shutil.copy(f, destdir) - - -def mkdir_p(dirpath): - try: - os.makedirs(dirpath) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(dirpath): - pass - else: - raise - - -def generate_client(out_dir): - global fastrtps_version - - # Rename work in the default path - if default_client_out != out_dir: - def_file = os.path.join(default_client_out, "microRTPS_client.cpp") - if os.path.isfile(def_file): - os.rename(def_file, def_file.replace(".cpp", ".cpp_")) - def_file = os.path.join(default_client_out, "microRTPS_transport.cpp") - if os.path.isfile(def_file): - os.rename(def_file, def_file.replace(".cpp", ".cpp_")) - def_file = os.path.join(default_client_out, "microRTPS_transport.h") - if os.path.isfile(def_file): - os.rename(def_file, def_file.replace(".h", ".h_")) - - px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, - out_dir, uorb_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_CLIENT_TEMPL_FILE) - - # Final steps to install client - cp_wildcard(os.path.join(urtps_templates_dir, - "microRTPS_transport.*"), out_dir) - - return 0 - - -if agent: - generate_agent(agent_out_dir) - print(("\nAgent created in: " + agent_out_dir)) - -if client: - generate_client(client_out_dir) - print(("\nClient created in: " + client_out_dir)) diff --git a/msg/tools/generate_msg_docs.py b/msg/tools/generate_msg_docs.py index c3f542a8e6..3daa9249b8 100755 --- a/msg/tools/generate_msg_docs.py +++ b/msg/tools/generate_msg_docs.py @@ -66,7 +66,7 @@ if __name__ == "__main__": with open(msg_filename, 'r') as source_file: msg_contents = source_file.read() - #Format markdown using msg name, comment, url, contents. + #Format markdown using msg name, comment, url, contents. markdown_output="""# %s (UORB message) %s @@ -93,9 +93,6 @@ if __name__ == "__main__": This list is [auto-generated](https://github.com/PX4/PX4-Autopilot/blob/master/msg/tools/generate_msg_docs.py) from the source code. ::: -This topic lists the UORB messages available in PX4 (some of which may be may be shared by the [PX4-ROS 2 Bridge](../ros/ros2_comm.md)). -Graphs showing how these are used [can be found here](../middleware/uorb_graph.md). - %s """ % (filelist_in_markdown) readme_file = os.path.join(output_dir, 'README.md') diff --git a/msg/tools/px_generate_uorb_topic_files.py b/msg/tools/px_generate_uorb_topic_files.py index 1872539fac..96a3a46fdb 100755 --- a/msg/tools/px_generate_uorb_topic_files.py +++ b/msg/tools/px_generate_uorb_topic_files.py @@ -34,7 +34,7 @@ """ px_generate_uorb_topic_files.py -Generates c/cpp header/source files for uorb topics from .msg (ROS syntax) +Generates c/cpp header/source files for uorb topics from .msg message files """ @@ -42,6 +42,7 @@ import os import shutil import filecmp import argparse +import re import sys try: @@ -76,93 +77,54 @@ except ImportError as e: __author__ = "Sergey Belash, Thomas Gubler, Beat Kueng" -__copyright__ = "Copyright (C) 2013-2016 PX4 Development Team." +__copyright__ = "Copyright (C) 2013-2021 PX4 Development Team." __license__ = "BSD" __email__ = "thomasgubler@gmail.com" -TEMPLATE_FILE = ['msg.h.em', 'msg.cpp.em'] -TOPICS_LIST_TEMPLATE_FILE = ['uORBTopics.hpp.em', 'uORBTopics.cpp.em'] -OUTPUT_FILE_EXT = ['.h', '.cpp'] +TEMPLATE_FILE = ['msg.h.em'] +OUTPUT_FILE_EXT = ['.h'] INCL_DEFAULT = ['std_msgs:./msg/std_msgs'] PACKAGE = 'px4' -TOPICS_TOKEN = '# TOPICS ' -IDL_TEMPLATE_FILE = 'msg.idl.em' - CONSTRAINED_FLASH = False - class MsgScope: NONE = 0 SEND = 1 RECEIVE = 2 - -def get_multi_topics(filename): - """ - Get TOPICS names from a "# TOPICS" line - """ - ofile = open(filename, 'r') - text = ofile.read() - result = [] - for each_line in text.split('\n'): - if each_line.startswith(TOPICS_TOKEN): - topic_names_str = each_line.strip() - topic_names_str = topic_names_str.replace(TOPICS_TOKEN, "") - result.extend(topic_names_str.split(" ")) - ofile.close() - return result - - -def get_msgs_list(msgdir): - """ - Makes list of msg files in the given directory - """ - return [fn for fn in os.listdir(msgdir) if fn.endswith(".msg")] - - -def generate_output_from_file(format_idx, filename, outputdir, package, templatedir, includepath): +def generate_output_from_file(filename, outputdir, package, templatedir, includepath): """ Converts a single .msg file to an uorb header/source file """ msg_context = genmsg.msg_loader.MsgContext.create_default() - full_type_name = genmsg.gentools.compute_full_type_name( - package, os.path.basename(filename)) - spec = genmsg.msg_loader.load_msg_from_file( - msg_context, filename, full_type_name) + full_type_name = genmsg.gentools.compute_full_type_name(package, os.path.basename(filename)) + + file_base_name = os.path.basename(filename).replace(".msg", "") + + full_type_name_snake = re.sub(r'(? maxinputtime: - maxinputtime = it - - # Find the most recent modification time in output dir - maxouttime = 0 - if os.path.isdir(outputdir): - for f in os.listdir(outputdir): - fni = os.path.join(outputdir, f) - if os.path.isfile(fni): - it = os.path.getmtime(fni) - if it > maxouttime: - maxouttime = it - - # Do not generate if nothing changed on the input - if (maxinputtime != 0 and maxouttime != 0 and maxinputtime < maxouttime): - return False - - includepath = INCL_DEFAULT + [':'.join([package, inputdir])] - for f in os.listdir(inputdir): - # Ignore hidden files - if f.startswith("."): - continue - - fn = os.path.join(inputdir, f) - # Only look at actual files - if not os.path.isfile(fn): - continue - - if fn[-4:].lower() != '.msg': - continue - - generate_output_from_file( - format_idx, fn, outputdir, package, templatedir, includepath) - return True - - -def copy_changed(inputdir, outputdir, prefix='', quiet=False): - """ - Copies files from inputdir to outputdir if they don't exist in - ouputdir or if their content changed - """ - - # Make sure output directory exists: - if not os.path.isdir(outputdir): - os.makedirs(outputdir) - - for input_file in os.listdir(inputdir): - fni = os.path.join(inputdir, input_file) - if os.path.isfile(fni): - # Check if input_file exists in outpoutdir, copy the file if not - fno = os.path.join(outputdir, prefix + input_file) - if not os.path.isfile(fno): - shutil.copy(fni, fno) - if not quiet: - print("{0}: new header file".format(fno)) - continue - - if os.path.getmtime(fni) > os.path.getmtime(fno): - # The file exists in inputdir and outputdir - # only copy if contents do not match - if not filecmp.cmp(fni, fno): - shutil.copy(fni, fno) - if not quiet: - print("{0}: updated".format(input_file)) - continue - - if not quiet: - print("{0}: unchanged".format(input_file)) - - -def convert_dir_save(format_idx, inputdir, outputdir, package, templatedir, temporarydir, prefix, quiet=False): - """ - Converts all .msg files in inputdir to uORB header files - Unchanged existing files are not overwritten. - """ - # Create new headers in temporary output directory - convert_dir(format_idx, inputdir, temporarydir, package, templatedir) - if generate_idx == 1: - generate_topics_list_file(inputdir, temporarydir, TOPICS_LIST_TEMPLATE_FILE[1], templatedir) - # Copy changed headers from temporary dir to output dir - copy_changed(temporarydir, outputdir, prefix, quiet) - - -def generate_topics_list_file(msgdir, outputdir, template_filename, templatedir): - # generate cpp file with topics list - msgs = get_msgs_list(msgdir) - multi_topics = [] - for msg in msgs: - msg_filename = os.path.join(msgdir, msg) - multi_topics.extend(get_multi_topics(msg_filename)) - tl_globals = {"msgs": msgs, "multi_topics": multi_topics} - tl_template_file = os.path.join(templatedir, template_filename) - tl_out_file = os.path.join(outputdir, template_filename.replace(".em", "")) - generate_by_template(tl_out_file, tl_template_file, tl_globals) - - -def generate_topics_list_file_from_files(files, outputdir, template_filename, templatedir): - # generate cpp file with topics list - filenames = [os.path.basename( - p) for p in files if os.path.basename(p).endswith(".msg")] - multi_topics = [] - for msg_filename in files: - multi_topics.extend(get_multi_topics(msg_filename)) - tl_globals = {"msgs": filenames, "multi_topics": multi_topics} - tl_template_file = os.path.join(templatedir, template_filename) - tl_out_file = os.path.join(outputdir, template_filename.replace(".em", "")) - generate_by_template(tl_out_file, tl_template_file, tl_globals) - - def append_to_include_path(path_to_append, curr_include, package): for p in path_to_append: curr_include.append('%s:%s' % (package, p)) if __name__ == "__main__": - parser = argparse.ArgumentParser( - description='Convert msg files to uorb headers/sources') - parser.add_argument('--headers', help='Generate header files', - action='store_true') - parser.add_argument('--sources', help='Generate source files', - action='store_true') - parser.add_argument('-d', dest='dir', help='directory with msg files') + parser = argparse.ArgumentParser(description='Convert msg files to uorb headers') parser.add_argument('-f', dest='file', help="files to convert (use only without -d)", nargs="+") @@ -500,14 +253,9 @@ if __name__ == "__main__": help='package name') parser.add_argument('-o', dest='outputdir', help='output directory for header files') - parser.add_argument('-t', dest='temporarydir', - help='temporary directory') parser.add_argument('-p', dest='prefix', default='', help='string added as prefix to the output file ' ' name when converting directories') - parser.add_argument('-q', dest='quiet', default=False, action='store_true', - help='string added as prefix to the output file ' - ' name when converting directories') parser.add_argument('--constrained-flash', dest='constrained_flash', default=False, action='store_true', help='set to save flash space') args = parser.parse_args() @@ -517,27 +265,6 @@ if __name__ == "__main__": CONSTRAINED_FLASH = args.constrained_flash - if args.headers: - generate_idx = 0 - elif args.sources: - generate_idx = 1 - else: - print('Error: either --headers or --sources must be specified') - exit(-1) if args.file is not None: for f in args.file: - generate_output_from_file( - generate_idx, f, args.temporarydir, args.package, args.templatedir, INCL_DEFAULT) - - generate_topics_list_file_from_files(args.file, args.outputdir, TOPICS_LIST_TEMPLATE_FILE[generate_idx], args.templatedir) - copy_changed(args.temporarydir, args.outputdir, args.prefix, args.quiet) - elif args.dir is not None: - convert_dir_save( - generate_idx, - args.dir, - args.outputdir, - args.package, - args.templatedir, - args.temporarydir, - args.prefix, - args.quiet) + generate_output_from_file(f, args.outputdir, args.package, args.templatedir, INCL_DEFAULT) diff --git a/msg/tools/px_generate_uorb_topic_helper.py b/msg/tools/px_generate_uorb_topic_helper.py index 1f90a01ddf..d2508cb64f 100644 --- a/msg/tools/px_generate_uorb_topic_helper.py +++ b/msg/tools/px_generate_uorb_topic_helper.py @@ -41,6 +41,7 @@ precompiled and thus message generation will be much faster import os import errno +import re import genmsg.msgs @@ -257,6 +258,7 @@ def print_field_def(field): type_prefix = '' if (sl_pos >= 0): type_name = type_name[sl_pos + 1:] + type_name = re.sub(r'(? None: - self.msg_folder = msg_folder - self.msg_map = self.parse_yaml_msgs_file(yaml_file) - - # Check if base types are defined correctly - self.check_base_type() - - # Get messages to send and to receive - self.msgs_to_send: Dict[str, int] = dict() - self.msgs_to_receive: Dict[str, int] = dict() - self.alias_msgs_to_send: List[Tuple[str, str]] = [] - self.alias_msgs_to_receive: List[Tuple[str, str]] = [] - self.msg_list: List[str] = [] - - # Create message map - self.setup_msg_map() - - self.msg_files_send = self.set_msg_files_send() - self.msg_files_receive = self.set_msg_files_receive() - - def setup_msg_map(self) -> None: - """Setup dictionary with an ID map for the messages.""" - for topic in self.msg_map['rtps']: - if 'send' in list(topic.keys()): - if 'base' in list(topic.keys()): - self.alias_msgs_to_send.append( - (topic['msg'], topic['base'])) - else: - self.msgs_to_send.update({topic['msg']: 0}) - if 'receive' in list(topic.keys()): - if 'base' in list(topic.keys()): - self.alias_msgs_to_receive.append( - (topic['msg'], topic['base'])) - else: - self.msgs_to_receive.update({topic['msg']: 0}) - self.msg_list.append(topic['msg']) - - def set_msg_files_send(self) -> list: - """ - Append the path to the files which messages are marked to - be sent. - """ - return [os.path.join(self.msg_folder, msg + ".msg") - for msg in list(self.msgs_to_send.keys())] - - def set_msg_files_receive(self) -> list: - """ - Append the path to the files which messages are marked to - be received. - """ - return [os.path.join(self.msg_folder, msg + ".msg") - for msg in list(self.msgs_to_receive.keys())] - - def check_base_type(self) -> None: - """Check if alias message has correct base type.""" - registered_alias_msgs = list( - topic['base'] for topic in self.msg_map['rtps'] if 'base' in list(topic.keys())) - - base_types = [] - for topic in self.msg_map['rtps']: - if 'base' not in list(topic.keys()): - base_types.append(topic['msg']) - - incorrect_base_types = list( - set(registered_alias_msgs) - set(base_types)) - - base_types_suggestion = {} - for incorrect in incorrect_base_types: - base_types_suggestion.update({incorrect: difflib.get_close_matches( - incorrect, base_types, n=1, cutoff=0.6)}) - - if len(base_types_suggestion) > 0: - raise AssertionError( - ('\n' + '\n'.join('\t- The multi-topic message base type \'{}\' does not exist.{}'.format(k, (' Did you mean \'' + v[0] + '\'?' if v else '')) for k, v in list(base_types_suggestion.items())))) - - @staticmethod - def parse_yaml_msgs_file(yaml_file) -> dict: - """Parses a yaml file into a dict.""" - try: - with open(yaml_file, 'r') as file: - return yaml.safe_load(file) - except OSError as err: - if err.errno == errno.ENOENT: - raise IOError(errno.ENOENT, os.strerror( - errno.ENOENT), yaml_file) - raise - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - - parser.add_argument("-s", "--send", dest='send', - action="store_true", help="Get topics to be sent") - parser.add_argument("-a", "--alias", dest='alias', - action="store_true", help="Get alias topics") - parser.add_argument("-r", "--receive", dest='receive', - action="store_true", help="Get topics to be received") - parser.add_argument("-i", "--ignore", dest='ignore', - action="store_true", help="Get topics to be ignored") - parser.add_argument("-p", "--path", dest='path', - action="store_true", help="Get msgs and its paths") - parser.add_argument("-m", "--topic-msg-dir", dest='msgdir', type=str, - help="Topics message dir, by default msg/", default="msg") - parser.add_argument("-y", "--rtps-ids-file", dest='yaml_file', type=str, - help="RTPS msg IDs definition file absolute path, by default use relative path to msg, tools/urtps_bridge_topics.yaml", - default='tools/urtps_bridge_topics.yaml') - - # Parse arguments - args = parser.parse_args() - - msg_dir = args.msgdir - if args.msgdir == 'msg': - msg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - else: - msg_dir = os.path.abspath(args.msgdir) - classifier = (Classifier(os.path.abspath(args.yaml_file), msg_dir) if os.path.isabs(args.yaml_file) - else Classifier(os.path.join(msg_dir, args.yaml_file), msg_dir)) - - if args.send: - if args.path: - print(('send files: ' + ', '.join(str(msg_file) - for msg_file in classifier.msg_files_send) + '\n')) - else: - if args.alias: - print((', '.join(str(msg) - for msg in sorted(classifier.msgs_to_send)) + (' alias ' + ', '.join(msg[0] - for msg in classifier.alias_msgs_to_send) if len(classifier.alias_msgs_to_send) > 0 else '') + '\n')) - else: - print((', '.join(str(msg) - for msg in sorted(classifier.msgs_to_send)))) - if args.receive: - if args.path: - print(('receive files: ' + ', '.join(str(msg_file) - for msg_file in classifier.msg_files_receive) + '\n')) - else: - if args.alias: - print((', '.join(str(msg) - for msg in sorted(classifier.msgs_to_receive)) + (' alias ' + ', '.join(msg[0] - for msg in classifier.alias_msgs_to_receive) if len(classifier.alias_msgs_to_receive) > 0 else '') + '\n')) - else: - print((', '.join(str(msg) - for msg in sorted(classifier.msgs_to_receive)))) diff --git a/msg/tools/uorb_to_ros_msgs.py b/msg/tools/uorb_to_ros_msgs.py deleted file mode 100755 index a0d2aa9fb3..0000000000 --- a/msg/tools/uorb_to_ros_msgs.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 -""" -Script to parse uORB message format to ROS msg format -Adapted from https://github.com/eProsima/px4_to_ros/blob/master/px4_to_ros2_PoC/px4_msgs/scripts/copy_and_rename.py -""" - -import os -import re -import sys -from shutil import copyfile - -__author__ = 'PX4 Development Team' -__copyright__ = \ - ''' - ' - ' Copyright (c) 2018 PX4 Development Team. All rights reserved. - ' - ' Redistribution and use in source and binary forms, or without - ' modification, permitted provided that the following conditions - ' are met: - ' - ' 1. Redistributions of source code must retain the above copyright - ' notice, list of conditions and the following disclaimer. - ' 2. Redistributions in binary form must reproduce the above copyright - ' notice, list of conditions and the following disclaimer in - ' the documentation and/or other materials provided with the - ' distribution. - ' 3. Neither the name PX4 nor the names of its contributors may be - ' used to endorse or promote products derived from self software - ' without specific prior written permission. - ' - ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ' "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, NOT - ' LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - ' FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - ' COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - ' INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, - ' BUT NOT LIMITED TO, OF SUBSTITUTE GOODS OR SERVICES; LOSS - ' OF USE, DATA, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - ' AND ON ANY THEORY OF LIABILITY, IN CONTRACT, STRICT - ' LIABILITY, TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ' ANY WAY OUT OF THE USE OF THIS SOFTWARE, IF ADVISED OF THE - ' POSSIBILITY OF SUCH DAMAGE. - ' - ''' -__credits__ = ['Nuno Marques '] -__license__ = 'BSD-3-Clause' -__version__ = '0.1.0' -__maintainer__ = 'Nuno Marques' -__email__ = 'nuno.marques@dronesolution.io' -__status__ = 'Development' - -input_dir = sys.argv[1] -output_dir = sys.argv[2] - - -def main(): - print("----------------------- \033[1mmicroRTPS bridge uORB to ROS messages\033[0m -----------------------") - print("-------------------------------------------------------------------------------------------------------") - - if not os.path.exists(os.path.abspath(output_dir)): - os.mkdir(os.path.abspath(output_dir)) - else: - ros_msg_dir = os.path.abspath(output_dir) - msg_files = os.listdir(ros_msg_dir) - for msg in msg_files: - if msg.endswith(".msg"): - os.remove(os.path.join(ros_msg_dir, msg)) - - msg_list = list() - - for filename in os.listdir(input_dir): - if '.msg' in filename: - msg_list.append(filename.rstrip('.msg')) - input_file = input_dir + filename - - output_file = output_dir + \ - filename.partition(".")[0].title().replace('_', '') + ".msg" - copyfile(input_file, output_file) - - for filename in os.listdir(output_dir): - if '.msg' in filename: - input_file = output_dir + filename - - fileUpdated = False - - with open(input_file, 'r') as f: - lines = f.readlines() - newlines = [] - alias_msgs = [] - alias_msg_files = [] - - for line in lines: - for msg_type in msg_list: - if ('px4/' + msg_type + ' ') in line: - fileUpdated = True - line = line.replace(('px4/' + msg_type), - msg_type.partition(".")[0].title().replace('_', '')) - - if re.findall('^' + msg_type + '[\s\[]', line.partition('#')[0]): - fileUpdated = True - line = line.replace(msg_type, - msg_type.partition(".")[0].title().replace('_', '')) - if '# TOPICS' in line: - fileUpdated = True - alias_msgs += line.split() - alias_msgs.remove('#') - alias_msgs.remove('TOPICS') - line = line.replace(line, '') - newlines.append(line) - - for msg_file in alias_msgs: - with open(output_dir + msg_file.partition(".")[0].title().replace('_', '') + ".msg", 'w+') as f: - for line in newlines: - f.write(line) - - if fileUpdated: - with open(input_file, 'w+') as f: - for line in newlines: - f.write(line) - - print("--\t\t- Generated {} ROS message files in '{}'".format(len(os.listdir(output_dir)), os.path.abspath(output_dir))) - print("-------------------------------------------------------------------------------------------------------") - - -if __name__ == '__main__': - main() diff --git a/msg/tools/uorb_to_ros_urtps_topics.py b/msg/tools/uorb_to_ros_urtps_topics.py deleted file mode 100755 index 8ff5f0e59d..0000000000 --- a/msg/tools/uorb_to_ros_urtps_topics.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python3 -""" -Script to read an yaml file containing the microRTPS topics and update the naming convention to PascalCase -""" - -import argparse -import errno -from pathlib import Path -import os -import six -import yaml - -__author__ = 'PX4 Development Team' -__copyright__ = \ - ''' - ' - ' Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. - ' - ' Redistribution and use in source and binary forms, or without - ' modification, permitted provided that the following conditions - ' are met: - ' - ' 1. Redistributions of source code must retain the above copyright - ' notice, list of conditions and the following disclaimer. - ' 2. Redistributions in binary form must reproduce the above copyright - ' notice, list of conditions and the following disclaimer in - ' the documentation and/or other materials provided with the - ' distribution. - ' 3. Neither the name PX4 nor the names of its contributors may be - ' used to endorse or promote products derived from self software - ' without specific prior written permission. - ' - ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ' "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, NOT - ' LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - ' FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - ' COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - ' INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, - ' BUT NOT LIMITED TO, OF SUBSTITUTE GOODS OR SERVICES; LOSS - ' OF USE, DATA, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - ' AND ON ANY THEORY OF LIABILITY, IN CONTRACT, STRICT - ' LIABILITY, TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ' ANY WAY OUT OF THE USE OF THIS SOFTWARE, IF ADVISED OF THE - ' POSSIBILITY OF SUCH DAMAGE. - ' - ''' -__credits__ = ['Nuno Marques '] -__license__ = 'BSD-3-Clause' -__version__ = '0.1.0' -__maintainer__ = 'Nuno Marques' -__email__ = 'nuno.marques@dronesolution.io' -__status__ = 'Development' - -verbose = False - - -class IndenterDumper(yaml.Dumper): - """ Custom dumper for yaml files that apply the correct indentation """ - - def increase_indent(self, flow=False, indentless=False): - return super(IndenterDumper, self).increase_indent(flow, False) - - -def load_yaml_file(file): - """ - Open yaml file and parse the data into a list of dict - - :param file: the yaml file to load - :returns: the list of dictionaries that represent the topics to send and receive - :raises IOError: raises and error when the file is not found - """ - try: - with open(file, 'r') as f: - if verbose: - print(("--\t\t- '%s' file loaded" % file)) - return yaml.safe_load(f) - except OSError as e: - if e.errno == errno.ENOENT: - raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), file) - else: - raise - - -def update_dict(list): - """ - Update the message naming on the dictionary to fit the PascalCase convention - - :param file: the list of dicts to be updated - """ - if verbose: - num_of_msgs = 0 - for i, dictionary in enumerate(list["rtps"]): - list["rtps"][i] = {k: v.title().replace('_', '') if isinstance( - v, six.string_types) else v for k, v in six.iteritems(dictionary)} - if verbose: - num_of_msgs += 1 - if verbose: - print(("--\t\t- %d ROS message file names updated" % num_of_msgs)) - - -def update_yaml_file(list, in_file, out_file): - """ - Open the yaml file to dump the new list of dict toself. - - :param list: the list of updated dicts - :param file: the yaml file to load and write the new data - :raises IOError: raises and error when the file is not found - """ - try: - with open(out_file, 'w') as f: - f.write("# AUTOGENERATED-FILE! DO NOT MODIFY IT DIRECTLY.\n#" - " Edit instead the same file under PX4-Autopilot/msg/tools and" - " use the \n# PX4-Autopilot/msg/tools/uorb_to_ros_urtps_topics.py" - " to regenerate this file.\n") - yaml.dump(list, f, Dumper=IndenterDumper, default_flow_style=False) - if verbose: - if in_file == out_file: - print(("--\t\t- '%s' updated" % in_file)) - else: - print(("--\t\t- '%s' created" % out_file)) - - except OSError as err: - if err.errno == errno.ENOENT: - raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), out_file) - raise - - -def main(): - parser = argparse.ArgumentParser( - description='Read an yaml file containing the microRTPS topics and update the naming convention to PascalCase') - optional = parser._action_groups.pop() - required = parser.add_argument_group('Required') - required.add_argument("-i", "--input-file", dest="input_file", - help="Yaml file to read", metavar="INFILE") - optional.add_argument("-o", "--output-file", dest="output_file", - help="Yaml file to dump. If not set, it is the same as the input", - metavar="OUTFILE", default="") - optional.add_argument("-q", "--quiet", action="store_false", dest="verbose", - default=True, help="Don't print status messages to stdout") - - args = parser.parse_args() - global verbose - verbose = args.verbose - in_file = Path(args.input_file) - out_file = Path(args.output_file) if ( - Path(args.output_file) != in_file and Path(args.output_file) != "") else in_file - - if verbose: - print("---------------------- \033[1mmicroRTPS bridge yaml PX4 to ROS topics\033[0m ----------------------") - print("-------------------------------------------------------------------------------------------------------") - - list = load_yaml_file(in_file) - update_dict(list) - update_yaml_file(list, in_file, out_file) - if verbose: - print("-------------------------------------------------------------------------------------------------------") - - -if __name__ == "__main__": - main() diff --git a/msg/tools/urtps_bridge_topics.yaml b/msg/tools/urtps_bridge_topics.yaml deleted file mode 100644 index 1cc21f7478..0000000000 --- a/msg/tools/urtps_bridge_topics.yaml +++ /dev/null @@ -1,91 +0,0 @@ -##### -# -# This file maps all the topics that are to be used on the microRTPS bridge. -# When one wants to add a new topic to the bridge, it should add it to this file -# and mark it to be sent or received from the link. -# For alias/multi-topic messages (i.e. the ones found on the '#TOPICS' of the -# uORB messages), these can be also added, requiring an extra entry ('base') to -# define the base message. -# -# IMPORTANT NOTICE: The IDs of the messages sent on the bridge get generated -# according to the order of the messages in this file. To keep consistency and -# backwards compatibility, it is recommended that any new message that one wants -# to be streamed in the bridge gets added to the end of the list. Any changes -# in the middle of the list (additions, removals, replacements) will change also -# the current message IDS, which might result with incompatibilities with -# previous PX4 versions (where the list with this format got introduced or -# subsisted). -# -# Any updates to this file should be mirrored in both sides of the bridge (i.e., -# PX4 and px4_ros_com), when using it with ROS2. That can be easily done using -# the 'msg/tools/uorb_to_ros_urtps_topics.py' script to regenerate this same -# file under 'px4_ros_com/templates/''. The same is not applicable/required if -# using this bridge with "raw" RTPS/DDS applications, since the microRTPS agent -# to be used and stored in 'build//src/modules/micrortps_bridge/'' -# gets generated using this same list. -# -##### -rtps: - # topic ID 1 - - msg: debug_array - receive: true - # topic ID 2 - - msg: debug_key_value - receive: true - # topic ID 3 - - msg: debug_value - receive: true - # ... - - msg: debug_vect - receive: true - - msg: offboard_control_mode - receive: true - - msg: optical_flow - receive: true - - msg: position_setpoint - receive: true - - msg: position_setpoint_triplet - receive: true - - msg: telemetry_status - receive: true - - msg: timesync - receive: true - send: true - - msg: trajectory_waypoint - send: true - - msg: vehicle_command - receive: true - - msg: vehicle_control_mode - send: true - - msg: vehicle_local_position_setpoint - receive: true - - msg: trajectory_setpoint # multi-topic / alias of vehicle_local_position_setpoint - base: vehicle_local_position_setpoint - receive: true - - msg: vehicle_odometry - send: true - - msg: vehicle_mocap_odometry # multi-topic / alias of vehicle_odometry - base: vehicle_odometry - receive: true - - msg: vehicle_visual_odometry # multi-topic / alias of vehicle_odometry - base: vehicle_odometry - receive: true - - msg: vehicle_status - send: true - - msg: vehicle_trajectory_waypoint - receive: true - - msg: vehicle_trajectory_waypoint_desired # multi-topic / alias of vehicle_trajectory_waypoint - base: vehicle_trajectory_waypoint - send: true - - msg: collision_constraints - send: true - - msg: onboard_computer_status - receive: true - - msg: trajectory_bezier - receive: true - - msg: vehicle_trajectory_bezier - receive: true - - msg: timesync_status - send: true - - msg: sensor_combined - send: true diff --git a/package.xml b/package.xml index d2eb83f934..7decc69d7a 100644 --- a/package.xml +++ b/package.xml @@ -1,66 +1,27 @@ - + + px4 - 1.0.0 - The PX4 Flight Stack package + 1.13.0 + PX4 Autopilot + dagar + BSD 3 - - - - Lorenz Meier + rosidl_default_generators + ament_cmake - - - - BSD + rclcpp + std_msgs + rosidl_default_runtime - - - - http://px4.io/ros + rosidl_interface_packages + ament_lint_auto + ament_lint_common - - - - - - - - - - - message_generation - - - - message_runtime - - - catkin - roscpp - rospy - std_msgs - libmavconn - tf - rostest - mav_msgs - roscpp - rospy - std_msgs - libmavconn - tf - mav_msgs - - - - - - - - + ament_cmake diff --git a/platforms/common/CMakeLists.txt b/platforms/common/CMakeLists.txt index 120a59fdfe..167ee4bdf3 100644 --- a/platforms/common/CMakeLists.txt +++ b/platforms/common/CMakeLists.txt @@ -35,13 +35,13 @@ set(SRCS) if(NOT "${PX4_BOARD}" MATCHES "io-v2" AND NOT "${PX4_BOARD_LABEL}" MATCHES "bootloader") list(APPEND SRCS + events.cpp px4_log.cpp ) endif() -add_library(px4_platform +add_library(px4_platform STATIC board_identity.c - events.cpp external_reset_lockout.cpp i2c.cpp i2c_spi_buses.cpp @@ -54,7 +54,7 @@ add_library(px4_platform ) target_link_libraries(px4_platform prebuild_targets px4_work_queue) -if (NOT "${PX4_BOARD}" MATCHES "io-v2") +if(NOT "${PX4_BOARD}" MATCHES "io-v2") add_subdirectory(uORB) endif() diff --git a/platforms/common/include/px4_platform_common/Node.hpp b/platforms/common/include/px4_platform_common/Node.hpp new file mode 100644 index 0000000000..53af88d21e --- /dev/null +++ b/platforms/common/include/px4_platform_common/Node.hpp @@ -0,0 +1,47 @@ + +#pragma once + +namespace px4 +{ + +class Node +{ +public: + explicit Node(const char *node_name) : + _name(node_name) + { + + } + + ~Node() override = default; + + + const char *get_name() const { return _name; } + + // bool get_parameter() + + +private: + + + const char *_name{nullptr}; + +}; + +}; + +// int main(int argc, char *argv[]) +// { +// rclcpp::init(argc, argv); +// rclcpp::spin(std::make_shared()); +// rclcpp::shutdown(); +// return 0; +// } + + +// #include "rclcpp_components/register_node_macro.hpp" + +// // Register the component with class_loader. +// // This acts as a sort of entry point, allowing the component to be discoverable when its library +// // is being loaded into a running process. +// RCLCPP_COMPONENTS_REGISTER_NODE(composition::Client) diff --git a/platforms/common/include/px4_platform_common/workqueue.h b/platforms/common/include/px4_platform_common/workqueue.h index 8b7ee24163..36ab23ed9f 100644 --- a/platforms/common/include/px4_platform_common/workqueue.h +++ b/platforms/common/include/px4_platform_common/workqueue.h @@ -43,7 +43,6 @@ #include #include -#include __BEGIN_DECLS diff --git a/platforms/common/uORB/CMakeLists.txt b/platforms/common/uORB/CMakeLists.txt index 0f0060836b..d94fe942d8 100644 --- a/platforms/common/uORB/CMakeLists.txt +++ b/platforms/common/uORB/CMakeLists.txt @@ -34,8 +34,6 @@ # this includes the generated topics directory include_directories(${CMAKE_CURRENT_BINARY_DIR}) -set(SRCS) - set(SRCS_COMMON ORBSet.hpp Publication.hpp @@ -49,22 +47,22 @@ set(SRCS_COMMON uORB.h uORBCommon.hpp uORBCommunicator.hpp - uORBManager.hpp uORBUtils.cpp uORBUtils.hpp - uORBDeviceMaster.hpp - uORBDeviceNode.hpp - ) +) set(SRCS_KERNEL uORBDeviceMaster.cpp + uORBDeviceMaster.hpp uORBDeviceNode.cpp + uORBDeviceNode.hpp uORBManager.cpp - ) + uORBManager.hpp +) set(SRCS_USER uORBManagerUsr.cpp - ) +) if (NOT DEFINED CONFIG_BUILD_FLAT AND "${PX4_PLATFORM}" MATCHES "nuttx") # Kernel side library in nuttx kernel/protected build diff --git a/platforms/common/uORB/Publication.hpp b/platforms/common/uORB/Publication.hpp index 7547e3b7fa..88a5e0de49 100644 --- a/platforms/common/uORB/Publication.hpp +++ b/platforms/common/uORB/Publication.hpp @@ -43,7 +43,7 @@ #include #include "uORBManager.hpp" -#include +#include namespace uORB { diff --git a/platforms/common/uORB/Subscription.hpp b/platforms/common/uORB/Subscription.hpp index 496b8cf261..5cb0ce63b0 100644 --- a/platforms/common/uORB/Subscription.hpp +++ b/platforms/common/uORB/Subscription.hpp @@ -39,7 +39,7 @@ #pragma once #include -#include +#include #include #include @@ -181,6 +181,10 @@ public: unsigned get_last_generation() const { return _last_generation; } orb_id_t get_topic() const { return get_orb_meta(_orb_id); } + ORB_ID get_topic_enum() const { return _orb_id; } + + size_t get_topic_size() const { return get_orb_meta(_orb_id)->o_size; } + protected: friend class SubscriptionCallback; diff --git a/platforms/common/uORB/SubscriptionInterval.hpp b/platforms/common/uORB/SubscriptionInterval.hpp index 31d1b0a7af..ddeda31de4 100644 --- a/platforms/common/uORB/SubscriptionInterval.hpp +++ b/platforms/common/uORB/SubscriptionInterval.hpp @@ -140,6 +140,9 @@ public: unsigned get_last_generation() const { return _subscription.get_last_generation(); } orb_id_t get_topic() const { return _subscription.get_topic(); } + ORB_ID get_topic_enum() const { return _subscription.get_topic_enum(); } + size_t get_topic_size() const { return _subscription.get_topic_size(); } + /** * Set the interval in microseconds * @param interval The interval in microseconds. diff --git a/platforms/common/uORB/uORB.h b/platforms/common/uORB/uORB.h index c96f3c08d4..702e3ebd42 100644 --- a/platforms/common/uORB/uORB.h +++ b/platforms/common/uORB/uORB.h @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2012-2015 PX4 Development Team. All rights reserved. + * Copyright (c) 2012-2021 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -251,10 +251,16 @@ void orb_print_message_internal(const struct orb_metadata *meta, const void *dat __END_DECLS /* Diverse uORB header defines */ //XXX: move to better location -#define ORB_ID_VEHICLE_ATTITUDE_CONTROLS ORB_ID(actuator_controls_0) typedef uint8_t arming_state_t; typedef uint8_t main_state_t; typedef uint8_t hil_state_t; typedef uint8_t navigation_state_t; typedef uint8_t switch_pos_t; + + +#ifdef __cplusplus + +#include + +#endif // __cplusplus diff --git a/platforms/common/uORB/uORBCommunicator.hpp b/platforms/common/uORB/uORBCommunicator.hpp index cbb996b82b..80ccb2d1ab 100644 --- a/platforms/common/uORB/uORBCommunicator.hpp +++ b/platforms/common/uORB/uORBCommunicator.hpp @@ -100,8 +100,6 @@ public: virtual int16_t add_subscription(const char *messageName, int32_t msgRateInHz) = 0; - - /** * @brief Interface to notify the remote entity of removal of a subscription * @@ -116,13 +114,11 @@ public: virtual int16_t remove_subscription(const char *messageName) = 0; - /** * Register Message Handler. This is internal for the IChannel implementer* */ virtual int16_t register_handler(uORBCommunicator::IChannelRxHandler *handler) = 0; - //========================================================================= // INTERFACES FOR Data messages //========================================================================= @@ -143,7 +139,6 @@ public: */ virtual int16_t send_message(const char *messageName, int32_t length, uint8_t *data) = 0; - }; /** @@ -153,7 +148,6 @@ public: class uORBCommunicator::IChannelRxHandler { public: - /** * Interface to process a received topic from remote. * @param topic_name @@ -184,7 +178,6 @@ public: virtual int16_t process_add_subscription(const char *messageName, int32_t msgRateInHz) = 0; - /** * Interface to process a received control msg to remove subscription * @param messageName @@ -198,7 +191,6 @@ public: virtual int16_t process_remove_subscription(const char *messageName) = 0; - /** * Interface to process the received data message. * @param messageName @@ -215,7 +207,6 @@ public: */ virtual int16_t process_received_message(const char *messageName, int32_t length, uint8_t *data) = 0; - }; #endif /* _uORBCommunicator_hpp_ */ diff --git a/platforms/common/uORB/uORBDeviceMaster.hpp b/platforms/common/uORB/uORBDeviceMaster.hpp index 94c97e61df..67c0491b3c 100644 --- a/platforms/common/uORB/uORBDeviceMaster.hpp +++ b/platforms/common/uORB/uORBDeviceMaster.hpp @@ -36,7 +36,7 @@ #include #include "uORBCommon.hpp" -#include +#include #include diff --git a/platforms/common/uORB/uORBManager.hpp b/platforms/common/uORB/uORBManager.hpp index d5df89605d..92b77d2f91 100644 --- a/platforms/common/uORB/uORBManager.hpp +++ b/platforms/common/uORB/uORBManager.hpp @@ -38,7 +38,7 @@ #include "uORBCommon.hpp" #include "uORBDeviceMaster.hpp" -#include // For ORB_ID enum +#include // For ORB_ID enum #include #ifdef __PX4_NUTTX diff --git a/platforms/common/uORB/uORB_tests/CMakeLists.txt b/platforms/common/uORB/uORB_tests/CMakeLists.txt index ea77e368f3..0ed2bdc1be 100644 --- a/platforms/common/uORB/uORB_tests/CMakeLists.txt +++ b/platforms/common/uORB/uORB_tests/CMakeLists.txt @@ -38,4 +38,13 @@ px4_add_module( SRCS uORB_tests_main.cpp uORBTest_UnitTest.cpp + PUBLICATIONS + "px4::msg::OrbTest /orb_test" + "px4::msg::OrbTest /orb_multitest" + "px4::msg::OrbTestMedium /orb_test_medium" + "px4::msg::OrbTestMedium /orb_test_medium_multi" + "px4::msg::OrbTestMedium /orb_test_medium_queue" + "px4::msg::OrbTestMedium /orb_test_medium_queue_poll" + "px4::msg::OrbTestMedium /orb_test_medium_wrap_around" + "px4::msg::OrbTestLarge /orb_test_large" ) diff --git a/platforms/common/work_queue/CMakeLists.txt b/platforms/common/work_queue/CMakeLists.txt index 2c77023dfb..75ec99a057 100644 --- a/platforms/common/work_queue/CMakeLists.txt +++ b/platforms/common/work_queue/CMakeLists.txt @@ -32,9 +32,9 @@ ############################################################################ # nuttx uses internal work queue currently -if (NOT "${PX4_PLATFORM}" MATCHES "nuttx") +if(NOT "${PX4_PLATFORM}" MATCHES "nuttx") - add_library(work_queue + add_library(work_queue STATIC dq_addlast.c dq_rem.c dq_remfirst.c diff --git a/platforms/common/work_queue/hrt_queue.c b/platforms/common/work_queue/hrt_queue.c index 74a4a8e17b..b145da282c 100644 --- a/platforms/common/work_queue/hrt_queue.c +++ b/platforms/common/work_queue/hrt_queue.c @@ -52,30 +52,6 @@ #include #include "hrt_work.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Public Variables - ****************************************************************************/ - -/**************************************************************************** - * Private Variables - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - /**************************************************************************** * Name: hrt_work_queue * @@ -125,7 +101,7 @@ int hrt_work_queue(struct work_s *work, worker_t worker, void *arg, uint32_t del dq_addlast((dq_entry_t *)work, &wqueue->q); - if (px4_getpid() != wqueue->pid) { /* only need to wake up if called from a different thread */ + if (getpid() != wqueue->pid) { /* only need to wake up if called from a different thread */ #ifdef __PX4_QURT px4_task_kill(wqueue->pid, SIGALRM); /* Wake up the worker thread */ #else diff --git a/platforms/common/work_queue/hrt_thread.c b/platforms/common/work_queue/hrt_thread.c index 26a7e509dd..1e058cc1d4 100644 --- a/platforms/common/work_queue/hrt_thread.c +++ b/platforms/common/work_queue/hrt_thread.c @@ -96,7 +96,7 @@ static void _sighandler(int sig_num); ****************************************************************************/ static void _sighandler(int sig_num) { - PX4_DEBUG("RECEIVED SIGNAL %d", sig_num); + //PX4_DEBUG("RECEIVED SIGNAL %d", sig_num); } /**************************************************************************** @@ -176,7 +176,7 @@ static void hrt_work_process() hrt_work_unlock(); if (!worker) { - PX4_ERR("MESSED UP: worker = 0"); + //PX4_ERR("MESSED UP: worker = 0"); } else { worker(arg); diff --git a/platforms/common/work_queue/work_lock.c b/platforms/common/work_queue/work_lock.c index fb65c70a73..1a3b118f54 100644 --- a/platforms/common/work_queue/work_lock.c +++ b/platforms/common/work_queue/work_lock.c @@ -35,6 +35,7 @@ #include #include "work_lock.h" +#include extern px4_sem_t _work_lock[]; diff --git a/platforms/nuttx/CMakeLists.txt b/platforms/nuttx/CMakeLists.txt index 1f82a8adb7..7b96b4f92e 100644 --- a/platforms/nuttx/CMakeLists.txt +++ b/platforms/nuttx/CMakeLists.txt @@ -242,6 +242,8 @@ else() arch_board_reset ) + target_link_libraries(nuttx_boards INTERFACE arch_board_reset) + target_link_libraries(nuttx_c INTERFACE nuttx_drivers) target_link_libraries(nuttx_drivers INTERFACE nuttx_c) target_link_libraries(nuttx_xx INTERFACE nuttx_c) diff --git a/platforms/nuttx/cmake/px4_impl_os.cmake b/platforms/nuttx/cmake/px4_impl_os.cmake index d68b4becb7..0c8e056ae6 100644 --- a/platforms/nuttx/cmake/px4_impl_os.cmake +++ b/platforms/nuttx/cmake/px4_impl_os.cmake @@ -64,10 +64,19 @@ function(px4_os_add_flags) ${PX4_SOURCE_DIR}/platforms/nuttx/NuttX/apps/include ) - # prevent using the toolchain's std c++ library - add_compile_options($<$:-nostdinc++>) - add_compile_options($<$:-fno-sized-deallocation>) + set(cxx_flags) + list(APPEND cxx_flags + -fno-exceptions + -fno-rtti + -fno-sized-deallocation + -fno-threadsafe-statics + -nostdinc++ # prevent using the toolchain's std c++ library + ) + + foreach(flag ${cxx_flags}) + add_compile_options($<$:${flag}>) + endforeach() add_definitions( -D__PX4_NUTTX diff --git a/platforms/nuttx/src/canbootloader/CMakeLists.txt b/platforms/nuttx/src/canbootloader/CMakeLists.txt index 8ca45a1e87..e9dfc73de8 100644 --- a/platforms/nuttx/src/canbootloader/CMakeLists.txt +++ b/platforms/nuttx/src/canbootloader/CMakeLists.txt @@ -33,7 +33,7 @@ add_subdirectory(arch/${PX4_CHIP_MANUFACTURER}) -px4_add_library(canbootloader +add_library(canbootloader common/nuttx_stubs.c fs/flash.c protocol/uavcan.c @@ -42,7 +42,7 @@ px4_add_library(canbootloader util/random.c util/cxx_init.c ) - +add_dependencies(canbootloader prebuild_targets) include_directories(include) target_include_directories(canbootloader INTERFACE include) diff --git a/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/CMakeLists.txt b/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/CMakeLists.txt index e41468fa17..d59eb2ff61 100644 --- a/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/CMakeLists.txt +++ b/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/CMakeLists.txt @@ -32,8 +32,11 @@ ############################################################################ include_directories(../../../include) -px4_add_library(arch_canbootloader + +add_library(arch_canbootloader board_identity.c drivers/can/driver.c drivers/flash/driver.c ) +target_link_libraries(arch_canbootloader PRIVATE crc) +add_dependencies(arch_canbootloader prebuild_targets) diff --git a/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/drivers/can/driver.c b/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/drivers/can/driver.c index 4ab6a987a5..cb60161b3b 100644 --- a/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/drivers/can/driver.c +++ b/platforms/nuttx/src/canbootloader/arch/nxp/s32k14x/drivers/can/driver.c @@ -53,7 +53,7 @@ #include #include "flexcan.h" -#include +#include #define CAN_TX_TIMEOUT_MS (200 /(1000/(1000000/CONFIG_USEC_PER_TICK))) diff --git a/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/CMakeLists.txt b/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/CMakeLists.txt index eccd131c6d..d9e1de6841 100644 --- a/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/CMakeLists.txt +++ b/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/CMakeLists.txt @@ -33,11 +33,11 @@ include_directories(../../../include) -px4_add_library(arch_canbootloader +add_library(arch_canbootloader board_identity.c drivers/can/driver.c ) - +add_dependencies(arch_canbootloader prebuild_targets) target_link_libraries(arch_canbootloader PRIVATE arch_watchdog_iwdg diff --git a/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/drivers/can/driver.c b/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/drivers/can/driver.c index c915cc0d33..265fdb103f 100644 --- a/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/drivers/can/driver.c +++ b/platforms/nuttx/src/canbootloader/arch/stm/stm32f4/drivers/can/driver.c @@ -55,7 +55,7 @@ #include -#include +#include #define INAK_TIMEOUT 65535 diff --git a/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/CMakeLists.txt b/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/CMakeLists.txt index eccd131c6d..d9e1de6841 100644 --- a/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/CMakeLists.txt +++ b/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/CMakeLists.txt @@ -33,11 +33,11 @@ include_directories(../../../include) -px4_add_library(arch_canbootloader +add_library(arch_canbootloader board_identity.c drivers/can/driver.c ) - +add_dependencies(arch_canbootloader prebuild_targets) target_link_libraries(arch_canbootloader PRIVATE arch_watchdog_iwdg diff --git a/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/drivers/can/driver.c b/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/drivers/can/driver.c index 5a90ea708f..769991e539 100644 --- a/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/drivers/can/driver.c +++ b/platforms/nuttx/src/canbootloader/arch/stm/stm32f7/drivers/can/driver.c @@ -54,7 +54,7 @@ #include -#include +#include #define INAK_TIMEOUT 65535 diff --git a/platforms/nuttx/src/canbootloader/protocol/uavcan.c b/platforms/nuttx/src/canbootloader/protocol/uavcan.c index a55d03aa82..ed00ab66da 100644 --- a/platforms/nuttx/src/canbootloader/protocol/uavcan.c +++ b/platforms/nuttx/src/canbootloader/protocol/uavcan.c @@ -49,7 +49,7 @@ #include "uavcan.h" #include "can.h" -#include +#include #define CAN_REQUEST_TIMEOUT 1000 #define ANY_NODE_ID 0 diff --git a/platforms/nuttx/src/canbootloader/uavcan/main.c b/platforms/nuttx/src/canbootloader/uavcan/main.c index d634715f4a..db1f6691f9 100644 --- a/platforms/nuttx/src/canbootloader/uavcan/main.c +++ b/platforms/nuttx/src/canbootloader/uavcan/main.c @@ -58,7 +58,7 @@ #include #include #include -#include +#include //#define DEBUG_APPLICATION_INPLACE 1 /* Never leave defined */ #define DEBUG_NO_FW_UPDATE 1 /* With DEBUG_APPLICATION_INPLACE diff --git a/platforms/nuttx/src/px4/nxp/imxrt/hrt/CMakeLists.txt b/platforms/nuttx/src/px4/nxp/imxrt/hrt/CMakeLists.txt index 3edd775b6a..9e6514d45d 100644 --- a/platforms/nuttx/src/px4/nxp/imxrt/hrt/CMakeLists.txt +++ b/platforms/nuttx/src/px4/nxp/imxrt/hrt/CMakeLists.txt @@ -31,7 +31,8 @@ # ############################################################################ -px4_add_library(arch_hrt +add_library(arch_hrt hrt.c ) -target_compile_options(arch_hrt PRIVATE -Wno-cast-align) # TODO: fix and enable +add_dependencies(arch_hrt prebuild_targets) +target_compile_options(arch_hrt PRIVATE -Wno-cast-align ${MAX_CUSTOM_OPT_LEVEL}) # TODO: fix and enable diff --git a/platforms/nuttx/src/px4/nxp/kinetis/hrt/CMakeLists.txt b/platforms/nuttx/src/px4/nxp/kinetis/hrt/CMakeLists.txt index ebd97f8a42..9e6514d45d 100644 --- a/platforms/nuttx/src/px4/nxp/kinetis/hrt/CMakeLists.txt +++ b/platforms/nuttx/src/px4/nxp/kinetis/hrt/CMakeLists.txt @@ -31,8 +31,8 @@ # ############################################################################ -px4_add_library(arch_hrt +add_library(arch_hrt hrt.c ) -target_compile_options(arch_hrt PRIVATE -Wno-cast-align) # TODO: fix and enable - +add_dependencies(arch_hrt prebuild_targets) +target_compile_options(arch_hrt PRIVATE -Wno-cast-align ${MAX_CUSTOM_OPT_LEVEL}) # TODO: fix and enable diff --git a/platforms/nuttx/src/px4/nxp/s32k1xx/hrt/CMakeLists.txt b/platforms/nuttx/src/px4/nxp/s32k1xx/hrt/CMakeLists.txt index 3edd775b6a..9e6514d45d 100644 --- a/platforms/nuttx/src/px4/nxp/s32k1xx/hrt/CMakeLists.txt +++ b/platforms/nuttx/src/px4/nxp/s32k1xx/hrt/CMakeLists.txt @@ -31,7 +31,8 @@ # ############################################################################ -px4_add_library(arch_hrt +add_library(arch_hrt hrt.c ) -target_compile_options(arch_hrt PRIVATE -Wno-cast-align) # TODO: fix and enable +add_dependencies(arch_hrt prebuild_targets) +target_compile_options(arch_hrt PRIVATE -Wno-cast-align ${MAX_CUSTOM_OPT_LEVEL}) # TODO: fix and enable diff --git a/platforms/nuttx/src/px4/rpi/rpi_common/hrt/CMakeLists.txt b/platforms/nuttx/src/px4/rpi/rpi_common/hrt/CMakeLists.txt index 9ce26ff632..f82c0c9edc 100644 --- a/platforms/nuttx/src/px4/rpi/rpi_common/hrt/CMakeLists.txt +++ b/platforms/nuttx/src/px4/rpi/rpi_common/hrt/CMakeLists.txt @@ -31,11 +31,8 @@ # ############################################################################ -px4_add_library(arch_hrt +add_library(arch_hrt hrt.c ) -target_compile_options(arch_hrt - PRIVATE - ${MAX_CUSTOM_OPT_LEVEL} - -Wno-cast-align # TODO: fix and enable -) +add_dependencies(arch_hrt prebuild_targets) +target_compile_options(arch_hrt PRIVATE -Wno-cast-align ${MAX_CUSTOM_OPT_LEVEL}) # TODO: fix and enable diff --git a/platforms/nuttx/src/px4/stm/stm32_common/hrt/CMakeLists.txt b/platforms/nuttx/src/px4/stm/stm32_common/hrt/CMakeLists.txt index 82854e0def..9e6514d45d 100644 --- a/platforms/nuttx/src/px4/stm/stm32_common/hrt/CMakeLists.txt +++ b/platforms/nuttx/src/px4/stm/stm32_common/hrt/CMakeLists.txt @@ -31,11 +31,8 @@ # ############################################################################ -px4_add_library(arch_hrt +add_library(arch_hrt hrt.c ) -target_compile_options(arch_hrt - PRIVATE - ${MAX_CUSTOM_OPT_LEVEL} - -Wno-cast-align # TODO: fix and enable -) +add_dependencies(arch_hrt prebuild_targets) +target_compile_options(arch_hrt PRIVATE -Wno-cast-align ${MAX_CUSTOM_OPT_LEVEL}) # TODO: fix and enable diff --git a/platforms/nuttx/src/px4/stm/stm32f1/watchdog/CMakeLists.txt b/platforms/nuttx/src/px4/stm/stm32f1/watchdog/CMakeLists.txt index 3c80878d02..b78c28bb56 100644 --- a/platforms/nuttx/src/px4/stm/stm32f1/watchdog/CMakeLists.txt +++ b/platforms/nuttx/src/px4/stm/stm32f1/watchdog/CMakeLists.txt @@ -31,6 +31,7 @@ # ############################################################################ -px4_add_library(arch_watchdog_iwdg +add_library(arch_watchdog_iwdg iwdg.c ) +add_dependencies(arch_watchdog_iwdg prebuild_targets) diff --git a/platforms/nuttx/src/px4/stm/stm32f4/watchdog/CMakeLists.txt b/platforms/nuttx/src/px4/stm/stm32f4/watchdog/CMakeLists.txt index 3c80878d02..b78c28bb56 100644 --- a/platforms/nuttx/src/px4/stm/stm32f4/watchdog/CMakeLists.txt +++ b/platforms/nuttx/src/px4/stm/stm32f4/watchdog/CMakeLists.txt @@ -31,6 +31,7 @@ # ############################################################################ -px4_add_library(arch_watchdog_iwdg +add_library(arch_watchdog_iwdg iwdg.c ) +add_dependencies(arch_watchdog_iwdg prebuild_targets) diff --git a/platforms/nuttx/src/px4/stm/stm32f7/watchdog/CMakeLists.txt b/platforms/nuttx/src/px4/stm/stm32f7/watchdog/CMakeLists.txt index 3c80878d02..b78c28bb56 100644 --- a/platforms/nuttx/src/px4/stm/stm32f7/watchdog/CMakeLists.txt +++ b/platforms/nuttx/src/px4/stm/stm32f7/watchdog/CMakeLists.txt @@ -31,6 +31,7 @@ # ############################################################################ -px4_add_library(arch_watchdog_iwdg +add_library(arch_watchdog_iwdg iwdg.c ) +add_dependencies(arch_watchdog_iwdg prebuild_targets) diff --git a/platforms/posix/CMakeLists.txt b/platforms/posix/CMakeLists.txt index 2cae2595ac..69a70123b2 100644 --- a/platforms/posix/CMakeLists.txt +++ b/platforms/posix/CMakeLists.txt @@ -3,13 +3,9 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) get_property(module_libraries GLOBAL PROPERTY PX4_MODULE_LIBRARIES) -# When building with catkin, do not change CMAKE_RUNTIME_OUTPUT_DIR from -# CMAKE_CURRENT_BINARY_DIR ''./platforms/posix' to './bin' -if (NOT CATKIN_DEVEL_PREFIX) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) -endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) set(PX4_SHELL_COMMAND_PREFIX "px4-") diff --git a/platforms/posix/include/px4_platform_types.h b/platforms/posix/include/px4_platform_types.h deleted file mode 100644 index abaf177444..0000000000 --- a/platforms/posix/include/px4_platform_types.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef __PX4_QURT -#include -#endif diff --git a/platforms/ros2/CMakeLists.txt b/platforms/ros2/CMakeLists.txt new file mode 100644 index 0000000000..28000b9860 --- /dev/null +++ b/platforms/ros2/CMakeLists.txt @@ -0,0 +1,29 @@ + +#include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +get_property(module_libraries GLOBAL PROPERTY PX4_MODULE_LIBRARIES) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) + +# add_executable(px4 +# src/px4/common/main.cpp +# ) + +add_compile_options(-Wno-error) + +include_directories(${PX4_BINARY_DIR}) + +# # mc_rate_control_ros2 +# add_executable(mc_rate_control ${CMAKE_SOURCE_DIR}/src/modules/mc_rate_control_ros2/MulticopterRateControl.cpp) +# target_link_libraries(mc_rate_control px4_layer work_queue) +# ament_target_dependencies(mc_rate_control rclcpp std_msgs) +# rosidl_target_interfaces(mc_rate_control ${PROJECT_NAME} "rosidl_typesupport_cpp") +# add_dependencies(mc_rate_control generate_parameters) + +# install( +# TARGETS +# mc_rate_control +# DESTINATION lib/${PROJECT_NAME} +# ) diff --git a/platforms/ros2/cmake/Toolchain-native.cmake b/platforms/ros2/cmake/Toolchain-native.cmake new file mode 100644 index 0000000000..2899671043 --- /dev/null +++ b/platforms/ros2/cmake/Toolchain-native.cmake @@ -0,0 +1,17 @@ +# compiler tools +foreach(tool nm ld) + string(TOUPPER ${tool} TOOL) + find_program(${TOOL} ${tool}) + if(NOT ${TOOL}) + message(FATAL_ERROR "could not find ${tool}") + endif() +endforeach() + +# os tools +foreach(tool echo grep rm mkdir nm cp touch make unzip) + string(TOUPPER ${tool} TOOL) + find_program(${TOOL} ${tool}) + if(NOT ${TOOL}) + message(FATAL_ERROR "could not find ${TOOL}") + endif() +endforeach() diff --git a/platforms/ros2/cmake/finalize.cmake b/platforms/ros2/cmake/finalize.cmake new file mode 100644 index 0000000000..51291d389e --- /dev/null +++ b/platforms/ros2/cmake/finalize.cmake @@ -0,0 +1,3 @@ + + +ament_package() diff --git a/platforms/ros2/cmake/init.cmake b/platforms/ros2/cmake/init.cmake new file mode 100644 index 0000000000..9a5c52a7d0 --- /dev/null +++ b/platforms/ros2/cmake/init.cmake @@ -0,0 +1,182 @@ + +cmake_policy(SET CMP0057 NEW) # Support IN_LIST if() operator + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(std_msgs REQUIRED) + + +rosidl_generate_interfaces(${PROJECT_NAME} + "msg/ActionRequest.msg" + "msg/ActuatorArmed.msg" + "msg/ActuatorControls.msg" + "msg/ActuatorControlsStatus.msg" + "msg/ActuatorMotors.msg" + "msg/ActuatorOutputs.msg" + "msg/ActuatorServos.msg" + "msg/ActuatorTest.msg" + "msg/ActuatorServosTrim.msg" + "msg/AdcReport.msg" + "msg/Airspeed.msg" + "msg/AirspeedValidated.msg" + "msg/AirspeedWind.msg" + "msg/AutotuneAttitudeControlStatus.msg" + "msg/BatteryStatus.msg" + "msg/CameraCapture.msg" + "msg/CameraStatus.msg" + "msg/CameraTrigger.msg" + "msg/CellularStatus.msg" + "msg/CollisionConstraints.msg" + "msg/CollisionReport.msg" + "msg/CommanderState.msg" + "msg/ControlAllocatorStatus.msg" + "msg/Cpuload.msg" + "msg/DebugArray.msg" + "msg/DebugKeyValue.msg" + "msg/DebugValue.msg" + "msg/DebugVect.msg" + "msg/DifferentialPressure.msg" + "msg/DistanceSensor.msg" + "msg/Ekf2Timestamps.msg" + "msg/EstimatorGpsStatus.msg" + "msg/EscReport.msg" + "msg/EscStatus.msg" + "msg/EstimatorBaroBias.msg" + "msg/EstimatorEventFlags.msg" + "msg/EstimatorInnovations.msg" + "msg/EstimatorOpticalFlowVel.msg" + "msg/EstimatorSelectorStatus.msg" + "msg/EstimatorSensorBias.msg" + "msg/EstimatorStates.msg" + "msg/EstimatorStatus.msg" + "msg/EstimatorStatusFlags.msg" + "msg/Event.msg" + "msg/FailureDetectorStatus.msg" + "msg/FollowTarget.msg" + "msg/GeneratorStatus.msg" + "msg/GeofenceResult.msg" + "msg/GimbalDeviceAttitudeStatus.msg" + "msg/GimbalDeviceInformation.msg" + "msg/GimbalDeviceSetAttitude.msg" + "msg/GimbalManagerInformation.msg" + "msg/GimbalManagerSetAttitude.msg" + "msg/GimbalManagerSetManualControl.msg" + "msg/GimbalManagerStatus.msg" + "msg/GpsDump.msg" + "msg/GpsInjectData.msg" + "msg/HeaterStatus.msg" + "msg/HomePosition.msg" + "msg/HoverThrustEstimate.msg" + "msg/InputRc.msg" + "msg/InternalCombustionEngineStatus.msg" + "msg/IridiumsbdStatus.msg" + "msg/IrlockReport.msg" + "msg/LandingGear.msg" + "msg/LandingTargetInnovations.msg" + "msg/LandingTargetPose.msg" + "msg/LedControl.msg" + "msg/LoggerStatus.msg" + "msg/LogMessage.msg" + "msg/MagnetometerBiasEstimate.msg" + "msg/MagWorkerData.msg" + "msg/ManualControlSetpoint.msg" + "msg/ManualControlSwitches.msg" + "msg/MavlinkLog.msg" + "msg/MavlinkTunnel.msg" + "msg/Mission.msg" + "msg/MissionResult.msg" + "msg/MountOrientation.msg" + "msg/NavigatorMissionItem.msg" + "msg/NpfgStatus.msg" + "msg/ObstacleDistance.msg" + "msg/OffboardControlMode.msg" + "msg/OnboardComputerStatus.msg" + "msg/OpticalFlow.msg" + "msg/OrbitStatus.msg" + "msg/OrbTest.msg" + "msg/OrbTestLarge.msg" + "msg/OrbTestMedium.msg" + "msg/ParameterUpdate.msg" + "msg/Ping.msg" + "msg/PositionControllerLandingStatus.msg" + "msg/PositionControllerStatus.msg" + "msg/PositionSetpoint.msg" + "msg/PositionSetpointTriplet.msg" + "msg/PowerButtonState.msg" + "msg/PowerMonitor.msg" + "msg/PpsCapture.msg" + "msg/PwmInput.msg" + "msg/Px4ioStatus.msg" + "msg/RadioStatus.msg" + "msg/RateCtrlStatus.msg" + "msg/RcChannels.msg" + "msg/RcParameterMap.msg" + "msg/Rpm.msg" + "msg/RtlTimeEstimate.msg" + "msg/Safety.msg" + "msg/SatelliteInfo.msg" + "msg/SensorAccel.msg" + "msg/SensorAccelFifo.msg" + "msg/SensorBaro.msg" + "msg/SensorCombined.msg" + "msg/SensorCorrection.msg" + "msg/SensorGps.msg" + "msg/SensorGyro.msg" + "msg/SensorGyroFft.msg" + "msg/SensorGyroFifo.msg" + "msg/SensorHygrometer.msg" + "msg/SensorMag.msg" + "msg/SensorPreflightMag.msg" + "msg/SensorSelection.msg" + "msg/SensorsStatusImu.msg" + "msg/SystemPower.msg" + "msg/TakeoffStatus.msg" + "msg/TaskStackInfo.msg" + "msg/TecsStatus.msg" + "msg/TelemetryStatus.msg" + "msg/TestMotor.msg" + "msg/Timesync.msg" + "msg/TimesyncStatus.msg" + "msg/TrajectoryBezier.msg" + "msg/TrajectoryWaypoint.msg" + "msg/TransponderReport.msg" + "msg/TuneControl.msg" + "msg/UavcanParameterRequest.msg" + "msg/UavcanParameterValue.msg" + "msg/UlogStream.msg" + "msg/UlogStreamAck.msg" + "msg/VehicleAcceleration.msg" + "msg/VehicleAirData.msg" + "msg/VehicleAngularAcceleration.msg" + "msg/VehicleAngularAccelerationSetpoint.msg" + "msg/VehicleAngularVelocity.msg" + "msg/VehicleAttitude.msg" + "msg/VehicleAttitudeSetpoint.msg" + "msg/VehicleCommand.msg" + "msg/VehicleCommandAck.msg" + "msg/VehicleConstraints.msg" + "msg/VehicleControlMode.msg" + "msg/VehicleGlobalPosition.msg" + "msg/VehicleGpsPosition.msg" + "msg/VehicleImu.msg" + "msg/VehicleImuStatus.msg" + "msg/VehicleLandDetected.msg" + "msg/VehicleLocalPosition.msg" + "msg/VehicleLocalPositionSetpoint.msg" + "msg/VehicleMagnetometer.msg" + "msg/VehicleOdometry.msg" + "msg/VehicleRatesSetpoint.msg" + "msg/VehicleRoi.msg" + "msg/VehicleStatus.msg" + "msg/VehicleStatusFlags.msg" + "msg/VehicleThrustSetpoint.msg" + "msg/VehicleTorqueSetpoint.msg" + "msg/VehicleTrajectoryBezier.msg" + "msg/VehicleTrajectoryWaypoint.msg" + "msg/VtolVehicleStatus.msg" + "msg/WheelEncoders.msg" + "msg/Wind.msg" + "msg/YawEstimatorStatus.msg" +) diff --git a/platforms/ros2/cmake/px4_impl_os.cmake b/platforms/ros2/cmake/px4_impl_os.cmake new file mode 100644 index 0000000000..8580083651 --- /dev/null +++ b/platforms/ros2/cmake/px4_impl_os.cmake @@ -0,0 +1,148 @@ +############################################################################ +# +# Copyright (c) 2021 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +#============================================================================= +# +# Defined functions in this file +# +# OS Specific Functions +# +# * px4_posix_generate_builtin_commands +# +# Required OS Interface Functions +# +# * px4_os_add_flags +# * px4_os_determine_build_chip +# * px4_os_prebuild_targets +# + +#============================================================================= +# +# px4_posix_generate_builtin_commands +# +# This function generates the builtin_commands.c src for posix +# +# Usage: +# px4_posix_generate_builtin_commands( +# MODULE_LIST +# OUT ) +# +# Input: +# MODULE_LIST : list of modules +# +# Output: +# OUT : stem of generated apps.cpp/apps.h ("apps") +# +# Example: +# px4_posix_generate_builtin_commands( +# OUT MODULE_LIST px4_simple_app) +# +function(px4_posix_generate_builtin_commands) + px4_parse_function_args( + NAME px4_posix_generate_builtin_commands + ONE_VALUE OUT + MULTI_VALUE MODULE_LIST + REQUIRED MODULE_LIST OUT + ARGN ${ARGN}) + +endfunction() + +#============================================================================= +# +# px4_os_add_flags +# +# Set the posix build flags. +# +# Usage: +# px4_os_add_flags() +# +function(px4_os_add_flags) + + add_definitions( + -D__PX4_ROS2 + -D__PX4_POSIX + -Dnoreturn_function=__attribute__\(\(noreturn\)\) + ) + + include_directories(platforms/ros2/include) + +endfunction() + +#============================================================================= +# +# px4_os_determine_build_chip +# +# Sets PX4_CHIP and PX4_CHIP_MANUFACTURER. +# +# Usage: +# px4_os_determine_build_chip() +# +function(px4_os_determine_build_chip) + + # always use generic chip and chip manufacturer + set(PX4_CHIP "generic" CACHE STRING "PX4 Chip" FORCE) + set(PX4_CHIP_MANUFACTURER "generic" CACHE STRING "PX4 Chip Manufacturer" FORCE) + +endfunction() + +#============================================================================= +# +# px4_os_prebuild_targets +# +# This function generates os dependent targets +# +# Usage: +# px4_os_prebuild_targets( +# OUT +# BOARD +# ) +# +# Input: +# BOARD : board +# +# Output: +# OUT : the target list +# +# Example: +# px4_os_prebuild_targets(OUT target_list BOARD px4_fmu-v2) +# +function(px4_os_prebuild_targets) + px4_parse_function_args( + NAME px4_os_prebuild_targets + ONE_VALUE OUT BOARD + REQUIRED OUT + ARGN ${ARGN}) + + add_library(prebuild_targets INTERFACE) + +endfunction() diff --git a/src/lib/systemlib/conversions.h b/platforms/ros2/include/hrt_work.h similarity index 68% rename from src/lib/systemlib/conversions.h rename to platforms/ros2/include/hrt_work.h index dc383e770f..2394ec0957 100644 --- a/src/lib/systemlib/conversions.h +++ b/platforms/ros2/include/hrt_work.h @@ -1,7 +1,6 @@ /**************************************************************************** * - * Copyright (C) 2012 PX4 Development Team. All rights reserved. - * Author: Lorenz Meier + * Copyright (C) 2015 Mark Charlebois. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,30 +31,34 @@ * ****************************************************************************/ -/** - * @file conversions.h - * Definition of commonly used conversions. - * - * Includes bit / byte / geo representation and unit conversions. - */ +#pragma once -#ifndef CONVERSIONS_H_ -#define CONVERSIONS_H_ -#include -#include +#include +#include +#include + +#include __BEGIN_DECLS -/** - * Converts a signed 16 bit integer from big endian to little endian. - * - * This function is for commonly used 16 bit big endian sensor data, - * delivered by driver routines as two 8 bit numbers in big endian order. - * Common vendors with big endian representation are Invense, Bosch and - * Honeywell. ST micro devices tend to use a little endian representation. - */ -__EXPORT int16_t int16_t_from_bytes(uint8_t bytes[]); +extern px4_sem_t _hrt_work_lock; +extern struct wqueue_s g_hrt_work; + +void hrt_work_queue_init(void); +int hrt_work_queue(struct work_s *work, worker_t worker, void *arg, uint32_t usdelay); +void hrt_work_cancel(struct work_s *work); + +static inline void hrt_work_lock(void); +static inline void hrt_work_lock() +{ + px4_sem_wait(&_hrt_work_lock); +} + +static inline void hrt_work_unlock(void); +static inline void hrt_work_unlock() +{ + px4_sem_post(&_hrt_work_lock); +} __END_DECLS -#endif /* CONVERSIONS_H_ */ diff --git a/platforms/ros2/include/px4_platform_common/Node.hpp b/platforms/ros2/include/px4_platform_common/Node.hpp new file mode 100644 index 0000000000..496600badd --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/Node.hpp @@ -0,0 +1,42 @@ + +#pragma once + +#include "rclcpp/rclcpp.hpp" + +namespace px4 +{ + +class Node : public rclcpp::Node +{ +public: + explicit Node(const char *node_name) : + rclcpp::Node(node_name) + { + + } + + + ~Node() override = default; + +private: + +}; + +}; + + +// int main(int argc, char *argv[]) +// { +// rclcpp::init(argc, argv); +// rclcpp::spin(std::make_shared()); +// rclcpp::shutdown(); +// return 0; +// } + + +// #include "rclcpp_components/register_node_macro.hpp" + +// // Register the component with class_loader. +// // This acts as a sort of entry point, allowing the component to be discoverable when its library +// // is being loaded into a running process. +// RCLCPP_COMPONENTS_REGISTER_NODE(composition::Client) diff --git a/platforms/ros2/include/px4_platform_common/defines.h b/platforms/ros2/include/px4_platform_common/defines.h new file mode 100644 index 0000000000..5667e5ef9d --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/defines.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * + * Copyright (c) 2014 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file defines.h + * + * Generally used magic defines + */ + +#pragma once + +/**************************************************************************** + * Defines for all platforms. + ****************************************************************************/ + +#define PX4_ERROR (-1) +#define PX4_OK 0 + +/* Define PX4_ISFINITE */ +#ifdef __cplusplus +constexpr bool PX4_ISFINITE(float x) { return __builtin_isfinite(x); } +constexpr bool PX4_ISFINITE(double x) { return __builtin_isfinite(x); } +#endif /* __cplusplus */ diff --git a/platforms/ros2/include/px4_platform_common/log.h b/platforms/ros2/include/px4_platform_common/log.h new file mode 100644 index 0000000000..f9cb4820d5 --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/log.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * Copyright (C) 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +//#include + +#define PX4_INFO(X) printf(X) //RCLCPP_INFO(get_logger(), X) +#define PX4_WARN(X) printf(X) //RCLCPP_WARN(get_logger(), X) +#define PX4_ERR(X) printf(X) //RCLCPP_ERR(get_logger(), X) +#define PX4_DEBUG(...) printf(X) //RCLCPP_DEBUG(get_logger(), __VA_ARGS__) diff --git a/platforms/ros2/include/px4_platform_common/module.h b/platforms/ros2/include/px4_platform_common/module.h new file mode 100644 index 0000000000..436c86ed92 --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/module.h @@ -0,0 +1,417 @@ +/**************************************************************************** + * + * Copyright (c) 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file px4_module.h + */ + +#pragma once + +#ifdef __cplusplus + +#include + +#include "rclcpp/rclcpp.hpp" + +/** + * @class ModuleBase + * Base class for modules, implementing common functionality, + * such as 'start', 'stop' and 'status' commands. + * Currently does not support modules which allow multiple instances, + * such as mavlink. + * + * The class is implemented as curiously recurring template pattern (CRTP). + * It allows to have a static object in the base class that is different for + * each module type, and call static methods from the base class. + * + * @note Required methods for a derived class: + * When running in its own thread: + * static int task_spawn(int argc, char *argv[]) + * { + * // call px4_task_spawn_cmd() with &run_trampoline as startup method + * // optional: wait until _object is not null, which means the task got initialized (use a timeout) + * // set _task_id and return 0 + * // on error return != 0 (and _task_id must be -1) + * } + * + * static T *instantiate(int argc, char *argv[]) + * { + * // this is called from within the new thread, from run_trampoline() + * // parse the arguments + * // create a new object T & return it + * // or return nullptr on error + * } + * + * static int custom_command(int argc, char *argv[]) + * { + * // support for custom commands + * // it none are supported, just do: + * return print_usage("unrecognized command"); + * } + * + * static int print_usage(const char *reason = nullptr) + * { + * // use the PRINT_MODULE_* methods... + * } + * + * When running on the work queue: + * - custom_command & print_usage as above + * static int task_spawn(int argc, char *argv[]) { + * // parse the arguments + * // set _object (here or from the work_queue() callback) + * // call work_queue() (with a custom cycle trampoline) + * // optional: wait until _object is not null, which means the task got initialized (use a timeout) + * // set _task_id to task_id_is_work_queue and return 0 + * // on error return != 0 (and _task_id must be -1) + * } + */ +template +class ModuleBase : public rclcpp::Node +{ +public: + ModuleBase() : Node(MODULE_NAME) + { + + RCLCPP_INFO(get_logger(), "constructing %lu", hrt_absolute_time()); + } + + virtual ~ModuleBase() {} + + /** + * @brief main Main entry point to the module that should be + * called directly from the module's main method. + * @param argc The task argument count. + * @param argc Pointer to the task argument variable array. + * @return Returns 0 iff successful, -1 otherwise. + */ + static int main(int argc, char *argv[]) + { + + + return ret; + } + + /** + * @brief Print the status if the module is running. This can be overridden by the module to provide + * more specific information. + * @return Returns 0 iff successful, -1 otherwise. + */ + virtual int print_status() + { + PX4_INFO("running"); + return 0; + } + + /** + * @brief Main loop method for modules running in their own thread. Called from run_trampoline(). + * This method must return when should_exit() returns true. + */ + virtual void run() + { + } + + /** + * @brief Returns the status of the module. + * @return Returns true if the module is running, false otherwise. + */ + static bool is_running() + { + return _task_id != -1; + } + +protected: + + /** + * @brief Tells the module to stop (used from outside or inside the module thread). + */ + virtual void request_stop() + { + _task_should_exit.store(true); + } + + /** + * @brief Checks if the module should stop (used within the module thread). + * @return Returns True iff successful, false otherwise. + */ + bool should_exit() const + { + return _task_should_exit.load(); + } + + /** + * @brief Exits the module and delete the object. Called from within the module's thread. + * For work queue modules, this needs to be called from the derived class in the + * cycle method, when should_exit() returns true. + */ + static void exit_and_cleanup() + { + // Take the lock here: + // - if startup fails and we're faster than the parent thread, it will set + // _task_id and subsequently it will look like the task is running. + // - deleting the object must take place inside the lock. + lock_module(); + + delete _object.load(); + _object.store(nullptr); + + _task_id = -1; // Signal a potentially waiting thread for the module to exit that it can continue. + unlock_module(); + } + + /** + * @brief Waits until _object is initialized, (from the new thread). This can be called from task_spawn(). + * @return Returns 0 iff successful, -1 on timeout or otherwise. + */ + static int wait_until_running(int timeout_ms = 1000) + { + int i = 0; + + do { + px4_usleep(2000); + + } while (!_object.load() && ++i < timeout_ms / 2); + + if (i >= timeout_ms / 2) { + PX4_ERR("Timed out while waiting for thread to start"); + return -1; + } + + return 0; + } + + /** + * @brief Get the module's object instance, (this is null if it's not running). + */ + static T *get_instance() + { + return (T *)_object.load(); + } + + /** + * @var _object Instance if the module is running. + * @note There will be one instance for each template type. + */ + static px4::atomic _object; + + /** @var _task_id The task handle: -1 = invalid, otherwise task is assumed to be running. */ + static int _task_id; + + /** @var task_id_is_work_queue Value to indicate if the task runs on the work queue. */ + static constexpr const int task_id_is_work_queue = -2; + +private: + /** + * @brief lock_module Mutex to lock the module thread. + */ + static void lock_module() + { + pthread_mutex_lock(&px4_modules_mutex); + } + + /** + * @brief unlock_module Mutex to unlock the module thread. + */ + static void unlock_module() + { + pthread_mutex_unlock(&px4_modules_mutex); + } + + /** @var _task_should_exit Boolean flag to indicate if the task should exit. */ + px4::atomic_bool _task_should_exit{false}; +}; + +template +px4::atomic ModuleBase::_object{nullptr}; + +template +int ModuleBase::_task_id = -1; + + +#endif /* __cplusplus */ + + +__BEGIN_DECLS + +/** + * @brief Module documentation and command usage help methods. + * These are extracted with the Tools/px_process_module_doc.py + * script and must be kept in sync. + */ + +#ifdef __PX4_NUTTX +/** + * @note Disable module description on NuttX to reduce Flash usage. + * There's a GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55971), preventing us to use + * a macro, but GCC will remove the string as well with this empty inline method. + * @param description The provided functionality of the module and potentially the most important parameters. + */ +static inline void PRINT_MODULE_DESCRIPTION(const char *description) {} +#else + +/** + * @brief Prints module documentation (will also be used for online documentation). It uses Markdown syntax + * and should include these sections: + * - ### Description + * Provided functionality of the module and potentially the most important parameters. + * - ### Implementation + * High-level implementation overview + * - ### Examples + * Examples how to use the CLI interface (if it's non-trivial) + * + * In addition to the Markdown syntax, a line beginning with '$ ' can be used to mark a command: + * $ module start -p param + */ +__EXPORT void PRINT_MODULE_DESCRIPTION(const char *description); +#endif + +/** + * @brief Prints the command name. + * @param executable_name: command name used in scripts & CLI + * @param category one of: driver, estimator, controller, system, communication, command, template + */ +__EXPORT void PRINT_MODULE_USAGE_NAME(const char *executable_name, const char *category); + +/** + * @brief Specify a subcategory (optional). + * @param subcategory e.g. if the category is 'driver', subcategory can be 'distance_sensor' + */ +__EXPORT void PRINT_MODULE_USAGE_SUBCATEGORY(const char *subcategory); + +/** + * @brief Prints the name for a command without any sub-commands (@see PRINT_MODULE_USAGE_NAME()). + */ +__EXPORT void PRINT_MODULE_USAGE_NAME_SIMPLE(const char *executable_name, const char *category); + + +/** + * @brief Prints a command with a short description what it does. + */ +__EXPORT void PRINT_MODULE_USAGE_COMMAND_DESCR(const char *name, const char *description); + +#define PRINT_MODULE_USAGE_COMMAND(name) \ + PRINT_MODULE_USAGE_COMMAND_DESCR(name, NULL); + +/** + * @brief Prints the default commands: stop & status. + */ +#define PRINT_MODULE_USAGE_DEFAULT_COMMANDS() \ + PRINT_MODULE_USAGE_COMMAND("stop"); \ + PRINT_MODULE_USAGE_COMMAND_DESCR("status", "print status info"); + +/** + * Print default params for I2C or SPI drivers + * @param i2c_support true if the driver supports I2C + * @param spi_support true if the driver supports SPI + */ +__EXPORT void PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(bool i2c_support, bool spi_support); + +/** + * Configurable I2C address (via -a

) + */ +__EXPORT void PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(uint8_t default_address); + +/** + * -k flag + */ +__EXPORT void PRINT_MODULE_USAGE_PARAMS_I2C_KEEP_RUNNING_FLAG(void); + +/** @note Each of the PRINT_MODULE_USAGE_PARAM_* methods apply to the previous PRINT_MODULE_USAGE_COMMAND_DESCR(). */ + +/** + * @brief Prints an integer parameter. + * @param option_char The option character. + * @param default_val The parameter default value (set to -1 if not applicable). + * @param min_val The parameter minimum value. + * @param max_val The parameter value. + * @param description Pointer to the usage description. + * @param is_optional true if this parameter is optional + */ +__EXPORT void PRINT_MODULE_USAGE_PARAM_INT(char option_char, int default_val, int min_val, int max_val, + const char *description, bool is_optional); + +/** + * @brief Prints a float parameter. + * @note See PRINT_MODULE_USAGE_PARAM_INT(). + * @param default_val The parameter default value (set to NaN if not applicable). + * @param min_val The parameter minimum value. + * @param max_val The parameter maximum value. + * @param description Pointer to the usage description. Pointer to the usage description. + * @param is_optional true if this parameter is optional + */ +__EXPORT void PRINT_MODULE_USAGE_PARAM_FLOAT(char option_char, float default_val, float min_val, float max_val, + const char *description, bool is_optional); + +/** + * @brief Prints a flag parameter, without any value. + * @note See PRINT_MODULE_USAGE_PARAM_INT(). + * @param option_char The option character. + * @param description Pointer to the usage description. + * @param is_optional true if this parameter is optional + */ +__EXPORT void PRINT_MODULE_USAGE_PARAM_FLAG(char option_char, const char *description, bool is_optional); + +/** + * @brief Prints a string parameter. + * @param option_char The option character. + * @param default_val The default value, can be nullptr. + * @param values The valid values, it has one of the following forms: + * - nullptr: leave unspecified, or any value is valid + * - "" or "": a file or more specifically a device file (eg. serial device) + * - "": uORB topic name + * - " []": a list of values + * - "on|off": a concrete set of valid strings separated by "|". + * @param description Pointer to the usage description. + * @param is_optional True iff this parameter is optional. + */ +__EXPORT void PRINT_MODULE_USAGE_PARAM_STRING(char option_char, const char *default_val, const char *values, + const char *description, bool is_optional); + +/** + * @brief Prints a comment, that applies to the next arguments or parameters. For example to indicate that + * a parameter applies to several or all commands. + * @param comment + */ +__EXPORT void PRINT_MODULE_USAGE_PARAM_COMMENT(const char *comment); + + +/** + * @brief Prints the definition for an argument, which does not have the typical -p form, + * but for example 'param set ' + * @param values eg. "", " " or " []" + * @param description Pointer to the usage description. + * @param is_optional true if this parameter is optional + */ +__EXPORT void PRINT_MODULE_USAGE_ARG(const char *values, const char *description, bool is_optional); + + +__END_DECLS diff --git a/msg/templates/px4/uorb/msg.h.em b/platforms/ros2/include/px4_platform_common/module_params.h similarity index 53% rename from msg/templates/px4/uorb/msg.h.em rename to platforms/ros2/include/px4_platform_common/module_params.h index deed11912d..d2e518f0ce 100644 --- a/msg/templates/px4/uorb/msg.h.em +++ b/platforms/ros2/include/px4_platform_common/module_params.h @@ -1,23 +1,6 @@ -@############################################### -@# -@# PX4 ROS compatible message source code -@# generation for C++ -@# -@# This file generates the multiplatform wrapper -@# -@# EmPy template for generating .h files -@# Based on the original template for ROS -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - file_name_in (String) Source file -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### /**************************************************************************** * - * Copyright (C) 2013-2015 PX4 Development Team. All rights reserved. + * Copyright (c) 2018 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,52 +31,66 @@ * ****************************************************************************/ -/* Auto-generated by genmsg_cpp from file @file_name_in */ - -@{ -import genmsg.msgs - - -native_type = '%s_s'%spec.short_name -topic_name = spec.short_name -}@ +/** + * @file px4_module_params.h + * + * @class ModuleParams is a C++ base class for modules/classes using configuration parameters. + */ #pragma once -@############################## -@# Generic Includes -@############################## -#include -#include "platforms/px4_message.h" +#include -@############################## -@# Class -@############################## +#include "param.h" -namespace px4 -{ -@[for multi_topic in topics]@ -@{ -cpp_class = 'px4_%s'%multi_topic -}@ - - -class __EXPORT @(cpp_class) : - public PX4Message<@(native_type)> +class ModuleParams : public ListNode { public: - @(cpp_class)() : - PX4Message<@(native_type)>() - {} - @(cpp_class)(@(native_type) msg) : - PX4Message<@(native_type)>(msg) - {} + ModuleParams(ModuleParams *parent) + { + setParent(parent); + } - ~@(cpp_class)() {} - - static PX4TopicHandle handle() {return ORB_ID(@(multi_topic));} -}; -@[end for] + /** + * @brief Sets the parent module. This is typically not required, + * only in cases where the parent cannot be set via constructor. + */ + void setParent(ModuleParams *parent) + { + if (parent) { + parent->_children.add(this); + } + } + virtual ~ModuleParams() = default; + + // Disallow copy construction and move assignment. + ModuleParams(const ModuleParams &) = delete; + ModuleParams &operator=(const ModuleParams &) = delete; + ModuleParams(ModuleParams &&) = delete; + ModuleParams &operator=(ModuleParams &&) = delete; + +protected: + /** + * @brief Call this method whenever the module gets a parameter change notification. + * It will automatically call updateParams() for all children, which then call updateParamsImpl(). + */ + virtual void updateParams() + { + for (const auto &child : _children) { + child->updateParams(); + } + + updateParamsImpl(); + } + + /** + * @brief The implementation for this is generated with the macro DEFINE_PARAMETERS() + */ + virtual void updateParamsImpl() {} + +private: + /** @list _children The module parameter list of inheriting classes. */ + List _children; }; diff --git a/platforms/ros2/include/px4_platform_common/param.h b/platforms/ros2/include/px4_platform_common/param.h new file mode 100644 index 0000000000..3a2aa80aac --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/param.h @@ -0,0 +1,452 @@ +/**************************************************************************** + * + * Copyright (c) 2018 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file param.h + * + * C++ Parameter class + */ + +#pragma once + +#include "param_macros.h" +#include +#include + +#include + +#define _DEFINE_SINGLE_PARAMETER(x) \ + do_not_explicitly_use_this_namespace::PAIR(x); + +#define _CALL_UPDATE(x) \ + STRIP(x).update(); + +// define the parameter update method, which will update all parameters. +// It is marked as 'final', so that wrong usages lead to a compile error (see below) +#define _DEFINE_PARAMETER_UPDATE_METHOD(...) \ + protected: \ + void updateParamsImpl() final { \ + APPLY_ALL(_CALL_UPDATE, __VA_ARGS__) \ + } \ + private: + +// Define a list of parameters. This macro also creates code to update parameters. +// If you get a compile error like: +// error: virtual function ‘virtual void ::updateParamsImpl()’ +// It means you have a custom inheritance tree (at least one class with params that inherits from another +// class with params) and you need to use DEFINE_PARAMETERS_CUSTOM_PARENT() for **all** classes in +// that tree. +#define DEFINE_PARAMETERS(...) \ + APPLY_ALL(_DEFINE_SINGLE_PARAMETER, __VA_ARGS__) \ + _DEFINE_PARAMETER_UPDATE_METHOD(__VA_ARGS__) + + +#define _DEFINE_PARAMETER_UPDATE_METHOD_CUSTOM_PARENT(parent_class, ...) \ + protected: \ + void updateParamsImpl() override { \ + parent_class::updateParamsImpl(); \ + APPLY_ALL(_CALL_UPDATE, __VA_ARGS__) \ + } \ + private: + +#define DEFINE_PARAMETERS_CUSTOM_PARENT(parent_class, ...) \ + APPLY_ALL(_DEFINE_SINGLE_PARAMETER, __VA_ARGS__) \ + _DEFINE_PARAMETER_UPDATE_METHOD_CUSTOM_PARENT(parent_class, __VA_ARGS__) + + + +// This namespace never needs to be used directly. Use the DEFINE_PARAMETERS_CUSTOM_PARENT and +// DEFINE_PARAMETERS macros instead (the Param classes don't depend on using the macro, the macro just +// makes sure that update() is automatically called). +namespace do_not_explicitly_use_this_namespace +{ + +template +class Param +{ +}; + +// We use partial template specialization for each param type. This is only supported for classes, not individual methods, +// which is why we have to repeat the whole class +template +class Param +{ +public: + // static type-check + static_assert(px4::parameters_type[(int)p] == PARAM_TYPE_FLOAT, "parameter type must be float"); + + Param() + { + //param_set_used(handle()); + update(); + } + + float get() const { return _val; } + + const float &reference() const { return _val; } + + /// Store the parameter value to the parameter storage (@see param_set()) + bool commit() const + { + return true; // TODO + //return param_set(handle(), &_val) == 0; + } + + /// Store the parameter value to the parameter storage, w/o notifying the system (@see param_set_no_notification()) + bool commit_no_notification() const + { + return true; // TODO + //return param_set_no_notification(handle(), &_val) == 0; + } + + /// Set and commit a new value. Returns true if the value changed. + bool commit_no_notification(float val) + { + if (fabsf(val - _val) > FLT_EPSILON) { + set(val); + commit_no_notification(); + return true; + } + + return false; + } + + void set(float val) { _val = val; } + + void reset() + { + //param_reset_no_notification(handle()); // TODO + update(); + } + + bool update() + { + return true; // TODO + //return param_get(handle(), &_val) == 0; + } + + //param_t handle() const { return param_handle(p); } +private: + float _val; +}; + +// external version +template +class Param +{ +public: + // static type-check + static_assert(px4::parameters_type[(int)p] == PARAM_TYPE_FLOAT, "parameter type must be float"); + + Param(float &external_val) + : _val(external_val) + { + //param_set_used(handle()); // TODO + update(); + } + + float get() const { return _val; } + + const float &reference() const { return _val; } + + /// Store the parameter value to the parameter storage (@see param_set()) + bool commit() const + { + //return param_set(handle(), &_val) == 0; // TODO + return true; + } + + /// Store the parameter value to the parameter storage, w/o notifying the system (@see param_set_no_notification()) + bool commit_no_notification() const + { + return true; + //return param_set_no_notification(handle(), &_val) == 0; + } + + /// Set and commit a new value. Returns true if the value changed. + bool commit_no_notification(float val) + { + if (fabsf(val - _val) > FLT_EPSILON) { + set(val); + commit_no_notification(); + return true; + } + + return false; + } + + void set(float val) { _val = val; } + + void reset() + { + //param_reset_no_notification(handle()); + update(); + } + + bool update() + { + return true; + //return param_get(handle(), &_val) == 0; + } + + //param_t handle() const { return param_handle(p); } +private: + float &_val; +}; + +template +class Param +{ +public: + // static type-check + static_assert(px4::parameters_type[(int)p] == PARAM_TYPE_INT32, "parameter type must be int32_t"); + + Param() + { + //param_set_used(handle()); + update(); + } + + int32_t get() const { return _val; } + + const int32_t &reference() const { return _val; } + + /// Store the parameter value to the parameter storage (@see param_set()) + bool commit() const + { + return true; + //return param_set(handle(), &_val) == 0; + } + + /// Store the parameter value to the parameter storage, w/o notifying the system (@see param_set_no_notification()) + bool commit_no_notification() const + { + //return param_set_no_notification(handle(), &_val) == 0; + return true; + } + + /// Set and commit a new value. Returns true if the value changed. + bool commit_no_notification(int32_t val) + { + if (val != _val) { + set(val); + commit_no_notification(); + return true; + } + + return false; + } + + void set(int32_t val) { _val = val; } + + void reset() + { + //param_reset_no_notification(handle()); + update(); + } + + bool update() + { + //return param_get(handle(), &_val) == 0; + return true; + } + + //param_t handle() const { return param_handle(p); } +private: + int32_t _val; +}; + +//external version +template +class Param +{ +public: + // static type-check + static_assert(px4::parameters_type[(int)p] == PARAM_TYPE_INT32, "parameter type must be int32_t"); + + Param(int32_t &external_val) + : _val(external_val) + { + //param_set_used(handle()); + update(); + } + + int32_t get() const { return _val; } + + const int32_t &reference() const { return _val; } + + /// Store the parameter value to the parameter storage (@see param_set()) + bool commit() const + { + return true; + //return param_set(handle(), &_val) == 0; + } + + /// Store the parameter value to the parameter storage, w/o notifying the system (@see param_set_no_notification()) + bool commit_no_notification() const + { + return true; + //return param_set_no_notification(handle(), &_val) == 0; + } + + /// Set and commit a new value. Returns true if the value changed. + bool commit_no_notification(int32_t val) + { + if (val != _val) { + set(val); + commit_no_notification(); + return true; + } + + return false; + } + + void set(int32_t val) { _val = val; } + + void reset() + { + //param_reset_no_notification(handle()); + update(); + } + + bool update() + { + //return param_get(handle(), &_val) == 0; + return true; + } + + //param_t handle() const { return param_handle(p); } +private: + int32_t &_val; +}; + +template +class Param +{ +public: + // static type-check + static_assert(px4::parameters_type[(int)p] == PARAM_TYPE_INT32, "parameter type must be int32_t"); + + Param() + { + //param_set_used(handle()); + update(); + } + + bool get() const { return _val; } + + const bool &reference() const { return _val; } + + /// Store the parameter value to the parameter storage (@see param_set()) + bool commit() const + { + int32_t value_int = (int32_t)_val; + //return param_set(handle(), &value_int) == 0; + return true; + } + + /// Store the parameter value to the parameter storage, w/o notifying the system (@see param_set_no_notification()) + bool commit_no_notification() const + { + int32_t value_int = (int32_t)_val; + //return param_set_no_notification(handle(), &value_int) == 0; + return true; + } + + /// Set and commit a new value. Returns true if the value changed. + bool commit_no_notification(bool val) + { + if (val != _val) { + set(val); + commit_no_notification(); + return true; + } + + return false; + } + + void set(bool val) { _val = val; } + + void reset() + { + //param_reset_no_notification(handle()); + update(); + } + + bool update() + { + int32_t value_int; + int ret = 0;//param_get(handle(), &value_int); + + if (ret == 0) { + _val = value_int != 0; + return true; + } + + return false; + } + + //param_t handle() const { return param_handle(p); } +private: + bool _val; +}; + +template +using ParamFloat = Param; + +template +using ParamInt = Param; + +template +using ParamExtFloat = Param; + +template +using ParamExtInt = Param; + +template +using ParamBool = Param; + +} /* namespace do_not_explicitly_use_this_namespace */ + + +// Raise an appropriate compile error if a Param class is used directly (just to simplify debugging) +template +class ParamInt +{ + static_assert((int)p &&false, "Do not use this class directly, use the DEFINE_PARAMETERS macro instead"); +}; +template +class ParamFloat +{ + static_assert((int)p &&false, "Do not use this class directly, use the DEFINE_PARAMETERS macro instead"); +}; diff --git a/platforms/ros2/include/px4_platform_common/param_macros.h b/platforms/ros2/include/px4_platform_common/param_macros.h new file mode 100644 index 0000000000..6b27b81a23 --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/param_macros.h @@ -0,0 +1,250 @@ +/**************************************************************************** + * + * Copyright (c) 2018 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file param_macros.h + * + * Helper macros used by px4_param.h + */ + +#pragma once + + +#define APPLY0(t) +#define APPLY1(t, aaa) t(aaa) +#define APPLY2(t, aaa, aab) t(aaa) t(aab) +#define APPLY3(t, aaa, aab, aac) t(aaa) t(aab) t(aac) +#define APPLY4(t, aaa, aab, aac, aad) t(aaa) t(aab) t(aac) t(aad) +#define APPLY5(t, aaa, aab, aac, aad, aae) t(aaa) t(aab) t(aac) t(aad) t(aae) +#define APPLY6(t, aaa, aab, aac, aad, aae, aaf) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) +#define APPLY7(t, aaa, aab, aac, aad, aae, aaf, aag) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) +#define APPLY8(t, aaa, aab, aac, aad, aae, aaf, aag, aah) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) +#define APPLY9(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) +#define APPLY10(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) +#define APPLY11(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) +#define APPLY12(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) +#define APPLY13(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) +#define APPLY14(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) +#define APPLY15(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) +#define APPLY16(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) +#define APPLY17(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) +#define APPLY18(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) +#define APPLY19(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) +#define APPLY20(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) +#define APPLY21(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) +#define APPLY22(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) +#define APPLY23(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) +#define APPLY24(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) +#define APPLY25(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) +#define APPLY26(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) +#define APPLY27(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) +#define APPLY28(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) +#define APPLY29(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) +#define APPLY30(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) +#define APPLY31(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) +#define APPLY32(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) +#define APPLY33(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) +#define APPLY34(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) +#define APPLY35(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) +#define APPLY36(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) +#define APPLY37(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) +#define APPLY38(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) +#define APPLY39(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) +#define APPLY40(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) +#define APPLY41(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) +#define APPLY42(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) +#define APPLY43(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) +#define APPLY44(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) +#define APPLY45(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) +#define APPLY46(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) +#define APPLY47(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) +#define APPLY48(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) +#define APPLY49(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) +#define APPLY50(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) +#define APPLY51(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) +#define APPLY52(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) +#define APPLY53(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) +#define APPLY54(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) +#define APPLY55(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) +#define APPLY56(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) +#define APPLY57(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) +#define APPLY58(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) +#define APPLY59(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) +#define APPLY60(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) +#define APPLY61(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) +#define APPLY62(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) +#define APPLY63(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) +#define APPLY64(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) +#define APPLY65(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) +#define APPLY66(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) +#define APPLY67(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) +#define APPLY68(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) +#define APPLY69(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) +#define APPLY70(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) +#define APPLY71(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) +#define APPLY72(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) +#define APPLY73(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) +#define APPLY74(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) +#define APPLY75(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) +#define APPLY76(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) +#define APPLY77(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) +#define APPLY78(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) +#define APPLY79(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) +#define APPLY80(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) +#define APPLY81(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) +#define APPLY82(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) +#define APPLY83(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) +#define APPLY84(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) +#define APPLY85(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) +#define APPLY86(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) +#define APPLY87(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) +#define APPLY88(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) +#define APPLY89(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) +#define APPLY90(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) +#define APPLY91(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) +#define APPLY92(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) +#define APPLY93(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) +#define APPLY94(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) +#define APPLY95(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) +#define APPLY96(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) +#define APPLY97(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) +#define APPLY98(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) +#define APPLY99(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) +#define APPLY100(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) +#define APPLY101(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) +#define APPLY102(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) +#define APPLY103(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) +#define APPLY104(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) +#define APPLY105(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) +#define APPLY106(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) +#define APPLY107(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) +#define APPLY108(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) +#define APPLY109(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) +#define APPLY110(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) +#define APPLY111(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) +#define APPLY112(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) +#define APPLY113(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) +#define APPLY114(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) +#define APPLY115(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) +#define APPLY116(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) +#define APPLY117(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) +#define APPLY118(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) +#define APPLY119(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) +#define APPLY120(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) +#define APPLY121(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) +#define APPLY122(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) +#define APPLY123(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) +#define APPLY124(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) +#define APPLY125(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) +#define APPLY126(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) +#define APPLY127(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) +#define APPLY128(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) +#define APPLY129(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) +#define APPLY130(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) +#define APPLY131(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) +#define APPLY132(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) +#define APPLY133(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) +#define APPLY134(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) +#define APPLY135(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) +#define APPLY136(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) +#define APPLY137(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) +#define APPLY138(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) +#define APPLY139(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) +#define APPLY140(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) +#define APPLY141(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) +#define APPLY142(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) +#define APPLY143(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) +#define APPLY144(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) +#define APPLY145(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn, afo) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) t(afo) +#define APPLY146(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn, afo, afp) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) t(afo) t(afp) +#define APPLY147(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn, afo, afp, afq) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) t(afo) t(afp) t(afq) +#define APPLY148(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn, afo, afp, afq, afr) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) t(afo) t(afp) t(afq) t(afr) +#define APPLY149(t, aaa, aab, aac, aad, aae, aaf, aag, aah, aai, aaj, aak, aal, aam, aan, aao, aap, aaq, aar, aas, aat, aau, aav, aaw, aax, aay, aaz, aba, abb, abc, abd, abe, abf, abg, abh, abi, abj, abk, abl, abm, abn, abo, abp, abq, abr, abs, abt, abu, abv, abw, abx, aby, abz, aca, acb, acc, acd, ace, acf, acg, ach, aci, acj, ack, acl, acm, acn, aco, acp, acq, acr, acs, act, acu, acv, acw, acx, acy, acz, ada, adb, adc, add, ade, adf, adg, adh, adi, adj, adk, adl, adm, adn, ado, adp, adq, adr, ads, adt, adu, adv, adw, adx, ady, adz, aea, aeb, aec, aed, aee, aef, aeg, aeh, aei, aej, aek, ael, aem, aen, aeo, aep, aeq, aer, aes, aet, aeu, aev, aew, aex, aey, aez, afa, afb, afc, afd, afe, aff, afg, afh, afi, afj, afk, afl, afm, afn, afo, afp, afq, afr, afs) t(aaa) t(aab) t(aac) t(aad) t(aae) t(aaf) t(aag) t(aah) t(aai) t(aaj) t(aak) t(aal) t(aam) t(aan) t(aao) t(aap) t(aaq) t(aar) t(aas) t(aat) t(aau) t(aav) t(aaw) t(aax) t(aay) t(aaz) t(aba) t(abb) t(abc) t(abd) t(abe) t(abf) t(abg) t(abh) t(abi) t(abj) t(abk) t(abl) t(abm) t(abn) t(abo) t(abp) t(abq) t(abr) t(abs) t(abt) t(abu) t(abv) t(abw) t(abx) t(aby) t(abz) t(aca) t(acb) t(acc) t(acd) t(ace) t(acf) t(acg) t(ach) t(aci) t(acj) t(ack) t(acl) t(acm) t(acn) t(aco) t(acp) t(acq) t(acr) t(acs) t(act) t(acu) t(acv) t(acw) t(acx) t(acy) t(acz) t(ada) t(adb) t(adc) t(add) t(ade) t(adf) t(adg) t(adh) t(adi) t(adj) t(adk) t(adl) t(adm) t(adn) t(ado) t(adp) t(adq) t(adr) t(ads) t(adt) t(adu) t(adv) t(adw) t(adx) t(ady) t(adz) t(aea) t(aeb) t(aec) t(aed) t(aee) t(aef) t(aeg) t(aeh) t(aei) t(aej) t(aek) t(ael) t(aem) t(aen) t(aeo) t(aep) t(aeq) t(aer) t(aes) t(aet) t(aeu) t(aev) t(aew) t(aex) t(aey) t(aez) t(afa) t(afb) t(afc) t(afd) t(afe) t(aff) t(afg) t(afh) t(afi) t(afj) t(afk) t(afl) t(afm) t(afn) t(afo) t(afp) t(afq) t(afr) t(afs) + +#define NUM_ARGS_H1(dummy, x149, x148, x147, x146, x145, x144, x143, x142, x141, x140, x139, x138, x137, x136, x135, x134, x133, x132, x131, x130, x129, x128, x127, x126, x125, x124, x123, x122, x121, x120, x119, x118, x117, x116, x115, x114, x113, x112, x111, x110, x109, x108, x107, x106, x105, x104, x103, x102, x101, x100, x99, x98, x97, x96, x95, x94, x93, x92, x91, x90, x89, x88, x87, x86, x85, x84, x83, x82, x81, x80, x79, x78, x77, x76, x75, x74, x73, x72, x71, x70, x69, x68, x67, x66, x65, x64, x63, x62, x61, x60, x59, x58, x57, x56, x55, x54, x53, x52, x51, x50, x49, x48, x47, x46, x45, x44, x43, x42, x41, x40, x39, x38, x37, x36, x35, x34, x33, x32, x31, x30, x29, x28, x27, x26, x25, x24, x23, x22, x21, x20, x19, x18, x17, x16, x15, x14, x13, x12, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0, ...) x0 +#define NUM_ARGS(...) NUM_ARGS_H1(dummy, ##__VA_ARGS__, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define APPLY_ALL_H3(t, n, ...) APPLY##n(t, __VA_ARGS__) +#define APPLY_ALL_H2(t, n, ...) APPLY_ALL_H3(t, n, __VA_ARGS__) +#define APPLY_ALL(t, ...) APPLY_ALL_H2(t, NUM_ARGS(__VA_ARGS__), __VA_ARGS__) + +/* + * The above macros are auto-generated using the following python script: +#! /bin/python +# generate a C/C++ macro APPLY_ALL up to 'count' arguments +import sys + +count = 150 + +def output(s): + sys.stdout.write(s) + +def var_name(i): + """ get a variable name in the form abc from a counter i """ + b = ord('a') + return chr(b+((i/26/26) % 26)) + chr(b+((i/26) % 26)) + chr(b+(i % 26)) + +for i in range(count): + output("#define APPLY{:}(t".format(i)) + output(''.join([", {:}".format(var_name(j)) for j in range(i)])) + output(") ") + output(''.join(["t({:}) ".format(var_name(j)) for j in range(i)])) + output("\n") + +output('''\n#define NUM_ARGS_H1(dummy{:}, ...) x0 +'''.format(''.join([', x'+str(x) for x in reversed(range(count))]))) +output('''#define NUM_ARGS(...) NUM_ARGS_H1(dummy, ##__VA_ARGS__{:}) +'''.format(''.join([', '+str(x) for x in reversed(range(count))]))) +output(''' +#define APPLY_ALL_H3(t, n, ...) APPLY##n(t, __VA_ARGS__) +#define APPLY_ALL_H2(t, n, ...) APPLY_ALL_H3(t, n, __VA_ARGS__) +#define APPLY_ALL(t, ...) APPLY_ALL_H2(t, NUM_ARGS(__VA_ARGS__), __VA_ARGS__) +''') + + */ + + +// helper macros to handle macro arguments in the form: (type) name + +#define REM(...) __VA_ARGS__ +#define EAT(...) + +// Retrieve the type +#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,) +#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__) +#define DETAIL_TYPEOF_HEAD(x, ...) REM x +#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__), +// Strip off the type, get the name +#define STRIP(x) EAT x +// Show the type without parenthesis +#define PAIR(x) REM x diff --git a/msg/templates/uorb_microcdr/uORBTopics.hpp.em b/platforms/ros2/include/px4_platform_common/posix.h similarity index 82% rename from msg/templates/uorb_microcdr/uORBTopics.hpp.em rename to platforms/ros2/include/px4_platform_common/posix.h index 6eebd31544..be991c66d4 100644 --- a/msg/templates/uorb_microcdr/uORBTopics.hpp.em +++ b/platforms/ros2/include/px4_platform_common/posix.h @@ -1,14 +1,6 @@ -@############################################### -@# -@# EmPy template for generating uORBTopics.cpp file -@# for logging purposes -@# -@############################################### -@# Start of Template -@############################################### /**************************************************************************** * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. + * Copyright (c) 2015 Mark Charlebois. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,6 +31,10 @@ * ****************************************************************************/ -#pragma once +/** + * @file px4_posix.h + * + * Includes POSIX-like functions for virtual character devices + */ -#include +#pragma once diff --git a/msg/templates/uorb_microcdr/uORBTopics.cpp.em b/platforms/ros2/include/px4_platform_common/px4_config.h similarity index 82% rename from msg/templates/uorb_microcdr/uORBTopics.cpp.em rename to platforms/ros2/include/px4_platform_common/px4_config.h index 88dc5255d8..d17f2aff7c 100644 --- a/msg/templates/uorb_microcdr/uORBTopics.cpp.em +++ b/platforms/ros2/include/px4_platform_common/px4_config.h @@ -1,14 +1,7 @@ -@############################################### -@# -@# EmPy template for generating uORBTopics.cpp file -@# for logging purposes -@# -@############################################### -@# Start of Template -@############################################### + /**************************************************************************** * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. + * Copyright (C) 2015 Mark Charlebois. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,4 +32,9 @@ * ****************************************************************************/ -#include +/** + * @file px4_config.h + Configuration flags used in code. + */ + +#pragma once diff --git a/platforms/ros2/include/px4_platform_common/sem.h b/platforms/ros2/include/px4_platform_common/sem.h new file mode 100644 index 0000000000..6805844e78 --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/sem.h @@ -0,0 +1,108 @@ +/**************************************************************************** + * + * Copyright (c) 2016 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file sem.h + * + * Synchronization primitive: Semaphore + */ + +#pragma once + +#include + +#if !defined(__PX4_NUTTX) +/* Values for protocol attribute */ + +#define SEM_PRIO_NONE 0 +#define SEM_PRIO_INHERIT 1 +#define SEM_PRIO_PROTECT 2 +#define sem_setprotocol(s,p) +#endif + +#if (defined(__PX4_DARWIN) || defined(__PX4_CYGWIN) || defined(__PX4_POSIX) || defined(__PX4_ROS2)) && !defined(__PX4_QURT) + +__BEGIN_DECLS + +typedef struct { + pthread_mutex_t lock; + pthread_cond_t wait; + int value; +} px4_sem_t; + +__EXPORT int px4_sem_init(px4_sem_t *s, int pshared, unsigned value); +__EXPORT int px4_sem_setprotocol(px4_sem_t *s, int protocol); +__EXPORT int px4_sem_wait(px4_sem_t *s); +__EXPORT int px4_sem_trywait(px4_sem_t *sem); +__EXPORT int px4_sem_timedwait(px4_sem_t *sem, const struct timespec *abstime); +__EXPORT int px4_sem_post(px4_sem_t *s); +__EXPORT int px4_sem_getvalue(px4_sem_t *s, int *sval); +__EXPORT int px4_sem_destroy(px4_sem_t *s); + +__END_DECLS + +//#elif defined(__PX4_QURT) + +//typedef sem_t px4_sem_t; + +//#define px4_sem_init sem_init +//#define px4_sem_setprotocol sem_setprotocol +//#define px4_sem_wait sem_wait +//#define px4_sem_trywait sem_trywait +//#define px4_sem_post sem_post +//#define px4_sem_getvalue sem_getvalue +//#define px4_sem_destroy sem_destroy + +#else + +typedef sem_t px4_sem_t; + +__BEGIN_DECLS + +#define px4_sem_init sem_init +#define px4_sem_setprotocol sem_setprotocol +#define px4_sem_wait sem_wait +#define px4_sem_trywait sem_trywait +#define px4_sem_post sem_post +#define px4_sem_getvalue sem_getvalue +#define px4_sem_destroy sem_destroy + +#if defined(__PX4_QURT) +__EXPORT int px4_sem_timedwait(px4_sem_t *sem, const struct timespec *abstime); +#else +#define px4_sem_timedwait sem_timedwait +#endif + +__END_DECLS + +#endif diff --git a/platforms/ros2/include/px4_platform_common/tasks.h b/platforms/ros2/include/px4_platform_common/tasks.h new file mode 100644 index 0000000000..c4d3e9c6c7 --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/tasks.h @@ -0,0 +1,187 @@ +/**************************************************************************** + * + * Copyright (c) 2012-2017 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file px4_tasks.h + * + * @author Lorenz Meier + * @author Mark Charlebois (2015) + * + * Preserve existing task API call signature with OS abstraction + */ + +#pragma once + +#include + +#if defined(__PX4_NUTTX) +typedef int px4_task_t; + +#include +#define px4_prctl prctl + +/** Default scheduler type */ +#if CONFIG_RR_INTERVAL > 0 +# define SCHED_DEFAULT SCHED_RR +#else +# define SCHED_DEFAULT SCHED_FIFO +#endif + +#define px4_task_exit(x) _exit(x) + +#elif defined(__PX4_POSIX) || defined(__PX4_QURT) + +#include +#include + +/** Default scheduler type */ +#define SCHED_DEFAULT SCHED_FIFO + +#if defined(__PX4_LINUX) || defined(__PX4_DARWIN) || defined(__PX4_CYGWIN) || defined(__PX4_ROS2) + +#define SCHED_PRIORITY_MAX sched_get_priority_max(SCHED_FIFO) +#define SCHED_PRIORITY_MIN sched_get_priority_min(SCHED_FIFO) +#define SCHED_PRIORITY_DEFAULT (((sched_get_priority_max(SCHED_FIFO) - sched_get_priority_min(SCHED_FIFO)) / 2) + sched_get_priority_min(SCHED_FIFO)) + +#elif defined(__PX4_QURT) + +#define SCHED_PRIORITY_MAX 255 +#define SCHED_PRIORITY_MIN 0 +#define SCHED_PRIORITY_DEFAULT 20 + +#define SCHED_PRIORITY_MAX sched_get_priority_max(SCHED_FIFO) +#define SCHED_PRIORITY_MIN sched_get_priority_min(SCHED_FIFO) +#define SCHED_PRIORITY_DEFAULT (((sched_get_priority_max(SCHED_FIFO) - sched_get_priority_min(SCHED_FIFO)) / 2) + sched_get_priority_min(SCHED_FIFO)) + +#else +#error "No target OS defined" +#endif + +#if defined (__PX4_LINUX) +#include +#else +#define PR_SET_NAME 1 +#endif + +typedef int px4_task_t; + +typedef struct { + int argc; + char **argv; +} px4_task_args_t; + +#else +#error "No target OS defined" +#endif + +// PX4 work queue starting high priority +#define PX4_WQ_HP_BASE (SCHED_PRIORITY_MAX - 15) + +// Fast drivers - they need to run as quickly as possible to minimize control +// latency. +#define SCHED_PRIORITY_FAST_DRIVER (SCHED_PRIORITY_MAX - 0) + +// Actuator outputs should run as soon as the rate controller publishes +// the actuator controls topic +#define SCHED_PRIORITY_ACTUATOR_OUTPUTS (PX4_WQ_HP_BASE - 3) + +// Attitude controllers typically are in a blocking wait on driver data +// they should be the first to run on an update, using the current sensor +// data and the *previous* attitude reference from the position controller +// which typically runs at a slower rate +#define SCHED_PRIORITY_ATTITUDE_CONTROL (PX4_WQ_HP_BASE - 4) + +// Estimators should run after the attitude controller but before anything +// else in the system. They wait on sensor data which is either coming +// from the sensor hub or from a driver. Keeping this class at a higher +// priority ensures that the estimator runs first if it can, but will +// wait for the sensor hub if its data is coming from it. +#define SCHED_PRIORITY_ESTIMATOR (PX4_WQ_HP_BASE - 5) + +// Position controllers typically are in a blocking wait on estimator data +// so when new sensor data is available they will run last. Keeping them +// on a high priority ensures that they are the first process to be run +// when the estimator updates. +#define SCHED_PRIORITY_POSITION_CONTROL (PX4_WQ_HP_BASE - 7) + +// The log capture (which stores log data into RAM) should run faster +// than other components, but should not run before the control pipeline +#define SCHED_PRIORITY_LOG_CAPTURE (PX4_WQ_HP_BASE - 10) + +// Slow drivers should run at a rate where they do not impact the overall +// system execution +#define SCHED_PRIORITY_SLOW_DRIVER (PX4_WQ_HP_BASE - 35) + +// The navigation system needs to execute regularly but has no realtime needs +#define SCHED_PRIORITY_NAVIGATION (SCHED_PRIORITY_DEFAULT + 5) +// SCHED_PRIORITY_DEFAULT +#define SCHED_PRIORITY_LOG_WRITER (SCHED_PRIORITY_DEFAULT - 10) +#define SCHED_PRIORITY_PARAMS (SCHED_PRIORITY_DEFAULT - 15) +// SCHED_PRIORITY_IDLE + +typedef int (*px4_main_t)(int argc, char *argv[]); + +__BEGIN_DECLS + +/** Starts a task and performs any specific accounting, scheduler setup, etc. */ +__EXPORT px4_task_t px4_task_spawn_cmd(const char *name, + int scheduler, + int priority, + int stack_size, + px4_main_t entry, + char *const argv[]); + +/** Deletes a task - does not do resource cleanup **/ +__EXPORT int px4_task_delete(px4_task_t pid); + +/** Send a signal to a task **/ +__EXPORT int px4_task_kill(px4_task_t pid, int sig); + +/** Exit current task with return value **/ +__EXPORT void px4_task_exit(int ret); + +/** Show a list of running tasks **/ +__EXPORT void px4_show_tasks(void); + +/** See if a task is running **/ +__EXPORT bool px4_task_is_running(const char *taskname); + +#ifdef __PX4_POSIX +/** set process (and thread) options */ +__EXPORT int px4_prctl(int option, const char *arg2, px4_task_t pid); +#endif + +/** return the name of the current task */ +__EXPORT const char *px4_get_taskname(void); + +__END_DECLS diff --git a/platforms/ros2/include/px4_platform_common/time.h b/platforms/ros2/include/px4_platform_common/time.h new file mode 100644 index 0000000000..e55aec5f6e --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/time.h @@ -0,0 +1,6 @@ + + +#define px4_clock_settime system_clock_settime +#define px4_usleep system_usleep +#define px4_sleep system_sleep +#define px4_pthread_cond_timedwait system_pthread_cond_timedwait diff --git a/platforms/ros2/include/px4_platform_common/workqueue.h b/platforms/ros2/include/px4_platform_common/workqueue.h new file mode 100644 index 0000000000..36ab23ed9f --- /dev/null +++ b/platforms/ros2/include/px4_platform_common/workqueue.h @@ -0,0 +1,140 @@ +/**************************************************************************** + * include/nuttx/wqueue.h + * + * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#if defined(__PX4_NUTTX) +#include +#include +#include +#elif defined(__PX4_POSIX) + +#include +#include + +__BEGIN_DECLS + +#define HPWORK 0 +#define LPWORK 1 +#define NWORKERS 2 + +struct wqueue_s { + pid_t pid; /* The task ID of the worker thread */ + struct dq_queue_s q; /* The queue of pending work */ +}; + +extern struct wqueue_s g_work[NWORKERS]; + +/* Defines the work callback */ + +typedef void (*worker_t)(void *arg); + +struct work_s { + struct dq_entry_s dq; /* Implements a doubly linked list */ + worker_t worker; /* Work callback */ + void *arg; /* Callback argument */ + uint64_t qtime; /* Time work queued */ + uint32_t delay; /* Delay until work performed */ +}; + +/**************************************************************************** + * Name: work_queues_init() + * + * Description: + * Initialize the work queues. + * + ****************************************************************************/ +void work_queues_init(void); + +/**************************************************************************** + * Name: work_queue + * + * Description: + * Queue work to be performed at a later time. All queued work will be + * performed on the worker thread of of execution (not the caller's). + * + * The work structure is allocated by caller, but completely managed by + * the work queue logic. The caller should never modify the contents of + * the work queue structure; the caller should not call work_queue() + * again until either (1) the previous work has been performed and removed + * from the queue, or (2) work_cancel() has been called to cancel the work + * and remove it from the work queue. + * + * Input parameters: + * qid - The work queue ID + * work - The work structure to queue + * worker - The worker callback to be invoked. The callback will invoked + * on the worker thread of execution. + * arg - The argument that will be passed to the workder callback when + * int is invoked. + * delay - Delay (in clock ticks) from the time queue until the worker + * is invoked. Zero means to perform the work immediately. + * + * Returned Value: + * Zero on success, a negated errno on failure + * + ****************************************************************************/ + +int work_queue(int qid, struct work_s *work, worker_t worker, void *arg, uint32_t delay); + +/**************************************************************************** + * Name: work_cancel + * + * Description: + * Cancel previously queued work. This removes work from the work queue. + * After work has been canceled, it may be re-queue by calling work_queue() + * again. + * + * Input parameters: + * qid - The work queue ID + * work - The previously queue work structure to cancel + * + * Returned Value: + * Zero on success, a negated errno on failure + * + ****************************************************************************/ + +int work_cancel(int qid, struct work_s *work); + +uint32_t clock_systimer(void); + +int work_hpthread(int argc, char *argv[]); +int work_lpthread(int argc, char *argv[]); + +__END_DECLS + +#else +#error "Unknown target OS" +#endif diff --git a/platforms/ros2/include/queue.h b/platforms/ros2/include/queue.h new file mode 100644 index 0000000000..d6315ca172 --- /dev/null +++ b/platforms/ros2/include/queue.h @@ -0,0 +1,134 @@ +/************************************************************************ + * include/queue.h + * + * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************/ + +#ifndef __INCLUDE_QUEUE_H +#define __INCLUDE_QUEUE_H + +/************************************************************************ + * Included Files + ************************************************************************/ + +#include + +#ifdef __cplusplus +#include // NULL +#else +#include // NULL +#endif + +/************************************************************************ + * Pre-processor Definitions + ************************************************************************/ + +#define sq_init(q) do { (q)->head = NULL; (q)->tail = NULL; } while (0) +#define dq_init(q) do { (q)->head = NULL; (q)->tail = NULL; } while (0) + +#define sq_next(p) ((p)->flink) +#define dq_next(p) ((p)->flink) +#define dq_prev(p) ((p)->blink) + +#define sq_empty(q) ((q)->head == NULL) +#define dq_empty(q) ((q)->head == NULL) + +#define sq_peek(q) ((q)->head) +#define dq_peek(q) ((q)->head) + +// Required for Linux +#define FAR + +/************************************************************************ + * Global Type Declarations + ************************************************************************/ + +struct sq_entry_s { + FAR struct sq_entry_s *flink; +}; +typedef struct sq_entry_s sq_entry_t; + +struct dq_entry_s { + FAR struct dq_entry_s *flink; + FAR struct dq_entry_s *blink; +}; +typedef struct dq_entry_s dq_entry_t; + +struct sq_queue_s { + FAR sq_entry_t *head; + FAR sq_entry_t *tail; +}; +typedef struct sq_queue_s sq_queue_t; + +struct dq_queue_s { + FAR dq_entry_t *head; + FAR dq_entry_t *tail; +}; +typedef struct dq_queue_s dq_queue_t; + +/************************************************************************ + * Global Function Prototypes + ************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +EXTERN void sq_addfirst(FAR sq_entry_t *node, sq_queue_t *queue); +EXTERN void dq_addfirst(FAR dq_entry_t *node, dq_queue_t *queue); +EXTERN void sq_addlast(FAR sq_entry_t *node, sq_queue_t *queue); +EXTERN void dq_addlast(FAR dq_entry_t *node, dq_queue_t *queue); +EXTERN void sq_addafter(FAR sq_entry_t *prev, FAR sq_entry_t *node, + sq_queue_t *queue); +EXTERN void dq_addafter(FAR dq_entry_t *prev, FAR dq_entry_t *node, + dq_queue_t *queue); +EXTERN void dq_addbefore(FAR dq_entry_t *next, FAR dq_entry_t *node, + dq_queue_t *queue); + +EXTERN FAR sq_entry_t *sq_remafter(FAR sq_entry_t *node, sq_queue_t *queue); +EXTERN void sq_rem(FAR sq_entry_t *node, sq_queue_t *queue); +EXTERN void dq_rem(FAR dq_entry_t *node, dq_queue_t *queue); +EXTERN FAR sq_entry_t *sq_remlast(sq_queue_t *queue); +EXTERN FAR dq_entry_t *dq_remlast(dq_queue_t *queue); +EXTERN FAR sq_entry_t *sq_remfirst(sq_queue_t *queue); +EXTERN FAR dq_entry_t *dq_remfirst(dq_queue_t *queue); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_QUEUE_H_ */ + diff --git a/msg/templates/uorb_microcdr/msg.h.em b/platforms/ros2/include/system_config.h similarity index 58% rename from msg/templates/uorb_microcdr/msg.h.em rename to platforms/ros2/include/system_config.h index 76b793ab70..46abd18a03 100644 --- a/msg/templates/uorb_microcdr/msg.h.em +++ b/platforms/ros2/include/system_config.h @@ -1,21 +1,6 @@ -@############################################### -@# -@# PX4 ROS compatible message source code -@# generation for C++ -@# -@# EmPy template for generating .h files -@# Based on the original template for ROS -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - file_name_in (String) Source file -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### /**************************************************************************** * - * Copyright (C) 2013-2021 PX4 Development Team. All rights reserved. + * Copyright (c) 2017 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -46,24 +31,36 @@ * ****************************************************************************/ -/* Auto-generated by genmsg_cpp from file @file_name_in */ - -@{ -import genmsg.msgs - -from px_generate_uorb_topic_helper import * # this is in Tools/ - -uorb_struct = '%s_s'%spec.short_name -topic_name = spec.short_name -}@ +/** + * @file system_config.h + * + * Definitions used by all SITL and Linux targets + */ #pragma once -@############################## -@# Generic Includes -@############################## -#include -#include +#define PX4_CPU_UUID_BYTE_LENGTH 16 +#define PX4_CPU_UUID_WORD32_LENGTH 4 -void serialize_@(topic_name)(ucdrBuffer *writer, const struct @(uorb_struct) *input, char *output, uint32_t *length); -void deserialize_@(topic_name)(ucdrBuffer *reader, struct @(uorb_struct) *output, const char *input); +#define PX4_CPU_MFGUID_BYTE_LENGTH PX4_CPU_UUID_BYTE_LENGTH +#define PX4_CPU_UUID_WORD32_UNIQUE_H 2 /* Most significant digits change the least */ +#define PX4_CPU_UUID_WORD32_UNIQUE_M 1 /* Middle significant digits */ +#define PX4_CPU_UUID_WORD32_UNIQUE_L 0 /* Least significant digits change the most */ +#define PX4_CPU_UUID_WORD32_FORMAT_SIZE (PX4_CPU_UUID_WORD32_LENGTH-1+(2*PX4_CPU_UUID_BYTE_LENGTH)+1) +#define PX4_CPU_MFGUID_FORMAT_SIZE ((2*PX4_CPU_MFGUID_BYTE_LENGTH)+1) + +#define BOARD_OVERRIDE_CPU_VERSION (-1) +#define board_mcu_version(rev, revstr, errata) BOARD_OVERRIDE_CPU_VERSION + +#define BOARD_HAS_NO_UUID + +#define CONFIG_NFILE_STREAMS 1 +#define CONFIG_SCHED_WORKQUEUE 1 +#define CONFIG_SCHED_HPWORK 1 +#define CONFIG_SCHED_LPWORK 1 + +/** time in ms between checks for work in work queues **/ +#define CONFIG_SCHED_WORKPERIOD 50000 + +#define CONFIG_SCHED_INSTRUMENTATION 1 +#define CONFIG_MAX_TASKS 32 diff --git a/platforms/ros2/include/uORB/Publication.hpp b/platforms/ros2/include/uORB/Publication.hpp new file mode 100644 index 0000000000..3b53424e0b --- /dev/null +++ b/platforms/ros2/include/uORB/Publication.hpp @@ -0,0 +1,148 @@ +/**************************************************************************** + * + * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file Publication.hpp + * + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" + +#include "uORB.h" + +namespace uORB +{ + +template class DefaultQueueSize +{ +private: + template + static constexpr uint8_t get_queue_size(decltype(T::ORB_QUEUE_LENGTH) *) + { + return T::ORB_QUEUE_LENGTH; + } + + template static constexpr uint8_t get_queue_size(...) + { + return 1; + } + +public: + static constexpr unsigned value = get_queue_size(nullptr); +}; + +/** + * uORB publication wrapper class + */ +template::value> +class Publication +{ +public: + + /** + * Constructor + * + * @param meta The uORB metadata (usually from the ORB_ID() macro) for the topic. + */ + Publication(rclcpp::Node *node, ORB_ID id) : _node(*node), _id(id) + { + _publisher = _node.create_publisher(::get_topic_string(_id), ORB_QSIZE); // TODO + } + + bool advertised() { return (_publisher != nullptr); } + + bool advertise() + { + if (!advertised()) { + _publisher = _node.create_publisher(get_topic_string(_id), ORB_QSIZE); + } + + return advertised(); + } + + /** + * Publish the struct + * @param data The uORB message struct we are updating. + */ + bool publish(const T &data) + { + if (!advertised()) { + advertise(); + } + + _publisher->publish(data); + + return true; + } + +private: + typename rclcpp::Publisher::SharedPtr _publisher{nullptr}; + + rclcpp::Node &_node; + + const ORB_ID _id; +}; + +/** + * The publication class with data embedded. + */ +template +class PublicationData : public Publication +{ +public: + /** + * Constructor + * + * @param meta The uORB metadata (usually from the ORB_ID() macro) for the topic. + */ + PublicationData(ORB_ID id) : Publication(id) {} + //PublicationData(const orb_metadata *meta) : Publication(meta) {} + + T &get() { return _data; } + void set(const T &data) { _data = data; } + + // Publishes the embedded struct. + bool update() { return Publication::publish(_data); } + bool update(const T &data) + { + _data = data; + return Publication::publish(_data); + } + +private: + T _data{}; +}; + +} // namespace uORB diff --git a/platforms/ros2/include/uORB/Subscription.hpp b/platforms/ros2/include/uORB/Subscription.hpp new file mode 100644 index 0000000000..f08d145867 --- /dev/null +++ b/platforms/ros2/include/uORB/Subscription.hpp @@ -0,0 +1,167 @@ +/**************************************************************************** + * + * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file Subscription.hpp + * + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" + +#include "uORB.h" + +using std::placeholders::_1; + +namespace uORB +{ + +class SubscriptionCallback; + +template +class Subscription2 +{ +public: + + /** + * Constructor + * + * @param id The uORB ORB_ID enum for the topic. + * @param instance The instance for multi sub. + */ + Subscription2(rclcpp::Node *node, ORB_ID id, uint8_t instance = 0) : + _node(*node), + _id(id) + { + subscribe(); + + (void)instance; // TODO: + } + + ~Subscription2() + { + unsubscribe(); + } + + bool subscribe() + { + if (_subscription == nullptr) { + _subscription = _node.create_subscription(::get_topic_string(_id), 1, std::bind(&Subscription2::topic_callback, + this, _1)); + } + + return (_subscription != nullptr); + } + + void unsubscribe() {} + + bool valid() const { return _subscription != nullptr; } + bool advertised() + { + if (valid()) { + return true; + } + + // try to initialize + if (subscribe()) { + // check again if valid + if (valid()) { + return true; + } + } + + return false; + } + + /** + * Check if there is a new update. + */ + //bool updated() { return advertised() && _node->updates_available(_last_generation); } + bool updated() { return true; } + + /** + * Update the struct + * @param dst The uORB message struct we are updating. + */ + bool update(void *dst) { return updated() && copy(dst); } + + /** + * Copy the struct + * @param dst The uORB message struct we are updating. + */ + bool copy(void *dst) + { + if (advertised()) { + S msg{}; + //_subscription->return_message(&msg); + memcpy(dst, &msg, sizeof(S)); + } + + return false; + } + + /** + * Change subscription instance + * @param instance The new multi-Subscription instance + */ + bool ChangeInstance(uint8_t instance); + + uint8_t get_instance() const { return _instance; } + unsigned get_last_generation() const { return _last_generation; } + orb_id_t get_topic() const { return get_orb_meta(_id); } + +protected: + + typename rclcpp::Subscription::SharedPtr _subscription{nullptr}; + rclcpp::Node &_node; + + unsigned _last_generation{0}; /**< last generation the subscriber has seen */ + + ORB_ID _id{ORB_ID::INVALID}; + uint8_t _instance{0}; + +private: + void topic_callback(const typename S::SharedPtr msg) + { + (void)msg; // TODO + //_msg_buffer = msg; + _topic_generation++; + } + + S _msg_buffer[1]; + + uint32_t _topic_generation{0}; +}; + +} // namespace uORB diff --git a/platforms/ros2/include/uORB/SubscriptionBasic.hpp b/platforms/ros2/include/uORB/SubscriptionBasic.hpp new file mode 100644 index 0000000000..c8f0d7a874 --- /dev/null +++ b/platforms/ros2/include/uORB/SubscriptionBasic.hpp @@ -0,0 +1,155 @@ +/**************************************************************************** + * + * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file Subscription.hpp + * + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" + +#include "rclcpp/subscription_base.hpp" + +#include "uORB.h" + +namespace uORB +{ + +// Base subscription wrapper class +class SubscriptionBasic +{ +public: + + /** + * Constructor + * + * @param id The uORB ORB_ID enum for the topic. + * @param instance The instance for multi sub. + */ + Subscription(rclcpp::Node *node, ORB_ID id, uint8_t instance = 0) : + _node(*node), + _id(id) + { + subscribe(); + + (void)instance; // TODO: + } + + ~Subscription() + { + unsubscribe(); + } + + bool subscribe() + { + if (_subscription == nullptr) { + //_subscription = _node.create_subscription("vehicle_status", 1, std::bind(&Subscription::topic_callback, this, _1)); + } + + return (_subscription != nullptr); + } + + void unsubscribe() {} + + bool valid() const { return _subscription != nullptr; } + bool advertised() + { + if (valid()) { + return true; + } + + // try to initialize + if (subscribe()) { + // check again if valid + if (valid()) { + return true; + } + } + + return false; + } + + /** + * Check if there is a new update. + */ + //bool updated() { return advertised() && _node->updates_available(_last_generation); } + bool updated() { return true; } + + /** + * Update the struct + * @param dst The uORB message struct we are updating. + */ + bool update(void *dst) { return updated() && copy(dst); } + + /** + * Copy the struct + * @param dst The uORB message struct we are updating. + */ + bool copy(void *dst) + { + if (advertised()) { + //_subscription->return_message(&msg); + //memcpy(dst, &msg, sizeof(px4::msg::VehicleStatus)); + } + + return false; + } + + /** + * Change subscription instance + * @param instance The new multi-Subscription instance + */ + bool ChangeInstance(uint8_t instance); + + uint8_t get_instance() const { return _instance; } + unsigned get_last_generation() const { return _last_generation; } + orb_id_t get_topic() const { return get_orb_meta(_id); } + +protected: + + rclcpp::SubscriptionBase::SharedPtr _subscription{nullptr}; + rclcpp::Node &_node; + + unsigned _last_generation{0}; /**< last generation the subscriber has seen */ + + ORB_ID _id{ORB_ID::INVALID}; + uint8_t _instance{0}; + +private: + + uint32_t _topic_generation{0}; +}; + +} // namespace uORB diff --git a/msg/templates/px4/ros/msg.h.em b/platforms/ros2/include/uORB/uORB.h similarity index 50% rename from msg/templates/px4/ros/msg.h.em rename to platforms/ros2/include/uORB/uORB.h index e102a087d4..c329106046 100644 --- a/msg/templates/px4/ros/msg.h.em +++ b/platforms/ros2/include/uORB/uORB.h @@ -1,23 +1,6 @@ -@############################################### -@# -@# PX4 ROS compatible message source code -@# generation for C++ -@# -@# This file generates the multiplatform wrapper -@# -@# EmPy template for generating .h files -@# Based on the original template for ROS -@# -@############################################### -@# Start of Template -@# -@# Context: -@# - file_name_in (String) Source file -@# - spec (msggen.MsgSpec) Parsed specification of the .msg file -@############################################### /**************************************************************************** * - * Copyright (C) 2013-2015 PX4 Development Team. All rights reserved. + * Copyright (c) 2021 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,47 +31,69 @@ * ****************************************************************************/ -/* Auto-generated by genmsg_cpp from file @file_name_in */ - -@{ -import genmsg.msgs - - -cpp_class = 'px4_%s'%spec.short_name -native_type = spec.short_name -topic_name = spec.short_name -}@ - #pragma once -@############################## -@# Generic Includes -@############################## -#include "px4/@(topic_name).h" -#include "platforms/px4_message.h" -@############################## -@# Class -@############################## +#include +#include +#include -namespace px4 -{ -class @(cpp_class) : - public PX4Message<@(native_type)> -{ -public: - @(cpp_class)() : - PX4Message<@(native_type)>() - {} - - @(cpp_class)(@(native_type) msg) : - PX4Message<@(native_type)>(msg) - {} - - ~@(cpp_class)() {} - - static PX4TopicHandle handle() {return "@(topic_name)";} +/** + * Object metadata. + */ +struct orb_metadata { + const char *o_name; /**< unique object name */ + const uint16_t o_size; /**< object size */ + const uint16_t o_size_no_padding; /**< object size w/o padding at the end (for logger) */ + const char *o_fields; /**< semicolon separated list of fields (with type) */ + uint8_t o_id; /**< ORB_ID enum */ }; -}; +typedef const struct orb_metadata *orb_id_t; + +/** + * Maximum number of multi topic instances. This must be <= 10 (because it's the last char of the node path) + */ +#if defined(CONSTRAINED_MEMORY) +# define ORB_MULTI_MAX_INSTANCES 4 +#else +# define ORB_MULTI_MAX_INSTANCES 10 +#endif + + + +#define ORB_ID(_name) &__orb_##_name + +#if defined(__cplusplus) +# define ORB_DECLARE(_name) extern "C" const struct orb_metadata __orb_##_name __EXPORT +#else +# define ORB_DECLARE(_name) extern const struct orb_metadata __orb_##_name __EXPORT +#endif + + +#define ORB_DEFINE(_name, _struct, _size_no_padding, _fields, _orb_id_enum) \ + const struct orb_metadata __orb_##_name = { \ + #_name, \ + sizeof(_struct), \ + _size_no_padding, \ + _fields, \ + _orb_id_enum \ + }; struct hack + +/** + * Print a topic to console. Do not call this directly, use print_message() instead. + * @param meta orb topic metadata + * @param data expected to be aligned to the largest member + */ +void orb_print_message_internal(const struct orb_metadata *meta, const void *data, bool print_topic_name); + +#ifndef PX4_INFO_RAW +# define PX4_INFO_RAW printf +#endif + +#ifdef __cplusplus + +#include + +#endif // __cplusplus diff --git a/platforms/ros2/src/px4/CMakeLists.txt b/platforms/ros2/src/px4/CMakeLists.txt new file mode 100644 index 0000000000..bf200988a5 --- /dev/null +++ b/platforms/ros2/src/px4/CMakeLists.txt @@ -0,0 +1,37 @@ +############################################################################ +# +# Copyright (c) 2021 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +add_subdirectory(common) + +add_subdirectory(${PX4_CHIP_MANUFACTURER}) + diff --git a/platforms/ros2/src/px4/common/CMakeLists.txt b/platforms/ros2/src/px4/common/CMakeLists.txt new file mode 100644 index 0000000000..dc850c246e --- /dev/null +++ b/platforms/ros2/src/px4/common/CMakeLists.txt @@ -0,0 +1,39 @@ +############################################################################ +# +# Copyright (c) 2015 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +add_library(px4_layer STATIC + drv_hrt.cpp + px4_sem.cpp +) +target_compile_definitions(px4_layer PRIVATE MODULE_NAME="px4") +target_compile_options(px4_layer PRIVATE -Wno-cast-align) # TODO: fix and enable diff --git a/platforms/ros2/src/px4/common/drv_hrt.cpp b/platforms/ros2/src/px4/common/drv_hrt.cpp new file mode 100644 index 0000000000..55861b6cbc --- /dev/null +++ b/platforms/ros2/src/px4/common/drv_hrt.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** + * + * Copyright (c) 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file drv_hrt.cpp + * + * High-resolution timer with callouts and timekeeping. + */ + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +sq_entry_t *sq_remafter(sq_entry_t *node, sq_queue_t *queue) +{ + sq_entry_t *ret = node->flink; + + if (queue->head && ret) { + if (queue->tail == ret) { + queue->tail = node; + node->flink = NULL; + + } else { + node->flink = ret->flink; + } + + ret->flink = NULL; + } + + return ret; +} + +void sq_rem(sq_entry_t *node, sq_queue_t *queue) +{ + if (queue->head && node) { + if (node == queue->head) { + queue->head = node->flink; + + if (node == queue->tail) { + queue->tail = NULL; + } + + } else { + sq_entry_t *prev; + + for (prev = (sq_entry_t *)queue->head; + prev && prev->flink != node; + prev = prev->flink) {} + + if (prev) { + sq_remafter(prev, queue); + } + } + } +} + + +// Intervals in usec +static constexpr unsigned HRT_INTERVAL_MIN = 50; +static constexpr unsigned HRT_INTERVAL_MAX = 50000000; + +/* + * Queue of callout entries. + */ +sq_entry_t callout_queue{}; + +/* latency baseline (last compare value applied) */ +//static uint64_t latency_baseline; + +/* latency histogram */ +// const uint16_t latency_bucket_count = LATENCY_BUCKET_COUNT; +// const uint16_t latency_buckets[LATENCY_BUCKET_COUNT] = { 1, 2, 5, 10, 20, 50, 100, 1000 }; +// __EXPORT uint32_t latency_counters[LATENCY_BUCKET_COUNT + 1]; + +static px4_sem_t _hrt_lock; +//static struct work_s _hrt_work; + +hrt_abstime hrt_absolute_time_offset() +{ + return 0; +} + +static void hrt_lock() +{ + px4_sem_wait(&_hrt_lock); +} + +static void hrt_unlock() +{ + px4_sem_post(&_hrt_lock); +} + +/* + * Get absolute time. + */ +hrt_abstime hrt_absolute_time() +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts_to_abstime(&ts); +} + +/* + * Convert a timespec to absolute time. + */ +hrt_abstime ts_to_abstime(const struct timespec *ts) +{ + return (hrt_abstime)(ts->tv_sec) * 1000000 + ts->tv_nsec / 1000; +} + +/* + * Compute the delta between a timestamp taken in the past + * and now. + * + * This function is safe to use even if the timestamp is updated + * by an interrupt during execution. + */ +hrt_abstime hrt_elapsed_time_atomic(const volatile hrt_abstime *then) +{ + // This is not atomic as the value on the application layer of POSIX is limited. + hrt_abstime delta = hrt_absolute_time() - *then; + return delta; +} + +/* + * Store the absolute time in an interrupt-safe fashion. + * + * This function ensures that the timestamp cannot be seen half-written by an interrupt handler. + */ +void hrt_store_absolute_time(volatile hrt_abstime *t) +{ + *t = hrt_absolute_time(); +} + +/* + * If this returns true, the entry has been invoked and removed from the callout list, + * or it has never been entered. + * + * Always returns false for repeating callouts. + */ +bool hrt_called(struct hrt_call *entry) +{ + return (entry->deadline == 0); +} + +/* + * Remove the entry from the callout list. + */ +void hrt_cancel(struct hrt_call *entry) +{ + hrt_lock(); + //sq_rem(&entry->link, &callout_queue); + entry->deadline = 0; + + /* if this is a periodic call being removed by the callout, prevent it from + * being re-entered when the callout returns. + */ + entry->period = 0; + hrt_unlock(); + // endif +} + +/* + * initialise a hrt_call structure + */ +void hrt_call_init(struct hrt_call *entry) +{ + memset(entry, 0, sizeof(*entry)); +} + +/* + * delay a hrt_call_every() periodic call by the given number of + * microseconds. This should be called from within the callout to + * cause the callout to be re-scheduled for a later time. The periodic + * callouts will then continue from that new base time at the + * previously specified period. + */ +void hrt_call_delay(struct hrt_call *entry, hrt_abstime delay) +{ + entry->deadline = hrt_absolute_time() + delay; +} + +/* + * Initialise the HRT. + */ +void hrt_init() +{ + //sq_init(&callout_queue); + + int sem_ret = px4_sem_init(&_hrt_lock, 0, 1); + + if (sem_ret) { + //PX4_ERR("SEM INIT FAIL: %s", strerror(errno)); + } +} + +void abstime_to_ts(struct timespec *ts, hrt_abstime abstime) +{ + ts->tv_sec = abstime / 1000000; + abstime -= ts->tv_sec * 1000000; + ts->tv_nsec = abstime * 1000; +} + +int px4_clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + return system_clock_gettime(clk_id, tp); +} diff --git a/platforms/ros2/src/px4/common/include/px4_platform/micro_hal.h b/platforms/ros2/src/px4/common/include/px4_platform/micro_hal.h new file mode 100644 index 0000000000..7e48584c1f --- /dev/null +++ b/platforms/ros2/src/px4/common/include/px4_platform/micro_hal.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * + * Copyright (c) 2019 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#pragma once + +#include +#include + +typedef int (*xcpt_t)(int irq, void *context, void *arg); + +static inline int px4_arch_configgpio(uint32_t pinset) { return -1; } +static inline int px4_arch_unconfiggpio(uint32_t pinset) { return -1; } +static inline bool px4_arch_gpioread(uint32_t pinset) { return false; } +static inline void px4_arch_gpiowrite(uint32_t pinset, bool value) { } +static inline int px4_arch_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge, + bool event, xcpt_t func, void *arg) { return -1; } + +#define px4_udelay(usec) px4_usleep(usec) +#define px4_mdelay(msec) px4_msleep(msec) + +#define px4_cache_aligned_data() +#define px4_cache_aligned_alloc malloc diff --git a/platforms/ros2/src/px4/common/main.cpp b/platforms/ros2/src/px4/common/main.cpp new file mode 100644 index 0000000000..af9af38773 --- /dev/null +++ b/platforms/ros2/src/px4/common/main.cpp @@ -0,0 +1,19 @@ + + + +// PX4 bridge + +// serial/UDP/TCP +// all data in -> publish + + +// data in then publish rclcpp::Publish +// time sync + + + +int main() +{ + + return 0; +} diff --git a/platforms/ros2/src/px4/common/px4_sem.cpp b/platforms/ros2/src/px4/common/px4_sem.cpp new file mode 100644 index 0000000000..77dafadeb9 --- /dev/null +++ b/platforms/ros2/src/px4/common/px4_sem.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** + * + * Copyright (c) 2015 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file px4_sem.cpp + * + * PX4 Middleware Wrapper Linux Implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if (defined(__PX4_DARWIN) || defined(__PX4_CYGWIN) || defined(__PX4_POSIX)) && !defined(__PX4_QURT) + +#include + +int px4_sem_init(px4_sem_t *s, int pshared, unsigned value) +{ + // We do not used the process shared arg + (void)pshared; + s->value = value; + pthread_cond_init(&(s->wait), nullptr); + pthread_mutex_init(&(s->lock), nullptr); + +#if !defined(__PX4_DARWIN) + // We want to use CLOCK_MONOTONIC if possible but we can't on macOS + // because it's not available. + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(&(s->wait), &attr); +#endif + + return 0; +} + +int px4_sem_setprotocol(px4_sem_t *s, int protocol) +{ + return 0; +} + +int px4_sem_wait(px4_sem_t *s) +{ + int ret = pthread_mutex_lock(&(s->lock)); + + if (ret) { + return ret; + } + + s->value--; + + if (s->value < 0) { + ret = pthread_cond_wait(&(s->wait), &(s->lock)); + + } else { + ret = 0; + } + + if (ret) { + //PX4_WARN("px4_sem_wait failure"); + } + + int mret = pthread_mutex_unlock(&(s->lock)); + + return (ret) ? ret : mret; +} + +int px4_sem_trywait(px4_sem_t *s) +{ + int ret = pthread_mutex_lock(&(s->lock)); + + if (ret) { + return ret; + } + + if (s->value <= 0) { + errno = EAGAIN; + ret = -1; + + } else { + s->value--; + } + + int mret = pthread_mutex_unlock(&(s->lock)); + + return (ret) ? ret : mret; +} + +int px4_sem_timedwait(px4_sem_t *s, const struct timespec *abstime) +{ + int ret = pthread_mutex_lock(&(s->lock)); + + if (ret) { + return ret; + } + + s->value--; + errno = 0; + + if (s->value < 0) { + ret = px4_pthread_cond_timedwait(&(s->wait), &(s->lock), abstime); + + } else { + ret = 0; + } + + errno = ret; + + if (ret != 0 && ret != ETIMEDOUT) { + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + const unsigned NAMELEN = 32; + char thread_name[NAMELEN] = {}; + (void)pthread_getname_np(pthread_self(), thread_name, NAMELEN); + //PX4_WARN("%s: px4_sem_timedwait failure: ret: %d", thread_name, ret); + } + + int mret = pthread_mutex_unlock(&(s->lock)); + + if (ret || mret) { + return -1; + } + + return 0; +} + +int px4_sem_post(px4_sem_t *s) +{ + int ret = pthread_mutex_lock(&(s->lock)); + + if (ret) { + return ret; + } + + s->value++; + + if (s->value <= 0) { + ret = pthread_cond_signal(&(s->wait)); + + } else { + ret = 0; + } + + if (ret) { + //PX4_WARN("px4_sem_post failure"); + } + + int mret = pthread_mutex_unlock(&(s->lock)); + + // return the cond signal failure if present, + // else return the mutex status + return (ret) ? ret : mret; +} + +int px4_sem_getvalue(px4_sem_t *s, int *sval) +{ + int ret = pthread_mutex_lock(&(s->lock)); + + if (ret) { + //PX4_WARN("px4_sem_getvalue failure"); + } + + if (ret) { + return ret; + } + + *sval = s->value; + ret = pthread_mutex_unlock(&(s->lock)); + + return ret; +} + +int px4_sem_destroy(px4_sem_t *s) +{ + pthread_mutex_lock(&(s->lock)); + pthread_cond_destroy(&(s->wait)); + pthread_mutex_unlock(&(s->lock)); + pthread_mutex_destroy(&(s->lock)); + + return 0; +} + +#endif diff --git a/platforms/ros2/src/px4/generic/CMakeLists.txt b/platforms/ros2/src/px4/generic/CMakeLists.txt new file mode 100644 index 0000000000..72584f3178 --- /dev/null +++ b/platforms/ros2/src/px4/generic/CMakeLists.txt @@ -0,0 +1,34 @@ +############################################################################ +# +# Copyright (c) 2019 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +add_subdirectory(${PX4_CHIP}) diff --git a/platforms/ros2/src/px4/generic/generic/CMakeLists.txt b/platforms/ros2/src/px4/generic/generic/CMakeLists.txt new file mode 100644 index 0000000000..18c32a3abd --- /dev/null +++ b/platforms/ros2/src/px4/generic/generic/CMakeLists.txt @@ -0,0 +1,32 @@ +############################################################################ +# +# Copyright (c) 2021 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ diff --git a/platforms/ros2/src/px4/generic/generic/include/px4_arch/CMakeLists.txt b/platforms/ros2/src/px4/generic/generic/include/px4_arch/CMakeLists.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/platforms/ros2/src/px4/generic/generic/include/px4_arch/i2c_hw_description.h b/platforms/ros2/src/px4/generic/generic/include/px4_arch/i2c_hw_description.h new file mode 100644 index 0000000000..82e42c1882 --- /dev/null +++ b/platforms/ros2/src/px4/generic/generic/include/px4_arch/i2c_hw_description.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * + * Copyright (C) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include + + +static inline constexpr px4_i2c_bus_t initI2CBusInternal(int bus) +{ + px4_i2c_bus_t ret{}; + ret.bus = bus; + ret.is_external = false; + return ret; +} + +static inline constexpr px4_i2c_bus_t initI2CBusExternal(int bus) +{ + px4_i2c_bus_t ret{}; + ret.bus = bus; + ret.is_external = true; + return ret; +} diff --git a/platforms/ros2/src/px4/generic/generic/include/px4_arch/micro_hal.h b/platforms/ros2/src/px4/generic/generic/include/px4_arch/micro_hal.h new file mode 100644 index 0000000000..d09b73c687 --- /dev/null +++ b/platforms/ros2/src/px4/generic/generic/include/px4_arch/micro_hal.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * + * Copyright (c) 2019 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#pragma once + +#include + diff --git a/platforms/ros2/src/px4/generic/generic/include/px4_arch/spi_hw_description.h b/platforms/ros2/src/px4/generic/generic/include/px4_arch/spi_hw_description.h new file mode 100644 index 0000000000..2ecdec5ebb --- /dev/null +++ b/platforms/ros2/src/px4/generic/generic/include/px4_arch/spi_hw_description.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * + * Copyright (C) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include + +static inline constexpr px4_spi_bus_device_t initSPIDevice(uint8_t devid_driver, uint8_t cs_index) +{ + px4_spi_bus_device_t ret{}; + ret.cs_gpio = 1; // set to some non-zero value to indicate this is used + ret.devid = PX4_SPIDEV_ID(PX4_SPI_DEVICE_ID, cs_index); + ret.devtype_driver = devid_driver; + return ret; +} + +static inline constexpr px4_spi_bus_t initSPIBus(int bus, const px4_spi_bus_devices_t &devices) +{ + px4_spi_bus_t ret{}; + + for (int i = 0; i < SPI_BUS_MAX_DEVICES; ++i) { + ret.devices[i] = devices.devices[i]; + } + + ret.bus = bus; + ret.is_external = false; // all buses are marked internal on Linux + ret.requires_locking = false; + return ret; +} diff --git a/src/drivers/adc/ads1115/CMakeLists.txt b/src/drivers/adc/ads1115/CMakeLists.txt index d65265709d..8d0957d03f 100644 --- a/src/drivers/adc/ads1115/CMakeLists.txt +++ b/src/drivers/adc/ads1115/CMakeLists.txt @@ -37,5 +37,9 @@ px4_add_module( SRCS ads1115_main.cpp ADS1115.cpp + ADS1115.h DEPENDS - ) \ No newline at end of file + px4_work_queue + PUBLICATIONS + "px4::msg::AdcReport /adc_report" + ) diff --git a/src/drivers/adc/board_adc/CMakeLists.txt b/src/drivers/adc/board_adc/CMakeLists.txt index e22c3d2c16..ebfa647399 100644 --- a/src/drivers/adc/board_adc/CMakeLists.txt +++ b/src/drivers/adc/board_adc/CMakeLists.txt @@ -40,4 +40,7 @@ px4_add_module( DEPENDS arch_adc px4_work_queue + PUBLICATIONS + "px4::msg::AdcReport /adc_report" + "px4::msg::SystemPower /system_power" ) diff --git a/src/drivers/barometer/bmp388/CMakeLists.txt b/src/drivers/barometer/bmp388/CMakeLists.txt index 35474254de..a410ceaefd 100644 --- a/src/drivers/barometer/bmp388/CMakeLists.txt +++ b/src/drivers/barometer/bmp388/CMakeLists.txt @@ -42,4 +42,6 @@ px4_add_module( DEPENDS drivers_barometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorBaro /sensor_baro" ) diff --git a/src/drivers/barometer/ms5611/CMakeLists.txt b/src/drivers/barometer/ms5611/CMakeLists.txt index 6faa5b7a46..19801d2720 100644 --- a/src/drivers/barometer/ms5611/CMakeLists.txt +++ b/src/drivers/barometer/ms5611/CMakeLists.txt @@ -46,4 +46,6 @@ px4_add_module( drivers__device drivers_barometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorBaro /sensor_baro" ) diff --git a/src/drivers/bootloaders/CMakeLists.txt b/src/drivers/bootloaders/CMakeLists.txt index 32f514a36a..1d8ab5d108 100644 --- a/src/drivers/bootloaders/CMakeLists.txt +++ b/src/drivers/bootloaders/CMakeLists.txt @@ -39,10 +39,10 @@ add_dependencies(drivers_bootloaders prebuild_targets) include_directories(include) target_include_directories(drivers_bootloaders INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(drivers_bootloaders PRIVATE systemlib) +target_link_libraries(drivers_bootloaders PRIVATE crc) # generate bootloader_app_shared_t -if(NOT "${PX4_BOARD_LABEL}" MATCHES "canbootloader") +if(NOT "${PX4_BOARD_LABEL}" MATCHES "canbootloader") set(HW_MAJOR ${uavcanblid_hw_version_major}) set(HW_MINOR ${uavcanblid_hw_version_minor}) set(SW_MAJOR ${uavcanblid_sw_version_major}) diff --git a/src/drivers/bootloaders/boot_alt_app_shared.c b/src/drivers/bootloaders/boot_alt_app_shared.c index ac0b726e85..fa8ee361f5 100644 --- a/src/drivers/bootloaders/boot_alt_app_shared.c +++ b/src/drivers/bootloaders/boot_alt_app_shared.c @@ -45,7 +45,7 @@ #include "boot_alt_app_shared.h" -#include +#include /**************************************************************************** * Name: bootloader_alt_app_shared_read diff --git a/src/drivers/bootloaders/boot_app_shared.c b/src/drivers/bootloaders/boot_app_shared.c index a63efa5430..2e77869317 100644 --- a/src/drivers/bootloaders/boot_app_shared.c +++ b/src/drivers/bootloaders/boot_app_shared.c @@ -46,7 +46,7 @@ #include "arm_arch.h" #include "boot_app_shared.h" -#include +#include #define BOOTLOADER_COMMON_APP_SIGNATURE 0xB0A04150u #define BOOTLOADER_COMMON_BOOTLOADER_SIGNATURE 0xB0A0424Cu diff --git a/src/drivers/camera_capture/CMakeLists.txt b/src/drivers/camera_capture/CMakeLists.txt index 37e9e88b86..1341427db6 100644 --- a/src/drivers/camera_capture/CMakeLists.txt +++ b/src/drivers/camera_capture/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2018 PX4 Development Team. All rights reserved. +# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,4 +46,7 @@ px4_add_module( camera_capture.cpp DEPENDS arch_io_pins + PUBLICATIONS + "px4::msg::CameraTrigger /camera_trigger" + "px4::msg::PpsCapture /pps_capture" # TODO ) diff --git a/src/drivers/camera_trigger/CMakeLists.txt b/src/drivers/camera_trigger/CMakeLists.txt index 77604edfc0..020639f0d1 100644 --- a/src/drivers/camera_trigger/CMakeLists.txt +++ b/src/drivers/camera_trigger/CMakeLists.txt @@ -50,4 +50,7 @@ px4_add_module( interfaces/src/gpio.cpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::CameraTrigger /camera_trigger" + "px4::msg::PpsCapture /pps_capture" # TODO ) diff --git a/src/drivers/differential_pressure/ets/CMakeLists.txt b/src/drivers/differential_pressure/ets/CMakeLists.txt index fe77c1172c..2a73c6c1d5 100644 --- a/src/drivers/differential_pressure/ets/CMakeLists.txt +++ b/src/drivers/differential_pressure/ets/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( ets_airspeed.cpp DEPENDS drivers__airspeed + PUBLICATIONS + "px4::msg::DifferentialPressure /differential_pressure" ) diff --git a/src/drivers/differential_pressure/ms4525/CMakeLists.txt b/src/drivers/differential_pressure/ms4525/CMakeLists.txt index bac360d01e..b39b77749c 100644 --- a/src/drivers/differential_pressure/ms4525/CMakeLists.txt +++ b/src/drivers/differential_pressure/ms4525/CMakeLists.txt @@ -39,4 +39,6 @@ px4_add_module( DEPENDS drivers__airspeed mathlib + PUBLICATIONS + "px4::msg::DifferentialPressure /differential_pressure" ) diff --git a/src/drivers/differential_pressure/ms5525/CMakeLists.txt b/src/drivers/differential_pressure/ms5525/CMakeLists.txt index 21e4195034..c5e8117050 100644 --- a/src/drivers/differential_pressure/ms5525/CMakeLists.txt +++ b/src/drivers/differential_pressure/ms5525/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( DEPENDS drivers__airspeed mathlib + PUBLICATIONS + "px4::msg::DifferentialPressure /differential_pressure" ) diff --git a/src/drivers/differential_pressure/sdp3x/CMakeLists.txt b/src/drivers/differential_pressure/sdp3x/CMakeLists.txt index 3ba5eba2fe..b852e23311 100644 --- a/src/drivers/differential_pressure/sdp3x/CMakeLists.txt +++ b/src/drivers/differential_pressure/sdp3x/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( DEPENDS drivers__airspeed mathlib + PUBLICATIONS + "px4::msg::DifferentialPressure /differential_pressure" ) diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/CMakeLists.txt b/src/drivers/distance_sensor/broadcom/afbrs50/CMakeLists.txt index 8073128881..efebc420a3 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/CMakeLists.txt +++ b/src/drivers/distance_sensor/broadcom/afbrs50/CMakeLists.txt @@ -54,6 +54,8 @@ px4_add_module( px4_work_queue drivers_rangefinder afbrs50_m4_fpu + PUBLICATIONS + "px4::msg::DistanceSensor /distance_sensor" ) target_link_libraries(afbrs50_m4_fpu INTERFACE drivers__distance_sensor__afbrs50) diff --git a/src/drivers/distance_sensor/cm8jl65/CMakeLists.txt b/src/drivers/distance_sensor/cm8jl65/CMakeLists.txt index 44d0e47c63..5b37394a6f 100644 --- a/src/drivers/distance_sensor/cm8jl65/CMakeLists.txt +++ b/src/drivers/distance_sensor/cm8jl65/CMakeLists.txt @@ -41,4 +41,6 @@ px4_add_module( module.yaml DEPENDS drivers_rangefinder + PUBLICATIONS + "px4::msg::DistanceSensor /distance_sensor" ) diff --git a/src/drivers/distance_sensor/gy_us42/CMakeLists.txt b/src/drivers/distance_sensor/gy_us42/CMakeLists.txt index 7d59f37cb4..f11850b845 100644 --- a/src/drivers/distance_sensor/gy_us42/CMakeLists.txt +++ b/src/drivers/distance_sensor/gy_us42/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( DEPENDS drivers_rangefinder px4_work_queue + PUBLICATIONS + "px4::msg::DistanceSensor /distance_sensor" ) diff --git a/src/drivers/distance_sensor/leddar_one/CMakeLists.txt b/src/drivers/distance_sensor/leddar_one/CMakeLists.txt index f76edb15a6..97fe6f46fe 100644 --- a/src/drivers/distance_sensor/leddar_one/CMakeLists.txt +++ b/src/drivers/distance_sensor/leddar_one/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( module.yaml DEPENDS drivers_rangefinder + PUBLICATIONS + "px4::msg::DistanceSensor /distance_sensor" ) diff --git a/src/drivers/distance_sensor/mappydot/CMakeLists.txt b/src/drivers/distance_sensor/mappydot/CMakeLists.txt index 58625fa8eb..d32806a3da 100644 --- a/src/drivers/distance_sensor/mappydot/CMakeLists.txt +++ b/src/drivers/distance_sensor/mappydot/CMakeLists.txt @@ -33,6 +33,8 @@ px4_add_module( MODULE drivers__mappydot MAIN mappydot + PUBLICATIONS + "px4::msg::DistanceSensor /distance_sensor" SRCS MappyDot.cpp DEPENDS diff --git a/src/drivers/dshot/CMakeLists.txt b/src/drivers/dshot/CMakeLists.txt index 757a7a8d24..f20470d6ed 100644 --- a/src/drivers/dshot/CMakeLists.txt +++ b/src/drivers/dshot/CMakeLists.txt @@ -52,4 +52,11 @@ px4_add_module( mixer_module MODULE_CONFIG module.yaml + PUBLICATIONS + "px4::msg::ActuatorOutputs /actuator_outputs" + "px4::msg::EscStatus /esc_status" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" ) diff --git a/src/drivers/gps/CMakeLists.txt b/src/drivers/gps/CMakeLists.txt index 96e517f355..4ec9a4f626 100644 --- a/src/drivers/gps/CMakeLists.txt +++ b/src/drivers/gps/CMakeLists.txt @@ -52,4 +52,11 @@ px4_add_module( module.yaml DEPENDS git_gps_devices + SUBSCRIPTIONS + "px4::msg::GpsInjectData /gps_inject_data" + PUBLICATIONS + "px4::msg::SensorGps /sensor_gps0" + "px4::msg::SensorGps /sensor_gps1" + "px4::msg::SatelliteInfo /satellite_info" + "px4::msg::GpsDump /gps_dump" ) diff --git a/src/drivers/heater/CMakeLists.txt b/src/drivers/heater/CMakeLists.txt index 6f1fe2e249..ed19edb6ff 100644 --- a/src/drivers/heater/CMakeLists.txt +++ b/src/drivers/heater/CMakeLists.txt @@ -36,4 +36,6 @@ px4_add_module( COMPILE_FLAGS SRCS heater.cpp + PUBLICATIONS + "px4::msg::HeaterStatus /heater_status" ) diff --git a/src/drivers/imu/bosch/bmi088/CMakeLists.txt b/src/drivers/imu/bosch/bmi088/CMakeLists.txt index 8326953f07..4b791ff78b 100644 --- a/src/drivers/imu/bosch/bmi088/CMakeLists.txt +++ b/src/drivers/imu/bosch/bmi088/CMakeLists.txt @@ -51,4 +51,9 @@ px4_add_module( drivers_accelerometer drivers_gyroscope px4_work_queue + PUBLICATIONS + "px4::msg::SensorAccel /sensor_accel" + "px4::msg::SensorGyro /sensor_gyro" + "px4::msg::SensorAccelFifo /sensor_accel_fifo" + "px4::msg::SensorGyroFifo /sensor_gyro_fifo" ) diff --git a/src/drivers/imu/invensense/icm20602/CMakeLists.txt b/src/drivers/imu/invensense/icm20602/CMakeLists.txt index 05ed93d307..b9acfeb48f 100644 --- a/src/drivers/imu/invensense/icm20602/CMakeLists.txt +++ b/src/drivers/imu/invensense/icm20602/CMakeLists.txt @@ -44,4 +44,9 @@ px4_add_module( drivers_accelerometer drivers_gyroscope px4_work_queue + PUBLICATIONS + "px4::msg::SensorAccel /sensor_accel" + "px4::msg::SensorGyro /sensor_gyro" + "px4::msg::SensorAccelFifo /sensor_accel_fifo" + "px4::msg::SensorGyroFifo /sensor_gyro_fifo" ) diff --git a/src/drivers/imu/invensense/icm42688p/CMakeLists.txt b/src/drivers/imu/invensense/icm42688p/CMakeLists.txt index 8ffd2f7328..81aa7c3ca0 100644 --- a/src/drivers/imu/invensense/icm42688p/CMakeLists.txt +++ b/src/drivers/imu/invensense/icm42688p/CMakeLists.txt @@ -44,4 +44,9 @@ px4_add_module( px4_work_queue drivers_accelerometer drivers_gyroscope + PUBLICATIONS + "px4::msg::SensorAccel /sensor_accel" + "px4::msg::SensorGyro /sensor_gyro" + "px4::msg::SensorAccelFifo /sensor_accel_fifo" + "px4::msg::SensorGyroFifo /sensor_gyro_fifo" ) diff --git a/src/drivers/imu/invensense/mpu6000/CMakeLists.txt b/src/drivers/imu/invensense/mpu6000/CMakeLists.txt index 65af48a8e8..25c3dd5d1a 100644 --- a/src/drivers/imu/invensense/mpu6000/CMakeLists.txt +++ b/src/drivers/imu/invensense/mpu6000/CMakeLists.txt @@ -44,4 +44,9 @@ px4_add_module( drivers_accelerometer drivers_gyroscope px4_work_queue + PUBLICATIONS + "px4::msg::SensorAccel /sensor_accel" + "px4::msg::SensorGyro /sensor_gyro" + "px4::msg::SensorAccelFifo /sensor_accel_fifo" + "px4::msg::SensorGyroFifo /sensor_gyro_fifo" ) diff --git a/src/drivers/irlock/CMakeLists.txt b/src/drivers/irlock/CMakeLists.txt index 7d376f2406..ed06eb4f9d 100644 --- a/src/drivers/irlock/CMakeLists.txt +++ b/src/drivers/irlock/CMakeLists.txt @@ -37,4 +37,6 @@ px4_add_module( -Wno-cast-align # TODO: fix and enable SRCS irlock.cpp + PUBLICATIONS + "px4::msg::IrlockReport /irlock_report" ) diff --git a/src/drivers/lights/rgbled/CMakeLists.txt b/src/drivers/lights/rgbled/CMakeLists.txt index fd69c3e836..ec9ae709f7 100644 --- a/src/drivers/lights/rgbled/CMakeLists.txt +++ b/src/drivers/lights/rgbled/CMakeLists.txt @@ -39,4 +39,6 @@ px4_add_module( DEPENDS drivers__device led + SUBSCRIPTIONS + "px4::msg::LedControl /led_control" ) diff --git a/src/drivers/lights/rgbled_ncp5623c/CMakeLists.txt b/src/drivers/lights/rgbled_ncp5623c/CMakeLists.txt index 951e1aa1d5..68faf94e13 100755 --- a/src/drivers/lights/rgbled_ncp5623c/CMakeLists.txt +++ b/src/drivers/lights/rgbled_ncp5623c/CMakeLists.txt @@ -39,4 +39,6 @@ px4_add_module( DEPENDS drivers__device led + SUBSCRIPTIONS + "px4::msg::LedControl /led_control" ) diff --git a/src/drivers/lights/rgbled_pwm/CMakeLists.txt b/src/drivers/lights/rgbled_pwm/CMakeLists.txt index 2001cdc492..90d0a7a098 100644 --- a/src/drivers/lights/rgbled_pwm/CMakeLists.txt +++ b/src/drivers/lights/rgbled_pwm/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( led arch_io_pins arch_led_pwm + SUBSCRIPTIONS + "px4::msg::LedControl /led_control" ) diff --git a/src/drivers/magnetometer/akm/ak09916/CMakeLists.txt b/src/drivers/magnetometer/akm/ak09916/CMakeLists.txt index 13c86a7a4d..b445358dc5 100644 --- a/src/drivers/magnetometer/akm/ak09916/CMakeLists.txt +++ b/src/drivers/magnetometer/akm/ak09916/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/akm/ak8963/CMakeLists.txt b/src/drivers/magnetometer/akm/ak8963/CMakeLists.txt index c81e466669..a85a651371 100644 --- a/src/drivers/magnetometer/akm/ak8963/CMakeLists.txt +++ b/src/drivers/magnetometer/akm/ak8963/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/bosch/bmm150/CMakeLists.txt b/src/drivers/magnetometer/bosch/bmm150/CMakeLists.txt index 536d810f28..9f8e27ae22 100644 --- a/src/drivers/magnetometer/bosch/bmm150/CMakeLists.txt +++ b/src/drivers/magnetometer/bosch/bmm150/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/hmc5883/CMakeLists.txt b/src/drivers/magnetometer/hmc5883/CMakeLists.txt index 77b8784629..25a1a0a1c3 100644 --- a/src/drivers/magnetometer/hmc5883/CMakeLists.txt +++ b/src/drivers/magnetometer/hmc5883/CMakeLists.txt @@ -43,5 +43,7 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/isentek/ist8308/CMakeLists.txt b/src/drivers/magnetometer/isentek/ist8308/CMakeLists.txt index 0d74541f4e..e8701191dc 100644 --- a/src/drivers/magnetometer/isentek/ist8308/CMakeLists.txt +++ b/src/drivers/magnetometer/isentek/ist8308/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/isentek/ist8310/CMakeLists.txt b/src/drivers/magnetometer/isentek/ist8310/CMakeLists.txt index c9bc5d6a80..b92c238a48 100644 --- a/src/drivers/magnetometer/isentek/ist8310/CMakeLists.txt +++ b/src/drivers/magnetometer/isentek/ist8310/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/lis3mdl/CMakeLists.txt b/src/drivers/magnetometer/lis3mdl/CMakeLists.txt index 41809d2277..7ff0edf64f 100644 --- a/src/drivers/magnetometer/lis3mdl/CMakeLists.txt +++ b/src/drivers/magnetometer/lis3mdl/CMakeLists.txt @@ -42,4 +42,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/lsm303agr/CMakeLists.txt b/src/drivers/magnetometer/lsm303agr/CMakeLists.txt index 486628bf85..844c818866 100644 --- a/src/drivers/magnetometer/lsm303agr/CMakeLists.txt +++ b/src/drivers/magnetometer/lsm303agr/CMakeLists.txt @@ -39,4 +39,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/lsm9ds1_mag/CMakeLists.txt b/src/drivers/magnetometer/lsm9ds1_mag/CMakeLists.txt index 2776a51aff..3a6f6fe207 100644 --- a/src/drivers/magnetometer/lsm9ds1_mag/CMakeLists.txt +++ b/src/drivers/magnetometer/lsm9ds1_mag/CMakeLists.txt @@ -42,4 +42,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/qmc5883l/CMakeLists.txt b/src/drivers/magnetometer/qmc5883l/CMakeLists.txt index a48c25e38a..e5c3a220cf 100644 --- a/src/drivers/magnetometer/qmc5883l/CMakeLists.txt +++ b/src/drivers/magnetometer/qmc5883l/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/rm3100/CMakeLists.txt b/src/drivers/magnetometer/rm3100/CMakeLists.txt index 7f3f41d191..e165de831b 100644 --- a/src/drivers/magnetometer/rm3100/CMakeLists.txt +++ b/src/drivers/magnetometer/rm3100/CMakeLists.txt @@ -42,4 +42,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/magnetometer/vtrantech/vcm1193l/CMakeLists.txt b/src/drivers/magnetometer/vtrantech/vcm1193l/CMakeLists.txt index 58d28fc1cc..c73c2a0eb4 100644 --- a/src/drivers/magnetometer/vtrantech/vcm1193l/CMakeLists.txt +++ b/src/drivers/magnetometer/vtrantech/vcm1193l/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( DEPENDS drivers_magnetometer px4_work_queue + PUBLICATIONS + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/drivers/optical_flow/paw3902/CMakeLists.txt b/src/drivers/optical_flow/paw3902/CMakeLists.txt index 3626379ed2..17935cad7c 100644 --- a/src/drivers/optical_flow/paw3902/CMakeLists.txt +++ b/src/drivers/optical_flow/paw3902/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( conversion drivers__device px4_work_queue + PUBLICATIONS + "px4::msg::OpticalFlow /optical_flow" ) diff --git a/src/drivers/optical_flow/pmw3901/CMakeLists.txt b/src/drivers/optical_flow/pmw3901/CMakeLists.txt index 33bb3c4511..b2930733fb 100644 --- a/src/drivers/optical_flow/pmw3901/CMakeLists.txt +++ b/src/drivers/optical_flow/pmw3901/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( pmw3901_main.cpp PMW3901.cpp PMW3901.hpp + PUBLICATIONS + "px4::msg::OpticalFlow /optical_flow" ) diff --git a/src/drivers/optical_flow/px4flow/CMakeLists.txt b/src/drivers/optical_flow/px4flow/CMakeLists.txt index e3928d80d1..344d5da5c6 100644 --- a/src/drivers/optical_flow/px4flow/CMakeLists.txt +++ b/src/drivers/optical_flow/px4flow/CMakeLists.txt @@ -37,4 +37,6 @@ px4_add_module( -Wno-cast-align # TODO: fix and enable SRCS px4flow.cpp + PUBLICATIONS + "px4::msg::OpticalFlow /optical_flow" ) diff --git a/src/drivers/optical_flow/thoneflow/CMakeLists.txt b/src/drivers/optical_flow/thoneflow/CMakeLists.txt index 4fcbd5e4ff..0515f8202f 100644 --- a/src/drivers/optical_flow/thoneflow/CMakeLists.txt +++ b/src/drivers/optical_flow/thoneflow/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( thoneflow_parser.cpp MODULE_CONFIG module.yaml + PUBLICATIONS + "px4::msg::OpticalFlow /optical_flow" ) diff --git a/src/drivers/power_monitor/ina228/CMakeLists.txt b/src/drivers/power_monitor/ina228/CMakeLists.txt index 32af49b463..4753a9bd24 100644 --- a/src/drivers/power_monitor/ina228/CMakeLists.txt +++ b/src/drivers/power_monitor/ina228/CMakeLists.txt @@ -41,4 +41,6 @@ px4_add_module( DEPENDS battery px4_work_queue + PUBLICATIONS + "px4::msg::BatteryStatus /battery_status" ) diff --git a/src/drivers/power_monitor/voxlpm/CMakeLists.txt b/src/drivers/power_monitor/voxlpm/CMakeLists.txt index c1043a7d7d..47719ccf83 100644 --- a/src/drivers/power_monitor/voxlpm/CMakeLists.txt +++ b/src/drivers/power_monitor/voxlpm/CMakeLists.txt @@ -39,4 +39,7 @@ px4_add_module( DEPENDS battery px4_work_queue + PUBLICATIONS + "px4::msg::BatteryStatus /battery_status" + "px4::msg::PowerMonitor /power_monitor" ) diff --git a/src/drivers/pps_capture/CMakeLists.txt b/src/drivers/pps_capture/CMakeLists.txt index cf613393f6..b6940e687f 100644 --- a/src/drivers/pps_capture/CMakeLists.txt +++ b/src/drivers/pps_capture/CMakeLists.txt @@ -44,4 +44,6 @@ px4_add_module( -DPARAM_PREFIX="${PARAM_PREFIX}" SRCS PPSCapture.cpp + PUBLICATIONS + "px4::msg::PpsCapture /pps_capture" ) diff --git a/src/drivers/protocol_splitter/Kconfig b/src/drivers/protocol_splitter/Kconfig deleted file mode 100644 index f251603986..0000000000 --- a/src/drivers/protocol_splitter/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -menuconfig DRIVERS_PROTOCOL_SPLITTER - bool "protocol_splitter" - default n - ---help--- - Enable support for protocol_splitter \ No newline at end of file diff --git a/src/drivers/protocol_splitter/protocol_splitter.cpp b/src/drivers/protocol_splitter/protocol_splitter.cpp deleted file mode 100644 index dacf0bb4c5..0000000000 --- a/src/drivers/protocol_splitter/protocol_splitter.cpp +++ /dev/null @@ -1,845 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2016-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/** - * @file protocol_splitter.cpp - * - * NuttX Driver to multiplex MAVLink and RTPS on a single serial port. - * Makes sure the two protocols can be read & written simultaneously by two - * processes. - * - * It will create two devices: - * /dev/mavlink - * /dev/rtps - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -class Mavlink2Dev; -class RtpsDev; -class ReadBuffer; - -extern "C" __EXPORT int protocol_splitter_main(int argc, char *argv[]); - -/* - MessageType is in MSB of header[1] - | - v - Mavlink 0000 0000b - Rtps 1000 0000b -*/ -enum class MessageType : uint8_t {Mavlink = 0x00, Rtps = 0x01}; - -constexpr char Sp2HeaderMagic = 'S'; -constexpr int Sp2HeaderSize = 4; - -/* - Header Structure: - - bits: 1 2 3 4 5 6 7 8 - header[0] - | Magic | - header[1] - |T| LenH | - header[2] - | LenL | - header[3] - | Checksum | -*/ -union __attribute__((packed)) Sp2Header { - uint8_t bytes[4]; - struct { - char magic; // 'S' - uint8_t len_h: 7, // Length MSB - type: 1; // 0=MAVLINK, 1=RTPS - uint8_t len_l; // Length LSB - uint8_t checksum; // XOR of the three bytes above - } fields; -}; - -struct StaticData { - Mavlink2Dev *mavlink2; - RtpsDev *rtps; - sem_t r_lock; - sem_t w_lock; - char device_name[16]; - ReadBuffer *read_buffer; -}; - -namespace -{ -static StaticData *objects = nullptr; -} - -// Perf counters -perf_counter_t bytes_received_count; -perf_counter_t header_bytes_received_count; -perf_counter_t bytes_lost_count; -perf_counter_t mavlink_messages_parsed_count; -perf_counter_t mavlink_bytes_parsed_count; -perf_counter_t rtps_messages_parsed_count; -perf_counter_t rtps_bytes_parsed_count; -perf_counter_t buffer_drops; - -class ReadBuffer -{ -public: - int read(int fd); - void copy(void *dest, size_t pos, size_t n); - void remove(size_t pos, size_t n); - - void print_stats(); - void update_lost_stats(); - - uint8_t buffer[1024] = {}; - size_t buf_size = 0; - - static const size_t BUFFER_THRESHOLD = sizeof(buffer) * 0.8; - - // We keep track of the first Mavlink and Rtps packet in the buffer. - // If start and end are equal there is no packet. - size_t start_mavlink = 0; - size_t end_mavlink = 0; - size_t start_rtps = 0; - size_t end_rtps = 0; - // Just for stats. - size_t mavlink_parsed = 0; - size_t rtps_parsed = 0; - size_t bytes_received = 0; - size_t bytes_lost = 0; - size_t header_bytes_received = 0; -}; - -int ReadBuffer::read(int fd) -{ - if (buf_size > BUFFER_THRESHOLD) { - // Drop the buffer if it's too full. This is not expected to happen, but it might, if one of the readers - // is too slow. In that case it's best to make space for new data, otherwise the faster reader might - // busy-loop w/o getting new data. - perf_count(buffer_drops); - - PX4_DEBUG("Buffer full, dropping: %zu %zu %zu %zu", start_mavlink, end_mavlink, start_rtps, end_rtps); - - // Drop only as much as we have to - size_t num_remove = math::max(start_mavlink, start_rtps); - - if (num_remove == 0) { - num_remove = buf_size; - } - - remove(0, num_remove); - start_mavlink -= math::min(num_remove, start_mavlink); - end_mavlink -= math::min(num_remove, end_mavlink); - start_rtps -= math::min(num_remove, start_rtps); - end_rtps -= math::min(num_remove, end_rtps); - } - - int bytes_available = 0; - int err = ::ioctl(fd, FIONREAD, (unsigned long)&bytes_available); - ssize_t r = 0; - - if (err != 0 || bytes_available > 0) { - r = ::read(fd, buffer + buf_size, sizeof(buffer) - buf_size); - } - - if (r <= 0) { - return r; - } - - buf_size += r; - bytes_received += r; - - // Update the lost/unused bytes count - update_lost_stats(); - - perf_set_count(bytes_received_count, bytes_received); - - return r; -} - -void ReadBuffer::copy(void *dest, size_t pos, size_t n) -{ - ASSERT(pos < buf_size); - ASSERT(pos + n <= buf_size); - - if (dest) { - memcpy(dest, buffer + pos, n); - } -} - -void ReadBuffer::remove(size_t pos, size_t n) -{ - ASSERT(pos < buf_size); - ASSERT(pos + n <= buf_size); - - memmove(buffer + pos, buffer + (pos + n), buf_size - pos - n); - buf_size -= n; -} - -void ReadBuffer::update_lost_stats() -{ - bytes_lost = bytes_received - mavlink_parsed - rtps_parsed - header_bytes_received; - - if (end_mavlink > start_mavlink) { - bytes_lost -= end_mavlink - start_mavlink; - } - - if (end_rtps > start_rtps) { - bytes_lost -= end_rtps - start_rtps; - } - - perf_set_count(bytes_lost_count, bytes_lost); -} - -void ReadBuffer::print_stats() -{ - PX4_INFO_RAW("\tReceived:\n"); - PX4_INFO_RAW("\tTotal: %9zu bytes\n", - bytes_received); - PX4_INFO_RAW("\tHeaders: %9zu bytes (%5.1f %%)\n", - header_bytes_received, - static_cast(static_cast(header_bytes_received) - / static_cast(bytes_received) - * 100.f)); - PX4_INFO_RAW("\tMAVLink: %9zu bytes (%5.1f %%)\n", - mavlink_parsed, - static_cast(static_cast(mavlink_parsed) - / static_cast(bytes_received - header_bytes_received) - * 100.f)); - PX4_INFO_RAW("\tRTPS: %9zu bytes (%5.1f %%)\n", - rtps_parsed, - static_cast(static_cast(rtps_parsed) - / static_cast(bytes_received - header_bytes_received) - * 100.f)); - - PX4_INFO_RAW("\tUnused: %9zu bytes (%5.1f %%)\n", bytes_lost, - static_cast(static_cast(bytes_lost) - / static_cast(bytes_received) - * 100.f)); -} - - -class DevCommon : public cdev::CDev -{ -public: - DevCommon(const char *device_path, ReadBuffer *read_buffer); - virtual ~DevCommon(); - - virtual int ioctl(struct file *filp, int cmd, unsigned long arg); - - virtual int open(file *filp); - virtual int close(file *filp); - - enum Operation {Read, Write}; - -protected: - - virtual pollevent_t poll_state(struct file *filp); - - int try_to_copy_data(char *buffer, size_t buflen, MessageType message_type); - void scan_for_packets(); - void cleanup(); - - void lock(enum Operation op) - { - sem_t *this_lock = op == Read ? &objects->r_lock : &objects->w_lock; - - while (sem_wait(this_lock) != 0) { - /* The only case that an error should occur here is if - * the wait was awakened by a signal. - */ - ASSERT(get_errno() == EINTR); - } - } - - void unlock(enum Operation op) - { - sem_t *this_lock = op == Read ? &objects->r_lock : &objects->w_lock; - sem_post(this_lock); - } - - int _fd = -1; - - Sp2Header _header; - - ReadBuffer *_read_buffer; -}; - -DevCommon::DevCommon(const char *device_path, ReadBuffer *read_buffer) - : CDev(device_path) - , _read_buffer(read_buffer) -{ -} - -DevCommon::~DevCommon() -{ - if (_fd >= 0) { - // discard all pending data, as close() might block otherwise on NuttX with flow control enabled - tcflush(_fd, TCIOFLUSH); - ::close(_fd); - } -} - -int DevCommon::ioctl(struct file *filp, int cmd, unsigned long arg) -{ - if (cmd == FIONSPACE) { - int ret = ::ioctl(_fd, FIONSPACE, arg); - - if (ret == 0) { - int *buf_free = (int *)arg; - *buf_free -= sizeof(_header); - - if (*buf_free < 0) { - *buf_free = 0; - } - } - - return ret; - } - - return ::ioctl(_fd, cmd, arg); -} - -int DevCommon::open(file *filp) -{ - _fd = ::open(objects->device_name, O_RDWR | O_NOCTTY); - - if (_fd < 0) { - PX4_ERR("open failed: %s", strerror(errno)); - return -1; - } - - CDev::open(filp); - - - return 0; -} - -int DevCommon::close(file *filp) -{ - //int ret = ::close(_fd); // FIXME: calling this results in a dead-lock, because DevCommon::close() - // is called from within another close(), and NuttX seems to hold a semaphore at this point - _fd = -1; - CDev::close(filp); - return 0; -} - -pollevent_t DevCommon::poll_state(struct file *filp) -{ - pollfd fds[1]; - fds[0].fd = _fd; - fds[0].events = POLLIN; - - /* Here we should just check the poll state (which is called before an actual poll waiting). - * Instead we poll on the fd with some timeout, and then pretend that there is data. - * This will let the calling poll return immediately (there's still no busy loop since - * we do actually poll here). - * We do this because there is no simple way with the given interface to poll on - * the _fd in here or by overriding some other method. - */ - - ::poll(fds, sizeof(fds) / sizeof(fds[0]), 10); - - return POLLIN; -} - -int DevCommon::try_to_copy_data(char *buffer, size_t buflen, MessageType message_type) -{ - if (buflen == 0) { - return 0; - } - - switch (message_type) { - case MessageType::Mavlink: - if (_read_buffer->start_mavlink < _read_buffer->end_mavlink) { - // We have Mavlink data ready to send. - const size_t len_available = _read_buffer->end_mavlink - _read_buffer->start_mavlink; - // We can only send what fits in the callers buffer. - const size_t len_to_copy = math::min(len_available, buflen); - - // Copy it to the callers buffer and remove it from our buffer. - _read_buffer->copy(buffer, _read_buffer->start_mavlink, len_to_copy); - - // Shift the markers accordingly. - _read_buffer->start_mavlink += len_to_copy; - - // Keep track for stats. - _read_buffer->mavlink_parsed += len_to_copy; - - // Update the lost/unused bytes count - _read_buffer->update_lost_stats(); - - // Update the number of MAVLink bytes parsed - perf_set_count(mavlink_bytes_parsed_count, _read_buffer->mavlink_parsed); - - // Update the number of MAVLink messages parsed - perf_count(mavlink_messages_parsed_count); - - return len_to_copy; - - } else { - return 0; - } - - case MessageType::Rtps: - if (_read_buffer->start_rtps < _read_buffer->end_rtps) { - // We have Rtps data ready to send - const size_t len_available = _read_buffer->end_rtps - _read_buffer->start_rtps; - // We can only send what fits in the callers buffer. - const size_t len_to_copy = math::min(len_available, buflen); - - // Copy it to the callers buffer and remove it from our buffer. - _read_buffer->copy(buffer, _read_buffer->start_rtps, len_to_copy); - - // Shift the markers accordingly. - _read_buffer->start_rtps += len_to_copy; - - // Keep track for stats. - _read_buffer->rtps_parsed += len_to_copy; - - // Update the lost/unused bytes count - _read_buffer->update_lost_stats(); - - // Update the number of RTPS bytes parsed - perf_set_count(rtps_bytes_parsed_count, _read_buffer->rtps_parsed); - - // Update the number of RTPS messages parsed - perf_count(rtps_messages_parsed_count); - - return len_to_copy; - - } else { - return 0; - } - - break; - - - default: - return 0; - } -} - -void DevCommon::scan_for_packets() -{ - if (_read_buffer->buf_size < Sp2HeaderSize) { - // We have not even one header in the buffer, no need to scan yet. - return; - } - - bool mavlink_available = (_read_buffer->start_mavlink < _read_buffer->end_mavlink); - bool rtps_available = (_read_buffer->start_rtps < _read_buffer->end_rtps); - - if (mavlink_available && rtps_available) { - // We still have data for both, no need to scan yet. - return; - } - - const size_t begin = math::min(_read_buffer->end_mavlink, _read_buffer->end_rtps); - - for (size_t i = begin; i < _read_buffer->buf_size - Sp2HeaderSize; /* ++i */) { - - const Sp2Header *header = reinterpret_cast(&_read_buffer->buffer[i]); - - if (header->fields.magic != Sp2HeaderMagic) { - // Not the magic byte that we're looking for. - ++i; - continue; - } - - const uint8_t checksum = (_read_buffer->buffer[i] ^ _read_buffer->buffer[i + 1] ^ _read_buffer->buffer[i + 2]); - - if (header->fields.checksum != checksum) { - // Checksum failed. - ++i; - continue; - } - - if (header->fields.type != static_cast(MessageType::Mavlink) && - header->fields.type != static_cast(MessageType::Rtps)) { - // Ignore unknown protocols - ++i; - continue; - } - - const size_t payload_len = ((uint16_t)header->fields.len_h << 8) | header->fields.len_l; - - if (payload_len > sizeof(_read_buffer->buffer)) { - // This can happen if by accident data matches the header including checksum. - // Given we skip most data using the last payload_len, we should not see this too often, - // unless the link is very lossy and we often have to re-sync. - PX4_DEBUG("payload size %zu > buffer size %zu: %d, protocol: %s", - payload_len, sizeof(_read_buffer->buffer), - (header->fields.type == static_cast(MessageType::Mavlink)) ? "Mavlink" : "Rtps"); - ++i; - continue; - } - - if (i + Sp2HeaderSize + payload_len > _read_buffer->buf_size) { - // We don't have a enough data in the buffer yet, try again later. - break; - } - - if (header->fields.type == static_cast(MessageType::Mavlink) && !mavlink_available) { - _read_buffer->start_mavlink = i + Sp2HeaderSize; - _read_buffer->end_mavlink = _read_buffer->start_mavlink + payload_len; - mavlink_available = true; - - // Overwrite header magic byte, so we don't parse them again. - _read_buffer->buffer[i] = 0; - _read_buffer->header_bytes_received += Sp2HeaderSize; - - } else if (header->fields.type == static_cast(MessageType::Rtps) && !rtps_available) { - _read_buffer->start_rtps = i + Sp2HeaderSize; - _read_buffer->end_rtps = _read_buffer->start_rtps + payload_len; - rtps_available = true; - - // Overwrite header magic byte, so we don't parse them again. - _read_buffer->buffer[i] = 0; - _read_buffer->header_bytes_received += Sp2HeaderSize; - } - - if (mavlink_available && rtps_available) { - // Both have at least one message ready, we can stop now. - break; - } - - // Update the lost/unused bytes count - _read_buffer->update_lost_stats(); - - perf_set_count(header_bytes_received_count, _read_buffer->header_bytes_received); - - i += Sp2HeaderSize + payload_len; - - } -} - - -void DevCommon::cleanup() -{ - const bool mavlink_available = (_read_buffer->start_mavlink < _read_buffer->end_mavlink); - const bool rtps_available = (_read_buffer->start_rtps < _read_buffer->end_rtps); - - // Clean up garbage bytes and accumulated headers - - size_t garbage_end = 0; - - if (!mavlink_available && !rtps_available) { - garbage_end = math::max(_read_buffer->start_mavlink, _read_buffer->start_rtps); - - } else { - garbage_end = math::min(_read_buffer->start_mavlink, _read_buffer->start_rtps); - } - - if (garbage_end > 0) { - _read_buffer->remove(0, garbage_end); - - _read_buffer->start_mavlink -= math::min(garbage_end, _read_buffer->start_mavlink); - _read_buffer->end_mavlink -= math::min(garbage_end, _read_buffer->end_mavlink); - _read_buffer->start_rtps -= math::min(garbage_end, _read_buffer->start_rtps); - _read_buffer->end_rtps -= math::min(garbage_end, _read_buffer->end_rtps); - } -} - - -class Mavlink2Dev : public DevCommon -{ -public: - Mavlink2Dev(ReadBuffer *read_buffer); - virtual ~Mavlink2Dev() {} - - virtual ssize_t read(struct file *filp, char *buffer, size_t buflen); - virtual ssize_t write(struct file *filp, const char *buffer, size_t buflen); -}; - -Mavlink2Dev::Mavlink2Dev(ReadBuffer *read_buffer) - : DevCommon("/dev/mavlink", read_buffer) -{ - _header.fields.magic = Sp2HeaderMagic; - _header.fields.len_h = 0; - _header.fields.len_l = 0; - _header.fields.checksum = 0; - _header.fields.type = static_cast(MessageType::Mavlink); -} - -ssize_t Mavlink2Dev::read(struct file *filp, char *buffer, size_t buflen) -{ - lock(Read); - - // The cleanup needs to be right after a scan, so we don't clean up - // something that we haven't found yet. - scan_for_packets(); - cleanup(); - - // If we have already a packet ready in the current buffer, we don't have - // to read and can grab data straightaway. - int ret = try_to_copy_data(buffer, buflen, MessageType::Mavlink); - - if (ret > 0) { - unlock(Read); - return ret; - } - - // Otherwise, we have to do a read. - ret = _read_buffer->read(_fd); - - if (ret <= 0) { - unlock(Read); - return ret; - } - - // Now we need to check again if there is data available. - scan_for_packets(); - - // And try to copy it out. - ret = try_to_copy_data(buffer, buflen, MessageType::Mavlink); - - unlock(Read); - return ret; -} - -ssize_t Mavlink2Dev::write(struct file *filp, const char *buffer, size_t buflen) -{ - _header.fields.len_h = (buflen >> 8) & 0x7f; - _header.fields.len_l = buflen & 0xff; - _header.fields.checksum = _header.bytes[0] ^ _header.bytes[1] ^ _header.bytes[2]; - lock(Write); - int buf_free = 0; - ::ioctl(_fd, FIONSPACE, (unsigned long)&buf_free); - ssize_t ret = -1; - - if ((size_t)buf_free >= sizeof(_header) + buflen) { - ret = ::write(_fd, _header.bytes, sizeof(_header)); - - if (ret == sizeof(_header)) { - ret = ::write(_fd, buffer, buflen); - } - } - - unlock(Write); - - return ret; -} - -class RtpsDev : public DevCommon -{ -public: - RtpsDev(ReadBuffer *read_buffer); - virtual ~RtpsDev() {} - - virtual ssize_t read(struct file *filp, char *buffer, size_t buflen); - virtual ssize_t write(struct file *filp, const char *buffer, size_t buflen); - -protected: - static const uint8_t HEADER_SIZE = 10; -}; - -RtpsDev::RtpsDev(ReadBuffer *read_buffer) - : DevCommon("/dev/rtps", read_buffer) -{ - _header.fields.magic = Sp2HeaderMagic; - _header.fields.len_h = 0; - _header.fields.len_l = 0; - _header.fields.checksum = 0; - _header.fields.type = static_cast(MessageType::Rtps); -} - -ssize_t RtpsDev::read(struct file *filp, char *buffer, size_t buflen) -{ - lock(Read); - - scan_for_packets(); - cleanup(); - - // If we have already a packet ready in the current buffer, we don't have to read. - int ret = try_to_copy_data(buffer, buflen, MessageType::Rtps); - - if (ret > 0) { - unlock(Read); - return ret; - } - - // Otherwise, we have to do a read. - ret = _read_buffer->read(_fd); - - if (ret <= 0) { - unlock(Read); - return ret; - } - - scan_for_packets(); - - // And check again. - ret = try_to_copy_data(buffer, buflen, MessageType::Rtps); - - unlock(Read); - return ret; -} - -ssize_t RtpsDev::write(struct file *filp, const char *buffer, size_t buflen) -{ - _header.fields.len_h = (buflen >> 8) & 0x7f; - _header.fields.len_l = buflen & 0xff; - _header.fields.checksum = _header.bytes[0] ^ _header.bytes[1] ^ _header.bytes[2]; - lock(Write); - int buf_free = 0; - ssize_t ret = -1; - ::ioctl(_fd, FIONSPACE, (unsigned long)&buf_free); - - if ((size_t)buf_free >= sizeof(_header) + buflen) { - ret = ::write(_fd, _header.bytes, sizeof(_header)); - - if (ret == sizeof(_header)) { - ret = ::write(_fd, buffer, buflen); - } - } - - unlock(Write); - - return ret; -} - -int protocol_splitter_main(int argc, char *argv[]) -{ - if (argc < 2) { - goto out; - } - - /* - * Start/load the driver. - */ - if (!strcmp(argv[1], "start")) { - if (objects) { - PX4_WARN("already running"); - return 1; - } - - if (argc != 3) { - goto out; - } - - objects = new StaticData(); - - if (!objects) { - PX4_ERR("alloc failed"); - return -1; - } - - bytes_received_count = perf_alloc(PC_COUNT, "protocol_splitter: bytes received"); - bytes_lost_count = perf_alloc(PC_COUNT, "protocol_splitter: bytes unused/lost"); - header_bytes_received_count = perf_alloc(PC_COUNT, "protocol_splitter: header bytes received"); - mavlink_messages_parsed_count = perf_alloc(PC_COUNT, "protocol_splitter: MAVLink msgs parsed"); - mavlink_bytes_parsed_count = perf_alloc(PC_COUNT, "protocol_splitter: MAVLink msgs bytes parsed"); - rtps_messages_parsed_count = perf_alloc(PC_COUNT, "protocol_splitter: RTPS msgs parsed"); - rtps_bytes_parsed_count = perf_alloc(PC_COUNT, "protocol_splitter: RTPS msgs bytes parsed"); - buffer_drops = perf_alloc(PC_COUNT, "protocol_splitter: buffer drops"); - - strncpy(objects->device_name, argv[2], sizeof(objects->device_name)); - sem_init(&objects->r_lock, 1, 1); - sem_init(&objects->w_lock, 1, 1); - objects->read_buffer = new ReadBuffer(); - objects->mavlink2 = new Mavlink2Dev(objects->read_buffer); - objects->rtps = new RtpsDev(objects->read_buffer); - - if (!objects->mavlink2 || !objects->rtps) { - delete objects->mavlink2; - delete objects->rtps; - delete objects->read_buffer; - sem_destroy(&objects->r_lock); - sem_destroy(&objects->w_lock); - delete objects; - objects = nullptr; - PX4_ERR("alloc failed"); - return -1; - - } else { - objects->mavlink2->init(); - objects->rtps->init(); - } - } - - if (!strcmp(argv[1], "stop")) { - if (objects) { - delete objects->mavlink2; - delete objects->rtps; - delete objects->read_buffer; - sem_destroy(&objects->r_lock); - sem_destroy(&objects->w_lock); - delete objects; - objects = nullptr; - - perf_free(bytes_received_count); - perf_free(header_bytes_received_count); - perf_free(bytes_lost_count); - perf_free(mavlink_messages_parsed_count); - perf_free(mavlink_bytes_parsed_count); - perf_free(rtps_messages_parsed_count); - perf_free(rtps_bytes_parsed_count); - perf_free(buffer_drops); - } - } - - /* - * Print driver status. - */ - if (!strcmp(argv[1], "status")) { - if (objects) { - PX4_INFO("running"); - - if (sem_wait(&objects->r_lock) != 0) { - return -1; - } - - objects->read_buffer->print_stats(); - sem_post(&objects->r_lock); - - } else { - PX4_INFO("not running"); - } - } - - return 0; - -out: - PX4_ERR("unrecognized command, try 'start ', 'stop', 'status'"); - return 1; -} diff --git a/src/drivers/pwm_input/CMakeLists.txt b/src/drivers/pwm_input/CMakeLists.txt index e3e8a7d32c..1acf6cec04 100644 --- a/src/drivers/pwm_input/CMakeLists.txt +++ b/src/drivers/pwm_input/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( -Wno-cast-align # TODO: fix and enable SRCS pwm_input.cpp + PUBLICATIONS + "px4::msg::PwmInput /pwm_input" ) diff --git a/src/drivers/pwm_out/CMakeLists.txt b/src/drivers/pwm_out/CMakeLists.txt index 1e5fa884bc..90a995a48d 100644 --- a/src/drivers/pwm_out/CMakeLists.txt +++ b/src/drivers/pwm_out/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015-2020 PX4 Development Team. All rights reserved. +# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -51,4 +51,21 @@ px4_add_module( arch_io_pins mixer mixer_module + PUBLICATIONS + "px4::msg::ActuatorOutputs /actuator_outputs" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" + "px4::msg::ActuatorControls /actuator_controls_3" + + # TODO: mixer_module + "px4::msg::ActuatorArmed /actuator_armed" + "px4::msg::ActuatorMotors /actuator_motors" + "px4::msg::ActuatorServosTrim /actuator_servos_trim" + "px4::msg::ActuatorTest /actuator_test" + "px4::msg::LandingGear /landing_gear" + "px4::msg::ManualControlSetpoint /manual_control_setpoint" + "px4::msg::TestMotor /test_motor" + "px4::msg::VehicleCommand /vehicle_command" ) diff --git a/src/drivers/pwm_out_sim/CMakeLists.txt b/src/drivers/pwm_out_sim/CMakeLists.txt index 49e6a87117..e35a203cb7 100644 --- a/src/drivers/pwm_out_sim/CMakeLists.txt +++ b/src/drivers/pwm_out_sim/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015 PX4 Development Team. All rights reserved. +# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -47,4 +47,10 @@ px4_add_module( DEPENDS mixer mixer_module + PUBLICATIONS + "px4::msg::ActuatorOutputs /actuator_outputs_sim" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" ) diff --git a/src/drivers/px4io/CMakeLists.txt b/src/drivers/px4io/CMakeLists.txt index afb5d189fb..2e77f7fcf2 100644 --- a/src/drivers/px4io/CMakeLists.txt +++ b/src/drivers/px4io/CMakeLists.txt @@ -45,6 +45,14 @@ px4_add_module( arch_px4io_serial circuit_breaker mixer_module + PUBLICATIONS + "px4::msg::InputRc /input_rc" + "px4::msg::Px4ioStatus /px4io_status" + "px4::msg::Safety /safety" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" ) # include the px4io binary in ROMFS diff --git a/src/drivers/rc_input/CMakeLists.txt b/src/drivers/rc_input/CMakeLists.txt index 6c58fb2605..1c3104128e 100644 --- a/src/drivers/rc_input/CMakeLists.txt +++ b/src/drivers/rc_input/CMakeLists.txt @@ -42,4 +42,6 @@ px4_add_module( module.yaml DEPENDS rc + PUBLICATIONS + "px4::msg::InputRc /input_rc" ) diff --git a/src/drivers/roboclaw/CMakeLists.txt b/src/drivers/roboclaw/CMakeLists.txt index 5543f89146..95ee0ea260 100644 --- a/src/drivers/roboclaw/CMakeLists.txt +++ b/src/drivers/roboclaw/CMakeLists.txt @@ -39,5 +39,7 @@ px4_add_module( RoboClaw.cpp MODULE_CONFIG module.yaml + PUBLICATIONS + "px4::msg::WheelEncoders /wheel_encoders" ) diff --git a/src/drivers/rpm/pcf8583/CMakeLists.txt b/src/drivers/rpm/pcf8583/CMakeLists.txt index 9eef9031a7..ecebf38084 100644 --- a/src/drivers/rpm/pcf8583/CMakeLists.txt +++ b/src/drivers/rpm/pcf8583/CMakeLists.txt @@ -41,4 +41,6 @@ px4_add_module( DEPENDS drivers__device px4_work_queue + PUBLICATIONS + "px4::msg::Rpm /rpm" ) diff --git a/src/drivers/rpm/rpm_simulator/CMakeLists.txt b/src/drivers/rpm/rpm_simulator/CMakeLists.txt index 0cf7ad37db..2ca3dd701b 100644 --- a/src/drivers/rpm/rpm_simulator/CMakeLists.txt +++ b/src/drivers/rpm/rpm_simulator/CMakeLists.txt @@ -36,4 +36,6 @@ px4_add_module( SRCS rpm_simulator.cpp DEPENDS + PUBLICATIONS + "px4::msg::Rpm /rpm" ) diff --git a/src/drivers/safety_button/CMakeLists.txt b/src/drivers/safety_button/CMakeLists.txt index f1fb9c3e6b..4c32057712 100644 --- a/src/drivers/safety_button/CMakeLists.txt +++ b/src/drivers/safety_button/CMakeLists.txt @@ -39,4 +39,9 @@ px4_add_module( DEPENDS circuit_breaker px4_work_queue + PUBLICATIONS + "px4::msg::LedControl /led_control" + "px4::msg::Safety /safety" + "px4::msg::TuneControl /tune_control" + "px4::msg::VehicleCommand /vehicle_command" # TODO srv ) diff --git a/src/drivers/tap_esc/CMakeLists.txt b/src/drivers/tap_esc/CMakeLists.txt index 4ef6473932..6ba918d6b6 100644 --- a/src/drivers/tap_esc/CMakeLists.txt +++ b/src/drivers/tap_esc/CMakeLists.txt @@ -48,4 +48,11 @@ px4_add_module( mixer mixer_module tunes + PUBLICATIONS + "px4::msg::ActuatorOutputs /actuator_outputs" + "px4::msg::EscStatus /esc_status" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" ) diff --git a/src/drivers/telemetry/frsky_telemetry/CMakeLists.txt b/src/drivers/telemetry/frsky_telemetry/CMakeLists.txt index 719227703b..d351e5f34e 100644 --- a/src/drivers/telemetry/frsky_telemetry/CMakeLists.txt +++ b/src/drivers/telemetry/frsky_telemetry/CMakeLists.txt @@ -40,5 +40,7 @@ px4_add_module( frsky_telemetry.cpp MODULE_CONFIG module.yaml + SUBSCRIPTIONS + "px4::msg::VehicleGlobalPosition /vehicle_global_position" ) diff --git a/src/drivers/telemetry/iridiumsbd/CMakeLists.txt b/src/drivers/telemetry/iridiumsbd/CMakeLists.txt index ec289764b9..8612675d28 100644 --- a/src/drivers/telemetry/iridiumsbd/CMakeLists.txt +++ b/src/drivers/telemetry/iridiumsbd/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( MODULE_CONFIG module.yaml DEPENDS + PUBLICATIONS + "px4::msg::IridiumsbdStatus /iridiumsbd_status" ) diff --git a/src/drivers/test_ppm/test_ppm.cpp b/src/drivers/test_ppm/test_ppm.cpp index 0e817ef8c9..3b280c9ad3 100644 --- a/src/drivers/test_ppm/test_ppm.cpp +++ b/src/drivers/test_ppm/test_ppm.cpp @@ -55,7 +55,6 @@ #include #include -#include #include #include diff --git a/src/drivers/tone_alarm/CMakeLists.txt b/src/drivers/tone_alarm/CMakeLists.txt index 3e9f6ba482..0ff8df226d 100644 --- a/src/drivers/tone_alarm/CMakeLists.txt +++ b/src/drivers/tone_alarm/CMakeLists.txt @@ -43,4 +43,6 @@ px4_add_module( circuit_breaker arch_tone_alarm tunes + PUBLICATIONS + "px4::msg::TuneControl /tune_control" ) diff --git a/src/drivers/uavcan/CMakeLists.txt b/src/drivers/uavcan/CMakeLists.txt index 8dd796f12e..67b87cb2e7 100644 --- a/src/drivers/uavcan/CMakeLists.txt +++ b/src/drivers/uavcan/CMakeLists.txt @@ -186,4 +186,10 @@ px4_add_module( # within libuavcan uavcan + + PUBLICATIONS + "px4::msg::EscStatus /esc_status" + "px4::msg::GpsInjectData /gps_inject_data" + "px4::msg::UavcanParameterValue /uavcan_parameter_value" # TODO: srv + "px4::msg::InternalCombustionEngineStatus /internal_combustion_engine_status" ) diff --git a/src/drivers/uavcan_v1/CMakeLists.txt b/src/drivers/uavcan_v1/CMakeLists.txt index b07540af96..fd114e5028 100644 --- a/src/drivers/uavcan_v1/CMakeLists.txt +++ b/src/drivers/uavcan_v1/CMakeLists.txt @@ -136,4 +136,16 @@ px4_add_module( git_legacy_data_types version ${DPNDS} + PUBLICATIONS + "px4::msg::BatteryStatus /battery_status" + #"px4::msg::OutputControl /output_control_mc" + SUBSCRIPTIONS + "px4::msg::ActuatorArmed /actuator_armed" + "px4::msg::ActuatorOutputs /actuator_outputs" + "px4::msg::ParameterUpdate /parameter_update" + "px4::msg::SensorGps /sensor_gps" + + "px4::msg::Event /event" + "px4::msg::LogMessage /log_message" + "px4::msg::MavlinkLog /mavlink_log" ) diff --git a/src/drivers/uavcannode/CMakeLists.txt b/src/drivers/uavcannode/CMakeLists.txt index a9fbda10af..b90145e1d4 100644 --- a/src/drivers/uavcannode/CMakeLists.txt +++ b/src/drivers/uavcannode/CMakeLists.txt @@ -153,4 +153,23 @@ px4_add_module( # within libuavcan uavcan + PUBLICATIONS + "px4::msg::GpsInjectData /gps_inject_data" + "px4::msg::LedControl /led_control" + "px4::msg::TuneControl /tune_control" + SUBSCRIPTIONS + "px4::msg::ActuatorArmed /actuator_armed" + "px4::msg::BatteryStatus /battery_status" + "px4::msg::DifferentialPressure /differential_pressure" + "px4::msg::DistanceSensor /distance_sensor" + "px4::msg::Event /event" + "px4::msg::LedControl /led_control" + "px4::msg::LogMessage /log_message" + "px4::msg::MavlinkLog /mavlink_log" + "px4::msg::OpticalFlow /optical_flow" + "px4::msg::ParameterUpdate /parameter_update" + "px4::msg::Safety /safety" + "px4::msg::SensorBaro /sensor_baro" + "px4::msg::SensorGps /sensor_gps" + "px4::msg::SensorMag /sensor_mag" ) diff --git a/src/examples/fake_imu/CMakeLists.txt b/src/examples/fake_imu/CMakeLists.txt index 2d037d7387..636d6d90b9 100644 --- a/src/examples/fake_imu/CMakeLists.txt +++ b/src/examples/fake_imu/CMakeLists.txt @@ -43,4 +43,10 @@ px4_add_module( drivers_accelerometer drivers_gyroscope px4_work_queue + PUBLICATIONS + "px4::msg::EscStatus /esc_status" + "px4::msg::SensorGyro /sensor_gyro" + "px4::msg::SensorGyroFifo /sensor_gyro_fifo" + "px4::msg::SensorAccel /sensor_accel" + "px4::msg::SensorAccelFifo /sensor_accel_fifo" ) diff --git a/src/examples/fixedwing_control/main.cpp b/src/examples/fixedwing_control/main.cpp index a37b93983e..856c299dcf 100644 --- a/src/examples/fixedwing_control/main.cpp +++ b/src/examples/fixedwing_control/main.cpp @@ -307,7 +307,7 @@ int fixedwing_control_thread_main(int argc, char *argv[]) * Advertise that this controller will publish actuator * control values and the rate setpoint */ - orb_advert_t actuator_pub = orb_advertise(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, &actuators); + orb_advert_t actuator_pub = orb_advertise(ORB_ID(actuator_controls_0), &actuators); orb_advert_t rates_pub = orb_advertise(ORB_ID(vehicle_rates_setpoint), &rates_sp); /* subscribe to topics. */ @@ -404,7 +404,7 @@ int fixedwing_control_thread_main(int argc, char *argv[]) PX4_ISFINITE(actuators.control[1]) && PX4_ISFINITE(actuators.control[2]) && PX4_ISFINITE(actuators.control[3])) { - orb_publish(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, actuator_pub, &actuators); + orb_publish(ORB_ID(actuator_controls_0), actuator_pub, &actuators); if (verbose) { warnx("published"); @@ -422,7 +422,7 @@ int fixedwing_control_thread_main(int argc, char *argv[]) actuators.control[i] = 0.0f; } - orb_publish(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, actuator_pub, &actuators); + orb_publish(ORB_ID(actuator_controls_0), actuator_pub, &actuators); fflush(stdout); diff --git a/src/examples/px4_simple_app/CMakeLists.txt b/src/examples/px4_simple_app/CMakeLists.txt index 6a91bc47e1..7689c1e004 100644 --- a/src/examples/px4_simple_app/CMakeLists.txt +++ b/src/examples/px4_simple_app/CMakeLists.txt @@ -34,6 +34,6 @@ px4_add_module( MODULE examples__px4_simple_app MAIN px4_simple_app SRCS - px4_simple_app.c + px4_simple_app.cpp DEPENDS ) diff --git a/src/examples/px4_simple_app/px4_simple_app.c b/src/examples/px4_simple_app/px4_simple_app.cpp similarity index 97% rename from src/examples/px4_simple_app/px4_simple_app.c rename to src/examples/px4_simple_app/px4_simple_app.cpp index c9f9d61927..1765f7e76e 100644 --- a/src/examples/px4_simple_app/px4_simple_app.c +++ b/src/examples/px4_simple_app/px4_simple_app.cpp @@ -51,9 +51,7 @@ #include #include -__EXPORT int px4_simple_app_main(int argc, char *argv[]); - -int px4_simple_app_main(int argc, char *argv[]) +extern "C" __EXPORT int px4_simple_app_main(int argc, char *argv[]) { PX4_INFO("Hello Sky!"); diff --git a/src/examples/rover_steering_control/main.cpp b/src/examples/rover_steering_control/main.cpp index ae00604e60..eeac6f13c6 100644 --- a/src/examples/rover_steering_control/main.cpp +++ b/src/examples/rover_steering_control/main.cpp @@ -261,7 +261,7 @@ int rover_steering_control_thread_main(int argc, char *argv[]) * Advertise that this controller will publish actuator * control values and the rate setpoint */ - orb_advert_t actuator_pub = orb_advertise(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, &actuators); + orb_advert_t actuator_pub = orb_advertise(ORB_ID(actuator_controls_0), &actuators); /* subscribe to topics. */ int att_sub = orb_subscribe(ORB_ID(vehicle_attitude)); @@ -357,7 +357,7 @@ int rover_steering_control_thread_main(int argc, char *argv[]) PX4_ISFINITE(actuators.control[1]) && PX4_ISFINITE(actuators.control[2]) && PX4_ISFINITE(actuators.control[3])) { - orb_publish(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, actuator_pub, &actuators); + orb_publish(ORB_ID(actuator_controls_0), actuator_pub, &actuators); if (verbose) { warnx("published"); @@ -377,7 +377,7 @@ int rover_steering_control_thread_main(int argc, char *argv[]) actuators.timestamp = hrt_absolute_time(); - orb_publish(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, actuator_pub, &actuators); + orb_publish(ORB_ID(actuator_controls_0), actuator_pub, &actuators); fflush(stdout); diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index dd87dc2dd7..22f8052f29 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -31,43 +31,44 @@ # ############################################################################ -add_subdirectory(airspeed) -add_subdirectory(avoidance) -add_subdirectory(battery) -add_subdirectory(bezier) -add_subdirectory(cdev) -add_subdirectory(circuit_breaker) -add_subdirectory(collision_prevention) -add_subdirectory(component_information) -add_subdirectory(controllib) -add_subdirectory(conversion) -add_subdirectory(crypto) -add_subdirectory(drivers) -add_subdirectory(field_sensor_bias_estimator) -add_subdirectory(geo) -add_subdirectory(hysteresis) -add_subdirectory(l1) -add_subdirectory(landing_slope) -add_subdirectory(led) -add_subdirectory(matrix) -add_subdirectory(mathlib) -add_subdirectory(mixer) -add_subdirectory(mixer_module) -add_subdirectory(motion_planning) -add_subdirectory(npfg) -add_subdirectory(perf) -add_subdirectory(pid) -add_subdirectory(pid_design) -add_subdirectory(pwm) -add_subdirectory(rc) -add_subdirectory(sensor_calibration) -add_subdirectory(slew_rate) -add_subdirectory(systemlib) -add_subdirectory(system_identification) -add_subdirectory(tecs) -add_subdirectory(terrain_estimation) -add_subdirectory(tunes) -add_subdirectory(version) -add_subdirectory(weather_vane) -add_subdirectory(wind_estimator) -add_subdirectory(world_magnetic_model) +add_subdirectory(airspeed EXCLUDE_FROM_ALL) +add_subdirectory(avoidance EXCLUDE_FROM_ALL) +add_subdirectory(battery EXCLUDE_FROM_ALL) +add_subdirectory(bezier EXCLUDE_FROM_ALL) +add_subdirectory(cdev EXCLUDE_FROM_ALL) +add_subdirectory(circuit_breaker EXCLUDE_FROM_ALL) +add_subdirectory(collision_prevention EXCLUDE_FROM_ALL) +add_subdirectory(component_information EXCLUDE_FROM_ALL) +add_subdirectory(controllib EXCLUDE_FROM_ALL) +add_subdirectory(conversion EXCLUDE_FROM_ALL) +add_subdirectory(crc EXCLUDE_FROM_ALL) +add_subdirectory(crypto EXCLUDE_FROM_ALL) +add_subdirectory(drivers EXCLUDE_FROM_ALL) +add_subdirectory(field_sensor_bias_estimator EXCLUDE_FROM_ALL) +add_subdirectory(geo EXCLUDE_FROM_ALL) +add_subdirectory(hysteresis EXCLUDE_FROM_ALL) +add_subdirectory(l1 EXCLUDE_FROM_ALL) +add_subdirectory(landing_slope EXCLUDE_FROM_ALL) +add_subdirectory(led EXCLUDE_FROM_ALL) +add_subdirectory(matrix EXCLUDE_FROM_ALL) +add_subdirectory(mathlib EXCLUDE_FROM_ALL) +add_subdirectory(mixer EXCLUDE_FROM_ALL) +add_subdirectory(mixer_module EXCLUDE_FROM_ALL) +add_subdirectory(motion_planning EXCLUDE_FROM_ALL) +add_subdirectory(npfg EXCLUDE_FROM_ALL) +add_subdirectory(perf EXCLUDE_FROM_ALL) +add_subdirectory(pid EXCLUDE_FROM_ALL) +add_subdirectory(pid_design EXCLUDE_FROM_ALL) +add_subdirectory(pwm EXCLUDE_FROM_ALL) +add_subdirectory(rc EXCLUDE_FROM_ALL) +add_subdirectory(sensor_calibration EXCLUDE_FROM_ALL) +add_subdirectory(slew_rate EXCLUDE_FROM_ALL) +add_subdirectory(systemlib EXCLUDE_FROM_ALL) +add_subdirectory(system_identification EXCLUDE_FROM_ALL) +add_subdirectory(tecs EXCLUDE_FROM_ALL) +add_subdirectory(terrain_estimation EXCLUDE_FROM_ALL) +add_subdirectory(tunes EXCLUDE_FROM_ALL) +add_subdirectory(version EXCLUDE_FROM_ALL) +add_subdirectory(weather_vane EXCLUDE_FROM_ALL) +add_subdirectory(wind_estimator EXCLUDE_FROM_ALL) +add_subdirectory(world_magnetic_model EXCLUDE_FROM_ALL) diff --git a/src/lib/crc/CMakeLists.txt b/src/lib/crc/CMakeLists.txt new file mode 100644 index 0000000000..95b85eb483 --- /dev/null +++ b/src/lib/crc/CMakeLists.txt @@ -0,0 +1,39 @@ +############################################################################ +# +# Copyright (c) 2015-2021 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +add_library(crc STATIC + EXCLUDE_FROM_ALL + crc.c crc.h +) +add_dependencies(crc prebuild_targets) +target_compile_options(crc PRIVATE ${MAX_CUSTOM_OPT_LEVEL}) diff --git a/src/lib/systemlib/crc.c b/src/lib/crc/crc.c similarity index 100% rename from src/lib/systemlib/crc.c rename to src/lib/crc/crc.c diff --git a/src/lib/systemlib/crc.h b/src/lib/crc/crc.h similarity index 100% rename from src/lib/systemlib/crc.h rename to src/lib/crc/crc.h diff --git a/src/lib/parameters/CMakeLists.txt b/src/lib/parameters/CMakeLists.txt index 20b315994b..20549fe6f4 100644 --- a/src/lib/parameters/CMakeLists.txt +++ b/src/lib/parameters/CMakeLists.txt @@ -146,9 +146,9 @@ add_custom_command(OUTPUT px4_parameters.hpp px_generate_params.py templates/px4_parameters.hpp.jinja ) +add_custom_target(generate_parameters DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/px4_parameters.hpp) set(SRCS) - list(APPEND SRCS parameters.cpp) if(BUILD_TESTING) list(APPEND SRCS param_translation_unit_tests.cpp) @@ -161,11 +161,9 @@ if(${PX4_PLATFORM} STREQUAL "nuttx") endif() # TODO: find a better way to do this -if (NOT "${PX4_BOARD}" MATCHES "px4_io") - add_library(parameters - ${SRCS} - px4_parameters.hpp - ) +if((NOT "${PX4_BOARD}" MATCHES "io-v2") AND (NOT "${PX4_BOARD_LABEL}" MATCHES "bootloader")) + + add_library(parameters STATIC EXCLUDE_FROM_ALL ${SRCS} px4_parameters.hpp) target_link_libraries(parameters PRIVATE perf tinybson px4_platform) @@ -177,7 +175,7 @@ if (NOT "${PX4_BOARD}" MATCHES "px4_io") -Wno-sign-compare # TODO: fix and enable ) else() - add_library(parameters ${PX4_SOURCE_DIR}/platforms/common/empty.c) + add_library(parameters STATIC EXCLUDE_FROM_ALL ${PX4_SOURCE_DIR}/platforms/common/empty.c) endif() add_dependencies(parameters prebuild_targets) diff --git a/src/lib/parameters/tinybson/CMakeLists.txt b/src/lib/parameters/tinybson/CMakeLists.txt index a8d5b43e79..58a635fc9b 100644 --- a/src/lib/parameters/tinybson/CMakeLists.txt +++ b/src/lib/parameters/tinybson/CMakeLists.txt @@ -31,7 +31,7 @@ # ############################################################################ -add_library(tinybson tinybson.cpp) +add_library(tinybson tinybson.cpp tinybson.h) target_compile_definitions(tinybson PRIVATE -DMODULE_NAME="tinybson") target_compile_options(tinybson PRIVATE -Wno-sign-compare) # TODO: fix this add_dependencies(tinybson prebuild_targets) diff --git a/src/lib/systemlib/CMakeLists.txt b/src/lib/systemlib/CMakeLists.txt index 155a1a2b24..726fa7c074 100644 --- a/src/lib/systemlib/CMakeLists.txt +++ b/src/lib/systemlib/CMakeLists.txt @@ -32,17 +32,17 @@ ############################################################################ set(SRCS - conversions.c - conversions.h - crc.c - crc.h + err.h mavlink_log.cpp mavlink_log.h + px4_macros.h ) if(${PX4_PLATFORM} STREQUAL "nuttx") list(APPEND SRCS otp.c + otp.h + ppm_decode.h ) endif() diff --git a/src/lib/systemlib/otp.c b/src/lib/systemlib/otp.c index 0c070a5146..ee1c4f865c 100644 --- a/src/lib/systemlib/otp.c +++ b/src/lib/systemlib/otp.c @@ -49,9 +49,8 @@ #include #include #include // memset -#include "conversions.h" #include "otp.h" -#include "err.h" // warnx +#include "err.h" // warnx #include diff --git a/src/modules/airship_att_control/CMakeLists.txt b/src/modules/airship_att_control/CMakeLists.txt index 9771e2ad51..d5be546482 100644 --- a/src/modules/airship_att_control/CMakeLists.txt +++ b/src/modules/airship_att_control/CMakeLists.txt @@ -40,4 +40,7 @@ px4_add_module( airship_att_control_main.cpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::VehicleTorqueSetpoint /vehicle_torque_setpoint" ) diff --git a/src/modules/airship_att_control/airship_att_control_main.cpp b/src/modules/airship_att_control/airship_att_control_main.cpp index d642746421..15d0a1a1b8 100644 --- a/src/modules/airship_att_control/airship_att_control_main.cpp +++ b/src/modules/airship_att_control/airship_att_control_main.cpp @@ -190,7 +190,7 @@ int AirshipAttitudeControl::print_status() perf_print_counter(_loop_perf); - print_message(ORB_ID(actuator_controls), _actuators); + print_message(ORB_ID(actuator_controls_0), _actuators); return 0; } diff --git a/src/modules/airspeed_selector/CMakeLists.txt b/src/modules/airspeed_selector/CMakeLists.txt index fb3d24f869..b4988ea5c2 100644 --- a/src/modules/airspeed_selector/CMakeLists.txt +++ b/src/modules/airspeed_selector/CMakeLists.txt @@ -39,4 +39,7 @@ px4_add_module( AirspeedValidator.hpp DEPENDS wind_estimator + PUBLICATIONS + "px4::msg::AirspeedValidated /airspeed_validated" + "px4::msg::AirspeedWind /airspeed_wind" ) diff --git a/src/modules/airspeed_selector/airspeed_selector_main.cpp b/src/modules/airspeed_selector/airspeed_selector_main.cpp index 55d3bf90a5..fd2dce8d13 100644 --- a/src/modules/airspeed_selector/airspeed_selector_main.cpp +++ b/src/modules/airspeed_selector/airspeed_selector_main.cpp @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include #include @@ -120,7 +120,7 @@ private: uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)}; uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)}; uORB::Subscription _vtol_vehicle_status_sub{ORB_ID(vtol_vehicle_status)}; - uORB::Subscription _position_setpoint_sub{ORB_ID(position_setpoint)}; + uORB::Subscription _position_setpoint_triplet_sub{ORB_ID(position_setpoint_triplet)}; uORB::SubscriptionMultiArray _airspeed_subs{ORB_ID::airspeed}; @@ -132,7 +132,7 @@ private: vehicle_local_position_s _vehicle_local_position {}; vehicle_status_s _vehicle_status {}; vtol_vehicle_status_s _vtol_vehicle_status {}; - position_setpoint_s _position_setpoint {}; + position_setpoint_s _position_setpoint{}; WindEstimator _wind_estimator_sideslip; /**< wind estimator instance only fusing sideslip */ airspeed_wind_s _wind_estimate_sideslip {}; /**< wind estimate message for wind estimator instance only fusing sideslip */ @@ -499,7 +499,14 @@ void AirspeedModule::poll_topics() _vehicle_status_sub.update(&_vehicle_status); _vtol_vehicle_status_sub.update(&_vtol_vehicle_status); _vehicle_local_position_sub.update(&_vehicle_local_position); - _position_setpoint_sub.update(&_position_setpoint); + + if (_position_setpoint_triplet_sub.updated()) { + position_setpoint_triplet_s position_setpoint_triplet; + + if (_position_setpoint_triplet_sub.update(&position_setpoint_triplet)) { + _position_setpoint = position_setpoint_triplet.current; + } + } _vehicle_local_position_valid = (_time_now_usec - _vehicle_local_position.timestamp < 1_s) diff --git a/src/modules/angular_velocity_controller/CMakeLists.txt b/src/modules/angular_velocity_controller/CMakeLists.txt index 4cfc369db7..3e191fe6f3 100644 --- a/src/modules/angular_velocity_controller/CMakeLists.txt +++ b/src/modules/angular_velocity_controller/CMakeLists.txt @@ -46,4 +46,9 @@ px4_add_module( mathlib AngularVelocityControl px4_work_queue + PUBLICATIONS + "px4::msg::RateCtrlStatus /rate_ctrl_status" + "px4::msg::VehicleAngularAccelerationSetpoint /vehicle_angular_acceleration_setpoint" + "px4::msg::VehicleThrustSetpoint /vehicle_thrust_setpoint" + "px4::msg::VehicleTorqueSetpoint /vehicle_torque_setpoint" ) diff --git a/src/modules/attitude_estimator_q/CMakeLists.txt b/src/modules/attitude_estimator_q/CMakeLists.txt index df9084fe20..5f7dc6b401 100644 --- a/src/modules/attitude_estimator_q/CMakeLists.txt +++ b/src/modules/attitude_estimator_q/CMakeLists.txt @@ -40,5 +40,7 @@ px4_add_module( attitude_estimator_q_main.cpp DEPENDS world_magnetic_model + PUBLICATIONS + "px4::msg::VehicleAttitude /vehicle_attitude" ) diff --git a/src/modules/battery_status/CMakeLists.txt b/src/modules/battery_status/CMakeLists.txt index df7c9967d4..f06516a31c 100644 --- a/src/modules/battery_status/CMakeLists.txt +++ b/src/modules/battery_status/CMakeLists.txt @@ -39,6 +39,8 @@ px4_add_module( analog_battery.cpp MODULE_CONFIG module.yaml + PUBLICATIONS + "px4::msg::BatteryStatus /battery_status" DEPENDS battery conversion diff --git a/src/modules/camera_feedback/CMakeLists.txt b/src/modules/camera_feedback/CMakeLists.txt index 152f2c1eae..c7d250709e 100644 --- a/src/modules/camera_feedback/CMakeLists.txt +++ b/src/modules/camera_feedback/CMakeLists.txt @@ -38,4 +38,6 @@ px4_add_module( CameraFeedback.hpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::CameraCapture /camera_capture" ) diff --git a/src/modules/commander/CMakeLists.txt b/src/modules/commander/CMakeLists.txt index 9a68a8f184..c0be9a8344 100644 --- a/src/modules/commander/CMakeLists.txt +++ b/src/modules/commander/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015 PX4 Development Team. All rights reserved. +# Copyright (c) 2015-2021 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,7 +37,45 @@ add_subdirectory(Arming) px4_add_module( MODULE modules__commander MAIN commander - COMPILE_FLAGS + PUBLICATIONS + "px4::msg::ActuatorArmed /actuator_armed" + "px4::msg::ActuatorTest /actuator_test" + "px4::msg::CommanderState /commander_state" + "px4::msg::FailureDetectorStatus /failure_detector_status" + "px4::msg::LedControl /led_control" + "px4::msg::HomePosition /home_position" + "px4::msg::MavlinkLog /mavlink_log" + "px4::msg::LogMessage /log_message" + "px4::msg::MagWorkerData /mag_worker_data" + "px4::msg::Event /event" + "px4::msg::Mission /mission" + "px4::msg::TestMotor /test_motor" + "px4::msg::TuneControl /tune_control" + "px4::msg::VehicleCommand /vehicle_command" # TODO: srv + "px4::msg::VehicleCommandAck /vehicle_command_ack" # TODO: srv + "px4::msg::VehicleControlMode /vehicle_control_mode" + "px4::msg::VehicleStatus /vehicle_status" + "px4::msg::VehicleStatusFlags /vehicle_status_flags" + SUBSCRIPTIONS + "px4::msg::ParameterUpdate /parameter_update" # TODO: parameter server + + "px4::msg::AirspeedValidated /airspeed_validated" + "px4::msg::Cpuload /cpuload" + "px4::msg::DifferentialPressure /differential_pressure" # airspeed calibration + "px4::msg::EscStatus /esc_status" + "px4::msg::EstimatorSelectorStatus /estimator_selector_status" + "px4::msg::EstimatorStatus /estimator_status" + "px4::msg::EstimatorStatusFlags /estimator_status_flags" + "px4::msg::EstimatorSensorBias /estimator_sensor_bias" + "px4::msg::PwmInput /pwm_input" # TODO: + "px4::msg::IridiumsbdStatus /iridiumsbd_status" # TODO: + "px4::msg::Safety /safety" # TODO: SITL + "px4::msg::SystemPower /system_power" # TODO: SITL + "px4::msg::PowerButtonState /power_button_state" # TODO: SITL + "px4::msg::VehicleGlobalPosition /vehicle_global_position" + "px4::msg::VehicleLocalPosition /vehicle_local_position" + "px4::msg::VtolVehicleStatus /vtol_vehicle_status" + "px4::msg::Wind /wind" SRCS accelerometer_calibration.cpp airspeed_calibration.cpp diff --git a/src/modules/commander/Commander.hpp b/src/modules/commander/Commander.hpp index 74d44a718d..dd640c814c 100644 --- a/src/modules/commander/Commander.hpp +++ b/src/modules/commander/Commander.hpp @@ -45,6 +45,8 @@ #include #include +#include + // publications #include #include @@ -410,7 +412,7 @@ private: // Subscriptions uORB::Subscription _action_request_sub {ORB_ID(action_request)}; - uORB::Subscription _actuator_controls_sub{ORB_ID_VEHICLE_ATTITUDE_CONTROLS}; + uORB::Subscription _actuator_controls_sub{ORB_ID(actuator_controls_0)}; uORB::Subscription _cmd_sub {ORB_ID(vehicle_command)}; uORB::Subscription _cpuload_sub{ORB_ID(cpuload)}; uORB::Subscription _esc_status_sub{ORB_ID(esc_status)}; diff --git a/src/modules/control_allocator/CMakeLists.txt b/src/modules/control_allocator/CMakeLists.txt index a3ab199f26..8c1094e5eb 100644 --- a/src/modules/control_allocator/CMakeLists.txt +++ b/src/modules/control_allocator/CMakeLists.txt @@ -53,4 +53,16 @@ px4_add_module( ControlAllocation mixer px4_work_queue + PUBLICATIONS + "px4::msg::ActuatorMotors /actuator_motors" + "px4::msg::ActuatorServos /actuator_servos" + "px4::msg::ActuatorServosTrim /actuator_servos_trim" + "px4::msg::ControlAllocatorStatus /control_allocator_status" + SUBSCRIPTIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_1" + "px4::msg::ActuatorControls /actuator_controls_2" + "px4::msg::VehicleTorqueSetpoint /vehicle_torque_setpoint" + "px4::msg::VehicleThrustSetpoint /vehicle_thrust_setpoint" + "px4::msg::VehicleStatus /vehicle_status" ) diff --git a/src/modules/ekf2/CMakeLists.txt b/src/modules/ekf2/CMakeLists.txt index be24c05d9d..17b0045b14 100644 --- a/src/modules/ekf2/CMakeLists.txt +++ b/src/modules/ekf2/CMakeLists.txt @@ -83,6 +83,37 @@ px4_add_module( px4_work_queue world_magnetic_model UNITY_BUILD + PUBLICATIONS + "px4::msg::Ekf2Timestamps /ekf2_timestamps" + "px4::msg::EstimatorBaroBias /estimator_baro_bias" + "px4::msg::EstimatorGpsStatus /estimator_gps_status" + "px4::msg::EstimatorInnovations /estimator_innovations" + "px4::msg::EstimatorInnovations /estimator_innovation_test_ratios" + "px4::msg::EstimatorInnovations /estimator_innovation_variances" + "px4::msg::EstimatorOpticalFlowVel /estimator_optical_flow_vel" + "px4::msg::EstimatorSensorBias /estimator_sensor_bias" + "px4::msg::EstimatorStates /estimator_states" + "px4::msg::EstimatorStatus /estimator_status" + "px4::msg::EstimatorStatusFlags /estimator_status_flags" + "px4::msg::EstimatorEventFlags /estimator_event_flags" + "px4::msg::VehicleOdometry /estimator_visual_odometry_aligned" + "px4::msg::YawEstimatorStatus /yaw_estimator_status" + + "px4::msg::VehicleAttitude /vehicle_attitude" + "px4::msg::VehicleLocalPosition /vehicle_local_position" + "px4::msg::VehicleGlobalPosition /vehicle_global_position" + "px4::msg::VehicleOdometry /vehicle_odometry" + "px4::msg::Wind /wind" + + # multi-EKF + "px4::msg::EstimatorSelectorStatus /estimator_selector_status" + "px4::msg::VehicleAttitude /estimator_attitude" + "px4::msg::VehicleLocalPosition /estimator_local_position" + "px4::msg::VehicleGlobalPosition /estimator_global_position" + "px4::msg::VehicleOdometry /estimator_odometry" + "px4::msg::Wind /estimator_wind" + SUBSCRIPTIONS + "px4::msg::OpticalFlow /optical_flow" ) if(BUILD_TESTING) diff --git a/src/modules/flight_mode_manager/CMakeLists.txt b/src/modules/flight_mode_manager/CMakeLists.txt index 1d8e82bf98..9925597f10 100644 --- a/src/modules/flight_mode_manager/CMakeLists.txt +++ b/src/modules/flight_mode_manager/CMakeLists.txt @@ -127,6 +127,21 @@ px4_add_module( px4_work_queue WeatherVane flighttasks_generated + PUBLICATIONS + "px4::msg::LandingGear /landing_gear" + "px4::msg::VehicleLocalPositionSetpoint /trajectory_setpoint" + "px4::msg::VehicleConstraints /vehicle_constraints" + "px4::msg::OrbitStatus /orbit_status" + "px4::msg::CollisionConstraints /collision_constraints" + "px4::msg::ObstacleDistance /obstacle_distance_fused" + "px4::msg::ObstacleDistance /obstacle_distance" + "px4::msg::VehicleTrajectoryBezier /vehicle_trajectory_bezier" + "px4::msg::VehicleTrajectoryWaypoint /vehicle_trajectory_waypoint" + "px4::msg::VehicleTrajectoryWaypoint /vehicle_trajectory_waypoint_desired" + SUBSCRIPTIONS + "px4::msg::VehicleAttitude /vehicle_attitude" + "px4::msg::VehicleLocalPosition /vehicle_local_position" + "px4::msg::VehicleLocalPositionSetpoint /vehicle_local_position_setpoint" ) # add all flight task dependencies diff --git a/src/modules/fw_att_control/CMakeLists.txt b/src/modules/fw_att_control/CMakeLists.txt index a8c79cf31c..4063b01733 100644 --- a/src/modules/fw_att_control/CMakeLists.txt +++ b/src/modules/fw_att_control/CMakeLists.txt @@ -44,4 +44,15 @@ px4_add_module( ecl_yaw_controller.cpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::ActuatorControls /actuator_controls_virtual_fw" # VTOL + "px4::msg::ActuatorControlsStatus /actuator_controls_status_0" + "px4::msg::ActuatorControlsStatus /actuator_controls_status_1" # VTOL + "px4::msg::VehicleAttitudeSetpoint /vehicle_attitude_setpoint" + "px4::msg::VehicleAttitudeSetpoint /fw_virtual_attitude_setpoint" # VTOL + "px4::msg::VehicleRatesSetpoint /vehicle_rates_setpoint" + "px4::msg::RateCtrlStatus /rate_ctrl_status" + "px4::msg::VehicleTorqueSetpoint /vehicle_torque_setpoint" + "px4::msg::VehicleThrustSetpoint /vehicle_thrust_setpoint" ) diff --git a/src/modules/fw_autotune_attitude_control/CMakeLists.txt b/src/modules/fw_autotune_attitude_control/CMakeLists.txt index 3c898a21ac..4630cbc931 100644 --- a/src/modules/fw_autotune_attitude_control/CMakeLists.txt +++ b/src/modules/fw_autotune_attitude_control/CMakeLists.txt @@ -44,4 +44,6 @@ px4_add_module( mathlib px4_work_queue SystemIdentification + PUBLICATIONS + "px4::msg::AutotuneAttitudeControlStatus /autotune_attitude_control_status" ) diff --git a/src/modules/fw_pos_control_l1/CMakeLists.txt b/src/modules/fw_pos_control_l1/CMakeLists.txt index 7aff012788..61b65a6ffa 100644 --- a/src/modules/fw_pos_control_l1/CMakeLists.txt +++ b/src/modules/fw_pos_control_l1/CMakeLists.txt @@ -42,11 +42,19 @@ px4_add_module( FixedwingPositionControl.hpp DEPENDS l1 - launchdetection landing_slope - npfg + launchdetection + motion_planning + npfg runway_takeoff SlewRate tecs - motion_planning + PUBLICATIONS + "px4::msg::VehicleAttitudeSetpoint /vehicle_attitude_setpoint" + "px4::msg::VehicleAttitudeSetpoint /fw_virtual_attitude_setpoint" # VTOL + "px4::msg::PositionControllerStatus /position_controller_status" + "px4::msg::PositionControllerLandingStatus /position_controller_landing_status" + "px4::msg::NpfgStatus /npfg_status" + "px4::msg::OrbitStatus /orbit_status" + "px4::msg::TecsStatus /tecs_status" ) diff --git a/src/modules/gimbal/CMakeLists.txt b/src/modules/gimbal/CMakeLists.txt index a99123b089..eb2a4fa04d 100644 --- a/src/modules/gimbal/CMakeLists.txt +++ b/src/modules/gimbal/CMakeLists.txt @@ -45,5 +45,12 @@ px4_add_module( gimbal.cpp DEPENDS geo + PUBLICATIONS + "px4::msg::GimbalManagerInformation /gimbal_manager_information" + "px4::msg::GimbalManagerStatus /gimbal_manager_status" + "px4::msg::GimbalDeviceSetAttitude /gimbal_device_set_attitude" + "px4::msg::GimbalDeviceAttitudeStatus /gimbal_device_attitude_status" + "px4::msg::MountOrientation /mount_orientation" + "px4::msg::VehicleCommand /gimbal_v1_command" ) diff --git a/src/modules/gyro_fft/CMakeLists.txt b/src/modules/gyro_fft/CMakeLists.txt index 85c6bbf461..52ea30ee52 100644 --- a/src/modules/gyro_fft/CMakeLists.txt +++ b/src/modules/gyro_fft/CMakeLists.txt @@ -77,4 +77,6 @@ px4_add_module( ${CMSIS_DSP}/Source/TransformFunctions/arm_rfft_q15.c DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::SensorGyroFft /sensor_gyro_fft" ) diff --git a/src/modules/land_detector/CMakeLists.txt b/src/modules/land_detector/CMakeLists.txt index 632c90d5cf..8559af764f 100644 --- a/src/modules/land_detector/CMakeLists.txt +++ b/src/modules/land_detector/CMakeLists.txt @@ -44,5 +44,12 @@ px4_add_module( AirshipLandDetector.cpp DEPENDS hysteresis + px4_work_queue + PUBLICATIONS + "px4::msg::VehicleLandDetected /vehicle_land_detected" + SUBSCRIPTIONS + "px4::msg::HoverThrustEstimate /hover_thrust_estimate" + "px4::msg::TakeoffStatus /takeoff_status" + "px4::msg::VehicleLocalPositionSetpoint /trajectory_setpoint" ) diff --git a/src/modules/landing_target_estimator/CMakeLists.txt b/src/modules/landing_target_estimator/CMakeLists.txt index 146869aa3f..7c6214203d 100644 --- a/src/modules/landing_target_estimator/CMakeLists.txt +++ b/src/modules/landing_target_estimator/CMakeLists.txt @@ -40,5 +40,8 @@ px4_add_module( LandingTargetEstimator.cpp KalmanFilter.cpp DEPENDS + PUBLICATIONS + "px4::msg::LandingTargetPose /landing_target_pose" + "px4::msg::LandingTargetInnovations /landing_target_innovations" ) diff --git a/src/modules/load_mon/CMakeLists.txt b/src/modules/load_mon/CMakeLists.txt index a2c6ee0647..66f0358184 100644 --- a/src/modules/load_mon/CMakeLists.txt +++ b/src/modules/load_mon/CMakeLists.txt @@ -39,4 +39,7 @@ px4_add_module( LoadMon.hpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::Cpuload /cpuload" + "px4::msg::TaskStackInfo /task_stack_info" ) diff --git a/src/modules/logger/CMakeLists.txt b/src/modules/logger/CMakeLists.txt index 58ecc3f837..fd467c20a0 100644 --- a/src/modules/logger/CMakeLists.txt +++ b/src/modules/logger/CMakeLists.txt @@ -48,4 +48,7 @@ px4_add_module( watchdog.cpp DEPENDS version + PUBLICATIONS + "px4::msg::LoggerStatus /logger_status" + "px4::msg::UlogStream /ulog_stream" ) diff --git a/src/modules/logger/logged_topics.cpp b/src/modules/logger/logged_topics.cpp index a878d38ee2..b98478140b 100644 --- a/src/modules/logger/logged_topics.cpp +++ b/src/modules/logger/logged_topics.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include diff --git a/src/modules/logger/logged_topics.h b/src/modules/logger/logged_topics.h index 4a990d5ba7..d17398da64 100644 --- a/src/modules/logger/logged_topics.h +++ b/src/modules/logger/logged_topics.h @@ -36,7 +36,7 @@ #include #include -#include +#include namespace px4 { diff --git a/src/modules/logger/logger.cpp b/src/modules/logger/logger.cpp index 4e7fa75fe1..143d8a3664 100644 --- a/src/modules/logger/logger.cpp +++ b/src/modules/logger/logger.cpp @@ -46,7 +46,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/modules/mag_bias_estimator/CMakeLists.txt b/src/modules/mag_bias_estimator/CMakeLists.txt index d52a439d3c..c1ce938081 100644 --- a/src/modules/mag_bias_estimator/CMakeLists.txt +++ b/src/modules/mag_bias_estimator/CMakeLists.txt @@ -40,4 +40,6 @@ px4_add_module( MagBiasEstimator.hpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::MagnetometerBiasEstimate /magnetometer_bias_estimate" ) diff --git a/src/modules/manual_control/CMakeLists.txt b/src/modules/manual_control/CMakeLists.txt index c96478afdf..265fd1d018 100644 --- a/src/modules/manual_control/CMakeLists.txt +++ b/src/modules/manual_control/CMakeLists.txt @@ -43,6 +43,11 @@ px4_add_module( ManualControlSelector.cpp DEPENDS px4_work_queue + PUBLICATIONS + "px4::msg::ActionRequest /action_request" + "px4::msg::LandingGear /landing_gear" + "px4::msg::ManualControlSetpoint /manual_control_input" + "px4::msg::ManualControlSetpoint /manual_control_setpoint" ) px4_add_unit_gtest(SRC ManualControlSelectorTest.cpp LINKLIBS module__manual_control) diff --git a/src/modules/mavlink/CMakeLists.txt b/src/modules/mavlink/CMakeLists.txt index 60a0196099..314bde3a8c 100644 --- a/src/modules/mavlink/CMakeLists.txt +++ b/src/modules/mavlink/CMakeLists.txt @@ -111,6 +111,81 @@ px4_add_module( tunes version UNITY_BUILD + PUBLICATIONS + "px4::msg::MavlinkTunnel /mavlink_tunnel" + "px4::msg::Mission /mission" + "px4::msg::RcParameterMap /rc_parameter_map" + "px4::msg::TelemetryStatus /telemetry_status" + "px4::msg::TimesyncStatus /timesync_status" + "px4::msg::UavcanParameterRequest /uavcan_parameter_request" # TODO: srv + "px4::msg::UavcanParameterValue /uavcan_parameter_value" # TODO: srv + "px4::msg::UlogStreamAck /ulog_stream_ack" # TODO: srv + + "px4::msg::InternalCombustionEngineStatus /internal_combustion_engine_status" # TODO: SITL + + # TODO: mavlink receiver + "px4::msg::CollisionReport /collision_report" + "px4::msg::VehicleOdometry /vehicle_visual_odometry" + "px4::msg::VehicleOdometry /vehicle_mocap_odometry" + "px4::msg::OffboardControlMode /offboard_control_mode" + "px4::msg::CameraStatus /camera_status" + "px4::msg::CellularStatus /cellular_status" + "px4::msg::FollowTarget /follow_target" + "px4::msg::GimbalManagerSetAttitude /gimbal_manager_set_attitude" + "px4::msg::GimbalManagerSetManualControl /gimbal_manager_set_manual_control" + "px4::msg::GimbalDeviceInformation /gimbal_device_information" + "px4::msg::LandingTargetPose /landing_target_pose" + "px4::msg::OnboardComputerStatus /onboard_computer_status" + "px4::msg::Ping /ping" + "px4::msg::GpsInjectData /gps_inject_data" + "px4::msg::RadioStatus /radio_status" + "px4::msg::TransponderReport /transponder_report" + "px4::msg::GeneratorStatus /generator_status" + "px4::msg::InputRc /input_rc" + "px4::msg::OpticalFlow /optical_flow" + "px4::msg::SensorGps /sensor_gps" + "px4::msg::VehicleOdometry /vehicle_odometry" + "px4::msg::ObstacleDistance /obstacle_distance" + "px4::msg::VehicleRatesSetpoint /vehicle_rates_setpoint" + + "px4::msg::DebugArray /debug_array" + "px4::msg::DebugKeyValue /debug_key_value" + "px4::msg::DebugValue /debug_value" + "px4::msg::DebugVect /debug_vect" + SUBSCRIPTIONS + "px4::msg::ActuatorOutputs /actuator_outputs" + "px4::msg::ActuatorOutputs /actuator_outputs_sim" + "px4::msg::AutotuneAttitudeControlStatus /autotune_attitude_control_status" + "px4::msg::CameraCapture /camera_capture" + "px4::msg::CameraTrigger /camera_trigger" + "px4::msg::MountOrientation /mount_orientation" + "px4::msg::SensorHygrometer /sensor_hygrometer" + "px4::msg::SatelliteInfo /satellite_info" + "px4::msg::TecsStatus /tecs_status" + "px4::msg::VehicleAttitudeSetpoint /fw_virtual_attitude_setpoint" # VTOL + "px4::msg::VehicleAttitudeSetpoint /mc_virtual_attitude_setpoint" # VTOL + "px4::msg::IrlockReport /irlock_report" + "px4::msg::Rpm /rpm" + + "px4::msg::GimbalDeviceAttitudeStatus /gimbal_device_attitude_status" + "px4::msg::GimbalDeviceSetAttitude /gimbal_device_set_attitude" + "px4::msg::GimbalManagerInformation /gimbal_manager_information" + "px4::msg::GimbalManagerStatus /gimbal_manager_status" + "px4::msg::VehicleCommand /gimbal_v1_command" + + "px4::msg::VehicleAttitudeSetpoint /vehicle_attitude_setpoint" + "px4::msg::VehicleTrajectoryBezier /vehicle_trajectory_bezier" + "px4::msg::VehicleTrajectoryWaypoint /vehicle_trajectory_waypoint" + "px4::msg::VehicleTrajectoryWaypoint /vehicle_trajectory_waypoint_desired" + + "px4::msg::ObstacleDistance /obstacle_distance_fused" + "px4::msg::OrbitStatus /orbit_status" + "px4::msg::VehicleLocalPositionSetpoint /vehicle_local_position_setpoint" + + "px4::msg::VehicleAngularVelocity /vehicle_angular_velocity_groundtruth" + "px4::msg::VehicleAttitude /vehicle_attitude_groundtruth" + "px4::msg::VehicleGlobalPosition /vehicle_global_position_groundtruth" + "px4::msg::VehicleLocalPosition /vehicle_local_position_groundtruth" ) if(PX4_TESTING) diff --git a/src/modules/mc_att_control/CMakeLists.txt b/src/modules/mc_att_control/CMakeLists.txt index abd0f4ff9c..551478b5f7 100644 --- a/src/modules/mc_att_control/CMakeLists.txt +++ b/src/modules/mc_att_control/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015-2019 PX4 Development Team. All rights reserved. +# Copyright (c) 2015-2021 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -36,6 +36,10 @@ add_subdirectory(AttitudeControl) px4_add_module( MODULE modules__mc_att_control MAIN mc_att_control + PUBLICATIONS + "px4::msg::VehicleAttitudeSetpoint /vehicle_attitude_setpoint" + "px4::msg::VehicleAttitudeSetpoint /mc_virtual_attitude_setpoint" # VTOL + "px4::msg::VehicleRatesSetpoint /vehicle_rates_setpoint" COMPILE_FLAGS ${MAX_CUSTOM_OPT_LEVEL} SRCS diff --git a/src/modules/mc_autotune_attitude_control/CMakeLists.txt b/src/modules/mc_autotune_attitude_control/CMakeLists.txt index 62dfc9ca3c..9600fd53ea 100644 --- a/src/modules/mc_autotune_attitude_control/CMakeLists.txt +++ b/src/modules/mc_autotune_attitude_control/CMakeLists.txt @@ -44,4 +44,6 @@ px4_add_module( mathlib px4_work_queue SystemIdentification + PUBLICATIONS + "px4::msg::AutotuneAttitudeControlStatus /autotune_attitude_control_status" ) diff --git a/src/modules/mc_hover_thrust_estimator/CMakeLists.txt b/src/modules/mc_hover_thrust_estimator/CMakeLists.txt index 9504a18add..848fbd05d4 100644 --- a/src/modules/mc_hover_thrust_estimator/CMakeLists.txt +++ b/src/modules/mc_hover_thrust_estimator/CMakeLists.txt @@ -49,6 +49,8 @@ px4_add_module( mathlib px4_work_queue zero_order_hover_thrust_ekf + PUBLICATIONS + "px4::msg::HoverThrustEstimate /hover_thrust_estimate" ) px4_add_unit_gtest(SRC zero_order_hover_thrust_ekf_test.cpp LINKLIBS zero_order_hover_thrust_ekf) diff --git a/src/modules/mc_pos_control/CMakeLists.txt b/src/modules/mc_pos_control/CMakeLists.txt index 49f61ac967..34f5d4e0a9 100644 --- a/src/modules/mc_pos_control/CMakeLists.txt +++ b/src/modules/mc_pos_control/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015-2020 PX4 Development Team. All rights reserved. +# Copyright (c) 2015-2021 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,6 +37,11 @@ add_subdirectory(Takeoff) px4_add_module( MODULE modules__mc_pos_control MAIN mc_pos_control + PUBLICATIONS + "px4::msg::TakeoffStatus /takeoff_status" + "px4::msg::VehicleAttitudeSetpoint /vehicle_attitude_setpoint" + "px4::msg::VehicleAttitudeSetpoint /mc_virtual_attitude_setpoint" # VTOL + "px4::msg::VehicleLocalPositionSetpoint /vehicle_local_position_setpoint" COMPILE_FLAGS SRCS MulticopterPositionControl.cpp diff --git a/src/modules/mc_rate_control/CMakeLists.txt b/src/modules/mc_rate_control/CMakeLists.txt index 758d56b7c8..27fa84dbf0 100644 --- a/src/modules/mc_rate_control/CMakeLists.txt +++ b/src/modules/mc_rate_control/CMakeLists.txt @@ -36,6 +36,14 @@ add_subdirectory(RateControl) px4_add_module( MODULE modules__mc_rate_control MAIN mc_rate_control + PUBLICATIONS + "px4::msg::ActuatorControls /actuator_controls_0" + "px4::msg::VehicleAttitudeSetpoint /actuator_controls_virtual_mc" # VTOL + "px4::msg::ActuatorControlsStatus /actuator_controls_status_0" + "px4::msg::RateCtrlStatus /rate_ctrl_status" + "px4::msg::VehicleRatesSetpoint /vehicle_rates_setpoint" + "px4::msg::VehicleTorqueSetpoint /vehicle_torque_setpoint" + "px4::msg::VehicleThrustSetpoint /vehicle_thrust_setpoint" COMPILE_FLAGS ${MAX_CUSTOM_OPT_LEVEL} SRCS diff --git a/src/modules/micrortps_bridge/CMakeLists.txt b/src/modules/micrortps_bridge/CMakeLists.txt deleted file mode 100644 index 003898f247..0000000000 --- a/src/modules/micrortps_bridge/CMakeLists.txt +++ /dev/null @@ -1,190 +0,0 @@ -############################################################################ -# -# Copyright (c) 2017 PX4 Development Team. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# 3. Neither the name PX4 nor the names of its contributors may be -# used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -############################################################################ - -#============================================================================= -# RTPS and micro-cdr -# - -find_program(FASTRTPSGEN fastrtpsgen PATHS $ENV{FASTRTPSGEN_DIR}) -if(NOT FASTRTPSGEN) - message(FATAL_ERROR "Unable to find fastrtpsgen") -else() - execute_process( - COMMAND $ENV{FASTRTPSGEN_DIR}fastrtpsgen -version - OUTPUT_VARIABLE FASTRTPSGEN_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - ) - message(STATUS "${FASTRTPSGEN_VERSION}") -endif() - -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS - ${PX4_SOURCE_DIR}/msg/tools/urtps_bridge_topics.yaml -) - -if (EXISTS "${PX4_SOURCE_DIR}/msg/tools/urtps_bridge_topics.yaml") - set(config_rtps_send_topics) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/msg/tools/uorb_rtps_classifier.py -sa - OUTPUT_VARIABLE config_rtps_send_topics - ) - set(config_rtps_send_alias_topics "") - string(FIND ${config_rtps_send_topics} "alias" found_send_alias) - if (NOT ${found_send_alias} EQUAL "-1") - STRING(REGEX REPLACE ".*alias " "" config_rtps_send_alias_topics "${config_rtps_send_topics}") - STRING(REPLACE ", " ";" config_rtps_send_alias_topics "${config_rtps_send_alias_topics}") - STRING(REPLACE "\n" "" config_rtps_send_alias_topics "${config_rtps_send_alias_topics}") - STRING(REGEX REPLACE " alias.*" "" config_rtps_send_topics "${config_rtps_send_topics}") - endif() - STRING(REGEX REPLACE ", " ";" config_rtps_send_topics "${config_rtps_send_topics}") - STRING(REGEX REPLACE "\n" "" config_rtps_send_topics "${config_rtps_send_topics}") - - set(config_rtps_receive_topics) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/msg/tools/uorb_rtps_classifier.py -ra - OUTPUT_VARIABLE config_rtps_receive_topics - ) - set(config_rtps_receive_alias_topics "") - string(FIND ${config_rtps_receive_topics} "alias" found_receive_alias) - if (NOT ${found_receive_alias} EQUAL "-1") - STRING(REGEX REPLACE ".*alias " "" config_rtps_receive_alias_topics "${config_rtps_receive_topics}") - STRING(REPLACE ", " ";" config_rtps_receive_alias_topics "${config_rtps_receive_alias_topics}") - STRING(REPLACE "\n" "" config_rtps_receive_alias_topics "${config_rtps_receive_alias_topics}") - STRING(REGEX REPLACE " alias.*" "" config_rtps_receive_topics "${config_rtps_receive_topics}") - endif() - STRING(REPLACE ", " ";" config_rtps_receive_topics "${config_rtps_receive_topics}") - STRING(REPLACE "\n" "" config_rtps_receive_topics "${config_rtps_receive_topics}") -endif() - -if (FASTRTPSGEN AND (config_rtps_send_topics OR config_rtps_receive_topics)) - option(GENERATE_RTPS_BRIDGE "enable RTPS and microCDR" ON) -endif() - -if (GENERATE_RTPS_BRIDGE) - add_subdirectory(micrortps_client) - - ############################################################################### - # micro-cdr serialization - ############################################################################### - include(px4_git) - px4_add_git_submodule(TARGET git_micro_cdr PATH micro-CDR) - - set(UCDR_SUPERBUILD CACHE BOOL "Disable micro-CDR superbuild compilation.") - add_subdirectory(micro-CDR) - - set(msg_out_path_microcdr ${PX4_BINARY_DIR}/uORB_microcdr/topics) - set(msg_source_out_path_microcdr ${CMAKE_CURRENT_BINARY_DIR}/topics_microcdr_sources) - - set(uorb_headers_microcdr) - set(uorb_sources_microcdr) - - # send topic files - STRING(REGEX REPLACE ";" ", " send_list "${config_rtps_send_topics};${config_rtps_send_alias_topics}") - message(STATUS "microRTPS bridge:") - message(STATUS " Publish to the bridge from: ${send_list}") - set(send_topic_files) - foreach(topic ${config_rtps_send_topics}) - list(APPEND send_topic_files ${PX4_SOURCE_DIR}/msg/${topic}.msg) - list(APPEND uorb_headers_microcdr ${msg_out_path_microcdr}/${topic}.h) - list(APPEND uorb_sources_microcdr ${msg_source_out_path_microcdr}/${topic}.cpp) - endforeach() - - # receive topic files - STRING(REGEX REPLACE ";" ", " rcv_list "${config_rtps_receive_topics};${config_rtps_receive_alias_topics}") - message(STATUS " Subscribe from the bridge to: ${rcv_list}") - set(receive_topic_files) - foreach(topic ${config_rtps_receive_topics}) - list(APPEND receive_topic_files ${PX4_SOURCE_DIR}/msg/${topic}.msg) - list(APPEND uorb_headers_microcdr ${msg_out_path_microcdr}/${topic}.h) - list(APPEND uorb_sources_microcdr ${msg_source_out_path_microcdr}/${topic}.cpp) - endforeach() - - list(REMOVE_DUPLICATES uorb_headers_microcdr) - list(REMOVE_DUPLICATES uorb_sources_microcdr) - - # Generate uORB serialization headers - add_custom_command(OUTPUT ${uorb_headers_microcdr} - COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/msg/tools/px_generate_uorb_topic_files.py - --headers - -f ${send_topic_files} ${receive_topic_files} - -i ${PX4_SOURCE_DIR}/msg/ - -o ${msg_out_path_microcdr} - -e ${PX4_SOURCE_DIR}/msg/templates/uorb_microcdr - -t ${CMAKE_CURRENT_BINARY_DIR}/tmp/headers_microcdr - -q - DEPENDS - ${receive_topic_files} - ${send_topic_files} - ${PX4_SOURCE_DIR}/msg/tools/px_generate_uorb_topic_files.py - ${PX4_SOURCE_DIR}/msg/tools/urtps_bridge_topics.yaml - COMMENT "Generating uORB microcdr topic headers" - VERBATIM - ) - add_custom_target(uorb_headers_microcdr_gen DEPENDS ${uorb_headers_microcdr}) - - # Generate uORB serialization sources - add_custom_command(OUTPUT ${uorb_sources_microcdr} - COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/msg/tools/px_generate_uorb_topic_files.py - --sources - -f ${send_topic_files} ${receive_topic_files} - -i ${PX4_SOURCE_DIR}/msg/ - -o ${msg_source_out_path_microcdr} - -e ${PX4_SOURCE_DIR}/msg/templates/uorb_microcdr - -t ${CMAKE_CURRENT_BINARY_DIR}/tmp/sources_microcdr - -q - DEPENDS - ${receive_topic_files} - ${send_topic_files} - ${PX4_SOURCE_DIR}/msg/tools/px_generate_uorb_topic_files.py - ${PX4_SOURCE_DIR}/msg/tools/urtps_bridge_topics.yaml - COMMENT "Generating uORB microcdr topic sources" - VERBATIM - ) - px4_add_library(uorb_msgs_microcdr ${uorb_sources_microcdr} ${uorb_headers_microcdr}) - add_dependencies(uorb_msgs_microcdr - uorb_headers_microcdr_gen - git_micro_cdr - microcdr - ) - add_dependencies(microcdr prebuild_targets) - - # microCDR - target_include_directories(uorb_msgs_microcdr - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/micro-CDR/include - ${CMAKE_CURRENT_BINARY_DIR}/micro-CDR/include - ${CMAKE_CURRENT_BINARY_DIR}/micro-CDR/include/microcdr - ) - - target_link_libraries(uorb_msgs_microcdr PRIVATE microcdr) -endif() diff --git a/src/modules/micrortps_bridge/Kconfig b/src/modules/micrortps_bridge/Kconfig deleted file mode 100644 index 2262e81f33..0000000000 --- a/src/modules/micrortps_bridge/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -menuconfig MODULES_MICRORTPS_BRIDGE - bool "micrortps_bridge" - default n - ---help--- - Enable support for micrortps_bridge - -menuconfig USER_MICRORTPS_BRIDGE - bool "micrortps_bridge running as userspace module" - default y - depends on BOARD_PROTECTED && MODULES_MICRORTPS_BRIDGE - ---help--- - Put micrortps_bridge in userspace memory diff --git a/src/modules/micrortps_bridge/README.md b/src/modules/micrortps_bridge/README.md deleted file mode 100644 index 22e86f1d48..0000000000 --- a/src/modules/micrortps_bridge/README.md +++ /dev/null @@ -1 +0,0 @@ -For see a complete documentation, please follow this [link](https://dev.px4.io/master/en/middleware/micrortps.html) diff --git a/src/modules/micrortps_bridge/micro-CDR b/src/modules/micrortps_bridge/micro-CDR deleted file mode 160000 index 21d3cfe3ae..0000000000 --- a/src/modules/micrortps_bridge/micro-CDR +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 21d3cfe3ae570d1674da0105ab23b80958e0449a diff --git a/src/modules/micrortps_bridge/micrortps_client/CMakeLists.txt b/src/modules/micrortps_bridge/micrortps_client/CMakeLists.txt deleted file mode 100644 index bf5b36f18d..0000000000 --- a/src/modules/micrortps_bridge/micrortps_client/CMakeLists.txt +++ /dev/null @@ -1,118 +0,0 @@ -############################################################################ -# -# Copyright (c) 2015 PX4 Development Team. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# 3. Neither the name PX4 nor the names of its contributors may be -# used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -############################################################################ - -set(msg_out_path ${CMAKE_CURRENT_BINARY_DIR}) -get_filename_component(micrortps_bridge_path ${msg_out_path} PATH) - -option(BUILD_MICRORTPS_AGENT "enable building the micrortps_agent after its generation" OFF) - -if (NOT "${config_rtps_send_topics}" STREQUAL "" OR NOT "${config_rtps_receive_topics}" STREQUAL "") - set(send_topic_files) - foreach(topic ${config_rtps_send_topics}) - list(APPEND send_topic_files ${PX4_SOURCE_DIR}/msg/${topic}.msg) - endforeach() - - set(receive_topic_files) - foreach(topic ${config_rtps_receive_topics}) - list(APPEND receive_topic_files ${PX4_SOURCE_DIR}/msg/${topic}.msg) - endforeach() - - if (NOT "${config_rtps_send_alias_topics}" STREQUAL "") - set(config_rtps_send_topics "${config_rtps_send_topics};${config_rtps_send_alias_topics}") - endif() - - if (NOT "${config_rtps_receive_alias_topics}" STREQUAL "") - set(config_rtps_receive_topics "${config_rtps_receive_topics};${config_rtps_receive_alias_topics}") - endif() - - foreach(topic ${config_rtps_send_topics}) - list(APPEND topic_bridge_files_out ${micrortps_bridge_path}/micrortps_agent/src/${topic}_Publisher.cpp) - list(APPEND topic_bridge_files_out ${micrortps_bridge_path}/micrortps_agent/src/${topic}_Publisher.h) - endforeach() - - foreach(topic ${config_rtps_receive_topics}) - list(APPEND topic_bridge_files_out ${micrortps_bridge_path}/micrortps_agent/src/${topic}_Subscriber.cpp) - list(APPEND topic_bridge_files_out ${micrortps_bridge_path}/micrortps_agent/src/${topic}_Subscriber.h) - endforeach() - - list(APPEND topic_bridge_files_out - ${micrortps_bridge_path}/micrortps_client/microRTPS_client.cpp - ${micrortps_bridge_path}/micrortps_client/microRTPS_transport.cpp - ${micrortps_bridge_path}/micrortps_client/microRTPS_transport.h - ) - - add_custom_command(OUTPUT ${topic_bridge_files_out} - COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/msg/tools/generate_microRTPS_bridge.py - --fastrtpsgen-dir $ENV{FASTRTPSGEN_DIR} - --generate-idl - --mkdir-build - --generate-cmakelists - --topic-msg-dir ${PX4_SOURCE_DIR}/msg - --uorb-templates-dir templates/uorb_microcdr - --urtps-templates-dir templates/urtps - --agent-outdir ${micrortps_bridge_path}/micrortps_agent/src - --client-outdir ${micrortps_bridge_path}/micrortps_client - --idl-dir ${micrortps_bridge_path}/micrortps_agent/idl - >micrortps_bridge.log 2>&1 || cat micrortps_bridge.log - DEPENDS ${send_topic_files} ${receive_topic_files} ${PX4_SOURCE_DIR}/msg/tools/urtps_bridge_topics.yaml - COMMENT "Generating RTPS topic bridge" - ) - add_custom_target(topic_bridge_files DEPENDS ${topic_bridge_files_out}) - - px4_add_module( - MODULE modules__micrortps_bridge__micrortps_client - MAIN micrortps_client - STACK_MAIN 4096 - INCLUDES - ${CMAKE_CURRENT_SOURCE_DIR} - ${micrortps_bridge_path}/micrortps_client - SRCS - microRTPS_client_main.cpp - ${micrortps_bridge_path}/micrortps_client/microRTPS_client.cpp - ${micrortps_bridge_path}/micrortps_client/microRTPS_transport.cpp - MODULE_CONFIG - module.yaml - DEPENDS - topic_bridge_files - ) - target_link_libraries(modules__micrortps_bridge__micrortps_client PRIVATE uorb_msgs_microcdr) - - if (BUILD_MICRORTPS_AGENT) - add_custom_command(TARGET modules__micrortps_bridge__micrortps_client POST_BUILD - COMMAND ${PX4_SOURCE_DIR}/Tools/build_micrortps_agent.sh - WORKING_DIRECTORY ${PX4_SOURCE_DIR} - COMMENT "Building micrortps_agent..." - ) - # add_subdirectory(${micrortps_bridge_path}/micrortps_agent) - endif() -endif() diff --git a/src/modules/micrortps_bridge/micrortps_client/microRTPS_client.h b/src/modules/micrortps_bridge/micrortps_client/microRTPS_client.h deleted file mode 100644 index 029bb2331d..0000000000 --- a/src/modules/micrortps_bridge/micrortps_client/microRTPS_client.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2019-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define LOOPS -1 -#define SLEEP_US 1000 -#define MAX_SLEEP_US 1000000 -#define BAUDRATE 460800 -#define MAX_DATA_RATE 10000000 -#define DEVICE "/dev/ttyACM0" -#define POLL_MS 1 -#define MAX_POLL_MS 1000 -#define DEFAULT_IP "127.0.0.1" -#define DEFAULT_RECV_PORT 2019 -#define DEFAULT_SEND_PORT 2020 -#define MIN_TX_INTERVAL_US 1000.f -#define MAX_TX_INTERVAL_US 1000000.f - - -void *send(void *args); -void micrortps_start_topics(const uint32_t &datarate, struct timespec &begin, uint64_t &total_rcvd, - uint64_t &total_sent, uint64_t &sent_last_sec, - uint64_t &rcvd_last_sec, uint64_t &received, uint64_t &sent, int &rcvd_loop, int &sent_loop); - -struct baudtype { - speed_t code; - uint32_t val; -}; - -struct options { - enum class eTransports { - UART, - UDP - }; - eTransports transport = options::eTransports::UART; - char device[64] = DEVICE; - char ip[16] = DEFAULT_IP; - uint16_t recv_port = DEFAULT_RECV_PORT; - uint16_t send_port = DEFAULT_SEND_PORT; - uint32_t sleep_us = SLEEP_US; - uint32_t baudrate = BAUDRATE; - uint32_t datarate = 0; - uint32_t poll_ms = POLL_MS; - int loops = LOOPS; - bool sw_flow_control = false; - bool hw_flow_control = false; - bool verbose_debug = false; -}; - -extern struct options _options; -extern bool _should_exit_task; -extern Transport_node *transport_node; diff --git a/src/modules/micrortps_bridge/micrortps_client/microRTPS_client_main.cpp b/src/modules/micrortps_bridge/micrortps_client/microRTPS_client_main.cpp deleted file mode 100644 index 1ff41c4682..0000000000 --- a/src/modules/micrortps_bridge/micrortps_client/microRTPS_client_main.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/**************************************************************************** - * - * Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima). - * Copyright (c) 2019-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -extern "C" __EXPORT int micrortps_client_main(int argc, char *argv[]); - -static int _rtps_task = -1; -bool _should_exit_task = false; -Transport_node *transport_node = nullptr; -struct options _options; - -struct timespec begin; -struct timespec end; - -uint64_t total_rcvd{0}; -uint64_t total_sent{0}; -uint64_t rcvd_last_sec{0}; -uint64_t sent_last_sec{0}; -uint64_t received{0}; -uint64_t sent{0}; -int rcv_loop{0}; -int send_loop{0}; - -static void usage(const char *name) -{ - PRINT_MODULE_USAGE_NAME("micrortps_client", "communication"); - PRINT_MODULE_USAGE_COMMAND("start"); - - PRINT_MODULE_USAGE_PARAM_STRING('t', "UART", "UART|UDP", "Transport protocol", true); - PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyACM0", "", "Select Serial Device", true); - PRINT_MODULE_USAGE_PARAM_INT('b', 460800, 9600, 3000000, "Baudrate (can also be p:)", true); - PRINT_MODULE_USAGE_PARAM_INT('m', 0, 0, MAX_DATA_RATE, "Maximum sending data rate in B/s (0=not limited)", true); - PRINT_MODULE_USAGE_PARAM_INT('p', 1, 1, MAX_POLL_MS, "Poll timeout for UART in milliseconds", true); - PRINT_MODULE_USAGE_PARAM_INT('l', -1, -1, INT32_MAX, "Limit number of iterations until the program exits (-1=infinite)", - true); - PRINT_MODULE_USAGE_PARAM_INT('w', 1000, 0, MAX_SLEEP_US, - "Iteration time for data publishing to the uORB side, in microseconds", true); - PRINT_MODULE_USAGE_PARAM_INT('r', 2019, 0, 65536, "Select UDP Network Port for receiving (local)", true); - PRINT_MODULE_USAGE_PARAM_INT('s', 2020, 0, 65536, "Select UDP Network Port for sending (remote)", true); - PRINT_MODULE_USAGE_PARAM_STRING('i', "127.0.0.1", "", "Select IP address (remote)", true); - PRINT_MODULE_USAGE_PARAM_FLAG('f', "Activate UART link SW flow control", true); - PRINT_MODULE_USAGE_PARAM_FLAG('h', "Activate UART link HW flow control", true); - PRINT_MODULE_USAGE_PARAM_FLAG('v', "Add more verbosity", true); - - PRINT_MODULE_USAGE_COMMAND("stop"); - PRINT_MODULE_USAGE_COMMAND("status"); -} - -static int parse_options(int argc, char *argv[]) -{ - int ch; - int myoptind = 1; - const char *myoptarg = nullptr; - - while ((ch = px4_getopt(argc, argv, "t:d:l:w:b:m:p:r:s:i:fhv", &myoptind, &myoptarg)) != EOF) { - switch (ch) { - case 't': _options.transport = strcmp(myoptarg, "UDP") == 0 ? - options::eTransports::UDP - : options::eTransports::UART; break; - - case 'd': if (nullptr != myoptarg) strcpy(_options.device, myoptarg); break; - - case 'l': _options.loops = strtol(myoptarg, nullptr, 10); break; - - case 'w': _options.sleep_us = strtoul(myoptarg, nullptr, 10); break; - - case 'b': { - int baudrate = 0; - - if (px4_get_parameter_value(myoptarg, baudrate) != 0) { - PX4_ERR("baudrate parsing failed"); - } - - _options.baudrate = baudrate; - - break; - } - - case 'm': { - int datarate = 0; - - if (px4_get_parameter_value(myoptarg, datarate) != 0) { - PX4_ERR("datarate parsing failed"); - } - - _options.datarate = datarate; - - break; - } - - case 'p': _options.poll_ms = strtoul(myoptarg, nullptr, 10); break; - - case 'r': _options.recv_port = strtoul(myoptarg, nullptr, 10); break; - - case 's': _options.send_port = strtoul(myoptarg, nullptr, 10); break; - - case 'i': if (nullptr != myoptarg) strcpy(_options.ip, myoptarg); break; - - case 'f': _options.sw_flow_control = true; break; - - case 'h': _options.hw_flow_control = true; break; - - case 'v': _options.verbose_debug = true; break; - - default: - usage(argv[1]); - return -1; - } - } - - if (_options.datarate > MAX_DATA_RATE) { - _options.datarate = MAX_DATA_RATE; - PX4_WARN("Data rate too high. Using max datarate of %d B/s instead", MAX_DATA_RATE); - } - - if (_options.poll_ms < POLL_MS) { - _options.poll_ms = POLL_MS; - PX4_WARN("Poll timeout too low. Using %d ms instead", POLL_MS); - - } else if (_options.poll_ms > MAX_POLL_MS) { - _options.poll_ms = MAX_POLL_MS; - PX4_WARN("Poll timeout too high. Using %d ms instead", MAX_POLL_MS); - } - - if (_options.sleep_us > MAX_SLEEP_US) { - _options.sleep_us = MAX_SLEEP_US; - PX4_WARN("Publishing iteration cycle too slow. Using %d us instead", MAX_SLEEP_US); - } - - if (_options.hw_flow_control && _options.sw_flow_control) { - PX4_ERR("HW and SW flow control set. Please set only one or another"); - return -1; - } - - return 0; -} - -static int micrortps_start(int argc, char *argv[]) -{ - if (0 > parse_options(argc, argv)) { - PX4_INFO("EXITING..."); - _rtps_task = -1; - return -1; - } - - // Set the system ID to FMU, in order to identify the client side - const uint8_t sys_id = static_cast(MicroRtps::System::FMU); - - switch (_options.transport) { - case options::eTransports::UART: { - transport_node = new UART_node(_options.device, _options.baudrate, _options.poll_ms, - _options.sw_flow_control, _options.hw_flow_control, sys_id, - _options.verbose_debug); - PX4_INFO("UART transport: device: %s; baudrate: %" PRIu32 "; poll: %" PRIu32 "ms; flow_control: %s", - _options.device, _options.baudrate, _options.poll_ms, - _options.sw_flow_control ? "SW enabled" : (_options.hw_flow_control ? "HW enabled" : "No")); - } - break; - - case options::eTransports::UDP: { - transport_node = new UDP_node(_options.ip, _options.recv_port, _options.send_port, - sys_id, _options.verbose_debug); - PX4_INFO("UDP transport: ip address: %s; recv port: %" PRIu16 "; send port: %" PRIu16, - _options.ip, _options.recv_port, _options.send_port); - - } - break; - - default: - _rtps_task = -1; - PX4_INFO("EXITING..."); - return -1; - } - - if (0 > transport_node->init()) { - PX4_INFO("EXITING..."); - _rtps_task = -1; - return -1; - } - - micrortps_start_topics(_options.datarate, begin, total_rcvd, total_sent, sent_last_sec, rcvd_last_sec, received, sent, - rcv_loop, - send_loop); - - px4_clock_gettime(CLOCK_REALTIME, &end); - - const double elapsed_secs = static_cast(end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9); - - PX4_INFO("RECEIVED: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - avg %.02fKB/s", - received, rcv_loop, total_rcvd, elapsed_secs, static_cast(total_rcvd / (1e3 * elapsed_secs))); - PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - avg %.02fKB/s", - sent, send_loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs)); - - delete transport_node; - - transport_node = nullptr; - - PX4_INFO("Stopped!"); - - fflush(stdout); - - _rtps_task = -1; - - return 0; -} - -int micrortps_client_main(int argc, char *argv[]) -{ - if (argc < 2) { - usage(argv[0]); - return -1; - } - - if (!strcmp(argv[1], "start")) { - if (_rtps_task != -1) { - PX4_INFO("Already running"); - return -1; - } - - _rtps_task = px4_task_spawn_cmd("micrortps_client", - SCHED_DEFAULT, - SCHED_PRIORITY_DEFAULT, - PX4_STACK_ADJUSTED(2900), - (px4_main_t) micrortps_start, - (char *const *)argv); - - if (_rtps_task < 0) { - PX4_WARN("Could not start task"); - _rtps_task = -1; - return -1; - } - - return 0; - } - - if (!strcmp(argv[1], "status")) { - if (_rtps_task == -1) { - PX4_INFO("Not running"); - - } else { - px4_clock_gettime(CLOCK_REALTIME, &end); - - const double elapsed_secs = static_cast(end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9); - - printf("\tup and running for %.03f seconds\n", elapsed_secs); - printf("\tnr. of messages received: %" PRIu64 "\n", received); - printf("\tnr. of messages sent: %" PRIu64 "\n", sent); - printf("\ttotal data read: %" PRIu64 " bytes\n", total_rcvd); - printf("\ttotal data sent: %" PRIu64 " bytes\n", total_sent); - printf("\trates:\n"); - printf("\t rx: %.3f kB/s\n", rcvd_last_sec / 1e3); - printf("\t tx: %.3f kB/s\n", sent_last_sec / 1e3); - printf("\t avg rx: %.3f kB/s\n", total_rcvd / (1e3 * elapsed_secs)); - printf("\t avg tx: %.3f kB/s\n", total_sent / (1e3 * elapsed_secs)); - printf("\t tx rate max:"); - - if (_options.datarate != 0) { - printf(" %.1f kB/s\n", _options.datarate / 1e3); - - } else { - printf(" Unlimited\n"); - } - } - - return 0; - } - - if (!strcmp(argv[1], "stop")) { - if (_rtps_task == -1) { - PX4_INFO("Not running"); - return -1; - } - - _should_exit_task = true; - - if (nullptr != transport_node) { transport_node->close(); } - - _rtps_task = -1; - return 0; - } - - usage(argv[0]); - return -1; -} diff --git a/src/modules/micrortps_bridge/micrortps_client/module.yaml b/src/modules/micrortps_bridge/micrortps_client/module.yaml deleted file mode 100644 index 10049eac32..0000000000 --- a/src/modules/micrortps_bridge/micrortps_client/module.yaml +++ /dev/null @@ -1,38 +0,0 @@ -module_name: RTPS -serial_config: - - command: | - protocol_splitter start ${SERIAL_DEV} - mavlink start -d /dev/mavlink -b p:${BAUD_PARAM} -m onboard -r 5000 -x - micrortps_client start -d /dev/rtps -b p:${BAUD_PARAM} -m p:RTPS_RATE -l -1 - port_config_param: - name: RTPS_MAV_CONFIG - group: RTPS - label: MAVLink + FastRTPS - - command: | - micrortps_client start -d ${SERIAL_DEV} -b p:${BAUD_PARAM} -m p:RTPS_RATE -l -1 - port_config_param: - name: RTPS_CONFIG - group: RTPS - label: FastRTPS - -parameters: - - group: RTPS - definitions: - RTPS_RATE: - description: - short: Maximum RTPS data rate - long: | - Configure the maximum sending rate for the RTPS streams in Bytes/sec. - If the configured streams exceed the maximum rate, the sending rate of - each stream is automatically decreased. - - 0 is unlimited. Note this can cause reliability issues - if enough RTPS topics are selected that exceed the - serial bus limit. - - type: int32 - min: 0 - unit: B/s - reboot_required: true - default: 0 - diff --git a/src/modules/micrortps_bridge/res/basic_example_flow.png b/src/modules/micrortps_bridge/res/basic_example_flow.png deleted file mode 100644 index a48972b7addbc2894d75590df54080d53277ea68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182298 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

l;x}2&$ku3E|0`89 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

S#rj7F4eLEb<6Hb#Jfu33TO0 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