Files
PX4-Autopilot/src/modules/mavlink/mavlink_main.h
T

762 lines
22 KiB
C++

/****************************************************************************
*
* Copyright (c) 2012-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 mavlink_main.h
*
* MAVLink 2.0 protocol interface definition.
*
* @author Lorenz Meier <lorenz@px4.io>
* @author Anton Babushkin <anton.babushkin@me.com>
*/
#pragma once
#include <pthread.h>
#include <stdbool.h>
#ifdef __PX4_NUTTX
#include <nuttx/fs/fs.h>
#else
#include <arpa/inet.h>
#include <drivers/device/device.h>
#include <sys/socket.h>
#endif
#if defined(CONFIG_NET) || !defined(__PX4_NUTTX)
#include <net/if.h>
#include <netinet/in.h>
#endif
#include <containers/List.hpp>
#include <parameters/param.h>
#include <perf/perf_counter.h>
#include <px4_platform_common/cli.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/defines.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/posix.h>
#include <uORB/Publication.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/radio_status.h>
#include <uORB/topics/telemetry_status.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_status.h>
#include "mavlink_command_sender.h"
#include "mavlink_events.h"
#include "mavlink_messages.h"
#include "mavlink_receiver.h"
#include "mavlink_shell.h"
#include "mavlink_ulog.h"
#define DEFAULT_BAUD_RATE 57600
#define DEFAULT_DEVICE_NAME "/dev/ttyS1"
#define HASH_PARAM "_HASH_CHECK"
#if defined(CONFIG_NET) || defined(__PX4_POSIX)
# define MAVLINK_UDP
# define DEFAULT_REMOTE_PORT_UDP 14550 ///< GCS port per MAVLink spec
#endif // CONFIG_NET || __PX4_POSIX
enum class Protocol {
SERIAL = 0,
#if defined(MAVLINK_UDP)
UDP,
#endif // MAVLINK_UDP
};
using namespace time_literals;
class Mavlink final : public ModuleParams
{
public:
/**
* Constructor
*/
Mavlink();
/**
* Destructor, also kills the mavlinks task.
*/
~Mavlink();
/**
* Start the mavlink task.
*
* @return OK on success.
*/
static int start(int argc, char *argv[]);
bool running() const { return _task_running.load(); }
bool should_exit() const { return _task_should_exit.load(); }
void request_stop()
{
_task_should_exit.store(true);
_receiver.request_stop();
}
/**
* Display the mavlink status.
*/
void display_status();
/**
* Display the status of all enabled streams.
*/
void display_status_streams();
static int stop_command(int argc, char *argv[]);
static int stream_command(int argc, char *argv[]);
static int instance_count();
static Mavlink *new_instance();
static Mavlink *get_instance_for_device(const char *device_name);
mavlink_message_t *get_buffer() { return &_mavlink_buffer; }
mavlink_status_t *get_status() { return &_mavlink_status; }
/**
* Set the MAVLink version
*
* Currently supporting v1 and v2
*
* @param version MAVLink version
*/
void set_proto_version(unsigned version);
static int destroy_all_instances();
static int get_status_all_instances(bool show_streams_status);
static bool serial_instance_exists(const char *device_name, Mavlink *self);
static bool component_was_seen(int system_id, int component_id, Mavlink *self = nullptr);
static void forward_message(const mavlink_message_t *msg, Mavlink *self);
bool check_events() const { return _should_check_events.load(); }
void check_events_enable() { _should_check_events.store(true); }
void check_events_disable() { _should_check_events.store(false); }
int get_uart_fd() const { return _uart_fd; }
/**
* Get the MAVLink system id.
*
* @return The system ID of this vehicle
*/
int get_system_id() const { return mavlink_system.sysid; }
/**
* Get the MAVLink component id.
*
* @return The component ID of this vehicle
*/
int get_component_id() const { return mavlink_system.compid; }
const char *_device_name{DEFAULT_DEVICE_NAME};
enum MAVLINK_MODE {
MAVLINK_MODE_NORMAL = 0,
MAVLINK_MODE_CUSTOM,
MAVLINK_MODE_ONBOARD,
MAVLINK_MODE_OSD,
MAVLINK_MODE_MAGIC,
MAVLINK_MODE_CONFIG,
MAVLINK_MODE_IRIDIUM,
MAVLINK_MODE_MINIMAL,
MAVLINK_MODE_EXTVISION,
MAVLINK_MODE_EXTVISIONMIN,
MAVLINK_MODE_GIMBAL,
MAVLINK_MODE_ONBOARD_LOW_BANDWIDTH,
MAVLINK_MODE_COUNT
};
enum BROADCAST_MODE {
BROADCAST_MODE_OFF = 0,
BROADCAST_MODE_ON,
BROADCAST_MODE_MULTICAST
};
enum FLOW_CONTROL_MODE {
FLOW_CONTROL_OFF = 0,
FLOW_CONTROL_AUTO,
FLOW_CONTROL_ON
};
static const char *mavlink_mode_str(enum MAVLINK_MODE mode)
{
switch (mode) {
case MAVLINK_MODE_NORMAL:
return "Normal";
case MAVLINK_MODE_CUSTOM:
return "Custom";
case MAVLINK_MODE_ONBOARD:
return "Onboard";
case MAVLINK_MODE_OSD:
return "OSD";
case MAVLINK_MODE_MAGIC:
return "Magic";
case MAVLINK_MODE_CONFIG:
return "Config";
case MAVLINK_MODE_IRIDIUM:
return "Iridium";
case MAVLINK_MODE_MINIMAL:
return "Minimal";
case MAVLINK_MODE_EXTVISION:
return "ExtVision";
case MAVLINK_MODE_EXTVISIONMIN:
return "ExtVisionMin";
case MAVLINK_MODE_GIMBAL:
return "Gimbal";
case MAVLINK_MODE_ONBOARD_LOW_BANDWIDTH:
return "OnboardLowBandwidth";
default:
return "Unknown";
}
}
enum MAVLINK_MODE get_mode() { return _mode; }
bool get_hil_enabled() { return _hil_enabled; }
bool get_use_hil_gps() { return _param_mav_usehilgps.get(); }
bool get_forward_externalsp() { return _param_mav_fwdextsp.get(); }
bool get_flow_control_enabled() { return _flow_control_mode; }
bool get_forwarding_on() { return _forwarding_on; }
bool is_connected() { return _tstatus.heartbeat_type_gcs; }
#if defined(MAVLINK_UDP)
static Mavlink *get_instance_for_network_port(unsigned long port);
bool broadcast_enabled() { return _mav_broadcast == BROADCAST_MODE_ON; }
bool multicast_enabled() { return _mav_broadcast == BROADCAST_MODE_MULTICAST; }
#endif // MAVLINK_UDP
/**
* Set the boot complete flag on all instances
*
* Setting the flag unblocks parameter transmissions, which are gated
* beforehand to ensure that the system is fully initialized.
*/
static void set_boot_complete();
/**
* Get the free space in the transmit buffer
*
* @return free space in the UART TX buffer
*/
unsigned get_free_tx_buf();
static int start_helper(int argc, char *argv[]);
/**
* Enable / disable Hardware in the Loop simulation mode.
*
* @param hil_enabled The new HIL enable/disable state.
* @return OK if the HIL state changed, ERROR if the
* requested change could not be made or was
* redundant.
*/
int set_hil_enabled(bool hil_enabled);
/**
* Set communication protocol for this mavlink instance
*/
void set_protocol(Protocol p) { _protocol = p; }
/**
* This is the beginning of a MAVLINK_START_UART_SEND/MAVLINK_END_UART_SEND transaction
*/
void send_start(int length);
/**
* Buffer bytes to send out on the link.
*/
void send_bytes(const uint8_t *buf, unsigned packet_len);
/**
* Flush the transmit buffer and send one MAVLink packet
*/
void send_finish();
/**
* Resend message as is, don't change sequence number and CRC.
*/
void resend_message(mavlink_message_t *msg) { _mavlink_resend_uart(_channel, msg); }
void handle_message(const mavlink_message_t *msg);
int get_instance_id() const { return _instance_id; }
/**
* Enable / disable hardware flow control.
*
* @param enabled True if hardware flow control should be enabled
*/
int setup_flow_control(enum FLOW_CONTROL_MODE enabled);
mavlink_channel_t get_channel() const { return _channel; }
void configure_stream_threadsafe(const char *stream_name, float rate = -1.0f);
orb_advert_t *get_mavlink_log_pub() { return &_mavlink_log_pub; }
/**
* Send a status text with loglevel INFO
*
* @param string the message to send (will be capped by mavlink max string length)
*/
void send_statustext_info(const char *string);
/**
* Send a status text with loglevel CRITICAL
*
* @param string the message to send (will be capped by mavlink max string length)
*/
void send_statustext_critical(const char *string);
/**
* Send a status text with loglevel EMERGENCY
*
* @param string the message to send (will be capped by mavlink max string length)
*/
void send_statustext_emergency(const char *string);
/**
* Send a status text with loglevel, the difference from mavlink_log_xxx() is that message sent
* only on this mavlink connection. Useful for reporting communication specific, not system-wide info
* only to client interested in it. Message will be not sent immediately but queued in buffer as
* for mavlink_log_xxx().
*
* @param string the message to send (will be capped by mavlink max string length)
* @param severity the log level
*/
void send_statustext(unsigned char severity, const char *string);
/**
* Send the capabilities of this autopilot in terms of the MAVLink spec
*/
bool send_autopilot_capabilities();
/**
* Send the protocol version of MAVLink
*/
void send_protocol_version();
List<MavlinkStream *> &get_streams() { return _streams; }
float get_rate_mult() const { return _rate_mult; }
float get_baudrate() { return _baudrate; }
/* Functions for waiting to start transmission until message received. */
void set_has_received_messages(bool received_messages) { _received_messages = received_messages; }
bool get_has_received_messages() { return _received_messages; }
void set_wait_to_transmit(bool wait) { _wait_to_transmit = wait; }
bool get_wait_to_transmit() { return _wait_to_transmit; }
bool should_transmit() { return (_transmitting_enabled && (!_wait_to_transmit || (_wait_to_transmit && _received_messages))); }
bool message_buffer_write(const void *ptr, int size);
void lockMessageBufferMutex(void) { pthread_mutex_lock(&_message_buffer_mutex); }
void unlockMessageBufferMutex(void) { pthread_mutex_unlock(&_message_buffer_mutex); }
/**
* Count transmitted bytes
*/
void count_txbytes(unsigned n) { _bytes_tx += n; };
/**
* Count bytes not transmitted because of errors
*/
void count_txerrbytes(unsigned n) { _bytes_txerr += n; };
/**
* Count received bytes
*/
void count_rxbytes(unsigned n) { _bytes_rx += n; };
/**
* Get the receive status of this MAVLink link
*/
telemetry_status_s &telemetry_status() { return _tstatus; }
void telemetry_status_updated() { _tstatus_updated = true; }
void set_telemetry_status_type(uint8_t type) { _tstatus.type = type; }
void update_radio_status(const radio_status_s &radio_status);
unsigned get_system_type() { return _param_mav_type.get(); }
Protocol get_protocol() const { return _protocol; }
int get_socket_fd() { return _socket_fd; };
#if defined(MAVLINK_UDP)
unsigned short get_network_port() { return _network_port; }
unsigned short get_remote_port() { return _remote_port; }
const in_addr query_netmask_addr(const int socket_fd, const ifreq &ifreq);
const in_addr compute_broadcast_addr(const in_addr &host_addr, const in_addr &netmask_addr);
struct sockaddr_in &get_client_source_address() { return _src_addr; }
void set_client_source_initialized() { _src_addr_initialized = true; }
bool get_client_source_initialized() { return _src_addr_initialized; }
#endif
uint64_t get_start_time() { return _mavlink_start_time; }
static bool boot_complete() { return _boot_complete; }
bool is_usb_uart() { return _is_usb_uart; }
int get_data_rate() { return _datarate; }
void set_data_rate(int rate) { if (rate > 0) { _datarate = rate; } }
unsigned get_main_loop_delay() const { return _main_loop_delay; }
/** get the Mavlink shell. Create a new one if there isn't one. It is *always* created via MavlinkReceiver thread.
* Returns nullptr if shell cannot be created */
MavlinkShell *get_shell();
/** close the Mavlink shell if it is open */
void close_shell();
/** get ulog streaming if active, nullptr otherwise */
MavlinkULog *get_ulog_streaming() { return _mavlink_ulog; }
void try_start_ulog_streaming(uint8_t target_system, uint8_t target_component)
{
if (_mavlink_ulog) { return; }
_mavlink_ulog = MavlinkULog::try_start(_datarate, 0.7f, target_system, target_component);
}
const events::SendProtocol &get_events_protocol() const { return _events; };
bool ftp_enabled() const { return _ftp_on; }
bool hash_check_enabled() const { return _param_mav_hash_chk_en.get(); }
bool forward_heartbeats_enabled() const { return _param_mav_hb_forw_en.get(); }
bool odometry_loopback_enabled() const { return _param_mav_odom_lp.get(); }
bool failure_injection_enabled() const { return _param_sys_failure_injection_enabled.get(); }
struct ping_statistics_s {
uint64_t last_ping_time;
uint32_t last_ping_seq;
uint32_t dropped_packets;
float last_rtt;
float mean_rtt;
float max_rtt;
float min_rtt;
};
/**
* Get the ping statistics of this MAVLink link
*/
struct ping_statistics_s &get_ping_statistics() { return _ping_stats; }
static hrt_abstime &get_first_start_time() { return _first_start_time; }
bool radio_status_critical() const { return _radio_status_critical; }
private:
MavlinkReceiver _receiver;
int _instance_id{-1};
int _task_id{-1};
px4::atomic_bool _task_should_exit{false};
px4::atomic_bool _task_running{false};
bool _transmitting_enabled{true};
bool _transmitting_enabled_commanded{false};
bool _first_heartbeat_sent{false};
orb_advert_t _mavlink_log_pub{nullptr};
uORB::Publication<vehicle_command_ack_s> _vehicle_command_ack_pub{ORB_ID(vehicle_command_ack)};
uORB::PublicationMulti<telemetry_status_s> _telemetry_status_pub{ORB_ID(telemetry_status)};
uORB::Subscription _event_sub{ORB_ID(event)};
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Subscription _vehicle_command_ack_sub{ORB_ID(vehicle_command_ack)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
static bool _boot_complete;
static constexpr int MAVLINK_MIN_INTERVAL{1500};
static constexpr int MAVLINK_MAX_INTERVAL{10000};
static constexpr float MAVLINK_MIN_MULTIPLIER{0.0005f};
mavlink_message_t _mavlink_buffer {};
mavlink_status_t _mavlink_status {};
/* states */
bool _hil_enabled{false}; /**< Hardware In the Loop mode */
bool _is_usb_uart{false}; /**< Port is USB */
bool _wait_to_transmit{false}; /**< Wait to transmit until received messages. */
bool _received_messages{false}; /**< Whether we've received valid mavlink messages. */
px4::atomic_bool _should_check_events{false}; /**< Events subscription: only one MAVLink instance should check */
unsigned _main_loop_delay{1000}; /**< mainloop delay, depends on data rate */
List<MavlinkStream *> _streams;
MavlinkShell *_mavlink_shell{nullptr};
MavlinkULog *_mavlink_ulog{nullptr};
static events::EventBuffer *_event_buffer;
events::SendProtocol _events{*_event_buffer, *this};
MAVLINK_MODE _mode{MAVLINK_MODE_NORMAL};
mavlink_channel_t _channel{MAVLINK_COMM_0};
bool _forwarding_on{false};
bool _ftp_on{false};
bool _use_software_mav_throttling{false};
int _uart_fd{-1};
int _baudrate{57600};
int _datarate{1000}; ///< data rate for normal streams (attitude, position, etc.)
float _rate_mult{1.0f};
bool _radio_status_available{false};
bool _radio_status_critical{false};
float _radio_status_mult{1.0f};
/**
* If the queue index is not at 0, the queue sending
* logic will send parameters from the current index
* to len - 1, the end of the param list.
*/
unsigned int _mavlink_param_queue_index{0};
bool _mavlink_link_termination_allowed{false};
char *_subscribe_to_stream{nullptr};
float _subscribe_to_stream_rate{0.0f}; ///< rate of stream to subscribe to (0=disable, -1=unlimited, -2=default)
bool _udp_initialised{false};
FLOW_CONTROL_MODE _flow_control_mode{Mavlink::FLOW_CONTROL_OFF};
uint64_t _last_write_success_time{0};
uint64_t _last_write_try_time{0};
uint64_t _mavlink_start_time{0};
int32_t _protocol_version_switch{-1};
int32_t _protocol_version{0};
unsigned _bytes_tx{0};
unsigned _bytes_txerr{0};
unsigned _bytes_rx{0};
hrt_abstime _bytes_timestamp{0};
#if defined(MAVLINK_UDP)
BROADCAST_MODE _mav_broadcast {BROADCAST_MODE_OFF};
sockaddr_in _myaddr {};
sockaddr_in _src_addr {};
sockaddr_in _bcast_addr {};
bool _src_addr_initialized{false};
bool _broadcast_address_found{false};
bool _broadcast_address_not_found_warned{false};
bool _broadcast_failed_warned{false};
unsigned short _network_port{14556};
unsigned short _remote_port{DEFAULT_REMOTE_PORT_UDP};
#endif // MAVLINK_UDP
uint8_t _buf[MAVLINK_MAX_PACKET_LEN] {};
unsigned _buf_fill{0};
bool _tx_buffer_low{false};
const char *_interface_name{nullptr};
int _socket_fd{-1};
Protocol _protocol{Protocol::SERIAL};
radio_status_s _rstatus {};
telemetry_status_s _tstatus {};
bool _tstatus_updated{false};
ping_statistics_s _ping_stats {};
struct mavlink_message_buffer {
int write_ptr;
int read_ptr;
int size;
char *data;
};
mavlink_message_buffer _message_buffer {};
pthread_mutex_t _message_buffer_mutex {};
pthread_mutex_t _send_mutex {};
pthread_mutex_t _radio_status_mutex {};
DEFINE_PARAMETERS(
(ParamInt<px4::params::MAV_SYS_ID>) _param_mav_sys_id,
(ParamInt<px4::params::MAV_COMP_ID>) _param_mav_comp_id,
(ParamInt<px4::params::MAV_PROTO_VER>) _param_mav_proto_ver,
(ParamInt<px4::params::MAV_SIK_RADIO_ID>) _param_sik_radio_id,
(ParamInt<px4::params::MAV_TYPE>) _param_mav_type,
(ParamBool<px4::params::MAV_USEHILGPS>) _param_mav_usehilgps,
(ParamBool<px4::params::MAV_FWDEXTSP>) _param_mav_fwdextsp,
(ParamBool<px4::params::MAV_HASH_CHK_EN>) _param_mav_hash_chk_en,
(ParamBool<px4::params::MAV_HB_FORW_EN>) _param_mav_hb_forw_en,
(ParamBool<px4::params::MAV_ODOM_LP>) _param_mav_odom_lp,
(ParamInt<px4::params::MAV_RADIO_TOUT>) _param_mav_radio_timeout,
(ParamInt<px4::params::SYS_HITL>) _param_sys_hitl,
(ParamBool<px4::params::SYS_FAILURE_EN>) _param_sys_failure_injection_enabled
)
perf_counter_t _loop_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": tx run elapsed")}; /**< loop performance counter */
perf_counter_t _loop_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": tx run interval")}; /**< loop interval performance counter */
perf_counter_t _send_byte_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": send_bytes error")}; /**< send bytes error count */
void mavlink_update_parameters();
int mavlink_open_uart(const int baudrate = DEFAULT_BAUD_RATE,
const char *uart_name = DEFAULT_DEVICE_NAME,
const FLOW_CONTROL_MODE flow_control = FLOW_CONTROL_AUTO);
static constexpr unsigned RADIO_BUFFER_CRITICAL_LOW_PERCENTAGE = 25;
static constexpr unsigned RADIO_BUFFER_LOW_PERCENTAGE = 35;
static constexpr unsigned RADIO_BUFFER_HALF_PERCENTAGE = 50;
static hrt_abstime _first_start_time;
/**
* Configure a single stream.
* @param stream_name
* @param rate streaming rate in Hz, -1 = unlimited rate
* @return 0 on success, <0 on error
*/
int configure_stream(const char *stream_name, const float rate = -1.0f);
/**
* Configure default streams according to _mode for either all streams or only a single
* stream.
* @param configure_single_stream: if nullptr, configure all streams, else only a single stream
* @return 0 on success, <0 on error
*/
int configure_streams_to_default(const char *configure_single_stream = nullptr);
int message_buffer_init(int size);
void message_buffer_destroy();
int message_buffer_count();
int message_buffer_is_empty() const { return (_message_buffer.read_ptr == _message_buffer.write_ptr); }
int message_buffer_get_ptr(void **ptr, bool *is_part);
void message_buffer_mark_read(int n) { _message_buffer.read_ptr = (_message_buffer.read_ptr + n) % _message_buffer.size; }
void pass_message(const mavlink_message_t *msg);
void publish_telemetry_status();
void check_requested_subscriptions();
/**
* Reconfigure a SiK radio if requested by MAV_SIK_RADIO_ID
*
* This convenience function allows to re-configure a connected
* SiK radio without removing it from the main system harness.
*/
void configure_sik_radio();
/**
* Update rate mult so total bitrate will be equal to _datarate.
*/
void update_rate_mult();
#if defined(MAVLINK_UDP)
void find_broadcast_address();
void init_udp();
#endif // MAVLINK_UDP
void set_channel();
bool set_instance_id();
/**
* Main mavlink task.
*/
int task_main(int argc, char *argv[]);
// Disallow copy construction and move assignment.
Mavlink(const Mavlink &) = delete;
Mavlink operator=(const Mavlink &) = delete;
};