LCOV - code coverage report
Current view: top level - AP_DroneCAN - AP_DroneCAN_DNA_Server.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 188 240 78.3 %
Date: 2026-09-09 11:02:08 Functions: 21 22 95.5 %

          Line data    Source code
       1             : /*
       2             :  * This file is free software: you can redistribute it and/or modify it
       3             :  * under the terms of the GNU General Public License as published by the
       4             :  * Free Software Foundation, either version 3 of the License, or
       5             :  * (at your option) any later version.
       6             :  *
       7             :  * This file is distributed in the hope that it will be useful, but
       8             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      10             :  * See the GNU General Public License for more details.
      11             :  *
      12             :  * You should have received a copy of the GNU General Public License along
      13             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  *
      15             :  * Author: Siddharth Bharat Purohit
      16             :  */
      17             : 
      18             : #include <AP_HAL/AP_HAL.h>
      19             : 
      20             : #if HAL_ENABLE_DRONECAN_DRIVERS
      21             : 
      22             : #include "AP_DroneCAN_DNA_Server.h"
      23             : #include "AP_DroneCAN.h"
      24             : #include <StorageManager/StorageManager.h>
      25             : #include <AP_Math/AP_Math.h>
      26             : #include <GCS_MAVLink/GCS.h>
      27             : #include <AP_Logger/AP_Logger.h>
      28             : #include <AP_BoardConfig/AP_BoardConfig.h>
      29             : #include <stdio.h>
      30             : extern const AP_HAL::HAL& hal;
      31             : 
      32             : // FORMAT REVISION DREAMS (things to address if the NodeRecord needs to be changed substantially)
      33             : // * have DNA server accept only a 16 byte local UID to avoid overhead from variable sized hash
      34             : // * have a real empty flag for entries and/or use a CRC which is not zero for an input of all zeros
      35             : // * fix FNV-1a hash folding to be to 48 bits (6 bytes) instead of 56
      36             : 
      37             : #define NODERECORD_MAGIC 0xAC01
      38             : #define NODERECORD_MAGIC_LEN 2 // uint16_t
      39             : #define MAX_NODE_ID    125
      40             : #define NODERECORD_LOC(node_id) ((node_id * sizeof(NodeRecord)) + NODERECORD_MAGIC_LEN)
      41             : 
      42             : // database is currently shared by all DNA servers
      43             : AP_DroneCAN_DNA_Server::Database AP_DroneCAN_DNA_Server::db;
      44             : 
      45             : // initialize database (storage accessor is always replaced with the one supplied)
      46           5 : void AP_DroneCAN_DNA_Server::Database::init(StorageAccess *storage_)
      47             : {
      48             :     // storage size must be synced with StorageCANDNA entry in StorageManager.cpp
      49             :     static_assert(NODERECORD_LOC(MAX_NODE_ID+1) <= 1024, "DNA storage too small");
      50             : 
      51             :     // might be called from multiple threads if multiple servers use the same database
      52           5 :     WITH_SEMAPHORE(sem);
      53             : 
      54           5 :     storage = storage_; // use supplied accessor
      55             : 
      56             :     // validate magic number
      57           5 :     uint16_t magic = storage->read_uint16(0);
      58           5 :     if (magic != NODERECORD_MAGIC) {
      59           4 :         reset(); // resetting the database will put the magic back
      60             :     }
      61             : 
      62             :     // check and note each possible node ID's registration's presence
      63         630 :     for (uint8_t i = 1; i <= MAX_NODE_ID; i++) {
      64         625 :         if (check_registration(i)) {
      65           2 :             node_registered.set(i);
      66             :         }
      67             :     }
      68           5 : }
      69             : 
      70             : // remove all registrations from the database
      71           4 : void AP_DroneCAN_DNA_Server::Database::reset()
      72             : {
      73           4 :     WITH_SEMAPHORE(sem);
      74             : 
      75             :     NodeRecord record;
      76           4 :     memset(&record, 0, sizeof(record));
      77           4 :     node_registered.clearall();
      78             : 
      79             :     // all-zero record means no registration
      80             :     // ensure node ID 0 is cleared even if we can't use it so we know the state
      81         508 :     for (uint8_t i = 0; i <= MAX_NODE_ID; i++) {
      82         504 :         write_record(record, i);
      83             :     }
      84             : 
      85             :     // mark the magic at the start to indicate a valid (and reset) database
      86           4 :     storage->write_uint16(0, NODERECORD_MAGIC);
      87           4 : }
      88             : 
      89             : // handle initializing the server with its own node ID and unique ID
      90           5 : void AP_DroneCAN_DNA_Server::Database::init_server(uint8_t own_node_id, const uint8_t own_unique_id[], uint8_t own_unique_id_len)
      91             : {
      92           5 :     WITH_SEMAPHORE(sem);
      93             : 
      94             :     // register server node ID and unique ID if not correctly registered. note
      95             :     // that ardupilot mixes the node ID into the unique ID so changing the node
      96             :     // ID will "leak" the old node ID
      97           5 :     if (own_node_id != find_node_id(own_unique_id, own_unique_id_len)) {
      98           4 :         register_uid(own_node_id, own_unique_id, own_unique_id_len);
      99             :     }
     100           5 : }
     101             : 
     102             : // handle processing the node info message. returns true if from a duplicate node
     103           9 : bool AP_DroneCAN_DNA_Server::Database::handle_node_info(uint8_t source_node_id, const uint8_t unique_id[])
     104             : {
     105           9 :     WITH_SEMAPHORE(sem);
     106             : 
     107           9 :     if (is_registered(source_node_id)) {
     108             :         // this device's node ID is associated with a different unique ID
     109           9 :         if (source_node_id != find_node_id(unique_id, 16)) {
     110           0 :             return true; // so raise as duplicate
     111             :         }
     112             :     } else {
     113           0 :         register_uid(source_node_id, unique_id, 16); // we don't know about this node ID, let's register it
     114             :     }
     115           9 :     return false; // not a duplicate
     116           9 : }
     117             : 
     118             : // handle the allocation message. returns the allocated node ID, or 0 if allocation failed
     119           1 : uint8_t AP_DroneCAN_DNA_Server::Database::handle_allocation(const uint8_t unique_id[])
     120             : {
     121           1 :     WITH_SEMAPHORE(sem);
     122             : 
     123           1 :     uint8_t resp_node_id = find_node_id(unique_id, 16);
     124           1 :     if (resp_node_id == 0) {
     125             :         // find free node ID, starting at the max as prescribed by the standard
     126           1 :         resp_node_id = MAX_NODE_ID;
     127           1 :         while (resp_node_id > 0) {
     128           1 :             if (!node_registered.get(resp_node_id)) {
     129           1 :                 break;
     130             :             }
     131           0 :             resp_node_id--;
     132             :         }
     133             : 
     134           1 :         if (resp_node_id != 0) {
     135           1 :             create_registration(resp_node_id, unique_id, 16);
     136             :         }
     137             :     }
     138           2 :     return resp_node_id; // will be 0 if not found and not created
     139           1 : }
     140             : 
     141             : // retrieve node ID that matches the given unique ID. returns 0 if not found
     142          19 : uint8_t AP_DroneCAN_DNA_Server::Database::find_node_id(const uint8_t unique_id[], uint8_t size)
     143             : {
     144             :     NodeRecord record, cmp_record;
     145          19 :     compute_uid_hash(cmp_record, unique_id, size);
     146             : 
     147        1259 :     for (int i = MAX_NODE_ID; i > 0; i--) {
     148        1250 :         if (node_registered.get(i)) {
     149          12 :             read_record(record, i);
     150          12 :             if (memcmp(record.uid_hash, cmp_record.uid_hash, sizeof(NodeRecord::uid_hash)) == 0) {
     151          10 :                 return i; // node ID found
     152             :             }
     153             :         }
     154             :     }
     155           9 :     return 0; // not found
     156             : }
     157             : 
     158             : // fill the given record with the hash of the given unique ID
     159          24 : void AP_DroneCAN_DNA_Server::Database::compute_uid_hash(NodeRecord &record, const uint8_t unique_id[], uint8_t size) const
     160             : {
     161          24 :     uint64_t hash = FNV_1_OFFSET_BASIS_64;
     162          24 :     hash_fnv_1a(size, unique_id, &hash);
     163             : 
     164             :     // xor-folding per http://www.isthe.com/chongo/tech/comp/fnv/
     165          24 :     hash = (hash>>56) ^ (hash&(((uint64_t)1<<56)-1)); // 56 should be 48 since we use 6 bytes
     166             : 
     167             :     // write it to ret
     168         168 :     for (uint8_t i=0; i<6; i++) {
     169         144 :         record.uid_hash[i] = (hash >> (8*i)) & 0xff;
     170             :     }
     171          24 : }
     172             : 
     173             : // register a given unique ID to a given node ID, deleting any existing registration for the unique ID
     174           4 : void AP_DroneCAN_DNA_Server::Database::register_uid(uint8_t node_id, const uint8_t unique_id[], uint8_t size)
     175             : {
     176           4 :     uint8_t prev_node_id = find_node_id(unique_id, size); // have we registered this unique ID under a different node ID?
     177           4 :     if (prev_node_id != 0) {
     178           0 :         delete_registration(prev_node_id); // yes, delete old node ID's registration
     179             :     }
     180             :     // overwrite an existing registration with this node ID, if any
     181           4 :     create_registration(node_id, unique_id, size);
     182           4 : }
     183             : 
     184             : // create the registration for the given node ID and set its record's unique ID
     185           5 : void AP_DroneCAN_DNA_Server::Database::create_registration(uint8_t node_id, const uint8_t unique_id[], uint8_t size)
     186             : {
     187             :     NodeRecord record;
     188           5 :     compute_uid_hash(record, unique_id, size);
     189             :     // compute and store CRC of the record's data to validate it
     190           5 :     record.crc = crc_crc8(record.uid_hash, sizeof(record.uid_hash));
     191             : 
     192           5 :     write_record(record, node_id);
     193             : 
     194           5 :     node_registered.set(node_id);
     195           5 : }
     196             : 
     197             : // delete the given node ID's registration
     198           0 : void AP_DroneCAN_DNA_Server::Database::delete_registration(uint8_t node_id)
     199             : {
     200           0 :     if (node_id > MAX_NODE_ID) {
     201           0 :         return;
     202             :     }
     203             : 
     204             :     NodeRecord record;
     205             : 
     206             :     // all-zero record means no registration
     207           0 :     memset(&record, 0, sizeof(record));
     208           0 :     write_record(record, node_id);
     209           0 :     node_registered.clear(node_id);
     210             : }
     211             : 
     212             : // return true if the given node ID has a registration
     213         625 : bool AP_DroneCAN_DNA_Server::Database::check_registration(uint8_t node_id)
     214             : {
     215             :     NodeRecord record;
     216         625 :     read_record(record, node_id);
     217             : 
     218         625 :     uint8_t empty_uid[sizeof(NodeRecord::uid_hash)] {};
     219         625 :     uint8_t crc = crc_crc8(record.uid_hash, sizeof(record.uid_hash));
     220         625 :     if (crc == record.crc && memcmp(&record.uid_hash[0], &empty_uid[0], sizeof(empty_uid)) != 0) {
     221           2 :         return true; // CRC matches and UID hash is not all zero
     222             :     }
     223         623 :     return false;
     224             : }
     225             : 
     226             : // read the given node ID's registration's record
     227         637 : void AP_DroneCAN_DNA_Server::Database::read_record(NodeRecord &record, uint8_t node_id)
     228             : {
     229         637 :     if (node_id > MAX_NODE_ID) {
     230           0 :         return;
     231             :     }
     232             : 
     233         637 :     storage->read_block(&record, NODERECORD_LOC(node_id), sizeof(NodeRecord));
     234             : }
     235             : 
     236             : // write the given node ID's registration's record
     237         509 : void AP_DroneCAN_DNA_Server::Database::write_record(const NodeRecord &record, uint8_t node_id)
     238             : {
     239         509 :     if (node_id > MAX_NODE_ID) {
     240           0 :         return;
     241             :     }
     242             : 
     243         509 :     storage->write_block(NODERECORD_LOC(node_id), &record, sizeof(NodeRecord));
     244             : }
     245             : 
     246             : 
     247           5 : AP_DroneCAN_DNA_Server::AP_DroneCAN_DNA_Server(AP_DroneCAN &ap_dronecan, CanardInterface &canard_iface, uint8_t driver_index) :
     248           5 :     storage(StorageManager::StorageCANDNA),
     249           5 :     _ap_dronecan(ap_dronecan),
     250           5 :     _canard_iface(canard_iface),
     251           5 :     allocation_sub(allocation_cb, driver_index),
     252           5 :     node_status_sub(node_status_cb, driver_index),
     253          15 :     node_info_client(_canard_iface, node_info_cb) {}
     254             : 
     255             : /* Initialises Publishers for respective UAVCAN Instance
     256             : Also resets the Server Record in case there is a mismatch
     257             : between specified node id and unique id against the existing
     258             : Server Record. */
     259           5 : bool AP_DroneCAN_DNA_Server::init(uint8_t own_unique_id[], uint8_t own_unique_id_len, uint8_t node_id)
     260             : {
     261             :     //Read the details from AP_DroneCAN
     262           5 :     server_state = HEALTHY;
     263             : 
     264           5 :     db.init(&storage); // initialize the database with our accessor
     265             : 
     266           5 :     if (_ap_dronecan.check_and_reset_option(AP_DroneCAN::Options::DNA_CLEAR_DATABASE)) {
     267           0 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "UC DNA database reset");
     268           0 :         db.reset();
     269             :     }
     270             : 
     271           5 :     db.init_server(node_id, own_unique_id, own_unique_id_len);
     272             : 
     273             :     /* Also add to seen node id this is to verify
     274             :     if any duplicates are on the bus carrying our Node ID */
     275           5 :     node_seen.set(node_id);
     276           5 :     node_verified.set(node_id);
     277           5 :     node_healthy.set(node_id);
     278           5 :     self_node_id = node_id;
     279           5 :     return true;
     280             : }
     281             : 
     282             : /* Run through the list of seen node ids for verification no more
     283             : than once per 5 second. We continually verify the nodes in our 
     284             : seen list, So that we can raise issue if there are duplicates
     285             : on the bus. */
     286       21794 : void AP_DroneCAN_DNA_Server::verify_nodes()
     287             : {
     288       21794 :     uint32_t now = AP_HAL::millis();
     289       21794 :     if ((now - last_verification_request) < 5000) {
     290       21694 :         return;
     291             :     }
     292             : 
     293             : #if HAL_LOGGING_ENABLED
     294         100 :     uint8_t log_count = AP::logger().get_log_start_count();
     295         100 :     if (log_count != last_logging_count) {
     296           3 :         last_logging_count = log_count;
     297           3 :         node_logged.clearall();
     298             :     }
     299             : #endif
     300             : 
     301             :     //Check if we got acknowledgement from previous request
     302             :     //except for requests using our own node_id
     303         100 :     if (curr_verifying_node == self_node_id) {
     304           0 :         nodeInfo_resp_rcvd = true;
     305             :     }
     306             : 
     307         100 :     if (!nodeInfo_resp_rcvd) {
     308             :         /* Also notify GCS about this
     309             :         Reason for this could be either the node was disconnected
     310             :         Or a node with conflicting ID appeared and is sending response
     311             :         at the same time. */
     312          93 :         node_verified.clear(curr_verifying_node);
     313             :     }
     314             : 
     315         100 :     last_verification_request = now;
     316             :     //Find the next registered Node ID to be verified.
     317       12691 :     for (uint8_t i = 0; i <= MAX_NODE_ID; i++) {
     318       12599 :         curr_verifying_node = (curr_verifying_node + 1) % (MAX_NODE_ID + 1);
     319       12599 :         if ((curr_verifying_node == self_node_id) || (curr_verifying_node == 0)) {
     320         199 :             continue;
     321             :         }
     322       12400 :         if (node_seen.get(curr_verifying_node)) {
     323           8 :             break;
     324             :         }
     325             :     }
     326         100 :     if (db.is_registered(curr_verifying_node)) {
     327             :         uavcan_protocol_GetNodeInfoRequest request;
     328           8 :         node_info_client.request(curr_verifying_node, request);
     329           8 :         nodeInfo_resp_rcvd = false;
     330             :     }
     331             : }
     332             : 
     333             : #if HAL_LOGGING_ENABLED
     334             : // Find the node status item for the given node id, will add new item if no existing item can be found
     335          52 : AP_DroneCAN_DNA_Server::node_status_log_data * AP_DroneCAN_DNA_Server::find_node_status_item(const uint8_t source_node_id)
     336             : {
     337             :     // Search exiting items
     338          52 :     for (auto *node = node_status_list; node != nullptr; node = node->next) {
     339          50 :         if (node->id == source_node_id) {
     340             :             // Return match
     341          50 :             return node;
     342             :         }
     343             :     }
     344             : 
     345             :     // Add new item
     346           2 :     auto *node = NEW_NOTHROW node_status_log_data(source_node_id);
     347           2 :     if (node == nullptr) {
     348           0 :         return nullptr;
     349             :     }
     350             : 
     351             :     // link into the list
     352           2 :     node->next = node_status_list;
     353           2 :     node_status_list = node;
     354             : 
     355             :     // Return item
     356           2 :     return node;
     357             : }
     358             : 
     359             : // Update node status linked list and log
     360          52 : void AP_DroneCAN_DNA_Server::update_node_status(const uint8_t source_node_id, const uavcan_protocol_NodeStatus& msg)
     361             : {
     362          52 :     AP_DroneCAN_DNA_Server::node_status_log_data *node = find_node_status_item(source_node_id);
     363          52 :     if (node == nullptr) {
     364           0 :         return;
     365             :     }
     366             : 
     367             :     // Rate limit logs to no more than 1.1Hz. AP_Periph sends at 1Hz, the head room is to
     368             :     // make sure that timing jitter does not result in skipping every other message
     369          52 :     const uint32_t now_ms = AP_HAL::millis();
     370          52 :     if ((now_ms - node->last_log_ms) >= 900) {
     371          42 :         node->last_log_ms = now_ms;
     372             : 
     373             :         // @LoggerMessage: CANH
     374             :         // @Description: DroneCAN heartbeat status
     375             :         // @Field: TimeUS: Time since system startup
     376             :         // @Field: Driver: Driver index
     377             :         // @Field: NodeId: Node ID
     378             :         // @Field: UpTime: Time since node startup
     379             :         // @Field: Health: 0:Ok, 1:Warning, 2:Error, 3:Critical
     380             :         // @Field: Mode: 0:Operational, 1:Initialization, 2:Maintenance, 3:Software update, 7:Offline
     381             :         // @Field: SubMode: Expected to be 0
     382             :         // @Field: VendorCode: vendor specific code. In AP_Periph this is available memory in bytes.
     383             : 
     384          84 :         AP::logger().WriteStreaming("CANH",
     385             :                                     "TimeUS,Driver,NodeId,UpTime,Health,Mode,SubMode,VendorCode",
     386             :                                     "s-#s----",
     387             :                                     "F-------",
     388             :                                     "QBBIBBBH",
     389             :                                     AP_HAL::micros64(),
     390          42 :                                     _ap_dronecan._driver_index,
     391             :                                     source_node_id,
     392          42 :                                     msg.uptime_sec,
     393          42 :                                     msg.health,
     394          42 :                                     msg.mode,
     395          42 :                                     msg.sub_mode,
     396          42 :                                     msg.vendor_specific_status_code);
     397             :     }
     398             : 
     399             :     // If uptime goes backwards re-trigger node info log
     400          52 :     if (node->last_uptime_sec > msg.uptime_sec) {
     401           0 :         node_logged.clear(source_node_id);
     402             :     }
     403          52 :     node->last_uptime_sec = msg.uptime_sec;
     404             : }
     405             : #endif // HAL_LOGGING_ENABLED
     406             : 
     407             : /* Handles Node Status Message, adds to the Seen Node list
     408             : Also starts the Service call for Node Info to complete the
     409             : Verification process. */
     410          43 : void AP_DroneCAN_DNA_Server::handleNodeStatus(const CanardRxTransfer& transfer, const uavcan_protocol_NodeStatus& msg)
     411             : {
     412          43 :     if (transfer.source_node_id > MAX_NODE_ID || transfer.source_node_id == 0) {
     413           0 :         return;
     414             :     }
     415         129 :     if ((msg.health != UAVCAN_PROTOCOL_NODESTATUS_HEALTH_OK ||
     416          43 :         msg.mode != UAVCAN_PROTOCOL_NODESTATUS_MODE_OPERATIONAL) &&
     417           0 :         !_ap_dronecan.option_is_set(AP_DroneCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) {
     418             :         //if node is not healthy or operational, clear resp health mask, and set fault_node_id
     419           0 :         fault_node_id = transfer.source_node_id;
     420           0 :         server_state = NODE_STATUS_UNHEALTHY;
     421           0 :         node_healthy.clear(transfer.source_node_id);
     422             :     } else {
     423          43 :         node_healthy.set(transfer.source_node_id);
     424          43 :         if (node_healthy == node_verified) {
     425          41 :             server_state = HEALTHY;
     426             :         }
     427             :     }
     428          43 :     if (!node_verified.get(transfer.source_node_id)) {
     429             :         //immediately begin verification of the node_id
     430             :         uavcan_protocol_GetNodeInfoRequest request;
     431           2 :         node_info_client.request(transfer.source_node_id, request);
     432             :     }
     433             :     //Add node to seen list if not seen before
     434          43 :     node_seen.set(transfer.source_node_id);
     435             : 
     436             : #if HAL_LOGGING_ENABLED
     437             :     // Update status data for logging
     438          43 :     update_node_status(transfer.source_node_id, msg);
     439             : #endif // HAL_LOGGING_ENABLED
     440             : }
     441             : 
     442             : /* Node Info message handler
     443             : Handle responses from GetNodeInfo Request. We verify the node info
     444             : against our records. Marks Verification mask if already recorded,
     445             : Or register if the node id is available and not recorded for the
     446             : received Unique ID */
     447           9 : void AP_DroneCAN_DNA_Server::handleNodeInfo(const CanardRxTransfer& transfer, const uavcan_protocol_GetNodeInfoResponse& rsp)
     448             : {
     449           9 :     if (transfer.source_node_id > MAX_NODE_ID || transfer.source_node_id == 0) {
     450           0 :         return;
     451             :     }
     452             :     /*
     453             :       if we haven't logged this node then log it now
     454             :      */
     455             : #if HAL_LOGGING_ENABLED
     456           9 :     if (!node_logged.get(transfer.source_node_id) && AP::logger().logging_started()) {
     457           0 :         node_logged.set(transfer.source_node_id);
     458             :         uint64_t uid[2];
     459           0 :         memcpy(uid, rsp.hardware_version.unique_id, sizeof(rsp.hardware_version.unique_id));
     460             :         // @LoggerMessage: CAND
     461             :         // @Description: Info from GetNodeInfo request
     462             :         // @Field: TimeUS: Time since system startup
     463             :         // @Field: Driver: Driver index
     464             :         // @Field: NodeId: Node ID
     465             :         // @Field: UID1: Hardware ID, part 1
     466             :         // @Field: UID2: Hardware ID, part 2
     467             :         // @Field: Name: Name string
     468             :         // @Field: Major: major revision id
     469             :         // @Field: Minor: minor revision id
     470             :         // @Field: Version: AP_Periph git hash
     471           0 :         AP::logger().Write("CAND", "TimeUS,Driver,NodeId,UID1,UID2,Name,Major,Minor,Version",
     472             :                            "s-#------", "F--------", "QBBQQZBBI",
     473             :                            AP_HAL::micros64(),
     474           0 :                            _ap_dronecan.get_driver_index(),
     475           0 :                            transfer.source_node_id,
     476             :                            uid[0], uid[1],
     477           0 :                            rsp.name.data,
     478           0 :                            rsp.software_version.major,
     479           0 :                            rsp.software_version.minor,
     480           0 :                            rsp.software_version.vcs_commit);
     481             :     }
     482             : 
     483             :     // Update status data for logging
     484           9 :     update_node_status(transfer.source_node_id, rsp.status);
     485             : #endif // HAL_LOGGING_ENABLED
     486             : 
     487           9 :     bool duplicate = db.handle_node_info(transfer.source_node_id, rsp.hardware_version.unique_id);
     488           9 :     if (duplicate) {
     489           0 :         if (!_ap_dronecan.option_is_set(AP_DroneCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) {
     490             :             /* This is a device with node_id already registered
     491             :             for another device */
     492           0 :             server_state = DUPLICATE_NODES;
     493           0 :             fault_node_id = transfer.source_node_id;
     494           0 :             memcpy(fault_node_name, rsp.name.data, sizeof(fault_node_name));
     495             :         }
     496             :     } else {
     497             :         //Verify as well
     498           9 :         node_verified.set(transfer.source_node_id);
     499           9 :         if (transfer.source_node_id == curr_verifying_node) {
     500           7 :             nodeInfo_resp_rcvd = true;
     501             :         }
     502             :     }
     503             : }
     504             : 
     505             : // process node ID allocation messages for DNA
     506          10 : void AP_DroneCAN_DNA_Server::handle_allocation(const CanardRxTransfer& transfer, const uavcan_protocol_dynamic_node_id_Allocation& msg)
     507             : {
     508          10 :     if (transfer.source_node_id != 0) {
     509           3 :         return; // ignore allocation messages that are not DNA requests
     510             :     }
     511          10 :     uint32_t now = AP_HAL::millis();
     512             : 
     513          10 :     if ((now - last_alloc_msg_ms) > UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_FOLLOWUP_TIMEOUT_MS) {
     514           7 :         rcvd_unique_id_offset = 0; // reset state, timed out
     515             :     }
     516             : 
     517          10 :     if (msg.first_part_of_unique_id) {
     518             :         // nodes waiting to send a follow up will instead send a first part
     519             :         // again if they see another allocation message. therefore we should
     520             :         // always reset and process a received first part, since any node we
     521             :         // were allocating will never send its follow up and we'd just time out
     522           5 :         rcvd_unique_id_offset = 0;
     523           5 :     } else if (rcvd_unique_id_offset == 0) {
     524           3 :         return; // not first part but we are expecting one, ignore
     525             :     }
     526             : 
     527           7 :     last_alloc_msg_ms = now;
     528           7 :     if ((rcvd_unique_id_offset + msg.unique_id.len) > sizeof(rcvd_unique_id)) {
     529           0 :         rcvd_unique_id_offset = 0; // reset state, request contains an over-long ID
     530           0 :         return;
     531             :     }
     532             : 
     533             :     // save the new portion of the unique ID
     534           7 :     memcpy(&rcvd_unique_id[rcvd_unique_id_offset], msg.unique_id.data, msg.unique_id.len);
     535           7 :     rcvd_unique_id_offset += msg.unique_id.len;
     536             : 
     537             :     // respond with the message containing the received unique ID so far, or
     538             :     // with the node ID if we successfully allocated one
     539           7 :     uavcan_protocol_dynamic_node_id_Allocation rsp {};
     540           7 :     memcpy(rsp.unique_id.data, rcvd_unique_id, rcvd_unique_id_offset);
     541           7 :     rsp.unique_id.len = rcvd_unique_id_offset;
     542             : 
     543           7 :     if (rcvd_unique_id_offset == sizeof(rcvd_unique_id)) { // full unique ID received, allocate it!
     544             :         // we ignore the preferred node ID as it seems nobody uses the feature
     545             :         // and we couldn't guarantee it anyway. we will always remember and
     546             :         // re-assign node IDs consistently, so the node could send a status
     547             :         // with a particular ID once then switch back to no preference for DNA
     548           1 :         rsp.node_id = db.handle_allocation(rcvd_unique_id);
     549           1 :         rcvd_unique_id_offset = 0; // reset state for next allocation
     550           1 :         if (rsp.node_id == 0) { // allocation failed
     551           0 :             GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "DroneCAN DNA allocation failed; database full");
     552             :             // don't send reply with a failed ID in case the allocatee does
     553             :             // silly things, though it is technically legal. the allocatee will
     554             :             // then time out and try again (though we still won't have an ID!)
     555           0 :             return;
     556             :         }
     557             :     }
     558             : 
     559           7 :     allocation_pub.broadcast(rsp, false); // never publish allocation message with CAN FD
     560             : }
     561             : 
     562             : //report the server state, along with failure message if any
     563         200 : bool AP_DroneCAN_DNA_Server::prearm_check(char* fail_msg, uint8_t fail_msg_len) const
     564             : {
     565         200 :     switch (server_state) {
     566         200 :     case HEALTHY:
     567         200 :         return true;
     568           0 :     case DUPLICATE_NODES: {
     569           0 :         if (_ap_dronecan.option_is_set(AP_DroneCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) {
     570             :             // ignore error
     571           0 :             return true;
     572             :         }
     573           0 :         snprintf(fail_msg, fail_msg_len, "Duplicate Node %s../%d!", fault_node_name, fault_node_id);
     574           0 :         return false;
     575             :     }
     576           0 :     case NODE_STATUS_UNHEALTHY: {
     577           0 :         if (_ap_dronecan.option_is_set(AP_DroneCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) {
     578             :             // ignore error
     579           0 :             return true;
     580             :         }
     581           0 :         snprintf(fail_msg, fail_msg_len, "Node %d unhealthy!", fault_node_id);
     582           0 :         return false;
     583             :     }
     584             :     }
     585             :     // should never get; compiler should enforce all server_states are covered
     586           0 :     return false;
     587             : }
     588             : 
     589             : #endif // HAL_ENABLE_DRONECAN_DRIVERS

Generated by: LCOV version 1.14