ThreadWriteDBManager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #include "ThreadWriteDBManager.h"
  2. #include "GlobalInfo.h"
  3. #include "GlobalVariable.h"
  4. #include "spdlog.h"
  5. #include "GlobalInfo.h"
  6. #include "SystemConfig.h"
  7. #include <mutex>
  8. #include <qdatetime.h>
  9. #include <thread>
  10. #include <QFile>
  11. ThreadWriteDBManager::~ThreadWriteDBManager()
  12. {
  13. }
  14. /* 线程工作函数 */
  15. void ThreadWriteDBManager::thread_task()
  16. {
  17. /* 初始化信息 */
  18. if(!initData())
  19. {
  20. SPDLOG_LOGGER_ERROR(m_logger, "写数据库线程初始化数据失败");
  21. return;
  22. }
  23. /* 设置标志位 */
  24. m_isRunning = true;
  25. m_isStop = false;
  26. m_threadState = EThreadState::State_Running;
  27. SPDLOG_LOGGER_INFO(m_logger, "✉ 写数据库线程开始运行");
  28. /* 运行线程功能 */
  29. task();
  30. /* 清理数据 */
  31. clearData();
  32. m_threadState = EThreadState::State_Stopped;
  33. m_isStop = true;
  34. SPDLOG_LOGGER_WARN(m_logger, "✉ 写数据库线程结束运行");
  35. }
  36. /* 停止线程 */
  37. void ThreadWriteDBManager::thread_stop()
  38. {
  39. m_isRunning = false; // 设置线程停止标志位
  40. }
  41. void ThreadWriteDBManager::thread_stop_block()
  42. {
  43. thread_stop();
  44. while(m_threadState != EThreadState::State_Stopped) // 等待线程停止
  45. {
  46. std::this_thread::sleep_for(std::chrono::milliseconds(1));
  47. }
  48. }
  49. /* 添加报警内容 */
  50. void ThreadWriteDBManager::addAlarmInfo(const AlarmInfo_t& alarmInfo)
  51. {
  52. std::lock_guard<std::mutex> lock(m_mutexListAlarm);
  53. m_listAlarm.push_back(alarmInfo);
  54. }
  55. void ThreadWriteDBManager::addAlarmInfo(const std::list<AlarmInfo_t>& listAlarm)
  56. {
  57. std::lock_guard<std::mutex> lock(m_mutexListAlarm);
  58. m_listAlarm.insert(m_listAlarm.end(), listAlarm.begin(), listAlarm.end());
  59. }
  60. /* 添加一致性报警信息,键为主通道信息,值为次通道信息 */
  61. void ThreadWriteDBManager::addAlarmInfo(const AlarmInfo_t& mainAlarm, const AlarmInfo_t& subAlarm)
  62. {
  63. std::lock_guard<std::mutex> lock(m_mutexListAlarmConsistency);
  64. m_listAlarmConsistency.push_back(std::make_pair(mainAlarm, subAlarm));
  65. }
  66. /* 录音文件写入数据库,isNewFile = true 是插入数据库,= false是更新数据库 */
  67. void ThreadWriteDBManager::addRecordFileInfo(std::list<RecordFileInfo_t>& listRecordFile, bool isNewFile)
  68. {
  69. std::lock_guard<std::mutex> lock(m_mutexListRecordFile);
  70. if(isNewFile)
  71. {
  72. m_listRecordFileInsert.insert(m_listRecordFileInsert.end(), listRecordFile.begin(), listRecordFile.end());
  73. } else {
  74. m_listRecordFileUpdate.insert(m_listRecordFileUpdate.end(), listRecordFile.begin(), listRecordFile.end());
  75. }
  76. }
  77. /* 线程工作函数 */
  78. void ThreadWriteDBManager::task()
  79. {
  80. while(m_isRunning.load())
  81. {
  82. std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 每100毫秒执行一次
  83. m_currentTime = QDateTime::currentDateTime();
  84. /* 写入普通报警文件 */
  85. alarmInfoToDB();
  86. /* 写入一致性报警文件 */
  87. consistencyAlarmInfoToDB();
  88. /* 写录音文件信息到数据库 */
  89. recordFileInfoToDB();
  90. /* 定时删除报警文件和录音文件,文件路径从数据库中获取 */
  91. deleteTimeoutFile();
  92. /* 定时删除数据库信息 */
  93. deleteTimeoutRecordInfo();
  94. }
  95. }
  96. /* 初始化数据 */
  97. bool ThreadWriteDBManager::initData()
  98. {
  99. if(m_logger == nullptr)
  100. {
  101. m_logger = spdlog::get("WriteDB");
  102. if(m_logger == nullptr)
  103. {
  104. fmt::print("Alarm is nullptr");
  105. return false; // 日志记录器获取失败
  106. }
  107. }
  108. /* 登陆WebAPI */
  109. if(!m_fromWebAPI.initWebApi(GInfo.webAPIUrl(), GInfo.webAPIID(), GInfo.webApiAppType()))
  110. {
  111. SPDLOG_LOGGER_ERROR(m_logger, "报警信息处理线程登录WebAPI失败");
  112. return false;
  113. }
  114. SPDLOG_LOGGER_INFO(m_logger, "报警信息处理线程登录WebAPI成功");
  115. /* 获取文件存储的根目录 */
  116. m_strRootPath = GInfo.rootPath();
  117. if(m_strRootPath.endsWith('/'))
  118. {
  119. m_strRootPath.chop(1);
  120. }
  121. /* 获取数据库的配置信息 */
  122. m_dbConfig = SysConfig.getDatabaseConfig();
  123. m_lastProcessLogTime = QDateTime::currentDateTime();
  124. m_lastDeleteFileTime = QDateTime::currentDateTime();
  125. return true;
  126. }
  127. /* 清理数据 */
  128. void ThreadWriteDBManager::clearData()
  129. {
  130. }
  131. /* 普通报警写入数据库 */
  132. void ThreadWriteDBManager::alarmInfoToDB()
  133. {
  134. /* 普通的报警数据写入数据库 */
  135. std::list<AlarmInfo_t> listAlarm;
  136. {
  137. std::lock_guard<std::mutex> lock(m_mutexListAlarm);
  138. if(m_listAlarm.empty())
  139. {
  140. return; // 没有报警信息
  141. }
  142. listAlarm = std::move(m_listAlarm); // 移动报警信息列表
  143. m_listAlarm.clear(); // 清空报警列表
  144. }
  145. for(auto& alarm : listAlarm)
  146. {
  147. /* 处理报警路径 */
  148. processFilePath(alarm.strAlarmFilePath);
  149. }
  150. if(!m_fromWebAPI.insertAlarmInfo(listAlarm))
  151. {
  152. SPDLOG_LOGGER_ERROR(m_logger, "写入报警信息失败");
  153. }
  154. /* 清空报警列表 */
  155. listAlarm.clear();
  156. }
  157. /* 一致性报警写入数据库 */
  158. void ThreadWriteDBManager::consistencyAlarmInfoToDB()
  159. {
  160. /* 写入一致性报警信息 */
  161. std::list<std::pair<AlarmInfo_t, AlarmInfo_t>> listAlarmConsistency;
  162. {
  163. std::lock_guard<std::mutex> lock(m_mutexListAlarmConsistency);
  164. if(m_listAlarmConsistency.empty())
  165. {
  166. return; // 没有一致性报警信息
  167. }
  168. listAlarmConsistency = std::move(m_listAlarmConsistency); // 移动一致性报警信息列表
  169. m_listAlarmConsistency.clear(); // 清空一致性报警列表
  170. }
  171. /* 处理报警路径 */
  172. for(auto it = listAlarmConsistency.begin(); it != listAlarmConsistency.end(); ++it)
  173. {
  174. processFilePath(it->first.strAlarmFilePath);
  175. processFilePath(it->second.strAlarmFilePath);
  176. }
  177. for(const auto& alarmPair : listAlarmConsistency)
  178. {
  179. if(!m_fromWebAPI.insertConsistencyAlarmInfo(alarmPair.first, alarmPair.second))
  180. {
  181. SPDLOG_LOGGER_ERROR(m_logger, "写入一致性报警信息失败");
  182. }
  183. }
  184. /* 清空报警信息 */
  185. listAlarmConsistency.clear();
  186. }
  187. /* 录音文件写数据库 */
  188. void ThreadWriteDBManager::recordFileInfoToDB()
  189. {
  190. std::list<RecordFileInfo_t> listRecordFileInsert;
  191. std::list<RecordFileInfo_t> listRecordFileUpdate;
  192. {
  193. std::lock_guard<std::mutex> lock(m_mutexListRecordFile);
  194. if(m_listRecordFileInsert.empty() && m_listRecordFileUpdate.empty())
  195. {
  196. return; // 没有录音文件信息
  197. }
  198. /* 移动录音文件 */
  199. if(!m_listRecordFileInsert.empty())
  200. {
  201. listRecordFileInsert = std::move(m_listRecordFileInsert); // 移动录音文件信息列表
  202. m_listRecordFileInsert.clear();
  203. }
  204. if(!m_listRecordFileUpdate.empty())
  205. {
  206. listRecordFileUpdate = std::move(m_listRecordFileUpdate); // 移动录音文件信息列表
  207. m_listRecordFileUpdate.clear();
  208. }
  209. }
  210. /* 处理录音文件路径 */
  211. for(auto& recordFile : listRecordFileInsert)
  212. {
  213. processFilePath(recordFile.FilePath);
  214. }
  215. for(auto& recordFile : listRecordFileUpdate)
  216. {
  217. processFilePath(recordFile.FilePath);
  218. }
  219. if(!listRecordFileInsert.empty())
  220. {
  221. if(!m_fromWebAPI.insertRecordFileInfo(listRecordFileInsert))
  222. {
  223. SPDLOG_LOGGER_ERROR(m_logger, "写入录音文件信息失败");
  224. }
  225. }
  226. if(!listRecordFileUpdate.empty())
  227. {
  228. if(!m_fromWebAPI.updateRecordFileInfo(listRecordFileUpdate))
  229. {
  230. SPDLOG_LOGGER_ERROR(m_logger, "更新录音文件信息失败");
  231. }
  232. }
  233. }
  234. /* 处理报警路径,去掉前面的本地路径,只保留服务自己创建的文件夹及之后的路径 */
  235. bool ThreadWriteDBManager::processFilePath(QString& strFilePath)
  236. {
  237. if(strFilePath.isEmpty())
  238. {
  239. return false;
  240. }
  241. /* 去掉前面的目录,只留下报警文件夹及之后的路径 */
  242. if(strFilePath.startsWith(m_strRootPath))
  243. {
  244. strFilePath = strFilePath.mid(m_strRootPath.length());
  245. if(!strFilePath.startsWith('/'))
  246. {
  247. strFilePath = '/' + strFilePath;
  248. }
  249. }
  250. return true;
  251. }
  252. /* 定时删除报警文件和录音文件,文件路径从数据库中获取,十分钟检查一次
  253. 注意: 这个函数需要在删除数据库记录之前执行,删除文件路径是从数据库中读取的 */
  254. void ThreadWriteDBManager::deleteTimeoutFile()
  255. {
  256. if(m_lastDeleteFileTime.secsTo(m_currentTime) < 600)
  257. {
  258. return; // 十分钟检查一次
  259. }
  260. m_lastDeleteFileTime = m_currentTime;
  261. /* 删除录音文件 */
  262. deleteTimeoutRecordFile();
  263. /* 删除报警文件 */
  264. deleteTimeoutAlarmFile();
  265. }
  266. /* 删除录音文件 */
  267. void ThreadWriteDBManager::deleteTimeoutRecordFile()
  268. {
  269. /* 计算录音文件删除时间 */
  270. int retainHour = SysConfig.getDatabaseConfig().nRecordFileRetain;
  271. QDateTime recordDeleteTime = m_currentTime.addSecs(-retainHour * 3600);
  272. /* 从数据库中获取早于这个时间的录音文件记录 */
  273. std::list<RecordFileInfo_t> listRecordFile;
  274. if(!m_fromWebAPI.getRecordFileInfoBeforeTime(recordDeleteTime, listRecordFile))
  275. {
  276. SPDLOG_LOGGER_ERROR(m_logger, "获取早于 {} 的录音文件信息失败", recordDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  277. return;
  278. }
  279. if(listRecordFile.empty())
  280. {
  281. SPDLOG_LOGGER_INFO(m_logger, "没有早于 {} 的录音文件信息", recordDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  282. return;
  283. }
  284. for(auto& recordFile : listRecordFile)
  285. {
  286. QString strFilePath = recordFile.FilePath;
  287. if(strFilePath.isEmpty())
  288. {
  289. continue; // 跳过空路径
  290. }
  291. /* 删除文件 */
  292. QString fullPath = m_strRootPath + strFilePath;
  293. if(QFile::exists(fullPath))
  294. {
  295. if(!QFile::remove(fullPath))
  296. {
  297. SPDLOG_LOGGER_ERROR(m_logger, "删除录音文件 {} 失败", fullPath.toStdString());
  298. } else
  299. {
  300. SPDLOG_LOGGER_TRACE(m_logger, "删除录音文件 {}", fullPath.toStdString());
  301. }
  302. } else
  303. {
  304. SPDLOG_LOGGER_WARN(m_logger, "录音文件 {} 不存在", fullPath.toStdString());
  305. }
  306. recordFile.fileState = eRecordState::eRS_Deleted;
  307. }
  308. /* 更新数据库录音文件信息 */
  309. if(!m_fromWebAPI.updateRecordFileInfo(listRecordFile))
  310. {
  311. SPDLOG_LOGGER_ERROR(m_logger, "更新数据库录音文件信息失败");
  312. return;
  313. }
  314. }
  315. /* 删除报警文件 */
  316. void ThreadWriteDBManager::deleteTimeoutAlarmFile()
  317. {
  318. /* 计算报警文件删除时间 */
  319. int retainDay = SysConfig.getDatabaseConfig().nAlarmFileRetain;
  320. QDateTime alarmDeleteTime = m_currentTime.addDays(-retainDay);
  321. /* 从数据库中获取早于这个时间的报警文件记录 */
  322. std::list<AlarmInfo_t> listAlarm;
  323. if(!m_fromWebAPI.getAlarmInfoBeforeTime(alarmDeleteTime, listAlarm))
  324. {
  325. SPDLOG_LOGGER_ERROR(m_logger, "获取早于 {} 的报警信息失败", alarmDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  326. return;
  327. }
  328. if(listAlarm.empty())
  329. {
  330. SPDLOG_LOGGER_INFO(m_logger, "没有早于 {} 的报警信息", alarmDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  331. return;
  332. }
  333. for(auto& alarm : listAlarm)
  334. {
  335. QString strFilePath = alarm.strAlarmFilePath;
  336. if(strFilePath.isEmpty())
  337. {
  338. continue; // 跳过空路径
  339. }
  340. /* 删除文件 */
  341. QString fullPath = m_strRootPath + strFilePath;
  342. if(QFile::exists(fullPath))
  343. {
  344. if(!QFile::remove(fullPath))
  345. {
  346. SPDLOG_LOGGER_ERROR(m_logger, "删除报警文件 {} 失败", fullPath.toStdString());
  347. } else
  348. {
  349. SPDLOG_LOGGER_TRACE(m_logger, "删除报警文件 {}", fullPath.toStdString());
  350. }
  351. } else
  352. {
  353. SPDLOG_LOGGER_WARN(m_logger, "删除报警音频文件失败,报警文件 {} 不存在", fullPath.toStdString());
  354. }
  355. alarm.fileState = eRecordState::eRS_Deleted;
  356. }
  357. /* 更新数据库中的报警信息 */
  358. if(!m_fromWebAPI.updateAlarmFileState(listAlarm))
  359. {
  360. SPDLOG_LOGGER_ERROR(m_logger, "更新数据库报警文件信息失败");
  361. return;
  362. }
  363. }
  364. /* 删除超过时长的数据库记录信息 */
  365. void ThreadWriteDBManager::deleteTimeoutRecordInfo()
  366. {
  367. if(m_lastProcessLogTime.secsTo(m_currentTime) < 600)
  368. {
  369. return;
  370. }
  371. m_lastProcessLogTime = m_currentTime;
  372. /* 计算录音文件删除时间 */
  373. int retainDay = SysConfig.getDatabaseConfig().nRecordLogRetain;
  374. QDateTime recordDeleteTime = m_currentTime.addDays(-retainDay);
  375. if(!m_fromWebAPI.deleteRecordFileInfoBeforeTime(recordDeleteTime))
  376. {
  377. SPDLOG_LOGGER_ERROR(m_logger, "删除早于 {} 的录音文件信息失败", recordDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  378. }
  379. /* 计算报警日志删除时间 */
  380. retainDay = SysConfig.getDatabaseConfig().nAlarmLogRetain;
  381. QDateTime alarmDeleteTime = m_currentTime.addDays(-retainDay);
  382. if(!m_fromWebAPI.deleteAlarmInfoBeforeTime(alarmDeleteTime))
  383. {
  384. SPDLOG_LOGGER_ERROR(m_logger, "删除早于 {} 的报警信息失败", alarmDeleteTime.toString("yyyy-MM-dd HH:mm:ss").toStdString());
  385. }
  386. }