|
|
|
@@ -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
|
|
|
|
|