/* * CSelectableCore.cpp * * Created on: 20 May 2016 * Author: wentzelc */ // redA Libraries #include "SelectableCore.h" #include "TimingCore.h" #include "LogCore.h" // Standard C/C++ Libraries #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- CSelectableCore::CSelectableCore( const char * Name, CSelect * Selector, EDebugLevel pDebugLevel, int pOutputDisplay ) : CFunctionCore( Name, pDebugLevel, pOutputDisplay ) { // Handles FirstHandle = NULL; // Select Select = Selector; } //--------------------------------------------------------------------------- CSelectableCore::~CSelectableCore() { THandle * NextHandle = NULL; // Destroy File Handles while (FirstHandle) { // Close handle if open if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen)) { Close( FirstHandle, true ); } NextHandle = FirstHandle->Next; DestroyHandle( FirstHandle ); FirstHandle = NextHandle; } } //--------------------------------------------------------------------------- THandle * CSelectableCore::CreateHandle( const char * HandleName, bool CreateLocalIO, bool AutoManage ) { THandle ** Handle = NULL; // Find Handle by Name or get end of list Handle = &FirstHandle; while ( *Handle && strcmp( HandleName, (*Handle)->Name )) Handle = &((*Handle)->Next); // Create if necessary if (!*Handle) { // Create File handle at end of list *Handle = (THandle*)malloc( sizeof(THandle) ); memset( *Handle, 0, sizeof(THandle) ); // Set name if (HandleName) { (*Handle)->Name = (char*)malloc( strlen(HandleName)+1 ); strcpy( (*Handle)->Name, HandleName ); } // Set File Descriptor (*Handle)->FD = -1; // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Created", Name, HandleName ); } // Create Matching IO point if (CreateLocalIO) { (*Handle)->LocalIO = AddLocalIO( HandleName ); } // Set other params (*Handle)->Auto = AutoManage; return *Handle; } //--------------------------------------------------------------------------- bool CSelectableCore::RemoveHandle( THandle * Handle ) { THandle ** HandlePtr = NULL; // Validate if (!Handle || (Handle->State == csOpen) || (Handle->State == csWaitingtoOpen)) { return false; } // Find in List HandlePtr = &FirstHandle; while (*HandlePtr && (*HandlePtr != Handle)) { HandlePtr = &((*HandlePtr)->Next); } // Remove from list if found if (*HandlePtr) { *HandlePtr = (*HandlePtr)->Next; } // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Removed", Name, Handle->Name ); // Destroy Child handle DestroyHandle( Handle ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::DestroyHandle( THandle * Handle ) { // Validate Handle if (!Handle) return false; // Clear parameters if (Handle->Name) free( Handle->Name ); if (Handle->FileName) free( Handle->FileName ); if (Handle->Address) free( Handle->Address ); // Destroy Buffers if (Handle->InBuffer) delete Handle->InBuffer; if (Handle->OutBuffer) delete Handle->OutBuffer; // Clear Input Markers if (Handle->InMarker) free( Handle->InMarker ); // Destroy Pointer free( Handle ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetPortHandle( THandle * Handle, const char * FileName ) { // Validate if (!Handle || (Handle->Type != ctNone) || !FileName) { return false; } // Set Type Handle->Type = ctPort; // Clear File Name if (Handle->FileName) { free( Handle->FileName ); } // Set name Handle->FileName = (char*)malloc( strlen(FileName)+1 ); strcpy( Handle->FileName, FileName ); // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as Port [%s]", Name, Handle->Name, FileName ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, const char * Address, const int PortNo, bool KeepAlive ) { // Validate if (!Handle || (Handle->Type != ctNone) || !Address || (Type == ctNone) || (Type == ctPort)) { return false; } // Set Type Handle->Type = Type; Handle->KeepAlive = KeepAlive; // Clear Address if (Handle->Address) { free( Handle->Address ); } // Set Address & Port Handle->Address = (char*)malloc( strlen(Address)+1 ); strcpy( Handle->Address, Address ); Handle->PortNo = PortNo; // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as %s [%s:%d]", Name, Handle->Name, ConnectTypeName[Type], Address, PortNo ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::ClearHandle( THandle * Handle ) { // Validate if (!Handle) { return false; } // Reset Type related parameters if (Handle->FileName) { free( Handle->FileName ); Handle->FileName = NULL; } if (Handle->Address) { free( Handle->Address ); Handle->Address = NULL; } Handle->PortNo = 0; // Reset Parameters Handle->Type = ctNone; // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as None", Name, Handle->Name ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetBuffers( THandle * Handle, int InBufSize, int OutBufSize, int InTimeout, const char * InMarker, int InMarkerLen ) { // Validate if (!Handle) { return false; } // Input Buffer if (Handle->InBuffer) { delete Handle->InBuffer; Handle->InBuffer = NULL; } if (InBufSize) { Handle->InBuffer = new CBuffer( InBufSize ); } // Output Buffer if (Handle->OutBuffer) { delete Handle->OutBuffer; Handle->OutBuffer = NULL; } if (OutBufSize) { Handle->OutBuffer = new CBuffer( OutBufSize ); } // Set Input Timeout Handle->InTimeout = InTimeout; // Set Input Markers if (InMarkerLen && InMarker) { Handle->InMarkerLen = InMarkerLen; Handle->InMarker = (char *)malloc( InMarkerLen+1 ); memcpy( Handle->InMarker, InMarker, InMarkerLen ); Handle->InMarker[InMarkerLen] = 0; } return true; } //--------------------------------------------------------------------------- int CSelectableCore::OpenPort( THandle * Handle ) { // Validate if (!Handle || (Handle->Type == ctNone)) { return -1; } else if (Handle->State == csOpen) { return Handle->FD; } // Check if port exits if (access( Handle->FileName, F_OK ) != 0) { // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port not found [%s]", Name, Handle->Name, Handle->FileName ); return -1; } // Open Port Handle->FD = open( Handle->FileName, O_RDWR ); if (Handle->FD == -1) { // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not open Port [%s]", Name, Handle->Name, Handle->FileName ); return -1; } // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port opened [%s]", Name, Handle->Name, Handle->FileName ); // Add to Select Lists if (Select) { Select->Add( Handle->FD, true, false, this ); } // Set state Handle->State = csOpen; return Handle->FD; } //--------------------------------------------------------------------------- int CSelectableCore::OpenServerSocket( THandle * Handle ) { socklen_t addr_len; struct sockaddr_in address; // Socket options struct linger ServerLinger_opt; ServerLinger_opt.l_onoff = 1; ServerLinger_opt.l_linger = 5; int Reuse_opt = 1; int KeepAlive_opt = 1; int TCPidle_opt = 60; int TCPint_opt = 15; int TCPcnt_opt = 3; // Validate Handle if (Handle->Type != ctServer) { return false; } // Create address address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(Handle->Address); address.sin_port = htons(Handle->PortNo); addr_len = sizeof(address); // Create socket if ((Handle->FD = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to create Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set state Handle->State = csFailed; return -1; }; // Configure connection if ((setsockopt( Handle->FD, SOL_SOCKET, SO_LINGER, &ServerLinger_opt, sizeof(ServerLinger_opt)) == -1) || (setsockopt( Handle->FD, SOL_SOCKET, SO_REUSEADDR, &Reuse_opt, sizeof(Reuse_opt)) == -1)) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not set socket options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set state Handle->State = csFailed; return -1; } // Configure TCP keep alive settings if (Handle->KeepAlive && ((setsockopt( Handle->FD, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) )) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not set KeepAlive options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set state Handle->State = csFailed; return -1; } // Set non-blocking flag int flags = fcntl( Handle->FD, F_GETFL, 0 ); fcntl( Handle->FD, F_SETFL, flags | O_NONBLOCK ); // Bind socket if (bind( Handle->FD, (struct sockaddr *)&address, addr_len ) < 0) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to bind Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set state close( Handle->FD ); Handle->FD = -1; Handle->State = csFailed; return -1; }; // Create que for 5 connections if (listen( Handle->FD, 5 ) < 0) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to listen on Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set state close( Handle->FD ); Handle->FD = -1; Handle->State = csFailed; return -1; }; // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server binded and listening [%s:%d]", Name, Handle->Name, Handle->Address, Handle->PortNo ); // Add to Select Lists if (Select) { Select->Add( Handle->FD, true, false, this ); } // Set state Handle->State = csOpen; return Handle->FD; } //--------------------------------------------------------------------------- int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) { THandle ** RemoteClient; int ClientFD; char ClientAddress[100]; char ClientName[100]; socklen_t addr_len; struct sockaddr_in address; // Validate if (!Handle) { return -1; } // Check Handle type if (Handle->Type == ctServer) { // Accept connection on current socket addr_len = sizeof( address ); if ((ClientFD = accept( Handle->FD, (struct sockaddr *)&address, &addr_len)) == -1) { // Log Event if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server failed to accept blocking connection (%s)", Name, Handle->Name, strerror(errno) ); else LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server failed to accept connection (%s)", Name, Handle->Name, strerror(errno) ); return -1; } // Set non-blocking flag int flags = fcntl( ClientFD, F_GETFL, 0 ); fcntl( ClientFD, F_SETFL, flags | O_NONBLOCK ); // Return client address strcpy( ClientAddress, inet_ntoa(address.sin_addr) ); // Get end of client list RemoteClient = &FirstHandle; while (*RemoteClient) { RemoteClient = &((*RemoteClient)->Next); } // Create Remote Client Handle sprintf( ClientName, "%s-%d", Handle->Name, ClientFD ); *RemoteClient = CreateHandle( ClientName, false, false ); SetSocketHandle( *RemoteClient, ctRemoteClient, ClientAddress, 0, Handle->KeepAlive ); // Copy Parent Buffer setup SetBuffers( *RemoteClient, ((Handle->InBuffer)? Handle->InBuffer->Size() : 0), ((Handle->OutBuffer)? Handle->OutBuffer->Size() : 0), Handle->InTimeout, Handle->InMarker, Handle->InMarkerLen ); // Set Key parameters (*RemoteClient)->FD = ClientFD; (*RemoteClient)->Parent = Handle; (*RemoteClient)->State = csWaitingtoOpen; // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server accepted Remote Client connection [%s]", Name, Handle->Name, ClientAddress ); // Add to Select Lists if (Select) { Select->Add( (*RemoteClient)->FD, true, true, this ); } return (*RemoteClient)->FD; } else if (Handle->Type == ctRemoteClient) { // Check state if (Handle->State == csOpen) { // Already open return Handle->FD; } else if (Handle->State == csWaitingtoOpen) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Remote Client connection open [%s]", Name, Handle->Name, Handle->Address ); // Update state Handle->State = csOpen; return Handle->FD; } } return -1; } //--------------------------------------------------------------------------- int CSelectableCore::OpenClientSocket( THandle * Handle ) { socklen_t addr_len; struct sockaddr_in address; // Socket options int KeepAlive_opt = 1; int TCPidle_opt = 5; int TCPcnt_opt = 3; int TCPint_opt = 2; // Check state if (Handle->State == csOpen) { // Already open return Handle->FD; } if (Handle->State != csWaitingtoOpen) { // Create File descriptor if ((Handle->FD = socket( AF_INET, SOCK_STREAM, 0 )) < 0) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to create Client socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set Status Handle->State = csFailed; return -1; }; // Set Non blocking open int flags = fcntl( Handle->FD, F_GETFL, 0 ); fcntl( Handle->FD, F_SETFL, O_NONBLOCK|flags ); // Configure TCP keep alive settings if (Handle->KeepAlive && ((setsockopt( Handle->FD, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) || (setsockopt( Handle->FD, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) )) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle %s - Could not set KeepAlive options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Set State close( Handle->FD ); Handle->FD = -1; Handle->State = csFailed; return -1; } } // Declare address address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr( Handle->Address ); address.sin_port = htons( Handle->PortNo ); addr_len = sizeof(address); if (!connect( Handle->FD, (struct sockaddr *)&address, addr_len )) { LogMessage( DebugLevel, dlMedium, "%s: Handle %s - Client connected [%s:%d]", Name, Handle->Name, Handle->Address, Handle->PortNo ); // Add to Select Lists if (Select) { Select->Add( Handle->FD, true, true, this ); } // Set status Handle->State = csOpen; return Handle->FD; } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINPROGRESS) || (errno == EALREADY)) { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client waiting to connect [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Add to Select Lists if (Select) { Select->Add( Handle->FD, true, true, this ); } // Set status Handle->State = csWaitingtoOpen; return Handle->FD; } else { // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client could not connect [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) ); // Remove from Select List if (Select) { Select->Remove( Handle->FD, true, true ); } // Close socket close( Handle->FD ); Handle->FD = -1; Handle->State = csFailed; return -1; } } //--------------------------------------------------------------------------- int CSelectableCore::Open( THandle * Handle ) { int FD = -1; // Validate if (!Handle) { return -1; } // Open correctly switch (Handle->Type) { case ctPort : FD = OpenPort( Handle ); break; case ctServer : FD = OpenServerSocket( Handle ); break; case ctClient : FD = OpenClientSocket( Handle ); break; case ctRemoteClient : FD = OpenRemoteClientSocket( Handle ); break; default: FD = -1; } return FD; }; //--------------------------------------------------------------------------- // Delete socket bool CSelectableCore::Close( THandle * Handle, bool CloseChildren ) { bool Fail; THandle * ChildHandle = NULL; THandle * NextHandle = NULL; // Validate if (!Handle || (Handle->FD == -1)) return false; // Close Children if (CloseChildren && (Handle->Type == ctServer)) { ChildHandle = FirstHandle; while (ChildHandle) { if (ChildHandle->Parent == Handle) { NextHandle = ChildHandle->Next; // Close Close( ChildHandle ); // Next Handle ChildHandle = NextHandle; } else { // Next Handle ChildHandle = ChildHandle->Next; } } } // Close Handle Fail = (close( Handle->FD ))? true : false; Handle->State = ((Fail)? csFailed : csClosed); // Show action switch (Handle->Type) { case ctPort: // Log Event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->FileName ); break; case ctServer: LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server %s [%s:%d]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address, Handle->PortNo ); break; case ctRemoteClient: LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Remote Client connection %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address ); break; case ctClient: LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client connection %s [%s:%d]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address, Handle->PortNo ); break; case ctNone: default: LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - %s, invalid Handle type", Name, Handle->Name, ((Fail)? "failed" : "closed") ); break; }; // Remove from Select List if (!Fail && Select) { Select->Remove( Handle->FD, true, true ); } // Reset FD Handle->FD = ((Fail)? Handle->FD : -1); return true; } //--------------------------------------------------------------------------- // Device Interface bool CSelectableCore::Read( THandle * Handle ) { int ClientFD = -1; int BytesRead = 0; int BytesWaiting = -1; // Validate if (!Handle) { return false; } // Log Read Event LogMessage( DebugLevel, dlHigh, "%s: Handle '%s' - Read Event", Name, Handle->Name ); // Check for closing/opening event on Socket if (Handle->Type == ctServer) { // Incoming client request ClientFD = OpenRemoteClientSocket( Handle ); if (ClientFD != -1) { // Add to Select Lists if (Select) { Select->Add( ClientFD, true, true, this ); } return true; } } else if ((Handle->Type == ctRemoteClient) || (Handle->Type == ctClient)) { // Check if socket ready (non-block open in progress) if (Handle->State == csWaitingtoOpen) { if (Handle->Type == ctRemoteClient) { OpenRemoteClientSocket( Handle ); } else if (Handle->Type == ctClient) { OpenClientSocket( Handle ); } return true; } // Check if anything to read ioctl( Handle->FD, FIONREAD, &BytesWaiting ); // EOF from server if (!BytesWaiting) { // Close Handle Close( Handle ); // Destroy Remote Client if (Handle->Type == ctRemoteClient) { RemoveHandle( Handle ); } return false; } } // Validate if (Handle->State != csOpen) { return false; } // Read File directly into buffer if (!Handle->InBuffer || !(BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD ))) { return false; } // Process Buffer ProcessBuffer( Handle, false ); // Reset timer SetStartTime( &(Handle->InStart) ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::Write( THandle * Handle ) { int Len = 0; char * Data = NULL; int BytesWritten = 0; // Validate if (!Handle) { return false; } // Log Ready for Write Event LogMessage( DebugLevel, dlHigh, "%s: Handle '%s' - Write Event", Name, Handle->Name ); if (Handle->State == csWaitingtoOpen) { // Complete socket open process if (Handle->Type == ctRemoteClient) { OpenRemoteClientSocket( Handle ); } else if (Handle->Type == ctClient) { OpenClientSocket( Handle ); } // Remove from set for select write if (Select) { Select->Remove( Handle->FD, false, true ); } return true; } else if (Handle->State == csOpen) { if (Handle->OutBuffer) { // Write to FD directly from buffer if ((BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD ))) { if (DebugLevel >= dlHigh) { // Show event Len = Handle->OutBuffer->Peek( &Data ); ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name ); } // Update Buffer Handle->OutBuffer->Clear( BytesWritten ); } // Check if Buffer emtpy if (!Handle->OutBuffer->Len()) { // Add to Select Write list if (Select) { Select->Remove( Handle->FD, false, true ); } } } else { // No Output buffer, so remove from Write list if (Select) { Select->Remove( Handle->FD, false, true ); } } return true; } return false; } //--------------------------------------------------------------------------- bool CSelectableCore::ProcessBuffer( THandle * Handle, bool Force ) { int Pos = 0; int Len = 0; char * Data = NULL; TLocalIO * LocalIO = NULL; TLinkedIO * Output = NULL; // Check if buffered data if (!Handle || !Handle->InBuffer || !Handle->InBuffer->Len()) { return false; } // Check if forced processed if (Force) { // Show Packet Len = Handle->InBuffer->Peek( &Data ); ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - IN-F:", Name, Handle->Name ); // Write buffer to Outputs if (Handle->Type == ctRemoteClient) { LocalIO = Handle->Parent->LocalIO; } else { LocalIO = Handle->LocalIO; } if (LocalIO) { Output = LocalIO->FirstOutput; while (Output) { Output->Function->Input( Output->IOName, Data, Len ); Output = Output->Next; } } // Clear processed bytes from buffer Handle->InBuffer->Clear( Len ); } else { // Search for end of packet marker while (Handle->InBuffer->FindStr( Handle->InMarker, Handle->InMarkerLen, Pos )) { // Show Packet Len = Handle->InBuffer->Peek( &Data, 0, Pos+1 ); ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - IN-M:", Name, Handle->Name ); // Write buffer to Outputs if (Handle->Type == ctRemoteClient) { LocalIO = Handle->Parent->LocalIO; } else { LocalIO = Handle->LocalIO; } if (LocalIO) { Output = LocalIO->FirstOutput; while (Output) { Output->Function->Input( Output->IOName, Data, Len ); Output = Output->Next; } } // Clear processed bytes from buffer Handle->InBuffer->Clear( Len ); } } return true; } //--------------------------------------------------------------------------- int CSelectableCore::ReadFromFD( int FD, char * Data, int MaxLen ) { int BytesRead = 0; int TotalRead = 0; int DataRemain = 0; // Check if buffer created if ((FD == -1) || !Data) { return 0; } // Read Data into buffer DataRemain = (MaxLen == -1)? strlen(Data) : MaxLen; while (DataRemain) { // Read from file descriptor BytesRead = read( FD, Data, DataRemain ); if (BytesRead <= 0) break; // Update Data Pointers TotalRead += BytesRead; DataRemain -= BytesRead; } return TotalRead; } //--------------------------------------------------------------------------- int CSelectableCore::WriteToFD( int FD, const char * Data, int Len ) { int BytesWritten = 0; int TotalWritten = 0; int DataRemain = 0; // Check if buffer created if ((FD == -1) || !Data) { return 0; } // Read Data into buffer DataRemain = (Len == -1)? strlen(Data) : Len; while (DataRemain) { // Read from file descriptor BytesWritten = write( FD, Data, DataRemain ); //write( FD, "\n", 1 ); if (BytesWritten <= 0) break; // Update Data Pointers TotalWritten += BytesWritten; DataRemain -= BytesWritten; } return TotalWritten; } //--------------------------------------------------------------------------- int CSelectableCore::Input( const char * IOName, const char * Data, int Len ) { THandle * Handle = NULL; THandle * ChildHandle = NULL; int BytesWritten = 0; // Validate if (!IOName || !Data) { return 0; } else if (Len == -1) { Len = strlen( Data ); } // Get File handle if (!(Handle = GetHandle( IOName ))) { // Log event LogMessage( DebugLevel, dlHigh, "%s: Local IO '%s' - Input rejected, Input not found", Name, IOName ); return 0; } // Log event ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: LocalIO '%s' - IN:", Name, IOName ); if (Handle->Type == ctServer) { // Cannot write to server socket, so Update Remote Client connections individually ChildHandle = FirstHandle; while (ChildHandle) { // Check if child if (ChildHandle->Parent == Handle) { // Decide where to put data if (ChildHandle->OutBuffer) { // Write to buffer BytesWritten = ChildHandle->OutBuffer->Push( Data, Len ); // Add to select write list if (BytesWritten) { Select->Add( ChildHandle->FD, false, true, this ); } } else { // Show event ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, ChildHandle->Name ); // Write directly to handle BytesWritten = WriteToFD( ChildHandle->FD, Data, Len ); } } // Next ChildHandle = ChildHandle->Next; } // Cannot verify individually, so assume all bytes was written BytesWritten = Len; } else { // Decide where to put data if (Handle->OutBuffer) { // Write to buffer BytesWritten = Handle->OutBuffer->Push( Data, Len ); // Add to select write list if (BytesWritten) { Select->Add( Handle->FD, false, true, this ); } } else { // Show event ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name ); // Write directly to handle BytesWritten = WriteToFD( Handle->FD, Data, Len ); } } return BytesWritten; } //--------------------------------------------------------------------------- bool CSelectableCore::Process() { THandle * Handle = NULL; // Check all handles Handle = FirstHandle; while (Handle) { // Auto manage handles if (Handle->Auto && ((Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed))) { // Reopen handle Open( Handle ); } // Check Input buffers if (Handle->InBuffer && (Handle->InBuffer->Len() > 0)) { // Check duration since last PortIn if (Timeout( Handle->InStart, Handle->InTimeout )) { // Process Input ProcessBuffer( Handle, true ); // Reset timer SetInterval( &(Handle->InStart), 0 ); } } Handle = Handle->Next; } return true; } //--------------------------------------------------------------------------- // Set serial port configuration parameters bool CSelectableCore::SerialConfig( THandle * Handle, int Baud, short DataBits, short StopBits, short Parity, short FlowCtrl, int Wait ) { struct termios newtio; int flags = 0; speed_t _baud = 0; int mcs = 0; // Get Handle if (!Handle || (Handle->Type != ctPort)) { return false; } // Flush Data from port tcflush( Handle->FD, TCIFLUSH ); // Set Open flags flags = fcntl( Handle->FD, F_GETFL, 0 ); fcntl( Handle->FD, F_SETFL, flags & ~O_NDELAY ); // Read options if (tcgetattr( Handle->FD, &newtio ) != 0) return false; // Process the baud rate switch (Baud) { case 921600: _baud = B921600; break; case 576000: _baud = B576000; break; case 460800: _baud = B460800; break; case 230400: _baud = B230400; break; //case 128000: _baud = B128000; break; case 115200: _baud = B115200; break; //case 76800: _baud = B76800; break; case 57600: _baud = B57600; break; case 38400: _baud = B38400; break; //case 28800: _baud = B28800; break; case 19200: _baud = B19200; break; //case 14400: _baud = B14400; break; case 9600: _baud = B9600; break; case 4800: _baud = B4800; break; case 2400: _baud = B2400; break; case 1800: _baud = B1800; break; case 1200: _baud = B1200; break; case 600: _baud = B600; break; case 300: _baud = B300; break; case 200: _baud = B200; break; case 150: _baud = B150; break; case 134: _baud = B134; break; case 110: _baud = B110; break; case 75: _baud = B75; break; case 50: _baud = B50; break; default: _baud = B9600; break; } // Set Baud rate cfsetospeed( &newtio, (speed_t)_baud ); cfsetispeed( &newtio, (speed_t)_baud ); // Generate Mark/Space parity if ((DataBits == 7) && ((Parity == MARK_PARITY) || (Parity == SPACE_PARITY))) DataBits = 8; // Process the data bits newtio.c_cflag &= ~CSIZE; switch (DataBits) { case 5: newtio.c_cflag |= CS5; break; case 6: newtio.c_cflag |= CS6; break; case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; default: newtio.c_cflag |= CS8; break; } // Set other flags newtio.c_cflag |= CLOCAL | CREAD; // Process Parity newtio.c_cflag &= ~(PARENB | PARODD); if (Parity == EVEN_PARITY) newtio.c_cflag |= PARENB; else if (Parity == ODD_PARITY) newtio.c_cflag |= (PARENB | PARODD); // Flow Control (for now) newtio.c_cflag &= ~CRTSCTS; // Process Stop Bits if (StopBits == 2) newtio.c_cflag |= CSTOPB; else newtio.c_cflag &= ~CSTOPB; newtio.c_iflag = IGNBRK; // Software Flow Control if (FlowCtrl == SW_FLOWCTRL) newtio.c_iflag |= IXON | IXOFF; else newtio.c_iflag &= ~(IXON | IXOFF | IXANY); // Set RAW input & output newtio.c_lflag=0; newtio.c_oflag=0; // Set wait parameters newtio.c_cc[VTIME] = Wait; // Blocking: Allow at least a tenth of a second to wait for data newtio.c_cc[VMIN] = 0; // Non-blocking: Don't set min bytes to receive // Set Options (first time) //tcflush( Handle->FD, TCIFLUSH); if (tcsetattr( Handle->FD, TCSANOW, &newtio)!=0) return false; // Set Terminal options ioctl( Handle->FD, TIOCMGET, &mcs); mcs |= TIOCM_RTS; ioctl( Handle->FD, TIOCMSET, &mcs); // Get Options (again) if (tcgetattr( Handle->FD, &newtio) != 0) return false; // Process Hardware Flow Control if (FlowCtrl == HW_FLOWCTRL) newtio.c_cflag |= CRTSCTS; else newtio.c_cflag &= ~CRTSCTS; // Set Options (second time) if (tcsetattr( Handle->FD, TCSANOW, &newtio ) != 0) { // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port not configured", Name, Handle->Name ); return false; } // Port configured // Log event LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port configured", Name, Handle->Name ); return true; } //---------------------------------------------------------------------------