1/*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19var PROTO_PATH = __dirname + '/../../../protos/route_guide.proto';
20
21var async = require('async');
22var fs = require('fs');
23var parseArgs = require('minimist');
24var path = require('path');
25var _ = require('lodash');
26var grpc = require('grpc');
27var protoLoader = require('@grpc/proto-loader');
28var packageDefinition = protoLoader.loadSync(
29    PROTO_PATH,
30    {keepCase: true,
31     longs: String,
32     enums: String,
33     defaults: true,
34     oneofs: true
35    });
36var routeguide = grpc.loadPackageDefinition(packageDefinition).routeguide;
37var client = new routeguide.RouteGuide('localhost:50051',
38                                       grpc.credentials.createInsecure());
39
40var COORD_FACTOR = 1e7;
41
42/**
43 * Run the getFeature demo. Calls getFeature with a point known to have a
44 * feature and a point known not to have a feature.
45 * @param {function} callback Called when this demo is complete
46 */
47function runGetFeature(callback) {
48  var next = _.after(2, callback);
49  function featureCallback(error, feature) {
50    if (error) {
51      callback(error);
52      return;
53    }
54    if (feature.name === '') {
55      console.log('Found no feature at ' +
56          feature.location.latitude/COORD_FACTOR + ', ' +
57          feature.location.longitude/COORD_FACTOR);
58    } else {
59      console.log('Found feature called "' + feature.name + '" at ' +
60          feature.location.latitude/COORD_FACTOR + ', ' +
61          feature.location.longitude/COORD_FACTOR);
62    }
63    next();
64  }
65  var point1 = {
66    latitude: 409146138,
67    longitude: -746188906
68  };
69  var point2 = {
70    latitude: 0,
71    longitude: 0
72  };
73  client.getFeature(point1, featureCallback);
74  client.getFeature(point2, featureCallback);
75}
76
77/**
78 * Run the listFeatures demo. Calls listFeatures with a rectangle containing all
79 * of the features in the pre-generated database. Prints each response as it
80 * comes in.
81 * @param {function} callback Called when this demo is complete
82 */
83function runListFeatures(callback) {
84  var rectangle = {
85    lo: {
86      latitude: 400000000,
87      longitude: -750000000
88    },
89    hi: {
90      latitude: 420000000,
91      longitude: -730000000
92    }
93  };
94  console.log('Looking for features between 40, -75 and 42, -73');
95  var call = client.listFeatures(rectangle);
96  call.on('data', function(feature) {
97      console.log('Found feature called "' + feature.name + '" at ' +
98          feature.location.latitude/COORD_FACTOR + ', ' +
99          feature.location.longitude/COORD_FACTOR);
100  });
101  call.on('end', callback);
102}
103
104/**
105 * Run the recordRoute demo. Sends several randomly chosen points from the
106 * pre-generated feature database with a variable delay in between. Prints the
107 * statistics when they are sent from the server.
108 * @param {function} callback Called when this demo is complete
109 */
110function runRecordRoute(callback) {
111  var argv = parseArgs(process.argv, {
112    string: 'db_path'
113  });
114  fs.readFile(path.resolve(argv.db_path), function(err, data) {
115    if (err) {
116      callback(err);
117      return;
118    }
119    var feature_list = JSON.parse(data);
120
121    var num_points = 10;
122    var call = client.recordRoute(function(error, stats) {
123      if (error) {
124        callback(error);
125        return;
126      }
127      console.log('Finished trip with', stats.point_count, 'points');
128      console.log('Passed', stats.feature_count, 'features');
129      console.log('Travelled', stats.distance, 'meters');
130      console.log('It took', stats.elapsed_time, 'seconds');
131      callback();
132    });
133    /**
134     * Constructs a function that asynchronously sends the given point and then
135     * delays sending its callback
136     * @param {number} lat The latitude to send
137     * @param {number} lng The longitude to send
138     * @return {function(function)} The function that sends the point
139     */
140    function pointSender(lat, lng) {
141      /**
142       * Sends the point, then calls the callback after a delay
143       * @param {function} callback Called when complete
144       */
145      return function(callback) {
146        console.log('Visiting point ' + lat/COORD_FACTOR + ', ' +
147            lng/COORD_FACTOR);
148        call.write({
149          latitude: lat,
150          longitude: lng
151        });
152        _.delay(callback, _.random(500, 1500));
153      };
154    }
155    var point_senders = [];
156    for (var i = 0; i < num_points; i++) {
157      var rand_point = feature_list[_.random(0, feature_list.length - 1)];
158      point_senders[i] = pointSender(rand_point.location.latitude,
159                                     rand_point.location.longitude);
160    }
161    async.series(point_senders, function() {
162      call.end();
163    });
164  });
165}
166
167/**
168 * Run the routeChat demo. Send some chat messages, and print any chat messages
169 * that are sent from the server.
170 * @param {function} callback Called when the demo is complete
171 */
172function runRouteChat(callback) {
173  var call = client.routeChat();
174  call.on('data', function(note) {
175    console.log('Got message "' + note.message + '" at ' +
176        note.location.latitude + ', ' + note.location.longitude);
177  });
178
179  call.on('end', callback);
180
181  var notes = [{
182    location: {
183      latitude: 0,
184      longitude: 0
185    },
186    message: 'First message'
187  }, {
188    location: {
189      latitude: 0,
190      longitude: 1
191    },
192    message: 'Second message'
193  }, {
194    location: {
195      latitude: 1,
196      longitude: 0
197    },
198    message: 'Third message'
199  }, {
200    location: {
201      latitude: 0,
202      longitude: 0
203    },
204    message: 'Fourth message'
205  }];
206  for (var i = 0; i < notes.length; i++) {
207    var note = notes[i];
208    console.log('Sending message "' + note.message + '" at ' +
209        note.location.latitude + ', ' + note.location.longitude);
210    call.write(note);
211  }
212  call.end();
213}
214
215/**
216 * Run all of the demos in order
217 */
218function main() {
219  async.series([
220    runGetFeature,
221    runListFeatures,
222    runRecordRoute,
223    runRouteChat
224  ]);
225}
226
227if (require.main === module) {
228  main();
229}
230
231exports.runGetFeature = runGetFeature;
232
233exports.runListFeatures = runListFeatures;
234
235exports.runRecordRoute = runRecordRoute;
236
237exports.runRouteChat = runRouteChat;
238