source: trunk/src/data_sources/k2ew/k2c_tcp.c @ 3206

Revision 3206, 19.8 KB checked in by paulf, 11 years ago (diff)

version 2.43 which improves modem handling for new kmi firmware on k2 instruments

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1
2/*
3 *   THIS FILE IS UNDER RCS - DO NOT MODIFY UNLESS YOU HAVE
4 *   CHECKED IT OUT USING THE COMMAND CHECKOUT.
5 *
6 *    $Id$
7 *
8 *    Revision history:
9 *     $Log$
10 *     Revision 1.11  2007/12/18 13:36:03  paulf
11 *     version 2.43 which improves modem handling for new kmi firmware on k2 instruments
12 *
13 *     Revision 1.10  2007/05/10 00:20:14  dietz
14 *     included <string.h> to fix missing prototypes
15 *
16 *     Revision 1.9  2003/08/22 20:12:13  friberg
17 *     added check for terminate flag to redo_socket in k2c_tcp.c
18 *     to prevent k2ew_tcp from taking too long to exit when in this
19 *     function call and a terminate signal arrives.
20 *
21 *     Revision 1.8  2002/05/06 18:27:43  kohler
22 *     Added line to function k2c_init_io() so that heartbeats will be sent
23 *     to statmgr which k2ew is attempting to make a socket connection to
24 *     the K2.
25 *
26 *     Revision 1.7  2001/05/08 00:14:53  kohler
27 *     Minor logging changes.
28 *
29 *     Revision 1.6  2000/11/29 20:35:33  kohler
30 *     When DontQuit parameter is set, program no longer exits
31 *     when it can't connect to the MSS100.  Instead, it retries
32 *     forever.
33 *
34 *     Revision 1.5  2000/08/30 17:32:46  lombard
35 *     See ChangeLog entry for 30 August 2000
36 *
37 *     Revision 1.4  2000/07/03 18:00:37  lombard
38 *     Added code to limit age of waiting packets; stops circ buffer overflows
39 *     Added and Deleted some config params.
40 *     Added check of K2 station name against restart file station name.
41 *     See ChangeLog for complete list.
42 *
43 *     Revision 1.3  2000/06/09 23:14:23  lombard
44 *     Several bug fixes and improvements; See Changelog entry of 2000-06-09.
45 *
46 *     Revision 1.2  2000/05/16 23:39:16  lombard
47 *     bug fixes, removed OutputThread Keepalive, added OnBattery alarm
48 *     made alarms report only once per occurence
49 *
50 *     Revision 1.1  2000/05/04 23:47:57  lombard
51 *     Initial revision
52 *
53 *
54 *
55 */
56/*
57 * k2c_tcp.c: K2 packet functions for TCP IO. These routines must have
58 *            the same API as other k2c_*.c routines
59 *
60 *  3/15/00 -- Pete Lombard: File started
61 *
62 */
63
64#include <stdio.h>
65#include <string.h>
66#include <earthworm.h>
67#include <error_ew.h>
68#include <socket_ew.h>
69#include "glbvars.h"
70#include "k2comif.h"
71#include "k2ewerrs.h"
72
73#define K2C_MAX_FLUSH 1000          /* max count value for 'k2c_flush_recv' */
74#define K2C_RD_SOCK_RETRY 5         /* number of times to retry socket */
75#define K2C_RD_SOCK_MS 5000         /* msec to wait during socket redo */
76#define MAX_ERR_MSG  128
77
78static char errormsg[MAX_ERR_MSG];  /* error message buffer */
79
80static SOCKET sfd;                  /* Our socket */
81struct sockaddr_in saddr;           /* socket address structure  */
82static int sockInit = 0;            /* Socket System init flag */
83
84/* A buffer for recv functions and its counters */
85static unsigned char recv_buff[RB_LEN];
86static int rb_read = 0;  /* Where to start reading from recv_buff */
87static int rb_count = 0; /* Number of bytes in recv_buff; note: we only write *
88                       * (by calling recv()) into recv_buff when it is empty */
89
90/* Internal function prototypes; */
91static int GetMoreBytes(int, int);
92static int redo_socket();
93
94/************************************************************************
95 * k2c_init_io:  tcp port initialization routine                        *
96 *      gen_io: generalize IO parameter structure                       *
97 *      toutms: timeout in milliseconds for connect()                   *
98 *      return value:  returns K2R_NO_ERROR if successful               *
99 *                     returns K2R_ERROR on error                       *
100 *                     or K2R_TIMEOUT                                   *
101 ************************************************************************/
102
103int k2c_init_io(s_gen_io *gen_io, int toutms)
104{
105  int addr, error;
106  struct hostent* hp;
107
108  if (gen_io->mode != IO_TCP )
109  {
110    logit("et", "k2c_init_io: IO mode is not TCP as expected\n");
111    return K2R_ERROR;
112  }
113
114  /* Initialize the socket system */
115  if (sockInit == 0)
116  {
117    SocketSysInit();   /* This exits on failure */
118
119    /* Set the socket address structure */
120    memset(&saddr, 0, sizeof(struct sockaddr_in));
121    saddr.sin_family = AF_INET;
122    saddr.sin_port = htons((unsigned short)gen_io->tcp_io.k2_port);
123
124    /* Assume we have an IP address and try to convert to network format.
125     * If that fails, assume a domain name and look up its IP address.
126     * Can't trust reverse name lookup, since some place may not have their
127     * name server properly configured.
128     */
129    if ( (addr = inet_addr(gen_io->tcp_io.k2_address)) != INADDR_NONE )
130    {
131      saddr.sin_addr.s_addr = addr;
132    }
133    else
134    {       /* it's not a dotted quad IP address */
135      if ( (hp = gethostbyname(gen_io->tcp_io.k2_address)) == NULL)
136      {
137        logit("et", "k2c_init_io: invalid K2 address <%s>\n",
138              gen_io->tcp_io.k2_address);
139        return K2R_ERROR;
140      }
141      memcpy((void *) &saddr.sin_addr, (void*)hp->h_addr, hp->h_length);
142    }
143    sockInit = 1;
144  }
145
146/* Loop until connect() succeeds
147   *****************************/
148  while ( 1 )
149  {
150     g_mt_working = 1;     /* Keep sending heartbeats */
151
152     if ( (sfd = socket_ew(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
153     {
154       ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
155       logit("et", "k2c_init_io: error opening socket: %s\n",
156             errormsg);
157       return K2R_ERROR;
158     }
159
160     if (connect_ew(sfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr),
161                    toutms) != -1)
162        break;                        /* connect() succeeded */
163
164     sfd = INVALID_SOCKET;            /* connect_ew closes socket on error */
165     if ( (error = GetLastError_ew()) == CONNECT_WOULDBLOCK_EW)
166     {
167       logit("et", "k2c_init_io: Timed out making connection to K2\n");
168       if ( !gcfg_dont_quit ) return K2R_TIMEOUT;
169     }
170     else
171     {
172       ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
173       logit("et", "k2c_init_io: Error making TCP connection to K2: %s\n",
174             errormsg);
175       if ( !gcfg_dont_quit ) return K2R_ERROR;
176     }
177     sleep_ew( toutms );
178  }
179
180  /* Mark our recv_buff as empty */
181  rb_count = rb_read = 0;
182
183  /* We're done! */
184  return K2R_NO_ERROR;
185}
186
187/************************************************************************
188 * k2c_close_io:  closes tcp socket opened by 'k2_init_io()'            *
189 *                sets the socket to INVALID_SOCKET                     *
190 *         return value: returns K2R_NO_ERROR on success,               *
191 *                        K2R_ERROR on error                            *
192 ************************************************************************/
193
194int k2c_close_io( void )
195{
196  if (sfd != INVALID_SOCKET)
197  {
198    if (closesocket( sfd ) == SOCKET_ERROR)
199    {
200      ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
201      logit("et", "k2c_init_io: error closing socket: %s\n",
202            errormsg);
203      sfd = INVALID_SOCKET;
204      return K2R_ERROR;
205    }
206    sfd = INVALID_SOCKET;
207  }
208  return K2R_NO_ERROR;
209}
210
211
212/**************************************************************************
213 * k2c_tran_buff:  transmits buffer of data (up to 65535 bytes) out TCP   *
214 *      port; with optional timeout value                                 *
215 *      If socket is re-opened, then entire buffer is sent again          *
216 *         buff     - address of buffer containing data bytes             *
217 *         datalen   - number of bytes from buffer to transmit            *
218 *         toutms   - timeout value in milliseconds; (note that the       *
219 *                    timeout is reset after each tcp packet is sent)     *
220 *         redo     - number of times to re-open the socket on timeouts   *
221 *      return value:  returns the number of bytes successfully           *
222 *                     transmitted or K2R_ERROR on error,                 *
223 *                     K2R_TIMEOUT on timeout                             *
224 **************************************************************************/
225
226int k2c_tran_buff(const unsigned char *buff, int datalen, int toutms, int redo)
227{
228  int nsent, error, rc;
229
230  if (gcfg_debug > 3)
231    logit("e", "entering k2c_tran_buff\n");
232  if (redo < 0) redo = 0;  /* just in case... */
233  while ( (nsent = send_ew(sfd, (char *)buff, datalen, 0, toutms)) < datalen)
234  {
235    if ( (error = socketGetError_ew()) == WOULDBLOCK_EW)
236    {
237      if (redo--)  /* decrement our local copy of redo */
238      {     /* Timeout occured; fiddle with the socket */
239        if ( (rc = redo_socket()) == K2R_NO_ERROR)
240          continue;    /* socket reopened; try sending again */
241        else
242        {
243          if (rc == K2R_ERROR)
244            return rc;   /* error or timeout */
245          else
246            return nsent;
247        }
248      }
249      return K2R_TIMEOUT;
250    }
251    else
252    {
253      ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
254      logit("et", "k2c_tran_buff: Error sending to K2: %s\n",
255            errormsg);
256      return K2R_ERROR;
257    }
258  }
259  return datalen;
260}
261
262
263
264/**************************************************************************
265 * k2c_rcvflg_tout:  waits for data to be received into TCP port          *
266 *         toutms   - a timeout value in milliseconds                     *
267 *      return value:  returns K2R_NO_ERROR if data is ready to read      *
268 *                returns K2R_TIMEOUT if no data to read within timeout   *
269 *                     returns K2R_ERROR on error                         *
270 **************************************************************************/
271
272int k2c_rcvflg_tout(int toutms)
273{
274  int sel_ret;
275  fd_set r_set;
276  struct timeval tv;
277
278  if (gcfg_debug > 3)
279    logit("e", "entering k2c_rcvflg_tout\n");
280  FD_ZERO( &r_set );
281  FD_SET(sfd, &r_set);
282  tv.tv_sec = toutms / 1000;
283  tv.tv_usec = (toutms % 1000) * 1000;
284
285  sel_ret = select( sfd+1, &r_set, NULL, NULL, &tv);
286  if (sel_ret > 0 && FD_ISSET( sfd, &r_set ))
287  {   /* Looks like there's something to receive */
288    if (gcfg_debug > 3)
289      logit("e", "k2c_rcvflg_tout ret NO_ERROR\n");
290    return K2R_NO_ERROR;
291  }
292  else if (sel_ret == 0)
293  {   /* Select timed out */
294    if (gcfg_debug > 3)
295      logit("e", "k2c_rcvflg_tout ret TIMEOUT\n");
296    return K2R_TIMEOUT;
297  }
298  else
299  {   /* Select had a booboo */
300    ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
301    logit("et", "k2c_rcvflg_tout: error in select(): %s\n",
302          errormsg);
303    return K2R_ERROR;
304  }
305}
306
307/**************************************************************************
308 * k2c_rcvbt_tout:  returns next byte received into TCP port; waits for   *
309 *      data ready with timeout                                           *
310 *         toutms   - if non-zero then a timeout value in milliseconds;   *
311 *         redo     - number of times to re-open the socket on timeouts   *
312 *      return value: returns the received byte (as an int) if successful *
313 *                    returns K2R_TIMEOUT if timeout                      *
314 *                    returns K2R_ERROR if an error detected              *
315 **************************************************************************/
316
317int k2c_rcvbt_tout(int toutms, int redo)
318{
319  int rc;
320  unsigned char bt;
321
322  if (gcfg_debug > 4)
323    logit("e", "entering k2c_rcvbt_tout\n");
324  if (redo < 0) redo = 0;
325  while (1)
326  {
327    if (rb_count <= rb_read)  /* Is the recv_buffer empty? */
328    {            /* Yes, fill it up */
329      if ( (rc = GetMoreBytes(RB_LEN, toutms)) != K2R_NO_ERROR)
330      {
331        if (rc == K2R_ERROR)
332          return rc;
333        else if (redo--)  /* decrement our local copy of redo */
334        {     /* Timeout occured; fiddle with the socket */
335          if ( (rc = redo_socket()) == K2R_NO_ERROR) {
336            if (gcfg_force_blkmde) 
337              k2mi_force_blkmde();      /* newly added for modem control regrab, if told to */
338            continue;    /* socket reopened; try for more */
339          } else {
340            return rc;   /* error or timeout */
341          }
342        }
343        else  /* no more redo's allowed, return the timeout */
344          return rc;
345      }
346    }
347    bt = recv_buff[rb_read++];
348    return (int) bt;          /* return received data byte */
349  }
350}
351
352
353/* ************************************************************************
354 * k2c_recv_buff:  fills buffer with received data; waits for given       *
355 *      number of bytes to be received; with optional timeout             *
356 *         rbuff    - address of buffer to receive data bytes             *
357 *         datalen  - number of data bytes to receive                     *
358 *         toutms   - timeout value in milliseconds;                      *
359 *         redo     - number of times to re-open the socket on timeouts   *
360 *      return value: returns the number of data bytes received and       *
361 *                    placed into 'rbuff[]'                               *
362 *                    returns K2R_ERROR on error                          *
363 *                    returns K2R_TIMEOUT if no data read before timeout  *
364 **************************************************************************/
365
366int k2c_recv_buff(unsigned char *rbuff, int datalen, int toutms, int redo)
367{
368  int bcount = 0;
369  int to_read, to_copy, rc;
370
371  if (gcfg_debug > 3)
372    logit("e", "entering k2c_recv_buff\n");
373  if (redo < 0) redo = 0;  /* just in case... */
374  while (bcount < datalen)
375  {
376    if (rb_count <= rb_read)  /* Is the recv_buffer empty? */
377    {    /* Yes, fill it up */
378      to_read = (datalen - bcount < RB_LEN) ? datalen - bcount : RB_LEN;
379      if ( (rc = GetMoreBytes(to_read, toutms)) != K2R_NO_ERROR)
380      {
381        if (rc == K2R_ERROR)
382          return rc;
383        else if (redo--)  /* decrement our local copy of redo */
384        {     /* Timeout occured; fiddle with the socket */
385          if ( (rc = redo_socket()) == K2R_NO_ERROR) {
386            if (gcfg_force_blkmde) 
387              k2mi_force_blkmde();      /* newly added for modem control regrab, if told to */
388            continue;    /* socket reopened; try for more */
389          } else {
390            if (rc == K2R_ERROR || bcount == 0)
391              return rc;   /* error or timeout */
392            else
393              return bcount;
394          }
395        }
396        else  /* no more redo's allowed, return TIMEOUT */
397          return ( (bcount > 0) ? bcount : rc);
398      }
399    }
400    /* Copy from recv_buff into our output buffer *
401     * Don't exceed capacity of either buffer     */
402    to_copy = (rb_count - rb_read < datalen - bcount) ?
403      rb_count - rb_read : datalen - bcount;
404    memcpy(&rbuff[bcount], &recv_buff[rb_read], to_copy);
405    rb_read += to_copy;
406    bcount += to_copy;
407  }
408  return bcount;
409}
410
411
412/**************************************************************************
413 * k2c_flush_recv:  flushes out the receive buffer by reading in and      *
414 *      discarding incoming data (up to 'K2C_MAX_FLUSH' bytes); with      *
415 *      optional timeout                                                  *
416 *         toutms - timeout value in milliseconds                         *
417 *                  during which any incoming data will be flushed;       *
418 *                  if zero then no timeout is used and only the          *
419 *                  data already received is flushed                      *
420 *      return value:  returns 0 if no error; -1 on error                 *
421 **************************************************************************/
422
423int k2c_flush_recv(int toutms)
424{
425  int bcount = 0;
426  int to_read, rc;
427
428  if (gcfg_debug > 3)
429    logit("e", "entering k2c_flush_recv; TO %d\n", toutms);
430  rb_read = rb_count = 0;
431  if (toutms <= 0)
432    bcount = K2C_MAX_FLUSH - 1;
433
434  while (bcount < K2C_MAX_FLUSH)
435  {
436    to_read = (K2C_MAX_FLUSH - bcount < RB_LEN) ? K2C_MAX_FLUSH - bcount :
437      RB_LEN;
438    rc = GetMoreBytes(to_read, toutms);
439    if (gcfg_debug > 3)
440      logit("e", "GetMoreBytes returned %d to go: %d\n", rc, K2C_MAX_FLUSH - bcount);
441    if ( rc == K2R_ERROR)
442      return rc;
443    else if (rc == K2R_TIMEOUT)
444      return K2R_NO_ERROR;
445
446    bcount += rb_count;
447  }
448  return K2R_NO_ERROR;
449}
450
451
452/************************************************************************
453 * GetMoreBytes: an internal function for filling the static recv_buff. *
454 *           to_read: number of bytes to recv; rb_count is set to       *
455 *               number of bytes actually read; rb_read is set to 0     *
456 *            toutms: if non-zero then a timeout value in milliseconds; *
457 *                    if zero then no timeout is used.                  *
458 *           returns: K2R_NO_ERROR on success, K2R_ERROR on error       *
459 *                    or K2R_TIMEOUT on timeout                         *
460 ************************************************************************/
461
462static int GetMoreBytes(int to_read, int toutms)
463{
464  int n_read, error;
465
466  if (to_read < 1 || to_read > RB_LEN)
467  {
468    logit("et", "GetMoreBytes: invalid read request <%d>\n", to_read);
469    return K2R_ERROR;
470  }
471  rb_read = rb_count = 0;
472
473  if ( (n_read = recv_ew(sfd, (char *)recv_buff, to_read, 0, toutms)) <= 0)
474  {
475    /* For NT, added test for n_read == 0 as proxy for timeout */
476    if ( n_read == 0 || (error = socketGetError_ew()) == WOULDBLOCK_EW)
477      return K2R_TIMEOUT;
478    else
479    {
480      ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
481      logit("et", "GetMoreBytes: error receiving: %s\n",
482            errormsg);
483      return K2R_ERROR;
484    }
485  }
486  if (gcfg_debug > 4)
487    logit("e", "GMB %d\n", n_read);
488  rb_count = n_read;
489  return K2R_NO_ERROR;
490}
491
492
493/****************************************************************
494 *  redo_socket: closes and reopens and connects the socket to  *
495 *               the address set by k2c_init_io(). Waits on     *
496 *               connection for K2C_RD_SOCK_MS milliseconds;    *
497 *               tries K2C_RD_SOCK_RETRY times if connections   *
498 *               time out; sets socket sfd to INVALID_SOCKET on *
499 *               failure.                                       *
500 *     return value: returns K2R_NO_ERROR on success;           *
501 *               K2R_ERROR on error, or                         *
502 *               K2R_TIMEOUT on final timeout                   *
503 ****************************************************************/
504static int redo_socket()
505{
506  int retry = K2C_RD_SOCK_RETRY;
507  int error;
508
509  if ( gcfg_debug > 0 )
510     logit("t", "Redoing socket\n");
511
512  while ( 1 )
513  {
514    if ( !gcfg_dont_quit )
515       if ( --retry ) break;
516
517    if (sfd != INVALID_SOCKET)
518    {
519      if (gcfg_debug > 1)
520        logit("et", "Closing socket\n");
521      if (closesocket(sfd) == SOCKET_ERROR)
522      {
523        ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
524        logit("et", "redo_socket: Error closing socket: %s\n",
525              errormsg);
526        sfd = INVALID_SOCKET;
527        return K2R_ERROR;
528      }
529      sfd = INVALID_SOCKET;
530    }
531    sleep_ew(K2C_RD_SOCK_MS);
532
533    if (gcfg_debug > 1)
534      logit("et", "Opening socket\n");
535
536    if ( (sfd = socket_ew(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
537    {
538      ew_fmt_err_msg(socketGetError_ew(), errormsg, MAX_ERR_MSG);
539      logit("et", "redo_socket: Error opening socket: %s\n",
540            errormsg);
541      return K2R_ERROR;
542    }
543
544    if (connect_ew(sfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr),
545                   K2C_RD_SOCK_MS) == -1)
546    {
547      sfd = INVALID_SOCKET;          /* connect_ew closes socket on error */
548      if ( (error = socketGetError_ew()) != CONNECT_WOULDBLOCK_EW)
549      {
550        ew_fmt_err_msg(error, errormsg, MAX_ERR_MSG);
551        logit("et", "redo_socket: Error reconnecting to K2: %d %s\n",
552              error, errormsg);
553        if ( !gcfg_dont_quit ) return K2R_ERROR;
554        if ( g_terminate_flg ) return K2R_ERROR;
555      } /* connection timed out; try again */
556    }
557    else   /* connection made, return OK */
558    {
559      logit("et", "redo_socket: Successful reconnection to K2\n");
560      return K2R_NO_ERROR;
561    }
562  }
563  /* Too many retries; give up */
564  logit("et", "redo_socket: Timed out reconnecting to K2\n");
565  return K2R_TIMEOUT;
566}
567
Note: See TracBrowser for help on using the repository browser.