diff options
Diffstat (limited to 'src/plugins/VshlCoreApi.cpp')
-rw-r--r-- | src/plugins/VshlCoreApi.cpp | 232 |
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; } |