/*! \brief Create an empty file \param filename The desired (path+)name of the file \returns XRV_OK if the file was created successfully */ XsResultValue IoInterfaceFile::create(const XsString& filename) { if (m_handle) return m_lastResult = XRV_ALREADYOPEN; //! \test does this work for non-existing files? Or do we need a check and //! create? #ifdef _WIN32 m_handle = _wfopen( filename.toStdWString().c_str(), L"w+b"); // open for update (r/w) #else m_handle = fopen(filename.c_str(), "w+b"); // open for update (r/w) #endif if (m_handle == nullptr) return m_lastResult = XRV_OUTPUTCANNOTBEOPENED; bool fail = false; #ifdef _WIN32 wchar_t fullpath[XS_MAX_FILENAME_LENGTH]; if (_wfullpath( fullpath, filename.toStdWString().c_str(), XS_MAX_FILENAME_LENGTH) == nullptr) fail = true; #else // based on the assumption that this doesn't concern the serial port, handle // it the same way using realpath(). Apparently realpath() doesn't require a // maximum length. One would possibly want to write a wrapper for it. char fullpath[XS_MAX_FILENAME_LENGTH * 2]; if (realpath(filename.c_str(), fullpath) == nullptr) fail = true; #endif m_filename = XsString(fullpath); if (fail) { fclose(m_handle); #ifdef _WIN32 _wunlink(m_filename.toStdWString().c_str()); #else unlink(m_filename.c_str()); #endif m_handle = 0; return m_lastResult = XRV_INVALIDPARAM; } m_readPos = 0; m_writePos = 0; m_fileSize = 0; m_reading = true; m_readOnly = false; return m_lastResult = XRV_OK; }
/*! \copydoc IoInterface::writeData \note The default timeout is respected in this operation. */ XsResultValue SerialInterface::writeData (const XsByteArray& data, XsSize* written) { XsSize bytes; if (written == NULL) written = &bytes; if (!isOpen()) return (m_lastResult = XRV_NOPORTOPEN); *written = 0; #ifdef _WIN32 DWORD lwritten = 0; if (WriteFile(m_handle, data.data(), (DWORD) data.size(), &lwritten, NULL) == 0) { DWORD wErr = ::GetLastError(); JLALERT(gJournal, "WriteFile returned windows error " << wErr); if (wErr == ERROR_ACCESS_DENIED) return (m_lastResult = XRV_UNEXPECTED_DISCONNECT); return (m_lastResult = XRV_ERROR); } *written = lwritten; #else ssize_t result = write(m_handle, (const void*)data.data(), data.size()); if (result <= 0) { int err = errno; *written = 0; switch (err) { case EAGAIN: #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif return XRV_TIMEOUT; case EIO: return XRV_UNEXPECTED_DISCONNECT; /* we don't expect any other errors to actually occur */ default: break; } } if (result < 0) *written = 0; else *written = result; #endif #ifdef LOG_RX_TX if (written[0] > 0) { if (!tx_log.isOpen()) { char fname[XS_MAX_FILENAME_LENGTH]; #ifdef _WIN32 sprintf(fname, "tx_%03d_%d.log", (int32_t) m_port, m_baudrate); #else char *devname = strrchr(m_portname, '/'); sprintf(fname,"tx_%s_%d.log", devname + 1, XsBaud::rateToNumeric(m_baudrate)); #endif makeFilenameUnique(fname); tx_log.create(XsString(fname), true); } tx_log.write(data.data(), 1, *written); #ifdef LOG_RX_TX_FLUSH tx_log.flush(); #endif } #endif return (m_lastResult = XRV_OK); }
/*! \brief Read data from the serial port and put it into the data buffer. \details This function reads up to \a maxLength bytes from the port (non-blocking) and puts it into the \a data buffer. \param maxLength The maximum amount of data read. \param data The buffer that will store the received data. \returns XRV_OK if no error occurred. It can be that no data is available and XRV_OK will be returned. Check data.size() for the number of bytes that were read. */ XsResultValue SerialInterface::readData(XsSize maxLength, XsByteArray& data) { if (!isOpen()) return (m_lastResult = XRV_NOPORTOPEN); #ifdef _WIN32 DWORD length; data.setSize(maxLength); BOOL rres = ::ReadFile(m_handle, data.data(), (DWORD) maxLength, &length, NULL); data.pop_back(maxLength-length); JLTRACE(gJournal, "ReadFile result " << rres << ", length " << length); if (!rres) { DWORD wErr = ::GetLastError(); JLALERT(gJournal, "ReadFile returned windows error " << wErr); if (wErr == ERROR_ACCESS_DENIED) return (m_lastResult = XRV_UNEXPECTED_DISCONNECT); if (wErr >= ERROR_INVALID_FUNCTION && wErr <= ERROR_INVALID_HANDLE) return (m_lastResult = XRV_NOFILEORPORTOPEN); return (m_lastResult = XRV_ERROR); } if (length == 0) return (m_lastResult = XRV_TIMEOUT); #else fd_set fd; fd_set err; timeval timeout; FD_ZERO(&fd); FD_ZERO(&err); FD_SET(m_handle, &fd); FD_SET(m_handle, &err); timeout.tv_sec = m_timeout/1000; timeout.tv_usec = (m_timeout - (timeout.tv_sec * 1000)) * 1000; int res = select(FD_SETSIZE, &fd, NULL, &err, &timeout); if (res < 0 || FD_ISSET(m_handle, &err)) { data.clear(); return (m_lastResult = XRV_ERROR); } else if (res == 0) { data.clear(); return (m_lastResult = XRV_TIMEOUT); } data.setSize(maxLength); int length = read(m_handle, (void*)data.data(), maxLength); if (length > 0) { data.pop_back(maxLength - length); } else { int err = errno; data.clear(); switch (err) { case EAGAIN: #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif return XRV_TIMEOUT; case EIO: return XRV_UNEXPECTED_DISCONNECT; default: break; } } #if defined(JLLOGLEVEL) && JLLOGLEVEL <= JLL_TRACE && !defined(ANDROID) serial_icounter_struct ic; res = ioctl(m_handle, TIOCGICOUNT, &ic); if (res == 0) { JLTRACE(gJournal, "rx: " << ic.rx); JLTRACE(gJournal, "tx: " << ic.tx); JLTRACE(gJournal, "frame " << ic.frame); JLTRACE(gJournal, "overrun " << ic.overrun); JLTRACE(gJournal, "buf_overrun " << ic.buf_overrun); } #endif #endif #ifdef LOG_RX_TX if (length > 0) { if (!rx_log.isOpen()) { char fname[XS_MAX_FILENAME_LENGTH]; #ifdef _WIN32 sprintf(fname, "rx_%03d_%d.log", (int32_t) m_port, m_baudrate); #else char *devname = strrchr(m_portname, '/'); sprintf(fname, "rx_%s_%d.log", devname + 1, XsBaud::rateToNumeric(m_baudrate)); #endif makeFilenameUnique(fname); rx_log.create(XsString(fname), true); } rx_log.write(data.data(), 1, length); #ifdef LOG_RX_TX_FLUSH rx_log.flush(); #endif } #endif JLTRACE(gJournal, "returned success, read " << length << " of " << maxLength << " bytes, first: " << JLHEXLOG(data[0])); return (m_lastResult = XRV_OK); }