diff --git a/CMakeLists.txt b/CMakeLists.txt index 388ee95..f24188e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") option(CMAKE_EXPORT_COMPILE_COMMANDS "Export compile command" TRUE) -project(lebai VERSION 1.1.8 LANGUAGES CXX) +project(lebai VERSION 1.1.9 LANGUAGES CXX) set(PROJECT_NAMESPACE lebai) message(STATUS "${PROJECT_NAME} version: ${PROJECT_VERSION}") # message(STATUS "major: ${PROJECT_VERSION_MAJOR}") diff --git a/Doxyfile b/Doxyfile index 7230143..f604157 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "lebai sdk" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.1.8 +PROJECT_NUMBER = 1.1.9 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/changelog.md b/doc/changelog.md index 9035e37..999ceaa 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -1,5 +1,9 @@ # ChangeLog +## 1.1.9 + +添加接口连接抛出异常的处理 + ## 1.1.8 speedl添加参考坐标系 diff --git a/examples/example.cc b/examples/example.cc index 534d99c..29195ae 100755 --- a/examples/example.cc +++ b/examples/example.cc @@ -1,18 +1,18 @@ /** * Copyright 2022 lebai.ltd - * + * * 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. -*/ + */ #include #include @@ -20,57 +20,77 @@ #include #include - -int main(int argc, char ** argv) +int main(int argc, char **argv) { - if(argc < 2) + if (argc < 2) { - std::cerr<<"You must specify the IP address of the robot"< 2) + if (argc > 2) { std::string sim_str = argv[2]; - if(sim_str == "sim") + if (sim_str == "sim") { sim = true; } } - std::cout<<"Connecting to robot at "< jp = {3.0/ 180.0 * M_PI, -48.0/ 180.0 * M_PI, 78.0/ 180.0 * M_PI, 9.0/ 180.0 * M_PI, -67.0/ 180.0 * M_PI, -3.0/ 180.0 * M_PI}; - //std::cout<<"jp "< jp = {3.0/ 180.0 * M_PI, -48.0/ 180.0 * M_PI, 78.0/ 180.0 * M_PI, 9.0/ 180.0 * M_PI, -67.0/ 180.0 * M_PI, -3.0/ 180.0 * M_PI}; + // std::cout<<"jp "<=3.1.5。 + * @note 接口的每一个函数都可能抛出异常std::runtime_error, 这是因为网络连接丢失或者调用的逻辑错误等。 + * * */ class Robot @@ -71,6 +73,7 @@ namespace lebai /** * @brief 内部实现. * @note 用户无需使用. + * */ class RobotImpl; @@ -80,11 +83,6 @@ namespace lebai * @brief 构造Robot对象. * @note 当你尝试创建Robot对象时,会根据传入的ip参数尝试去连接机械臂,但是可能会连接失败,当连接不成功时,对象依然会创建。 * - * 但是如果网络没有连接成功而你调用和机械臂交互的接口时,会抛出std::runtime_error异常,表示网络连接异常. - * - * 你可以通过@ref is_network_connected()方法来判断是否连接成功,如果连接不成功,你可以尝试重新创建Robot对象,或者等待网络恢复. - * - * * * @param ip: 机械臂IP地址. * @param simulator: 用于表示机械臂是否为仿真机械臂(docker仿真或控制器运行在仿真模式下)的macs标志.True表示仿真模式,False表示实物机械臂. @@ -115,7 +113,7 @@ namespace lebai /** * @brief 返回是否和机械臂的网络连接正常,如果网络连接异常,调用和机械臂交互的接口会抛出异常std::runtime_error。 - * + * @note 不建议使用,直接catch接口调用获取网络异常。 * @return true 表示网络连接正常,false表示网络连接异常. */ bool is_network_connected(); diff --git a/sdk/src/jsonrpc_connector.cc b/sdk/src/jsonrpc_connector.cc index f9a2668..c609981 100755 --- a/sdk/src/jsonrpc_connector.cc +++ b/sdk/src/jsonrpc_connector.cc @@ -1,163 +1,178 @@ /** * Copyright 2022 lebai.ltd - * + * * 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. -*/ + */ #include "jsonrpc_connector.hh" #include #include +#include #include "protos/utils.hh" namespace lebai { -JSONRpcConnector::JSONRpcConnector(const std::string & ip, uint16_t port) -{ - jsonrpc_id_ = 0; - std::string url = "ws://"+ip + ":" + std::to_string(port); - id_ = endpoint_.connect(url); -} -JSONRpcConnector::~JSONRpcConnector(){} - -// int JSONRpcConnector::Call(const std::string & method, rapidjson::Value & req_data, rapidjson::Value & resp_data) -// { -// rapidjson::Document d; -// rapidjson::Value req; -// req.SetObject(); -// req.AddMember("jsonrpc", "2.0", d.GetAllocator()); -// // req.AddMember("id", jsonrpc_id_++, d.GetAllocator()); -// rapidjson::Value method_value; -// method_value.SetString(method.c_str(), method.size(), d.GetAllocator()); -// req.AddMember("method", method_value, d.GetAllocator()); -// req.AddMember("params", req_data, d.GetAllocator()); -// rapidjson::StringBuffer buffer; -// rapidjson::Writer writer(buffer); -// req.Accept(writer); -// std::string req_str = buffer.GetString(); -// if(GetConnectionStatus() != kOpen) -// { -// return -1; -// } -// // std::cout << "req_str: " << req_str << std::endl; -// endpoint_.send(id_, req_str); -// auto const & resp_str = endpoint_.get_metadata(id_)->GetMessageStr(); -// // std::cout<<"resp_str: "<(resp_str.c_str()); -// rapidjson::Value::MemberIterator iter = d.FindMember("result"); -// if(iter == d.MemberEnd()) -// { -// return -1; -// } -// resp_data = iter->value; -// return 0; -// } - -// int JSONRpcConnector::CallString(int id, const std::string &method, const std::string & req_str, std::string * resp_str) -// { -// if(GetConnectionStatus() != kOpen) -// { -// return -1; -// } -// // std::string str_jsonrpc = "\"jsonrpc\":\"2.0\","; -// // std::string str_method = "\"method\":\"" + method + "\","; -// // std::string str_params = "\"params\":" + req_str + ","; -// // std::string str_id = "\"id\":" + std::to_string(jsonrpc_id_); -// // jsonrpc_id_++; -// std::string jsonrpc_req = "{\"jsonrpc\":\"2.0\",\"method\":\"" +method + -// "\",\"params\":"+req_str+",\"id\":"+ std::to_string(id)+ "}"; -// // str_method + str_params + str_id + "}"; -// // std::cerr<<"xxx jsonrpc_req: "<GetMessageStr(); -// *resp_str = jsonrpc_resp; + JSONRpcConnector::JSONRpcConnector(const std::string &ip, uint16_t port) + { + jsonrpc_id_ = 0; + std::string url = "ws://" + ip + ":" + std::to_string(port); + id_ = endpoint_.connect(url); + } + JSONRpcConnector::~JSONRpcConnector() {} -// // std::cerr << "jsonrpc_resp: " << *resp_str << std::endl; -// } -// // {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null} + // int JSONRpcConnector::Call(const std::string & method, rapidjson::Value & req_data, rapidjson::Value & resp_data) + // { + // rapidjson::Document d; + // rapidjson::Value req; + // req.SetObject(); + // req.AddMember("jsonrpc", "2.0", d.GetAllocator()); + // // req.AddMember("id", jsonrpc_id_++, d.GetAllocator()); + // rapidjson::Value method_value; + // method_value.SetString(method.c_str(), method.size(), d.GetAllocator()); + // req.AddMember("method", method_value, d.GetAllocator()); + // req.AddMember("params", req_data, d.GetAllocator()); + // rapidjson::StringBuffer buffer; + // rapidjson::Writer writer(buffer); + // req.Accept(writer); + // std::string req_str = buffer.GetString(); + // if(GetConnectionStatus() != kOpen) + // { + // return -1; + // } + // // std::cout << "req_str: " << req_str << std::endl; + // endpoint_.send(id_, req_str); + // auto const & resp_str = endpoint_.get_metadata(id_)->GetMessageStr(); + // // std::cout<<"resp_str: "<(resp_str.c_str()); + // rapidjson::Value::MemberIterator iter = d.FindMember("result"); + // if(iter == d.MemberEnd()) + // { + // return -1; + // } + // resp_data = iter->value; + // return 0; + // } -// return 0; -// } + // int JSONRpcConnector::CallString(int id, const std::string &method, const std::string & req_str, std::string * resp_str) + // { + // if(GetConnectionStatus() != kOpen) + // { + // return -1; + // } + // // std::string str_jsonrpc = "\"jsonrpc\":\"2.0\","; + // // std::string str_method = "\"method\":\"" + method + "\","; + // // std::string str_params = "\"params\":" + req_str + ","; + // // std::string str_id = "\"id\":" + std::to_string(jsonrpc_id_); + // // jsonrpc_id_++; + // std::string jsonrpc_req = "{\"jsonrpc\":\"2.0\",\"method\":\"" +method + + // "\",\"params\":"+req_str+",\"id\":"+ std::to_string(id)+ "}"; + // // str_method + str_params + str_id + "}"; + // // std::cerr<<"xxx jsonrpc_req: "<GetMessageStr(); + // *resp_str = jsonrpc_resp; -// int JSONRpcConnector::CallRpc(const std::string & req_str, std::string * resp_str) -// { -// std::cout<<"req_str "<GetMessageStr(); -// int resp_id; -// ExtractJSONRpcRespString(*resp_str, resp_id, *resp_str); -// if(resp_id != id) -// { -// return -1; -// } -// std::cout<<"resp_str "<<*resp_str<<"\n"; -// } -// } + // // std::cerr << "jsonrpc_resp: " << *resp_str << std::endl; + // } + // // {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null} + // return 0; + // } + // int JSONRpcConnector::CallRpc(const std::string & req_str, std::string * resp_str) + // { + // std::cout<<"req_str "<GetMessageStr(); + // int resp_id; + // ExtractJSONRpcRespString(*resp_str, resp_id, *resp_str); + // if(resp_id != id) + // { + // return -1; + // } + // std::cout<<"resp_str "<<*resp_str<<"\n"; + // } + // } -int JSONRpcConnector::CallRpc(const std::string & method, const std::string & req_data_str, std::string * resp_data_str) -{ - int call_jsonrpc_id = jsonrpc_id_++; - std::string jsonrpc_req; - std::string jsonrpc_resp; - jsonrpc_req = ToJSONRpcReqString(call_jsonrpc_id, method, req_data_str); - // std::cout<<"jsonrpc_req id:"<(resq_data); - if(error_code < 0) + int call_jsonrpc_id = jsonrpc_id_++; + std::string jsonrpc_req; + std::string jsonrpc_resp; + jsonrpc_req = ToJSONRpcReqString(call_jsonrpc_id, method, req_data_str); + auto future = endpoint_.createFuture(id_, call_jsonrpc_id); + if (!endpoint_.send(id_, jsonrpc_req)) { - throw std::runtime_error(std::get<1>(resq_data)); + endpoint_.deletePromise(id_, call_jsonrpc_id); + throw std::runtime_error("Send Jsonrpc request failed!"); + } + using namespace std::chrono_literals; + // TODO(@liufang) This corner case is not not good, need to be improved. + if (method != "wait_move") + { + // TODO(@liufang) Make the timeout configurable. + std::future_status status = future.wait_for(5s); + switch (status) + { + case std::future_status::deferred: + case std::future_status::timeout: + // std::cout << "timeout!\n"; + throw std::runtime_error("No response from robot controller!"); + case std::future_status::ready: + break; + } } - *resp_data_str = std::get<1>(resq_data); - } - return 0; -} -JSONRpcConnector::ConnectionStatus JSONRpcConnector::GetConnectionStatus() -{ - auto status = endpoint_.get_metadata(id_)->getStatus(); - if(status == "Open") - { - return kOpen; - } - else if(status == "Connecting") - { - return kConnecting; - } - else if(status == "Closed") - { - return kClosed; + auto resq_data = future.get(); + if (resp_data_str) + { + auto error_code = std::get<0>(resq_data); + if (error_code < 0) + { + throw std::runtime_error(std::get<1>(resq_data)); + } + *resp_data_str = std::get<1>(resq_data); + } + return 0; } - else + + JSONRpcConnector::ConnectionStatus JSONRpcConnector::GetConnectionStatus() { - return kFailed; + auto status = endpoint_.get_metadata(id_)->getStatus(); + if (status == "Open") + { + return kOpen; + } + else if (status == "Connecting") + { + return kConnecting; + } + else if (status == "Closed") + { + return kClosed; + } + else + { + return kFailed; + } } -} - }; \ No newline at end of file diff --git a/sdk/src/robot_impl.cc b/sdk/src/robot_impl.cc index 1a39632..75d1cae 100644 --- a/sdk/src/robot_impl.cc +++ b/sdk/src/robot_impl.cc @@ -57,6 +57,7 @@ namespace lebai bool Robot::RobotImpl::isNetworkConnected() { + // return json_rpc_connector_->Ping(); return json_rpc_connector_->GetConnectionStatus() == JSONRpcConnector::kOpen; } diff --git a/sdk/src/websocket.hh b/sdk/src/websocket.hh index 628b0f0..0a24a70 100644 --- a/sdk/src/websocket.hh +++ b/sdk/src/websocket.hh @@ -91,6 +91,7 @@ namespace lebai auto ret = ExtractJSONRpcRespString(message_str, callback_jsonrpc_id, error_code, resp_data_str); if(ret == JSONRpcRespParseResult::kInvalid) { + std::cout<<"invalid jsonrpc response\n"; return; } else @@ -133,7 +134,14 @@ namespace lebai promises_[rpc_id] = std::make_unique>>(); return promises_[rpc_id]->get_future();; } - + void deletePromise(int rpc_id) + { + std::lock_guard guard(promises_map_mutex_); + if(promises_.find(rpc_id) != promises_.end()) + { + promises_.erase(rpc_id); + } + } // friend std::ostream& operator<<(std::ostream& out, @@ -300,6 +308,12 @@ namespace lebai return metadata_it->second->createPromise(rpc_id); } + void deletePromise(int id, int rpc_id) + { + ConList::iterator metadata_it = connection_list_.find(id); + metadata_it->second->deletePromise(rpc_id); + } + ConnectionMetadata::ptr get_metadata(int id) const { diff --git a/sdk/third/websocketpp/connection.hpp b/sdk/third/websocketpp/connection.hpp index d019fce..e769b36 100644 --- a/sdk/third/websocketpp/connection.hpp +++ b/sdk/third/websocketpp/connection.hpp @@ -373,7 +373,7 @@ class connection m_fail_handler = h; } - /// Set ping handler + /// Set ping hangfpdler /** * The ping handler is called whenever the connection receives a ping * control frame. The ping payload is included.