LCOV - code coverage report
Current view: top level - src - heartbeat_manager.cpp (source / functions) Coverage Total Hit
Test: ESP-NOW Manager Unified Coverage Lines: 100.0 % 63 63
Test Date: 2026-04-21 02:14:32 Functions: 100.0 % 9 9
Branches: 90.0 % 10 9

             Branch data     Line data    Source code
       1                 :             : #include <cstring>
       2                 :             : 
       3                 :             : #include "esp_log.h"
       4                 :             : 
       5                 :             : #include "heartbeat_manager.hpp"
       6                 :             : 
       7                 :             : static const char* TAG = "HeartbeatMgr";
       8                 :             : 
       9                 :          36 : HeartbeatManager::HeartbeatManager(ITxManager& tx_mgr, IPeerManager& peer_mgr, ITimerHAL& hal_timer)
      10                 :          36 :     : tx_mgr_(tx_mgr)
      11                 :          36 :     , peer_mgr_(peer_mgr)
      12                 :          36 :     , hal_timer_(hal_timer)
      13                 :             : {
      14                 :          36 : }
      15                 :             : 
      16                 :          17 : void HeartbeatManager::init(NodeId id, NodeType type, uint32_t interval_ms)
      17                 :             : {
      18                 :          17 :     my_id_ = id;
      19                 :          17 :     my_type_ = type;
      20                 :          17 :     interval_ms_ = interval_ms;
      21                 :          17 :     is_initialized_ = true;
      22                 :          17 : }
      23                 :             : 
      24                 :           1 : void HeartbeatManager::deinit()
      25                 :             : {
      26                 :           1 :     is_initialized_ = false;
      27                 :           1 : }
      28                 :             : 
      29                 :           1 : void HeartbeatManager::set_interval_ms(uint32_t heartbeat_interval_ms)
      30                 :             : {
      31                 :           1 :     interval_ms_ = heartbeat_interval_ms;
      32                 :           1 : }
      33                 :             : 
      34                 :          14 : void HeartbeatManager::tick(int64_t now_ms)
      35                 :             : {
      36   [ +  +  +  +  :          14 :     if (!is_initialized_ || my_type_ == ReservedTypes::HUB || interval_ms_ <= 0) {
                   +  - ]
      37                 :             :         return;
      38                 :             :     }
      39                 :             : 
      40         [ +  + ]:          11 :     if (now_ms - last_heartbeat_ms_ >= interval_ms_) {
      41                 :           9 :         send_heartbeat();
      42                 :           9 :         last_heartbeat_ms_ = now_ms;
      43                 :             :     }
      44                 :             : }
      45                 :             : 
      46                 :           1 : void HeartbeatManager::handle_response(const DecodedRxPacket& decoded)
      47                 :             : {
      48                 :           1 :     last_rssi_ = decoded.raw.rssi;
      49                 :           1 :     ESP_LOGI(TAG, "Heartbeat response received from Hub (RSSI: %d dBm)", last_rssi_);
      50                 :           1 : }
      51                 :             : 
      52                 :           2 : void HeartbeatManager::handle_request(const DecodedRxPacket& decoded)
      53                 :             : {
      54                 :           2 :     const MessageHeader& header = decoded.header;
      55                 :             : 
      56                 :           2 :     int64_t now_ms = get_time_ms();
      57                 :             : 
      58                 :           2 :     peer_mgr_.update_last_seen(header.sender_node_id, now_ms);
      59                 :           2 :     ESP_LOGI(TAG, "Heartbeat received from Node ID %d.", (int)header.sender_node_id);
      60                 :             : 
      61                 :           2 :     DecodedTxPacket tx_packet{};
      62                 :           2 :     memcpy(tx_packet.dest_mac, decoded.raw.src_mac, 6);
      63                 :             : 
      64                 :           2 :     tx_packet.header.msg_type = MessageType::HEARTBEAT_RESPONSE;
      65                 :           2 :     tx_packet.header.sender_node_id = my_id_;
      66                 :           2 :     tx_packet.header.sender_type = my_type_;
      67                 :           2 :     tx_packet.header.dest_node_id = header.sender_node_id;
      68                 :           2 :     tx_packet.header.timestamp_ms = now_ms;
      69                 :             : 
      70                 :           2 :     HeartbeatResponse resp{};
      71                 :           2 :     resp.server_time_ms = now_ms;
      72                 :             : 
      73                 :             :     // Copy only the payload portion of HeartbeatResponse, skipping the MessageHeader
      74                 :             :     // which is already set separately in tx_packet.header above.
      75                 :             :     // &resp.server_time_ms points to the first field after the header.
      76                 :           2 :     tx_packet.payload_len = sizeof(HeartbeatResponse) - sizeof(MessageHeader);
      77                 :           2 :     memcpy(tx_packet.payload, &resp.server_time_ms, tx_packet.payload_len);
      78                 :             : 
      79                 :           2 :     tx_mgr_.queue_packet(tx_packet);
      80                 :           2 : }
      81                 :             : 
      82                 :          13 : void HeartbeatManager::send_heartbeat()
      83                 :             : {
      84                 :          13 :     DecodedTxPacket tx_packet{};
      85         [ +  + ]:          13 :     if (!peer_mgr_.find_mac(ReservedIds::HUB, tx_packet.dest_mac)) {
      86                 :          12 :         memcpy(tx_packet.dest_mac, BROADCAST_MAC, 6);
      87                 :             :     }
      88                 :             : 
      89                 :          13 :     int64_t now_ms = get_time_ms();
      90                 :             : 
      91                 :          13 :     tx_packet.header.msg_type = MessageType::HEARTBEAT;
      92                 :          13 :     tx_packet.header.sender_node_id = my_id_;
      93                 :          13 :     tx_packet.header.sender_type = my_type_;
      94                 :          13 :     tx_packet.header.dest_node_id = ReservedIds::HUB;
      95                 :          13 :     tx_packet.header.timestamp_ms = now_ms;
      96                 :             : 
      97                 :          13 :     HeartbeatMessage hb{};
      98                 :          13 :     hb.uptime_ms = now_ms;
      99                 :          13 :     hb.rssi = last_rssi_;
     100                 :             :     // battery_mv remains zero until hardware support is added
     101                 :             : 
     102                 :             :     // Copy only the payload portion of HeartbeatMessage, skipping MessageHeader.
     103                 :             :     // &hb.uptime_ms points to the first field after the header.
     104                 :          13 :     tx_packet.payload_len = sizeof(HeartbeatMessage) - sizeof(MessageHeader);
     105                 :          13 :     memcpy(tx_packet.payload, &hb.uptime_ms, tx_packet.payload_len);
     106                 :             : 
     107                 :          13 :     tx_mgr_.queue_packet(tx_packet);
     108                 :          13 : }
     109                 :             : 
     110                 :             : // ==========================================================================================
     111                 :             : // Private methods
     112                 :             : // ==========================================================================================
     113                 :             : 
     114                 :          15 : int64_t HeartbeatManager::get_time_ms() const
     115                 :             : {
     116                 :          15 :     return hal_timer_.get_time_us() / 1000;
     117                 :             : }
        

Generated by: LCOV version 2.0-1