livoxGen/Proto1: Refine auto-detection vs heuristic logic
This commit is contained in:
@@ -81,7 +81,7 @@ Device::Device(const std::string &deviceIdentifier,
|
|||||||
""),
|
""),
|
||||||
componentThread(componentThread),
|
componentThread(componentThread),
|
||||||
handshakeTimeoutMs(handshakeTimeoutMs), retryDelayMs(retryDelayMs),
|
handshakeTimeoutMs(handshakeTimeoutMs), retryDelayMs(retryDelayMs),
|
||||||
smoIp(smoIp), smoSubnetNbits(smoSubnetNbits),
|
smoIp(smoIp), detectedSmoListeningIp(""), smoSubnetNbits(smoSubnetNbits),
|
||||||
dataPort(dataPort), cmdPort(cmdPort), imuPort(imuPort),
|
dataPort(dataPort), cmdPort(cmdPort), imuPort(imuPort),
|
||||||
heartbeatFd(-1),
|
heartbeatFd(-1),
|
||||||
heartbeatActive(false)
|
heartbeatActive(false)
|
||||||
@@ -286,7 +286,8 @@ void Device::connectToKnownDeviceReq(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Create the connection request object to hold state and callbacks
|
// 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();
|
auto& protoState = livoxProto1::getProtoState();
|
||||||
if (!protoState.deviceManager)
|
if (!protoState.deviceManager)
|
||||||
@@ -314,6 +315,30 @@ void Device::connectToKnownDeviceReq(
|
|||||||
// Use the IP address from the broadcast message
|
// Use the IP address from the broadcast message
|
||||||
request->deviceIP = request->deviceInfo->ipAddr;
|
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
|
// Execute handshake with the known device using async method
|
||||||
request->device.executeHandshakeReq(
|
request->device.executeHandshakeReq(
|
||||||
request->deviceIP,
|
request->deviceIP,
|
||||||
@@ -358,6 +383,24 @@ void Device::connectByDeviceIdentifierReq(
|
|||||||
Device::connectByDeviceIdentifierReqCbFn callback
|
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
|
// Create the connection request object to hold state and callbacks
|
||||||
auto request = std::make_shared<ConnectByDeviceIdentifierReq>(
|
auto request = std::make_shared<ConnectByDeviceIdentifierReq>(
|
||||||
*this, std::move(callback));
|
*this, std::move(callback));
|
||||||
@@ -366,6 +409,16 @@ void Device::connectByDeviceIdentifierReq(
|
|||||||
request->deviceIP = generateClientDeviceIpFromSerialNumber(
|
request->deviceIP = generateClientDeviceIpFromSerialNumber(
|
||||||
request->device.discoveredDevice.deviceIdentifier);
|
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
|
// Execute handshake using async method
|
||||||
request->device.executeHandshakeReq(
|
request->device.executeHandshakeReq(
|
||||||
request->deviceIP,
|
request->deviceIP,
|
||||||
@@ -508,9 +561,9 @@ private:
|
|||||||
/** EXPLANATION:
|
/** EXPLANATION:
|
||||||
* Prepare handshake request.
|
* Prepare handshake request.
|
||||||
*/
|
*/
|
||||||
std::string smoIp = device.getSmoIp(deviceIP);
|
|
||||||
comms::HandshakeRequest handshakeReq(
|
comms::HandshakeRequest handshakeReq(
|
||||||
smoIp, device.dataPort, device.cmdPort, device.imuPort);
|
device.detectedSmoListeningIp,
|
||||||
|
device.dataPort, device.cmdPort, device.imuPort);
|
||||||
handshakeReq.swapContentsToProtocolEndianness();
|
handshakeReq.swapContentsToProtocolEndianness();
|
||||||
handshakeReq.header.setCrc16FromRawBytes();
|
handshakeReq.header.setCrc16FromRawBytes();
|
||||||
handshakeReq.header.swapCrc16ToProtocolEndianness();
|
handshakeReq.header.swapCrc16ToProtocolEndianness();
|
||||||
@@ -764,6 +817,14 @@ void Device::executeHandshakeReq(
|
|||||||
auto request = std::make_shared<ExecuteHandshakeReq>(
|
auto request = std::make_shared<ExecuteHandshakeReq>(
|
||||||
*this, deviceIP, std::move(callback));
|
*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 {
|
try {
|
||||||
if (!request->setupSocket())
|
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:
|
/** EXPLANATION:
|
||||||
* If smo-ip was provided, return it.
|
* This is only used when connecting to a device that's already known to
|
||||||
* Otherwise, try to detect it based on the client device's IP address.
|
* the broadcastListener.
|
||||||
* If detection failed, throw an exception.
|
* 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;
|
return smoIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto detectedIp = detectSmoIp(deviceIP);
|
|
||||||
if (detectedIp.has_value()) {
|
if (detectedIp.has_value()) {
|
||||||
return detectedIp.value();
|
return detectedIp.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
// Auto-detection failed
|
||||||
std::string(__func__) + ": Failed to detect SMO IP address for device "
|
return std::nullopt;
|
||||||
+ deviceIP + " with subnet mask /" + std::to_string(smoSubnetNbits));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace livoxProto1
|
} // namespace livoxProto1
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public:
|
|||||||
std::shared_ptr<smo::ComponentThread> componentThread;
|
std::shared_ptr<smo::ComponentThread> componentThread;
|
||||||
int handshakeTimeoutMs, retryDelayMs;
|
int handshakeTimeoutMs, retryDelayMs;
|
||||||
std::string smoIp;
|
std::string smoIp;
|
||||||
|
std::string detectedSmoListeningIp;
|
||||||
uint8_t smoSubnetNbits;
|
uint8_t smoSubnetNbits;
|
||||||
uint16_t dataPort, cmdPort, imuPort;
|
uint16_t dataPort, cmdPort, imuPort;
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Utility methods
|
// 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
|
// Callback function type definitions for async methods
|
||||||
typedef std::function<void(bool success)> connectReqCbFn;
|
typedef std::function<void(bool success)> connectReqCbFn;
|
||||||
|
|||||||
@@ -145,7 +145,23 @@ The `livoxProto1` provider accepts the following parameters:
|
|||||||
|
|
||||||
## Device Discovery and Connection
|
## 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
|
1. **Initial Check**: Check if device is already known from broadcasts
|
||||||
2. **Direct Connect**: Attempt direct connection based on calculated IP address
|
2. **Direct Connect**: Attempt direct connection based on calculated IP address
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ extern "C" int livoxGen1_attachDeviceReq(
|
|||||||
uint16_t dataPort = 56000; // Default data port
|
uint16_t dataPort = 56000; // Default data port
|
||||||
uint16_t cmdPort = 56001; // Default command port
|
uint16_t cmdPort = 56001; // Default command port
|
||||||
uint16_t imuPort = 56002; // Default IMU port
|
uint16_t imuPort = 56002; // Default IMU port
|
||||||
// Default: 10.42.0.1
|
// Default: empty string (will trigger IP auto-detection)
|
||||||
std::string smoIp = "10.42.0.1";
|
std::string smoIp = "";
|
||||||
|
|
||||||
// Parse optional integer parameters from provider params
|
// Parse optional integer parameters from provider params
|
||||||
for (const auto& param : desc->providerParams)
|
for (const auto& param : desc->providerParams)
|
||||||
|
|||||||
Reference in New Issue
Block a user