//! \~\file piconnection.h //! \~\ingroup IO-Utils //! \~\brief //! \~english Connection routing helper built on shared devices and packet filters //! \~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; //! \~\ingroup IO-Utils //! \~\brief //! \~english Routes data between shared devices, packet extractors, channels and periodic senders. //! \~russian Маршрутизирует данные между общими устройствами, извлекателями пакетов, каналами и периодическими отправителями. //! //! \~\details //! \~english //! %PIConnection uses a process-wide device pool so several connections can //! share the same physical device. Raw device data may be forwarded into named //! filters, routed through channels, monitored with diagnostics and emitted by //! periodic senders. //! \~russian //! %PIConnection использует общий для процесса пул устройств, поэтому несколько //! соединений могут разделять одно физическое устройство. Сырые данные //! устройств могут передаваться в именованные фильтры, маршрутизироваться по //! каналам, контролироваться диагностикой и отправляться периодическими //! отправителями. class PIP_EXPORT PIConnection: public PIObject { PIOBJECT_SUBCLASS(PIConnection, PIObject); public: //! \~english Constructs an empty connection with name "name". //! \~russian Создает пустое соединение с именем "name". PIConnection(const PIString & name = PIStringAscii("connection")); //! \~english Constructs a connection and immediately configures it from section "name" of file "config". //! \~russian Создает соединение и сразу настраивает его из секции "name" файла "config". PIConnection(const PIString & config, const PIString & name); //! \~english Constructs a connection and immediately configures it from section "name" stored in "string". //! \~russian Создает соединение и сразу настраивает его из секции "name", хранящейся в "string". PIConnection(PIString * string, const PIString & name); //! \~english Releases all bindings owned by this connection. //! \~russian Освобождает все привязки, принадлежащие этому соединению. ~PIConnection(); //! \~english Reconfigures the connection from section "name" of file "config". //! \~russian Перенастраивает соединение из секции "name" файла "config". //! \~\details //! \~english \b Warning: all devices, filters and channels removed before configure! //! \~russian \b Внимание: все устройства, фильтры и каналы удаляются перед настройкой! bool configureFromConfig(const PIString & config, const PIString & name = PIStringAscii("connection")); //! \~english Reconfigures the connection from section "name" stored in "string". //! \~russian Перенастраивает соединение из секции "name", хранящейся в "string". //! \~\details //! \~english \b Warning: all devices, filters and channels removed before configure! //! \~russian \b Внимание: все устройства, фильтры и каналы удаляются перед настройкой! bool configureFromString(PIString * string, const PIString & name = PIStringAscii("connection")); //! \~english Serializes current connection state into one configuration section. //! \~russian Сериализует текущее состояние соединения в одну секцию конфигурации. PIString makeConfig() const; //! \~english Adds device "full_path" to the shared device pool and binds it to this connection. //! \~russian Добавляет устройство "full_path" в общий пул устройств и привязывает его к этому соединению. //! \~\details //! \~english Returns the shared device instance or \c nullptr when creation fails. When "start" is \b true, threaded read starts //! immediately. //! \~russian Возвращает общий экземпляр устройства или \c nullptr, если создание не удалось. Если "start" равно \b true, потоковое //! чтение запускается сразу. PIIODevice * addDevice(const PIString & full_path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite, bool start = false); //! \~english Assigns alias "name" to device "dev" inside this connection. //! \~russian Назначает устройству "dev" псевдоним "name" внутри этого соединения. void setDeviceName(PIIODevice * dev, const PIString & name); //! \~english Returns all aliases assigned to device "dev" in this connection. //! \~russian Возвращает все псевдонимы устройства "dev" в этом соединении. PIStringList deviceNames(const PIIODevice * dev) const; //! \~english Unbinds device "full_path" from this connection. //! \~russian Отвязывает устройство "full_path" от этого соединения. //! \~\details //! \~english The shared device object is deleted from the pool only when no connections still use it. //! \~russian Общий объект устройства удаляется из пула только тогда, когда им больше не пользуется ни одно соединение. bool removeDevice(const PIString & full_path); //! \~english Removes all devices currently bound to this connection. //! \~russian Удаляет все устройства, привязанные к этому соединению. //! \~\details //! \~english Devices remain in the shared pool while they are still referenced by other connections. //! \~russian Устройства остаются в общем пуле, пока на них ссылаются другие соединения. void removeAllDevices(); //! \~english Returns bound device by normalized full path. //! \~russian Возвращает привязанное устройство по нормализованному полному пути. PIIODevice * deviceByFullPath(const PIString & full_path) const; //! \~english Returns bound device by alias. //! \~russian Возвращает привязанное устройство по псевдониму. PIIODevice * deviceByName(const PIString & name) const; //! \~english Returns all devices currently bound to this connection. //! \~russian Возвращает все устройства, привязанные к этому соединению. PIVector boundedDevices() const; //! \~english Creates or reuses filter "name" and binds source "full_path_name" to it. //! \~russian Создает или повторно использует фильтр "name" и привязывает к нему источник "full_path_name". //! \~\details //! \~english If there is no filter with name "name", connection create new with split mode "mode" and bound //! to it device "full_path_name" or filter "full_path_name". If filter with name "name" already exists, //! device "full_path_name" or filter "full_path_name" add to this filter. //! This function returns PIPacketExtractor * assosiated with this filter. //! \~russian Если фильтра с именем "name" не существует, соединение создаст новый фильтр с режимом разделения "mode" //! и привяжет к нему устройство "full_path_name" или фильтр "full_path_name". Если фильтр с именем "name" уже существует, //! устройство "full_path_name" или фильтр "full_path_name" добавляется к этому фильтру. //! Эта функция возвращает PIPacketExtractor *, связанный с этим фильтром. //! \~\note //! \~english \b Attention! "mode" is altual olny if new filter was created! //! \~russian \b Внимание! "mode" актуален только если был создан новый фильтр! //! \~\sa PIPacketExtractor PIPacketExtractor * addFilter(const PIString & name, const PIString & full_path_name, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None); //! \~english Creates or reuses filter "name" and binds device "dev" to it. //! \~russian Создает или повторно использует фильтр "name" и привязывает к нему устройство "dev". PIPacketExtractor * addFilter(const PIString & name, const PIIODevice * dev, PIPacketExtractor::SplitMode mode = PIPacketExtractor::None) { return addFilter(name, devFPath(dev), mode); } //! \~english Binds existing extractor object "filter" to source "full_path_name". //! \~russian Привязывает существующий объект извлекателя "filter" к источнику "full_path_name". PIPacketExtractor * addFilter(PIPacketExtractor * filter, const PIString & full_path_name); //! \~english Binds existing extractor object "filter" to device "dev". //! \~russian Привязывает существующий объект извлекателя "filter" к устройству "dev". PIPacketExtractor * addFilter(PIPacketExtractor * filter, const PIIODevice * dev) { return addFilter(filter, devFPath(dev)); } //! \~english Unbinds source "full_path_name" from filter "name". //! \~russian Отвязывает источник "full_path_name" от фильтра "name". //! \~\details //! \~english Removes the filter itself when it no longer has any bound sources. //! \~russian Удаляет сам фильтр, когда у него больше не остается привязанных источников. bool removeFilter(const PIString & name, const PIString & full_path_name); //! \~english Unbinds device or upstream filter "dev" from filter "name". //! \~russian Отвязывает устройство или вышестоящий фильтр "dev" от фильтра "name". bool removeFilter(const PIString & name, const PIIODevice * dev); //! \~english Removes filter "name" together with all its bindings. //! \~russian Удаляет фильтр "name" вместе со всеми его привязками. bool removeFilter(const PIString & name); //! \~english Removes all filters from this connection. //! \~russian Удаляет все фильтры из этого соединения. void removeAllFilters(); //! \~english Returns all filters owned by this connection. //! \~russian Возвращает все фильтры, принадлежащие этому соединению. PIVector filters() const; //! \~english Returns names of all filters owned by this connection. //! \~russian Возвращает имена всех фильтров, принадлежащих этому соединению. PIStringList filterNames() const; //! \~english Returns filter "name" or \c nullptr when it does not exist. //! \~russian Возвращает фильтр "name" или \c nullptr, если такого фильтра нет. PIPacketExtractor * filter(const PIString & name) const; //! \~english Returns all sources currently bound to filter "name". //! \~russian Возвращает все источники, привязанные к фильтру "name". PIVector filterBoundedDevices(const PIString & name) const; //! \~english Adds a routing channel from "name_from" to "name_to". //! \~russian Добавляет канал маршрутизации от "name_from" к "name_to". //! \~\details //! \~english Both endpoints may reference a device full path, a device alias or a filter name. //! \~russian Оба конца канала могут ссылаться на полный путь устройства, псевдоним устройства или имя фильтра. bool addChannel(const PIString & name_from, const PIString & name_to); //! \~english Adds a routing channel from "name_from" to device "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 Adds a routing channel from device "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 Adds a routing channel from device "dev_from" to device "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 Removes routing channel from "name_from" to "name_to". //! \~russian Удаляет канал маршрутизации от "name_from" к "name_to". bool removeChannel(const PIString & name_from, const PIString & name_to); //! \~english Removes routing channel from "name_from" to device "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 Removes routing channel from device "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 Removes routing channel from device "dev_from" to device "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 Removes all outgoing channels starting from "name_from". //! \~russian Удаляет все исходящие каналы, начинающиеся в "name_from". bool removeChannel(const PIString & name_from); //! \~english Removes all outgoing channels starting from device "dev_from". //! \~russian Удаляет все исходящие каналы, начинающиеся от устройства "dev_from". bool removeChannel(const PIIODevice * dev_from) { return removeChannel(devFPath(dev_from)); } //! \~english Removes all routing channels from this connection. //! \~russian Удаляет все каналы маршрутизации из этого соединения. void removeAllChannels(); //! \~english Returns all routing channels as source and destination pairs. //! \~russian Возвращает все каналы маршрутизации как пары источника и назначения. PIVector> channels() const; //! \~english Creates or reuses sender "name" and binds device "full_path_name" to it. //! \~russian Создает или повторно использует отправитель "name" и привязывает к нему устройство "full_path_name". //! \~\details //! \~english If there is no sender with name "name", connection create new, bound //! to it device "full_path_name" and start sender timer with frequency "frequency". //! If sender with name "name" already exists, device "full_path_name" add to this sender //! If "start" is true, sender is started immediately. Else, you can start sender with //! functions \a startSender(). //! \~russian Если отправителя с именем "name" не существует, соединение создаст новый отправитель, привяжет //! к нему устройство "full_path_name" и запускает таймер отправителя с частотой "frequency". //! Если отправитель с именем "name" уже существует, устройство "full_path_name" добавляется к этому отправителю //! Если "start" равно true, отправитель запускается немедленно. В противном случае можно запустить отправителя с помощью //! функций \a startSender(). //! \~\note //! \~english \b Attention! "frequency" is actual olny if new sender was created! //! \~russian \b Внимание! "frequency" актуален только если был создан новый отправитель! //! \~\sa startSender() void addSender(const PIString & name, const PIString & full_path_name, float frequency, bool start = false); //! \~english Creates or reuses sender "name" and binds device "dev" to it. //! \~russian Создает или повторно использует отправитель "name" и привязывает к нему устройство "dev". void addSender(const PIString & name, const PIIODevice * dev, float frequency, bool start = false) { addSender(name, devFPath(dev), frequency, start); } //! \~english Unbinds device "full_path_name" from sender "name". //! \~russian Отвязывает устройство "full_path_name" от отправителя "name". //! \~\details //! \~english 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 Unbinds device "dev" from sender "name". //! \~russian Отвязывает устройство "dev" от отправителя "name". bool removeSender(const PIString & name, const PIIODevice * dev) { return removeSender(name, devFPath(dev)); } //! \~english Removes sender "name" together with its timer state. //! \~russian Удаляет отправитель "name" вместе с состоянием его таймера. bool removeSender(const PIString & name); //! \~english Assigns fixed payload "data" to sender "name". //! \~russian Назначает фиксированную нагрузку "data" отправителю "name". bool setSenderFixedData(const PIString & name, const PIByteArray & data); //! \~english Clears fixed payload for sender "name". //! \~russian Очищает фиксированную нагрузку отправителя "name". bool clearSenderFixedData(const PIString & name); //! \~english Returns fixed payload configured for sender "name". //! \~russian Возвращает фиксированную нагрузку, настроенную для отправителя "name". PIByteArray senderFixedData(const PIString & name) const; //! \~english Returns sender timer frequency. //! \~russian Возвращает частоту таймера отправителя. //! \~\details //! \~english Returns -1 when the sender does not exist and 0 when it exists but is not running. //! \~russian Возвращает -1, если отправителя не существует, и 0, если он существует, но не запущен. float senderFrequency(const PIString & name) const; //! \~english Removes all senders from this connection. //! \~russian Удаляет все отправители из этого соединения. void removeAllSenders(); //! \~english Starts threaded read for source "full_path_name". //! \~russian Запускает потоковое чтение для источника "full_path_name". void startThreadedRead(const PIString & full_path_name); //! \~english Starts threaded read for device "dev". //! \~russian Запускает потоковое чтение для устройства "dev". void startThreadedRead(const PIIODevice * dev) { startThreadedRead(devFPath(dev)); } //! \~english Starts threaded read for all devices bound to the shared pool. //! \~russian Запускает потоковое чтение для всех устройств, привязанных к общему пулу. void startAllThreadedReads(); //! \~english Starts sender timer "name". //! \~russian Запускает таймер отправителя "name". void startSender(const PIString & name); //! \~english Starts all sender timers. //! \~russian Запускает таймеры всех отправителей. void startAllSenders(); //! \~english Starts all threaded reads and all senders. //! \~russian Запускает все потоковые чтения и все отправители. void start() { startAllThreadedReads(); startAllSenders(); } //! \~english Stops threaded read for source "full_path_name". //! \~russian Останавливает потоковое чтение для источника "full_path_name". void stopThreadedRead(const PIString & full_path_name); //! \~english Stops threaded read for device "dev". //! \~russian Останавливает потоковое чтение для устройства "dev". void stopThreadedRead(const PIIODevice * dev) { stopThreadedRead(devFPath(dev)); } //! \~english Stops threaded read for all bound devices. //! \~russian Останавливает потоковое чтение для всех привязанных устройств. void stopAllThreadedReads(); //! \~english Stops sender timer "name". //! \~russian Останавливает таймер отправителя "name". void stopSender(const PIString & name); //! \~english Stops all sender timers. //! \~russian Останавливает таймеры всех отправителей. void stopAllSenders(); //! \~english Stops all threaded reads and all senders. //! \~russian Останавливает все потоковые чтения и все отправители. void stop() { stopAllThreadedReads(); stopAllSenders(); } //! \~english Stops the connection and removes all bound devices. //! \~russian Останавливает соединение и удаляет все привязанные устройства. void destroy() { stop(); removeAllDevices(); } //! \~english Returns whether the connection currently has no bound devices. //! \~russian Возвращает, нет ли сейчас у соединения привязанных устройств. bool isEmpty() const { return device_modes.isEmpty(); } //! \~english Returns diagnostics object for device or filter "full_path_name". //! \~russian Возвращает объект диагностики для устройства или фильтра "full_path_name". PIDiagnostics * diagnostic(const PIString & full_path_name) const; //! \~english Returns diagnostics object associated with device or filter "dev". //! \~russian Возвращает объект диагностики, связанный с устройством или фильтром "dev". PIDiagnostics * diagnostic(const PIIODevice * dev) const { return diags_.value(const_cast(dev), 0); } //! \~english Writes "data" to device resolved by full path "full_path". //! \~russian Записывает "data" в устройство, найденное по полному пути "full_path". int writeByFullPath(const PIString & full_path, const PIByteArray & data); //! \~english Writes "data" to device resolved by alias "name". //! \~russian Записывает "data" в устройство, найденное по псевдониму "name". int writeByName(const PIString & name, const PIByteArray & data); //! \~english Writes "data" directly to device "dev". //! \~russian Записывает "data" непосредственно в устройство "dev". int write(PIIODevice * dev, const PIByteArray & data); //! \~english Returns all currently alive %PIConnection objects. //! \~russian Возвращает все существующие в приложении объекты %PIConnection. static PIVector allConnections(); //! \~english Returns all devices currently stored in the shared device pool. //! \~russian Возвращает все устройства, которые сейчас хранятся в общем пуле устройств. static PIVector allDevices(); //! \~english Enables or disables shared device-pool fake mode and returns previous state. //! \~russian Включает или выключает режим имитации общего пула устройств и возвращает предыдущее состояние. static bool setFakeMode(bool yes); //! \~english Returns whether the shared device pool works in fake mode. //! \~russian Возвращает, работает ли общий пул устройств в режиме имитации. static bool isFakeMode(); 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; PIVector boundedConnections() const; PIVector boundedDevices() const; PIVector boundedDevices(const PIConnection * parent) const; protected: struct PIP_EXPORT DeviceData { DeviceData(): dev(0), rthread(0), started(false) {} ~DeviceData(); PIIODevice * dev; PIThread * rthread; bool started; PIVector listeners; }; void run() override; void deviceReaded(DeviceData * dd, const PIByteArray & data); PIMap devices; bool fake; }; //! \events //! \{ //! \fn void dataReceivedEvent(const PIString & from, const PIByteArray & data) //! \~english Emitted when raw data is received from source "from". //! \~russian Генерируется при получении сырых данных от источника "from". EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data); //! \fn void packetReceivedEvent(const PIString & from, const PIByteArray & data) //! \~english Emitted when filter "from" produces a packet. //! \~russian Генерируется, когда фильтр "from" выдает пакет. EVENT2(packetReceivedEvent, const PIString &, from, const PIByteArray &, data); //! \fn void qualityChanged(const PIIODevice * device, PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality) //! \~english Emitted when diagnostics quality of "device" changes. //! \~russian Генерируется при изменении качества диагностики устройства "device". EVENT3(qualityChanged, const PIIODevice *, dev, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality); //! \} protected: //! \~english Called after raw data is received from source "from". //! \~russian Вызывается после получения сырых данных от источника "from". virtual void dataReceived(const PIString & from, const PIByteArray & data) {} //! \~english Called after filter "from" produces a packet. //! \~russian Вызывается после того, как фильтр "from" выдает пакет. virtual void packetReceived(const PIString & from, const PIByteArray & data) {} //! \~english Returns dynamic payload 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