| 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;
|
|
|