trace_view.c 8.64 KB
Newer Older
Jeroen Vreeken's avatar
Jeroen Vreeken committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
	Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2007
	Copyright Stichting C.A. Muller Radioastronomiestation, 2007

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <math.h>
#include <time.h>
29
#include <err.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
30

31
#include <trace/trace.h>
Jeroen Vreeken's avatar
Jeroen Vreeken committed
32
33
34

time_t starttime;

35
36
37
int val_buflen;

//#define VAL_BUFLEN (60 * 5)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
38
39
40
FILE *fdplot = NULL;
int doplot = 0;

41
42
struct trace_view {
	struct trace *trace;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
43
44
	char *tmpfilename;
	FILE *fdtmp;
45
	struct trace_value *value;
46
	int bufpos;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
47
48
};

49
50
struct trace_view *traces;

51
52
53
54
55
56
57
58
59
60
float samp_min, samp_max;

static void add_to_range(float val)
{

	if (val < samp_min)
		samp_min = val;
	if (val > samp_max)
		samp_max = val;
}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
61

62
void plot_values(struct trace_view *traces, int nr_traces)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
63
64
{
	int i;
65
	int bufpos = (traces[0].bufpos + val_buflen - 1) % val_buflen;
66
67
	long long now = traces[0].value[bufpos].t.tv_sec;
	long nown = traces[0].value[bufpos].t.tv_nsec;
68
	float range, range_low, range_high;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
69
	
70
	fprintf(fdplot, "plot ");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
71

72
73
74
75
76
77
78
79
	for(i = 0; i < nr_traces; i++) {
		if (i > 0)
			fprintf(fdplot, ", ");
		fprintf(fdplot, "'%s' using ($1-%lld.%09ld):($2) title '%s [%s]' with lines",
		    traces[i].tmpfilename,
		    now, nown,
		    traces[i].trace->name, 
		    "");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
80
	}
81
	fprintf(fdplot, "\n");
82
83
84
85
86
87
88
89
90
91
92
93
94

	// Calculate an y range that will fit all samples, plus a tiny bit
	// more so the lowest/highest values are still visible at the edges of
	// the plot.
	//printf("sample range: %f, %f\n", samp_min, samp_max);
	range = samp_max - samp_min;
	if (range == 0.0)
		range = 0.1;

	range_low = samp_min - range * 0.02;
	range_high = samp_max + range * 0.02;
	fprintf(fdplot, "set yrange [%e:%e]\n", range_low, range_high);

95
	fflush(NULL);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
96

97
	return;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
98
99
}

100
101
static void print_value(FILE *fd,
    struct trace *trace, struct trace_value *value)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
