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         // release thread for receiving event
00220         is_run = false;
00221         event_receiver.join();
00222         event_receiver.cancel();
00223 
00224         if (file_descriptor != INVALID_HANDLE_VALUE) {
00225                 // These return values, but assume it works ok, not really critical.
00226                 SetCommMask(file_descriptor, 0);
00227                 PurgeComm(file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
00228                 CloseHandle(file_descriptor);
00229                 file_descriptor = INVALID_HANDLE_VALUE;
00230         }
00231 
00232     is_open = false;
00233 }
00234 
00235 /*****************************************************************************
00236 ** Implementation [Serial][Threading Procedure]
00237 *****************************************************************************/
00238 void event_proc(void* arg) {
00239         Serial* owner = (Serial*)arg;
00240         if (!owner)
00241                 return;
00242         while (owner->is_run) {
00243                 DWORD mask;
00244                 if (!WaitCommEvent(owner->file_descriptor, &mask, NULL)) {
00245                         if (0x000003e3 == GetLastError()) {
00246                                 break;
00247                         }
00248                 }
00249         }
00250         owner->is_open = false;
00251 }
00252 
00253 /*****************************************************************************
00254 ** Implementation [Serial][Writing]
00255 *****************************************************************************/
00256 
00257 long Serial::write(const char &c) ecl_assert_throw_decl(StandardException) {
00258         return write(&c, 1);
00259 }
00260 
00261 long Serial::write(const char *s, unsigned long n) ecl_assert_throw_decl(StandardException) {
00262         DWORD   written, error, error_flags;
00263         COMSTAT comstat;
00264         int    result;
00265 
00266         result = WriteFile( file_descriptor, s, n, &written, &m_osWrite);
00267 
00268         if (!result) {
00269                 if (GetLastError() == ERROR_IO_PENDING) {
00270                         while (!GetOverlappedResult(file_descriptor, &m_osWrite, &written, TRUE)) {
00271                                 error = GetLastError();
00272                                 if (error != ERROR_IO_INCOMPLETE) {
00273                                         ClearCommError( file_descriptor, &error_flags, &comstat);
00274                                         break;
00275                                 }
00276                         }
00277                 } else {
00278                         written = 0;
00279                         ClearCommError(file_descriptor, &error_flags, &comstat);
00280                 }
00281         }
00282         ecl_assert_throw( written != 0, StandardException(LOC,WriteError) );
00283         return written;
00284 }
00285 
00286 /*****************************************************************************
00287 ** Implementation [Serial][Reading Modes]
00288 *****************************************************************************/
00289 
00290 void Serial::block(const long &timeout) ecl_assert_throw_decl(StandardException) {
00291 
00292         ecl_assert_throw( timeout >= 0, StandardException(LOC, InvalidInputError, "Serial port timeouts must be greater than 0ms.") );
00293     COMMTIMEOUTS    timeouts;
00294     GetCommTimeouts( file_descriptor, &timeouts );
00295 
00296     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00297     // about this combination of settings.
00298     timeouts.ReadIntervalTimeout = MAXDWORD;
00299     timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00300     timeouts.ReadTotalTimeoutConstant = static_cast<DWORD>(timeout);
00301     SetCommTimeouts( file_descriptor, &timeouts );
00302 }
00303 
00304 void Serial::unblock() {
00305     COMMTIMEOUTS    timeouts;
00306     GetCommTimeouts( file_descriptor, &timeouts );
00307 
00308     // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
00309     // about this combination of settings.
00310     timeouts.ReadIntervalTimeout = MAXDWORD;
00311     timeouts.ReadTotalTimeoutMultiplier = 0;
00312     timeouts.ReadTotalTimeoutConstant = 0;
00313     SetCommTimeouts( file_descriptor, &timeouts );
00314 }
00315 
00316 /*****************************************************************************
00317 ** Implementation [Serial][Reading]
00318 *****************************************************************************/
00319 long Serial::remaining() {
00320     unsigned long error;
00321     _COMSTAT status;
00322     if ( ClearCommError(file_descriptor,&error,&status) ) {
00323         return status.cbInQue;
00324     } else {
00325         return -1;
00326     }
00327 }
00328 
00329 long Serial::read(char &c) ecl_assert_throw_decl(StandardException) {
00330         return read(&c,1);
00331 }
00332 
00333 long Serial::read(char *s, const unsigned long &n) ecl_assert_throw_decl(StandardException)
00334 {
00335     COMSTAT comstat;
00336     DWORD   read=0, error, error_flags;
00337     DWORD dwRes;
00338 
00339     /*********************
00340     ** Windows Hack
00341     **********************/
00342     // If its blocking for a minimum number of characters,
00343     /*********************
00344     ** Reading Serial
00345     **********************/
00346     if (file_descriptor == INVALID_HANDLE_VALUE) {
00347         return 0;
00348     }
00349 
00350     if (!ReadFile( file_descriptor, s, n, &read, &m_osRead) ) {
00351         error = GetLastError();
00352 
00353         if( error != ERROR_IO_PENDING ) {
00354                 if (error != ERROR_ACCESS_DENIED)
00355                         ClearCommError(file_descriptor, &error_flags, &comstat);
00356 
00357             return 0;
00358         } else {
00359             dwRes = WaitForSingleObject( m_osRead.hEvent, INFINITE );
00360 
00361             switch( dwRes ) {
00362             case WAIT_OBJECT_0:
00363                 if( !GetOverlappedResult( file_descriptor, &m_osRead, &read, FALSE) ) {
00364                     ClearCommError(file_descriptor, &error_flags, &comstat);
00365                     return 0;
00366                 } else {
00367                     ClearCommError(file_descriptor, &error_flags, &comstat);
00368                     return read;
00369                 }
00370                 break;
00371             default:
00372                 {
00373                     ClearCommError(file_descriptor, &error_flags, &comstat);
00374                     return 0;
00375                 }
00376                 break;
00377             }
00378         }
00379     }
00380     return read;
00381 }
00382 } // namespace ecl
00383 
00384 #endif /* ECL_IS_WIN32 */


ecl_devices
Author(s): Daniel Stonier
autogenerated on Sun Oct 5 2014 23:35:57