event_recorder.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021-04-09 armink the first version
  9. */
  10. #include <stdbool.h>
  11. #include "event_recorder.h"
  12. #include <rtthread.h>
  13. #define LOG_TAG "event_recorder"
  14. #define LOG_LVL DBG_INFO
  15. #include <rtdbg.h>
  16. struct event_source {
  17. uint8_t id;
  18. const char *name;
  19. int (*replay)(uint8_t id, const uint8_t *value, size_t value_len);
  20. };
  21. struct er_event {
  22. union {
  23. struct {
  24. uint8_t id;
  25. uint8_t value_len;
  26. rt_tick_t interval;
  27. uint8_t value[EVENT_VALUE_MAX_SIZE];
  28. } element;
  29. uint8_t raw[8 + EVENT_VALUE_MAX_SIZE];
  30. } content;
  31. };
  32. static struct event_source source_tabel[EVENT_SOURCE_MAX_NUM] = { 0 };
  33. static struct rt_mutex locker;
  34. static bool is_init = false, is_recording = false, is_replaing = false;
  35. static rt_tick_t last_trigger_tick = 0, repaly_start = 0, replay_setting_duration = 0;
  36. #ifdef EVENT_RECORDER_USING_FLASHDB
  37. #include <flashdb.h>
  38. /* TSDB object */
  39. struct fdb_tsdb tsdb = { 0 };
  40. #define TSDB_TSL_LEN 1024
  41. #define TSDB_SEC 32768
  42. #define TSDB_SIZE (TSDB_SEC * 4)
  43. #endif /* EVENT_RECORDER_USING_FLASHDB */
  44. #define LOCK() rt_mutex_take(&locker, RT_WAITING_FOREVER)
  45. #define UNLOCK() rt_mutex_release(&locker)
  46. int event_recorder_init(void)
  47. {
  48. if (is_init)
  49. {
  50. return 0;
  51. }
  52. rt_mutex_init(&locker, "ev_reco", RT_IPC_FLAG_FIFO);
  53. is_init = true;
  54. return 0;
  55. }
  56. int event_recorder_control(void)
  57. {
  58. return 0;
  59. }
  60. static fdb_time_t get_time(void)
  61. {
  62. static fdb_time_t counts = 0;
  63. return ++counts;
  64. }
  65. #ifdef EVENT_RECORDER_USING_FLASHDB
  66. static int open_tsdb(const char *dir, const char *name)
  67. {
  68. bool file_mode = true, rollver = false;
  69. uint32_t tsdb_len = TSDB_TSL_LEN;
  70. uint32_t tsdb_sec = TSDB_SEC, tsdb_size = TSDB_SIZE;
  71. fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_SEC_SIZE, &tsdb_sec);
  72. fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_FILE_MODE, &file_mode);
  73. fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_MAX_SIZE, &tsdb_size);
  74. if (fdb_tsdb_init(&tsdb, name, dir, get_time, tsdb_len, NULL) != FDB_NO_ERR)
  75. {
  76. LOG_E("tsdb(%s:%s) create failed", dir, name);
  77. return -1;
  78. }
  79. fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_ROLLOVER, &rollver);
  80. return 0;
  81. }
  82. #endif /* EVENT_RECORDER_USING_FLASHDB */
  83. int event_recorder_start(const char *dir, const char *name)
  84. {
  85. int result = 0;
  86. RT_ASSERT(is_init);
  87. LOCK();
  88. if (!is_recording)
  89. {
  90. #ifdef EVENT_RECORDER_USING_FLASHDB
  91. if (open_tsdb(dir, name) != 0)
  92. {
  93. result = -1;
  94. }
  95. else
  96. #endif /* EVENT_RECORDER_USING_FLASHDB */
  97. {
  98. is_recording = true;
  99. }
  100. }
  101. else
  102. {
  103. LOG_W("The recorder is already starting. Please STOP before you want to restart.");
  104. }
  105. UNLOCK();
  106. return result;
  107. }
  108. int event_recorder_stop(void)
  109. {
  110. RT_ASSERT(is_init);
  111. LOCK();
  112. if (is_recording)
  113. {
  114. is_recording = false;
  115. last_trigger_tick = 0;
  116. #ifdef EVENT_RECORDER_USING_FLASHDB
  117. /* close database */
  118. fdb_tsdb_deinit(&tsdb);
  119. #endif /* EVENT_RECORDER_USING_FLASHDB */
  120. }
  121. UNLOCK();
  122. return 0;
  123. }
  124. int event_source_register(uint8_t id, const char *name,
  125. int (*replay)(uint8_t id, const uint8_t *value, size_t value_len))
  126. {
  127. off_t i, empty_index = -1;
  128. int result = -1;
  129. RT_ASSERT(is_init);
  130. RT_ASSERT(id != 0);
  131. RT_ASSERT(name);
  132. RT_ASSERT(replay);
  133. LOCK();
  134. //TODO 检查是否处于录制或回放中
  135. for (i = 0; i < EVENT_SOURCE_MAX_NUM; i++)
  136. {
  137. /* check the same source */
  138. if (source_tabel[i].id == id)
  139. {
  140. source_tabel[i].name = name;
  141. source_tabel[i].replay = replay;
  142. result = 0;
  143. goto __exit;
  144. }
  145. else if (source_tabel[i].id == 0 && empty_index == -1)
  146. {
  147. empty_index = i;
  148. }
  149. }
  150. /* not found the same source */
  151. if (empty_index != -1)
  152. {
  153. source_tabel[empty_index].id = id;
  154. source_tabel[empty_index].name = name;
  155. source_tabel[empty_index].replay = replay;
  156. result = 0;
  157. }
  158. __exit:
  159. UNLOCK();
  160. return result;
  161. }
  162. int event_source_trigger(uint8_t event_id, uint8_t *event_value, size_t value_len)
  163. {
  164. if (!is_init)
  165. {
  166. return -1;
  167. }
  168. LOCK();
  169. if (is_recording)
  170. {
  171. struct er_event event;
  172. struct fdb_blob blob;
  173. rt_tick_t interval = 0;
  174. fdb_err_t result = FDB_NO_ERR;
  175. /* calculate the interval from last record */
  176. rt_tick_t cur_tick = rt_tick_get();
  177. if (last_trigger_tick == 0) {
  178. last_trigger_tick = cur_tick;
  179. }
  180. interval = cur_tick - last_trigger_tick;
  181. last_trigger_tick = cur_tick;
  182. /* make event object */
  183. event.content.element.id = event_id;
  184. event.content.element.interval = interval;
  185. if (value_len > EVENT_VALUE_MAX_SIZE)
  186. {
  187. LOG_W("the event value length (%d) is too long", value_len);
  188. value_len = EVENT_VALUE_MAX_SIZE;
  189. }
  190. event.content.element.value_len = value_len;
  191. memcpy(event.content.element.value, event_value, value_len);
  192. LOG_D("Record an event (%d), len %d", event_id, value_len);
  193. /* append the new record */
  194. result = fdb_tsl_append(&tsdb, fdb_blob_make(&blob, event.content.raw, sizeof(event.content.raw)));
  195. if (result == FDB_SAVED_FULL)
  196. {
  197. LOG_E("Record log is FULL, please increase the TSDB max size.");
  198. }
  199. else if (result != FDB_NO_ERR)
  200. {
  201. LOG_E("Record log append failed(%d).", result);
  202. }
  203. }
  204. UNLOCK();
  205. return 0;
  206. }
  207. static bool query_cb(fdb_tsl_t tsl, void *arg)
  208. {
  209. int *result = (int *)arg;
  210. struct fdb_blob blob;
  211. off_t i;
  212. struct er_event event;
  213. /* read the event record */
  214. fdb_blob_read((fdb_db_t) &tsdb,
  215. fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, event.content.raw, sizeof(event.content.raw))));
  216. /* delay some time for every event interval */
  217. rt_thread_delay(event.content.element.interval);
  218. if (rt_tick_get() - repaly_start <= replay_setting_duration && is_replaing)
  219. {
  220. /* replay the event */
  221. for (i = 0; i < EVENT_SOURCE_MAX_NUM; i++)
  222. {
  223. /* check the same source */
  224. if (source_tabel[i].id == event.content.element.id)
  225. {
  226. LOG_D("Replay an event (%d), len %d", event.content.element.id, event.content.element.value_len);
  227. if (source_tabel[i].replay(event.content.element.id, event.content.element.value,
  228. event.content.element.value_len) < 0)
  229. {
  230. LOG_W("Event source %s replay failed. Now will STOP replay.", source_tabel[i].name);
  231. *result = -1;
  232. return true;
  233. }
  234. break;
  235. }
  236. }
  237. return false;
  238. }
  239. else
  240. {
  241. return true;
  242. }
  243. }
  244. static void replay_entry(void *parameter)
  245. {
  246. int result = 0, count = 0;
  247. repaly_start = rt_tick_get();
  248. while (rt_tick_get() - repaly_start <= replay_setting_duration && is_replaing && result >= 0)
  249. {
  250. LOG_I("Replay count is [%d].", ++count);
  251. fdb_tsl_iter(&tsdb, query_cb, &result);
  252. }
  253. event_replay_stop();
  254. }
  255. int event_replay_start(const char *dir, const char *name, uint32_t time_s)
  256. {
  257. int result = 0;
  258. RT_ASSERT(is_init);
  259. LOCK();
  260. if (is_recording)
  261. {
  262. LOG_W("Event is recording, please STOP record first.");
  263. result = -1;
  264. goto __exit;
  265. }
  266. if (!is_replaing)
  267. {
  268. rt_thread_t tid = rt_thread_create("replay", replay_entry, NULL, 4096, 20, 15);
  269. if (tid)
  270. {
  271. if (open_tsdb(dir, name) == 0)
  272. {
  273. if (time_s)
  274. {
  275. replay_setting_duration = rt_tick_from_millisecond(time_s * 1000);
  276. }
  277. else
  278. {
  279. replay_setting_duration = UINT32_MAX;
  280. }
  281. is_replaing = true;
  282. rt_thread_startup(tid);
  283. }
  284. else
  285. {
  286. rt_thread_delete(tid);
  287. result = -1;
  288. }
  289. }
  290. }
  291. __exit:
  292. UNLOCK();
  293. return result;
  294. }
  295. int event_replay_stop(void)
  296. {
  297. RT_ASSERT(is_init);
  298. LOCK();
  299. if (is_replaing)
  300. {
  301. is_replaing = false;
  302. replay_setting_duration = 0;
  303. /* close database */
  304. fdb_tsdb_deinit(&tsdb);
  305. }
  306. UNLOCK();
  307. return 0;
  308. }