livoxProto1: Keep protocol headers pure;
Split the BroadcastListener, DiscoveredDevice and other concerns out of the protocol header and implementation files.
This commit is contained in:
@@ -6,6 +6,7 @@ if(ENABLE_LIB_livoxProto1)
|
|||||||
livoxProto1Core.cpp
|
livoxProto1Core.cpp
|
||||||
livoxProto1Device.cpp
|
livoxProto1Device.cpp
|
||||||
livoxProto1Protocol.cpp
|
livoxProto1Protocol.cpp
|
||||||
|
broadcastListener.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set config define for header generation
|
# Set config define for header generation
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include "broadcastListener.h"
|
||||||
|
|
||||||
|
namespace livoxProto1 {
|
||||||
|
namespace comms {
|
||||||
|
|
||||||
|
BroadcastListener::BroadcastListener(
|
||||||
|
const std::shared_ptr<smo::ComponentThread>& componentThread,
|
||||||
|
uint16_t listeningPort, uint16_t connectPort
|
||||||
|
)
|
||||||
|
: componentThread(componentThread),
|
||||||
|
listeningPort(listeningPort),
|
||||||
|
connectPort(connectPort),
|
||||||
|
deviceGoneAwayCb(nullptr),
|
||||||
|
socket(componentThread->getIoService()),
|
||||||
|
listeningEndpoint(boost::asio::ip::udp::v4(), listeningPort),
|
||||||
|
isListening(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DiscoveredDevice>
|
||||||
|
BroadcastListener::getDevice(const std::string &deviceIdentifier) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(discoveredDevices.begin(), discoveredDevices.end(),
|
||||||
|
[&deviceIdentifier](const std::shared_ptr<DiscoveredDevice>& device) {
|
||||||
|
return device->deviceIdentifier == deviceIdentifier;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return it != discoveredDevices.end() ? *it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BroadcastListener::broadcastMsgInd(
|
||||||
|
const boost::system::error_code& ec, std::size_t bytes_received)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
std::cerr << __func__ << ": Error receiving broadcast message: "
|
||||||
|
<< ec.message() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_received < sizeof(BroadcastMessage))
|
||||||
|
{
|
||||||
|
std::cerr << "Received packet too small: " << bytes_received
|
||||||
|
<< " bytes (expected at least " << sizeof(BroadcastMessage) << ")"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use placement new to construct BroadcastMessage in the buffer
|
||||||
|
BroadcastMessage* msg = new (bcastMsgRecvBuffer) BroadcastMessage;
|
||||||
|
|
||||||
|
// Validate the message using sanity check methods
|
||||||
|
if (!msg->sanityCheck())
|
||||||
|
{
|
||||||
|
std::cerr << "Broadcast message failed sanity check" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from little-endian to host endianness
|
||||||
|
msg->swapToHostEndianness();
|
||||||
|
|
||||||
|
// Extract device information
|
||||||
|
std::string senderIP = senderEndpoint.address().to_string();
|
||||||
|
std::string broadcastCode(reinterpret_cast<const char*>(msg->broadcast_code));
|
||||||
|
|
||||||
|
// Early return if device already exists
|
||||||
|
if (deviceExists(broadcastCode))
|
||||||
|
{
|
||||||
|
// Device already exists, just log the update
|
||||||
|
std::cout << "Received broadcast from known device: " << broadcastCode
|
||||||
|
<< " at " << senderIP << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new DiscoveredDevice using conversion constructor
|
||||||
|
auto device = std::make_shared<DiscoveredDevice>(*msg, senderIP);
|
||||||
|
discoveredDevices.push_back(device);
|
||||||
|
|
||||||
|
// Output device information using stringify
|
||||||
|
std::cout << "Discovered new Livox device: " << device->stringify()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BroadcastListener::start(void)
|
||||||
|
{
|
||||||
|
if (isListening.load()) { return; }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
/** EXPLANATION:
|
||||||
|
* Set up a boost::asio udp listening socket on the broadcast listening
|
||||||
|
* port.
|
||||||
|
*
|
||||||
|
* FIXME:
|
||||||
|
* We should also set up a timer to check for devices that have gone
|
||||||
|
* away.
|
||||||
|
*/
|
||||||
|
socket.open(boost::asio::ip::udp::v4());
|
||||||
|
socket.bind(listeningEndpoint);
|
||||||
|
|
||||||
|
isListening.store(true);
|
||||||
|
// Start the first async receive operation
|
||||||
|
startReceive();
|
||||||
|
std::cout << "BroadcastListener started on port " << listeningPort
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
catch (const boost::system::system_error& e)
|
||||||
|
{
|
||||||
|
isListening.store(false);
|
||||||
|
std::cerr << "Failed to start BroadcastListener: " << e.what()
|
||||||
|
<< std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BroadcastListener::startReceive(void)
|
||||||
|
{
|
||||||
|
if (!isListening.load()) { return; }
|
||||||
|
|
||||||
|
socket.async_receive_from(
|
||||||
|
boost::asio::buffer(bcastMsgRecvBuffer, sizeof(bcastMsgRecvBuffer)),
|
||||||
|
senderEndpoint,
|
||||||
|
[this](const boost::system::error_code& ec, std::size_t bytes_received)
|
||||||
|
{
|
||||||
|
broadcastMsgInd(ec, bytes_received);
|
||||||
|
|
||||||
|
// Continue listening for the next packet
|
||||||
|
if (isListening.load())
|
||||||
|
{ startReceive(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BroadcastListener::stop(void)
|
||||||
|
{
|
||||||
|
if (!isListening.load()) { return; }
|
||||||
|
|
||||||
|
isListening.store(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
std::cout << "BroadcastListener stopped" << std::endl;
|
||||||
|
}
|
||||||
|
catch (const boost::system::system_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error stopping BroadcastListener: " << e.what()
|
||||||
|
<< std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace comms
|
||||||
|
} // namespace livoxProto1
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#ifndef BROADCAST_LISTENER_H
|
||||||
|
#define BROADCAST_LISTENER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <user/senseApiDesc.h>
|
||||||
|
#include "livoxProto1Device.h"
|
||||||
|
|
||||||
|
namespace livoxProto1 {
|
||||||
|
namespace comms {
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* This class merely listens for UDP bcast dgrams on the designated listening
|
||||||
|
* port. It then builds a list of client device IP addrs that it has heard from.
|
||||||
|
* It doesn't connect to them or signal any events to the rest of the lib,
|
||||||
|
* except in the case that a device which the lib is using has gone away.
|
||||||
|
*
|
||||||
|
* Other than that, its role is to tell the lib which devices are available
|
||||||
|
* on the network.
|
||||||
|
*/
|
||||||
|
#define UDP_BCAST_MSG_BUFFER_NBYTES (1024)
|
||||||
|
|
||||||
|
class BroadcastListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BroadcastListener(
|
||||||
|
const std::shared_ptr<smo::ComponentThread>& componentThread,
|
||||||
|
uint16_t listeningPort=55000, uint16_t connectPort=65000);
|
||||||
|
|
||||||
|
~BroadcastListener() = default;
|
||||||
|
|
||||||
|
typedef void (DeviceGoneAwayCbFn)(const DiscoveredDevice &device);
|
||||||
|
void setDeviceGoneAwayCb(DeviceGoneAwayCbFn *cb)
|
||||||
|
{ deviceGoneAwayCb = cb; }
|
||||||
|
|
||||||
|
bool deviceExists(const std::string &deviceIdentifier) const
|
||||||
|
{ return getDevice(deviceIdentifier) != nullptr; }
|
||||||
|
|
||||||
|
std::shared_ptr<DiscoveredDevice>
|
||||||
|
getDevice(const std::string &deviceIdentifier) const;
|
||||||
|
|
||||||
|
void start(void);
|
||||||
|
void stop(void);
|
||||||
|
|
||||||
|
void broadcastMsgInd(
|
||||||
|
const boost::system::error_code& ec, std::size_t bytes_received);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startReceive(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<smo::ComponentThread> componentThread;
|
||||||
|
/** EXPLANATION:
|
||||||
|
* The Livox proto says that client devices will spam broadcast UDP
|
||||||
|
* dgrams to us on the listening port. We can then use the source IP from
|
||||||
|
* the bcast dgram to figure out the client device's IP addr. Then we
|
||||||
|
* should send a connect dgram to the connect port. This will tell the
|
||||||
|
* client device our IP addr.
|
||||||
|
*/
|
||||||
|
uint16_t listeningPort, connectPort;
|
||||||
|
DeviceGoneAwayCbFn *deviceGoneAwayCb;
|
||||||
|
std::vector<std::shared_ptr<DiscoveredDevice>> discoveredDevices;
|
||||||
|
|
||||||
|
boost::asio::ip::udp::socket socket;
|
||||||
|
boost::asio::ip::udp::endpoint listeningEndpoint, senderEndpoint;
|
||||||
|
std::atomic<bool> isListening;
|
||||||
|
|
||||||
|
uint8_t bcastMsgRecvBuffer[UDP_BCAST_MSG_BUFFER_NBYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace comms
|
||||||
|
} // namespace livoxProto1
|
||||||
|
|
||||||
|
#endif // BROADCAST_LISTENER_H
|
||||||
@@ -4,20 +4,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "livoxProto1Protocol.h"
|
#include "livoxProto1Device.h"
|
||||||
|
#include "broadcastListener.h"
|
||||||
|
|
||||||
namespace livoxProto1 {
|
namespace livoxProto1 {
|
||||||
|
|
||||||
class Device
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Device(const comms::DiscoveredDevice &discoveredDevice);
|
|
||||||
~Device() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
comms::DiscoveredDevice discoveredDevice;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeviceManager
|
class DeviceManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include <sstream>
|
||||||
|
#include "livoxProto1Device.h"
|
||||||
|
|
||||||
|
namespace livoxProto1 {
|
||||||
|
namespace comms {
|
||||||
|
|
||||||
|
// DiscoveredDevice constructors
|
||||||
|
DiscoveredDevice::DiscoveredDevice(
|
||||||
|
const std::string &deviceIdentifier,
|
||||||
|
DeviceType deviceType,
|
||||||
|
const std::string &ipAddr)
|
||||||
|
: deviceIdentifier(deviceIdentifier),
|
||||||
|
deviceType(deviceType),
|
||||||
|
ipAddr(ipAddr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscoveredDevice::DiscoveredDevice(
|
||||||
|
const BroadcastMessage &msg, const std::string &ipAddr
|
||||||
|
)
|
||||||
|
: DiscoveredDevice(
|
||||||
|
reinterpret_cast<const char*>(msg.broadcast_code),
|
||||||
|
static_cast<DeviceType>(msg.dev_type),
|
||||||
|
ipAddr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DiscoveredDevice::stringify(void) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "DiscoveredDevice{"
|
||||||
|
<< "identifier='" << deviceIdentifier << "', "
|
||||||
|
<< "ipAddr='" << ipAddr << "', "
|
||||||
|
<< "deviceType=" << (int)deviceType << " (" << getDeviceTypeName() << ")"
|
||||||
|
<< "}";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DiscoveredDevice::getDeviceTypeName(void) const
|
||||||
|
{
|
||||||
|
switch (deviceType)
|
||||||
|
{
|
||||||
|
case DeviceType::Hub: return "Hub";
|
||||||
|
case DeviceType::Mid40: return "Mid-40";
|
||||||
|
case DeviceType::Tele15: return "Tele-15";
|
||||||
|
case DeviceType::Horizon: return "Horizon";
|
||||||
|
case DeviceType::Mid70: return "Mid-70";
|
||||||
|
case DeviceType::Avia: return "Avia";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace comms
|
||||||
|
|
||||||
|
// Device implementation
|
||||||
|
Device::Device(const comms::DiscoveredDevice &discoveredDevice)
|
||||||
|
: discoveredDevice(discoveredDevice)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace livoxProto1
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef LIVOX_PROTO1_DEVICE_H
|
||||||
|
#define LIVOX_PROTO1_DEVICE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "livoxProto1Protocol.h"
|
||||||
|
|
||||||
|
namespace livoxProto1 {
|
||||||
|
namespace comms {
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* This class represents a discovered device. It is used to store the
|
||||||
|
* device identifier and IP address of a discovered device.
|
||||||
|
*/
|
||||||
|
class DiscoveredDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DiscoveredDevice(
|
||||||
|
const std::string &deviceIdentifier,
|
||||||
|
DeviceType deviceType,
|
||||||
|
const std::string &ipAddr);
|
||||||
|
|
||||||
|
// "Conversion" constructor from BroadcastMessage
|
||||||
|
DiscoveredDevice(const BroadcastMessage &msg, const std::string &ipAddr);
|
||||||
|
|
||||||
|
~DiscoveredDevice() = default;
|
||||||
|
|
||||||
|
bool operator==(const DiscoveredDevice &other) const
|
||||||
|
{ return deviceIdentifier == other.deviceIdentifier; }
|
||||||
|
|
||||||
|
std::string stringify(void) const;
|
||||||
|
std::string getDeviceTypeName(void) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string deviceIdentifier;
|
||||||
|
DeviceType deviceType;
|
||||||
|
std::string ipAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace comms
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Device(const comms::DiscoveredDevice &discoveredDevice);
|
||||||
|
~Device() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
comms::DiscoveredDevice discoveredDevice;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace livoxProto1
|
||||||
|
|
||||||
|
#endif // LIVOX_PROTO1_DEVICE_H
|
||||||
|
|||||||
@@ -54,190 +54,5 @@ bool BroadcastMessage::sanityCheck() const
|
|||||||
footer.sanityCheck();
|
footer.sanityCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscoveredDevice constructors
|
|
||||||
DiscoveredDevice::DiscoveredDevice(
|
|
||||||
const std::string &deviceIdentifier,
|
|
||||||
DeviceType deviceType,
|
|
||||||
const std::string &ipAddr)
|
|
||||||
: deviceIdentifier(deviceIdentifier),
|
|
||||||
deviceType(deviceType),
|
|
||||||
ipAddr(ipAddr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DiscoveredDevice::DiscoveredDevice(
|
|
||||||
const BroadcastMessage &msg, const std::string &ipAddr
|
|
||||||
)
|
|
||||||
: DiscoveredDevice(
|
|
||||||
reinterpret_cast<const char*>(msg.broadcast_code),
|
|
||||||
static_cast<DeviceType>(msg.dev_type),
|
|
||||||
ipAddr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DiscoveredDevice::stringify(void) const
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "DiscoveredDevice{"
|
|
||||||
<< "identifier='" << deviceIdentifier << "', "
|
|
||||||
<< "ipAddr='" << ipAddr << "', "
|
|
||||||
<< "deviceType=" << (int)deviceType << " (" << getDeviceTypeName() << ")"
|
|
||||||
<< "}";
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DiscoveredDevice::getDeviceTypeName(void) const
|
|
||||||
{
|
|
||||||
switch (deviceType)
|
|
||||||
{
|
|
||||||
case DeviceType::Hub: return "Hub";
|
|
||||||
case DeviceType::Mid40: return "Mid-40";
|
|
||||||
case DeviceType::Tele15: return "Tele-15";
|
|
||||||
case DeviceType::Horizon: return "Horizon";
|
|
||||||
case DeviceType::Mid70: return "Mid-70";
|
|
||||||
case DeviceType::Avia: return "Avia";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BroadcastListener::BroadcastListener(
|
|
||||||
const std::shared_ptr<smo::ComponentThread>& componentThread,
|
|
||||||
uint16_t listeningPort, uint16_t connectPort
|
|
||||||
)
|
|
||||||
: componentThread(componentThread),
|
|
||||||
listeningPort(listeningPort),
|
|
||||||
connectPort(connectPort),
|
|
||||||
deviceGoneAwayCb(nullptr),
|
|
||||||
socket(componentThread->getIoService()),
|
|
||||||
listeningEndpoint(boost::asio::ip::udp::v4(), listeningPort),
|
|
||||||
isListening(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<DiscoveredDevice>
|
|
||||||
BroadcastListener::getDevice(const std::string &deviceIdentifier) const
|
|
||||||
{
|
|
||||||
auto it = std::find_if(discoveredDevices.begin(), discoveredDevices.end(),
|
|
||||||
[&deviceIdentifier](const std::shared_ptr<DiscoveredDevice>& device) {
|
|
||||||
return device->deviceIdentifier == deviceIdentifier;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return it != discoveredDevices.end() ? *it : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroadcastListener::broadcastMsgInd(
|
|
||||||
const boost::system::error_code& ec, std::size_t bytes_received)
|
|
||||||
{
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
std::cerr << __func__ << ": Error receiving broadcast message: "
|
|
||||||
<< ec.message() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes_received < sizeof(BroadcastMessage))
|
|
||||||
{
|
|
||||||
std::cerr << "Received packet too small: " << bytes_received
|
|
||||||
<< " bytes (expected at least " << sizeof(BroadcastMessage) << ")"
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use placement new to construct BroadcastMessage in the buffer
|
|
||||||
BroadcastMessage* msg = new (bcastMsgRecvBuffer) BroadcastMessage;
|
|
||||||
|
|
||||||
if (!msg->sanityCheck())
|
|
||||||
{
|
|
||||||
std::cerr << "Broadcast message failed sanity check" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert from little-endian to host endianness
|
|
||||||
msg->swapToHostEndianness();
|
|
||||||
|
|
||||||
// Extract device information
|
|
||||||
std::string senderIP = senderEndpoint.address().to_string();
|
|
||||||
std::string broadcastCode(reinterpret_cast<const char*>(msg->broadcast_code));
|
|
||||||
|
|
||||||
// Early return if device already exists
|
|
||||||
if (deviceExists(broadcastCode)) { return; }
|
|
||||||
|
|
||||||
// Create new DiscoveredDevice using conversion constructor
|
|
||||||
auto device = std::make_shared<DiscoveredDevice>(*msg, senderIP);
|
|
||||||
discoveredDevices.push_back(device);
|
|
||||||
std::cout << "Discovered new Livox device: " << device->stringify()
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroadcastListener::start(void)
|
|
||||||
{
|
|
||||||
if (isListening.load()) { return; }
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
/** EXPLANATION:
|
|
||||||
* Set up a boost::asio udp listening socket on the broadcast listening
|
|
||||||
* port.
|
|
||||||
*
|
|
||||||
* FIXME:
|
|
||||||
* We should also set up a timer to check for devices that have gone
|
|
||||||
* away.
|
|
||||||
*/
|
|
||||||
socket.open(boost::asio::ip::udp::v4());
|
|
||||||
socket.bind(listeningEndpoint);
|
|
||||||
|
|
||||||
isListening.store(true);
|
|
||||||
// Start the first async receive operation
|
|
||||||
startReceive();
|
|
||||||
std::cout << "BroadcastListener started on port " << listeningPort
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
catch (const boost::system::system_error& e)
|
|
||||||
{
|
|
||||||
isListening.store(false);
|
|
||||||
std::cerr << "Failed to start BroadcastListener: " << e.what()
|
|
||||||
<< std::endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroadcastListener::startReceive(void)
|
|
||||||
{
|
|
||||||
if (!isListening.load()) { return; }
|
|
||||||
|
|
||||||
socket.async_receive_from(
|
|
||||||
boost::asio::buffer(bcastMsgRecvBuffer, sizeof(bcastMsgRecvBuffer)),
|
|
||||||
senderEndpoint,
|
|
||||||
[this](const boost::system::error_code& ec, std::size_t bytes_received)
|
|
||||||
{
|
|
||||||
broadcastMsgInd(ec, bytes_received);
|
|
||||||
|
|
||||||
// Continue listening for the next packet
|
|
||||||
if (isListening.load())
|
|
||||||
{ startReceive(); }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BroadcastListener::stop(void)
|
|
||||||
{
|
|
||||||
if (!isListening.load()) { return; }
|
|
||||||
|
|
||||||
isListening.store(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.close();
|
|
||||||
std::cout << "BroadcastListener stopped" << std::endl;
|
|
||||||
}
|
|
||||||
catch (const boost::system::system_error& e)
|
|
||||||
{
|
|
||||||
std::cerr << "Error stopping BroadcastListener: " << e.what()
|
|
||||||
<< std::endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace comms
|
} // namespace comms
|
||||||
} // namespace livoxProto1
|
} // namespace livoxProto1
|
||||||
|
|||||||
@@ -81,94 +81,6 @@ struct BroadcastMessage
|
|||||||
bool sanityCheck() const;
|
bool sanityCheck() const;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
/** EXPLANATION:
|
|
||||||
* This class represents a discovered device. It is used to store the
|
|
||||||
* device identifier and IP address of a discovered device.
|
|
||||||
*/
|
|
||||||
class DiscoveredDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DiscoveredDevice(
|
|
||||||
const std::string &deviceIdentifier,
|
|
||||||
DeviceType deviceType,
|
|
||||||
const std::string &ipAddr);
|
|
||||||
|
|
||||||
// "Conversion" constructor from BroadcastMessage
|
|
||||||
DiscoveredDevice(const BroadcastMessage &msg, const std::string &ipAddr);
|
|
||||||
|
|
||||||
~DiscoveredDevice() = default;
|
|
||||||
|
|
||||||
bool operator==(const DiscoveredDevice &other) const
|
|
||||||
{ return deviceIdentifier == other.deviceIdentifier; }
|
|
||||||
|
|
||||||
std::string stringify(void) const;
|
|
||||||
std::string getDeviceTypeName(void) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string deviceIdentifier;
|
|
||||||
DeviceType deviceType;
|
|
||||||
std::string ipAddr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** EXPLANATION:
|
|
||||||
* This class merely listens for UDP bcast dgrams on the designated listening
|
|
||||||
* port. It then builds a list of client device IP addrs that it has heard from.
|
|
||||||
* It doesn't connect to them or signal any events to the rest of the lib,
|
|
||||||
* except in the case that a device which the lib is using has gone away.
|
|
||||||
*
|
|
||||||
* Other than that, its role is to tell the lib which devices are available
|
|
||||||
* on the network.
|
|
||||||
*/
|
|
||||||
#define UDP_BCAST_MSG_BUFFER_NBYTES (1024)
|
|
||||||
|
|
||||||
class BroadcastListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BroadcastListener(
|
|
||||||
const std::shared_ptr<smo::ComponentThread>& componentThread,
|
|
||||||
uint16_t listeningPort=55000, uint16_t connectPort=65000);
|
|
||||||
|
|
||||||
~BroadcastListener() = default;
|
|
||||||
|
|
||||||
typedef void (DeviceGoneAwayCbFn)(const DiscoveredDevice &device);
|
|
||||||
void setDeviceGoneAwayCb(DeviceGoneAwayCbFn *cb)
|
|
||||||
{ deviceGoneAwayCb = cb; }
|
|
||||||
|
|
||||||
bool deviceExists(const std::string &deviceIdentifier) const
|
|
||||||
{ return getDevice(deviceIdentifier) != nullptr; }
|
|
||||||
|
|
||||||
std::shared_ptr<DiscoveredDevice>
|
|
||||||
getDevice(const std::string &deviceIdentifier) const;
|
|
||||||
|
|
||||||
void start(void);
|
|
||||||
void stop(void);
|
|
||||||
|
|
||||||
void broadcastMsgInd(
|
|
||||||
const boost::system::error_code& ec, std::size_t bytes_received);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void startReceive(void);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<smo::ComponentThread> componentThread;
|
|
||||||
/** EXPLANATION:
|
|
||||||
* The Livox proto says that client devices will spam broadcast UDP
|
|
||||||
* dgrams to us on the listening port. We can then use the source IP from
|
|
||||||
* the bcast dgram to figure out the client device's IP addr. Then we
|
|
||||||
* should send a connect dgram to the connect port. This will tell the
|
|
||||||
* client device our IP addr.
|
|
||||||
*/
|
|
||||||
uint16_t listeningPort, connectPort;
|
|
||||||
DeviceGoneAwayCbFn *deviceGoneAwayCb;
|
|
||||||
std::vector<std::shared_ptr<DiscoveredDevice>> discoveredDevices;
|
|
||||||
|
|
||||||
boost::asio::ip::udp::socket socket;
|
|
||||||
boost::asio::ip::udp::endpoint listeningEndpoint, senderEndpoint;
|
|
||||||
std::atomic<bool> isListening;
|
|
||||||
|
|
||||||
uint8_t bcastMsgRecvBuffer[UDP_BCAST_MSG_BUFFER_NBYTES];
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace comms
|
} // namespace comms
|
||||||
} // namespace livoxProto1
|
} // namespace livoxProto1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user