nmxptool.c 26.8 KB
Newer Older
Matteo Quintiliani's avatar
Matteo Quintiliani committed
1
2
3
4
5
6
7
8
9
/*! \file
 *
 * \brief Nanometrics Protocol Tool
 *
 * Author:
 * 	Matteo Quintiliani
 * 	Istituto Nazionale di Geofisica e Vulcanologia - Italy
 *	quintiliani@ingv.it
 *
10
 * $Id: nmxptool.c,v 1.96 2007-12-16 18:25:17 mtheo Exp $
Matteo Quintiliani's avatar
Matteo Quintiliani committed
11
 *
Matteo Quintiliani's avatar
Matteo Quintiliani committed
12
13
 */

Matteo Quintiliani's avatar
Matteo Quintiliani committed
14
15
#include "config.h"

16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Matteo Quintiliani's avatar
Matteo Quintiliani committed
19
#include <errno.h>
20
21

#include <nmxp.h>
Matteo Quintiliani's avatar
Matteo Quintiliani committed
22

Matteo Quintiliani's avatar
Matteo Quintiliani committed
23
#ifndef HAVE_WINDOWS_H
Matteo Quintiliani's avatar
Matteo Quintiliani committed
24
25
26
#include <signal.h>
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
27
28
29
30
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif

31
#include "nmxptool_getoptlong.h"
32
33

#ifdef HAVE_EARTHWORMOBJS
34
#include "nmxptool_ew.h"
35
#endif
36

37
38
39
#ifdef HAVE_LIBMSEED
#include <libmseed.h>
#endif
40

41
42
#ifdef HAVE___SRC_SEEDLINK_PLUGIN_H
#include "seedlink_plugin.h"
43
44
#endif

45
46
47
#define CURRENT_NETWORK ( (params.network)? params.network : DEFAULT_NETWORK )
#define NETCODE_OR_CURRENT_NETWORK ( (network_code[0] != 0)? network_code : CURRENT_NETWORK )

Matteo Quintiliani's avatar
Matteo Quintiliani committed
48
#define GAP_TOLLERANCE 0.001
49

50
51
52
typedef struct {
    int significant;
    double last_time;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
53
    time_t last_time_call_raw_stream;
54
    int32_t x_1;
55
    NMXP_RAW_STREAM_DATA raw_stream_buffer;
56
57
} NMXPTOOL_CHAN_SEQ;

Matteo Quintiliani's avatar
Matteo Quintiliani committed
58
59

#ifndef HAVE_WINDOWS_H
Matteo Quintiliani's avatar
Matteo Quintiliani committed
60
61
static void clientShutdown(int sig);
static void clientDummyHandler(int sig);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
62
#endif
Matteo Quintiliani's avatar
Matteo Quintiliani committed
63

64
static void saving_channel_states();
Matteo Quintiliani's avatar
Matteo Quintiliani committed
65
66
static void flushing_raw_data_stream();

67
#ifdef HAVE_LIBMSEED
Matteo Quintiliani's avatar
Matteo Quintiliani committed
68
int nmxptool_write_miniseed(NMXP_DATA_PROCESS *pd);
69
70
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
71
#ifdef HAVE___SRC_SEEDLINK_PLUGIN_C
Matteo Quintiliani's avatar
Matteo Quintiliani committed
72
int nmxptool_send_raw_depoch(NMXP_DATA_PROCESS *pd);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
73
74
#endif

75
int nmxptool_print_seq_no(NMXP_DATA_PROCESS *pd);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
76
77

int nmxptool_check_and_log_gap(double time1, double time2, const double gap_tollerance, const char *station, const char *channel);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
78
void nmxptool_str_time_to_filename(char *str_time);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
79
80


81
82
83
84
85
/* Global variable for main program and handling terminitation program */
NMXPTOOL_PARAMS params;
int naqssock = 0;
FILE *outfile = NULL;
NMXP_CHAN_LIST *channelList = NULL;
86
NMXP_CHAN_LIST_NET *channelList_subset = NULL;
87
NMXPTOOL_CHAN_SEQ *channelListSeq = NULL;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
88
89
90
int n_func_pd = 0;
int (*p_func_pd[NMXP_MAX_FUNC_PD]) (NMXP_DATA_PROCESS *);

91
92
93
94
95
96
97
98

#ifdef HAVE_LIBMSEED
/* Mini-SEED variables */
NMXP_DATA_SEED data_seed;
MSRecord *msr_list_chan[MAX_N_CHAN];
#endif


99
int main (int argc, char **argv) {
100
    int32_t connection_time;
101
    int request_SOCKET_OK;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
102
    int i_chan, cur_chan = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
103
    int to_cur_chan = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
104
    int exitpdscondition;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
105
    int exitdapcondition;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
106
    time_t timeout_for_channel;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
107

Matteo Quintiliani's avatar
Matteo Quintiliani committed
108
    int span_interval = 10;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
109
110
    int time_to_sleep = 0;

111
112
    char str_start_time[200];
    char str_end_time[200];
Matteo Quintiliani's avatar
Matteo Quintiliani committed
113
    char str_pd_time[200];
114
115
116

    NMXP_MSG_SERVER type;
    void *buffer;
117
    int32_t length;
118
119
    int ret;

Matteo Quintiliani's avatar
Matteo Quintiliani committed
120
121
    int recv_errno = 0;

122
    char filename[500];
123
    char station_code[20], channel_code[20], network_code[20];
124
125
126
127
128
129
130
131

    NMXP_DATA_PROCESS *pd;

#ifdef HAVE_LIBMSEED
    /* Init mini-SEED variables */
    nmxp_data_seed_init(&data_seed);
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
132
#ifndef HAVE_WINDOWS_H
Matteo Quintiliani's avatar
Matteo Quintiliani committed
133
134
135
136
137
138
139
    /* Signal handling, use POSIX calls with standardized semantics */
    struct sigaction sa;

    sa.sa_handler = clientDummyHandler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGALRM, &sa, NULL);
