//! \~\file pigrabberbase.h //! \~\ingroup Thread //! \~\brief //! \~english Grabber thread base class //! \~russian Базовый класс потока-граббера /* PIP - Platform Independent Primitives Grabber thread base class 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 PIGRABBERBASE_H #define PIGRABBERBASE_H #include "pidiagnostics.h" #include "pithread.h" #include "pitime.h" //! \~\ingroup Thread //! \~\brief //! \~english Base polling grabber thread with a pending queue, recording virtual methods, and a last-item snapshot. //! \~russian Базовый поток-граббер с очередью ожидающих элементов, виртуальными методами записи и снимком последнего элемента. //! \~\details //! \~english //! Captured items are appended to the internal queue and also copied into \a last(). The snapshot is independent from the queue. //! \~russian //! Захваченные элементы добавляются во внутреннюю очередь и также копируются в \a last(). Этот снимок не зависит от очереди. template class PIGrabberBase: public PIThread { PIOBJECT_SUBCLASS(PIGrabberBase, PIThread); public: //! \~english Constructs a closed non-recording grabber. //! \~russian Создает закрытый граббер без записи. PIGrabberBase() { is_opened = false; is_recording = false; } //! \~english Stops the grabber thread and releases recording/open state. //! \~russian Останавливает поток-граббер и освобождает состояние записи и открытия. virtual ~PIGrabberBase() { stopGrabber(false); } //! \~english Returns whether the grabber is currently opened. //! \~russian Возвращает, открыт ли сейчас граббер. virtual bool isOpened() const { return is_opened; } //! \~english Returns whether captured items are currently being recorded. //! \~russian Возвращает, записываются ли сейчас захваченные элементы. virtual bool isRecording() const { return is_recording; } //! \~english Starts recording subsequent captured items if the grabber is opened and recording is not active yet. //! \~russian Запускает запись последующих захваченных элементов, если граббер открыт и запись еще не активна. virtual void startRecord(const PIString & filename) { if (!isOpened()) return; if (isRecording()) return; rec_mutex.lock(); startRecordInternal(filename); is_recording = true; rec_mutex.unlock(); } //! \~english Stops recording if it is active. //! \~russian Останавливает запись, если она активна. virtual void stopRecord() { if (!isOpened()) return; if (!isRecording()) return; rec_mutex.lock(); is_recording = false; stopRecordInternal(); rec_mutex.unlock(); } //! \~english Returns a copy of the last successfully captured item. This snapshot is kept separately from the pending queue and is not //! consumed by \a dequeue(). //! \~russian Возвращает копию последнего успешно захваченного элемента. Этот снимок хранится отдельно от очереди ожидания и не //! извлекается через \a dequeue(). T last() const { T ret; last_mutex.lock(); ret = last_; last_mutex.unlock(); return ret; } //! \~english Returns whether the pending queue is empty. //! \~russian Возвращает, пуста ли очередь ожидающих элементов. bool isEmpty() { bool ret; que_mutex.lock(); ret = que.isEmpty(); que_mutex.unlock(); return ret; } //! \~english Returns the current number of pending captured items. //! \~russian Возвращает текущее количество ожидающих захваченных элементов. int queSize() { int ret; que_mutex.lock(); ret = que.size(); que_mutex.unlock(); return ret; } //! \~english Dequeues and returns the oldest pending captured item. If the queue is empty, returns a default-constructed value. //! \~russian Извлекает и возвращает самый старый ожидающий захваченный элемент. Если очередь пуста, возвращает значение, созданное //! конструктором по умолчанию. T dequeue() { T ret; // piCoutObj << "start"; que_mutex.lock(); if (!que.isEmpty()) { // piCoutObj << "dequeue"; ret = que.dequeue(); } // piCoutObj << "end"; que_mutex.unlock(); return ret; } //! \~english Stops the grabber thread. With \a wait_forever equal to \c false the method waits briefly and may terminate the thread //! forcibly. //! \~russian Останавливает поток-граббер. При \a wait_forever равном \c false метод ждет недолго и может принудительно завершить поток. void stopGrabber(bool wait_forever = true) { if (isRunning()) { stop(); if (wait_forever) waitForFinish(); else { if (!waitForFinish(100)) terminate(); stopRecord(); close(); } } } //! \~english Opens the grabber immediately. The thread loop also tries to open it automatically when running in a closed state. //! \~russian Немедленно открывает граббер. Цикл потока также пытается открыть его автоматически, если поток работает в закрытом //! состоянии. bool open() { bool ret = openInternal(); if (!is_opened && ret) opened(); is_opened = ret; return ret; } //! \~english Closes the grabber and resets the last-item snapshot. //! \~russian Закрывает граббер и сбрасывает снимок последнего элемента. void close() { bool em = is_opened; closeInternal(); last_ = T(); if (em) closed(); is_opened = false; } //! \~english Returns diagnostics collected for captured and recorded items. //! \~russian Возвращает диагностику для захваченных и записанных элементов. const PIDiagnostics & diag() const { return diag_; } //! \~english Clears only the pending queue. The value returned by \a last() is not changed. //! \~russian Очищает только очередь ожидающих элементов. Значение, возвращаемое \a last(), не изменяется. void clear() { que_mutex.lock(); que.clear(); que_mutex.unlock(); } //! \~english Clears the pending queue and closes the grabber. //! \~russian Очищает очередь ожидания и закрывает граббер. void restart() { clear(); close(); } //! \events //! \{ //! \fn void dataReady() //! \brief //! \~english Raised after a new item has been captured, queued, and copied into \a last(). //! \~russian Вызывается после захвата нового элемента, его добавления в очередь и копирования в \a last(). EVENT(dataReady); //! \fn void opened() //! \brief //! \~english Raised when \a open() switches the grabber into the opened state. //! \~russian Вызывается, когда \a open() переводит граббер в открытое состояние. EVENT(opened); //! \fn void closed() //! \brief //! \~english Raised when \a close() closes a previously opened grabber. //! \~russian Вызывается, когда \a close() закрывает ранее открытый граббер. EVENT(closed); //! \} protected: //! \~english Virtual method executed once when the thread starts, before polling begins. //! \~russian Виртуальный метод, выполняемый один раз при старте потока до начала опроса. virtual void init() {} //! \~english Opens the underlying grabber resource. //! \~russian Открывает базовый ресурс граббера. virtual bool openInternal() = 0; //! \~english Closes the underlying grabber resource. //! \~russian Закрывает базовый ресурс граббера. virtual void closeInternal() = 0; //! \~english Polls the next item into \a val. Return \c 0 when a new item was captured, a positive value when no item is ready yet, and //! a negative value on failure that should close the grabber. //! \~russian Опрашивает следующий элемент в \a val. Возвращает \c 0, когда новый элемент захвачен, положительное значение, когда //! элемент еще не готов, и отрицательное значение при ошибке, после которой граббер должен закрыться. virtual int get(T & val) = 0; //! \~english Records a captured item when recording mode is active. //! \~russian Записывает захваченный элемент, когда активен режим записи. virtual void record(const T & val) {} //! \~english Virtual method called before the recording flag becomes active. //! \~russian Виртуальный метод, вызываемый перед активацией флага записи. virtual void startRecordInternal(const PIString & filename) {} //! \~english Virtual method called after the recording flag is cleared. //! \~russian Виртуальный метод, вызываемый после сброса флага записи. virtual void stopRecordInternal() {} bool is_opened, is_recording; mutable PIMutex rec_mutex; private: void begin() override { init(); } void run() override { if (!isOpened()) { open(); diag_.reset(); if (!is_opened) piMSleep(200); } if (isOpened()) { T c; int ret = get(c); if (ret < 0) { close(); return; } if (ret > 0) { piMinSleep(); return; } diag_.received(1); que_mutex.lock(); que.enqueue(c); que_mutex.unlock(); if (isRecording()) { rec_mutex.lock(); record(c); rec_mutex.unlock(); diag_.sended(1); } last_mutex.lock(); last_ = c; last_mutex.unlock(); dataReady(); } } void end() override { stopRecord(); close(); } T last_; PIQueue que; PIDiagnostics diag_; mutable PIMutex que_mutex, last_mutex; }; #endif // PIGRABBERBASE_H