| /* |
| * |
| * Copyright 2015, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| // Package main implements a simple gRPC server that demonstrates how to use gRPC-Go libraries |
| // to perform unary, client streaming, server streaming and full duplex RPCs. |
| // |
| // It implements the route guide service whose definition can be found in proto/route_guide.proto. |
| package main |
| |
| import ( |
| "encoding/json" |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "math" |
| "net" |
| "time" |
| |
| "golang.org/x/net/context" |
| "google.golang.org/grpc" |
| |
| "google.golang.org/grpc/credentials" |
| "google.golang.org/grpc/grpclog" |
| |
| "github.com/golang/protobuf/proto" |
| |
| pb "google.golang.org/grpc/examples/route_guide/routeguide" |
| ) |
| |
| var ( |
| tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP") |
| certFile = flag.String("cert_file", "testdata/server1.pem", "The TLS cert file") |
| keyFile = flag.String("key_file", "testdata/server1.key", "The TLS key file") |
| jsonDBFile = flag.String("json_db_file", "testdata/route_guide_db.json", "A json file containing a list of features") |
| port = flag.Int("port", 10000, "The server port") |
| ) |
| |
| type routeGuideServer struct { |
| savedFeatures []*pb.Feature |
| routeNotes map[string][]*pb.RouteNote |
| } |
| |
| // GetFeature returns the feature at the given point. |
| func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { |
| for _, feature := range s.savedFeatures { |
| if proto.Equal(feature.Location, point) { |
| return feature, nil |
| } |
| } |
| // No feature was found, return an unnamed feature |
| return &pb.Feature{"", point}, nil |
| } |
| |
| // ListFeatures lists all features comtained within the given bounding Rectangle. |
| func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { |
| for _, feature := range s.savedFeatures { |
| if inRange(feature.Location, rect) { |
| if err := stream.Send(feature); err != nil { |
| return err |
| } |
| } |
| } |
| return nil |
| } |
| |
| // RecordRoute records a route composited of a sequence of points. |
| // |
| // It gets a stream of points, and responds with statistics about the "trip": |
| // number of points, number of known features visited, total distance traveled, and |
| // total time spent. |
| func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { |
| var pointCount, featureCount, distance int32 |
| var lastPoint *pb.Point |
| startTime := time.Now() |
| for { |
| point, err := stream.Recv() |
| if err == io.EOF { |
| endTime := time.Now() |
| return stream.SendAndClose(&pb.RouteSummary{ |
| PointCount: pointCount, |
| FeatureCount: featureCount, |
| Distance: distance, |
| ElapsedTime: int32(endTime.Sub(startTime).Seconds()), |
| }) |
| } |
| if err != nil { |
| return err |
| } |
| pointCount++ |
| for _, feature := range s.savedFeatures { |
| if proto.Equal(feature.Location, point) { |
| featureCount++ |
| } |
| } |
| if lastPoint != nil { |
| distance += calcDistance(lastPoint, point) |
| } |
| lastPoint = point |
| } |
| } |
| |
| // RouteChat receives a stream of message/location pairs, and responds with a stream of all |
| // previous messages at each of those locations. |
| func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { |
| for { |
| in, err := stream.Recv() |
| if err == io.EOF { |
| return nil |
| } |
| if err != nil { |
| return err |
| } |
| key := serialize(in.Location) |
| if _, present := s.routeNotes[key]; !present { |
| s.routeNotes[key] = []*pb.RouteNote{in} |
| } else { |
| s.routeNotes[key] = append(s.routeNotes[key], in) |
| } |
| for _, note := range s.routeNotes[key] { |
| if err := stream.Send(note); err != nil { |
| return err |
| } |
| } |
| } |
| } |
| |
| // loadFeatures loads features from a JSON file. |
| func (s *routeGuideServer) loadFeatures(filePath string) { |
| file, err := ioutil.ReadFile(filePath) |
| if err != nil { |
| grpclog.Fatalf("Failed to load default features: %v", err) |
| } |
| if err := json.Unmarshal(file, &s.savedFeatures); err != nil { |
| grpclog.Fatalf("Failed to load default features: %v", err) |
| } |
| } |
| |
| func toRadians(num float64) float64 { |
| return num * math.Pi / float64(180) |
| } |
| |
| // calcDistance calculates the distance between two points using the "haversine" formula. |
| // This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. |
| func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 { |
| const CordFactor float64 = 1e7 |
| const R float64 = float64(6371000) // metres |
| lat1 := float64(p1.Latitude) / CordFactor |
| lat2 := float64(p2.Latitude) / CordFactor |
| lng1 := float64(p1.Longitude) / CordFactor |
| lng2 := float64(p2.Longitude) / CordFactor |
| φ1 := toRadians(lat1) |
| φ2 := toRadians(lat2) |
| Δφ := toRadians(lat2 - lat1) |
| Δλ := toRadians(lng2 - lng1) |
| |
| a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + |
| math.Cos(φ1)*math.Cos(φ2)* |
| math.Sin(Δλ/2)*math.Sin(Δλ/2) |
| c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) |
| |
| distance := R * c |
| return int32(distance) |
| } |
| |
| func inRange(point *pb.Point, rect *pb.Rectangle) bool { |
| left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude)) |
| right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude)) |
| top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude)) |
| bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude)) |
| |
| if float64(point.Longitude) >= left && |
| float64(point.Longitude) <= right && |
| float64(point.Latitude) >= bottom && |
| float64(point.Latitude) <= top { |
| return true |
| } |
| return false |
| } |
| |
| func serialize(point *pb.Point) string { |
| return fmt.Sprintf("%d %d", point.Latitude, point.Longitude) |
| } |
| |
| func newServer() *routeGuideServer { |
| s := new(routeGuideServer) |
| s.loadFeatures(*jsonDBFile) |
| s.routeNotes = make(map[string][]*pb.RouteNote) |
| return s |
| } |
| |
| func main() { |
| flag.Parse() |
| lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) |
| if err != nil { |
| grpclog.Fatalf("failed to listen: %v", err) |
| } |
| var opts []grpc.ServerOption |
| if *tls { |
| creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) |
| if err != nil { |
| grpclog.Fatalf("Failed to generate credentials %v", err) |
| } |
| opts = []grpc.ServerOption{grpc.Creds(creds)} |
| } |
| grpcServer := grpc.NewServer(opts...) |
| pb.RegisterRouteGuideServer(grpcServer, newServer()) |
| grpcServer.Serve(lis) |
| } |