bt_test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #include "bt_test.h"
  2. #include <gui/canvas.h>
  3. #include <gui/elements.h>
  4. #include <m-array.h>
  5. #include <furi.h>
  6. #include <stdint.h>
  7. struct BtTestParam {
  8. const char* label;
  9. uint8_t current_value_index;
  10. FuriString* current_value_text;
  11. uint8_t values_count;
  12. BtTestParamChangeCallback change_callback;
  13. void* context;
  14. };
  15. ARRAY_DEF(BtTestParamArray, BtTestParam, M_POD_OPLIST);
  16. struct BtTest {
  17. View* view;
  18. BtTestChangeStateCallback change_state_callback;
  19. BtTestBackCallback back_callback;
  20. void* context;
  21. };
  22. typedef struct {
  23. BtTestState state;
  24. BtTestParamArray_t params;
  25. uint8_t position;
  26. uint8_t window_position;
  27. const char* message;
  28. float rssi;
  29. uint32_t packets_num_rx;
  30. uint32_t packets_num_tx;
  31. } BtTestModel;
  32. #define BT_TEST_START_MESSAGE "Ok - Start"
  33. #define BT_TEST_STOP_MESSAGE "Ok - Stop"
  34. static void bt_test_process_up(BtTest* bt_test);
  35. static void bt_test_process_down(BtTest* bt_test);
  36. static void bt_test_process_left(BtTest* bt_test);
  37. static void bt_test_process_right(BtTest* bt_test);
  38. static void bt_test_process_ok(BtTest* bt_test);
  39. static void bt_test_process_back(BtTest* bt_test);
  40. static void bt_test_draw_callback(Canvas* canvas, void* _model) {
  41. BtTestModel* model = _model;
  42. char info_str[32];
  43. const uint8_t param_height = 16;
  44. const uint8_t param_width = 123;
  45. canvas_clear(canvas);
  46. uint8_t position = 0;
  47. BtTestParamArray_it_t it;
  48. canvas_set_font(canvas, FontSecondary);
  49. for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
  50. BtTestParamArray_next(it)) {
  51. uint8_t param_position = position - model->window_position;
  52. uint8_t params_on_screen = 3;
  53. uint8_t y_offset = 0;
  54. if(param_position < params_on_screen) {
  55. const BtTestParam* param = BtTestParamArray_cref(it);
  56. uint8_t param_y = y_offset + (param_position * param_height);
  57. uint8_t param_text_y = param_y + param_height - 4;
  58. if(position == model->position) {
  59. canvas_set_color(canvas, ColorBlack);
  60. elements_slightly_rounded_box(
  61. canvas, 0, param_y + 1, param_width, param_height - 2);
  62. canvas_set_color(canvas, ColorWhite);
  63. } else {
  64. canvas_set_color(canvas, ColorBlack);
  65. }
  66. canvas_draw_str(canvas, 6, param_text_y, param->label);
  67. if(param->current_value_index > 0) {
  68. canvas_draw_str(canvas, 50, param_text_y, "<");
  69. }
  70. canvas_draw_str(
  71. canvas, 61, param_text_y, furi_string_get_cstr(param->current_value_text));
  72. if(param->current_value_index < (param->values_count - 1)) {
  73. canvas_draw_str(canvas, 113, param_text_y, ">");
  74. }
  75. }
  76. position++;
  77. }
  78. elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params));
  79. canvas_draw_str(canvas, 6, 60, model->message);
  80. if(model->state == BtTestStateStarted) {
  81. if(model->rssi != 0.0f) {
  82. snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi);
  83. canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
  84. }
  85. } else if(model->state == BtTestStateStopped) {
  86. if(model->packets_num_rx) {
  87. snprintf(info_str, sizeof(info_str), "%ld pack rcv", model->packets_num_rx);
  88. canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
  89. } else if(model->packets_num_tx) {
  90. snprintf(info_str, sizeof(info_str), "%ld pack sent", model->packets_num_tx);
  91. canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
  92. }
  93. }
  94. }
  95. static bool bt_test_input_callback(InputEvent* event, void* context) {
  96. BtTest* bt_test = context;
  97. furi_assert(bt_test);
  98. bool consumed = false;
  99. if(event->type == InputTypeShort) {
  100. switch(event->key) {
  101. case InputKeyUp:
  102. consumed = true;
  103. bt_test_process_up(bt_test);
  104. break;
  105. case InputKeyDown:
  106. consumed = true;
  107. bt_test_process_down(bt_test);
  108. break;
  109. case InputKeyLeft:
  110. consumed = true;
  111. bt_test_process_left(bt_test);
  112. break;
  113. case InputKeyRight:
  114. consumed = true;
  115. bt_test_process_right(bt_test);
  116. break;
  117. case InputKeyOk:
  118. consumed = true;
  119. bt_test_process_ok(bt_test);
  120. break;
  121. case InputKeyBack:
  122. consumed = false;
  123. bt_test_process_back(bt_test);
  124. break;
  125. default:
  126. break;
  127. }
  128. }
  129. return consumed;
  130. }
  131. void bt_test_process_up(BtTest* bt_test) {
  132. with_view_model(
  133. bt_test->view,
  134. BtTestModel * model,
  135. {
  136. uint8_t params_on_screen = 3;
  137. if(model->position > 0) {
  138. model->position--;
  139. if(((model->position - model->window_position) < 1) &&
  140. model->window_position > 0) {
  141. model->window_position--;
  142. }
  143. } else {
  144. model->position = BtTestParamArray_size(model->params) - 1;
  145. if(model->position > (params_on_screen - 1)) {
  146. model->window_position = model->position - (params_on_screen - 1);
  147. }
  148. }
  149. },
  150. true);
  151. }
  152. void bt_test_process_down(BtTest* bt_test) {
  153. with_view_model(
  154. bt_test->view,
  155. BtTestModel * model,
  156. {
  157. uint8_t params_on_screen = 3;
  158. if(model->position < (BtTestParamArray_size(model->params) - 1)) {
  159. model->position++;
  160. if((model->position - model->window_position) > (params_on_screen - 2) &&
  161. model->window_position <
  162. (BtTestParamArray_size(model->params) - params_on_screen)) {
  163. model->window_position++;
  164. }
  165. } else {
  166. model->position = 0;
  167. model->window_position = 0;
  168. }
  169. },
  170. true);
  171. }
  172. BtTestParam* bt_test_get_selected_param(BtTestModel* model) {
  173. BtTestParam* param = NULL;
  174. BtTestParamArray_it_t it;
  175. uint8_t position = 0;
  176. for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
  177. BtTestParamArray_next(it)) {
  178. if(position == model->position) {
  179. break;
  180. }
  181. position++;
  182. }
  183. param = BtTestParamArray_ref(it);
  184. furi_assert(param);
  185. return param;
  186. }
  187. void bt_test_process_left(BtTest* bt_test) {
  188. BtTestParam* param;
  189. with_view_model(
  190. bt_test->view,
  191. BtTestModel * model,
  192. {
  193. param = bt_test_get_selected_param(model);
  194. if(param->current_value_index > 0) {
  195. param->current_value_index--;
  196. if(param->change_callback) {
  197. model->state = BtTestStateStopped;
  198. model->message = BT_TEST_START_MESSAGE;
  199. model->rssi = 0.0f;
  200. model->packets_num_rx = 0;
  201. model->packets_num_tx = 0;
  202. }
  203. }
  204. },
  205. true);
  206. if(param->change_callback) {
  207. param->change_callback(param);
  208. }
  209. }
  210. void bt_test_process_right(BtTest* bt_test) {
  211. BtTestParam* param;
  212. with_view_model(
  213. bt_test->view,
  214. BtTestModel * model,
  215. {
  216. param = bt_test_get_selected_param(model);
  217. if(param->current_value_index < (param->values_count - 1)) {
  218. param->current_value_index++;
  219. if(param->change_callback) {
  220. model->state = BtTestStateStopped;
  221. model->message = BT_TEST_START_MESSAGE;
  222. model->rssi = 0.0f;
  223. model->packets_num_rx = 0;
  224. model->packets_num_tx = 0;
  225. }
  226. }
  227. },
  228. true);
  229. if(param->change_callback) {
  230. param->change_callback(param);
  231. }
  232. }
  233. void bt_test_process_ok(BtTest* bt_test) {
  234. BtTestState state;
  235. with_view_model(
  236. bt_test->view,
  237. BtTestModel * model,
  238. {
  239. if(model->state == BtTestStateStarted) {
  240. model->state = BtTestStateStopped;
  241. model->message = BT_TEST_START_MESSAGE;
  242. model->rssi = 0.0f;
  243. model->packets_num_rx = 0;
  244. model->packets_num_tx = 0;
  245. } else if(model->state == BtTestStateStopped) {
  246. model->state = BtTestStateStarted;
  247. model->message = BT_TEST_STOP_MESSAGE;
  248. }
  249. state = model->state;
  250. },
  251. true);
  252. if(bt_test->change_state_callback) {
  253. bt_test->change_state_callback(state, bt_test->context);
  254. }
  255. }
  256. void bt_test_process_back(BtTest* bt_test) {
  257. with_view_model(
  258. bt_test->view,
  259. BtTestModel * model,
  260. {
  261. model->state = BtTestStateStopped;
  262. model->rssi = 0.0f;
  263. model->packets_num_rx = 0;
  264. model->packets_num_tx = 0;
  265. },
  266. false);
  267. if(bt_test->back_callback) {
  268. bt_test->back_callback(bt_test->context);
  269. }
  270. }
  271. BtTest* bt_test_alloc() {
  272. BtTest* bt_test = malloc(sizeof(BtTest));
  273. bt_test->view = view_alloc();
  274. view_set_context(bt_test->view, bt_test);
  275. view_allocate_model(bt_test->view, ViewModelTypeLocking, sizeof(BtTestModel));
  276. view_set_draw_callback(bt_test->view, bt_test_draw_callback);
  277. view_set_input_callback(bt_test->view, bt_test_input_callback);
  278. with_view_model(
  279. bt_test->view,
  280. BtTestModel * model,
  281. {
  282. model->state = BtTestStateStopped;
  283. model->message = "Ok - Start";
  284. BtTestParamArray_init(model->params);
  285. model->position = 0;
  286. model->window_position = 0;
  287. model->rssi = 0.0f;
  288. model->packets_num_tx = 0;
  289. model->packets_num_rx = 0;
  290. },
  291. true);
  292. return bt_test;
  293. }
  294. void bt_test_free(BtTest* bt_test) {
  295. furi_assert(bt_test);
  296. with_view_model(
  297. bt_test->view,
  298. BtTestModel * model,
  299. {
  300. BtTestParamArray_it_t it;
  301. for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
  302. BtTestParamArray_next(it)) {
  303. furi_string_free(BtTestParamArray_ref(it)->current_value_text);
  304. }
  305. BtTestParamArray_clear(model->params);
  306. },
  307. false);
  308. view_free(bt_test->view);
  309. free(bt_test);
  310. }
  311. View* bt_test_get_view(BtTest* bt_test) {
  312. furi_assert(bt_test);
  313. return bt_test->view;
  314. }
  315. BtTestParam* bt_test_param_add(
  316. BtTest* bt_test,
  317. const char* label,
  318. uint8_t values_count,
  319. BtTestParamChangeCallback change_callback,
  320. void* context) {
  321. BtTestParam* param = NULL;
  322. furi_assert(label);
  323. furi_assert(bt_test);
  324. with_view_model(
  325. bt_test->view,
  326. BtTestModel * model,
  327. {
  328. param = BtTestParamArray_push_new(model->params);
  329. param->label = label;
  330. param->values_count = values_count;
  331. param->change_callback = change_callback;
  332. param->context = context;
  333. param->current_value_index = 0;
  334. param->current_value_text = furi_string_alloc();
  335. },
  336. true);
  337. return param;
  338. }
  339. void bt_test_set_rssi(BtTest* bt_test, float rssi) {
  340. furi_assert(bt_test);
  341. with_view_model(
  342. bt_test->view, BtTestModel * model, { model->rssi = rssi; }, true);
  343. }
  344. void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) {
  345. furi_assert(bt_test);
  346. with_view_model(
  347. bt_test->view, BtTestModel * model, { model->packets_num_tx = packets_num; }, true);
  348. }
  349. void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) {
  350. furi_assert(bt_test);
  351. with_view_model(
  352. bt_test->view, BtTestModel * model, { model->packets_num_rx = packets_num; }, true);
  353. }
  354. void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) {
  355. furi_assert(bt_test);
  356. furi_assert(callback);
  357. bt_test->change_state_callback = callback;
  358. }
  359. void bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback) {
  360. furi_assert(bt_test);
  361. furi_assert(callback);
  362. bt_test->back_callback = callback;
  363. }
  364. void bt_test_set_context(BtTest* bt_test, void* context) {
  365. furi_assert(bt_test);
  366. bt_test->context = context;
  367. }
  368. void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index) {
  369. param->current_value_index = current_value_index;
  370. }
  371. void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) {
  372. furi_string_set(param->current_value_text, current_value_text);
  373. }
  374. uint8_t bt_test_get_current_value_index(BtTestParam* param) {
  375. return param->current_value_index;
  376. }
  377. void* bt_test_get_context(BtTestParam* param) {
  378. return param->context;
  379. }