140

Matteo Quintiliani's avatar
Matteo Quintiliani committed
141
142
143
144
145
146
147
148
149
150
151
    sa.sa_handler = clientShutdown;
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL); 
    sigaction(SIGTERM, &sa, NULL);

    sa.sa_handler = SIG_IGN;
    sigaction(SIGHUP, &sa, NULL);
    sigaction(SIGPIPE, &sa, NULL); 
#endif

    /* Default is normal output */
152
    nmxp_log(NMXP_LOG_SET, NMXP_LOG_D_NULL);
153
154
155
156
157
158

    /* Initialize params from argument values */
    if(nmxptool_getopt_long(argc, argv, &params) != 0) {
	return 1;
    }

159
    if(params.ew_configuration_file) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
160
161

#ifdef HAVE_EARTHWORMOBJS
162
163
	nmxp_log_init(nmxptool_ew_logit_msg, nmxptool_ew_logit_err);

Matteo Quintiliani's avatar
Matteo Quintiliani committed
164
	nmxptool_ew_configure(argv, &params);
165

Matteo Quintiliani's avatar
Matteo Quintiliani committed
166
167
168
169
170
	/* Check consistency of params */
	if(nmxptool_check_params(&params) != 0) {
	    return 1;
	}
#endif
171

172
    } else {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
173

Matteo Quintiliani's avatar
Matteo Quintiliani committed
174
175
	nmxp_log_init(nmxp_log_stdout, nmxp_log_stderr);

176
177
178
179
180
	/* Check consistency of params */
	if(nmxptool_check_params(&params) != 0) {
	    return 1;
	}

181
182
	/* List available channels on server */
	if(params.flag_listchannels) {
183

184
	    nmxp_getMetaChannelList(params.hostname, params.portnumberdap, NMXP_DATA_TIMESERIES, params.flag_request_channelinfo);
185
	    return 1;
186

187
188
189
190
	} else if(params.flag_listchannelsnaqs) {

	    channelList = nmxp_getAvailableChannelList(params.hostname, params.portnumberpds, NMXP_DATA_TIMESERIES);
	    nmxp_chan_print_channelList(channelList);
191
	    return 1;
192

193
194
	}
    }
195

196
197
198
    nmxp_log(NMXP_LOG_SET, params.verbose_level);
    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_ANY, "verbose_level %d\n", params.verbose_level);

199
200
    /* Get list of available channels and get a subset list of params.channels */
    channelList = nmxp_getAvailableChannelList(params.hostname, params.portnumberpds, NMXP_DATA_TIMESERIES);
201
    channelList_subset = nmxp_chan_subset(channelList, NMXP_DATA_TIMESERIES, params.channels, CURRENT_NETWORK);
202
203
204

    /* Check if some channel already exists */
    if(channelList_subset->number <= 0) {
205
	nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CHANNEL, "Channels not found!\n");
206
	return 1;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
207
    } else {
208
	nmxp_chan_print_netchannelList(channelList_subset);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
209

210
	nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_CHANNEL, "Init channelListSeq.\n");
211
212
213
214
215
216

	/* init channelListSeq */
	channelListSeq = (NMXPTOOL_CHAN_SEQ *) malloc(sizeof(NMXPTOOL_CHAN_SEQ) * channelList_subset->number);
	for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
	    channelListSeq[i_chan].significant = 0;
	    channelListSeq[i_chan].last_time = 0.0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
217
	    channelListSeq[i_chan].last_time_call_raw_stream = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
218
	    channelListSeq[i_chan].x_1 = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
219
	    nmxp_raw_stream_init(&(channelListSeq[i_chan].raw_stream_buffer), params.max_tolerable_latency, params.timeoutrecv);
220
221
	}

Matteo Quintiliani's avatar
Matteo Quintiliani committed
222
#ifdef HAVE_LIBMSEED
223
	nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_EXTRA, "Init mini-SEED record list.\n");
224
225

	/* Init mini-SEED record list */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
226
	for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
227

228
	    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_EXTRA, "Init mini-SEED record for %s\n", channelList_subset->channel[i_chan].name);
229
230
231
232

	    msr_list_chan[i_chan] = msr_init(NULL);

	    /* Separate station_code and channel_code */
233
	    if(nmxp_chan_cpy_sta_chan(channelList_subset->channel[i_chan].name, station_code, channel_code, network_code)) {
234

235
		nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_EXTRA, "%s.%s.%s\n", NETCODE_OR_CURRENT_NETWORK, station_code, channel_code);
236

237
		strcpy(msr_list_chan[i_chan]->network, NETCODE_OR_CURRENT_NETWORK);
238
239
240
241
242
243
244
		strcpy(msr_list_chan[i_chan]->station, station_code);
		strcpy(msr_list_chan[i_chan]->channel, channel_code);

		msr_list_chan[i_chan]->reclen = 512;         /* byte record length */
		msr_list_chan[i_chan]->encoding = DE_STEIM1;  /* Steim 1 compression */

	    } else {
245
		nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CHANNEL, "Channels %s error in format!\n");
