SerialDevice.cpp
Go to the documentation of this file.
00001 /*
00002  * SerialDevice.cpp
00003  *
00004  *  Created on: Oct 13, 2011
00005  *      Author: mriedel
00006  */
00007 
00008 #include <telekyb_serial/SerialDevice.h>
00009 #include <telekyb_serial/SerialException.h>
00010 
00011 // stl
00012 #include <sys/file.h>
00013 
00014 // ros
00015 #include <ros/console.h>
00016 
00017 #define INVALID_FD -1
00018 
00019 namespace TELEKYB_NAMESPACE
00020 {
00021 
00022 SerialDevice::SerialDevice()
00023         : deviceFD(INVALID_FD)
00024 {
00025 
00026 }
00027 
00028 SerialDevice::SerialDevice(const std::string& deviceName_, bool autoOpen, int oflag)
00029         : deviceName(deviceName_), deviceFD(INVALID_FD)
00030 {
00031 
00032         if (autoOpen) {
00033                 openDevice(oflag);
00034         }
00035 }
00036 
00037 SerialDevice::~SerialDevice()
00038 {
00039         closeDevice();
00040 }
00041 
00042 void SerialDevice::openDevice(int oflag)
00043 {
00044         // already open?
00045         if (isOpen()) {
00046                 ROS_WARN_STREAM("Serial Device " << deviceName << "is already open.");
00047                 //return true;
00048         }
00049 
00050         try {
00051                 if ((deviceFD = open(deviceName.c_str(), oflag)) < 0) {
00052                         throw SerialException("Unable to open Serial Device " + deviceName + " !", SerialExceptionCode::UNABLE_TO_OPEN);
00053                 }
00054 
00055                 if (!isatty(deviceFD)) {
00056                         throw SerialException("Device " + deviceName + " is not a serial (tty) device.", SerialExceptionCode::NO_TTY);
00057                 }
00058 
00059                 if (flock(deviceFD, LOCK_EX | LOCK_NB) < 0) {
00060                         // only issue warning
00061                         throw SerialException("Unable to open Serial Device " + deviceName + " ! It's locked.",
00062                                         SerialExceptionCode::LOCKED,
00063                                         ros::console::levels::Warn);
00064                 }
00065 
00066                 if(tcgetattr(deviceFD, &deviceAttr) < 0 ) {
00067                         throw SerialException("Unable get termios attributes of serial device " + deviceName + " !",
00068                                         SerialExceptionCode::IO_ERROR);
00069                 }
00070 
00071         } catch (SerialException &e) {
00072                 // catch to close!
00073                 //e.process();
00074                 closeDevice();
00075                 // rethrow
00076                 throw;
00077                 //return false;
00078         }
00079 
00080         // everything ok;
00081         //return true;
00082 }
00083 
00084 void SerialDevice::closeDevice()
00085 {
00086         //std::cout << "called SerialDevice::closeDevice()" << std::endl;
00087         // not open?
00088         if (!isOpen()) {
00089                 return;
00090         }
00091 
00092         close(deviceFD);
00093         deviceFD = INVALID_FD;
00094 }
00095 
00096 bool SerialDevice::isOpen() const
00097 {
00098         return deviceFD != INVALID_FD;
00099 }
00100 
00101 bool SerialDevice::readAvailable(timeval timeout) const
00102 {
00103         fd_set readfs;
00104         FD_ZERO(&readfs);
00105         FD_SET(deviceFD, &readfs);
00106 
00107         int returnVal = select(deviceFD + 1, &readfs, NULL, NULL, &timeout);
00108 
00109 //      ROS_INFO("Return Value Select: %d", returnVal);
00110 
00111         if (returnVal < 0) {
00112                 ROS_ERROR("select call failed on %s", deviceName.c_str());
00113                 return false;
00114         } else {
00115                 return (returnVal > 0);
00116         }
00117 
00118 }
00119 
00120 int SerialDevice::readDevice(char* buffer, size_t bufferSize, std::string terminalChars) const
00121 {
00122         //std::cout << "Reading from Device!" << std::endl;
00123         boost::mutex::scoped_lock lock(readMutex);
00124 
00125         int nbytes;
00126         char* bufptr = buffer;
00127 
00128         // break if bytes == 0 or message contains terminalChar -> break
00129         while ((nbytes = read(deviceFD, buffer, buffer + bufferSize - bufptr - 1)) > 0) {
00130                 bufptr += nbytes;
00131                 if (terminalChars.find(bufptr[-1]) != terminalChars.npos) {
00132 //                      ROS_INFO("Break in readDevice because of %d.", (int)bufptr[-1]);
00133                         break;
00134                 }
00135         }
00136 
00137         return (bufptr - buffer);
00138 }
00139 
00140 std::ostream& operator<<(std::ostream& stream, const SerialDevice& device)
00141 {
00142         // TODO
00143         return stream;
00144 }
00145 
00146 std::string& operator<<(std::string& string, const SerialDevice& device)
00147 {
00148         char buffer[DEFAULT_BUFFERSIZE];
00149 
00150         int nbytes;
00151         do {
00152                 nbytes = device.readDevice(buffer, DEFAULT_BUFFERSIZE, "\r\n ");
00153                 string.append(buffer, nbytes);
00154         } while (nbytes == DEFAULT_BUFFERSIZE);
00155 
00156         return string;
00157 }
00158 
00159 int SerialDevice::writeDevice(const void* buffer, size_t size) throw(SerialException)
00160 {
00161 //      int n = write(deviceFD, buffer, size);
00162 //      std::cout << "n: " << n << std::endl;
00163         boost::mutex::scoped_lock lock(writeMutex);
00164 
00165         int n;
00166         if ((n = write(deviceFD, buffer, size)) < 0) {
00167                 ROS_ERROR_STREAM("An error occured while writing to Serial Device "
00168                                 << deviceName << ". Error ("<< errno << "): " << strerror( errno ));
00169         }
00170 
00171         // TODO implement loop
00172         if (n != (signed)size) {
00173                 //ROS_ERROR_STREAM("Wrote less bytes than received!");
00174                 throw SerialException("Wrote less bytes than received! " + deviceName + " !");
00175                 //std::cout << "Wrote less bytes than received!" << std::endl;
00176         }
00177 
00178         return n;
00179 }
00180 
00181 std::istream& operator>>(std::istream& stream, SerialDevice& device)
00182 {
00183 
00184         return stream;
00185 }
00186 
00187 std::string& operator>>(std::string& string, SerialDevice& device)
00188 {
00189         char* cString = new char[string.size() + 1];
00190         std::copy(string.begin(), string.end(), cString);
00191         cString[string.size()] = '\0';
00192 
00193         device.writeDevice(cString, string.size() + 1);
00194 
00195         delete[] cString;
00196         return string;
00197 }
00198 
00199 bool SerialDevice::setTermiosAttr(tcflag_t c_iflag, tcflag_t c_oflag, tcflag_t c_cflag, tcflag_t c_lflag,
00200                 speed_t ispeed, speed_t ospeed, int optionalOptions)
00201 {
00202         if (!isOpen()) {
00203                 ROS_ERROR_STREAM("Unable to set Termios Attributes on Device " << deviceName << ". Not open!");
00204                 return false;
00205         }
00206 
00207         deviceAttr.c_iflag = c_iflag;
00208         deviceAttr.c_oflag = c_oflag;
00209         deviceAttr.c_cflag = c_cflag;
00210         deviceAttr.c_lflag = c_lflag;
00211 
00212         if (ispeed != BUNDEF) {
00213                 cfsetispeed(&deviceAttr, ispeed);
00214         }
00215         if (ospeed != BUNDEF) {
00216                 cfsetospeed(&deviceAttr, ospeed);
00217         }
00218 
00219         // Write.
00220         if (tcsetattr(deviceFD, optionalOptions, &deviceAttr) < 0) {
00221                 ROS_ERROR_STREAM("Unable to set Termios Attributes on Device " << deviceName << ". Closing Device.");
00222                 closeDevice();
00223                 return false;
00224         }
00225 
00226         return true;
00227 }
00228 
00229 
00230 bool SerialDevice::setTermiosAttrIFlag(tcflag_t c_iflag, int optionalOptions )
00231 {
00232         return setTermiosAttr(
00233                         c_iflag,
00234                         deviceAttr.c_oflag,
00235                         deviceAttr.c_cflag,
00236                         deviceAttr.c_lflag,
00237                         BUNDEF,
00238                         BUNDEF,
00239                         optionalOptions
00240                         );
00241 }
00242 bool SerialDevice::setTermiosAttrOFlag(tcflag_t c_oflag, int optionalOptions )
00243 {
00244         return setTermiosAttr(
00245                         deviceAttr.c_iflag,
00246                         c_oflag,
00247                         deviceAttr.c_cflag,
00248                         deviceAttr.c_lflag,
00249                         BUNDEF,
00250                         BUNDEF,
00251                         optionalOptions
00252                         );
00253 }
00254 bool SerialDevice::setTermiosAttrCFlag(tcflag_t c_cflag, int optionalOptions )
00255 {
00256         return setTermiosAttr(
00257                         deviceAttr.c_iflag,
00258                         deviceAttr.c_oflag,
00259                         c_cflag,
00260                         deviceAttr.c_lflag,
00261                         BUNDEF,
00262                         BUNDEF,
00263                         optionalOptions
00264                         );
00265 }
00266 bool SerialDevice::setTermiosAttrLFlag(tcflag_t c_lflag, int optionalOptions )
00267 {
00268         return setTermiosAttr(
00269                         deviceAttr.c_iflag,
00270                         deviceAttr.c_oflag,
00271                         deviceAttr.c_cflag,
00272                         c_lflag,
00273                         BUNDEF,
00274                         BUNDEF,
00275                         optionalOptions
00276                         );
00277 }
00278 bool SerialDevice::setTermiosAttrSpeed(speed_t ispeed, speed_t ospeed, int optionalOptions )
00279 {
00280         return setTermiosAttr(
00281                         deviceAttr.c_iflag,
00282                         deviceAttr.c_oflag,
00283                         deviceAttr.c_cflag,
00284                         deviceAttr.c_lflag,
00285                         ispeed,
00286                         ospeed,
00287                         optionalOptions
00288                         );
00289 }
00290 
00291 void SerialDevice::printTermiosAttr() const
00292 {
00293         if (!isOpen()) {
00294                 ROS_ERROR_STREAM("Unable to print Termios Attributes. Device " << deviceName << " not open!");
00295                 return;
00296         }
00297 
00298         // c_iflag
00299         std::cout << "Look up Meaning: http://dce.felk.cvut.cz/pos/cv5/doc/serial.html" << std::endl;
00300         std::cout << std::endl;
00301         std::cout << "c_iflag (" << deviceAttr.c_iflag << "): ";
00302         std::cout << "IGNBRK(" << ((deviceAttr.c_iflag & IGNBRK) == IGNBRK) << ") ";
00303         std::cout << "BRKINT(" << ((deviceAttr.c_iflag & BRKINT) == BRKINT) << ") ";
00304         std::cout << "IGNPAR(" << ((deviceAttr.c_iflag & IGNPAR) == IGNPAR) << ") ";
00305         std::cout << "PARMRK(" << ((deviceAttr.c_iflag & PARMRK) == PARMRK) << ") ";
00306         std::cout << "INPCK(" << ((deviceAttr.c_iflag & INPCK) == INPCK) << ") ";
00307         std::cout << "ISTRIP(" << ((deviceAttr.c_iflag & ISTRIP) == ISTRIP) << ") ";
00308         std::cout << "INLCR(" << ((deviceAttr.c_iflag & INLCR) == INLCR) << ") ";
00309         std::cout << "IGNCR(" << ((deviceAttr.c_iflag & IGNCR) == IGNCR) << ") ";
00310         std::cout << "ICRNL(" << ((deviceAttr.c_iflag & ICRNL) == ICRNL) << ") ";
00311 #ifndef __APPLE__
00312         std::cout << "IUCLC(" << ((deviceAttr.c_iflag & IUCLC) == IUCLC) << ") ";
00313 #endif
00314         std::cout << "IXON(" << ((deviceAttr.c_iflag & IXON) == IXON) << ") ";
00315         std::cout << "IXANY(" << ((deviceAttr.c_iflag & IXANY) == IXANY) << ") ";
00316         std::cout << "IXOFF(" << ((deviceAttr.c_iflag & IXOFF) == IXOFF) << ") ";
00317         std::cout << "IMAXBEL(" << ((deviceAttr.c_iflag & IMAXBEL) == IMAXBEL) << ") ";
00318         std::cout << "IUTF8(" << ((deviceAttr.c_iflag & IUTF8) == IUTF8) << ") ";
00319         std::cout << std::endl;;
00320         std::cout << "------------------------"  << std::endl;
00321 
00322         std::cout << "c_oflag (" << deviceAttr.c_oflag << "): ";
00323 
00324         std::cout << "OPOST(" << ((deviceAttr.c_oflag & OPOST) == OPOST) << ") ";
00325 #ifndef __APPLE__
00326         std::cout << "OLCUC(" << ((deviceAttr.c_oflag & OLCUC) == OLCUC) << ") ";
00327 #endif
00328         std::cout << "ONLCR(" << ((deviceAttr.c_oflag & ONLCR) == ONLCR) << ") ";
00329         std::cout << "OCRNL(" << ((deviceAttr.c_oflag & OCRNL) == OCRNL) << ") ";
00330         std::cout << "ONOCR(" << ((deviceAttr.c_oflag & ONOCR) == ONOCR) << ") ";
00331         std::cout << "ONLRET(" << ((deviceAttr.c_oflag & ONLRET) == ONLRET) << ") ";
00332         std::cout << "OFILL(" << ((deviceAttr.c_oflag & OFILL) == OFILL) << ") ";
00333         std::cout << "OFDEL(" << ((deviceAttr.c_oflag & OFDEL) == OFDEL) << ") ";
00334 #if defined __USE_MISC || defined __USE_XOPEN
00335         //std::cout << "NLDLY(" << ((deviceAttr.c_oflag & NLDLY) == NLDLY) << ") ";
00336         std::cout << "  NL0(" << ((deviceAttr.c_oflag & NLDLY) == NL0) << ") ";
00337         std::cout << "  NL1(" << ((deviceAttr.c_oflag & NLDLY) == NL1) << ") ";
00338         //std::cout << "CRDLY(" << ((deviceAttr.c_oflag & CRDLY) == CRDLY) << ") ";
00339         std::cout << "  CR0(" << ((deviceAttr.c_oflag & CRDLY) == CR0) << ") ";
00340         std::cout << "  CR1(" << ((deviceAttr.c_oflag & CRDLY) == CR1) << ") ";
00341         std::cout << "  CR2(" << ((deviceAttr.c_oflag & CRDLY) == CR2) << ") ";
00342         std::cout << "  CR3(" << ((deviceAttr.c_oflag & CRDLY) == CR3) << ") ";
00343         //std::cout << "TABDLY(" << ((deviceAttr.c_oflag & TABDLY) == TABDLY) << ") ";
00344         std::cout << "  TAB0(" << ((deviceAttr.c_oflag & TABDLY) == TAB0) << ") ";
00345         std::cout << "  TAB1(" << ((deviceAttr.c_oflag & TABDLY) == TAB1) << ") ";
00346         std::cout << "  TAB2(" << ((deviceAttr.c_oflag & TABDLY) == TAB2) << ") ";
00347         std::cout << "  TAB3(" << ((deviceAttr.c_oflag & TABDLY) == TAB3) << ") ";
00348         //std::cout << "BSDLY(" << ((deviceAttr.c_oflag & BSDLY) == BSDLY) << ") ";
00349         std::cout << "  BS0(" << ((deviceAttr.c_oflag & BSDLY) == BS0) << ") ";
00350         std::cout << "  BS1(" << ((deviceAttr.c_oflag & BSDLY) == BS1) << ") ";
00351         //std::cout << "FFDLY(" << ((deviceAttr.c_oflag & FFDLY) == FFDLY) << ") ";
00352         std::cout << "  FF0(" << ((deviceAttr.c_oflag & FFDLY) == FF0) << ") ";
00353         std::cout << "  FF1(" << ((deviceAttr.c_oflag & FFDLY) == FF1) << ") ";
00354 #endif
00355         std::cout << std::endl;
00356 
00357 
00358         std::cout << "------------------------" << std::endl;
00359 
00360         std::cout << "c_cflag (" << deviceAttr.c_cflag << "): ";
00361 
00362 #ifndef __APPLE__
00363         std::cout << "B0(" << ((deviceAttr.c_cflag & CBAUD) == B0) << ") ";
00364         std::cout << "B50(" << ((deviceAttr.c_cflag & CBAUD) == B50) << ") ";
00365         std::cout << "B75(" << ((deviceAttr.c_cflag & CBAUD) == B75) << ") ";
00366         std::cout << "B110(" << ((deviceAttr.c_cflag & CBAUD) == B110) << ") ";
00367         std::cout << "B134(" << ((deviceAttr.c_cflag & CBAUD) == B134) << ") ";
00368         std::cout << "B150(" << ((deviceAttr.c_cflag & CBAUD) == B150) << ") ";
00369         std::cout << "B200(" << ((deviceAttr.c_cflag & CBAUD) == B200) << ") ";
00370         std::cout << "B300(" << ((deviceAttr.c_cflag & CBAUD) == B300) << ") ";
00371         std::cout << "B600(" << ((deviceAttr.c_cflag & CBAUD) == B600) << ") ";
00372         std::cout << "B1200(" << ((deviceAttr.c_cflag & CBAUD) == B1200) << ") ";
00373         std::cout << "B1800(" << ((deviceAttr.c_cflag & CBAUD) == B1800) << ") ";
00374         std::cout << "B2400(" << ((deviceAttr.c_cflag & CBAUD) == B2400) << ") ";
00375         std::cout << "B4800(" << ((deviceAttr.c_cflag & CBAUD) == B4800) << ") ";
00376         std::cout << "B9600(" << ((deviceAttr.c_cflag & CBAUD) == B9600) << ") ";
00377         std::cout << "B19200(" << ((deviceAttr.c_cflag & CBAUD) == B19200) << ") ";
00378         std::cout << "B38400(" << ((deviceAttr.c_cflag & CBAUD) == B38400) << ") ";
00379         std::cout << "B57600(" << ((deviceAttr.c_cflag & CBAUD) == B57600) << ") ";
00380         std::cout << "B115200(" << ((deviceAttr.c_cflag & CBAUD) == B115200) << ") ";
00381         std::cout << "B230400(" << ((deviceAttr.c_cflag & CBAUD) == B230400) << ") ";
00382         std::cout << "B460800(" << ((deviceAttr.c_cflag & CBAUD) == B460800) << ") ";
00383         std::cout << "B500000(" << ((deviceAttr.c_cflag & CBAUD) == B500000) << ") ";
00384         std::cout << "B576000(" << ((deviceAttr.c_cflag & CBAUD) == B576000) << ") ";
00385         std::cout << "B921600(" << ((deviceAttr.c_cflag & CBAUD) == B921600) << ") ";
00386         std::cout << "B1000000(" << ((deviceAttr.c_cflag & CBAUD) == B1000000) << ") ";
00387         std::cout << "B1152000(" << ((deviceAttr.c_cflag & CBAUD) == B1152000) << ") ";
00388         std::cout << "B1500000(" << ((deviceAttr.c_cflag & CBAUD) == B1500000) << ") ";
00389         std::cout << "B2000000(" << ((deviceAttr.c_cflag & CBAUD) == B2000000) << ") ";
00390         std::cout << "B2500000(" << ((deviceAttr.c_cflag & CBAUD) == B2500000) << ") ";
00391         std::cout << "B3000000(" << ((deviceAttr.c_cflag & CBAUD) == B3000000) << ") ";
00392         std::cout << "B3500000(" << ((deviceAttr.c_cflag & CBAUD) == B3500000) << ") ";
00393         std::cout << "B4000000(" << ((deviceAttr.c_cflag & CBAUD) == B4000000) << ") ";
00394 #endif
00395 
00396         //std::cout << "CSIZE(" << ((deviceAttr.c_cflag & CSIZE) == B0) << ") ";
00397         std::cout << "CS5(" << ((deviceAttr.c_cflag & CSIZE) == CS5) << ") ";
00398         std::cout << "CS6(" << ((deviceAttr.c_cflag & CSIZE) == CS6) << ") ";
00399         std::cout << "CS7(" << ((deviceAttr.c_cflag & CSIZE) == CS7) << ") ";
00400         std::cout << "CS8(" << ((deviceAttr.c_cflag & CSIZE) == CS8) << ") ";
00401         std::cout << "CSTOPB(" << ((deviceAttr.c_cflag & CSTOPB) == CSTOPB) << ") ";
00402         std::cout << "CREAD(" << ((deviceAttr.c_cflag & CREAD) == CREAD) << ") ";
00403         std::cout << "PARENB(" << ((deviceAttr.c_cflag & PARENB) == PARENB) << ") ";
00404         std::cout << "PARODD(" << ((deviceAttr.c_cflag & PARODD) == PARODD) << ") ";
00405         std::cout << "HUPCL(" << ((deviceAttr.c_cflag & HUPCL) == HUPCL) << ") ";
00406         std::cout << "CLOCAL(" << ((deviceAttr.c_cflag & CLOCAL) == CLOCAL) << ") ";
00407 #ifdef __USE_MISC
00408         std::cout << "CBAUDEX(" << ((deviceAttr.c_cflag & CBAUDEX) == CBAUDEX) << ") ";
00409 #endif
00410 
00411 #ifdef __USE_MISC
00412         std::cout << "CIBAUD(" << ((deviceAttr.c_cflag & CIBAUD) == CIBAUD) << ") ";
00413         std::cout << "CMSPAR(" << ((deviceAttr.c_cflag & CMSPAR) == CMSPAR) << ") ";
00414         std::cout << "CRTSCTS(" << ((deviceAttr.c_cflag & CRTSCTS) == CRTSCTS) << ") ";
00415 #endif
00416 
00417         std::cout << std::endl;
00418         speed_t ispeed = cfgetispeed(&deviceAttr);
00419         speed_t ospeed = cfgetospeed(&deviceAttr);
00420         std::cout << "BAUD Rate (IN): " << ispeed << " " << "BAUD Rate (OUT): " << ospeed << std::endl;
00421         std::cout << "------------------------" << std::endl;
00422 
00423         std::cout << "c_lflag (" << deviceAttr.c_lflag << "): ";
00424         std::cout << "ISIG(" << ((deviceAttr.c_lflag & ISIG) == ISIG) << ") ";
00425         std::cout << "ICANON(" << ((deviceAttr.c_lflag & ICANON) == ICANON) << ") ";
00426 #if defined __USE_MISC || defined __USE_XOPEN
00427         std::cout << "XCASE(" << ((deviceAttr.c_lflag & XCASE) == XCASE) << ") ";
00428 #endif
00429         std::cout << "ECHO(" << ((deviceAttr.c_lflag & ECHO) == ECHO) << ") ";
00430         std::cout << "ECHOE(" << ((deviceAttr.c_lflag & ECHOE) == ECHOE) << ") ";
00431         std::cout << "ECHOK(" << ((deviceAttr.c_lflag & ECHOK) == ECHOK) << ") ";
00432         std::cout << "ECHONL(" << ((deviceAttr.c_lflag & ECHONL) == ECHONL) << ") ";
00433         std::cout << "NOFLSH(" << ((deviceAttr.c_lflag & NOFLSH) == NOFLSH) << ") ";
00434         std::cout << "TOSTOP(" << ((deviceAttr.c_lflag & TOSTOP) == TOSTOP) << ") ";
00435 #ifdef __USE_MISC
00436         std::cout << "ECHOCTL(" << ((deviceAttr.c_lflag & ECHOCTL) == ECHOCTL) << ") ";
00437         std::cout << "ECHOPRT(" << ((deviceAttr.c_lflag & ECHOPRT) == ECHOPRT) << ") ";
00438         std::cout << "ECHOKE(" << ((deviceAttr.c_lflag & ECHOKE) == ECHOKE) << ") ";
00439         std::cout << "FLUSHO(" << ((deviceAttr.c_lflag & FLUSHO) == FLUSHO) << ") ";
00440         std::cout << "PENDIN(" << ((deviceAttr.c_lflag & PENDIN) == PENDIN) << ") ";
00441 #endif
00442         std::cout << "IEXTEN(" << ((deviceAttr.c_lflag & IEXTEN) == IEXTEN) << ") ";
00443 #ifdef __USE_BSD
00444         std::cout << "EXTPROC(" << ((deviceAttr.c_lflag & EXTPROC) == EXTPROC) << ") ";
00445 #endif
00446         std::cout << std::endl;
00447         std::cout << "------------------------" << std::endl;
00448 
00449 
00450 
00451 }
00452 
00453 } // namespace
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Defines


telekyb_serial
Author(s): Martin Riedel
autogenerated on Mon Nov 11 2013 11:14:08