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), is_run(false), file_descriptor(INVALID_HANDLE_VALUE)
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 (strstr(port_name.c_str(), "\\\\.\\"))
00060                 port = port_name;
00061         else
00062                 port = std::string("\\\\.\\") + port_name;
00063 
00064     /******************************************
00065      * Reset Timeouts
00066      ******************************************/
00067     m_osRead.Offset = 0;
00068     m_osRead.OffsetHigh = 0;
00069     if (! (m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
00070         throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
00071     }
00072     m_osWrite.Offset = 0;
00073     m_osWrite.OffsetHigh = 0;
00074     if (! (m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
00075         throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
00076     }
00077 
00078     /******************************************
00079      * Open the port handle
00080      ******************************************/
00081     /*
00082      * CreateFileA is the ascii function. CreateFileW is the unicode function.
00083      * FILE_FLAG_OVERLAPPED - do not block operations (use events to return)
00084      * If the above flag is not specified, it defaults to non-overlapped mode.
00085      * When in non-overlapped mode you can't sit in a thread trying to read the
00086      * port and simultaneously try to write from another.
00087      */
00088     file_descriptor = CreateFileA( port.c_str(),
00089                             GENERIC_READ | GENERIC_WRITE,
00090                             0,
00091                             NULL,
00092                             OPEN_EXISTING, // Serial ports already exist
00093                             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
00094                             NULL);
00095 
00096     /******************************************
00097      * Open - error handling
00098      ******************************************/
00099     if (file_descriptor == INVALID_HANDLE_VALUE ) {
00100         int error = GetLastError();
00101         switch (error) {
00102             case (ERROR_PATH_NOT_FOUND) : {
00103                 throw StandardException(LOC,NotFoundError);
00104                 break;
00105             }
00106             case (ERROR_FILE_NOT_FOUND) : {
00107                 throw StandardException(LOC,NotFoundError);
00108                 break;
00109             }
00110             default : {
00111                 throw StandardException(LOC,OpenError);
00112             }
00113         }
00114     }
00115 
00116     /******************************************
00117         ** Configuration
00118         *******************************************/
00119     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 };
00120     static const int stop_bits_flags[] = { ONESTOPBIT, TWOSTOPBITS }; // Windoze has a ONE5STOPBITS as well
00121     static const int parity_types_flags[] = { NOPARITY, ODDPARITY, EVENPARITY }; // There are others but we dont need
00122     static const int data_bits_flags[] = { 5, 6, 7, 8 };
00123 
00124     /******************************************
00125      * Initialise events to monitor
00126      ******************************************/
00127     // EV_RXCHAR : A character was received and placed in the input buffer.
00128     SetCommMask( file_descriptor, EV_RXCHAR );
00129         
00130     /******************************************
00131      * Initialise the input/output buffer's size
00132      ******************************************/
00133     SetupComm( file_descriptor, 2048, 2048 ); // 4096,4096
00134 
00135     /******************************************
00136      * Clear
00137      ******************************************/
00138     PurgeComm( file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
00139 
00140         /******************************************
00141         * Dont care about write timeouts (could be dangerous!?)
00142         ******************************************/
00143         COMMTIMEOUTS    timeouts;
00144         GetCommTimeouts( file_descriptor, &timeouts );
00145         timeouts.WriteTotalTimeoutMultiplier = 0;
00146         timeouts.WriteTotalTimeoutConstant = 0;
00147         SetCommTimeouts( file_descriptor, &timeouts );
00148 
00149         /******************************************
00150         * Setup the Configuration Structure
00151         ******************************************/
00152         DCB dcb;
00153 
00154         FillMemory(&dcb, sizeof(dcb), 0);
00155 
00156         // MS recommends just grabbing the current state and modifying the required parameters on the
00157         // off chance the structure may change in the future.
00158         if ( !GetCommState( file_descriptor, &dcb) ) {
00159          throw StandardException(LOC,ConfigurationError,"There was an error retrieving the state of the port.");
00160         };
00161 
00162         /******************************************
00163         * Protocol Configuration
00164         ******************************************/
00165         dcb.DCBlength = sizeof(dcb);
00166         dcb.BaudRate = baud_rate_flags[baud_rate];
00167         dcb.ByteSize = data_bits_flags[data_bits];
00168         dcb.Parity = parity_types_flags[parity];
00169         dcb.StopBits = stop_bits_flags[stop_bits];
00170         dcb.fNull= FALSE; // If true, discard null bytes if they are received
00171 
00172         // dcb.fAbortOnError = TRUE; // Need?
00173 
00174         /******************************************
00175         * Flow Control
00176         ******************************************/
00177         dcb.fInX = FALSE; // XON/XOFF flow control during reception.
00178         dcb.fOutX = FALSE; // XON/XOFF flow control during transmission.
00179         dcb.fDtrControl = DTR_CONTROL_DISABLE; // Data-Terminal-Ready flow control
00180         dcb.fRtsControl = RTS_CONTROL_DISABLE; // Request-To-Send flow control.
00181 
00182         dcb.fOutxCtsFlow = FALSE; // don't monitor the Clear-To-Send output flow control signal.
00183         dcb.fOutxDsrFlow = FALSE; // don't monitor the Data-Set-Ready output flow control signal.
00184 
00185         // dcb.XonLim = 1000; // Number of bytes in the input buffer before flow control is activated to inhibit the sender.
00186         // dcb.XoffLim = 1000; // Similar situation as above...
00187         // dcb.XonChar = ASCII_XON;
00188         // dcb.XoffChar = ASCII_XOFF;
00189 
00190     /******************************************
00191      * Save and Handle Errors
00192      ******************************************/
00193     if (!SetCommState( file_descriptor, &dcb)) {
00194         int error = GetLastError();
00195         switch ( error ) {
00196             case ( ERROR_INVALID_PARAMETER ) : {
00197                 throw StandardException(LOC,ConfigurationError,"A parameter was configured incorrectly (ERROR_INVALID_PARAMETER).");
00198                 break;
00199             }
00200             default: {
00201                 throw StandardException(LOC,ConfigurationError);
00202                 break;
00203             }
00204         }
00205     }
00206 
00207         /******************************************
00208         * Prepare thread for receiving event generated by communication port
00209         ******************************************/
00210     is_run = true;
00211         event_receiver.cancel();
00212     event_receiver.start(generateFunctionObject(event_proc, this));
00213 
00214     block(5000);
00215     is_open = true;
00216 }
00217 
00218 void Serial::close() {
00219         if ( open() ) {
00220 
00221                 // release thread for receiving event
00222                 is_run = false;
00223                 event_receiver.join();
00224                 event_receiver.cancel();
00225 
00226                 if (file_descriptor != INVALID_HANDLE_VALUE) {
00227                         // These return values, but assume it works ok, not really critical.
00228                         SetCommMask(file_descriptor, 0);
00229                         PurgeComm(file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
00230                         CloseHandle(file_descriptor);
00231                         file_descriptor = INVALID_HANDLE_VALUE;
00232                 }
00233 
00234             is_open = false;
00235         }
00236 }
00237 
00238 /*****************************************************************************
00239 ** Implementation [Serial][Threading Procedure]
00240 *****************************************************************************/
00241 void event_proc(void* arg) {
00242         Serial* owner = (Serial*)arg;
00243         if (!owner)
00244                 return;
00245         while (owner->is_run) {
00246                 DWORD mask;
00247                 if (!WaitCommEvent(owner->file_descriptor, &mask, NULL)) {
00248                         if (0x000003e3 == GetLastError()) {
00249                                 break;
00250                         }
00251                 }
00252         }
00253         owner->file_descriptor = INVALID_HANDLE_VALUE;
00254         owner->is_open = false;
00255 }
00256 
00257 /*****************************************************************************
00258 ** Implementation [Serial][Writing]
00259 *****************************************************************************/
00260 
00261 long Serial::write(const char &c) ecl_assert_throw_decl(StandardException) {
00262         return write(&c, 1);
00263 }
00264 
00265 long Serial::write(const char *s, unsigned long n) ecl_assert_throw_decl(StandardException) {
00266         DWORD   written, error, error_flags;
00267         COMSTAT comstat;
00268         int    result;
00269 
00270         result = WriteFile( file_descriptor, s, n, &written, &m_osWrite);
00271 
00272         if (!result) {
00273                 if (GetLastError() == ERROR_IO_PENDING) {
00274                         while (!GetOverlappedResult(file_descriptor, &m_osWrite, &written, TRUE)) {
00275                                 error = GetLastError();
00276                                 if (error != ERROR_IO_INCOMPLETE) {
00277                                         ClearCommError( file_descriptor, &error_flags, &comstat);
00278                                         break;
00279                                 }
00280                         }
00281                 } else {
00282                         written = 0;
00283                         ClearCommError(file_descriptor, &error_flags, &comstat);
00284                 }
00285         }
00286         ecl_assert_throw( written != 0, StandardException(LOC,WriteError) );
00287         return written;
00288 }
00289 
00290 /*****************************************************************************
00291 ** Implementation [Serial][Reading Modes]
00292 *****************************************************************************/
00293 
00294 void Serial::block(const long &timeout) ecl_assert_throw_decl(StandardException) {
00295 
00296         ecl_assert_throw( timeout >= 0, StandardException(LOC, InvalidInputError, "Serial port timeouts must be greater than 0ms.") );
00297     COMMTIMEOUTS    timeouts;
00298     GetCommTimeouts( file_descriptor, &timeouts );
00299 
00300     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00301     // about this combination of settings.
00302     timeouts.ReadIntervalTimeout = MAXDWORD;
00303     timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00304     timeouts.ReadTotalTimeoutConstant = static_cast<DWORD>(timeout);
00305     SetCommTimeouts( file_descriptor, &timeouts );
00306 }
00307 
00308 void Serial::unblock() {
00309     COMMTIMEOUTS    timeouts;
00310     GetCommTimeouts( file_descriptor, &timeouts );
00311 
00312     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00313     // about this combination of settings.
00314     timeouts.ReadIntervalTimeout = MAXDWORD;
00315     timeouts.ReadTotalTimeoutMultiplier = 0;
00316     timeouts.ReadTotalTimeoutConstant = 0;
00317     SetCommTimeouts( file_descriptor, &timeouts );
00318 }
00319 
00320 /*****************************************************************************
00321 ** Implementation [Serial][Reading]
00322 *****************************************************************************/
00323 long Serial::remaining() {
00324     unsigned long error;
00325     _COMSTAT status;
00326     if ( ClearCommError(file_descriptor,&error,&status) ) {
00327         return status.cbInQue;
00328     } else {
00329         return -1;
00330     }
00331 }
00332 
00333 long Serial::read(char &c) ecl_assert_throw_decl(StandardException) {
00334         return read(&c,1);
00335 }
00336 
00337 long Serial::read(char *s, const unsigned long &n) ecl_assert_throw_decl(StandardException)
00338 {
00339     COMSTAT comstat;
00340     DWORD   read=0, error, error_flags;
00341     DWORD dwRes;
00342 
00343     /*********************
00344     ** Windows Hack
00345     **********************/
00346     // If its blocking for a minimum number of characters,
00347     /*********************
00348     ** Reading Serial
00349     **********************/
00350     if (file_descriptor == INVALID_HANDLE_VALUE) {
00351         return 0;
00352     }
00353 
00354     if (!ReadFile( file_descriptor, s, n, &read, &m_osRead) ) {
00355         error = GetLastError();
00356 
00357         if( error != ERROR_IO_PENDING ) {
00358                 if (error != ERROR_ACCESS_DENIED)
00359                         ClearCommError(file_descriptor, &error_flags, &comstat);
00360 
00361             return 0;
00362         } else {
00363             dwRes = WaitForSingleObject( m_osRead.hEvent, INFINITE );
00364 
00365             switch( dwRes ) {
00366             case WAIT_OBJECT_0:
00367                 if( !GetOverlappedResult( file_descriptor, &m_osRead, &read, FALSE) ) {
00368                     ClearCommError(file_descriptor, &error_flags, &comstat);
00369                     return 0;
00370                 } else {
00371                     ClearCommError(file_descriptor, &error_flags, &comstat);
00372                     return read;
00373                 }
00374                 break;
00375             default:
00376                 {
00377                     ClearCommError(file_descriptor, &error_flags, &comstat);
00378                     return 0;
00379                 }
00380                 break;
00381             }
00382         }
00383     }
00384     return read;
00385 }
00386 } // namespace ecl
00387 
00388 #endif /* ECL_IS_WIN32 */


ecl_devices
Author(s): Daniel Stonier
autogenerated on Thu Jun 6 2019 21:18:03