mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-15 20:44:46 +00:00
[WIP] Moving test services to this repo
This commit is contained in:
113
rpc/rpc-server/handler/handler.go
Normal file
113
rpc/rpc-server/handler/handler.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
pb "github.com/micro/micro/v3/test/service/rpc/proto"
|
||||
)
|
||||
|
||||
// RouteGuide implements the route guide handler interface
|
||||
type RouteGuide struct {
|
||||
Features []*pb.Feature
|
||||
Notes map[string][]*pb.RouteNote
|
||||
NotesLock sync.Mutex
|
||||
}
|
||||
|
||||
// GetFeature obtains the feature at a given position.
|
||||
func (r *RouteGuide) GetFeature(ctx context.Context, point *pb.Point, feature *pb.Feature) error {
|
||||
for _, f := range r.Features {
|
||||
if proto.Equal(f.Location, point) {
|
||||
*feature = *f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// No feature was found, return an unnamed feature
|
||||
feature.Location = point
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListFeatures obtains the Features available within the given Rectangle. Results are
|
||||
// streamed rather than returned at once (e.g. in a response message with a
|
||||
// repeated field), as the rectangle may cover a large area and contain a
|
||||
// huge number of features.
|
||||
func (r *RouteGuide) ListFeatures(ctx context.Context, rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesStream) error {
|
||||
for _, f := range r.Features {
|
||||
if inRange(f.Location, rect) {
|
||||
if err := stream.Send(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordRoute accepts a stream of Points on a route being traversed, returning a
|
||||
// RouteSummary when traversal is completed.
|
||||
func (r *RouteGuide) RecordRoute(ctx context.Context, stream pb.RouteGuide_RecordRouteStream) 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 _, f := range r.Features {
|
||||
if proto.Equal(f.Location, point) {
|
||||
featureCount++
|
||||
}
|
||||
}
|
||||
if lastPoint != nil {
|
||||
distance += calcDistance(lastPoint, point)
|
||||
}
|
||||
lastPoint = point
|
||||
}
|
||||
}
|
||||
|
||||
// RouteChat accepts a stream of RouteNotes sent while a route is being traversed,
|
||||
// while receiving other RouteNotes (e.g. from other users).
|
||||
func (r *RouteGuide) RouteChat(ctx context.Context, stream pb.RouteGuide_RouteChatStream) error {
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := serialize(in.Location)
|
||||
|
||||
r.NotesLock.Lock()
|
||||
r.Notes[key] = append(r.Notes[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(r.Notes[key]))
|
||||
copy(rn, r.Notes[key])
|
||||
r.NotesLock.Unlock()
|
||||
|
||||
for _, note := range rn {
|
||||
if err := stream.Send(note); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
rpc/rpc-server/handler/util.go
Normal file
52
rpc/rpc-server/handler/util.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
pb "github.com/micro/micro/v3/test/service/rpc/proto"
|
||||
)
|
||||
|
||||
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(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)
|
||||
}
|
||||
Reference in New Issue
Block a user