serial_w32.cpp
Go to the documentation of this file.
00001 
00009 /*****************************************************************************
00010 ** Platform Check
00011 *****************************************************************************/
00012 
00013 #include <ecl/config.hpp>
00014 #ifdef ECL_IS_WIN32
00015 
00016 /*****************************************************************************
00017 ** Includes
00018 *****************************************************************************/
00019 
00020 #include <ecl/exceptions/standard_exception.hpp>
00021 #include "../../include/ecl/devices/serial_w32.hpp"
00022 
00023 /*****************************************************************************
00024 ** Namespaces
00025 *****************************************************************************/
00026 
00027 namespace ecl {
00028 
00029 /*****************************************************************************
00030 ** Implementation [Serial][C&D]
00031 *****************************************************************************/
00032 
00033 Serial::Serial(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
00034                 const StopBits &stop_bits, const Parity &parity ) throw(StandardException) :
00035                                 port(port_name)
00036 {
00037         try {
00038                 open(port_name, baud_rate, data_bits, stop_bits, parity);
00039         } catch ( StandardException &e ) {
00040                 throw StandardException(LOC,e);
00041         }
00042 }
00043 
00044 Serial::~Serial() {
00045         close();
00046 }
00047 
00048 /*****************************************************************************
00049 ** Implementation [Serial][Open]
00050 *****************************************************************************/
00051 
00052 void Serial::open(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
00053                 const StopBits &stop_bits, const Parity &parity ) throw(StandardException) {
00054 
00055         if ( open() ) {
00056                 close();
00057         }
00058 
00059     if ( port_name.length() == 5 ) {
00060         /******************************************
00061          * Ugly windoze hack for COM# > 10
00062          ******************************************/
00063         // Assume COMxx call is basically correct and prefix
00064         port = std::string("\\\\.\\") + port_name;
00065     }
00066     /******************************************
00067      * Reset Timeouts
00068      ******************************************/
00069     m_osRead.Offset = 0;
00070     m_osRead.OffsetHigh = 0;
00071     if (! (m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
00072         throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
00073     }
00074     m_osWrite.Offset = 0;
00075     m_osWrite.OffsetHigh = 0;
00076     if (! (m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
00077         throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
00078     }
00079     /******************************************
00080      * Open the port handle
00081      ******************************************/
00082     /*
00083      * CreateFileA is the ascii function. CreateFileW is the unicode function.
00084      * FILE_FLAG_OVERLAPPED - do not block operations (use events to return)
00085      * If the above flag is not specified, it defaults to non-overlapped mode.
00086      * When in non-overlapped mode you can't sit in a thread trying to read the
00087      * port and simultaneously try to write from another.
00088      */
00089     file_descriptor = CreateFileA( port.c_str(),
00090                             GENERIC_READ | GENERIC_WRITE,
00091                             0,
00092                             NULL,
00093                             OPEN_EXISTING, // Serial ports already exist
00094                             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
00095                             NULL);
00096 
00097     /******************************************
00098      * Open - error handling
00099      ******************************************/
00100     if (file_descriptor == INVALID_HANDLE_VALUE ) {
00101         int error = GetLastError();
00102         switch (error) {
00103             case (ERROR_PATH_NOT_FOUND) : {
00104                 throw StandardException(LOC,OpenError);
00105                 break;
00106             }
00107             case (ERROR_FILE_NOT_FOUND) : {
00108                 throw StandardException(LOC,OpenError);
00109                 break;
00110             }
00111             default : {
00112                 throw StandardException(LOC,OpenError);
00113             }
00114         }
00115     }
00116 
00117     /******************************************
00118         ** Configuration
00119         *******************************************/
00120     static const int baud_rate_flags[] = { CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200 };
00121     static const int stop_bits_flags[] = { ONESTOPBIT, TWOSTOPBITS }; // Windoze has a ONE5STOPBITS as well
00122     static const int parity_types_flags[] = { NOPARITY, ODDPARITY, EVENPARITY }; // There are others but we dont need
00123     static const int data_bits_flags[] = { 5, 6, 7, 8 };
00124 
00125     /******************************************
00126      * Initialise events to monitor
00127      ******************************************/
00128     // EV_RXCHAR : A character was received and placed in the input buffer.
00129     SetCommMask( file_descriptor, EV_RXCHAR );
00130 
00131     /******************************************
00132      * Initialise the input/output buffer's size
00133      ******************************************/
00134     SetupComm( file_descriptor, 2048, 2048 ); // 4096,4096
00135 
00136     /******************************************
00137      * Clear
00138      ******************************************/
00139     PurgeComm( file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
00140 
00141         /******************************************
00142         * Dont care about write timeouts (could be dangerous!?)
00143         ******************************************/
00144         COMMTIMEOUTS    timeouts;
00145         GetCommTimeouts( file_descriptor, &timeouts );
00146         timeouts.WriteTotalTimeoutMultiplier = 0;
00147         timeouts.WriteTotalTimeoutConstant = 0;
00148         SetCommTimeouts( file_descriptor, &timeouts );
00149 
00150         /******************************************
00151         * Setup the Configuration Structure
00152         ******************************************/
00153         DCB dcb;
00154 
00155         FillMemory(&dcb, sizeof(dcb), 0);
00156 
00157         // MS recommends just grabbing the current state and modifying the required parameters on the
00158         // off chance the structure may change in the future.
00159         if ( !GetCommState( file_descriptor, &dcb) ) {
00160          throw StandardException(LOC,ConfigurationError,"There was an error retrieving the state of the port.");
00161         };
00162 
00163         /******************************************
00164         * Protocol Configuration
00165         ******************************************/
00166         dcb.DCBlength = sizeof(dcb);
00167         dcb.BaudRate = baud_rate_flags[baud_rate];
00168         dcb.ByteSize = data_bits_flags[data_bits];
00169         dcb.Parity = parity_types_flags[parity];
00170         dcb.StopBits = stop_bits_flags[stop_bits];
00171         dcb.fNull= FALSE; // If true, discard null bytes if they are received
00172 
00173         // dcb.fAbortOnError = TRUE; // Need?
00174 
00175         /******************************************
00176         * Flow Control
00177         ******************************************/
00178         dcb.fInX = FALSE; // XON/XOFF flow control during reception.
00179         dcb.fOutX = FALSE; // XON/XOFF flow control during transmission.
00180         dcb.fDtrControl = DTR_CONTROL_DISABLE; // Data-Terminal-Ready flow control
00181         dcb.fRtsControl = RTS_CONTROL_DISABLE; // Request-To-Send flow control.
00182 
00183         dcb.fOutxCtsFlow = FALSE; // don't monitor the Clear-To-Send output flow control signal.
00184         dcb.fOutxDsrFlow = FALSE; // don't monitor the Data-Set-Ready output flow control signal.
00185 
00186         // dcb.XonLim = 1000; // Number of bytes in the input buffer before flow control is activated to inhibit the sender.
00187         // dcb.XoffLim = 1000; // Similar situation as above...
00188         // dcb.XonChar = ASCII_XON;
00189         // dcb.XoffChar = ASCII_XOFF;
00190 
00191     /******************************************
00192      * Save and Handle Errors
00193      ******************************************/
00194     if (!SetCommState( file_descriptor, &dcb)) {
00195         int error = GetLastError();
00196         switch ( error ) {
00197             case ( ERROR_INVALID_PARAMETER ) : {
00198                 throw StandardException(LOC,ConfigurationError,"A parameter was configured incorrectly (ERROR_INVALID_PARAMETER).");
00199                 break;
00200             }
00201             default: {
00202                 throw StandardException(LOC,ConfigurationError);
00203                 break;
00204             }
00205         }
00206     }
00207 
00208     block(5000);
00209     is_open = true;
00210 }
00211 void Serial::close() {
00212         if ( open() ) {
00213             // These return values, but assume it works ok, not really critical.
00214             SetCommMask(file_descriptor, 0);
00215             PurgeComm(file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
00216             CloseHandle(file_descriptor);
00217             is_open = false;
00218         }
00219 }
00220 /*****************************************************************************
00221 ** Implementation [Serial][Writing]
00222 *****************************************************************************/
00223 
00224 long Serial::write(const char &c) ecl_assert_throw_decl(StandardException) {
00225         return write(&c, 1);
00226 }
00227 
00228 long Serial::write(const char *s, unsigned long n) ecl_assert_throw_decl(StandardException) {
00229         DWORD   written, error, error_flags;
00230         COMSTAT comstat;
00231         int    result;
00232 
00233         result = WriteFile( file_descriptor, s, n, &written, &m_osWrite);
00234 
00235         if (!result) {
00236                 if (GetLastError() == ERROR_IO_PENDING) {
00237                         while (!GetOverlappedResult(file_descriptor, &m_osWrite, &written, TRUE)) {
00238                                 error = GetLastError();
00239                                 if (error != ERROR_IO_INCOMPLETE) {
00240                                         ClearCommError( file_descriptor, &error_flags, &comstat);
00241                                         break;
00242                                 }
00243                         }
00244                 } else {
00245                         written = 0;
00246                         ClearCommError(file_descriptor, &error_flags, &comstat);
00247                 }
00248         }
00249         ecl_assert_throw( written != 0, StandardException(LOC,WriteError) );
00250         return written;
00251 }
00252 
00253 /*****************************************************************************
00254 ** Implementation [Serial][Reading Modes]
00255 *****************************************************************************/
00256 
00257 void Serial::block(const long &timeout) ecl_assert_throw_decl(StandardException) {
00258 
00259         ecl_assert_throw( timeout >= 0, StandardException(LOC, InvalidInputError, "Serial port timeouts must be greater than 0ms.") );
00260     COMMTIMEOUTS    timeouts;
00261     GetCommTimeouts( file_descriptor, &timeouts );
00262 
00263     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00264     // about this combination of settings.
00265     timeouts.ReadIntervalTimeout = MAXDWORD;
00266     timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00267     timeouts.ReadTotalTimeoutConstant = static_cast<DWORD>(timeout);
00268     SetCommTimeouts( file_descriptor, &timeouts );
00269 }
00270 
00271 void Serial::unblock() {
00272     COMMTIMEOUTS    timeouts;
00273     GetCommTimeouts( file_descriptor, &timeouts );
00274 
00275     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00276     // about this combination of settings.
00277     timeouts.ReadIntervalTimeout = MAXDWORD;
00278     timeouts.ReadTotalTimeoutMultiplier = 0;
00279     timeouts.ReadTotalTimeoutConstant = 0;
00280     SetCommTimeouts( file_descriptor, &timeouts );
00281 }
00282 
00283 /*****************************************************************************
00284 ** Implementation [Serial][Reading]
00285 *****************************************************************************/
00286 long Serial::remaining() {
00287     unsigned long error;
00288     _COMSTAT status;
00289     if ( ClearCommError(file_descriptor,&error,&status) ) {
00290         return status.cbInQue;
00291     } else {
00292         return -1;
00293     }
00294 }
00295 
00296 long Serial::read(char &c) ecl_assert_throw_decl(StandardException) {
00297         return read(&c,1);
00298 }
00299 
00300 long Serial::read(char *s, const unsigned long &n) ecl_assert_throw_decl(StandardException)
00301 {
00302     COMSTAT comstat;
00303     DWORD   read=0, error, error_flags;
00304     DWORD dwRes;
00305 
00306     /*********************
00307     ** Windows Hack
00308     **********************/
00309     // If its blocking for a minimum number of characters,
00310     /*********************
00311     ** Reading Serial
00312     **********************/
00313     if (!ReadFile( file_descriptor, s, n, &read, &m_osRead) ) {
00314         error = GetLastError();
00315 
00316         if( error != ERROR_IO_PENDING ) {
00317             ClearCommError(file_descriptor, &error_flags, &comstat);
00318 
00319             return 0;
00320         } else {
00321             dwRes = WaitForSingleObject( m_osRead.hEvent, INFINITE );
00322 
00323             switch( dwRes ) {
00324             case WAIT_OBJECT_0:
00325                 if( !GetOverlappedResult( file_descriptor, &m_osRead, &read, FALSE) ) {
00326                     ClearCommError(file_descriptor, &error_flags, &comstat);
00327                     return 0;
00328                 } else {
00329                     ClearCommError(file_descriptor, &error_flags, &comstat);
00330                     return read;
00331                 }
00332                 break;
00333             default:
00334                 {
00335                     ClearCommError(file_descriptor, &error_flags, &comstat);
00336                     return 0;
00337                 }
00338                 break;
00339             }
00340         }
00341     }
00342     return read;
00343 }
00344 } // namespace ecl
00345 
00346 #endif /* ECL_IS_WIN32 */


ecl_devices
Author(s): Daniel Stonier (d.stonier@gmail.com)
autogenerated on Thu Jan 2 2014 11:12:50