616 lines
19 KiB
C++
616 lines
19 KiB
C++
/*
|
||
PIP - Platform Independent Primitives
|
||
Abstract input/output device
|
||
Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Lesser General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "piiodevice.h"
|
||
#include "piconfig.h"
|
||
#include "piconnection.h"
|
||
#include "pipropertystorage.h"
|
||
|
||
|
||
//! \class PIIODevice piiodevice.h
|
||
//! \~\details
|
||
//! \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_sec6 Configuration
|
||
//! This is virtual function \a configureDevice() which executes when \a configure()
|
||
//! executes. This function takes two arguments: "e_main" and "e_parent" as void*. There
|
||
//! are pointers to PIConfig::Entry entries of section "section" and their parent. If
|
||
//! there is no parent "e_parent" = 0. Function \a configure() set three parameters of
|
||
//! device: "reopenEnabled", "reopenTimeout" and "threadedReadBufferSize", then execute
|
||
//! function \a configureDevice().
|
||
//! \n Each ancestor of %PIIODevice reimlements \a configureDevice() function to be able
|
||
//! to be confured from configuration file. This parameters described at section
|
||
//! "Configurable parameters" in the class reference. \n Usage example:
|
||
//! \snippet piiodevice.cpp configure
|
||
//! Implementation example:
|
||
//! \snippet piiodevice.cpp configureDevice
|
||
//!
|
||
//! \section PIIODevice_sec7 Creating devices by unambiguous string
|
||
//! There are some virtual functions to describe child class without its declaration.
|
||
//! \n \a constructFullPath() should returns full unambiguous string, contains prefix and all device parameters
|
||
//! \n \a configureFromFullPath() provide configuring device from full unambiguous string without prefix and "://"
|
||
//! \n Macro PIIODEVICE should be used instead of PIOBJECT
|
||
//! \n Macro REGISTER_DEVICE should be used after declaration of class, i.e. at the last line of *.h file
|
||
//! \n \n If custom I/O device corresponds there rules, it can be returned by function \a createFromFullPath().
|
||
//! \n Each PIP I/O device has custom unambiguous string description:
|
||
//! * PIFile: "file://<path>"
|
||
//! * PIBinaryLog: "binlog://<logDir>[:<filePrefix>][:<defaultID>]"
|
||
//! * PISerial: "ser://<device>:<speed(50|...|115200)>[:<dataBitsCount(6|7|8)>][:<parity(N|E|O)>][:<stopBits(1|2)>]"
|
||
//! * PIEthernet: UDP "eth://UDP:<readIP>:<readPort>:<sendIP>:<sendPort>[:<multicast(mcast:<ip>)>]"
|
||
//! * PIEthernet: TCP "eth://TCP:<IP>:<Port>"
|
||
//! * PIUSB: "usb://<vid>:<pid>[:<deviceNumber>][:<readEndpointNumber>][:<writeEndpointNumber>]"
|
||
//! \n \n Examples:
|
||
//! * PIFile: "file://../text.txt"
|
||
//! * PIBinaryLog: "binlog://../logs/:mylog_:1"
|
||
//! * PISerial: "ser:///dev/ttyUSB0:9600:8:N:1", equivalent "ser:///dev/ttyUSB0:9600"
|
||
//! * PIEthernet: "eth://TCP:127.0.0.1:16666", "eth://UDP:192.168.0.5:16666:192.168.0.6:16667:mcast:234.0.2.1:mcast:234.0.2.2"
|
||
//! * PIUSB: "usb://0bb4:0c86:1:1:2"
|
||
//! \n \n
|
||
//! So, custom I/O device can be created with next call:
|
||
//! \code{cpp}
|
||
//! // creatring devices
|
||
//! PISerial * ser = (PISerial * )PIIODevice::createFromFullPath("ser://COM1:115200");
|
||
//! PIEthernet * eth = (PIEthernet * )PIIODevice::createFromFullPath("eth://UDP:127.0.0.1:4001:127.0.0.1:4002");
|
||
//! // examine devices
|
||
//! piCout << ser << ser->properties();
|
||
//! piCout << eth << eth->properties();
|
||
//! \endcode
|
||
//!
|
||
//! \section PIIODevice_ex0 Example
|
||
//! \snippet piiodevice.cpp 0
|
||
//!
|
||
|
||
|
||
PIMutex PIIODevice::nfp_mutex;
|
||
PIMap<PIString, PIString> PIIODevice::nfp_cache;
|
||
|
||
|
||
PIIODevice::PIIODevice(): PIThread() {
|
||
mode_ = ReadOnly;
|
||
_init();
|
||
setPath(PIString());
|
||
}
|
||
|
||
|
||
PIIODevice::PIIODevice(const PIString & path, PIIODevice::DeviceMode mode): PIThread() {
|
||
mode_ = mode;
|
||
_init();
|
||
setPath(path);
|
||
}
|
||
|
||
|
||
PIIODevice::~PIIODevice() {
|
||
stop();
|
||
}
|
||
|
||
|
||
void PIIODevice::setOptions(PIIODevice::DeviceOptions o) {
|
||
options_ = o;
|
||
optionsChanged();
|
||
}
|
||
|
||
|
||
bool PIIODevice::setOption(PIIODevice::DeviceOption o, bool yes) {
|
||
bool ret = isOptionSet(o);
|
||
options_.setFlag(o, yes);
|
||
optionsChanged();
|
||
return ret;
|
||
}
|
||
|
||
|
||
//! \~\details
|
||
//! \~english
|
||
//! Set external static function of threaded read that will be executed
|
||
//! at every successful threaded read. Function should have format
|
||
//! "bool func(void * data, uchar * readed, int size)"
|
||
//! \~russian
|
||
//! Устанавливает внешний статический метод, который будет вызван
|
||
//! после каждого успешного потокового чтения. Метод должен быть
|
||
//! в формате "bool func(void * data, uchar * readed, int size)"
|
||
void PIIODevice::setThreadedReadSlot(ReadRetFunc func) {
|
||
func_read = func;
|
||
}
|
||
|
||
|
||
//! \~\details
|
||
//! \~english
|
||
//! Default size is 4096 bytes. If your device can read at single read
|
||
//! more than 4096 bytes you should use this function to adjust buffer size
|
||
//! \~russian
|
||
//! По умолчанию 4096 байт. Если устройство за одно чтение может читать
|
||
//! более 4096 байт, необходимо использовать этот метод для установки
|
||
//! нужного размера буфера
|
||
void PIIODevice::setThreadedReadBufferSize(int new_size) {
|
||
threaded_read_buffer_size = new_size;
|
||
threadedReadBufferSizeChanged();
|
||
}
|
||
|
||
|
||
bool stopThread(PIThread * t, bool hard) {
|
||
#ifdef MICRO_PIP
|
||
t->stop(true);
|
||
#else
|
||
if (hard) {
|
||
t->terminate();
|
||
return true;
|
||
} else {
|
||
t->stop();
|
||
if (!t->waitForFinish(10000)) {
|
||
t->terminate();
|
||
return true;
|
||
}
|
||
}
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
|
||
void PIIODevice::stopThreadedRead(bool hard) {
|
||
if (stopThread(this, hard))
|
||
threadedReadTerminated();
|
||
}
|
||
|
||
|
||
void PIIODevice::stopThreadedWrite(bool hard) {
|
||
if (stopThread(&write_thread, hard))
|
||
threadedWriteTerminated();
|
||
}
|
||
|
||
|
||
void PIIODevice::clearThreadedWriteQueue() {
|
||
write_thread.lock();
|
||
write_queue.clear();
|
||
write_thread.unlock();
|
||
}
|
||
|
||
|
||
void PIIODevice::start() {
|
||
startThreadedRead();
|
||
startThreadedWrite();
|
||
}
|
||
|
||
|
||
void PIIODevice::stop(bool hard) {
|
||
stopThreadedRead(hard);
|
||
stopThreadedWrite(hard);
|
||
}
|
||
|
||
|
||
PIByteArray PIIODevice::read(int max_size) {
|
||
buffer_in.resize(max_size);
|
||
int ret = readDevice(buffer_in.data(), max_size);
|
||
if (ret < 0) return PIByteArray();
|
||
return buffer_in.resized(ret);
|
||
}
|
||
|
||
|
||
void PIIODevice::_init() {
|
||
opened_ = init_ = thread_started_ = false;
|
||
raise_threaded_read_ = true;
|
||
func_read = nullptr;
|
||
ret_data_ = nullptr;
|
||
tri = 0;
|
||
setOptions(0);
|
||
setReopenEnabled(true);
|
||
setReopenTimeout(1000);
|
||
#ifdef MICRO_PIP
|
||
threaded_read_buffer_size = 512;
|
||
//setThreadedReadBufferSize(512);
|
||
#else
|
||
threaded_read_buffer_size = 4096;
|
||
#endif
|
||
timer.setName("__S__.PIIODevice.reopen_timer");
|
||
write_thread.setName("__S__.PIIODevice.write_thread");
|
||
CONNECT2(void, void * , int, &timer, tickEvent, this, check_start);
|
||
CONNECT(void, &write_thread, started, this, write_func);
|
||
}
|
||
|
||
|
||
void PIIODevice::check_start(void * data, int delim) {
|
||
//cout << "check " << tread_started_ << endl;
|
||
if (open()) {
|
||
thread_started_ = true;
|
||
timer.stop(false);
|
||
}
|
||
}
|
||
|
||
|
||
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);
|
||
}
|
||
piMinSleep();
|
||
}
|
||
}
|
||
|
||
|
||
PIIODevice * PIIODevice::newDeviceByPrefix(const char * prefix) {
|
||
if (!prefix) return nullptr;
|
||
auto fi = fabrics().value(prefix);
|
||
if (fi.fabricator)
|
||
return fi.fabricator();
|
||
return nullptr;
|
||
}
|
||
|
||
|
||
void PIIODevice::terminate() {
|
||
timer.stop();
|
||
thread_started_ = false;
|
||
if (!init_) return;
|
||
if (isRunning()) {
|
||
#ifdef MICRO_PIP
|
||
stop(true);
|
||
#else
|
||
stop();
|
||
PIThread::terminate();
|
||
#endif
|
||
}
|
||
}
|
||
|
||
|
||
void PIIODevice::begin() {
|
||
//cout << " begin\n";
|
||
buffer_tr.resize(threaded_read_buffer_size);
|
||
if (threaded_read_buffer_size == 0)
|
||
piCoutObj << "Warning: threadedReadBufferSize() == 0, read may be useless!";
|
||
thread_started_ = false;
|
||
if (!opened_) {
|
||
if (open()) {
|
||
thread_started_ = true;
|
||
//cout << " open && ok\n";
|
||
return;
|
||
}
|
||
} else {
|
||
thread_started_ = true;
|
||
//cout << " ok\n";
|
||
return;
|
||
}
|
||
if (!timer.isRunning() && isReopenEnabled()) timer.start(reopenTimeout());
|
||
}
|
||
|
||
|
||
void PIIODevice::run() {
|
||
if (!isReadable()) {
|
||
//cout << "not readable\n";
|
||
PIThread::stop();
|
||
return;
|
||
}
|
||
if (!thread_started_) {
|
||
piMSleep(10);
|
||
//cout << "not started\n";
|
||
return;
|
||
}
|
||
readed_ = read(buffer_tr.data(), buffer_tr.size_s());
|
||
if (readed_ <= 0) {
|
||
piMSleep(10);
|
||
//cout << readed_ << ", " << errno << ", " << errorString() << endl;
|
||
return;
|
||
}
|
||
//piCoutObj << "readed" << readed_;// << ", " << errno << ", " << errorString();
|
||
threadedRead(buffer_tr.data(), readed_);
|
||
if (raise_threaded_read_) threadedReadEvent(buffer_tr.data(), readed_);
|
||
}
|
||
|
||
|
||
PIByteArray PIIODevice::readForTime(double timeout_ms) {
|
||
PIByteArray str;
|
||
if (timeout_ms <= 0.) return str;
|
||
int ret;
|
||
uchar * td = new uchar[threaded_read_buffer_size];
|
||
tm.reset();
|
||
while (tm.elapsed_m() < timeout_ms) {
|
||
ret = read(td, threaded_read_buffer_size);
|
||
if (ret <= 0) piMinSleep();
|
||
else str.append(td, ret);
|
||
}
|
||
delete[] td;
|
||
return str;
|
||
}
|
||
|
||
|
||
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::open() {
|
||
if (!init_) init();
|
||
buffer_tr.resize(threaded_read_buffer_size);
|
||
opened_ = openDevice();
|
||
if (opened_) opened();
|
||
return opened_;
|
||
}
|
||
|
||
|
||
bool PIIODevice::open(const PIString & _path) {
|
||
setPath(_path);
|
||
return open();
|
||
}
|
||
|
||
|
||
bool PIIODevice::open(DeviceMode _mode) {
|
||
mode_ = _mode;
|
||
return open();
|
||
}
|
||
|
||
|
||
bool PIIODevice::open(const PIString & _path, DeviceMode _mode) {
|
||
setPath(_path);
|
||
mode_ = _mode;
|
||
return open();
|
||
}
|
||
|
||
|
||
bool PIIODevice::close() {
|
||
opened_ = !closeDevice();
|
||
if (!opened_) closed();
|
||
return !opened_;
|
||
}
|
||
|
||
int PIIODevice::write(PIByteArray data) {
|
||
return writeDevice(data.data(), data.size_s());
|
||
}
|
||
|
||
|
||
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", isReopenEnabled(), &ex).toBool());
|
||
if (!ex) setReopenEnabled(em.getValue("reopenEnabled", isReopenEnabled()).toBool());
|
||
setReopenTimeout(ep->getValue("reopenTimeout", reopenTimeout(), &ex).toInt());
|
||
if (!ex) setReopenTimeout(em.getValue("reopenTimeout", reopenTimeout()).toInt());
|
||
setThreadedReadBufferSize(ep->getValue("threadedReadBufferSize", int(threaded_read_buffer_size), &ex).toInt());
|
||
if (!ex) setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", int(threaded_read_buffer_size)).toInt());
|
||
} else {
|
||
setReopenEnabled(em.getValue("reopenEnabled", isReopenEnabled()).toBool());
|
||
setReopenTimeout(em.getValue("reopenTimeout", reopenTimeout()).toInt());
|
||
setThreadedReadBufferSize(em.getValue("threadedReadBufferSize", int(threaded_read_buffer_size)).toInt());
|
||
}
|
||
return configureDevice(&em, ep);
|
||
}
|
||
|
||
|
||
PIString PIIODevice::constructFullPath() const {
|
||
return fullPathPrefix().toString() + PIStringAscii("://") + constructFullPathDevice() + fullPathOptions();
|
||
}
|
||
|
||
|
||
void PIIODevice::configureFromFullPath(const PIString & full_path) {
|
||
PIString fp;
|
||
DeviceMode dm = ReadWrite;
|
||
DeviceOptions op = 0;
|
||
splitFullPath(full_path, &fp, &dm, &op);
|
||
setMode(dm);
|
||
setOptions(op);
|
||
configureFromFullPathDevice(fp);
|
||
}
|
||
|
||
|
||
PIVariantTypes::IODevice PIIODevice::constructVariant() const {
|
||
PIVariantTypes::IODevice ret;
|
||
ret.prefix = fullPathPrefix();
|
||
ret.mode = mode();
|
||
ret.options = options();
|
||
ret.set(constructVariantDevice());
|
||
return ret;
|
||
}
|
||
|
||
|
||
void PIIODevice::configureFromVariant(const PIVariantTypes::IODevice & d) {
|
||
setMode((DeviceMode)d.mode);
|
||
setOptions((DeviceOptions)d.options);
|
||
configureFromVariantDevice(d.get());
|
||
}
|
||
|
||
|
||
void PIIODevice::splitFullPath(PIString fpwm, PIString * full_path, DeviceMode * mode, DeviceOptions * opts) {
|
||
int dm = 0;
|
||
DeviceOptions op = 0;
|
||
if (fpwm.find('(') > 0 && fpwm.find(')') > 0) {
|
||
PIString dms(fpwm.right(fpwm.length() - fpwm.findLast('(')).takeRange('(', ')').trim().toLowerCase().removeAll(' '));
|
||
PIStringList opts(dms.split(","));
|
||
piForeachC (PIString & o, opts) {
|
||
//piCout << dms;
|
||
if (o == PIStringAscii("r") || o == PIStringAscii("ro") || o == PIStringAscii("read") || o == PIStringAscii("readonly"))
|
||
dm |= ReadOnly;
|
||
if (o == PIStringAscii("w") || o == PIStringAscii("wo") || o == PIStringAscii("write") || o == PIStringAscii("writeonly"))
|
||
dm |= WriteOnly;
|
||
if (o == PIStringAscii("br") || o == PIStringAscii("blockr") || o == PIStringAscii("blockread") || o == PIStringAscii("blockingread"))
|
||
op |= BlockingRead;
|
||
if (o == PIStringAscii("bw") || o == PIStringAscii("blockw") || o == PIStringAscii("blockwrite") || o == PIStringAscii("blockingwrite"))
|
||
op |= BlockingWrite;
|
||
if (o == PIStringAscii("brw") || o == PIStringAscii("bwr") || o == PIStringAscii("blockrw") || o == PIStringAscii("blockwr") || o == PIStringAscii("blockreadrite") || o == PIStringAscii("blockingreadwrite"))
|
||
op |= BlockingRead | BlockingWrite;
|
||
}
|
||
fpwm.cutRight(fpwm.length() - fpwm.findLast('(')).trim();
|
||
}
|
||
if (dm == 0) dm = ReadWrite;
|
||
if (full_path) *full_path = fpwm;
|
||
if (mode) *mode = (DeviceMode)dm;
|
||
if (opts) *opts = op;
|
||
}
|
||
|
||
|
||
PIStringList PIIODevice::availablePrefixes() {
|
||
PIStringList ret;
|
||
for (const auto & i: fabrics())
|
||
ret << i.second.prefix.toString();
|
||
return ret;
|
||
}
|
||
|
||
|
||
PIStringList PIIODevice::availableClasses() {
|
||
PIStringList ret;
|
||
for (const auto & i: fabrics())
|
||
ret << i.second.classname.toString();
|
||
return ret;
|
||
}
|
||
|
||
|
||
void PIIODevice::registerDevice(PIConstChars prefix, PIConstChars classname, PIIODevice * (*fabric)()) {
|
||
if (prefix.isEmpty()) return;
|
||
//printf("registerDevice %s %d %d\n", prefix, p.isEmpty(), fabrics().size());
|
||
if (!fabrics().contains(prefix)) {
|
||
FabricInfo fi;
|
||
fi.prefix = prefix;
|
||
fi.classname = classname;
|
||
fi.fabricator = fabric;
|
||
fabrics()[prefix] = fi;
|
||
}
|
||
}
|
||
|
||
|
||
PIString PIIODevice::fullPathOptions() const {
|
||
if (mode_ == ReadWrite && options_ == 0) return PIString();
|
||
PIString ret(" (");
|
||
bool f = true;
|
||
if (mode_ == ReadOnly) {if (!f) ret += ","; f = false; ret += "ro";}
|
||
if (mode_ == WriteOnly) {if (!f) ret += ","; f = false; ret += "wo";}
|
||
if (options_[BlockingRead]) {if (!f) ret += ","; f = false; ret += "br";}
|
||
if (options_[BlockingWrite]) {if (!f) ret += ","; f = false; ret += "bw";}
|
||
return ret + ")";
|
||
}
|
||
|
||
|
||
//! \~\details
|
||
//! \~english
|
||
//! To function \a configureFromFullPath() "full_path" passed without \a fullPathPrefix() and "://".
|
||
//! See \ref PIIODevice_sec7
|
||
//! \~russian
|
||
//! В метод \a configureFromFullPath() "full_path" передается без \a fullPathPrefix() и "://".
|
||
//! См. \ref PIIODevice_sec7
|
||
PIIODevice * PIIODevice::createFromFullPath(const PIString & full_path) {
|
||
PIString prefix = full_path.left(full_path.find(":"));
|
||
PIIODevice * nd = newDeviceByPrefix(prefix.dataAscii());
|
||
if (!nd) return nullptr;
|
||
nd->configureFromFullPath(full_path.mid(prefix.length() + 3));
|
||
cacheFullPath(full_path, nd);
|
||
return nd;
|
||
}
|
||
|
||
|
||
PIIODevice * PIIODevice::createFromVariant(const PIVariantTypes::IODevice & d) {
|
||
PIIODevice * nd = newDeviceByPrefix(d.prefix.dataAscii());
|
||
if (!nd) return nullptr;
|
||
nd->configureFromVariant(d);
|
||
return nd;
|
||
}
|
||
|
||
|
||
PIString PIIODevice::normalizeFullPath(const PIString & full_path) {
|
||
nfp_mutex.lock();
|
||
PIString ret = nfp_cache.value(full_path);
|
||
if (!ret.isEmpty()) {
|
||
nfp_mutex.unlock();
|
||
return ret;
|
||
}
|
||
nfp_mutex.unlock();
|
||
PIIODevice * d = createFromFullPath(full_path);
|
||
//piCout << "normalizeFullPath" << d;
|
||
if (d == 0) return PIString();
|
||
ret = d->constructFullPath();
|
||
delete d;
|
||
return ret;
|
||
}
|
||
|
||
|
||
void PIIODevice::cacheFullPath(const PIString & full_path, const PIIODevice * d) {
|
||
PIMutexLocker nfp_ml(nfp_mutex);
|
||
nfp_cache[full_path] = d->constructFullPath();
|
||
}
|
||
|
||
|
||
PIMap<PIConstChars, PIIODevice::FabricInfo> & PIIODevice::fabrics() {
|
||
static PIMap<PIConstChars, FabricInfo> ret;
|
||
return ret;
|
||
}
|
||
|
||
|
||
bool PIIODevice::threadedRead(const uchar *readed, int size) {
|
||
// piCout << "iodevice threaded read";
|
||
if (func_read) return func_read(readed, size, ret_data_);
|
||
return true;
|
||
}
|
||
|
||
|
||
PIPropertyStorage PIIODevice::constructVariantDevice() const {
|
||
PIPropertyStorage ret;
|
||
ret.addProperty("path", path());
|
||
return ret;
|
||
}
|
||
|
||
|
||
void PIIODevice::configureFromVariantDevice(const PIPropertyStorage & d) {
|
||
setPath(d.propertyValueByName("path").toString());
|
||
}
|
||
|