signalk-racer 0.9.8 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ReleaseNotes.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.9.10 Updates for KIP widgets
2
+ + Do not set a start time unless timer is running
3
+ + Schema RFC 3339 units for start time.
4
+
5
+ ## 0.9.9 Working with KIP prototypes
6
+ + Fixed API handlers to use return status rather than callback
7
+
1
8
  ## 0.9.8
2
9
  + Minor updates to webapp UI
3
10
 
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  module.exports = (app) => {
3
2
  const state = {};
4
3
  const geolib = require('geolib')
@@ -51,6 +50,7 @@ module.exports = (app) => {
51
50
  {
52
51
  "path": "navigation.racing.startLineLength",
53
52
  "value": {
53
+ "type": "number",
54
54
  "units": "m",
55
55
  "description": "Length of the start line",
56
56
  "displayName": "Length of the start line",
@@ -60,6 +60,7 @@ module.exports = (app) => {
60
60
  {
61
61
  "path": "navigation.racing.stbLineBias",
62
62
  "value": {
63
+ "type": "number",
63
64
  "units": "m",
64
65
  "description": "Bias of the start line for the starboard end",
65
66
  "displayName": "Bias of the start line for the starboard end",
@@ -69,6 +70,7 @@ module.exports = (app) => {
69
70
  {
70
71
  "path": "navigation.racing.nextLegHeading",
71
72
  "value": {
73
+ "type": "number",
72
74
  "units": "rad",
73
75
  "description": "Heading of the next leg of the course",
74
76
  "displayName": "Next Heading",
@@ -78,12 +80,26 @@ module.exports = (app) => {
78
80
  {
79
81
  "path": "navigation.racing.nextLegTrueWindAngle",
80
82
  "value": {
83
+ "type": "number",
81
84
  "units": "rad",
82
85
  "description": "TWA on the next leg of the course",
83
86
  "displayName": "Next TWA",
84
87
  "shortName": "NextTWA",
85
88
  }
86
89
  },
90
+ {
91
+ "path": "navigation.racing.startTime",
92
+ "value": {
93
+ "type": "string",
94
+ "units": "RFC 3339 (UTC)",
95
+ "example": "2014-04-10T08:33:53.123Z",
96
+ "format": "date-time",
97
+ "pattern" : ".*Z$",
98
+ "description": "Time of the race start in RFC 3339 UTC only format ",
99
+ "displayName": "Start Time",
100
+ "shortName": "StartTime",
101
+ }
102
+ },
87
103
  ]
88
104
  }
89
105
  ]
@@ -136,10 +152,13 @@ module.exports = (app) => {
136
152
  }
137
153
 
138
154
  function toOtherEnd(end) {
139
- switch(end) {
140
- case 'port' : return 'stb';
141
- case 'stb' : return 'port';
142
- default: return null;
155
+ switch (end) {
156
+ case 'port' :
157
+ return 'stb';
158
+ case 'stb' :
159
+ return 'port';
160
+ default:
161
+ return null;
143
162
  }
144
163
  }
145
164
 
@@ -170,9 +189,9 @@ module.exports = (app) => {
170
189
  }
171
190
 
172
191
  function complete(callback, statusCode, message, details) {
173
- const result = {state: 'COMPLETED', statusCode, message, details};
192
+ const result = {state: statusCode === 200 ? 'SUCCESS' : 'FAILURE', statusCode, message, details};
174
193
  app.debug(JSON.stringify(result));
175
- callback(result);
194
+ return result;
176
195
  }
177
196
 
178
197
  // Put an absolute position
@@ -180,30 +199,26 @@ module.exports = (app) => {
180
199
  app.debug(`putStartLineEnd: ${end} ${JSON.stringify(position)}`);
181
200
 
182
201
  try {
183
- if (!end || !position || typeof position.latitude !== 'number' || typeof position.longitude !== 'number' ) {
184
- complete(callback, 400, 'Failed to put start line end: !end || !position');
185
- return;
202
+ if (!end || !position || typeof position.latitude !== 'number' || typeof position.longitude !== 'number') {
203
+ return complete(callback, 400, 'Failed to put start line end: !end || !position');
186
204
  }
187
205
 
188
206
  end = end.toLowerCase();
189
207
  if (end !== 'port' && end !== 'stb') {
190
- complete(callback, 400, 'Failed to put start line end: unknown end');
191
- return;
208
+ return complete(callback, 400, 'Failed to put start line end: unknown end');
192
209
  }
193
210
 
194
211
  let startLine = state.startLine;
195
212
  if (startLine) {
196
213
  // We have a line, check if this end is the same?
197
214
  if (startLine[end].latitude === position.latitude && startLine[end].longitude === position.longitude) {
198
- complete(callback, 304, 'Put start line end: unchanged line end');
199
- return;
215
+ return complete(callback, 304, 'Put start line end: unchanged line end');
200
216
  }
201
217
  } else {
202
218
  // We don't have a line, so check if this is just an update for one end of the line?
203
219
  const thisEnd = app.getSelfPath(`navigation.racing.startLine${camelCase(end)}`);
204
220
  if (thisEnd && thisEnd.value.lat === position.latitude && thisEnd.value.longitude === position.longitude) {
205
- complete(callback, 304, 'Put start line end: unchanged end');
206
- return;
221
+ return complete(callback, 304, 'Put start line end: unchanged end');
207
222
  }
208
223
 
209
224
  // If we now have both ends, we have a line!
@@ -237,7 +252,7 @@ module.exports = (app) => {
237
252
 
238
253
  // Set/create the waypoint if we are configured to do so
239
254
  if (state.options.updateStartLineWaypoint || updateWaypoint) {
240
- const waypointConfig =`startLine${camelCase(end)}`;
255
+ const waypointConfig = `startLine${camelCase(end)}`;
241
256
  app.debug(`waypointConfig: ${waypointConfig}`);
242
257
  const waypointName = state.options[waypointConfig];
243
258
  app.debug(`waypointName: ${waypointName}`);
@@ -268,6 +283,11 @@ module.exports = (app) => {
268
283
  coordinates: [position.longitude, position.latitude]
269
284
  },
270
285
  properties: {}
286
+ },
287
+ type: end === 'port' ? 'start-pin' : 'start-boat',
288
+ position: {
289
+ latitude: position.latitude,
290
+ longitude: position.longitude
271
291
  }
272
292
  }
273
293
 
@@ -283,36 +303,33 @@ module.exports = (app) => {
283
303
  app.debug('startline waypoint not updated');
284
304
  }
285
305
 
286
- // Complete the handler successfully
287
- complete(callback, 200, 'Put start line end: OK');
288
306
 
289
307
  // If we have a startLine, then process position against the new line
290
308
  if (startLine)
291
309
  processPosition(null);
310
+ // Complete the handler successfully
311
+ return complete(callback, 200, 'Put start line end: OK');
292
312
  } catch (err) {
293
- complete(callback, 500, 'Put start line end: Failed ' + JSON.stringify(err));
313
+ return complete(callback, 500, 'Put start line end: Failed ' + JSON.stringify(err));
294
314
  }
295
-
296
- return { state: 'PENDING' }
297
315
  }
298
316
 
299
317
  async function setStartLine(context, path, args, callback) {
300
318
  try {
301
319
  app.debug('setStartLine:', JSON.stringify(args));
302
320
  if (!args || !args.end || typeof args.end !== 'string') {
303
- complete(callback, 400, 'Failed to set the startLine: !end');
304
- return;
321
+ return complete(callback, 400, 'Failed to set the startLine: !end');
305
322
  }
306
323
  const end = args.end.toLowerCase();
307
324
  if (end !== 'port' && end !== 'stb') {
308
- complete(callback, 400, 'Failed to set the startLine: unknown end');
309
- return;
325
+ return complete(callback, 400, 'Failed to set the startLine: unknown end');
310
326
  }
311
327
 
312
328
  // Get the position either as arg or as the current bow position
313
329
  let position = args.position;
314
- if (position === 'bow') {
330
+ if (position === 'bow' || !position && !args.delta && !args.rotate ) {
315
331
  position = app.getSelfPath('navigation.position');
332
+ app.debug('bowPosition:' + JSON.stringify(position));
316
333
  if (position)
317
334
  position = bowPosition(position.value);
318
335
  }
@@ -332,7 +349,7 @@ module.exports = (app) => {
332
349
  app.debug(`initialBearing: ${initialBearing} currentDistance: ${currentDistance}`);
333
350
 
334
351
  const newDistance = args.delta && typeof args.delta === 'number' ? (currentDistance + args.delta) : currentDistance;
335
- const newBearing = args.rotate && typeof args.rotate === 'number'? (initialBearing + toDegrees(args.rotate)) : initialBearing;
352
+ const newBearing = args.rotate && typeof args.rotate === 'number' ? (initialBearing + toDegrees(args.rotate)) : initialBearing;
336
353
  app.debug(`newDistance: ${newDistance} newBearing: ${newBearing}`);
337
354
 
338
355
  const newPosition = geolib.computeDestinationPoint(otherEnd, newDistance, newBearing);
@@ -342,11 +359,10 @@ module.exports = (app) => {
342
359
 
343
360
  if (position)
344
361
  return putStartLineEnd(end, position, callback, args.updateWaypoint === true);
345
- complete(callback, 500, 'Failed to set the startLine: No position');
362
+ return complete(callback, 500, 'Failed to set the startLine: No position');
346
363
  } catch (err) {
347
- complete(callback, 500, 'Failed to set the startLine: ' + JSON.stringify(err));
364
+ return complete(callback, 500, 'Failed to set the startLine: ' + JSON.stringify(err));
348
365
  }
349
- return { state: 'PENDING' }
350
366
  }
351
367
 
352
368
  function setTimer() {
@@ -358,7 +374,7 @@ module.exports = (app) => {
358
374
  return;
359
375
  }
360
376
  state.timeToStart--;
361
- sendDeltas([{ path: 'navigation.racing.timeToStart', value: state.timeToStart }]);
377
+ sendDeltas([{path: 'navigation.racing.timeToStart', value: state.timeToStart}]);
362
378
  }, 1000);
363
379
  }
364
380
 
@@ -367,13 +383,11 @@ module.exports = (app) => {
367
383
  try {
368
384
  app.debug('startTimerCommand:', JSON.stringify(args));
369
385
  if (!args || !args.command || typeof args.command !== 'string') {
370
- complete(callback, 400, 'Failed to command timer: no command');
371
- return;
386
+ return complete(callback, 400, 'Failed to command timer: no command');
372
387
  }
373
388
  const command = args.command.toLowerCase();
374
389
 
375
- switch (command)
376
- {
390
+ switch (command) {
377
391
  case 'start': {
378
392
  const now = new Date();
379
393
  const timeToStart = state.timeToStart ?? state.options.timer ?? 300;
@@ -389,8 +403,7 @@ module.exports = (app) => {
389
403
  ]);
390
404
 
391
405
  setTimer();
392
- complete(callback, 200, 'start timer: OK');
393
- break;
406
+ return complete(callback, 200, 'start timer: OK');
394
407
  }
395
408
  case 'reset': {
396
409
  state.timerRunning = false;
@@ -401,14 +414,12 @@ module.exports = (app) => {
401
414
  {path: 'navigation.racing.timeToStart', value: state.timeToStart},
402
415
  {path: 'navigation.racing.startTime', value: null}
403
416
  ]);
404
- complete(callback, 200, 'reset timer: OK');
405
- break;
417
+ return complete(callback, 200, 'reset timer: OK');
406
418
  }
407
419
 
408
420
  case 'sync': {
409
421
  if (!state.timerRunning || state.timeToStart == null) {
410
- complete(callback, 500, 'sync timer: !running');
411
- return;
422
+ return complete(callback, 500, 'sync timer: !running');
412
423
  }
413
424
 
414
425
  const rounded = Math.round(state.timeToStart / 60) * 60;
@@ -416,24 +427,21 @@ module.exports = (app) => {
416
427
  state.startTime = new Date(Date.now() + rounded * 1000).toISOString();
417
428
 
418
429
  sendDeltas([
419
- { path: 'navigation.racing.timeToStart', value: state.timeToStart },
420
- { path: 'navigation.racing.startTime', value: state.startTime }
430
+ {path: 'navigation.racing.timeToStart', value: state.timeToStart},
431
+ {path: 'navigation.racing.startTime', value: state.startTime}
421
432
  ]);
422
- complete(callback, 200, 'sync timer: OK');
423
- break;
433
+ return complete(callback, 200, 'sync timer: OK');
424
434
  }
425
435
 
426
436
  case 'set': {
427
437
  const input = args.startTime;
428
438
  if (!input) {
429
- complete(callback, 400, 'reset timer: !startTime');
430
- return;
439
+ return complete(callback, 400, 'reset timer: !startTime');
431
440
  }
432
441
 
433
442
  const inputTime = new Date(input);
434
443
  if (isNaN(inputTime)) {
435
- complete(callback, 400, 'reset timer: invalid startTime');
436
- return;
444
+ return complete(callback, 400, 'reset timer: invalid startTime');
437
445
  }
438
446
 
439
447
  const now = new Date();
@@ -444,11 +452,10 @@ module.exports = (app) => {
444
452
  state.timeToStart = 0;
445
453
  state.startTime = null;
446
454
  sendDeltas([
447
- { path: 'navigation.racing.timeToStart', value: 0 },
448
- { path: 'navigation.racing.startTime', value: null }
455
+ {path: 'navigation.racing.timeToStart', value: 0},
456
+ {path: 'navigation.racing.startTime', value: null}
449
457
  ]);
450
- complete(callback, 200, 'set timer: 0K');
451
- return;
458
+ return complete(callback, 200, 'set timer: 0K');
452
459
  }
453
460
 
454
461
  state.timerRunning = true;
@@ -456,46 +463,47 @@ module.exports = (app) => {
456
463
  state.startTime = inputTime.toISOString();
457
464
 
458
465
  sendDeltas([
459
- { path: 'navigation.racing.timeToStart', value: state.timeToStart },
460
- { path: 'navigation.racing.startTime', value: state.startTime }
466
+ {path: 'navigation.racing.timeToStart', value: state.timeToStart},
467
+ {path: 'navigation.racing.startTime', value: state.startTime}
461
468
  ]);
462
469
 
463
470
  setTimer();
464
- complete(callback, 200, 'set timer: OK');
465
- break;
471
+ return complete(callback, 200, 'set timer: OK');
466
472
  }
467
473
 
468
474
  case 'adjust': {
469
475
  const delta = Number(args.delta);
470
476
  if (isNaN(delta) || state.timeToStart == null || !state.startTime) {
471
- cb(null, { state: 'FAILED', message: 'Cannot adjust: invalid state or delta' });
477
+ cb(null, {state: 'FAILED', message: 'Cannot adjust: invalid state or delta'});
472
478
  return;
473
479
  }
474
480
 
475
- state.timeToStart += delta;
476
- if (state.timeToStart < 0) state.timeToStart = 0;
481
+ state.timeToStart = Math.max(state.timeToStart + delta, 0);
477
482
 
478
- let now = Date.now();
479
- const adjustedStart = new Date(now - (now % 1000) + state.timeToStart * 1000).toISOString();
480
- state.startTime = adjustedStart;
483
+ if (state.timerRunning) {
484
+ let now = Date.now();
485
+ state.startTime = new Date(now - (now % 1000) + state.timeToStart * 1000).toISOString();
486
+ }
481
487
 
482
- sendDeltas([
483
- { path: 'navigation.racing.timeToStart', value: state.timeToStart },
484
- { path: 'navigation.racing.startTime', value: adjustedStart }
485
- ]);
486
- complete(callback, 200, 'adjust timer: OK');
487
- break;
488
+ const deltas = [
489
+ { path: 'navigation.racing.timeToStart', value: state.timeToStart }
490
+ ];
491
+ if (state.timerRunning && state.startTime) {
492
+ deltas.push({path: 'navigation.racing.startTime', value: state.startTime });
493
+ }
494
+ sendDeltas(deltas);
495
+
496
+ return complete(callback, 200, 'adjust timer: OK');
488
497
  }
489
498
 
490
499
  default : {
491
- complete(callback, 400, 'Failed to command timer: unknown command');
492
- return;
500
+ return complete(callback, 400, 'Failed to command timer: unknown command');
493
501
  }
494
502
  }
495
503
  } catch (err) {
496
- complete(callback, 500, 'Failed to command timer: ' + JSON.stringify(err));
504
+ app.debug('startTimerCommand:', JSON.stringify(err));
505
+ return complete(callback, 500, 'Failed to command timer: ' + JSON.stringify(err));
497
506
  }
498
- return { state: 'PENDING' }
499
507
  }
500
508
 
501
509
  function findLineAndThenProcess(position, alwaysFindLine = false) {
@@ -613,8 +621,7 @@ module.exports = (app) => {
613
621
  sendDeltas([
614
622
  {path: 'navigation.racing.distanceStartline', value: distanceToLine},
615
623
  ]);
616
- }
617
- else if (state.distanceToLine) {
624
+ } else if (state.distanceToLine) {
618
625
  state.distanceToLine = null;
619
626
  sendDeltas([
620
627
  {path: 'navigation.racing.distanceStartline', value: null},
@@ -841,8 +848,8 @@ module.exports = (app) => {
841
848
  {
842
849
  context: 'vessels.' + app.selfId,
843
850
  subscribe: [
844
- { path: 'navigation.racing.startLinePort', policy: 'instant'},
845
- { path: 'navigation.racing.startLineStb', policy: 'instant'},
851
+ {path: 'navigation.racing.startLinePort', policy: 'instant'},
852
+ {path: 'navigation.racing.startLineStb', policy: 'instant'},
846
853
  ]
847
854
  },
848
855
  unsubscribes,
@@ -857,10 +864,14 @@ module.exports = (app) => {
857
864
  if (u.source && u.source.label && u.source.label !== 'signalk-racer') {
858
865
  switch (v.path) {
859
866
  case 'navigation.racing.startLinePort' :
860
- putStartLineEnd('port', v.value, ignored => {}, false).then(ignored =>{});
867
+ putStartLineEnd('port', v.value, ignored => {
868
+ }, false).then(ignored => {
869
+ });
861
870
  break;
862
871
  case 'navigation.racing.startLineStb' :
863
- putStartLineEnd('port', v.value, ignored => {}, false).then(ignored =>{});
872
+ putStartLineEnd('port', v.value, ignored => {
873
+ }, false).then(ignored => {
874
+ });
864
875
  break;
865
876
  }
866
877
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-racer",
3
- "version": "0.9.8",
3
+ "version": "0.9.10",
4
4
  "description": "Signalk plugin to calculate values of interest to sail racers: such as Distance to Line; Next leg TWA.",
5
5
  "main": "index.js",
6
6
  "scripts": {