client_utils.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. /*
  2. * Copyright (c) 2019 Clementine Computing LLC.
  3. *
  4. * This file is part of PopuFare.
  5. *
  6. * PopuFare is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * PopuFare is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <errno.h>
  24. #include <string.h>
  25. #include <time.h>
  26. #include <stdarg.h>
  27. #include "../common/common_defs.h"
  28. #include "commhub.h"
  29. #include "client_utils.h"
  30. gps_status gps_stat = {0};
  31. stop_status stop_stat = {0};
  32. driver_status driver_stat = {0};
  33. pass_status pass_stat = {0};
  34. bill_status bill_stat = {0};
  35. state_info_t state_info = {0};
  36. //-----------------------------------------------------------------
  37. float effective_lat() {
  38. if (gps_stat.gps_good) {
  39. return (float)gps_stat.lat;
  40. }
  41. else {
  42. return (float)stop_stat.lat;
  43. }
  44. }
  45. float effective_lon() {
  46. if (gps_stat.gps_good) {
  47. return (float)gps_stat.lon;
  48. }
  49. else {
  50. return (float)stop_stat.lon;
  51. }
  52. }
  53. //-----------------------------------------------------------------
  54. typedef struct message_callback_struct {
  55. char mailbox[MAILBOX_NAME_MAX + 1];
  56. int ident;
  57. message_notify_func callback;
  58. void *param;
  59. struct message_callback_struct *next;
  60. } message_callback;
  61. static message_callback *dispatch_list_head = NULL;
  62. //This function registers a callback to happen when a certain message is received.
  63. //It takes four parameters:
  64. // mailbox = string containing name of mailbox to trigger this callback
  65. // ident = a numeric identifier specifying both a unique identifier for this callback, and processing order
  66. // func = a message handling callback function
  67. // param = an untyped pointer used to pass any outside contect to the callback
  68. //
  69. int register_dispatch_callback(char *mailbox, int ident, message_notify_func func, void *param) {
  70. message_callback *p, *q, *n;
  71. if (!func || !mailbox) {
  72. return -1;
  73. }
  74. q = NULL;
  75. p = dispatch_list_head;
  76. // Scan our existing list
  77. //
  78. while (p) {
  79. // stop when we find a node with an identifier greater than ident or run off the end
  80. //
  81. if (p->ident > ident) {
  82. break;
  83. }
  84. q = p;
  85. p = p->next;
  86. }
  87. n = (message_callback *)malloc(sizeof(message_callback));
  88. if (n == NULL) {
  89. return -1;
  90. }
  91. // clear our new node
  92. //
  93. memset(n, 0, sizeof(message_callback));
  94. // copy our match string
  95. //
  96. strncpy(n->mailbox, mailbox, MAILBOX_NAME_MAX);
  97. // null terminate
  98. //
  99. n->mailbox[MAILBOX_NAME_MAX] = '\0';
  100. // record our identifier
  101. //
  102. n->ident = ident;
  103. // callback
  104. //
  105. n->callback = func;
  106. // and parameter
  107. //
  108. n->param = param;
  109. // register our next pointer as per above scan's determined insert point
  110. //
  111. n->next = p;
  112. // If we're inserting at the head of the lsit
  113. //
  114. if (q == NULL) {
  115. // set the list head to n
  116. //
  117. dispatch_list_head = n;
  118. }
  119. else {
  120. // tweak the next pointer of the previous node to perform the insertion
  121. //
  122. q->next = n;
  123. }
  124. return 0;
  125. }
  126. // This function unregisters a message callback function by its mailbox name and identifier
  127. // this allows for easy selective removal of a specific callback while still keeping the rest of the chain intact
  128. //
  129. int unregister_dispatch_callback(char *mailbox, int ident) {
  130. message_callback *p, *q;
  131. // if we have a bogus mailbox, give up
  132. //
  133. if (!mailbox) {
  134. return -1;
  135. }
  136. q = NULL;
  137. p = dispatch_list_head;
  138. // Scan our callback list looking for the identifier in question
  139. //
  140. while (p) {
  141. // if we find a mailbox with both a matching identifier and mailbox name, that's the one...
  142. //
  143. if ( (p->ident == ident) && !strncmp(mailbox, p->mailbox, MAILBOX_NAME_MAX) ) {
  144. break;
  145. }
  146. q = p;
  147. p = p->next;
  148. }
  149. // if we didn't find it, that is reported by the < 0 return value
  150. //
  151. if (p == NULL) {
  152. return -1;
  153. }
  154. // If we are removing at the head...
  155. //
  156. if (q == NULL) {
  157. // advance the head pointer down the list
  158. //
  159. dispatch_list_head = p->next;
  160. }
  161. else {
  162. // snip the node in question out mid-list
  163. //
  164. q->next = p->next;
  165. }
  166. // free the node in question
  167. //
  168. free(p);
  169. return 0;
  170. }
  171. // This function unregisters ALL callbacks in preparation for a clean exit
  172. //
  173. int unregister_all_callbacks() {
  174. message_callback *p, *q;
  175. q = NULL;
  176. p = dispatch_list_head;
  177. // traverse the list freeing all nodes
  178. //
  179. while (p) {
  180. q = p;
  181. p = p->next;
  182. free(q);
  183. }
  184. // reset our list pointers to empty state
  185. //
  186. dispatch_list_head = NULL;
  187. return 0;
  188. }
  189. // This function is used to translate any special "Command" messages (things addressed
  190. // by module or PID rather than mailbox, where the payload is a mailbox name for special
  191. // notifications (primarily MAILBOX_PING and MAILBOX_EXIT).
  192. //
  193. static int process_addressed_message(struct message_record *msg) {
  194. struct message_record translated_msg;
  195. // If this message is special...
  196. //
  197. if ( (!strncmp((char *)msg->payload, MAILBOX_PING, MAILBOX_NAME_MAX)) ||
  198. (!strncmp((char *)msg->payload, MAILBOX_HUP, MAILBOX_NAME_MAX)) ||
  199. (!strncmp((char *)msg->payload, MAILBOX_EXIT, MAILBOX_NAME_MAX)) ) {
  200. // Copy the message as a whole
  201. //
  202. memcpy(&translated_msg, msg, sizeof(translated_msg));
  203. // Then change its mailbox name to the special mailbox specified in the payload
  204. //
  205. strncpy(translated_msg.header.mailbox_name, (char *)msg->payload, MAILBOX_NAME_MAX);
  206. translated_msg.header.mailbox_name[MAILBOX_NAME_MAX] = '\0'; //this is not a bug, since mailbox_name is defined as MAILBOX_NAME_MAX + 1 bytes long
  207. // Go and feed this through the callback chain as if it originally came on on the actual mailbox...
  208. //
  209. process_message(&translated_msg);
  210. return 0;
  211. }
  212. return 0;
  213. }
  214. //---
  215. // Update _state_info with _gps_stat
  216. //
  217. int update_state_info_with_gps(state_info_t *_state_info, gps_status *_gps_stat) {
  218. _state_info->lat = _gps_stat->lat;
  219. _state_info->lon = _gps_stat->lon;
  220. _state_info->heading = _gps_stat->heading;
  221. _state_info->velocity = _gps_stat->velocity;
  222. _state_info->num_sats = _gps_stat->num_sats;
  223. _state_info->gps_good = _gps_stat->gps_good;
  224. _state_info->stamp = _gps_stat->stamp;
  225. _state_info->gpstime = _gps_stat->gpstime;
  226. return 0;
  227. }
  228. // Update _state_info with _stop_stat
  229. //
  230. int update_state_info_with_stop(state_info_t *_state_info, stop_status *_stop_stat) {
  231. _state_info->paddle = _stop_stat->paddle;
  232. _state_info->route = _stop_stat->route;
  233. _state_info->trip = _stop_stat->trip;
  234. _state_info->stop = _stop_stat->stop;
  235. _state_info->stop_lat = _stop_stat->lat;
  236. _state_info->stop_lon = _stop_stat->lon;
  237. memcpy(_state_info->stopname, _stop_stat->stopname, ( STATE_INFO_FIELD_SIZE < STOP_NAME_LEN ) ? STATE_INFO_FIELD_SIZE : STOP_NAME_LEN );
  238. _state_info->stopname[STATE_INFO_FIELD_SIZE-1] = '\0';
  239. return 0;
  240. }
  241. // Update _state_info with _driver_stat
  242. //
  243. int update_state_info_with_driver(state_info_t *_state_info, driver_status *_driver_stat) {
  244. _state_info->logged_in_driver = _driver_stat->logged_in_driver;
  245. memcpy(_state_info->driver_name, _driver_stat->driver_name, ( STATE_INFO_FIELD_SIZE < DRIVER_NAME_LEN) ? STATE_INFO_FIELD_SIZE : DRIVER_NAME_LEN );
  246. _state_info->driver_name[STATE_INFO_FIELD_SIZE-1] = '\0';
  247. _state_info->equip_num = _driver_stat->equip_num;
  248. return 0;
  249. }
  250. // Load global variable, state_info, with stat information,
  251. // stored in local file.
  252. // Other global variables might need to be initalized based
  253. // on the state_info so this function was created to help
  254. // facilitate that in the future, if needed.
  255. //
  256. int init_state_info(void) {
  257. get_state_info(&state_info);
  258. return 0;
  259. }
  260. //---
  261. // This function actually processes a message through the dispatch list
  262. // using the return values of the callbacks to determine whether to stop or continue
  263. // the return value is the return value of the last handler to have read the message.
  264. // a return value of MESSAGE_UNHANDLED means that no handler successfully handled the message.
  265. message_callback_return process_message(struct message_record *msg) {
  266. message_callback *p = dispatch_list_head;
  267. message_callback_return ret = MESSAGE_UNHANDLED;
  268. // if there is no message
  269. //
  270. if (msg == NULL) {
  271. // we can't handle it
  272. //
  273. return ret;
  274. }
  275. // Walk through this list of handlers (which is maintained sorted by ident)
  276. //
  277. while (p) {
  278. // if the message's mailbox matches the callback's mailbox
  279. //
  280. if (!strncmp(msg->header.mailbox_name, p->mailbox, MAILBOX_NAME_MAX)) {
  281. // go and run the callback
  282. //
  283. ret = p->callback(msg, p->param);
  284. // if this callback requested a STOP, do so
  285. //
  286. if (ret == MESSAGE_HANDLED_STOP) {
  287. break;
  288. }
  289. }
  290. // otherwise, keep going...
  291. //
  292. p = p->next;
  293. }
  294. // If nobody has said STOP yet, see if this is an individually addressed message
  295. //
  296. if ( (msg->header.mailbox_name[0] == ':') || (msg->header.mailbox_name[0] == '>') ) {
  297. process_addressed_message(msg);
  298. }
  299. return ret;
  300. }
  301. //------------------------------------ SOME USEFUL DEFAULT CALLBACKS -------------------------------------------
  302. message_callback_return ignore_message(struct message_record *msg, void *param) {
  303. return MESSAGE_HANDLED_STOP;
  304. }
  305. //------------------------------------ SYSTEM CALLBACKS -------------------------------------------------------
  306. static message_callback_return update_gps_status(struct message_record *msg, void *param) {
  307. // guard against version mismatch
  308. //
  309. if (msg->header.payload_length != sizeof(gps_status)) {
  310. return MESSAGE_HANDLED_STOP;
  311. }
  312. memcpy(&gps_stat, msg->payload, sizeof(gps_status)); //otherwise, update our structure
  313. return MESSAGE_HANDLED_CONT;
  314. }
  315. static message_callback_return update_stop_status(struct message_record *msg, void *param) {
  316. // guard against version mismatch
  317. //
  318. if (msg->header.payload_length != sizeof(stop_status)) {
  319. return MESSAGE_HANDLED_STOP;
  320. }
  321. // otherwise, update our structure
  322. //
  323. memcpy(&stop_stat, msg->payload, sizeof(stop_status));
  324. return MESSAGE_HANDLED_CONT;
  325. }
  326. static message_callback_return update_driver_status(struct message_record *msg, void *param) {
  327. // guard against version mismatch
  328. //
  329. if (msg->header.payload_length != sizeof(driver_status)) {
  330. return MESSAGE_HANDLED_STOP;
  331. }
  332. // otherwise, update our structure
  333. //
  334. memcpy(&driver_stat, msg->payload, sizeof(driver_stat));
  335. return MESSAGE_HANDLED_CONT;
  336. }
  337. static message_callback_return update_bill_status(struct message_record *msg, void *param) {
  338. // guard against version mismatch
  339. //
  340. if (msg->header.payload_length != sizeof(bill_status)) {
  341. return MESSAGE_HANDLED_STOP;
  342. }
  343. // otherwise, update our structure
  344. //
  345. memcpy(&bill_stat, msg->payload, sizeof(bill_stat));
  346. return MESSAGE_HANDLED_CONT;
  347. }
  348. static message_callback_return update_pass_status(struct message_record *msg, void *param) {
  349. // guard against version mismatch
  350. //
  351. if (msg->header.payload_length != sizeof(pass_status)) {
  352. return MESSAGE_HANDLED_STOP;
  353. }
  354. memcpy(&pass_stat, msg->payload, sizeof(pass_stat)); //otherwise, update our structure
  355. return MESSAGE_HANDLED_CONT;
  356. }
  357. static message_callback_return polite_exit_request_message(struct message_record *msg, void *param) {
  358. request_polite_exit(EXIT_REQUEST_POLITE, "%s", (char *)msg->payload);
  359. return MESSAGE_HANDLED_CONT;
  360. }
  361. static message_callback_return hup_request_message(struct message_record *msg, void *param) {
  362. request_hup("HUP(%s)", (char *)msg->payload);
  363. return MESSAGE_HANDLED_CONT;
  364. }
  365. static message_callback_return ping_request_message(struct message_record *msg, void *param) {
  366. struct message_record outgoing;
  367. prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
  368. send_message(msg->header.from_fd, &outgoing);
  369. return MESSAGE_HANDLED_CONT;
  370. }
  371. // This function unregisters the default system status callbacks
  372. //
  373. int unregister_system_status_callbacks() {
  374. unregister_dispatch_callback(MAILBOX_GPS_STATUS, CALLBACK_SYSTEM);
  375. unregister_dispatch_callback(MAILBOX_STOP_STATUS, CALLBACK_SYSTEM);
  376. unregister_dispatch_callback(MAILBOX_DRIVER_STATUS, CALLBACK_SYSTEM);
  377. unregister_dispatch_callback(MAILBOX_PASS_STATUS, CALLBACK_SYSTEM);
  378. unregister_dispatch_callback(MAILBOX_BILL_STATUS, CALLBACK_SYSTEM);
  379. unregister_dispatch_callback(MAILBOX_EXIT, CALLBACK_SYSTEM);
  380. unregister_dispatch_callback(MAILBOX_HUP, CALLBACK_SYSTEM);
  381. unregister_dispatch_callback(MAILBOX_PING, CALLBACK_SYSTEM);
  382. return 0;
  383. }
  384. // This function registers the default system status callbacks
  385. int register_system_status_callbacks() {
  386. unregister_system_status_callbacks();
  387. register_dispatch_callback(MAILBOX_GPS_STATUS, CALLBACK_SYSTEM, update_gps_status, NULL);
  388. register_dispatch_callback(MAILBOX_STOP_STATUS, CALLBACK_SYSTEM, update_stop_status, NULL);
  389. register_dispatch_callback(MAILBOX_DRIVER_STATUS, CALLBACK_SYSTEM, update_driver_status, NULL);
  390. register_dispatch_callback(MAILBOX_PASS_STATUS, CALLBACK_SYSTEM, update_pass_status, NULL);
  391. register_dispatch_callback(MAILBOX_BILL_STATUS, CALLBACK_SYSTEM, update_bill_status, NULL);
  392. register_dispatch_callback(MAILBOX_EXIT, CALLBACK_SYSTEM, polite_exit_request_message, NULL);
  393. register_dispatch_callback(MAILBOX_HUP, CALLBACK_SYSTEM, hup_request_message, NULL);
  394. register_dispatch_callback(MAILBOX_PING, CALLBACK_SYSTEM, ping_request_message, NULL);
  395. return 0;
  396. }
  397. // This function subscribes to a handful of essential messages
  398. //
  399. int subscribe_to_default_messages(int fd) {
  400. struct message_record outgoing_msg;
  401. if (fd < 0) {
  402. return -1;
  403. }
  404. //------------- things that keep us informed about the other modules
  405. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_GPS_STATUS, strlen(MAILBOX_GPS_STATUS));
  406. send_message(fd, &outgoing_msg);
  407. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_STOP_STATUS, strlen(MAILBOX_STOP_STATUS));
  408. send_message(fd, &outgoing_msg);
  409. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_DRIVER_STATUS, strlen(MAILBOX_DRIVER_STATUS));
  410. send_message(fd, &outgoing_msg);
  411. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_PASS_STATUS, strlen(MAILBOX_PASS_STATUS));
  412. send_message(fd, &outgoing_msg);
  413. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_BILL_STATUS, strlen(MAILBOX_BILL_STATUS));
  414. send_message(fd, &outgoing_msg);
  415. //------------- things that every process should have a crack at
  416. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_STATUS_REQUEST, strlen(MAILBOX_STATUS_REQUEST));
  417. send_message(fd, &outgoing_msg);
  418. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_EXIT, strlen(MAILBOX_EXIT));
  419. send_message(fd, &outgoing_msg);
  420. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_HUP, strlen(MAILBOX_HUP));
  421. send_message(fd, &outgoing_msg);
  422. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_PING, strlen(MAILBOX_PING));
  423. send_message(fd, &outgoing_msg);
  424. return 0;
  425. }
  426. //--------------------------------------------------------------------------------------------------------------
  427. int compress_tabs_to_spaces(char *dest, char *src, int n) {
  428. int i;
  429. for (i=0; i < n; i++) {
  430. switch (src[i]) {
  431. case '\t':
  432. case '\r':
  433. case '\n':
  434. dest[i] = ' ';
  435. break;
  436. default:
  437. dest[i] = src[i];
  438. break;
  439. }
  440. if (src[i] == '\0') {
  441. break;
  442. }
  443. }
  444. return 0;
  445. }
  446. // This function is a printf-line interface to format user display messages:
  447. //
  448. // target = blank IPC message to fill with this log entry
  449. // line = which line of the display to draw on (0 or 1)
  450. // priority = what message priority?
  451. // duration = how many seconds to display this (0 makes it the default message)
  452. // fmt = printf format
  453. // ... = printf args
  454. //
  455. int format_piu_message(struct message_record *target, int line, int priority, int duration, const char *fmt, ...) {
  456. piu_message pmsg = {0};
  457. va_list ap;
  458. pmsg.line = line;
  459. pmsg.priority = priority;
  460. pmsg.seconds = duration;
  461. va_start(ap, fmt);
  462. vsnprintf(pmsg.message, PIU_MESSAGE_LEN, fmt, ap);
  463. va_end(ap);
  464. return prepare_message(target, MAILBOX_PIU_MESSAGE, &pmsg, sizeof(pmsg));
  465. }
  466. // This function takes a buffer and a buffer size and puts a
  467. // log prefix in that contains the equipment number and the local time
  468. //
  469. int make_log_prefix(char *prefix, int max) {
  470. int eqnum;
  471. time_t foo;
  472. struct tm t;
  473. foo = time(NULL);
  474. localtime_r(&foo, &t);
  475. // If the driver interface module has a current EQ num
  476. //
  477. if (driver_stat.equip_num > 0) {
  478. // use it so as not to thrash the file system
  479. //
  480. eqnum = driver_stat.equip_num;
  481. }
  482. else {
  483. // otherwise, get it off disk
  484. //
  485. eqnum = get_equip_num();
  486. }
  487. return snprintf(prefix, max, "EQ# %d %04d-%02d-%02d %02d:%02d:%02d ", eqnum, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
  488. }
  489. // This function is a printf-line interface to format log messages for the server:
  490. //
  491. // target = blank IPC message to fill with this log entry
  492. // loglevel = one of the following: LOGLEVEL_DEBUG, LOGLEVEL_WARN, LOGLEVEL_ERROR
  493. // fmt = printf format
  494. // ... = printf args
  495. //
  496. // NOTE: DO NOT UNDER ANY CIRCUMSTANCES TERMINATE A LOG MESSAGE WITH A '\n' CHARACTER!
  497. //
  498. int format_log_message(struct message_record *target, char loglevel, const char *fmt, ...) {
  499. int len = 0;
  500. char payload[MAX_PAYLOAD_LENGTH] = {0};
  501. va_list ap;
  502. if (!target || !fmt) {
  503. return -1;
  504. }
  505. switch (loglevel) {
  506. case LOGLEVEL_DEBUG:
  507. case LOGLEVEL_WARN:
  508. case LOGLEVEL_ERROR:
  509. payload[0] = loglevel;
  510. len += 1;
  511. break;
  512. default:
  513. payload[0] = LOGLEVEL_ERROR;
  514. len += 1;
  515. break;
  516. }
  517. len += make_log_prefix(payload + len, MAX_PAYLOAD_LENGTH - len);
  518. va_start(ap, fmt);
  519. len += vsnprintf(payload + len, MAX_PAYLOAD_LENGTH - len, fmt, ap);
  520. va_end(ap);
  521. return prepare_message(target, MAILBOX_BILLING_LOG, payload, len);
  522. }
  523. // This function printf-line interface to format messages for debug and trace purposes
  524. //
  525. int format_trace_message(struct message_record *target, const char *fmt, ...) {
  526. int len = 0;
  527. char payload[MAX_PAYLOAD_LENGTH] = {0};
  528. va_list ap;
  529. if (!target || !fmt) {
  530. return -1;
  531. }
  532. va_start(ap, fmt);
  533. len += vsnprintf(payload + len, MAX_PAYLOAD_LENGTH - len, fmt, ap);
  534. va_end(ap);
  535. return prepare_message(target, "DEBUG_TRACE", payload, len);
  536. }
  537. // This function is a printf-line interface to format messages for the driver:
  538. //
  539. // target = blank IPC message to fill with this log entry
  540. // loglevel = one of the following: LOGLEVEL_DEBUG, LOGLEVEL_WARN, LOGLEVEL_ERROR
  541. // fmt = printf format
  542. // ... = printf args
  543. //
  544. // NOTE: DO NOT UNDER ANY CIRCUMSTANCES TERMINATE A LOG MESSAGE WITH A '\n' CHARACTER!
  545. //
  546. int format_driver_message(struct message_record *target, char loglevel, const char *fmt, ...) {
  547. int len = 0;
  548. char payload[MAX_PAYLOAD_LENGTH] = {0};
  549. va_list ap;
  550. if (!target || !fmt) {
  551. return -1;
  552. }
  553. switch(loglevel) {
  554. case LOGLEVEL_DEBUG:
  555. case LOGLEVEL_WARN:
  556. case LOGLEVEL_ERROR:
  557. case LOGLEVEL_ACCEPT:
  558. case LOGLEVEL_REJECT:
  559. case LOGLEVEL_EVENT:
  560. payload[0] = loglevel;
  561. len += 1;
  562. break;
  563. default:
  564. payload[0] = LOGLEVEL_ERROR;
  565. len += 1;
  566. break;
  567. }
  568. va_start(ap, fmt);
  569. len += vsnprintf(payload + len, MAX_PAYLOAD_LENGTH - len, fmt, ap);
  570. va_end(ap);
  571. return prepare_message(target, MAILBOX_DRIVER_NOTIFY, payload, len);
  572. }
  573. // This function formats a billing log entry into a form ready to pass off to the billdb process:
  574. //
  575. // target = blank IPC message to fill with this log entry
  576. // action = string specifying billing action (ACCEPT, REJECT, PASSBACK, etc...)
  577. // rule = string specifying which rule (if any) generated this billing entry
  578. // ruleparam = string specifying the parameter passed to the above rule
  579. // reason = human readable reason for the reject or accept of this rider
  580. // credential = string containing the magnetic or RFID credential used to identify the rider
  581. // logical_card_id = rider ID from pass table
  582. // cash_value = number of cents paid for this fare if it is a cash fare
  583. //
  584. // All other required values are supplied by the system status structures maintained by the default status callbacks:
  585. //
  586. // equipment number, GPS locaction, driver, paddle, route, trip, stop, and stop name
  587. //
  588. int format_billing_message(struct message_record *target, char *action, char *rule, char *ruleparam, char *reason, char *credential, unsigned long long logical_card_id, int cash_value) {
  589. int len = 0;
  590. char foo[MAX_PAYLOAD_LENGTH];
  591. char payload[MAX_PAYLOAD_LENGTH] = {0};
  592. if (!target)
  593. return -1;
  594. if (!action) action = "";
  595. if (!rule) rule = "";
  596. if (!ruleparam) ruleparam = "";
  597. if (!reason) reason = "";
  598. if (!credential) credential = "";
  599. // equipment number (which bus)
  600. //
  601. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", driver_stat.equip_num);
  602. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", driver_stat.logged_in_driver);
  603. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", stop_stat.paddle);
  604. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", stop_stat.route);
  605. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", stop_stat.trip);
  606. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", stop_stat.stop);
  607. // the UTC timestamp of now...
  608. //
  609. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", (int)time(NULL));
  610. // if we have a valid GPS fix, put that into the structure
  611. //
  612. if (gps_stat.gps_good) {
  613. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%f\t", gps_stat.lat);
  614. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%f\t", gps_stat.lon);
  615. }
  616. // otherwise, use the location recorded in the paddle
  617. //
  618. else {
  619. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%f\t", stop_stat.lat);
  620. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%f\t", stop_stat.lon);
  621. }
  622. // billing action code
  623. //
  624. compress_tabs_to_spaces(foo, action, BILLING_ACTION_LEN);
  625. foo[BILLING_ACTION_LEN] = '\0';
  626. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  627. // billing rule code
  628. //
  629. compress_tabs_to_spaces(foo, rule, BILLING_RULE_LEN);
  630. foo[BILLING_RULE_LEN] = '\0';
  631. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  632. // billing rule parameter
  633. //
  634. compress_tabs_to_spaces(foo, ruleparam, BILLING_RULEPARAM_LEN);
  635. foo[BILLING_RULEPARAM_LEN] = '\0';
  636. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  637. // billing reason code
  638. //
  639. compress_tabs_to_spaces(foo, reason, BILLING_REASON_LEN);
  640. foo[BILLING_REASON_LEN] = '\0';
  641. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  642. // billing credential code
  643. //
  644. compress_tabs_to_spaces(foo, credential, BILLING_CREDENTIAL_LEN);
  645. foo[BILLING_CREDENTIAL_LEN] = '\0';
  646. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  647. // rider ID from pass table
  648. //
  649. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%lld\t", logical_card_id);
  650. // cash fare in pennies
  651. //
  652. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%d\t", cash_value);
  653. // billing stop name
  654. //
  655. compress_tabs_to_spaces(foo, stop_stat.stopname, STOP_NAME_LEN);
  656. foo[STOP_NAME_LEN] = '\0';
  657. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%s\t", foo);
  658. // This field is not used by the database, but distinguishes multiple identical cash fares per second
  659. //
  660. // NOTE: This field ends without a newline OR tab, the newline is added by the billdb module before being sent
  661. //
  662. len += snprintf(payload + len, MAX_PAYLOAD_LENGTH - len, "%llu", get_usec_time());
  663. return prepare_message(target, MAILBOX_BILLING_LOG, payload, len);
  664. }
  665. /*
  666. int main(int argc, char **argv)
  667. {
  668. struct message_record msg;
  669. stop_stat.lat = 9999.99999f;
  670. stop_stat.lon = 9999.99999f;
  671. stop_stat.route = 99999;
  672. stop_stat.paddle = 99999;
  673. stop_stat.trip = 99;
  674. stop_stat.stop = 99;
  675. strcpy(stop_stat.stopname, "ABCDEFGIJKABCDEFGIJKABCDEFGIJKABCDEFGIJKABCDEFGIJKABCDEFGIJK123");
  676. driver_stat.logged_in_driver = 12345;
  677. driver_stat.equip_num = 12345;
  678. format_billing_message(&msg, "ACCEPT1ACCEPT2_", "RULE0123456789012345678", "PARA0123456789012345678", "R012345678900123456789001234567890", "C012345678900123456789001234567890", 9999);
  679. printf("%s", msg.payload);
  680. return 0;
  681. }
  682. */