@@ -17,6 +17,8 @@ namespace qnc::xml {
1717
1818using  namespace  qnc ::upnp; 
1919
20+ template  <> DeviceModel              ¤tObject (DeviceDescription       &device)  { return  device.model ; }
21+ template  <> DeviceManufacturer       ¤tObject (DeviceDescription       &device)  { return  device.manufacturer ; }
2022template  <> IconDescription          ¤tObject (DeviceDescription       &device)  { return  device.icons .last (); }
2123template  <> ServiceDescription       ¤tObject (DeviceDescription       &device)  { return  device.services .last (); }
2224template  <> ActionDescription        ¤tObject (ControlPointDescription &service) { return  service.actions .last (); }
@@ -160,10 +162,8 @@ class DetailLoader : public QObject
160162        , m_device{std::move (device)}
161163    {}
162164
163-     void  fetchIcons ();
164-     void  parseServiceDescription ();
165-     void  parseControlDescription ();
166-     void  parseEventingDescription ();
165+     void  loadIcons ();
166+     void  loadServiceDescription ();
167167
168168    const  DeviceDescription &device () const  { return  m_device; }
169169    QList<QNetworkReply *> pendingReplies () const  { return  m_pendingReplies; }
@@ -172,6 +172,9 @@ class DetailLoader : public QObject
172172    void  finished ();
173173
174174private: 
175+     template  <class  Container >
176+     void  load (const  char  *topic, Container *container);
177+ 
175178    void  checkIfAllRequestsFinished ();
176179
177180    QNetworkAccessManager *const  m_networkAccessManager;
@@ -216,16 +219,16 @@ QList<DeviceDescription> DeviceDescriptionParser::read(const QUrl &deviceUrl, St
216219            State::Device, {
217220                {u" deviceType" 
218221                {u" friendlyName" 
219-                 {u" manufacturer" DeviceDescription::manufacturerName >(device)},
220-                 {u" manufacturerURL" DeviceDescription::manufacturerUrl >(device)},
221-                 {u" modelDescription" DeviceDescription::modelDescription >(device)},
222-                 {u" modelName" DeviceDescription::modelName >(device)},
223-                 {u" modelNumber" DeviceDescription::modelNumber >(device)},
224-                 {u" modelURL" DeviceDescription::modelUrl >(device)},
222+                 {u" manufacturer" DeviceManufacturer::name >(device)},
223+                 {u" manufacturerURL" DeviceManufacturer::url >(device)},
224+                 {u" modelDescription" DeviceModel::description >(device)},
225+                 {u" modelName" DeviceModel::name >(device)},
226+                 {u" modelNumber" DeviceModel::number >(device)},
227+                 {u" modelURL" DeviceModel::url >(device)},
225228                {u" presentationURL" 
226229                {u" serialNumber" 
227230                {u" UDN" 
228-                 {u" UPC" DeviceDescription ::universalProductCode>(device)},
231+                 {u" UPC" DeviceModel ::universalProductCode>(device)},
229232
230233                {u" deviceList" 
231234                {u" iconList" 
@@ -304,6 +307,7 @@ std::optional<ControlPointDescription> ControlPointDescriptionParser::read(State
304307            State::Argument, {
305308                {u" name" 
306309                {u" direction" 
310+                 {u" retval" 
307311                {u" relatedStateVariable" 
308312            }
309313        }, {
@@ -339,25 +343,67 @@ std::optional<ControlPointDescription> ControlPointDescriptionParser::read(State
339343    return  service;
340344}
341345
342- void  DetailLoader::fetchIcons ()
346+ QUrl itemUrl (const  IconDescription &icon)
347+ {
348+     return  icon.url ;
349+ }
350+ 
351+ QUrl itemUrl (const  ServiceDescription &service)
352+ {
353+     return  service.scpdUrl ;
354+ }
355+ 
356+ bool  itemHasData (const  IconDescription &icon)
357+ {
358+     return  !icon.data .isEmpty ();
359+ }
360+ 
361+ bool  itemHasData (const  ServiceDescription &service)
362+ {
363+     return  service.scpd .has_value ();
364+ }
365+ 
366+ void  updateItem (IconDescription &icon, QNetworkReply *reply)
367+ {
368+     icon.data  = reply->readAll ();
369+ }
370+ 
371+ void  updateItem (ServiceDescription &service, QNetworkReply *reply)
343372{
344-     for  (auto  icon = m_device.icons .begin (); icon != m_device.icons .end (); ++icon) {
373+     service.scpd  = ControlPointDescription::parse (reply);
374+ }
375+ 
376+ template  <class  Container >
377+ void  DetailLoader::load (const  char  *topic, Container *container)
378+ {
379+     const  auto  first = container->cbegin ();
380+     const  auto  last  = container->cend ();
381+ 
382+     for  (auto  it = first; it != last; ++it) {
383+         const  auto  &url = itemUrl (*it);
345384
346-         if  (icon-> url .isEmpty ())
385+         if  (url.isEmpty ())
347386            continue ;
348-         if  (!icon-> data . isEmpty ( ))
387+         if  (itemHasData (*it ))
349388            continue ;
350389
351-         const  auto  index = icon - m_device.icons .begin ();
352-         const  auto  reply = m_networkAccessManager->get (QNetworkRequest{icon->url });
390+         const  auto  request = QNetworkRequest{m_device.url .resolved (url)};
391+         const  auto  reply   = m_networkAccessManager->get (request);
392+         const  auto  index   = it - first;
353393
354-         connect (reply, &QNetworkReply::finished, this , [this , index, reply] {
394+         qCInfo (lcResolver,
395+                " Downloading %s for %ls from <%ls>" 
396+                qUtf16Printable (m_device.uniqueDeviceName ),
397+                qUtf16Printable (request.url ().toDisplayString ()));
398+ 
399+         connect (reply, &QNetworkReply::finished, this , [this , container, index, reply] {
355400            if  (reply->error () != QNetworkReply::NoError) {
356-                 qCWarning (lcResolver, " Could not download icon from %ls: %ls" 
401+                 qCWarning (lcResolver, " Could not download detail for %ls from %ls: %ls" 
402+                           qUtf16Printable (m_device.uniqueDeviceName ),
357403                          qUtf16Printable (reply->url ().toDisplayString ()),
358404                          qUtf16Printable (reply->errorString ()));
359405            } else  {
360-                 m_device. icons [index]. data  =  reply-> readAll ( );
406+                 updateItem ((*container) [index],  reply);
361407            }
362408
363409            checkIfAllRequestsFinished ();
@@ -367,16 +413,14 @@ void DetailLoader::fetchIcons()
367413    }
368414}
369415
370- void  DetailLoader::parseServiceDescription ()
371- {
372- }
373- 
374- void  DetailLoader::parseControlDescription ()
416+ void  DetailLoader::loadIcons ()
375417{
418+     load (" icons" icons );
376419}
377420
378- void  DetailLoader::parseEventingDescription  ()
421+ void  DetailLoader::loadServiceDescription  ()
379422{
423+     load (" services" services );
380424}
381425
382426void  DetailLoader::checkIfAllRequestsFinished ()
@@ -385,6 +429,10 @@ void DetailLoader::checkIfAllRequestsFinished()
385429        if  (reply && reply->isRunning ())
386430            return ;
387431
432+     qCInfo (lcResolver,
433+            " All details downloaded for %ls" 
434+            qUtf16Printable (m_device.uniqueDeviceName ));
435+ 
388436    emit finished ();
389437}
390438
@@ -444,16 +492,33 @@ QNetworkAccessManager *Resolver::networkAccessManager() const
444492
445493void  Resolver::onServiceFound (const  ssdp::ServiceDescription &service)
446494{
447-     if  (m_networkAccessManager) {
448-         const  auto  &locationList = service.locations ();
495+     const  auto  &locationList = service.locations ();
496+ 
497+     for  (const  auto  &url : locationList) {
498+         if  (m_networkAccessManager) {
499+             qCInfo (lcResolver,
500+                    " Downloading device description for %ls from <%ls>" 
501+                    qUtf16Printable (service.name ()), qUtf16Printable (url.toDisplayString ()));
449502
450-         for  (const  auto  &url: locationList) {
451503            const  auto  reply = m_networkAccessManager->get (QNetworkRequest{url});
504+ 
452505            connect (reply, &QNetworkReply::finished, this , [this , reply] {
453506                onDeviceDescriptionReceived (reply);
454507            });
455508
456509            m_pendingReplies += reply;
510+         } else  {
511+             qCInfo (lcResolver,
512+                    " Directly reporting %ls without downloading from <%ls>" 
513+                    qUtf16Printable (service.name ()), qUtf16Printable (url.toDisplayString ()));
514+ 
515+             auto  device = DeviceDescription{};
516+ 
517+             device.url               = url;
518+             device.deviceType        = service.type ();
519+             device.uniqueDeviceName  = service.name ();
520+ 
521+             emit deviceFound (device);
457522        }
458523    }
459524}
@@ -462,22 +527,28 @@ void Resolver::onDeviceDescriptionReceived(QNetworkReply *reply)
462527{
463528    m_pendingReplies.removeOne (reply);
464529
465-     qInfo () << reply->url () << reply->error () << reply->errorString ();
530+     if  (reply->error () == QNetworkReply::NoError) {
531+         qCInfo (lcResolver,
532+                " Device description received from <%ls>" 
533+                qUtf16Printable (reply->url ().toDisplayString ()));
534+     } else  {
535+         qCWarning (lcResolver,
536+                   " Could not download device description received from <%ls>: %ls" 
537+                   qUtf16Printable (reply->url ().toDisplayString ()),
538+                   qUtf16Printable (reply->errorString ()));
539+     }
466540
467541    auto  deviceList = DeviceDescription::parse (reply, reply->url ());
468542
469543    for  (auto  &device : deviceList) {
470544        if  (m_behaviors && m_networkAccessManager) {
471545            const  auto  detailLoader = new  DetailLoader{std::move (device), m_networkAccessManager, this };
472546
473-             if  (m_behaviors.testFlag (Behavior::FetchIcons))
474-                 detailLoader->fetchIcons ();
475-             if  (m_behaviors.testFlag (Behavior::ParseServiceDescription))
476-                 detailLoader->parseServiceDescription ();
477-             if  (m_behaviors.testFlag (Behavior::ParseControlDescription))
478-                 detailLoader->parseControlDescription ();
479-             if  (m_behaviors.testFlag (Behavior::ParseEventingDescription))
480-                 detailLoader->parseEventingDescription ();
547+             if  (m_behaviors.testFlag (Behavior::LoadIcons))
548+                 detailLoader->loadIcons ();
549+ 
550+             if  (m_behaviors.testFlag (Behavior::LoadServiceDescription))
551+                 detailLoader->loadServiceDescription ();
481552
482553            m_pendingReplies += detailLoader->pendingReplies ();
483554
@@ -514,4 +585,87 @@ std::optional<ControlPointDescription> ControlPointDescription::parse(QIODevice
514585
515586} //  namespace qnc::upnp
516587
588+ QDebug operator <<(QDebug debug, const  qnc::upnp::DeviceDescription &device)
589+ {
590+     const  auto  _ = QDebugStateSaver{debug};
591+ 
592+     const  auto  verbose = (debug.verbosity () > QDebug::DefaultVerbosity);
593+     const  auto  silent  = (debug.verbosity () < QDebug::DefaultVerbosity);
594+ 
595+     if  (verbose)
596+         debug.nospace () << device.staticMetaObject .className ();
597+ 
598+     debug << " (udn=" uniqueDeviceName 
599+           << " , type=" deviceType ;
600+ 
601+     if  (verbose)
602+         debug << " , spec=" specVersion ;
603+ 
604+     if  (!device.displayName .isEmpty ())
605+         debug << " , name=" displayName ;
606+ 
607+     debug << " , manufacturer=" manufacturer 
608+           << " , model=" model ;
609+ 
610+     if  (!device.presentationUrl .isEmpty ())
611+         debug << " , presentationUrl=" presentationUrl ;
612+     if  (!device.serialNumber .isEmpty ())
613+         debug << " , serialNumber=" serialNumber ;
614+     if  (!device.url .isEmpty ())
615+         debug << " , url=" url ;
616+ 
617+     if  (!silent) {
618+          //      QList<IconDescription>    icons = {};
619+          //      QList<ServiceDescription> services = {};
620+     }
621+ 
622+     return  debug << ' )' 
623+ }
624+ 
625+ QDebug operator <<(QDebug debug, const  qnc::upnp::DeviceManufacturer &manufacturer)
626+ {
627+     const  auto  _ = QDebugStateSaver{debug};
628+ 
629+     const  auto  verbose = (debug.verbosity () > QDebug::DefaultVerbosity);
630+     const  auto  silent  = (debug.verbosity () < QDebug::DefaultVerbosity);
631+ 
632+     if  (verbose)
633+         debug.nospace () << manufacturer.staticMetaObject .className ();
634+ 
635+     debug << ' (' 
636+ 
637+     if  (!manufacturer.name .isEmpty ())
638+         debug << " , name=" name ;
639+     if  (!silent && !manufacturer.url .isEmpty ())
640+         debug << " , url=" url ;
641+ 
642+     return  debug << ' )' 
643+ }
644+ 
645+ QDebug operator <<(QDebug debug, const  qnc::upnp::DeviceModel &model)
646+ {
647+     const  auto  _ = QDebugStateSaver{debug};
648+ 
649+     const  auto  verbose = (debug.verbosity () > QDebug::DefaultVerbosity);
650+     const  auto  silent  = (debug.verbosity () < QDebug::DefaultVerbosity);
651+ 
652+     if  (verbose)
653+         debug.nospace () << model.staticMetaObject .className ();
654+ 
655+     debug << ' (' 
656+ 
657+     if  (!model.universalProductCode .isEmpty ())
658+         debug << " , universalProductCode=" universalProductCode ;
659+     if  (!model.description .isEmpty ())
660+         debug << " , description=" description ;
661+     if  (!model.name .isEmpty ())
662+         debug << " , name=" name ;
663+     if  (!model.number .isEmpty ())
664+         debug << " , number=" number ;
665+     if  (!silent && !model.url .isEmpty ())
666+         debug << " , url=" url ;
667+ 
668+     return  debug << ' )' 
669+ }
670+ 
517671#include  " upnpresolver.moc" 
0 commit comments