Files
pip/libs/main/thread/pigrabberbase.h
2026-03-12 14:46:57 +03:00

296 lines
11 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 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 <http://www.gnu.org/licenses/>.
*/
#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<typename T = PIByteArray>
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<T> que;
PIDiagnostics diag_;
mutable PIMutex que_mutex, last_mutex;
};
#endif // PIGRABBERBASE_H