$search
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 */