runtime.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. #include <stdio.h>
  2. #include "duktape/duktape.h"
  3. #include "runtime.h"
  4. #include "oboo.h"
  5. // function definitions:
  6. void duk_print_stack_size(duk_context* ctx);
  7. static duk_ret_t native_print(duk_context *ctx);
  8. static duk_ret_t native_createCard(duk_context *ctx);
  9. static duk_ret_t native_createElement(duk_context *ctx);
  10. static duk_ret_t native_isCardValid(duk_context *ctx);
  11. static duk_ret_t native_updateElement(duk_context *ctx);
  12. static duk_ret_t native_createCalendar(duk_context *ctx);
  13. static duk_ret_t native_updateCalendar(duk_context *ctx);
  14. static duk_ret_t native_selectCard(duk_context *ctx);
  15. static duk_ret_t native_setStatus(duk_context *ctx);
  16. static duk_ret_t native_deleteCardByUid(duk_context *ctx);
  17. static duk_ret_t native_deleteCard(duk_context *ctx);
  18. static duk_ret_t native_setNotification(duk_context *ctx);
  19. static duk_ret_t native_clearNotification(duk_context *ctx);
  20. static duk_ret_t native_setNotificationDuration(duk_context *ctx);
  21. /* helper functions */
  22. void publishCardSelected (int cardId) {
  23. int status;
  24. char buf[128];
  25. // publish to /cardResponse topic
  26. sprintf(buf, "{\"cardId\":%d, \"action\":\"select\", \"success\":true}", cardId);
  27. status = sendMessage(MQ_CARD_RESPONSE_TOPIC, buf);
  28. }
  29. /* duktape/C functions */
  30. static duk_ret_t native_print(duk_context *ctx) {
  31. printf("%s\n", duk_to_string(ctx, 0));
  32. return 0; /* no return value (= undefined) */
  33. }
  34. duk_ret_t native_publish(duk_context *ctx) {
  35. printf("publishing to topic '%s': '%s'\n", duk_to_string(ctx, 0), duk_json_encode(ctx, 1));
  36. int status = sendMessage(duk_to_string(ctx, 0), duk_json_encode(ctx, 1));
  37. duk_push_int(ctx, status);
  38. return 1; /* 1 = return value at top */
  39. }
  40. static duk_ret_t native_createCard(duk_context *ctx) {
  41. int idx;
  42. // retreive function arguments
  43. // 0 background color
  44. // 1 card unique id
  45. int bgColor = (int)duk_to_int(ctx, 0);
  46. printf("> C: Adding card with bgColor 0x%03x\n", bgColor);
  47. // add card to card manager
  48. idx = lv_add_card(bgColor);
  49. if (duk_is_string(ctx, 1)) {
  50. printf("> C: setting unique id\n");
  51. lv_set_card_uid(idx, duk_to_string(ctx, 1));
  52. }
  53. printf("> C: added card with index %d\n", idx);
  54. // return the index of the card
  55. duk_push_int(ctx, idx);
  56. return 1; /* 1 = return value at top */
  57. }
  58. static duk_ret_t native_deleteCardByUid(duk_context *ctx) {
  59. int idx;
  60. // function arguments
  61. // 0 card unique id
  62. // get the card id based on the free ptr uid
  63. idx = lv_oboo_get_card_id_by_fp(duk_to_string(ctx, 0));
  64. //printf("DBG: id by fp is %d\n", idx);
  65. if (idx >= 0) {
  66. printf("> C: Deleting card with index %d\n", idx);
  67. lv_delete_card(idx);
  68. }
  69. // make sure there is a selected card
  70. printf("> C: selecting a remaining card\n");
  71. // TODO: update lv_ob_select_card function to still operate correctly if there are no cards remaining - shouldn't call publishSelectedCard in that case
  72. idx = lv_ob_select_card("left");
  73. //printf("idx: %d\n", idx);
  74. if(idx != -1){
  75. publishCardSelected(idx);
  76. }
  77. return 0; /* no return value (= undefined) */
  78. }
  79. static duk_ret_t native_deleteCard(duk_context *ctx) {
  80. int idx;
  81. // function arguments
  82. // 0 card index
  83. idx = (int)duk_to_int(ctx, 0);
  84. if (idx >= 0) {
  85. printf("> C: Deleting card with index %d\n", idx);
  86. lv_delete_card(idx);
  87. }
  88. // make sure there is a selected card
  89. idx = lv_ob_select_card("left");
  90. publishCardSelected(idx);
  91. return 0; /* no return value (= undefined) */
  92. }
  93. static duk_ret_t native_updateCalendar(duk_context *ctx) {
  94. // printf("native update calendar\n");
  95. int cardId = (int)duk_to_int(ctx, 0);
  96. int elementId = (int)duk_to_int(ctx, 1);
  97. int year = duk_to_int(ctx, 2);
  98. int month = duk_to_int(ctx, 3);
  99. int day = duk_to_int(ctx, 4);
  100. // printf("> C: update calendar %d: with date y/m/d %d/%d/%d elementId %d\n", cardId, year, month, day, elementId );
  101. lv_obj_t *element = lv_obj_get_card_element(cardId, elementId);
  102. struct oboo_card_element_data *elementData = NULL;
  103. if(NULL != element){
  104. elementData = lv_obj_get_free_ptr(element);
  105. //printf("calendar exist! element Data type is: %d\n", elementData->type);
  106. }else{
  107. printf("there is no such calendar with id: %d\n", cardId);
  108. }
  109. if (elementData != NULL && elementData->type == OB_CALENDAR_ELEMENT) {
  110. //TODO: check is date valid
  111. oboo_calendar_set_day(element, year, month, day);
  112. //printf("calendar updated\n");
  113. }else{
  114. //printf("element data is some crap\n");
  115. }
  116. }
  117. static duk_ret_t native_createCalendar(duk_context *ctx) {
  118. //printf("native create calendar\n");
  119. /*
  120. cardId, \
  121. msg.elements[i].type, \
  122. msg.elements[i].position.x || 0, \
  123. msg.elements[i].position.y || 0,\
  124. msg.elements[i].size || 0,\
  125. msg.elements[i].date.year || 1970,\
  126. msg.elements[i].date.month || 1,\
  127. msg.elements[i].date.day || 1,\
  128. msg.elements[i].position.align || 'left',\
  129. msg.elements[i].id\
  130. */
  131. // retreive function arguments
  132. int cardId = (int)duk_to_int(ctx, 0);
  133. // char type[64] = duk_to_string(ctx, 1); //string
  134. int posX = (int)duk_to_int(ctx, 2);
  135. int posY = (int)duk_to_int(ctx, 3);
  136. int size = (int)duk_to_int(ctx, 4);
  137. int year = duk_to_int(ctx, 5);
  138. int month = duk_to_int(ctx, 6);
  139. int day = duk_to_int(ctx, 7);
  140. // char align[64] = duk_to_string(ctx, 8);
  141. int elementId = (int)duk_to_int(ctx, 9);
  142. // printf("> C: Adding calendar %d: type '%s' at (%d, %d) of size %d with date y/m/d %d/%d/%d elementId %d\n", cardId, duk_to_string(ctx, 1), posX, posY, size, year, month, day, elementId );
  143. //// directly using lv functions - need to implement calendar element handling
  144. // get the card
  145. lv_obj_t *card = ob_cardview_get_card(lv_oboo_obj_get_card_manager(), cardId);
  146. // add the calendar
  147. // lv_obj_t *calendar = oboo_calendar_create(card, 5, 0, 230, 240);
  148. lv_obj_t *calendar = NULL;
  149. if(card != NULL){
  150. //printf("card exists!\n");
  151. calendar = oboo_calendar_create(card, posX, posY, OB_CAL_DEFAULT_WIDTH, OB_CAL_DEFAULT_HEIGHT);
  152. if (calendar != NULL) {
  153. // setup the calendar
  154. oboo_calendar_set_day(calendar, year, month, day);
  155. //try to add elem so it can be found for update
  156. struct oboo_card_element_data *elementData = malloc(sizeof (struct oboo_card_element_data));
  157. if(elementData != NULL){
  158. elementData->type = OB_CALENDAR_ELEMENT;
  159. elementData->alignment = LV_ALIGN_CENTER; //TODO: blind setup (without device)
  160. elementData->posX = posX;
  161. elementData->posY = posY;
  162. lv_obj_set_free_ptr(calendar, (void*)elementData);
  163. printf("calendar data added\n");
  164. }else{
  165. printf("malloc failed!\n");
  166. }
  167. }
  168. }
  169. return 0; /* no return value (= undefined) */
  170. }
  171. static duk_ret_t native_isCardValid(duk_context *ctx) {
  172. int idx;
  173. int bValid = 0;
  174. // function arguments
  175. // 0 card index
  176. idx = (int)duk_to_int(ctx, 0);
  177. if (idx >= 0) {
  178. bValid = lv_ob_check_card_exists(idx);
  179. printf("> C: card with index %d is valid: %d\n", idx, bValid);
  180. }
  181. duk_push_boolean(ctx, bValid); // return bValid
  182. return 1;
  183. }
  184. static duk_ret_t native_createElement(duk_context *ctx) {
  185. // retreive function arguments
  186. int cardId = (int)duk_to_int(ctx, 0);
  187. // char type[64] = duk_to_string(ctx, 1); //string
  188. int posX = (int)duk_to_int(ctx, 2);
  189. int posY = (int)duk_to_int(ctx, 3);
  190. int size = (int)duk_to_int(ctx, 4);
  191. // char align[64] = duk_to_string(ctx, 5);
  192. // char value[64] = duk_to_string(ctx, 6);
  193. int elementId = (int)duk_to_int(ctx, 7);
  194. lv_add_card_element(cardId, elementId, duk_to_string(ctx, 1), posX, posY, size, duk_to_string(ctx, 5), duk_to_string(ctx, 6));
  195. return 0; /* no return value (= undefined) */
  196. }
  197. static duk_ret_t native_updateElement(duk_context *ctx) {
  198. // retreive function arguments
  199. int cardId = (int)duk_to_int(ctx, 0);
  200. int elementId = (int)duk_to_int(ctx, 1);
  201. printf("> C: Updating card %d, element %d\n", cardId, elementId);
  202. lv_update_card_element(cardId, elementId, duk_to_string(ctx, 2));
  203. return 0; /* no return value (= undefined) */
  204. }
  205. static duk_ret_t native_selectCard(duk_context *ctx) {
  206. int status;
  207. int cardId = lv_ob_select_card(duk_to_string(ctx, 0));
  208. printf("select card: %d", cardId);
  209. // publish to /cardResponse topic
  210. if(cardId!=-1)
  211. publishCardSelected(cardId);
  212. return 0; /* no return value (= undefined) */
  213. }
  214. static duk_ret_t native_setStatus(duk_context *ctx) {
  215. lv_ob_set_status(duk_to_string(ctx, 0), (int)duk_to_int(ctx, 1) );
  216. return 0; /* no return value (= undefined) */
  217. }
  218. // arguments:
  219. // 0 - notification message - string
  220. // 1 - notification font size - int (optional)
  221. static duk_ret_t native_setNotification(duk_context *ctx) {
  222. int fontSize = OBOO_NOTIFICATION_DEFAULT_FONT_SIZE;
  223. if (duk_is_number(ctx, 1) && duk_to_int(ctx, 1) > 0) {
  224. fontSize = duk_to_int(ctx, 1);
  225. }
  226. printf("> C: Setting notification to '%s' with '%d' font size\n", duk_to_string(ctx, 0), fontSize);
  227. oboo_notification_show(NULL, duk_to_string(ctx, 0), fontSize);
  228. return 0; /* no return value (= undefined) */
  229. }
  230. static duk_ret_t native_clearNotification(duk_context *ctx) {
  231. oboo_notification_clear();
  232. return 0; /* no return value (= undefined) */
  233. }
  234. static duk_ret_t native_setNotificationDuration(duk_context *ctx) {
  235. int duration = duk_to_int(ctx, 0); // notification duration in seconds
  236. // duration of 0 means permanent notification
  237. if (duration > 0) {
  238. // printf("setting notification duration to %d\n", duration);
  239. // TODO: need to implement notification duration
  240. }
  241. return 0; /* no return value (= undefined) */
  242. }
  243. // debug function
  244. void duk_print_stack_size(duk_context* ctx) {
  245. duk_idx_t idx_top;
  246. idx_top = duk_get_top_index(ctx);
  247. if (idx_top == DUK_INVALID_INDEX) {
  248. printf("DBG: duktape stack is empty\n");
  249. } else {
  250. printf("DBG: duktape stack: index of top element: %ld, type is ", (long) idx_top);
  251. switch (duk_get_type(ctx, idx_top)) {
  252. case DUK_TYPE_NULL:
  253. printf("null\n");
  254. break;
  255. case DUK_TYPE_BOOLEAN:
  256. printf("boolean\n");
  257. break;
  258. case DUK_TYPE_NUMBER:
  259. printf("number\n");
  260. break;
  261. case DUK_TYPE_STRING:
  262. printf("string\n");
  263. break;
  264. case DUK_TYPE_OBJECT:
  265. printf("object\n");
  266. break;
  267. case DUK_TYPE_BUFFER:
  268. printf("buffer\n");
  269. break;
  270. case DUK_TYPE_POINTER:
  271. printf("pointer\n");
  272. break;
  273. case DUK_TYPE_LIGHTFUNC:
  274. printf("lightfunc\n");
  275. break;
  276. default:
  277. printf("OTHER\n");
  278. break;
  279. }
  280. }
  281. }
  282. // api functions
  283. /*duktape init*/
  284. void initRuntime() {
  285. ctx = duk_create_heap_default();
  286. if (!ctx) {
  287. printf("ERROR: unable to create duk_context\n");
  288. exit(1);
  289. }
  290. /* duktape - register duktape/C functions */
  291. duk_push_c_function(ctx, native_print, 1 /*nargs*/);
  292. duk_put_global_string(ctx, "print");
  293. duk_push_c_function(ctx, native_createCard, 2 /*nargs*/);
  294. duk_put_global_string(ctx, "createCard");
  295. duk_push_c_function(ctx, native_createElement, 8 /*nargs*/);
  296. duk_put_global_string(ctx, "createElement");
  297. duk_push_c_function(ctx, native_createCalendar, 10 /*nargs*/);
  298. duk_put_global_string(ctx, "createCalendar");
  299. duk_push_c_function(ctx, native_updateElement, 3 /*nargs*/);
  300. duk_put_global_string(ctx, "updateElement");
  301. duk_push_c_function(ctx, native_updateCalendar, 5 /*nargs*/);
  302. duk_put_global_string(ctx, "updateCalendar");
  303. duk_push_c_function(ctx, native_selectCard, 1 /*nargs*/);
  304. duk_put_global_string(ctx, "selectCard");
  305. duk_push_c_function(ctx, native_setStatus, 2 /*nargs*/);
  306. duk_put_global_string(ctx, "setStatus");
  307. duk_push_c_function(ctx, native_publish, 2 /*nargs*/);
  308. duk_put_global_string(ctx, "publish");
  309. duk_push_c_function(ctx, native_deleteCardByUid, 1 /*nargs*/);
  310. duk_put_global_string(ctx, "deleteCardByUid");
  311. duk_push_c_function(ctx, native_deleteCard, 1 /*nargs*/);
  312. duk_put_global_string(ctx, "deleteCard");
  313. duk_push_c_function(ctx, native_setNotification, 2 /*nargs*/);
  314. duk_put_global_string(ctx, "setNotification");
  315. duk_push_c_function(ctx, native_clearNotification, 0 /*nargs*/);
  316. duk_put_global_string(ctx, "clearNotification");
  317. duk_push_c_function(ctx, native_setNotificationDuration, 1 /*nargs*/);
  318. duk_put_global_string(ctx, "setNotificationDuration");
  319. duk_push_c_function(ctx, native_isCardValid, 1 /*nargs*/);
  320. duk_put_global_string(ctx, "isCardValid");
  321. /*duktape - register js functions*/
  322. // function to parse new_card commands
  323. duk_eval_string(ctx, "function newCard(msg) { \
  324. print ('new_card command received');\
  325. if (msg.bg_color != null && msg.elements && Array.isArray(msg.elements) && msg.responseTopic) {\
  326. print('lets make a new card');\
  327. var cardId = createCard(msg.bg_color, msg.responseTopic);\
  328. print('elements array has length of ' + msg.elements.length);\
  329. for (var i = 0; i < msg.elements.length; i++) {\
  330. if(msg.elements[i].type !== null && msg.elements[i].type === 'calendar'){\
  331. createCalendar( cardId, \
  332. msg.elements[i].type, \
  333. msg.elements[i].position.x || 0, \
  334. msg.elements[i].position.y || 0,\
  335. msg.elements[i].size || 0,\
  336. msg.elements[i].date.year || 1970,\
  337. msg.elements[i].date.month || 1,\
  338. msg.elements[i].date.day || 1,\
  339. msg.elements[i].position.align || 'left',\
  340. msg.elements[i].id\
  341. );\
  342. }else if(msg.elements[i].id !== null && msg.elements[i].type !== null && msg.elements[i].value !== null && msg.elements[i].type !== null && msg.elements[i].type !== 'calendar'){\
  343. createElement( cardId, \
  344. msg.elements[i].type, \
  345. msg.elements[i].position.x || 0, \
  346. msg.elements[i].position.y || 0,\
  347. msg.elements[i].size || 0,\
  348. msg.elements[i].position.align || 'left',\
  349. msg.elements[i].value,\
  350. msg.elements[i].id\
  351. );\
  352. }\
  353. }\
  354. publish('/cardResponse', {\
  355. cardId: cardId,\
  356. attention: msg.responseTopic,\
  357. action: 'create',\
  358. success: true\
  359. });\
  360. selectCard('last');\
  361. }\
  362. }");
  363. duk_push_global_object(ctx); // make this function available globally
  364. duk_get_prop_string(ctx, -1, "newCard" ); // pushes function from loaded script to stack
  365. // function to parse update_card commands
  366. duk_eval_string(ctx, "function updateCard(msg) { \
  367. print ('update_card command received');\
  368. if (msg.id !== null && msg.elements && Array.isArray(msg.elements)) {\
  369. if (isCardValid(msg.id)) {\
  370. print('card ' + msg.id + ': elements array has length of ' + msg.elements.length);\
  371. for (var i = 0; i < msg.elements.length; i++) {\
  372. print('element ' + i + ' being updated to ' + msg.elements[i].value);\
  373. if (msg.elements[i].id !== null && msg.elements[i].value !== null && msg.elements[i].type !== 'calendar') {\
  374. updateElement( msg.id,\
  375. msg.elements[i].id,\
  376. msg.elements[i].value\
  377. );\
  378. }else{\
  379. print('calendar update');\
  380. updateCalendar( msg.id,\
  381. msg.elements[i].id,\
  382. msg.elements[i].date.year || 1970,\
  383. msg.elements[i].date.month || 1,\
  384. msg.elements[i].date.day || 1\
  385. );\
  386. }\
  387. }\
  388. } else { \
  389. print('Invalid card with id ', msg.id); \
  390. publish('/cardResponse', {\
  391. cardId: msg.id,\
  392. action: 'update',\
  393. success: false,\
  394. reason: 'invalid cardId'\
  395. });\
  396. }\
  397. }else{\
  398. print('there is no Id of object in json');\
  399. }\
  400. }");
  401. duk_push_global_object(ctx); // make this function available globally
  402. duk_get_prop_string(ctx, -1, "updateCard" ); // pushes function from loaded script to stack
  403. // function to parse remove_card commands
  404. duk_eval_string(ctx, "function removeCard(msg) { \
  405. print ('remove_card command received');\
  406. if ('id' in msg) {\
  407. print ('deleting card with id ' + msg.id);\
  408. deleteCard(msg.id);\
  409. }\
  410. else if ('cardName' in msg) {\
  411. print ('deleting card with string uid ' + msg.cardName);\
  412. deleteCardByUid(msg.cardName);\
  413. }\
  414. }");
  415. duk_push_global_object(ctx); // make this function available globally
  416. duk_get_prop_string(ctx, -1, "removeCard" ); // pushes function from loaded script to stack
  417. // function to parse all incoming messages
  418. duk_eval_string(ctx, "function parseMsg(topic, msg) {\
  419. print ('parseMsg running');\
  420. switch (topic) {\
  421. case '/card':\
  422. switch (msg.cmd) {\
  423. case 'new_card':\
  424. newCard(msg);\
  425. break;\
  426. case 'update_card':\
  427. updateCard(msg);\
  428. break;\
  429. case 'remove_card':\
  430. removeCard(msg);\
  431. break;\
  432. case 'select_card':\
  433. print ('select_card command received');\
  434. if (msg.action != null) {\
  435. selectCard(msg.action);\
  436. }\
  437. break;\
  438. default:\
  439. print ('no cmd specified!');\
  440. break;\
  441. }\
  442. break;\
  443. case '/status':\
  444. switch (msg.cmd) {\
  445. case 'update':\
  446. print ('update command received');\
  447. if (msg.elements && Array.isArray(msg.elements)) {\
  448. for (var i = 0; i < msg.elements.length; i++) {\
  449. if (msg.elements[i].type != null && msg.elements[i].value != null) {\
  450. print('element ' + i + ': ' + msg.elements[i].type + ' ' + msg.elements[i].value);\
  451. if (typeof msg.elements[i].value === 'number') {\
  452. print('value is a number!');\
  453. setStatus(msg.elements[i].type, msg.elements[i].value);\
  454. }\
  455. }\
  456. }\
  457. }\
  458. break;\
  459. default:\
  460. print ('no cmd specified!');\
  461. break;\
  462. }\
  463. break;\
  464. case '/notification':\
  465. print('Notification command received: ' + msg.cmd);\
  466. switch (msg.cmd) {\
  467. case 'set':\
  468. print(' set notification to ' + msg.text + ' w/ size ' + msg.size);\
  469. setNotification(msg.text, msg.size);\
  470. if ('duration' in msg) {\
  471. setNotificationDuration(msg.duration);\
  472. }\
  473. break;\
  474. case 'clear':\
  475. clearNotification();\
  476. break;\
  477. default:\
  478. print ('notification: no cmd specified!');\
  479. break;\
  480. }\
  481. break;\
  482. default:\
  483. print ('topic not recognized specified!');\
  484. break;\
  485. }\
  486. }");
  487. duk_push_global_object(ctx); // make this function available globally
  488. duk_get_prop_string(ctx, -1, "parseMsg" ); // pushes function from loaded script to stack
  489. }
  490. void destroyRuntime() {
  491. duk_destroy_heap(ctx);
  492. }
  493. void handleMessage(char* topic, char* payload) {
  494. duk_dup_top(ctx); // duplicate the parseMsg function so that the original doesn't get consumed
  495. // push the topic name
  496. duk_push_string(ctx, topic);
  497. // push the json string
  498. printf("pushing string '%s' to ctx\n", payload);
  499. duk_push_string(ctx, payload);
  500. duk_json_decode(ctx, -1);
  501. // todo: catch error for bad json
  502. duk_pcall(ctx, 2);
  503. duk_pop(ctx);
  504. }