LivoxProto1: Print when Lidar isn't ready for work
This commit is contained in:
@@ -981,9 +981,76 @@ static void discardHeartbeatAck(
|
|||||||
const struct sockaddr_in& senderAddr
|
const struct sockaddr_in& senderAddr
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
(void)data;
|
|
||||||
(void)bytesReceived;
|
|
||||||
(void)senderAddr;
|
(void)senderAddr;
|
||||||
|
|
||||||
|
// Check if we have enough data for a HeartbeatACK message
|
||||||
|
if (bytesReceived
|
||||||
|
< static_cast<ssize_t>(sizeof(livoxProto1::comms::HeartbeatACK)))
|
||||||
|
{
|
||||||
|
std::cout << __func__ << ": Received heartbeat ACK with insufficient "
|
||||||
|
"data (" << bytesReceived << " bytes, expected "
|
||||||
|
<< sizeof(livoxProto1::comms::HeartbeatACK) << ")" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directly use a non-const reference to HeartbeatACK structure
|
||||||
|
livoxProto1::comms::HeartbeatACK& ack =
|
||||||
|
*reinterpret_cast<livoxProto1::comms::HeartbeatACK*>(
|
||||||
|
const_cast<uint8_t*>(data));
|
||||||
|
|
||||||
|
ack.swapContentsToHostEndianness();
|
||||||
|
|
||||||
|
if (!ack.validateCrc32())
|
||||||
|
{
|
||||||
|
std::cerr << __func__ << ": Discarded heartbeat ACK - CRC32 validation "
|
||||||
|
"failed" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ack.header.validateCrc16())
|
||||||
|
{
|
||||||
|
std::cerr << __func__ << ": Discarded heartbeat ACK - CRC16 validation "
|
||||||
|
"failed" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ack.sanityCheck())
|
||||||
|
{
|
||||||
|
std::cerr << __func__ << ": Discarded heartbeat ACK - sanity check "
|
||||||
|
"failed" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ack.work_state == 0x01) { return; }
|
||||||
|
|
||||||
|
// Print work_state with human-readable description
|
||||||
|
std::string workStateStr;
|
||||||
|
switch (ack.work_state)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
workStateStr = "Initializing";
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
workStateStr = "Normal";
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
workStateStr = "Power-Saving";
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
workStateStr = "Standby";
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
workStateStr = "Error";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
workStateStr = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << __func__ << ": Lidar not ready for operation: work_state: 0x"
|
||||||
|
<< std::hex << static_cast<int>(ack.work_state) << std::dec
|
||||||
|
<< " (" << workStateStr << "), ack_msg: 0x"
|
||||||
|
<< std::hex << ack.ack_msg << std::dec << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::startHeartbeat()
|
void Device::startHeartbeat()
|
||||||
|
|||||||
@@ -800,5 +800,43 @@ bool SamplingResponse::validateCrc32() const
|
|||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeartbeatACK methods
|
||||||
|
void HeartbeatACK::swapContentsToHostEndianness()
|
||||||
|
{
|
||||||
|
if (endian::isLittleEndian()) { return; }
|
||||||
|
// Only swap content fields, not CRC fields
|
||||||
|
header.swapToHostEndianness();
|
||||||
|
command.swapToHostEndianness();
|
||||||
|
ack_msg = __builtin_bswap32(ack_msg);
|
||||||
|
// Note: footer.swapToHostEndianness() swaps CRC, so we skip it here
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartbeatACK::sanityCheck() const
|
||||||
|
{
|
||||||
|
return header.sanityCheck() &&
|
||||||
|
command.sanityCheck() &&
|
||||||
|
(command.cmd_set == 0x00) && (command.cmd_id == 0x03) &&
|
||||||
|
footer.sanityCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartbeatACK::validateCrc32() const
|
||||||
|
{
|
||||||
|
// Calculate CRC32 for the entire message excluding the footer.crc_32 field
|
||||||
|
const uint8_t* messageData = reinterpret_cast<const uint8_t*>(this);
|
||||||
|
size_t messageSize = sizeof(HeartbeatACK) - sizeof(footer.crc_32);
|
||||||
|
uint32_t calculatedCrc = comms::calculateCrc32(messageData, messageSize);
|
||||||
|
|
||||||
|
// Compare with the CRC in the footer
|
||||||
|
bool isValid = (calculatedCrc == footer.crc_32);
|
||||||
|
|
||||||
|
// Debug output only if validation fails
|
||||||
|
if (!isValid) {
|
||||||
|
std::cout << "HeartbeatACK CRC32 Debug: calculated=0x" << std::hex << calculatedCrc
|
||||||
|
<< ", received=0x" << footer.crc_32 << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace comms
|
} // namespace comms
|
||||||
} // namespace livoxProto1
|
} // namespace livoxProto1
|
||||||
|
|||||||
@@ -280,6 +280,25 @@ struct SamplingResponse
|
|||||||
bool validateCrc32() const;
|
bool validateCrc32() const;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* Complete heartbeat ACK response frame from Livox devices.
|
||||||
|
* This is the complete wire format including header, command fields, data, and footer.
|
||||||
|
*/
|
||||||
|
struct HeartbeatACK
|
||||||
|
{
|
||||||
|
Header header; // 0-8: Protocol frame header
|
||||||
|
Command command; // 9-10: Command identification
|
||||||
|
uint8_t ret_code; // 11: Return Code (0x00 = Success, 0x01 = Fail)
|
||||||
|
uint8_t work_state; // 12: LiDAR/Hub State (0x00: Initializing, 0x01: Normal, 0x02: Power-Saving, 0x03: Standby, 0x04: Error)
|
||||||
|
uint8_t feature_msg; // 13: LiDAR Feature Message (Bit0: Rain/Fog Suppression Switch)
|
||||||
|
uint32_t ack_msg; // 14-17: ACK Message (Initialization Progress or Status Code)
|
||||||
|
Footer footer; // 18-21: Protocol frame footer
|
||||||
|
|
||||||
|
void swapContentsToHostEndianness();
|
||||||
|
bool sanityCheck() const;
|
||||||
|
bool validateCrc32() const;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
} // namespace comms
|
} // namespace comms
|
||||||
} // namespace livoxProto1
|
} // namespace livoxProto1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user