246
247
248
		return 1;
	    }

Matteo Quintiliani's avatar
Matteo Quintiliani committed
249
250
	}
#endif
251

252
253
254
255
256
257
258
259
    }

    /* Free the complete channel list */
    if(channelList) {
	free(channelList);
	channelList = NULL;
    }

260
    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_CONNFLOW, "Starting comunication.\n");
Matteo Quintiliani's avatar
Matteo Quintiliani committed
261

262
    /* TODO condition starting DAP or PDS */
263
264
265
    if( (params.start_time != 0   &&   params.end_time != 0)
	    || params.delay > 0
	    ) {
266

Matteo Quintiliani's avatar
Matteo Quintiliani committed
267
	if(params.delay > 0) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
268
	    params.start_time = ((time(NULL) - params.delay - span_interval) / 10) * 10;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
269
270
271
	    params.end_time = params.start_time + span_interval;
	}

272

273
274
275
	/* ************************************************************** */
	/* Start subscription protocol "DATA ACCESS PROTOCOL" version 1.0 */
	/* ************************************************************** */
276

277
278
	/* DAP Step 1: Open a socket */
	if( (naqssock = nmxp_openSocket(params.hostname, params.portnumberdap)) == NMXP_SOCKET_ERROR) {
279
	    nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "Error opening socket!\n");
280
281
	    return 1;
	}
282

283
284
	/* DAP Step 2: Read connection time */
	if(nmxp_readConnectionTime(naqssock, &connection_time) != NMXP_SOCKET_OK) {
285
	    nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "Error reading connection time from server!\n");
286
287
	    return 1;
	}
288

289
290
	/* DAP Step 3: Send a ConnectRequest */
	if(nmxp_sendConnectRequest(naqssock, params.datas_username, params.datas_password, connection_time) != NMXP_SOCKET_OK) {
291
	    nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "Error sending connect request!\n");
292
293
294
295
296
	    return 1;
	}

	/* DAP Step 4: Wait for a Ready message */
	if(nmxp_waitReady(naqssock) != NMXP_SOCKET_OK) {
297
	    nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "Error waiting Ready message!\n");
298
299
	    return 1;
	}
300

301
302
303
304
	exitdapcondition = 1;

	while(exitdapcondition) {

305
	nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_EXTRA, "start_time = %d - end_time = %d\n", params.start_time, params.end_time);
306

307
308
309
	/* Start loop for sending requests */
	i_chan=0;
	request_SOCKET_OK = NMXP_SOCKET_OK;
310

311
	while(request_SOCKET_OK == NMXP_SOCKET_OK  &&  i_chan < channelList_subset->number) {
312

313
	    /* DAP Step 5: Send Data Request */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
314
	    request_SOCKET_OK = nmxp_sendDataRequest(naqssock, channelList_subset->channel[i_chan].key, (double) params.start_time, (double) params.end_time);
315

316
	    if(request_SOCKET_OK == NMXP_SOCKET_OK) {
317

318
319
320
321
322
		nmxp_data_to_str(str_start_time, params.start_time);
		nmxp_data_to_str(str_end_time, params.end_time);
		nmxptool_str_time_to_filename(str_start_time);
		nmxptool_str_time_to_filename(str_end_time);

323
324
		if(params.flag_writefile) {
		    /* Open output file */
325
326
327
328
329
330
331
332
333
334
335
336
337
		    if(nmxp_chan_cpy_sta_chan(channelList_subset->channel[i_chan].name, station_code, channel_code, network_code)) {
			sprintf(filename, "%s.%s.%s_%s_%s.nmx",
				NETCODE_OR_CURRENT_NETWORK,
				station_code,
				channel_code,
				str_start_time,
				str_end_time);
		    } else {
			sprintf(filename, "%s_%s_%s.nmx",
				channelList_subset->channel[i_chan].name,
				str_start_time,
				str_end_time);
		    }
338

339
340
		    outfile = fopen(filename, "w");
		    if(!outfile) {
341
			nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_EXTRA, "Can not to open file %s!", filename);
342
		    }
343
344
345
		}

#ifdef HAVE_LIBMSEED
346
347
		if(params.flag_writeseed) {
		    /* Open output Mini-SEED file */
348
349
350
351
352
353
354
355
356
357
358
359
360
		    if(nmxp_chan_cpy_sta_chan(channelList_subset->channel[i_chan].name, station_code, channel_code, network_code)) {
			sprintf(data_seed.filename_mseed, "%s.%s.%s_%s_%s.miniseed",
				NETCODE_OR_CURRENT_NETWORK,
				station_code,
				channel_code,
				str_start_time,
				str_end_time);
		    } else {
			sprintf(filename, "%s_%s_%s.miniseed",
				channelList_subset->channel[i_chan].name,
				str_start_time,
				str_end_time);
		    }
361

362
363
		    data_seed.outfile_mseed = fopen(data_seed.filename_mseed, "w");
		    if(!data_seed.outfile_mseed) {
364
			nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_EXTRA, "Can not to open file %s!", data_seed.filename_mseed);
365
366
367
		    }
		}
#endif
368

369
370
371
		if(params.flag_writefile  &&  outfile) {
		    /* Compute SNCL line */

372
		    /* Separate station_code_old_way and channel_code_old_way */
373
		    if(nmxp_chan_cpy_sta_chan(channelList_subset->channel[i_chan].name, station_code, channel_code, network_code)) {
374
375
376
			/* Write SNCL line */
			fprintf(outfile, "%s.%s.%s.%s\n",
				station_code,
377
				NETCODE_OR_CURRENT_NETWORK,
378
379
				channel_code,
				(params.location)? params.location : "");
380
		    }
381
382
383

		}

