/* * * Copyright 2021 gRPC authors. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "helper.h" #ifdef BAZEL_BUILD #include "examples/protos/route_guide.grpc.pb.h" #else #include "route_guide.grpc.pb.h" #endif using grpc::Channel; using grpc::ClientContext; using grpc::Status; using routeguide::Feature; using routeguide::Point; using routeguide::Rectangle; using routeguide::RouteGuide; using routeguide::RouteNote; using routeguide::RouteSummary; Point MakePoint(long latitude, long longitude) { Point p; p.set_latitude(latitude); p.set_longitude(longitude); return p; } Feature MakeFeature(const std::string& name, long latitude, long longitude) { Feature f; f.set_name(name); f.mutable_location()->CopyFrom(MakePoint(latitude, longitude)); return f; } RouteNote MakeRouteNote(const std::string& message, long latitude, long longitude) { RouteNote n; n.set_message(message); n.mutable_location()->CopyFrom(MakePoint(latitude, longitude)); return n; } class RouteGuideClient { public: RouteGuideClient(std::shared_ptr channel, const std::string& db) : stub_(RouteGuide::NewStub(channel)) { routeguide::ParseDb(db, &feature_list_); } void GetFeature() { Point point; Feature feature; point = MakePoint(409146138, -746188906); GetOneFeature(point, &feature); point = MakePoint(0, 0); GetOneFeature(point, &feature); } void ListFeatures() { routeguide::Rectangle rect; Feature feature; rect.mutable_lo()->set_latitude(400000000); rect.mutable_lo()->set_longitude(-750000000); rect.mutable_hi()->set_latitude(420000000); rect.mutable_hi()->set_longitude(-730000000); std::cout << "Looking for features between 40, -75 and 42, -73" << std::endl; class Reader : public grpc::ClientReadReactor { public: Reader(RouteGuide::Stub* stub, float coord_factor, const routeguide::Rectangle& rect) : coord_factor_(coord_factor) { stub->async()->ListFeatures(&context_, &rect, this); StartRead(&feature_); StartCall(); } void OnReadDone(bool ok) override { if (ok) { std::cout << "Found feature called " << feature_.name() << " at " << feature_.location().latitude() / coord_factor_ << ", " << feature_.location().longitude() / coord_factor_ << std::endl; StartRead(&feature_); } } void OnDone(const Status& s) override { std::unique_lock l(mu_); status_ = s; done_ = true; cv_.notify_one(); } Status Await() { std::unique_lock l(mu_); cv_.wait(l, [this] { return done_; }); return std::move(status_); } private: ClientContext context_; float coord_factor_; Feature feature_; std::mutex mu_; std::condition_variable cv_; Status status_; bool done_ = false; }; Reader reader(stub_.get(), kCoordFactor_, rect); Status status = std::move(reader.Await()); if (status.ok()) { std::cout << "ListFeatures rpc succeeded." << std::endl; } else { std::cout << "ListFeatures rpc failed." << std::endl; } } void RecordRoute() { class Recorder : public grpc::ClientWriteReactor { public: Recorder(RouteGuide::Stub* stub, float coord_factor, const std::vector* feature_list) : coord_factor_(coord_factor), feature_list_(feature_list), generator_( std::chrono::system_clock::now().time_since_epoch().count()), feature_distribution_(0, feature_list->size() - 1), delay_distribution_(500, 1500) { stub->async()->RecordRoute(&context_, &stats_, this); // Use a hold since some StartWrites are invoked indirectly from a // delayed lambda in OnWriteDone rather than directly from the reaction // itself AddHold(); NextWrite(); StartCall(); } void OnWriteDone(bool ok) override { // Delay and then do the next write or WritesDone alarm_.Set( std::chrono::system_clock::now() + std::chrono::milliseconds(delay_distribution_(generator_)), [this](bool /*ok*/) { NextWrite(); }); } void OnDone(const Status& s) override { std::unique_lock l(mu_); status_ = s; done_ = true; cv_.notify_one(); } Status Await(RouteSummary* stats) { std::unique_lock l(mu_); cv_.wait(l, [this] { return done_; }); *stats = stats_; return std::move(status_); } private: void NextWrite() { if (points_remaining_ != 0) { const Feature& f = (*feature_list_)[feature_distribution_(generator_)]; std::cout << "Visiting point " << f.location().latitude() / coord_factor_ << ", " << f.location().longitude() / coord_factor_ << std::endl; StartWrite(&f.location()); points_remaining_--; } else { StartWritesDone(); RemoveHold(); } } ClientContext context_; float coord_factor_; int points_remaining_ = 10; Point point_; RouteSummary stats_; const std::vector* feature_list_; std::default_random_engine generator_; std::uniform_int_distribution feature_distribution_; std::uniform_int_distribution delay_distribution_; grpc::Alarm alarm_; std::mutex mu_; std::condition_variable cv_; Status status_; bool done_ = false; }; Recorder recorder(stub_.get(), kCoordFactor_, &feature_list_); RouteSummary stats; Status status = std::move(recorder.Await(&stats)); if (status.ok()) { std::cout << "Finished trip with " << stats.point_count() << " points\n" << "Passed " << stats.feature_count() << " features\n" << "Travelled " << stats.distance() << " meters\n" << "It took " << stats.elapsed_time() << " seconds" << std::endl; } else { std::cout << "RecordRoute rpc failed." << std::endl; } } void RouteChat() { class Chatter : public grpc::ClientBidiReactor { public: explicit Chatter(RouteGuide::Stub* stub) : notes_{MakeRouteNote("First message", 0, 0), MakeRouteNote("Second message", 0, 1), MakeRouteNote("Third message", 1, 0), MakeRouteNote("Fourth message", 0, 0)}, notes_iterator_(notes_.begin()) { stub->async()->RouteChat(&context_, this); NextWrite(); StartRead(&server_note_); StartCall(); } void OnWriteDone(bool /*ok*/) override { NextWrite(); } void OnReadDone(bool ok) override { if (ok) { std::cout << "Got message " << server_note_.message() << " at " << server_note_.location().latitude() << ", " << server_note_.location().longitude() << std::endl; StartRead(&server_note_); } } void OnDone(const Status& s) override { std::unique_lock l(mu_); status_ = s; done_ = true; cv_.notify_one(); } Status Await() { std::unique_lock l(mu_); cv_.wait(l, [this] { return done_; }); return std::move(status_); } private: void NextWrite() { if (notes_iterator_ != notes_.end()) { const auto& note = *notes_iterator_; std::cout << "Sending message " << note.message() << " at " << note.location().latitude() << ", " << note.location().longitude() << std::endl; StartWrite(¬e); notes_iterator_++; } else { StartWritesDone(); } } ClientContext context_; const std::vector notes_; std::vector::const_iterator notes_iterator_; RouteNote server_note_; std::mutex mu_; std::condition_variable cv_; Status status_; bool done_ = false; }; Chatter chatter(stub_.get()); Status status = std::move(chatter.Await()); if (!status.ok()) { std::cout << "RouteChat rpc failed." << std::endl; } } private: bool GetOneFeature(const Point& point, Feature* feature) { ClientContext context; bool result; std::mutex mu; std::condition_variable cv; bool done = false; stub_->async()->GetFeature( &context, &point, feature, [&result, &mu, &cv, &done, feature, this](Status status) { bool ret; if (!status.ok()) { std::cout << "GetFeature rpc failed." << std::endl; ret = false; } else if (!feature->has_location()) { std::cout << "Server returns incomplete feature." << std::endl; ret = false; } else if (feature->name().empty()) { std::cout << "Found no feature at " << feature->location().latitude() / kCoordFactor_ << ", " << feature->location().longitude() / kCoordFactor_ << std::endl; ret = true; } else { std::cout << "Found feature called " << feature->name() << " at " << feature->location().latitude() / kCoordFactor_ << ", " << feature->location().longitude() / kCoordFactor_ << std::endl; ret = true; } std::lock_guard lock(mu); result = ret; done = true; cv.notify_one(); }); std::unique_lock lock(mu); cv.wait(lock, [&done] { return done; }); return result; } const float kCoordFactor_ = 10000000.0; std::unique_ptr stub_; std::vector feature_list_; }; int main(int argc, char** argv) { // Expect only arg: --db_path=path/to/route_guide_db.json. std::string db = routeguide::GetDbFileContent(argc, argv); RouteGuideClient guide( grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()), db); std::cout << "-------------- GetFeature --------------" << std::endl; guide.GetFeature(); std::cout << "-------------- ListFeatures --------------" << std::endl; guide.ListFeatures(); std::cout << "-------------- RecordRoute --------------" << std::endl; guide.RecordRoute(); std::cout << "-------------- RouteChat --------------" << std::endl; guide.RouteChat(); return 0; }