1 // Copyright 2015 gRPC authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 using System; 16 using System.Collections.Concurrent; 17 using System.Collections.Generic; 18 using System.Diagnostics; 19 using System.Linq; 20 using System.Text; 21 using System.Threading.Tasks; 22 23 using Grpc.Core; 24 using Grpc.Core.Utils; 25 26 namespace Routeguide 27 { 28 /// <summary> 29 /// Example implementation of RouteGuide server. 30 /// </summary> 31 public class RouteGuideImpl : RouteGuide.RouteGuideBase 32 { 33 readonly List<Feature> features; 34 readonly object myLock = new object(); 35 readonly Dictionary<Point, List<RouteNote>> routeNotes = new Dictionary<Point, List<RouteNote>>(); 36 RouteGuideImpl(List<Feature> features)37 public RouteGuideImpl(List<Feature> features) 38 { 39 this.features = features; 40 } 41 42 /// <summary> 43 /// Gets the feature at the requested point. If no feature at that location 44 /// exists, an unnammed feature is returned at the provided location. 45 /// </summary> GetFeature(Point request, ServerCallContext context)46 public override Task<Feature> GetFeature(Point request, ServerCallContext context) 47 { 48 return Task.FromResult(CheckFeature(request)); 49 } 50 51 /// <summary> 52 /// Gets all features contained within the given bounding rectangle. 53 /// </summary> ListFeatures(Rectangle request, IServerStreamWriter<Feature> responseStream, ServerCallContext context)54 public override async Task ListFeatures(Rectangle request, IServerStreamWriter<Feature> responseStream, ServerCallContext context) 55 { 56 var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) ); 57 foreach (var response in responses) 58 { 59 await responseStream.WriteAsync(response); 60 } 61 } 62 63 /// <summary> 64 /// Gets a stream of points, and responds with statistics about the "trip": number of points, 65 /// number of known features visited, total distance traveled, and total time spent. 66 /// </summary> RecordRoute(IAsyncStreamReader<Point> requestStream, ServerCallContext context)67 public override async Task<RouteSummary> RecordRoute(IAsyncStreamReader<Point> requestStream, ServerCallContext context) 68 { 69 int pointCount = 0; 70 int featureCount = 0; 71 int distance = 0; 72 Point previous = null; 73 var stopwatch = new Stopwatch(); 74 stopwatch.Start(); 75 76 while (await requestStream.MoveNext()) 77 { 78 var point = requestStream.Current; 79 pointCount++; 80 if (CheckFeature(point).Exists()) 81 { 82 featureCount++; 83 } 84 if (previous != null) 85 { 86 distance += (int) previous.GetDistance(point); 87 } 88 previous = point; 89 } 90 91 stopwatch.Stop(); 92 93 return new RouteSummary 94 { 95 PointCount = pointCount, 96 FeatureCount = featureCount, 97 Distance = distance, 98 ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / 1000) 99 }; 100 } 101 102 /// <summary> 103 /// Receives a stream of message/location pairs, and responds with a stream of all previous 104 /// messages at each of those locations. 105 /// </summary> RouteChat(IAsyncStreamReader<RouteNote> requestStream, IServerStreamWriter<RouteNote> responseStream, ServerCallContext context)106 public override async Task RouteChat(IAsyncStreamReader<RouteNote> requestStream, IServerStreamWriter<RouteNote> responseStream, ServerCallContext context) 107 { 108 while (await requestStream.MoveNext()) 109 { 110 var note = requestStream.Current; 111 List<RouteNote> prevNotes = AddNoteForLocation(note.Location, note); 112 foreach (var prevNote in prevNotes) 113 { 114 await responseStream.WriteAsync(prevNote); 115 } 116 } 117 } 118 119 /// <summary> 120 /// Adds a note for location and returns a list of pre-existing notes for that location (not containing the newly added note). 121 /// </summary> AddNoteForLocation(Point location, RouteNote note)122 private List<RouteNote> AddNoteForLocation(Point location, RouteNote note) 123 { 124 lock (myLock) 125 { 126 List<RouteNote> notes; 127 if (!routeNotes.TryGetValue(location, out notes)) { 128 notes = new List<RouteNote>(); 129 routeNotes.Add(location, notes); 130 } 131 var preexistingNotes = new List<RouteNote>(notes); 132 notes.Add(note); 133 return preexistingNotes; 134 } 135 } 136 137 /// <summary> 138 /// Gets the feature at the given point. 139 /// </summary> 140 /// <param name="location">the location to check</param> 141 /// <returns>The feature object at the point Note that an empty name indicates no feature.</returns> CheckFeature(Point location)142 private Feature CheckFeature(Point location) 143 { 144 var result = features.FirstOrDefault((feature) => feature.Location.Equals(location)); 145 if (result == null) 146 { 147 // No feature was found, return an unnamed feature. 148 return new Feature { Name = "", Location = location }; 149 } 150 return result; 151 } 152 } 153 } 154