384
		/* DAP Step 6: Receive Data until receiving a Ready message */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
385
		ret = nmxp_receiveMessage(naqssock, &type, &buffer, &length, 0, &recv_errno);
386
		nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_CONNFLOW, "ret = %d, type = %d\n", ret, type);
387

388
		while(ret == NMXP_SOCKET_OK   &&    type != NMXP_MSG_READY) {
389

390
		    /* Process a packet and return value in NMXP_DATA_PROCESS structure */
391
		    pd = nmxp_processCompressedData(buffer, length, channelList_subset, NETCODE_OR_CURRENT_NETWORK);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
392
		    nmxp_data_trim(pd, params.start_time, params.end_time, 0);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
393
394

		    /* Log contents of last packet */
395
396
397
		    if(params.flag_logdata) {
			nmxp_data_log(pd);
		    }
398

399
		    /* Set cur_chan */
400
		    cur_chan = nmxp_chan_lookupKeyIndex(pd->key, channelList_subset);
401
402

		    /* Management of gaps */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
403
		    if(!channelListSeq[cur_chan].significant && pd->nSamp > 0) {
404
405
			channelListSeq[cur_chan].significant = 1;
		    } else {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
406
			if(channelListSeq[cur_chan].significant) {
407
408
			    if(nmxptool_check_and_log_gap(pd->time, channelListSeq[cur_chan].last_time, GAP_TOLLERANCE, pd->station, pd->channel)) {
				channelListSeq[cur_chan].x_1 = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
409
				nmxp_data_to_str(str_pd_time, pd->time);
410
				nmxp_log(NMXP_LOG_WARN, NMXP_LOG_D_EXTRA, "%s.%s x0 set to zero at %s!\n", pd->station, pd->channel, str_pd_time);
411
			    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
412
413
			}
		    }
414
		    if(channelListSeq[cur_chan].significant && pd->nSamp > 0) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
415
			channelListSeq[cur_chan].last_time = pd->time + ((double) pd->nSamp / (double) pd->sampRate);
416
417
		    }

418
#ifdef HAVE_LIBMSEED
419
420
		    /* Write Mini-SEED record */
		    if(params.flag_writeseed) {
421
			nmxptool_write_miniseed(pd);
422
		    }
423
#endif
424

425
#ifdef HAVE___SRC_SEEDLINK_PLUGIN_C
426
		    /* Send data to SeedLink Server */
427
		    if(params.flag_slink) {
428
			nmxptool_send_raw_depoch(pd);
429
		    }
430
431
#endif

432
433
434
		    if(params.flag_writefile  &&  outfile) {
			/* Write buffer to the output file */
			if(outfile && buffer && length > 0) {
435
			    int32_t length_int = length;
436
437
438
439
440
			    nmxp_data_swap_4b((int32_t *) &length_int);
			    fwrite(&length_int, sizeof(length_int), 1, outfile);
			    fwrite(buffer, length, 1, outfile);
			}
		    }
441

Matteo Quintiliani's avatar
Matteo Quintiliani committed
442
443
444
445
446
		    /* Store x_1 */
		    if(pd->nSamp > 0) {
			channelListSeq[cur_chan].x_1 = pd->pDataPtr[pd->nSamp-1];
		    }
		    /* Free pd->buffer */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
447
448
449
		    if(pd->buffer) {
			free(pd->buffer);
			pd->buffer = NULL;
450
		    }
451
452

		    /* Receive Data */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
453
		    ret = nmxp_receiveMessage(naqssock, &type, &buffer, &length, 0, &recv_errno);
454
		    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_CONNFLOW, "ret = %d, type = %d\n", ret, type);
455
456
		}

457
458
459
		if(params.flag_writefile  &&  outfile) {
		    /* Close output file */
		    fclose(outfile);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
460
		    outfile = NULL;
461
462
		}

463
464
465
466
#ifdef HAVE_LIBMSEED
		if(params.flag_writeseed  &&  data_seed.outfile_mseed) {
		    /* Close output Mini-SEED file */
		    fclose(data_seed.outfile_mseed);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
467
		    data_seed.outfile_mseed = NULL;
468
469
		}
#endif
470
471

	    }
472
473
474
	    i_chan++;
	}
	/* DAP Step 7: Repeat steps 5 and 6 for each data request */
475

Matteo Quintiliani's avatar
Matteo Quintiliani committed
476
	if(params.delay > 0) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
477
478
	    time_to_sleep = (params.end_time - params.start_time) - (time(NULL) - (params.start_time + params.delay + span_interval));
	    if(time_to_sleep >= 0) {
479
		nmxp_sleep(time_to_sleep);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
480
	    } else {
481
		nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "time to sleep %d sec.\n", time_to_sleep);
482
		nmxp_sleep(3);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
483
	    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
484
485
486
487
488
489
	    params.start_time = params.end_time;
	    params.end_time = params.start_time + span_interval;
	} else {
	    exitdapcondition = 0;
	}

490

Matteo Quintiliani's avatar
Matteo Quintiliani committed
491
    } /* END while(exitdapcondition) */
492

