Files
pip/libs/main/io_devices/piiodevice.h
peri4 db954ffdaa version 5.5.0
add PIIODevice::threadedReadTimeout
2025-09-29 18:48:20 +03:00

606 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file piiodevice.h
* \ingroup IO
* \~\brief
* \~english Abstract input/output device
* \~russian Базовый класс утройств ввода/вывода
*/
/*
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/>.
*/
#ifndef PIIODEVICE_H
#define PIIODEVICE_H
#include "piinit.h"
#include "piqueue.h"
#include "pithread.h"
/// TODO: написать документацию, тут ничего не понятно
// function executed from threaded read, pass readedData, sizeOfData, ThreadedReadData
typedef std::function<bool(const uchar *, int, void *)> ReadRetFunc;
#ifdef DOXYGEN
//! \relatesalso PIIODevice
//! \brief
//! \~english Enable device instances creation with \a PIIODevice::createFromFullPath() function.
//! \~russian Включить создание экземпляров устройства с помощью метода \a PIIODevice::createFromFullPath().
//! \~\details
//! \~english This macro may be placed in cpp or header file, but preferred place is header
//! \~russian Этот макрос может быть расположен в cpp или заголовочном файле, но предпочтительнее распологать в заголовочном
# define REGISTER_DEVICE(class)
//! \relatesalso PIIODevice
//! \brief
//! \~english Use this macro instead of PIOBJECT when describe your own PIIODevice.
//! \~russian Используйте этот макрос вместо PIOBJECT при объявлении своего PIIODevice.
//! \~\param "prefix"
//! \~english Unique device prefix in quotes, may be ""
//! \~russian Уникальный префикс устройства в кавычках, может быть ""
# define PIIODEVICE(class, "prefix")
#else
# define REGISTER_DEVICE(name) \
STATIC_INITIALIZER_BEGIN \
PIIODevice::registerDevice(name::fullPathPrefixS(), #name, []() -> PIIODevice * { return new name(); }); \
STATIC_INITIALIZER_END
# define PIIODEVICE(name, prefix) \
PIOBJECT_SUBCLASS(name, PIIODevice) \
PIIODevice * copy() const override { \
return new name(); \
} \
\
public: \
PIConstChars fullPathPrefix() const override { \
return prefix; \
} \
static PIConstChars fullPathPrefixS() { \
return prefix; \
} \
\
private:
#endif
//! \ingroup IO
//! \~\brief
//! \~english Base class for input/output devices.
//! \~russian Базовый класс утройств ввода/вывода.
class PIP_EXPORT PIIODevice: public PIObject {
PIOBJECT_SUBCLASS(PIIODevice, PIObject);
friend void __DevicePool_threadReadDP(void * ddp);
public:
NO_COPY_CLASS(PIIODevice);
//! \~english Constructs a empty %PIIODevice
//! \~russian Создает пустой %PIIODevice
explicit PIIODevice();
//! \~english Open modes for PIIODevice
//! \~russian Режимы открытия для PIIODevice
enum DeviceMode {
ReadOnly /*! \~english Device can only read \~russian Устройство может только читать */ = 0x01,
WriteOnly /*! \~english Device can only write \~russian Устройство может только писать */ = 0x02,
ReadWrite /*! \~english Device can both read and write \~russian Устройство может читать и писать */ = 0x03
};
//! \~english Options for PIIODevice, works with some devices
//! \~russian Опции для PIIODevice, работает для некоторых устройств
enum DeviceOption {
BlockingRead /*! \~english \a read() block until data is received, default off \~russian \a read() блокируется, пока данные не
поступят, по умолчанию выключено */
= 0x01,
BlockingWrite /*! \~english \a write() block until data is sent, default off \~russian \a write() блокируется, пока данные не
запишутся, по умолчанию выключено */
= 0x02
};
//! \~english Characteristics of PIIODevice channel
//! \~russian Характеристики канала PIIODevice
enum DeviceInfoFlag {
Sequential /*! \~english Continuous bytestream without packets \~russian Непрерывный поток байт, без пакетирования */ = 0x01,
Reliable /*! \~english Channel without data errors or corruptions \~russian Канал без ошибок или повреждений данных */ = 0x02
};
struct FabricInfo {
PIConstChars prefix;
PIConstChars classname;
PIIODevice * (*fabricator)() = nullptr;
};
typedef PIFlags<DeviceOption> DeviceOptions;
typedef PIFlags<DeviceInfoFlag> DeviceInfoFlags;
//! \~english Constructs %PIIODevice with path "path" and open mode "mode"
//! \~russian Создает %PIIODevice с путём "path" и режимом открытия "mode"
explicit PIIODevice(const PIString & path, DeviceMode mode = ReadWrite);
virtual ~PIIODevice();
//! \~english Returns current open mode of device
//! \~russian Возвращает текущий режим открытия устройства
DeviceMode mode() const { return mode_; }
//! \~english Set open mode of device. Don`t reopen device
//! \~russian Устанавливает режим открытия устройства. Не переоткрывает устройство
void setMode(DeviceMode m) { mode_ = m; }
//! \~english Returns current device options
//! \~russian Возвращает текущие опции устройства
DeviceOptions options() const { return options_; }
//! \~english Returns current device option "o" state
//! \~russian Возвращает текущее состояние опции "o"
bool isOptionSet(DeviceOption o) const { return options_[o]; }
//! \~english Set device options
//! \~russian Устанавливает опции устройства
void setOptions(DeviceOptions o);
//! \~english Set device option "o" to "yes" and returns previous state
//! \~russian Устанавливает опцию "o" устройства в "yes" и возвращает предыдущее состояние опции
bool setOption(DeviceOption o, bool yes = true);
//! \~english Returns device characteristic flags
//! \~russian Возвращает характеристики канала
DeviceInfoFlags infoFlags() const { return deviceInfoFlags(); }
//! \~english Returns current path of device
//! \~russian Возвращает текущий путь устройства
PIString path() const { return property("path").toString(); }
//! \~english Set path of device. Don`t reopen device
//! \~russian Устанавливает путь устройства. Не переоткрывает устройство
void setPath(const PIString & path) { setProperty("path", path); }
//! \~english Returns if mode is ReadOnly or ReadWrite
//! \~russian Возвращает равен ли режим открытия ReadOnly или ReadWrite
bool isReadable() const { return (mode_ & ReadOnly); }
//! \~english Returns if mode is WriteOnly or ReadWrite
//! \~russian Возвращает равен ли режим открытия WriteOnly или ReadWrite
bool isWriteable() const { return (mode_ & WriteOnly); }
//! \~english Returns if device is successfully opened
//! \~russian Возвращает успешно ли открыто устройство
bool isOpened() const { return opened_; }
//! \~english Returns if device is closed
//! \~russian Возвращает закрыто ли устройство
bool isClosed() const { return !opened_; }
//! \~english Returns if device can read \b now
//! \~russian Возвращает может ли устройство читать \b сейчас
virtual bool canRead() const { return opened_ && (mode_ & ReadOnly); }
//! \~english Returns if device can write \b now
//! \~russian Возвращает может ли устройство писать \b сейчас
virtual bool canWrite() const { return opened_ && (mode_ & WriteOnly); }
//! \~english Set calling of \a open() enabled while threaded read on closed device
//! \~russian Устанавливает возможность вызова \a open() при потоковом чтении на закрытом устройстве
void setReopenEnabled(bool yes = true);
//! \~english Set timeout between \a open() tryings if reopen is enabled
//! \~russian Устанавливает задержку между вызовами \a open() если переоткрытие активно
void setReopenTimeout(PISystemTime timeout);
//! \~english Returns reopen enable
//! \~russian Возвращает активно ли переоткрытие
bool isReopenEnabled() const { return property("reopenEnabled").toBool(); }
//! \~english Returns reopen timeout
//! \~russian Возвращает задержку переоткрытия
PISystemTime reopenTimeout() { return property("reopenTimeout").toSystemTime(); }
//! \~english Set threaded read callback
//! \~russian Устанавливает callback потокового чтения
void setThreadedReadSlot(ReadRetFunc func);
//! \~english Set custom data that will be passed to threaded read callback
//! \~russian Устанавливает произвольный указатель, который будет передан в callback потокового чтения
void setThreadedReadData(void * d) { ret_data_ = d; }
//! \~english Set size of threaded read buffer
//! \~russian Устанавливает размер буфера потокового чтения
void setThreadedReadBufferSize(int new_size);
//! \~english Returns size of threaded read buffer
//! \~russian Возвращает размер буфера потокового чтения
int threadedReadBufferSize() const { return threaded_read_buffer_size; }
//! \~english Returns content of threaded read buffer
//! \~russian Возвращает содержимое буфера потокового чтения
const uchar * threadedReadBuffer() const { return buffer_tr.data(); }
//! \~english Returns custom data that will be passed to threaded read callback
//! \~russian Возвращает произвольный указатель, который будет передан в callback потокового чтения
void * threadedReadData() const { return ret_data_; }
//! \~english Returns if threaded read is started
//! \~russian Возвращает запущен ли поток чтения
bool isThreadedRead() const;
//! \~english Returns if threaded read is stopping
//! \~russian Возвращает останавливается ли поток чтения
bool isThreadedReadStopping() const { return read_thread.isStopping(); }
//! \~english Start threaded read
//! \~russian Запускает потоковое чтение
void startThreadedRead();
//! \~english Start threaded read and assign threaded read callback to "func"
//! \~russian Запускает потоковое чтение и устанавливает callback потокового чтения в "func"
void startThreadedRead(ReadRetFunc func);
//! \~english Stop threaded read.
//! \~russian Останавливает потоковое чтение.
void stopThreadedRead();
//! \~english Terminate threaded read.
//! \~russian Прерывает потоковое чтение.
//! \~\warning
//! \~english Try not to use! This method may cause memory corruption!
//! \~russian Старайтесь не использовать! Этот метод может привести к повреждению памяти!
void terminateThreadedRead();
//! \~english Wait for threaded read finish no longer than "timeout".
//! \~russian Ожидает завершения потокового чтения в течении не более "timeout".
bool waitThreadedReadFinished(PISystemTime timeout = {});
uint threadedReadTimeout() const { return threaded_read_timeout_ms; }
void setThreadedReadTimeout(uint ms) { threaded_read_timeout_ms = ms; }
//! \~english Returns if threaded write is started
//! \~russian Возвращает запущен ли поток записи
bool isThreadedWrite() const;
//! \~english Start threaded write
//! \~russian Запускает потоковую запись
void startThreadedWrite();
//! \~english Stop threaded write.
//! \~russian Останавливает потоковую запись.
void stopThreadedWrite();
//! \~english Terminate threaded write.
//! \~russian Прерывает потоковую запись.
//! \~\warning
//! \~english Try not to use! This method may cause memory corruption!
//! \~russian Старайтесь не использовать! Этот метод может привести к повреждению памяти!
void terminateThreadedWrite();
//! \~english Wait for threaded write finish no longer than "timeout".
//! \~russian Ожидает завершения потоковой записи в течении не более "timeout".
bool waitThreadedWriteFinished(PISystemTime timeout = {});
//! \~english Clear threaded write task queue
//! \~russian Очищает очередь потоковой записи
void clearThreadedWriteQueue();
//! \~english Start both threaded read and threaded write
//! \~russian Запускает потоковое чтение и запись
void start();
//! \~english Stop both threaded read and threaded write.
//! \~russian Останавливает потоковое чтение и запись.
void stop();
//! \~english Stop both threaded read and threaded write and wait for finish.
//! \~russian Останавливает потоковое чтение и запись и ожидает завершения.
void stopAndWait(PISystemTime timeout = {});
//! \~english Interrupt blocking operation.
//! \~russian Прерывает блокирующую операцию.
virtual void interrupt() {}
//! \~english Read from device maximum "max_size" bytes to "read_to"
//! \~russian Читает из устройства не более "max_size" байт в "read_to"
ssize_t read(void * read_to, ssize_t max_size);
//! \~english Read from device to memory block "mb"
//! \~russian Читает из устройства в блок памяти "mb"
ssize_t read(PIMemoryBlock mb);
//! \~english Read from device maximum "max_size" bytes and returns them as PIByteArray
//! \~russian Читает из устройства не более "max_size" байт и возвращает данные как PIByteArray
PIByteArray read(ssize_t max_size);
//! \~english Returns the number of bytes that are available for reading.
//! \~russian Возвращает количество байт доступных для чтения
//! \~\details
//! \~english This function is commonly used with sequential devices
//! to determine the number of bytes to allocate in a buffer before reading.
//! If function returns -1 it mean that number of bytes undefined.
//! \~russian Эта функция как правило используется чтобы знать какой
//! размер буфера нужен в памяти для чтения.
//! Если функция возвращает -1 это значит что количество байт для чтения не известно.
virtual ssize_t bytesAvailable() const { return -1; }
//! \~english Write maximum "max_size" bytes of "data" to device
//! \~russian Пишет в устройство не более "max_size" байт из "data"
ssize_t write(const void * data, ssize_t max_size);
//! \~english Read from device for "timeout" and return readed data as PIByteArray.
//! \~russian Читает из устройства в течении "timeout" и возвращает данные как PIByteArray.
PIByteArray readForTime(PISystemTime timeout);
//! \~english Add task to threaded write queue and return task ID
//! \~russian Добавляет данные в очередь на потоковую запись и возвращает ID задания
ullong writeThreaded(const void * data, ssize_t max_size) { return writeThreaded(PIByteArray(data, uint(max_size))); }
//! \~english Add task to threaded write queue and return task ID
//! \~russian Добавляет данные в очередь на потоковую запись и возвращает ID задания
ullong writeThreaded(const PIByteArray & data);
//! \~english Configure device from section "section" of file "config_file", if "parent_section" parent section also will be read
//! \~russian
bool configure(const PIString & config_file, const PIString & section, bool parent_section = false);
//! \~english Returns full unambiguous string prefix. \ref PIIODevice_sec7
//! \~russian Возвращает префикс устройства. \ref PIIODevice_sec7
virtual PIConstChars fullPathPrefix() const { return ""; }
static PIConstChars fullPathPrefixS() { return ""; }
//! \~english Returns full unambiguous string, describes this device, \a fullPathPrefix() + "://" + ...
//! \~russian Возвращает строку полного описания для этого устройства, \a fullPathPrefix() + "://" + ...
PIString constructFullPath() const;
//! \~english Configure device with parameters of full unambiguous string
//! \~russian Настраивает устройство из параметров строки полного описания
void configureFromFullPath(const PIString & full_path);
//! \~english Returns PIVariantTypes::IODevice, describes this device
//! \~russian Возвращает PIVariantTypes::IODevice, описывающий это устройство
PIVariantTypes::IODevice constructVariant() const;
//! \~english Configure device from PIVariantTypes::IODevice
//! \~russian Настраивает устройство из PIVariantTypes::IODevice
void configureFromVariant(const PIVariantTypes::IODevice & d);
//! \~english Try to create new device by prefix, configure it with \a configureFromFullPath() and returns it.
//! \~russian Пытается создать новое устройство по префиксу, настраивает с помощью \a configureFromFullPath() и возвращает его
static PIIODevice * createFromFullPath(const PIString & full_path);
//! \~english Try to create new device by prefix, configure it with \a configureFromVariant() and returns it.
//! \~russian Пытается создать новое устройство по префиксу, настраивает с помощью \a configureFromVariant() и возвращает его
static PIIODevice * createFromVariant(const PIVariantTypes::IODevice & d);
static PIString normalizeFullPath(const PIString & full_path);
static void splitFullPath(PIString fpwm, PIString * full_path, DeviceMode * mode = 0, DeviceOptions * opts = 0);
//! \~english Returns fullPath prefixes of all registered devices
//! \~russian Возвращает префиксы всех зарегистрированных устройств
static PIStringList availablePrefixes();
//! \~english Returns class names of all registered devices
//! \~russian Возвращает имена классов всех зарегистрированных устройств
static PIStringList availableClasses();
static void registerDevice(PIConstChars prefix, PIConstChars classname, PIIODevice * (*fabric)());
EVENT_HANDLER(bool, open);
EVENT_HANDLER1(bool, open, const PIString &, _path);
bool open(DeviceMode _mode);
EVENT_HANDLER2(bool, open, const PIString &, _path, DeviceMode, _mode);
EVENT_HANDLER(bool, close);
EVENT_HANDLER1(ssize_t, write, PIByteArray, data);
//! \~english Write memory block "mb" to device
//! \~russian Пишет в устройство блок памяти "mb"
ssize_t write(const PIMemoryBlock & mb) { return write(mb.data(), mb.size()); }
EVENT_VHANDLER(void, flush) { ; }
EVENT(opened);
EVENT(closed);
EVENT2(threadedReadEvent, const uchar *, readed, ssize_t, size);
EVENT2(threadedWriteEvent, ullong, id, ssize_t, written_size);
//! \handlers
//! \{
//! \fn bool open()
//! \~english Open device
//! \~russian Открывает устройство
//! \fn bool open(const PIString & path)
//! \~english Open device with path "path"
//! \~russian Открывает устройство с путём "path"
//! \fn bool open(const DeviceMode & mode)
//! \~english Open device with mode "mode"
//! \~russian Открывает устройство с режимом открытия "mode"
//! \fn bool open(const PIString & path, const DeviceMode & mode)
//! \~english Open device with path "path" and mode "mode"
//! \~russian Открывает устройство с путём "path" и режимом открытия "mode"
//! \fn bool close()
//! \~english Close device
//! \~russian Закрывает устройство
//! \fn ssize_t write(PIByteArray data)
//! \~english Write "data" to device
//! \~russian Пишет "data" в устройство
//! \}
//! \vhandlers
//! \{
//! \fn void flush()
//! \~english Immediate write all buffers
//! \~russian Немедленно записать все буферизированные данные
//! \}
//! \events
//! \{
//! \fn void opened()
//! \~english Raise if succesfull open
//! \~russian Вызывается при успешном открытии
//! \fn void closed()
//! \~english Raise if succesfull close
//! \~russian Вызывается при успешном закрытии
//! \fn void threadedReadEvent(const uchar * readed, ssize_t size)
//! \~english Raise if read thread succesfull read some data
//! \~russian Вызывается при успешном потоковом чтении данных
//! \fn void threadedWriteEvent(ullong id, ssize_t written_size)
//! \~english Raise if write thread successfull write some data of task with ID "id"
//! \~russian Вызывается при успешной потоковой записи данных с ID задания "id"
//! \}
//! \ioparams
//! \{
#ifdef DOXYGEN
//! \~english setReopenEnabled, default "true"
//! \~russian setReopenEnabled, по умолчанию "true"
bool reopenEnabled;
//! \~english setReopenTimeout, default 1_s
//! \~russian setReopenTimeout, по умолчанию 1_s
int reopenTimeout;
//! \~english setThreadedReadBufferSize in bytes, default 4096
//! \~russian setThreadedReadBufferSize в байтах, по умолчанию 4096
int threadedReadBufferSize;
#endif
//! \}
protected:
//! \~english Reimplement to configure device from entries "e_main" and "e_parent", cast arguments to \a PIConfig::Entry*
//! \~russian
virtual bool configureDevice(const void * e_main, const void * e_parent = 0) { return true; }
//! \~english Reimplement to open device, return value will be set to "opened_" variable.
//! Don't call this function in subclass, use \a open()!
//! \~russian Переопределите для открытия устройства, возвращаемое значение будет установлено в
//! переменную "opened_". Не используйте напрямую, только через \a open()!
virtual bool openDevice() = 0; // use path_, type_, opened_, init_ variables
//! \~english Reimplement to close device, inverse return value will be set to "opened_" variable
//! \~russian Переопределите для закрытия устройства, обратное возвращаемое значение будет установлено в переменную "opened_"
virtual bool closeDevice() { return true; } // use path_, type_, opened_, init_ variables
//! \~english Reimplement this function to read from your device
//! \~russian Переопределите для чтения данных из устройства
virtual ssize_t readDevice(void * read_to, ssize_t max_size) {
piCoutObj << "\"readDevice\" is not implemented!";
return -2;
}
//! \~english Reimplement this function to write to your device
//! \~russian Переопределите для записи данных в устройство
virtual ssize_t writeDevice(const void * data, ssize_t max_size) {
piCoutObj << "\"writeDevice\" is not implemented!";
return -2;
}
//! \~english Function executed when thread read some data, default implementation execute external callback "ret_func_"
//! \~russian Метод вызывается после каждого успешного потокового чтения, по умолчанию вызывает callback "ret_func_"
virtual bool threadedRead(const uchar * readed, ssize_t size);
//! \~english Reimplement to construct full unambiguous string, describes this device.
//! Default implementation returns \a path()
//! \~russian Переопределите для создания строки полного описания устройства.
//! По умолчанию возвращает \a path()
virtual PIString constructFullPathDevice() const { return path(); }
//! \~english Reimplement to configure your device with parameters of full unambiguous string.
//! Default implementation call \a setPath()
//! \~russian Переопределите для настройки устройства из строки полного описания.
//! По умолчанию вызывает \a setPath()
virtual void configureFromFullPathDevice(const PIString & full_path) { setPath(full_path); }
//! \~english Reimplement to construct device properties.
//! Default implementation return PIPropertyStorage with \"path\" entry
//! \~russian Переопределите для создания свойств устройства.
//! По умолчанию возвращает PIPropertyStorage со свойством \"path\"
virtual PIPropertyStorage constructVariantDevice() const;
//! \~english Reimplement to configure your device from PIPropertyStorage. Options and mode already applied.
//! Default implementation apply \"path\" entry
//! \~russian Переопределите для настройки устройства из PIPropertyStorage. Опции и режим уже применены.
//! По умолчанию устанавливает свойство \"path\"
virtual void configureFromVariantDevice(const PIPropertyStorage & d);
//! \~english Reimplement to apply new device options
//! \~russian Переопределите для применения новых опций устройства
virtual void optionsChanged() { ; }
//! \~english Reimplement to return correct \a DeviceInfoFlags. Default implementation returns 0
//! \~russian Переопределите для возврата правильных \a DeviceInfoFlags. По умолчанию возвращает 0
virtual DeviceInfoFlags deviceInfoFlags() const { return 0; }
//! \~english Reimplement to apply new \a threadedReadBufferSize()
//! \~russian Переопределите для применения нового \a threadedReadBufferSize()
virtual void threadedReadBufferSizeChanged() { ; }
static PIIODevice * newDeviceByPrefix(const char * prefix);
DeviceMode mode_ = ReadOnly;
DeviceOptions options_;
ReadRetFunc func_read = nullptr;
std::atomic_bool opened_;
void * ret_data_ = nullptr;
private:
EVENT_HANDLER(void, read_func);
EVENT_HANDLER(void, write_func);
virtual PIIODevice * copy() const { return nullptr; }
PIString fullPathOptions() const;
void _init();
static void cacheFullPath(const PIString & full_path, const PIIODevice * d);
static PIMap<PIConstChars, FabricInfo> & fabrics();
PITimeMeasurer tm, reopen_tm;
PIThread read_thread, write_thread;
PIByteArray buffer_in, buffer_tr;
PIQueue<PIPair<PIByteArray, ullong>> write_queue;
PISystemTime reopen_timeout;
ullong tri = 0;
uint threaded_read_buffer_size, threaded_read_timeout_ms = 10;
bool reopen_enabled = true, destroying = false;
static PIMutex nfp_mutex;
static PIMap<PIString, PIString> nfp_cache;
};
#endif // PIIODEVICE_H