// $Id: RlinkPort.cpp 887 2017-04-28 19:32:52Z mueller $ // // Copyright 2011-2017 by Walter F.J. Mueller // // This program is free software; you may redistribute and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation, either version 2, or at your option any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for complete details. // // Revision History: // Date Rev Version Comment // 2017-04-07 868 1.4.1 Dump(): add detail arg // 2017-02-19 853 1.4 use Rtime, drop TimeOfDayAsDouble // 2015-04-11 666 1.3 add fXon, XonEnable() // 2014-12-10 611 1.2.4 add time stamps for Read/Write for logs // 2014-11-29 607 1.2.3 BUGFIX: fix time handling on RawRead() // 2014-11-23 606 1.2.2 use Rtools::TimeOfDayAsDouble() // 2014-08-22 584 1.2.1 use nullptr // 2013-02-23 492 1.2 use RparseUrl // 2013-02-22 491 1.1 use new RlogFile/RlogMsg interfaces // 2013-02-10 485 1.0.5 add static const defs // 2013-02-03 481 1.0.4 use Rexception // 2013-01-27 477 1.0.3 add RawRead(),RawWrite() methods // 2012-12-28 466 1.0.2 allow Close() even when not open // 2012-12-26 465 1.0.1 add CloseFd() method // 2011-03-27 375 1.0 Initial version // 2011-01-15 356 0.1 First draft // --------------------------------------------------------------------------- /*! \file \brief Implemenation of RlinkPort. */ #include #include #include #include #include "librtools/RosFill.hpp" #include "librtools/RosPrintf.hpp" #include "librtools/RosPrintBvi.hpp" #include "librtools/Rexception.hpp" #include "librtools/RlogMsg.hpp" #include "librtools/Rtools.hpp" #include "RlinkPort.hpp" using namespace std; /*! \class Retro::RlinkPort \brief FIXME_docs */ // all method definitions in namespace Retro namespace Retro { //------------------------------------------+----------------------------------- // constants definitions const int RlinkPort::kEof; const int RlinkPort::kTout; const int RlinkPort::kErr; //------------------------------------------+----------------------------------- //! Default constructor RlinkPort::RlinkPort() : fIsOpen(false), fUrl(), fXon(false), fFdRead(-1), fFdWrite(-1), fspLog(), fTraceLevel(0), fTsLastRead(), fTsLastWrite(), fStats() { fStats.Define(kStatNPortWrite, "NPortWrite", "Port::Write() calls"); fStats.Define(kStatNPortRead, "NPortRead", "Port::Read() calls"); fStats.Define(kStatNPortTxByt, "NPortTxByt", "Port Tx bytes send"); fStats.Define(kStatNPortRxByt, "NPortRxByt", "Port Rx bytes rcvd"); fStats.Define(kStatNPortRawWrite, "NPortRawWrite", "Port::RawWrite() calls"); fStats.Define(kStatNPortRawRead, "NPortRawRead", "Port::RawRead() calls"); } //------------------------------------------+----------------------------------- //! Destructor RlinkPort::~RlinkPort() { if (IsOpen()) RlinkPort::Close(); } //------------------------------------------+----------------------------------- //! FIXME_docs void RlinkPort::Close() { if (!IsOpen()) return; if (fFdWrite == fFdRead) fFdWrite = -1; CloseFd(fFdWrite); CloseFd(fFdRead); fIsOpen = false; fUrl.Clear(); return; } //------------------------------------------+----------------------------------- //! FIXME_docs int RlinkPort::Read(uint8_t* buf, size_t size, const Rtime& timeout, RerrMsg& emsg) { if (!IsOpen()) throw Rexception("RlinkPort::Read()","Bad state: port not open"); if (buf == nullptr) throw Rexception("RlinkPort::Read()","Bad args: buf==nullptr"); if (size == 0) throw Rexception("RlinkPort::Read()","Bad args: size==0"); fStats.Inc(kStatNPortRead); bool rdpoll = PollRead(timeout); if (!rdpoll) return kTout; int irc = -1; while (irc < 0) { irc = ::read(fFdRead, (void*) buf, size); if (irc < 0 && errno != EINTR) { emsg.InitErrno("RlinkPort::Read()", "read() failed : ", errno); if (fspLog && fTraceLevel>0) fspLog->Write(emsg.Message(), 'E'); return kErr; } } if (fspLog && fTraceLevel>0) { RlogMsg lmsg(*fspLog, 'I'); lmsg << "port read nchar=" << RosPrintf(irc,"d",4); Rtime now(CLOCK_MONOTONIC); if (fTsLastRead.IsPositive()) lmsg << " dt_rd=" << RosPrintf(double(now-fTsLastRead),"f",8,6); if (fTsLastWrite.IsPositive()) lmsg << " dt_wr=" << RosPrintf(double(now-fTsLastWrite),"f",8,6); fTsLastRead = now; if (fTraceLevel>1) { size_t ncol = (80-5-6)/(2+1); for (int i=0; i0) { RlogMsg lmsg(*fspLog, 'I'); lmsg << "port write nchar=" << RosPrintf(size,"d",4); Rtime now(CLOCK_MONOTONIC); if (fTsLastRead.IsPositive()) lmsg << " dt_rd=" << RosPrintf(double(now-fTsLastRead),"f",8,6); if (fTsLastWrite.IsPositive()) lmsg << " dt_wr=" << RosPrintf(double(now-fTsLastWrite),"f",8,6); fTsLastWrite = now; if (fTraceLevel>1) { size_t ncol = (80-5-6)/(2+1); for (size_t i=0; i0) fspLog->Write(emsg.Message(), 'E'); return kErr; } } // FIXME_code: handle eof ?? ndone += irc; } fStats.Inc(kStatNPortTxByt, double(ndone)); return ndone; } //------------------------------------------+----------------------------------- //! FIXME_docs bool RlinkPort::PollRead(const Rtime& timeout) { if (! IsOpen()) throw Rexception("RlinkPort::PollRead()","Bad state: port not open"); if (timeout.IsNegative()) throw Rexception("RlinkPort::PollRead()","Bad args: timeout < 0"); int ito = timeout.ToMSec(); struct pollfd fds[1] = {{fFdRead, // fd POLLIN, // events 0}}; // revents int irc = -1; while (irc < 0) { irc = ::poll(fds, 1, ito); if (irc < 0 && errno != EINTR) throw Rexception("RlinkPort::PollRead()","poll() failed: rc<0: ", errno); } if (irc == 0) return false; if (fds[0].revents == POLLERR) throw Rexception("RlinkPort::PollRead()", "poll() failed: POLLERR"); return true; } //------------------------------------------+----------------------------------- //! FIXME_docs int RlinkPort::RawRead(uint8_t* buf, size_t size, bool exactsize, const Rtime& timeout, Rtime& tused, RerrMsg& emsg) { if (!timeout.IsPositive()) throw Rexception("RlinkPort::RawRead()", "Bad args: timeout <= 0."); if (size <= 0) throw Rexception("RlinkPort::RawRead()", "Bad args: size <= 0"); fStats.Inc(kStatNPortRawRead); tused.Clear(); Rtime tnow(CLOCK_MONOTONIC); Rtime tend = tnow + timeout; Rtime tbeg = tnow; size_t ndone = 0; while (tnow < tend && ndone= 0) { ::close(fd); fd = -1; } return; } } // end namespace Retro