102
{
103
104
105
106
	switch (trace->type) {
		case TRACE_VALUE_TYPE_FLOAT:
			fprintf(fd, "%ld.%09ld %e\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.f);
107
			add_to_range(value->value.f);
108
109
110
111
			break;
		case TRACE_VALUE_TYPE_BOOL:
			fprintf(fd, "%ld.%09ld %d\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.b);
112
			add_to_range(value->value.b);
113
114
115
116
			break;
		case TRACE_VALUE_TYPE_UINT8:
			fprintf(fd, "%ld.%09ld %u\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.u8);
117
			add_to_range(value->value.u8);
118
119
120
121
			break;
		case TRACE_VALUE_TYPE_UINT16:
			fprintf(fd, "%ld.%09ld %u\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.u16);
122
			add_to_range(value->value.u16);
123
124
125
126
			break;
		case TRACE_VALUE_TYPE_UINT32:
			fprintf(fd, "%ld.%09ld %u\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.u32);
127
			add_to_range(value->value.u32);
128
129
130
131
			break;
		case TRACE_VALUE_TYPE_SINT8:
			fprintf(fd, "%ld.%09ld %d\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.s8);
132
			add_to_range(value->value.s8);
133
134
135
136
			break;
		case TRACE_VALUE_TYPE_SINT16:
			fprintf(fd, "%ld.%09ld %d\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.s16);
137
			add_to_range(value->value.s16);
138
139
140
141
			break;
		case TRACE_VALUE_TYPE_SINT32:
			fprintf(fd, "%ld.%09ld %d\n", 
			value->t.tv_sec, value->t.tv_nsec, value->value.s32);
142
			add_to_range(value->value.s32);
143
			break;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
144
145
146
	}
}

147
static void handler_value(struct trace *trace, int channel,
148
    struct trace_value *value)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
149
{
150
	struct trace_view *view;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
151
152
	int i;
	
153
154
155
	view = trace->private;
	
	view->value[view->bufpos] = *value;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
156

157
	freopen(NULL, "w", view->fdtmp);
158
159
160
	for (i = view->bufpos + 1; i < val_buflen; i++) {
		if ((!view->value[i].t.tv_sec) ||
		    (view->value[i].t.tv_sec == 1))
161
162
163
			continue;

		print_value(view->fdtmp, trace, &view->value[i]);
Jeroen Vreeken's avatar
Jeroen Vreeken committed
164
	}
165
166
167
	for (i = 0; i <= view->bufpos; i++) {
		if (!view->value[i].t.tv_sec)
			continue;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
168

169
170
171
172
173
174
		print_value(view->fdtmp, trace, &view->value[i]);
	}
	fflush(view->fdtmp);


	view->bufpos++;
175
	view->bufpos %= val_buflen;
176
177
	
	doplot++;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
178
179
}

180
181
182
183
184
185
186
187
188
189
190
191
192
193
static void usage(char *progname)
{

	fprintf(stderr, "usage: %s [options] [one or more trace names]\n"
	    "available options:\n"
	    " --host [hostname]       set the name of the host to connect to\n"
	    " --port [port#]          set the remote port number to connect "
	    "to\n"
	    " --interval [seconds]    set the sample interval\n",
	    " --history [seconds]     set the length of the plot\n",
	    progname);
	exit(1);
}

Jeroen Vreeken's avatar
Jeroen Vreeken committed
194
195
int main(int argc, char **argv)
{
196
197
198
199
200
	struct timespec t_int;
	struct trace_view *t;
	double interval, history;
	char *host;
        int trace_nr, i, port, ret = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
201
202

	starttime = time(NULL);
203
204
205
206
207
208
209
210
	host = "localhost";
	port = 10000;

	if (argc < 2)
		usage(argv[0]);

	history = 60.0;
	interval = 0.2;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
211

212
	traces = calloc(sizeof(struct trace_view), argc - 1);
213
	trace_nr = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
214
	for (i = 0; i < argc - 1; i++) {
215
216
217
		char *tracename = argv[i+1];
		int fdmkstemp;
		
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
		if (strcmp(tracename, "--host") == 0) {
			if (i >= argc - 2)
				errx(1, "missing hostname");
			
			i++;
			host = argv[i + 1];
			fprintf(stderr, "Setting remote host to '%s'\n",
			    host);
			continue;
		}
		if (strcmp(tracename, "--port") == 0) {
			if (i >= argc - 2)
				errx(1, "missing port number");
			
			i++;
			port = atoi(argv[i + 1]);
			fprintf(stderr, "Setting remote port to %d\n", port);
			continue;
		}
		if (strcmp(tracename, "--interval") == 0) {
			if (i >= argc - 2)
				errx(1, "missing interval argument");
			
			i++;
			interval = atof(argv[i + 1]);
			fprintf(stderr, "Setting sample interval %fs\n",
			    interval);
			continue;
		}
		if (strcmp(tracename, "--history") == 0) {
			if (i >= argc - 2)
				errx(1, "missing history argument");
			
			i++;
			history = atof(argv[i + 1]);
			fprintf(stderr, "Setting history to %fs\n", history);
			continue;
		}
		
		fprintf(stderr, "Adding trace '%s'\n", tracename);
		t = &traces[trace_nr];
		t->trace = trace_open(host, port);
		if (!t->trace) {
			fprintf(stderr, "opening trace on %s:%d failed\n",
			    host, port);
263
264
			return 1;
		}
265
266
		t->trace->private = &traces[trace_nr];
		trace_name_set(t->trace, tracename);
267

268
269
270
271
		t->tmpfilename = malloc(100);
		sprintf(t->tmpfilename,
		     "/tmp/trace_view_XXXXXX");
		fdmkstemp = mkstemp(t->tmpfilename);
272
273
	
		printf("Going to use %s temp file for '%s' data\n",
274
275
276
		    t->tmpfilename, tracename);
		t->fdtmp = fdopen(fdmkstemp, "w");
		if (t->fdtmp == NULL) {
277
278
279
280
			perror("fopen() failed");
			return 1;
		}

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
		t->trace->handler_value = handler_value;
		trace_nr++;
	}
	
	if (trace_nr == 0) {
		// Only options, but no trace names..
		fprintf(stderr, "Error: Need at least one trace name.\n\n");
		usage(argv[0]);
	}
	
	// calculate buffer length & sample interval
	if (interval <= 0.0)
		errx(1, "invalid interval!");
	if (history <= 0.0)
		errx(1, "invalid history length!");
	if (history < interval)
		errx(1, "history length must be >= interval!");
	
	t_int.tv_sec = floor(interval);
	t_int.tv_nsec = (interval - t_int.tv_sec) * 1.0e9;
301

302
303
304
305
306
307
308
309
	//t_int.tv_sec = 0;
	//t_int.tv_nsec = 200 * 1000;
	
	// set interval on all trace connections
	for (i = 0; i < trace_nr; i++) {
		struct trace_pkt *pkt;

		t = &traces[i];
310
311
312
		pkt = trace_packet_new();
		trace_packet_interval_set(pkt, 
		    &t_int, TRACE_INTERVAL_TYPE_INTERVAL);
313
		trace_packet_write(t->trace, pkt);
314
		trace_packet_put(pkt);
315
316
	}
	
317

318
319
320
321
322
323
324
325
326
327
	// allocate memory for enough samples
	val_buflen = ceil(history / interval);
	//printf("buflen=%d samples\n", val_buflen);
	//printf("interval= %d.%09d\n", t_int.tv_sec, t_int.tv_nsec);

	for (i = 0; i < trace_nr; i++) {
		traces[i].value = calloc(val_buflen,
		    sizeof(struct trace_value));
		if (traces[i].value == NULL)
			errx(1, "out of memory!");
Jeroen Vreeken's avatar
Jeroen Vreeken committed
328
329
330
331
332
333
334
335
336
337
	}
	
	fdplot = popen("gnuplot -noraise", "w");
	if (fdplot == NULL) {
		perror("popen() failed");
		return 1;
	}
	
	fprintf(fdplot, "set grid\n");

338
339
340
	samp_min = +INFINITY;
	samp_max = -INFINITY;

Jeroen Vreeken's avatar
Jeroen Vreeken committed
341
	do {
342
343
344
		fd_set fdrx;
		
		int high = 0;
345

346
347
		FD_ZERO(&fdrx);
	
348
		for (i = 0; i < trace_nr; i++) {
349
			trace_fd_set(traces[i].trace, &fdrx, &high);
350
		}
351
352
353
		
		select(high+1, &fdrx, NULL, NULL, NULL);

354
		for (i = 0; i < trace_nr; i++) {
355
			trace_handle(traces[i].trace, &fdrx);
356
		}
357

358
359
		if (doplot >= trace_nr) {
			plot_values(traces, trace_nr);
360
			doplot = 0;
361
362
363

			samp_min = +INFINITY;
			samp_max = -INFINITY;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
364
365
366
367
368
369
370
371
		}
	} while (ret == 0);

	for (i = 0; i < argc - 1; i++)
		unlink(traces[i].tmpfilename);

	return 0;
}