/* * * 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 import ( "encoding/json" "flag" "io" "io/ioutil" "log" "math" "net" "strconv" "time" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" pb "google.golang.org/grpc/examples/route_guide" ) var ( useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP") certFile = flag.String("tls_cert_file", "testdata/server1.pem", "The TLS cert file") keyFile = flag.String("tls_key_file", "testdata/server1.key", "The TLS key file") jsonDBFile = flag.String("route_guide_db", "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[pb.Point][]*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 *(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 { 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)) for _, feature := range s.savedFeatures { if float64(feature.Location.Longitude) >= left && float64(feature.Location.Longitude) <= right && float64(feature.Location.Latitude) >= bottom && float64(feature.Location.Latitude) <= top { 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 *(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 } point := *(in.Location) if _, present := s.routeNotes[point]; !present { s.routeNotes[point] = []*pb.RouteNote{in} } else { s.routeNotes[point] = append(s.routeNotes[point], in) } for _, note := range s.routeNotes[point] { 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 { log.Fatal("Failed to load default features: %v\n", err) } if err := json.Unmarshal(file, &(s.savedFeatures)); err != nil { log.Fatal("Failed to load default features: %v\n", err) } } func toRadians(num float64) float64 { return num * math.Pi / float64(180) } func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 { const COORD_FACTOR float64 = 1e7 const R float64 = float64(6371000) // metres lat1 := float64(p1.Latitude) / COORD_FACTOR lat2 := float64(p2.Latitude) / COORD_FACTOR lon1 := float64(p1.Longitude) / COORD_FACTOR lon2 := float64(p2.Longitude) / COORD_FACTOR φ1 := toRadians(lat1) φ2 := toRadians(lat2) Δφ := toRadians(lat2 - lat1) Δλ := toRadians(lon2 - lon1) 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 newServer() *routeGuideServer { s := new(routeGuideServer) s.loadFeatures(*jsonDBFile) s.routeNotes = make(map[pb.Point][]*pb.RouteNote, 0) return s } func main() { flag.Parse() p := strconv.Itoa(*port) lis, err := net.Listen("tcp", ":"+p) if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() pb.RegisterRouteGuideServer(grpcServer, newServer()) if *useTLS { creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) if err != nil { log.Fatalf("Failed to generate credentials %v", err) } grpcServer.Serve(creds.NewListener(lis)) } else { grpcServer.Serve(lis) } }