/* hcb.cpp Henriks communication API www.eit.se/hcpips Copyright 2008 Henrik Björkman www.eit.se May be redistributed as under Gnu General Public License version 3. http://www.gnu.org/licenses/licenses.html#GPL You may modify this code as long as you document the modifications you have made in the History section below and do not remove this copyright notice and the file history section. It should work with both Windows and UNIX (such as Solaris & Linux). But the latest version is only tested under Win32. History 080917 Created by Henrik Bjorkman using code from hca.c */ /* When linking for windows perhaps wsock32.lib needs to be added to project. */ #ifdef WIN32 #include #include #include #endif #include #include #include #include #include #include #include #include #ifndef WIN32 #include #endif #include "hca.h" #include "hcb.h" //#define DEBUG /* Macro for debug printouts */ #ifdef DEBUG #define D(x) x #else #define D(x) #endif #ifdef DEBUG /* To write a buffer in hex */ static void HexPrint(const char *ptr, int len) { D(fprintf(stderr,"HexPrint %2d: ",len);) if (len>30) len=30; while(len-->0) { printf("%02x",(unsigned char)*ptr++); } printf("\n"); } /* To write all graphic characters in a buffer */ static void GraphPrint(const char *ptr, int len) { D(fprintf(stderr,"GraphPrint %2d: ",len);) if (len>64) len=64; while(len-->0) { if (isgraph(*ptr)) {putc(*ptr,stdout);} else {putc('.',stdout);} ptr++; } printf("\n"); } #endif /*****************************************************************************/ /* The following variables and functions are only to be used internaly. */ /* Global data for the communication */ static const char* findFirstNonPrint(const char *str, int n) { while (n>0) { if (!isprint(*str)) { return str; } str++; n--; } return NULL; } static const char *findFirstGraph(const char *str, int n) { while (n>0) { if (isgraph(*str)) { return str; } str++; n--; } return NULL; } static void movemem(char *dst, const char* src, int n) { while (n) { *dst=*src; dst++; src++; n--; } } #if 0 /* To write all graphic characters in a buffer */ static void GraphPrint(char *ptr, int len) { D(printf("GraphPrint %d\n: ",len);) if (len>64) len=64; while(len-->0) { if (isgraph(*ptr)) {putc(*ptr,stdout);} else {putc('.',stdout);} ptr++; } /*printf("\n");*/ fflush(stdout); } #endif #ifdef WIN32 /* Hcb::Hcb(): hSerialPort(INVALID_HANDLE_VALUE), fd(1), head(NULL), tail(NULL), bufIndex(0), cmdEnd(0), transparentMode(false), echo(false) { memset(buf, 0, sizeof(buf)); } */ Hcb::Hcb(HANDLE hSerialPort): hSerialPort(hSerialPort), nError(0), fd(HCB_NO_FD), head(NULL), tail(NULL), bufIndex(0), cmdEnd(0), transparentMode(false), echo(false) { memset(buf, 0, sizeof(buf)); } Hcb::Hcb(int fd): hSerialPort(INVALID_HANDLE_VALUE), nError(0), fd(fd), head(NULL), tail(NULL), bufIndex(0), cmdEnd(0), transparentMode(false), echo(false) { memset(buf, 0, sizeof(buf)); } #else /* Hcb::Hcb(): nError(0), fd(1), head(NULL), tail(NULL), bufIndex(0), cmdEnd(0), transparentMode(false), echo(false) { D(printf("Hcb::Hcb %d\n", this->fd);) fd=hca_open_standard_input(); memset(buf, 0, sizeof(buf)); } */ Hcb::Hcb(int fd): nError(0), fd(fd), head(NULL), tail(NULL), cmdEnd(NULL), bufIndex(0), transparentMode(false), echo(false) { D(printf("Hcb::Hcb %d\n", this->fd);) /* the following should not be needed */ if ((head!=NULL) || (tail!=NULL)) { printf("constructor is not correct\n"); head=NULL; tail=NULL; } if (this->fd<0) { printf("Hcb::Hcb fd<0 %d\n", this->fd); } memset(buf, 0, sizeof(buf)); } #endif /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Our own signal handler. It does nothing but count the signals */ /* and print an error message */ /* static int hca_signal_count=0; static void hca_signal_hander(int i) { hca_signal_count++; fprintf(stderr,"hca: signal %d %d\n",hca_signal_count, i); } */ Hcb::OutFifoPost::OutFifoPost(int len): first(0), len(len), next(NULL) { str=(char*)malloc(len); }; Hcb::OutFifoPost::~OutFifoPost() { free(str); str=NULL; }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* The following functions are to be used by other functions. */ /* Wait for something to happen Dont use 1000000 micro seconds or longer timeout. Return codes are: <0 Something went wrong 0 Timeout >0 something received from one of the sockets or a new client wants to connect. After this function use "hca_accept" to check if a new client connected (only if this is a server). Use "hca_get_next_socket_to_read" to check from which socket something was received. */ int Hcb::wait(int timeout_us) { const int nready=hca_wait(timeout_us); return(nready); } /* Initiates global data and sets up the communication. This function must be called before uning any of the others. Returns >= 0 if ok. */ int Hcb::init() { return hca_init(); } /* The CreateFile function can create a handle to a communications resource, such as the serial port COM1. For communications resources, the dwCreationDisposition parameter must be OPEN_EXISTING, and the hTemplate parameter must be NULL. Read, write, or read/write access can be specified, and the handle can be opened for overlapped I/O. For more information about communications, see Communications. Read more: http://www.lookrs232.com/com_port_programming/api_open_port.htm Open a serial port dev_name chall be: COM1 COM2 etc Returns: fd if ok, HCB_NO_FD if not ok. */ Hcb* Hcb::open_serial_port(const char *dev_name, int baudrate) { #ifdef WIN32 const HANDLE h=hca_open_serial_port(dev_name, baudrate); if ((h==INVALID_HANDLE_VALUE) || (h<0)) { return NULL; } Hcb *hca=new Hcb(h); return hca; #else const int s=hca_open_serial_port(dev_name, baudrate); if ((s==HCA_NO_FD) || (s<0)) { return NULL; } Hcb *hca=new Hcb(s); return hca; #endif } // An alternative to the current implementation below would be // to use CreateFile to create a handle to console input (CONIN$). /* Open standard input */ Hcb* Hcb::open_standard_input() { // stdin is actually on fd 0, while stdout is 1. // but here we try to use same for both input and output. D(printf("Hcb::open_standard_input\n");) int fd=hca_open_standard_input(); if (fd>=0) { Hcb *hca=new Hcb(fd); return hca; } return NULL; } /* Initiates global data and sets up the communication as a server. Returns >=0 if ok, HCB_NO_FD if not ok. */ int Hcb::server(unsigned short port) { D(printf("Hcb::server %d\n", port);) return hca_server(port); } /* Create a socket and bind it to the server port number */ /* All clients connected this way shall be disconnected with a "delete". */ Hcb* Hcb::client(const char *hostname, unsigned short port) { D(printf("Hcb::client %s %d\n", hostname, port);) const int s=hca_client(hostname, port); if ((s==HCA_NO_FD) || (s<0)) { return NULL; } Hcb *hca=new Hcb(s); return(hca); } /* To deactivate the communication, This is not nessesary. */ /* But on win32s it is nessesary! */ Hcb::~Hcb() { D(fprintf(stderr,"~Hcb\n")); while (!isEmpty()) { deleteFirstPostFromList(); } if (fd>0) { hca_close(fd); } #ifdef WIN32 else if (hSerialPort) { hca_close_handle(hSerialPort); } #endif } /* This is supposed to be called before program terminates to clean things up. */ void Hcb::cleanUp() { hca_cleanup(); } /* This function sends data on a socket. */ /* Pretty much same as standard write */ int Hcb::write(const char *buf, unsigned long len) { D(printf("Hcb::write %d\n", fd);) D(GraphPrint(buf, len);) int bytesWritten; #ifdef WIN32 if (fd>1) { bytesWritten=hca_write(fd, buf, len); } else if (hSerialPort!=INVALID_HANDLE_VALUE) { bytesWritten=hca_write_handle(hSerialPort, buf, len); } else { bytesWritten=_write(1,buf,len); } #else if (fd>0) { bytesWritten=hca_write(fd, buf, len); } #endif return(bytesWritten); } /* To read data that has arrived on a socket. Ungefdr som vanlig read plus en felutskrift om det blev fel. Return codes: >0 The number of characters read. 0 no data from this client. -1 The client has probably disconnected. -2 Other error. */ int Hcb::read(char *buf_ptr, const unsigned long buf_len) { int nBytesRead; #ifdef WIN32 if (fd>1) #else if (fd>=0) #endif { nBytesRead=hca_read(fd, buf_ptr, buf_len); if (nBytesRead<0) { if (nBytesRead==-1) { if (nError<100) { nError++; return 0; } printf("client disconnected %d\n", fd); hca_close(fd); fd=HCB_NO_FD; } else { if (nError<100) { nError++; return 0; } hca_close(fd); fd=HCB_NO_FD; } } else { nError=0; } } #ifdef WIN32 else if (hSerialPort!=INVALID_HANDLE_VALUE) { nBytesRead=hca_read_handle(hSerialPort, buf_ptr, buf_len); if (nBytesRead<0) { hca_close_handle(hSerialPort); hSerialPort=INVALID_HANDLE_VALUE; } } else if (fd>=0) { if (kbhit() && (buf_len>0)) { *buf_ptr=getch(); D(GraphPrint(buf_ptr,1);) nBytesRead=1; } else { nBytesRead=0; } } #endif else { return -1; } return nBytesRead; } /* Accept a new socket for communication with a new client. */ /* Remember to delete the hca object when it is no longer needed. */ Hcb *Hcb::accept(int serversock, bool only_localhost) { const int newsock = hca_accept(serversock, only_localhost); if (newsock!=HCA_NO_FD) { Hcb *hcb=new Hcb(newsock); return(hcb); } return(NULL); } bool Hcb::isConnected() const { #ifdef WIN32 return fd!=HCB_NO_FD || hSerialPort!=INVALID_HANDLE_VALUE; #else return fd!=HCB_NO_FD ; #endif }; /*****************************************************************/ int Hcb::process() { if (!isConnected()) { D(printf("not connected\n");) return -1; } // send data while (!isEmpty()) { D(printf("send queue not empty\n");) const int n=head->write(this); if (n>0) { // not all data in buffer could be written. // we must wait before sending the remaining data. break; } if (n==0) { // all data in buffer was sent so we are done with that buffer. D(printf("buffer was sent, fd=%d\n",fd);) OutFifoPost *p=head; head=p->next; delete p; } else if (n<0) { // Something wrong. fprintf(stderr,"OutFifo::process: Hcb::write n=%d, fd=%d\n",n, fd); if (fd>=0) { hca_close(fd); fd=-1; } return -1; } } // receive { const int n=read(buf+bufIndex,(sizeof(buf)-1)-bufIndex); if (n>0) { if (echo) { write(buf+bufIndex, n); } bufIndex+=n; if (!transparentMode) { assert(bufIndex>=0); assert(bufIndex=0) { hca_close(fd); fd=-1; } return -1; } } return 0; } void Hcb::writeDone(char *str, int len) { assert(str==tail->str); assert(len<=tail->len); D(printf("Hcb::writeDone %d\n", len);) tail->len=len; } void Hcb::readDone(const char *str) { assert(str==buf); const int cmdLen=cmdEnd-buf; const int remaining=bufIndex-cmdLen-1; if (remaining>0) { movemem(buf, cmdEnd, remaining); bufIndex=remaining; } else { bufIndex=0; } cmdEnd=NULL; } bool Hcb::isEmpty() const { return head==NULL; } /* end of file */