From: Oliver Hartkopp Date: Sat, 6 Jan 2007 15:43:12 +0000 (+0000) Subject: Minor fix and cosmetics in color handling in candump.c . X-Git-Url: https://rtime.felk.cvut.cz/gitweb/sojka/can-utils.git/commitdiff_plain/4d129ea348ef62f296f3882cb8e1272907fe7b6a Minor fix and cosmetics in color handling in candump.c . Added new tool 'canplayer' to replay logfiles generated by candump -l . Features of canplayer: - Input from stdin or file. - throttling of the replay to get nearly original timestamps / message gaps - mapping and selection of CAN interfaces (assignment) e.g. canplay -I logfile vcan2=can2 vcan0=can1 can2=can3 means: send frames received on can1 in the logfile to vcan0 and so on ... - if no assignment is made the original interfaces are used for replay - handling of multiple CAN interfaces simultaneously (if in logfile) - option: throttle disable (do not look on timestamps => very FAST replay!) - option: change the 'sleep time' in milli seconds Remarks: canplayer uses nanosleep() for throttling which means that the resolution of the canplayer is about 1ms (Kernel HZ = 1000) or 10ms (Kernel HZ = 100). After each nanosleep() all the CAN frames are send that had to be transmitted until the timestamp at the current time. Giving e.g. the option '-g 500' for 500ms let's you see the behaviour. Using nanosleep() makes canplay a very performant tool with minimum CPU load. To transfer CAN frames over a TCP/IP network you may now say something like: candump -> netcat -> netcat -> canplayer --- diff --git a/Makefile b/Makefile index 441dbcb..b649438 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ CFLAGS = -O2 -Wall -Wno-parentheses -I../kernel/2.6/include \ -fno-strict-aliasing -PROGRAMS = candump can-sniffer cansend cangen log2long log2asc +PROGRAMS = candump can-sniffer cansend canplayer cangen log2long log2asc all: $(PROGRAMS) @@ -53,14 +53,16 @@ clean: distclean: rm -f $(PROGRAMS) *.o *~ -cansend.o: lib.h -cangen.o: lib.h -candump.o: lib.h -log2long.o: lib.h -log2asc.o: lib.h +cansend.o: lib.h +cangen.o: lib.h +candump.o: lib.h +canplayer.o: lib.h +log2long.o: lib.h +log2asc.o: lib.h -cansend: cansend.o lib.o -cangen: cangen.o lib.o -candump: candump.o lib.o -log2long: log2long.o lib.o -log2asc: log2asc.o lib.o +cansend: cansend.o lib.o +cangen: cangen.o lib.o +candump: candump.o lib.o +canplayer: canplayer.o lib.o +log2long: log2long.o lib.o +log2asc: log2asc.o lib.o diff --git a/candump.c b/candump.c index 444cf7d..83ac4bb 100644 --- a/candump.c +++ b/candump.c @@ -465,7 +465,7 @@ int main(int argc, char **argv) continue; } - printf(" %s",(color>2)?col_on[idx]:""); + printf(" %s", (color>2)?col_on[idx]:""); switch (timestamp) { @@ -508,13 +508,13 @@ int main(int argc, char **argv) break; } - printf(" %s",(color && (color<3))?col_on[idx]:""); + printf(" %s", (color && (color<3))?col_on[idx]:""); printf("%*s", max_devname_len, devname[idx]); - printf("%s ",(color<2)?col_off:""); + printf("%s ", (color==1)?col_off:""); fprint_long_canframe(stdout, &frame, NULL, ascii); - printf("%s",(color>1)?col_off:""); + printf("%s", (color>1)?col_off:""); printf("\n"); } fflush(stdout); diff --git a/canplayer.c b/canplayer.c new file mode 100644 index 0000000..8cfd291 --- /dev/null +++ b/canplayer.c @@ -0,0 +1,394 @@ +/* + * $Id$ + */ + +/* + * canplayer.c - replay a compact CAN frame logfile to CAN devices + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, the following disclaimer and + * the referenced file 'COPYING'. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "lib.h" + +#define DEFAULT_GAP 1 /* ms */ +#define CHANNELS 20 /* anyone using more than 20 CAN interfaces at a time? */ +#define BUFSZ 100 /* for one line in the logfile */ + +struct assignment { + char txif[IFNAMSIZ]; + int txifidx; + char rxif[IFNAMSIZ]; +}; +static struct assignment asgn[CHANNELS]; + +extern int optind, opterr, optopt; + +void print_usage(char *prg) +{ + fprintf(stderr, "\nUsage: %s [interface assignment]*\n\n", prg); + fprintf(stderr, "Options: -I (default stdin)\n"); + fprintf(stderr, " -t (ignore timestamps: " + "send frames immediately)\n"); + fprintf(stderr, " -g (gap in milli " + "seconds - default: %d ms)\n", DEFAULT_GAP); + fprintf(stderr, " -v (verbose: print " + "sent CAN frames)\n\n"); + fprintf(stderr, "Interface assignment: 0..n assignments like " + "=\n"); + fprintf(stderr, "e.g. vcan2=can0 ( send frames received from can0 on " + "vcan2 )\n"); + fprintf(stderr, "No assignments => send frames to the interface(s) they " + "had been received from.\n\n"); +} + +/* copied from /usr/src/linux/include/linux/time.h ... + * lhs < rhs: return <0 + * lhs == rhs: return 0 + * lhs > rhs: return >0 + */ +static inline int timeval_compare(struct timeval *lhs, struct timeval *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_usec - rhs->tv_usec; +} + +static inline int frames_to_send(struct timeval *today, struct timeval *diff, + struct timeval *log) +{ + /* return value <0 when log + diff < today */ + + struct timeval cmp; + + cmp.tv_sec = log->tv_sec + diff->tv_sec; + cmp.tv_usec = log->tv_usec + diff->tv_usec; + + if (cmp.tv_usec > 1000000) { + cmp.tv_usec -= 1000000; + cmp.tv_sec++; + } + + return timeval_compare(&cmp, today); +} + +int get_txidx(char *logif_name) { + + int i; + + for (i=0; i= IFNAMSIZ) { + fprintf(stderr, "write-if interface name '%s' too long!", txname); + return 1; + } + strcpy(asgn[i].txif, txname); + + if (strlen(rxname) >= IFNAMSIZ) { + fprintf(stderr, "log-if interface name '%s' too long!", rxname); + return 1; + } + strcpy(asgn[i].rxif, rxname); + + strcpy(ifr.ifr_name, txname); + if (ioctl(socket, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + fprintf(stderr, "write-if interface name '%s' is wrong!\n", txname); + return 1; + } + asgn[i].txifidx = ifr.ifr_ifindex; + + if (verbose > 1) /* use -v -v to see this */ + printf("added %s assignment: log-if=%s write-if=%s write-if-idx=%d\n", + mode, asgn[i].rxif, asgn[i].txif, asgn[i].txifidx); + + return 0; +} + +int main(int argc, char **argv) +{ + static char buf[BUFSZ], device[BUFSZ], ascframe[BUFSZ]; + struct sockaddr_can addr; + static struct can_frame frame; + static struct timeval today_tv, log_tv, diff_tv; + struct timespec sleep_ts; + int s; /* CAN_RAW socket */ + FILE *infile = stdin; + unsigned long gap = DEFAULT_GAP; + int use_timestamps = 1; + static int verbose, opt, loops; + int assignments; /* assignments defined on the commandline */ + int txidx; /* sendto() interface index */ + int nbytes, i, j; + + while ((opt = getopt(argc, argv, "I:tg:v")) != -1) { + switch (opt) { + case 'I': + infile = fopen(optarg, "r"); + if (!infile) { + perror("infile"); + return 1; + } + break; + + case 't': + use_timestamps = 0; + break; + + case 'g': + gap = strtoul(optarg, NULL, 10); + break; + + case 'v': + verbose++; + break; + + default: + print_usage(basename(argv[0])); + return 1; + break; + } + } + + assignments = argc - optind; /* find real number of user assignments */ + + sleep_ts.tv_sec = gap / 1000; + sleep_ts.tv_nsec = (gap % 1000) * 1000000; + + /* open socket */ + if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("socket"); + return 1; + } + + addr.can_family = AF_CAN; + addr.can_ifindex = 0; + + /* disable unneeded default receive filter on this RAW socket */ + setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; + } + + if (assignments) { + /* add & check user assginments from commandline */ + for (i=0; i= BUFSZ) { + fprintf(stderr, "Assignment too long!\n"); + print_usage(basename(argv[0])); + return 1; + } + strcpy(buf, argv[optind+i]); + for (j=0; j assign this device automatically */ + if (add_assignment("auto", s, device, device, verbose)) + return 1; + txidx = get_txidx(device); + } + + if (txidx) { /* only send to valid CAN devices */ + + if (parse_canframe(ascframe, &frame)) { + fprintf(stderr, "wrong CAN frame format: '%s'!", ascframe); + return 1; + } + + addr.can_family = AF_CAN; + addr.can_ifindex = txidx; /* send via this interface */ + + nbytes = sendto(s, &frame, sizeof(struct can_frame), 0, + (struct sockaddr*)&addr, sizeof(addr)); + + if (nbytes != sizeof(struct can_frame)) { + perror("sendto"); + return 1; + } + + if (verbose) { + printf("%s (%s) ", get_txname(device), device); + fprint_long_canframe(stdout, &frame, "\n", 1); + } + } + + /* read next frame from logfile */ + if (!fgets(buf, BUFSZ-1, infile)) + goto out; /* nothing to read */ + + if (sscanf(buf, "(%ld.%ld) %s %s", &log_tv.tv_sec, &log_tv.tv_usec, + device, ascframe) != 4) + return 1; + + if (use_timestamps) /* save a syscall if possible */ + gettimeofday(&today_tv, NULL); + } + + if (nanosleep(&sleep_ts, NULL)) + return 1; + + loops++; /* private statistics */ + gettimeofday(&today_tv, NULL); + } + + out: + + close(s); + fclose(infile); + + if (verbose > 1) /* use -v -v to see this */ + printf("%d loops\n", loops); + + return 0; +}