222 lines
6.9 KiB
C++
222 lines
6.9 KiB
C++
/*
|
|
PIP - Platform Independent Primitives
|
|
Abstract input/output device
|
|
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, 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 more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "piiodevice.h"
|
|
#include "piconfig.h"
|
|
|
|
|
|
/*! \class PIIODevice
|
|
* \brief Base class for input/output classes
|
|
*
|
|
* \section PIIODevice_sec0 Synopsis
|
|
* This class provide open/close logic, threaded read/write and virtual input/output
|
|
* functions \a read() and \a write(). You should implement pure virtual
|
|
* function \a openDevice() in your subclass.
|
|
*
|
|
* \section PIIODevice_sec1 Open and close
|
|
* PIIODevice have boolean variable indicated open status. Returns of functions
|
|
* \a openDevice() and \a closeDevice() change this variable.
|
|
*
|
|
* \section PIIODevice_sec2 Threaded read
|
|
* PIIODevice based on PIThread, so it`s overload \a run() to exec \a read()
|
|
* in background thread. If read is successful virtual function \a threadedRead()
|
|
* is executed. Default implementation of this function execute external static
|
|
* function set by \a setThreadedReadSlot() with data set by \a setThreadedReadData().
|
|
* Extrenal static function should have format \n
|
|
* bool func_name(void * Threaded_read_data, uchar * readed_data, int readed_size)\n
|
|
* Threaded read starts with function \a startThreadedRead().
|
|
*
|
|
* \section PIIODevice_sec3 Threaded write
|
|
* PIIODevice aggregate another PIThread to perform a threaded write by function
|
|
* \a writeThreaded(). This function add task to internal queue and return
|
|
* queue entry ID. You should start write thread by function \a startThreadedWrite.
|
|
* On successful write event \a threadedWriteEvent is raised with two arguments -
|
|
* task ID and written bytes count.
|
|
*
|
|
* \section PIIODevice_sec4 Internal buffer
|
|
* PIIODevice have internal buffer for threaded read, and \a threadedRead() function
|
|
* receive pointer to this buffer in first argument. You can adjust size of this buffer
|
|
* by function \a setThreadedReadBufferSize() \n
|
|
* Default size of this buffer is 4096 bytes
|
|
*
|
|
* \section PIIODevice_sec5 Reopen
|
|
* When threaded read is begin its call \a open() if device is closed. While threaded
|
|
* read running PIIODevice check if device opened every read and if not call \a open()
|
|
* every reopen timeout if reopen enabled. Reopen timeout is set by \a setReopenTimeout(),
|
|
* reopen enable is set by \a setReopenEnabled().
|
|
*
|
|
* \section PIIODevice_ex0 Example
|
|
* \snippet piiodevice.cpp 0
|
|
*/
|
|
|
|
|
|
PIIODevice::PIIODevice(): PIThread() {
|
|
mode_ = ReadOnly;
|
|
opened_ = init_ = thread_started_ = false;
|
|
reopen_enabled_ = raise_threaded_read_ = true;
|
|
reopen_timeout_ = 1000;
|
|
ret_func_ = 0;
|
|
ret_data_ = 0;
|
|
tri = 0;
|
|
buffer_tr.resize(4096);
|
|
CONNECT2(void, void * , int, &timer, timeout, this, check_start);
|
|
CONNECT(void, &write_thread, started, this, write_func);
|
|
init();
|
|
}
|
|
|
|
|
|
/*! \brief Constructs a PIIODevice with path and mode
|
|
* \param path path to device
|
|
* \param type mode for open
|
|
* \param initNow init or not in constructor */
|
|
PIIODevice::PIIODevice(const PIString & path, PIIODevice::DeviceMode type, bool initNow): PIThread() {
|
|
path_ = path;
|
|
mode_ = type;
|
|
opened_ = init_ = thread_started_ = false;
|
|
reopen_enabled_ = raise_threaded_read_ = true;
|
|
reopen_timeout_ = 1000;
|
|
ret_func_ = 0;
|
|
ret_data_ = 0;
|
|
tri = 0;
|
|
buffer_tr.resize(4096);
|
|
CONNECT2(void, void * , int, &timer, timeout, this, check_start);
|
|
CONNECT(void, &write_thread, started, this, write_func);
|
|
if (initNow) init();
|
|
}
|
|
|
|
|
|
PIIODevice::~PIIODevice() {
|
|
stop();
|
|
if (opened_) {
|
|
closeDevice();
|
|
if (!opened_)
|
|
closed();
|
|
}
|
|
}
|
|
|
|
|
|
void PIIODevice::check_start(void * data, int delim) {
|
|
//cout << "check " << tread_started_ << endl;
|
|
if (open()) {
|
|
thread_started_ = true;
|
|
timer.stop();
|
|
}
|
|
}
|
|
|
|
|
|
void PIIODevice::write_func() {
|
|
while (!write_thread.isStopping()) {
|
|
while (!write_queue.isEmpty()) {
|
|
if (write_thread.isStopping()) return;
|
|
write_thread.lock();
|
|
PIPair<PIByteArray, ullong> item(write_queue.dequeue());
|
|
write_thread.unlock();
|
|
int ret = write(item.first);
|
|
threadedWriteEvent(item.second, ret);
|
|
}
|
|
msleep(1);
|
|
}
|
|
}
|
|
|
|
|
|
void PIIODevice::terminate() {
|
|
thread_started_ = false;
|
|
if (!isInitialized()) return;
|
|
if (isRunning()) {
|
|
stop();
|
|
PIThread::terminate();
|
|
}
|
|
}
|
|
|
|
|
|
void PIIODevice::begin() {
|
|
//cout << " begin\n";
|
|
thread_started_ = false;
|
|
if (!opened_) {
|
|
if (open()) {
|
|
thread_started_ = true;
|
|
//cout << " open && ok\n";
|
|
return;
|
|
}
|
|
} else {
|
|
thread_started_ = true;
|
|
//cout << " ok\n";
|
|
return;
|
|
}
|
|
//init();
|
|
if (!timer.isRunning() && reopen_enabled_) timer.start(reopen_timeout_);
|
|
}
|
|
|
|
|
|
void PIIODevice::run() {
|
|
if (!isReadable()) {
|
|
//cout << "not readable\n";
|
|
PIThread::stop();
|
|
return;
|
|
}
|
|
if (!thread_started_) {
|
|
msleep(1);
|
|
//cout << "not started\n";
|
|
return;
|
|
}
|
|
readed_ = read(buffer_tr.data(), buffer_tr.size_s());
|
|
if (readed_ <= 0) {
|
|
msleep(10);
|
|
//cout << readed_ << ", " << errno << ", " << errorString() << endl;
|
|
return;
|
|
}
|
|
threadedRead(buffer_tr.data(), readed_);
|
|
if (raise_threaded_read_) threadedReadEvent(buffer_tr.data(), readed_);
|
|
}
|
|
|
|
|
|
ullong PIIODevice::writeThreaded(const PIByteArray & data) {
|
|
write_thread.lock();
|
|
write_queue.enqueue(PIPair<PIByteArray, ullong>(data, tri));
|
|
++tri;
|
|
write_thread.unlock();
|
|
return tri - 1;
|
|
}
|
|
|
|
|
|
bool PIIODevice::configure(const PIString & config_file, const PIString & section, bool parent_section) {
|
|
PIConfig conf(config_file, PIIODevice::ReadOnly);
|
|
if (!conf.isOpened()) return false;
|
|
bool ex = true;
|
|
PIConfig::Entry em;
|
|
if (section.isEmpty()) em = conf.rootEntry();
|
|
else em = conf.getValue(section, PIString(), &ex);
|
|
if (!ex) return false;
|
|
PIConfig::Entry * ep = 0;
|
|
if (parent_section) ep = em.parent();
|
|
if (ep != 0) {
|
|
setReopenEnabled(ep->getValue("reopenEnabled", reopen_enabled_, &ex));
|
|
if (!ex) setReopenEnabled(em.getValue("reopenEnabled", reopen_enabled_));
|
|
setReopenTimeout(ep->getValue("reopenTimeout", reopen_timeout_, &ex));
|
|
if (!ex) setReopenTimeout(em.getValue("reopenTimeout", reopen_timeout_));
|
|
setThreadedReadBufferSize(ep->getValue("threadedReadBufferSize", buffer_tr.size_s(), &ex));
|
|
if (!ex) setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", buffer_tr.size_s()));
|
|
} else {
|
|
setReopenEnabled(em.getValue("reopenEnabled", reopen_enabled_));
|
|
setReopenTimeout(em.getValue("reopenTimeout", reopen_timeout_));
|
|
setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", buffer_tr.size_s()));
|
|
}
|
|
return configureDevice(&em, ep);
|
|
}
|