mirror of
https://github.com/genuinetools/reg.git
synced 2024-09-12 06:11:03 -04:00
238 lines
7 KiB
Go
238 lines
7 KiB
Go
/*
|
|
*
|
|
* Copyright 2015 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.
|
|
*
|
|
*/
|
|
|
|
//go:generate protoc -I ../routeguide --go_out=plugins=grpc:../routeguide ../routeguide/route_guide.proto
|
|
|
|
// 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 routeguide/route_guide.proto.
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"math"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/testdata"
|
|
|
|
"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", "", "The TLS cert file")
|
|
keyFile = flag.String("key_file", "", "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 // read-only after initialized
|
|
|
|
mu sync.Mutex // protects routeNotes
|
|
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{Location: point}, nil
|
|
}
|
|
|
|
// ListFeatures lists all features contained 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)
|
|
|
|
s.mu.Lock()
|
|
s.routeNotes[key] = append(s.routeNotes[key], in)
|
|
// Note: this copy prevents blocking other clients while serving this one.
|
|
// We don't need to do a deep copy, because elements in the slice are
|
|
// insert-only and never modified.
|
|
rn := make([]*pb.RouteNote, len(s.routeNotes[key]))
|
|
copy(rn, s.routeNotes[key])
|
|
s.mu.Unlock()
|
|
|
|
for _, note := range rn {
|
|
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.Fatalf("Failed to load default features: %v", err)
|
|
}
|
|
if err := json.Unmarshal(file, &s.savedFeatures); err != nil {
|
|
log.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.
|
|
// The formula is based on http://mathforum.org/library/drmath/view/51879.html.
|
|
func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
|
|
const CordFactor float64 = 1e7
|
|
const R float64 = float64(6371000) // earth radius in metres
|
|
lat1 := toRadians(float64(p1.Latitude) / CordFactor)
|
|
lat2 := toRadians(float64(p2.Latitude) / CordFactor)
|
|
lng1 := toRadians(float64(p1.Longitude) / CordFactor)
|
|
lng2 := toRadians(float64(p2.Longitude) / CordFactor)
|
|
dlat := lat2 - lat1
|
|
dlng := lng2 - lng1
|
|
|
|
a := math.Sin(dlat/2)*math.Sin(dlat/2) +
|
|
math.Cos(lat1)*math.Cos(lat2)*
|
|
math.Sin(dlng/2)*math.Sin(dlng/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 := &routeGuideServer{routeNotes: make(map[string][]*pb.RouteNote)}
|
|
s.loadFeatures(*jsonDBFile)
|
|
return s
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
|
|
if err != nil {
|
|
log.Fatalf("failed to listen: %v", err)
|
|
}
|
|
var opts []grpc.ServerOption
|
|
if *tls {
|
|
if *certFile == "" {
|
|
*certFile = testdata.Path("server1.pem")
|
|
}
|
|
if *keyFile == "" {
|
|
*keyFile = testdata.Path("server1.key")
|
|
}
|
|
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
|
|
if err != nil {
|
|
log.Fatalf("Failed to generate credentials %v", err)
|
|
}
|
|
opts = []grpc.ServerOption{grpc.Creds(creds)}
|
|
}
|
|
grpcServer := grpc.NewServer(opts...)
|
|
pb.RegisterRouteGuideServer(grpcServer, newServer())
|
|
grpcServer.Serve(lis)
|
|
}
|