00001 /*! \file Cmt1.cpp 00002 00003 For information about objects in this file, see the appropriate header: 00004 \ref Cmt1.h 00005 00006 \section FileCopyright Copyright Notice 00007 Copyright (C) Xsens Technologies B.V., 2006. All rights reserved. 00008 00009 This source code is intended for use only by Xsens Technologies BV and 00010 those that have explicit written permission to use it from 00011 Xsens Technologies BV. 00012 00013 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 00014 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 00015 IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 00016 PARTICULAR PURPOSE. 00017 00018 \section FileChangelog Changelog 00019 \par 2006-04-12, v0.0.1 00020 \li Job Mulder: Created 00021 \par 2006-07-21, v0.1.0 00022 \li Job Mulder: Updated file for release 0.1.0 00023 */ 00024 00025 #include "cmt1.h" 00026 #include <errno.h> 00027 #ifndef _WIN32 00028 # include <stdlib.h> 00029 # include <unistd.h> // close 00030 # include <sys/ioctl.h> // ioctl 00031 # include <fcntl.h> // open, O_RDWR 00032 # include <string.h> // strcpy 00033 # include <sys/param.h> 00034 // We have to redefine PATH_MAX from 4096 to CMT_MAX_FILENAME_LENGTH to mainain compatibility 00035 // The PATH_MAX definition is used by realpath() to determine the maximum path length. According 00036 // to the realpath (3) man page, the function is best avoided and it might be necessary to 00037 // write a custom function for it (couldn't find a proper replacement). 00038 # undef PATH_MAX 00039 # define PATH_MAX CMT_MAX_FILENAME_LENGTH 00040 #else 00041 # include <io.h> 00042 #endif 00043 00044 #ifndef _CRT_SECURE_NO_DEPRECATE 00045 # define _CRT_SECURE_NO_DEPRECATE 00046 # ifdef _WIN32 00047 # pragma warning(disable:4996) 00048 # endif 00049 #endif 00050 00051 #ifdef _WIN32 00052 # define FSEEK(x) _fseeki64(m_handle, x, SEEK_SET) 00053 # define FSEEK_R(x) _fseeki64(m_handle, x, SEEK_END) 00054 # define FTELL() _ftelli64(m_handle) 00055 #else 00056 # define FSEEK(x) fseeko(m_handle, x, SEEK_SET) 00057 # define FSEEK_R(x) fseeko(m_handle, x, SEEK_END) 00058 # define FTELL() ftello(m_handle) 00059 #endif 00060 00061 // The namespace of all Xsens software since 2006. 00062 namespace xsens { 00063 00064 #ifndef _WIN32 00065 int _wcsnicmp(const wchar_t* s1, const wchar_t* s2,int count) 00066 { 00067 for (int i = 0; i < count; ++i, ++s1, ++s2) 00068 if (*s1 == L'\0') 00069 if (*s2 == L'\0') 00070 return 0; 00071 else 00072 return -1; 00073 else 00074 if (*s2 == L'\0') 00075 return 1; 00076 else 00077 if (*s1 < *s2) 00078 return -1; 00079 else if (*s1 > *s2) 00080 return 1; 00081 return 0; 00082 } 00083 #endif 00084 00085 00086 #if defined(_DEBUG) || defined(_LOG_ALWAYS) 00087 #if !defined(_LOG_TO_DBVIEW) 00088 #ifdef _LOG_TO_STDOUT 00089 #else // !dbview && !stdout 00090 FILE* debug_log_fp = NULL; 00091 int32_t debug_log_valid = 0; 00092 00093 FILE* debug_qlog_fp = NULL; 00094 int32_t debug_qlog_valid = 0; 00095 #endif 00096 #endif 00097 00098 // write to a log file/screen/debug-stream 00099 void CMTLOG(const char *str, ...) 00100 { 00101 #ifdef _LOG_TO_STDOUT 00102 va_list ptr; 00103 va_start(ptr,str); 00104 vprintf(str,ptr); 00105 #else 00106 #ifdef _LOG_TO_DBVIEW 00107 char buf[2048]; 00108 00109 va_list ptr; 00110 va_start(ptr,str); 00111 vsprintf(buf,str,ptr); 00112 00113 OutputDebugString(buf); 00114 #else 00115 if (debug_log_valid == 0) 00116 { 00117 fopen_s(&debug_log_fp,"debug_log_cmt.log","w"); 00118 if (debug_log_fp != NULL) 00119 debug_log_valid = 1; 00120 else 00121 debug_log_valid = -1; 00122 } 00123 if (debug_log_valid == 1) 00124 { 00125 char buf[2048]; 00126 00127 va_list ptr; 00128 va_start(ptr,str); 00129 int32_t sz = vsprintf_s(buf,str,ptr); 00130 00131 uint32_t nw = getTimeOfDay(); 00132 fprintf(debug_log_fp,"%5u.%03u %s",nw/1000,nw%1000,buf); 00133 //fwrite(buf,1,sz,debug_log_fp); 00134 fflush(debug_log_fp); 00135 } 00136 #endif 00137 #endif 00138 } 00139 #endif 00140 00141 // maybe log to nothing at this level 00142 #ifdef _LOG_CMT1 00143 #define CMT1LOG CMTLOG 00144 #else 00145 #define CMT1LOG(...) 00146 #endif 00147 00148 ////////////////////////////////////////////////////////////////////////////////////////// 00149 ///////////////////////////////////////// Cmt1s ///////////////////////////////////////// 00150 ////////////////////////////////////////////////////////////////////////////////////////// 00151 00152 ////////////////////////////////////////////////////////////////////////////////////////// 00153 // Default constructor, initializes all members to their default values. 00154 Cmt1s::Cmt1s() : 00155 m_onBytesReceived(NULL) 00156 { 00157 m_port = 0; 00158 m_isOpen = false; 00159 m_lastResult = XRV_OK; 00160 m_timeout = CMT1_DEFAULT_TIMEOUT; 00161 m_endTime = 0; 00162 m_baudrate = 0; 00163 00164 #ifdef _LOG_RX_TX 00165 rx_log = NULL; 00166 tx_log = NULL; 00167 #endif 00168 } 00169 00170 ////////////////////////////////////////////////////////////////////////////////////////// 00171 // Destructor, de-initializes, frees memory allocated for buffers, etc. 00172 Cmt1s::~Cmt1s() 00173 { 00174 close(); 00175 } 00176 00177 ////////////////////////////////////////////////////////////////////////////////////////// 00178 // Close the serial communication port. 00179 XsensResultValue Cmt1s::close (void) 00180 { 00181 #ifdef _LOG_RX_TX 00182 if (rx_log != NULL) 00183 fclose(rx_log); 00184 if (tx_log != NULL) 00185 fclose(tx_log); 00186 rx_log = NULL; 00187 tx_log = NULL; 00188 #endif 00189 if (!m_isOpen) 00190 return m_lastResult = XRV_NOPORTOPEN; 00191 00192 #ifdef _WIN32 00193 ::FlushFileBuffers(m_handle); 00194 // read all data before closing the handle, a Flush is not enough for FTDI devices unfortunately 00195 // we first need to set the COMM timeouts to instantly return when no more data is available 00196 COMMTIMEOUTS cto; 00197 ::GetCommTimeouts(m_handle,&cto); 00198 cto.ReadIntervalTimeout = MAXDWORD; 00199 cto.ReadTotalTimeoutConstant = 0; 00200 cto.ReadTotalTimeoutMultiplier = 0; 00201 ::SetCommTimeouts(m_handle,&cto); 00202 char buffer[1024]; 00203 uint32_t length; 00204 do { 00205 ::ReadFile(m_handle, buffer, 1024, &length, NULL); 00206 } while (length > 0); 00207 BOOL murf = ::CloseHandle(m_handle); 00208 #else 00209 ::close(m_handle); 00210 #endif 00211 m_isOpen = false; 00212 m_endTime = 0; 00213 00214 return m_lastResult = XRV_OK; 00215 } 00216 00217 ////////////////////////////////////////////////////////////////////////////////////////// 00218 // Manipulate the Serial control lines 00219 XsensResultValue Cmt1s::escape (const CmtControlLine mask, const CmtControlLine state) 00220 { 00221 if (!m_isOpen) 00222 return (m_lastResult = XRV_NOPORTOPEN); 00223 #ifdef _WIN32 00224 BOOL rv = 0; 00225 if (mask & CMT_CONTROL_DTR) 00226 { 00227 if (state & CMT_CONTROL_DTR) 00228 rv = EscapeCommFunction(m_handle,SETDTR); 00229 else 00230 rv = EscapeCommFunction(m_handle,CLRDTR); 00231 } 00232 00233 if (mask & CMT_CONTROL_RTS) 00234 { 00235 if (state & CMT_CONTROL_RTS) 00236 rv = EscapeCommFunction(m_handle,SETRTS); 00237 else 00238 rv = EscapeCommFunction(m_handle,CLRRTS); 00239 } 00240 if (rv) 00241 return m_lastResult = XRV_OK; 00242 else 00243 return m_lastResult = XRV_ERROR; 00244 #else 00245 bool rv = true; 00246 int32_t status; 00247 if (mask & CMT_CONTROL_DTR) 00248 { 00249 if (ioctl(m_handle, TIOCMGET, &status) == -1) 00250 { 00251 if (state & CMT_CONTROL_DTR) status |= TIOCM_DTR; 00252 else status &= ~TIOCM_DTR; 00253 rv = (ioctl(m_handle, TIOCMSET, &status) == -1); 00254 } 00255 else 00256 rv = false; 00257 } 00258 if (rv && (mask & CMT_CONTROL_RTS)) 00259 { 00260 if (ioctl(m_handle, TIOCMGET, &status) == -1) 00261 { 00262 if (state & CMT_CONTROL_RTS) status |= TIOCM_RTS; 00263 else status &= ~TIOCM_RTS; 00264 rv = (ioctl(m_handle, TIOCMSET, &status) == -1); 00265 } 00266 else 00267 rv = false; 00268 } 00269 if (rv) 00270 return m_lastResult = XRV_OK; 00271 else 00272 return m_lastResult = XRV_ERROR; 00273 #endif 00274 } 00275 00276 ////////////////////////////////////////////////////////////////////////////////////////// 00277 // Flush all data to be transmitted / received. 00278 XsensResultValue Cmt1s::flushData (void) 00279 { 00280 #ifdef _WIN32 00281 // Remove any 'old' data in buffer 00282 PurgeComm(m_handle, PURGE_TXCLEAR | PURGE_RXCLEAR); 00283 #else 00284 tcflush(m_handle, TCIOFLUSH); 00285 #endif 00286 m_endTime = 0; 00287 return (m_lastResult = XRV_OK); 00288 } 00289 00290 ////////////////////////////////////////////////////////////////////////////////////////// 00291 // Open a communication channel to the given serial port name. 00292 XsensResultValue Cmt1s::open( const char *portName, 00293 const uint32_t baudRate, 00294 uint32_t readBufSize, 00295 uint32_t writeBufSize) 00296 { 00297 m_endTime = 0; 00298 00299 CMT1LOG("L1: Open port %s at %d baud\n", portName, baudRate); 00300 00301 if (m_isOpen) 00302 { 00303 CMT1LOG("L1: Port already open\n"); 00304 return (m_lastResult = XRV_ALREADYOPEN); 00305 } 00306 m_baudrate = baudRate; 00307 00308 #ifdef _WIN32 00309 char winPortName[32]; 00310 00311 // Open port 00312 sprintf(winPortName, "\\\\.\\%s", portName); 00313 m_handle = CreateFile(winPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, 00314 OPEN_EXISTING, 0, NULL); 00315 if (m_handle == INVALID_HANDLE_VALUE) 00316 { 00317 CMT1LOG("L1: Port cannot be opened\n"); 00318 return (m_lastResult = XRV_INPUTCANNOTBEOPENED); 00319 } 00320 00321 // Once here, port is open 00322 m_isOpen = true; 00323 00324 //Get the current state & then change it 00325 GetCommState(m_handle, &m_commState); // Get current state 00326 00327 m_commState.BaudRate = baudRate; // Setup the baud rate 00328 m_commState.Parity = NOPARITY; // Setup the Parity 00329 m_commState.ByteSize = 8; // Setup the data bits 00330 m_commState.StopBits = TWOSTOPBITS; // Setup the stop bits 00331 m_commState.fDsrSensitivity = FALSE; // Setup the flow control 00332 m_commState.fOutxCtsFlow = FALSE; // NoFlowControl: 00333 m_commState.fOutxDsrFlow = FALSE; 00334 m_commState.fOutX = FALSE; 00335 m_commState.fInX = FALSE; 00336 if (!SetCommState(m_handle, (LPDCB)&m_commState)) {// Set new state 00337 // Bluetooth ports cannot always be opened with 2 stopbits 00338 // Now try to open port with 1 stopbit. 00339 m_commState.StopBits = ONESTOPBIT; 00340 if (!SetCommState(m_handle, (LPDCB)&m_commState)) { 00341 CloseHandle(m_handle); 00342 m_handle = INVALID_HANDLE_VALUE; 00343 m_isOpen = false; 00344 return (m_lastResult = XRV_INPUTCANNOTBEOPENED); 00345 } 00346 } 00347 m_port = atoi(&portName[3]); 00348 sprintf(m_portname, "%s", portName); 00349 00350 setTimeout(m_timeout); 00351 00352 // Other initialization functions 00353 EscapeCommFunction(m_handle, SETRTS); // Enable RTS (for Xbus Master use) 00354 // Set DTR (Calibration sensors need DTR to startup, won't hurt otherwise 00355 EscapeCommFunction(m_handle, SETDTR); 00356 SetupComm(m_handle,readBufSize,writeBufSize); // Set queue size 00357 00358 // Remove any 'old' data in buffer 00359 //PurgeComm(m_handle, PURGE_TXCLEAR | PURGE_RXCLEAR); 00360 PurgeComm(m_handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); 00361 #else // !_WIN32 00362 (void)readBufSize; 00363 (void)writeBufSize; 00364 // Open port 00365 m_handle = ::open(portName, O_RDWR | O_NOCTTY); 00366 // O_RDWR: Read+Write 00367 // O_NOCTTY: Raw input, no "controlling terminal" 00368 // O_NDELAY: Don't care about DCD signal 00369 00370 if (m_handle < 0) { 00371 // Port not open 00372 return m_lastResult = XRV_INPUTCANNOTBEOPENED; 00373 } 00374 00375 // Once here, port is open 00376 m_isOpen = true; 00377 00378 /* Start configuring of port for non-canonical transfer mode */ 00379 // Get current options for the port 00380 tcgetattr(m_handle, &m_commState); 00381 00382 // Set baudrate. 00383 cfsetispeed(&m_commState, baudRate); 00384 cfsetospeed(&m_commState, baudRate); 00385 00386 // Enable the receiver and set local mode 00387 m_commState.c_cflag |= (CLOCAL | CREAD); 00388 // Set character size to data bits and set no parity Mask the characte size bits 00389 m_commState.c_cflag &= ~(CSIZE|PARENB); 00390 m_commState.c_cflag |= CS8; // Select 8 data bits 00391 m_commState.c_cflag |= CSTOPB; // send 2 stop bits 00392 // Disable hardware flow control 00393 m_commState.c_cflag &= ~CRTSCTS; 00394 m_commState.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); 00395 // Disable software flow control 00396 m_commState.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); 00397 // Set Raw output 00398 m_commState.c_oflag &= ~OPOST; 00399 // Timeout 0.001 sec for first byte, read minimum of 0 bytes 00400 m_commState.c_cc[VMIN] = 0; 00401 m_commState.c_cc[VTIME] = (m_timeout+99)/100; // 1 00402 00403 // Set the new options for the port 00404 tcsetattr(m_handle,TCSANOW, &m_commState); 00405 00406 m_port = 1; 00407 sprintf(m_portname, "%s", portName); 00408 00409 tcflush(m_handle, TCIOFLUSH); 00410 00411 // setting RTS and DTR; RTS for Xbus Master, DTR for calibration sensors 00412 int cmbits; 00413 if (ioctl(m_handle, TIOCMGET, &cmbits) < 0) 00414 { 00415 return (m_lastResult = XRV_ERROR); 00416 } 00417 00418 cmbits |= TIOCM_RTS|TIOCM_DTR; 00419 00420 if (ioctl(m_handle, TIOCMSET, &cmbits) < 0) 00421 { 00422 return (m_lastResult = XRV_ERROR); 00423 } 00424 #endif // !_WIN32 00425 00426 CMT1LOG("L1: Port opened\n"); 00427 return (m_lastResult = XRV_OK); 00428 } 00429 00430 #ifdef _WIN32 00431 ////////////////////////////////////////////////////////////////////////////////////////// 00432 // Open a communication channel to the given COM port number. 00433 XsensResultValue Cmt1s::open ( const uint32_t portNumber, 00434 const uint32_t baudRate, 00435 uint32_t readBufSize, 00436 uint32_t writeBufSize) 00437 { 00438 char comFileName[32]; 00439 00440 // Create file name 00441 sprintf(comFileName, "COM%d", portNumber); 00442 00443 return Cmt1s::open(comFileName, baudRate, readBufSize, writeBufSize); 00444 } 00445 #endif 00446 00447 ////////////////////////////////////////////////////////////////////////////////////////// 00448 // Read data from the serial port and put it into the data buffer. 00449 XsensResultValue Cmt1s::readData (const uint32_t maxLength, uint8_t* data, 00450 uint32_t* length) 00451 { 00452 CMT1LOG("L1: readData, maxlength=%u, length=%p\n",maxLength,length); 00453 uint32_t ln; 00454 if (length == NULL) 00455 length = &ln; 00456 00457 if (!m_isOpen) 00458 return (m_lastResult = XRV_NOPORTOPEN); 00459 00460 #ifdef _WIN32 00461 BOOL rres = ::ReadFile(m_handle, data, maxLength, length, NULL); 00462 CMT1LOG("L1: readData, ReadFile returns %u (%u)\n",rres,*length); 00463 if (m_onBytesReceived != NULL && *length > 0) 00464 { 00465 CmtBinaryData* bytes = (CmtBinaryData*) malloc(sizeof(CmtBinaryData)); 00466 bytes->m_size = *length; 00467 bytes->m_portNr = m_port; 00468 memcpy(bytes->m_data,data,*length); 00469 #ifdef _LOG_CALLBACKS 00470 CMTLOG("C1: onBytesReceived(%d,(%d,%d),%p)\n",(int32_t) m_onBytesReceivedInstance, (int32_t) bytes->m_size, (int32_t) bytes->m_portNr, m_onBytesReceivedParam); 00471 #endif 00472 m_onBytesReceived(m_onBytesReceivedInstance,CMT_CALLBACK_ONBYTESRECEIVED,bytes,m_onBytesReceivedParam); 00473 } 00474 00475 if (!rres) 00476 { 00477 CMT1LOG("L1: readData, ReadFile returned error %u\n",::GetLastError()); 00478 return (m_lastResult = XRV_ERROR); 00479 } 00480 #else 00481 *length = read(m_handle, data, maxLength); 00482 #endif 00483 00484 #ifdef _LOG_RX_TX 00485 if (*length > 0) 00486 { 00487 if (rx_log == NULL) 00488 { 00489 char fname[CMT_MAX_FILENAME_LENGTH]; 00490 sprintf(fname,"rx_%03d_%d.log",(int32_t) m_port,m_baudrate); 00491 rx_log = fopen(fname,"wb"); 00492 } 00493 fwrite(data,1,*length,rx_log); 00494 } 00495 #endif 00496 00497 CMT1LOG((length[0]?"L1: readData returned success, read %u of %u bytes, first: %02x\n":"L1: readData returned success, read %u bytes\n"),length[0],maxLength,data[0]); 00498 return (m_lastResult = XRV_OK); 00499 } 00500 00501 ////////////////////////////////////////////////////////////////////////////////////////// 00502 // Set the callback function for when bytes have been received 00503 XsensResultValue Cmt1s::setCallbackFunction(CmtCallbackType tp, int32_t instance, CmtCallbackFunction func, void* param) 00504 { 00505 if (tp == CMT_CALLBACK_ONBYTESRECEIVED) 00506 { 00507 m_onBytesReceived = func; 00508 m_onBytesReceivedInstance = instance; 00509 m_onBytesReceivedParam = param; 00510 return m_lastResult = XRV_OK; 00511 } 00512 return m_lastResult = XRV_INVALIDPARAM; 00513 } 00514 00515 ////////////////////////////////////////////////////////////////////////////////////////// 00516 // Set the default timeout value to use in blocking operations. 00517 XsensResultValue Cmt1s::setTimeout (const uint32_t ms) 00518 { 00519 CMT1LOG("L1: Setting timeout to %u ms\n",ms); 00520 00521 m_timeout = ms; 00522 #ifdef _WIN32 00523 // Set COM timeouts 00524 COMMTIMEOUTS commTimeouts; 00525 00526 GetCommTimeouts(m_handle,&commTimeouts); // Fill CommTimeouts structure 00527 00528 // immediate return if data is available, wait 1ms otherwise 00529 if (m_timeout > 0) 00530 { 00531 commTimeouts.ReadIntervalTimeout = 0; 00532 commTimeouts.ReadTotalTimeoutConstant = m_timeout; // ms time 00533 commTimeouts.ReadTotalTimeoutMultiplier = 0; 00534 commTimeouts.WriteTotalTimeoutConstant = m_timeout; 00535 commTimeouts.WriteTotalTimeoutMultiplier = 0; 00536 } 00537 else 00538 { 00539 // immediate return whether data is available or not 00540 commTimeouts.ReadIntervalTimeout = MAXDWORD; 00541 commTimeouts.ReadTotalTimeoutConstant = 0; 00542 commTimeouts.ReadTotalTimeoutMultiplier = 0; 00543 commTimeouts.WriteTotalTimeoutConstant = 0; 00544 commTimeouts.WriteTotalTimeoutMultiplier = 0; 00545 } 00546 00547 SetCommTimeouts(m_handle, &commTimeouts); // Set CommTimeouts structure 00548 #else 00549 // Timeout 0.1 sec for first byte, read minimum of 0 bytes 00550 m_commState.c_cc[VMIN] = 0; 00551 m_commState.c_cc[VTIME] = (m_timeout+99)/100; // ds time 00552 00553 // Set the new options for the port if it is open 00554 if (m_isOpen) 00555 tcsetattr(m_handle,TCSANOW, &m_commState); 00556 #endif 00557 return (m_lastResult = XRV_OK); 00558 } 00559 00560 ////////////////////////////////////////////////////////////////////////////////////////// 00561 // Wait for data to arrive or a timeout to occur. 00562 XsensResultValue Cmt1s::waitForData (const uint32_t maxLength, 00563 uint8_t* data, uint32_t* length) 00564 { 00565 CMT1LOG("L1: waitForData, mto=%u, length=%p\n",m_timeout,length); 00566 uint32_t timeout = m_timeout; 00567 00568 uint32_t ln; 00569 if (length == NULL) 00570 length = &ln; 00571 uint32_t eTime = getTimeOfDay(NULL) + timeout; 00572 uint32_t newLength = 0; 00573 00574 *length = 0; 00575 while ((*length < maxLength) && (getTimeOfDay() <= eTime)) 00576 { 00577 readData(maxLength - *length, data + *length, &newLength); 00578 *length += newLength; 00579 } 00580 CMT1LOG("L1: waitForData result: read %u of %u bytes\n",length[0],maxLength); 00581 00582 if (length[0] < maxLength) 00583 return (m_lastResult = XRV_TIMEOUT); 00584 else 00585 return (m_lastResult = XRV_OK); 00586 } 00587 00588 ////////////////////////////////////////////////////////////////////////////////////////// 00589 // Write the data to the serial port. 00590 XsensResultValue Cmt1s::writeData (const uint32_t length, const uint8_t* data, 00591 uint32_t* written) 00592 { 00593 uint32_t bytes; 00594 if (written == NULL) 00595 written = &bytes; 00596 00597 if (!m_isOpen) 00598 return (m_lastResult = XRV_NOPORTOPEN); 00599 00600 #ifdef _WIN32 00601 if (WriteFile(m_handle, data, length, written, NULL)) 00602 { 00603 #ifdef _LOG_RX_TX 00604 if (written[0] > 0) 00605 { 00606 if (tx_log == NULL) 00607 { 00608 char fname[CMT_MAX_FILENAME_LENGTH]; 00609 sprintf(fname,"tx_%03d_%d.log",(int32_t) m_port,m_baudrate); 00610 tx_log = fopen(fname,"wb"); 00611 } 00612 fwrite(data,1,*written,tx_log); 00613 } 00614 #endif 00615 return (m_lastResult = XRV_OK); 00616 } 00617 else 00618 return (m_lastResult = XRV_ERROR); 00619 #else 00620 *written = write(m_handle, data, length); 00621 // if (*written == length) 00622 return (m_lastResult = XRV_OK); 00623 // else 00624 // return (m_lastResult = XRV_ERROR); 00625 #endif 00626 } 00627 00628 ////////////////////////////////////////////////////////////////////////////////////////// 00629 ///////////////////////////////////////// Cmt1f ///////////////////////////////////////// 00630 ////////////////////////////////////////////////////////////////////////////////////////// 00631 00632 ////////////////////////////////////////////////////////////////////////////////////////// 00633 // Default constructor, initializes all members to their default values. 00634 Cmt1f::Cmt1f() 00635 { 00636 m_readPos = 0; 00637 m_writePos = 0; 00638 m_lastResult = XRV_OK; 00639 m_reading = true; 00640 m_isOpen = false; 00641 m_filename[0] = '\0'; 00642 m_fileSize = 0; 00643 m_readOnly = false; 00644 m_unicode = false; 00645 } 00646 00647 ////////////////////////////////////////////////////////////////////////////////////////// 00648 // Destructor. 00649 Cmt1f::~Cmt1f() 00650 { 00651 close(); 00652 } 00653 00654 ////////////////////////////////////////////////////////////////////////////////////////// 00655 // Write data to the end of the file. 00656 XsensResultValue Cmt1f::appendData (const uint32_t length, const void* data) 00657 { 00658 if (!m_isOpen) 00659 return m_lastResult = XRV_NOFILEOPEN; 00660 if (m_readOnly) 00661 return m_lastResult = XRV_READONLY; 00662 00663 if (m_reading || m_writePos != m_fileSize) 00664 { 00665 m_reading = false; 00666 FSEEK_R(0); 00667 } 00668 fwrite(data, 1, length, m_handle); 00669 m_writePos = FTELL(); 00670 m_fileSize = m_writePos; 00671 00672 return (m_lastResult = XRV_OK); 00673 } 00674 00675 ////////////////////////////////////////////////////////////////////////////////////////// 00676 // Close the file. 00677 XsensResultValue Cmt1f::close (void) 00678 { 00679 if (m_isOpen) 00680 { 00681 #ifdef _WIN32 00682 fflush(m_handle); 00683 fclose(m_handle); 00684 #else 00685 ::fflush(m_handle); 00686 ::fclose(m_handle); 00687 #endif 00688 } 00689 m_isOpen = false; 00690 m_readPos = 0; 00691 m_writePos = 0; 00692 m_reading = true; 00693 m_isOpen = false; 00694 m_fileSize = 0; 00695 m_readOnly = false; 00696 00697 return m_lastResult = XRV_OK; 00698 } 00699 00700 ////////////////////////////////////////////////////////////////////////////////////////// 00701 // Close the file and delete it. 00702 XsensResultValue Cmt1f::closeAndDelete(void) 00703 { 00704 if (m_isOpen) 00705 { 00706 #ifdef _WIN32 00707 fflush(m_handle); 00708 fclose(m_handle); 00709 #else 00710 ::fflush(m_handle); 00711 ::fclose(m_handle); 00712 #endif 00713 if (m_readOnly) 00714 m_lastResult = XRV_READONLY; 00715 else 00716 { 00717 #ifdef _WIN32 00718 if (m_unicode) 00719 { 00720 if (_wremove(m_filename_w) != 0) 00721 m_lastResult = XRV_READONLY; 00722 else 00723 m_lastResult = XRV_OK; 00724 } 00725 else 00726 #endif 00727 { 00728 #ifdef _WIN32 00729 if (_unlink(m_filename) != 0) 00730 #else 00731 if (unlink(m_filename) != 0) 00732 #endif 00733 m_lastResult = XRV_READONLY; 00734 else 00735 m_lastResult = XRV_OK; 00736 } 00737 } 00738 } 00739 else 00740 m_lastResult = XRV_NOFILEOPEN; 00741 00742 m_isOpen = false; 00743 m_readPos = 0; 00744 m_writePos = 0; 00745 m_reading = true; 00746 m_isOpen = false; 00747 m_fileSize = 0; 00748 m_readOnly = false; 00749 00750 return m_lastResult; 00751 } 00752 00753 ////////////////////////////////////////////////////////////////////////////////////////// 00754 // Create a new file. 00755 XsensResultValue Cmt1f::create (const char* filename) 00756 { 00757 if (m_isOpen) 00758 return m_lastResult = XRV_ALREADYOPEN; 00759 00760 //! \test does this work for non-existing files? Or do we need a check and create? 00761 m_handle = fopen(filename, "w+b"); // open for update (r/w) 00762 if (m_handle == NULL) 00763 return m_lastResult = XRV_OUTPUTCANNOTBEOPENED; 00764 00765 #ifdef _WIN32 00766 if (_fullpath(m_filename,filename,CMT_MAX_FILENAME_LENGTH) == NULL) 00767 { 00768 fclose(m_handle); 00769 remove(filename); 00770 return m_lastResult = XRV_INVALIDPARAM; 00771 } 00772 #else 00773 // based on the assumption that this doesn't concern the serial port, handle 00774 // it the same way using realpath(). Apparently realpath() doesn't require a 00775 // maximum length. One would possibly want to write a wrapper for it. 00776 if (realpath(filename, m_filename) == NULL) 00777 { 00778 fclose(m_handle); 00779 remove(filename); 00780 return m_lastResult = XRV_INVALIDPARAM; 00781 } 00782 #endif 00783 mbstowcs(m_filename_w,m_filename,CMT_MAX_FILENAME_LENGTH); 00784 m_unicode = false; 00785 00786 m_isOpen = true; 00787 m_readPos = 0; 00788 m_writePos = 0; 00789 m_fileSize = 0; 00790 m_reading = true; 00791 m_readOnly = false; 00792 return m_lastResult = XRV_OK; 00793 } 00794 00795 ////////////////////////////////////////////////////////////////////////////////////////// 00796 // Create a new file. 00797 XsensResultValue Cmt1f::create (const wchar_t* filename) 00798 { 00799 if (m_isOpen) 00800 return m_lastResult = XRV_ALREADYOPEN; 00801 00802 #ifdef _WIN32 00803 //! \test does this work for non-existing files? Or do we need a check and create? 00804 m_handle = _wfopen(filename, L"w+b"); // open for update (r/w) 00805 if (m_handle == NULL) 00806 return m_lastResult = XRV_OUTPUTCANNOTBEOPENED; 00807 00808 if (_wfullpath(m_filename_w,filename,CMT_MAX_FILENAME_LENGTH) == NULL) 00809 { 00810 fclose(m_handle); 00811 _wremove(filename); 00812 return m_lastResult = XRV_INVALIDPARAM; 00813 } 00814 wcstombs(m_filename,m_filename_w,CMT_MAX_FILENAME_LENGTH); 00815 00816 m_isOpen = true; 00817 m_readPos = 0; 00818 m_writePos = 0; 00819 m_fileSize = 0; 00820 m_reading = true; 00821 m_readOnly = false; 00822 #else 00823 char tFilename[CMT_MAX_FILENAME_LENGTH*2]; 00824 wcstombs(tFilename, filename, CMT_MAX_FILENAME_LENGTH); 00825 XsensResultValue res = create(tFilename); 00826 if (res != XRV_OK) 00827 return res; 00828 #endif 00829 m_unicode = true; 00830 return m_lastResult = XRV_OK; 00831 } 00832 00833 ////////////////////////////////////////////////////////////////////////////////////////// 00834 // Delete the given data from the file. 00835 XsensResultValue Cmt1f::deleteData (const CmtFilePos start, const uint32_t length) 00836 { 00837 if (!m_isOpen) 00838 return m_lastResult = XRV_NOFILEOPEN; 00839 if (m_readOnly) 00840 return m_lastResult = XRV_READONLY; 00841 00842 gotoWrite(); 00843 00844 CmtFilePos wPos = start; 00845 CmtFilePos rPos = wPos + length; 00846 00847 size_t read1; 00848 CmtFilePos endPos = (start + (CmtFilePos) length); 00849 if (endPos < m_fileSize) 00850 { 00851 CmtFilePos remaining = m_fileSize - endPos; 00852 char buffer[512]; 00853 00854 // copy data 00855 FSEEK(rPos); 00856 00857 while (remaining > 0) 00858 { 00859 if (remaining >= 512) 00860 read1 = fread(buffer,1,512,m_handle); 00861 else 00862 read1 = fread(buffer,1,(size_t) remaining,m_handle); 00863 00864 remaining -= read1; 00865 rPos += read1; 00866 00867 // write block to the correct position 00868 FSEEK(wPos); 00869 wPos += fwrite(buffer, 1, read1, m_handle); 00870 FSEEK(rPos); 00871 } 00872 m_fileSize -= length; 00873 } 00874 else 00875 { 00876 m_fileSize = start; 00877 } 00878 00879 #ifdef _WIN32 00880 int32_t rv = _chsize(_fileno(m_handle),(int32_t) m_fileSize); 00881 #else 00882 int32_t rv = ftruncate(fileno(m_handle),(int32_t) m_fileSize); 00883 #endif 00884 int32_t eno = 0; 00885 if (rv != 0) 00886 eno = errno; 00887 m_writePos = start; 00888 FSEEK(wPos); 00889 if (rv != 0) 00890 { 00891 switch(eno) 00892 { 00893 case EACCES: 00894 return m_lastResult = XRV_BUSY; 00895 case EBADF: 00896 return m_lastResult = XRV_INVALIDINSTANCE; 00897 case ENOSPC: 00898 return m_lastResult = XRV_OUTOFMEMORY; 00899 case EINVAL: 00900 return m_lastResult = XRV_INVALIDPARAM; 00901 default: 00902 return m_lastResult = XRV_ERROR; 00903 } 00904 } 00905 00906 return m_lastResult = XRV_OK; 00907 } 00908 00909 ////////////////////////////////////////////////////////////////////////////////////////// 00910 // Find a string of bytes in the file 00911 XsensResultValue Cmt1f::find (const void* needleV, const uint32_t needleLength, CmtFilePos& pos) 00912 { 00913 if (!m_isOpen) 00914 return m_lastResult = XRV_NOFILEOPEN; 00915 00916 const char* needle = (const char*) needleV; 00917 00918 gotoRead(); 00919 00920 pos = 0; 00921 00922 char buffer[512]; 00923 uint32_t bufferPos, needlePos = 0; 00924 size_t readBytes; 00925 if (m_readPos & 0x1FF) // read a block of data 00926 readBytes = fread(buffer,1,(512-((size_t) m_readPos & 0x1FF)),m_handle); 00927 else 00928 readBytes = fread(buffer,1,512,m_handle); // read a block of data 00929 00930 while (readBytes > 0) 00931 { 00932 m_readPos += readBytes; 00933 bufferPos = 0; 00934 00935 while (bufferPos < readBytes && needlePos < needleLength) 00936 { 00937 if (buffer[bufferPos] == needle[needlePos]) 00938 { 00939 // found a byte 00940 ++needlePos; 00941 } 00942 else 00943 { 00944 if (needlePos > 0) 00945 needlePos = 0; 00946 else 00947 if (buffer[bufferPos] == needle[0]) 00948 { 00949 // found a byte 00950 needlePos = 1; 00951 } 00952 } 00953 ++bufferPos; 00954 } 00955 if (needlePos < needleLength) 00956 readBytes = fread(buffer,1,512,m_handle); // read next block 00957 else 00958 { 00959 m_readPos = m_readPos + bufferPos - readBytes - needleLength; // or without needleLength 00960 pos = m_readPos; // - needleLength; 00961 FSEEK(m_readPos); 00962 return m_lastResult = XRV_OK; 00963 } 00964 } 00965 return m_lastResult = XRV_ENDOFFILE; 00966 } 00967 00968 ////////////////////////////////////////////////////////////////////////////////////////// 00969 // Flush all data to be written. 00970 XsensResultValue Cmt1f::flushData (void) 00971 { 00972 fflush(m_handle); 00973 00974 return m_lastResult = XRV_OK; 00975 } 00976 00977 ////////////////////////////////////////////////////////////////////////////////////////// 00978 // Retrieve the filename that was last successfully opened. 00979 XsensResultValue Cmt1f::getName(char* filename) const 00980 { 00981 strcpy(filename, m_filename); 00982 return m_lastResult = XRV_OK; 00983 } 00984 00985 ////////////////////////////////////////////////////////////////////////////////////////// 00986 // Retrieve the filename that was last successfully opened. 00987 XsensResultValue Cmt1f::getName(wchar_t* filename) const 00988 { 00989 #ifdef _WIN32 00990 wcscpy(filename, m_filename_w); 00991 #else 00992 mbstowcs(filename, m_filename, CMT_MAX_FILENAME_LENGTH); 00993 #endif 00994 return m_lastResult = XRV_OK; 00995 } 00996 00997 ////////////////////////////////////////////////////////////////////////////////////////// 00998 // Change from writing to reading mode 00999 void Cmt1f::gotoRead(void) 01000 { 01001 if (m_reading) 01002 return; 01003 01004 FSEEK(m_readPos); 01005 m_reading = true; 01006 } 01007 01008 ////////////////////////////////////////////////////////////////////////////////////////// 01009 // Change from reading to writing mode 01010 void Cmt1f::gotoWrite(void) 01011 { 01012 if (!m_reading) 01013 return; 01014 01015 FSEEK(m_writePos); 01016 m_reading = false; 01017 } 01018 01019 ////////////////////////////////////////////////////////////////////////////////////////// 01020 // Insert the given data into the file. 01021 XsensResultValue Cmt1f::insertData (const CmtFilePos start, const uint32_t length, const void* data) 01022 { 01023 if (!m_isOpen) 01024 return m_lastResult = XRV_NOFILEOPEN; 01025 if (m_readOnly) 01026 return m_lastResult = XRV_READONLY; 01027 01028 gotoWrite(); 01029 01030 CmtFilePos rPos = start; 01031 CmtFilePos wPos = rPos + length; 01032 01033 size_t read1, read2; 01034 CmtFilePos remaining = m_fileSize - start; 01035 size_t bsize = (length > 512)?length:512; 01036 char* buffer1 = (char*) malloc(bsize); 01037 char* buffer2 = (char*) malloc(bsize); 01038 char* btemp; 01039 01040 // copy data 01041 FSEEK(rPos); 01042 01043 if (remaining >= (CmtFilePos) bsize) 01044 read1 = fread(buffer1,1,bsize,m_handle); 01045 else 01046 read1 = fread(buffer1,1,(size_t) remaining,m_handle); 01047 01048 remaining -= read1; 01049 rPos += read1; 01050 01051 while(remaining > 0) 01052 { 01053 // move data to correct buffer 01054 read2 = read1; 01055 btemp = buffer1; buffer1 = buffer2; buffer2 = btemp; 01056 01057 // read next block 01058 if (remaining >= (CmtFilePos) bsize) 01059 read1 = fread(buffer1,1,bsize,m_handle); 01060 else 01061 read1 = fread(buffer1,1,(size_t) remaining,m_handle); 01062 01063 remaining -= read1; 01064 rPos += read1; 01065 01066 // write block to the correct position 01067 FSEEK(wPos); 01068 wPos += fwrite(buffer2, 1, read2, m_handle); 01069 FSEEK(rPos); 01070 } 01071 01072 FSEEK(wPos); 01073 wPos += fwrite(buffer1, 1, read1, m_handle); 01074 01075 FSEEK(start); 01076 m_writePos = start + fwrite(data, 1, length, m_handle); 01077 m_fileSize += length; 01078 01079 free(buffer1); 01080 free(buffer2); 01081 return m_lastResult = XRV_OK; 01082 } 01083 01084 ////////////////////////////////////////////////////////////////////////////////////////// 01085 // Open a file. 01086 XsensResultValue Cmt1f::open(const char* filename, const bool create, const bool readOnly) 01087 { 01088 if (m_isOpen) 01089 return m_lastResult = XRV_ALREADYOPEN; 01090 01091 //! \test does this work for non-existing files? Or do we need a check and create? 01092 m_readOnly = readOnly; 01093 if (readOnly) 01094 m_handle = fopen(filename, "rb"); // open for read only (r) 01095 else 01096 m_handle = fopen(filename, "r+b"); // open for update (r/w) 01097 if (m_handle == NULL) 01098 { 01099 if (create) 01100 m_handle = fopen(filename, "w+b"); // create for update (r/w) 01101 else 01102 { 01103 m_handle = fopen(filename, "rb"); // open for read only (r) 01104 m_readOnly = true; 01105 } 01106 } 01107 if (m_handle == NULL) 01108 return m_lastResult = XRV_INPUTCANNOTBEOPENED; 01109 01110 #ifdef _WIN32 01111 if (_fullpath(m_filename,filename,CMT_MAX_FILENAME_LENGTH) == NULL) 01112 { 01113 fclose(m_handle); 01114 return m_lastResult = XRV_INVALIDPARAM; 01115 } 01116 #else 01117 // use the same trick again. 01118 if (realpath(filename, m_filename) == NULL) 01119 { 01120 fclose(m_handle); 01121 return m_lastResult = XRV_INVALIDPARAM; 01122 } 01123 #endif 01124 mbstowcs(m_filename_w,m_filename,CMT_MAX_FILENAME_LENGTH); 01125 m_unicode = false; 01126 01127 m_isOpen = true; 01128 m_readPos = 0; 01129 m_writePos = 0; 01130 m_reading = true; 01131 FSEEK_R(0); 01132 m_fileSize = FTELL(); 01133 FSEEK(0); 01134 return (m_lastResult = XRV_OK); 01135 } 01136 01137 ////////////////////////////////////////////////////////////////////////////////////////// 01138 // Open a file. 01139 XsensResultValue Cmt1f::open(const wchar_t* filename, const bool create, const bool readOnly) 01140 { 01141 if (m_isOpen) 01142 return m_lastResult = XRV_ALREADYOPEN; 01143 01144 #ifdef _WIN32 01145 //! \test does this work for non-existing files? Or do we need a check and create? 01146 m_readOnly = readOnly; 01147 if (readOnly) 01148 m_handle = _wfopen(filename, L"rb"); // open for read only (r) 01149 else 01150 m_handle = _wfopen(filename, L"r+b"); // open for update (r/w) 01151 if (m_handle == NULL) 01152 { 01153 if (create) 01154 m_handle = _wfopen(filename, L"w+b"); // create for update (r/w) 01155 else 01156 { 01157 m_handle = _wfopen(filename, L"rb"); // open for read only (r) 01158 m_readOnly = true; 01159 } 01160 } 01161 if (m_handle == NULL) 01162 return m_lastResult = XRV_INPUTCANNOTBEOPENED; 01163 01164 if (_wfullpath(m_filename_w,filename,CMT_MAX_FILENAME_LENGTH) == NULL) 01165 { 01166 fclose(m_handle); 01167 return m_lastResult = XRV_INVALIDPARAM; 01168 } 01169 wcstombs(m_filename,m_filename_w,CMT_MAX_FILENAME_LENGTH); 01170 01171 m_isOpen = true; 01172 m_readPos = 0; 01173 m_writePos = 0; 01174 m_reading = true; 01175 FSEEK_R(0); 01176 m_fileSize = FTELL(); 01177 FSEEK(0); 01178 #else 01179 char tFilename[CMT_MAX_FILENAME_LENGTH*2]; 01180 wcstombs(tFilename,filename,CMT_MAX_FILENAME_LENGTH*2); 01181 XsensResultValue res = open(tFilename,create,readOnly); 01182 if (res != XRV_OK) 01183 return res; 01184 #endif 01185 m_unicode = true; 01186 return m_lastResult = XRV_OK; 01187 } 01188 01189 ////////////////////////////////////////////////////////////////////////////////////////// 01190 // Read data from the file and put it into the data buffer. 01191 XsensResultValue Cmt1f::readData(const uint32_t maxLength, void* data, uint32_t* length) 01192 { 01193 if (!m_isOpen) 01194 return m_lastResult = XRV_NOFILEOPEN; 01195 01196 if (maxLength == 0) 01197 return m_lastResult = XRV_OK; 01198 01199 uint32_t len; 01200 if (length == NULL) 01201 length = &len; 01202 01203 gotoRead(); 01204 01205 length[0] = (uint32_t) fread(data,1,maxLength,m_handle); 01206 if (length[0] == 0) 01207 return (m_lastResult = XRV_ENDOFFILE); 01208 01209 m_readPos += length[0]; 01210 return m_lastResult = XRV_OK; 01211 } 01212 01213 ////////////////////////////////////////////////////////////////////////////////////////// 01214 // Read data from the file until the terminator and put it into the data buffer. 01215 XsensResultValue Cmt1f::readData (const uint32_t maxLength, const char terminator, void* dataV, uint32_t* length) 01216 { 01217 if (!m_isOpen) 01218 return m_lastResult = XRV_NOFILEOPEN; 01219 01220 uint32_t len; 01221 if (length == NULL) 01222 length = &len; 01223 01224 char* data = (char*) dataV; 01225 int32_t readChar; 01226 01227 gotoRead(); 01228 01229 *length = 0; 01230 readChar = (uint32_t) fgetc(m_handle); 01231 01232 while (!feof(m_handle) && !ferror(m_handle)) 01233 { 01234 data[*length] = (char) readChar; 01235 ++(*length); 01236 ++m_readPos; 01237 01238 if (((char) readChar == terminator) || ((*length) >= maxLength)) 01239 return m_lastResult = XRV_OK; 01240 } 01241 return m_lastResult = XRV_ENDOFFILE; 01242 } 01243 01244 ////////////////////////////////////////////////////////////////////////////////////////// 01245 // Set the new absolute read position 01246 XsensResultValue Cmt1f::setReadPos (const CmtFilePos pos) 01247 { 01248 if (!m_isOpen) 01249 return m_lastResult = XRV_NOFILEOPEN; 01250 01251 if (m_readPos != pos) 01252 { 01253 m_readPos = pos; 01254 if (m_reading) 01255 FSEEK(m_readPos); 01256 } 01257 01258 return m_lastResult = XRV_OK; 01259 } 01260 01261 ////////////////////////////////////////////////////////////////////////////////////////// 01262 // Set the new absolute write position 01263 XsensResultValue Cmt1f::setWritePos(const CmtFilePos pos) 01264 { 01265 if (!m_isOpen) 01266 return m_lastResult = XRV_NOFILEOPEN; 01267 if (m_readOnly) 01268 return m_lastResult = XRV_READONLY; 01269 01270 if (pos == -1) 01271 { 01272 if (m_reading) 01273 m_reading = false; 01274 FSEEK_R(0); 01275 m_writePos = FTELL(); 01276 } 01277 else 01278 { 01279 if (m_writePos != pos) 01280 { 01281 m_writePos = pos; 01282 if (!m_reading) 01283 FSEEK(m_writePos); 01284 } 01285 } 01286 01287 return m_lastResult = XRV_OK; 01288 } 01289 01290 ////////////////////////////////////////////////////////////////////////////////////////// 01291 // Write data to the file. 01292 XsensResultValue Cmt1f::writeData (const uint32_t length, const void* data) 01293 { 01294 if (!m_isOpen) 01295 return m_lastResult = XRV_NOFILEOPEN; 01296 if (m_readOnly) 01297 return m_lastResult = XRV_READONLY; 01298 01299 gotoWrite(); 01300 size_t writeRes = fwrite(data, 1, length, m_handle); 01301 if (writeRes == (size_t)EOF || writeRes < length) 01302 { 01303 int32_t err = (int32_t)errno; 01304 switch (err) 01305 { 01306 case 0: break; 01307 case ENOSPC: return m_lastResult = XRV_INSUFFICIENTSPACE; 01308 case ENOMEM: return m_lastResult = XRV_OUTOFMEMORY; 01309 default: return m_lastResult = XRV_ERROR; 01310 } 01311 } 01312 m_writePos += writeRes; 01313 01314 if (m_writePos > m_fileSize) 01315 m_fileSize = m_writePos; 01316 01317 return m_lastResult = XRV_OK; 01318 } 01319 01320 } // end of xsens namespace