You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

836 lines
21 KiB

  1. /*
  2. * Labjack Tools
  3. * Copyright (c) 2003-2007 Jim Paris <jim@jtan.com>
  4. *
  5. * This is free software; you can redistribute it and/or modify it and
  6. * it is provided under the terms of version 2 of the GNU General Public
  7. * License as published by the Free Software Foundation; see COPYING.
  8. */
  9. #include <stdint.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <errno.h>
  14. #include <sys/time.h>
  15. #include <time.h>
  16. #include <sys/stat.h>
  17. #include <signal.h>
  18. #include <unistd.h>
  19. #include "debug.h"
  20. #include "ue9.h"
  21. #include "ue9error.h"
  22. #include "nerdjack.h"
  23. #include "opt.h"
  24. #include "version.h"
  25. #include "compat.h"
  26. #include "ethstream.h"
  27. #include "example.inc"
  28. #define DEFAULT_HOST "192.168.1.209"
  29. #define UE9_COMMAND_PORT 52360
  30. #define UE9_DATA_PORT 52361
  31. #define MAX_CHANNELS 256
  32. struct callbackInfo {
  33. struct ue9Calibration calib;
  34. int convert;
  35. int maxlines;
  36. };
  37. struct options opt[] = {
  38. {'a', "address", "string", "host/address of device (192.168.1.209)"},
  39. {'n', "numchannels", "n", "sample the first N ADC channels (2)"},
  40. {'C', "channels", "a,b,c", "sample channels a, b, and c"},
  41. {'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
  42. {'L', "labjack", NULL, "Force LabJack device"},
  43. {'t', "timers", "a[:A],b[:B]", "set LabJack timer modes a,b and optional values A,B"},
  44. {'T', "timerdivisor", "n", "set LabJack timer divisor to n"},
  45. {'N', "nerdjack", NULL, "Force NerdJack device"},
  46. {'d', "detect", NULL, "Detect NerdJack IP address"},
  47. {'R', "range", "a,b",
  48. "Set range on NerdJack for channels 0-5,6-11 to either 5 or 10 (10,10)"},
  49. {'g', "gain", "a,b,c", "Set Labjack AIN channel gains: 0,1,2,4,8 in -C channel order"},
  50. {'o', "oneshot", NULL, "don't retry in case of errors"},
  51. {'f', "forceretry", NULL, "retry no matter what happens"},
  52. {'c', "convert", NULL, "convert output to volts/temperature"},
  53. {'H', "converthex", NULL, "convert output to hex"},
  54. {'m', "showmem", NULL, "output memory stats with data (NJ only)"},
  55. {'l', "lines", "num", "if set, output this many lines and quit"},
  56. {'h', "help", NULL, "this help"},
  57. {'v', "verbose", NULL, "be verbose"},
  58. {'V', "version", NULL, "show version number and exit"},
  59. {'i', "info", NULL, "get info from device (NJ only)"},
  60. {'X', "examples", NULL, "show ethstream examples and exit"},
  61. {0, NULL, NULL, NULL}
  62. };
  63. int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
  64. int *channel_list, int channel_count,
  65. int *timer_mode_list, int *timer_value_list,
  66. int timer_mode_count, int timer_divisor,
  67. int *gain_list, int gain_count,
  68. int convert, int maxlines);
  69. int nerdDoStream(const char *address, int *channel_list, int channel_count,
  70. int precision, unsigned long period, int convert, int lines,
  71. int showmem);
  72. int data_callback(int channels, int *channel_list, int gain_count, int *gain_list,
  73. uint16_t * data, void *context);
  74. int columns_left = 0;
  75. ////////EXTRA GLOBAL VARS///////////
  76. // for clean shutdown //
  77. // added by John Donnal 2015 //
  78. ////////////////////////////////////
  79. int fd_cmd, fd_data;
  80. int ue9_running = 0; //flag if labjack is currently streaming data
  81. void handle_sig(int sig)
  82. {
  83. while (columns_left--) {
  84. printf(" 0");
  85. }
  86. /******************************************************
  87. * added by John Donnal 2015 *
  88. * Close out connection to LabJack, firmware glitches *
  89. * if the stream is not closed correctly *
  90. ******************************************************/
  91. if(ue9_running==1){
  92. printf("Performing clean shutdown of LabJack\n");
  93. ue9_stream_stop(fd_cmd);
  94. ue9_buffer_flush(fd_cmd);
  95. ue9_close(fd_data);
  96. ue9_close(fd_cmd);
  97. }
  98. /******************************************************/
  99. fflush(stdout);
  100. exit(0);
  101. }
  102. int main(int argc, char *argv[])
  103. {
  104. int optind;
  105. char *optarg, *endp;
  106. char c;
  107. int tmp, i;
  108. FILE *help = stderr;
  109. char *address = strdup(DEFAULT_HOST);
  110. double desired_rate = 8000.0;
  111. int lines = 0;
  112. double actual_rate;
  113. int oneshot = 0;
  114. int forceretry = 0;
  115. int convert = CONVERT_DEC;
  116. int showmem = 0;
  117. int inform = 0;
  118. uint8_t scanconfig;
  119. uint16_t scaninterval;
  120. int timer_mode_list[UE9_TIMERS];
  121. int timer_value_list[UE9_TIMERS];
  122. int timer_mode_count = 0;
  123. int timer_divisor = 1;
  124. int gain_list[MAX_CHANNELS];
  125. int gain_count = 0;
  126. int channel_list[MAX_CHANNELS];
  127. int channel_count = 0;
  128. int nerdjack = 0;
  129. int labjack = 0;
  130. int detect = 0;
  131. int precision = 0;
  132. int addressSpecified = 0;
  133. int donerdjack = 0;
  134. unsigned long period = NERDJACK_CLOCK_RATE / desired_rate;
  135. /* Parse arguments */
  136. opt_init(&optind);
  137. while ((c = opt_parse(argc, argv, &optind, &optarg, opt)) != 0) {
  138. switch (c) {
  139. case 'a':
  140. free(address);
  141. address = strdup(optarg);
  142. addressSpecified = 1;
  143. break;
  144. case 'n':
  145. channel_count = 0;
  146. tmp = strtol(optarg, &endp, 0);
  147. if (*endp || tmp < 1 || tmp > MAX_CHANNELS) {
  148. info("bad number of channels: %s\n", optarg);
  149. goto printhelp;
  150. }
  151. for (i = 0; i < tmp; i++)
  152. channel_list[channel_count++] = i;
  153. break;
  154. case 'C':
  155. channel_count = 0;
  156. do {
  157. tmp = strtol(optarg, &endp, 0);
  158. if (*endp != '\0' && *endp != ',') {
  159. info("bad channel number: %s\n",
  160. optarg);
  161. goto printhelp;
  162. }
  163. //We do not want to overflow channel_list, so we need the check here
  164. //The rest of the sanity checking can come later after we know
  165. //whether this is a
  166. //LabJack or a NerdJack
  167. if (channel_count >= MAX_CHANNELS) {
  168. info("error: too many channels specified\n");
  169. goto printhelp;
  170. }
  171. channel_list[channel_count++] = tmp;
  172. optarg = endp + 1;
  173. }
  174. while (*endp);
  175. break;
  176. case 'g': /* labjack only */
  177. gain_count = 0;
  178. do {
  179. tmp = strtol(optarg, &endp, 0);
  180. if (*endp != '\0' && *endp != ',') {
  181. info("bad gain number: %s\n",
  182. optarg);
  183. goto printhelp;
  184. }
  185. if (gain_count >= MAX_CHANNELS) {
  186. info("error: too many gains specified\n");
  187. goto printhelp;
  188. }
  189. if (!(tmp == 0 || tmp == 1 || tmp == 2 || tmp == 3 || tmp == 8)) {
  190. info("error: invalid gain specified\n");
  191. goto printhelp;
  192. }
  193. gain_list[gain_count++] = tmp;
  194. optarg = endp + 1;
  195. }
  196. while (*endp);
  197. break;
  198. case 't': /* labjack only */
  199. timer_mode_count = 0;
  200. do {
  201. /* get mode */
  202. tmp = strtol(optarg, &endp, 0);
  203. if (*endp != '\0' && *endp != ',' && *endp != ':') {
  204. info("bad timer mode: %s\n", optarg);
  205. goto printhelp;
  206. }
  207. if (timer_mode_count >= UE9_TIMERS) {
  208. info("error: too many timers specified\n");
  209. goto printhelp;
  210. }
  211. timer_mode_list[timer_mode_count] = tmp;
  212. /* get optional value */
  213. if (*endp == ':') {
  214. optarg = endp + 1;
  215. tmp = strtol(optarg, &endp, 0);
  216. if (*endp != '\0' && *endp != ',') {
  217. info("bad timer value: %s\n", optarg);
  218. goto printhelp;
  219. }
  220. timer_value_list[timer_mode_count] = tmp;
  221. } else {
  222. timer_value_list[timer_mode_count] = 0;
  223. }
  224. timer_mode_count++;
  225. optarg = endp + 1;
  226. }
  227. while (*endp);
  228. break;
  229. case 'T': /* labjack only */
  230. timer_divisor = strtod(optarg, &endp);
  231. if (*endp || timer_divisor < 0 || timer_divisor > 255) {
  232. info("bad timer divisor: %s\n", optarg);
  233. goto printhelp;
  234. }
  235. break;
  236. case 'r':
  237. desired_rate = strtod(optarg, &endp);
  238. if (*endp || desired_rate <= 0) {
  239. info("bad rate: %s\n", optarg);
  240. goto printhelp;
  241. }
  242. break;
  243. case 'l':
  244. lines = strtol(optarg, &endp, 0);
  245. if (*endp || lines <= 0) {
  246. info("bad number of lines: %s\n", optarg);
  247. goto printhelp;
  248. }
  249. break;
  250. case 'R':
  251. tmp = strtol(optarg, &endp, 0);
  252. if (*endp != ',') {
  253. info("bad range number: %s\n", optarg);
  254. goto printhelp;
  255. }
  256. if (tmp != 5 && tmp != 10) {
  257. info("valid choices for range are 5 or 10\n");
  258. goto printhelp;
  259. }
  260. if (tmp == 5)
  261. precision = precision + 1;
  262. optarg = endp + 1;
  263. if (*endp == '\0') {
  264. info("Range needs two numbers, one for channels 0-5 and another for 6-11\n");
  265. goto printhelp;
  266. }
  267. tmp = strtol(optarg, &endp, 0);
  268. if (*endp != '\0') {
  269. info("Range needs only two numbers, one for channels 0-5 and another for 6-11\n");
  270. goto printhelp;
  271. }
  272. if (tmp != 5 && tmp != 10) {
  273. info("valid choices for range are 5 or 10\n");
  274. goto printhelp;
  275. }
  276. if (tmp == 5)
  277. precision = precision + 2;
  278. break;
  279. case 'N':
  280. nerdjack++;
  281. break;
  282. case 'L':
  283. labjack++;
  284. break;
  285. case 'd':
  286. detect++;
  287. break;
  288. case 'o':
  289. oneshot++;
  290. break;
  291. case 'f':
  292. forceretry++;
  293. break;
  294. case 'c':
  295. if (convert != 0) {
  296. info("specify only one conversion type\n");
  297. goto printhelp;
  298. }
  299. convert = CONVERT_VOLTS;
  300. break;
  301. case 'H':
  302. if (convert != 0) {
  303. info("specify only one conversion type\n");
  304. goto printhelp;
  305. }
  306. convert = CONVERT_HEX;
  307. break;
  308. case 'm':
  309. showmem++;
  310. case 'v':
  311. verb_count++;
  312. break;
  313. case 'X':
  314. printf("%s", examplestring);
  315. return 0;
  316. break;
  317. case 'V':
  318. printf("ethstream " VERSION "\n");
  319. printf("Written by Jim Paris <jim@jtan.com>\n");
  320. printf("and Zachary Clifford <zacharyc@mit.edu>.\n");
  321. printf("This program comes with no warranty and is "
  322. "provided under the GPLv2.\n");
  323. return 0;
  324. break;
  325. case 'i':
  326. inform++;
  327. break;
  328. case 'h':
  329. help = stdout;
  330. default:
  331. printhelp:
  332. fprintf(help, "Usage: %s [options]\n", *argv);
  333. opt_help(opt, help);
  334. fprintf(help, "Read data from the specified Labjack UE9"
  335. " via Ethernet. See README for details.\n");
  336. return (help == stdout) ? 0 : 1;
  337. }
  338. }
  339. if (detect && labjack) {
  340. info("The LabJack does not support autodetection\n");
  341. goto printhelp;
  342. }
  343. if (detect && !nerdjack) {
  344. info("Only the NerdJack supports autodetection - assuming -N option\n");
  345. nerdjack = 1;
  346. }
  347. if (detect && addressSpecified) {
  348. info("Autodetection and specifying address are mutually exclusive\n");
  349. goto printhelp;
  350. }
  351. if (nerdjack && labjack) {
  352. info("Nerdjack and Labjack options are mutually exclusive\n");
  353. goto printhelp;
  354. }
  355. donerdjack = nerdjack;
  356. //First if no options were supplied try the Nerdjack
  357. //The second time through, donerdjack will be true and this will not fire
  358. if (!nerdjack && !labjack) {
  359. info("No device specified...Defaulting to Nerdjack\n");
  360. donerdjack = 1;
  361. }
  362. doneparse:
  363. if (inform) {
  364. //We just want information from NerdJack
  365. if (!detect) {
  366. if (nerd_get_version(address) < 0) {
  367. info("Could not find NerdJack at specified address\n");
  368. } else {
  369. return 0;
  370. }
  371. }
  372. info("Autodetecting NerdJack address\n");
  373. free(address);
  374. if (nerdjack_detect(address) < 0) {
  375. info("Error with autodetection\n");
  376. goto printhelp;
  377. } else {
  378. info("Found NerdJack at address: %s\n", address);
  379. if (nerd_get_version(address) < 0) {
  380. info("Error getting NerdJack version\n");
  381. goto printhelp;
  382. }
  383. return 0;
  384. }
  385. }
  386. if (donerdjack) {
  387. if (channel_count > NERDJACK_CHANNELS) {
  388. info("Too many channels for NerdJack\n");
  389. goto printhelp;
  390. }
  391. for (i = 0; i < channel_count; i++) {
  392. if (channel_list[i] >= NERDJACK_CHANNELS) {
  393. info("Channel is out of NerdJack range: %d\n",
  394. channel_list[i]);
  395. goto printhelp;
  396. }
  397. }
  398. } else {
  399. if (channel_count > UE9_MAX_CHANNEL_COUNT) {
  400. info("Too many channels for LabJack\n");
  401. goto printhelp;
  402. }
  403. for (i = 0; i < channel_count; i++) {
  404. if (channel_list[i] > UE9_MAX_CHANNEL) {
  405. info("Channel is out of LabJack range: %d\n",
  406. channel_list[i]);
  407. goto printhelp;
  408. }
  409. }
  410. }
  411. /* Timer requires Labjack */
  412. if (timer_mode_count && !labjack) {
  413. info("Can't use timers on NerdJack\n");
  414. goto printhelp;
  415. }
  416. /* Individual Analog Channel Gain Set requires Labjack*/
  417. if (gain_count && !labjack) {
  418. info("Can't use Individual Gain Set on NerdJack\n");
  419. goto printhelp;
  420. }
  421. if (optind < argc) {
  422. info("error: too many arguments (%s)\n\n", argv[optind]);
  423. goto printhelp;
  424. }
  425. if (forceretry && oneshot) {
  426. info("forceretry and oneshot options are mutually exclusive\n");
  427. goto printhelp;
  428. }
  429. /* Two channels if none specified */
  430. if (channel_count == 0) {
  431. channel_list[channel_count++] = 0;
  432. channel_list[channel_count++] = 1;
  433. }
  434. if (verb_count) {
  435. info("Scanning channels:");
  436. for (i = 0; i < channel_count; i++)
  437. info_no_timestamp(" AIN%d", channel_list[i]);
  438. info_no_timestamp("\n");
  439. }
  440. /* Figure out actual rate. */
  441. if (donerdjack) {
  442. if (nerdjack_choose_scan(desired_rate, &actual_rate, &period) <
  443. 0) {
  444. info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
  445. }
  446. } else {
  447. if (ue9_choose_scan(desired_rate, &actual_rate,
  448. &scanconfig, &scaninterval) < 0) {
  449. info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
  450. }
  451. }
  452. if ((desired_rate != actual_rate) || verb_count) {
  453. info("Actual scanrate is %lf Hz\n", actual_rate);
  454. info("Period is %ld\n", period);
  455. }
  456. if (verb_count && lines) {
  457. info("Stopping capture after %d lines\n", lines);
  458. }
  459. signal(SIGINT, handle_sig);
  460. signal(SIGTERM, handle_sig);
  461. #ifdef SIGPIPE /* not on Windows */
  462. /* Ignore SIGPIPE so I/O errors to the network device won't kill the process */
  463. signal(SIGPIPE, SIG_IGN);
  464. #endif
  465. if (detect) {
  466. info("Autodetecting NerdJack address\n");
  467. free(address);
  468. if (nerdjack_detect(address) < 0) {
  469. info("Error with autodetection\n");
  470. goto printhelp;
  471. } else {
  472. info("Found NerdJack at address: %s\n", address);
  473. }
  474. }
  475. for (;;) {
  476. int ret;
  477. if (donerdjack) {
  478. ret =
  479. nerdDoStream(address, channel_list, channel_count,
  480. precision, period, convert, lines,
  481. showmem);
  482. verb("nerdDoStream returned %d\n", ret);
  483. } else {
  484. ret = doStream(address, scanconfig, scaninterval,
  485. channel_list, channel_count,
  486. timer_mode_list, timer_value_list,
  487. timer_mode_count, timer_divisor,
  488. gain_list, gain_count,
  489. convert, lines);
  490. verb("doStream returned %d\n", ret);
  491. }
  492. if (oneshot)
  493. break;
  494. if (ret == 0)
  495. break;
  496. //Neither options specified at command line and first time through.
  497. //Try LabJack
  498. if (ret == -ENOTCONN && donerdjack && !labjack && !nerdjack) {
  499. info("Could not connect NerdJack...Trying LabJack\n");
  500. donerdjack = 0;
  501. goto doneparse;
  502. }
  503. //Neither option supplied, no address, and second time through.
  504. //Try autodetection
  505. if (ret == -ENOTCONN && !donerdjack && !labjack && !nerdjack
  506. && !addressSpecified) {
  507. info("Could not connect LabJack...Trying to autodetect Nerdjack\n");
  508. detect = 1;
  509. donerdjack = 1;
  510. goto doneparse;
  511. }
  512. if (ret == -ENOTCONN && nerdjack && !detect
  513. && !addressSpecified) {
  514. info("Could not reach NerdJack...Trying to autodetect\n");
  515. detect = 1;
  516. goto doneparse;
  517. }
  518. if (ret == -ENOTCONN && !forceretry) {
  519. info("Initial connection failed, giving up\n");
  520. break;
  521. }
  522. if (ret == -EAGAIN || ret == -ENOTCONN) {
  523. /* Some transient error. Wait a tiny bit, then retry */
  524. info("Retrying in 5 secs.\n");
  525. sleep(5);
  526. } else {
  527. info("Retrying now.\n");
  528. }
  529. }
  530. debug("Done loop\n");
  531. return 0;
  532. }
  533. int
  534. nerdDoStream(const char *address, int *channel_list, int channel_count,
  535. int precision, unsigned long period, int convert, int lines,
  536. int showmem)
  537. {
  538. int retval = -EAGAIN;
  539. int fd_data;
  540. static int first_call = 1;
  541. static int started = 0;
  542. static int wasreset = 0;
  543. getPacket command;
  544. static unsigned short currentcount = 0;
  545. tryagain:
  546. //If this is the first time, set up acquisition
  547. //Otherwise try to resume the previous one
  548. if (started == 0) {
  549. if (nerd_generate_command
  550. (&command, channel_list, channel_count, precision,
  551. period) < 0) {
  552. info("Failed to create configuration command\n");
  553. goto out;
  554. }
  555. if (nerd_send_command(address, "STOP", 4) < 0) {
  556. if (first_call) {
  557. retval = -ENOTCONN;
  558. if (verb_count)
  559. info("Failed to send STOP command\n");
  560. } else {
  561. info("Failed to send STOP command\n");
  562. }
  563. goto out;
  564. }
  565. if (nerd_send_command(address, &command, sizeof(command)) < 0) {
  566. info("Failed to send GET command\n");
  567. goto out;
  568. }
  569. } else {
  570. //If we had a transmission in progress, send a command to resume from there
  571. char cmdbuf[10];
  572. sprintf(cmdbuf, "SETC%05hd", currentcount);
  573. retval = nerd_send_command(address, cmdbuf, strlen(cmdbuf));
  574. if (retval == -4) {
  575. info("NerdJack was reset\n");
  576. //Assume we have not started yet, reset on this side.
  577. //If this routine is retried, start over
  578. printf("# NerdJack was reset here\n");
  579. currentcount = 0;
  580. started = 0;
  581. wasreset = 1;
  582. goto tryagain;
  583. } else if (retval < 0) {
  584. info("Failed to send SETC command\n");
  585. goto out;
  586. }
  587. }
  588. //The transmission has begun
  589. started = 1;
  590. /* Open connection */
  591. fd_data = nerd_open(address, NERDJACK_DATA_PORT);
  592. if (fd_data < 0) {
  593. info("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
  594. goto out;
  595. }
  596. retval = nerd_data_stream
  597. (fd_data, channel_count, channel_list, precision, convert, lines,
  598. showmem, &currentcount, period, wasreset);
  599. wasreset = 0;
  600. if (retval == -3) {
  601. retval = 0;
  602. }
  603. if (retval < 0) {
  604. info("Failed to open data stream\n");
  605. goto out1;
  606. }
  607. info("Stream finished\n");
  608. retval = 0;
  609. out1:
  610. nerd_close_conn(fd_data);
  611. out:
  612. //We've tried communicating, so this is not the first call anymore
  613. first_call = 0;
  614. return retval;
  615. }
  616. int
  617. doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
  618. int *channel_list, int channel_count,
  619. int *timer_mode_list, int *timer_value_list,
  620. int timer_mode_count, int timer_divisor,
  621. int *gain_list, int gain_count,
  622. int convert, int lines)
  623. {
  624. int retval = -EAGAIN;
  625. // int fd_cmd, fd_data; *these are now globals so sighandler can use them*
  626. int ret;
  627. static int first_call = 1;
  628. struct callbackInfo ci = {
  629. .convert = convert,
  630. .maxlines = lines,
  631. };
  632. /* Open command connection. If this fails, and this is the
  633. first attempt, return a different error code so we give up. */
  634. fd_cmd = ue9_open(address, UE9_COMMAND_PORT);
  635. if (fd_cmd < 0) {
  636. info("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
  637. if (first_call)
  638. retval = -ENOTCONN;
  639. goto out;
  640. }
  641. first_call = 0;
  642. /* Make sure nothing is left over from a previous stream */
  643. if (ue9_stream_stop(fd_cmd) == 0)
  644. verb("Stopped previous stream.\n");
  645. ue9_buffer_flush(fd_cmd);
  646. /* Open data connection */
  647. fd_data = ue9_open(address, UE9_DATA_PORT);
  648. if (fd_data < 0) {
  649. info("Connect failed: %s:%d\n", address, UE9_DATA_PORT);
  650. goto out1;
  651. }
  652. /* Get calibration */
  653. if (ue9_get_calibration(fd_cmd, &ci.calib) < 0) {
  654. info("Failed to get device calibration\n");
  655. goto out2;
  656. }
  657. /* Set timer configuration */
  658. if (timer_mode_count &&
  659. ue9_timer_config(fd_cmd, timer_mode_list, timer_value_list,
  660. timer_mode_count, timer_divisor) < 0) {
  661. info("Failed to set timer configuration\n");
  662. goto out2;
  663. }
  664. if (gain_count) {
  665. /* Set stream configuration */
  666. if (ue9_streamconfig(fd_cmd, channel_list, channel_count,
  667. scanconfig, scaninterval,
  668. gain_list, gain_count) < 0) {
  669. info("Failed to set stream configuration\n");
  670. goto out2;
  671. }
  672. } else {
  673. /* Set stream configuration */
  674. if (ue9_streamconfig_simple(fd_cmd, channel_list, channel_count,
  675. scanconfig, scaninterval,
  676. UE9_BIPOLAR_GAIN1) < 0) {
  677. info("Failed to set stream configuration\n");
  678. goto out2;
  679. }
  680. }
  681. /* Start stream */
  682. if (ue9_stream_start(fd_cmd) < 0) {
  683. info("Failed to start stream\n");
  684. goto out2;
  685. }
  686. /* Stream data */
  687. ue9_running = 1;
  688. ret =
  689. ue9_stream_data(fd_data, channel_count, channel_list, gain_count, gain_list, data_callback, (void *)&ci);
  690. if (ret < 0) {
  691. info("Data stream failed with error %d\n", ret);
  692. goto out3;
  693. }
  694. info("Stream finished\n");
  695. retval = 0;
  696. out3:
  697. /* Stop stream and clean up */
  698. ue9_stream_stop(fd_cmd);
  699. ue9_buffer_flush(fd_cmd);
  700. out2:
  701. ue9_close(fd_data);
  702. out1:
  703. ue9_close(fd_cmd);
  704. out:
  705. ue9_running = 0;
  706. return retval;
  707. }
  708. int data_callback(int channels, int *channel_list, int gain_count, int *gain_list, uint16_t * data, void *context)
  709. {
  710. int i;
  711. struct callbackInfo *ci = (struct callbackInfo *)context;
  712. static int lines = 0;
  713. columns_left = channels;
  714. for (i = 0; i < channels; i++) {
  715. if (ci->convert == CONVERT_VOLTS &&
  716. channel_list[i] <= UE9_MAX_ANALOG_CHANNEL) {
  717. /* CONVERT_VOLTS */
  718. if (i < gain_count)
  719. {
  720. if (printf("%lf", ue9_binary_to_analog(
  721. &ci->calib, gain_list[i],
  722. 12, data[i])) < 0)
  723. goto bad;
  724. } else {
  725. if (printf("%lf", ue9_binary_to_analog(
  726. &ci->calib, 0,
  727. 12, data[i])) < 0)
  728. goto bad;
  729. }
  730. } else if (ci->convert == CONVERT_VOLTS &&
  731. (channel_list[i] == 141 || channel_list[i] == 133)) {
  732. /* CONVERT_VOLTS but output temperature */
  733. if (printf("%lf", ue9_binary_to_temperature(
  734. &ci->calib, data[i])) < 0)
  735. goto bad;
  736. } else if (ci->convert == CONVERT_HEX) {
  737. /* CONVERT_HEX */
  738. if (printf("%04X", data[i]) < 0)
  739. goto bad;
  740. } else {
  741. /* CONVERT_DEC */
  742. if (printf("%d", data[i]) < 0)
  743. goto bad;
  744. }
  745. columns_left--;
  746. if (i < (channels - 1)) {
  747. if (ci->convert != CONVERT_HEX && putchar(' ') < 0)
  748. goto bad;
  749. } else {
  750. if (putchar('\n') < 0)
  751. goto bad;
  752. lines++;
  753. if (ci->maxlines && lines >= ci->maxlines)
  754. return -1;
  755. }
  756. }
  757. return 0;
  758. bad:
  759. info("Output error (disk full?)\n");
  760. return -3;
  761. }