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 messages = require('./route_guide_pb');
20var services = require('./route_guide_grpc_pb');
21
22var async = require('async');
23var fs = require('fs');
24var parseArgs = require('minimist');
25var path = require('path');
26var _ = require('lodash');
27var grpc = require('grpc');
28
29var client = new services.RouteGuideClient('localhost:50051',
30                                           grpc.credentials.createInsecure());
31
32var COORD_FACTOR = 1e7;
33
34/**
35 * Run the getFeature demo. Calls getFeature with a point known to have a
36 * feature and a point known not to have a feature.
37 * @param {function} callback Called when this demo is complete
38 */
39function runGetFeature(callback) {
40  var next = _.after(2, callback);
41  function featureCallback(error, feature) {
42    if (error) {
43      callback(error);
44      return;
45    }
46    var latitude = feature.getLocation().getLatitude();
47    var longitude = feature.getLocation().getLongitude();
48    if (feature.getName() === '') {
49      console.log('Found no feature at ' +
50          latitude/COORD_FACTOR + ', ' + longitude/COORD_FACTOR);
51    } else {
52      console.log('Found feature called "' + feature.getName() + '" at ' +
53          latitude/COORD_FACTOR + ', ' + longitude/COORD_FACTOR);
54    }
55    next();
56  }
57  var point1 = new messages.Point();
58  point1.setLatitude(409146138);
59  point1.setLongitude(-746188906);
60  var point2 = new messages.Point();
61  point2.setLatitude(0);
62  point2.setLongitude(0);
63  client.getFeature(point1, featureCallback);
64  client.getFeature(point2, featureCallback);
65}
66
67/**
68 * Run the listFeatures demo. Calls listFeatures with a rectangle containing all
69 * of the features in the pre-generated database. Prints each response as it
70 * comes in.
71 * @param {function} callback Called when this demo is complete
72 */
73function runListFeatures(callback) {
74  var rect = new messages.Rectangle();
75  var lo = new messages.Point();
76  lo.setLatitude(400000000);
77  lo.setLongitude(-750000000);
78  rect.setLo(lo);
79  var hi = new messages.Point();
80  hi.setLatitude(420000000);
81  hi.setLongitude(-730000000);
82  rect.setHi(hi);
83  console.log('Looking for features between 40, -75 and 42, -73');
84  var call = client.listFeatures(rect);
85  call.on('data', function(feature) {
86      console.log('Found feature called "' + feature.getName() + '" at ' +
87          feature.getLocation().getLatitude()/COORD_FACTOR + ', ' +
88          feature.getLocation().getLongitude()/COORD_FACTOR);
89  });
90  call.on('end', callback);
91}
92
93/**
94 * Run the recordRoute demo. Sends several randomly chosen points from the
95 * pre-generated feature database with a variable delay in between. Prints the
96 * statistics when they are sent from the server.
97 * @param {function} callback Called when this demo is complete
98 */
99function runRecordRoute(callback) {
100  var argv = parseArgs(process.argv, {
101    string: 'db_path'
102  });
103  fs.readFile(path.resolve(argv.db_path), function(err, data) {
104    if (err) {
105      callback(err);
106      return;
107    }
108    // Transform the loaded features to Feature objects
109    var feature_list = _.map(JSON.parse(data), function(value) {
110      var feature = new messages.Feature();
111      feature.setName(value.name);
112      var location = new messages.Point();
113      location.setLatitude(value.location.latitude);
114      location.setLongitude(value.location.longitude);
115      feature.setLocation(location);
116      return feature;
117    });
118
119    var num_points = 10;
120    var call = client.recordRoute(function(error, stats) {
121      if (error) {
122        callback(error);
123        return;
124      }
125      console.log('Finished trip with', stats.getPointCount(), 'points');
126      console.log('Passed', stats.getFeatureCount(), 'features');
127      console.log('Travelled', stats.getDistance(), 'meters');
128      console.log('It took', stats.getElapsedTime(), 'seconds');
129      callback();
130    });
131    /**
132     * Constructs a function that asynchronously sends the given point and then
133     * delays sending its callback
134     * @param {messages.Point} location The point to send
135     * @return {function(function)} The function that sends the point
136     */
137    function pointSender(location) {
138      /**
139       * Sends the point, then calls the callback after a delay
140       * @param {function} callback Called when complete
141       */
142      return function(callback) {
143        console.log('Visiting point ' + location.getLatitude()/COORD_FACTOR +
144            ', ' + location.getLongitude()/COORD_FACTOR);
145        call.write(location);
146        _.delay(callback, _.random(500, 1500));
147      };
148    }
149    var point_senders = [];
150    for (var i = 0; i < num_points; i++) {
151      var rand_point = feature_list[_.random(0, feature_list.length - 1)];
152      point_senders[i] = pointSender(rand_point.getLocation());
153    }
154    async.series(point_senders, function() {
155      call.end();
156    });
157  });
158}
159
160/**
161 * Run the routeChat demo. Send some chat messages, and print any chat messages
162 * that are sent from the server.
163 * @param {function} callback Called when the demo is complete
164 */
165function runRouteChat(callback) {
166  var call = client.routeChat();
167  call.on('data', function(note) {
168    console.log('Got message "' + note.getMessage() + '" at ' +
169        note.getLocation().getLatitude() + ', ' +
170        note.getLocation().getLongitude());
171  });
172
173  call.on('end', callback);
174
175  var notes = [{
176    location: {
177      latitude: 0,
178      longitude: 0
179    },
180    message: 'First message'
181  }, {
182    location: {
183      latitude: 0,
184      longitude: 1
185    },
186    message: 'Second message'
187  }, {
188    location: {
189      latitude: 1,
190      longitude: 0
191    },
192    message: 'Third message'
193  }, {
194    location: {
195      latitude: 0,
196      longitude: 0
197    },
198    message: 'Fourth message'
199  }];
200  for (var i = 0; i < notes.length; i++) {
201    var note = notes[i];
202    console.log('Sending message "' + note.message + '" at ' +
203        note.location.latitude + ', ' + note.location.longitude);
204    var noteMsg = new messages.RouteNote();
205    noteMsg.setMessage(note.message);
206    var location = new messages.Point();
207    location.setLatitude(note.location.latitude);
208    location.setLongitude(note.location.longitude);
209    noteMsg.setLocation(location);
210    call.write(noteMsg);
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