diff options
author | Kevron Rees <tripzero.kev@gmail.com> | 2015-01-14 15:36:24 -0800 |
---|---|---|
committer | Kevron Rees <tripzero.kev@gmail.com> | 2015-01-14 15:36:24 -0800 |
commit | 89216a903083efa20969ba4f47c6560b2d9773a8 (patch) | |
tree | 080a9993f5c9fbf704f5d7e3ae6e1cb5c9c55b26 | |
parent | bf4ba5f6aff0d12b1b1524b452f9743bc284298b (diff) | |
parent | acfe65e70189b99b37f6c0a4d00ff8915de6a3be (diff) | |
download | automotive-message-broker-89216a903083efa20969ba4f47c6560b2d9773a8.tar.gz |
Merge pull request #42 from tripzero/master
0.13.801
96 files changed, 2224 insertions, 1332 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e95bab52..b8c57dfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,8 @@ include(CMakeDependentOption) set(PROJECT_NAME "automotive-message-broker") set(PROJECT_PRETTY_NAME "Automotive Message Broker") -set(PROJECT_VERSION "0.13.800") -set(PROJECT_CODENAME "") +set(PROJECT_VERSION "0.13.801") +set(PROJECT_CODENAME "74A") set(PROJECT_QUALITY "alpha") add_definitions(-DPROJECT_VERSION="${PROJECT_VERSION}") @@ -21,7 +21,8 @@ set (LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) set (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/${CMAKE_LIBRARY_ARCHITECTURE}" ) set (PLUGIN_INSTALL_PATH "${LIB_INSTALL_DIR}/${PROJECT_NAME}") set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include") -set (DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/packages/${PROJECT_NAME}") +set (DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/packages/${PROJECT_NAME}/${PROJECT_VERSION}") +set (PLUGIN_SEGMENT_INSTALL_PATH "/etc/ambd/plugins.d") option(qtmainloop "Use QCoreApplication mainloop " OFF) option(websocket_plugin "websocket source and sink plugins" OFF) @@ -47,11 +48,6 @@ set(XWALK_EXTENSION_PATH "/automotive-message-broker/xwalk" CACHE PATH "director set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpie -pie -std=c++1y") -if(opencvlux_plugin) - message(STATUS "OpenCV Lux plugin enabled") - -endif(opencvlux_plugin) - include (CMakeForceCompiler) if (enable_icecc) @@ -86,6 +82,8 @@ add_custom_target(dist COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD | bzip configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/RELEASE.in.md" "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE.md" IMMEDIATE @ONLY) + # packaging stuff: # Common things to every type of package SET(CPACK_PACKAGE_DESCRIPTION "daemon that provides access vehicle data") diff --git a/README b/README deleted file mode 100644 index 90916606..00000000 --- a/README +++ /dev/null @@ -1,80 +0,0 @@ -Automotive Message Broker is a vehicle network abstraction system. It brokers information from the vehicle -to applications. It provides application with a rich API for accessing vehicle data. - -Automotive Message Broker is built using CMake and requires libltdl (libtool), libjson-c, and boost packages. - - -About the Git Tree: -master is expected to be unstable and may not even compile. If you want something more stable, checkout one of the -release branches (ie, 0.9.0, 0.10, etc) - - -To build: - -cd automotive-message-broker -mkdir build -cd build -cmake .. -make - -To install: - -sudo make install - -To run: - -ambd - -ambd can load different plugins. The config file specifies what plugins to use. The default config located in -/etc/ambd/config. You can change this or use your own config to have ambd use your own plugins. For example: - -# copy the config to your own config -cp /etc/ambd/config myconfig - -# edit myconfig and specify the path to your plugin: -# change the line: -"sources" : [ { "path" : "../plugins/examplesourceplugin.so" } ], -# to: -"sources" : [ { "path" : "/path/to/mysourceplugin.so" } ], - -Now you can run ambd with: - -ambd -c /path/to/myconfig - -also see ambd -h - -Typically, AMB will be used with the DBus plugin. The DBus plugin automatically makes internal AMB properties -available over DBus. See the DBus plugin documentation for more information (plugins/dbus/README). - - -Running with other plugins - -To learn about running AMB with other plugins, please see the plugins/*/README. - - -Running with the Qt mainloop: - -Some source and sink plugins may want to use the Qt-based mainloop to take advantage of Qt features. To enable -the Qt mainloop, run cmake with -Duse_qtcore=On: - -cmake .. -Dqtmainloop=On - -You will also need to edit your config to enable the Qt-based mainloop: - -{ - "mainloop" : "/usr/lib/automotive-message-broker/qtmainloopplugin.so", - "sources" : [...], - "sinks" : [...] -} - -NOTE: by default the glib mainloop will be used. - - - -Questions/Issues/Comments: - -Questions or Comments can be emailed to: -tripzero.kev@gmail.com - -Issues can be submitted on our github page: -https://github.com/otcshare/automotive-message-broker/issues diff --git a/README.md b/README.md new file mode 100644 index 00000000..96f46239 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Automotive Message Broker + +Version 0.13.801 + +## Introduction + +Automotive Message Broker is a vehicle network abstraction system. It brokers information from the vehicle +to applications. It provides application with a rich API for accessing vehicle data. + +Automotive Message Broker is built using CMake and requires libltdl (libtool), libjson-c, and boost packages. + +## Git +About the Git Tree: +master is expected to be unstable and may not even compile. If you want something more stable, checkout one of the +release branches (ie, 0.9.0, 0.10, etc) + +## Building + +To build: + +~~~~~~~~~~~~~{.bash} +cd automotive-message-broker +mkdir build +cd build +cmake .. +make +~~~~~~~~~~~~~ + +## Installing + +To install: + +~~~~~~~~~~~~~{.bash} +sudo make install +~~~~~~~~~~~~~ + +## Running +To run: + +~~~~~~~~~~~~~{.bash} +ambd +~~~~~~~~~~~~~ + +*also see **ambd -h** for additional command line options* + +## Configuration + +For information on ambd's configuration, please see ambd-configuration.idl. + +## Running with the Qt mainloop: + +Some source and sink plugins may want to use the Qt-based mainloop to take advantage of Qt features. To enable +the Qt mainloop, run cmake with -Duse_qtcore=On: + +~~~~~~~~~~~~~{.bash} +cmake .. -Dqtmainloop=On +~~~~~~~~~~~~~ + +You will also need to edit your config to enable the Qt-based mainloop: + +~~~~~~~~~~~~~{.json} +{ + "mainloop" : "/usr/lib/i386-linux-gnu/automotive-message-broker/qtmainloopplugin.so", + "plugins" : "/etc/ambd/plugins.d" +} +~~~~~~~~~~~~~ + +NOTE: by default the glib mainloop will be used. + +## Questions/Issues/Comments: + +Questions or Comments can be emailed to the amb mailing list: +**amb at lists.01.org** + +Issues and Feature requests can be submitted on our github page: +https://github.com/otcshare/automotive-message-broker/issues diff --git a/RELEASE b/RELEASE deleted file mode 100644 index 6caafcf3..00000000 --- a/RELEASE +++ /dev/null @@ -1,12 +0,0 @@ -Release notes for release 0.14 - -New features: -- [DBus] some classes for exporting custom interfaces moved to plugins-common -- [Bluemonkey plugin] support for creating custom dbus interfaces: bluemonkey.exportInterface() - -Changes: -- Plugin create() method signature changed. -- Removed AbstractSinkManager class -- Removed deprecated dbus interfaces - -Fixes: diff --git a/RELEASE.in.md b/RELEASE.in.md new file mode 100644 index 00000000..90b5de00 --- /dev/null +++ b/RELEASE.in.md @@ -0,0 +1,20 @@ +AMB Release Notes +Version: @PROJECT_VERSION@ + +New features: +- [DBus] some classes for exporting custom interfaces moved to plugins-common +- [Bluemonkey plugin] support for creating custom dbus interfaces: bluemonkey.exportInterface() +- [ambctl] support for enabling disabling plugins +- [ambd] support plugins.d - now plugin segments can be stuck in there and amb will try to load all plugins that + are enabled. See the example segments: /etc/ambd/plugins.d/dbus, examplesink, examplesource +- [xwalk] vehicle extension now dynamically exports all AMB supported objects + +Changes: +- Plugin create() method signature changed. +- Removed AbstractSinkManager class +- Removed deprecated dbus interfaces +- Removed deprecated 'FooChanged' signal introspection description +- Deprecated 'GetFoo' method. This doesn't contain much more information that cannot already be + obtained through the Properties interface. + +Fixes: diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..73584fa5 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,20 @@ +AMB Release Notes +Version: 0.13.801 + +New features: +- [DBus] some classes for exporting custom interfaces moved to plugins-common +- [Bluemonkey plugin] support for creating custom dbus interfaces: bluemonkey.exportInterface() +- [ambctl] support for enabling disabling plugins +- [ambd] support plugins.d - now plugin segments can be stuck in there and amb will try to load all plugins that + are enabled. See the example segments: /etc/ambd/plugins.d/dbus, examplesink, examplesource +- [xwalk] vehicle extension now dynamically exports all AMB supported objects + +Changes: +- Plugin create() method signature changed. +- Removed AbstractSinkManager class +- Removed deprecated dbus interfaces +- Removed deprecated 'FooChanged' signal introspection description +- Deprecated 'GetFoo' method. This doesn't contain much more information that cannot already be + obtained through the Properties interface. + +Fixes: @@ -1,10 +1,4 @@ - - Refactor obd2 plugin with AsyncQueue. Use thread safe-update property. -- update json protocol to support rangerequests with PropertyList instead of a singel property (Verify) - source export from database plugin does not reflect the source in the database -- no reason for pluginloader to track sources. core already does it. -- create docs for all plugins (README) -- handle badly formed messages properly (ie not crash) in websocketsink - grep all the TODOs in the code and do them -- investigate and enable use of provisioning in ssl websockets -- enable ambd/plugins.d/ + diff --git a/ambd/CMakeLists.txt b/ambd/CMakeLists.txt index d1af5653..70091458 100644 --- a/ambd/CMakeLists.txt +++ b/ambd/CMakeLists.txt @@ -34,8 +34,7 @@ add_executable(ambd ${ambd_sources}) include_directories(${include_dirs} ) target_link_libraries(ambd ${link_libraries} dl amb) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.in ${CMAKE_CURRENT_BINARY_DIR}/config @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.in.json ${CMAKE_CURRENT_BINARY_DIR}/config @ONLY) install (TARGETS ambd RUNTIME DESTINATION bin) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/config DESTINATION /etc/ambd) - diff --git a/ambd/config.in b/ambd/config.in deleted file mode 100644 index 850ec786..00000000 --- a/ambd/config.in +++ /dev/null @@ -1,19 +0,0 @@ -{ - "sources" : [ - { - "name" : "ExampleSouce", - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" - } - ], - "sinks": [ - { - "name" : "ExampleSink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - }, - { - "name" : "DBusSink", - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - } - ] -} - diff --git a/ambd/config.in.json b/ambd/config.in.json new file mode 100644 index 00000000..1e17508a --- /dev/null +++ b/ambd/config.in.json @@ -0,0 +1,6 @@ +{ + "plugins" : "@PLUGIN_SEGMENT_INSTALL_PATH@", + "sources" : [ ], + "sinks": [ ] +} + diff --git a/ambd/pluginloader.cpp b/ambd/pluginloader.cpp index e38547e1..c1e58f1e 100644 --- a/ambd/pluginloader.cpp +++ b/ambd/pluginloader.cpp @@ -20,16 +20,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "pluginloader.h" #include "glibmainloop.h" #include "core.h" + #include <json.h> +#include <gio/gio.h> +#include <picojson.h> + #include <iostream> #include <stdexcept> #include <boost/concept_check.hpp> std::string get_file_contents(const char *filename) { - //FILE *in = fopen(filename,"r"); - std::ifstream in(filename, std::ios::in); + if(!in.is_open()) + { + DebugOut(DebugOut::Error) << "Failed to open file: " << filename << endl; + return ""; + } + std::string output; std::string line; while(in.good()) @@ -43,36 +51,21 @@ std::string get_file_contents(const char *filename) PluginLoader::PluginLoader(string configFile, int argc, char** argv): f_create(NULL), routingEngine(nullptr), mMainLoop(nullptr) { DebugOut()<<"Loading config file: "<<configFile<<endl; - json_object *rootobject; - json_tokener *tokener = json_tokener_new(); std::string configBuffer = get_file_contents(configFile.c_str()); - if(configBuffer == "") - { - throw std::runtime_error("No config or config empty"); - } - enum json_tokener_error err; - do - { - rootobject = json_tokener_parse_ex(tokener, configBuffer.c_str(),configBuffer.length()); - } while ((err = json_tokener_get_error(tokener)) == json_tokener_continue); - if (err != json_tokener_success) - { - fprintf(stderr, "Error: %s\n", json_tokener_error_desc(err)); - // Handle errors, as appropriate for your application. - throw std::runtime_error("Invalid config"); - } - if (tokener->char_offset < configFile.length()) // XXX shouldn't access internal fields + + std::string picojsonerr = ""; + picojson::value v; + picojson::parse(v, configBuffer.begin(), configBuffer.end(), &picojsonerr); + + if(!picojsonerr.empty()) { - // Handle extra characters after parsed object as desired. - // e.g. issue an error, parse another object from that point, etc... + DebugOut(DebugOut::Error) << "Failed to parse main config! " << endl; + throw std::runtime_error("Error parsing config"); } - json_object *coreobject = json_object_object_get(rootobject,"routingEngine"); - if (coreobject) + if(v.contains("routingEngine")) { - /// there is a mainloop entry. Load the plugin: - - string restr = string(json_object_get_string(coreobject)); + string restr = v.get("routingEngine").to_str(); routingEngine = loadRoutingEngine(restr); @@ -90,46 +83,33 @@ PluginLoader::PluginLoader(string configFile, int argc, char** argv): f_create(N /// core wants some specific configuration settings: std::map<std::string,std::string> settings; - json_object *lpq = json_object_object_get(rootobject,"lowPriorityQueueSize"); - if (lpq) - { - /// there is a mainloop entry. Load the plugin: - - string restr = string(json_object_get_string(lpq)); - settings["lowPriorityQueueSize"] = restr; - } - - json_object *npq = json_object_object_get(rootobject,"normalPriorityQueueSize"); - if (npq) - { - /// there is a mainloop entry. Load the plugin: - - string restr = string(json_object_get_string(npq)); - settings["normalPriorityQueueSize"] = restr; - } - json_object *hpq = json_object_object_get(rootobject,"highPriorityQueueSize"); - if (hpq) + for (auto q : {"lowPriorityQueueSize", "normalPriorityQueueSize", "highPriorityQueueSize"}) { - /// there is a mainloop entry. Load the plugin: - - string restr = string(json_object_get_string(hpq)); - settings["highPriorityQueueSize"] = restr; + if (v.contains(q)) + { + string restr = v.get(q).to_str(); + settings[q] = restr; + } } routingEngine = new Core(settings); } + if(v.contains("plugins")) + { + std::string pluginsPath = v.get("plugins").to_str(); + scanPluginDir(pluginsPath); + } - json_object *mainloopobject = json_object_object_get(rootobject,"mainloop"); - if (mainloopobject) + if(v.contains("mainloop")) { /// there is a mainloop entry. Load the plugin: - string mainloopstr = string(json_object_get_string(mainloopobject)); + string mainloopstr = v.get("mainloop").to_str(); - mMainLoop = loadMainLoop(mainloopstr,argc, argv); + mMainLoop = loadMainLoop(mainloopstr, argc, argv); if(!mMainLoop) { @@ -140,110 +120,169 @@ PluginLoader::PluginLoader(string configFile, int argc, char** argv): f_create(N { /// there is no mainloop entry, use default glib DebugOut()<<"No mainloop specified in config. Using glib by default."<<endl; - mMainLoop = new GlibMainLoop(argc,argv); + mMainLoop = new GlibMainLoop(argc, argv); } - json_object *sourcesobject = json_object_object_get(rootobject,"sources"); - if(!sourcesobject) + for (auto q : {"sources", "sinks"}) { - DebugOut()<<"Error getting sources member: "<<endl; - throw std::runtime_error("Error getting sources member"); - } + if(v.contains("sources")) + { + picojson::array list = v.get(q).get<picojson::array>(); + if (!list.size()) + { + DebugOut() << "Error getting list for " << q << endl; + throw std::runtime_error("Error getting sources list"); + } - //g_assert(json_reader_is_array(reader)); - g_assert(json_object_get_type(sourcesobject)==json_type_array); + for(auto src : list) + { + std::map<std::string, std::string> configurationMap; + for( auto obj : src.get<picojson::object>()) + { + string valstr = obj.second.to_str(); + string key = obj.first; + DebugOut() << "plugin config key: " << key << "value:" << valstr << endl; - array_list *sourceslist = json_object_get_array(sourcesobject); - if (!sourceslist) - { - DebugOut() << "Error getting source list" << endl; - throw std::runtime_error("Error getting sources list"); - } + configurationMap[key] = valstr; + } - for(int i=0; i < array_list_length(sourceslist); i++) - { - json_object *obj = (json_object*)array_list_get_idx(sourceslist,i); //This is an object + string path = configurationMap["path"]; - std::map<std::string, std::string> configurationMap; - json_object_object_foreach(obj, key, val) - { - string valstr = json_object_get_string(val); - DebugOut() << "plugin config key: " << key << "value:" << valstr << endl; - configurationMap[key] = valstr; - } + if(!loadPlugin(path, configurationMap)) + DebugOut(DebugOut::Warning) << "Failed to load plugin: " << path <<endl; + } - string path = configurationMap["path"]; + } + } - if(!loadPlugin(path,configurationMap)) - DebugOut(DebugOut::Warning) << "Failed to load plugin: " << path <<endl; + Core* core = static_cast<Core*>(routingEngine); + if( core != nullptr ) + { + core->inspectSupported(); } +} - //json_object_put(sourcesobject); - ///read the sinks: +PluginLoader::~PluginLoader() +{ + for(auto handle : openHandles) + dlclose(handle); +} - json_object *sinksobject = json_object_object_get(rootobject,"sinks"); +IMainLoop *PluginLoader::mainloop() +{ + return mMainLoop; +} - if (!sinksobject) - { - DebugOut() << "Error getting sink object" << endl; - throw std::runtime_error("Error getting sink object"); - } +std::string PluginLoader::errorString() +{ + return mErrorString; +} + +void PluginLoader::scanPluginDir(const std::string & dir) +{ + DebugOut() << "Scanning plugin directory: " << dir << endl; - array_list *sinkslist = json_object_get_array(sinksobject); + auto pluginsDirectory = amb::make_gobject(g_file_new_for_path(dir.c_str())); + GError* enumerateError = nullptr; - if (!sinkslist) + auto enumerator = amb::make_gobject(g_file_enumerate_children(pluginsDirectory.get(), G_FILE_ATTRIBUTE_ID_FILE, + G_FILE_QUERY_INFO_NONE, nullptr, + &enumerateError)); + auto enumerateErrorPtr = amb::make_super(enumerateError); + + if(enumerateErrorPtr) { - DebugOut() << "Error getting sink list" << endl; - throw std::runtime_error("Error getting sink list"); + DebugOut(DebugOut::Error) << "Scanning plugin directory: " << enumerateErrorPtr->message << endl; + return; } - - for(int i=0; i < array_list_length(sinkslist); i++) + GError* errorGetFile = nullptr; + while(auto pluginConfig = amb::make_gobject(g_file_enumerator_next_file(enumerator.get(), nullptr, &errorGetFile))) { - json_object *obj = (json_object*)array_list_get_idx(sinkslist,i); + std::string name = g_file_info_get_name(pluginConfig.get()); + + DebugOut() << "Found file: " << name << endl; + std::string fullpath = dir + (boost::algorithm::ends_with(dir, "/") ? "":"/") + name; + std::string data = get_file_contents(fullpath.c_str()); - std::map<std::string, std::string> configurationMap; + DebugOut() << "data: " << data << endl; - json_object_object_foreach(obj, key, val) + if(!readPluginConfig(data)) { - string valstr = json_object_get_string(val); - DebugOut() << "plugin config key: " << key << "value:" << valstr << endl; - configurationMap[key] = valstr; + DebugOut(DebugOut::Error) << "Reading contentds of file: " << name << endl; } + } + auto errorGetFilePtr = amb::make_super(errorGetFile); - string path = configurationMap["path"]; + if(errorGetFilePtr) + { + DebugOut(DebugOut::Error) << "enumerating file: " << errorGetFilePtr->message << endl; + return; + } +} + +bool PluginLoader::readPluginConfig(const string &configData) +{ + picojson::value v; + std::string err; - loadPlugin(path, configurationMap); + picojson::parse(v, configData.begin(), configData.end(), &err); + + if (!err.empty()) + { + DebugOut(DebugOut::Error) << err << endl; + return false; } - //json_object_put(sinksobject); - json_object_put(rootobject); - json_tokener_free(tokener); + std::string pluginName; + if(v.contains("name")) + { + pluginName = v.get("name").to_str(); + } - Core* core = static_cast<Core*>(routingEngine); - if( core != nullptr ) + std::string pluginPath; + if(v.contains("path")) { - core->inspectSupported(); + pluginPath = v.get("path").to_str(); + } + else + { + DebugOut(DebugOut::Error) << "config missing 'path'." << endl; + return false; } -} -PluginLoader::~PluginLoader() -{ - for(auto handle : openHandles) - dlclose(handle); -} + bool enabled = false; + if(v.contains("enabled")) + { + enabled = v.get("enabled").get<bool>(); + } + else + { + DebugOut(DebugOut::Error) << "config missing 'enabled'." << endl; + return false; + } -IMainLoop *PluginLoader::mainloop() -{ - return mMainLoop; -} + DebugOut() << "Plugin: " << pluginName << endl; + DebugOut() << "Path: " << pluginPath << endl; + DebugOut() << "Enabled: " << enabled << endl; -std::string PluginLoader::errorString() -{ - return mErrorString; + if(enabled) + { + std::map<std::string, std::string> otherConfig; + + picojson::object obj = v.get<picojson::object>(); + for(auto itr : obj) + { + otherConfig[itr.first] = itr.second.to_str(); + } + + loadPlugin(pluginPath, otherConfig); + } + + return true; } diff --git a/ambd/pluginloader.h b/ambd/pluginloader.h index fb2494da..7700feed 100644 --- a/ambd/pluginloader.h +++ b/ambd/pluginloader.h @@ -30,9 +30,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "debugout.h" #include "imainloop.h" - -using namespace std; - typedef void create_t(AbstractRoutingEngine*, map<string, string> ); typedef void* create_mainloop_t(int argc, char** argv); typedef void* createRoutingEngine(void); @@ -41,17 +38,21 @@ class PluginLoader { public: - PluginLoader(string configFile, int argc, char** argv); + PluginLoader(std::string configFile, int argc, char** argv); ~PluginLoader(); IMainLoop* mainloop(); std::string errorString(); + void scanPluginDir(const std::string &); + + bool readPluginConfig(const std::string & configData); private: ///methods: - bool loadPlugin(string pluginName, map<string, string> config) + + bool loadPlugin(string pluginName, std::map<std::string, std::string> config) { DebugOut()<<"Loading plugin: "<<pluginName<<endl; diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 445856cf..66c97726 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -33,4 +33,6 @@ endif(enable_docs) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/amb.in.fidl ${CMAKE_CURRENT_SOURCE_DIR}/amb.fidl @ONLY) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/mainpage.in.idl ${CMAKE_CURRENT_SOURCE_DIR}/mainpage.idl @ONLY) - +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/ambd-configuration.in.idl ${CMAKE_CURRENT_SOURCE_DIR}/ambd-configuration.idl @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/ambd.in.md ${CMAKE_CURRENT_SOURCE_DIR}/ambd.md @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/ambd.in.md ${CMAKE_SOURCE_DIR}/README.md @ONLY) diff --git a/docs/Doxyfile.mainpage.in b/docs/Doxyfile.mainpage.in index bb8e6882..10fc0604 100644 --- a/docs/Doxyfile.mainpage.in +++ b/docs/Doxyfile.mainpage.in @@ -1,4 +1,4 @@ PROJECT_NAME = @PROJECT_NAME@ PROJECT_NUMBER = @PROJECT_VERSION@ -INPUT = @CMAKE_SOURCE_DIR@/docs/mainpage.idl +INPUT = @CMAKE_SOURCE_DIR@/docs/mainpage.idl @CMAKE_SOURCE_DIR@/docs/ambd-configuration.idl @CMAKE_SOURCE_DIR@/docs/ambd.md @CMAKE_SOURCE_DIR@/RELEASE.md IMAGE_PATH = @CMAKE_SOURCE_DIR@/docs/images diff --git a/docs/amb.in.fidl b/docs/amb.in.fidl index d0381900..2fd78306 100644 --- a/docs/amb.in.fidl +++ b/docs/amb.in.fidl @@ -1,5 +1,8 @@ /*! * \mainpage Automotive Message Broker DBus API Documentation +* \version @PROJECT_VERSION@ +* +* <a href="../../html/index.html">Back to AMB Documentation Main</a> * * \section intro Introduction * AMB organizes the API into two general interface categories. First the Manager interface (see manager.txt) @@ -113,6 +116,27 @@ map Dictionary { } /*! + * \brief HistoryObject is returned with GetHistory call + */ +interface HistoryObject { + + /*! + * \brief Name of property + */ + attribute String Name readonly + + /*! + * \brief Value of property + */ + attribute Variant Value readonly + + /*! + * \brief Time in seconds since unix epoch. + */ + attribute Double Time readonly +} + +/*! * VehiclePropertyType * \brief VehiclePropertyType is the base class for all Data types. */ @@ -135,7 +159,7 @@ interface VehiclePropertyType { * \arg endTime time stamp in Seconds since Unix Epoc */ method GetHistory(Double beginTime, Double endTime) { - out{ Dictionary result} + out{ array of HistoryObject result } } } diff --git a/docs/ambd-configuration.in.idl b/docs/ambd-configuration.in.idl new file mode 100644 index 00000000..253459d9 --- /dev/null +++ b/docs/ambd-configuration.in.idl @@ -0,0 +1,84 @@ +/*! + * \file ambd-configuration.idl + * \brief This document describes the ambd (AMB daemon) configuration. + * This file is typically located as "/etc/ambd/config". Plugin segments are supported as of 0.14 and should be installed in "@PLUGIN_SEGMENT_INSTALL_PATH@". + * \section example Configuration Example + * \code + * { + * "plugins" : "@PLUGIN_SEGMENT_INSTALL_PATH@", + * "sources" : [ ], + * "sinks": [ ] + * } + * \endcode + */ + +/*! + * \brief Config object is the root JSON object in the config. + */ +interface Config { + + /*! + * \brief plugins - path to plugin segments. + * The AMB Daemon will scan this path for any plugin configuration segments and load them if enabled. + */ + readonly attribute DOMString plugins; + + /*! + * \brief mainloop - path to mainloop plugin. + * The mainloop plugin is by default glib. AMB also provides a qt-based mainloop so plugins that use Qt mainloop features can be used. + */ + readonly attribute DOMString mainloop; + + /*! + * \brief source plugins. + * *depricated*. Use plugin configuration segments placed in @PLUGIN_SEGMENT_INSTALL_PATH@. + * Plugins defined here will be loaded by AMB. + */ + readonly attribute Plugin[] sources; + + /*! + * \brief sink plugins. + * *depricated*. Use plugin configuration segments placed in @PLUGIN_SEGMENT_INSTALL_PATH@. + * Plugins defined here will be loaded by AMB. + */ + readonly attribute Plugin[] sinks; + +} + +/*! + * \brief the Plugin interface describes the configuration for a plugin. + * This interface may be extended with plugin specific options. See the specific plugin's documentation for information on extended attributes. + */ +interface Plugin { + + /*! + * \brief name of plugin (ie 'ExamplePlugin') + */ + readonly attribute DOMString name; + + /*! + * \brief path to the plugins .so file (ie @PLUGIN_INSTALL_PATH@/examplesourceplugin.so) + */ + readonly attribute DOMString path; +} + +/*! + * \brief PluginSegment root object for plugin segments. + * PluginSegment inherits the attributes from Plugin and adds the "enabled" attribute. + * Only plugins that have 'enabled' : true will be loaded by the AMB daemon. PluginSements are usually installed + * in @PLUGIN_SEGMENT_INSTALL_PATH@. The following is a basic example of a plugin segment configuration: + * \code + * { + * "name" : "MyPlugin", + * "path" : "@PLUGIN_INSTALL_PATH@/myplugin.so", + * "enabled" : false + * } + * \endcode + */ +interface PluginSegment : Plugin { + + /*! + * \brief enabled - returns true if the plugin is enabled or not. + */ + readonly attribute boolean? enabled; +} diff --git a/docs/ambd.in.md b/docs/ambd.in.md new file mode 100644 index 00000000..bdfe0b08 --- /dev/null +++ b/docs/ambd.in.md @@ -0,0 +1,76 @@ +# Automotive Message Broker + +Version @PROJECT_VERSION@ + +## Introduction + +Automotive Message Broker is a vehicle network abstraction system. It brokers information from the vehicle +to applications. It provides application with a rich API for accessing vehicle data. + +Automotive Message Broker is built using CMake and requires libltdl (libtool), libjson-c, and boost packages. + +## Git +About the Git Tree: +master is expected to be unstable and may not even compile. If you want something more stable, checkout one of the +release branches (ie, 0.9.0, 0.10, etc) + +## Building + +To build: + +~~~~~~~~~~~~~{.bash} +cd automotive-message-broker +mkdir build +cd build +cmake .. +make +~~~~~~~~~~~~~ + +## Installing + +To install: + +~~~~~~~~~~~~~{.bash} +sudo make install +~~~~~~~~~~~~~ + +## Running +To run: + +~~~~~~~~~~~~~{.bash} +ambd +~~~~~~~~~~~~~ + +*also see **ambd -h** for additional command line options* + +## Configuration + +For information on ambd's configuration, please see ambd-configuration.idl. + +## Running with the Qt mainloop: + +Some source and sink plugins may want to use the Qt-based mainloop to take advantage of Qt features. To enable +the Qt mainloop, run cmake with -Duse_qtcore=On: + +~~~~~~~~~~~~~{.bash} +cmake .. -Dqtmainloop=On +~~~~~~~~~~~~~ + +You will also need to edit your config to enable the Qt-based mainloop: + +~~~~~~~~~~~~~{.json} +{ + "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", + "plugins" : "@PLUGIN_SEGMENT_INSTALL_PATH@" +} +~~~~~~~~~~~~~ + +NOTE: by default the glib mainloop will be used. + +## Questions/Issues/Comments: + +Questions or Comments can be emailed to the amb mailing list: +**amb at lists.01.org** + +Issues and Feature requests can be submitted on our github page: +https://github.com/otcshare/automotive-message-broker/issues diff --git a/docs/libamb.in b/docs/libamb.in index ace28a9e..f2197dd5 100644 --- a/docs/libamb.in +++ b/docs/libamb.in @@ -1,6 +1,9 @@ -/** +/*! \mainpage Automotive Message Broker Library Documentation \version @PROJECT_VERSION@ + + <a href="../../html/index.html">Back to AMB Documentation Main</a> + \section intro Introduction Automotive Message Broker (AMB) Library documentation outlines the internal classes and structures for building plugins for AMB. diff --git a/docs/mainpage.in.idl b/docs/mainpage.in.idl index 0e29a6f1..186438ec 100644 --- a/docs/mainpage.in.idl +++ b/docs/mainpage.in.idl @@ -2,13 +2,45 @@ * \mainpage Automotive Message Broker Documentation * \version @PROJECT_VERSION@ * \section links AMB Documentation Sections -* - <a href="../dbus/html/index.html">DBus API documentation</a> -* - <a href="../amb/html/index.html">libamb internal API documentation</a> +* - \ref ambd.md - AMB Daemon Documentation +* - <a href="../dbus/html/index.html">DBus API documentation</a> - Using AMB with your application +* - <a href="../amb/html/index.html">libamb internal API documentation</a> - Developing AMB or AMB plugins * - <a href="../plugins/bluemonkey/html/index.html">Bluemonkey Javascript API documentation</a> +* - <a href="../plugins/websocket/html/index.html">AMB Websocket protocol documentation</a> +* - \ref RELEASE.md - Current release notes * * \section intro Introduction * Automotive Message Broker (AMB) is a framework for providing applications with standardized access to vehicle data. * It uses a plugin architecture to allow customization for different vehicles and devices. Here is a diagram of how * AMB is architected: * \image html AMBArchitecture.png +* AMB works by passing "Properties" from "source" plugins to interested "sink" plugins. These properties represent +* vehicle data, ie 'VehicleSpeed' represents the vehicle's ground velocity. +* +* In General, AMB "source" plugins produce data and AMB "sink" plugins consume them. However, it is possible that a +* source can also consumes data. AMB supports multiple simultaneous source and sink plugins. This allows for aggregation +* from multiple vehicle networks, the cloud, or even from other instances of AMB running elsewhere (via the websocket +* plugins). +* +* More information about AMB plugins can be found in the <a href="../amb/html/index.html">library documentation</a>. +* \section plugins AMB Plugins +* AMB plugins each have their own usage documentation including any special Properties they define. Below is a description +* of several AMB plugins and their documenation: +* - <a href="../plugins/bluemonkey.README">Bluemonkey</a> - Extensible javascript engine plugin for defining plugin behavior in javascript +* - <a href="../plugins/database.README">Database</a> - Plugin that logs data in a sqlite database and can play back logs +* - <a href="../plugins/dbus.README">DBus</a> - Exposes AMB Properties on DBus +* - <a href="../plugins/gpsnmea.README">GPSNmea</a> - GPS Plugin that provides location position data from NMEA compatible devices +* - <a href="../plugins/obd2.README">OBD-II</a> - Plugin that provides data from OBD-II compatible vehicles +* - <a href="../plugins/opencvlux.README">OpenCVLux</a> - Plugin that uses OpenCV to produce ADAS data and perform video logging +* - <a href="../plugins/websocket.README">Websocket</a> - Both source and sink plugin that uses a websocket protocol to communicate +* - <a href="../plugins/wheel.README">Wheel</a> - Plugin that generates data using the Logitech G27 racing wheel +* \section crosswalk Crosswalk Vehicle Extension +* AMB also comes with a <a href="http://crosswalk-project.org">crosswalk</a> extension that implements the W3C Automotive Business group +* <a href="https://rawgit.com/w3c/automotive-bg/master/vehicle_spec.html">vehicle</a> and <a href="https://rawgit.com/w3c/automotive-bg/master/data_spec.html">data</a> specifications. +* \section licensing Licensing +* AMB is licensed LGPL v2. This allows the creation of proprietary plugins. +* \section previous_versions Previous Versions +* - <a href="../../0.13/index.html">0.13</a> +* - <a href="../../0.12/docs/amb/html/index.html">0.12 library Documentation</a> +* - <a href="../../0.12/docs/dbus/html/index.html">0.12 DBus API Documentation</a> */ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 54cea0b6..1100120c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,21 +1,34 @@ -set(amb_examples configwheel databasesource dbusconfig exampleconfig - gpsnmea obdsourceconfig opencvluxconfig opencvdbusconfig qtmainloopconfig websocketsink2 websocketsource2 - testsourceconfig bluemonkey/bluemonkeyconfig) +set(amb_examples ${CMAKE_CURRENT_BINARY_DIR}/configwheel + ${CMAKE_CURRENT_BINARY_DIR}/databasesource + ${CMAKE_CURRENT_BINARY_DIR}/dbusconfig + ${CMAKE_CURRENT_BINARY_DIR}/exampleconfig + ${CMAKE_CURRENT_BINARY_DIR}/gpsnmea + ${CMAKE_CURRENT_BINARY_DIR}/obdsourceconfig + ${CMAKE_CURRENT_BINARY_DIR}/opencvluxconfig + ${CMAKE_CURRENT_BINARY_DIR}/opencvdbusconfig + ${CMAKE_CURRENT_BINARY_DIR}/qtmainloopconfig + ${CMAKE_CURRENT_BINARY_DIR}/websocketsink2 + ${CMAKE_CURRENT_BINARY_DIR}/websocketsource2 + ${CMAKE_CURRENT_BINARY_DIR}/testsourceconfig + ${CMAKE_CURRENT_BINARY_DIR}/bluemonkey/bluemonkeyconfig) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/configwheel.in ${CMAKE_CURRENT_SOURCE_DIR}/configwheel @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/databasesource.in ${CMAKE_CURRENT_SOURCE_DIR}/databasesource @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/dbusconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/dbusconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/exampleconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/exampleconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/gpsnmea.in ${CMAKE_CURRENT_SOURCE_DIR}/gpsnmea @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/obdsourceconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/obdsourceconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/opencvluxconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/opencvluxconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/opencvdbusconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/opencvdbusconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/qtmainloopconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/qtmainloopconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/websocketsink2.in ${CMAKE_CURRENT_SOURCE_DIR}/websocketsink2 @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/websocketsource2.in ${CMAKE_CURRENT_SOURCE_DIR}/websocketsource2 @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/testsourceconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/testsourceconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cangenconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/cangenconfig @ONLY) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/bluemonkey/bluemonkeyconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/bluemonkey/bluemonkeyconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/configwheel.in.json ${CMAKE_CURRENT_BINARY_DIR}/configwheel @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/databasesource.in.json ${CMAKE_CURRENT_BINARY_DIR}/databasesource @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/databaselogging.in.json ${CMAKE_CURRENT_BINARY_DIR}/databaselogging @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/dbusconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/dbusconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/exampleconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/exampleconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/gpsnmea.in.json ${CMAKE_CURRENT_BINARY_DIR}/gpsnmea @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/obdsourceconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/obdsourceconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/opencvluxconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/opencvluxconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/opencvdbusconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/opencvdbusconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/qtmainloopconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/qtmainloopconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/websocketsink2.in.json ${CMAKE_CURRENT_BINARY_DIR}/websocketsink2 @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/websocketsource2.in.json ${CMAKE_CURRENT_BINARY_DIR}/websocketsource2 @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/databasewebsocketsink.in.json ${CMAKE_CURRENT_BINARY_DIR}/databasewebsocketsink @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/testsourceconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/testsourceconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cangenconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/cangenconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/bluemonkey/bluemonkeyconfig.in.json ${CMAKE_CURRENT_BINARY_DIR}/bluemonkey/bluemonkeyconfig @ONLY) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/testplugins.d.in.json ${CMAKE_CURRENT_SOURCE_DIR}/testplugins.d.json @ONLY) install (FILES ${amb_examples} DESTINATION /etc/ambd/examples) diff --git a/examples/bluemonkey/bluemonkeyconfig.in b/examples/bluemonkey/bluemonkeyconfig.in deleted file mode 100644 index 0ba2c813..00000000 --- a/examples/bluemonkey/bluemonkeyconfig.in +++ /dev/null @@ -1,21 +0,0 @@ -{ - "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", - - "sources" : [ - { - "path" : "@PLUGIN_INSTALL_PATH@/bluemonkeyplugin.so", - "config" : "/etc/ambd/bluemonkey/config.js" - } - ], - - "sinks" : [ - - { - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - }, - { - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - } - ] -} - diff --git a/examples/bluemonkey/bluemonkeyconfig.in.json b/examples/bluemonkey/bluemonkeyconfig.in.json new file mode 100644 index 00000000..ea92a3dc --- /dev/null +++ b/examples/bluemonkey/bluemonkeyconfig.in.json @@ -0,0 +1,18 @@ +{ + "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", + "sources" : [ + { + "path" : "@PLUGIN_INSTALL_PATH@/bluemonkeyplugin.so", + "config" : "/etc/ambd/bluemonkey/config.js" + } + ], + "sinks" : [ + { + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + }, + { + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + } + ] +} + diff --git a/examples/cangenconfig.in b/examples/cangenconfig.in.json index 81add2cb..9886c7a9 100644 --- a/examples/cangenconfig.in +++ b/examples/cangenconfig.in.json @@ -1,11 +1,11 @@ { - "sources" : [ + "sources" : [ { "name" : "CANSimPlugin", "path":"@PLUGIN_INSTALL_PATH@/cansimplugin.so", "interfaces" : ["vcan0", "vcan1"] - }, - { + }, + { "name" : "CANGenPlugin", "path":"@PLUGIN_INSTALL_PATH@/cangenplugin.so" } diff --git a/examples/configwheel.in b/examples/configwheel.in deleted file mode 100644 index ebd9335e..00000000 --- a/examples/configwheel.in +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sources" : [ - { - "name" : "WheelSource", - "path" : "@PLUGIN_INSTALL_PATH@/wheelsourceplugin.so", - "device" : "/dev/input/js0" - } - ], - "sinks": [ - { - "name" : "DBusSink", - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - } - ] -} - diff --git a/examples/configwheel.in.json b/examples/configwheel.in.json new file mode 100644 index 00000000..2bef1ed2 --- /dev/null +++ b/examples/configwheel.in.json @@ -0,0 +1,16 @@ +{ + "sources" : [ + { + "name" : "WheelSource", + "path" : "@PLUGIN_INSTALL_PATH@/wheelsourceplugin.so", + "device" : "/dev/input/js0" + } + ], + "sinks": [ + { + "name" : "DBusSink", + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + } + ] +} + diff --git a/examples/databaselogging.in.json b/examples/databaselogging.in.json new file mode 100644 index 00000000..1f76e0f5 --- /dev/null +++ b/examples/databaselogging.in.json @@ -0,0 +1,28 @@ +{ + "sources" : [ + { + "name" : "ExampleSouce", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", + "delay" : "1000" + }, + { + "name" : "Database Source", + "path" : "@PLUGIN_INSTALL_PATH@/databasesinkplugin.so", + "properties" : "{ 'properties' : ['VehicleSpeed','EngineSpeed'] }", + "startOnLoad" : "true", + "databaseFile" : "/tmp/storage", + "frequency" : "1", + "bufferLength" : "1" + } + ], + "sinks": [ + { + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + }, + { + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + } + ] +} + diff --git a/examples/databasesource.in b/examples/databasesource.in deleted file mode 100644 index 69ab8cb9..00000000 --- a/examples/databasesource.in +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sources" : [ - { - "name" : "Database Source", - "path" : "@PLUGIN_INSTALL_PATH@/databasesinkplugin.so", - "playbackOnLoad" : "true", - "databaseFile" : "/tmp/storage" - } - ], - "sinks": [ - { - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - } - ] -} - diff --git a/examples/databasesource.in.json b/examples/databasesource.in.json new file mode 100644 index 00000000..cc21c85e --- /dev/null +++ b/examples/databasesource.in.json @@ -0,0 +1,16 @@ +{ + "sources" : [ + { + "name" : "Database Source", + "path" : "@PLUGIN_INSTALL_PATH@/databasesinkplugin.so", + "playbackOnLoad" : "true", + "databaseFile" : "/tmp/storage" + } + ], + "sinks": [ + { + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + } + ] +} + diff --git a/examples/databasewebsocketsink.in.json b/examples/databasewebsocketsink.in.json new file mode 100644 index 00000000..54b8bf76 --- /dev/null +++ b/examples/databasewebsocketsink.in.json @@ -0,0 +1,29 @@ +{ + "sources" : [ + { + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" + }, + { + "name" : "Database", + "path" : "@PLUGIN_INSTALL_PATH@/databasesinkplugin.so", + "databaseFile" : "../tests/generated.db", + "bufferLength" : "1", + "properties" : "{ 'properties' : ['VehicleSpeed','EngineSpeed'] }", + "startOnLoad" : "true", + "playbackOnLoad" : "false", + "playbackMultiplier" : "1", + "frequency" : "1" + } + ], + "sinks": [ + { + "name" : "WebSocketSink", + "path" : "@PLUGIN_INSTALL_PATH@/websocketsink.so", + "interface" : "lo", + "ssl" : "false", + "port" : "23000", + "binaryProtocol" : "false", + "useExtensions" : "true" + } + ] +} diff --git a/examples/dbusconfig.in b/examples/dbusconfig.in deleted file mode 100644 index 22ebc564..00000000 --- a/examples/dbusconfig.in +++ /dev/null @@ -1,18 +0,0 @@ -{ - "sources" : [ - { - "name" : "ExampleSouce", - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", - "delay" : "6" - } - ], - "sinks": [ - { - "name" : "DBusSink", - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so", - "frequency" : "30" - } - - ] -} - diff --git a/examples/dbusconfig.in.json b/examples/dbusconfig.in.json new file mode 100644 index 00000000..d554f4f5 --- /dev/null +++ b/examples/dbusconfig.in.json @@ -0,0 +1,18 @@ +{ + "sources" : [ + { + "name" : "ExampleSouce", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", + "delay" : "6" + } + ], + "sinks": [ + { + "name" : "DBusSink", + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so", + "frequency" : "30" + } + + ] +} + diff --git a/examples/exampleconfig.in b/examples/exampleconfig.in deleted file mode 100644 index f6c3520a..00000000 --- a/examples/exampleconfig.in +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sources" : [ - { - "name" : "ExampleSouce", - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", - "delay" : "1" - } - ], - "sinks": [ - { - "name" : "ExampleSink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - } - ] -} - diff --git a/examples/exampleconfig.in.json b/examples/exampleconfig.in.json new file mode 100644 index 00000000..fef96698 --- /dev/null +++ b/examples/exampleconfig.in.json @@ -0,0 +1,16 @@ +{ + "sources" : [ + { + "name" : "ExampleSouce", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", + "delay" : "1" + } + ], + "sinks": [ + { + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + } + ] +} + diff --git a/examples/gpsnmea.in b/examples/gpsnmea.in deleted file mode 100644 index 95f5602a..00000000 --- a/examples/gpsnmea.in +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sources" : [ - { - "name" : "gps nmea plugin", - "path" : "@PLUGIN_INSTALL_PATH@/gpsnmea.so", - "test" : "true", - "device" : "/dev/ttyACM0" - } - ], - "sinks": [ - { - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - } - ] -} - diff --git a/examples/gpsnmea.in.json b/examples/gpsnmea.in.json new file mode 100644 index 00000000..de4a93ab --- /dev/null +++ b/examples/gpsnmea.in.json @@ -0,0 +1,16 @@ +{ + "sources" : [ + { + "name" : "gps nmea plugin", + "path" : "@PLUGIN_INSTALL_PATH@/gpsnmea.so", + "test" : "true", + "device" : "/dev/ttyACM0" + } + ], + "sinks": [ + { + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + } + ] +} + diff --git a/examples/obdsourceconfig.in b/examples/obdsourceconfig.in deleted file mode 100644 index 57f4aa2a..00000000 --- a/examples/obdsourceconfig.in +++ /dev/null @@ -1,18 +0,0 @@ -{ - "sources" : [ - { - "name" : "OBD2Source", - "path" : "@PLUGIN_INSTALL_PATH@/obd2sourceplugin.so", - "device" : "/dev/pts/5", - "baud" : "115200", - "bluetoothAdapter" : "" - } - ], - "sinks": [ - { - "name" : "ExampleSink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - } - ] -} - diff --git a/examples/obdsourceconfig.in.json b/examples/obdsourceconfig.in.json new file mode 100644 index 00000000..1c28ebfe --- /dev/null +++ b/examples/obdsourceconfig.in.json @@ -0,0 +1,18 @@ +{ + "sources" : [ + { + "name" : "OBD2Source", + "path" : "@PLUGIN_INSTALL_PATH@/obd2sourceplugin.so", + "device" : "/dev/pts/5", + "baud" : "115200", + "bluetoothAdapter" : "" + } + ], + "sinks": [ + { + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + } + ] +} + diff --git a/examples/opencvdbusconfig.in b/examples/opencvdbusconfig.in deleted file mode 100644 index 7634c755..00000000 --- a/examples/opencvdbusconfig.in +++ /dev/null @@ -1,30 +0,0 @@ -{ - "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", - "sources" : [ - { - "name" : "OpenCV LUX", - "path" : "@PLUGIN_INSTALL_PATH@/opencvluxplugin.so", - "threaded" : "true", - "opencl" : "true", - "fps" : "30", - "pixelLowerBound" : "18", - "pixelUpperBound" : "255", - "device" : "0", - "codec" : "h264", - "logging" : "false", - "logfile" : "/tmp/video.avi", - "ddd" : "true", - "faceCascade" : "/usr/share/OpenCV/lbpcascades/lbpcascade_frontalface.xml", - "eyeCascade" : "/usr/share/OpenCV/haarcascades/haarcascade_eye_tree_eyeglasses.xml" - }, - { - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" - } - ], - "sinks": [ - { - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so", - }, - ] -} - diff --git a/examples/opencvdbusconfig.in.json b/examples/opencvdbusconfig.in.json new file mode 100644 index 00000000..c1e4642c --- /dev/null +++ b/examples/opencvdbusconfig.in.json @@ -0,0 +1,30 @@ +{ + "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", + "sources" : [ + { + "name" : "OpenCV LUX", + "path" : "@PLUGIN_INSTALL_PATH@/opencvluxplugin.so", + "threaded" : "true", + "opencl" : "true", + "fps" : "30", + "pixelLowerBound" : "18", + "pixelUpperBound" : "255", + "device" : "0", + "codec" : "h264", + "logging" : "false", + "logfile" : "/tmp/video.avi", + "ddd" : "true", + "faceCascade" : "/usr/share/OpenCV/lbpcascades/lbpcascade_frontalface.xml", + "eyeCascade" : "/usr/share/OpenCV/haarcascades/haarcascade_eye_tree_eyeglasses.xml" + }, + { + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" + } + ], + "sinks": [ + { + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so", + }, + ] +} + diff --git a/examples/opencvluxconfig.in b/examples/opencvluxconfig.in deleted file mode 100644 index b6860614..00000000 --- a/examples/opencvluxconfig.in +++ /dev/null @@ -1,24 +0,0 @@ -{ - "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", - "sources" : [ - { - "name" : "OpenCV Lux plugin", - "path" : "@PLUGIN_INSTALL_PATH@/opencvluxplugin.so", - "threaded" : "true", - "kinect" : "false", - "opencl" : "false", - "cuda" : "true", - "pixelLowerBound" : "0", - "pixelUpperBound" : "255", - "fps" : "30", - "device" : "0" - } - ], - "sinks": [ - { - "name" : "Example sink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - } - ] -} - diff --git a/examples/opencvluxconfig.in.json b/examples/opencvluxconfig.in.json new file mode 100644 index 00000000..7ca298a0 --- /dev/null +++ b/examples/opencvluxconfig.in.json @@ -0,0 +1,24 @@ +{ + "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", + "sources" : [ + { + "name" : "OpenCV Lux plugin", + "path" : "@PLUGIN_INSTALL_PATH@/opencvluxplugin.so", + "threaded" : "true", + "kinect" : "false", + "opencl" : "false", + "cuda" : "true", + "pixelLowerBound" : "0", + "pixelUpperBound" : "255", + "fps" : "30", + "device" : "0" + } + ], + "sinks": [ + { + "name" : "Example sink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + } + ] +} + diff --git a/examples/qtmainloopconfig.in b/examples/qtmainloopconfig.in deleted file mode 100644 index 99a01542..00000000 --- a/examples/qtmainloopconfig.in +++ /dev/null @@ -1,18 +0,0 @@ -{ - "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", - "sources" : [ - { - "name" : "ExampleSouce", - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" - } - ], - "sinks": [ - { - "name" : "Example sink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so", - "interface" : "lo", - "port" : "23000" - } - ] -} - diff --git a/examples/qtmainloopconfig.in.json b/examples/qtmainloopconfig.in.json new file mode 100644 index 00000000..fb959265 --- /dev/null +++ b/examples/qtmainloopconfig.in.json @@ -0,0 +1,18 @@ +{ + "mainloop" : "@PLUGIN_INSTALL_PATH@/qtmainloopplugin.so", + "sources" : [ + { + "name" : "ExampleSouce", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so" + } + ], + "sinks": [ + { + "name" : "Example sink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so", + "interface" : "lo", + "port" : "23000" + } + ] +} + diff --git a/examples/testplugins.d.in.json b/examples/testplugins.d.in.json new file mode 100644 index 00000000..8fb41c76 --- /dev/null +++ b/examples/testplugins.d.in.json @@ -0,0 +1,4 @@ +{ + "plugins" : "/home/kev/src/automotive-message-broker/examples/plugins.d" +} + diff --git a/examples/testsourceconfig.in b/examples/testsourceconfig.in deleted file mode 100644 index 01029c0d..00000000 --- a/examples/testsourceconfig.in +++ /dev/null @@ -1,15 +0,0 @@ -{ - "sources" : [ - { - "name" : "TestPlugin", - "path" : "@PLUGIN_INSTALL_PATH@/testplugin.so" - } - ], - "sinks": [ - { - "name" : "ExampleSink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - } - ] -} - diff --git a/examples/testsourceconfig.in.json b/examples/testsourceconfig.in.json new file mode 100644 index 00000000..6af6118e --- /dev/null +++ b/examples/testsourceconfig.in.json @@ -0,0 +1,15 @@ +{ + "sources" : [ + { + "name" : "TestPlugin", + "path" : "@PLUGIN_INSTALL_PATH@/testplugin.so" + } + ], + "sinks": [ + { + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + } + ] +} + diff --git a/examples/websocketsink2.in b/examples/websocketsink2.in deleted file mode 100644 index b326dbba..00000000 --- a/examples/websocketsink2.in +++ /dev/null @@ -1,21 +0,0 @@ -{ - "sources" : [ - { - "name" : "ExampleSouce", - "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", - "delay" : "10000" - } - ], - "sinks": [ - { - "name" : "WebSocketSink", - "path" : "@PLUGIN_INSTALL_PATH@/websocketsink.so", - "interface" : "eth1", - "ssl" : "false", - "port" : "23000", - "binaryProtocol" : "false", - "useExtensions" : "true" - } - ] -} - diff --git a/examples/websocketsink2.in.json b/examples/websocketsink2.in.json new file mode 100644 index 00000000..f74ab5d7 --- /dev/null +++ b/examples/websocketsink2.in.json @@ -0,0 +1,21 @@ +{ + "sources" : [ + { + "name" : "ExampleSouce", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", + "delay" : "10000" + } + ], + "sinks": [ + { + "name" : "WebSocketSink", + "path" : "@PLUGIN_INSTALL_PATH@/websocketsink.so", + "interface" : "eth1", + "ssl" : "false", + "port" : "23000", + "binaryProtocol" : "false", + "useExtensions" : "true" + } + ] +} + diff --git a/examples/websocketsource2.in b/examples/websocketsource2.in deleted file mode 100644 index 053b1ab8..00000000 --- a/examples/websocketsource2.in +++ /dev/null @@ -1,24 +0,0 @@ -{ - "sources" : [ - { - "name" : "WebsocketSource", - "path" : "@PLUGIN_INSTALL_PATH@/websocketsource.so", - "port" : "23000", - "ssl" : "false", - "ip" : "127.0.0.1", - "binaryProtocol" : "false", - "useExtensions" : "true" - } - ], - "sinks": [ - { - "name" : "DBusSink", - "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" - }, - { - "name" : "ExampleSink", - "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" - } - ] -} - diff --git a/examples/websocketsource2.in.json b/examples/websocketsource2.in.json new file mode 100644 index 00000000..e072f58d --- /dev/null +++ b/examples/websocketsource2.in.json @@ -0,0 +1,24 @@ +{ + "sources" : [ + { + "name" : "WebsocketSource", + "path" : "@PLUGIN_INSTALL_PATH@/websocketsource.so", + "port" : "23000", + "ssl" : "false", + "ip" : "127.0.0.1", + "binaryProtocol" : "false", + "useExtensions" : "true" + } + ], + "sinks": [ + { + "name" : "DBusSink", + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so" + }, + { + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so" + } + ] +} + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0b41a490..b479aaf4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,5 +1,5 @@ -set(amb_sources abstractpropertytype.cpp abstractroutingengine.cpp listplusplus.cpp abstractsink.cpp vehicleproperty.cpp abstractsource.cpp debugout.cpp timestamp.cpp uuidhelper.cpp mappropertytype.hpp propertyinfo.hpp superptr.hpp asyncqueue.hpp ambpluginimpl.cpp ambplugin.h) -set(amb_headers_install abstractpropertytype.h nullptr.h abstractroutingengine.h listplusplus.h abstractsink.h vehicleproperty.h debugout.h abstractsource.h timestamp.h uuidhelper.h mappropertytype.hpp propertyinfo.hpp superptr.hpp asyncqueue.hpp ambplugin.h ambpluginimpl.h) +set(amb_sources abstractpropertytype.cpp abstractroutingengine.cpp listplusplus.cpp abstractsink.cpp vehicleproperty.cpp abstractsource.cpp debugout.cpp timestamp.cpp uuidhelper.cpp mappropertytype.hpp propertyinfo.hpp superptr.hpp asyncqueue.hpp ambpluginimpl.cpp ambplugin.h picojson.h) +set(amb_headers_install abstractpropertytype.h nullptr.h abstractroutingengine.h listplusplus.h abstractsink.h vehicleproperty.h debugout.h abstractsource.h timestamp.h uuidhelper.h mappropertytype.hpp propertyinfo.hpp superptr.hpp asyncqueue.hpp ambplugin.h ambpluginimpl.h picojson.h) add_library(amb SHARED ${amb_sources}) diff --git a/lib/abstractpropertytype.h b/lib/abstractpropertytype.h index 6efbee13..a097c142 100644 --- a/lib/abstractpropertytype.h +++ b/lib/abstractpropertytype.h @@ -714,6 +714,9 @@ public: } }; +/*! + * \brief ListPropertyType is a AbstractPropertyType for arrays of AbstractPropertyTypes + */ template <class T = AbstractPropertyType> class ListPropertyType: public AbstractPropertyType { diff --git a/lib/abstractroutingengine.h b/lib/abstractroutingengine.h index 3c4bc8d0..cce0140e 100644 --- a/lib/abstractroutingengine.h +++ b/lib/abstractroutingengine.h @@ -112,8 +112,8 @@ public: }; /*! - * \brief The AsyncPropertyReply class is used by sources to reply to Get and Set operations. The source should - * set success to true if the call is successful or 'false' if the request was not successful and set 'error' + * \brief The AsyncPropertyReply class is used by sources to reply to Get and Set operations. + * The source should set success to true if the call is successful or 'false' if the request was not successful and set 'error' * to the appropriate error. * \see AbstractRoutingEngine::getPropertyAsync * \see AsyncPropertyReply @@ -142,6 +142,9 @@ public: ZoneNotSupported }; + /*! + * \brief errorToStr returns string representing the Error + */ static std::string errorToStr(Error err) { if(err == NoError) @@ -154,6 +157,29 @@ public: return "PermissionDenied"; else if(err == ZoneNotSupported) return "ZoneNotSupported"; + + DebugOut(DebugOut::Warning) << "Could not translate error: " << err << endl; + return ""; + } + + /*! + * \brief strToError returns Error representing the string + */ + static Error strToError(std::string err) + { + if(err == "NoError") + return NoError; + else if(err == "Timeout") + return Timeout; + else if(err == "InvalidOperation") + return InvalidOperation; + else if(err == "PermissionDenied") + return PermissionDenied; + else if(err == "ZoneNotSupported") + return ZoneNotSupported; + + DebugOut(DebugOut::Warning) << "Could not translate error string: " << err << endl; + return NoError; } /*! @@ -259,19 +285,22 @@ public: Zone::Type zone; /*! - * \brief completed callback that is called when the ranged request is complete. The reply from this request is passed + * \brief completed callback + * 'completed' is called when the ranged request is complete. The reply from this request is passed * into the completed call. The completed callback must free the reply before it returns or there will be a leak. */ GetRangedPropertyCompletedSignal completed; /*! - * \brief timeBegin set this to request values for the specified property beggining at this time. Time is seconds\ + * \brief timeBegin + * Set this to request values for the specified property beggining at this time. Time is seconds\ * since the unix epoc. Set this to '0' if you do not want values within a time range. */ double timeBegin; /*! - * \brief timeEnd set this to request values for the specified property beggining at this time. Time is seconds\ + * \brief timeEnd + * Set this to request values for the specified property beggining at this time. Time is seconds\ * since the unix epoc. Set this to '0' if you do not want values within a time range. */ double timeEnd; @@ -296,7 +325,7 @@ public: /*! * \brief The AsyncRangePropertyReply class is used by a source to reply to an AsyncRangePropertyRequest. - * the source should set success to 'true' and populate the 'values' member if the request was successful. + * The source should set success to 'true' and populate the 'values' member if the request was successful. * If the request is not successful, 'success' should be set to 'false' and the 'error' member should be set. */ class AsyncRangePropertyReply: public AsyncRangePropertyRequest @@ -373,7 +402,7 @@ public: * /see AsyncPropertyReply. * /param request requested property. * /return AsyncPropertyReply. The returned AsyncPropertyReply is owned by the caller of getPropertyAsync. - * /example AsyncPropertyRequest request; + * /code AsyncPropertyRequest request; * request.property = VehicleProperty::VehicleSpeed * request.completed = [](AsyncPropertyReply* reply) * { @@ -381,14 +410,15 @@ public: * delete reply; * }; * routingEngine->getPropertyAsync(request); + * /endcode */ virtual AsyncPropertyReply * getPropertyAsync(AsyncPropertyRequest request) = 0; /*! * \brief getRangePropertyAsync is used for getting a range of properties that are within the specified time or sequence parameters. - * \param request the request containing the property and other information required by the query + * \arg request the request containing the property and other information required by the query * \return a pointer to the reply. - * \example AsyncRangePropertyRequest vehicleSpeedFromLastWeek; + * \code AsyncRangePropertyRequest vehicleSpeedFromLastWeek; * * vehicleSpeedFromLastWeek.timeBegin = amb::currentTime() - 10; * vehicleSpeedFromLastWeek.timeEnd = amb::currentTime(); @@ -411,16 +441,15 @@ public: * }; * * routingEngine->getRangePropertyAsync(vehicleSpeedFromLastWeek); - * + * \endcode */ - virtual void getRangePropertyAsync(AsyncRangePropertyRequest request) = 0; /*! * \brief setProperty sets a property to a value. * \see AsyncSetPropertyRequest * \see AsyncPropertyReply - * \param request the request containing the property and the value to set + * \arg request the request containing the property and the value to set * \return a pointer to the reply which is owned by the caller of this method * \example */ @@ -428,24 +457,24 @@ public: /*! * \brief subscribeToProperty subscribes to propertyName. Value changes will be passed to callback. - * \param propertyName - * \param callback - * \param pid process id of the requesting application + * \arg propertyName + * \arg callback + * \arg pid process id of the requesting application * \return subscription handle */ virtual uint subscribeToProperty(const VehicleProperty::Property & propertyName, PropertyChangedType callback, std::string pid="") = 0; /*! * \brief unsubscribeToProperty - * \param handle + * \arg handle */ virtual void unsubscribeToProperty(uint handle) = 0; /*! * \brief subscribeToProperty subscribe to changes made to a property value. - * \param propertyName name of the property to request a subscription for. - * \param self pointer to the sink who is subscribing. - * \example + * \arg propertyName name of the property to request a subscription for. + * \arg self pointer to the sink who is subscribing. + * \code * //somewhere in the sink: * routingEngine->subscribeToProperty(VehicleProperty::EngineSpeed, this); * @@ -457,23 +486,24 @@ public: * ... * } * } + * \endcode */ virtual bool subscribeToProperty(const VehicleProperty::Property & propertyName, AbstractSink* self) = 0; /*! * \brief subscribeToProperty subscribe to changes made to a property value. - * \param propertyName name of the property to request a subscription for. - * \param sourceUuidFilter source UUID to filter. Only property updates from this source will be sent to the sink. - * \param self pointer to the sink who is subscribing. + * \arg propertyName name of the property to request a subscription for. + * \arg sourceUuidFilter source UUID to filter. Only property updates from this source will be sent to the sink. + * \arg self pointer to the sink who is subscribing. */ virtual bool subscribeToProperty(const VehicleProperty::Property & propertyName, const std::string & sourceUuidFilter, AbstractSink *self) = 0; /*! * \brief subscribeToProperty subscribe to changes made to a property value. - * \param propertyName name of the property to request a subscription for. - * \param sourceUuidFilter source UUID to filter. Only property updates from this source will be sent to the sink. - * \param zoneFilter zone to filter. Only updates from this zone will be passed to the sink. - * \param self pointer to the sink who is subscribing. + * \arg propertyName name of the property to request a subscription for. + * \arg sourceUuidFilter source UUID to filter. Only property updates from this source will be sent to the sink. + * \arg zoneFilter zone to filter. Only updates from this zone will be passed to the sink. + * \arg self pointer to the sink who is subscribing. */ virtual bool subscribeToProperty(const VehicleProperty::Property & propertyName, const std::string & sourceUuidFilter, Zone::Type zoneFilter, AbstractSink *self) = 0; diff --git a/lib/asyncqueue.hpp b/lib/asyncqueue.hpp index ed0c69b4..ada42ac7 100644 --- a/lib/asyncqueue.hpp +++ b/lib/asyncqueue.hpp @@ -24,6 +24,7 @@ #include <mutex> #include <condition_variable> #include <unordered_set> +#include <vector> namespace amb { @@ -32,8 +33,8 @@ template <typename T, class Pred = std::equal_to<T> > class Queue { public: - Queue(bool blocking = false) - :mBlocking(blocking) + Queue(bool unique = false, bool blocking = false) + :mUnique(unique), mBlocking(blocking) { } @@ -56,7 +57,7 @@ public: if(mBlocking) { - while(!mQueue.size()) + if(!mQueue.size()) { cond.wait(lock); } @@ -79,12 +80,16 @@ public: { std::lock_guard<std::mutex> lock(mutex); - mQueue.insert(item); + if(contains(mQueue, item)) + { + mQueue.erase(std::find(mQueue.begin(), mQueue.end(), item)); + } + mQueue.push_back(item); } if(mBlocking) { - cond.notify_one(); + cond.notify_all(); } } @@ -94,11 +99,12 @@ public: removeOne(&mQueue, item); } -protected: +private: bool mBlocking; + bool mUnique; std::mutex mutex; std::condition_variable cond; - std::unordered_set<T, std::hash<T>, Pred> mQueue; + std::vector<T> mQueue; }; template <typename T, class Pred = std::equal_to<T> > diff --git a/lib/debugout.h b/lib/debugout.h index b8030ee9..d6a49e2e 100644 --- a/lib/debugout.h +++ b/lib/debugout.h @@ -30,6 +30,30 @@ using namespace std; void debugOut(const string &message); +/*! + * \brief The DebugOut class represents a class used for outputing debug information + * The specified debug level will only be outputed if the debug level is => the debug threshhold + * Here's a simple example: + * \code + * DebugOut::setDebugThreshhold(3); + * DebugOut(DebugOut::Warning) << "This is a warning" << std::endl; + * DebugOut(3) << "This will only show if the threshhold is 3 or lower." << std::endl; + * + * /// Start logging to a file: + * ofstream logfile; + * logfile.open("amb.log", ios::out | ios::trunc); + * DebugOut::setOutput(logfile) + * + * /// Throw exception on warning or error: + * DebugOut::setThrowErr(true); + * DebugOut::setThrowWarn(true); + * DebugOut(DebugOut::Error) << "This will throw an exception." << std::endl; + * + * /// Log to stderr: + * DebugOut::setOutput(std::cerr); + * DebugOut() << "This will log to stderr." << std::endl; + * \endcode + */ class DebugOut { public: diff --git a/xwalk/common/picojson.h b/lib/picojson.h index 93171e3f..93171e3f 100644 --- a/xwalk/common/picojson.h +++ b/lib/picojson.h diff --git a/lib/superptr.hpp b/lib/superptr.hpp index 671ef3b8..b2c2f530 100644 --- a/lib/superptr.hpp +++ b/lib/superptr.hpp @@ -71,7 +71,10 @@ struct traits<GDBusConnection> { }; template<typename T> using super_ptr = - ::std::unique_ptr<T, typename traits<T>::delete_functor>; + ::std::unique_ptr<T, typename traits<T>::delete_functor>; + +template<typename T> using gobject_ptr = + ::std::unique_ptr<T , std::function<void(T*)> >; template<typename T> super_ptr<T> make_super(T* t) { @@ -83,5 +86,10 @@ template<typename T> ::std::unique_ptr<T> make_unique(T* t) return ::std::unique_ptr<T>(t); } +template<typename T> gobject_ptr<T> make_gobject(T* t) +{ + return gobject_ptr<T>(t, [](auto ptr) { if(ptr) g_object_unref(ptr);}); +} + } #endif diff --git a/lib/vehicleproperty.cpp b/lib/vehicleproperty.cpp index 35b58cc6..b0365907 100644 --- a/lib/vehicleproperty.cpp +++ b/lib/vehicleproperty.cpp @@ -538,20 +538,24 @@ AbstractPropertyType* VehicleProperty::getPropertyTypeForPropertyNameValue(Vehic if(registeredPropertyFactoryMap.count(name) > 0) { VehicleProperty::PropertyTypeFactoryCallback cb = registeredPropertyFactoryMap[name]; - if ( cb != NULL ) + if ( cb != nullptr ) { AbstractPropertyType* type = cb(); - if(type == NULL) + if(type == nullptr) + { throw std::runtime_error("Cannot return NULL in a PropertyTypeFactory"); + } if(value != "" ) + { type->fromString(value); + } return type; } } - DebugOut(DebugOut::Error)<<"Property not found"<<endl; + DebugOut(DebugOut::Warning) << "Property not found: " << name << endl; return nullptr; } diff --git a/packaging.in/amb.manifest b/packaging.in/amb.manifest index 171a6041..bcb207a0 100644 --- a/packaging.in/amb.manifest +++ b/packaging.in/amb.manifest @@ -24,7 +24,7 @@ </define> <assign> <dbus name="org.automotive.message.broker" own="AMB" bus="system"> - <node name="*/0/MachineGunTurretStatus" > + <!--<node name="*/0/MachineGunTurretStatus" > <interface name="org.freedesktop.DBus.Properties"> <method name="Set" > <annotation name="com.tizen.smack" value="AMB::machinegun" /> @@ -38,6 +38,7 @@ </method> </interface> </node> + --> <!--<node name="*" > <interface name="org.freedesktop.DBus.Properties"> <method name="Get" > diff --git a/packaging.in/automotive-message-broker.spec.in b/packaging.in/automotive-message-broker.spec.in index 1ad1cf76..f0023a3d 100644 --- a/packaging.in/automotive-message-broker.spec.in +++ b/packaging.in/automotive-message-broker.spec.in @@ -28,6 +28,7 @@ BuildRequires: pkgconfig(opencv) BuildRequires: murphy BuildRequires: pkgconfig(murphy-glib) BuildRequires: pkgconfig(dbus-1) +BuildRequires: doxygen %if %{with qt5} BuildRequires: qt5-qtcore-devel BuildRequires: qt5-qtconcurrent-devel diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 45802660..32a41be6 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -21,7 +21,12 @@ install(TARGETS examplesourceplugin LIBRARY DESTINATION ${PLUGIN_INSTALL_PATH}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/exampleplugins.README.txt ${CMAKE_CURRENT_BINARY_DIR}/exampleplugins.README @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examplesource.in.json ${CMAKE_CURRENT_BINARY_DIR}/examplesource @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examplesink.in.json ${CMAKE_CURRENT_BINARY_DIR}/examplesink @ONLY) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/exampleplugins.README DESTINATION ${DOC_INSTALL_DIR}/plugins) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/examplesource DESTINATION ${PLUGIN_SEGMENT_INSTALL_PATH}) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/examplesink DESTINATION ${PLUGIN_SEGMENT_INSTALL_PATH}) add_subdirectory(common) diff --git a/plugins/bluemonkey/bluemonkey.in.idl b/plugins/bluemonkey/bluemonkey.in.idl index b729492d..bddfde54 100644 --- a/plugins/bluemonkey/bluemonkey.in.idl +++ b/plugins/bluemonkey/bluemonkey.in.idl @@ -1,6 +1,8 @@ /*! * \mainpage Automotive Message Broker Library Documentation - Bluemonkey Plugin * \version @PROJECT_VERSION@ + * + * <a href="../../../html/index.html">Back to AMB Documentation Main</a> * \section intro Introduction * Bluemonkey is a javascript rendering engine that allows the scripting of source plugin behavior in javascript. * It allows developers a quick way to prototype plugin code as well as a way to create custom properties. @@ -112,12 +114,14 @@ interface Bluemonkey { void createCustomProperty(DOMString name, any value, optional unsigned short zone); /*! - * \brief exportInterface + * \brief exportInterface export a custom DBus interface as org.automotive.[interfaceName] with properties */ void exportInterface(DOMString interfaceName, ExportMap[] properties); }; - +/*! + * \brief PropertyInterface represents an interface to an AMB Property + */ interface PropertyInterface { /*! * \brief AMB property name of this property @@ -149,6 +153,10 @@ interface PropertyInterface { */ readonly attribute Signal changed; + /*! + * \brief get logged data between \arg begin and \arg end. + * callback is called with the results of this method. + */ void getHistory(Date begin, Date end, HistoryCallback callback); }; @@ -163,6 +171,12 @@ interface Signal { void connect(ChangedCallback callback) }; +/*! + * \brief ExportMap is a dictionary from AmbPropertyName to DBusPropertyName + * AmbPropertyName should represent an internally defined or custom AMB property type. + * The DBusPropertyName should represent what you want the property to be represented in + * the DBus interface. + */ interface ExportMap { /*! * \brief internal AMB property name diff --git a/plugins/bluemonkey/config.js b/plugins/bluemonkey/config.js index e69e6765..cb7549e2 100644 --- a/plugins/bluemonkey/config.js +++ b/plugins/bluemonkey/config.js @@ -20,17 +20,13 @@ bluemonkey.createCustomProperty("AnswerToTheUniverse", 42); dbusConnected = bluemonkey.subscribeTo("DBusConnected"); dbusConnected.changed.connect(function () { - bluemonkey.log("WEEEEEEEEEEEEEEEEEEEEEEEEEEEEE!" + dbusConnected.value); + if(dbusConnected.value !== true) + return; - if(dbusConnected.value !== true) - return; - - bluemonkey.exportInterface("Bluemonkey",[{'BluemonkeySuperProperty' : 'SuperProperty'}, - {'AnswerToTheUniverse' : 'AnswerToTheUniverse'}]); + bluemonkey.exportInterface("Bluemonkey",[{'BluemonkeySuperProperty' : 'SuperProperty'}, + {'AnswerToTheUniverse' : 'AnswerToTheUniverse'}]); }); - - bluemonkey.createCustomProperty("VehicleSpeed", 10); bluemonkey.createCustomProperty("EngineSpeed", 5000); bluemonkey.createCustomProperty("PowertrainTorque", 324); diff --git a/plugins/common/abstractdbusinterface.cpp b/plugins/common/abstractdbusinterface.cpp index c3ece473..2f2053ab 100644 --- a/plugins/common/abstractdbusinterface.cpp +++ b/plugins/common/abstractdbusinterface.cpp @@ -64,9 +64,7 @@ const uint getPid(const char *owner) return thePid; } - - -static void handleMyMethodCall(GDBusConnection *connection, +void AbstractDBusInterface::handleMyMethodCall(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, @@ -87,7 +85,80 @@ static void handleMyMethodCall(GDBusConnection *connection, g_assert(iface); - if(method == "GetHistory") + if(std::string(interface_name) == "org.freedesktop.DBus.Properties") + { + if(method == "Get") + { + gchar* propertyName = nullptr; + gchar* ifaceName = nullptr; + g_variant_get(parameters, "(ss)", &ifaceName, &propertyName); + + DebugOut(6) << "Parameter signature: " << g_variant_get_type_string(parameters) << endl; +// DebugOut(6) << "Get property " << propertyName << " for interface " << ifaceName << endl; + + GError* error = nullptr; + auto value = amb::make_super(AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, propertyName, &error, iface)); + amb::make_super(error); + + if(!value) + { + g_dbus_method_invocation_return_dbus_error(invocation, std::string(std::string(ifaceName)+".PropertyNotFound").c_str(), "Property not found in interface"); + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", value.get())); + return; + } + else if(method == "GetAll") + { + gchar* ifaceName = nullptr; + g_variant_get(parameters, "(s)", &ifaceName); + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + + auto propertyMap = iface->getProperties(); + + for(auto itr : propertyMap) + { + auto prop = itr.second; + GError* error = nullptr; + auto value = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, prop->name().c_str(), &error, iface); + amb::make_super(error); + g_variant_builder_add(&builder, "{sv}", prop->name().c_str(), g_variant_new("v", value)); + //sequence + auto sequence = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"Sequence").c_str(), &error, iface); + g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"Sequence").c_str(), g_variant_new("v", sequence)); + } + + auto time = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Time", nullptr, iface); + auto zone = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Zone", nullptr, iface); + g_variant_builder_add(&builder, "{sv}", "Time", g_variant_new("v", time)); + g_variant_builder_add(&builder, "{sv}", "Zone", g_variant_new("v", zone)); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", &builder)); + return; + } + else if(method == "Set") + { + gchar* ifaceName = nullptr; + gchar* propName = nullptr; + GVariant* value; + g_variant_get(parameters, "(ssv)", &ifaceName, &propName, &value); + + AbstractDBusInterface::setProperty(connection, sender, object_path, ifaceName, propName, value, nullptr, iface, + [&invocation, &ifaceName](bool success, AsyncPropertyReply::Error error) { + if(success) + { + g_dbus_method_invocation_return_value(invocation, nullptr); + } + else + { + g_dbus_method_invocation_return_dbus_error(invocation, ifaceName, AsyncPropertyReply::errorToStr(error).c_str()); + } + }); + } + } + else if(method == "GetHistory") { double beginTime = 0; double endTime = 0; @@ -136,17 +207,17 @@ static void handleMyMethodCall(GDBusConnection *connection, } GVariantBuilder builder; - g_variant_builder_init(&builder, G_VARIANT_TYPE("a(svd)")); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{svd}")); for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++) { AbstractPropertyType* value = *itr; - g_variant_builder_add(&builder, "(svd)", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp); + g_variant_builder_add(&builder, "{svd}", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp); } - g_dbus_method_invocation_return_value(invocation,g_variant_new("(a(svd))",&builder)); + g_dbus_method_invocation_return_value(invocation,g_variant_new("(a{svd})",&builder)); }; iface->re->getRangePropertyAsync(request); @@ -154,6 +225,7 @@ static void handleMyMethodCall(GDBusConnection *connection, return; } + ///TODO: Deprecated in 0.15 else if(boost::algorithm::starts_with(method, "Get")) { std::string propertyName = method.substr(3); @@ -219,7 +291,6 @@ AbstractDBusInterface::~AbstractDBusInterface() } objectMap.erase(mObjectPath); - } void AbstractDBusInterface::addProperty(VariantType * property) @@ -242,16 +313,14 @@ void AbstractDBusInterface::addProperty(VariantType * property) ///see which properties are supported: introspectionXml += "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />" + "<!-- 'GetFoo' is deprecated as of 0.14 -->" + /// TODO: remove GetFoo in 0.15 "<method name='Get" + pn + "'>" " <arg type='v' direction='out' name='value' />" " <arg type='d' direction='out' name='timestamp' />" " <arg type='i' direction='out' name='sequence' />" " <arg type='i' direction='out' name='updateFrequency' />" "</method>" - "<signal name='" + pn + "Changed' >" - " <arg type='v' name='" + nameToLower + "' direction='out' />" - " <arg type='d' name='imestamp' direction='out' />" - "</signal>" "<property type='i' name='" + pn + "Sequence' access='read' />"; properties[pn] = property; @@ -302,7 +371,7 @@ void AbstractDBusInterface::registerObject() GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str()); - const GDBusInterfaceVTable vtable = { handleMyMethodCall, AbstractDBusInterface::getProperty, AbstractDBusInterface::setProperty }; + const GDBusInterfaceVTable vtable = { handleMyMethodCall, nullptr, nullptr }; GError* error2=NULL; @@ -399,6 +468,23 @@ void AbstractDBusInterface::startRegistration() //unregisterObject(); introspectionXml ="<node>" ; introspectionXml += + "<interface name='org.freedesktop.DBus.Properties'>" + "<method name='Get'>" + " <arg type='s' direction='in' name='interface' />" + " <arg type='s' direction='in' name='property' />" + " <arg type='v' direction='out' name='value' />" + "</method>" + "<method name='Set'>" + " <arg type='s' direction='in' name='interface' />" + " <arg type='s' direction='in' name='property' />" + " <arg type='v' direction='in' name='value' />" + "</method>" + "<method name='GetAll'>" + " <arg type='s' direction='in' name='interface' />" + " <arg type='a{sv}' direction='out' name='interface' />" + "</method>" + "</interface>"; + introspectionXml += "<interface name='"+ mInterfaceName + "' >" "<property type='i' name='Zone' access='read' />" "<property type='d' name='Time' access='read' />" @@ -409,7 +495,8 @@ void AbstractDBusInterface::startRegistration() "</method>"; } -GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GError** error, gpointer userData) +GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, + const gchar* propertyName, GError** error, gpointer userData) { if(DebugOut::getDebugThreshhold() >= 6) { @@ -478,7 +565,9 @@ GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const return nullptr; } -gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GVariant* value, GError** error, gpointer userData) +gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, + const gchar* propertyName, GVariant* value, GError** error, gpointer userData, + std::function<void (bool, AsyncPropertyReply::Error)> callback) { if(DebugOut::getDebugThreshhold() >= 6) { @@ -488,22 +577,22 @@ gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const g if(objectMap.count(objectPath)) { - objectMap[objectPath]->setProperty(propertyName, value); + objectMap[objectPath]->setProperty(propertyName, value, callback); return true; } return false; } -void AbstractDBusInterface::setProperty(string propertyName, GVariant *value) +void AbstractDBusInterface::setProperty(string propertyName, GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback) { if(properties.count(propertyName)) { - properties[propertyName]->fromVariant(value); + properties[propertyName]->fromVariant(value, callback); } - else + else if(callback) { - throw -1; + callback(false, AsyncPropertyReply::InvalidOperation); } } @@ -511,8 +600,8 @@ GVariant *AbstractDBusInterface::getProperty(string propertyName) { if(properties.count(propertyName)) return properties[propertyName]->toVariant(); - else - throw -1; + + return nullptr; } void AbstractDBusInterface::setTimeout(int timeout) diff --git a/plugins/common/abstractdbusinterface.h b/plugins/common/abstractdbusinterface.h index a0ba866e..9bff3da9 100644 --- a/plugins/common/abstractdbusinterface.h +++ b/plugins/common/abstractdbusinterface.h @@ -131,9 +131,13 @@ protected: gpointer userData); static gboolean setProperty(GDBusConnection * connection, const gchar * sender, const gchar *objectPath, const gchar *interfaceName, const gchar * propertyName, GVariant *value, - GError** error, gpointer userData); + GError** error, gpointer userData, std::function<void (bool, AsyncPropertyReply::Error)> callback); - virtual void setProperty(std::string propertyName, GVariant * value); + static void handleMyMethodCall(GDBusConnection *connection, const gchar *sender, const gchar *object_path, + const gchar *interface_name, const gchar *method_name, GVariant *parameters, + GDBusMethodInvocation *invocation, gpointer user_data); + + virtual void setProperty(std::string propertyName, GVariant * value, std::function<void (bool, AsyncPropertyReply::Error)> callback); virtual GVariant * getProperty(std::string propertyName); void setTimeout(int timeout); diff --git a/plugins/common/varianttype.cpp b/plugins/common/varianttype.cpp index 46e5ec80..3cd1254f 100644 --- a/plugins/common/varianttype.cpp +++ b/plugins/common/varianttype.cpp @@ -77,7 +77,7 @@ GVariant *VariantType::toVariant() return v->toVariant(); } -void VariantType::fromVariant(GVariant *val) +void VariantType::fromVariant(GVariant *val, std::function<void (bool, AsyncPropertyReply::Error)> callback) { AbstractPropertyType *v = VehicleProperty::getPropertyTypeForPropertyNameValue(name()); v->fromVariant(val); @@ -89,11 +89,9 @@ void VariantType::fromVariant(GVariant *val) request.completed = [&](AsyncPropertyReply* r) { auto reply = amb::make_unique(r); - /// TODO: throw dbus exception - if(!reply->success) - { - DebugOut(DebugOut::Error)<<"SetProperty fail: "<<reply->error<<endl; - } + + if(callback) + callback(reply->success, reply->error); }; routingEngine->setProperty(request); diff --git a/plugins/common/varianttype.h b/plugins/common/varianttype.h index 1c83d9cc..1904ab64 100644 --- a/plugins/common/varianttype.h +++ b/plugins/common/varianttype.h @@ -31,10 +31,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA class AbstractDBusInterface; -using namespace std; - -typedef function<void (boost::any)> SetterFunc; - class VariantType { @@ -57,11 +53,6 @@ public: && other.zoneFilter() == zoneFilter()); } - virtual void setSetterFunction(SetterFunc setterFunc) - { - mSetterFunc = setterFunc; - } - virtual const string signature() { GVariant* var = toVariant(); @@ -100,7 +91,7 @@ public: Zone::Type zoneFilter() { return mZoneFilter; } virtual GVariant* toVariant(); - virtual void fromVariant(GVariant *value); + virtual void fromVariant(GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback); double timestamp() { @@ -166,7 +157,6 @@ protected: AbstractRoutingEngine* routingEngine; string mPropertyName; VehicleProperty::Property mAmbPropertyName; - SetterFunc mSetterFunc; Access mAccess; AbstractPropertyType* mValue; AbstractDBusInterface* mInterface; diff --git a/plugins/database/databasesink.cpp b/plugins/database/databasesink.cpp index 4a9c8b37..8881ae53 100644 --- a/plugins/database/databasesink.cpp +++ b/plugins/database/databasesink.cpp @@ -47,7 +47,7 @@ static void * cbFunc(Shared* shared) NameValuePair<string> zone("zone", boost::lexical_cast<string>(obj.zone)); NameValuePair<string> four("time", boost::lexical_cast<string>(obj.time)); NameValuePair<string> five("sequence", boost::lexical_cast<string>(obj.sequence)); - NameValuePair<string> six("tripId", boost::lexical_cast<string>(shared->tripId)); + NameValuePair<string> six("tripId", shared->tripId); dict.push_back(one); dict.push_back(two); @@ -76,9 +76,8 @@ static void * cbFunc(Shared* shared) /// final flush of whatever is still in the queue: shared->db->exec("BEGIN IMMEDIATE TRANSACTION"); - for(int i=0; i< insertList.size(); i++) + for(auto d : insertList) { - DictionaryList<string> d = insertList[i]; shared->db->insert(d); } shared->db->exec("END TRANSACTION"); @@ -380,6 +379,8 @@ void DatabaseSink::propertyChanged(AbstractPropertyType *value) { VehicleProperty::Property property = value->name; + DebugOut() << "Received property change for " << property << endl; + if(!shared) return; diff --git a/plugins/database/databasesink.h b/plugins/database/databasesink.h index 8976f416..459ec928 100644 --- a/plugins/database/databasesink.h +++ b/plugins/database/databasesink.h @@ -52,7 +52,7 @@ public: bool quit; - bool operator ==(const DBObject & other) const + bool operator == (const DBObject & other) const { return (key == other.key && source == other.source && zone == other.zone && value == other.value && sequence == other.sequence && time == other.time); @@ -88,7 +88,7 @@ namespace std { { size_t operator()(const DBObject & x) const { - return x.key.length(); + return x.key.length() * x.value.length() + x.time; } }; } diff --git a/plugins/dbus/CMakeLists.txt b/plugins/dbus/CMakeLists.txt index 67d6ba65..c4e6fef1 100644 --- a/plugins/dbus/CMakeLists.txt +++ b/plugins/dbus/CMakeLists.txt @@ -25,8 +25,10 @@ set(dbus_mapping_headers ${dbus_mapping_headers} ${CMAKE_CURRENT_SOURCE_DIR}/par set(dbus_mapping_headers ${dbus_mapping_headers} ${CMAKE_CURRENT_SOURCE_DIR}/drivingsafety.h CACHE INTERNAL "dbus mapping headers") set(dbus_mapping_headers ${dbus_mapping_headers} ${CMAKE_CURRENT_SOURCE_DIR}/personalization.h CACHE INTERNAL "dbus mapping headers") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/README ${CMAKE_CURRENT_BINARY_DIR}/dbus.README @ONLY) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/dbus.README DESTINATION ${DOC_INSTALL_DIR}/plugins) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dbus.in.json ${CMAKE_CURRENT_BINARY_DIR}/dbus @ONLY) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/dbus DESTINATION ${PLUGIN_SEGMENT_INSTALL_PATH}) + add_subdirectory(amb-qt) diff --git a/plugins/dbus/automotivemanager.cpp b/plugins/dbus/automotivemanager.cpp index a6b1ca25..64919e05 100644 --- a/plugins/dbus/automotivemanager.cpp +++ b/plugins/dbus/automotivemanager.cpp @@ -340,11 +340,11 @@ static void handleMethodCall(GDBusConnection *connection, } } DebugOut(6) << "member " << propertyToFindStrPtr.get() << " of " << objectNamePtr.get() << " was not found." << endl; - g_dbus_method_invocation_return_value(invocation,g_variant_new("(b)", false)); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", false)); } else { - g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method."); + g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method."); } } @@ -438,14 +438,16 @@ AutomotiveManager::AutomotiveManager(GDBusConnection *connection) regId = g_dbus_connection_register_object(mConnection, "/", mInterfaceInfo, &interfaceVTable, this, NULL, &error); g_dbus_node_info_unref(introspection); - if(error){ - g_error_free(error); + auto errorPtr = amb::make_super(error); + + if(errorPtr){ + DebugOut(DebugOut::Error) << "registering dbus object: " << "'org.automotive.Manager' " << errorPtr->message << endl; throw -1; } g_assert(regId > 0); - g_dbus_connection_signal_subscribe(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL), "org.freedesktop.DBus", "org.freedesktop.DBus", + g_dbus_connection_signal_subscribe(mConnection, "org.freedesktop.DBus", "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus", NULL, G_DBUS_SIGNAL_FLAGS_NONE, signalCallback, this, NULL); } diff --git a/plugins/dbus/dbus.in.json b/plugins/dbus/dbus.in.json new file mode 100644 index 00000000..c924d6c9 --- /dev/null +++ b/plugins/dbus/dbus.in.json @@ -0,0 +1,6 @@ +{ + "name" : "DBusSink", + "path" : "@PLUGIN_INSTALL_PATH@/dbussinkplugin.so", + "frequency" : "30", + "enabled" : true +} diff --git a/plugins/exampleplugin.cpp b/plugins/exampleplugin.cpp index 32901731..4b643c67 100644 --- a/plugins/exampleplugin.cpp +++ b/plugins/exampleplugin.cpp @@ -365,8 +365,10 @@ void ExampleSourcePlugin::randomizeProperties() DebugOut()<<"setting enginespeed to: "<<engineSpeed<<endl; vel.setValue(velocity); + vel.sequence++; vel.priority = AbstractPropertyType::High; es.setValue(engineSpeed); + es.sequence++; es.priority = AbstractPropertyType::Low; ac.setValue(accelerationX); swa.setValue(steeringWheelAngle); diff --git a/plugins/examplesink.cpp b/plugins/examplesink.cpp index 85f3d39f..bd0afef4 100644 --- a/plugins/examplesink.cpp +++ b/plugins/examplesink.cpp @@ -183,7 +183,8 @@ void ExampleSink::supportedChanged(const PropertyList & supportedProperties) for(auto itr = values.begin(); itr != values.end(); itr++) { auto val = *itr; - DebugOut(1)<<"Value from past: ("<<val->name<<"): "<<val->toString()<<" time: "<<val->timestamp<<endl; + DebugOut(1) <<"Value from past: (" << val->name << "): " << val->toString() + <<" time: " << val->timestamp << " sequence: " << val->sequence << endl; } delete reply; diff --git a/plugins/examplesink.in.json b/plugins/examplesink.in.json new file mode 100644 index 00000000..254ce541 --- /dev/null +++ b/plugins/examplesink.in.json @@ -0,0 +1,6 @@ +{ + "name" : "ExampleSink", + "path" : "@PLUGIN_INSTALL_PATH@/examplesinkplugin.so", + "frequency" : "30", + "enabled" : true +} diff --git a/plugins/examplesource.in.json b/plugins/examplesource.in.json new file mode 100644 index 00000000..b221fbab --- /dev/null +++ b/plugins/examplesource.in.json @@ -0,0 +1,6 @@ +{ + "name" : "ExampleSource", + "path" : "@PLUGIN_INSTALL_PATH@/examplesourceplugin.so", + "delay" : "6", + "enabled" : true +} diff --git a/plugins/opencvlux/CMakeLists.txt b/plugins/opencvlux/CMakeLists.txt index de3645c3..da0b8eb2 100644 --- a/plugins/opencvlux/CMakeLists.txt +++ b/plugins/opencvlux/CMakeLists.txt @@ -32,14 +32,11 @@ endif(cuda) find_package(Qt5Core REQUIRED) if(Qt5Core_FOUND) - message(STATUS "using Qt5") - set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ) set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") add_definitions(${Qt5Core_DEFINITIONS}) add_definitions(-DQT_NO_KEYWORDS) - endif(Qt5Core_FOUND) set(CMAKE_AUTOMOC ON) diff --git a/plugins/testplugin/testplugin.cpp b/plugins/testplugin/testplugin.cpp index 5fa32c56..35679a93 100644 --- a/plugins/testplugin/testplugin.cpp +++ b/plugins/testplugin/testplugin.cpp @@ -45,7 +45,7 @@ void testBooleanToStringFromString() boolean.fromString(boolean.toString()); std::string isTrue2 = boolean.toString(); - g_assert(isTrue == isTrue2); + TEST(isTrue == isTrue2); } bool beginsWith(std::string a, std::string b) diff --git a/plugins/websocket/CMakeLists.txt b/plugins/websocket/CMakeLists.txt index 12ebe299..4da95b28 100644 --- a/plugins/websocket/CMakeLists.txt +++ b/plugins/websocket/CMakeLists.txt @@ -8,8 +8,6 @@ include_directories(${CMAKE_SOURCE_DIR}/lib ${include_dirs}) find_package(Qt5Core REQUIRED) if(Qt5Core_FOUND) - message(STATUS "using Qt5") - set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ) set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") @@ -40,11 +38,26 @@ target_link_libraries(websocketsource amb ${websockets_LIBRARIES} -L${CMAKE_CURR configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/vehicle.js ${CMAKE_CURRENT_SOURCE_DIR}/test/vehicle.js) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/test.js ${CMAKE_CURRENT_SOURCE_DIR}/test/test.js) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/events.js ${CMAKE_CURRENT_SOURCE_DIR}/test/events.js) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/index.html ${CMAKE_CURRENT_SOURCE_DIR}/test/index.html) install(TARGETS websocketsource LIBRARY DESTINATION ${PLUGIN_INSTALL_PATH}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/README ${CMAKE_CURRENT_BINARY_DIR}/websocket.README @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/protocol.idl ${CMAKE_CURRENT_BINARY_DIR}/docs/protocol.idl @ONLY) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/websocket.README DESTINATION ${DOC_INSTALL_DIR}/plugins) +if(enable_docs) + find_package(Doxygen) + if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(websocket_docs ALL ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating Websocket protocol documentation with Doxygen" VERBATIM) + + install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${DOC_INSTALL_DIR}/plugins/websocket COMPONENT Docs) + install (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test DESTINATION ${DOC_INSTALL_DIR}/plugins/websocket COMPONENT Docs) + endif(DOXYGEN_FOUND) + +endif(enable_docs) + endif(websocket_plugin) diff --git a/plugins/websocket/Doxyfile.in b/plugins/websocket/Doxyfile.in new file mode 100644 index 00000000..ee0e2735 --- /dev/null +++ b/plugins/websocket/Doxyfile.in @@ -0,0 +1,4 @@ +PROJECT_NAME = @PROJECT_NAME@ +PROJECT_NUMBER = @PROJECT_VERSION@ +GENERATE_LATEX = NO +INPUT = @CMAKE_CURRENT_BINARY_DIR@/docs/ diff --git a/plugins/websocket/protocol b/plugins/websocket/protocol deleted file mode 100644 index 2d723a0f..00000000 --- a/plugins/websocket/protocol +++ /dev/null @@ -1,37 +0,0 @@ -Example protocol messages - -Property changed event: -{"type":"valuechanged","name":"VehicleSpeed","data":{"value": "217","zone": 0, 'type' : 'UInt16', "timestamp":"1354521964.60253","sequence":"0"}, "transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66", } - -Get property request: -{"type":"method","name":"get","data": { "property" : "VehicleSpeed", "zone" : 0 }, "transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Get property reply: -{"type":"methodReply","name":"get","data":{"property":"VehicleSpeed","value":"17", "timestamp" : "1354521964.24962", "sequence": "0" },"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Get supported request: -{"type":"method","name":"getSupported","transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Get supported reply: -{"type":"methodReply","name":"getSupported","data":[{'name' :'EngineSpeed', 'zone' : 0, 'type' : 'UInt16'}, "{'name' :'VehicleSpeed', 'zone' : 0, 'type' : 'UInt16'}],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Supported Changed -{"type":"methodReply","name":"supportedChanged","data":[{'name' :'EngineSpeed', 'zone' : 0, 'type' : 'UInt16'}, "{'name' :'VehicleSpeed', 'zone' : 0, 'type' : 'UInt16'}],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Subscribe to data: -{"type":"method","name":"subscribe","property":"EngineSpeed","transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Subscribe to data reply: -{"type":"methodReply","name":"subscribe","property":"EngineSpeed","transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Unsubscribe to data: -{"type":"method", "name":"unsubscribe", "property":"EngineSpeed", "transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} - -Get History request: -{"type":"method","name":"getRange","data": {"property":"VehicleSpeed", "timeBegin":"1368825008.35948","timeEnd":"1368825018.35948","sequenceBegin":"-1","sequenceEnd":"-1"},"transactionid":"b07589ba-417c-4604-80c6-01c0dcbd524d"} - -Set property request: -{ "type" : "method", "name" : "set", "data" : { "property" : "MachineGunTurretStatus", "value" : "true", "zone" : 0 }, "transactionid" : "4123123123" } - -Set property reply: -{ "type" : "methodReply", "name" : "set", "data" : { "property" : "MachineGunTurretStatus", "value" : "true", "zone" : 0, "success" : true }, "transactionid" : "4123123123" } diff --git a/plugins/websocket/protocol.idl b/plugins/websocket/protocol.idl new file mode 100644 index 00000000..cb24cf1e --- /dev/null +++ b/plugins/websocket/protocol.idl @@ -0,0 +1,440 @@ +/*! + * \mainpage Websocket Plugin Protocol Documentation + * \version @PROJECT_VERSION@ + * + * <a href="../../../html/index.html">Back to AMB Documentation Main</a> + * \section intro Introduction + * This document describes the AMB Websocket protocol. The messages are passed either as JSON or in binary format. The binary format is + * defined by the <a href="http://doc.qt.io/qt-5/qjsondocument.html#toBinaryData">Qt project</a>. The JSON format is described in protocol.idl. + * + * For information about the using the plugin with AMB, see <a href="../../websocket.README">the plugin documentation</a> + * + * \section example Example javascript + * The following is an example of using the websocket protocol in html5. For a more complete example, see the <a href="../test/index.html">html test</a>. + * \code + * socket = new WebSocket('ws://localhost:23000/'); + * socket.onmessage = function (msg) + * { + * // we got a reply! + * console.log(msg); + * }; + * + * socket.onopen = function() { + * var obj = { + * "type" : "method", + * "name" : "getSupported", + * "transactionid" : 'ReallyUniqueId1', + * } + * socket.send(JSON.stringify(obj)); + * }; + * \endcode + */ + +/*! + * \file protocol.idl + * \brief this document describes the websocket protocol + * + */ + +enum MessageType { + "method", + "methodReply", + "valuechanged" +} + + +interface BaseMessage { + + /*! + * \brief transactionid, id for this transaction. For messages with responses, the transaction id is used to match the request with the response. + */ + attribute DOMString transactionid; +} + +enum PropertyType { + "UInt16", + "UInt32", + "Int16", + "Int32", + "String", + "Double", + "Boolean" +} + +/*! + * \brief Property represents an AMB property + */ +interface Property { + + /*! + * \brief property - AMB Property name + */ + attribute DOMString? property; + + /*! + * \brief value, value of the property + */ + attribute DOMString? value; + + /*! + * \brief zone, zone which this property is in + */ + attribute unsigned short zone; + + /*! + * \brief type - type of value + */ + attribute PropertyType? type; + + /*! + * \brief timestamp + */ + attribute unsigned double? timestamp; + + /*! + * \brief sequence + */ + attribute unsigned long? sequence; +} + +/*! + * \brief ValueChanged is a message which is generated when a subscribed Property changes. + * Subscribe will cause this message to be generated. Unsubscribe stops this message. + * \see Subscribe + * \see Unsubscribe + * The following is an example message for this interface: + * \code + * {"type" : "valuechanged", "name" : "VehicleSpeed", "data" : {"value" : "217", "zone" : 0, 'type' : 'UInt16', "timestamp" : "1354521964.60253", "sequence" : "0"}, "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66" } + * \endcode + */ +interface ValueChanged : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "valuechanged"; + + /*! + * \brief name of this message. + * This represents the property name which changed in this message + */ + attribute DOMString name; + + /*! + * \brief data represents the value for the property indicated by 'name' + */ + attribute Property data; +} + +/*! + * \brief GetPropertyRequest - request the value of a property + * The following is an example of this message: + * \code + * {"type" : "method", "name" : "get", "data" : { "property" : "VehicleSpeed", "zone" : 0 }, "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface GetPropertyRequest : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of this message. + * This represents the property name which changed in this message + */ + const DOMString name = "get"; + + /*! + * \brief data represents property to get + * Property attributes type, timestamp, value, and sequence are ignored. + */ + attribute Property data; +} + +/*! + * \brief GetPropertyReply - response to GetPropertyRequest + * The following is an example of this message: + * \code + * {"type" : "methodReply", "name" : "get", "data" : {"property" : "VehicleSpeed", "value" : "17", "timestamp" : "1354521964.24962", "sequence" : "0" }, "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface GetPropertyReply : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "methodReply"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "get"; + + /*! + * \brief data represents the requested property value + */ + attribute Property data; +} + +/*! + * \brief GetSupportedRequest - request supported properties + * The following is an example of this message: + * \code + * {"type" : "method", "name" : "getSupported", "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface GetSupportedRequest : BaseMessage { + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of this method. + */ + const DOMString name = "getSupported"; +} + +/*! + * \brief GetSupportedReply- reply for supported properties + * The following is an example of this message: + * \code + * {"type" : "methodReply", "name" : "getSupported", "data" : [{'property' :'EngineSpeed', 'zone' : 0, 'type' : 'UInt16'}, "{'property' :'VehicleSpeed', 'zone' : 0, 'type' : 'UInt16'}], "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface GetSupportedReply : BaseMessage { + /*! + * \brief message type + */ + const MessageType type = "methodReply"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "getSupported"; + + /*! + * \brief data - array of properties supported by the system + */ + attribute Property[] data; +} + +/*! + * \brief SupportedChanged - message occures when the system's supported properties changes + * NOTE: this message is not being generated in 0.13 + * The following is an example of this message: + * \code + * {"type" : "methodReply", "name" : "supportedChanged", "data" : [{'property' :'EngineSpeed', 'zone' : 0, 'type' : 'UInt16'}, "{'property' :'VehicleSpeed', 'zone' : 0, 'type' : 'UInt16'}], "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface SupportedChanged : BaseMessage { + /*! + * \brief message type + */ + const MessageType type = "methodReply"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "getSupported"; + + /*! + * \brief data - array of properties supported by the system + */ + attribute Property[] data; +} + +/*! + * \brief Subscribe - subscribe request + * The following is an example of this message: + * \code + * {"type" : "method", "name" : "subscribe", "property" : "EngineSpeed", "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface Subscribe : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of the method. + */ + const DOMString name = "subscribe"; + + /*! + * \brief property to subscribe to + */ + attribute DOMString property; +} + +/*! + * \brief Unsubscribe - unsubscribe request + * The following is an example of this message: + * \code + * {"type" : "method", "name" : "unsubscribe", "property" : "EngineSpeed", "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface Unsubscribe : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of the method. + */ + const DOMString name = "subscribe"; + + /*! + * \brief property to subscribe to + */ + attribute DOMString property; +} + +/*! + * \brief GetRangedRequest - request a range of logged properties + * The following is an example of this message: + * \code + * {"type" : "method", "name" : "getRange", "data" : ["VehicleSpeed", "EngineSpeed"], "timeBegin" : "1368825008.35948", + * "timeEnd" : "1368825018.35948", "sequenceBegin" : "-1", "sequenceEnd" : "-1", "transactionid" : "b07589ba-417c-4604-80c6-01c0dcbd524d"} + * \endcode + */ +interface GetRangedRequest : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of the method. + */ + const DOMString name = "getRange"; + + /*! + * \brief data - properties to request + */ + attribute DOMString[] data; + + /*! + * \brief zone, zone which this property is in + */ + attribute unsigned short? zone; + + /*! + * \brief timeBegin in seconds since Unix Epoc + */ + attribute unsigned double? timeBegin; + + /*! + * \brief timeEnd in seconds since Unix Epoc + */ + attribute unsigned double? timeEnd; + + /*! + * \brief sequenceBegin + */ + attribute unsigned long? sequenceBegin; + + /*! + * \brief sequenceEnd + */ + attribute unsigned long? sequenceEnd; +} + +/*! + * \brief GetRangedReply - reply for GetRangedRequest + * The following is an example of this message: + * \code + * {"type" : "methodReply", "name" : "getRanged", "data" : [{'property' :'EngineSpeed', 'zone' : 0, 'type' : 'UInt16'}, "{'property' :'VehicleSpeed', 'zone' : 0, 'type' : 'UInt16'}], "transactionid" : "d293f670-f0b3-11e1-aff1-0800200c9a66"} + * \endcode + */ +interface GetRangedReply : BaseMessage { + /*! + * \brief message type + */ + const MessageType type = "methodReply"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "getRanged"; + + /*! + * \brief data - array of properties supported by the system + */ + attribute Property[] data; +} + +/*! + * \brief SetPropertyRequest - request to set a property + * The following is an example of this message: + * \code + * { "type" : "method", "name" : "set", "data" : { "property" : "MachineGunTurretStatus", "value" : "true", "zone" : 0 }, "transactionid" : "4123123123" } + * \endcode + */ +interface SetPropertyRequest : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "method"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "set"; + + /*! + * \brief data represents the new property value + */ + attribute Property data; +} + +enum Error { + "NoError", + "Timeout", + "InvalidOperation", + "PermissionDenied", + "ZoneNotSupported" +} + +/*! + * \brief SetPropertyReply - reply for SetPropertyRequest + * The following is an example of this message: + * \code + * { "type" : "methodReply", "name" : "set", "data" : { "property" : "MachineGunTurretStatus", "value" : "true", "zone" : 0}, "success" : true, "error" : "NoError", "transactionid" : "4123123123" } + * \endcode + */ +interface SetPropertyReply : BaseMessage { + + /*! + * \brief message type + */ + const MessageType type = "methodReply"; + + /*! + * \brief name of this methodReply. + */ + const DOMString name = "set"; + + /*! + * \brief data represents the new property value + */ + attribute Property data; + + /*! + * \brief success - true if the set operation was successful + */ + attribute boolean success; + + /*! + * \brief error - error code + */ + attribute Error error; +} diff --git a/plugins/websocket/test/servertest/client.html b/plugins/websocket/test/servertest/client.html deleted file mode 100644 index 9ef2ee3b..00000000 --- a/plugins/websocket/test/servertest/client.html +++ /dev/null @@ -1,17 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <title>IVI API Tester</title> - <meta charset="utf-8"> - <link rel="stylesheet" href="../style.css"/> - <script src="../api.js"></script> -</head> -<body onload='init("ws://localhost:23023/vehicle?client", "")'> - <div id="result"> - </div> - <div id="tester"> - </div> - <script src="../events.js"></script> - <script src="../test.js"></script> -</body> -</html> diff --git a/plugins/websocket/test/servertest/server.html b/plugins/websocket/test/servertest/server.html deleted file mode 100644 index 43dc72ab..00000000 --- a/plugins/websocket/test/servertest/server.html +++ /dev/null @@ -1,22 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <title>IVI API Server Test</title> - <meta charset="utf-8"> - <style> -#result { - position: absolute; - height: 99%; - width: 99%; - overflow-y: auto; - word-wrap: break-word; -} - </style> - <script src="../events.js"></script> - <script src="server.js"></script> -</head> -<body> - <div id="result"> - </div> -</body> -</html> diff --git a/plugins/websocket/test/servertest/server.js b/plugins/websocket/test/servertest/server.js deleted file mode 100644 index 3fcda404..00000000 --- a/plugins/websocket/test/servertest/server.js +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (c) 2012, Intel Corporation. - * - * This program is licensed under the terms and conditions of the - * Apache License, version 2.0. The full text of the Apache License is at - * http://www.apache.org/licenses/LICENSE-2.0 - * - */ - -/* --------------------------- utility code ------------------------------- */ - -var PRINT = { - logElement : null, - init : function(log_id) { - this.logElement = document.getElementById(log_id); - }, - - scrollToBottom : function() { - this.logElement.scrollTop = this.logElement.scrollHeight; - }, - - incoming : function(msg) { - this.logElement.innerHTML += "<div style='color: blue'> REQUEST: " + msg + "</div>"; - this.scrollToBottom(); - }, - - outgoing : function(msg) { - this.logElement.innerHTML += "<div style='color: purple'> RESPONSE: " + msg + "</div>"; - this.scrollToBottom(); - }, - - pass : function(msg) { - this.logElement.innerHTML += "<div style='color: green'> SUCCESS: " + msg + "</div>"; - this.scrollToBottom(); - }, - - fail : function(msg) { - this.logElement.innerHTML += "<div style='color: red'> FAIL: " + msg + "</div>"; - this.scrollToBottom(); - }, - - log : function(msg) { - this.logElement.innerHTML += "<div class='LogClass'> " + msg + "</div>"; - this.scrollToBottom(); - }, -} - -/* ----------------------------- test code --------------------------------- */ - -function VehicleServer(socketUrl) -{ - var self = this; - this.vehicleEventType = new VehicleEventType(); - this.subscriptions = []; - - this.Signal = function(name) - { - var me = this; - this.users = 0; - this.name = name; - this.start = function() { - if(me.users <= 0) - { - var interval = Math.floor(Math.random()*5000) + 1000; - me.timer = setInterval(function() { - var value = parseInt(self.vehicleEventType.getValue(me.name)) + 1; - self.vehicleEventType.setValue(me.name, value); - var obj = { - "type" : "valuechanged", - "name": me.name, - "data" : value - }; - self.socket.send(JSON.stringify(obj)); - }, interval); - } - me.users = 1; - } - this.stop = function() { - me.users--; - if((me.users <= 0)&&(me.timer != undefined)) - { - clearInterval(me.timer); - } - } - } - - function init() { - if ("WebSocket" in window) - { - var list = self.vehicleEventType.getValueEventList(); - for(var i = 0; i < list.length; i++) - { - self.subscriptions[i] = new self.Signal(list[i]); - } - - self.socket = new WebSocket(socketUrl); - self.socket.onopen = function() - { - PRINT.pass("Server READY"); - }; - self.socket.onclose = function() - { - PRINT.fail("Server CLOSED"); - }; - self.socket.onerror = function(e) - { - PRINT.fail("Server ERROR: "+e.data); - }; - self.socket.onmessage = function (e) - { - self.receive(e.data); - }; - } - else - { - PRINT.fail("This browser doesn't appear to support websockets!"); - } - } - init(); -} - -VehicleServer.prototype.subscribe = function(list) -{ - for(var i = 0; i < this.subscriptions.length; i++) - { - if(list.indexOf(this.subscriptions[i].name) >= 0) - { - this.subscriptions[i].start(); - } - } -} - -VehicleServer.prototype.unsubscribe = function(list) -{ - for(var i = 0; i < this.subscriptions.length; i++) - { - if(list.indexOf(this.subscriptions[i].name) >= 0) - { - this.subscriptions[i].stop(); - } - } -} - -VehicleServer.prototype.receive = function(msg) -{ - var event = JSON.parse(msg); - /* accept only methods with transaction ids */ - if((event == undefined)||(event.transactionid == undefined)|| - (event.type != "method")) - { - return; - } - - var obj; - PRINT.incoming(msg); - if(event.name === "getSupportedEventTypes") - { - var data; - if(event.writeable) - { - data = this.vehicleEventType.getValueEventList(event.data); - } - else - { - data = this.vehicleEventType.getSupportedEventList(event.data); - } - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "data" : data - }; - } - else if(event.name === "get") - { - var names = this.vehicleEventType.getValuesEventList(event.data); - if(names.length > 0) - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "data" : [] - }; - for(i in names) - { - var value = this.vehicleEventType.getValue(names[i]); - obj.data.push({"name" : names[i], "value" : value}); - } - } - else - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "error" : event.data + " is not a valid event" - }; - } - } - else if(event.name === "set") - { - var bad = []; - var good = []; - for(var i = 0; i < event.data.length; i++) - { - if((event.data[i].value != undefined) && - this.vehicleEventType.isValueEvent(event.data[i].property)) - { - this.vehicleEventType.setValue(event.data[i].property, parseInt(event.data[i].value)); - good[good.length] = event.data[i].property; - } - else - { - bad[bad.length] = event.data[i].property; - } - } - - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid - }; - - if(bad.length > 0) - { - obj.error = "Failed to set:"; - for(var i = 0; i < bad.length; i++) - { - obj.error += " "+bad[i]; - } - } - - if(good.length > 0) - { - obj.data = "Successfully set:"; - for(var i = 0; i < good.length; i++) - { - obj.data += " "+good[i]; - } - } - } - else if(event.name === "subscribe") - { - var names = this.vehicleEventType.getValuesEventList(event.data); - if(names.length > 0) - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "data" : names - }; - for(i in names) - { - this.subscribe(names[i]); - } - } - else - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "error" : "no valid events provided" - }; - } - } - else if(event.name === "unsubscribe") - { - var names = this.vehicleEventType.getValuesEventList(event.data); - if(names.length > 0) - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "data" : names - }; - for(i in names) - { - this.unsubscribe(names[i]); - } - } - else - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "error" : "no valid events provided" - }; - } - } - else - { - obj = { - "type" : "methodReply", - "name": event.name, - "transactionid" : event.transactionid, - "error" : event.name + " is not a valid method" - }; - } - PRINT.outgoing(JSON.stringify(obj)); - this.socket.send(JSON.stringify(obj)); -} - -window.addEventListener('load', function () { - "use strict"; - PRINT.init("result"); - var server = new VehicleServer("ws://localhost:23023/vehicle?server"); -}); diff --git a/plugins/websocket/websocketsinkmanager.cpp b/plugins/websocket/websocketsinkmanager.cpp index b75db03a..5975c65f 100644 --- a/plugins/websocket/websocketsinkmanager.cpp +++ b/plugins/websocket/websocketsinkmanager.cpp @@ -191,19 +191,23 @@ void WebSocketSinkManager::addSingleShotRangedSink(libwebsocket* socket, Propert rangedRequest.timeEnd = end; rangedRequest.sequenceBegin = seqstart; rangedRequest.sequenceEnd = seqend; + rangedRequest.properties = properties; - rangedRequest.completed = [socket,id](AsyncRangePropertyReply* reply) + rangedRequest.completed = [socket, id](AsyncRangePropertyReply* reply) { QVariantMap replyvar; QVariantList list; std::list<AbstractPropertyType*> values = reply->values; - for(auto itr = values.begin(); itr != values.end(); itr++) + for(auto value : values) { QVariantMap obj; - obj["value"]= (*itr)->toString().c_str(); - obj["timestamp"] = (*itr)->timestamp; - obj["sequence"] = (*itr)->sequence; + obj["name"] = value->name.c_str(); + obj["property"] = value->name.c_str(); + obj["value"] = value->toString().c_str(); + obj["timestamp"] = value->timestamp; + obj["zone"] = value->zone; + obj["sequence"] = value->sequence; list.append(obj); } @@ -242,27 +246,33 @@ void WebSocketSinkManager::removeSink(libwebsocket* socket,VehicleProperty::Prop lwsWriteVariant(socket, reply); } } -void WebSocketSinkManager::setValue(libwebsocket* socket,VehicleProperty::Property property,string value,Zone::Type zone,string uuid) +void WebSocketSinkManager::setValue(libwebsocket* socket, VehicleProperty::Property property, string value,Zone::Type zone, string uuid) { - AbstractPropertyType* type = VehicleProperty::getPropertyTypeForPropertyNameValue(property,value); + AbstractPropertyType* type = VehicleProperty::getPropertyTypeForPropertyNameValue(property, value); AsyncSetPropertyRequest request; request.property = property; + request.value = type; request.zoneFilter = zone; request.completed = [&](AsyncPropertyReply* reply) { QVariantMap data; - data["property"] = property.c_str(); - data["zone"] = zone; - data["source"] = reply->value->sourceUuid.c_str(); - data["success"] = reply->success; + if(reply->value) + { + data["property"] = property.c_str(); + data["zone"] = zone; + data["source"] = reply->value->sourceUuid.c_str(); + data["value"] = reply->value->toString().c_str(); + } QVariantMap replyvar; replyvar["type"]="methodReply"; replyvar["name"]="set"; replyvar["data"]= data; replyvar["transactionid"]=uuid.c_str(); + replyvar["success"] = reply->success; + replyvar["error"] = AsyncPropertyReply::errorToStr(reply->error).c_str(); lwsWriteVariant(socket, replyvar); @@ -452,16 +462,21 @@ static int websocket_callback(struct libwebsocket_context *context,struct libweb { if(name == "getRanged") { - QVariantMap data = call["data"].toMap(); + QVariant dataVariant = call["data"]; + + QVariantList data = dataVariant.toList(); PropertyList propertyList; - propertyList.push_back(data["property"].toString().toStdString()); + Q_FOREACH(QVariant v, data) + { + propertyList.push_back(v.toString().toStdString()); + } - double timeBegin = data["timeBegin"].toDouble(); - double timeEnd = data["timeEnd"].toDouble(); - double sequenceBegin = data["sequenceBegin"].toInt(); - double sequenceEnd = data["sequenceEnd"].toInt(); + double timeBegin = call["timeBegin"].toDouble(); + double timeEnd = call["timeEnd"].toDouble(); + int sequenceBegin = call["sequenceBegin"].toInt(); + int sequenceEnd = call["sequenceEnd"].toInt(); if ((timeBegin < 0 && timeEnd > 0) || (timeBegin > 0 && timeEnd < 0)) { @@ -473,7 +488,7 @@ static int websocket_callback(struct libwebsocket_context *context,struct libweb } else { - sinkManager->addSingleShotRangedSink(wsi,propertyList,timeBegin,timeEnd,sequenceBegin,sequenceEnd,id); + sinkManager->addSingleShotRangedSink(wsi, propertyList, timeBegin, timeEnd, sequenceBegin, sequenceEnd, id); } } else if (name == "get") @@ -495,7 +510,7 @@ static int websocket_callback(struct libwebsocket_context *context,struct libweb { zone = data["zone"].toInt(); } - sinkManager->setValue(wsi,data["property"].toString().toStdString(), data["value"].toString().toStdString(), zone, id); + sinkManager->setValue(wsi, data["property"].toString().toStdString(), data["value"].toString().toStdString(), zone, id); } else if (name == "subscribe") { @@ -530,6 +545,7 @@ static int websocket_callback(struct libwebsocket_context *context,struct libweb QVariantMap map; map["zone"] = zone; map["name"] = i.c_str(); + map["property"] = i.c_str(); map["type"] = basicType.c_str(); map["source"] = source.c_str(); diff --git a/plugins/websocket/websocketsinkmanager.h b/plugins/websocket/websocketsinkmanager.h index 1d1594e8..1b2fba64 100644 --- a/plugins/websocket/websocketsinkmanager.h +++ b/plugins/websocket/websocketsinkmanager.h @@ -45,7 +45,7 @@ public: void init(); std::map<std::string, list<WebSocketSink*> > m_sinkMap; void setConfiguration(map<string, string> config); - void setValue(libwebsocket* socket,VehicleProperty::Property property,string value, Zone::Type zone, string uuid); + void setValue(libwebsocket* socket,VehicleProperty::Property property, string value, Zone::Type zone, string uuid); PropertyList getSupportedProperties(); AbstractRoutingEngine * router() { return routingEngine; } diff --git a/plugins/websocket/websocketsource.cpp b/plugins/websocket/websocketsource.cpp index eb0211a5..f4b2ff9e 100644 --- a/plugins/websocket/websocketsource.cpp +++ b/plugins/websocket/websocketsource.cpp @@ -391,8 +391,6 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket string name = call["name"].toString().toStdString(); string id = call["transactionid"].toString().toStdString(); - list<pair<string,string> > pairdata; - if(type == "multiframe") { manager->expectedMessageFrames = call["frames"].toInt(); @@ -464,7 +462,7 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket { QVariantMap d = p.toMap(); Zone::Type zone = d["zone"].toInt(); - std::string name = d["name"].toString().toStdString(); + std::string name = d["property"].toString().toStdString(); std::string proptype = d["type"].toString().toStdString(); std::string source = d["source"].toString().toStdString(); @@ -484,12 +482,17 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket { QVariantMap obj = d.toMap(); - std::string name = obj["name"].toString().toStdString(); + std::string name = obj["property"].toString().toStdString(); std::string value = obj["value"].toString().toStdString(); double timestamp = obj["timestamp"].toDouble(); int sequence = obj["sequence"].toInt(); AbstractPropertyType* type = VehicleProperty::getPropertyTypeForPropertyNameValue(name, value); + if(!type) + { + DebugOut() << "TODO: support custom types here: " << endl; + continue; + } type->timestamp = timestamp; type->sequence = sequence; @@ -511,7 +514,7 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket else if (name == "get") { - DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Got \"GET\" event:" << pairdata.size()<<endl; + DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Got \"GET\" reply" << endl; if (source->uuidReplyMap.find(id) != source->uuidReplyMap.end()) { QVariantMap obj = call["data"].toMap(); @@ -522,14 +525,15 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket int sequence = obj["sequence"].toInt(); Zone::Type zone = obj["zone"].toInt(); - AbstractPropertyType* v = VehicleProperty::getPropertyTypeForPropertyNameValue(property, value); + auto v = amb::make_unique(VehicleProperty::getPropertyTypeForPropertyNameValue(property, value)); + v->timestamp = timestamp; v->sequence = sequence; v->zone = zone; if (source->uuidReplyMap.find(id) != source->uuidReplyMap.end() && source->uuidReplyMap[id]->error != AsyncPropertyReply::Timeout) { - source->uuidReplyMap[id]->value = v; + source->uuidReplyMap[id]->value = v.get(); source->uuidReplyMap[id]->success = true; source->uuidReplyMap[id]->completed(source->uuidReplyMap[id]); source->uuidReplyMap.erase(id); @@ -539,15 +543,56 @@ static int callback_http_only(libwebsocket_context *context, struct libwebsocket { DebugOut() << "get methodReply has been recieved, without a request being in!. This is likely due to a request coming in after the timeout has elapsed.\n"; } - - delete v; } else { DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "GET Method Reply INVALID! Multiple properties detected, only single are supported!!!" << "\n"; } - //data will contain a property/value map. + + } + else if (name == "set") + { + DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Got \"SET\" event" << endl; + std::string id = call["transactionid"].toString().toStdString(); + + if(source->setReplyMap.find(id) != source->setReplyMap.end() && source->setReplyMap[id]->error != AsyncPropertyReply::Timeout) + { + AsyncPropertyReply* reply = source->setReplyMap[id]; + + reply->success = call["success"].toBool(); + reply->error = AsyncPropertyReply::strToError(call["error"].toString().toStdString()); + + QVariantMap obj = call["data"].toMap(); + + std::string property = obj["property"].toString().toStdString(); + std::string value = obj["value"].toString().toStdString(); + + double timestamp = obj["timestamp"].toDouble(); + int sequence = obj["sequence"].toInt(); + Zone::Type zone = obj["zone"].toInt(); + + auto v = amb::make_unique(VehicleProperty::getPropertyTypeForPropertyNameValue(property, value)); + + if(v) + { + v->timestamp = timestamp; + v->sequence = sequence; + v->zone = zone; + } + else + { + throw std::runtime_error("property may not be registered."); + } + + reply->value = v.get(); + reply->completed(reply); + source->setReplyMap.erase(id); + } + } + else + { + DebugOut(DebugOut::Warning) << "Unhandled methodReply: " << name << endl; } } @@ -698,7 +743,6 @@ void WebSocketSource::getRangePropertyAsync(AsyncRangePropertyReply *reply) replyvar["sequenceBegin"] = reply->sequenceBegin; replyvar["sequenceEnd"] = reply->sequenceEnd; - QStringList properties; for (auto itr = reply->properties.begin(); itr != reply->properties.end(); itr++) @@ -716,23 +760,23 @@ AsyncPropertyReply * WebSocketSource::setProperty( AsyncSetPropertyRequest reque { AsyncPropertyReply* reply = new AsyncPropertyReply(request); + std::string uuid = amb::createUuid(); + QVariantMap data; data["property"] = request.property.c_str(); data["value"] = request.value->toString().c_str(); data["zone"] = request.zoneFilter; - QVariantMap replyvar; replyvar["type"] = "method"; replyvar["name"] = "set"; replyvar["data"] = data; - replyvar["transactionid"] = amb::createUuid().c_str(); + replyvar["transactionid"] = uuid.c_str(); lwsWriteVariant(clientsocket, replyvar); - ///TODO: we should actually wait for a response before we simply complete the call - reply->success = true; - reply->completed(reply); + setReplyMap[uuid] = reply; + return reply; } diff --git a/plugins/websocket/websocketsource.h b/plugins/websocket/websocketsource.h index c19a6e86..4af23ba2 100644 --- a/plugins/websocket/websocketsource.h +++ b/plugins/websocket/websocketsource.h @@ -52,9 +52,10 @@ public: void supportedChanged(const PropertyList &) {} void setConfiguration(std::map<std::string, std::string> config); - std::map<std::string,AsyncPropertyReply*> uuidReplyMap; + std::map<std::string, AsyncPropertyReply*> uuidReplyMap; std::map<std::string,double> uuidTimeoutMap; std::map<std::string, AsyncRangePropertyReply*> uuidRangedReplyMap; + std::map<std::string, AsyncPropertyReply*> setReplyMap; int partialMessageIndex; QByteArray incompleteMessage; diff --git a/tools/ambctl.py b/tools/ambctl.py index 3d79a7fe..0a84d26c 100644 --- a/tools/ambctl.py +++ b/tools/ambctl.py @@ -22,34 +22,121 @@ class bcolors: WHITE = '\x1b[37m' BLUE = '\x1b[34m' +class Autocomplete: + class Cmd: + name = "" + description = "" + def __init__(self, n, d): + self.name = n + self.description = d + + commands = [] + properties = [] + + def __init__(self): + self.commands = [] + self.commands.append(Autocomplete.Cmd('help', 'Prints help data (also see COMMAND help)')) + self.commands.append(Autocomplete.Cmd('list', 'List supported ObjectNames')) + self.commands.append(Autocomplete.Cmd('get', 'Get properties from an ObjectName')) + self.commands.append(Autocomplete.Cmd('listen', 'Listen for changes on an ObjectName')) + self.commands.append(Autocomplete.Cmd('set', 'Set a property for an ObjectName')) + self.commands.append(Autocomplete.Cmd('getHistory', 'Get logged data within a time range')) + self.commands.append(Autocomplete.Cmd('plugin', 'enable, disable and get info on a plugin')) + self.commands.append(Autocomplete.Cmd('quit', 'Exit ambctl')) + + bus = dbus.SystemBus() + try: + managerObject = bus.get_object("org.automotive.message.broker", "/"); + managerInterface = dbus.Interface(managerObject, "org.automotive.Manager") + self.properties = managerInterface.List() + except dbus.exceptions.DBusException, error: + print error + + def complete(self, partialString): + results = [] + + sameString = "" + + for cmd in self.commands: + if not (len(partialString)) or cmd.name.startswith(partialString): + results.append(cmd.name) + + for property in self.properties: + if not(len(partialString)) or property.startswith(partialString): + results.append(str(property)) + + if len(results) > 1 and len(results[0]) > 0: + for i in range(len(results[0])): + for j in range(len(results[0])-i+1): + if j > len(sameString) and all(results[0][i:i+j] in x for x in results): + sameString = results[0][i:i+j] + elif len(results) == 1: + sameString = results[0] + + return results, sameString + + def help(): - help = ("Available commands:\n" - +bcolors.HEADER+ "help" +bcolors.WHITE+ " Prints help data\n" - +bcolors.HEADER+ "list" +bcolors.WHITE+ " List supported ObjectNames\n" - +bcolors.HEADER+ "get" +bcolors.WHITE+ " Get properties from an ObjectName\n" - +bcolors.HEADER+ "listen" +bcolors.WHITE+ " Listen for changes on an ObjectName\n" - +bcolors.HEADER+ "set" +bcolors.WHITE+ " Set a property for an ObjectName\n" - +bcolors.HEADER+ "getHistory" +bcolors.WHITE+ " Get logged data within a time range\n" - +bcolors.HEADER+ "quit" +bcolors.WHITE+ " Exit ambctl\n") + help = ("Available commands:\n") + autocomplete = Autocomplete() + for cmd in autocomplete.commands: + help += bcolors.HEADER + cmd.name + bcolors.WHITE + for i in range(1, 15 - len(cmd.name)): + help += " " + help += cmd.description + "\n" + return help def changed(interface, properties, invalidated): print json.dumps(properties, indent=2) +def listPlugins(): + list = [] + for root, dirs, files in os.walk('@PLUGIN_SEGMENT_INSTALL_PATH@'): + for file in files: + fullpath = root + "/" + file; + pluginFile = open(fullpath) + data = json.load(pluginFile) + data['segmentPath'] = fullpath + pluginFile.close() + list.append(data) + return list + +def enablePlugin(pluginName, enabled): + list = listPlugins() + + for plugin in list: + if plugin["name"] == pluginName: + try : + plugin["enabled"] = enabled + file = open(plugin["segmentPath"], 'rw+') + plugin.pop('segmentPath', None) + fixedData = json.dumps(plugin, separators=(', ', ' : '), indent=4) + file.truncate() + file.write(fixedData) + file.close() + return True + except IOError, error: + print error + return False + return False + + + def processCommand(command, commandArgs, noMain=True): if command == 'help': - print help() - return 1 + print help() + return 1 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() try: - managerObject = bus.get_object("org.automotive.message.broker", "/"); - managerInterface = dbus.Interface(managerObject, "org.automotive.Manager") + managerObject = bus.get_object("org.automotive.message.broker", "/"); + managerInterface = dbus.Interface(managerObject, "org.automotive.Manager") except: - print "Error connecting to AMB. is AMB running?" - return 1 + print "Error connecting to AMB. is AMB running?" + return 1 if command == "list" : supportedList = managerInterface.List() @@ -112,7 +199,7 @@ def processCommand(command, commandArgs, noMain=True): if property == realValue: print propertyName + " = ", property else: - print "Error setting property" + print "Error setting property. Expected value: ", realValue, " Received: ", property return 1 elif command == "getHistory": if len(commandArgs) == 0: @@ -136,13 +223,52 @@ def processCommand(command, commandArgs, noMain=True): object = managerInterface.FindObjectForZone(objectName, zone); propertiesInterface = dbus.Interface(bus.get_object("org.automotive.message.broker", object),"org.automotive."+objectName) print json.dumps(propertiesInterface.GetHistory(start, end), indent=2) + elif command == "plugin": + if len(commandArgs) == 0: + commandArgs = ['help'] + if commandArgs[0] == 'help': + print "[list] [pluginName] [on/off]" + return 1 + elif commandArgs[0] == 'list': + for plugin in listPlugins(): + print plugin['name'] + return 1 + elif len(commandArgs) == 1: + for plugin in listPlugins(): + if plugin['name'] == commandArgs[0]: + print json.dumps(plugin, indent=4) + return 1 + else: + print "name: " + plugin['name'] + "==?" + commandArgs[0] + print "plugin not found: ", commandArgs[0] + return 0 + else: + if len(commandArgs) < 2: + return 1 + enArg = commandArgs[1] + if not enArg == "on" and not enArg == "off": + print "please use 'on' or 'off' to enable/disable the plugin" + return 1 + plugin = commandArgs[0] + enabled = enArg == "on" + enStr = "disabled" + if enablePlugin(plugin, enabled): + if enabled: + enStr = "enabled" + else: + print "Error could not enable", plugin + + print plugin, enStr + + return 1 + else: print "Invalid command" return 1 -parser = argparse.ArgumentParser(prog="ambctl", description='Process DBus mappings.', add_help=False) +parser = argparse.ArgumentParser(prog="ambctl", description='Automotive Message Broker DBus client tool', add_help=False) parser.add_argument('command', metavar='COMMAND [help]', nargs='?', default='stdin', help='amb dbus command') parser.add_argument('commandArgs', metavar='ARG', nargs='*', help='amb dbus command arguments') @@ -166,6 +292,7 @@ if args.command == "stdin": fullprompt = promptAmbctl + promptEnd curpos = 0 historypos = -1 + autocomplete = Autocomplete() def full_line_len(self): return len(self.fullprompt) + len(self.line) def insert(self, str): @@ -262,65 +389,105 @@ if args.command == "stdin": #print "char: ", ord(str) if len(str) > 1: - if ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 68: #left arrow - if data.arrow_back(): - cursor_left() - sys.stdout.flush() - elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 67: #right arrow - if data.arrow_forward(): - cursor_right() - sys.stdout.flush() - elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 70: #end - while data.arrow_forward(): - cursor_right() - sys.stdout.flush() - elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 72: #home - while data.arrow_back(): - cursor_left() - sys.stdout.flush() - elif len(str) == 4 and ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 51 and ord(str[3]) == 126: #del - data.delete() - redraw(data) - elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 65: - #up arrow - data.save_temp() - data.history_up() - while data.arrow_forward(): - cursor_right() - redraw(data) - elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 66: - #down arrow - data.history_down() - while data.arrow_forward(): - cursor_right() - redraw(data) - elif ord(str) == 10: #enter - if data.line == "": - return True - print "" - words = data.line.split(' ') - if words[0] == "quit": - termios.tcsetattr(fd, termios.TCSAFLUSH, old) - fcntl.fcntl(fd, fcntl.F_SETFL, oldflags) - sys.exit() - try: - if len(words) > 1: - processCommand(words[0], words[1:]) - else: - processCommand(words[0], []) - except dbus.exceptions.DBusException, error: - print error - except: - print "Error running command ", sys.exc_info()[0] - data.push(); - data.clear() + if ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 68: + #left arrow + if data.arrow_back(): + cursor_left() + sys.stdout.flush() + elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 67: + #right arrow + if data.arrow_forward(): + cursor_right() + sys.stdout.flush() + elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 70: + #end + while data.arrow_forward(): + cursor_right() + sys.stdout.flush() + elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 72: #home + while data.arrow_back(): + cursor_left() + sys.stdout.flush() + elif len(str) == 4 and ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 51 and ord(str[3]) == 126: + #del + data.delete() redraw(data) - elif ord(str) == 127: #backspace - data.back_space() + elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 65: + #up arrow + data.save_temp() + data.history_up() + while data.arrow_forward(): + cursor_right() redraw(data) - elif curses.ascii.isalnum(ord(str)) or ord(str) == curses.ascii.SP: #regular text - data.insert(str) + elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 66: + #down arrow + data.history_down() + while data.arrow_forward(): + cursor_right() redraw(data) + elif ord(str) == 10: + #enter + if data.line == "": + return True + print "" + words = data.line.split(' ') + if words[0] == "quit": + termios.tcsetattr(fd, termios.TCSAFLUSH, old) + fcntl.fcntl(fd, fcntl.F_SETFL, oldflags) + sys.exit() + try: + if len(words) > 1: + processCommand(words[0], words[1:]) + else: + processCommand(words[0], []) + except dbus.exceptions.DBusException, error: + print error + except: + print "Error running command ", sys.exc_info()[0] + data.push(); + data.clear() + redraw(data) + elif ord(str) == 127: #backspace + data.back_space() + redraw(data) + elif ord(str) == 9: + #tab + #get last string: + wordsList = data.line.split(' ') + toComplete = wordsList[-1] + results, samestring = data.autocomplete.complete(toComplete) + if len(samestring) and len(samestring) > len(toComplete) and not (samestring == toComplete): + if len(wordsList) > 1: + data.line = ' '.join(wordsList[0:-1]) + ' ' + samestring + else: + data.line = samestring + while data.arrow_forward(): + cursor_right() + + elif len(results) and not results[0] == toComplete: + print "" + if len(results) < 3: + print ' '.join(results) + else: + longestLen = 0 + for r in results: + if len(r) > longestLen: + longestLen = len(r) + for i in range(0, len(results) / 3): + row = "" + endRow = -1 + if len(results) >= i+3: + endRow = 2 + for col in results[i : endRow]: + row += col + for i in range((longestLen + 5) - len(col)): + row += ' ' + print row + + redraw(data) + elif curses.ascii.isalnum(ord(str)) or ord(str) == curses.ascii.SP: #regular text + data.insert(str) + redraw(data) return True print "@PROJECT_PRETTY_NAME@ @PROJECT_VERSION@" @@ -349,6 +516,7 @@ if args.command == "stdin": finally: termios.tcsetattr(fd, termios.TCSAFLUSH, old) fcntl.fcntl(fd, fcntl.F_SETFL, oldflags) + print "" sys.exit() else: diff --git a/tools/genmapping.py b/tools/genmapping.py index 143a67fc..136f9db7 100755 --- a/tools/genmapping.py +++ b/tools/genmapping.py @@ -17,13 +17,22 @@ interfaces = [] class Member: ambName = "" memberName = "" - + interfaceName = "" + def __init__(self, ifaceName): + self.interfaceName = ifaceName def __repr__(self): return "Member" def toString(self): return "{" + self.ambName + " => " + self.memberName + "}" def toIdl(self): - return ' const DOMString ' + self.ambName + ' = "' + self.memberName + '";\n' + idl = [] + idl.append('') + idl.append('\t/*!') + idl.append('\t * \\brief corresponds with DBus property ' + self.memberName + ' for interface org.automotive.' + self.interfaceName) + idl.append('\t * AMB fulfills this member with VehicleProperty::' + self.ambName) + idl.append('\t */') + idl.append('\tconst DOMString ' + self.ambName + ' = "' + self.memberName + '";\n') + return '\n'.join(idl) class Interface: def __init__(self): @@ -37,7 +46,7 @@ class Interface: output += member.toString() + "," return output def toIdl(self): - output = "interface " + self.name + " {\n" + output = "/*! \n * \\brief Corresponds with DBus Interface org.automotive." + self.name + "\n */\ninterface " + self.name + " {\n" for member in self.members: output += member.toIdl() output += "\n};\n" @@ -59,7 +68,7 @@ for input in args.mappingFiles: wantPropertyVariant = 'wantPropertyVariant(' i = line.find(wantPropertyVariant) if i!= -1: - member = Member() + member = Member(interfaces[-1].name) ambNameEnd = line.find(', "')-2 member.ambName = line[i+len(wantPropertyVariant) : i + ambNameEnd].replace("VehicleProperty::", "") memberNameBeg = line.find(', "')+3 @@ -78,7 +87,9 @@ with outputFile: " * \\brief This describes the AMB internal property names to AMB DBus interface property names\n" " * AMB internal property names are designed to be flat variable names (ie, 'ConvertableRoofStatus'). The DBus\n" " * properties however follow the naming scheme defined in the W3C automotive business group vehicle <a href='http://w3c.github.io/automotive-bg/data_spec.html'>data specification</a>\n" - " * The pattern each interface is 'const DOMString AMBProperty = DBusProperty' where 'AMBProperty' is the internal name and 'DBusProperty' is the DBus property name") + " * The pattern each interface is 'const DOMString AMBProperty = DBusProperty' where 'AMBProperty' is the internal name and 'DBusProperty' is the DBus property name.\n" + " *\n" + " * For documentation on the interface and members, please see amb.fidl or the AMB DBus documentation.\n") header += " */\n\n" outputFile.write(header) for iface in interfaces: diff --git a/xwalk/CMakeLists.txt b/xwalk/CMakeLists.txt index fb96843f..c2ae60f4 100644 --- a/xwalk/CMakeLists.txt +++ b/xwalk/CMakeLists.txt @@ -2,14 +2,14 @@ if(xwalk_vehicle_extension) pkg_check_modules(gio REQUIRED gio-2.0) -set(vehicle_api_headers vehicle.h vehicle_instance.h vehicle_extension.h common/extension.h common/picojson.h common/utils.h common/virtual_fs.h - common/XW_Extension_EntryPoints.h common/XW_Extension.h common/XW_Permissions.h common/XW_Extension_Runtime.h common/XW_Extension_SyncMessage.h) -set(vehicle_api_sources vehicle.cc vehicle_extension.cc vehicle_instance.cc common/extension.cc common/picojson.h) +set(vehicle_api_headers vehicle.h vehicle_instance.h vehicle_extension.h common/extension.h common/utils.h common/virtual_fs.h + common/XW_Extension_EntryPoints.h common/XW_Extension.h common/XW_Permissions.h common/XW_Extension_Runtime.h common/XW_Extension_SyncMessage.h) +set(vehicle_api_sources vehicle.cc vehicle_extension.cc vehicle_instance.cc common/extension.cc) include_directories(${include_dirs} ${CMAKE_CURRENT_SOURCE_DIR}/) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vehicle_api.cc - COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/generate_api.py ${CMAKE_CURRENT_SOURCE_DIR}/vehicle_api.js kSource_vehicle_api ${CMAKE_CURRENT_BINARY_DIR}/vehicle_api.cc ) + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/generate_api.py ${CMAKE_CURRENT_SOURCE_DIR}/vehicle_api.js kSource_vehicle_api ${CMAKE_CURRENT_BINARY_DIR}/vehicle_api.cc ) add_library(vehicle_extension MODULE ${vehicle_api_sources} ${CMAKE_CURRENT_BINARY_DIR}/vehicle_api.cc) target_link_libraries(vehicle_extension ${link_libraries} amb ${gio_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib) diff --git a/xwalk/vehicle.cc b/xwalk/vehicle.cc index cea84802..13275f0f 100644 --- a/xwalk/vehicle.cc +++ b/xwalk/vehicle.cc @@ -9,6 +9,7 @@ #include <gio/gio.h> #include <glib.h> #include <listplusplus.h> +#include <picojson.h> #include <superptr.hpp> #include <algorithm> @@ -16,7 +17,6 @@ #include <memory> #include "common/extension.h" -#include "common/picojson.h" common::Instance* Vehicle::CallbackInfo::instance = nullptr; @@ -320,6 +320,45 @@ Vehicle::Vehicle(common::Instance* instance) DebugOut(DebugOut::Error) << "calling GetAutomotiveManager: " << error_ptr->message << endl; } + + + GError* list_error = nullptr; + auto supported_list_variant = amb::make_super( + g_dbus_proxy_call_sync(manager_proxy_.get(), + "List", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &list_error)); + + auto list_error_ptr = amb::make_super(list_error); + + if (list_error_ptr) { + DebugOut(DebugOut::Error) << "error calling List: " + << error_ptr->message << endl; + return; + } + + GVariantIter iter; + g_variant_iter_init(&iter, supported_list_variant.get()); + picojson::array list; + + gchar* propertyName = nullptr; + + while(g_variant_iter_next(&iter,"s", &propertyName)) + { + auto propertyNamePtr = amb::make_super(propertyName); + + std::string p = propertyNamePtr.get(); + + std::transform(p.begin(), p.begin()+1, p.begin(), ::tolower); + list.push_back(picojson::value(p)); + } + + picojson::object obj; + obj["method"] = picojson::value("vehicleSupportedAttributes"); + obj["value"] = picojson::value(list); + + instance->PostMessage(picojson::value(obj).serialize().c_str()); } Vehicle::~Vehicle() { diff --git a/xwalk/vehicle.h b/xwalk/vehicle.h index 9308b105..877f031f 100644 --- a/xwalk/vehicle.h +++ b/xwalk/vehicle.h @@ -8,13 +8,13 @@ #include <abstractpropertytype.h> #include <gio/gio.h> #include <glib.h> +#include <picojson.h> #include <superptr.hpp> #include <string> #include <thread> // NOLINT #include <vector> -#include "common/picojson.h" namespace common { diff --git a/xwalk/vehicle_api.js b/xwalk/vehicle_api.js index 9880558f..23b22b9f 100644 --- a/xwalk/vehicle_api.js +++ b/xwalk/vehicle_api.js @@ -8,68 +8,68 @@ var async_calls = {}; var subscriptions = []; function makeCall(call, msg) { - async_calls[next_async_call_id] = call; - msg.asyncCallId = next_async_call_id; - ++next_async_call_id; + async_calls[next_async_call_id] = call; + msg.asyncCallId = next_async_call_id; + ++next_async_call_id; - extension.postMessage(JSON.stringify(msg)); + extension.postMessage(JSON.stringify(msg)); } function vehicleInterfaceCommonContructor(obj, attname) { - obj.attributeName = attname; + obj.attributeName = attname; - var msg = {}; - msg['method'] = 'zones'; - msg['name'] = obj.attributeName; + var msg = {}; + msg['method'] = 'zones'; + msg['name'] = obj.attributeName; - obj._zones = new Zone; - obj._supported = false; + obj._zones = new Zone; + obj._supported = false; - var call = new AsyncCall(function(data) { - obj._zones = data; - }); + var call = new AsyncCall(function(data) { + obj._zones = data; + }); - makeCall(call, msg); + makeCall(call, msg); - var supportedMessage = {}; - supportedMessage['method'] = 'supported'; - supportedMessage['name'] = obj.attributeName; + var supportedMessage = {}; + supportedMessage['method'] = 'supported'; + supportedMessage['name'] = obj.attributeName; - var supportedCall = new AsyncCall(function(data) { + var supportedCall = new AsyncCall(function(data) { obj._supported = data; - }); + }); - makeCall(supportedCall, supportedMessage); + makeCall(supportedCall, supportedMessage); - Object.defineProperty(obj, 'zones', { get: function() { return obj._zones } }); - Object.defineProperty(obj, 'supported', { get: function() { return obj._supported; } }); + Object.defineProperty(obj, 'zones', { get: function() { return obj._zones } }); + Object.defineProperty(obj, 'supported', { get: function() { return obj._supported; } }); } function VehicleInterface(attname) { - vehicleInterfaceCommonContructor(this, attname); + vehicleInterfaceCommonContructor(this, attname); } VehicleInterface.prototype.get = function(zone) { - var msg = {}; - msg['method'] = 'get'; - msg['name'] = this.attributeName; - msg['zone'] = zone; + var msg = {}; + msg['method'] = 'get'; + msg['name'] = this.attributeName; + msg['zone'] = zone; - return createPromise(msg); + return createPromise(msg); }; VehicleInterface.prototype.availableForRetrieval = function(attName) { - return isAvailable(this, attName); + return isAvailable(this, attName); } VehicleInterface.prototype.availabilityChangedListener = function(callback) { - if(this.changedListenerCount) { - this.changedListenerCount++; - } - else { - this.changedListenerCount = 0; - } - return this.changedListenerCount; + if(this.changedListenerCount) { + this.changedListenerCount++; + } + else { + this.changedListenerCount = 0; + } + return this.changedListenerCount; } VehicleInterface.prototype.removeAvailabilityChangedListener = function(handle) { @@ -77,229 +77,242 @@ VehicleInterface.prototype.removeAvailabilityChangedListener = function(handle) } function VehicleSignalInterface(attname) { - vehicleInterfaceCommonContructor(this, attname); + vehicleInterfaceCommonContructor(this, attname); } VehicleSignalInterface.prototype = VehicleInterface.prototype; VehicleSignalInterface.prototype.subscribe = function(callback, zone) { - if (!zone) zone = new Zone(); + if (!zone) zone = new Zone(); - var msg = {}; - msg['method'] = 'subscribe'; - msg['name'] = this.attributeName; - msg['zone'] = zone; + var msg = {}; + msg['method'] = 'subscribe'; + msg['name'] = this.attributeName; + msg['zone'] = zone; - extension.postMessage(JSON.stringify(msg)); + extension.postMessage(JSON.stringify(msg)); - msg['callback'] = callback; + msg['callback'] = callback; - subscriptions.push(msg); + subscriptions.push(msg); - return subscriptions.length - 1; + return subscriptions.length - 1; }; VehicleSignalInterface.prototype.unsubscribe = function(handle) { - var obj = subscriptions[handle]; - subscriptions.splice(handle, 1); + var obj = subscriptions[handle]; + subscriptions.splice(handle, 1); - var unsubscribe = true; + var unsubscribe = true; - for (var i = 0; i < subscriptions.length; i++) { - var testObj = subscriptions[i]; + for (var i = 0; i < subscriptions.length; i++) { + var testObj = subscriptions[i]; - if (testObj.name === obj.name && testObj.zone.equals(obj.zone)) { - unsubscribe = false; - break; + if (testObj.name === obj.name && testObj.zone.equals(obj.zone)) { + unsubscribe = false; + break; + } } - } - if (unsubscribe) { - var msg = {}; - msg['method'] = 'unsubscribe'; - msg['name'] = this.attributeName; - msg['zone'] = obj.zone; + if (unsubscribe) { + var msg = {}; + msg['method'] = 'unsubscribe'; + msg['name'] = this.attributeName; + msg['zone'] = obj.zone; - extension.postMessage(JSON.stringify(msg)); - } + extension.postMessage(JSON.stringify(msg)); + } }; VehicleSignalInterface.prototype.set = function (value, zone) { - var msg = {}; - msg['method'] = 'set'; - msg['name'] = this.attributeName; - msg['zone'] = zone; - msg['value'] = value; + var msg = {}; + msg['method'] = 'set'; + msg['name'] = this.attributeName; + msg['zone'] = zone; + msg['value'] = value; - return createPromise(msg); + return createPromise(msg); } VehicleSignalInterface.prototype.availableForSubscription = function(attName) { - return isAvailable(this, attName); + return isAvailable(this, attName); } VehicleSignalInterface.prototype.availableForSetting = function(attName) { - return isAvailable(this, attName); + return isAvailable(this, attName); } function isAvailable(obj, attName) { - var msg = {}; - msg["method"] = 'availableForRetrieval'; - msg["name"] = obj.attributeName; - msg["attName"] = attName; - - var reply = extension.internal.sendSyncMessage(JSON.stringify(msg)); - - if (reply === "true") { - return "available"; - } else { - return "not_supported"; - } + var msg = {}; + msg["method"] = 'availableForRetrieval'; + msg["name"] = obj.attributeName; + msg["attName"] = attName; + + var reply = extension.internal.sendSyncMessage(JSON.stringify(msg)); + + if (reply === "true") { + return "available"; + } else { + return "not_supported"; + } } function AsyncCall(resolve, reject) { - this.resolve = resolve; - this.reject = reject; + this.resolve = resolve; + this.reject = reject; } function createPromise(msg) { - var promise = new Promise(function(resolve, reject) { - async_calls[next_async_call_id] = new AsyncCall(resolve, reject); - }); + var promise = new Promise(function(resolve, reject) { + async_calls[next_async_call_id] = new AsyncCall(resolve, reject); + }); - msg.asyncCallId = next_async_call_id; - extension.postMessage(JSON.stringify(msg)); - ++next_async_call_id; + msg.asyncCallId = next_async_call_id; + extension.postMessage(JSON.stringify(msg)); + ++next_async_call_id; - return promise; + return promise; } window.Zone = function(zone) { - this.value = zone ? zone : []; + this.value = zone ? zone : []; - Object.defineProperty(this, 'driver', - { enumerable: false, get: function() { - return new Zone(['Front', 'Left']); - } }); + Object.defineProperty(this, 'driver', + { enumerable: false, get: function() { + return new Zone(['Front', 'Left']); + } }); }; window.Zone.prototype.equals = function(zone) { - var is_equal = true; + var is_equal = true; - for (var i = 0; i < zone.value.length; i++) { - is_equal &= this.value.indexOf(zone.value[i]) !== -1; - } + for (var i = 0; i < zone.value.length; i++) { + is_equal &= this.value.indexOf(zone.value[i]) !== -1; + } - for (var i = 0; i < this.value.length; i++) { - is_equal &= zone.value.indexOf(this.value[i]) !== -1; - } + for (var i = 0; i < this.value.length; i++) { + is_equal &= zone.value.indexOf(this.value[i]) !== -1; + } - return is_equal; + return is_equal; }; function _defineVehicleProperty(obj, prop) { - Object.defineProperty(obj, prop, { enumerable: true, value: new VehicleInterface(prop) }); + Object.defineProperty(obj, prop, { enumerable: true, value: new VehicleInterface(prop) }); } function _defineVehicleSignalProperty(obj, prop) { - Object.defineProperty(obj, prop, { enumerable: true, value: new VehicleSignalInterface(prop) }); + Object.defineProperty(obj, prop, { enumerable: true, value: new VehicleSignalInterface(prop) }); } extension.setMessageListener(function(json) { - try { - var msg = JSON.parse(json); - - switch (msg.method) { - case 'get': - handlePromiseReply(msg); - break; - case 'zones': - handleZonesReply(msg); - break; - case 'subscribe': - handleSubscribeReply(msg); - break; - case 'set': - handlePromiseReply(msg); - break; - case 'supported': - handleSupportedReply(msg) - break; - default: - break; + try { + var msg = JSON.parse(json); + + switch (msg.method) { + case 'get': + handlePromiseReply(msg); + break; + case 'zones': + handleZonesReply(msg); + break; + case 'subscribe': + handleSubscribeReply(msg); + break; + case 'set': + handlePromiseReply(msg); + break; + case 'supported': + handleSupportedReply(msg) + break; + case 'vehicleSupportedAttributes': + handleVehicleSupported(msg); + break; + default: + break; + } + } catch (error) { + console.log('Error in message listener: ' + error); + console.log("msg: " + JSON.stringify(msg)) } - } catch (error) { - console.log('Error in message listener: ' + error); - console.log("msg: " + JSON.stringify(msg)) - } }); function handlePromiseReply(msg) { - var cbobj = async_calls[msg.asyncCallId]; - - if (msg.error) { - var error = {}; - error.error = msg.value; - switch (msg.value) { - case 'permission_denied': - error.message = 'Permission denied'; - break; - case 'invalid_operation': - error.message = 'Invalid operation'; - break; - case 'timeout': - error.message = 'Operation timed out'; - break; - case 'invalid_zone': - error.message = 'Zone invalid or not found'; - break; - case 'unknown': - error.message = 'An unknown error occured'; - break; - default: - error.message = 'Unknown'; - break; - } - - cbobj.reject(error); - } else { - if (msg.value && msg.value.zone) { - msg.value.zone = new Zone(msg.value.zone); + var cbobj = async_calls[msg.asyncCallId]; + + if (msg.error) { + var error = {}; + error.error = msg.value; + switch (msg.value) { + case 'permission_denied': + error.message = 'Permission denied'; + break; + case 'invalid_operation': + error.message = 'Invalid operation'; + break; + case 'timeout': + error.message = 'Operation timed out'; + break; + case 'invalid_zone': + error.message = 'Zone invalid or not found'; + break; + case 'unknown': + error.message = 'An unknown error occured'; + break; + default: + error.message = 'Unknown'; + break; + } + + cbobj.reject(error); + } else { + if (msg.value && msg.value.zone) { + msg.value.zone = new Zone(msg.value.zone); + } + cbobj.resolve(msg.value); } - cbobj.resolve(msg.value); - } - delete async_calls[msg.asyncCallId]; + delete async_calls[msg.asyncCallId]; } function handleZonesReply(msg) { - var cbobj = async_calls[msg.asyncCallId]; + var cbobj = async_calls[msg.asyncCallId]; - if (cbobj) - cbobj.resolve(new Zone(msg.value)); + if (cbobj) + cbobj.resolve(new Zone(msg.value)); } function handleSupportedReply(msg) { - var cbobj = async_calls[msg.asyncCallId]; + var cbobj = async_calls[msg.asyncCallId]; - if (cbobj) - cbobj.resolve(msg.value); + if (cbobj) + cbobj.resolve(msg.value); } function handleSubscribeReply(msg) { - delete async_calls[msg.asyncCallId]; - var value = msg.value; - value.zone = new Zone(value.zone); - - for (var i = 0; i < subscriptions.length; i++) { - var itr = subscriptions[i]; - var ifaceIs = (value.interfaceName.toLowerCase() === itr.name.toLowerCase()); - if (ifaceIs === true && value.zone.equals(itr.zone)) { - itr.callback(value); + delete async_calls[msg.asyncCallId]; + var value = msg.value; + value.zone = new Zone(value.zone); + + for (var i = 0; i < subscriptions.length; i++) { + var itr = subscriptions[i]; + var ifaceIs = (value.interfaceName.toLowerCase() === itr.name.toLowerCase()); + if (ifaceIs === true && value.zone.equals(itr.zone)) { + itr.callback(value); + } } - } } + +function handleVehicleSupported(msg) { + var value = msg.value; + for(i in value) { + if(exports[i] !== undefined) { + _defineVehicleSignalProperty(exports, i); + } + } +} + /// Runningstatus attributes: _defineVehicleSignalProperty(exports, 'vehicleSpeed'); _defineVehicleSignalProperty(exports, 'engineSpeed'); diff --git a/xwalk/vehicle_instance.h b/xwalk/vehicle_instance.h index 33dc8ba8..48093b63 100644 --- a/xwalk/vehicle_instance.h +++ b/xwalk/vehicle_instance.h @@ -7,8 +7,9 @@ #include <string> +#include <picojson.h> + #include "common/extension.h" -#include "common/picojson.h" #include "vehicle.h" class VehicleInstance : public common::Instance { |