493
494
495
496
497
498
499
500
501
502
503
	/* DAP Step 8: Send a Terminate message (optional) */
	nmxp_sendTerminateSubscription(naqssock, NMXP_SHUTDOWN_NORMAL, "Bye!");

	/* DAP Step 9: Close the socket */
	nmxp_closeSocket(naqssock);

	/* ************************************************************ */
	/* End subscription protocol "DATA ACCESS PROTOCOL" version 1.0 */
	/* ************************************************************ */


504

Matteo Quintiliani's avatar
Matteo Quintiliani committed
505
    } else {
506

507
508
	if(params.stc == -1) {

509

510
511
512
513
	    if(params.flag_logdata) {
		p_func_pd[n_func_pd++] = nmxptool_print_seq_no;
	    }

514
515
516
517
518
519
520
521
522
523
524
525
526
#ifdef HAVE_LIBMSEED
	    /* Write Mini-SEED record */
	    if(params.flag_writeseed) {
		p_func_pd[n_func_pd++] = nmxptool_write_miniseed;
	    }
#endif

#ifdef HAVE___SRC_SEEDLINK_PLUGIN_C
	    /* Send data to SeedLink Server */
	    if(params.flag_slink) {
		p_func_pd[n_func_pd++] = nmxptool_send_raw_depoch;
	    }
#endif
Matteo Quintiliani's avatar
Matteo Quintiliani committed
527
528
529
530
531
532
533

#ifdef HAVE_EARTHWORMOBJS
	    if(params.ew_configuration_file) {
		p_func_pd[n_func_pd++] = nmxptool_ew_nmx2ew;
	    }
#endif

534
	}
535

536
537
538
539
540
541
542
543
544
	/* ************************************************************* */
	/* Start subscription protocol "PRIVATE DATA STREAM" version 1.4 */
	/* ************************************************************* */

	/* PDS Step 1: Open a socket */
	naqssock = nmxp_openSocket(params.hostname, params.portnumberpds);

	if(naqssock == NMXP_SOCKET_ERROR) {
	    return 1;
545
546
	}

547
548
549
550
551
552
553
554
555
556
557
558
559
	/* PDS Step 2: Send a Connect */
	if(nmxp_sendConnect(naqssock) != NMXP_SOCKET_OK) {
	    printf("Error on sendConnect()\n");
	    return 1;
	}

	/* PDS Step 3: Receive ChannelList */
	if(nmxp_receiveChannelList(naqssock, &channelList) != NMXP_SOCKET_OK) {
	    printf("Error on receiveChannelList()\n");
	    return 1;
	}

	/* Get a subset of channel from arguments */
560
	channelList_subset = nmxp_chan_subset(channelList, NMXP_DATA_TIMESERIES, params.channels, CURRENT_NETWORK);
561
562
563


	/* PDS Step 4: Send a Request Pending (optional) */
564
565


566
567
568
569
570
571
	/* PDS Step 5: Send AddChannels */
	/* Request Data */
	nmxp_sendAddTimeSeriesChannel(naqssock, channelList_subset, params.stc, params.rate, (params.flag_buffered)? NMXP_BUFFER_YES : NMXP_BUFFER_NO);

	/* PDS Step 6: Repeat until finished: receive and handle packets */

Matteo Quintiliani's avatar
Matteo Quintiliani committed
572
573
574
575
#ifdef HAVE_LIBMSEED
	if(params.flag_writeseed) {
	    /* Open output Mini-SEED file */
	    sprintf(data_seed.filename_mseed, "%s.realtime.miniseed",
576
		    CURRENT_NETWORK);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
577
578
579

	    data_seed.outfile_mseed = fopen(data_seed.filename_mseed, "w");
	    if(!data_seed.outfile_mseed) {
580
		nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_EXTRA, "Can not to open file %s!", data_seed.filename_mseed);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
581
	    } else {
582
		nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_EXTRA, "Opened file %s!\n", data_seed.filename_mseed);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
583
584
585
586
	    }
	}
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
587
588
589
	// TODO
	exitpdscondition = 1;

Matteo Quintiliani's avatar
Matteo Quintiliani committed
590
591
592
593
594
595
#ifdef HAVE_EARTHWORMOBJS
	if(params.ew_configuration_file) {
	    nmxptool_ew_attach();
	}
#endif

596
	
Matteo Quintiliani's avatar
Matteo Quintiliani committed
597
	while(exitpdscondition) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
598

599
	    /* Process Compressed or Decompressed Data */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
600
	    pd = nmxp_receiveData(naqssock, channelList_subset, NETCODE_OR_CURRENT_NETWORK, params.timeoutrecv, &recv_errno);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
601

602
	    if(recv_errno == 0) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
603
604
605
		// TODO
		exitpdscondition = 1;
	    } else {
606
		nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CONNFLOW, "Error receiving data. pd=%p recv_errno=%d\n", pd, recv_errno);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
607
608
		exitpdscondition = 0;
	    }
609

Matteo Quintiliani's avatar
Matteo Quintiliani committed
610
	    /* Log contents of last packet */
611
612
613
	    if(params.flag_logdata) {
		nmxp_data_log(pd);
	    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
614

Matteo Quintiliani's avatar
Matteo Quintiliani committed
615
	    if(pd) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
616
617
		/* Set cur_chan */
		cur_chan = nmxp_chan_lookupKeyIndex(pd->key, channelList_subset);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
618
	    }
619

620
621
	    /* Manage Raw Stream */
	    if(params.stc == -1) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
