//! \addtogroup IO-Utils //! \{ //! \file piconnection.h //! \brief //! \~english Complex I/O point //! \~russian Составное устройство ввода/вывода //! \details //! \~english This class provides a complex I/O point that can manage multiple devices, filters, channels, and senders. //! \~russian Этот класс предоставляет составное устройство ввода/вывода, которое может управлять множеством устройств, фильтров, каналов и отправителей. //! \~\} /* PIP - Platform Independent Primitives Complex I/O point Ivan Pelipenko peri4ko@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 . */ #ifndef PICONNECTION_H #define PICONNECTION_H #include "pidiagnostics.h" #include "pipacketextractor.h" class PIConfig; //! \~english Complex I/O point //! \~russian Составное устройство ввода/вывода //! \details This class provides a complex I/O point that can manage multiple devices, filters, channels, and senders. //! \~russian Этот класс предоставляет составное устройство ввода/вывода, которое может управлять множеством устройств, фильтров, каналов и отправителей. class PIP_EXPORT PIConnection: public PIObject { PIOBJECT_SUBCLASS(PIConnection, PIObject); public: //! \~english Constructs connection with name "name", or with default name = "connection" //! \~russian Создает подключение с именем "name" или с именем по умолчанию "connection" PIConnection(const PIString & name = PIStringAscii("connection")); //! \~english Constructs connection and configure it from config file "config" from section "name" //! \~russian Создает подключение и настраивает его из файла конфигурации "config" из секции "name" PIConnection(const PIString & config, const PIString & name); //! \~english Constructs connection and configure it from config content "string" from section "name" //! \~russian Создает подключение и настраивает его из содержимого конфигурации "string" из секции "name" PIConnection(PIString * string, const PIString & name); //! \~english Destructor //! \~russian Деструктор ~PIConnection(); //! \~english Configure connection from config file "config" from section "name". Returns if configuration was successful //! \~russian Настраивает подключение из файла конфигурации "config" из секции "name". Возвращает успешность настройки //! \details \b Warning: all devices, filters and channels removed before configure! //! \~russian \b Внимание: все устройства, фильтры и каналы удаляются перед настройкой! bool configureFromConfig(const PIString & config, const PIString & name = PIStringAscii("connection")); //! \~english Configure connection from config content "string" from section "name". Returns if configuration was successful //! \~russian Настраивает подключение из содержимого конфигурации "string" из секции "name". Возвращает успешность настройки //! \details \b Warning: all devices, filters and channels removed before configure! //! \~russian \b Внимание: все устройства, фильтры и каналы удаляются перед настройкой! bool configureFromString(PIString * string, const PIString & name = PIStringAscii("connection")); //! \~english Returns config file section of current connection configuration //! \~russian Возвращает секцию файла конфигурации текущей конфигурации подключения PIString makeConfig() const; //! \~english Add device with full path "full_path", open mode "mode" to Device pool and connection //! \~russian Добавляет устройство с полным путем "full_path", режимом открытия "mode" в пул устройств и подключение //! \details Returns pointer to device or null if device can not be created. If "start" is true, //! \~english read thread is started immediately. Else, you can start read thread with functions \a startThreadedRead() //! \~russian поток чтения запускается немедленно. В противном случае можно запустить поток чтения с помощью функций \a startThreadedRead() //! \~english or \a startAllThreadedReads(). By default, read thread doesn`t start //! \~russian или \a startAllThreadedReads(). По умолчанию поток чтения не запускается PIIODevice * addDevice(const PIString & full_path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite, bool start = false); //! \~english Sets the name for device "dev" //! \~russian Устанавливает имя для устройства "dev" void setDeviceName(PIIODevice * dev, const PIString & name); //! \~english Returns list of device names //! \~russian Возвращает список имен устройств PIStringList deviceNames(const PIIODevice * dev) const; //! \~english Remove device with full path "full_path" from connection //! \~russian Удаляет устройство с полным путем "full_path" из подключения //! \details Returns if device was removed. If there is no connection bounded to this device, //! \~english it will be removed from Device pool //! \~russian оно будет удалено из пула устройств bool removeDevice(const PIString & full_path); //! \~english Remove all device from connection //! \~russian Удаляет все устройства из подключения //! \details If there is no connection bounded to there devices, they removed from Device pool //! \~russian Если к устройствам не привязано подключение, они удаляются из пула устройств void removeAllDevices(); //! \~english Returns device with full path "full_path" or null if there is no such device //! \~russian Возвращает устройство с полным путем "full_path" или null, если такого устройства нет PIIODevice * deviceByFullPath(const PIString & full_path) const; //! \~english Returns device with name "name" or null if there is no such device //! \~russian Возвращает устройство с именем "name" или null, если такого устройства нет PIIODevice * deviceByName(const PIString & name) const; //! \~english Returns all devices bounded to this connection //! \~russian Возвращает все устройства, привязанные к этому подключению PIVector boundedDevices() const; //! \~english Add filter with name "name" to device with full path "full_path_name" or filter "full_path_name" //! \~russian Добавляет фильтр с именем "name" к устройству с полным путем "full_path_name" или фильтру "full_path_name" //! \details If there is no filter with name "name", connection create new with split mode "mode" and bound //! \~english to it device "full_path_name" or filter "full_path_name". If filter with name "name" already exists, //! \~english device "full_path_name" or filter "full_path_name" add to this filter. //! \~russian к нему устройство "full_path_name" или фильтр "full_path_name". Если фильтр с именем "name" уже существует, //! \~english This function returns PIPacketExtractor * assosiated with this filter //! \~russian устройство "full_path_name" или фильтр "full_path_name" добавляется к этому фильтру. //! \~english This function returns PIPacketExtractor * assosiated with this filter //! \~russian Эта функция возвращает PIPacketExtractor *, связанный с этим фильтром //! \~\sa PIPacketExtractor //! \details \b Attention! "mode" is altual olny if new filter was created! //! \~russian \b Внимание! "mode" актуален только если был создан новый фильтр! PIPacketExtractor * addFilter(const PIString & name, const PIString & full_path_name, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None); //! \~english Add filter with name "name" to device "dev" //! \~russian Добавляет фильтр с именем "name" к устройству "dev" PIPacketExtractor * addFilter(const PIString & name, const PIIODevice * dev, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None) { return addFilter(name, devFPath(dev), mode); } //! \~english Add filter with "filter" to device "dev" //! \~russian Добавляет фильтр "filter" к устройству "dev" PIPacketExtractor * addFilter(PIPacketExtractor * filter, const PIString & full_path_name); //! \~english Add filter with "filter" to device "dev" //! \~russian Добавляет фильтр "filter" к устройству "dev" PIPacketExtractor * addFilter(PIPacketExtractor * filter, const PIIODevice * dev) { return addFilter(filter, devFPath(dev)); } //! \~english Remove from filter with name "name" device with full path "full_path_name" or filter "full_path_name" //! \~russian Удаляет из фильтра с именем "name" устройство с полным путем "full_path_name" или фильтр "full_path_name" //! \details If there is no devices bounded to this filter, it will be removed. Returns if device was removed //! \~russian Если к этому фильтру не привязано устройств, он будет удален. Возвращает успешность удаления устройства bool removeFilter(const PIString & name, const PIString & full_path_name); //! \~english Remove from filter with name "name" device or filter "dev" //! \~russian Удаляет из фильтра с именем "name" устройство или фильтр "dev" bool removeFilter(const PIString & name, const PIIODevice * dev); //! \~english Remove filter with name "name". Returns if filter was removed //! \~russian Удаляет фильтр с именем "name". Возвращает успешность удаления фильтра bool removeFilter(const PIString & name); //! \~english Remove all filters from connection //! \~russian Удаляет все фильтры из подключения void removeAllFilters(); //! \~english Returns all filters of connection //! \~russian Возвращает все фильтры подключения PIVector filters() const; //! \~english Returns all filter names of connection //! \~russian Возвращает все имена фильтров подключения PIStringList filterNames() const; //! \~english Returns PIPacketExtractor * assosiated with filter "name" or null if there is no such filter //! \~russian Возвращает PIPacketExtractor *, связанный с фильтром "name" или null, если такого фильтра нет PIPacketExtractor * filter(const PIString & name) const; //! \~english Returns all devices bounded to filter "name" //! \~russian Возвращает все устройства, привязанные к фильтру "name" PIVector filterBoundedDevices(const PIString & name) const; //! \~english Add to connection channel from "name_from" to "name_to" //! \~russian Добавляет в подключение канал от "name_from" к "name_to" //! \details "name_from" and "name_to" can be full pathes of devices or device names or filter names. //! \~english Returns \b false if there if no such device or filter, else create channel and returns \b true //! \~russian Возвращает \b false, если такого устройства или фильтра нет, иначе создает канал и возвращает \b true bool addChannel(const PIString & name_from, const PIString & name_to); //! \~english Add to connection channel from "name_from" to "dev_to" //! \~russian Добавляет в подключение канал от "name_from" к "dev_to" bool addChannel(const PIString & name_from, const PIIODevice * dev_to) { return addChannel(name_from, devFPath(dev_to)); } //! \~english Add to connection channel from "dev_from" to "name_to" //! \~russian Добавляет в подключение канал от "dev_from" к "name_to" bool addChannel(const PIIODevice * dev_from, const PIString & name_to) { return addChannel(devFPath(dev_from), name_to); } //! \~english Add to connection channel from "dev_from" to "dev_to" //! \~russian Добавляет в подключение канал от "dev_from" к "dev_to" bool addChannel(const PIIODevice * dev_from, const PIIODevice * dev_to) { return addChannel(devFPath(dev_from), devFPath(dev_to)); } //! \~english Remove from connection channel from "name_from" to "name_to" //! \~russian Удаляет из подключения канал от "name_from" к "name_to" //! \details "name_from" and "name_to" can be full pathes of devices or filter names. //! \~english Returns \b false if there if no such device or filter, else remove channel and returns \b true //! \~russian Возвращает \b false, если такого устройства или фильтра нет, иначе удаляет канал и возвращает \b true bool removeChannel(const PIString & name_from, const PIString & name_to); //! \~english Remove from connection channel from "name_from" to "dev_to" //! \~russian Удаляет из подключения канал от "name_from" к "dev_to" bool removeChannel(const PIString & name_from, const PIIODevice * dev_to) { return removeChannel(name_from, devFPath(dev_to)); } //! \~english Remove from connection channel from "dev_from" to "name_to" //! \~russian Удаляет из подключения канал от "dev_from" к "name_to" bool removeChannel(const PIIODevice * dev_from, const PIString & name_to) { return removeChannel(devFPath(dev_from), name_to); } //! \~english Remove from connection channel from "dev_from" to "dev_to" //! \~russian Удаляет из подключения канал от "dev_from" к "dev_to" bool removeChannel(const PIIODevice * dev_from, const PIIODevice * dev_to) { return removeChannel(devFPath(dev_from), devFPath(dev_to)); } //! \~english Remove from connection all channels from "name_from" //! \~russian Удаляет из подключения все каналы от "name_from" //! \details "name_from" can be full path of device or filter name. //! \~english Returns \b false if there if no such device or filter, else remove channels and returns \b true //! \~russian Возвращает \b false, если такого устройства или фильтра нет, иначе удаляет каналы и возвращает \b true bool removeChannel(const PIString & name_from); //! \~english Remove from connection all channels from "dev_from" //! \~russian Удаляет из подключения все каналы от "dev_from" bool removeChannel(const PIIODevice * dev_from) { return removeChannel(devFPath(dev_from)); } //! \~english Remove from connection all channels //! \~russian Удаляет из подключения все каналы void removeAllChannels(); //! \~english Returns all channels of this connection as full pathes or filter names pair array (from, to) //! \~russian Возвращает все каналы этого подключения в виде массива пар полных путей или имен фильтров (от, до) PIVector> channels() const; //! \~english Add to connection sender with name "name" device with full path "full_path" //! \~russian Добавляет в подключение отправителя с именем "name" устройство с полным путем "full_path" //! \details If there is no sender with name "name", connection create new, bound //! \~english to it device "full_path_name" and start sender timer with frequency "frequency". //! \~russian к нему устройство "full_path_name" и запускает таймер отправителя с частотой "frequency". //! \~english If sender with name "name" already exists, device "full_path_name" add to this sender //! \~russian Если отправитель с именем "name" уже существует, устройство "full_path_name" добавляется к этому отправителю //! \~english If "start" is true, sender is started immediately. Else, you can start sender with //! \~russian Если "start" равно true, отправитель запускается немедленно. В противном случае можно запустить отправителя с помощью //! \~english functions \a startSender() //! \~russian функций \a startSender() //! \~\sa startSender() //! \details \b Attention! "frequency" is actual olny if new sender was created! //! \~russian \b Внимание! "frequency" актуален только если был создан новый отправитель! void addSender(const PIString & name, const PIString & full_path_name, float frequency, bool start = false); //! \~english Add to connection sender with name "name" device "dev" //! \~russian Добавляет в подключение отправителя с именем "name" устройство "dev" void addSender(const PIString & name, const PIIODevice * dev, float frequency, bool start = false) { addSender(name, devFPath(dev), frequency, start); } //! \~english Remove from sender with name "name" device with full path "full_path_name" //! \~russian Удаляет из отправителя с именем "name" устройство с полным путем "full_path_name" //! \details If there is no devices bounded to this sender, it will be removed. Returns if sender was removed //! \~russian Если к этому отправителю не привязано устройств, он будет удален. Возвращает успешность удаления отправителя bool removeSender(const PIString & name, const PIString & full_path_name); //! \~english Remove from sender with name "name" device "dev" //! \~russian Удаляет из отправителя с именем "name" устройство "dev" bool removeSender(const PIString & name, const PIIODevice * dev) { return removeSender(name, devFPath(dev)); } //! \~english Remove sender with name "name", returns if sender was removed //! \~russian Удаляет отправителя с именем "name", возвращает успешность удаления отправителя bool removeSender(const PIString & name); //! \~english Set sender "name" fixed send data "data", returns if sender exists //! \~russian Устанавливает фиксированные данные отправки для отправителя "name", возвращает существование отправителя bool setSenderFixedData(const PIString & name, const PIByteArray & data); //! \~english Remove sender "name" fixed send data, returns if sender exists //! \~russian Удаляет фиксированные данные отправки для отправителя "name", возвращает существование отправителя bool clearSenderFixedData(const PIString & name); //! \~english Returns sender "name" fixed send data //! \~russian Возвращает фиксированные данные отправки для отправителя "name" PIByteArray senderFixedData(const PIString & name) const; //! \~english Returns sender "name" timer frequency, -1 if there is no such sender, or 0 if sender is not started yet //! \~russian Возвращает частоту таймера отправителя "name", -1 если такого отправителя нет, или 0 если отправитель еще не запущен float senderFrequency(const PIString & name) const; //! \~english Remove from connection all senders //! \~russian Удаляет из подключения всех отправителей void removeAllSenders(); //! \~english Start read thread of device with full path "full_path" //! \~russian Запускает поток чтения устройства с полным путем "full_path" void startThreadedRead(const PIString & full_path_name); //! \~english Start read thread of device "dev" //! \~russian Запускает поток чтения устройства "dev" void startThreadedRead(const PIIODevice * dev) { startThreadedRead(devFPath(dev)); } //! \~english Start read threads of all Device pool device //! \~russian Запускает потоки чтения всех устройств пула устройств void startAllThreadedReads(); //! \~english Start sender "name" timer //! \~russian Запускает таймер отправителя "name" void startSender(const PIString & name); //! \~english Start all senders timers //! \~russian Запускает все таймеры отправителей void startAllSenders(); //! \~english Start all read threads and senders //! \~russian Запускает все потоки чтения и отправителей void start() { startAllThreadedReads(); startAllSenders(); } //! \~english Stop read thread of device with full path "full_path" //! \~russian Останавливает поток чтения устройства с полным путем "full_path" void stopThreadedRead(const PIString & full_path_name); //! \~english Stop read thread of device "dev" //! \~russian Останавливает поток чтения устройства "dev" void stopThreadedRead(const PIIODevice * dev) { stopThreadedRead(devFPath(dev)); } //! \~english Stop read threads of all Device pool device //! \~russian Останавливает потоки чтения всех устройств пула устройств void stopAllThreadedReads(); //! \~english Stop sender "name" timer //! \~russian Останавливает таймер отправителя "name" void stopSender(const PIString & name); //! \~english Stop all senders timers //! \~russian Останавливает все таймеры отправителей void stopAllSenders(); //! \~english Stop all read threads and senders //! \~russian Останавливает все потоки чтения и отправителей void stop() { stopAllThreadedReads(); stopAllSenders(); } //! \~english Stop connection and remove all devices //! \~russian Останавливает подключение и удаляет все устройства void destroy() { stop(); removeAllDevices(); } //! \~english Returns if there are no devices in this connection //! \~russian Возвращает, если в этом подключении нет устройств bool isEmpty() const { return device_modes.isEmpty(); } //! \~english Returns PIDiagnostics * assosiated with device with full path "full_path_name", name "full_path_name" or filter "full_path_name" //! \~russian Возвращает PIDiagnostics *, связанный с устройством с полным путем "full_path_name", именем "full_path_name" или фильтром "full_path_name" PIDiagnostics * diagnostic(const PIString & full_path_name) const; //! \~english Returns PIDiagnostics * assosiated with device or filter "dev" //! \~russian Возвращает PIDiagnostics *, связанный с устройством или фильтром "dev" PIDiagnostics * diagnostic(const PIIODevice * dev) const { return diags_.value(const_cast(dev), 0); } //! \~english Write data "data" to device with full path "full_path" and returns result of \a write() function of device //! \~russian Записывает данные "data" в устройство с полным путем "full_path" и возвращает результат функции \a write() устройства int writeByFullPath(const PIString & full_path, const PIByteArray & data); //! \~english Write data "data" to device with name "name" and returns result of \a write() function of device //! \~russian Записывает данные "data" в устройство с именем "name" и возвращает результат функции \a write() устройства int writeByName(const PIString & name, const PIByteArray & data); //! \~english Write data "data" to device "dev" and returns result of \a write() function of device //! \~russian Записывает данные "data" в устройство "dev" и возвращает результат функции \a write() устройства int write(PIIODevice * dev, const PIByteArray & data); //! \~english Returns all connections in application //! \~russian Возвращает все подключения в приложении static PIVector allConnections(); //! \~english Returns all devices in Device pool //! \~russian Возвращает все устройства в пуле устройств static PIVector allDevices(); //! \~english Set Device pool fake mode to \"yes\" and returns previous mode //! \~russian Устанавливает фальшивый режим пула устройств в \"yes\" и возвращает предыдущий режим static bool setFakeMode(bool yes); //! \~english Returns if Device pool works in fake mode //! \~russian Возвращает, работает ли пул устройств в фальшивом режиме static bool isFakeMode(); //! \~english Device pool class //! \~russian Класс пула устройств class PIP_EXPORT DevicePool: public PIThread { PIOBJECT_SUBCLASS(DevicePool, PIThread); friend void __DevicePool_threadReadDP(void * ddp); friend class PIConnection; protected: struct DeviceData; public: DevicePool(); ~DevicePool(); void init(); PIIODevice * addDevice(PIConnection * parent, const PIString & fp, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite, bool start = true); bool removeDevice(PIConnection * parent, const PIString & fp); void unboundConnection(PIConnection * parent); PIIODevice * device(const PIString & fp) const; DeviceData * deviceData(PIIODevice * d) const; //! \~english Returns list of bounded connections //! \~russian Возвращает список привязанных подключений PIVector boundedConnections() const; //! \~english Returns list of bounded devices //! \~russian Возвращает список привязанных устройств PIVector boundedDevices() const; //! \~english Returns list of bounded devices for specific parent connection //! \~russian Возвращает список привязанных устройств для конкретного родительского подключения PIVector boundedDevices(const PIConnection * parent) const; protected: //! \~english Device data structure //! \~russian Структура данных устройства struct PIP_EXPORT DeviceData { //! \~english Constructor //! \~russian Конструктор DeviceData(): dev(0), rthread(0), started(false) {} //! \~english Destructor //! \~russian Деструктор ~DeviceData(); //! \~english Device pointer //! \~russian Указатель на устройство PIIODevice * dev; //! \~english Read thread pointer //! \~russian Указатель на поток чтения PIThread * rthread; //! \~english Started flag //! \~russian Флаг запуска bool started; //! \~english List of listeners //! \~russian Список слушателей PIVector listeners; }; //! \~english Thread execution function //! \~russian Функция выполнения потока void run() override; //! \~english Called when device is read //! \~russian Вызывается при чтении устройства void deviceReaded(DeviceData * dd, const PIByteArray & data); //! \~english Map of devices //! \~russian Карта устройств PIMap devices; //! \~english Fake mode flag //! \~russian Флаг фальшивого режима bool fake; }; //! \~english Data received event //! \~russian Событие получения данных EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data); //! \~english Packet received event //! \~russian Событие получения пакета EVENT2(packetReceivedEvent, const PIString &, from, const PIByteArray &, data); //! \~english Quality changed event //! \~russian Событие изменения качества EVENT3(qualityChanged, const PIIODevice *, dev, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality); //! \~english Events //! \~russian События //! \{ //! \fn void dataReceivedEvent(const PIString & from, const PIByteArray & data) //! \~english Raise on data received from device with full path "from" //! \~russian Возникает при получении данных от устройства с полным путем "from" //! \fn void packetReceivedEvent(const PIString & from, const PIByteArray & data) //! \~english Raise on packet received from filter with name "from" //! \~russian Возникает при получении пакета от фильтра с именем "from" //! \fn void qualityChanged(const PIIODevice * device, PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality) //! \~english Raise on diagnostic quality of device "device" changed from "old_quality" to "new_quality" //! \~russian Возникает при изменении диагностического качества устройства "device" с "old_quality" на "new_quality" //! \} protected: //! \~english Executes on data received from device with full path "from" //! \~russian Выполняется при получении данных от устройства с полным путем "from" virtual void dataReceived(const PIString & from, const PIByteArray & data) {} //! \~english Executes on packet received from filter with name "from" //! \~russian Выполняется при получении пакета от фильтра с именем "from" virtual void packetReceived(const PIString & from, const PIByteArray & data) {} //! \~english You should returns data for sender "sender_name" //! \~russian Вы должны возвращать данные для отправителя "sender_name" virtual PIByteArray senderData(const PIString & sender_name); private: bool configure(PIConfig & conf, const PIString & name_); void rawReceived(PIIODevice * dev, const PIString & from, const PIByteArray & data); void unboundExtractor(PIPacketExtractor * pe); EVENT_HANDLER2(void, packetExtractorReceived, const uchar *, data, int, size); EVENT_HANDLER2(void, diagQualityChanged, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality); PIString devPath(const PIIODevice * d) const; PIString devFPath(const PIIODevice * d) const; PIIODevice * devByString(const PIString & s) const; struct PIP_EXPORT Extractor { Extractor(): extractor(0) {} ~Extractor(); PIPacketExtractor * extractor; PIVector devices; }; class PIP_EXPORT Sender: public PITimer { PIOBJECT_SUBCLASS(Sender, PIObject); public: Sender(PIConnection * parent_ = 0); ~Sender() { stopAndWait(); } PIConnection * parent; PIVector devices; PIByteArray sdata; PISystemTime int_; void tick(int) override; }; PIMap extractors; PIMap senders; PIMap device_names; PIMap device_modes; PIMap> bounded_extractors; PIMap> channels_; PIMap diags_; static PIVector _connections; }; void __DevicePool_threadReadDP(void * ddp); extern PIP_EXPORT PIConnection::DevicePool * __device_pool__; class PIP_EXPORT __DevicePoolContainer__ { public: __DevicePoolContainer__(); static bool inited_; }; static __DevicePoolContainer__ __device_pool_container__; #endif // PICONNECTION_H