Index: third_party/grpc/examples/node/route_guide/route_guide_server.js |
diff --git a/third_party/grpc/examples/node/route_guide/route_guide_server.js b/third_party/grpc/examples/node/route_guide/route_guide_server.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9fa98279911e6b6f21e7670f86a66fb63a6f13bf |
--- /dev/null |
+++ b/third_party/grpc/examples/node/route_guide/route_guide_server.js |
@@ -0,0 +1,253 @@ |
+/* |
+ * |
+ * Copyright 2015-2016, 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. |
+ * |
+ */ |
+ |
+var PROTO_PATH = __dirname + '/../../protos/route_guide.proto'; |
+ |
+var fs = require('fs'); |
+var parseArgs = require('minimist'); |
+var path = require('path'); |
+var _ = require('lodash'); |
+var grpc = require('grpc'); |
+var routeguide = grpc.load(PROTO_PATH).routeguide; |
+ |
+var COORD_FACTOR = 1e7; |
+ |
+/** |
+ * For simplicity, a point is a record type that looks like |
+ * {latitude: number, longitude: number}, and a feature is a record type that |
+ * looks like {name: string, location: point}. feature objects with name==='' |
+ * are points with no feature. |
+ */ |
+ |
+/** |
+ * List of feature objects at points that have been requested so far. |
+ */ |
+var feature_list = []; |
+ |
+/** |
+ * Get a feature object at the given point, or creates one if it does not exist. |
+ * @param {point} point The point to check |
+ * @return {feature} The feature object at the point. Note that an empty name |
+ * indicates no feature |
+ */ |
+function checkFeature(point) { |
+ var feature; |
+ // Check if there is already a feature object for the given point |
+ for (var i = 0; i < feature_list.length; i++) { |
+ feature = feature_list[i]; |
+ if (feature.location.latitude === point.latitude && |
+ feature.location.longitude === point.longitude) { |
+ return feature; |
+ } |
+ } |
+ var name = ''; |
+ feature = { |
+ name: name, |
+ location: point |
+ }; |
+ return feature; |
+} |
+ |
+/** |
+ * getFeature request handler. Gets a request with a point, and responds with a |
+ * feature object indicating whether there is a feature at that point. |
+ * @param {EventEmitter} call Call object for the handler to process |
+ * @param {function(Error, feature)} callback Response callback |
+ */ |
+function getFeature(call, callback) { |
+ callback(null, checkFeature(call.request)); |
+} |
+ |
+/** |
+ * listFeatures request handler. Gets a request with two points, and responds |
+ * with a stream of all features in the bounding box defined by those points. |
+ * @param {Writable} call Writable stream for responses with an additional |
+ * request property for the request value. |
+ */ |
+function listFeatures(call) { |
+ var lo = call.request.lo; |
+ var hi = call.request.hi; |
+ var left = _.min([lo.longitude, hi.longitude]); |
+ var right = _.max([lo.longitude, hi.longitude]); |
+ var top = _.max([lo.latitude, hi.latitude]); |
+ var bottom = _.min([lo.latitude, hi.latitude]); |
+ // For each feature, check if it is in the given bounding box |
+ _.each(feature_list, function(feature) { |
+ if (feature.name === '') { |
+ return; |
+ } |
+ if (feature.location.longitude >= left && |
+ feature.location.longitude <= right && |
+ feature.location.latitude >= bottom && |
+ feature.location.latitude <= top) { |
+ call.write(feature); |
+ } |
+ }); |
+ call.end(); |
+} |
+ |
+/** |
+ * Calculate the distance between two points using the "haversine" formula. |
+ * This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. |
+ * @param start The starting point |
+ * @param end The end point |
+ * @return The distance between the points in meters |
+ */ |
+function getDistance(start, end) { |
+ function toRadians(num) { |
+ return num * Math.PI / 180; |
+ } |
+ var lat1 = start.latitude / COORD_FACTOR; |
+ var lat2 = end.latitude / COORD_FACTOR; |
+ var lon1 = start.longitude / COORD_FACTOR; |
+ var lon2 = end.longitude / COORD_FACTOR; |
+ var R = 6371000; // metres |
+ var φ1 = toRadians(lat1); |
+ var φ2 = toRadians(lat2); |
+ var Δφ = toRadians(lat2-lat1); |
+ var Δλ = toRadians(lon2-lon1); |
+ |
+ var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + |
+ Math.cos(φ1) * Math.cos(φ2) * |
+ Math.sin(Δλ/2) * Math.sin(Δλ/2); |
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); |
+ |
+ return R * c; |
+} |
+ |
+/** |
+ * recordRoute handler. 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. |
+ * @param {Readable} call The request point stream. |
+ * @param {function(Error, routeSummary)} callback The callback to pass the |
+ * response to |
+ */ |
+function recordRoute(call, callback) { |
+ var point_count = 0; |
+ var feature_count = 0; |
+ var distance = 0; |
+ var previous = null; |
+ // Start a timer |
+ var start_time = process.hrtime(); |
+ call.on('data', function(point) { |
+ point_count += 1; |
+ if (checkFeature(point).name !== '') { |
+ feature_count += 1; |
+ } |
+ /* For each point after the first, add the incremental distance from the |
+ * previous point to the total distance value */ |
+ if (previous != null) { |
+ distance += getDistance(previous, point); |
+ } |
+ previous = point; |
+ }); |
+ call.on('end', function() { |
+ callback(null, { |
+ point_count: point_count, |
+ feature_count: feature_count, |
+ // Cast the distance to an integer |
+ distance: distance|0, |
+ // End the timer |
+ elapsed_time: process.hrtime(start_time)[0] |
+ }); |
+ }); |
+} |
+ |
+var route_notes = {}; |
+ |
+/** |
+ * Turn the point into a dictionary key. |
+ * @param {point} point The point to use |
+ * @return {string} The key for an object |
+ */ |
+function pointKey(point) { |
+ return point.latitude + ' ' + point.longitude; |
+} |
+ |
+/** |
+ * routeChat handler. Receives a stream of message/location pairs, and responds |
+ * with a stream of all previous messages at each of those locations. |
+ * @param {Duplex} call The stream for incoming and outgoing messages |
+ */ |
+function routeChat(call) { |
+ call.on('data', function(note) { |
+ var key = pointKey(note.location); |
+ /* For each note sent, respond with all previous notes that correspond to |
+ * the same point */ |
+ if (route_notes.hasOwnProperty(key)) { |
+ _.each(route_notes[key], function(note) { |
+ call.write(note); |
+ }); |
+ } else { |
+ route_notes[key] = []; |
+ } |
+ // Then add the new note to the list |
+ route_notes[key].push(JSON.parse(JSON.stringify(note))); |
+ }); |
+ call.on('end', function() { |
+ call.end(); |
+ }); |
+} |
+ |
+/** |
+ * Get a new server with the handler functions in this file bound to the methods |
+ * it serves. |
+ * @return {Server} The new server object |
+ */ |
+function getServer() { |
+ var server = new grpc.Server(); |
+ server.addProtoService(routeguide.RouteGuide.service, { |
+ getFeature: getFeature, |
+ listFeatures: listFeatures, |
+ recordRoute: recordRoute, |
+ routeChat: routeChat |
+ }); |
+ return server; |
+} |
+ |
+if (require.main === module) { |
+ // If this is run as a script, start a server on an unused port |
+ var routeServer = getServer(); |
+ routeServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); |
+ var argv = parseArgs(process.argv, { |
+ string: 'db_path' |
+ }); |
+ fs.readFile(path.resolve(argv.db_path), function(err, data) { |
+ if (err) throw err; |
+ feature_list = JSON.parse(data); |
+ routeServer.start(); |
+ }); |
+} |
+ |
+exports.getServer = getServer; |