avs-device-sdk/ADSL/src/DirectiveSequencer.cpp

185 lines
6.2 KiB
C++

/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include <algorithm>
#include <iostream>
#include <sstream>
#include <AVSCommon/AVS/ExceptionErrorType.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include <AVSCommon/Utils/Metrics.h>
#include "ADSL/DirectiveSequencer.h"
/// String to identify log entries originating from this file.
static const std::string TAG("DirectiveSequencer");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
namespace alexaClientSDK {
namespace adsl {
using namespace avsCommon;
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
std::unique_ptr<DirectiveSequencerInterface> DirectiveSequencer::create(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender) {
if (!exceptionSender) {
ACSDK_INFO(LX("createFailed").d("reason", "nullptrExceptionSender"));
return nullptr;
}
return std::unique_ptr<DirectiveSequencerInterface>(new DirectiveSequencer(exceptionSender));
}
bool DirectiveSequencer::addDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
return m_directiveRouter.addDirectiveHandler(handler);
}
bool DirectiveSequencer::removeDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
return m_directiveRouter.removeDirectiveHandler(handler);
}
void DirectiveSequencer::setDialogRequestId(const std::string& dialogRequestId) {
m_directiveProcessor->setDialogRequestId(dialogRequestId);
}
bool DirectiveSequencer::onDirective(std::shared_ptr<AVSDirective> directive) {
if (!directive) {
ACSDK_ERROR(LX("onDirectiveFailed").d("action", "ignored").d("reason", "nullptrDirective"));
return false;
}
std::lock_guard<std::mutex> lock(m_mutex);
if (m_isShuttingDown || !m_isEnabled) {
ACSDK_WARN(LX("onDirectiveFailed")
.d("directive", directive->getHeaderAsString())
.d("action", "ignored")
.d("reason", m_isShuttingDown ? "isShuttingDown" : "disabled"));
return false;
}
ACSDK_INFO(LX("onDirective").d("directive", directive->getHeaderAsString()));
m_receivingQueue.push_back(directive);
m_wakeReceivingLoop.notify_one();
return true;
}
DirectiveSequencer::DirectiveSequencer(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender) :
DirectiveSequencerInterface{"DirectiveSequencer"},
m_mutex{},
m_exceptionSender{exceptionSender},
m_isShuttingDown{false},
m_isEnabled{true} {
m_directiveProcessor = std::make_shared<DirectiveProcessor>(&m_directiveRouter);
m_receivingThread = std::thread(&DirectiveSequencer::receivingLoop, this);
}
void DirectiveSequencer::doShutdown() {
ACSDK_DEBUG9(LX("doShutdown"));
{
std::lock_guard<std::mutex> lock(m_mutex);
m_isShuttingDown = true;
m_wakeReceivingLoop.notify_one();
}
if (m_receivingThread.joinable()) {
m_receivingThread.join();
}
m_directiveProcessor->shutdown();
m_directiveRouter.shutdown();
m_exceptionSender.reset();
}
void DirectiveSequencer::disable() {
ACSDK_DEBUG9(LX("disable"));
std::lock_guard<std::mutex> lock(m_mutex);
m_isEnabled = false;
m_directiveProcessor->setDialogRequestId("");
m_directiveProcessor->disable();
m_wakeReceivingLoop.notify_one();
}
void DirectiveSequencer::enable() {
ACSDK_DEBUG9(LX("disable"));
std::lock_guard<std::mutex> lock(m_mutex);
m_isEnabled = true;
m_directiveProcessor->enable();
m_wakeReceivingLoop.notify_one();
}
void DirectiveSequencer::receivingLoop() {
auto wake = [this]() { return !m_receivingQueue.empty() || m_isShuttingDown; };
std::unique_lock<std::mutex> lock(m_mutex);
while (true) {
m_wakeReceivingLoop.wait(lock, wake);
if (m_isShuttingDown) {
break;
}
receiveDirectiveLocked(lock);
}
}
void DirectiveSequencer::receiveDirectiveLocked(std::unique_lock<std::mutex>& lock) {
if (m_receivingQueue.empty()) {
return;
}
auto directive = m_receivingQueue.front();
m_receivingQueue.pop_front();
lock.unlock();
if (directive->getName() == "StopCapture" || directive->getName() == "Speak") {
ACSDK_METRIC_MSG(TAG, directive, Metrics::Location::ADSL_DEQUEUE);
}
bool handled = false;
/**
* Previously it was expected that all directives resulting from a Recognize event
* would be tagged with the dialogRequestId of that event. In practice that is not
* the observed behavior.
*/
#ifdef DIALOG_REQUEST_ID_IN_ALL_RESPONSE_DIRECTIVES
if (directive->getDialogRequestId().empty()) {
handled = m_directiveRouter.handleDirectiveImmediately(directive);
} else {
handled = m_directiveRouter.handleDirectiveWithPolicyHandleImmediately(directive);
if (!handled) {
handled = m_directiveProcessor->onDirective(directive);
}
}
#else
handled = m_directiveRouter.handleDirectiveWithPolicyHandleImmediately(directive);
if (!handled) {
handled = m_directiveProcessor->onDirective(directive);
}
#endif
if (!handled) {
ACSDK_INFO(LX("sendingExceptionEncountered").d("messageId", directive->getMessageId()));
m_exceptionSender->sendExceptionEncountered(
directive->getUnparsedDirective(), ExceptionErrorType::UNSUPPORTED_OPERATION, "Unsupported operation");
}
lock.lock();
}
} // namespace adsl
} // namespace alexaClientSDK