MythTV  master
mythudplistener.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QCoreApplication>
3 #include <QDomDocument>
4 #include <QHostAddress>
5 
6 // MythTV
9 #include "mythmainwindow.h"
10 #include "mythudplistener.h"
11 
12 // Std
13 #include <thread>
14 
15 #define LOC QString("UDPListener: ")
16 
18 {
20 }
21 
23 {
24  DoEnable(false);
25 }
26 
27 void MythUDPListener::DoEnable(bool Enable)
28 {
29  if (Enable)
30  {
31  if (m_socketPool)
32  return;
33 
34  LOG(VB_GENERAL, LOG_INFO, LOC + "Enabling");
35  m_socketPool = new ServerPool(this);
37  QList<QHostAddress> addrs = ServerPool::DefaultListen();
39  auto port = static_cast<uint16_t>(gCoreContext->GetNumSetting("UDPNotifyPort", 0));
40  if (!m_socketPool->bind(addrs, port, false))
41  {
42  delete m_socketPool;
43  m_socketPool = nullptr;
44  }
45  }
46  else
47  {
48  if (!m_socketPool)
49  return;
50 
51  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling");
53  delete m_socketPool;
54  m_socketPool = nullptr;
55  }
56 }
57 
58 void MythUDPListener::Process(const QByteArray& Buffer, const QHostAddress& /*Sender*/,
59  quint16 /*SenderPort*/)
60 {
61  QString errormsg;
62  int line = 0;
63  int column = 0;
64  QDomDocument doc;
65 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
66  if (!doc.setContent(Buffer, false, &errormsg, &line, &column))
67  {
68  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error parsing xml: Line: %1 Column: %2 Error: %3")
69  .arg(line).arg(column).arg(errormsg));
70  return;
71  }
72 #else
73  auto parseResult = doc.setContent(Buffer);
74  if (!parseResult)
75  {
76  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error parsing xml: Line: %1 Column: %2 Error: %3")
77  .arg(parseResult.errorLine).arg(parseResult.errorColumn).arg(parseResult.errorMessage));
78  return;
79  }
80 #endif
81 
82  auto element = doc.documentElement();
83  bool notification = false;
84  if (!element.isNull())
85  {
86  if (element.tagName() != "mythmessage" && element.tagName() != "mythnotification")
87  {
88  LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown UDP packet (not <mythmessage> XML)");
89  return;
90  }
91 
92  if (element.tagName() == "mythnotification")
93  notification = true;
94 
95  if (auto version = element.attribute("version", ""); version.isEmpty())
96  {
97  LOG(VB_GENERAL, LOG_ERR, LOC + "<mythmessage> missing 'version' attribute");
98  return;
99  }
100  }
101 
102  QString msg;
103  std::chrono::seconds timeout = 0s;
104  QString image;
105  QString origin;
106  QString description;
107  QString extra;
108  QString progress_text;
109  float progress = -1.0F;
110  bool fullscreen = false;
111  bool error = false;
112  int visibility = 0;
113  QString type = "normal";
114 
115  auto node = element.firstChild();
116  while (!node.isNull())
117  {
118  auto dom = node.toElement();
119  if (!dom.isNull())
120  {
121  auto tagname = dom.tagName();
122  if (tagname == "text")
123  msg = dom.text();
124  else if (tagname == "timeout")
125  timeout = std::chrono::seconds(dom.text().toUInt());
126  else if (notification && tagname == "image")
127  image = dom.text();
128  else if (notification && tagname == "origin")
129  origin = dom.text();
130  else if (notification && tagname == "description")
131  description = dom.text();
132  else if (notification && tagname == "extra")
133  extra = dom.text();
134  else if (notification && tagname == "progress_text")
135  progress_text = dom.text();
136  else if (notification && tagname == "fullscreen")
137  fullscreen = dom.text().toLower() == "true";
138  else if (notification && tagname == "error")
139  error = dom.text().toLower() == "true";
140  else if (tagname == "visibility")
141  visibility = dom.text().toInt();
142  else if (tagname == "type")
143  type = dom.text();
144  else if (notification && tagname == "progress")
145  {
146  bool ok = false;
147  if (progress = dom.text().toFloat(&ok); !ok)
148  progress = -1.0F;
149  }
150  else
151  {
152  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown element: %1")
153  .arg(tagname));
154  }
155  }
156  node = node.nextSibling();
157  }
158 
159  if (!msg.isEmpty() || !image.isEmpty() || !extra.isEmpty())
160  {
161  LOG(VB_GENERAL, LOG_INFO, QString("Received %1 '%2', timeout %3")
162  .arg(notification ? "notification" : "message",
163  msg, QString::number(timeout.count())));
164  if (timeout > 1000s)
165  timeout = notification ? 5s : 0s;
166  if (notification)
167  {
168  origin = origin.isEmpty() ? tr("UDP Listener") : origin;
171  msg, origin, description, image, extra,
172  progress_text, progress, timeout,
173  fullscreen, static_cast<VNMask>(visibility));
174  }
175  else
176  {
177  QStringList args(QString::number(timeout.count()));
178  qApp->postEvent(GetMythMainWindow(), new MythEvent(MythEvent::kMythUserMessage, msg, args));
179  }
180  }
181 }
182 
183 void MythUDP::EnableUDPListener(bool Enable)
184 {
186  {
187  emit Instance().m_listener->EnableUDPListener(Enable);
188  }
189  else
190  {
191  LOG(VB_GENERAL, LOG_ERR, LOC +
192  "EnableUDPListener called after MythUDPListener instance is deleted");
193  }
194 }
195 
197 {
198  static MythUDP s_instance;
199  return s_instance;
200 }
201 
203  : m_listener(new MythUDPListener),
204  m_thread(new MThread("UDP"))
205 {
206  m_listener->moveToThread(m_thread->qthread());
207  m_thread->start();
208  do { std::this_thread::sleep_for(5us); }
209  while (!m_thread->qthread()->isRunning());
210 }
211 
213 {
214  if (m_thread)
215  {
216  m_thread->quit();
217  m_thread->wait();
218  }
219  delete m_thread;
220  delete m_listener;
221 }
222 
224 {
225  if (Instance().m_thread)
226  {
227  Instance().m_thread->quit();
228  Instance().m_thread->wait();
229  delete Instance().m_thread;
230  Instance().m_thread = nullptr;
231  }
232 
233  delete Instance().m_listener;
234  Instance().m_listener = nullptr;
235 }
build_compdb.args
args
Definition: build_compdb.py:11
MythUDP::m_thread
MThread * m_thread
Definition: mythudplistener.h:46
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:102
MythEvent::kMythUserMessage
static const Type kMythUserMessage
Definition: mythevent.h:80
MThread::quit
void quit(void)
calls exit(0)
Definition: mthread.cpp:295
error
static void error(const char *str,...)
Definition: vbi.cpp:37
ShowNotification
void ShowNotification(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
Definition: mythnotificationcenter.cpp:1437
ServerPool::newDatagram
void newDatagram(QByteArray, QHostAddress, quint16)
ServerPool::close
void close(void)
Definition: serverpool.cpp:374
MythUDP
Definition: mythudplistener.h:33
progress
bool progress
Definition: mythcommflag.cpp:69
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
ServerPool
Manages a collection of sockets listening on different ports.
Definition: serverpool.h:59
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythNotification::TypeFromString
static Type TypeFromString(const QString &Type)
Definition: mythnotification.cpp:236
MythUDPListener::Process
static void Process(const QByteArray &Buffer, const QHostAddress &, quint16)
Definition: mythudplistener.cpp:58
mythlogging.h
MythUDP::Instance
static MythUDP & Instance()
Definition: mythudplistener.cpp:196
LOC
#define LOC
Definition: mythudplistener.cpp:15
VNMask
unsigned int VNMask
Definition: mythnotification.h:27
MThread::qthread
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:233
MythNotification::kError
static const Type kError
Definition: mythnotification.h:35
ServerPool::DefaultListen
static QList< QHostAddress > DefaultListen(void)
Definition: serverpool.cpp:305
MythUDPListener::EnableUDPListener
void EnableUDPListener(bool Enable=true)
MythUDPListener
Definition: mythudplistener.h:11
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
ServerPool::bind
bool bind(QList< QHostAddress > addrs, quint16 port, bool requireall=true)
Definition: serverpool.cpp:495
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:916
MythUDP::EnableUDPListener
static void EnableUDPListener(bool Enable=true)
Definition: mythudplistener.cpp:183
MythUDP::~MythUDP
~MythUDP()
Definition: mythudplistener.cpp:212
Buffer
Definition: MythExternControl.h:36
MythUDPListener::MythUDPListener
MythUDPListener()
Definition: mythudplistener.cpp:17
mythcorecontext.h
mythudplistener.h
MythUDP::m_listener
MythUDPListener * m_listener
Definition: mythudplistener.h:45
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
MythUDPListener::~MythUDPListener
~MythUDPListener() override
Definition: mythudplistener.cpp:22
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
MythUDPListener::m_socketPool
ServerPool * m_socketPool
Definition: mythudplistener.h:30
uint16_t
unsigned short uint16_t
Definition: iso6937tables.h:3
MythUDP::MythUDP
MythUDP()
Definition: mythudplistener.cpp:202
ServerPool::DefaultBroadcast
static QList< QHostAddress > DefaultBroadcast(void)
Definition: serverpool.cpp:339
mythmainwindow.h
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:77
MythUDPListener::DoEnable
void DoEnable(bool Enable=true)
Definition: mythudplistener.cpp:27
MythUDP::StopUDPListener
static void StopUDPListener()
Definition: mythudplistener.cpp:223