/** * @RDE_HEADER@ * AvsCommandQueue * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, NLSE, 2018-02-23 */ #include "avs_command_queue.h" #include "icommand.h" #include namespace AudioVideoManagement { AvsCommandQueue::AvsCommandQueue(std::chrono::milliseconds timeout) : m_logger(getLogger("AvsCommandQueue")) { if (timeout.count()) { m_timer.setSingleShot(true); m_timer.setInterval(timeout.count()); connect(&m_timer, &QTimer::timeout, this, &AvsCommandQueue::timeout); // signal -> signal } } AvsCommandQueue::~AvsCommandQueue() { flush(); } void AvsCommandQueue::queue(std::unique_ptr cmd, std::unique_ptr connection) { Q_ASSERT(cmd); bool noPendingCommand = m_queue.empty(); m_queue.push_back(CommandWithConn{std::move(cmd), std::move(connection)}); DEBUG_LOG(m_logger) << "queueing command, queue size: " << m_queue.size(); tryReduce(); if (noPendingCommand) nextCommand(); } void AvsCommandQueue::done() { Q_ASSERT(!m_queue.empty()); m_timer.stop(); m_queue.pop_front(); DEBUG_LOG(m_logger) << "command finished, queue size: " << m_queue.size(); if (m_queue.empty()) emit queueEmpty(); else nextCommand(); } ICommand *AvsCommandQueue::currentCommand() const { Q_ASSERT(!m_queue.empty()); return m_queue.front().cmd.get(); } bool AvsCommandQueue::currentHasConnection() const { Q_ASSERT(!m_queue.empty()); return m_queue.front().connection != nullptr; } LtsUtils::IConnection *AvsCommandQueue::currentConnection() const { Q_ASSERT(!m_queue.empty()); Q_ASSERT(m_queue.front().connection); return m_queue.front().connection.get(); } void AvsCommandQueue::flush() { if (m_queue.empty()) return; DEBUG_LOG(m_logger) << "flushing " << m_queue.size() << " commands"; m_timer.stop(); m_queue.clear(); emit queueEmpty(); } void AvsCommandQueue::alive() { Q_ASSERT(!m_queue.empty()); m_timer.start(); } void AvsCommandQueue::nextCommand() { Q_ASSERT(!m_queue.empty()); DEBUG_LOG(m_logger) << "sending next command"; ICommand *cmd = currentCommand(); m_timer.start(); cmd->send(); } /** * Given the last queued command in the queue this method * tries to find commands queued earlier where the effect * of the earlier command will be nullified by the new command. * If such a command is found, it will be dropped. * For example when there was a SetVolume(x) followed by * a SetVolume(y), we can simply drop the first one. */ void AvsCommandQueue::tryReduce() { if (m_queue.size() <= 2) return; const CommandWithConn &lastCommand = m_queue.back(); // Do not check against the first command as it is beeing executed right now. for (size_t i = 1; i < m_queue.size() - 1; ++i) { const CommandWithConn ¤t = m_queue[i]; if (!lastCommand.cmd->nullifies(current.cmd.get())) continue; DEBUG_LOG(m_logger) << "dropping command due to override"; m_queue.erase(m_queue.begin() + i); return; } } } // namespace AudioVideoManagement