622
623
624
625
626
627
628
629
630

		/* cur_char is computed only for pd != NULL */
		if(pd) {
		    nmxp_raw_stream_manage(&(channelListSeq[cur_chan].raw_stream_buffer), pd, p_func_pd, n_func_pd);
		    channelListSeq[cur_chan].last_time_call_raw_stream = nmxp_data_gmtime_now();
		}

		/* Check timeout for other channels */
		if(params.timeoutrecv > 0) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
631
		    exitpdscondition = 1;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
632
633
634
635
636
		    to_cur_chan = 0;
		    while(to_cur_chan < channelList_subset->number) {
			timeout_for_channel = nmxp_data_gmtime_now() - channelListSeq[to_cur_chan].last_time_call_raw_stream;
			if(channelListSeq[to_cur_chan].last_time_call_raw_stream != 0
				&& timeout_for_channel >= params.timeoutrecv) {
637
			    nmxp_log(NMXP_LOG_WARN, NMXP_LOG_D_DOD, "Timeout for channel %s (%d sec.)\n",
Matteo Quintiliani's avatar
Matteo Quintiliani committed
638
639
640
641
642
643
644
645
				    channelList_subset->channel[to_cur_chan].name, timeout_for_channel);
			    nmxp_raw_stream_manage(&(channelListSeq[to_cur_chan].raw_stream_buffer), NULL, p_func_pd, n_func_pd);
			    channelListSeq[to_cur_chan].last_time_call_raw_stream = nmxp_data_gmtime_now();
			}
			to_cur_chan++;
		    }
		}

646
	    } else {
647

Matteo Quintiliani's avatar
Matteo Quintiliani committed
648
	    if(pd) {
649
650
651
652
653
654
655
		/* Management of gaps */
		if(!channelListSeq[cur_chan].significant && pd->nSamp > 0) {
		    channelListSeq[cur_chan].significant = 1;
		} else {
		    if(channelListSeq[cur_chan].significant) {
			if(nmxptool_check_and_log_gap(pd->time, channelListSeq[cur_chan].last_time, GAP_TOLLERANCE, pd->station, pd->channel)) {
			    channelListSeq[cur_chan].x_1 = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
656
			    nmxp_data_to_str(str_pd_time, pd->time);
657
			    nmxp_log(NMXP_LOG_WARN, NMXP_LOG_D_EXTRA, "%s.%s x0 set to zero at %s!\n", pd->station, pd->channel, str_pd_time);
658
			}
659
		    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
660
		}
661
662
663
		if(channelListSeq[cur_chan].significant && pd->nSamp > 0) {
		    channelListSeq[cur_chan].last_time = pd->time + ((double) pd->nSamp / (double) pd->sampRate);
		}
664

Matteo Quintiliani's avatar
Matteo Quintiliani committed
665
666

#ifdef HAVE_LIBMSEED
667
668
669
670
		/* Write Mini-SEED record */
		if(params.flag_writeseed) {
		    nmxptool_write_miniseed(pd);
		}
Matteo Quintiliani's avatar
Matteo Quintiliani committed
671
672
#endif

673
#ifdef HAVE___SRC_SEEDLINK_PLUGIN_C
674
675
676
677
		/* Send data to SeedLink Server */
		if(params.flag_slink) {
		    nmxptool_send_raw_depoch(pd);
		}
678
#endif
679
	    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
680
	    }
681

Matteo Quintiliani's avatar
Matteo Quintiliani committed
682
	    if(pd) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
683
684
685
686
687
	    /* Store x_1 */
	    if(pd->nSamp > 0) {
		channelListSeq[cur_chan].x_1 = pd->pDataPtr[pd->nSamp-1];
	    }
	    /* Free pd->buffer */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
688
689
690
691
	    if(pd->buffer) {
		free(pd->buffer);
		pd->buffer = NULL;
	    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
692
	    }
Matteo Quintiliani's avatar
Matteo Quintiliani committed
693

Matteo Quintiliani's avatar
Matteo Quintiliani committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
#ifdef HAVE_EARTHWORMOBJS
	    if(params.ew_configuration_file) {

		/* Check if we are being asked to terminate */
		if( nmxptool_ew_check_flag_terminate() ) {
		    logit ("t", "nmxptool terminating on request\n");
		    exitpdscondition = 0;
		}

		/* Check if we need to send heartbeat message */
		nmxptool_ew_send_heartbeat_if_needed();

	    }
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
709
	}
Matteo Quintiliani's avatar
Matteo Quintiliani committed
710
711
712
	
	/* Flush raw data stream for each channel */
	flushing_raw_data_stream();
713
	saving_channel_states();
Matteo Quintiliani's avatar
Matteo Quintiliani committed
714

Matteo Quintiliani's avatar
Matteo Quintiliani committed
715
716
717
718
719
720
#ifdef HAVE_EARTHWORMOBJS
	if(params.ew_configuration_file) {
	    nmxptool_ew_detach();
	}
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
721
722
723
724
725
726
727
#ifdef HAVE_LIBMSEED
	if(params.flag_writeseed  &&  data_seed.outfile_mseed) {
	    /* Close output Mini-SEED file */
	    fclose(data_seed.outfile_mseed);
	}
#endif

728
729
730
731
732
733
734
735
736
737
738

	/* PDS Step 7: Send Terminate Subscription */
	nmxp_sendTerminateSubscription(naqssock, NMXP_SHUTDOWN_NORMAL, "Good Bye!");

	/* PDS Step 8: Close the socket */
	nmxp_closeSocket(naqssock);

	/* *********************************************************** */
	/* End subscription protocol "PRIVATE DATA STREAM" version 1.4 */
	/* *********************************************************** */

739
740


741
    }
