MythTV master
zmserver.cpp
Go to the documentation of this file.
1/* Implementation of the ZMServer class.
2 * ============================================================
3 * This program is free software; you can redistribute it
4 * and/or modify it under the terms of the GNU General
5 * Public License as published bythe Free Software Foundation;
6 * either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * ============================================================ */
15
16
17#include <algorithm>
18#include <array>
19#include <filesystem>
20#include <fstream>
21#include <iostream>
22#include <cstdlib>
23#include <cstring>
24#include <cstdio>
25#include <cerrno>
26#include <sys/socket.h>
27#include <fcntl.h>
28#include <netinet/in.h>
29#include <sys/stat.h>
30#include <sys/shm.h>
31#include <sys/mman.h>
32#include <utility>
33
34#ifndef MSG_NOSIGNAL
35static constexpr int MSG_NOSIGNAL { 0 }; // Apple also has SO_NOSIGPIPE?
36#endif
37
38#include "zmserver.h"
39
40// the version of the protocol we understand
41static constexpr const char* ZM_PROTOCOL_VERSION { "11" };
42
43static inline void ADD_STR(std::string& list, const std::string& s)
44{ list += s; list += "[]:[]"; };
45static inline void ADD_INT(std::string& list, int n)
46{ list += std::to_string(n); list += "[]:[]"; };
47
48// error messages
49static constexpr const char* ERROR_TOKEN_COUNT { "Invalid token count" };
50static constexpr const char* ERROR_MYSQL_QUERY { "Mysql Query Error" };
51static constexpr const char* ERROR_MYSQL_ROW { "Mysql Get Row Error" };
52static constexpr const char* ERROR_FILE_OPEN { "Cannot open event file" };
53static constexpr const char* ERROR_INVALID_MONITOR { "Invalid Monitor" };
54static constexpr const char* ERROR_INVALID_POINTERS { "Cannot get shared memory pointers" };
55static constexpr const char* ERROR_INVALID_MONITOR_FUNCTION { "Invalid Monitor Function" };
56static constexpr const char* ERROR_INVALID_MONITOR_ENABLE_VALUE { "Invalid Monitor Enable Value" };
57static constexpr const char* ERROR_NO_FRAMES { "No frames found for event" };
58
59// Subpixel ordering (from zm_rgb.h)
60// Based on byte order naming. For example, for ARGB (on both little endian or big endian)
61// byte+0 should be alpha, byte+1 should be red, and so on.
62enum ZM_SUBPIX_ORDER : std::uint8_t {
70};
71
73std::string g_zmversion;
74std::string g_password;
75std::string g_server;
76std::string g_database;
77std::string g_webPath;
78std::string g_user;
79std::string g_webUser;
80std::string g_binPath;
81std::string g_mmapPath;
82std::string g_eventsPath;
86
88
89// returns true if the ZM version >= the requested version
90bool checkVersion(int major, int minor, int revision)
91{
92 return g_majorVersion >= major &&
94 g_revisionVersion >= revision;
95}
96
97void loadZMConfig(const std::string &configfile)
98{
99 std::cout << "loading zm config from " << configfile << '\n';
100
101 std::ifstream ifs(configfile);
102 if ( ifs.fail() )
103 {
104 fprintf(stderr, "Can't open %s\n", configfile.c_str());
105 }
106
107 std::string line {};
108 while ( std::getline(ifs, line) )
109 {
110 // Trim off begining and ending whitespace including cr/lf line endings
111 constexpr const char *whitespace = " \t\r\n";
112 auto begin = line.find_first_not_of(whitespace);
113 if (begin == std::string::npos)
114 continue; // Only whitespace
115 auto end = line.find_last_not_of(whitespace);
116 if (end != std::string::npos)
117 end = end + 1;
118 line = line.substr(begin, end);
119
120 // Check for comment or empty line
121 if ( line.empty() || line[0] == '#' )
122 continue;
123
124 // Now look for the '=' in the middle of the line
125 auto index = line.find('=');
126 if (index == std::string::npos)
127 {
128 fprintf(stderr,"Invalid data in %s: '%s'\n", configfile.c_str(), line.c_str() );
129 continue;
130 }
131
132 // Assign the name and value parts
133 std::string name = line.substr(0,index);
134 std::string val = line.substr(index+1);
135
136 // Trim trailing space from the name part
137 end = name.find_last_not_of(whitespace);
138 if (end != std::string::npos)
139 end = end + 1;
140 name = name.substr(0, end);
141
142 // Remove leading white space from the value part
143 begin = val.find_first_not_of(whitespace);
144 if (begin != std::string::npos)
145 val = val.substr(begin);
146
147 // convert name to uppercase
148 std::ranges::transform(name, name.begin(), ::toupper);
149
150 if ( name == "ZM_DB_HOST" ) g_server = val;
151 else if ( name == "ZM_DB_NAME" ) g_database = val;
152 else if ( name == "ZM_DB_USER" ) g_user = val;
153 else if ( name == "ZM_DB_PASS" ) g_password = val;
154 else if ( name == "ZM_PATH_WEB" ) g_webPath = val;
155 else if ( name == "ZM_PATH_BIN" ) g_binPath = val;
156 else if ( name == "ZM_WEB_USER" ) g_webUser = val;
157 else if ( name == "ZM_VERSION" ) g_zmversion = val;
158 else if ( name == "ZM_PATH_MAP" ) g_mmapPath = val;
159 else if ( name == "ZM_DIR_EVENTS" ) g_eventsPath = val;
160 }
161}
162
163#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 80000
164using reconnect_t = int;
165#else
166using reconnect_t = my_bool;
167#endif
168
170{
171 if (!mysql_init(&g_dbConn))
172 {
173 std::cout << "Error: Can't initialise structure: " << mysql_error(&g_dbConn) << '\n';
174 exit(static_cast<int>(mysql_errno(&g_dbConn)));
175 }
176
177 reconnect_t reconnect = 1;
178 mysql_options(&g_dbConn, MYSQL_OPT_RECONNECT, &reconnect);
179
180 if (!mysql_real_connect(&g_dbConn, g_server.c_str(), g_user.c_str(),
181 g_password.c_str(), nullptr, 0, nullptr, 0))
182 {
183 std::cout << "Error: Can't connect to server: " << mysql_error(&g_dbConn) << '\n';
184 exit(static_cast<int>(mysql_errno( &g_dbConn)));
185 }
186
187 if (mysql_select_db(&g_dbConn, g_database.c_str()))
188 {
189 std::cout << "Error: Can't select database: " << mysql_error(&g_dbConn) << '\n';
190 exit(static_cast<int>(mysql_errno(&g_dbConn)));
191 }
192}
193
195{
196 if (Clock::now() < g_lastDBKick + DB_CHECK_TIME)
197 return;
198
199 if (debug)
200 std::cout << "Kicking database connection\n";
201
202 g_lastDBKick = Clock::now();
203
204 if (mysql_query(&g_dbConn, "SELECT NULL;") == 0)
205 {
206 MYSQL_RES *res = mysql_store_result(&g_dbConn);
207 if (res)
208 mysql_free_result(res);
209 return;
210 }
211
212 std::cout << "Lost connection to DB - trying to reconnect\n";
213
214 // failed so try to reconnect to the DB
215 mysql_close(&g_dbConn);
217}
218
220
221void MONITOR::initMonitor(bool debug, const std::string &mmapPath, int shmKey)
222{
223 size_t shared_data_size = 0;
224 size_t frame_size = static_cast<size_t>(m_width) * m_height * m_bytesPerPixel;
225
226 if (!m_enabled)
227 return;
228
229 if (checkVersion(1, 34, 0))
230 {
231 shared_data_size = sizeof(SharedData34) +
232 sizeof(TriggerData26) +
233 ((m_imageBufferCount) * (sizeof(struct timeval))) +
234 ((m_imageBufferCount) * frame_size) + 64;
235 }
236 else if (checkVersion(1, 32, 0))
237 {
238 shared_data_size = sizeof(SharedData32) +
239 sizeof(TriggerData26) +
240 ((m_imageBufferCount) * (sizeof(struct timeval))) +
241 ((m_imageBufferCount) * frame_size) + 64;
242 }
243 else if (checkVersion(1, 26, 0))
244 {
245 shared_data_size = sizeof(SharedData26) +
246 sizeof(TriggerData26) +
247 ((m_imageBufferCount) * (sizeof(struct timeval))) +
248 ((m_imageBufferCount) * frame_size) + 64;
249 }
250 else
251 {
252 shared_data_size = sizeof(SharedData) +
253 sizeof(TriggerData) +
254 ((m_imageBufferCount) * (sizeof(struct timeval))) +
255 ((m_imageBufferCount) * frame_size);
256 }
257
258#if _POSIX_MAPPED_FILES > 0L
259 /*
260 * Try to open the mmap file first if the architecture supports it.
261 * Otherwise, legacy shared memory will be used below.
262 */
263 std::stringstream mmap_filename;
264 mmap_filename << mmapPath << "/zm.mmap." << m_monId;
265
266 m_mapFile = open(mmap_filename.str().c_str(), O_RDONLY, 0x0);
267 if (m_mapFile >= 0)
268 {
269 if (debug)
270 std::cout << "Opened mmap file: " << mmap_filename.str() << '\n';
271
272 m_shmPtr = mmap(nullptr, shared_data_size, PROT_READ,
273 MAP_SHARED, m_mapFile, 0x0);
274 if (m_shmPtr == MAP_FAILED)
275 {
276 std::cout << "Failed to map shared memory from file ["
277 << mmap_filename.str() << "] " << "for monitor: "
278 << m_monId << '\n';
279 m_status = "Error";
280
281 if (close(m_mapFile) == -1)
282 std::cout << "Failed to close mmap file\n";
283
284 m_mapFile = -1;
285 m_shmPtr = nullptr;
286
287 return;
288 }
289 }
290 else
291 {
292 // this is not necessarily a problem, maybe the user is still
293 // using the legacy shared memory support
294 if (debug)
295 {
296 std::cout << "Failed to open mmap file [" << mmap_filename.str() << "] "
297 << "for monitor: " << m_monId
298 << " : " << strerror(errno) << '\n';
299 std::cout << "Falling back to the legacy shared memory method\n";
300 }
301 }
302#endif
303
304 if (m_shmPtr == nullptr)
305 {
306 // fail back to shmget() functionality if mapping memory above failed.
307 int shmid = shmget((shmKey & 0xffff0000) | m_monId,
308 shared_data_size, SHM_R);
309 if (shmid == -1)
310 {
311 std::cout << "Failed to shmget for monitor: " << m_monId << '\n';
312 m_status = "Error";
313 switch(errno)
314 {
315 case EACCES: std::cout << "EACCES - no rights to access segment\n"; break;
316 case EEXIST: std::cout << "EEXIST - segment already exists\n"; break;
317 case EINVAL: std::cout << "EINVAL - size < SHMMIN or size > SHMMAX\n"; break;
318 case ENFILE: std::cout << "ENFILE - limit on open files has been reached\n"; break;
319 case ENOENT: std::cout << "ENOENT - no segment exists for the given key\n"; break;
320 case ENOMEM: std::cout << "ENOMEM - couldn't reserve memory for segment\n"; break;
321 case ENOSPC: std::cout << "ENOSPC - shmmni or shmall limit reached\n"; break;
322 }
323
324 return;
325 }
326
327 m_shmPtr = shmat(shmid, nullptr, SHM_RDONLY);
328
329
330 if (m_shmPtr == nullptr)
331 {
332 std::cout << "Failed to shmat for monitor: " << m_monId << '\n';
333 m_status = "Error";
334 return;
335 }
336 }
337
338 if (checkVersion(1, 34, 0))
339 {
340 m_sharedData = nullptr;
341 m_sharedData26 = nullptr;
342 m_sharedData32 = nullptr;
344
345 m_sharedImages = (unsigned char*) m_shmPtr +
346 sizeof(SharedData34) + sizeof(TriggerData26) + sizeof(VideoStoreData) +
347 ((m_imageBufferCount) * sizeof(struct timeval)) ;
348
349 if (((unsigned long)m_sharedImages % 64) != 0)
350 {
351 // align images buffer to nearest 64 byte boundary
352 m_sharedImages += (64 - ((unsigned long)m_sharedImages % 64));
353 }
354 }
355 else if (checkVersion(1, 32, 0))
356 {
357 m_sharedData = nullptr;
358 m_sharedData26 = nullptr;
360 m_sharedData34 = nullptr;
361
362 m_sharedImages = (unsigned char*) m_shmPtr +
363 sizeof(SharedData32) + sizeof(TriggerData26) + sizeof(VideoStoreData) +
364 ((m_imageBufferCount) * sizeof(struct timeval)) ;
365
366 if (((unsigned long)m_sharedImages % 64) != 0)
367 {
368 // align images buffer to nearest 64 byte boundary
369 m_sharedImages += (64 - ((unsigned long)m_sharedImages % 64));
370 }
371 }
372 else if (checkVersion(1, 26, 0))
373 {
374 m_sharedData = nullptr;
376 m_sharedData32 = nullptr;
377 m_sharedData34 = nullptr;
378
379 m_sharedImages = (unsigned char*) m_shmPtr +
380 sizeof(SharedData26) + sizeof(TriggerData26) +
381 ((m_imageBufferCount) * sizeof(struct timeval));
382
383 if (((unsigned long)m_sharedImages % 16) != 0)
384 {
385 // align images buffer to nearest 16 byte boundary
386 m_sharedImages += (16 - ((unsigned long)m_sharedImages % 16));
387 }
388 }
389 else
390 {
392 m_sharedData26 = nullptr;
393 m_sharedData32 = nullptr;
394 m_sharedData34 = nullptr;
395
396 m_sharedImages = (unsigned char*) m_shmPtr +
397 sizeof(SharedData) + sizeof(TriggerData) +
398 ((m_imageBufferCount) * sizeof(struct timeval));
399 }
400}
401
403{
404 if (checkVersion(1, 34, 0))
405 return m_sharedData34 != nullptr && m_sharedImages != nullptr;
406
407 if (checkVersion(1, 32, 0))
408 return m_sharedData32 != nullptr && m_sharedImages != nullptr;
409
410 if (checkVersion(1, 26, 0))
411 return m_sharedData26 != nullptr && m_sharedImages != nullptr;
412
413 // must be version >= 1.24.0 and < 1.26.0
414 return m_sharedData != nullptr && m_sharedImages != nullptr;
415}
416
417
418std::string MONITOR::getIdStr(void)
419{
420 if (m_id.empty())
421 {
422 std::stringstream out;
423 out << m_monId;
424 m_id = out.str();
425 }
426 return m_id;
427}
428
430{
431 if (m_sharedData)
433
434 if (m_sharedData26)
436
437 if (m_sharedData32)
439
440 if (m_sharedData34)
442
443 return 0;
444}
445
447{
448 if (m_sharedData)
449 return m_sharedData->state;
450
451 if (m_sharedData26)
452 return m_sharedData26->state;
453
454 if (m_sharedData32)
455 return m_sharedData32->state;
456
457 if (m_sharedData34)
458 return m_sharedData34->state;
459
460 return 0;
461}
462
464{
465 if (m_sharedData)
466 {
467 if (m_bytesPerPixel == 1)
469 return ZM_SUBPIX_ORDER_RGB;
470 }
471
472 if (m_sharedData26)
473 return m_sharedData26->format;
474
475 if (m_sharedData32)
476 return m_sharedData32->format;
477
478 if (m_sharedData34)
479 return m_sharedData34->format;
480
482}
483
485{
486 if (m_sharedData)
488
489 if (m_sharedData26)
491
492 if (m_sharedData32)
494
495 if (m_sharedData34)
497
498 return 0;
499}
500
502
504{
505 if (debug)
506 std::cout << "Using server protocol version '" << ZM_PROTOCOL_VERSION << "'\n";
507
508 m_sock = sock;
509 m_debug = debug;
510
511 // get the shared memory key
512 m_shmKey = 0x7a6d2000;
513 std::string setting = getZMSetting("ZM_SHM_KEY");
514
515 if (!setting.empty())
516 {
517 unsigned long long tmp = m_shmKey;
518 sscanf(setting.c_str(), "%20llx", &tmp);
519 m_shmKey = tmp;
520 }
521
522 if (m_debug)
523 {
524 std::cout << "Shared memory key is: 0x"
525 << std::hex << (unsigned int)m_shmKey
526 << std::dec << '\n';
527 }
528
529 // get the MMAP path
530 if (checkVersion(1, 32, 0))
532 else
533 m_mmapPath = getZMSetting("ZM_PATH_MAP");
534
535 if (m_debug)
536 {
537 std::cout << "Memory path directory is: " << m_mmapPath << '\n';
538 }
539
540 // get the event filename format
541 setting = getZMSetting("ZM_EVENT_IMAGE_DIGITS");
542 int eventDigits = atoi(setting.c_str());
543 std::string eventDigitsFmt = "%0" + std::to_string(eventDigits) + "d";
544 m_eventFileFormat = eventDigitsFmt + "-capture.jpg";
545 if (m_debug)
546 std::cout << "Event file format is: " << m_eventFileFormat << '\n';
547
548 // get the analysis filename format
549 m_analysisFileFormat = eventDigitsFmt + "-analyse.jpg";
550 if (m_debug)
551 std::cout << "Analysis file format is: " << m_analysisFileFormat << '\n';
552
553 // is ZM using the deep storage directory format?
554 m_useDeepStorage = (getZMSetting("ZM_USE_DEEP_STORAGE") == "1");
555 if (m_debug)
556 {
558 std::cout << "using deep storage directory structure\n";
559 else
560 std::cout << "using flat directory structure\n";
561 }
562
563 // is ZM creating analysis images?
564 m_useAnalysisImages = (getZMSetting("ZM_CREATE_ANALYSIS_IMAGES") == "1");
565 if (m_debug)
566 {
568 std::cout << "using analysis images\n";
569 else
570 std::cout << "not using analysis images\n";
571 }
572
574}
575
577{
578 for (auto *mon : m_monitors)
579 {
580 if (mon->m_mapFile != -1)
581 {
582 if (close(mon->m_mapFile) == -1)
583 std::cout << "Failed to close mapFile\n";
584 else
585 if (m_debug)
586 std::cout << "Closed mapFile for monitor: " << mon->m_name << '\n';
587 }
588
589 delete mon;
590 }
591
592 m_monitors.clear();
593 m_monitorMap.clear();
594
595 if (m_debug)
596 std::cout << "ZMServer destroyed\n";
597}
598
599void ZMServer::tokenize(const std::string &command, std::vector<std::string> &tokens)
600{
601 std::string token;
602 tokens.clear();
603 std::string::size_type startPos = 0;
604 std::string::size_type endPos = 0;
605
606 while((endPos = command.find("[]:[]", startPos)) != std::string::npos)
607 {
608 token = command.substr(startPos, endPos - startPos);
609 tokens.push_back(token);
610 startPos = endPos + 5;
611 }
612
613 // make sure we add the last token
614 if (endPos != command.length())
615 {
616 token = command.substr(startPos);
617 tokens.push_back(token);
618 }
619}
620
621// returns true if we get a QUIT command from the client
622bool ZMServer::processRequest(char* buf, int nbytes)
623{
624#if 0
625 // first 8 bytes is the length of the following data
626 char len[9];
627 memcpy(len, buf, 8);
628 len[8] = '\0';
629 int dataLen = atoi(len);
630#endif
631
632 buf[nbytes] = '\0';
633 std::string s(buf+8);
634 std::vector<std::string> tokens;
635 tokenize(s, tokens);
636
637 if (tokens.empty())
638 return false;
639
640 if (m_debug)
641 std::cout << "Processing: '" << tokens[0] << "'\n";
642
643 if (tokens[0] == "HELLO")
644 handleHello();
645 else if (tokens[0] == "QUIT")
646 return true;
647 else if (tokens[0] == "GET_SERVER_STATUS")
649 else if (tokens[0] == "GET_MONITOR_STATUS")
651 else if (tokens[0] == "GET_ALARM_STATES")
653 else if (tokens[0] == "GET_EVENT_LIST")
654 handleGetEventList(tokens);
655 else if (tokens[0] == "GET_EVENT_DATES")
656 handleGetEventDates(tokens);
657 else if (tokens[0] == "GET_EVENT_FRAME")
658 handleGetEventFrame(tokens);
659 else if (tokens[0] == "GET_ANALYSE_FRAME")
661 else if (tokens[0] == "GET_LIVE_FRAME")
662 handleGetLiveFrame(tokens);
663 else if (tokens[0] == "GET_FRAME_LIST")
664 handleGetFrameList(tokens);
665 else if (tokens[0] == "GET_CAMERA_LIST")
667 else if (tokens[0] == "GET_MONITOR_LIST")
669 else if (tokens[0] == "DELETE_EVENT")
670 handleDeleteEvent(tokens);
671 else if (tokens[0] == "DELETE_EVENT_LIST")
672 handleDeleteEventList(tokens);
673 else if (tokens[0] == "RUN_ZMAUDIT")
675 else if (tokens[0] == "SET_MONITOR_FUNCTION")
677 else
678 send("UNKNOWN_COMMAND");
679
680 return false;
681}
682
683bool ZMServer::send(const std::string &s) const
684{
685 // send length
686 std::string str = "0000000" + std::to_string(s.size());
687 str.erase(0, str.size()-8);
688 int status = ::send(m_sock, str.data(), 8, MSG_NOSIGNAL);
689 if (status == -1)
690 return false;
691
692 // send message
693 status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
694 return status != -1;
695}
696
697bool ZMServer::send(const std::string &s, const unsigned char *buffer, int dataLen) const
698{
699 // send length
700 std::string str = "0000000" + std::to_string(s.size());
701 str.erase(0, str.size()-8);
702 int status = ::send(m_sock, str.data(), 8, MSG_NOSIGNAL);
703 if (status == -1)
704 return false;
705
706 // send message
707 status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
708 if ( status == -1 )
709 return false;
710
711 // send data
712 status = ::send(m_sock, buffer, dataLen, MSG_NOSIGNAL);
713 return status != -1;
714}
715
716void ZMServer::sendError(const std::string &error)
717{
718 std::string outStr;
719 ADD_STR(outStr, std::string("ERROR - ") + error);
720 send(outStr);
721}
722
724{
725 // just send OK so the client knows all is well
726 // followed by the protocol version we understand
727 std::string outStr;
728 ADD_STR(outStr, "OK");
730 send(outStr);
731}
732
733static uintmax_t disk_usage_percent(const std::filesystem::space_info& space_info)
734{
735 constexpr uintmax_t k_unknown_size {static_cast<std::uintmax_t>(-1)};
736 if (
737 (space_info.capacity == 0
738 || space_info.free == 0
739 || space_info.available == 0
740 ) ||
741 (space_info.capacity == k_unknown_size
742 || space_info.free == k_unknown_size
743 || space_info.available == k_unknown_size
744 )
745 )
746 {
747 return 100;
748 }
749 return (100 * (space_info.capacity - space_info.available)) / space_info.capacity;
750}
751
753{
754 std::string outStr;
755 ADD_STR(outStr, "OK");
756
757 // server status
758 std::string status = runCommand(g_binPath + "/zmdc.pl check");
759 ADD_STR(outStr, status);
760
761 // get load averages
762 std::array<double,3> loads {};
763 if (getloadavg(loads.data(), 3) == -1)
764 {
765 ADD_STR(outStr, "Unknown");
766 }
767 else
768 {
769 // to_string gives six decimal places. Drop last four.
770 std::string buf = std::to_string(loads[0]);
771 buf.resize(buf.size() - 4);
772 ADD_STR(outStr, buf);
773 }
774
775 std::string eventsDir = g_webPath + "/events/";
776 std::string buf =
777 std::to_string(disk_usage_percent(std::filesystem::space(eventsDir))) + "%";
778 ADD_STR(outStr, buf);
779
780 send(outStr);
781}
782
784{
785 std::string outStr;
786 ADD_STR(outStr, "OK");
787
788 // add the monitor count
789 ADD_INT(outStr, (int)m_monitors.size());
790
791 for (auto *monitor : m_monitors)
792 {
793 // add monitor ID
794 ADD_INT(outStr, monitor->m_monId);
795
796 // add monitor status
797 ADD_INT(outStr, monitor->getState());
798 }
799
800 send(outStr);
801}
802
803void ZMServer::handleGetEventList(std::vector<std::string> tokens)
804{
805 std::string outStr;
806
807 if (tokens.size() != 5)
808 {
810 return;
811 }
812
813 const std::string& monitor = tokens[1];
814 bool oldestFirst = (tokens[2] == "1");
815 const std::string& date = tokens[3];
816 bool includeContinuous = (tokens[4] == "1");
817
818 if (m_debug)
819 std::cout << "Loading events for monitor: " << monitor << ", date: " << date << '\n';
820
821 ADD_STR(outStr, "OK");
822
823 std::string sql("SELECT E.Id, E.Name, M.Id AS MonitorID, M.Name AS MonitorName, E.StartTime, "
824 "E.Length, M.Width, M.Height, M.DefaultRate, M.DefaultScale "
825 "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
826
827 if (monitor != "<ANY>")
828 {
829 sql += "WHERE M.Name = '" + monitor + "' ";
830
831 if (date != "<ANY>")
832 sql += "AND DATE(E.StartTime) = DATE('" + date + "') ";
833 }
834 else
835 {
836 if (date != "<ANY>")
837 {
838 sql += "WHERE DATE(E.StartTime) = DATE('" + date + "') ";
839
840 if (!includeContinuous)
841 sql += "AND Cause != 'Continuous' ";
842 }
843 else
844 if (!includeContinuous)
845 {
846 sql += "WHERE Cause != 'Continuous' ";
847 }
848 }
849
850 if (oldestFirst)
851 sql += "ORDER BY E.StartTime ASC";
852 else
853 sql += "ORDER BY E.StartTime DESC";
854
855 if (mysql_query(&g_dbConn, sql.c_str()))
856 {
857 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
859 return;
860 }
861
862 MYSQL_RES *res = mysql_store_result(&g_dbConn);
863 int eventCount = mysql_num_rows(res);
864
865 if (m_debug)
866 std::cout << "Got " << eventCount << " events\n";
867
868 ADD_INT(outStr, eventCount);
869
870 for (int x = 0; x < eventCount; x++)
871 {
872 MYSQL_ROW row = mysql_fetch_row(res);
873 if (row)
874 {
875 ADD_STR(outStr, row[0]); // eventID
876 ADD_STR(outStr, row[1]); // event name
877 ADD_STR(outStr, row[2]); // monitorID
878 ADD_STR(outStr, row[3]); // monitor name
879 row[4][10] = 'T';
880 ADD_STR(outStr, row[4]); // start time
881 ADD_STR(outStr, row[5]); // length
882 }
883 else
884 {
885 std::cout << "Failed to get mysql row\n";
887 return;
888 }
889 }
890
891 mysql_free_result(res);
892
893 send(outStr);
894}
895
896void ZMServer::handleGetEventDates(std::vector<std::string> tokens)
897{
898 std::string outStr;
899
900 if (tokens.size() != 3)
901 {
903 return;
904 }
905
906 const std::string& monitor = tokens[1];
907 bool oldestFirst = (tokens[2] == "1");
908
909 if (m_debug)
910 std::cout << "Loading event dates for monitor: " << monitor << '\n';
911
912 ADD_STR(outStr, "OK");
913
914 std::string sql("SELECT DISTINCT DATE(E.StartTime) "
915 "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
916
917 if (monitor != "<ANY>")
918 sql += "WHERE M.Name = '" + monitor + "' ";
919
920 if (oldestFirst)
921 sql += "ORDER BY E.StartTime ASC";
922 else
923 sql += "ORDER BY E.StartTime DESC";
924
925 if (mysql_query(&g_dbConn, sql.c_str()))
926 {
927 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
929 return;
930 }
931
932 MYSQL_RES *res = mysql_store_result(&g_dbConn);
933 int dateCount = mysql_num_rows(res);
934
935 if (m_debug)
936 std::cout << "Got " << dateCount << " dates\n";
937
938 ADD_INT(outStr, dateCount);
939
940 for (int x = 0; x < dateCount; x++)
941 {
942 MYSQL_ROW row = mysql_fetch_row(res);
943 if (row)
944 {
945 ADD_STR(outStr, row[0]); // event date
946 }
947 else
948 {
949 std::cout << "Failed to get mysql row\n";
951 return;
952 }
953 }
954
955 mysql_free_result(res);
956
957 send(outStr);
958}
959
961{
962 std::string outStr;
963 ADD_STR(outStr, "OK");
964
965 // get monitor list
966 // Function is reserverd word so but ticks around it
967 std::string sql("SELECT Id, Name, Type, Device, Host, Channel, `Function`, Enabled "
968 "FROM Monitors;");
969 if (mysql_query(&g_dbConn, sql.c_str()))
970 {
971 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
973 return;
974 }
975
976 MYSQL_RES *res = mysql_store_result(&g_dbConn);
977
978 // add monitor count
979 int monitorCount = mysql_num_rows(res);
980
981 if (m_debug)
982 std::cout << "Got " << monitorCount << " monitors\n";
983
984 ADD_INT(outStr, monitorCount);
985
986 for (int x = 0; x < monitorCount; x++)
987 {
988 MYSQL_ROW row = mysql_fetch_row(res);
989 if (row)
990 {
991 std::string id = row[0];
992 std::string type = row[2];
993 std::string device = row[3];
994 std::string host = row[4] ? row[4] : "";
995 std::string channel = row[5];
996 std::string function = row[6];
997 std::string enabled = row[7];
998 std::string name = row[1];
999 std::string events;
1000 std::string zmcStatus;
1001 std::string zmaStatus;
1002 getMonitorStatus(id, type, device, host, channel, function,
1003 zmcStatus, zmaStatus, enabled);
1004
1005 std::string sql2("SELECT count(if(Archived=0,1,NULL)) AS EventCount "
1006 "FROM Events AS E "
1007 "WHERE MonitorId = " + id);
1008
1009 if (mysql_query(&g_dbConn, sql2.c_str()))
1010 {
1011 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1013 return;
1014 }
1015
1016 MYSQL_RES *res2 = mysql_store_result(&g_dbConn);
1017 if (mysql_num_rows(res2) > 0)
1018 {
1019 MYSQL_ROW row2 = mysql_fetch_row(res2);
1020 if (row2)
1021 {
1022 events = row2[0];
1023 }
1024 else
1025 {
1026 std::cout << "Failed to get mysql row\n";
1028 return;
1029 }
1030 }
1031
1032 ADD_STR(outStr, id);
1033 ADD_STR(outStr, name);
1034 ADD_STR(outStr, zmcStatus);
1035 ADD_STR(outStr, zmaStatus);
1036 ADD_STR(outStr, events);
1037 ADD_STR(outStr, function);
1038 ADD_STR(outStr, enabled);
1039
1040 mysql_free_result(res2);
1041 }
1042 else
1043 {
1044 std::cout << "Failed to get mysql row\n";
1046 return;
1047 }
1048 }
1049
1050 mysql_free_result(res);
1051
1052 send(outStr);
1053}
1054
1055std::string ZMServer::runCommand(const std::string& command)
1056{
1057 std::string outStr;
1058 FILE *fd = popen(command.c_str(), "r");
1059 if (nullptr == fd)
1060 {
1061 std::cout << "Call to popen() failed.\n";
1062 return {};
1063 }
1064
1065 std::array<char,100> buffer {};
1066 while (fgets(buffer.data(), buffer.size(), fd) != nullptr)
1067 {
1068 outStr += buffer.data();
1069 }
1070 pclose(fd);
1071 return outStr;
1072}
1073
1074void ZMServer::getMonitorStatus(const std::string &id, const std::string &type,
1075 const std::string &device, const std::string &host,
1076 const std::string &channel, const std::string &function,
1077 std::string &zmcStatus, std::string &zmaStatus,
1078 const std::string &enabled)
1079{
1080 zmaStatus = "";
1081 zmcStatus = "";
1082
1083 std::string command(g_binPath + "/zmdc.pl status");
1084 std::string status = runCommand(command);
1085
1086 if (type == "Local")
1087 {
1088 if (enabled == "0")
1089 zmaStatus = device + "(" + channel + ") [-]";
1090 else if (status.find("'zma -m " + id + "' running") != std::string::npos)
1091 zmaStatus = device + "(" + channel + ") [R]";
1092 else
1093 zmaStatus = device + "(" + channel + ") [S]";
1094 }
1095 else
1096 {
1097 if (enabled == "0")
1098 zmaStatus = host + " [-]";
1099 else if (status.find("'zma -m " + id + "' running") != std::string::npos)
1100 zmaStatus = host + " [R]";
1101 else
1102 zmaStatus = host + " [S]";
1103 }
1104
1105 if (type == "Local")
1106 {
1107 if (enabled == "0")
1108 zmcStatus = function + " [-]";
1109 else if (status.find("'zmc -d "+ device + "' running") != std::string::npos)
1110 zmcStatus = function + " [R]";
1111 else
1112 zmcStatus = function + " [S]";
1113 }
1114 else
1115 {
1116 if (enabled == "0")
1117 zmcStatus = function + " [-]";
1118 else if (status.find("'zmc -m " + id + "' running") != std::string::npos)
1119 zmcStatus = function + " [R]";
1120 else
1121 zmcStatus = function + " [S]";
1122 }
1123}
1124
1125void ZMServer::handleGetEventFrame(std::vector<std::string> tokens)
1126{
1127 static FrameData s_buffer {};
1128
1129 if (tokens.size() != 5)
1130 {
1132 return;
1133 }
1134
1135 const std::string& monitorID(tokens[1]);
1136 const std::string& eventID(tokens[2]);
1137 int frameNo = atoi(tokens[3].c_str());
1138 const std::string& eventTime(tokens[4]);
1139
1140 if (m_debug)
1141 {
1142 std::cout << "Getting frame " << frameNo << " for event " << eventID
1143 << " on monitor " << monitorID << " event time is " << eventTime
1144 << '\n';
1145 }
1146
1147 std::string outStr;
1148
1149 ADD_STR(outStr, "OK");
1150
1151 // try to find the frame file
1152 std::string filepath;
1153 std::string str (100,'\0');
1154
1155 if (checkVersion(1, 32, 0))
1156 {
1157 int year = 0;
1158 int month = 0;
1159 int day = 0;
1160
1161 sscanf(eventTime.data(), "%2d/%2d/%2d", &year, &month, &day);
1162 sprintf(str.data(), "20%02d-%02d-%02d", year, month, day);
1163
1164 filepath = g_eventsPath + "/" + monitorID + "/" + str + "/" + eventID + "/";
1165 sprintf(str.data(), m_eventFileFormat.c_str(), frameNo);
1166 filepath += str;
1167 }
1168 else
1169 {
1170 if (m_useDeepStorage)
1171 {
1172 filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
1173 sprintf(str.data(), m_eventFileFormat.c_str(), frameNo);
1174 filepath += str;
1175 }
1176 else
1177 {
1178 filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
1179 sprintf(str.data(), m_eventFileFormat.c_str(), frameNo);
1180 filepath += str;
1181 }
1182 }
1183
1184 int fileSize = 0;
1185 FILE *fd = fopen(filepath.c_str(), "r" );
1186 if (fd != nullptr)
1187 {
1188 fileSize = fread(s_buffer.data(), 1, s_buffer.size(), fd);
1189 fclose(fd);
1190 }
1191 else
1192 {
1193 std::cout << "Can't open " << filepath << ": " << strerror(errno) << '\n';
1194 sendError(ERROR_FILE_OPEN + std::string(" - ") + filepath + " : " + strerror(errno));
1195 return;
1196 }
1197
1198 if (m_debug)
1199 std::cout << "Frame size: " << fileSize << '\n';
1200
1201 // get the file size
1202 ADD_INT(outStr, fileSize);
1203
1204 // send the data
1205 send(outStr, s_buffer.data(), fileSize);
1206}
1207
1208void ZMServer::handleGetAnalysisFrame(std::vector<std::string> tokens)
1209{
1210 static FrameData s_buffer {};
1211 std::array<char,100> str {};
1212
1213 if (tokens.size() != 5)
1214 {
1216 return;
1217 }
1218
1219 const std::string& monitorID(tokens[1]);
1220 const std::string& eventID(tokens[2]);
1221 int frameNo = atoi(tokens[3].c_str());
1222 const std::string& eventTime(tokens[4]);
1223 int frameID = 0;
1224 int frameCount = 0;
1225
1226 if (m_debug)
1227 {
1228 std::cout << "Getting analysis frame " << frameNo << " for event " << eventID
1229 << " on monitor " << monitorID << " event time is " << eventTime
1230 << '\n';
1231 }
1232
1233 // get the 'alarm' frames from the Frames table for this event
1234 std::string sql;
1235 sql += "SELECT FrameId FROM Frames ";
1236 sql += "WHERE EventID = " + eventID + " ";
1237 sql += "AND Type = 'Alarm' ";
1238 sql += "ORDER BY FrameID";
1239
1240 if (mysql_query(&g_dbConn, sql.c_str()))
1241 {
1242 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1244 return;
1245 }
1246
1247 MYSQL_RES *res = mysql_store_result(&g_dbConn);
1248 frameCount = mysql_num_rows(res);
1249
1250 // if we didn't find any alarm frames get the list of normal frames
1251 if (frameCount == 0)
1252 {
1253 mysql_free_result(res);
1254
1255 sql = "SELECT FrameId FROM Frames ";
1256 sql += "WHERE EventID = " + eventID + " ";
1257 sql += "ORDER BY FrameID";
1258
1259 if (mysql_query(&g_dbConn, sql.c_str()))
1260 {
1261 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1263 return;
1264 }
1265
1266 res = mysql_store_result(&g_dbConn);
1267 frameCount = mysql_num_rows(res);
1268 }
1269
1270 // if frameCount is 0 then we can't go any further
1271 if (frameCount == 0)
1272 {
1273 std::cout << "handleGetAnalyseFrame: Failed to find any frames\n";
1275 return;
1276 }
1277
1278 // if the required frame mumber is 0 or out of bounds then use the middle frame
1279 if (frameNo == 0 || frameNo < 0 || frameNo > frameCount)
1280 frameNo = (frameCount / 2) + 1;
1281
1282 // move to the required frame in the table
1283 MYSQL_ROW row = nullptr;
1284 for (int x = 0; x < frameNo; x++)
1285 {
1286 row = mysql_fetch_row(res);
1287 }
1288
1289 if (row)
1290 {
1291 frameID = atoi(row[0]);
1292 }
1293 else
1294 {
1295 std::cout << "handleGetAnalyseFrame: Failed to get mysql row for frameNo " << frameNo << '\n';
1297 return;
1298 }
1299
1300 mysql_free_result(res);
1301
1302 std::string outStr;
1303 std::string filepath;
1304 std::string frameFile;
1305
1306 if (checkVersion(1, 32, 0))
1307 {
1308 int year = 0;
1309 int month = 0;
1310 int day = 0;
1311
1312 sscanf(eventTime.c_str(), "%2d/%2d/%2d", &year, &month, &day);
1313 sprintf(str.data(), "20%02d-%02d-%02d", year, month, day);
1314 filepath = g_eventsPath + "/" + monitorID + "/" + str.data() + "/" + eventID + "/";
1315 }
1316 else
1317 {
1318 if (m_useDeepStorage)
1319 filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
1320 else
1321 filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
1322 }
1323
1324 ADD_STR(outStr, "OK");
1325
1326 FILE *fd = nullptr;
1327 int fileSize = 0;
1328
1329 // try to find an analysis frame for the frameID
1331 {
1332 sprintf(str.data(), m_analysisFileFormat.c_str(), frameID);
1333 frameFile = filepath + str.data();
1334
1335 fd = fopen(frameFile.c_str(), "r" );
1336 if (fd != nullptr)
1337 {
1338 fileSize = fread(s_buffer.data(), 1, s_buffer.size(), fd);
1339 fclose(fd);
1340
1341 if (m_debug)
1342 std::cout << "Frame size: " << fileSize << '\n';
1343
1344 // get the file size
1345 ADD_INT(outStr, fileSize);
1346
1347 // send the data
1348 send(outStr, s_buffer.data(), fileSize);
1349 return;
1350 }
1351 }
1352
1353 // try to find a normal frame for the frameID these should always be available
1354 sprintf(str.data(), m_eventFileFormat.c_str(), frameID);
1355 frameFile = filepath + str.data();
1356
1357 fd = fopen(frameFile.c_str(), "r" );
1358 if (fd != nullptr)
1359 {
1360 fileSize = fread(s_buffer.data(), 1, s_buffer.size(), fd);
1361 fclose(fd);
1362 }
1363 else
1364 {
1365 std::cout << "Can't open " << frameFile << ": " << strerror(errno) << '\n';
1366 sendError(ERROR_FILE_OPEN + std::string(" - ") + frameFile + " : " + strerror(errno));
1367 return;
1368 }
1369
1370 if (m_debug)
1371 std::cout << "Frame size: " << fileSize << '\n';
1372
1373 // get the file size
1374 ADD_INT(outStr, fileSize);
1375
1376 // send the data
1377 send(outStr, s_buffer.data(), fileSize);
1378}
1379
1380void ZMServer::handleGetLiveFrame(std::vector<std::string> tokens)
1381{
1382 static FrameData s_buffer {};
1383
1384 // we need to periodically kick the DB connection here to make sure it
1385 // stays alive because the user may have left the frontend on the live
1386 // view which doesn't query the DB at all and eventually the connection
1387 // will timeout
1389
1390 if (tokens.size() != 2)
1391 {
1393 return;
1394 }
1395
1396 int monitorID = atoi(tokens[1].c_str());
1397
1398 if (m_debug)
1399 std::cout << "Getting live frame from monitor: " << monitorID << '\n';
1400
1401 std::string outStr;
1402
1403 ADD_STR(outStr, "OK");
1404
1405 // echo the monitor id
1406 ADD_INT(outStr, monitorID);
1407
1408 // try to find the correct MONITOR
1409 if (!m_monitorMap.contains(monitorID))
1410 {
1412 return;
1413 }
1414 MONITOR *monitor = m_monitorMap[monitorID];
1415
1416 // are the data pointers valid?
1417 if (!monitor->isValid())
1418 {
1420 return;
1421 }
1422
1423 // read a frame from the shared memory
1424 int dataSize = getFrame(s_buffer, monitor);
1425
1426 if (m_debug)
1427 std::cout << "Frame size: " << dataSize << '\n';
1428
1429 if (dataSize == 0)
1430 {
1431 // not really an error
1432 outStr = "";
1433 ADD_STR(outStr, "WARNING - No new frame available");
1434 send(outStr);
1435 return;
1436 }
1437
1438 // add status
1439 ADD_STR(outStr, monitor->m_status);
1440
1441 // send the data size
1442 ADD_INT(outStr, dataSize);
1443
1444 // send the data
1445 send(outStr, s_buffer.data(), dataSize);
1446}
1447
1448void ZMServer::handleGetFrameList(std::vector<std::string> tokens)
1449{
1450 std::string eventID;
1451 std::string outStr;
1452
1453 if (tokens.size() != 2)
1454 {
1456 return;
1457 }
1458
1459 eventID = tokens[1];
1460
1461 if (m_debug)
1462 std::cout << "Loading frames for event: " << eventID << '\n';
1463
1464 ADD_STR(outStr, "OK");
1465
1466 // check to see what type of event this is
1467 std::string sql = "SELECT Cause, Length, Frames FROM Events ";
1468 sql += "WHERE Id = " + eventID + " ";
1469
1470 if (mysql_query(&g_dbConn, sql.c_str()))
1471 {
1472 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1474 return;
1475 }
1476
1477 MYSQL_RES *res = mysql_store_result(&g_dbConn);
1478 MYSQL_ROW row = mysql_fetch_row(res);
1479
1480 // make sure we have some frames to display
1481 if (row[1] == nullptr || row[2] == nullptr)
1482 {
1484 return;
1485 }
1486
1487 std::string cause = row[0];
1488 double length = atof(row[1]);
1489 int frameCount = atoi(row[2]);
1490
1491 mysql_free_result(res);
1492
1493 if (cause == "Continuous")
1494 {
1495 // event is a continuous recording so guess the frame delta's
1496
1497 if (m_debug)
1498 std::cout << "Got " << frameCount << " frames (continuous event)\n";
1499
1500 ADD_INT(outStr, frameCount);
1501
1502 if (frameCount > 0)
1503 {
1504 double delta = length / frameCount;
1505
1506 for (int x = 0; x < frameCount; x++)
1507 {
1508 ADD_STR(outStr, "Normal"); // Type
1509 ADD_STR(outStr, std::to_string(delta)); // Delta
1510 }
1511 }
1512 }
1513 else
1514 {
1515 sql = "SELECT Type, Delta FROM Frames ";
1516 sql += "WHERE EventID = " + eventID + " ";
1517 sql += "ORDER BY FrameID";
1518
1519 if (mysql_query(&g_dbConn, sql.c_str()))
1520 {
1521 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1523 return;
1524 }
1525
1526 res = mysql_store_result(&g_dbConn);
1527 frameCount = mysql_num_rows(res);
1528
1529 if (m_debug)
1530 std::cout << "Got " << frameCount << " frames\n";
1531
1532 ADD_INT(outStr, frameCount);
1533
1534 for (int x = 0; x < frameCount; x++)
1535 {
1536 row = mysql_fetch_row(res);
1537 if (row)
1538 {
1539 ADD_STR(outStr, row[0]); // Type
1540 ADD_STR(outStr, row[1]); // Delta
1541 }
1542 else
1543 {
1544 std::cout << "handleGetFrameList: Failed to get mysql row " << x << '\n';
1546 return;
1547 }
1548 }
1549
1550 mysql_free_result(res);
1551 }
1552
1553 send(outStr);
1554}
1555
1557{
1558 std::string outStr;
1559
1560 ADD_STR(outStr, "OK");
1561
1562 ADD_INT(outStr, (int)m_monitors.size());
1563
1564 for (auto & monitor : m_monitors)
1565 {
1566 ADD_STR(outStr, monitor->m_name);
1567 }
1568
1569 send(outStr);
1570}
1571
1573{
1574 std::string outStr;
1575
1576 ADD_STR(outStr, "OK");
1577
1578 if (m_debug)
1579 std::cout << "We have " << m_monitors.size() << " monitors\n";
1580
1581 ADD_INT(outStr, (int)m_monitors.size());;
1582
1583 for (auto *mon : m_monitors)
1584 {
1585 ADD_INT(outStr, mon->m_monId);
1586 ADD_STR(outStr, mon->m_name);
1587 ADD_INT(outStr, mon->m_width);
1588 ADD_INT(outStr, mon->m_height);
1589 ADD_INT(outStr, mon->m_bytesPerPixel);
1590
1591 if (m_debug)
1592 {
1593 std::cout << "id: " << mon->m_monId << '\n';
1594 std::cout << "name: " << mon->m_name << '\n';
1595 std::cout << "width: " << mon->m_width << '\n';
1596 std::cout << "height: " << mon->m_height << '\n';
1597 std::cout << "palette: " << mon->m_palette << '\n';
1598 std::cout << "byte per pixel: " << mon->m_bytesPerPixel << '\n';
1599 std::cout << "sub pixel order:" << mon->getSubpixelOrder() << '\n';
1600 std::cout << "-------------------\n";
1601 }
1602 }
1603
1604 send(outStr);
1605}
1606
1607void ZMServer::handleDeleteEvent(std::vector<std::string> tokens)
1608{
1609 std::string eventID;
1610 std::string outStr;
1611
1612 if (tokens.size() != 2)
1613 {
1615 return;
1616 }
1617
1618 eventID = tokens[1];
1619
1620 if (m_debug)
1621 std::cout << "Deleting event: " << eventID << '\n';
1622
1623 ADD_STR(outStr, "OK");
1624
1625 std::string sql;
1626 sql += "DELETE FROM Events WHERE Id = " + eventID;
1627
1628 if (mysql_query(&g_dbConn, sql.c_str()))
1629 {
1630 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1632 return;
1633 }
1634
1635 // run zmaudit.pl to clean everything up
1636 std::string command(g_binPath + "/zmaudit.pl &");
1637 errno = 0;
1638 if (system(command.c_str()) < 0 && errno)
1639 std::cerr << "Failed to run '" << command << "'\n";
1640
1641 send(outStr);
1642}
1643
1644void ZMServer::handleDeleteEventList(std::vector<std::string> tokens)
1645{
1646 std::string eventList;
1647 std::string outStr;
1648
1649 auto it = tokens.begin();
1650 if (it != tokens.end())
1651 ++it;
1652 while (it != tokens.end())
1653 {
1654 if (eventList.empty())
1655 eventList = (*it);
1656 else
1657 eventList += "," + (*it);
1658
1659 ++it;
1660 }
1661
1662 if (m_debug)
1663 std::cout << "Deleting events: " << eventList << '\n';
1664
1665 std::string sql;
1666 sql += "DELETE FROM Events WHERE Id IN (" + eventList + ")";
1667
1668 if (mysql_query(&g_dbConn, sql.c_str()))
1669 {
1670 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1672 return;
1673 }
1674
1675 ADD_STR(outStr, "OK");
1676 send(outStr);
1677}
1678
1680{
1681 std::string outStr;
1682
1683 // run zmaudit.pl to clean up orphaned db entries etc
1684 std::string command(g_binPath + "/zmaudit.pl &");
1685
1686 if (m_debug)
1687 std::cout << "Running command: " << command << '\n';
1688
1689 errno = 0;
1690 if (system(command.c_str()) < 0 && errno)
1691 std::cerr << "Failed to run '" << command << "'\n";
1692
1693 ADD_STR(outStr, "OK");
1694 send(outStr);
1695}
1696
1698{
1699 m_monitors.clear();
1700 m_monitorMap.clear();
1701
1702 // Function is reserverd word so but ticks around it
1703 std::string sql("SELECT Id, Name, Width, Height, ImageBufferCount, MaxFPS, Palette, ");
1704 sql += " Type, `Function`, Enabled, Device, Host, Controllable, TrackMotion";
1705
1706 if (checkVersion(1, 26, 0))
1707 sql += ", Colours";
1708
1709 sql += " FROM Monitors";
1710 sql += " ORDER BY Sequence";
1711
1712 if (mysql_query(&g_dbConn, sql.c_str()))
1713 {
1714 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1715 return;
1716 }
1717
1718 MYSQL_RES *res = mysql_store_result(&g_dbConn);
1719 int monitorCount = mysql_num_rows(res);
1720
1721 if (m_debug)
1722 std::cout << "Got " << monitorCount << " monitors\n";
1723
1724 for (int x = 0; x < monitorCount; x++)
1725 {
1726 MYSQL_ROW row = mysql_fetch_row(res);
1727 if (row)
1728 {
1729 auto *m = new MONITOR;
1730 m->m_monId = atoi(row[0]);
1731 m->m_name = row[1];
1732 m->m_width = atoi(row[2]);
1733 m->m_height = atoi(row[3]);
1734 m->m_imageBufferCount = atoi(row[4]);
1735 m->m_palette = atoi(row[6]);
1736 m->m_type = row[7];
1737 m->m_function = row[8];
1738 m->m_enabled = atoi(row[9]);
1739 m->m_device = row[10];
1740 m->m_host = row[11] ? row[11] : "";
1741 m->m_controllable = atoi(row[12]);
1742 m->m_trackMotion = atoi(row[13]);
1743
1744 // from version 1.26.0 ZM can have 1, 3 or 4 bytes per pixel
1745 // older versions can be 1 or 3
1746 if (checkVersion(1, 26, 0))
1747 m->m_bytesPerPixel = atoi(row[14]);
1748 else
1749 if (m->m_palette == 1)
1750 m->m_bytesPerPixel = 1;
1751 else
1752 m->m_bytesPerPixel = 3;
1753
1754 m_monitors.push_back(m);
1755 m_monitorMap[m->m_monId] = m;
1756
1757 m->initMonitor(m_debug, m_mmapPath, m_shmKey);
1758 }
1759 else
1760 {
1761 std::cout << "Failed to get mysql row\n";
1762 return;
1763 }
1764 }
1765
1766 mysql_free_result(res);
1767}
1768
1770{
1771 // is there a new frame available?
1772 if (monitor->getLastWriteIndex() == monitor->m_lastRead )
1773 return 0;
1774
1775 // sanity check last_read
1776 if (monitor->getLastWriteIndex() < 0 ||
1777 monitor->getLastWriteIndex() >= (monitor->m_imageBufferCount - 1))
1778 return 0;
1779
1780 monitor->m_lastRead = monitor->getLastWriteIndex();
1781
1782 switch (monitor->getState())
1783 {
1784 case IDLE:
1785 monitor->m_status = "Idle";
1786 break;
1787 case PREALARM:
1788 monitor->m_status = "Pre Alarm";
1789 break;
1790 case ALARM:
1791 monitor->m_status = "Alarm";
1792 break;
1793 case ALERT:
1794 monitor->m_status = "Alert";
1795 break;
1796 case TAPE:
1797 monitor->m_status = "Tape";
1798 break;
1799 default:
1800 monitor->m_status = "Unknown";
1801 break;
1802 }
1803
1804 // FIXME: should do some sort of compression JPEG??
1805 // just copy the data to our buffer for now
1806
1807 // fixup the colours if necessary we aim to always send RGB24 images
1808 unsigned char *data = monitor->m_sharedImages +
1809 (static_cast<ptrdiff_t>(monitor->getFrameSize()) * monitor->m_lastRead);
1810 unsigned int rpos = 0;
1811 unsigned int wpos = 0;
1812
1813 switch (monitor->getSubpixelOrder())
1814 {
1816 {
1817 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 1)
1818 {
1819 buffer[wpos + 0] = data[rpos + 0]; // r
1820 buffer[wpos + 1] = data[rpos + 0]; // g
1821 buffer[wpos + 2] = data[rpos + 0]; // b
1822 }
1823
1824 break;
1825 }
1826
1828 {
1829 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 3)
1830 {
1831 buffer[wpos + 0] = data[rpos + 0]; // r
1832 buffer[wpos + 1] = data[rpos + 1]; // g
1833 buffer[wpos + 2] = data[rpos + 2]; // b
1834 }
1835
1836 break;
1837 }
1838
1840 {
1841 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 3)
1842 {
1843 buffer[wpos + 0] = data[rpos + 2]; // r
1844 buffer[wpos + 1] = data[rpos + 1]; // g
1845 buffer[wpos + 2] = data[rpos + 0]; // b
1846 }
1847
1848 break;
1849 }
1851 {
1852 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 4)
1853 {
1854 buffer[wpos + 0] = data[rpos + 2]; // r
1855 buffer[wpos + 1] = data[rpos + 1]; // g
1856 buffer[wpos + 2] = data[rpos + 0]; // b
1857 }
1858
1859 break;
1860 }
1861
1863 {
1864 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3 ); wpos += 3, rpos += 4)
1865 {
1866 buffer[wpos + 0] = data[rpos + 0]; // r
1867 buffer[wpos + 1] = data[rpos + 1]; // g
1868 buffer[wpos + 2] = data[rpos + 2]; // b
1869 }
1870
1871 break;
1872 }
1873
1875 {
1876 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 4)
1877 {
1878 buffer[wpos + 0] = data[rpos + 3]; // r
1879 buffer[wpos + 1] = data[rpos + 2]; // g
1880 buffer[wpos + 2] = data[rpos + 1]; // b
1881 }
1882
1883 break;
1884 }
1885
1887 {
1888 for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->m_width * monitor->m_height * 3); wpos += 3, rpos += 4)
1889 {
1890 buffer[wpos + 0] = data[rpos + 1]; // r
1891 buffer[wpos + 1] = data[rpos + 2]; // g
1892 buffer[wpos + 2] = data[rpos + 3]; // b
1893 }
1894
1895 break;
1896 }
1897 }
1898
1899 return monitor->m_width * monitor->m_height * 3;
1900}
1901
1902std::string ZMServer::getZMSetting(const std::string &setting) const
1903{
1904 std::string result;
1905 std::string sql("SELECT Name, Value FROM Config ");
1906 sql += "WHERE Name = '" + setting + "'";
1907
1908 if (mysql_query(&g_dbConn, sql.c_str()))
1909 {
1910 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1911 return "";
1912 }
1913
1914 MYSQL_RES *res = mysql_store_result(&g_dbConn);
1915 MYSQL_ROW row = mysql_fetch_row(res);
1916 if (row)
1917 {
1918 result = row[1];
1919 }
1920 else
1921 {
1922 std::cout << "Failed to get mysql row\n";
1923 result = "";
1924 }
1925
1926 if (m_debug)
1927 std::cout << "getZMSetting: " << setting << " Result: " << result << '\n';
1928
1929 mysql_free_result(res);
1930
1931 return result;
1932}
1933
1934void ZMServer::handleSetMonitorFunction(std::vector<std::string> tokens)
1935{
1936 std::string outStr;
1937
1938 if (tokens.size() != 4)
1939 {
1941 return;
1942 }
1943
1944 const std::string& monitorID(tokens[1]);
1945 const std::string& function(tokens[2]);
1946 const std::string& enabled(tokens[3]);
1947
1948 // Check validity of input passed to server. Does monitor exist && is function ok
1949 if (!m_monitorMap.contains(atoi(monitorID.c_str())))
1950 {
1952 return;
1953 }
1954
1955 if (function != FUNCTION_NONE && function != FUNCTION_MONITOR &&
1956 function != FUNCTION_MODECT && function != FUNCTION_NODECT &&
1957 function != FUNCTION_RECORD && function != FUNCTION_MOCORD)
1958 {
1960 return;
1961 }
1962
1963 if (enabled != "0" && enabled != "1")
1964 {
1966 return;
1967 }
1968
1969 if (m_debug)
1970 std::cout << "User input validated OK\n";
1971
1972
1973 // Now perform db update && (re)start/stop daemons as required.
1974 MONITOR *monitor = m_monitorMap[atoi(monitorID.c_str())];
1975 std::string oldFunction = monitor->m_function;
1976 const std::string& newFunction = function;
1977 int oldEnabled = monitor->m_enabled;
1978 int newEnabled = atoi(enabled.c_str());
1979 monitor->m_function = newFunction;
1980 monitor->m_enabled = newEnabled;
1981
1982 if (m_debug)
1983 {
1984 std::cout << "SetMonitorFunction MonitorId: " << monitorID << '\n'
1985 << " oldEnabled: " << oldEnabled << '\n'
1986 << " newEnabled: " << newEnabled << '\n'
1987 << " oldFunction: " << oldFunction << '\n'
1988 << " newFunction: " << newFunction << '\n';
1989 }
1990
1991 if ( newFunction != oldFunction || newEnabled != oldEnabled)
1992 {
1993 std::string sql("UPDATE Monitors ");
1994 sql += "SET Function = '" + function + "', ";
1995 sql += "Enabled = '" + enabled + "' ";
1996 sql += "WHERE Id = '" + monitorID + "'";
1997
1998 if (mysql_query(&g_dbConn, sql.c_str()))
1999 {
2000 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
2002 return;
2003 }
2004
2005 if (m_debug)
2006 std::cout << "Monitor function SQL update OK\n";
2007
2008 std::string status = runCommand(g_binPath + "/zmdc.pl check");
2009
2010 // Now refresh servers
2011 if (RUNNING.compare(0, RUNNING.size(), status, 0, RUNNING.size()) == 0)
2012 {
2013 if (m_debug)
2014 std::cout << "Monitor function Refreshing daemons\n";
2015
2016 bool restart = (oldFunction == FUNCTION_NONE) ||
2017 (newFunction == FUNCTION_NONE) ||
2018 (newEnabled != oldEnabled);
2019
2020 if (restart)
2021 zmcControl(monitor, RESTART);
2022 else
2023 zmcControl(monitor, "");
2024 zmaControl(monitor, RELOAD);
2025 }
2026 else
2027 if (m_debug)
2028 {
2029 std::cout << "zm daemons are not running\n";
2030 }
2031 }
2032 else
2033 {
2034 std::cout << "Not updating monitor function as identical to existing configuration\n";
2035 }
2036
2037 ADD_STR(outStr, "OK");
2038 send(outStr);
2039}
2040
2041void ZMServer::zmcControl(MONITOR *monitor, const std::string &mode)
2042{
2043 std::string zmcArgs;
2044 std::string sql;
2045 sql += "SELECT count(if(Function!='None',1,NULL)) as ActiveCount ";
2046 sql += "FROM Monitors ";
2047
2048 if (monitor->m_type == "Local" )
2049 {
2050 sql += "WHERE Device = '" + monitor->m_device + "'";
2051 zmcArgs = "-d " + monitor->m_device;
2052 }
2053 else
2054 {
2055 sql += "WHERE Id = '" + monitor->getIdStr() + "'";
2056 zmcArgs = "-m " + monitor->getIdStr();
2057 }
2058
2059 if (mysql_query(&g_dbConn, sql.c_str()))
2060 {
2061 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
2063 return;
2064 }
2065
2066 MYSQL_RES *res = mysql_store_result(&g_dbConn);
2067 MYSQL_ROW row = mysql_fetch_row(res);
2068 if (row == nullptr)
2069 {
2071 return;
2072 }
2073 int activeCount = atoi(row[0]);
2074
2075 if (!activeCount)
2076 {
2077 runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
2078 }
2079 else
2080 {
2081 if (mode == RESTART)
2082 runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
2083
2084 runCommand(g_binPath + "/zmdc.pl start zmc " + zmcArgs);
2085 }
2086}
2087
2088void ZMServer::zmaControl(MONITOR *monitor, const std::string &mode)
2089{
2090 int zmOptControl = atoi(getZMSetting("ZM_OPT_CONTROL").c_str());
2091 int zmOptFrameServer = atoi(getZMSetting("ZM_OPT_FRAME_SERVER").c_str());
2092
2093 if (monitor->m_function == FUNCTION_MODECT ||
2094 monitor->m_function == FUNCTION_RECORD ||
2095 monitor->m_function == FUNCTION_MOCORD ||
2096 monitor->m_function == FUNCTION_NODECT)
2097 {
2098 if (mode == RESTART)
2099 {
2100 if (zmOptControl)
2101 runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
2102
2103 runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
2104
2105 if (zmOptFrameServer)
2106 runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
2107 }
2108
2109 if (zmOptFrameServer)
2110 runCommand(g_binPath + "/zmdc.pl start zmf -m " + monitor->getIdStr());
2111
2112 runCommand(g_binPath + "/zmdc.pl start zma -m " + monitor->getIdStr());
2113
2114 if (zmOptControl && monitor->m_controllable && monitor->m_trackMotion &&
2115 ( monitor->m_function == FUNCTION_MODECT || monitor->m_function == FUNCTION_MOCORD) )
2116 runCommand(g_binPath + "/zmdc.pl start zmtrack.pl -m " + monitor->getIdStr());
2117
2118 if (mode == RELOAD)
2119 runCommand(g_binPath + "/zmdc.pl reload zma -m " + monitor->getIdStr());
2120 }
2121 else
2122 {
2123 if (zmOptControl)
2124 runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
2125
2126 runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
2127
2128 if (zmOptFrameServer)
2129 runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
2130 }
2131}
int getSubpixelOrder(void)
Definition: zmserver.cpp:463
SharedData * m_sharedData
Definition: zmserver.h:295
void initMonitor(bool debug, const std::string &mmapPath, int shmKey)
Definition: zmserver.cpp:221
int m_imageBufferCount
Definition: zmserver.h:281
int m_monId
Definition: zmserver.h:285
int m_trackMotion
Definition: zmserver.h:291
std::string m_device
Definition: zmserver.h:279
std::string getIdStr(void)
Definition: zmserver.cpp:418
bool isValid(void)
Definition: zmserver.cpp:402
int m_controllable
Definition: zmserver.h:290
SharedData32 * m_sharedData32
Definition: zmserver.h:297
int m_bytesPerPixel
Definition: zmserver.h:284
int m_lastRead
Definition: zmserver.h:287
SharedData26 * m_sharedData26
Definition: zmserver.h:296
int getLastWriteIndex(void)
Definition: zmserver.cpp:429
int m_enabled
Definition: zmserver.h:278
std::string m_function
Definition: zmserver.h:277
int m_mapFile
Definition: zmserver.h:292
std::string m_status
Definition: zmserver.h:288
SharedData34 * m_sharedData34
Definition: zmserver.h:298
int m_height
Definition: zmserver.h:283
int getFrameSize(void)
Definition: zmserver.cpp:484
std::string m_id
Definition: zmserver.h:299
int getState(void)
Definition: zmserver.cpp:446
void * m_shmPtr
Definition: zmserver.h:293
std::string m_type
Definition: zmserver.h:276
int m_width
Definition: zmserver.h:282
unsigned char * m_sharedImages
Definition: zmserver.h:286
bool m_debug
Definition: zmserver.h:343
void handleHello(void)
Definition: zmserver.cpp:723
std::vector< MONITOR * > m_monitors
Definition: zmserver.h:345
static std::string runCommand(const std::string &command)
Definition: zmserver.cpp:1055
void zmaControl(MONITOR *monitor, const std::string &mode)
Definition: zmserver.cpp:2088
void handleGetAlarmStates(void)
Definition: zmserver.cpp:783
key_t m_shmKey
Definition: zmserver.h:351
void zmcControl(MONITOR *monitor, const std::string &mode)
Definition: zmserver.cpp:2041
void handleGetMonitorStatus(void)
Definition: zmserver.cpp:960
std::string getZMSetting(const std::string &setting) const
Definition: zmserver.cpp:1902
void handleGetEventDates(std::vector< std::string > tokens)
Definition: zmserver.cpp:896
std::string m_eventFileFormat
Definition: zmserver.h:349
void handleGetEventList(std::vector< std::string > tokens)
Definition: zmserver.cpp:803
static void tokenize(const std::string &command, std::vector< std::string > &tokens)
Definition: zmserver.cpp:599
bool m_useDeepStorage
Definition: zmserver.h:347
void handleSetMonitorFunction(std::vector< std::string > tokens)
Definition: zmserver.cpp:1934
void handleGetServerStatus(void)
Definition: zmserver.cpp:752
void handleGetMonitorList(void)
Definition: zmserver.cpp:1572
bool processRequest(char *buf, int nbytes)
Definition: zmserver.cpp:622
std::string m_mmapPath
Definition: zmserver.h:352
static int getFrame(FrameData &buffer, MONITOR *monitor)
Definition: zmserver.cpp:1769
void getMonitorList(void)
Definition: zmserver.cpp:1697
std::map< int, MONITOR * > m_monitorMap
Definition: zmserver.h:346
bool m_useAnalysisImages
Definition: zmserver.h:348
void handleGetAnalysisFrame(std::vector< std::string > tokens)
Definition: zmserver.cpp:1208
void handleGetEventFrame(std::vector< std::string > tokens)
Definition: zmserver.cpp:1125
void handleGetLiveFrame(std::vector< std::string > tokens)
Definition: zmserver.cpp:1380
void handleDeleteEvent(std::vector< std::string > tokens)
Definition: zmserver.cpp:1607
bool send(const std::string &s) const
Definition: zmserver.cpp:683
void handleRunZMAudit(void)
Definition: zmserver.cpp:1679
void sendError(const std::string &error)
Definition: zmserver.cpp:716
int m_sock
Definition: zmserver.h:344
ZMServer(int sock, bool debug)
Definition: zmserver.cpp:503
void handleGetFrameList(std::vector< std::string > tokens)
Definition: zmserver.cpp:1448
void handleDeleteEventList(std::vector< std::string > tokens)
Definition: zmserver.cpp:1644
static void getMonitorStatus(const std::string &id, const std::string &type, const std::string &device, const std::string &host, const std::string &channel, const std::string &function, std::string &zmcStatus, std::string &zmaStatus, const std::string &enabled)
Definition: zmserver.cpp:1074
std::string m_analysisFileFormat
Definition: zmserver.h:350
void handleGetCameraList(void)
Definition: zmserver.cpp:1556
#define getloadavg(x, y)
Definition: compat.h:130
#define close
Definition: compat.h:28
#define minor(X)
Definition: compat.h:58
def error(message)
Definition: smolt.py:409
int FILE
Definition: mythburn.py:137
uint32_t state
Definition: zmserver.h:110
uint8_t format
Definition: zmserver.h:122
uint32_t last_write_index
Definition: zmserver.h:108
uint32_t imagesize
Definition: zmserver.h:123
uint32_t state
Definition: zmserver.h:143
uint32_t imagesize
Definition: zmserver.h:156
uint32_t last_write_index
Definition: zmserver.h:141
uint8_t format
Definition: zmserver.h:155
uint32_t imagesize
Definition: zmserver.h:194
uint8_t format
Definition: zmserver.h:193
uint32_t last_write_index
Definition: zmserver.h:179
uint32_t state
Definition: zmserver.h:181
int last_write_index
Definition: zmserver.h:89
State state
Definition: zmserver.h:88
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
void loadZMConfig(const std::string &configfile)
Definition: zmserver.cpp:97
static uintmax_t disk_usage_percent(const std::filesystem::space_info &space_info)
Definition: zmserver.cpp:733
static constexpr const char * ERROR_NO_FRAMES
Definition: zmserver.cpp:57
std::string g_eventsPath
Definition: zmserver.cpp:82
TimePoint g_lastDBKick
Definition: zmserver.cpp:87
static constexpr const char * ERROR_MYSQL_QUERY
Definition: zmserver.cpp:50
static constexpr const char * ERROR_INVALID_MONITOR_FUNCTION
Definition: zmserver.cpp:55
static constexpr const char * ERROR_INVALID_MONITOR
Definition: zmserver.cpp:53
static constexpr const char * ERROR_FILE_OPEN
Definition: zmserver.cpp:52
bool checkVersion(int major, int minor, int revision)
Definition: zmserver.cpp:90
std::string g_server
Definition: zmserver.cpp:75
int g_revisionVersion
Definition: zmserver.cpp:85
static constexpr const char * ERROR_INVALID_MONITOR_ENABLE_VALUE
Definition: zmserver.cpp:56
my_bool reconnect_t
Definition: zmserver.cpp:166
static constexpr int MSG_NOSIGNAL
Definition: zmserver.cpp:35
std::string g_user
Definition: zmserver.cpp:78
std::string g_database
Definition: zmserver.cpp:76
int g_majorVersion
Definition: zmserver.cpp:83
static void ADD_INT(std::string &list, int n)
Definition: zmserver.cpp:45
std::string g_webUser
Definition: zmserver.cpp:79
static constexpr const char * ZM_PROTOCOL_VERSION
Definition: zmserver.cpp:41
void kickDatabase(bool debug)
Definition: zmserver.cpp:194
ZM_SUBPIX_ORDER
Definition: zmserver.cpp:62
@ ZM_SUBPIX_ORDER_NONE
Definition: zmserver.cpp:63
@ ZM_SUBPIX_ORDER_ABGR
Definition: zmserver.cpp:68
@ ZM_SUBPIX_ORDER_ARGB
Definition: zmserver.cpp:69
@ ZM_SUBPIX_ORDER_BGR
Definition: zmserver.cpp:65
@ ZM_SUBPIX_ORDER_RGB
Definition: zmserver.cpp:64
@ ZM_SUBPIX_ORDER_RGBA
Definition: zmserver.cpp:67
@ ZM_SUBPIX_ORDER_BGRA
Definition: zmserver.cpp:66
static constexpr const char * ERROR_TOKEN_COUNT
Definition: zmserver.cpp:49
static constexpr const char * ERROR_INVALID_POINTERS
Definition: zmserver.cpp:54
int g_minorVersion
Definition: zmserver.cpp:84
std::string g_zmversion
Definition: zmserver.cpp:73
MYSQL g_dbConn
Definition: zmserver.cpp:72
static void ADD_STR(std::string &list, const std::string &s)
Definition: zmserver.cpp:43
static constexpr const char * ERROR_MYSQL_ROW
Definition: zmserver.cpp:51
std::string g_password
Definition: zmserver.cpp:74
std::string g_webPath
Definition: zmserver.cpp:77
std::string g_mmapPath
Definition: zmserver.cpp:81
void connectToDatabase(void)
Definition: zmserver.cpp:169
std::string g_binPath
Definition: zmserver.cpp:80
const std::string FUNCTION_NODECT
Definition: zmserver.h:59
@ TAPE
Definition: zmserver.h:74
@ ALERT
Definition: zmserver.h:73
@ ALARM
Definition: zmserver.h:72
@ PREALARM
Definition: zmserver.h:71
@ IDLE
Definition: zmserver.h:70
const std::string RELOAD
Definition: zmserver.h:65
std::array< uint8_t, MAX_IMAGE_SIZE > FrameData
Definition: zmserver.h:33
const std::string RUNNING
Definition: zmserver.h:66
const std::string FUNCTION_MONITOR
Definition: zmserver.h:57
const std::string FUNCTION_MODECT
Definition: zmserver.h:58
const std::string FUNCTION_RECORD
Definition: zmserver.h:60
std::chrono::time_point< Clock > TimePoint
Definition: zmserver.h:29
const std::string FUNCTION_MOCORD
Definition: zmserver.h:61
const std::string FUNCTION_NONE
Definition: zmserver.h:62
const std::string RESTART
Definition: zmserver.h:64
static constexpr std::chrono::seconds DB_CHECK_TIME
Definition: zmserver.h:54