//******************************************************************************* //* Copyright (c) 2008-2014 Synchrotron SOLEIL //* All rights reserved. This program and the accompanying materials //* are made available under the terms of the GNU Lesser Public License v3 //* which accompanies this distribution, and is available at //* http://www.gnu.org/licenses/lgpl.html //****************************************************************************** //****************************************************************************************** // // // september 13, 2004 : Source file for the communication in socket mode // // with a Lecroy scope (avaiable for all models) // // author : X.Elattaoui // // SocketLecroy.cpp: implementation of the SocketLecroy class. // //****************************************************************************************** //- INCLUDE #include <iostream> #include "SocketLecroy.h" #include <sys/time.h> #include <time.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <sys/select.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <cstdio> //- perf!!! #include <yat/time/Timer.h> static int hSocket; static int sTimeout = 2; //- second(s) static char sCurrentAddress[256]; bool SocketLecroy::sConnectedFlag = false; const int CMD_BUF_LEN = 8192; static char sCommandBuffer[CMD_BUF_LEN]; //- init of the static instance SocketLecroy* SocketLecroy::SL_instance = 0; //- ptr on the SocketLecroy instance SocketLecroy* SocketLecroy::get_instance() { if( !SL_instance ) SL_instance = new SocketLecroy(); return SL_instance; } void SocketLecroy::delete_instance() { // std::cout << "\t\t\tSocketLecroy::deleted_instance ... SL_instance = " << SL_instance << std::endl; if(SL_instance) { delete SL_instance; SL_instance = 0; } // std::cout << "\t\t\tSocketLecroy::deleted_instance DONE -> SL_instance = " << SL_instance << std::endl; } //- CTOR SocketLecroy::SocketLecroy() { sConnectedFlag=false; } //- DTOR SocketLecroy::~SocketLecroy() { // std::cout << "\t\t\tSocketLecroy::DTOR SocketLecroy ..." << std::endl; TCP_Disconnect(); // std::cout << "\t\t\tSocketLecroy::DTOR DONE." << std::endl; } //- Build the connection void SocketLecroy::TCP_Connect(char *ip_address) throw (lecroy::SocketException) { yat::Timer t; struct sockaddr_in serverAddr; int result; const int resp = 1; fd_set wr_set; FD_ZERO(&wr_set); struct timeval tval; unsigned long argp; char tmpStr[256]; //- connection test if (sConnectedFlag) return; strcpy(sCurrentAddress, ip_address); tval.tv_sec = 1; tval.tv_usec = 0; //- build server socket address serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); if ((serverAddr.sin_addr.s_addr = inet_addr(ip_address)) == INADDR_NONE) { throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Bad server address.", "SocketLecroy::TCP_Connect( )."); } //- create client's socket if ((hSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) //INVALID_SOCKET) { throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to create client's socket.", "SocketLecroy::TCP_Connect( )."); } //- set Timeout for read operations if (setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tval, sizeof(struct timeval)) != 0) { throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to set socket option to TCP_NODELAY.", "SocketLecroy::TCP_Connect( )."); } if (setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&resp, sizeof(resp)) != 0) { throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to set socket option to TCP_NODELAY.", "SocketLecroy::TCP_Connect( )."); } FD_SET(hSocket, &wr_set); argp = 1;//-non blocking mode ioctl(hSocket, FIONBIO, &argp); int opts; opts = fcntl (hSocket, F_GETFL); if (opts >= 0) opts = (opts | O_NONBLOCK); fcntl (hSocket, F_SETFL, opts); //connect(hSocket, (SOCKADDR FAR *) &serverAddr, sockAddrSize); int status = ::connect(hSocket, ( sockaddr *)&serverAddr, sizeof(serverAddr)); if(status < 0) // We are not connected : so retry { if(errno == EINPROGRESS) // But the connection is in progress { int nb = 0; struct timespec time_to_sleep, time_remaining; while(nb++ < 5) // We will attempt to connect every 100 ms for 5 times max. { status = ::connect (hSocket, ( sockaddr *)&serverAddr, sizeof(serverAddr)); if(status != 0) // Still not connected { if(errno == EALREADY) // This is the right error ! { time_to_sleep.tv_sec = 0; time_to_sleep.tv_nsec = 150000000L; nanosleep(&time_to_sleep, &time_remaining); // Sleep for 150 ms } }// Connection is OK. else break; }//TODO : throw ; // Too much attempts, so failure ! }// TODO : throw ; // Not the right error, so failure ! }// Connected at first attempt ! result = select(hSocket, NULL, &wr_set, NULL, &tval); argp = 0; //-blocking mode ioctl(hSocket, FIONBIO, &argp); //- connect to server (scope) if (result < 0) { sprintf(tmpStr, "Unable to make connection to IP:%s", ip_address); throw lecroy::SocketException("COMMUNICATION_BROKEN ", tmpStr, "SocketLecroy::TCP_Connect( )."); } sConnectedFlag = true; // std::cout << "\t\t\t SocketLecroy::TCP_Connect done in " << t.elapsed_msec() << " ms" << std::endl; } //- DisconnectFromScope: disconnect from a network device void SocketLecroy::TCP_Disconnect(void) throw (lecroy::SocketException) { // std::cout << "\t\t\tSocketLecroy::comm closing socket ENTREING ...." << std::endl; if (sConnectedFlag) { // std::cout << "\t\t\tSocketLecroy::comm closing socket ...." << std::endl; close(hSocket); // std::cout << "\t\t\tSocketLecroy::comm closed." << std::endl; sConnectedFlag = false; } // std::cout << "\t\t\tSocketLecroy::comm closing socket DONE ...." << std::endl; } //- Clear a connection void SocketLecroy::TCP_ClearDevice(void) throw (lecroy::SocketException) { if ( !sConnectedFlag ) throw lecroy::SocketException("COMMUNICATION_BROKEN", "Disconnection already done.", "SocketLecroy::TCP_ClearDevice( )."); TCP_Disconnect(); TCP_Connect(sCurrentAddress); } void SocketLecroy::write_read(char* in_buf, unsigned int in_length, char* out_buf, int* out_length, bool eoi_flag) { yat::Timer t; yat::AutoMutex<> guard(lock_); TCP_WriteDevice(in_buf, in_length, eoi_flag); // std::cout << "\t\t\t SocketLecroy::write_read WRITE done in " << t.elapsed_msec() << " ms" << std::endl; TCP_ReadDevice(out_buf, *out_length, out_length); // std::cout << "\t\t\t SocketLecroy::write_read done in " << t.elapsed_msec() << " ms" << std::endl; } //- Send commands to the remote device void SocketLecroy::TCP_WriteDevice(char *buf, int len, bool eoi_flag) throw (lecroy::SocketException) { TCP_HEADER header; int result, bytes_more, bytes_xferd; char *idxPtr; // std::cout << "\t\t\t TCP_WriteDevice -> ENTERING ..." << std::endl; //- test connection if ( !sConnectedFlag ) throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Device not connected.", "SocketLecroy::TCP_WriteDevice( )."); if (len < CMD_BUF_LEN) strcpy(sCommandBuffer, buf); //- set the header info header.bEOI_Flag = DATA_FLAG; header.bEOI_Flag |= (eoi_flag)? EOI_FLAG:0; header.reserved[0] = 1; header.reserved[1] = 0; header.reserved[2] = 0; header.iLength = htonl(len); // std::cout << "\t\t\t TCP_WriteDevice -> send ..." << std::endl; //- write the header first if (send(hSocket, (char *) &header, sizeof(TCP_HEADER), 0) != sizeof(TCP_HEADER)) { throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to send header info to the server.", "SocketLecroy::TCP_WriteDevice( )."); } // std::cout << "\t\t\t TCP_WriteDevice -> sent!" << std::endl; bytes_more = len; idxPtr = buf; bytes_xferd = 0; while (1) { //- then write the rest of the block idxPtr = buf + bytes_xferd; if ((result = send(hSocket, (char *) idxPtr, bytes_more, 0)) < 0) { // std::cout << "\t\t\tFATAL TCP_WriteDevice -> send ..." << std::endl; throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to send data to the server.", "SocketLecroy::TCP_WriteDevice( )."); } bytes_xferd += result; bytes_more -= result; if (bytes_more <= 0) break; } // std::cout << "\t\t\tFATAL TCP_WriteDevice -> DONE." << std::endl; } //- Read the device answer void SocketLecroy::TCP_ReadDevice(char *buf, unsigned int len, int *recv_count) throw (lecroy::SocketException) { TCP_HEADER header; int result; unsigned int accum, space_left, bytes_more, buf_count; char tmpStr[256]; char *idxPtr; fd_set rd_set; FD_ZERO(&rd_set); struct timeval tval; //- test connection if ( !sConnectedFlag ) throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Device not connected.", "SocketLecroy::TCP_ReadDevice( )."); *recv_count = 0; if (!buf) throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Buffer memory not allocated.", "SocketLecroy::TCP_ReadDevice( )."); FD_SET(hSocket, &rd_set); tval.tv_sec = 0; tval.tv_usec = 500000L; memset(buf, 0, len); buf_count = 0; space_left = len; while (1) { result = select(hSocket, &rd_set, NULL, NULL, &tval); if (result < 0) { // std::cout << "\tSocketLecroy::TCP_ReadDevice -> result < 0." << std::endl; TCP_ClearDevice(); throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Read Timeout.", "SocketLecroy::TCP_ReadDevice( )."); } //- get the header info first accum = 0; while (1) { memset(&header, 0, sizeof(TCP_HEADER)); if ((result = recv(hSocket, (char *) &header + accum, sizeof(header) - accum, 0)) <= 0) { TCP_ClearDevice(); throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to receive header info from the server.", "SocketLecroy::TCP_ReadDevice( )."); } accum += result; if (accum>=sizeof(header)) break; } // header.iLength = ntohl(header.iLength); unsigned int headerLength = ntohl(header.iLength); //- only read to len amount if (headerLength > space_left) { headerLength = space_left; /* throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Read buffer size is too small.", "SocketLecroy::TCP_ReadDevice( ).");*/ } //- read the rest of the block accum = 0; while (1) { idxPtr = buf + (buf_count + accum); bytes_more = headerLength - accum; if ((space_left-accum) < TCP_MINIMUM_PACKET_SIZE) { TCP_ClearDevice(); sprintf(tmpStr, "Read buffer needs to be adjusted, must be minimum of %d bytes", TCP_MINIMUM_PACKET_SIZE); throw lecroy::SocketException("COMMUNICATION_BROKEN ", tmpStr, "SocketLecroy::TCP_ReadDevice( )."); } if ((result = recv(hSocket, (char *) idxPtr, (bytes_more>2048)?2048:bytes_more, 0)) < 0) { TCP_ClearDevice(); //-MessageBox(0, "Unable to receive data from the server.", "ERROR", MB_OK); throw lecroy::SocketException("COMMUNICATION_BROKEN ", "Unable to receive data from the server", "SocketLecroy::TCP_ReadDevice( )."); } accum += result; if (accum >= headerLength) break; if ((accum + buf_count) >= len) break; } buf_count += accum; space_left -= accum; if (header.bEOI_Flag & EOI_FLAG) break; if (space_left <= 0) break; } *recv_count = buf_count; }