mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-05-24 05:45:59 +08:00
added possibility for several telemetry channels
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<dl_settings>
|
||||
|
||||
<dl_settings NAME="Misc">
|
||||
<dl_setting var="telemetry_mode_Main" min="0" step="1" max="11" module="booz2_telemetry" shortname="telemetry" values="Default|PPM|Raw sensors|Scaled sensors|AHRS|Rate loop|Att loop|Vert loop|H loop|Aligner|HS_att_roll|Tune_hover"/>
|
||||
<dl_setting var="telemetry_mode_Main_DefaultChannel" min="0" step="1" max="11" module="booz2_telemetry" shortname="telemetry" values="Default|PPM|Raw sensors|Scaled sensors|AHRS|Rate loop|Att loop|Vert loop|H loop|Aligner|HS_att_roll|Tune_hover"/>
|
||||
<dl_setting var="booz2_autopilot_mode_auto2" min="0" step="1" max="12" module="booz2_autopilot" shortname="auto2" values="Fail|Kill|Rate|Att|Rate_rcC|Att_rcC|Att_C|Rate_Z|Att_Z|Hover|Hover_C|Hover_Z|Nav"/>
|
||||
</dl_settings>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<!ATTLIST telemetry>
|
||||
<!ATTLIST process
|
||||
name CDATA #REQUIRED
|
||||
channel CDATA "DefaultChannel"
|
||||
>
|
||||
<!ATTLIST mode
|
||||
name CDATA #REQUIRED
|
||||
|
||||
+46
-46
@@ -52,129 +52,129 @@
|
||||
#endif
|
||||
|
||||
|
||||
#define PERIODIC_SEND_ALIVE() DOWNLINK_SEND_ALIVE(16, MD5SUM);
|
||||
#define PERIODIC_SEND_ALIVE(_chan) DOWNLINK_SEND_ALIVE(_chan, 16, MD5SUM);
|
||||
|
||||
#define PERIODIC_SEND_BAT() Downlink({ int16_t e = energy; DOWNLINK_SEND_BAT(&v_ctl_throttle_slewed, &vsupply, &estimator_flight_time, &kill_throttle, &block_time, &stage_time, &e); })
|
||||
#define PERIODIC_SEND_BAT(_chan) Downlink({ int16_t e = energy; DOWNLINK_SEND_BAT(_chan, &v_ctl_throttle_slewed, &vsupply, &estimator_flight_time, &kill_throttle, &block_time, &stage_time, &e); })
|
||||
|
||||
#ifdef MCU_SPI_LINK
|
||||
#define PERIODIC_SEND_DEBUG_MCU_LINK() DOWNLINK_SEND_DEBUG_MCU_LINK(&link_mcu_nb_err, &link_mcu_fbw_nb_err, &mcu1_ppm_cpt);
|
||||
#define PERIODIC_SEND_DEBUG_MCU_LINK(_chan) DOWNLINK_SEND_DEBUG_MCU_LINK(_chan, &link_mcu_nb_err, &link_mcu_fbw_nb_err, &mcu1_ppm_cpt);
|
||||
#else
|
||||
#define PERIODIC_SEND_DEBUG_MCU_LINK() {}
|
||||
#define PERIODIC_SEND_DEBUG_MCU_LINK(_chan) {}
|
||||
#endif
|
||||
|
||||
|
||||
#define PERIODIC_SEND_DOWNLINK() { \
|
||||
#define PERIODIC_SEND_DOWNLINK(_chan) { \
|
||||
static uint16_t last; \
|
||||
uint16_t rate = (downlink_nb_bytes - last) / PERIOD_DOWNLINK_0; \
|
||||
uint16_t rate = (downlink_nb_bytes - last) / PERIOD_DOWNLINK_DefaultChannel_0; \
|
||||
last = downlink_nb_bytes; \
|
||||
DOWNLINK_SEND_DOWNLINK(&downlink_nb_ovrn, &rate, &downlink_nb_msgs); \
|
||||
DOWNLINK_SEND_DOWNLINK(_chan, &downlink_nb_ovrn, &rate, &downlink_nb_msgs); \
|
||||
}
|
||||
|
||||
|
||||
#define PERIODIC_SEND_ATTITUDE() Downlink({ \
|
||||
DOWNLINK_SEND_ATTITUDE(&estimator_phi, &estimator_psi, &estimator_theta); \
|
||||
#define PERIODIC_SEND_ATTITUDE(_chan) Downlink({ \
|
||||
DOWNLINK_SEND_ATTITUDE(_chan, &estimator_phi, &estimator_psi, &estimator_theta); \
|
||||
})
|
||||
|
||||
#define PERIODIC_SEND_PPRZ_MODE() DOWNLINK_SEND_PPRZ_MODE(&pprz_mode, &v_ctl_mode, &lateral_mode, &horizontal_mode, &rc_settings_mode, &mcu1_status);
|
||||
#define PERIODIC_SEND_DESIRED() DOWNLINK_SEND_DESIRED(&h_ctl_roll_setpoint, &h_ctl_pitch_loop_setpoint, &h_ctl_course_setpoint, &desired_x, &desired_y, &v_ctl_altitude_setpoint, &v_ctl_climb_setpoint);
|
||||
#define PERIODIC_SEND_PPRZ_MODE(_chan) DOWNLINK_SEND_PPRZ_MODE(_chan, &pprz_mode, &v_ctl_mode, &lateral_mode, &horizontal_mode, &rc_settings_mode, &mcu1_status);
|
||||
#define PERIODIC_SEND_DESIRED(_chan) DOWNLINK_SEND_DESIRED(_chan, &h_ctl_roll_setpoint, &h_ctl_pitch_loop_setpoint, &h_ctl_course_setpoint, &desired_x, &desired_y, &v_ctl_altitude_setpoint, &v_ctl_climb_setpoint);
|
||||
|
||||
#define PERIODIC_SEND_NAVIGATION_REF() DOWNLINK_SEND_NAVIGATION_REF(&nav_utm_east0, &nav_utm_north0, &nav_utm_zone0);
|
||||
#define PERIODIC_SEND_NAVIGATION_REF(_chan) DOWNLINK_SEND_NAVIGATION_REF(_chan, &nav_utm_east0, &nav_utm_north0, &nav_utm_zone0);
|
||||
|
||||
|
||||
#define DownlinkSendWp(i) { \
|
||||
#define DownlinkSendWp(_chan, i) { \
|
||||
float x = nav_utm_east0 + waypoints[i].x; \
|
||||
float y = nav_utm_north0 + waypoints[i].y; \
|
||||
DOWNLINK_SEND_WP_MOVED(&i, &x, &y, &(waypoints[i].a),&nav_utm_zone0); \
|
||||
DOWNLINK_SEND_WP_MOVED(_chan, &i, &x, &y, &(waypoints[i].a),&nav_utm_zone0); \
|
||||
}
|
||||
|
||||
|
||||
#define PERIODIC_SEND_WP_MOVED() { \
|
||||
#define PERIODIC_SEND_WP_MOVED(_chan) { \
|
||||
static uint8_t i; \
|
||||
i++; if (i >= nb_waypoint) i = 0; \
|
||||
DownlinkSendWp(i); \
|
||||
DownlinkSendWp(_chan, i); \
|
||||
}
|
||||
|
||||
#ifdef RADIO_CONTROL_SETTINGS
|
||||
#define PERIODIC_SEND_SETTINGS() if (!RcSettingsOff()) DOWNLINK_SEND_SETTINGS(&slider_1_val, &slider_2_val);
|
||||
#define PERIODIC_SEND_SETTINGS(_chan) if (!RcSettingsOff()) DOWNLINK_SEND_SETTINGS(_chan, &slider_1_val, &slider_2_val);
|
||||
#else
|
||||
#define PERIODIC_SEND_SETTINGS() {}
|
||||
#define PERIODIC_SEND_SETTINGS(_chan) {}
|
||||
#endif
|
||||
|
||||
#ifdef INFRARED
|
||||
#define PERIODIC_SEND_IR_SENSORS() DOWNLINK_SEND_IR_SENSORS(&ir_ir1, &ir_ir2, &ir_pitch, &ir_roll, &ir_top);
|
||||
#define PERIODIC_SEND_IR_SENSORS(_chan) DOWNLINK_SEND_IR_SENSORS(_chan, &ir_ir1, &ir_ir2, &ir_pitch, &ir_roll, &ir_top);
|
||||
#endif
|
||||
|
||||
#define PERIODIC_SEND_ADC() {}
|
||||
#define PERIODIC_SEND_ADC(_chan) {}
|
||||
|
||||
#ifdef IDG300
|
||||
#include "gyro.h"
|
||||
#define PERIODIC_SEND_GYRO_RATES() DOWNLINK_SEND_GYRO_RATES(&roll_rate_adc, &estimator_p, &estimator_q)
|
||||
#define PERIODIC_SEND_GYRO_RATES(_chan) DOWNLINK_SEND_GYRO_RATES(_chan, &roll_rate_adc, &estimator_p, &estimator_q)
|
||||
#elif defined ADXRS150
|
||||
#define PERIODIC_SEND_GYRO_RATES() DOWNLINK_SEND_GYRO_RATES(&roll_rate_adc, &estimator_p, &temp_comp)
|
||||
#define PERIODIC_SEND_GYRO_RATES(_chan) DOWNLINK_SEND_GYRO_RATES(_chan, &roll_rate_adc, &estimator_p, &temp_comp)
|
||||
#else
|
||||
#define PERIODIC_SEND_GYRO_RATES() {}
|
||||
#define PERIODIC_SEND_GYRO_RATES(_chan) {}
|
||||
#endif
|
||||
|
||||
#define PERIODIC_SEND_CALIBRATION() DOWNLINK_SEND_CALIBRATION(&v_ctl_auto_throttle_sum_err, &v_ctl_auto_throttle_submode)
|
||||
#define PERIODIC_SEND_CALIBRATION(_chan) DOWNLINK_SEND_CALIBRATION(_chan, &v_ctl_auto_throttle_sum_err, &v_ctl_auto_throttle_submode)
|
||||
|
||||
#define PERIODIC_SEND_CIRCLE() if (nav_in_circle) { DOWNLINK_SEND_CIRCLE(&nav_circle_x, &nav_circle_y, &nav_circle_radius); }
|
||||
#define PERIODIC_SEND_CIRCLE(_chan) if (nav_in_circle) { DOWNLINK_SEND_CIRCLE(_chan, &nav_circle_x, &nav_circle_y, &nav_circle_radius); }
|
||||
|
||||
#define PERIODIC_SEND_SEGMENT() if (nav_in_segment) { DOWNLINK_SEND_SEGMENT(&nav_segment_x_1, &nav_segment_y_1, &nav_segment_x_2, &nav_segment_y_2); }
|
||||
#define PERIODIC_SEND_SEGMENT(_chan) if (nav_in_segment) { DOWNLINK_SEND_SEGMENT(_chan, &nav_segment_x_1, &nav_segment_y_1, &nav_segment_x_2, &nav_segment_y_2); }
|
||||
|
||||
#ifdef IMU_ANALOG
|
||||
#define PERIODIC_SEND_IMU() { int16_t dummy = 42; DOWNLINK_SEND_IMU(&(from_fbw.euler_dot[0]), &(from_fbw.euler_dot[1]), &(from_fbw.euler_dot[2]), &dummy, &dummy, &dummy); }
|
||||
#define PERIODIC_SEND_IMU(_chan) { int16_t dummy = 42; DOWNLINK_SEND_IMU(_chan, &(from_fbw.euler_dot[0]), &(from_fbw.euler_dot[1]), &(from_fbw.euler_dot[2]), &dummy, &dummy, &dummy); }
|
||||
#else
|
||||
#define PERIODIC_SEND_IMU() {}
|
||||
#define PERIODIC_SEND_IMU(_chan) {}
|
||||
#endif
|
||||
|
||||
#define PERIODIC_SEND_ESTIMATOR() DOWNLINK_SEND_ESTIMATOR(&estimator_z, &estimator_z_dot)
|
||||
#define PERIODIC_SEND_ESTIMATOR(_chan) DOWNLINK_SEND_ESTIMATOR(_chan, &estimator_z, &estimator_z_dot)
|
||||
|
||||
#define SEND_NAVIGATION() Downlink({ DOWNLINK_SEND_NAVIGATION(&nav_block, &nav_stage, &estimator_x, &estimator_y, &dist2_to_wp, &dist2_to_home);})
|
||||
#define SEND_NAVIGATION(_chan) Downlink({ DOWNLINK_SEND_NAVIGATION(_chan, &nav_block, &nav_stage, &estimator_x, &estimator_y, &dist2_to_wp, &dist2_to_home);})
|
||||
|
||||
#define PERIODIC_SEND_NAVIGATION() SEND_NAVIGATION()
|
||||
#define PERIODIC_SEND_NAVIGATION(_chan) SEND_NAVIGATION(_chan)
|
||||
|
||||
#if defined CAM || defined MOBILE_CAM
|
||||
#define SEND_CAM() Downlink({ int16_t x = cam_target_x; int16_t y = cam_target_y; int8_t phi = DegOfRad(cam_phi_c); int8_t theta = DegOfRad(cam_theta_c); DOWNLINK_SEND_CAM(&phi, &theta, &x, &y);})
|
||||
#define SEND_CAM(_chan) Downlink({ int16_t x = cam_target_x; int16_t y = cam_target_y; int8_t phi = DegOfRad(cam_phi_c); int8_t theta = DegOfRad(cam_theta_c); DOWNLINK_SEND_CAM(_chan, &phi, &theta, &x, &y);})
|
||||
#else
|
||||
#define SEND_CAM() {}
|
||||
#define SEND_CAM(_chan) {}
|
||||
#endif
|
||||
|
||||
#define PERIODIC_SEND_DL_VALUE() PeriodicSendDlValue() /** generated from the xml settings config in conf/settings */
|
||||
#define PERIODIC_SEND_DL_VALUE(_chan) PeriodicSendDlValue(_chan) /** generated from the xml settings config in conf/settings */
|
||||
|
||||
#define PERIODIC_SEND_SURVEY() { \
|
||||
#define PERIODIC_SEND_SURVEY(_chan) { \
|
||||
if (nav_survey_active) \
|
||||
DOWNLINK_SEND_SURVEY(&nav_survey_east, &nav_survey_north, &nav_survey_west, &nav_survey_south); \
|
||||
DOWNLINK_SEND_SURVEY(_chan, &nav_survey_east, &nav_survey_north, &nav_survey_west, &nav_survey_south); \
|
||||
}
|
||||
|
||||
#define PERIODIC_SEND_RANGEFINDER() DOWNLINK_SEND_RANGEFINDER(&rangemeter, &ctl_grz_z_dot, &ctl_grz_z_dot_sum_err, &ctl_grz_z_dot_setpoint, &ctl_grz_z_sum_err, &ctl_grz_z_setpoint, &flying)
|
||||
#define PERIODIC_SEND_RANGEFINDER(_chan) DOWNLINK_SEND_RANGEFINDER(_chan, &rangemeter, &ctl_grz_z_dot, &ctl_grz_z_dot_sum_err, &ctl_grz_z_dot_setpoint, &ctl_grz_z_sum_err, &ctl_grz_z_setpoint, &flying)
|
||||
|
||||
#define PERIODIC_SEND_TUNE_ROLL() DOWNLINK_SEND_TUNE_ROLL(&estimator_p,&estimator_phi, &h_ctl_roll_setpoint);
|
||||
#define PERIODIC_SEND_TUNE_ROLL(_chan) DOWNLINK_SEND_TUNE_ROLL(_chan, &estimator_p,&estimator_phi, &h_ctl_roll_setpoint);
|
||||
|
||||
#if defined GPS || defined SITL
|
||||
#define PERIODIC_SEND_GPS_SOL() DOWNLINK_SEND_GPS_SOL(&gps_Pacc, &gps_Sacc, &gps_PDOP, &gps_numSV)
|
||||
#define PERIODIC_SEND_GPS_SOL(_chan) DOWNLINK_SEND_GPS_SOL(_chan, &gps_Pacc, &gps_Sacc, &gps_PDOP, &gps_numSV)
|
||||
#endif
|
||||
|
||||
/* add by Haiyang Chao for debugging msg used by osam_imu*/
|
||||
#if defined UGEAR
|
||||
#define PERIODIC_SEND_GPS() DOWNLINK_SEND_GPS(&gps_mode, &gps_utm_east, &gps_utm_north, &gps_course, &gps_alt, &gps_gspeed,&gps_climb, &gps_week, &gps_itow, &gps_utm_zone, &gps_nb_ovrn)
|
||||
#define PERIODIC_SEND_GPS_SOL() DOWNLINK_SEND_GPS_SOL(&gps_Pacc, &gps_Sacc, &gps_PDOP, &gps_numSV)
|
||||
#define PERIODIC_SEND_DebugChao() DOWNLINK_SEND_DebugChao(&ugear_debug1, &ugear_debug2, &ugear_debug3, &ugear_debug4, &ugear_debug5, &ugear_debug6)
|
||||
#define PERIODIC_SEND_GPS(_chan) DOWNLINK_SEND_GPS(_chan, &gps_mode, &gps_utm_east, &gps_utm_north, &gps_course, &gps_alt, &gps_gspeed,&gps_climb, &gps_week, &gps_itow, &gps_utm_zone, &gps_nb_ovrn)
|
||||
#define PERIODIC_SEND_GPS_SOL(_chan) DOWNLINK_SEND_GPS_SOL(_chan, &gps_Pacc, &gps_Sacc, &gps_PDOP, &gps_numSV)
|
||||
#define PERIODIC_SEND_DebugChao(_chan) DOWNLINK_SEND_DebugChao(_chan, &ugear_debug1, &ugear_debug2, &ugear_debug3, &ugear_debug4, &ugear_debug5, &ugear_debug6)
|
||||
#else
|
||||
# ifdef PERIOD_GPS_0
|
||||
# include "gps.h"
|
||||
# define PERIODIC_SEND_GPS() gps_send()
|
||||
# define PERIODIC_SEND_GPS(_chan) gps_send()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_BARO_MS5534A
|
||||
#include "baro_MS5534A.h"
|
||||
#define PERIODIC_SEND_BARO_MS5534A() DOWNLINK_SEND_BARO_MS5534A(&baro_MS5534A_pressure, &baro_MS5534A_temp, &baro_MS5534A_z)
|
||||
#define PERIODIC_SEND_BARO_MS5534A(_chan) DOWNLINK_SEND_BARO_MS5534A(_chan, &baro_MS5534A_pressure, &baro_MS5534A_temp, &baro_MS5534A_z)
|
||||
#else
|
||||
#define PERIODIC_SEND_BARO_MS5534A() {}
|
||||
#define PERIODIC_SEND_BARO_MS5534A(_chan) {}
|
||||
#endif
|
||||
|
||||
#include "fw_h_ctl_a.h"
|
||||
#define PERIODIC_SEND_H_CTL_A() DOWNLINK_SEND_H_CTL_A(&h_ctl_roll_sum_err, &h_ctl_ref_roll_angle, &h_ctl_pitch_sum_err, &h_ctl_ref_pitch_angle)
|
||||
#define PERIODIC_SEND_H_CTL_A(_chan) DOWNLINK_SEND_H_CTL_A(_chan, &h_ctl_roll_sum_err, &h_ctl_ref_roll_angle, &h_ctl_pitch_sum_err, &h_ctl_ref_pitch_angle)
|
||||
|
||||
|
||||
#endif /* AP_DOWNLINK_H */
|
||||
|
||||
@@ -47,7 +47,7 @@ void dl_parse_msg(void) {
|
||||
|
||||
case DL_PING:
|
||||
{
|
||||
DOWNLINK_SEND_PONG();
|
||||
DOWNLINK_SEND_PONG(DefaultChannel);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -56,7 +56,7 @@ void dl_parse_msg(void) {
|
||||
uint8_t i = DL_SETTING_index(dl_buffer);
|
||||
float var = DL_SETTING_value(dl_buffer);
|
||||
DlSetting(i, var);
|
||||
DOWNLINK_SEND_DL_VALUE(&i, &var);
|
||||
DOWNLINK_SEND_DL_VALUE(DefaultChannel, &i, &var);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -91,7 +91,7 @@ void dl_parse_msg(void) {
|
||||
enu.y = POS_BFP_OF_REAL(enu.y)/100;
|
||||
enu.z = POS_BFP_OF_REAL(enu.z)/100;
|
||||
VECT3_ASSIGN(waypoints[wp_id], enu.x, enu.y, enu.z);
|
||||
DOWNLINK_SEND_WP_MOVED_LTP(&wp_id, &enu.x, &enu.y, &enu.z);
|
||||
DOWNLINK_SEND_WP_MOVED_LTP(DefaultChannel, &wp_id, &enu.x, &enu.y, &enu.z);
|
||||
}
|
||||
break;
|
||||
#endif /* USE_NAVIGATION */
|
||||
|
||||
@@ -24,5 +24,5 @@
|
||||
#include "booz2_telemetry.h"
|
||||
|
||||
|
||||
uint8_t telemetry_mode_Main;
|
||||
uint8_t telemetry_mode_Main_DefaultChannel;
|
||||
|
||||
|
||||
+243
-193
File diff suppressed because it is too large
Load Diff
@@ -75,7 +75,7 @@ void dl_parse_msg(void) {
|
||||
uint8_t msg_id = IdOfMsg(dl_buffer);
|
||||
|
||||
if (msg_id == DL_PING) {
|
||||
DOWNLINK_SEND_PONG();
|
||||
DOWNLINK_SEND_PONG(DefaultChannel);
|
||||
} else
|
||||
#ifdef TRAFFIC_INFO
|
||||
if (msg_id == DL_ACINFO && DL_ACINFO_ac_id(dl_buffer) != AC_ID) {
|
||||
@@ -105,7 +105,7 @@ void dl_parse_msg(void) {
|
||||
coordinates */
|
||||
latlong_utm_x = waypoints[wp_id].x + nav_utm_east0;
|
||||
latlong_utm_y = waypoints[wp_id].y + nav_utm_north0;
|
||||
DOWNLINK_SEND_WP_MOVED(&wp_id, &latlong_utm_x, &latlong_utm_y, &a, &nav_utm_zone0);
|
||||
DOWNLINK_SEND_WP_MOVED(DefaultChannel, &wp_id, &latlong_utm_x, &latlong_utm_y, &a, &nav_utm_zone0);
|
||||
} else if (msg_id == DL_BLOCK && DL_BLOCK_ac_id(dl_buffer) == AC_ID) {
|
||||
nav_goto_block(DL_BLOCK_block_id(dl_buffer));
|
||||
} else
|
||||
@@ -116,7 +116,7 @@ void dl_parse_msg(void) {
|
||||
wind_north = DL_WIND_INFO_north(dl_buffer);
|
||||
estimator_airspeed = DL_WIND_INFO_airspeed(dl_buffer);
|
||||
#ifdef WIND_INFO_RET
|
||||
DOWNLINK_SEND_WIND_INFO_RET(&wind_east, &wind_north, &estimator_airspeed);
|
||||
DOWNLINK_SEND_WIND_INFO_RET(DefaultChannel, &wind_east, &wind_north, &estimator_airspeed);
|
||||
#endif
|
||||
} else
|
||||
#endif /** WIND_INFO */
|
||||
@@ -147,11 +147,11 @@ void dl_parse_msg(void) {
|
||||
uint8_t i = DL_SETTING_index(dl_buffer);
|
||||
float val = DL_SETTING_value(dl_buffer);
|
||||
DlSetting(i, val);
|
||||
DOWNLINK_SEND_DL_VALUE(&i, &val);
|
||||
DOWNLINK_SEND_DL_VALUE(DefaultChannel, &i, &val);
|
||||
} else if (msg_id == DL_GET_SETTING && DL_GET_SETTING_ac_id(dl_buffer) == AC_ID) {
|
||||
uint8_t i = DL_SETTING_index(dl_buffer);
|
||||
float val = settings_get_value(i);
|
||||
DOWNLINK_SEND_DL_VALUE(&i, &val);
|
||||
DOWNLINK_SEND_DL_VALUE(DefaultChannel, &i, &val);
|
||||
} else
|
||||
#endif /** Else there is no dl_settings section in the flight plan */
|
||||
#ifdef USE_JOYSTICK
|
||||
|
||||
@@ -34,14 +34,14 @@
|
||||
#ifndef TELEMETRY_MODE_FBW
|
||||
#define TELEMETRY_MODE_FBW 0
|
||||
#endif
|
||||
uint8_t telemetry_mode_Fbw = TELEMETRY_MODE_FBW;
|
||||
uint8_t telemetry_mode_Fbw_DefaultChannel = TELEMETRY_MODE_FBW;
|
||||
#endif /** FBW */
|
||||
|
||||
#ifdef AP
|
||||
#ifndef TELEMETRY_MODE_AP
|
||||
#define TELEMETRY_MODE_AP 0
|
||||
#endif
|
||||
uint8_t telemetry_mode_Ap =TELEMETRY_MODE_AP;
|
||||
uint8_t telemetry_mode_Ap_DefaultChannel = TELEMETRY_MODE_AP;
|
||||
#endif /** AP */
|
||||
|
||||
uint8_t downlink_nb_ovrn;
|
||||
|
||||
+29
-25
@@ -53,14 +53,18 @@
|
||||
#include "xbee.h"
|
||||
#endif /** !SITL */
|
||||
|
||||
#ifndef DefaultChannel
|
||||
#define DefaultChannel DOWNLINK_TRANSPORT
|
||||
#endif
|
||||
|
||||
#ifdef AP
|
||||
/** Telemetry mode for AP process: index in the telemetry.xml file */
|
||||
extern uint8_t telemetry_mode_Ap;
|
||||
extern uint8_t telemetry_mode_Ap_DefaultChannel;
|
||||
#endif
|
||||
|
||||
#ifdef FBW
|
||||
/** Telemetry mode for FBW process: index in the telemetry.xml file */
|
||||
extern uint8_t telemetry_mode_Fbw;
|
||||
extern uint8_t telemetry_mode_Fbw_DefaultChannel;
|
||||
#endif
|
||||
|
||||
/** Counter of messages not sent because of unavailibity of the output buffer*/
|
||||
@@ -71,41 +75,41 @@ extern uint16_t downlink_nb_msgs;
|
||||
|
||||
#define __Transport(dev, _x) dev##_x
|
||||
#define _Transport(dev, _x) __Transport(dev, _x)
|
||||
#define Transport(_x) _Transport(DOWNLINK_TRANSPORT, _x)
|
||||
#define Transport(_chan, _fun) _Transport(_chan, _fun)
|
||||
|
||||
|
||||
/** Set of macros for generated code (messages.h) from messages.xml */
|
||||
/** 2 = ac_id + msg_id */
|
||||
#define DownlinkIDsSize(_x) (_x+2)
|
||||
#define DownlinkSizeOf(_x) Transport(SizeOf(DownlinkIDsSize(_x)))
|
||||
#define DownlinkIDsSize(_chan, _x) (_x+2)
|
||||
#define DownlinkSizeOf(_chan, _x) Transport(_chan, SizeOf(DownlinkIDsSize(_chan, _x)))
|
||||
|
||||
#define DownlinkCheckFreeSpace(_x) Transport(CheckFreeSpace((uint8_t)(_x)))
|
||||
#define DownlinkCheckFreeSpace(_chan, _x) Transport(_chan, CheckFreeSpace((uint8_t)(_x)))
|
||||
|
||||
#define DownlinkPutUint8(_x) Transport(PutUint8(_x))
|
||||
#define DownlinkPutUint8(_chan, _x) Transport(_chan, PutUint8(_x))
|
||||
|
||||
#define DownlinkPutInt8ByAddr(_x) Transport(PutInt8ByAddr(_x))
|
||||
#define DownlinkPutUint8ByAddr(_x) Transport(PutUint8ByAddr(_x))
|
||||
#define DownlinkPutInt16ByAddr(_x) Transport(PutInt16ByAddr(_x))
|
||||
#define DownlinkPutUint16ByAddr(_x) Transport(PutUint16ByAddr(_x))
|
||||
#define DownlinkPutInt32ByAddr(_x) Transport(PutInt32ByAddr(_x))
|
||||
#define DownlinkPutUint32ByAddr(_x) Transport(PutUint32ByAddr(_x))
|
||||
#define DownlinkPutFloatByAddr(_x) Transport(PutFloatByAddr(_x))
|
||||
#define DownlinkPutInt8ByAddr(_chan, _x) Transport(_chan, PutInt8ByAddr(_x))
|
||||
#define DownlinkPutUint8ByAddr(_chan, _x) Transport(_chan, PutUint8ByAddr(_x))
|
||||
#define DownlinkPutInt16ByAddr(_chan, _x) Transport(_chan, PutInt16ByAddr(_x))
|
||||
#define DownlinkPutUint16ByAddr(_chan, _x) Transport(_chan, PutUint16ByAddr(_x))
|
||||
#define DownlinkPutInt32ByAddr(_chan, _x) Transport(_chan, PutInt32ByAddr(_x))
|
||||
#define DownlinkPutUint32ByAddr(_chan, _x) Transport(_chan, PutUint32ByAddr(_x))
|
||||
#define DownlinkPutFloatByAddr(_chan, _x) Transport(_chan, PutFloatByAddr(_x))
|
||||
|
||||
#define DownlinkPutFloatArray(_n, _x) Transport(PutFloatArray(_n, _x))
|
||||
#define DownlinkPutInt16Array(_n, _x) Transport(PutInt16Array(_n, _x))
|
||||
#define DownlinkPutUint16Array(_n, _x) Transport(PutUint16Array(_n, _x))
|
||||
#define DownlinkPutUint8Array(_n, _x) Transport(PutUint8Array(_n, _x))
|
||||
#define DownlinkPutFloatArray(_chan, _n, _x) Transport(_chan, PutFloatArray(_n, _x))
|
||||
#define DownlinkPutInt16Array(_chan, _n, _x) Transport(_chan, PutInt16Array(_n, _x))
|
||||
#define DownlinkPutUint16Array(_chan, _n, _x) Transport(_chan, PutUint16Array(_n, _x))
|
||||
#define DownlinkPutUint8Array(_chan, _n, _x) Transport(_chan, PutUint8Array(_n, _x))
|
||||
|
||||
#define DownlinkOverrun() downlink_nb_ovrn++;
|
||||
#define DownlinkCountBytes(_n) downlink_nb_bytes += _n;
|
||||
#define DownlinkOverrun(_chan) downlink_nb_ovrn++;
|
||||
#define DownlinkCountBytes(_chan, _n) downlink_nb_bytes += _n;
|
||||
|
||||
#define DownlinkStartMessage(_name, msg_id, payload_len) { \
|
||||
#define DownlinkStartMessage(_chan, _name, msg_id, payload_len) { \
|
||||
downlink_nb_msgs++; \
|
||||
Transport(Header(DownlinkIDsSize(payload_len))); \
|
||||
Transport(PutUint8(AC_ID)); \
|
||||
Transport(PutNamedUint8(_name, msg_id)); \
|
||||
Transport(_chan, Header(DownlinkIDsSize(_chan, payload_len))); \
|
||||
Transport(_chan, PutUint8(AC_ID)); \
|
||||
Transport(_chan, PutNamedUint8(_name, msg_id)); \
|
||||
}
|
||||
|
||||
#define DownlinkEndMessage() Transport(Trailer())
|
||||
#define DownlinkEndMessage(_chan) Transport(_chan, Trailer())
|
||||
|
||||
#endif /* DOWNLINK_H */
|
||||
|
||||
+14
-14
@@ -50,37 +50,37 @@
|
||||
#define DOWNLINK_DEVICE DOWNLINK_FBW_DEVICE
|
||||
#include "downlink.h"
|
||||
|
||||
#define PERIODIC_SEND_COMMANDS() DOWNLINK_SEND_COMMANDS(COMMANDS_NB, commands)
|
||||
#define PERIODIC_SEND_COMMANDS(_chan) DOWNLINK_SEND_COMMANDS(_chan, COMMANDS_NB, commands)
|
||||
|
||||
#ifdef RADIO_CONTROL
|
||||
#define PERIODIC_SEND_FBW_STATUS() DOWNLINK_SEND_FBW_STATUS(&rc_status, &fbw_mode, &fbw_vsupply_decivolt, &fbw_current_milliamp)
|
||||
#define PERIODIC_SEND_PPM() DOWNLINK_SEND_PPM(&last_ppm_cpt, PPM_NB_PULSES, ppm_pulses)
|
||||
#define PERIODIC_SEND_RC() DOWNLINK_SEND_RC(PPM_NB_PULSES, rc_values)
|
||||
#define PERIODIC_SEND_FBW_STATUS(_chan) DOWNLINK_SEND_FBW_STATUS(_chan, &rc_status, &fbw_mode, &fbw_vsupply_decivolt, &fbw_current_milliamp)
|
||||
#define PERIODIC_SEND_PPM(_chan) DOWNLINK_SEND_PPM(_chan, &last_ppm_cpt, PPM_NB_PULSES, ppm_pulses)
|
||||
#define PERIODIC_SEND_RC(_chan) DOWNLINK_SEND_RC(_chan, PPM_NB_PULSES, rc_values)
|
||||
#else // RADIO_CONTROL
|
||||
#define PERIODIC_SEND_FBW_STATUS() { uint8_t dummy = 0; DOWNLINK_SEND_FBW_STATUS(&dummy, &fbw_mode, &fbw_vsupply_decivolt, &fbw_current_milliamp); }
|
||||
#define PERIODIC_SEND_PPM() {}
|
||||
#define PERIODIC_SEND_RC() {}
|
||||
#define PERIODIC_SEND_FBW_STATUS(_chan) { uint8_t dummy = 0; DOWNLINK_SEND_FBW_STATUS(_chan, &dummy, &fbw_mode, &fbw_vsupply_decivolt, &fbw_current_milliamp); }
|
||||
#define PERIODIC_SEND_PPM(_chan) {}
|
||||
#define PERIODIC_SEND_RC(_chan) {}
|
||||
#endif // RADIO_CONTROL
|
||||
|
||||
#ifdef ACTUATORS
|
||||
#define PERIODIC_SEND_ACTUATORS() DOWNLINK_SEND_ACTUATORS(SERVOS_NB, actuators)
|
||||
#define PERIODIC_SEND_ACTUATORS(_chan) DOWNLINK_SEND_ACTUATORS(_chan, SERVOS_NB, actuators)
|
||||
#else
|
||||
#define PERIODIC_SEND_ACTUATORS() {}
|
||||
#define PERIODIC_SEND_ACTUATORS(_chan) {}
|
||||
#endif
|
||||
|
||||
#ifdef BRICOLAGE_ADC
|
||||
extern uint16_t adc0_val[];
|
||||
|
||||
#define PERIODIC_SEND_ADC() { \
|
||||
static const uint8_t mcu = 0; \
|
||||
DOWNLINK_SEND_ADC(&mcu, 8, adc0_val); \
|
||||
#define PERIODIC_SEND_ADC(_chan) { \
|
||||
static const uint8_t mcu = 0; \
|
||||
DOWNLINK_SEND_ADC(_chan, &mcu, 8, adc0_val); \
|
||||
}
|
||||
#else
|
||||
#define PERIODIC_SEND_ADC() {}
|
||||
#define PERIODIC_SEND_ADC(_chan) {}
|
||||
#endif
|
||||
|
||||
static inline void fbw_downlink_periodic_task(void) {
|
||||
PeriodicSendFbw()
|
||||
PeriodicSendFbw_DefaultChannel()
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -70,13 +70,13 @@ void gps_downlink( void ) {
|
||||
|
||||
void gps_send( void ) {
|
||||
|
||||
DOWNLINK_SEND_GPS(&gps_mode, &gps_utm_east, &gps_utm_north, &gps_course, &gps_alt, &gps_gspeed,&gps_climb, &gps_week, &gps_itow, &gps_utm_zone, &gps_nb_ovrn);
|
||||
DOWNLINK_SEND_GPS(DefaultChannel, &gps_mode, &gps_utm_east, &gps_utm_north, &gps_course, &gps_alt, &gps_gspeed,&gps_climb, &gps_week, &gps_itow, &gps_utm_zone, &gps_nb_ovrn);
|
||||
|
||||
static uint8_t i;
|
||||
static uint8_t last_cnos[GPS_NB_CHANNELS];
|
||||
if (i == gps_nb_channels) i = 0;
|
||||
if (i < gps_nb_channels && gps_svinfos[i].cno > 0 && gps_svinfos[i].cno != last_cnos[i]) {
|
||||
DOWNLINK_SEND_SVINFO(&i, &gps_svinfos[i].svid, &gps_svinfos[i].flags, &gps_svinfos[i].qi, &gps_svinfos[i].cno, &gps_svinfos[i].elev, &gps_svinfos[i].azim);
|
||||
DOWNLINK_SEND_SVINFO(DefaultChannel, &i, &gps_svinfos[i].svid, &gps_svinfos[i].flags, &gps_svinfos[i].qi, &gps_svinfos[i].cno, &gps_svinfos[i].elev, &gps_svinfos[i].azim);
|
||||
last_cnos[i] = gps_svinfos[i].cno;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ void gps_send( void ) {
|
||||
for(j = 0; j < gps_nb_channels; j++) {
|
||||
uint8_t cno = gps_svinfos[j].cno;
|
||||
if (cno > 0 && j != i && abs(cno-last_cnos[j]) >= 2) {
|
||||
DOWNLINK_SEND_SVINFO(&j, &gps_svinfos[j].svid, &gps_svinfos[j].flags, &gps_svinfos[j].qi, &cno, &gps_svinfos[j].elev, &gps_svinfos[j].azim);
|
||||
DOWNLINK_SEND_SVINFO(DefaultChannel, &j, &gps_svinfos[j].svid, &gps_svinfos[j].flags, &gps_svinfos[j].qi, &cno, &gps_svinfos[j].elev, &gps_svinfos[j].azim);
|
||||
last_cnos[j] = gps_svinfos[j].cno;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,12 +241,12 @@ static inline void reporting_task( void ) {
|
||||
|
||||
/** initialisation phase during boot */
|
||||
if (boot) {
|
||||
DOWNLINK_SEND_BOOT(&version);
|
||||
DOWNLINK_SEND_BOOT(DefaultChannel, &version);
|
||||
boot = FALSE;
|
||||
}
|
||||
/** then report periodicly */
|
||||
else {
|
||||
PeriodicSendAp();
|
||||
PeriodicSendAp_DefaultChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ inline void telecommand_task( void ) {
|
||||
}
|
||||
mode_changed |= mcu1_status_update();
|
||||
if ( mode_changed )
|
||||
PERIODIC_SEND_PPRZ_MODE();
|
||||
PERIODIC_SEND_PPRZ_MODE(DefaultChannel);
|
||||
|
||||
#if defined RADIO_CONTROL || defined RADIO_CONTROL_AUTO1
|
||||
/** In AUTO1 mode, compute roll setpoint and pitch setpoint from
|
||||
@@ -333,7 +333,7 @@ static void navigation_task( void ) {
|
||||
if (pprz_mode == PPRZ_MODE_AUTO2 || pprz_mode == PPRZ_MODE_HOME) {
|
||||
last_pprz_mode = pprz_mode;
|
||||
pprz_mode = PPRZ_MODE_GPS_OUT_OF_ORDER;
|
||||
PERIODIC_SEND_PPRZ_MODE();
|
||||
PERIODIC_SEND_PPRZ_MODE(DefaultChannel);
|
||||
gps_lost = TRUE;
|
||||
}
|
||||
} else /* GPS is ok */ if (gps_lost) {
|
||||
@@ -341,7 +341,7 @@ static void navigation_task( void ) {
|
||||
pprz_mode = last_pprz_mode;
|
||||
gps_lost = FALSE;
|
||||
|
||||
PERIODIC_SEND_PPRZ_MODE();
|
||||
PERIODIC_SEND_PPRZ_MODE(DefaultChannel);
|
||||
}
|
||||
}
|
||||
#endif /* GPS && FAILSAFE_DELAY_WITHOUT_GPS */
|
||||
@@ -359,7 +359,7 @@ static void navigation_task( void ) {
|
||||
#endif
|
||||
|
||||
#ifndef PERIOD_NAVIGATION_0 // If not sent periodically (in default 0 mode)
|
||||
SEND_NAVIGATION();
|
||||
SEND_NAVIGATION(DefaultChannel);
|
||||
#endif
|
||||
|
||||
SEND_CAM();
|
||||
@@ -491,7 +491,7 @@ void periodic_task_ap( void ) {
|
||||
estimator_hspeed_mod > MIN_SPEED_FOR_TAKEOFF) {
|
||||
estimator_flight_time = 1;
|
||||
launch = TRUE; /* Not set in non auto launch */
|
||||
DOWNLINK_SEND_TAKEOFF(&cpu_time_sec);
|
||||
DOWNLINK_SEND_TAKEOFF(DefaultChannel, &cpu_time_sec);
|
||||
}
|
||||
|
||||
#ifdef DIGITAL_CAM
|
||||
|
||||
@@ -101,10 +101,10 @@ module Gen_onboard = struct
|
||||
let print_field = fun h (t, name, (_f: format option)) ->
|
||||
match t with
|
||||
Basic _ ->
|
||||
fprintf h "\t DownlinkPut%sByAddr((%s)); \\\n" (Syntax.nameof t) name
|
||||
fprintf h "\t DownlinkPut%sByAddr(_chan, (%s)); \\\n" (Syntax.nameof t) name
|
||||
| Array (t, varname) ->
|
||||
let _s = Syntax.sizeof (Basic t) in
|
||||
fprintf h "\t DownlinkPut%sArray(%s, %s); \\\n" (Syntax.nameof (Basic t)) (Syntax.length_name varname) name
|
||||
fprintf h "\t DownlinkPut%sArray(_chan, %s, %s); \\\n" (Syntax.nameof (Basic t)) (Syntax.length_name varname) name
|
||||
|
||||
let print_parameter h = function
|
||||
(Array _, s, _) -> fprintf h "%s, %s" (Syntax.length_name s) s
|
||||
@@ -124,21 +124,27 @@ module Gen_onboard = struct
|
||||
let size_of_message = fun m -> size_fields m.fields "0"
|
||||
|
||||
let print_downlink_macro = fun h {name=s; fields = fields} ->
|
||||
fprintf h "#define DOWNLINK_SEND_%s(" s;
|
||||
if List.length fields > 0 then begin
|
||||
fprintf h "#define DOWNLINK_SEND_%s(_chan, " s;
|
||||
end else
|
||||
fprintf h "#define DOWNLINK_SEND_%s(_chan " s;
|
||||
print_macro_parameters h fields;
|
||||
fprintf h "){ \\\n";
|
||||
let size = (size_fields fields "0") in
|
||||
fprintf h "\tif (DownlinkCheckFreeSpace(DownlinkSizeOf(%s))) {\\\n" size;
|
||||
fprintf h "\t DownlinkCountBytes(DownlinkSizeOf(%s)); \\\n" size;
|
||||
fprintf h "\t DownlinkStartMessage(\"%s\", DL_%s, %s) \\\n" s s size;
|
||||
fprintf h "\tif (DownlinkCheckFreeSpace(_chan, DownlinkSizeOf(_chan, %s))) {\\\n" size;
|
||||
fprintf h "\t DownlinkCountBytes(_chan, DownlinkSizeOf(_chan, %s)); \\\n" size;
|
||||
fprintf h "\t DownlinkStartMessage(_chan, \"%s\", DL_%s, %s) \\\n" s s size;
|
||||
List.iter (print_field h) fields;
|
||||
fprintf h "\t DownlinkEndMessage() \\\n";
|
||||
fprintf h "\t DownlinkEndMessage(_chan ) \\\n";
|
||||
fprintf h "\t} else \\\n";
|
||||
fprintf h "\t DownlinkOverrun(); \\\n";
|
||||
fprintf h "\t DownlinkOverrun(_chan ); \\\n";
|
||||
fprintf h "}\n\n"
|
||||
|
||||
let print_null_downlink_macro = fun h {name=s; fields = fields} ->
|
||||
fprintf h "#define DOWNLINK_SEND_%s(" s;
|
||||
if List.length fields > 0 then begin
|
||||
fprintf h "#define DOWNLINK_SEND_%s(_chan, " s;
|
||||
end else
|
||||
fprintf h "#define DOWNLINK_SEND_%s(_chan " s;
|
||||
print_macro_parameters h fields;
|
||||
fprintf h ") {}\n"
|
||||
|
||||
|
||||
@@ -50,12 +50,12 @@ let remove_dup = fun l ->
|
||||
loop (List.sort compare l)
|
||||
|
||||
|
||||
let output_modes = fun avr_h process_name modes ->
|
||||
let output_modes = fun avr_h process_name channel_name modes ->
|
||||
(** For each mode in this process *)
|
||||
List.iter
|
||||
(fun mode ->
|
||||
let mode_name = ExtXml.attrib mode "name" in
|
||||
lprintf avr_h "if (telemetry_mode_%s == TELEMETRY_MODE_%s_%s) {\\\n" process_name process_name mode_name;
|
||||
lprintf avr_h "if (telemetry_mode_%s_%s == TELEMETRY_MODE_%s_%s_%s) {\\\n" process_name channel_name process_name channel_name mode_name;
|
||||
right ();
|
||||
|
||||
(** Computes the required modulos *)
|
||||
@@ -94,7 +94,7 @@ let output_modes = fun avr_h process_name modes ->
|
||||
l := (p, !phase) :: !l;
|
||||
i := !i + !freq/10;
|
||||
right ();
|
||||
lprintf avr_h "PERIODIC_SEND_%s();\\\n" message_name;
|
||||
lprintf avr_h "PERIODIC_SEND_%s(%s);\\\n" message_name channel_name;
|
||||
left ();
|
||||
lprintf avr_h "} \\\n"
|
||||
)
|
||||
@@ -125,9 +125,10 @@ let _ =
|
||||
(** For each process *)
|
||||
List.iter
|
||||
(fun process ->
|
||||
let process_name = ExtXml.attrib process "name" in
|
||||
let process_name = ExtXml.attrib process "name" and
|
||||
channel_name = ExtXml.attrib process "channel" in
|
||||
|
||||
fprintf avr_h "\n/* Macros for %s process */\n" process_name;
|
||||
fprintf avr_h "\n/* Macros for %s process channel %s */\n" process_name channel_name;
|
||||
|
||||
let modes = Xml.children process in
|
||||
|
||||
@@ -135,20 +136,20 @@ let _ =
|
||||
(** For each mode of this process *)
|
||||
List.iter (fun mode ->
|
||||
let name = ExtXml.attrib mode "name" in
|
||||
Xml2h.define (sprintf "TELEMETRY_MODE_%s_%s" process_name name) (string_of_int !i);
|
||||
Xml2h.define (sprintf "TELEMETRY_MODE_%s_%s_%s" process_name channel_name name) (string_of_int !i);
|
||||
(* Output the periods of the messages *)
|
||||
List.iter
|
||||
(fun x ->
|
||||
let p = ExtXml.attrib x "period"
|
||||
and n = ExtXml.attrib x "name" in
|
||||
Xml2h.define (sprintf "PERIOD_%s_%d" n !i) (sprintf "(%s)" p))
|
||||
Xml2h.define (sprintf "PERIOD_%s_%s_%d" n channel_name !i) (sprintf "(%s)" p))
|
||||
(Xml.children mode);
|
||||
incr i)
|
||||
modes;
|
||||
|
||||
lprintf avr_h "#define PeriodicSend%s() { /* %dHz */ \\\n" process_name !freq;
|
||||
lprintf avr_h "#define PeriodicSend%s_%s() { /* %dHz */ \\\n" process_name channel_name !freq;
|
||||
right ();
|
||||
output_modes avr_h process_name modes;
|
||||
output_modes avr_h process_name channel_name modes;
|
||||
left ();
|
||||
lprintf avr_h "}\n"
|
||||
)
|
||||
|
||||
@@ -96,7 +96,7 @@ let print_dl_settings = fun settings ->
|
||||
let nb_values = !idx in
|
||||
|
||||
(** Macro to call to downlink current values *)
|
||||
lprintf "#define PeriodicSendDlValue() { \\\n";
|
||||
lprintf "#define PeriodicSendDlValue(_chan) { \\\n";
|
||||
if nb_values > 0 then begin
|
||||
right ();
|
||||
lprintf "static uint8_t i;\\\n";
|
||||
@@ -113,7 +113,7 @@ let print_dl_settings = fun settings ->
|
||||
lprintf "default: var = 0.; break;\\\n";
|
||||
left ();
|
||||
lprintf "}\\\n";
|
||||
lprintf "DOWNLINK_SEND_DL_VALUE(&i, &var);\\\n";
|
||||
lprintf "DOWNLINK_SEND_DL_VALUE(_chan, &i, &var);\\\n";
|
||||
lprintf "i++;\\\n";
|
||||
left ()
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user