LCOV - code coverage report
Current view: top level - GCS_MAVLink - GCS_Param.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 213 246 86.6 %
Date: 2026-09-09 11:02:08 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :    GCS MAVLink functions related to parameter handling
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "GCS_config.h"
      19             : 
      20             : #if HAL_GCS_ENABLED
      21             : 
      22             : #include <AP_HAL/AP_HAL.h>
      23             : 
      24             : #include "GCS.h"
      25             : #include <AP_Logger/AP_Logger.h>
      26             : #include <AP_BoardConfig/AP_BoardConfig.h>
      27             : 
      28             : extern const AP_HAL::HAL& hal;
      29             : 
      30             : // queue of pending parameter requests and replies
      31             : ObjectBuffer<GCS_MAVLINK::pending_param_request> GCS_MAVLINK::param_requests(20);
      32             : ObjectBuffer<GCS_MAVLINK::pending_param_reply> GCS_MAVLINK::param_replies(5);
      33             : 
      34             : bool GCS_MAVLINK::param_timer_registered;
      35             : 
      36             : /**
      37             :  * @brief Send the next pending parameter, called from deferred message
      38             :  * handling code
      39             :  */
      40             : void
      41     1625060 : GCS_MAVLINK::queued_param_send()
      42             : {
      43             :     // send parameter async replies
      44     1625060 :     uint8_t async_replies_sent_count = send_parameter_async_replies();
      45             : 
      46             :     // now send the streaming parameters (from PARAM_REQUEST_LIST)
      47     1625060 :     if (_queued_parameter == nullptr) {
      48             :         // .... or not....
      49     1617356 :         return;
      50             :     }
      51             : 
      52        7704 :     const uint32_t tnow = AP_HAL::millis();
      53        7704 :     const uint32_t tstart = AP_HAL::micros();
      54             : 
      55             :     // use at most 30% of bandwidth on parameters
      56        7704 :     const uint32_t link_bw = _port->bw_in_bytes_per_second();
      57             : 
      58        7704 :     uint32_t bytes_allowed = link_bw * (tnow - _queued_parameter_send_time_ms) / 3333;
      59        7704 :     const uint16_t size_for_one_param_value_msg = MAVLINK_MSG_ID_PARAM_VALUE_LEN + packet_overhead();
      60        7704 :     if (bytes_allowed < size_for_one_param_value_msg) {
      61          66 :         bytes_allowed = size_for_one_param_value_msg;
      62             :     }
      63        7704 :     if (bytes_allowed > txspace()) {
      64        7588 :         bytes_allowed = txspace();
      65             :     }
      66        7704 :     uint32_t count = bytes_allowed / size_for_one_param_value_msg;
      67             : 
      68             :     // when we don't have flow control we really need to keep the
      69             :     // param download very slow, or it tends to stall
      70        7704 :     if (!have_flow_control() && count > 5) {
      71         679 :         count = 5;
      72             :     }
      73        7704 :     if (async_replies_sent_count >= count) {
      74           6 :         return;
      75             :     }
      76        7698 :     count -= async_replies_sent_count;
      77             : 
      78     1436585 :     while (count && _queued_parameter != nullptr && last_txbuf_is_greater(33)) {
      79             :         char param_name[AP_MAX_NAME_SIZE];
      80     1428887 :         _queued_parameter->copy_name_token(_queued_parameter_token, param_name, sizeof(param_name), true);
      81             : 
      82     1428887 :         mavlink_msg_param_value_send(
      83             :             chan,
      84             :             param_name,
      85     1428887 :             _queued_parameter->cast_to_float(_queued_parameter_type),
      86     1428887 :             mav_param_type(_queued_parameter_type),
      87     1428887 :             _queued_parameter_count,
      88     1428887 :             _queued_parameter_index);
      89             : 
      90     1428887 :         _queued_parameter = AP_Param::next_scalar(&_queued_parameter_token, &_queued_parameter_type);
      91     1428887 :         _queued_parameter_index++;
      92             : 
      93     1428887 :         if (AP_HAL::micros() - tstart > 1000) {
      94             :             // don't use more than 1ms sending blocks of parameters
      95           0 :             break;
      96             :         }
      97     1428887 :         count--;
      98             :     }
      99        7698 :     _queued_parameter_send_time_ms = tnow;
     100             : }
     101             : 
     102             : /*
     103             :   return true if a channel has flow control
     104             :  */
     105        7704 : bool GCS_MAVLINK::have_flow_control(void)
     106             : {
     107        7704 :     if (_port == nullptr) {
     108           0 :         return false;
     109             :     }
     110             : 
     111        7704 :     if (_port->flow_control_enabled()) {
     112        7012 :         return true;
     113             :     }
     114             : 
     115         692 :     if (chan == MAVLINK_COMM_0) {
     116             :         // assume USB console has flow control
     117           0 :         return hal.gpio->usb_connected();
     118             :     }
     119             : 
     120         692 :     return false;
     121             : }
     122             : 
     123             : 
     124             : /*
     125             :   handle a request to change stream rate. Note that copter passes in
     126             :   save==false so we don't want the save to happen when the user connects the
     127             :   ground station.
     128             :  */
     129        2124 : void GCS_MAVLINK::handle_request_data_stream(const mavlink_message_t &msg)
     130             : {
     131             :     mavlink_request_data_stream_t packet;
     132        2124 :     mavlink_msg_request_data_stream_decode(&msg, &packet);
     133             : 
     134        2124 :     int16_t freq = 0;     // packet frequency
     135             : 
     136        2124 :     if (packet.start_stop == 0)
     137           0 :         freq = 0;                     // stop sending
     138        2124 :     else if (packet.start_stop == 1)
     139        2124 :         freq = packet.req_message_rate;                     // start sending
     140             :     else
     141        2122 :         return;
     142             : 
     143             :     // if stream_id is still NUM_STREAMS at the end of this switch
     144             :     // block then either we set stream rates for all streams, or we
     145             :     // were asked to set the streamrate for an unrecognised stream
     146        2124 :     streams stream_id = NUM_STREAMS;
     147        2124 :     switch (packet.req_stream_id) {
     148        2122 :     case MAV_DATA_STREAM_ALL:
     149       23342 :         for (uint8_t i=0; i<NUM_STREAMS; i++) {
     150       21220 :             if (i == STREAM_PARAMS) {
     151             :                 // don't touch parameter streaming rate; it is
     152             :                 // considered "internal".
     153        2122 :                 continue;
     154             :             }
     155       19098 :             if (persist_streamrates()) {
     156        6354 :                 streamRates[i].set_and_save_ifchanged(freq);
     157             :             } else {
     158       12744 :                 streamRates[i].set(freq);
     159             :             }
     160       19098 :             initialise_message_intervals_for_stream((streams)i);
     161             :         }
     162        2122 :         break;
     163           0 :     case MAV_DATA_STREAM_RAW_SENSORS:
     164           0 :         stream_id = STREAM_RAW_SENSORS;
     165           0 :         break;
     166           0 :     case MAV_DATA_STREAM_EXTENDED_STATUS:
     167           0 :         stream_id = STREAM_EXTENDED_STATUS;
     168           0 :         break;
     169           1 :     case MAV_DATA_STREAM_RC_CHANNELS:
     170           1 :         stream_id = STREAM_RC_CHANNELS;
     171           1 :         break;
     172           0 :     case MAV_DATA_STREAM_RAW_CONTROLLER:
     173           0 :         stream_id = STREAM_RAW_CONTROLLER;
     174           0 :         break;
     175           0 :     case MAV_DATA_STREAM_POSITION:
     176           0 :         stream_id = STREAM_POSITION;
     177           0 :         break;
     178           0 :     case MAV_DATA_STREAM_EXTRA1:
     179           0 :         stream_id = STREAM_EXTRA1;
     180           0 :         break;
     181           0 :     case MAV_DATA_STREAM_EXTRA2:
     182           0 :         stream_id = STREAM_EXTRA2;
     183           0 :         break;
     184           1 :     case MAV_DATA_STREAM_EXTRA3:
     185           1 :         stream_id = STREAM_EXTRA3;
     186           1 :         break;
     187             :     }
     188             : 
     189        2124 :     if (stream_id == NUM_STREAMS) {
     190             :         // asked to set rate on unknown stream (or all were set already)
     191        2122 :         return;
     192             :     }
     193             : 
     194           2 :     AP_Int16 *rate = &streamRates[stream_id];
     195             : 
     196           2 :     if (rate != nullptr) {
     197           2 :         if (persist_streamrates()) {
     198           0 :             rate->set_and_save_ifchanged(freq);
     199             :         } else {
     200           2 :             rate->set(freq);
     201             :         }
     202           2 :         initialise_message_intervals_for_stream(stream_id);
     203             :     }
     204             : }
     205             : 
     206         998 : void GCS_MAVLINK::handle_param_request_list(const mavlink_message_t &msg)
     207             : {
     208         998 :     if (!params_ready()) {
     209           0 :         return;
     210             :     }
     211             : 
     212             :     mavlink_param_request_list_t packet;
     213         998 :     mavlink_msg_param_request_list_decode(&msg, &packet);
     214             : 
     215             :     // requesting parameters is a convenient way to get extra information
     216         998 :     send_banner();
     217             : 
     218             :     // Start sending parameters - next call to ::update will kick the first one out
     219         998 :     _queued_parameter = AP_Param::first(&_queued_parameter_token, &_queued_parameter_type);
     220         998 :     _queued_parameter_index = 0;
     221         998 :     _queued_parameter_count = AP_Param::count_parameters();
     222         998 :     _queued_parameter_send_time_ms = AP_HAL::millis(); // avoid initial flooding
     223             : }
     224             : 
     225       27497 : void GCS_MAVLINK::handle_param_request_read(const mavlink_message_t &msg)
     226             : {
     227       27497 :     if (param_requests.space() == 0) {
     228             :         // we can't process this right now, drop it
     229        2200 :         return;
     230             :     }
     231             :     
     232             :     mavlink_param_request_read_t packet;
     233       25297 :     mavlink_msg_param_request_read_decode(&msg, &packet);
     234             : 
     235             :     /*
     236             :       we reserve some space for sending parameters if the client ever
     237             :       fails to get a parameter due to lack of space
     238             :      */
     239       25297 :     uint32_t saved_reserve_param_space_start_ms = reserve_param_space_start_ms;
     240       25297 :     reserve_param_space_start_ms = 0; // bypass packet_overhead_chan reservation checking
     241       25297 :     if (!HAVE_PAYLOAD_SPACE(chan, PARAM_VALUE)) {
     242           0 :         reserve_param_space_start_ms = AP_HAL::millis();
     243             :     } else {
     244       25297 :         reserve_param_space_start_ms = saved_reserve_param_space_start_ms;
     245             :     }
     246             : 
     247             :     struct pending_param_request req;
     248       25297 :     req.src_system_id = msg.sysid;
     249       25297 :     req.src_component_id = msg.compid;
     250       25297 :     req.chan = chan;
     251       25297 :     req.param_index = packet.param_index;
     252       25297 :     memcpy(req.param_name, packet.param_id, MIN(sizeof(packet.param_id), sizeof(req.param_name)));
     253       25297 :     req.param_name[AP_MAX_NAME_SIZE] = 0;
     254             : 
     255             :     // queue it for processing by io timer
     256       25297 :     param_requests.push(req);
     257             : 
     258             :     // speaking of which, we'd best make sure it is running:
     259       25297 :     if (!param_timer_registered) {
     260        2082 :         param_timer_registered = true;
     261        2082 :         hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&GCS_MAVLINK::param_io_timer, void));
     262             :     }
     263             : }
     264             : 
     265       10125 : void GCS_MAVLINK::handle_param_set(const mavlink_message_t &msg)
     266             : {
     267             :     mavlink_param_set_t packet;
     268       10125 :     mavlink_msg_param_set_decode(&msg, &packet);
     269             :     enum ap_var_type var_type;
     270             : 
     271             :     // set parameter
     272             :     AP_Param *vp;
     273             :     char key[AP_MAX_NAME_SIZE+1];
     274       10125 :     strncpy(key, (char *)packet.param_id, AP_MAX_NAME_SIZE);
     275       10125 :     key[AP_MAX_NAME_SIZE] = 0;
     276             : 
     277             :     // find existing param so we can get the old value
     278       10125 :     uint16_t parameter_flags = 0;
     279       10125 :     vp = AP_Param::find(key, &var_type, &parameter_flags);
     280       10125 :     if (vp == nullptr) {
     281           1 :         send_param_error(msg, packet, MAV_PARAM_ERROR_DOES_NOT_EXIST);
     282           9 :         return;
     283             :     }
     284       10124 :     if (isnan(packet.param_value) || isinf(packet.param_value)) {
     285           1 :         send_param_error(msg, packet, MAV_PARAM_ERROR_VALUE_OUT_OF_RANGE);
     286           1 :         return;
     287             :     }
     288             : 
     289       10123 :     float old_value = vp->cast_to_float(var_type);
     290             : 
     291       10123 :     if (!vp->allow_set_via_mavlink(parameter_flags)) {
     292             :         // don't warn the user about this failure if we are dropping
     293             :         // messages here.  This is on the assumption that scripting is
     294             :         // currently responsible for setting parameters and may set
     295             :         // the value instead of us.
     296           7 :         if (gcs().get_allow_param_set()) {
     297           1 :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Param write denied (%s)", key);
     298           1 :             send_param_error(msg, packet, MAV_PARAM_ERROR_PERMISSION_DENIED);
     299             :         }
     300             :         // send the readonly value
     301           7 :         send_parameter_value(key, var_type, old_value);
     302           7 :         return;
     303             :     }
     304             : 
     305             :     // set the value
     306       10116 :     vp->set_float(packet.param_value, var_type);
     307             : 
     308             :     /*
     309             :       we force the save if the value is not equal to the old
     310             :       value. This copes with the use of override values in
     311             :       constructors, such as PID elements. Otherwise a set to the
     312             :       default value which differs from the constructor value doesn't
     313             :       save the change
     314             :      */
     315       10116 :     bool force_save = !is_equal(packet.param_value, old_value);
     316             : 
     317             :     // save the change
     318       10116 :     vp->save(force_save);
     319             : 
     320       10116 :     if (force_save && (parameter_flags & AP_PARAM_FLAG_ENABLE)) {
     321        1301 :         AP_Param::invalidate_count();
     322             :     }
     323             : 
     324             : #if HAL_LOGGING_ENABLED
     325       10116 :     AP_Logger *logger = AP_Logger::get_singleton();
     326       10116 :     if (logger != nullptr) {
     327       10116 :         logger->Write_Parameter(key, vp->cast_to_float(var_type));
     328             :     }
     329             : #endif
     330             : }
     331             : 
     332           7 : void GCS_MAVLINK::send_parameter_value(const char *param_name, ap_var_type param_type, float param_value)
     333             : {
     334           7 :     if (!HAVE_PAYLOAD_SPACE(chan, PARAM_VALUE)) {
     335           0 :         return;
     336             :     }
     337           7 :     mavlink_msg_param_value_send(
     338             :         chan,
     339             :         param_name,
     340             :         param_value,
     341           7 :         mav_param_type(param_type),
     342           7 :         AP_Param::count_parameters(),
     343             :         -1);
     344             : }
     345             : 
     346             : /*
     347             :   send a parameter value message to all active MAVLink connections
     348             :  */
     349       87691 : void GCS::send_parameter_value(const char *param_name, ap_var_type param_type, float param_value)
     350             : {
     351       87691 :     mavlink_param_value_t packet{};
     352       87691 :     const uint8_t to_copy = MIN(ARRAY_SIZE(packet.param_id), strlen(param_name));
     353       87691 :     memcpy(packet.param_id, param_name, to_copy);
     354       87691 :     packet.param_value = param_value;
     355       87691 :     packet.param_type = GCS_MAVLINK::mav_param_type(param_type);
     356       87691 :     packet.param_count = AP_Param::count_parameters();
     357       87691 :     packet.param_index = -1;
     358             : 
     359       87691 :     gcs().send_to_active_channels(MAVLINK_MSG_ID_PARAM_VALUE,
     360             :                                   (const char *)&packet);
     361             : 
     362             : #if HAL_LOGGING_ENABLED
     363             :     // also log to AP_Logger
     364       87691 :     AP_Logger *logger = AP_Logger::get_singleton();
     365       87691 :     if (logger != nullptr) {
     366       87691 :         logger->Write_Parameter(param_name, param_value);
     367             :     }
     368             : #endif
     369       87691 : }
     370             : 
     371             : 
     372             : /*
     373             :   timer callback for async parameter requests
     374             :  */
     375   233768128 : void GCS_MAVLINK::param_io_timer(void)
     376             : {
     377             :     struct pending_param_request req;
     378             : 
     379             :     // this is mostly a no-op, but doing this here means we won't
     380             :     // block the main thread counting parameters (~30ms on PH)
     381   233768128 :     AP_Param::count_parameters();
     382             : 
     383   233768128 :     if (param_replies.space() == 0) {
     384             :         // no room
     385   233742831 :         return;
     386             :     }
     387             :     
     388   233532682 :     if (!param_requests.pop(req)) {
     389             :         // nothing to do
     390   233507385 :         return;
     391             :     }
     392             : 
     393             :     struct pending_param_reply reply;
     394             :     AP_Param *vp;
     395             : 
     396       25297 :     if (req.param_index != -1) {
     397           1 :         AP_Param::ParamToken token {};
     398           1 :         vp = AP_Param::find_by_index(req.param_index, &reply.p_type, &token);
     399           1 :         if (vp != nullptr) {
     400           0 :             vp->copy_name_token(token, reply.param_name, AP_MAX_NAME_SIZE, true);
     401             :         } else {
     402           1 :             memset(reply.param_name, '\0', sizeof(reply.param_name));
     403             :         }
     404             :     } else {
     405       25296 :         strncpy(reply.param_name, req.param_name, AP_MAX_NAME_SIZE+1);
     406       25296 :         vp = AP_Param::find(req.param_name, &reply.p_type);
     407             :     }
     408             : 
     409       25297 :     reply.chan = req.chan;
     410       25297 :     reply.src_system_id = req.src_system_id;
     411       25297 :     reply.src_component_id = req.src_component_id;
     412       25297 :     reply.param_name[AP_MAX_NAME_SIZE] = 0;
     413       25297 :     if (vp != nullptr) {
     414       25291 :         reply.value = vp->cast_to_float(reply.p_type);
     415       25291 :         reply.param_error = MAV_PARAM_ERROR_NO_ERROR;
     416             :     } else {
     417           6 :         reply.value = NaNf;
     418           6 :         reply.param_error = MAV_PARAM_ERROR_DOES_NOT_EXIST;
     419             :     }
     420       25297 :     reply.param_index = req.param_index;
     421       25297 :     reply.count = AP_Param::count_parameters();
     422             : 
     423             :     // queue for transmission
     424       25297 :     param_replies.push(reply);
     425             : }
     426             : 
     427             : /*
     428             :   A variant of send_param_error which sends based off the received mavlink message information
     429             :  */
     430           3 : void GCS_MAVLINK::send_param_error(const mavlink_message_t &msg, const mavlink_param_set_t &param_set, MAV_PARAM_ERROR error)
     431             : {
     432           3 :     if (!HAVE_PAYLOAD_SPACE(chan, PARAM_ERROR)) {
     433           0 :         return;
     434             :     }
     435           3 :     char param_id[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN] {};
     436           3 :     strncpy_noterm(param_id, param_set.param_id, ARRAY_SIZE(param_id));
     437           3 :     mavlink_msg_param_error_send(
     438             :         chan,
     439           3 :         msg.sysid,
     440           3 :         msg.compid,
     441             :         param_id,
     442             :         -1,
     443             :         error
     444             :         );
     445             : }
     446             : 
     447             : /*
     448             :   A variant of send_param_error which is sent based on what the
     449             :   parameter set thread returns
     450             :  */
     451           6 : void GCS_MAVLINK::send_param_error(const GCS_MAVLINK::pending_param_reply &reply, MAV_PARAM_ERROR error)
     452             : {
     453           6 :     if (!HAVE_PAYLOAD_SPACE(chan, PARAM_ERROR)) {
     454           0 :         return;
     455             :     }
     456           6 :     char param_id[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN] {};
     457           6 :     strncpy_noterm(param_id, reply.param_name, ARRAY_SIZE(param_id));
     458           6 :     mavlink_msg_param_error_send(
     459             :         chan,
     460           6 :         reply.src_system_id,
     461           6 :         reply.src_component_id,
     462             :         param_id,
     463           6 :         reply.param_index,
     464             :         error
     465             :         );
     466             : }
     467             : 
     468             : /*
     469             :   send replies to PARAM_REQUEST_READ
     470             :  */
     471     1625060 : uint8_t GCS_MAVLINK::send_parameter_async_replies()
     472             : {
     473     1625060 :     uint8_t async_replies_sent_count = 0;
     474             : 
     475     1650357 :     while (async_replies_sent_count < 5) {
     476             :         struct pending_param_reply reply;
     477     1648500 :         if (!param_replies.peek(reply)) {
     478     1623203 :             return async_replies_sent_count;
     479             :         }
     480             : 
     481             :         uint16_t required_space;
     482       25297 :         if (reply.param_error == MAV_PARAM_ERROR_NO_ERROR) {
     483       25291 :             required_space = PAYLOAD_SIZE(chan, PARAM_VALUE);
     484             :         } else {
     485           6 :             required_space = PAYLOAD_SIZE(chan, PARAM_ERROR);
     486             :         }
     487             : 
     488             :         /*
     489             :           we reserve some space for sending parameters if the client ever
     490             :           fails to get a parameter due to lack of space
     491             :         */
     492       25297 :         uint32_t saved_reserve_param_space_start_ms = reserve_param_space_start_ms;
     493       25297 :         reserve_param_space_start_ms = 0; // bypass packet_overhead_chan reservation checking
     494       25297 :         if (txspace() < required_space) {
     495           0 :             out_of_space_to_send();
     496           0 :             reserve_param_space_start_ms = AP_HAL::millis();
     497           0 :             return async_replies_sent_count;
     498             :         }
     499       25297 :         reserve_param_space_start_ms = saved_reserve_param_space_start_ms;
     500             : 
     501       25297 :         if (reply.param_error == MAV_PARAM_ERROR_NO_ERROR) {
     502       25291 :             mavlink_msg_param_value_send(
     503             :                 reply.chan,
     504             :                 reply.param_name,
     505             :                 reply.value,
     506       25291 :                 mav_param_type(reply.p_type),
     507       25291 :                 reply.count,
     508       25291 :                 reply.param_index);
     509             :         } else {
     510           6 :             send_param_error(reply, reply.param_error);
     511             :         }
     512             : 
     513       25297 :         _queued_parameter_send_time_ms = AP_HAL::millis();
     514       25297 :         async_replies_sent_count++;
     515             : 
     516       25297 :         if (!param_replies.pop()) {
     517             :             // internal error...
     518           0 :             return async_replies_sent_count;
     519             :         }
     520             :     }
     521        1857 :     return async_replies_sent_count;
     522             : }
     523             : 
     524       38620 : void GCS_MAVLINK::handle_common_param_message(const mavlink_message_t &msg)
     525             : {
     526       38620 :     switch (msg.msgid) {
     527         998 :     case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
     528         998 :         handle_param_request_list(msg);
     529         998 :         break;
     530       10125 :     case MAVLINK_MSG_ID_PARAM_SET:
     531       10125 :         handle_param_set(msg);
     532       10125 :         break;
     533       27497 :     case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
     534       27497 :         handle_param_request_read(msg);
     535       27497 :         break;
     536             :     }
     537       38620 : }
     538             : 
     539             : #endif  // HAL_GCS_ENABLED

Generated by: LCOV version 1.14