From bbec87d314f5b706afda260d04f3180b1f848d6b Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 19 Feb 2014 20:50:17 +0100 Subject: More work on prolog datamodel --- CMakeLists.txt | 1 + src/uscxml/Message.cpp | 34 ++++++ src/uscxml/Message.h | 2 + .../plugins/datamodel/prolog/swi/SWIDataModel.cpp | 116 +++++++++++++----- .../plugins/datamodel/prolog/swi/SWIDataModel.h | 7 +- .../plugins/invoker/umundo/UmundoInvoker.cpp | 129 +++++++++++---------- 6 files changed, 195 insertions(+), 94 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0377bf..facfcd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -734,6 +734,7 @@ if (WIN32) find_package(UMUNDO COMPONENTS convenience) else() find_package(UMUNDO COMPONENTS rpc serial core) + # find_package(UMUNDO COMPONENTS convenience) endif() if (UMUNDO_FOUND) include_directories (${UMUNDO_INCLUDE_DIR}) diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index bbd7f28..e8ae98c 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -123,6 +123,40 @@ Data::Data(const char* _data, size_t _size, const std::string& mimeType, bool ad binary = boost::shared_ptr(new Blob((void*)_data, _size, mimeType, adopt)); } +void Data::merge(const Data& other) { + if (other.compound.size() > 0) { + if (compound.size() == 0) { + compound = other.compound; + } else { + std::map::const_iterator compIter = other.compound.begin(); + while (compIter != other.compound.end()) { + if (compound.find(compIter->first) != compound.end()) { + // we do have the same key, merge + compound[compIter->first].merge(compIter->second); + } else { + compound[compIter->first] = compIter->second; + } + compIter++; + } + } + } + if (other.array.size() > 0) { + if (array.size() == 0) { + array = other.array; + } else { + std::list::const_iterator arrIter = other.array.begin(); + while(arrIter != other.array.end()) { + array.push_back(*arrIter); + arrIter++; + } + } + } + if (other.atom.size() > 0) { + atom = other.atom; + type = other.type; + } +} + Data::Data(const Arabica::DOM::Node& dom) { // we may need to convert some keys to arrays if we have the same name as an element std::map > arrays; diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index 242547a..bdb9498 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -98,6 +98,8 @@ public: return operator[](key.c_str()); } + void merge(const Data& other); + const Data operator[](const char* key) const { if (hasKey(key)) return compound.at(key); diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp index 78b6a6f..23c6e1a 100644 --- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp +++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp @@ -291,6 +291,19 @@ void SWIDataModel::setEvent(const Event& event) { assertFromData(event.data, "event(data(", 2); } + Event::params_t::const_iterator paramIter = event.params.begin(); + while(paramIter != event.params.end()) { + assertFromData(paramIter->second, "event(param(" + paramIter->first + "(", 3); + paramIter++; + } + + Event::namelist_t::const_iterator namelistIter = event.namelist.begin(); + while(namelistIter != event.namelist.end()) { + assertFromData(paramIter->second, "event(param(" + namelistIter->first + "(", 3); + namelistIter++; + } + +#if 0 // event.params size_t uniqueKeys = 0; Event::params_t::const_iterator paramIter = event.params.begin(); @@ -321,6 +334,7 @@ void SWIDataModel::setEvent(const Event& event) { paramIter = lastValueIter; } } +#endif } RETHROW_PLEX_AS_EVENT; } @@ -398,26 +412,71 @@ void SWIDataModel::assertFromData(const Data& data, const std::string& expr, siz } } +#if 0 + std::list SWIDataModel::getSolutions(PlCompound compound) { + std::list solutions; + + PlTermv termv(compound.arity()); + for (int i = 0; i < compound.arity(); i++) { + termv[i] = compound[i + 1]; + } + PlQuery query(compound.name(), termv); + while(query.next_solution()) { +// std::cout << (char*)compound << std::endl; + solutions.push_back(compound); + } + return solutions; + } +#endif + Data SWIDataModel::getStringAsData(const std::string& content) { SET_PL_CONTEXT try { - PlTerm term(content.c_str()); - return(termAsData(term)); + PlCompound compound(content.c_str()); + PlCompound orig(content.c_str()); + Data data; + + PlTermv termv(compound.arity()); + for (int i = 0; i < compound.arity(); i++) { + termv[i] = compound[i + 1]; + } + PlQuery query(compound.name(), termv); + + while(query.next_solution()) { + std::map vars = resolveAtoms(compound, orig); + std::map::const_iterator varIter = vars.begin(); + + while(varIter != vars.end()) { + data.merge(termAsData(varIter->second)); + varIter++; + } + + } +// std::cout << Data::toJSON(data) << std::endl; + return data; + } catch (PlException plex) { + try { + // could not parse as compound, try term and type in termAsData + PlTerm term(content.c_str()); + return(termAsData(term)); + } + RETHROW_PLEX_AS_EVENT } - RETHROW_PLEX_AS_EVENT + return Data(); } Data SWIDataModel::termAsData(PlTerm term) { + Data data; - // std::cout << term.name() << (char*)term << std::endl; switch (term.type()) { case PL_TERM: for (int i = 1; i <= term.arity(); i++) { // arguments start at 1 - data.compound[term.name()].array.push_back(termAsData(term[i])); + data.compound[term.name()] = termAsData(term[i]); } break; + case PL_VARIABLE: case PL_INTEGER: case PL_FLOAT: case PL_SHORT: @@ -427,6 +486,7 @@ Data SWIDataModel::termAsData(PlTerm term) { data.atom = std::string(term); data.type = Data::INTERPRETED; break; + case PL_STRING: case PL_ATOM: data.atom = std::string(term); data.type = Data::VERBATIM; @@ -643,15 +703,16 @@ bool SWIDataModel::evalAsBool(const Arabica::DOM::Node& node, const return false; } } - + std::string SWIDataModel::evalAsString(const std::string& expr) { SET_PL_CONTEXT try { - PlCompound orig(expr.c_str()); // keep the original to find variables PlCompound compound(expr.c_str()); - + if (strlen(compound.name())) { + PlCompound orig(expr.c_str()); + PlTermv termv(compound.arity()); for (int i = 0; i < compound.arity(); i++) { termv[i] = compound[i + 1]; @@ -691,32 +752,33 @@ std::string SWIDataModel::evalAsString(const std::string& expr) { } } + // this is similar to http://etalis.googlecode.com/svn/eEtalis/src/term.c std::map SWIDataModel::resolveAtoms(PlTerm& term, PlTerm& orig) { SET_PL_CONTEXT try { std::map atoms; switch (orig.type()) { - case PL_VARIABLE: { - atoms[(char *)orig] = term; - break; - } - case PL_ATOM: - break; - case PL_STRING: - break; - case PL_INTEGER: - break; - case PL_TERM: - for (int i = 1; i <= orig.arity(); i++) { - PlTerm newTerm = term[i]; - PlTerm newOrig = orig[i]; - std::map result = resolveAtoms(newTerm, newOrig); - atoms.insert(result.begin(), result.end()); + case PL_VARIABLE: { + atoms[(char *)orig] = term; + break; } - break; - default: - LOG(ERROR) << "Resolving variable of unknown type in query solution"; + case PL_ATOM: + break; + case PL_STRING: + break; + case PL_INTEGER: + break; + case PL_TERM: + for (int i = 1; i <= orig.arity(); i++) { + PlTerm newTerm = term[i]; + PlTerm newOrig = orig[i]; + std::map result = resolveAtoms(newTerm, newOrig); + atoms.insert(result.begin(), result.end()); + } + break; + default: + LOG(ERROR) << "Resolving variable of unknown type in query solution"; } return atoms; } diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h index 01d3556..75e2d74 100644 --- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h +++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h @@ -78,20 +78,21 @@ public: virtual bool isDeclared(const std::string& expr); virtual Data getStringAsData(const std::string& content); - + virtual std::string evalAsString(const std::string& expr); virtual bool evalAsBool(const Arabica::DOM::Node& node, const std::string& expr); virtual bool evalAsBool(const std::string& expr); static foreign_t inPredicate(term_t a0, int arity, void* context); protected: - std::map resolveAtoms(PlTerm& term, PlTerm& orig); void assertFromData(const Data& data, const std::string& expr, size_t nesting); static Data termAsData(PlTerm term); static PlTerm dataAsTerm(Data data); - +// virtual std::list getSolutions(PlCompound compound); + virtual std::map resolveAtoms(PlTerm& term, PlTerm& orig); + static int dictCallBack(term_t key, term_t value, int last, void *closure); static PL_blob_t blobType; diff --git a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp index 1861db7..a6a2df3 100644 --- a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp +++ b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp @@ -73,79 +73,80 @@ void UmundoInvoker::send(const SendRequest& req) { } else { msg.putMeta("event", "umundo"); } + + try { + Data data = req.data; + + if (!data && req.content.length()) + data = _interpreter->getDataModel().getStringAsData(req.content); + + if (!data) { + LOG(ERROR) << "Cannot transform content to data object per datamodel or no data given"; + return; + } + +// std::cout << Data::toJSON(data) << std::endl; + + std::string type; + if (req.params.find("type") != req.params.end()) { + // we are supposed to build a typed object + type = req.params.find("type")->second.atom; - if (req.content.length()) { - try { - Data data = _interpreter->getDataModel().getStringAsData(req.content); - if (!data) { - LOG(ERROR) << "Cannot transform content to data object per datamodel"; + const google::protobuf::Message* protoMsg = umundo::PBSerializer::getProto(type); + if (protoMsg == NULL) { + LOG(ERROR) << "No type '" << type << "' is known, pass a directory with proto .desc files via types param when invoking"; return; } - std::string type; - if (req.params.find("type") != req.params.end()) { - // we are supposed to build a typed object - type = req.params.find("type")->second.atom; - - const google::protobuf::Message* protoMsg = umundo::PBSerializer::getProto(type); - if (protoMsg == NULL) { - LOG(ERROR) << "No type '" << type << "' is known, pass a directory with proto .desc files via types param when invoking"; - return; - } - - google::protobuf::Message* pbMsg = protoMsg->New(); - if (!dataToProtobuf(pbMsg, data)) { - LOG(ERROR) << "Cannot create message from JSON - not sending"; - return; - } + google::protobuf::Message* pbMsg = protoMsg->New(); + if (!dataToProtobuf(pbMsg, data)) { + LOG(ERROR) << "Cannot create message from JSON - not sending"; + return; + } - if (!_isService) { - // add all s11n properties - _pub->prepareMsg(&msg, type, pbMsg); - _pub->send(&msg); - } else { - // invoke as service - std::map::iterator svcIter = _svcs.begin(); - while(svcIter != _svcs.end()) { - umundo::ServiceStub* stub = svcIter->second; - Event event; - void* rv = NULL; - stub->callStubMethod(req.name, pbMsg, type, rv, ""); - protobufToData(event.data, *(const google::protobuf::Message*)rv); - - event.name = _invokeId + ".reply." + req.name; - event.origin = msg.getMeta("um.channel"); - event.origintype = "umundo"; - event.eventType = Event::EXTERNAL; - - returnEvent(event); - svcIter++; - } - } + if (!_isService) { + // add all s11n properties + _pub->prepareMsg(&msg, type, pbMsg); + _pub->send(&msg); } else { - // just encode JSON - JSONProto* jsonProtoMsg = new JSONProto(); - if (!dataToJSONbuf(jsonProtoMsg, data)) { - LOG(ERROR) << "Cannot create message from JSON - not sending"; - return; - } - - if (!_isService) { - // add all s11n properties - _pub->prepareMsg(&msg, "JSON", jsonProtoMsg); - _pub->send(&msg); - } else { - LOG(ERROR) << "Cannot invoke services with untyped JSON"; - return; + // invoke as service + std::map::iterator svcIter = _svcs.begin(); + while(svcIter != _svcs.end()) { + umundo::ServiceStub* stub = svcIter->second; + Event event; + void* rv = NULL; + stub->callStubMethod(req.name, pbMsg, type, rv, ""); + protobufToData(event.data, *(const google::protobuf::Message*)rv); + + event.name = _invokeId + ".reply." + req.name; + event.origin = msg.getMeta("um.channel"); + event.origintype = "umundo"; + event.eventType = Event::EXTERNAL; + + returnEvent(event); + svcIter++; } + } + } else { + // just encode JSON + JSONProto* jsonProtoMsg = new JSONProto(); + if (!dataToJSONbuf(jsonProtoMsg, data)) { + LOG(ERROR) << "Cannot create message from JSON - not sending"; + return; + } + if (!_isService) { + // add all s11n properties + _pub->prepareMsg(&msg, "JSON", jsonProtoMsg); + _pub->send(&msg); + } else { + LOG(ERROR) << "Cannot invoke services with untyped JSON"; + return; } - } catch (Event e) { - LOG(ERROR) << "Syntax error when invoking umundo:" << std::endl << e << std::endl; - return; + } - } else { - LOG(ERROR) << "Required JSON object in content" << std::endl; + } catch (Event e) { + LOG(ERROR) << "Syntax error when invoking umundo:" << std::endl << e << std::endl; return; } } @@ -534,7 +535,7 @@ bool UmundoInvoker::dataToProtobuf(google::protobuf::Message* msg, Data& data) { if (data.compound.find(key) == data.compound.end()) { if (fieldDesc->is_required()) { - LOG(ERROR) << "required field " << key << " not given in JSON"; + LOG(ERROR) << "required field " << key << " not given"; return false; } continue; -- cgit v0.12