00001
00002
00003
00004
00005 #include <unistd.h>
00006 #include <sys/wait.h>
00007 #include <cmath>
00008 #include <netinet/in.h>
00009 #include <assert.h>
00010
00011
00012 #include <qapplication.h>
00013 #include <qregexp.h>
00014
00015
00016 #include "mythtv/mythcontext.h"
00017 #include "mythtv/mythdialogs.h"
00018 #include "mythtv/util.h"
00019
00020
00021 #include "zmclient.h"
00022
00023
00024 #define ZM_PROTOCOL_VERSION "6"
00025
00026 #define BUFFER_SIZE (2048*1536*3)
00027
00028 ZMClient::ZMClient()
00029 : QObject(NULL, "ZMClient"),
00030 m_socket(NULL),
00031 m_socketLock(true),
00032 m_hostname("localhost"),
00033 m_port(6548),
00034 m_bConnected(false),
00035 m_retryTimer(new QTimer(this)),
00036 m_zmclientReady(false)
00037 {
00038 connect(m_retryTimer, SIGNAL(timeout()), this, SLOT(restartConnection()));
00039 }
00040
00041 bool ZMClient::m_server_unavailable = false;
00042 class ZMClient *ZMClient::m_zmclient = NULL;
00043
00044 class ZMClient *ZMClient::get(void)
00045 {
00046 if (m_zmclient == NULL && m_server_unavailable == false)
00047 m_zmclient = new ZMClient;
00048 return m_zmclient;
00049 }
00050
00051 bool ZMClient::setupZMClient(void)
00052 {
00053 QString zmserver_host;
00054 int zmserver_port;
00055
00056 if (m_zmclient)
00057 {
00058 delete m_zmclient;
00059 m_zmclient = NULL;
00060 m_server_unavailable = false;
00061 }
00062
00063 zmserver_host = gContext->GetSetting("ZoneMinderServerIP", "localhost");
00064 zmserver_port = gContext->GetNumSetting("ZoneMinderServerPort", 6548);
00065
00066 class ZMClient *zmclient = ZMClient::get();
00067 if (zmclient->connectToHost(zmserver_host, zmserver_port) == false)
00068 {
00069 delete m_zmclient;
00070 m_zmclient = NULL;
00071 m_server_unavailable = false;
00072 return false;
00073 }
00074
00075 return true;
00076 }
00077
00078 bool ZMClient::connectToHost(const QString &lhostname, unsigned int lport)
00079 {
00080 QMutexLocker locker(&m_socketLock);
00081
00082 m_hostname = lhostname;
00083 m_port = lport;
00084
00085 m_bConnected = false;
00086 int count = 0;
00087 do
00088 {
00089 ++count;
00090
00091 VERBOSE(VB_GENERAL, QString("Connecting to zm server: "
00092 "%1:%2 (try %3 of 10)").arg(m_hostname).arg(m_port)
00093 .arg(count));
00094 if (m_socket)
00095 {
00096 m_socket->DownRef();
00097 m_socket = NULL;
00098 }
00099
00100 m_socket = new MythSocket();
00101
00102 if (!m_socket->connect(m_hostname, m_port))
00103 {
00104 m_socket->DownRef();
00105 m_socket = NULL;
00106 }
00107 else
00108 {
00109 m_zmclientReady = true;
00110 m_bConnected = true;
00111 }
00112
00113 usleep(500000);
00114
00115 } while (count < 10 && !m_bConnected);
00116
00117 if (!m_bConnected)
00118 {
00119 MythPopupBox::showOkPopup(gContext->GetMainWindow(), "Connection failure",
00120 tr("Cannot connect to the mythzmserver - Is it running? "
00121 "Have you set the correct IP and port in the settings?"));
00122 }
00123
00124
00125 if (m_bConnected && !checkProtoVersion())
00126 {
00127 m_zmclientReady = false;
00128 m_bConnected = false;
00129 }
00130
00131 if (m_bConnected == false)
00132 m_server_unavailable = true;
00133
00134 return m_bConnected;
00135 }
00136
00137 bool ZMClient::sendReceiveStringList(QStringList &strList)
00138 {
00139 bool ok = false;
00140 if (m_bConnected)
00141 {
00142 m_socket->writeStringList(strList);
00143 ok = m_socket->readStringList(strList, false);
00144 }
00145
00146 if (!ok)
00147 {
00148 VERBOSE(VB_IMPORTANT, "Connection to mythzmserver lost");
00149
00150 if (!connectToHost(m_hostname, m_port))
00151 {
00152 VERBOSE(VB_IMPORTANT, "Re connection to mythzmserver failed");
00153 return false;
00154 }
00155
00156
00157 m_socket->writeStringList(strList);
00158 ok = m_socket->readStringList(strList, false);
00159 if (!ok)
00160 {
00161 m_bConnected = false;
00162 return false;
00163 }
00164 }
00165
00166
00167 if (strList[0] == "UNKNOWN_COMMAND")
00168 {
00169 VERBOSE(VB_IMPORTANT, "Somethings is getting passed to the server that it doesn't understand");
00170 return false;
00171 }
00172
00173
00174 if (strList[0].startsWith("ERROR"))
00175 {
00176 VERBOSE(VB_IMPORTANT, QString("The server failed to process the command. "
00177 "The error was:- \n\t\t\t%1").arg(strList[0]));
00178 return false;
00179 }
00180
00181
00182 if (strList[0] != "OK")
00183 return false;
00184
00185 return true;
00186 }
00187
00188 bool ZMClient::checkProtoVersion(void)
00189 {
00190 QStringList strList = "HELLO";
00191 if (!sendReceiveStringList(strList))
00192 {
00193 VERBOSE(VB_IMPORTANT, QString("Server didn't respond to 'HELLO'!!"));
00194
00195 MythPopupBox::showOkPopup(gContext->GetMainWindow(), "Connection failure",
00196 tr(QString("The mythzmserver didn't respond to our request to get the protocol version!!")));
00197 return false;
00198 }
00199
00200 if (strList[1] != ZM_PROTOCOL_VERSION)
00201 {
00202 VERBOSE(VB_IMPORTANT, QString("Protocol version mismatch (plugin=%1, "
00203 "mythzmserver=%2)").arg(ZM_PROTOCOL_VERSION).arg(strList[1]));
00204
00205 MythPopupBox::showOkPopup(gContext->GetMainWindow(), "Connection failure",
00206 tr(QString("The mythzmserver uses protocol version %1, "
00207 "but this client only understands version %2. "
00208 "Make sure you are running compatible versions of "
00209 "both the server and plugin.")
00210 .arg(strList[1]).arg(ZM_PROTOCOL_VERSION)));
00211 return false;
00212 }
00213
00214 VERBOSE(VB_IMPORTANT, QString("Using protocol version %1").arg(ZM_PROTOCOL_VERSION));
00215 return true;
00216 }
00217
00218 void ZMClient::restartConnection()
00219 {
00220
00221 m_zmclientReady = false;
00222 m_bConnected = false;
00223 m_server_unavailable = false;
00224
00225
00226 connectToHost(m_hostname, m_port);
00227 }
00228
00229 void ZMClient::shutdown()
00230 {
00231 QMutexLocker locker(&m_socketLock);
00232
00233 if (m_socket)
00234 m_socket->close();
00235
00236 m_zmclientReady = false;
00237 m_bConnected = false;
00238 }
00239
00240 ZMClient::~ZMClient()
00241 {
00242 m_zmclient = NULL;
00243
00244 if (m_socket)
00245 {
00246 m_socket->DownRef();
00247 m_zmclientReady = false;
00248 }
00249
00250 if (m_retryTimer)
00251 delete m_retryTimer;
00252 }
00253
00254 void ZMClient::getServerStatus(QString &status, QString &cpuStat, QString &diskStat)
00255 {
00256 QStringList strList = "GET_SERVER_STATUS";
00257 if (!sendReceiveStringList(strList))
00258 return;
00259
00260 status = strList[1];
00261 cpuStat = strList[2];
00262 diskStat = strList[3];
00263 }
00264
00265 void ZMClient::getMonitorStatus(vector<Monitor*> *monitorList)
00266 {
00267 monitorList->clear();
00268
00269 QStringList strList = "GET_MONITOR_STATUS";
00270 if (!sendReceiveStringList(strList))
00271 return;
00272
00273 bool bOK;
00274 int monitorCount = strList[1].toInt(&bOK);
00275 if (!bOK)
00276 {
00277 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getMonitorStatus()");
00278 return;
00279 }
00280
00281 for (int x = 0; x < monitorCount; x++)
00282 {
00283 Monitor *item = new Monitor;
00284 item->id = strList[x * 7 + 2].toInt();
00285 item->name = strList[x * 7 + 3];
00286 item->zmcStatus = strList[x * 7 + 4];
00287 item->zmaStatus = strList[x * 7 + 5];
00288 item->events = strList[x * 7 + 6].toInt();
00289 item->function = strList[x * 7 + 7];
00290 item->enabled = strList[x * 7 + 8].toInt();
00291 monitorList->push_back(item);
00292 }
00293 }
00294
00295 void ZMClient::getEventList(const QString &monitorName, bool oldestFirst,
00296 QString date, vector<Event*> *eventList)
00297 {
00298 eventList->clear();
00299
00300 QStringList strList = "GET_EVENT_LIST";
00301 strList << monitorName << (oldestFirst ? "1" : "0") ;
00302 strList << date;
00303
00304 if (!sendReceiveStringList(strList))
00305 return;
00306
00307 bool bOK;
00308 int eventCount = strList[1].toInt(&bOK);
00309 if (!bOK)
00310 {
00311 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getEventList()");
00312 return;
00313 }
00314
00315
00316 if ((int)(strList.size() - 2) / 6 != eventCount)
00317 {
00318 VERBOSE(VB_IMPORTANT, "ZMClient got a mismatch between the number of events and "
00319 "the expected number of stringlist items in getEventList()");
00320 return;
00321 }
00322
00323 QString dateFormat = gContext->GetSetting("ZoneMinderDateFormat", "ddd - dd/MM");
00324 QString timeFormat = gContext->GetSetting("ZoneMinderTimeFormat", "hh:mm:ss");
00325
00326 QStringList::Iterator it = strList.begin();
00327 it++; it++;
00328 for (int x = 0; x < eventCount; x++)
00329 {
00330 Event *item = new Event;
00331 item->eventID = (*it++).toInt();
00332 item->eventName = *it++;
00333 item->monitorID = (*it++).toInt();
00334 item->monitorName = *it++;
00335 QString sDate = *it++;
00336 QDateTime dt = QDateTime::fromString(sDate, Qt::ISODate);
00337 item->startTime = dt.toString(dateFormat + " " + timeFormat);
00338 item->length = *it++;
00339 eventList->push_back(item);
00340 }
00341 }
00342
00343 void ZMClient::getEventDates(const QString &monitorName, bool oldestFirst,
00344 QStringList &dateList)
00345 {
00346 dateList.clear();
00347
00348 QStringList strList = "GET_EVENT_DATES";
00349 strList << monitorName << (oldestFirst ? "1" : "0") ;
00350
00351 if (!sendReceiveStringList(strList))
00352 return;
00353
00354 bool bOK;
00355 int dateCount = strList[1].toInt(&bOK);
00356 if (!bOK)
00357 {
00358 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getEventDates()");
00359 return;
00360 }
00361
00362
00363 if ((int)(strList.size() - 3) != dateCount)
00364 {
00365 VERBOSE(VB_IMPORTANT, "ZMClient got a mismatch between the number of dates and "
00366 "the expected number of stringlist items in getEventDates()");
00367 return;
00368 }
00369
00370 QStringList::Iterator it = strList.begin();
00371 it++; it++;
00372 for (int x = 0; x < dateCount; x++)
00373 {
00374 dateList.append(*it++);
00375 }
00376 }
00377
00378 void ZMClient::getFrameList(int eventID, vector<Frame*> *frameList)
00379 {
00380 frameList->clear();
00381
00382 QStringList strList = "GET_FRAME_LIST";
00383 strList << QString::number(eventID);
00384 if (!sendReceiveStringList(strList))
00385 return;
00386
00387 bool bOK;
00388 int frameCount = strList[1].toInt(&bOK);
00389 if (!bOK)
00390 {
00391 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getFrameList()");
00392 return;
00393 }
00394
00395
00396 if ((int)(strList.size() - 2) / 2 != frameCount)
00397 {
00398 VERBOSE(VB_IMPORTANT, "ZMClient got a mismatch between the number of frames and "
00399 "the expected number of stringlist items in getFrameList()");
00400 return;
00401 }
00402
00403 QStringList::Iterator it = strList.begin();
00404 it++; it++;
00405 for (int x = 0; x < frameCount; x++)
00406 {
00407 Frame *item = new Frame;
00408 item->type = *it++;
00409 item->delta = (*it++).toDouble();
00410 frameList->push_back(item);
00411 }
00412 }
00413
00414 void ZMClient::deleteEvent(int eventID)
00415 {
00416 QStringList strList = "DELETE_EVENT";
00417 strList << QString::number(eventID);
00418 sendReceiveStringList(strList);
00419 }
00420
00421 void ZMClient::deleteEventList(vector<Event*> *eventList)
00422 {
00423
00424 QStringList strList = "DELETE_EVENT_LIST";
00425 int count = 0;
00426 vector<Event*>::iterator it;
00427 for (it = eventList->begin(); it != eventList->end(); it++)
00428 {
00429 strList << QString::number((*it)->eventID);
00430
00431 if (++count == 100)
00432 {
00433 sendReceiveStringList(strList);
00434 strList = "DELETE_EVENT_LIST";
00435 count = 0;
00436 }
00437 }
00438
00439
00440 sendReceiveStringList(strList);
00441
00442
00443 strList = "RUN_ZMAUDIT";
00444 sendReceiveStringList(strList);
00445 }
00446
00447 bool ZMClient::readData(unsigned char *data, int dataSize)
00448 {
00449 Q_LONG read = 0;
00450 int errmsgtime = 0;
00451 MythTimer timer;
00452 timer.start();
00453 int elapsed;
00454
00455 while (dataSize > 0)
00456 {
00457 Q_LONG sret = m_socket->readBlock((char*) data + read, dataSize);
00458 if (sret > 0)
00459 {
00460 read += sret;
00461 dataSize -= sret;
00462 if (dataSize > 0)
00463 {
00464 timer.start();
00465 }
00466 }
00467 else if (sret < 0 && m_socket->error() != QSocketDevice::NoError)
00468 {
00469 VERBOSE(VB_GENERAL, QString("readData: Error, readBlock %1")
00470 .arg(m_socket->errorToString()));
00471 m_socket->close();
00472 return false;
00473 }
00474 else if (!m_socket->isValid())
00475 {
00476 VERBOSE(VB_IMPORTANT, "readData: Error, socket went unconnected");
00477 m_socket->close();
00478 return false;
00479 }
00480 else
00481 {
00482 elapsed = timer.elapsed();
00483 if (elapsed > 10000)
00484 {
00485 if ((elapsed - errmsgtime) > 10000)
00486 {
00487 errmsgtime = elapsed;
00488 VERBOSE(VB_GENERAL, QString("m_socket->: Waiting for data: %1 %2")
00489 .arg(read).arg(dataSize));
00490 }
00491 }
00492
00493 if (elapsed > 100000)
00494 {
00495 VERBOSE(VB_GENERAL, "Error, readData timeout (readBlock)");
00496 return false;
00497 }
00498
00499 usleep(500);
00500 }
00501 }
00502
00503 return true;
00504 }
00505
00506 void ZMClient::getEventFrame(int monitorID, int eventID, int frameNo, QImage &image)
00507 {
00508 QStringList strList = "GET_EVENT_FRAME";
00509 strList << QString::number(monitorID);
00510 strList << QString::number(eventID);
00511 strList << QString::number(frameNo);
00512 if (!sendReceiveStringList(strList))
00513 {
00514 image = QImage();
00515 return;
00516 }
00517
00518
00519 int imageSize = strList[1].toInt();
00520
00521
00522 unsigned char *data = new unsigned char[imageSize];
00523 if (!readData(data, imageSize))
00524 {
00525 VERBOSE(VB_GENERAL, "ZMClient::getEventFrame(): Failed to get image data");
00526 image = QImage();
00527 }
00528
00529
00530 if (!image.loadFromData(data, imageSize, "JPEG"))
00531 {
00532 VERBOSE(VB_GENERAL, "ZMClient::getEventFrame(): Failed to load image from data");
00533 image = QImage();
00534 }
00535 }
00536
00537 void ZMClient::getAnalyseFrame(int monitorID, int eventID, int frameNo, QImage &image)
00538 {
00539 QStringList strList = "GET_ANALYSE_FRAME";
00540 strList << QString::number(monitorID);
00541 strList << QString::number(eventID);
00542 strList << QString::number(frameNo);
00543 if (!sendReceiveStringList(strList))
00544 {
00545 image = QImage();
00546 return;
00547 }
00548
00549
00550 int imageSize = strList[1].toInt();
00551
00552
00553 unsigned char *data = new unsigned char[imageSize];
00554 if (!readData(data, imageSize))
00555 {
00556 VERBOSE(VB_GENERAL, "ZMClient::getAnalyseFrame(): Failed to get image data");
00557 image = QImage();
00558 }
00559
00560
00561 if (!image.loadFromData(data, imageSize, "JPEG"))
00562 {
00563 VERBOSE(VB_GENERAL, "ZMClient::getAnalyseFrame(): Failed to load image from data");
00564 image = QImage();
00565 }
00566 }
00567
00568 int ZMClient::getLiveFrame(int monitorID, QString &status, unsigned char* buffer, int bufferSize)
00569 {
00570 QStringList strList = "GET_LIVE_FRAME";
00571 strList << QString::number(monitorID);
00572 if (!sendReceiveStringList(strList))
00573 {
00574
00575
00576 if (strList[0].startsWith("WARNING"))
00577 return 0;
00578 else
00579 {
00580 status = strList[0];
00581 return 0;
00582 }
00583 }
00584
00585
00586 status = strList[2];
00587
00588
00589 int imageSize = strList[3].toInt();
00590
00591 assert(bufferSize > imageSize);
00592
00593
00594 if (imageSize == 0)
00595 return 0;
00596
00597 if (!readData(buffer, imageSize))
00598 {
00599 VERBOSE(VB_GENERAL, "ZMClient::getLiveFrame(): Failed to get image data");
00600 return 0;
00601 }
00602
00603 return imageSize;
00604 }
00605
00606 void ZMClient::getCameraList(QStringList &cameraList)
00607 {
00608 cameraList.clear();
00609
00610 QStringList strList = "GET_CAMERA_LIST";
00611 if (!sendReceiveStringList(strList))
00612 return;
00613
00614 bool bOK;
00615 int cameraCount = strList[1].toInt(&bOK);
00616 if (!bOK)
00617 {
00618 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getCameraList()");
00619 return;
00620 }
00621
00622 for (int x = 0; x < cameraCount; x++)
00623 {
00624 cameraList.append(strList[x + 2]);
00625 }
00626 }
00627
00628 void ZMClient::getMonitorList(vector<Monitor*> *monitorList)
00629 {
00630 monitorList->clear();
00631
00632 QStringList strList = "GET_MONITOR_LIST";
00633 if (!sendReceiveStringList(strList))
00634 return;
00635
00636 bool bOK;
00637 int monitorCount = strList[1].toInt(&bOK);
00638 if (!bOK)
00639 {
00640 VERBOSE(VB_IMPORTANT, "ZMClient received bad int in getMonitorList()");
00641 return;
00642 }
00643
00644 for (int x = 0; x < monitorCount; x++)
00645 {
00646 Monitor *item = new Monitor;
00647 item->id = strList[x * 5 + 2].toInt();
00648 item->name = strList[x * 5 + 3];
00649 item->width = strList[x * 5 + 4].toInt();
00650 item->height = strList[x * 5 + 5].toInt();
00651 item->palette = strList[x * 5 + 6].toInt();
00652 item->zmcStatus = "";
00653 item->zmaStatus = "";
00654 item->events = 0;
00655 item->status = "";
00656 monitorList->push_back(item);
00657 VERBOSE(VB_IMPORTANT, QString("Monitor: %1 (%2) is using palette: %3")
00658 .arg(item->name).arg(item->id).arg(item->palette));
00659 }
00660 }
00661
00662 void ZMClient::setMonitorFunction(const int monitorID, const QString &function, const int enabled)
00663 {
00664 QStringList strList("SET_MONITOR_FUNCTION");
00665 strList << QString::number(monitorID);
00666 strList << function;
00667 strList << QString::number(enabled);
00668
00669 if (!sendReceiveStringList(strList))
00670 return;
00671 }