/*************************************************************************** * sl_setuniparams: * * Set the parameters for a uni-station mode connection for the * given SLCD struct. If the stream entry already exists, overwrite * the previous settings. * Also sets the multistation flag to 0 (false). * * - selectors should be 0 if there are none to use * - seqnum should be -1 to start at the next data * - timestamp should be 0 if it should not be used * * Returns 0 if successfully added or -1 on error. ***************************************************************************/ int sl_setuniparams (SLCD * slconn, const char * selectors, int seqnum, const char * timestamp) { SLstream *newstream; newstream = slconn->streams; if ( newstream == NULL ) { newstream = (SLstream *) malloc (sizeof(SLstream)); if ( newstream == NULL ) { sl_log_r (slconn, 2, 0, "sl_setuniparams(): error allocating memory\n"); return -1; } } else if ( strcmp (newstream->net, UNINETWORK) != 0 || strcmp (newstream->sta, UNISTATION) != 0) { sl_log_r (slconn, 2, 0, "sl_setuniparams(): multi-station mode already configured!\n"); return -1; } newstream->net = UNINETWORK; newstream->sta = UNISTATION; if ( selectors == 0 || selectors == NULL ) newstream->selectors = 0; else newstream->selectors = strdup(selectors); newstream->seqnum = seqnum; if ( timestamp == 0 || timestamp == NULL ) newstream->timestamp[0] = '\0'; else strncpy(newstream->timestamp, timestamp, 20); newstream->next = NULL; slconn->streams = newstream; slconn->multistation = 0; return 0; } /* End of sl_setuniparams() */
/*************************************************************************** * sl_terminate: * * Set the terminate flag in the SLCD. ***************************************************************************/ void sl_terminate (SLCD * slconn) { sl_log_r (slconn, 1, 1, "Terminating connection\n"); slconn->terminate = 1; } /* End of sl_terminate() */
/*************************************************************************** * sl_request_info: * * Add an INFO request to the SeedLink Connection Description. * * Returns 0 if successful and -1 if error. ***************************************************************************/ int sl_request_info (SLCD * slconn, const char * infostr) { if ( slconn->info != NULL ) { sl_log_r (slconn, 2, 0, "Cannot make '%.15s' INFO request, one is already pending\n", infostr); return -1; } else { slconn->info = infostr; return 0; } } /* End of sl_request_info() */
/*************************************************************************** * slp_dtime: * * Get the current time from the system as Unix/POSIX epoch time with double * precision. On the WIN32 platform this function has millisecond * resulution, on *nix platforms this function has microsecond resolution. * * Return a double precision Unix/POSIX epoch time. ***************************************************************************/ double slp_dtime (void) { #if defined(SLP_WIN32) static const __int64 SECS_BETWEEN_EPOCHS = 11644473600; static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */ __int64 UnixTime; SYSTEMTIME SystemTime; FILETIME FileTime; double depoch; GetSystemTime(&SystemTime); SystemTimeToFileTime(&SystemTime, &FileTime); /* Get the full win32 epoch value, in 100ns */ UnixTime = ((__int64)FileTime.dwHighDateTime << 32) + FileTime.dwLowDateTime; /* Convert to the Unix epoch */ UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS); UnixTime /= SECS_TO_100NS; /* now convert to seconds */ if ( (double)UnixTime != UnixTime ) { sl_log_r (NULL, 2, 0, "slp_dtime(): resulting value is too big for a double value\n"); } depoch = (double) UnixTime + ((double) SystemTime.wMilliseconds / 1000.0); return depoch; #else struct timeval tv; gettimeofday (&tv, (struct timezone *) 0); return ((double) tv.tv_sec + ((double) tv.tv_usec / 1000000.0)); #endif } /* End of slp_dtime() */
/*************************************************************************** * sl_read_streamlist: * * Read a list of streams and selectors from a file and add them to the * stream chain for configuring a multi-station connection. * * If 'defselect' is not NULL or 0 it will be used as the default selectors * for entries will no specific selectors indicated. * * The file is expected to be repeating lines of the form: * <NET> <STA> [selectors] * For example: * -------- * # Comment lines begin with a '#' or '*' * GE ISP BH?.D * NL HGN * MN AQU BH? HH? * -------- * * Returns the number of streams configured or -1 on error. ***************************************************************************/ int sl_read_streamlist (SLCD * slconn, const char * streamfile, const char * defselect) { char net[3]; char sta[6]; char selectors[100]; char line[100]; int streamfd; int fields; int count; int stacount; int addret; net[0] = '\0'; sta[0] = '\0'; selectors[0] = '\0'; /* Open the stream list file */ if ( (streamfd = slp_openfile (streamfile, 'r')) < 0 ) { if (errno == ENOENT) { sl_log_r (slconn, 2, 0, "could not find stream list file: %s\n", streamfile); return -1; } else { sl_log_r (slconn, 2, 0, "opening stream list file, %s\n", strerror (errno)); return -1; } } sl_log_r (slconn, 1, 1, "Reading stream list from %s\n", streamfile); count = 1; stacount = 0; while ( (sl_readline (streamfd, line, sizeof(line))) >= 0 ) { fields = sscanf (line, "%2s %5s %99[a-zA-Z0-9!?. ]\n", net, sta, selectors); /* Ignore blank or comment lines */ if ( fields < 0 || net[0] == '#' || net[0] == '*' ) continue; if ( fields < 2 ) { sl_log_r (slconn, 2, 0, "cannot parse line %d of stream list\n", count); } /* Add this stream to the stream chain */ if ( fields == 3 ) { sl_addstream (slconn, net, sta, selectors, -1, NULL); stacount++; } else { addret = sl_addstream (slconn, net, sta, defselect, -1, NULL); stacount++; } count++; } if ( stacount == 0 ) { sl_log_r (slconn, 2, 0, "no streams defined in %s\n", streamfile); } else if ( stacount > 0 ) { sl_log_r (slconn, 1, 2, "Read %d streams from %s\n", stacount, streamfile); } if ( close (streamfd) ) { sl_log_r (slconn, 2, 0, "closing stream list file, %s\n", strerror (errno)); return -1; } return count; } /* End of sl_read_streamlist() */
/*************************************************************************** * sl_parse_streamlist: * * Parse a string of streams and selectors and add them to the stream * chain for configuring a multi-station connection. * * The string should be of the following form: * "stream1[:selectors1],stream2[:selectors2],..." * * For example: * "IU_KONO:BHE BHN,GE_WLF,MN_AQU:HH?.D" * * Returns the number of streams configured or -1 on error. ***************************************************************************/ int sl_parse_streamlist (SLCD * slconn, const char * streamlist, const char * defselect) { int count = 0; int fields; const char *staselect; char *net; char *sta; SLstrlist *ringlist = NULL; /* split streamlist on ',' */ SLstrlist *reqlist = NULL; /* split ringlist on ':' */ SLstrlist *netstalist = NULL; /* split reqlist[0] on "_" */ SLstrlist *ringptr = NULL; SLstrlist *reqptr = NULL; SLstrlist *netstaptr = NULL; /* Parse the streams and selectors */ sl_strparse (streamlist, ",", &ringlist); ringptr = ringlist; while (ringptr != 0) { net = NULL; sta = NULL; staselect = NULL; fields = sl_strparse (ringptr->element, ":", &reqlist); reqptr = reqlist; /* Fill in the NET and STA fields */ if (sl_strparse (reqptr->element, "_", &netstalist) != 2) { sl_log_r (slconn, 2, 0, "not in NET_STA format: %s\n", reqptr->element); count = -1; } else { /* Point to the first element, should be a network code */ netstaptr = netstalist; if (strlen (netstaptr->element) == 0) { sl_log_r (slconn, 2, 0, "not in NET_STA format: %s\n", reqptr->element); count = -1; } net = netstaptr->element; /* Point to the second element, should be a station code */ netstaptr = netstaptr->next; if (strlen (netstaptr->element) == 0) { sl_log_r (slconn, 2, 0, "not in NET_STA format: %s\n", reqptr->element); count = -1; } sta = netstaptr->element; } if (fields > 1) { /* Selectors were included */ /* Point to the second element of reqptr, should be selectors */ reqptr = reqptr->next; if (strlen (reqptr->element) == 0) { sl_log_r (slconn, 2, 0, "empty selector: %s\n", reqptr->element); count = -1; } staselect = reqptr->element; } else /* If no specific selectors, use the default */ { staselect = defselect; } /* Add this to the stream chain */ if ( count != -1 ) { sl_addstream(slconn, net, sta, staselect, -1, 0); count++; } /* Free the netstalist (the 'NET_STA' part) */ sl_strparse (NULL, NULL, &netstalist); /* Free the reqlist (the 'NET_STA:selector' part) */ sl_strparse (NULL, NULL, &reqlist); ringptr = ringptr->next; } if ( netstalist != NULL ) { sl_strparse (NULL, NULL, &netstalist); } if ( reqlist != NULL ) { sl_strparse (NULL, NULL, &reqlist); } if ( count == 0 ) { sl_log_r (slconn, 2, 0, "no streams defined in stream list\n"); } else if ( count > 0 ) { sl_log_r (slconn, 1, 2, "Parsed %d streams from stream list\n", count); } /* Free the ring list */ sl_strparse (NULL, NULL, &ringlist); return count; } /* End of sl_parse_streamlist() */
/*************************************************************************** * sl_addstream: * * Add a new stream entry to the stream chain for the given SLCD * struct. No checking is done for duplicate streams. * * - selectors should be 0 if there are none to use * - seqnum should be -1 to start at the next data * - timestamp should be 0 if it should not be used * * Returns 0 if successfully added or -1 on error. ***************************************************************************/ int sl_addstream (SLCD * slconn, const char * net, const char * sta, const char * selectors, int seqnum, const char * timestamp) { SLstream *curstream; SLstream *newstream; SLstream *laststream = NULL; curstream = slconn->streams; /* Sanity, check for a uni-station mode entry */ if ( curstream ) { if ( strcmp (curstream->net, UNINETWORK) == 0 && strcmp (curstream->sta, UNISTATION) == 0 ) { sl_log_r (slconn, 2, 0, "sl_addstream(): uni-station mode already configured!\n"); return -1; } } /* Search the stream chain */ while ( curstream != NULL ) { laststream = curstream; curstream = curstream->next; } newstream = (SLstream *) malloc (sizeof(SLstream)); if ( newstream == NULL ) { sl_log_r (slconn, 2, 0, "sl_addstream(): error allocating memory\n"); return -1; } newstream->net = strdup(net); newstream->sta = strdup(sta); if ( selectors == 0 || selectors == NULL ) newstream->selectors = 0; else newstream->selectors = strdup(selectors); newstream->seqnum = seqnum; if ( timestamp == 0 || timestamp == NULL ) newstream->timestamp[0] = '\0'; else strncpy(newstream->timestamp, timestamp, 20); newstream->next = NULL; if ( slconn->streams == NULL ) { slconn->streams = newstream; } else if ( laststream ) { laststream->next = newstream; } slconn->multistation = 1; return 0; } /* End of sl_addstream() */
/*************************************************************************** * sl_newslcd: * * Allocate, initialze and return a pointer to a new SLCD struct. * * Returns allocated SLCD struct on success, NULL on error. ***************************************************************************/ SLCD * sl_newslcd (void) { SLCD * slconn; slconn = (SLCD *) malloc (sizeof(SLCD)); if ( slconn == NULL ) { sl_log_r (NULL, 2, 0, "new_slconn(): error allocating memory\n"); return NULL; } /* Set defaults */ slconn->streams = NULL; slconn->sladdr = NULL; slconn->begin_time = NULL; slconn->end_time = NULL; slconn->resume = 1; slconn->multistation = 0; slconn->dialup = 0; slconn->batchmode = 0; slconn->lastpkttime = 0; slconn->terminate = 0; slconn->keepalive = 0; slconn->netto = 600; slconn->netdly = 30; slconn->link = -1; slconn->info = NULL; slconn->protocol_ver = 0.0; /* Allocate the associated persistent state struct */ slconn->stat = (SLstat *) malloc (sizeof(SLstat)); if ( slconn->stat == NULL ) { sl_log_r (NULL, 2, 0, "new_slconn(): error allocating memory\n"); free (slconn); return NULL; } slconn->stat->recptr = 0; slconn->stat->sendptr = 0; slconn->stat->expect_info = 0; slconn->stat->netto_trig = -1; slconn->stat->netdly_trig = 0; slconn->stat->keepalive_trig = -1; slconn->stat->netto_time = 0.0; slconn->stat->netdly_time = 0.0; slconn->stat->keepalive_time = 0.0; slconn->stat->sl_state = SL_DOWN; slconn->stat->query_mode = NoQuery; slconn->log = NULL; return slconn; } /* End of sl_newslconn() */
/*************************************************************************** * update_stream: * * Update the appropriate stream chain entries given a Mini-SEED * record. * * Returns 0 if successfully updated and -1 if not found or error. ***************************************************************************/ int update_stream (SLCD * slconn, SLpacket * slpack) { SLstream *curstream; struct sl_fsdh_s fsdh; int seqnum; int swapflag = 0; int updates = 0; char net[3]; char sta[6]; if ( (seqnum = sl_sequence (slpack)) == -1 ) { sl_log_r (slconn, 2, 0, "update_stream(): could not determine sequence number\b"); return -1; } /* Copy fixed header */ memcpy (&fsdh, &slpack->msrecord, sizeof(struct sl_fsdh_s)); /* Check to see if byte swapping is needed (bogus year makes good test) */ if ((fsdh.start_time.year < 1900) || (fsdh.start_time.year > 2050)) swapflag = 1; /* Change byte order? */ if ( swapflag ) { sl_gswap2 (&fsdh.start_time.year); sl_gswap2 (&fsdh.start_time.day); } curstream = slconn->streams; /* Generate some "clean" net and sta strings */ if ( curstream != NULL ) { sl_strncpclean (net, fsdh.network, 2); sl_strncpclean (sta, fsdh.station, 5); } /* For uni-station mode */ if ( curstream != NULL ) { if ( strcmp (curstream->net, UNINETWORK) == 0 && strcmp (curstream->sta, UNISTATION) == 0 ) { int month = 0; int mday = 0; sl_doy2md (fsdh.start_time.year, fsdh.start_time.day, &month, &mday); curstream->seqnum = seqnum; snprintf (curstream->timestamp, 20, "%04d,%02d,%02d,%02d,%02d,%02d", fsdh.start_time.year, month, mday, fsdh.start_time.hour, fsdh.start_time.min, fsdh.start_time.sec); return 0; } } /* For multi-station mode, search the stream chain and update all matching entries */ while ( curstream != NULL ) { /* Use glob matching to match wildcarded network and station codes */ if ( sl_globmatch (net, curstream->net) && sl_globmatch (sta, curstream->sta) ) { int month = 0; int mday = 0; sl_doy2md (fsdh.start_time.year, fsdh.start_time.day, &month, &mday); curstream->seqnum = seqnum; snprintf (curstream->timestamp, 20, "%04d,%02d,%02d,%02d,%02d,%02d", fsdh.start_time.year, month, mday, fsdh.start_time.hour, fsdh.start_time.min, fsdh.start_time.sec); updates++; } curstream = curstream->next; } /* If no updates then no match was found */ if ( updates == 0 ) sl_log_r (slconn, 2, 0, "unexpected data received: %.2s %.6s\n", net, sta); return (updates == 0) ? -1 : 0; } /* End of update_stream() */
/*************************************************************************** * sl_collect_nb: * * Routine to manage a connection to a SeedLink server based on the values * given in the slconn struct and collect data. * * Designed to run in a tight loop at the heart of a client program, this * function is a non-blocking version sl_collect(). SLNOPACKET will be * returned when no data is available. * * Returns SLPACKET when something is received and sets the slpack * pointer to the received packet. Returns SLNOPACKET when no packets * have been received, slpack is set to NULL. When the connection was * closed by the server or the termination sequence completed * SLTERMINATE is returned and the slpack pointer is set to NULL. ***************************************************************************/ int sl_collect_nb (SLCD * slconn, SLpacket ** slpack) { int bytesread; double current_time; char retpacket; *slpack = NULL; /* Check if the info was set */ if ( slconn->info != NULL ) { slconn->stat->query_mode = InfoQuery; } /* If the connection is not up check the SLCD and reset the timing variables */ if ( slconn->link == -1 ) { if ( sl_checkslcd(slconn) ) { sl_log_r (slconn, 2, 0, "problems with the connection description\n"); return SLTERMINATE; } slconn->stat->netto_trig = -1; /* Init net timeout trigger to reset state */ slconn->stat->keepalive_trig = -1; /* Init keepalive trigger to reset state */ } /* Start the main sequence */ if ( ! slconn->terminate ) { if (slconn->link == -1) { slconn->stat->sl_state = SL_DOWN; } /* Check for network timeout */ if (slconn->stat->sl_state == SL_DATA && slconn->netto && slconn->stat->netto_trig > 0) { sl_log_r (slconn, 1, 0, "network timeout (%ds), reconnecting in %ds\n", slconn->netto, slconn->netdly); slconn->link = sl_disconnect (slconn); slconn->stat->sl_state = SL_DOWN; slconn->stat->netto_trig = -1; slconn->stat->netdly_trig = -1; } /* Check if a keepalive packet needs to be sent */ if (slconn->stat->sl_state == SL_DATA && !slconn->stat->expect_info && slconn->keepalive && slconn->stat->keepalive_trig > 0) { sl_log_r (slconn, 1, 2, "sending keepalive request\n"); if ( sl_send_info (slconn, "ID", 3) != -1 ) { slconn->stat->query_mode = KeepAliveQuery; slconn->stat->expect_info = 1; slconn->stat->keepalive_trig = -1; } } /* Check if an in-stream INFO request needs to be sent */ if (slconn->stat->sl_state == SL_DATA && !slconn->stat->expect_info && slconn->info) { if ( sl_send_info (slconn, slconn->info, 1) != -1 ) { slconn->stat->query_mode = InfoQuery; slconn->stat->expect_info = 1; } else { slconn->stat->query_mode = NoQuery; } slconn->info = NULL; } /* Throttle the loop while delaying */ if (slconn->stat->sl_state == SL_DOWN && slconn->stat->netdly_trig > 0) { slp_usleep (500000); } /* Connect to remote SeedLink */ if (slconn->stat->sl_state == SL_DOWN && slconn->stat->netdly_trig == 0) { if (sl_connect (slconn, 1) != -1) { slconn->stat->sl_state = SL_UP; } slconn->stat->netto_trig = -1; slconn->stat->netdly_trig = -1; slconn->stat->keepalive_trig = -1; } /* Negotiate/configure the connection */ if (slconn->stat->sl_state == SL_UP) { int slconfret = 0; /* Only send query if a query is set and no streams are defined, * if streams are defined we'll send the query after configuration. */ if (slconn->info && slconn->streams == NULL) { if ( sl_send_info (slconn, slconn->info, 1) != -1 ) { slconn->stat->query_mode = InfoQuery; slconn->stat->expect_info = 1; } else { slconn->stat->query_mode = NoQuery; slconn->stat->expect_info = 0; } slconn->info = NULL; } else { slconfret = sl_configlink (slconn); slconn->stat->expect_info = 0; } if (slconfret != -1) { slconn->stat->recptr = 0; /* initialize the data buffer pointers */ slconn->stat->sendptr = 0; slconn->stat->sl_state = SL_DATA; } else { sl_log_r (slconn, 2, 0, "negotiation with remote SeedLink failed\n"); slconn->link = sl_disconnect (slconn); slconn->stat->netdly_trig = -1; } } } else /* We are terminating */ { if (slconn->link != -1) { slconn->link = sl_disconnect (slconn); } slconn->stat->sl_state = SL_DATA; } /* DEBUG sl_log_r (slconn, 1, 0, "link: %d, sendptr: %d, recptr: %d, diff: %d\n", slconn->link, slconn->stat->sendptr, slconn->stat->recptr, (slconn->stat->recptr - slconn->stat->sendptr) ); */ /* Process data in buffer */ while (slconn->stat->recptr - slconn->stat->sendptr >= SLHEADSIZE + SLRECSIZE) { retpacket = 1; /* Check for an INFO packet */ if (!strncmp (&slconn->stat->databuf[slconn->stat->sendptr], INFOSIGNATURE, 6)) { char terminator; terminator = (slconn->stat->databuf[slconn->stat->sendptr + SLHEADSIZE - 1] != '*'); if ( !slconn->stat->expect_info ) { sl_log_r (slconn, 2, 0, "unexpected INFO packet received, skipping\n"); } else { if ( terminator ) { slconn->stat->expect_info = 0; } /* Keep alive packets are not returned */ if ( slconn->stat->query_mode == KeepAliveQuery ) { retpacket = 0; if ( !terminator ) { sl_log_r (slconn, 2, 0, "non-terminated keep-alive packet received!?!\n"); } else { sl_log_r (slconn, 1, 2, "keepalive packet received\n"); } } } if ( slconn->stat->query_mode != NoQuery ) { slconn->stat->query_mode = NoQuery; } } else /* Update the stream chain entry if not an INFO packet */ { if ( (update_stream (slconn, (SLpacket *) &slconn->stat->databuf[slconn->stat->sendptr])) == -1 ) { /* If updating didn't work the packet is broken */ retpacket = 0; } } /* Increment the send pointer */ slconn->stat->sendptr += (SLHEADSIZE + SLRECSIZE); /* Return packet */ if ( retpacket ) { *slpack = (SLpacket *) &slconn->stat->databuf[slconn->stat->sendptr - (SLHEADSIZE + SLRECSIZE)]; return SLPACKET; } } /* A trap door for terminating, all complete data packets from the buffer have been sent to the caller */ if ( slconn->terminate ) { return SLTERMINATE; } /* After processing the packet buffer shift the data */ if ( slconn->stat->sendptr ) { memmove (slconn->stat->databuf, &slconn->stat->databuf[slconn->stat->sendptr], slconn->stat->recptr - slconn->stat->sendptr); slconn->stat->recptr -= slconn->stat->sendptr; slconn->stat->sendptr = 0; } /* Catch cases where the data stream stopped */ if ((slconn->stat->recptr - slconn->stat->sendptr) == 7 && !strncmp (&slconn->stat->databuf[slconn->stat->sendptr], "ERROR\r\n", 7)) { sl_log_r (slconn, 2, 0, "SeedLink server reported an error with the last command\n"); slconn->link = sl_disconnect (slconn); return SLTERMINATE; } if ((slconn->stat->recptr - slconn->stat->sendptr) == 3 && !strncmp (&slconn->stat->databuf[slconn->stat->sendptr], "END", 3)) { sl_log_r (slconn, 1, 1, "End of buffer or selected time window\n"); slconn->link = sl_disconnect (slconn); return SLTERMINATE; } /* Process data in our buffer and then read incoming data */ if (slconn->stat->sl_state == SL_DATA) { /* Check for more available data from the socket */ bytesread = 0; bytesread = sl_recvdata (slconn, (void *) &slconn->stat->databuf[slconn->stat->recptr], BUFSIZE - slconn->stat->recptr, slconn->sladdr); if (bytesread < 0 && ! slconn->terminate) /* read() failed */ { slconn->link = sl_disconnect (slconn); slconn->stat->netdly_trig = -1; } else if (bytesread > 0) /* Data is here, process it */ { slconn->stat->recptr += bytesread; /* Reset the timeout and keepalive timers */ slconn->stat->netto_trig = -1; slconn->stat->keepalive_trig = -1; } } /* Update timing variables */ current_time = sl_dtime (); /* Network timeout timing logic */ if (slconn->netto) { if (slconn->stat->netto_trig == -1) /* reset timer */ { slconn->stat->netto_time = current_time; slconn->stat->netto_trig = 0; } else if (slconn->stat->netto_trig == 0 && (current_time - slconn->stat->netto_time) > slconn->netto) { slconn->stat->netto_trig = 1; } } /* Keepalive/heartbeat interval timing logic */ if (slconn->keepalive) { if (slconn->stat->keepalive_trig == -1) /* reset timer */ { slconn->stat->keepalive_time = current_time; slconn->stat->keepalive_trig = 0; } else if (slconn->stat->keepalive_trig == 0 && (current_time - slconn->stat->keepalive_time) > slconn->keepalive) { slconn->stat->keepalive_trig = 1; } } /* Network delay timing logic */ if (slconn->netdly) { if (slconn->stat->netdly_trig == -1) /* reset timer */ { slconn->stat->netdly_time = current_time; slconn->stat->netdly_trig = 1; } else if (slconn->stat->netdly_trig == 1 && (current_time - slconn->stat->netdly_time) > slconn->netdly) { slconn->stat->netdly_trig = 0; } } /* Non-blocking and no data was returned */ return SLNOPACKET; } /* End of sl_collect_nb() */