/* * Copyright (C) 2015 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifdef _WIN32 #include #pragma comment(lib, "Rpcrt4.lib") #else /* UNIX */ #include #ifdef HAVE_UUID #include #endif #endif #include "RestWebPlugin.hh" using namespace gazebo; using namespace std; ////////////////////////////////////////////////// RestWebPlugin::RestWebPlugin() : node(new gazebo::transport::Node()), stopMsgProcessing(false), requestQThread(NULL) { // generate a unique session ID // On Windows #ifdef _WIN32 UUID uuid; C_STATUS Result = ::UuidCreate(uuid); if (Result != RPC_S_OK) { gzerr << "Call to UuidCreate return a non success RPC call. " << "Return code: " << Result << std::endl; } char* szUuid = NULL; if (::UuidToStringA(&this->data, reinterpret_cast(&szUuid)) == RPC_S_OK) { this->session = szUuid; ::RpcStringFreeA(reinterpret_cast(&szUuid)); } // or on UNIX #else #ifdef HAVE_UUID uuid_t uuid; uuid_generate_random(uuid); char s[37]; uuid_unparse(uuid, s); this->session = s; #endif #endif if (this->session.empty()) { // alternative to uuid this->session = common::Time::GetWallTimeAsISOString(); } gzmsg << "REST web Session : " << this->session << endl; } ////////////////////////////////////////////////// RestWebPlugin::~RestWebPlugin() { // tell the requestQ to stop precessing this->stopMsgProcessing = true; if (this->requestQThread && this->requestQThread->joinable()) { this->requestQThread->join(); delete this->requestQThread; } } ////////////////////////////////////////////////// void RestWebPlugin::Init() { // setup our node for communication this->node->Init(); this->subLogin = node->Subscribe("/gazebo/rest/rest_login", &RestWebPlugin::OnRestLoginRequest, this); this->subLogout = node->Subscribe("/gazebo/rest/rest_logout", &RestWebPlugin::OnRestLogoutRequest, this); this->subEvent = node->Subscribe("/gazebo/rest/rest_post", &RestWebPlugin::OnEventRestPost, this); this->subSimEvent = node->Subscribe("/gazebo/sim_events", &RestWebPlugin::OnSimEvent, this); this->requestQThread = new boost::thread( boost::bind(&RestWebPlugin::RunRequestQ, this)); } ////////////////////////////////////////////////// void RestWebPlugin::Load(int /*_argc*/, char ** /*_argv*/) { // nothing to do for now } ////////////////////////////////////////////////// void RestWebPlugin::OnSimEvent(ConstSimEventPtr &_msg) { gazebo::msgs::RestResponse msg; std::string response; try { // where to post the data on the REST server std::string route = "/events/new"; std::string eType = _msg->type(); std::string name = _msg->name(); std::string data = _msg->data(); msgs::WorldStatistics ws = _msg->world_statistics(); msgs::Time simT = ws.sim_time(); msgs::Time realT = ws.real_time(); msgs::Time pauseT = ws.pause_time(); bool paused = ws.paused(); std::string worldName = physics::get_world()->GetName(); std::string event = "{\n"; event += "\"session\": \"" + this->session + "\", "; event += "\"name\": \"" + name + "\", "; event += "\"type\": \"" + eType + "\",\n"; event += "\"data\": " + data + ", "; event += "\"world\": {"; event += "\"name\": "; event += "\""; event += worldName; event += "\", "; event += "\"paused\": "; event += "\""; if (paused) event += "true"; else event += "false"; event += "\", "; event += "\"clock_time\": "; event += "\""; event += common::Time::GetWallTimeAsISOString(); event += "\", "; event += "\"real_time\": "; event += "\""; event += msgs::Convert(realT).FormattedString(); event += "\", "; event += "\"sim_time\": "; event += "\""; event += msgs::Convert(simT).FormattedString(); event += "\", "; event += "\"pause_time\": "; event += "\""; event += msgs::Convert(pauseT).FormattedString(); event += "\""; event += "}\n"; // world element event += "}"; // root element // post it with curl this->restApi.PostJsonData(route.c_str(), event.c_str()); msg.set_type(msgs::RestResponse::SUCCESS); } catch(RestException &x) { response = "There was a problem trying to send data to the server: "; response += x.what(); msg.set_type(msgs::RestResponse::ERROR); // alert the user via the gui plugin gzerr << "ERROR in REST service POST request: " << response << std::endl; } if (_msg->has_id()) msg.set_id(_msg->id()); msg.set_msg(response); this->pub->Publish(msg); } ////////////////////////////////////////////////// void RestWebPlugin::OnEventRestPost(ConstRestPostPtr &_msg) { gzmsg << "RestWebPlugin::OnRestPost"; gzmsg << "[" << _msg->route() << ", " << _msg->json() << "]" << std::endl; gzmsg << std::endl; gazebo::msgs::RestResponse msg; std::string response; try { std::string event = "{"; event += "\"event\": " + _msg->json() + ", "; physics::WorldPtr world = physics::get_world(); if (!world) { gzerr << "Can't access world before web service POST" << std::endl; } else { event += "\"session\": \"" + this->session + "\", "; event += "\"world\": {"; event += "\"name\": "; event += "\""; event += world->GetName(); event += "\", "; if (!world->IsPaused()) { event += "\"is_running\": \"true\", "; } else { event += "\"is_running\": \"false\", "; } common::Time t; event += "\"clock_time\": "; event += "\""; event += common::Time::GetWallTimeAsISOString(); event += "\", "; event += "\"real_time\": "; event += "\""; t = world->GetRealTime(); event += t.FormattedString(); event += "\", "; event += "\"sim_time\": "; event += "\""; t = world->GetSimTime(); event += t.FormattedString(); event += "\", "; event += "\"pause_time\": "; event += "\""; t = world->GetPauseTime(); event += t.FormattedString(); event += "\" "; event += "}"; } event += "}"; this->restApi.PostJsonData(_msg->route().c_str(), event.c_str()); msg.set_type(msgs::RestResponse::SUCCESS); } catch(RestException &x) { response = "There was a problem trying to send data to the server: "; response += x.what(); msg.set_type(msgs::RestResponse::ERROR); // alert the user via the gui plugin gzerr << "ERROR in REST request: " << response << std::endl; } if (_msg->has_id()) msg.set_id(_msg->id()); msg.set_msg(response); this->pub->Publish(msg); } ////////////////////////////////////////////////// void RestWebPlugin::OnRestLoginRequest(ConstRestLoginPtr &_msg) { boost::mutex::scoped_lock lock(this->requestQMutex); this->msgLoginQ.push_back(_msg); } ////////////////////////////////////////////////// void RestWebPlugin::OnRestLogoutRequest(ConstRestLogoutPtr &_msg) { boost::mutex::scoped_lock lock(this->requestQMutex); this->restApi.Logout(); gazebo::msgs::RestResponse msg; if (_msg->has_id()) msg.set_id(_msg->id()); msg.set_type(msgs::RestResponse::LOGOUT); msg.set_msg("Success"); this->pub->Publish(msg); } ////////////////////////////////////////////////// void RestWebPlugin::ProcessLoginRequest(ConstRestLoginPtr _msg) { gazebo::msgs::RestResponse msg; std::string response; // this is executed asynchronously try { this->restApi.Login(_msg->url().c_str(), "/login", _msg->username().c_str(), _msg->password().c_str()); response = "Success"; msg.set_type(msgs::RestResponse::LOGIN); } catch(RestException &x) { response = "There was a problem trying to login to the server: "; response += x.what(); msg.set_type(msgs::RestResponse::ERROR); // alert the user via the gui plugin gzerr << "ERROR in REST login request. : " << response << std::endl; } if (_msg->has_id()) msg.set_id(_msg->id()); msg.set_msg(response); this->pub->Publish(msg); } ////////////////////////////////////////////////// void RestWebPlugin::RunRequestQ() { // be ready to send errors back to the UI std::string path("/gazebo/rest/rest_response"); this->pub = node->Advertise(path); // process any login or post data that ha been received while (!this->stopMsgProcessing) { gazebo::common::Time::MSleep(50); try { boost::shared_ptr login; // Grab the mutex and remove first message the queue { boost::mutex::scoped_lock lock(this->requestQMutex); if (!this->msgLoginQ.empty()) { login = this->msgLoginQ.front(); this->msgLoginQ.pop_front(); } } if (login) { this->ProcessLoginRequest(login); } } catch(...) { gzerr << "Unhandled exception while processing request message" << std::endl; } } } // plugin registration GZ_REGISTER_SYSTEM_PLUGIN(RestWebPlugin)