Implemented async MDNS scan

This commit is contained in:
2021-10-14 19:36:55 +02:00
parent 8ae01044c9
commit 20019d16bf
4 changed files with 270 additions and 0 deletions

39
CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
set(headers
src/asyncmdnsresults.h
src/asyncmdnssearch.h
)
set(sources
src/asyncmdnssearch.cpp
)
set(dependencies
mdns
cpputils
espchrono
espcpputils
espwifistack
expected
fmt
)
idf_component_register(
INCLUDE_DIRS
src
SRCS
${headers}
${sources}
REQUIRES
${dependencies}
)
target_compile_options(${COMPONENT_TARGET}
PRIVATE
-fstack-reuse=all
-fstack-protector-all
-Wno-unused-function
-Wno-deprecated-declarations
-Wno-missing-field-initializers
-Wno-parentheses
)

121
src/asyncmdnsresults.h Normal file
View File

@ -0,0 +1,121 @@
#pragma once
// esp-idf includes
#include <mdns.h>
// 3rdparty lib includes
#include <arrayview.h>
class AsyncMdnsResults
{
template<typename T>
struct linked_list_iter
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
constexpr linked_list_iter(pointer ptr) noexcept : m_ptr{ptr} {}
reference operator*() const { return *m_ptr; }
pointer operator->() { return m_ptr; }
linked_list_iter &operator++() { m_ptr = m_ptr->next; return *this; }
friend bool operator== (const linked_list_iter& a, const linked_list_iter& b) { return a.m_ptr == b.m_ptr; };
friend bool operator!= (const linked_list_iter& a, const linked_list_iter& b) { return a.m_ptr != b.m_ptr; };
private:
pointer m_ptr;
};
public:
using value_type = mdns_result_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = linked_list_iter<value_type>;
using const_iterator = linked_list_iter<const value_type>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// not providing reverse_iterator
// not providing const_reverse_iterator
AsyncMdnsResults() noexcept = default;
AsyncMdnsResults(mdns_result_t *results, std::size_t num_results) noexcept :
m_results{results}, m_num_results{num_results}
{}
AsyncMdnsResults(const AsyncMdnsResults &) noexcept = delete;
AsyncMdnsResults(AsyncMdnsResults &&other) noexcept :
m_results{std::move(other.m_results)}, m_num_results{std::move(other.m_num_results)}
{
other.m_results = {};
other.m_num_results = {};
}
AsyncMdnsResults &operator=(const AsyncMdnsResults &) noexcept = delete;
AsyncMdnsResults &operator=(AsyncMdnsResults &&other)
{
if (&other != this)
{
free();
m_results = std::move(other.m_results);
other.m_results = {};
m_num_results = std::move(other.m_num_results);
other.m_num_results = {};
}
return *this;
}
~AsyncMdnsResults()
{
free();
}
// Iterators.
constexpr iterator begin() noexcept { return iterator(data()); }
constexpr const_iterator begin() const noexcept { return const_iterator(data()); }
constexpr iterator end() noexcept { return iterator(nullptr); }
constexpr const_iterator end() const noexcept { return const_iterator(nullptr); }
// not providing rbegin()
// not providing rend()
constexpr const_iterator cbegin() const noexcept { return const_iterator(data()); }
constexpr const_iterator cend() const noexcept { return const_iterator(nullptr); }
// not providing crbegin();
// not providing crend();
// Capacity.
constexpr size_type size() const noexcept { return m_num_results; }
constexpr size_type max_size() const noexcept { return size(); }
constexpr bool empty() const noexcept { return data() == nullptr; }
// Element access.
// not providing operator[]
// not providing at()
// not providing at()
constexpr reference front() noexcept { return *data(); }
constexpr const_reference front() const noexcept { return *data(); }
// not providing back()
constexpr pointer data() noexcept { return m_results; }
constexpr const_pointer data() const noexcept { return m_results; }
private:
void free()
{
if (m_results)
{
mdns_query_results_free(m_results);
m_results = {};
}
m_num_results = {};
}
mdns_result_t *m_results{};
std::size_t m_num_results{};
};

74
src/asyncmdnssearch.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "asyncmdnssearch.h"
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <cleanuphelper.h>
namespace {
constexpr const char * const TAG = "ASYNCMDNS";
} // namespace
AsyncMdnsSearch::AsyncMdnsSearch(AsyncMdnsSearch &&other) :
m_mdnsScan{std::move(other.m_mdnsScan)}
{
other.m_mdnsScan = {};
}
AsyncMdnsSearch::~AsyncMdnsSearch()
{
if (searchStarted())
deleteSearch();
}
AsyncMdnsSearch &AsyncMdnsSearch::operator=(AsyncMdnsSearch &&other)
{
m_mdnsScan = std::move(other.m_mdnsScan);
other.m_mdnsScan = {};
return *this;
}
tl::expected<void, std::string> AsyncMdnsSearch::startSearch(const char *name, const char *service, const char *proto, uint16_t type, std::chrono::milliseconds timeout, size_t max_results)
{
ESP_LOGI(TAG, "starting search...");
if (searchStarted())
return tl::make_unexpected("last scan not finished yet");
if (!max_results)
return tl::make_unexpected("max_results should be greater than 0");
m_mdnsScan = mdns_query_async_new(name, service, proto, type, timeout.count(), max_results);
if (!searchStarted())
return tl::make_unexpected("mdns_query_async_new() returned invalid");
return {};
}
std::optional<AsyncMdnsResults> AsyncMdnsSearch::getResults()
{
if (!searchStarted())
{
ESP_LOGW(TAG, "called without a running search");
return std::nullopt;
}
mdns_result_t *results{};
uint8_t num_results{};
if (!mdns_query_async_get_result(m_mdnsScan, 0, &results, &num_results))
return std::nullopt;
deleteSearch();
return AsyncMdnsResults{results, num_results};
}
void AsyncMdnsSearch::deleteSearch()
{
if (!m_mdnsScan)
ESP_LOGW(TAG, "no search started!");
mdns_query_async_delete(m_mdnsScan);
m_mdnsScan = nullptr;
}

36
src/asyncmdnssearch.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
// system includes
#include <string>
#include <chrono>
#include <optional>
// esp-idf includes
#include <mdns.h>
// 3rdparty lib includes
#include <tl/expected.hpp>
// local includes
#include "asyncmdnsresults.h"
class AsyncMdnsSearch
{
public:
AsyncMdnsSearch() = default;
AsyncMdnsSearch(const AsyncMdnsSearch &) = delete;
AsyncMdnsSearch(AsyncMdnsSearch &&other);
~AsyncMdnsSearch();
AsyncMdnsSearch &operator=(const AsyncMdnsSearch &) = delete;
AsyncMdnsSearch &operator=(AsyncMdnsSearch &&other);
bool searchStarted() const { return m_mdnsScan; }
tl::expected<void, std::string> startSearch(const char *name, const char *service, const char *proto, uint16_t type, std::chrono::milliseconds timeout, size_t max_results);
std::optional<AsyncMdnsResults> getResults();
void deleteSearch();
private:
mdns_search_once_t *m_mdnsScan{};
};