//! \~\file pibasetransfer.h //! \~\ingroup IO-Utils //! \~\brief //! \~english Base class for reliable packet sessions with acknowledgements, pause and resume //! \~russian Базовый класс для надежных пакетных сессий с подтверждениями, паузой и продолжением /* PIP - Platform Independent Primitives Base class for reliable send and receive data in fixed packets with error correction, pause and resume 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 . */ #ifndef PIBASETRANSFER_H #define PIBASETRANSFER_H #include "picrc.h" #include "pidiagnostics.h" //! \~\ingroup IO-Utils //! \~\brief //! \~english Base transport for reliable fixed-size packet exchange over an external channel. //! \~russian Базовый транспорт для надежного обмена пакетами фиксированного размера через внешний канал. //! \~\details //! \~english This class provides a foundation for reliable data transfer with features like error correction, pause/resume functionality, //! and packet-based communication. //! \~russian Этот класс предоставляет основу для надежной передачи данных с возможностью коррекции ошибок, паузы/возобновления и пакетной //! коммуникации. class PIP_EXPORT PIBaseTransfer: public PIObject { PIOBJECT_SUBCLASS(PIBaseTransfer, PIObject); public: //! \~english Constructs transfer with default packet size, timeout and diagnostics. //! \~russian Создает передачу с размером пакета, таймаутом и диагностикой по умолчанию. PIBaseTransfer(); //! \~english Stops active transfer state and owned diagnostics. //! \~russian Останавливает активное состояние передачи и встроенную диагностику. ~PIBaseTransfer(); #pragma pack(push, 1) //! \~english Common header placed before every protocol packet. //! \~russian Общий заголовок, размещаемый перед каждым пакетом протокола. struct PIP_EXPORT PacketHeader { //! \~english Packet signature used to recognize transfer packets. //! \~russian Сигнатура пакета, используемая для распознавания пакетов передачи. uint sig; //! \~english Packet type from the transfer protocol. //! \~russian Тип пакета из протокола передачи. int type; // PacketType //! \~english Session identifier for current transfer exchange. //! \~russian Идентификатор сессии текущего обмена. int session_id; //! \~english Sequential packet identifier inside the session. //! \~russian Последовательный идентификатор пакета внутри сессии. uint id; //! \~english CRC calculated for the packet payload. //! \~russian CRC, вычисленная для полезной нагрузки пакета. uint crc; //! \~english Returns whether the packet signature matches \a packetSignature(). //! \~russian Возвращает, совпадает ли сигнатура пакета с \a packetSignature(). bool check_sig() { return (sig == signature); } }; //! \~english Logical fragment description used to split data into packets. //! \~russian Описание логического фрагмента, используемое для разбиения данных на пакеты. struct PIP_EXPORT Part { //! \~english Constructs part metadata for item "id_", fragment size "size_" and offset "start_". //! \~russian Создает метаданные части для элемента "id_", размера фрагмента "size_" и смещения "start_". Part(uint id_ = 0, ullong size_ = 0, ullong start_ = 0): id(id_), size(size_), start(start_) {} //! \~english Identifier of the logical item being transferred. //! \~russian Идентификатор логического элемента, который передается. uint id; //! \~english Size of this fragment in bytes. //! \~russian Размер этого фрагмента в байтах. ullong size; //! \~english Byte offset of this fragment inside the logical item. //! \~russian Смещение этого фрагмента в байтах внутри логического элемента. ullong start; }; #pragma pack(pop) //! \~english Requests cancellation of the current send session. //! \~russian Запрашивает отмену текущей сессии отправки. void stopSend(); //! \~english Requests cancellation of the current receive session. //! \~russian Запрашивает отмену текущей сессии приема. void stopReceive(); //! \~english Returns whether a send session is active. //! \~russian Возвращает, активна ли сессия отправки. bool isSending() const { return is_sending; } //! \~english Returns whether a receive session is active. //! \~russian Возвращает, активна ли сессия приема. bool isReceiving() const { return is_receiving; } //! \~english Returns whether the transfer is currently paused. //! \~russian Возвращает, находится ли передача сейчас на паузе. bool isPause() const { return is_pause; } //! \~english Switches current session between paused and resumed states. //! \~russian Переключает текущую сессию между состояниями паузы и продолжения. void setPause(bool pause_); //! \~english Sets maximum encoded packet size in bytes. //! \~russian Устанавливает максимальный размер кодированного пакета в байтах. void setPacketSize(int size) { packet_size = size; } //! \~english Returns maximum encoded packet size in bytes. //! \~russian Возвращает максимальный размер кодированного пакета в байтах. int packetSize() const { return packet_size; } //! \~english Sets session timeout in seconds for start negotiation, acknowledgements and pause recovery. //! \~russian Устанавливает таймаут сессии в секундах для согласования старта, подтверждений и восстановления после паузы. void setTimeout(double sec); //! \~english Returns session timeout in seconds. //! \~russian Возвращает таймаут сессии в секундах. double timeout() const { return timeout_; } //! \~english Enables or disables CRC validation for data packets. //! \~russian Включает или выключает проверку CRC для пакетов данных. void setCRCEnabled(bool en = true) { crc_enabled = en; } //! \~english Returns whether CRC validation is enabled. //! \~russian Возвращает, включена ли проверка CRC. bool isCRCEnabled() const { return crc_enabled; } //! \~english Returns short textual state of the current session. //! \~russian Возвращает краткое текстовое состояние текущей сессии. PIString stateString() const { return state_string; } //! \~english Returns a map of successfully received or transmitted packets for the current session. //! \~russian Возвращает карту успешно принятых или переданных пакетов для текущей сессии. PIString packetMap() const { return pm_string; } //! \~english Returns total number of bytes planned for the current session. //! \~russian Возвращает общее число байтов, запланированных для текущей сессии. llong bytesAll() const { return bytes_all; } //! \~english Returns number of bytes already processed in the current session. //! \~russian Возвращает число байтов, уже обработанных в текущей сессии. llong bytesCur() const { return bytes_cur; } //! \~english Get diagnostics object //! \~russian Получить объект диагностики //! \~\return //! \~english Diagnostic object reference //! \~russian Ссылка на объект диагностики const PIDiagnostics & diagnostic() { return diag; } //! \~english Returns the packet signature constant used by the protocol. //! \~russian Возвращает константу сигнатуры пакета, используемую протоколом. static uint packetSignature() { return signature; } //! \~\handlers //! \~\{ //! \~\fn void received(PIByteArray data) //! \~english Processes a single encoded packet received from an external transport. //! \~russian Обрабатывает один закодированный пакет, полученный от внешнего транспорта. //! \~\note //! \~english This handler must be called when data is received by an external transport. //! \~russian Этот обработчик необходимо вызывать при получении данных внешним транспортом. EVENT_HANDLER1(void, received, PIByteArray, data); //! \~\fn void stop() //! \~english Stops both sending and receiving sides of the current session. //! \~russian Останавливает и отправку, и прием текущей сессии. //! \~\note //! \~english Equivalent to calling both `stopSend()` and `stopReceive()`. //! \~russian Эквивалентно вызову `stopSend()` и `stopReceive()`. EVENT_HANDLER(void, stop) { stopSend(); stopReceive(); } //! \~\fn void pause() //! \~english Switches current session to paused state. //! \~russian Переводит текущую сессию в состояние паузы. //! \~\note //! \~english Triggers the `paused()` signal. //! \~russian Генерирует событие `paused()`. EVENT_HANDLER(void, pause) { setPause(true); } //! \~\fn void resume() //! \~english Resumes the current paused session. //! \~russian Продолжает текущую приостановленную сессию. //! \~\note //! \~english Triggers the `resumed()` signal. //! \~russian Генерирует событие `resumed()`. EVENT_HANDLER(void, resume) { setPause(false); } //! \~\} //! \~\events //! \~\{ //! \~\fn void receiveStarted() //! \~english Emitted when a receive session is accepted and initialized. //! \~russian Генерируется, когда сессия приема принята и инициализирована. //! \~\note //! \~english Triggered by internal logic after receiving a valid `pt_Start` packet. //! \~russian Генерируется внутренней логикой после получения корректного пакета типа `pt_Start`. EVENT(receiveStarted); //! \~\fn void paused() //! \~english Emitted when the transfer enters paused state. //! \~russian Генерируется, когда передача переходит в состояние паузы. //! \~\note //! \~english Triggered when `pause` handler is invoked (e.g., via `setPause(true)` or incoming `pt_Pause`). //! \~russian Генерируется при вызове обработчика `pause` (например, через `setPause(true)` или получении `pt_Pause`). EVENT(paused); //! \~\fn void resumed() //! \~english Emitted when the transfer leaves paused state. //! \~russian Генерируется, когда передача выходит из состояния паузы. //! \~\note //! \~english Triggered when `resume` handler is invoked (e.g., via `setPause(false)` or incoming `pt_Start` during pause). //! \~russian Генерируется при вызове обработчика `resume` (например, через `setPause(false)` или получении `pt_Start` во время //! паузы). EVENT(resumed); //! \~\fn void receiveFinished(bool ok) //! \~english Emitted when the receive session finishes with result "ok". //! \~russian Генерируется, когда сессия приема завершается с результатом "ok". //! \~\param ok //! \~english `true` if all packets were received and verified, `false` on error or interruption. //! \~russian `true`, если все пакеты получены и проверены, `false` — при ошибке или прерывании. EVENT1(receiveFinished, bool, ok); //! \~\fn void sendStarted() //! \~english Emitted when a prepared send session starts. //! \~russian Генерируется при запуске подготовленной сессии отправки. //! \~\note //! \~english Triggered after `buildSession()` and before the first packet is sent. //! \~russian Генерируется после `buildSession()` и до отправки первого пакета. EVENT(sendStarted); //! \~\fn void sendFinished(bool ok) //! \~english Emitted when the send session finishes with result "ok". //! \~russian Генерируется, когда сессия отправки завершается с результатом "ok". //! \~\param ok //! \~english `true` if all packets were sent and acknowledged, `false` on error or timeout. //! \~russian `true`, если все пакеты отправлены и подтверждены, `false` — при ошибке или таймауте. EVENT1(sendFinished, bool, ok); //! \~\fn void sendRequest(PIByteArray &data) //! \~english Emitted for every encoded packet that must be written to the external transport. //! \~russian Генерируется для каждого закодированного пакета, который нужно записать во внешний транспорт. //! \~\param data //! \~english Encoded packet including protocol header and payload. //! \~russian Закодированный пакет, включая заголовок протокола и полезную нагрузку. //! \~\note //! \~english The external transport should send this data. //! \~russian Внешний транспорт должен отправить эти данные. EVENT1(sendRequest, PIByteArray &, data); //! \~\} protected: //! \~english Builds session packet layout for logical parts in "parts". //! \~russian Формирует раскладку пакетов сессии для логических частей из "parts". void buildSession(PIVector parts); //! \~english Returns payload bytes for one requested logical fragment. //! \~russian Возвращает байты полезной нагрузки для одного запрошенного логического фрагмента. virtual PIByteArray buildPacket(Part fi) = 0; //! \~english Consumes one received logical fragment and optional custom packet header. //! \~russian Обрабатывает один принятый логический фрагмент и необязательный пользовательский заголовок пакета. virtual void receivePart(Part fi, PIByteArray ba, PIByteArray pheader) = 0; //! \~english Called after a new receive session is accepted and initialized. //! \~russian Вызывается после принятия и инициализации новой сессии приема. virtual void beginReceive() { ; } //! \~english Custom header //! \~russian Пользовательский заголовок virtual PIByteArray customHeader() { return PIByteArray(); } //! \~english Runs the prepared send session until success, failure or cancellation. //! \~russian Выполняет подготовленную сессию отправки до успеха, ошибки или отмены. bool send_process(); uint packet_header_size, part_header_size; bool break_, is_sending, is_receiving, is_pause; PIString state_string; llong bytes_all, bytes_cur; private: enum PacketType { pt_Unknown, pt_Data, pt_ReplySuccess, pt_ReplyInvalid, pt_Break, pt_Start, pt_Pause }; #pragma pack(push, 1) struct PIP_EXPORT StartRequest { uint packets; ullong size; }; #pragma pack(pop) BINARY_STREAM_FRIEND(PIBaseTransfer::StartRequest); void processData(int id, PIByteArray & data); PIByteArray build_packet(int id); int checkSession(); void sendBreak(int session_id); void sendReply(PacketType reply); bool getStartRequest(); bool finish_send(bool ok); void finish_receive(bool ok, bool quet = false); static const uint signature; int packet_size, packets_count; double timeout_; PIVector> session; PIVector replies; PITimeMeasurer send_tm, pause_tm; PacketHeader header; CRC_16 crc; int send_queue; int send_up; PIDiagnostics diag; PIMutex mutex_session; PIMutex mutex_send; PIMutex mutex_header; bool crc_enabled; PIString pm_string; }; //! \~english Binary stream write operator for PacketHeader //! \~russian Оператор записи в бинарный поток для PacketHeader BINARY_STREAM_WRITE(PIBaseTransfer::PacketHeader) { s << v.sig << v.type << v.session_id << v.id << v.crc; return s; } //! \~english Binary stream read operator for PacketHeader //! \~russian Оператор чтения из бинарного потока для PacketHeader BINARY_STREAM_READ(PIBaseTransfer::PacketHeader) { s >> v.sig >> v.type >> v.session_id >> v.id >> v.crc; return s; } //! \~english Binary stream write operator for Part //! \~russian Оператор записи в бинарный поток для Part BINARY_STREAM_WRITE(PIBaseTransfer::Part) { s << v.id << v.size << v.start; return s; } //! \~english Binary stream read operator for Part //! \~russian Оператор чтения из бинарного потока для Part BINARY_STREAM_READ(PIBaseTransfer::Part) { s >> v.id >> v.size >> v.start; return s; } //! \~english Binary stream write operator for StartRequest //! \~russian Оператор записи в бинарный поток для StartRequest BINARY_STREAM_WRITE(PIBaseTransfer::StartRequest) { s << v.packets << v.size; return s; } //! \~english Binary stream read operator for StartRequest //! \~russian Оператор чтения из бинарного потока для StartRequest BINARY_STREAM_READ(PIBaseTransfer::StartRequest) { s >> v.packets >> v.size; return s; } //! \~\relatesalso PICout //! \~english Writes Part to \a PICout. //! \~russian Выводит Part в \a PICout. inline PICout operator<<(PICout s, const PIBaseTransfer::Part & v) { s.saveAndSetControls(0); s << "Part(\"" << v.id << "\", " << PIString::readableSize(v.start) << " b | " << PIString::readableSize(v.size) << " b)"; s.restoreControls(); return s; } #endif // PIBASETRANSFER_H