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