742

743
744
745
746
747
748
749
750
751
752
#ifdef HAVE_LIBMSEED
	if(*msr_list_chan) {
	    for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
		if(msr_list_chan[i_chan]) {
		    msr_free(&(msr_list_chan[i_chan]));
		}
	    }
	}
#endif

753
	for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
754
	    nmxp_raw_stream_free(&(channelListSeq[i_chan].raw_stream_buffer));
755
756
	}

757
758
759
760
761
762
763
764
765
	if(channelListSeq) {
	    free(channelListSeq);
	}

	/* This has to be tha last */
	if(channelList_subset) {
	    free(channelList_subset);
	}

766
    return 0;
767
} /* End MAIN */
768
769


770
771
772
773
774
775
static void saving_channel_states() {
    int to_cur_chan;
    char last_time_str[30];
    char raw_last_sample_time_str[30];
    char state_line_str[1000];

776
777
778
779
780
    if(params.statefile) {
	FILE *fstatefile = fopen(params.statefile, "w");
	if(fstatefile == NULL) {
	    nmxp_log(NMXP_LOG_NORM_NO, NMXP_LOG_D_ANY, "Unable to write channel states into %s!\n", params.statefile);
	}
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
	/* Save state for each channel */
	// if(params.stc == -1)
	to_cur_chan = 0;
	while(to_cur_chan < channelList_subset->number) {
	    nmxp_data_to_str(last_time_str, channelListSeq[to_cur_chan].last_time);
	    nmxp_data_to_str(raw_last_sample_time_str, channelListSeq[to_cur_chan].raw_stream_buffer.last_sample_time);
	    sprintf(state_line_str, "%-14s %16.4f %s %16.4f %s",
		    channelList_subset->channel[to_cur_chan].name,
		    channelListSeq[to_cur_chan].last_time, last_time_str,
		    channelListSeq[to_cur_chan].raw_stream_buffer.last_sample_time, raw_last_sample_time_str
		   );
	    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_CHANNEL, "%s\n", state_line_str);
	    if(fstatefile) {
		fprintf(fstatefile, "%s\n", state_line_str);
	    }
	    to_cur_chan++;
	}
799
	if(fstatefile) {
800
	    fclose(fstatefile);
801
802
803
804
	}
    }
}

805

Matteo Quintiliani's avatar
Matteo Quintiliani committed
806
807
808
809
810
811
812
static void flushing_raw_data_stream() {
    int to_cur_chan;

    /* Flush raw data stream for each channel */
    if(params.stc == -1) {
	to_cur_chan = 0;
	while(to_cur_chan < channelList_subset->number) {
813
	    nmxp_log(NMXP_LOG_WARN, NMXP_LOG_D_ANY, "Flushing data for channel %s\n",
Matteo Quintiliani's avatar
Matteo Quintiliani committed
814
815
816
817
818
819
		    channelList_subset->channel[to_cur_chan].name);
	    nmxp_raw_stream_manage(&(channelListSeq[to_cur_chan].raw_stream_buffer), NULL, p_func_pd, n_func_pd);
	    to_cur_chan++;
	}
    }
}
820

Matteo Quintiliani's avatar
Matteo Quintiliani committed
821
#ifndef HAVE_WINDOWS_H
Matteo Quintiliani's avatar
Matteo Quintiliani committed
822
823
/* Do any needed cleanup and exit */
static void clientShutdown(int sig) {
824

825
    nmxp_log(NMXP_LOG_NORM, NMXP_LOG_D_ANY, "Program interrupted!\n");
Matteo Quintiliani's avatar
Matteo Quintiliani committed
826

Matteo Quintiliani's avatar
Matteo Quintiliani committed
827
    flushing_raw_data_stream();
828
    saving_channel_states();
Matteo Quintiliani's avatar
Matteo Quintiliani committed
829

Matteo Quintiliani's avatar
Matteo Quintiliani committed
830
831
832
833
834
    if(params.flag_writefile  &&  outfile) {
	/* Close output file */
	fclose(outfile);
    }

835
836
837
838
839
840
#ifdef HAVE_EARTHWORMOBJS
	if(params.ew_configuration_file) {
	    nmxptool_ew_detach();
	}
#endif

Matteo Quintiliani's avatar
Matteo Quintiliani committed
841
842
843
844
845
846
847
#ifdef HAVE_LIBMSEED
    if(params.flag_writeseed  &&  data_seed.outfile_mseed) {
	/* Close output Mini-SEED file */
	fclose(data_seed.outfile_mseed);
    }
#endif

848

Matteo Quintiliani's avatar
Matteo Quintiliani committed
849
850
851
852
853
854
    /* PDS Step 7: Send Terminate Subscription */
    nmxp_sendTerminateSubscription(naqssock, NMXP_SHUTDOWN_NORMAL, "Good Bye!");

    /* PDS Step 8: Close the socket */
    nmxp_closeSocket(naqssock);

855

Matteo Quintiliani's avatar
Matteo Quintiliani committed
856
857
858

    /* Free the complete channel list */
    if(channelList) {
Matteo Quintiliani's avatar
Matteo Quintiliani committed
859
	free(channelList);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
860
	channelList = NULL;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
861
    }
862

863
864
    int i_chan = 0;

865
866
867
868
869
870
871
872
873
874
#ifdef HAVE_LIBMSEED
    if(*msr_list_chan) {
	for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
	    if(msr_list_chan[i_chan]) {
		msr_free(&(msr_list_chan[i_chan]));
	    }
	}
    }
