Building Controls Virtual Test Bed
util/utilSocket.c
Go to the documentation of this file.
1 // Methods for interfacing clients using BSD sockets.
2 
3 /*
4 ********************************************************************
5 Copyright Notice
6 ----------------
7 
8 Building Controls Virtual Test Bed (BCVTB) Copyright (c) 2008-2009, The
9 Regents of the University of California, through Lawrence Berkeley
10 National Laboratory (subject to receipt of any required approvals from
11 the U.S. Dept. of Energy). All rights reserved.
12 
13 If you have questions about your rights to use or distribute this
14 software, please contact Berkeley Lab's Technology Transfer Department
15 at TTD@lbl.gov
16 
17 NOTICE. This software was developed under partial funding from the U.S.
18 Department of Energy. As such, the U.S. Government has been granted for
19 itself and others acting on its behalf a paid-up, nonexclusive,
20 irrevocable, worldwide license in the Software to reproduce, prepare
21 derivative works, and perform publicly and display publicly. Beginning
22 five (5) years after the date permission to assert copyright is obtained
23 from the U.S. Department of Energy, and subject to any subsequent five
24 (5) year renewals, the U.S. Government is granted for itself and others
25 acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide
26 license in the Software to reproduce, prepare derivative works,
27 distribute copies to the public, perform publicly and display publicly,
28 and to permit others to do so.
29 
30 
31 Modified BSD License agreement
32 ------------------------------
33 
34 Building Controls Virtual Test Bed (BCVTB) Copyright (c) 2008-2009, The
35 Regents of the University of California, through Lawrence Berkeley
36 National Laboratory (subject to receipt of any required approvals from
37 the U.S. Dept. of Energy). All rights reserved.
38 
39 Redistribution and use in source and binary forms, with or without
40 modification, are permitted provided that the following conditions are met:
41 
42  1. Redistributions of source code must retain the above copyright
43  notice, this list of conditions and the following disclaimer.
44  2. Redistributions in binary form must reproduce the above copyright
45  notice, this list of conditions and the following disclaimer in
46  the documentation and/or other materials provided with the
47  distribution.
48  3. Neither the name of the University of California, Lawrence
49  Berkeley National Laboratory, U.S. Dept. of Energy nor the names
50  of its contributors may be used to endorse or promote products
51  derived from this software without specific prior written permission.
52 
53 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
54 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
55 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
56 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
57 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 
65 You are under no obligation whatsoever to provide any bug fixes,
66 patches, or upgrades to the features, functionality or performance of
67 the source code ("Enhancements") to anyone; however, if you choose to
68 make your Enhancements available either publicly, or directly to
69 Lawrence Berkeley National Laboratory, without imposing a separate
70 written license agreement for such Enhancements, then you hereby grant
71 the following license: a non-exclusive, royalty-free perpetual license
72 to install, use, modify, prepare derivative works, incorporate into
73 other computer software, distribute, and sublicense such enhancements or
74 derivative works thereof, in binary and source code form.
75 
76 ********************************************************************
77 */
78 
109 #include "utilSocket.h"
110 // Global variable to check for FMUExport case
111 int FMUEXPORT = 0;
125 int save_append(char* *buffer, const char *toAdd, int *bufLen){
126  const int size = 1024;
127  const int nNewCha = strlen(toAdd);
128  const int nBufCha = strlen(*buffer);
129  // reallocate memory if needed
130  if ( *bufLen < nNewCha + nBufCha + 1){
131  *bufLen = *bufLen + size * (((nNewCha + nBufCha) / size)+1);
132  *buffer = realloc(*buffer, *bufLen);
133  if (*buffer == NULL) {
134  perror("Realloc failed in save_append.");
135 #ifdef NDEBUG
136  fprintf(f1, "Realloc failed in save_append.\n");
137 #endif
138  return EXIT_FAILURE;
139  }
140  }
141  // append toAdd to buffer
142  strcpy(*buffer + strlen(*buffer), toAdd);
143  return 0;
144 }
145 
159 int assembleBuffer(int flag,
160  int nDbl, int nInt, int nBoo,
161  double curSimTim,
162  double dblVal[], int intVal[], int booVal[],
163  char* *buffer, int *bufLen)
164 {
165  int i;
166  int retVal;
167  char temCha[1024]; // temporary character array
168  memset((char*) *buffer, '\0', *bufLen);
169  // Set up how many values will be in buffer
170  // This is an internally used version number to make update
171  // of the format possible later without braking old versions
172  sprintf(temCha, "%d ", MAINVERSION);
173  retVal = save_append(buffer, temCha, bufLen);
174  if ( retVal != 0 ) return retVal;
175  sprintf(temCha, "%d ", flag);
176  retVal = save_append(buffer, temCha, bufLen);
177  if ( retVal != 0 ) return retVal;
178  if ( flag == 0 ){
179  // Only process data if the flag is zero.
180  sprintf(temCha, "%d ", nDbl);
181  retVal = save_append(buffer, temCha, bufLen);
182  if ( retVal != 0 ) return retVal;
183  sprintf(temCha, "%d ", nInt);
184  retVal = save_append(buffer, temCha, bufLen);
185  if ( retVal != 0 ) return retVal;
186  sprintf(temCha, "%d ", nBoo);
187  retVal = save_append(buffer, temCha, bufLen);
188  if ( retVal != 0 ) return retVal;
189  sprintf(temCha,"%20.15e ", curSimTim);
190  retVal = save_append(buffer, temCha, bufLen);
191  if ( retVal != 0 ) return retVal;
192  // add values to buffer
193  for(i = 0; i < nDbl; i++){
194  sprintf(temCha,"%20.15e ", dblVal[i]);
195  retVal = save_append(buffer, temCha, bufLen);
196  if ( retVal != 0 ) return retVal;
197  }
198  for(i = 0; i < nInt; i++){
199  sprintf(temCha,"%d ", intVal[i]);
200  retVal = save_append(buffer, temCha, bufLen);
201  if ( retVal != 0 ) return retVal;
202  }
203  for(i = 0; i < nBoo; i++){
204  sprintf(temCha,"%d ", booVal[i]);
205  retVal = save_append(buffer, temCha, bufLen);
206  if ( retVal != 0 ) return retVal;
207  }
208  }
209  // For the Java server to read the line, the line
210  // needs to be terminated with '\n'
211  sprintf(temCha,"\n");
212  retVal = save_append(buffer, temCha, bufLen);
213  if ( retVal != 0 ) return retVal;
214  // No error, return 0
215  return 0;
216 }
217 
227 int getIntCheckError(const char *nptr, char **endptr, const int base,
228  int* val){
229  errno = 0; // must reset errno to 0
230  // Parse integer
231  *val = strtol(nptr, endptr, base);
233  // do error checking
234  if ((errno == ERANGE)
235  || (errno != 0 && *val == 0)) {
236  perror("strtol caused error.");
237  if (strlen(nptr) < 1) {
238  fprintf(stderr, "strtol() was called with a string of length less than 1. This can occur when no data is read.\n");
239  } else {
240  fprintf(stderr, "strtol was called with strtol(%s, %s, %d)\n", nptr, *endptr, base);
241  }
242  return EXIT_FAILURE;
243  }
244  if (*endptr == nptr) {
245  //FMU Export - added the check to skip the error message for FMUExport
246  if (FMUEXPORT != 1){
247  fprintf(stderr, "Error: No digits were found in getIntCheckError.\n");
248  fprintf(stderr, "Further characters after number: %s\n", *endptr);
249  fprintf(stderr, "Sending EXIT_FAILURE = : %d\n", EXIT_FAILURE);
250  }
251  return EXIT_FAILURE;
252  }
253  return 0;
254 }
255 
264 int getDoubleCheckError(const char *nptr, char **endptr,
265  double* val){
266  errno = 0; // must reset errno to 0
267  *val = strtod(nptr, endptr);
269  // do error checking
270  if ((errno == ERANGE && (*val == HUGE_VAL || *val == -HUGE_VAL))
271  || (errno != 0 && *val == 0)) {
272  perror("strtod caused error.");
273  return EXIT_FAILURE;
274  }
275  if (*endptr == nptr) {
276  fprintf(stderr, "Error: No digits were found in getDoubleCheckError.\n");
277  fprintf(stderr, "Further characters after number: %s\n", *endptr);
278  fprintf(stderr, "Sending EXIT_FAILURE = : %d\n", EXIT_FAILURE);
279  return EXIT_FAILURE;
280  }
281  return 0;
282 }
283 
297 int disassembleHeaderBuffer(const char* buffer,
298  char **endptr, const int base,
299  int *fla,
300  int *nDbl, int *nInt, int *nBoo)
301 {
302  int retVal; // return value
304  // Get first few integers to set up dictionaray
305  int i;
306  // set number of received values to zero to ensure that
307  // if retVal != 0, we have the values initialized
308  *nDbl = 0;
309  *nInt = 0;
310  *nBoo = 0;
311  // version number
312  retVal = getIntCheckError(buffer, endptr, base, &SERVER_VERSION);
313  if ( retVal )
314  return retVal;
316  // communication flag
317  retVal = getIntCheckError(*endptr, endptr, base, fla);
318  if ( retVal )
319  return retVal;
320  // number of doubles, integers and booleans
321  retVal = getIntCheckError(*endptr, endptr, base, nDbl);
322  if ( retVal )
323  return retVal;
324  retVal = getIntCheckError(*endptr, endptr, base, nInt);
325  if ( retVal )
326  return retVal;
327  retVal = getIntCheckError(*endptr, endptr, base, nBoo);
328  return retVal;
329 }
330 
343 int disassembleBuffer(const char* buffer,
344  int *fla,
345  int *nDbl, int *nInt, int *nBoo,
346  double *curSimTim,
347  double dblVal[], int intVal[], int booVal[])
348 {
349  int i;
350  int retVal; // return value
351  const int base = 10;
352  char *endptr = 0;
353  retVal = disassembleHeaderBuffer(buffer, &endptr, base,
354  fla, nDbl, nInt, nBoo);
355  if ( retVal ) {
356 #ifdef NDEBUG
357  fprintf(f1, "Error while disassembling the header of the buffer.\n");
358 #endif
359  return retVal;
360  }
361 
362  *curSimTim = 0;
363  // current simulation time
364  retVal = getDoubleCheckError(endptr, &endptr, curSimTim);
365  if ( retVal ) {
366 #ifdef NDEBUG
367  fprintf(f1, "Error while getting the current simulation time.\n");
368 #endif
369  return retVal;
370  }
372  // Get doubles
373  for(i=0; i < *nDbl; i++){
374  retVal = getDoubleCheckError(endptr, &endptr, &dblVal[i]);
375  if ( retVal ) {
376 #ifdef NDEBUG
377  fprintf(f1, "Error while getting double %d of %d.\n", i, *nDbl);
378 #endif
379  return retVal;
380  }
381  }
383  // Get integers
384  for(i=0; i < *nInt; i++){
385  retVal = getIntCheckError(endptr, &endptr, base, &intVal[i]);
386  if ( retVal ){
387 #ifdef NDEBUG
388  fprintf(f1, "Error while getting integer %d of %d.\n", i, *nInt);
389 #endif
390  return retVal;
391  }
392  }
394  // Get boolean
395  for(i=0; i < *nBoo; i++){
396  retVal = getIntCheckError(endptr, &endptr, base, &booVal[i]);
397  if ( retVal ){
398 #ifdef NDEBUG
399  fprintf(f1, "Error while getting boolean %d of %d.\n", i, *nBoo);
400 #endif
401  return retVal;
402  }
403  }
404  return retVal;
405 }
406 
413 int getsocketportnumber(const char *const docname) {
414  int retVal;
415  char *xPat = "//ipc/socket[@port]";
416  char *res;
417  int i;
418  res = malloc(BUFFER_LENGTH);
419  if (res == NULL) {
420  perror("malloc failed in getsocketportnumber.");
421 #ifdef NDEBUG
422  fprintf(f1, "malloc failed in getsocketportnumber.\n");
423 #endif
424  return -1;
425  }
426  if (0 == getxmlvalue(docname, xPat, res, &i, BUFFER_LENGTH))
427  retVal = atoi((char*)res);
428  else
429  retVal = -1;
430  free(res);
431  return retVal;
432 }
433 
444  return MAINVERSION;
445 }
446 
454 int getsockethost(const char *const docname, char *const hostname) {
455  char *xPat = "//ipc/socket[@hostname]";
456  int i;
457  int r = getxmlvalue(docname, xPat, hostname, &i, BUFFER_LENGTH);
458  return r;
459 }
460 
468 int establishclientsocket(const char *const docname){
469  int portNo, retVal, sockfd;
470  char* hostname;
471  char* serverIP;
472 #ifdef _MSC_VER /************* Windows specific code ********/
473  struct hostent* FAR server;
474  WSADATA wsaData;
475  WORD wVersionRequested;
476 #else /************* End of Windows specific code *******/
477  struct hostent *server;
478 #endif
479  struct sockaddr_in serAdd;
480  const int arg=1; // 1: true
481 #ifdef NDEBUG
482  if (f1 == NULL)
483  f1 = fopen ("utilSocket.log", "w");
484  if (f1 == NULL){
485  fprintf(stderr, "Could not open file '%s'\n", "utilSocket.log");
486  return -3;
487  }
488  else
489  fprintf(f1, "utilSocket: Establishing socket based on file %s.\n", docname);
490 #endif
491  fflush(f1);
492  hostname = malloc(BUFFER_LENGTH);
493  if (hostname == NULL) {
494  perror("malloc failed in establishclientsocket.");
495 #ifdef NDEBUG
496  fprintf(f1, "malloc failed in establishclientsocket.\n");
497 #endif
498  return -2;
499  }
500 
502  // get the socket port number
503 #ifdef NDEBUG
504  fprintf(f1, "Getting socket port number.\n");
505 #endif
506  portNo = getsocketportnumber(docname);
507 #ifdef NDEBUG
508  fprintf(f1, "Received socket port number %d.\n", portNo);
509 #endif
510  if ( portNo < 0 ){
511  fprintf(stderr, "Error: Could not obtain socket port number. Return value = %d.\n", portNo);
512 #ifdef NDEBUG
513  fprintf(f1, "Error: Could not obtain socket port number. Return value = %d.\n", portNo);
514 #endif
515  return portNo;
516  }
517 #ifdef NDEBUG
518  fprintf(f1, "Socket port number = %d.\n", portNo);
519 #endif
520  // get the socket host name
522  retVal = getsockethost(docname, hostname);
523  if ( retVal < 0 ){
524 #ifdef NDEBUG
525  fprintf(f1, "Error: Could not obtain socket hostname. Return value = %d.\n", retVal);
526 #endif
527  return retVal;
528  }
529  // open socket
530 #ifdef _MSC_VER /************* Windows specific code ********/
531  wVersionRequested = MAKEWORD( 2, 2 );
532  retVal = WSAStartup( wVersionRequested, &wsaData );
533  if ( retVal != 0 ) {
534  /* Tell the user that we could not find a usable */
535  /* WinSock DLL. */
536 #ifdef NDEBUG
537  fprintf(f1, "Error: Could not find a usable WinSock DLL.\n");
538  fprintf(f1, "WSAGetLastError = %d\n", WSAGetLastError());
539 #endif
540  return -4;
541  }
542  /* Confirm that the WinSock DLL supports 2.2.*/
543  /* Note that if the DLL supports versions greater */
544  /* than 2.2 in addition to 2.2, it will still return */
545  /* 2.2 in wVersion since that is the version we */
546  /* requested. */
547  if ( LOBYTE( wsaData.wVersion ) != 2 ||
548  HIBYTE( wsaData.wVersion ) != 2 ) {
549  /* Tell the user that we could not find a usable */
550  /* WinSock DLL. */
551 #ifdef NDEBUG
552  fprintf(f1, "Error: Could not find a usable WinSock DLL for requested version.\n");
553 #endif
554  WSACleanup( );
555  return -5;
556  }
557 #ifdef NDEBUG
558  fprintf(f1, "WinSock DLL is acceptable.\n");
559 #endif
560  /* The WinSock DLL is acceptable. Proceed. */
561 #endif /************* End of Windows specific code *******/
562  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
563  if( sockfd < 0){
564 #ifdef NDEBUG
565  fprintf(f1, "Error opening socket. sockfd = %d.\n", sockfd);
566 #endif
567  return sockfd;
568  }
569 #ifdef NDEBUG
570  fprintf(f1, "Socket opened, sockfd = %d.\n", sockfd);
571 #endif
572 
573  // See
574  // http://msdn.microsoft.com/en-us/library/ms740476%28VS.85%29.aspx
575  // for increasing the buffer size.
576  // Note that getsockopt must be called to see if the increased buffer
577  // size was provided by the implementation.
578  if( setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&arg, sizeof(arg)) != 0){
579 #ifdef NDEBUG
580  fprintf(f1, "Error setting socket option keep alive.\n");
581  fprintf(f1, "Error flag errno = %d.\n", errno);
582 #endif
583  return -6;
584  }
585 
586  if (sockfd < 0){
587 #ifdef NDEBUG
588  fprintf(f1, "Error opening socket\n");
589 #endif
590  return sockfd;
591  }
593  // establish server address
594  server = gethostbyname(hostname);
595  if (server == NULL) {
596 #ifdef NDEBUG
597  fprintf(f1,"Error, no such host: %s\n", hostname);
598 #ifdef __APPLE__
599  fprintf(f1,"gethostbyname(%s) returned %d, which means '%s'\n", hostname, h_errno, hstrerror(h_errno));
600 #endif // __APPLE__
601 #endif // NDEBUG
602 
603  server = gethostbyname("localhost");
604  if (server == NULL) {
605 #ifdef NDEBUG
606  fprintf(f1,"Error, no such host: %s\n", "localhost");
607 #ifdef __APPLE__
608  fprintf(f1,"gethostbyname(%s) returned %d, which means '%s'\n", "localhost", h_errno, hstrerror(h_errno));
609 #endif // __APPLE__
610 #endif // NDEBUG
611  free(hostname);
612  return -7;
613  } else {
614 #ifdef NDEBUG
615  fprintf(f1,"Warning: gethostbyname(\"%s\") returned null, but gethostbyname(\"localhost\") returned non-null.\n", hostname);
616  fprintf(f1,"This sometimes happens on a Mac.\n");
617 #endif
618  fprintf(stderr,"Warning: gethostbyname(\"%s\") returned null, but gethostbyname(\"localhost\") returned non-null.\n", hostname);
619  fprintf(stderr,"This sometimes happens on a Mac.\n");
620  }
621  }
622  free(hostname);
623 
624  serverIP = inet_ntoa(*(struct in_addr *)*server->h_addr_list);
625  memset((char *) &serAdd, '\0', sizeof(serAdd));
626  serAdd.sin_family = AF_INET;
627  serAdd.sin_addr.s_addr = inet_addr(serverIP);
628  serAdd.sin_port = htons(portNo);
630  // establish connection
631  //retVal = connect(sockfd, (void *)&serAdd, sizeof(serAdd));
632  retVal = connect(sockfd, (const struct sockaddr*)&serAdd, sizeof(serAdd));
633  if ( retVal < 0){
634 #ifdef _MSC_VER
635  fprintf(stderr, "Error when connecting to socket %d on %s: WSAGetLastError = %d\n", portNo, serverIP, WSAGetLastError());
636 #else
637  fprintf(stderr, "Error when connecting to socket %d on %s: %s\n", portNo, serverIP, strerror(errno));
638 #endif
639 #ifdef NDEBUG
640 #ifdef _MSC_VER
641  fprintf(f1, "Error when connecting to socket %d on %s: WSAGetLastError = %d\n", portNo, serverIP, WSAGetLastError());
642 #else
643  fprintf(f1, "Error when connecting to socket %d on %s: %s\n", portNo, serverIP, strerror(errno));
644 #endif
645 #endif
646  return retVal;
647  }
648  // Set flags that indicate that we have not yet written to
649  // or read from the buffer
652  return sockfd;
653 }
654 
670 int writetosocket(const int *sockfd,
671  const int *flaWri,
672  const int *nDblWri, const int *nIntWri, const int *nBooWri,
673  double *curSimTim,
674  double dblValWri[], int intValWri[], int booValWri[])
675 {
676  int retVal;
677  // buffer used to exchange data
678  char *buffer;
679  int bufLen = REQUIRED_WRITE_LENGTH;
680 
681 #ifdef NDEBUG
682  if (f1 == NULL) // open file
683  f1 = fopen ("utilSocket.log", "w");
684  if (f1 == NULL){
685  fprintf(stderr, "Cannot open file %s\n", "utilSocket.log");
686  return -1;
687  }
688 #endif
689 
691  // make sure that the socketFD is valid
692 if (*sockfd < 0 ){
693  fprintf(stderr, "Error: Called write to socket with negative socket number.\n");
694  fprintf(stderr, " sockfd : %d\n", *sockfd);
695 #ifdef NDEBUG
696  fprintf(f1, "Error: Called write to socket with negative socket number.\n");
697  fprintf(f1, " sockfd : %d\n", *sockfd);
698  fflush(f1);
699 #endif
700  return -1; // return a negative value in case of an error
701  }
702 
704  // allocate storage for buffer
705 #ifdef NDEBUG
706  fprintf(f1, "Assembling buffer.\n", *sockfd);
707 #endif
708  buffer = malloc(bufLen);
709  if (buffer == NULL) {
710  perror("malloc failed in writetosocket.");
711 #ifdef NDEBUG
712  fprintf(f1, "malloc failed in writetosocket.\n");
713 #endif
714  return -1;
715  }
717  // copy arguments to buffer
718  retVal = assembleBuffer(*flaWri, *nDblWri, *nIntWri, *nBooWri,
719  *curSimTim,
720  dblValWri, intValWri, booValWri,
721  &buffer, &bufLen);
722 
723  if (retVal != 0 ){
724  fprintf(stderr, "Error: Failed to allocate memory for buffer before writing to socket.\n");
725  fprintf(stderr, " retVal : %d\n", retVal);
726  fprintf(stderr, " Message: %s\n", strerror(errno));
727 #ifdef NDEBUG
728  fprintf(f1, "Error: Failed to allocate memory for buffer before writing to socket.\n");
729  fprintf(f1, " retVal : %d\n", retVal);
730  fprintf(f1, " Message: %s\n", strerror(errno));
731  fflush(f1);
732 #endif
733  free(buffer);
734  return -1; // return a negative value in case of an error
735  }
737  // write to socket
738 #ifdef NDEBUG
739  fprintf(f1, "Write to socket with fd = %d\n", *sockfd);
740  fprintf(f1, "Buffer = %s\n", buffer);
741 #endif
742 
743 #ifdef _MSC_VER
744  retVal = send(*sockfd,buffer,strlen(buffer), 0);
745 #else
746  retVal = write(*sockfd,buffer,strlen(buffer));
747 #endif
748 
749 #ifdef NDEBUG
750  if (retVal >= 0)
751  fprintf(f1, "Wrote %d characters to socket.\n", retVal);
752  else
753  fprintf(f1, "Error writing to socket: Return value = %d.\n", retVal);
754 #endif
755  if (retVal < 0){
756 #ifdef NDEBUG
757 #ifdef _MSC_VER
758  fprintf(f1, "Error writing to socket: WSAGetLastError = %d\n", WSAGetLastError());
759 #else
760  fprintf(f1, "Error writing to socket: %s\n", strerror(errno));
761 #endif
762  fflush(f1);
763 #endif
764  }
765  free(buffer);
766  return retVal;
767 }
768 
785 int sendclientmessage(const int *sockfd, const int *flaWri){
786  int zI = 0;
787  int retVal = 0;
788  double zD = 0;
789  char *inpBuf;
790 
791  if ( *sockfd >= 0 ){
792  retVal = writetosocket(sockfd, flaWri, &zI, &zI, &zI, &zD,
793  NULL, NULL, NULL);
794 #ifdef NDEBUG
795  fprintf(f1, "sendclientmessage wrote flag %d, return value = %d.\n",
796  *flaWri, retVal);
797 #endif
798  if ( retVal >= 0 ){
799  // No error. Wait for acknowledgement. This is needed on Windows for E+.
800  // Otherwise, E+ sometimes terminates and breaks the socket connection before
801  // Ptolemy read the message.
802 
803  inpBuf = malloc(REQUIRED_READ_LENGTH * sizeof(char));
804  if (inpBuf == NULL) {
805  perror("malloc failed in sendclientmessage.");
806 #ifdef NDEBUG
807  fprintf(f1, "malloc failed in sendclientmessage.\n");
808 #endif
809  free(inpBuf);
810  return -11;
811  }
812  memset(inpBuf, '\0', REQUIRED_READ_LENGTH);
813 
814  retVal = readbufferfromsocket(sockfd, inpBuf, &REQUIRED_READ_LENGTH);
815  free(inpBuf);
816  }
817  }
818  else
819  retVal = 0;
820  return retVal;
821 }
822 
831  int retVal;
832  char buffer[HEADER_LENGTH];
833  const int base = 10;
834  char *endptr = NULL;
835  int fla = 0;
836  int nDbl = 0;
837  int nInt = 0;
838  int nBoo = 0;
839 
840  memset(buffer, '\0', HEADER_LENGTH);
841 #ifdef _MSC_VER
842  // MSG_WAITALL is not in the winsock2.h file, at least not on my system...
843 #define MSG_WAITALL 0x8 // do not complete until packet is completely filled
844  retVal = recv(*sockfd, buffer, HEADER_LENGTH, MSG_PEEK);
845 #else
846  retVal = recv(*sockfd, buffer, HEADER_LENGTH, MSG_PEEK);
847 #endif
848  if ( retVal < 1 ){
849  perror("Failed to peek at socket.");
850  return retVal;
851  }
852  retVal = disassembleHeaderBuffer(buffer, &endptr, base,
853  &fla, &nDbl, &nInt, &nBoo);
854  if ( retVal < 0 ){
855  perror("Failed to disassemble header buffer.");
856  return retVal;
857  }
858  return getrequiredbufferlength(nDbl, nInt, nBoo);
859 }
867 int getrequiredbufferlength(const int nDbl, const int nInt, const int nBoo){
868  int retVal;
869  if ( ( nInt > 0 ) || ( nBoo > 0) ){
870  fprintf(stderr, "Error: Integers and booleans are currently not\n");
871  fprintf(stderr, " implemented in utilSocket:getrequiredbufferlength.\n");
872  fprintf(stderr, " Received %d integers and %d boolean.\n", nInt, nBoo);
873  retVal = -1;
874  }
875  else{
876  // Header has 4 integers and the current simulation time.
877  // Each double has 21 characters plus one space behind it.
878  // The last number is for the EOL character.
879  retVal = HEADER_LENGTH + 21 + (21+1) * nDbl + 1;
880  }
881  return retVal;
882 }
883 
899 int readfromsocket(const int *sockfd, int *flaRea,
900  int *nDblRea, int *nIntRea, int *nBooRea,
901  double *curSimTim,
902  double dblValRea[], int intValRea[], int booValRea[])
903 {
904  int retVal, i;
905  char *inpBuf;
907  // make sure that the socketFD is valid
908  if (*sockfd < 0 ){
909  fprintf(stderr, "Error: Called read from socket with negative socket number.\n");
910  fprintf(stderr, " sockfd : %d\n", *sockfd);
911 #ifdef NDEBUG
912  fprintf(f1, "Error: Called read from socket with negative socket number.\n");
913  fprintf(f1, " sockfd : %d\n", *sockfd);
914  fflush(f1);
915 #endif
916  return -1; // return a negative value in case of an error
917  }
918  // In the first call, set the socket buffer length
919  // This is done here since we know how many data we need to read.
920  if ( REQUIRED_READ_LENGTH < 1 ){
921  // Peak into the socket message to see how many data we need to read
922  // This is required to assign enough storage for the buffer.
923  // This call also sets the version number SERVER_VERSION
924  // that is sent by the server.
926  if ( REQUIRED_READ_LENGTH <= 0 )
927  return -1;
928  }
929  // Increase the buffer that is used to store the data
930  inpBuf = malloc(REQUIRED_READ_LENGTH * sizeof(char));
931  if (inpBuf == NULL) {
932  perror("malloc failed in readfromsocket.");
933 #ifdef NDEBUG
934  fprintf(f1, "malloc failed in readfromsocket.\n");
935 #endif
936  free(inpBuf);
937  return -1;
938  }
939 
940  memset(inpBuf, '\0', REQUIRED_READ_LENGTH);
941  retVal = readbufferfromsocket(sockfd, inpBuf, &REQUIRED_READ_LENGTH);
942  if (retVal < 0){
943 #ifdef NDEBUG
944 #ifdef _MSC_VER
945  fprintf(f1, "Error reading: WSAGetLastError = %d\n", WSAGetLastError());
946 #else
947  fprintf(f1, "Error reading: %s\n", strerror(errno));
948 #endif
949  fflush(f1);
950 #endif
951  free(inpBuf);
952  return retVal;
953  }
955  // disassemble buffer and store values in function argument
956  retVal = disassembleBuffer(inpBuf,
957  flaRea,
958  nDblRea, nIntRea, nBooRea,
959  curSimTim,
960  dblValRea, intValRea, booValRea);
961 #ifdef NDEBUG
962  fprintf(f1, "Disassembled buffer.\n");
963 #endif
964  free(inpBuf);
965  return retVal;
966 }
967 
977 int readbufferfromsocket(const int *sockfd,
978  char *buffer, int *bufLen){
979  int retVal;
980  int reachedEnd = 0;
981  // The number 8192 needs to be the same as in Server.java
982  int maxChaRea = 8192;
983  int chaSta = 0;
984  // Loop until we read the '\n' character
985  do {
986 #ifdef _MSC_VER
987  // MSG_WAITALL is not in the winsock2.h file, at least not on my system...
988 #define MSG_WAITALL 0x8 /* do not complete until packet is completely filled */
989  retVal = recv(*sockfd, buffer, *bufLen, 0);
990 #else
991  retVal = read(*sockfd, &buffer[chaSta], maxChaRea);
992 #endif
993 
994 #ifdef NDEBUG
995  fprintf(f1, "In readbufferfromsocket: Read %d chars, maximum is %d.\n", retVal, REQUIRED_READ_LENGTH);
996 #endif
997  if ( retVal == 0 ){
998  fprintf(stderr, "Error: The server closed the socket while the client was reading.\n");
999  return -1;
1000  }
1001  if ( retVal < 0 ){
1002  fprintf(stderr, "Error: Unspecified error when reading from socket.\n");
1003  return retVal;
1004  }
1005 
1006  // Check if we received '\n', in which case we finish the reading
1007  if ( NULL == memchr(&buffer[chaSta], '\n', retVal) ){
1008  chaSta += retVal;
1009  if (SERVER_VERSION == 1){
1010  fprintf(stderr, "Error: This version of the socket interface cannot process such large data.\n");
1011  fprintf(stderr, " You will need to update to BCVTB 0.8.0 or higher.\n");
1012 #ifdef NDEBUG
1013  fprintf(f1, "Error: This version of the socket interface cannot process such large data.\n");
1014  fprintf(f1, " You will need to update to BCVTB 0.8.0 or higher.\n");
1015 #endif
1016  return -1;
1017  }
1018  }
1019  else{
1020  reachedEnd = 1; // found the end of the string
1021  }
1022  } while(reachedEnd == 0);
1023  return retVal;
1024 }
1025 
1049 int exchangewithsocket(const int *sockfd,
1050  const int *flaWri, int *flaRea,
1051  const int *nDblWri, const int *nIntWri, const int *nBooWri,
1052  int *nDblRea, int *nIntRea, int *nBooRea,
1053  double *simTimWri,
1054  double dblValWri[], int intValWri[], int booValWri[],
1055  double *simTimRea,
1056  double dblValRea[], int intValRea[], int booValRea[]){
1057  int retVal;
1058 #ifdef NDEBUG
1059  if (f1 == NULL)
1060  f1 = fopen ("utilSocket.log", "w");
1061  if (f1 == NULL){
1062  fprintf(stderr, "Cannot open file %s\n", "utilSocket.log");
1063  return -1;
1064  }
1065  rewind(f1);
1066  fprintf(f1, "*** BCVTB client log file.\n", *simTimWri);
1067  fprintf(f1, "*************************.\n", *simTimWri);
1068  fprintf(f1, "Writing to socket at time = %e\n", *simTimWri);
1069 #endif
1070 
1071  // In the first call, set the socket buffer length
1072  // This is done here since we know how many data we need to send.
1073  if ( REQUIRED_WRITE_LENGTH < 1 ){
1074  REQUIRED_WRITE_LENGTH = getrequiredbufferlength(*nDblWri, *nIntWri, *nBooWri);
1075  if ( REQUIRED_WRITE_LENGTH <= 0 )
1076  return -1;
1077  // Increase the buffer length for the socket
1078  // retVal = setrequiredbufferlength(*sockfd, REQUIRED_WRITE_LENGTH, SO_SNDBUF);
1079  // if ( retVal != 0 )
1080  // return retVal;
1081  }
1082 
1083  retVal = writetosocket(sockfd, flaWri,
1084  nDblWri, nIntWri, nBooWri,
1085  simTimWri,
1086  dblValWri, intValWri, booValWri);
1087  if ( retVal >= 0 ){
1088 #ifdef NDEBUG
1089  fprintf(f1, "Reading from socket.\n");
1090  fflush(f1);
1091 #endif
1092  retVal = readfromsocket(sockfd, flaRea,
1093  nDblRea, nIntRea, nBooRea,
1094  simTimRea,
1095  dblValRea, intValRea, booValRea);
1096  }
1097 #ifdef NDEBUG
1098  fprintf(f1, "Finished exchanging data with socket: simTimRea=%e, flag=%d.\n", *simTimRea, retVal);
1099  fflush(f1);
1100 #endif
1101  return retVal;
1102 }
1103 
1120  const int *flaWri, int *flaRea,
1121  const int *nDblWri,
1122  int *nDblRea,
1123  double *simTimWri,
1124  double dblValWri[],
1125  double *simTimRea,
1126  double dblValRea[]){
1127  const int zer = 0;
1128  int nIntRea = 0;
1129  int nBooRea = 0;
1130  int intValRea[1]; // allocate array of non-zero size
1131  int booValRea[1]; // allocate array of non-zero size
1132  return exchangewithsocket(sockfd,
1133  flaWri, flaRea,
1134  nDblWri, &zer, &zer,
1135  nDblRea, &nIntRea, &nBooRea,
1136  simTimWri,
1137  dblValWri, NULL, NULL,
1138  simTimRea,
1139  dblValRea, intValRea, booValRea);
1140 }
1141 
1161  const int *flaWri, int *flaRea,
1162  const int *nDblWri,
1163  int *nDblRea,
1164  double *simTimWri,
1165  double dblValWri[],
1166  double *simTimRea,
1167  double dblValRea[],
1168  const int* flaExport){
1169  const int zer = 0;
1170  int nIntRea = 0;
1171  int nBooRea = 0;
1172  int intValRea[1]; // allocate array of non-zero size
1173  int booValRea[1]; // allocate array of non-zero size
1174  if (*flaExport == 1){
1175  FMUEXPORT = 1;
1176  }
1177  return exchangewithsocket(sockfd,
1178  flaWri, flaRea,
1179  nDblWri, &zer, &zer,
1180  nDblRea, &nIntRea, &nBooRea,
1181  simTimWri,
1182  dblValWri, NULL, NULL,
1183  simTimRea,
1184  dblValRea, intValRea,
1185  booValRea);
1186 }
1187 
1193 int closeipc(int* sockfd){
1194 #ifdef _MSC_VER
1195  return closesocket(*sockfd);
1196 #else
1197  return close(*sockfd);
1198 #endif
1199 }
1200 
1201 
1202 int main( int argc, const char* argv[] )
1203 {
1204  fprintf(stderr, "Calling establ...'\n");
1205  // establishclientsocket("socket.cfg");
1206 }
1207 
1208 
function sockfd
int exchangedoubleswithsocketFMU(const int *sockfd, const int *flaWri, int *flaRea, const int *nDblWri, int *nDblRea, double *simTimWri, double dblValWri[], double *simTimRea, double dblValRea[], const int *flaExport)
Exchanges data with the socket.
int getIntCheckError(const char *nptr, char **endptr, const int base, int *val)
Gets an integer and does the required error checking.
int exchangedoubleswithsocket(const int *sockfd, const int *flaWri, int *flaRea, const int *nDblWri, int *nDblRea, double *simTimWri, double dblValWri[], double *simTimRea, double dblValRea[])
Exchanges data with the socket.
int main(int argc, const char *argv[])
for i
Definition: compile.m:69
int REQUIRED_WRITE_LENGTH
int getsocketportnumber(const char *const docname)
Gets the port number for the BSD socket communication.
int getDoubleCheckError(const char *nptr, char **endptr, double *val)
Gets a double and does the required error checking.
function r
int getRequiredReadBufferLength(const int *sockfd)
Returns the required socket buffer length by reading from the socket how many data it contains...
#define MAINVERSION
The main version of the socket interface.
Definition: defines.h:37
int disassembleHeaderBuffer(const char *buffer, char **endptr, const int base, int *fla, int *nDbl, int *nInt, int *nBoo)
Disassembles the header of the buffer that has been received through the IPC.
#define HEADER_LENGTH
int REQUIRED_READ_LENGTH
int establishclientsocket(const char *const docname)
Establishes a connection to the socket.
function retVal
Definition: closeIPC.m:1
int getrequiredbufferlength(const int nDbl, const int nInt, const int nBoo)
Returns the required socket buffer length.
fprintf([' ', 'Trying to send client error from MATLAB to the BCVTB.'])
int getmainversionnumber()
Returns the version number of the client.
int SERVER_VERSION
This will be overwritten to contain the version number of the server.
int sendclientmessage(const int *sockfd, const int *flaWri)
Writes a message flag to the socket stream.
FILE * f1
end Get return values from pointers flaRea
int closeipc(int *sockfd)
Closes the inter process communication socket.
int getsockethost(const char *const docname, char *const hostname)
Gets the hostname for the BSD socket communication.
int writetosocket(const int *sockfd, const int *flaWri, const int *nDblWri, const int *nIntWri, const int *nBooWri, double *curSimTim, double dblValWri[], int intValWri[], int booValWri[])
Writes data to the socket.
int readbufferfromsocket(const int *sockfd, char *buffer, int *bufLen)
Reads a character buffer from the socket.
int save_append(char **buffer, const char *toAdd, int *bufLen)
Appends a character array to another character array.
int exchangewithsocket(const int *sockfd, const int *flaWri, int *flaRea, const int *nDblWri, const int *nIntWri, const int *nBooWri, int *nDblRea, int *nIntRea, int *nBooRea, double *simTimWri, double dblValWri[], int intValWri[], int booValWri[], double *simTimRea, double dblValRea[], int intValRea[], int booValRea[])
Exchanges data with the socket.
int readfromsocket(const int *sockfd, int *flaRea, int *nDblRea, int *nIntRea, int *nBooRea, double *curSimTim, double dblValRea[], int intValRea[], int booValRea[])
Reads data from the socket.
#define BUFFER_LENGTH
Debug flag, uncomment to enable debug messages
Definition: defines.h:26
int assembleBuffer(int flag, int nDbl, int nInt, int nBoo, double curSimTim, double dblVal[], int intVal[], int booVal[], char **buffer, int *bufLen)
Assembles the buffer that will be exchanged through the IPC.
int FMUEXPORT
int getxmlvalue(char *const fileName, char *const exp, char *const str, int *const nVals, int const strLen)
Definition: util/utilXml.c:828
int disassembleBuffer(const char *buffer, int *fla, int *nDbl, int *nInt, int *nBoo, double *curSimTim, double dblVal[], int intVal[], int booVal[])
Disassembles the buffer that has been received through the IPC.