client_utils.c 24 KB

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