livoxGen/Proto1: Refine auto-detection vs heuristic logic

This commit is contained in:
2025-09-09 19:54:14 -04:00
parent 73b2d981f9
commit 0449e557b0
4 changed files with 115 additions and 20 deletions
+91 -13
View File
@@ -81,7 +81,7 @@ Device::Device(const std::string &deviceIdentifier,
""),
componentThread(componentThread),
handshakeTimeoutMs(handshakeTimeoutMs), retryDelayMs(retryDelayMs),
smoIp(smoIp), smoSubnetNbits(smoSubnetNbits),
smoIp(smoIp), detectedSmoListeningIp(""), smoSubnetNbits(smoSubnetNbits),
dataPort(dataPort), cmdPort(cmdPort), imuPort(imuPort),
heartbeatFd(-1),
heartbeatActive(false)
@@ -286,7 +286,8 @@ void Device::connectToKnownDeviceReq(
)
{
// Create the connection request object to hold state and callbacks
auto request = std::make_shared<ConnectToKnownDeviceReq>(*this, std::move(callback));
auto request = std::make_shared<ConnectToKnownDeviceReq>(
*this, std::move(callback));
auto& protoState = livoxProto1::getProtoState();
if (!protoState.deviceManager)
@@ -314,6 +315,30 @@ void Device::connectToKnownDeviceReq(
// Use the IP address from the broadcast message
request->deviceIP = request->deviceInfo->ipAddr;
// Determine the final listening IP address
auto smoIpResult = request->device.getSmoIp(request->deviceIP);
if (!smoIpResult.has_value())
{
// Auto-detection failed, fail early
std::cerr << __func__ << ": Failed to detect SMO listening IP for "
<< "known device ("
<< request->device.discoveredDevice.deviceIdentifier << ")"
<< " @(" << request->deviceIP << ").\n";
request->callOriginalCallbackWithFailure();
return;
}
if (OptionParser::getOptions().verbose)
{
std::cout << __func__ << ": Detected SMO listening IP for known device "
<< request->device.discoveredDevice.deviceIdentifier
<< " @(" << request->deviceIP << ") is "
<< smoIpResult.value() << ". About to try to handshake.\n";
}
request->device.detectedSmoListeningIp = smoIpResult.value();
// Execute handshake with the known device using async method
request->device.executeHandshakeReq(
request->deviceIP,
@@ -358,6 +383,24 @@ void Device::connectByDeviceIdentifierReq(
Device::connectByDeviceIdentifierReqCbFn callback
)
{
/** EXPLANATION:
* This method uses heuristic device IP construction from the serial number.
* This requires smoIp to be provided because:
* 1. We need the network prefix to generate a valid device IP address
* 2. Without a target device IP, we cannot detect which interface faces the device
* 3. Therefore, if smoIp is omitted, heuristic construction is impossible
*
* If smoIp is not provided, the driver must rely only on broadcast advertisements
* from the device (handled by connectToKnownDeviceReq).
*/
// Check if smoIp is provided - required for heuristic construction
if (smoIp.empty())
{
callback(false, "", -1);
return;
}
// Create the connection request object to hold state and callbacks
auto request = std::make_shared<ConnectByDeviceIdentifierReq>(
*this, std::move(callback));
@@ -366,6 +409,16 @@ void Device::connectByDeviceIdentifierReq(
request->deviceIP = generateClientDeviceIpFromSerialNumber(
request->device.discoveredDevice.deviceIdentifier);
// For heuristic construction, always use the provided smoIp.
request->device.detectedSmoListeningIp = request->device.smoIp;
if (OptionParser::getOptions().verbose)
{
std::cout << __func__ << ": About to try to connect to device by "
<< "identifier (" << discoveredDevice.deviceIdentifier << ")"
<< " at IP (" << smoIp << ").\n";
}
// Execute handshake using async method
request->device.executeHandshakeReq(
request->deviceIP,
@@ -508,9 +561,9 @@ private:
/** EXPLANATION:
* Prepare handshake request.
*/
std::string smoIp = device.getSmoIp(deviceIP);
comms::HandshakeRequest handshakeReq(
smoIp, device.dataPort, device.cmdPort, device.imuPort);
device.detectedSmoListeningIp,
device.dataPort, device.cmdPort, device.imuPort);
handshakeReq.swapContentsToProtocolEndianness();
handshakeReq.header.setCrc16FromRawBytes();
handshakeReq.header.swapCrc16ToProtocolEndianness();
@@ -764,6 +817,14 @@ void Device::executeHandshakeReq(
auto request = std::make_shared<ExecuteHandshakeReq>(
*this, deviceIP, std::move(callback));
// Check if detectedSmoListeningIp is empty - this should not happen
if (detectedSmoListeningIp.empty())
{
// This should not happen as it should be set by the calling method
request->callOriginalCallbackWithFailure();
return;
}
try {
if (!request->setupSocket())
{
@@ -1143,25 +1204,42 @@ std::optional<std::string> Device::detectSmoIp(const std::string& deviceIP)
}
}
std::string Device::getSmoIp(const std::string& deviceIP)
std::optional<std::string> Device::getSmoIp(const std::string& deviceIP)
{
/** EXPLANATION:
* If smo-ip was provided, return it.
* Otherwise, try to detect it based on the client device's IP address.
* If detection failed, throw an exception.
* This is only used when connecting to a device that's already known to
* the broadcastListener.
* It is NOT and SHOULD not be used when connecting by heuristic IP
* construction using the client device's serial number.
*
* Determines the SMO listening IP address for this device.
* If smoIp is provided, validate it against detected IP and return it.
* If smoIp is empty, attempt auto-detection based on device IP.
* Returns std::optional<std::string> - empty if detection fails.
*/
if (!smoIp.empty()) {
auto detectedIp = detectSmoIp(deviceIP);
if (!smoIp.empty())
{
// smoIp was provided, validate it against detected IP
if (detectedIp.has_value() && detectedIp.value() != smoIp)
{
// Print warning if provided smoIp doesn't match detected IP
std::cerr << "Warning: Provided smo-ip (" << smoIp
<< ") doesn't match detected IP (" << detectedIp.value()
<< ") for device " << discoveredDevice.deviceIdentifier
<< " @(" << deviceIP << ")"
<< ". Using provided smo-ip anyway." << std::endl;
}
return smoIp;
}
auto detectedIp = detectSmoIp(deviceIP);
if (detectedIp.has_value()) {
return detectedIp.value();
}
throw std::runtime_error(
std::string(__func__) + ": Failed to detect SMO IP address for device "
+ deviceIP + " with subnet mask /" + std::to_string(smoSubnetNbits));
// Auto-detection failed
return std::nullopt;
}
} // namespace livoxProto1
+2 -1
View File
@@ -73,6 +73,7 @@ public:
std::shared_ptr<smo::ComponentThread> componentThread;
int handshakeTimeoutMs, retryDelayMs;
std::string smoIp;
std::string detectedSmoListeningIp;
uint8_t smoSubnetNbits;
uint16_t dataPort, cmdPort, imuPort;
@@ -96,7 +97,7 @@ private:
public:
// Utility methods
std::string getSmoIp(const std::string& deviceIP);
std::optional<std::string> getSmoIp(const std::string& deviceIP);
// Callback function type definitions for async methods
typedef std::function<void(bool success)> connectReqCbFn;
+17 -1
View File
@@ -145,7 +145,23 @@ The `livoxProto1` provider accepts the following parameters:
## Device Discovery and Connection
The specification uses a retry-based connection strategy:
The specification uses a retry-based connection strategy with two different approaches:
### Connection Methods
**1. Broadcast-Based Connection (connectToKnownDeviceReq)**
- Uses device IP addresses discovered from broadcast advertisements
- **smo-ip parameter**: Optional - if omitted, driver auto-detects the appropriate interface
- **smo-subnet-nbits parameter**: Optional - used for validation if smo-ip is provided
- **When to use**: When devices are actively broadcasting their presence
**2. Heuristic Connection (connectByDeviceIdentifierReq)**
- Generates device IP addresses from serial numbers using network prefix
- **smo-ip parameter**: **Required** - needed to determine network prefix for IP generation
- **smo-subnet-nbits parameter**: **Required** - needed to calculate valid device IP addresses
- **When to use**: When devices are not broadcasting or for initial setup
### Connection Strategy
1. **Initial Check**: Check if device is already known from broadcasts
2. **Direct Connect**: Attempt direct connection based on calculated IP address
+2 -2
View File
@@ -176,8 +176,8 @@ extern "C" int livoxGen1_attachDeviceReq(
uint16_t dataPort = 56000; // Default data port
uint16_t cmdPort = 56001; // Default command port
uint16_t imuPort = 56002; // Default IMU port
// Default: 10.42.0.1
std::string smoIp = "10.42.0.1";
// Default: empty string (will trigger IP auto-detection)
std::string smoIp = "";
// Parse optional integer parameters from provider params
for (const auto& param : desc->providerParams)