summaryrefslogtreecommitdiffstats
path: root/src/plugins/VshlCoreApi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/VshlCoreApi.cpp')
-rw-r--r--src/plugins/VshlCoreApi.cpp232
1 files changed, 184 insertions, 48 deletions
diff --git a/src/plugins/VshlCoreApi.cpp b/src/plugins/VshlCoreApi.cpp
index 05c894d..4c32391 100644
--- a/src/plugins/VshlCoreApi.cpp
+++ b/src/plugins/VshlCoreApi.cpp
@@ -17,7 +17,10 @@
#include <list>
#include <sstream>
+#include <fstream>
+#include <filesystem>
#include <json.hpp>
+#include <json-c/json.h>
#include "afb/AFBApiImpl.h"
#include "afb/AFBRequestImpl.h"
@@ -49,6 +52,10 @@ static std::string STARTLISTENING_JSON_ATTR_REQUEST = "request_id";
static std::string EVENTS_JSON_ATTR_VA_ID = "va_id";
static std::string EVENTS_JSON_ATTR_EVENTS = "events";
+static std::string CONFIG_JSON_FILE = "/etc/xdg/AGL/voice-high.json";
+static std::string JSON_EXT = ".json";
+static std::string VA_CONFIG_JSON_DIR = "/etc/xdg/AGL/voiceagents";
+
static std::shared_ptr<vshlcore::utilities::logging::Logger> sLogger;
static std::shared_ptr<vshlcore::common::interfaces::IAFBApi> sAfbApi;
static std::unique_ptr<vshlcore::core::VRRequestProcessor> sVRRequestProcessor;
@@ -151,69 +158,198 @@ CTLP_CAPI(onDialogStateEvent, source, argsJ, eventJ) {
return 0;
}
-CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) {
- if (sVoiceAgentsDataManager == nullptr) {
- sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: Voice service not initialized.");
+// Helper function to add events for voice agent to controller
+// configuration. It relies on the controller configuration being
+// available via the userdata of the API pointer.
+static int AddVoiceAgentEvents(std::string &agentApi) {
+ // Retrieve section config from api handle
+ CtlConfigT *ctrlConfig = (CtlConfigT*) afb_api_get_userdata(sAfbApi->getApi());
+ if (ctrlConfig == nullptr) {
+ sLogger->log(Level::ERROR, TAG, "AddVoiceAgentEvents: ctrlConfig == nullptr");
return -1;
}
- if (argsJ == nullptr) {
- sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: No arguments supplied.");
+ CtlSectionT* section = nullptr;
+ for (int idx = 0; ctrlConfig->sections[idx].key != NULL; ++idx) {
+ if (!strcasecmp(ctrlConfig->sections[idx].key, "events")) {
+ section = &(ctrlConfig->sections[idx]);
+ break;
+ }
+ }
+ if (section == nullptr) {
+ sLogger->log(Level::ERROR, TAG, "AddVoiceAgentEvents: events section not found");
return -1;
}
- json agentsConfigJson = json::parse(json_object_to_json_string(argsJ));
- if (agentsConfigJson.find(VA_JSON_ATTR_AGENTS) == agentsConfigJson.end()) {
- sLogger->log(Level::ERROR, TAG, "loadVoiceAgentsConfig: No agents object found in agents json");
- return -1;
+ // Fill out events JSON array to pass to the controller
+ json_object* eventsJ = json_object_new_array();
+
+ json_object *eventJ = json_object_new_object();
+ std::string uid = agentApi + "/voice_authstate_event";
+ json_object *uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ json_object *actionJ = json_object_new_string("plugin://vshl-core#onAuthStateEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
+ eventJ = json_object_new_object();
+ uid = agentApi + "/voice_connectionstate_event";
+ uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ actionJ = json_object_new_string("plugin://vshl-core#onConnectionStateEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
+ eventJ = json_object_new_object();
+ uid = agentApi + "/voice_dialogstate_event";
+ uidJ = json_object_new_string(uid.c_str());
+ json_object_object_add(eventJ, "uid", uidJ);
+ actionJ = json_object_new_string("plugin://vshl-core#onDialogStateEvent");
+ json_object_object_add(eventJ, "action", actionJ);
+ json_object_array_add(eventsJ, eventJ);
+
+ // Call into controller to add event actions
+ // NOTE: AFAICT the JSON objects end up reused by the controller data
+ // structures, so eventsJ should not be freed with a put after
+ // this call.
+ int rc = AddActionsToSection(sAfbApi->getApi(), section, eventsJ, 0);
+ if (rc != 0) {
+ stringstream message;
+ message << "AddVoiceAgentEvents: AddActionsToSection rc = " << rc;
+ sLogger->log(Level::WARNING, TAG, message.str().c_str());
}
+ return rc;
+}
- json agentsJson = agentsConfigJson[VA_JSON_ATTR_AGENTS];
- for (auto agentIt = agentsJson.begin(); agentIt != agentsJson.end(); ++agentIt) {
- json agentJson = *agentIt;
-
- if (agentJson.find(VA_JSON_ATTR_ID) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_ACTIVE) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_NAME) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_API) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_WWS) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_ACTIVE_WW) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_DESCRIPTION) == agentJson.end() ||
- agentJson.find(VA_JSON_ATTR_VENDOR) == agentJson.end()) {
- std::stringstream error;
- error << "loadVoiceAgentsConfig: One or more missing params in agent "
- "config "
- << agentJson.dump();
- sLogger->log(Level::WARNING, TAG, error.str().c_str());
- continue;
- }
+// Helper function to parse a voiceagent's configuration
+static bool parseVoiceAgentConfig(const json &agentJson) {
+ if (agentJson.find(VA_JSON_ATTR_ID) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_ACTIVE) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_NAME) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_API) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_WWS) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_ACTIVE_WW) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_DESCRIPTION) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_VENDOR) == agentJson.end()) {
+ stringstream error;
+ error << "loadVoiceAgentConfig: One or more missing params in agent "
+ "config "
+ << agentJson.dump();
+ sLogger->log(Level::WARNING, TAG, error.str().c_str());
+ return false;
+ }
- std::string id(agentJson[VA_JSON_ATTR_ID].get<string>());
- std::string name(agentJson[VA_JSON_ATTR_NAME].get<string>());
- std::string api(agentJson[VA_JSON_ATTR_API].get<string>());
- std::string description(agentJson[VA_JSON_ATTR_DESCRIPTION].get<string>());
- std::string vendor(agentJson[VA_JSON_ATTR_VENDOR].get<string>());
- std::string activeWakeword(agentJson[VA_JSON_ATTR_ACTIVE_WW].get<string>());
- bool isActive(agentJson[VA_JSON_ATTR_ACTIVE].get<bool>());
-
- shared_ptr<unordered_set<string>> wakewords = std::make_shared<unordered_set<string>>();
- json wakewordsJson = agentJson[VA_JSON_ATTR_WWS];
- for (auto wwIt = wakewordsJson.begin(); wwIt != wakewordsJson.end(); ++wwIt) {
- wakewords->insert(wwIt->get<string>());
- }
+ std::string id(agentJson[VA_JSON_ATTR_ID].get<string>());
+ std::string name(agentJson[VA_JSON_ATTR_NAME].get<string>());
+ std::string api(agentJson[VA_JSON_ATTR_API].get<string>());
+ std::string description(agentJson[VA_JSON_ATTR_DESCRIPTION].get<string>());
+ std::string vendor(agentJson[VA_JSON_ATTR_VENDOR].get<string>());
+ std::string activeWakeword(agentJson[VA_JSON_ATTR_ACTIVE_WW].get<string>());
+ bool isActive(agentJson[VA_JSON_ATTR_ACTIVE].get<bool>());
+
+ shared_ptr<unordered_set<string>> wakewords = std::make_shared<unordered_set<string>>();
+ json wakewordsJson = agentJson[VA_JSON_ATTR_WWS];
+ for (auto wwIt = wakewordsJson.begin(); wwIt != wakewordsJson.end(); ++wwIt) {
+ wakewords->insert(wwIt->get<string>());
+ }
+
+ if (sAfbApi->requireApi(api, true) != 0) {
+ stringstream error;
+ error << "loadVoiceAgentConfig: Failed to require API \""
+ << api
+ << "\" for voiceagent \""
+ << name
+ << "\".";
+ sLogger->log(Level::ERROR, TAG, error.str().c_str());
+ return false;
+ }
+
+ if (AddVoiceAgentEvents(api) != 0) {
+ stringstream error;
+ error << "loadVoiceAgentConfig: Failed to add events for voiceagent \""
+ << name
+ << "\".";
+ sLogger->log(Level::ERROR, TAG, error.str().c_str());
+ return false;
+ }
+
+ return sVoiceAgentsDataManager->addNewVoiceAgent(
+ id, name, description, api, vendor, activeWakeword, isActive, wakewords);
+}
- sVoiceAgentsDataManager->addNewVoiceAgent(
- id, name, description, api, vendor, activeWakeword, isActive, wakewords);
+// Helper function to parse voiceagents configuration
+static void parseVoiceAgentsConfig(const json &agentsConfigJson, std::string &defaultVoiceAgent) {
+ if (agentsConfigJson.find(VA_JSON_ATTR_AGENTS) != agentsConfigJson.end()) {
+ json agentsJson = agentsConfigJson[VA_JSON_ATTR_AGENTS];
+ for (auto agentIt = agentsJson.begin(); agentIt != agentsJson.end(); ++agentIt) {
+ json agentJson = *agentIt;
+ parseVoiceAgentConfig(agentJson);
+ }
}
- // Set the default agent.
- if (agentsConfigJson.find(VA_JSON_ATTR_DEFAULT) == agentsConfigJson.end()) {
- sLogger->log(Level::ERROR, TAG, "loadVoiceAgentsConfig: No default agent found in agents json");
+ // Save the default agent if specified
+ if (agentsConfigJson.find(VA_JSON_ATTR_DEFAULT) != agentsConfigJson.end()) {
+ defaultVoiceAgent = agentsConfigJson[VA_JSON_ATTR_DEFAULT].get<string>();
+ }
+}
+
+CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: Voice service not initialized.");
return -1;
}
- std::string defaultAgentId(agentsConfigJson[VA_JSON_ATTR_DEFAULT].get<string>());
- sVoiceAgentsDataManager->setDefaultVoiceAgent(defaultAgentId);
+ string defaultVoiceAgent;
+
+ if (argsJ != nullptr) {
+ // Parse any agent configs from controller arguments
+ json agentsConfigJson = json::parse(json_object_to_json_string(argsJ));
+ parseVoiceAgentsConfig(agentsConfigJson, defaultVoiceAgent);
+ }
+
+ // Load external configuration if present
+ filesystem::path agentsConfigFile = CONFIG_JSON_FILE;
+ if(filesystem::exists(agentsConfigFile)) {
+ stringstream message;
+ message << "loadVoiceAgentsConfig: reading " << agentsConfigFile.string();;
+ sLogger->log(Level::INFO, TAG, message.str().c_str());
+
+ ifstream agentsConfigJsonFile(agentsConfigFile.string());
+ json agentsConfigJson;
+ agentsConfigJsonFile >> agentsConfigJson;
+ parseVoiceAgentsConfig(agentsConfigJson, defaultVoiceAgent);
+ }
+
+ // Load any external individual voiceagent configurations
+ error_code ec;
+ for (const auto& entry : filesystem::directory_iterator(VA_CONFIG_JSON_DIR, ec)) {
+ if (entry.is_regular_file(ec) &&
+ entry.path().extension().string() == JSON_EXT) {
+ const auto filename = entry.path().string();
+
+ stringstream message;
+ message << "loadVoiceAgentsConfig: reading " << filename;
+ sLogger->log(Level::INFO, TAG, message.str().c_str());
+
+ ifstream jsonFile(filename);
+ json agentJson;
+ jsonFile >> agentJson;
+ parseVoiceAgentConfig(agentJson);
+ }
+ }
+
+ // Set the default agent if specified
+ if (!defaultVoiceAgent.empty()) {
+ stringstream message;
+ message << "loadVoiceAgentsConfig: setting default agent to " << defaultVoiceAgent;
+ sLogger->log(Level::INFO, TAG, message.str().c_str());
+
+ sVoiceAgentsDataManager->setDefaultVoiceAgent(defaultVoiceAgent);
+ } else {
+ sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: No default agent found");
+ }
+
+ // Always return zero so service will start
return 0;
}