/** * @file udps_dump.c * @brief Example UDPS client: connects to a UDPStreamer and prints what arrives. * * Build with the Makefile in the parent directory, then for a unicast stream: * * ./udps_dump --host 127.0.0.1 --port 44500 * * or, for a multicast one: * * ./udps_dump --host 127.0.0.1 --port 44500 \ * --multicast 239.0.0.1 --iface 127.0.0.1 * * Ctrl-C prints a summary of what was received. */ #define _POSIX_C_SOURCE 200809L #include "udps_client.h" #include #include #include #include #include static volatile sig_atomic_t g_stop = 0; static void on_sigint(int sig) { (void)sig; g_stop = 1; } typedef struct { double print_interval; /**< Seconds between frame printouts. */ double last_print; uint64_t frames; uint64_t max_frames; } dump_state_t; static double now_wall(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9; } static const char *time_mode_name(uint8_t m) { switch (m) { case UDPS_TIME_PACKET: return "packet"; case UDPS_TIME_FULL_ARRAY: return "full-array"; case UDPS_TIME_FIRST_SAMPLE: return "first-sample"; case UDPS_TIME_LAST_SAMPLE: return "last-sample"; default: return "?"; } } static const char *publish_mode_name(uint8_t m) { switch (m) { case UDPS_PUBLISH_STRICT: return "strict"; case UDPS_PUBLISH_ACCUMULATE: return "accumulate"; case UDPS_PUBLISH_DECIMATE: return "decimate"; default: return "?"; } } static void on_config(const udps_signal_t *sigs, uint32_t n, uint8_t mode, void *user) { uint32_t i; (void)user; printf("\nCONFIG: %u signal(s), publish mode %s\n", (unsigned)n, publish_mode_name(mode)); printf(" %-3s %-24s %-8s %-10s %-8s %-10s %s\n", "#", "name", "type", "shape", "unit", "rate[Hz]", "time-mode"); for (i = 0u; i < n; i++) { char shape[32]; const udps_signal_t *s = &sigs[i]; if (s->num_cols > 1u) { snprintf(shape, sizeof shape, "%ux%u", (unsigned)s->num_rows, (unsigned)s->num_cols); } else { snprintf(shape, sizeof shape, "%u", (unsigned)udps_signal_num_elements(s)); } printf(" %-3u %-24s %-8s %-10s %-8s %-10.6g %s%s\n", (unsigned)i, s->name, udps_type_name(s->type_code), shape, (s->unit[0] != '\0') ? s->unit : "-", s->sampling_rate, time_mode_name(s->time_mode), (s->quant_type != UDPS_QUANT_NONE) ? " (quantised)" : ""); } fflush(stdout); } static void on_data(const udps_frame_t *f, void *user) { dump_state_t *st = (dump_state_t *)user; uint32_t i; double now; st->frames++; now = now_wall(); if ((now - st->last_print) < st->print_interval) { return; /* Streams run far faster than a terminal can be read. */ } st->last_print = now; printf("\nframe #%lu t=%.6f samples=%u (%lu frames so far)\n", (unsigned long)f->counter, f->recv_time, (unsigned)f->num_samples, (unsigned long)st->frames); for (i = 0u; i < f->num_signals; i++) { const double *v = f->values[i].values; uint32_t cnt = f->values[i].count; double lo, hi; uint32_t k; if (cnt == 0u) { continue; } lo = hi = v[0]; for (k = 1u; k < cnt; k++) { if (v[k] < lo) { lo = v[k]; } if (v[k] > hi) { hi = v[k]; } } printf(" %-24s n=%-6u first=%-12.6g last=%-12.6g min=%-12.6g max=%-12.6g %s\n", f->signals[i].name, (unsigned)cnt, v[0], v[cnt - 1u], lo, hi, f->signals[i].unit); } fflush(stdout); } static void on_event(udps_event_t ev, const char *detail, void *user) { (void)user; switch (ev) { case UDPS_EVENT_CONNECTED: printf("[connected to %s]\n", detail ? detail : ""); break; case UDPS_EVENT_DISCONNECTED: printf("[disconnected: %s]\n", detail ? detail : ""); break; case UDPS_EVENT_ERROR: fprintf(stderr, "[error] %s\n", detail ? detail : ""); break; default: break; } fflush(stdout); } static void usage(const char *argv0) { printf("Usage: %s --host ADDR --port N [options]\n" "\n" " --host ADDR server address (default 127.0.0.1)\n" " --port N server UDP port, or TCP control port in multicast\n" " mode (default 44500)\n" " --multicast GROUP join GROUP for data instead of unicast\n" " --iface ADDR local interface address for the multicast join\n" " --data-port N multicast data port (default: --port + 1)\n" " --silence SEC reconnect after SEC without data (default 1, 0 off)\n" " --interval SEC seconds between printouts (default 1)\n" " --frames N exit after N frames (default: run until Ctrl-C)\n" " --help this text\n", argv0); } int main(int argc, char **argv) { udps_client_config_t cfg; udps_client_t *cli; dump_state_t st; udps_stats_t stats; struct sigaction sa; const char *host = "127.0.0.1"; int i; udps_client_config_init(&cfg); cfg.server_port = 44500u; memset(&st, 0, sizeof st); st.print_interval = 1.0; for (i = 1; i < argc; i++) { const char *a = argv[i]; const char *next = (i + 1 < argc) ? argv[i + 1] : NULL; if (strcmp(a, "--help") == 0) { usage(argv[0]); return 0; } if (next == NULL) { fprintf(stderr, "missing value for %s\n", a); return 2; } if (strcmp(a, "--host") == 0) { host = next; } else if (strcmp(a, "--port") == 0) { cfg.server_port = (uint16_t)atoi(next); } else if (strcmp(a, "--multicast") == 0) { cfg.multicast_group = next; } else if (strcmp(a, "--iface") == 0) { cfg.interface_addr = next; } else if (strcmp(a, "--data-port") == 0) { cfg.data_port = (uint16_t)atoi(next); } else if (strcmp(a, "--silence") == 0) { cfg.silence_timeout_s = atof(next); } else if (strcmp(a, "--interval") == 0) { st.print_interval = atof(next); } else if (strcmp(a, "--frames") == 0) { st.max_frames = (uint64_t)strtoull(next, NULL, 10); } else { fprintf(stderr, "unknown option %s\n", a); usage(argv[0]); return 2; } i++; } cfg.server_addr = host; cli = udps_client_create(&cfg); if (cli == NULL) { fprintf(stderr, "could not create client for %s:%u\n", host, (unsigned)cfg.server_port); return 1; } udps_client_set_callbacks(cli, on_config, on_data, on_event, &st); memset(&sa, 0, sizeof sa); sa.sa_handler = on_sigint; (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGTERM, &sa, NULL); printf("listening to %s:%u%s%s ... (Ctrl-C to stop)\n", host, (unsigned)cfg.server_port, cfg.multicast_group ? " via multicast " : "", cfg.multicast_group ? cfg.multicast_group : ""); while (!g_stop && (st.max_frames == 0u || st.frames < st.max_frames)) { /* All the work — connecting, receiving, decoding, reconnecting — and * every callback happens inside this call. */ (void)udps_client_poll(cli, 200); } udps_client_stats(cli, &stats); printf("\n--- summary ---\n" "packets %lu\n" "bytes %.1f MiB\n" "frames %lu\n" "configs %lu\n" "gaps %lu (datagrams lost)\n" "dropped %lu (fragments)\n" "reconnects %lu\n", (unsigned long)stats.packets_received, (double)stats.bytes_received / (1024.0 * 1024.0), (unsigned long)stats.frames_delivered, (unsigned long)stats.config_updates, (unsigned long)stats.counter_gaps, (unsigned long)stats.fragments_dropped, (unsigned long)stats.reconnects); udps_client_destroy(cli); return 0; }