Bonjour: Add txt key-val extraction, filtering based on printer tech

This commit is contained in:
Vojtech Kral 2019-03-12 17:41:43 +01:00
parent f02f24c4b7
commit 2c49dcb9be
5 changed files with 174 additions and 96 deletions

View File

@ -10,6 +10,7 @@
#include <wx/listctrl.h> #include <wx/listctrl.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/timer.h> #include <wx/timer.h>
#include <wx/wupdlock.h>
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
@ -49,14 +50,16 @@ struct LifetimeGuard
LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {} LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {}
}; };
// FIXME: use em, resizable
BonjourDialog::BonjourDialog(wxWindow *parent) : BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
wxDialog(parent, wxID_ANY, _(L("Network lookup"))), : wxDialog(parent, wxID_ANY, _(L("Network lookup")))
list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))), , list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300)))
replies(new ReplySet), , replies(new ReplySet)
label(new wxStaticText(this, wxID_ANY, "")), , label(new wxStaticText(this, wxID_ANY, ""))
timer(new wxTimer()), , timer(new wxTimer())
timer_state(0) , timer_state(0)
, tech(tech)
{ {
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
@ -67,7 +70,9 @@ BonjourDialog::BonjourDialog(wxWindow *parent) :
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50); list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50);
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100); list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100);
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200); list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200);
if (tech == ptFFF) {
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50); list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50);
}
vsizer->Add(list, 1, wxEXPAND | wxALL, 10); vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
@ -110,7 +115,11 @@ bool BonjourDialog::show_and_lookup()
// so that both threads can access it safely. // so that both threads can access it safely.
auto dguard = std::make_shared<LifetimeGuard>(this); auto dguard = std::make_shared<LifetimeGuard>(this);
// Note: More can be done here when we support discovery of hosts other than Octoprint and SL1
Bonjour::TxtKeys txt_keys { "version", "model" };
bonjour = std::move(Bonjour("octoprint") bonjour = std::move(Bonjour("octoprint")
.set_txt_keys(std::move(txt_keys))
.set_retries(3) .set_retries(3)
.set_timeout(4) .set_timeout(4)
.on_reply([dguard](BonjourReply &&reply) { .on_reply([dguard](BonjourReply &&reply) {
@ -157,9 +166,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
return; return;
} }
// Filter replies based on selected technology
const auto model = e.reply.txt_data.find("model");
const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) {
return;
}
replies->insert(std::move(e.reply)); replies->insert(std::move(e.reply));
auto selected = get_selected(); auto selected = get_selected();
wxWindowUpdateLocker freeze_guard(this);
(void)freeze_guard;
list->DeleteAllItems(); list->DeleteAllItems();
// The whole list is recreated so that we benefit from it already being sorted in the set. // The whole list is recreated so that we benefit from it already being sorted in the set.
@ -168,12 +188,18 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
auto item = list->InsertItem(0, reply.full_address); auto item = list->InsertItem(0, reply.full_address);
list->SetItem(item, 1, reply.hostname); list->SetItem(item, 1, reply.hostname);
list->SetItem(item, 2, reply.service_name); list->SetItem(item, 2, reply.service_name);
list->SetItem(item, 3, reply.version);
if (tech == ptFFF) {
const auto it = reply.txt_data.find("version");
if (it != reply.txt_data.end()) {
list->SetItem(item, 3, GUI::from_u8(it->second));
}
}
} }
for (int i = 0; i < 4; i++) { for (int i = 0; i < list->GetColumnCount(); i++) {
this->list->SetColumnWidth(i, wxLIST_AUTOSIZE); list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); } if (list->GetColumnWidth(i) < 100) { list->SetColumnWidth(i, 100); }
} }
if (!selected.IsEmpty()) { if (!selected.IsEmpty()) {

View File

@ -5,6 +5,8 @@
#include <wx/dialog.h> #include <wx/dialog.h>
#include "libslic3r/PrintConfig.hpp"
class wxListView; class wxListView;
class wxStaticText; class wxStaticText;
class wxTimer; class wxTimer;
@ -21,7 +23,7 @@ class ReplySet;
class BonjourDialog: public wxDialog class BonjourDialog: public wxDialog
{ {
public: public:
BonjourDialog(wxWindow *parent); BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology);
BonjourDialog(BonjourDialog &&) = delete; BonjourDialog(BonjourDialog &&) = delete;
BonjourDialog(const BonjourDialog &) = delete; BonjourDialog(const BonjourDialog &) = delete;
BonjourDialog &operator=(BonjourDialog &&) = delete; BonjourDialog &operator=(BonjourDialog &&) = delete;
@ -37,6 +39,7 @@ private:
std::shared_ptr<Bonjour> bonjour; std::shared_ptr<Bonjour> bonjour;
std::unique_ptr<wxTimer> timer; std::unique_ptr<wxTimer> timer;
unsigned timer_state; unsigned timer_state;
Slic3r::PrinterTechnology tech;
void on_reply(BonjourReplyEvent &); void on_reply(BonjourReplyEvent &);
void on_timer(wxTimerEvent &); void on_timer(wxTimerEvent &);

View File

@ -1612,25 +1612,21 @@ bool Tab::current_preset_is_dirty()
void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
{ {
const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA; const PrinterTechnology tech = m_presets->get_selected_preset().printer_technology();
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
if (! sla) { if (tech == ptFFF) {
optgroup->append_single_option_line("host_type"); optgroup->append_single_option_line("host_type");
} }
auto printhost_browse = [this, optgroup] (wxWindow* parent) { auto printhost_browse = [=](wxWindow* parent) {
// TODO: SLA Bonjour
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
btn->SetBitmap(create_scaled_bitmap("zoom.png")); btn->SetBitmap(create_scaled_bitmap("zoom.png"));
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn); sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent &e) { btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
BonjourDialog dialog(parent); BonjourDialog dialog(parent, tech);
if (dialog.show_and_lookup()) { if (dialog.show_and_lookup()) {
optgroup->set_value("print_host", std::move(dialog.get_selected()), true); optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
optgroup->get_field("print_host")->field_changed(); optgroup->get_field("print_host")->field_changed();
@ -1643,7 +1639,6 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto print_host_test = [this](wxWindow* parent) { auto print_host_test = [this](wxWindow* parent) {
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
btn->SetBitmap(create_scaled_bitmap("wrench.png")); btn->SetBitmap(create_scaled_bitmap("wrench.png"));
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn); sizer->Add(btn);

View File

@ -5,6 +5,7 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include <string> #include <string>
#include <map>
#include <thread> #include <thread>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
@ -33,7 +34,9 @@ namespace Slic3r {
// the implementations has been tested with AFL. // the implementations has been tested with AFL.
// Relevant RFC: https://www.ietf.org/rfc/rfc6762.txt // Relevant RFCs:
// https://tools.ietf.org/html/rfc6762.txt
// https://tools.ietf.org/html/rfc6763.txt
struct DnsName: public std::string struct DnsName: public std::string
@ -156,9 +159,9 @@ struct DnsQuestion
uint16_t type; uint16_t type;
uint16_t qclass; uint16_t qclass;
DnsQuestion() : DnsQuestion()
type(0), : type(0)
qclass(0) , qclass(0)
{} {}
static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset) static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset)
@ -187,10 +190,10 @@ struct DnsResource
uint32_t ttl; uint32_t ttl;
std::vector<char> data; std::vector<char> data;
DnsResource() : DnsResource()
type(0), : type(0)
rclass(0), , rclass(0)
ttl(0) , ttl(0)
{} {}
static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset) static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset)
@ -310,9 +313,9 @@ struct DnsRR_TXT
TAG = 0x10, TAG = 0x10,
}; };
std::vector<std::string> values; BonjourReply::TxtData data;
static optional<DnsRR_TXT> decode(const DnsResource &rr) static optional<DnsRR_TXT> decode(const DnsResource &rr, const Bonjour::TxtKeys &txt_keys)
{ {
const size_t size = rr.data.size(); const size_t size = rr.data.size();
if (size < 2) { if (size < 2) {
@ -328,11 +331,21 @@ struct DnsRR_TXT
} }
++it; ++it;
std::string value(val_size, ' '); const auto it_end = it + val_size;
std::copy(it, it + val_size, value.begin()); const auto it_eq = std::find(it, it_end, '=');
res.values.push_back(std::move(value)); if (it_eq > it && it_eq < it_end - 1) {
std::string key(it_eq - it, ' ');
std::copy(it, it_eq, key.begin());
it += val_size; if (txt_keys.find(key) != txt_keys.end() || key == "path") {
// This key-value has been requested for
std::string value(it_end - it_eq - 1, ' ');
std::copy(it_eq + 1, it_end, value.begin());
res.data.insert(std::make_pair(std::move(key), std::move(value)));
}
}
it = it_end;
} }
return std::move(res); return std::move(res);
@ -389,7 +402,7 @@ struct DnsMessage
DnsSDMap sdmap; DnsSDMap sdmap;
static optional<DnsMessage> decode(const std::vector<char> &buffer) static optional<DnsMessage> decode(const std::vector<char> &buffer, const Bonjour::TxtKeys &txt_keys)
{ {
const auto size = buffer.size(); const auto size = buffer.size();
if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) { if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
@ -414,14 +427,15 @@ struct DnsMessage
if (!rr) { if (!rr) {
return boost::none; return boost::none;
} else { } else {
res.parse_rr(buffer, std::move(*rr), dataoffset); res.parse_rr(buffer, std::move(*rr), dataoffset, txt_keys);
} }
} }
return std::move(res); return std::move(res);
} }
private: private:
void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset) void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys)
{ {
switch (rr.type) { switch (rr.type) {
case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break; case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break;
@ -432,7 +446,7 @@ private:
break; break;
} }
case DnsRR_TXT::TAG: { case DnsRR_TXT::TAG: {
auto txt = DnsRR_TXT::decode(rr); auto txt = DnsRR_TXT::decode(rr, txt_keys);
if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); } if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
break; break;
} }
@ -442,26 +456,28 @@ private:
std::ostream& operator<<(std::ostream &os, const DnsMessage &msg) std::ostream& operator<<(std::ostream &os, const DnsMessage &msg)
{ {
os << "DnsMessage(ID: " << msg.header.id << ", " os << boost::format("DnsMessage(ID: %1%, Q: %2%, A: %3%, AAAA: %4%, services: [")
<< "Q: " << (msg.question ? msg.question->name.c_str() : "none") << ", " % msg.header.id
<< "A: " << (msg.rr_a ? msg.rr_a->ip.to_string() : "none") << ", " % (msg.question ? msg.question->name.c_str() : "none")
<< "AAAA: " << (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none") << ", " % (msg.rr_a ? msg.rr_a->ip.to_string() : "none")
<< "services: ["; % (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none");
enum { SRV_PRINT_MAX = 3 }; enum { SRV_PRINT_MAX = 3 };
unsigned i = 0; unsigned i = 0;
for (const auto &sdpair : msg.sdmap) { for (const auto &sdpair : msg.sdmap) {
os << sdpair.first << ", "; if (i > 0) { os << ", "; }
if (++i >= SRV_PRINT_MAX) { if (i < SRV_PRINT_MAX) {
os << sdpair.first;
} else {
os << "..."; os << "...";
break; break;
} }
i++;
} }
os << "])"; return os << "])";
return os;
} }
@ -525,8 +541,9 @@ optional<BonjourRequest> BonjourRequest::make(const std::string &service, const
struct Bonjour::priv struct Bonjour::priv
{ {
const std::string service; const std::string service;
const std::string protocol; std::string protocol;
const std::string service_dn; std::string service_dn;
TxtKeys txt_keys;
unsigned timeout; unsigned timeout;
unsigned retries; unsigned retries;
@ -535,19 +552,18 @@ struct Bonjour::priv
Bonjour::ReplyFn replyfn; Bonjour::ReplyFn replyfn;
Bonjour::CompleteFn completefn; Bonjour::CompleteFn completefn;
priv(std::string service, std::string protocol); priv(std::string &&service);
std::string strip_service_dn(const std::string &service_name) const; std::string strip_service_dn(const std::string &service_name) const;
void udp_receive(udp::endpoint from, size_t bytes); void udp_receive(udp::endpoint from, size_t bytes);
void lookup_perform(); void lookup_perform();
}; };
Bonjour::priv::priv(std::string service, std::string protocol) : Bonjour::priv::priv(std::string &&service)
service(std::move(service)), : service(std::move(service))
protocol(std::move(protocol)), , protocol("tcp")
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()), , timeout(10)
timeout(10), , retries(1)
retries(1)
{ {
buffer.resize(DnsMessage::MAX_SIZE); buffer.resize(DnsMessage::MAX_SIZE);
} }
@ -573,13 +589,13 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
} }
buffer.resize(bytes); buffer.resize(bytes);
const auto dns_msg = DnsMessage::decode(buffer); auto dns_msg = DnsMessage::decode(buffer, txt_keys);
if (dns_msg) { if (dns_msg) {
asio::ip::address ip = from.address(); asio::ip::address ip = from.address();
if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; } if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; } else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; }
for (const auto &sdpair : dns_msg->sdmap) { for (auto &sdpair : dns_msg->sdmap) {
if (! sdpair.second.srv) { if (! sdpair.second.srv) {
continue; continue;
} }
@ -590,20 +606,12 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
std::string path; std::string path;
std::string version; std::string version;
BonjourReply::TxtData txt_data;
if (sdpair.second.txt) { if (sdpair.second.txt) {
static const std::string tag_path = "path="; txt_data = std::move(sdpair.second.txt->data);
static const std::string tag_version = "version=";
for (const auto &value : sdpair.second.txt->values) {
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
path = std::move(value.substr(tag_path.size()));
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
version = std::move(value.substr(tag_version.size()));
}
}
} }
BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version)); BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(txt_data));
replyfn(std::move(reply)); replyfn(std::move(reply));
} }
} }
@ -611,6 +619,8 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
void Bonjour::priv::lookup_perform() void Bonjour::priv::lookup_perform()
{ {
service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str();
const auto brq = BonjourRequest::make(service, protocol); const auto brq = BonjourRequest::make(service, protocol);
if (!brq) { if (!brq) {
return; return;
@ -671,21 +681,29 @@ void Bonjour::priv::lookup_perform()
// API - public part // API - public part
BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version) : BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, BonjourReply::TxtData txt_data)
ip(std::move(ip)), : ip(std::move(ip))
port(port), , port(port)
service_name(std::move(service_name)), , service_name(std::move(service_name))
hostname(std::move(hostname)), , hostname(std::move(hostname))
path(path.empty() ? std::move(std::string("/")) : std::move(path)), , txt_data(std::move(txt_data))
version(version.empty() ? std::move(std::string("Unknown")) : std::move(version))
{ {
std::string proto; std::string proto;
std::string port_suffix; std::string port_suffix;
if (port == 443) { proto = "https://"; } if (port == 443) { proto = "https://"; }
if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); } if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); }
if (this->path[0] != '/') { this->path.insert(0, 1, '/'); }
std::string path = this->path();
if (path[0] != '/') { path.insert(0, 1, '/'); }
full_address = proto + ip.to_string() + port_suffix; full_address = proto + ip.to_string() + port_suffix;
if (this->path != "/") { full_address += path; } if (path != "/") { full_address += path; }
txt_data["path"] = std::move(path);
}
std::string BonjourReply::path() const
{
const auto it = txt_data.find("path");
return it != txt_data.end() ? it->second : std::string("/");
} }
bool BonjourReply::operator==(const BonjourReply &other) const bool BonjourReply::operator==(const BonjourReply &other) const
@ -707,14 +725,22 @@ bool BonjourReply::operator<(const BonjourReply &other) const
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply) std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
{ {
os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", " os << boost::format("BonjourReply(%1%, %2%, %3%, %4%")
<< reply.hostname << ", " << reply.path << ", " << reply.version << ")"; % reply.ip.to_string()
return os; % reply.service_name
% reply.hostname
% reply.full_address;
for (const auto &kv : reply.txt_data) {
os << boost::format(", %1%=%2%") % kv.first % kv.second;
}
return os << ')';
} }
Bonjour::Bonjour(std::string service, std::string protocol) : Bonjour::Bonjour(std::string service)
p(new priv(std::move(service), std::move(protocol))) : p(new priv(std::move(service)))
{} {}
Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {} Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {}
@ -726,6 +752,18 @@ Bonjour::~Bonjour()
} }
} }
Bonjour& Bonjour::set_protocol(std::string protocol)
{
if (p) { p->protocol = std::move(protocol); }
return *this;
}
Bonjour& Bonjour::set_txt_keys(TxtKeys txt_keys)
{
if (p) { p->txt_keys = std::move(txt_keys); }
return *this;
}
Bonjour& Bonjour::set_timeout(unsigned timeout) Bonjour& Bonjour::set_timeout(unsigned timeout)
{ {
if (p) { p->timeout = timeout; } if (p) { p->timeout = timeout; }

View File

@ -4,6 +4,8 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
#include <set>
#include <unordered_map>
#include <functional> #include <functional>
#include <boost/asio/ip/address.hpp> #include <boost/asio/ip/address.hpp>
@ -13,16 +15,24 @@ namespace Slic3r {
struct BonjourReply struct BonjourReply
{ {
typedef std::unordered_map<std::string, std::string> TxtData;
boost::asio::ip::address ip; boost::asio::ip::address ip;
uint16_t port; uint16_t port;
std::string service_name; std::string service_name;
std::string hostname; std::string hostname;
std::string full_address; std::string full_address;
std::string path;
std::string version; TxtData txt_data;
BonjourReply() = delete; BonjourReply() = delete;
BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version); BonjourReply(boost::asio::ip::address ip,
uint16_t port,
std::string service_name,
std::string hostname,
TxtData txt_data);
std::string path() const;
bool operator==(const BonjourReply &other) const; bool operator==(const BonjourReply &other) const;
bool operator<(const BonjourReply &other) const; bool operator<(const BonjourReply &other) const;
@ -39,11 +49,17 @@ public:
typedef std::shared_ptr<Bonjour> Ptr; typedef std::shared_ptr<Bonjour> Ptr;
typedef std::function<void(BonjourReply &&)> ReplyFn; typedef std::function<void(BonjourReply &&)> ReplyFn;
typedef std::function<void()> CompleteFn; typedef std::function<void()> CompleteFn;
typedef std::set<std::string> TxtKeys;
Bonjour(std::string service, std::string protocol = "tcp"); Bonjour(std::string service);
Bonjour(Bonjour &&other); Bonjour(Bonjour &&other);
~Bonjour(); ~Bonjour();
// Set requested service protocol, "tcp" by default
Bonjour& set_protocol(std::string protocol);
// Set which TXT key-values should be collected
// Note that "path" is always collected
Bonjour& set_txt_keys(TxtKeys txt_keys);
Bonjour& set_timeout(unsigned timeout); Bonjour& set_timeout(unsigned timeout);
Bonjour& set_retries(unsigned retries); Bonjour& set_retries(unsigned retries);
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent). // ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).