// // Created by stephane bourque on 2021-09-07. // #include "APConfig.h" #include "StorageService.h" namespace OpenWifi { APConfig::APConfig(const std::string &SerialNumber, const std::string &DeviceType, Poco::Logger &L, bool Explain) : SerialNumber_(SerialNumber), DeviceType_(DeviceType), Logger_(L), Explain_(Explain) { } APConfig::APConfig(const std::string & SerialNumber, Poco::Logger & L) : SerialNumber_(SerialNumber), Logger_(L) { Explain_ = false; Sub_ = true; } bool APConfig::FindRadio(const std::string &Band, const Poco::JSON::Array::Ptr &Arr, Poco::JSON::Object::Ptr & Radio) { for(const auto &i:*Arr) { auto R = i.extract(); if(R->has("band") && R->get("band").toString()==Band) { Radio = R; return true; } } return false; } bool APConfig::RemoveBand(const std::string &Band, const Poco::JSON::Array::Ptr &A_in,Poco::JSON::Array::Ptr &A_Out) { for(const auto &i:*A_in) { auto R = i.extract(); if(R->has("band") && R->get("band").toString()==Band) { } else { A_Out->add(i); } } return false; } [[maybe_unused ]] static void ShowJSON([[maybe_unused]] const char *S, [[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) { /* std::stringstream O; Poco::JSON::Stringifier::stringify(Obj,O); std::cout << S << ":" << std::endl; std::cout << ">>>" << std::endl << O.str() << std::endl << "<<<" << std::endl; */ } bool APConfig::ReplaceVariablesInObject( const Poco::JSON::Object::Ptr & Original, Poco::JSON::Object::Ptr & Result) { // get all the names and expand auto Names = Original->getNames(); for(const auto &i:Names) { if(i=="__variableBlock") { if(Original->isArray(i)) { auto UUIDs = Original->getArray(i); for(const auto &uuid:*UUIDs) { ProvObjects::VariableBlock VB; if(StorageService()->VariablesDB().GetRecord("id", uuid, VB)) { for(const auto &var:VB.variables) { Poco::JSON::Parser P; auto VariableBlockInfo = P.parse(var.value).extract(); auto VarNames = VariableBlockInfo->getNames(); for(const auto &j:VarNames) { Result->set(j,VariableBlockInfo->get(j)); } } } } } } else if(Original->isArray(i)) { auto Arr = Poco::makeShared(); auto Obj = Original->getArray(i); ReplaceVariablesInArray(Obj,Arr); Result->set(i,Arr); } else if (Original->isObject(i)) { auto Expanded = Poco::makeShared(); auto Obj = Original->getObject(i); ReplaceVariablesInObject(Obj,Expanded); Result->set(i,Expanded); } else { Result->set(i,Original->get(i)); } } return true; } bool APConfig::ReplaceVariablesInArray( const Poco::JSON::Array::Ptr & Original, Poco::JSON::Array::Ptr & ResultArray) { for(const auto &element:*Original) { if(element.isArray()) { auto Expanded = Poco::makeShared(); const auto & Object = element.extract(); ReplaceVariablesInArray(Object,Expanded); ResultArray->add(Expanded); } else if(element.isStruct()) { auto Expanded = Poco::makeShared(); const auto & Object = element.extract(); ReplaceVariablesInObject(Object,Expanded); ResultArray->add(Expanded); } else if( element.isString() || element.isNumeric() || element.isBoolean() || element.isInteger() || element.isSigned() ) { ResultArray->add(element); } else { auto Expanded = Poco::makeShared(); const auto & Object = element.extract(); ReplaceVariablesInObject(Object,Expanded); ResultArray->add(Expanded); } } return true; } bool APConfig::Get(Poco::JSON::Object::Ptr & Configuration) { if(Config_.empty()) { Explanation_.clear(); try { if(!Sub_) { ProvObjects::InventoryTag D; if (StorageService()->InventoryDB().GetRecord("serialNumber", SerialNumber_, D)) { if (!D.deviceConfiguration.empty()) { AddConfiguration(D.deviceConfiguration); } if (!D.entity.empty()) { AddEntityConfig(D.entity); } else if (!D.venue.empty()) { AddVenueConfig(D.venue); } } } else { ProvObjects::SubscriberDevice D; if (StorageService()->SubscriberDeviceDB().GetRecord("serialNumber", SerialNumber_, D)) { if (!D.configuration.empty()) { AddConfiguration(D.configuration); } } } // Now we have all the config we need. } catch (const Poco::Exception &E ) { Logger_.log(E); } } std::set Sections; for(const auto &i:Config_) { Poco::JSON::Parser P; auto O = P.parse(i.element.configuration).extract(); auto Names = O->getNames(); for(const auto &SectionName:Names) { auto InsertInfo = Sections.insert(SectionName); if (InsertInfo.second) { if (O->isArray(SectionName)) { auto OriginalArray = O->getArray(SectionName); if (Explain_) { Poco::JSON::Object ExObj; ExObj.set("from-uuid", i.info.id); ExObj.set("from-name", i.info.name); ExObj.set("action", "added"); ExObj.set("element", OriginalArray); Explanation_.add(ExObj); } auto ExpandedArray = Poco::makeShared(); ReplaceVariablesInArray(OriginalArray, ExpandedArray); Configuration->set(SectionName, ExpandedArray); } else if (O->isObject(SectionName)) { auto OriginalSection = O->get(SectionName).extract(); if (Explain_) { Poco::JSON::Object ExObj; ExObj.set("from-uuid", i.info.id); ExObj.set("from-name", i.info.name); ExObj.set("action", "added"); ExObj.set("element", OriginalSection); Explanation_.add(ExObj); } auto ExpandedSection = Poco::makeShared(); ReplaceVariablesInObject(OriginalSection, ExpandedSection); Configuration->set(SectionName, ExpandedSection); } else { std::cout << " --- unknown element type --- " << O->get(SectionName).toString() << std::endl; } } else { if (Explain_) { Poco::JSON::Object ExObj; ExObj.set("from-uuid", i.info.id); ExObj.set("from-name", i.info.name); ExObj.set("action", "ignored"); ExObj.set("reason", "weight insufficient"); ExObj.set("element", O->get(SectionName)); Explanation_.add(ExObj); } } } } if(Config_.empty()) return false; return true; } static bool DeviceTypeMatch(const std::string &DeviceType, const Types::StringVec & Types) { for(const auto &i:Types) { if(i=="*" || Poco::icompare(DeviceType,i)==0) return true; } return false; } void APConfig::AddConfiguration(const ProvObjects::DeviceConfigurationElementVec &Elements) { for(const auto &i:Elements) { if(i.weight==0) { VerboseElement VE{ .element = i, .info = ProvObjects::ObjectInfo{} }; Config_.push_back(VE); } else { // we need to insert after everything bigger or equal auto Hint = std::lower_bound(Config_.cbegin(),Config_.cend(),i.weight, [](const VerboseElement &Elem, uint64_t Value) { return Elem.element.weight>=Value; }); VerboseElement VE{ .element = i, .info = ProvObjects::ObjectInfo{}}; Config_.insert(Hint,VE); } } } void APConfig::AddConfiguration(const Types::UUIDvec_t &UUIDs) { for(const auto &i:UUIDs) AddConfiguration(i); } void APConfig::AddConfiguration(const std::string &UUID) { if(UUID.empty()) return; ProvObjects::DeviceConfiguration Config; if(StorageService()->ConfigurationDB().GetRecord("id", UUID, Config)) { if(!Config.configuration.empty()) { if(DeviceTypeMatch(DeviceType_,Config.deviceTypes)) { for(const auto &i:Config.configuration) { if(i.weight==0) { VerboseElement VE{ .element = i, .info = Config.info}; Config_.push_back(VE); } else { // we need to insert after everything bigger or equal auto Hint = std::lower_bound(Config_.cbegin(),Config_.cend(),i.weight, [](const VerboseElement &Elem, uint64_t Value) { return Elem.element.weight>=Value; }); VerboseElement VE{ .element = i, .info = Config.info}; Config_.insert(Hint,VE); } } } else { Poco::JSON::Object ExObj; ExObj.set("from-uuid", Config.info.id); ExObj.set("from-name",Config.info.name ); ExObj.set("action", "ignored"); ExObj.set("reason", "deviceType mismatch"); Explanation_.add(ExObj); } } } } void APConfig::AddEntityConfig(const std::string &UUID) { ProvObjects::Entity E; if(StorageService()->EntityDB().GetRecord("id",UUID,E)) { AddConfiguration(E.configurations); if(!E.parent.empty()) { AddEntityConfig(E.parent); } } else { } } void APConfig::AddVenueConfig(const std::string &UUID) { ProvObjects::Venue V; if(StorageService()->VenueDB().GetRecord("id",UUID,V)) { AddConfiguration(V.configurations); if(!V.entity.empty()) { AddEntityConfig(V.entity); } else if(!V.parent.empty()) { AddVenueConfig(V.parent); } } else { } } }