#endif

875
    for(i_chan = 0; i_chan < channelList_subset->number; i_chan++) {
876
	nmxp_raw_stream_free(&(channelListSeq[i_chan].raw_stream_buffer));
877
878
    }

879
880
881
882
883
    if(channelListSeq) {
	free(channelListSeq);
    }

    /* This has to be the last */
Matteo Quintiliani's avatar
Matteo Quintiliani committed
884
885
886
887
888
889
    if(channelList_subset == NULL) {
	free(channelList_subset);
    }

    exit( sig );
} /* End of clientShutdown() */
890

Matteo Quintiliani's avatar
Matteo Quintiliani committed
891
892
893
894

/* Empty signal handler routine */
static void clientDummyHandler(int sig) {
}
Matteo Quintiliani's avatar
Matteo Quintiliani committed
895

Matteo Quintiliani's avatar
Matteo Quintiliani committed
896
897
#endif /* HAVE_WINDOWS_H */

Matteo Quintiliani's avatar
Matteo Quintiliani committed
898
899
900



901
#ifdef HAVE_LIBMSEED
Matteo Quintiliani's avatar
Matteo Quintiliani committed
902
903
904
905
906
int nmxptool_write_miniseed(NMXP_DATA_PROCESS *pd) {
    int cur_chan;
    int ret = 0;
    if( (cur_chan = nmxp_chan_lookupKeyIndex(pd->key, channelList_subset)) != -1) {

907
	ret = nmxp_data_msr_pack(pd, &data_seed, msr_list_chan[cur_chan]);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
908
909

    } else {
910
	nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_CHANNEL, "Key %d not found in channelList_subset!\n", pd->key);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
911
912
913
    }
    return ret;
}
914
#endif
Matteo Quintiliani's avatar
Matteo Quintiliani committed
915

916
917
int nmxptool_print_seq_no(NMXP_DATA_PROCESS *pd) {
    int ret = 0;
Matteo Quintiliani's avatar
Matteo Quintiliani committed
918
919
    char str_time[200];
    nmxp_data_to_str(str_time, pd->time);
920

921
    nmxp_log(NMXP_LOG_NORM_NO, NMXP_LOG_D_ANY, "Process %s.%s.%s %2d %d %d %s %dpts lat. %.1fs\n",
Matteo Quintiliani's avatar
Matteo Quintiliani committed
922
	    pd->network,
Matteo Quintiliani's avatar
Matteo Quintiliani committed
923
924
	    pd->station,
	    pd->channel,
925
926
	    pd->packet_type,
	    pd->seq_no,
927
	    pd->oldest_seq_no,
Matteo Quintiliani's avatar
Matteo Quintiliani committed
928
929
	    str_time,
	    pd->nSamp,
930
	    nmxp_data_latency(pd)
931
932
933
934
935
	    );

    return ret;
}

936

Matteo Quintiliani's avatar
Matteo Quintiliani committed
937
#ifdef HAVE___SRC_SEEDLINK_PLUGIN_C
Matteo Quintiliani's avatar
Matteo Quintiliani committed
938
939
940
941
942
943
944
945
int nmxptool_send_raw_depoch(NMXP_DATA_PROCESS *pd) {
    /* TODO Set values */
    const int usec_correction = 0;
    const int timing_quality = 100;

    return send_raw_depoch(pd->station, pd->channel, pd->time, usec_correction, timing_quality,
	    pd->pDataPtr, pd->nSamp);
}
Matteo Quintiliani's avatar
Matteo Quintiliani committed
946
#endif
Matteo Quintiliani's avatar
Matteo Quintiliani committed
947
948
949
950



int nmxptool_check_and_log_gap(double time1, double time2, const double gap_tollerance, const char *station, const char *channel) {
951
952
    char str_time1[200];
    char str_time2[200];
Matteo Quintiliani's avatar
Matteo Quintiliani committed
953
954
    int ret = 0;
    double gap = time1 - time2 ;
955
956
    nmxp_data_to_str(str_time1, time1);
    nmxp_data_to_str(str_time2, time2);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
957
    if(gap > gap_tollerance) {
958
	nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_GAP, "Gap %.2f sec. for %s.%s from %s to %s!\n", gap, station, channel, str_time2, str_time1);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
959
960
	ret = 1;
    } else if (gap < -gap_tollerance) {
961
	nmxp_log(NMXP_LOG_ERR, NMXP_LOG_D_GAP, "Overlap %.2f sec. for %s.%s from %s to %s!\n", gap, station, channel, str_time1, str_time2);
Matteo Quintiliani's avatar
Matteo Quintiliani committed
962
963
964
965
	ret = 1;
    }
    return ret;
}
966

Matteo Quintiliani's avatar
Matteo Quintiliani committed
967
void nmxptool_str_time_to_filename(char *str_time) {
968
969
970
971
972
973
974
975
976
977
978
979
980
    int i;
    for(i=0; i<strlen(str_time); i++) {
	if(   (str_time[i] >= 'A'  &&  str_time[i] <= 'Z')
		|| (str_time[i] >= 'a'  &&  str_time[i] <= 'z')
		|| (str_time[i] >= '0'  &&  str_time[i] <= '9')
		|| (str_time[i] == '_')
	  ) {
	    /* Do nothing */
	} else {
	    str_time[i] = '.';
	}
    }
}