engage-engine 1.216.90540001

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/engage.cpp ADDED
@@ -0,0 +1,1110 @@
1
+ //
2
+ // Copyright (c) 2019 Rally Tactical Systems, Inc.
3
+ // All rights reserved.
4
+ //
5
+
6
+ #include <nan.h>
7
+ #include <node_buffer.h>
8
+ #include <string>
9
+ #include <iostream>
10
+ #include <thread>
11
+ #include <map>
12
+ #include <mutex>
13
+ #include <atomic>
14
+
15
+ #include "EngageInterface.h"
16
+ #include "EngagePlatformNotifications.h"
17
+
18
+ using namespace std;
19
+ using namespace Nan;
20
+ using namespace v8;
21
+
22
+ #define ENGAGE_BINDING(_nm) \
23
+ Nan::Set(target, \
24
+ New<String>(#_nm).ToLocalChecked(), \
25
+ GetFunction(New<FunctionTemplate>(_nm)).ToLocalChecked());
26
+
27
+ #define STRVAL(_infoIndex) \
28
+ *Nan::Utf8String(info[_infoIndex]->ToString(Nan::GetCurrentContext()).FromMaybe(v8::Local<v8::String>()))
29
+
30
+ #define INTVAL(_index) \
31
+ info[_index]->Int32Value(Nan::GetCurrentContext()).FromJust()
32
+
33
+ #define ENGAGE_CB_NO_PARAMS(_ename) \
34
+ void on_ ## _ename(const char *eventExtraJson) \
35
+ { \
36
+ CrossThreadCallbackWorker *cbw = getCallback(#_ename); \
37
+ if(!cbw) \
38
+ { \
39
+ return; \
40
+ } \
41
+ cbw->enqueue(eventExtraJson); \
42
+ cbw->RELEASE_OBJECT_REFERENCE(); \
43
+ }
44
+
45
+ #define ENGAGE_CB_STR_PARAM(_ename) \
46
+ void on_ ## _ename(const char *str, const char *eventExtraJson) \
47
+ { \
48
+ CrossThreadCallbackWorker *cbw = getCallback(#_ename); \
49
+ if(!cbw) \
50
+ { \
51
+ return; \
52
+ } \
53
+ cbw->enqueue(str, eventExtraJson); \
54
+ cbw->RELEASE_OBJECT_REFERENCE(); \
55
+ }
56
+
57
+ #define ENGAGE_CB_ID_PARAM(_ename) \
58
+ void on_ ## _ename(const char *id, const char *eventExtraJson) \
59
+ { \
60
+ CrossThreadCallbackWorker *cbw = getCallback(#_ename); \
61
+ if(!cbw) \
62
+ { \
63
+ return; \
64
+ } \
65
+ cbw->enqueue(id, eventExtraJson); \
66
+ cbw->RELEASE_OBJECT_REFERENCE(); \
67
+ }
68
+
69
+ #define ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(_ename) \
70
+ void on_ ## _ename(const char *id, const char *s, const char *eventExtraJson) \
71
+ { \
72
+ CrossThreadCallbackWorker *cbw = getCallback(#_ename); \
73
+ if(!cbw) \
74
+ { \
75
+ return; \
76
+ } \
77
+ cbw->enqueue(id, s, eventExtraJson); \
78
+ cbw->RELEASE_OBJECT_REFERENCE(); \
79
+ }
80
+
81
+ #define ENGAGE_CB_TABLE_ENTRY(_pfn, _ename) \
82
+ g_eventCallbacks._pfn = on_ ## _ename;
83
+
84
+ // This little class wraps callbacks into JS from non-JS threads. It does so by
85
+ // posting the callback/event onto the libuv event loop from which the instance
86
+ // of this worker class was created. So ... be very careful only to create instances
87
+ // of this class from a thread that originated in libuv!!
88
+ class CrossThreadCallbackWorker
89
+ {
90
+ public:
91
+ class Parameter
92
+ {
93
+ public:
94
+ typedef enum {ptString, ptInt, ptStringVector} Type_t;
95
+
96
+ Type_t _type;
97
+ };
98
+
99
+ class StringParameter : public Parameter
100
+ {
101
+ public:
102
+ StringParameter(const char *s)
103
+ {
104
+ _type = ptString;
105
+ if(s != nullptr && s[0] != 0)
106
+ {
107
+ _val = s;
108
+ }
109
+ }
110
+
111
+ std::string _val;
112
+ };
113
+
114
+ class IntParameter : public Parameter
115
+ {
116
+ public:
117
+ IntParameter(int i)
118
+ {
119
+ _type = ptInt;
120
+ _val = i;
121
+ }
122
+
123
+ int _val;
124
+ };
125
+
126
+ class StringVectorParameter : public Parameter
127
+ {
128
+ public:
129
+ StringVectorParameter(const char *array[])
130
+ {
131
+ _type = ptStringVector;
132
+
133
+ // Our array is either null to begin with or terminated with a nullptr at the end
134
+ if(array != nullptr)
135
+ {
136
+ size_t index = 0;
137
+ while(array[index] != nullptr)
138
+ {
139
+ _val.push_back(array[index] != nullptr ? array[index] : "");
140
+ index++;
141
+ }
142
+ }
143
+ }
144
+
145
+ std::vector<std::string> _val;
146
+ };
147
+
148
+ explicit CrossThreadCallbackWorker(v8::Local<v8::Function> fn)
149
+ {
150
+ Nan::HandleScope scope;
151
+
152
+ // We're going to post this work on this libuv queue
153
+ _evLoop = GetCurrentEventLoop();
154
+
155
+ // Marshal the V8 function into a NAN callback structure
156
+ _cb.Reset(fn);
157
+
158
+ _workCtx.data = this;
159
+ _isBusy = false;
160
+ _refCount = 0;
161
+
162
+ // TODO: I'm not convinced we even need this as there's no object here to get
163
+ // garbage-collected. TBD !!
164
+ v8::Local<v8::Object> obj = New<v8::Object>();
165
+ _persistentHandle.Reset(obj);
166
+ _resource = new AsyncResource("CrossThreadCallbackWorker", obj);
167
+
168
+ ADD_OBJECT_REFERENCE();
169
+ }
170
+
171
+ virtual ~CrossThreadCallbackWorker()
172
+ {
173
+ Nan::HandleScope scope;
174
+
175
+ if (!_persistentHandle.IsEmpty())
176
+ {
177
+ _persistentHandle.Reset();
178
+ }
179
+
180
+ delete _resource;
181
+ }
182
+
183
+ void ADD_OBJECT_REFERENCE()
184
+ {
185
+ assert(_refCount >= 0);
186
+ _refCount++;
187
+ }
188
+
189
+ void RELEASE_OBJECT_REFERENCE()
190
+ {
191
+ assert(_refCount > 0);
192
+ if(_refCount.fetch_sub(1) == 1)
193
+ {
194
+ delete this;
195
+ }
196
+ }
197
+
198
+ void enqueue(const char *extra)
199
+ {
200
+ std::vector<Parameter*> *params = new std::vector<Parameter*>();
201
+ params->push_back(new StringParameter(extra));
202
+ enqueue(params);
203
+ }
204
+
205
+ void enqueue(const char *s, const char *extra)
206
+ {
207
+ std::vector<Parameter*> *params = new std::vector<Parameter*>();
208
+ params->push_back(new StringParameter(s));
209
+ params->push_back(new StringParameter(extra));
210
+ enqueue(params);
211
+ }
212
+
213
+ void enqueue(const char *s1, const char *s2, const char *extra)
214
+ {
215
+ std::vector<Parameter*> *params = new std::vector<Parameter*>();
216
+ params->push_back(new StringParameter(s1));
217
+ params->push_back(new StringParameter(s2));
218
+ params->push_back(new StringParameter(extra));
219
+ enqueue(params);
220
+ }
221
+
222
+ void enqueue(std::vector<Parameter*> *parameters)
223
+ {
224
+ // We need to wait here for the worker to become available (it may already be
225
+ // queued in JS). The chances of this happening are very slim as callbacks
226
+ // don't happen that often and, when they do, they'll return in just a few
227
+ // microseconds. But, let's be paranoid shall we!
228
+ _lock.lock();
229
+
230
+ while(_isBusy)
231
+ {
232
+ _lock.unlock();
233
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
234
+ _lock.lock();
235
+ }
236
+
237
+ // Pass over the pending parameters
238
+ _pendingParameters = parameters;
239
+
240
+ _isBusy = true;
241
+
242
+ _lock.unlock();
243
+
244
+ ADD_OBJECT_REFERENCE();
245
+ uv_queue_work(_evLoop,
246
+ &_workCtx,
247
+ CrossThreadCallbackWorker::onExecuteWork,
248
+ reinterpret_cast<uv_after_work_cb>(CrossThreadCallbackWorker::onWorkCompleted));
249
+ }
250
+
251
+ static void onExecuteWork(uv_work_t* workCtx)
252
+ {
253
+ // libuv wants us to execute something. But we don't have any actual work to do. So
254
+ // we'll just return.
255
+ }
256
+
257
+ static void onWorkCompleted(uv_work_t* workCtx)
258
+ {
259
+ // We'll just bounce this though to the instance's method
260
+ static_cast<CrossThreadCallbackWorker*>(workCtx->data)->internal_onWorkCompleted();
261
+ }
262
+
263
+ private:
264
+ NAN_DISALLOW_ASSIGN_COPY_MOVE(CrossThreadCallbackWorker)
265
+
266
+ void internal_onWorkCompleted()
267
+ {
268
+ _lock.lock();
269
+
270
+ // The real work here is to make the callback...
271
+ Nan::HandleScope scope;
272
+
273
+ v8::Isolate *isolate = v8::Isolate::GetCurrent();
274
+ v8::Local<Context> context = v8::Context::New(isolate);
275
+
276
+ // ... which we'll do here.
277
+ if(_pendingParameters == nullptr || _pendingParameters->size() == 0)
278
+ {
279
+ _cb.Call(0, nullptr, _resource);
280
+ }
281
+ else
282
+ {
283
+ // We can only call into V8 here to build up the parameters because only now are we
284
+ // on a thread that V8 owns.
285
+ v8::Local<v8::Value> *argv = new v8::Local<v8::Value>[_pendingParameters->size()];
286
+
287
+ int index = 0;
288
+ for(std::vector<Parameter*>::iterator itr = _pendingParameters->begin();
289
+ itr != _pendingParameters->end();
290
+ itr++)
291
+ {
292
+ if((*itr)->_type == Parameter::ptString)
293
+ {
294
+ argv[index] = Nan::New<v8::String>(((StringParameter*)(*itr))->_val).ToLocalChecked();
295
+ }
296
+ else if((*itr)->_type == Parameter::ptStringVector)
297
+ {
298
+ StringVectorParameter *svp = (StringVectorParameter*)(*itr);
299
+ v8::Local<v8::Array> jsArray = Nan::New<v8::Array>(svp->_val.size());
300
+
301
+ int speakerIndex = 0;
302
+ for(std::vector<std::string>::iterator itrSpeakers = svp->_val.begin();
303
+ itrSpeakers != svp->_val.end();
304
+ itrSpeakers++)
305
+ {
306
+ #ifndef WIN32
307
+ #pragma GCC diagnostic push
308
+ #pragma GCC diagnostic ignored "-Wunused-result"
309
+ #endif
310
+ {
311
+ jsArray->Set(context, speakerIndex, v8::String::NewFromUtf8(isolate, itrSpeakers->c_str(), NewStringType::kNormal).ToLocalChecked());
312
+ }
313
+ #ifndef WIN32
314
+ #pragma GCC diagnostic pop
315
+ #endif
316
+
317
+ speakerIndex++;
318
+ }
319
+
320
+ argv[index] = jsArray;
321
+ }
322
+ else if((*itr)->_type == Parameter::ptInt)
323
+ {
324
+ argv[index] = Nan::New<v8::Integer>(((IntParameter*)(*itr))->_val);
325
+ }
326
+
327
+ index++;
328
+ }
329
+
330
+ // Call into JS-land
331
+ _cb.Call(_pendingParameters->size(), argv, _resource);
332
+
333
+ // Get rid of any pending parameters
334
+ if(_pendingParameters != nullptr)
335
+ {
336
+ for(std::vector<Parameter*>::iterator itr = _pendingParameters->begin();
337
+ itr != _pendingParameters->end();
338
+ itr++)
339
+ {
340
+ delete *itr;
341
+ }
342
+
343
+ _pendingParameters->clear();
344
+ delete _pendingParameters;
345
+ _pendingParameters = nullptr;
346
+ }
347
+ }
348
+
349
+ // ... and only now can we say that this worker is no longer in use - i.e. its out
350
+ // out the libuv queue and won't cause a bus error in case it gets reused very
351
+ // quickly (that can happen if this code runs in onExecuteWork).
352
+ _isBusy = false;
353
+
354
+ _lock.unlock();
355
+
356
+ // Finally, let go of the reference we added when this was enqueue
357
+ RELEASE_OBJECT_REFERENCE();
358
+ }
359
+
360
+ std::mutex _lock;
361
+ uv_loop_t *_evLoop;
362
+ uv_work_t _workCtx;
363
+ bool _isBusy;
364
+ Nan::Callback _cb;
365
+ Nan::Persistent<v8::Object> _persistentHandle;
366
+ AsyncResource *_resource;
367
+ std::atomic<int> _refCount;
368
+ std::vector<Parameter*> *_pendingParameters;
369
+ };
370
+
371
+ typedef std::map<std::string, CrossThreadCallbackWorker*> CallbackMap_t;
372
+
373
+ static EngageEvents_t g_eventCallbacks;
374
+ static bool g_wantCallbacks = true;
375
+ static CallbackMap_t g_cbMap;
376
+ static std::mutex g_cbMapLock;
377
+ static std::string g_loggingHookFn;
378
+
379
+ //--------------------------------------------------------
380
+ // Returns the callback associated with an event name (if any)
381
+ static CrossThreadCallbackWorker *getCallback(const char *cbName)
382
+ {
383
+ if(!g_wantCallbacks)
384
+ {
385
+ return nullptr;
386
+ }
387
+
388
+ CrossThreadCallbackWorker *rc = nullptr;
389
+
390
+ g_cbMapLock.lock();
391
+
392
+ CallbackMap_t::iterator itr = g_cbMap.find(cbName);
393
+ if(itr != g_cbMap.end())
394
+ {
395
+ rc = itr->second;
396
+ rc->ADD_OBJECT_REFERENCE();
397
+ }
398
+
399
+ g_cbMapLock.unlock();
400
+
401
+ return rc;
402
+ }
403
+
404
+
405
+ // The following convey events from Engage up to JS. They all follow the same
406
+ // basic flow of finding a callback for the event name and, if one is found, calling
407
+ // it. Generally callbacks have no parameters, or a single string parameter. But,
408
+ // some of them are a little unique. So, for those that look the same, we'll use the
409
+ // ENGAGE_CB_xx_PARAMS macros. For the others, we'll actually write all the code for each.
410
+ ENGAGE_CB_NO_PARAMS(engineStarted)
411
+ ENGAGE_CB_NO_PARAMS(engineStopped)
412
+ ENGAGE_CB_NO_PARAMS(engineAudioDevicesRefreshed)
413
+
414
+ ENGAGE_CB_ID_PARAM(rpPausingConnectionAttempt)
415
+ ENGAGE_CB_ID_PARAM(rpConnecting)
416
+ ENGAGE_CB_ID_PARAM(rpConnected)
417
+ ENGAGE_CB_ID_PARAM(rpDisconnected)
418
+ void on_rpRoundtripReport(const char *id, uint32_t rtMs, uint32_t rtQualityRating, const char * eventExtraJson)
419
+ {
420
+ CrossThreadCallbackWorker *cbw = getCallback("rpRoundtripReport");
421
+ if(!cbw)
422
+ {
423
+ return;
424
+ }
425
+
426
+ std::vector<CrossThreadCallbackWorker::Parameter*> *params = new std::vector<CrossThreadCallbackWorker::Parameter*>();
427
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(id));
428
+ params->push_back(new CrossThreadCallbackWorker::IntParameter((int)rtMs));
429
+ params->push_back(new CrossThreadCallbackWorker::IntParameter((int)rtQualityRating));
430
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(eventExtraJson));
431
+ cbw->enqueue(params);
432
+
433
+ cbw->RELEASE_OBJECT_REFERENCE();
434
+ }
435
+
436
+ ENGAGE_CB_ID_PARAM(groupCreated)
437
+ ENGAGE_CB_ID_PARAM(groupCreateFailed)
438
+ ENGAGE_CB_ID_PARAM(groupDeleted)
439
+
440
+ ENGAGE_CB_ID_PARAM(groupConnected)
441
+ ENGAGE_CB_ID_PARAM(groupConnectFailed)
442
+ ENGAGE_CB_ID_PARAM(groupDisconnected)
443
+
444
+ ENGAGE_CB_ID_PARAM(groupJoined)
445
+ ENGAGE_CB_ID_PARAM(groupJoinFailed)
446
+ ENGAGE_CB_ID_PARAM(groupLeft)
447
+
448
+ ENGAGE_CB_ID_PARAM(groupRxStarted)
449
+ ENGAGE_CB_ID_PARAM(groupRxEnded)
450
+
451
+ ENGAGE_CB_ID_PARAM(groupRxMuted)
452
+ ENGAGE_CB_ID_PARAM(groupRxUnmuted)
453
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupRxSpeakersChanged)
454
+
455
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupNodeDiscovered)
456
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupNodeRediscovered)
457
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupNodeUndiscovered)
458
+
459
+ ENGAGE_CB_ID_PARAM(groupTxStarted);
460
+ ENGAGE_CB_ID_PARAM(groupTxEnded);
461
+ ENGAGE_CB_ID_PARAM(groupTxFailed);
462
+ ENGAGE_CB_ID_PARAM(groupTxUsurpedByPriority);
463
+ ENGAGE_CB_ID_PARAM(groupMaxTxTimeExceeded);
464
+
465
+ ENGAGE_CB_ID_PARAM(groupTxMuted)
466
+ ENGAGE_CB_ID_PARAM(groupTxUnmuted)
467
+
468
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupAssetDiscovered);
469
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupAssetRediscovered);
470
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupAssetUndiscovered);
471
+
472
+ ENGAGE_CB_NO_PARAMS(licenseChanged)
473
+ ENGAGE_CB_NO_PARAMS(licenseExpired)
474
+ ENGAGE_CB_STR_PARAM(licenseExpiring)
475
+
476
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupTimelineEventStarted);
477
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupTimelineEventUpdated);
478
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupTimelineEventEnded);
479
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupTimelineReport);
480
+ ENGAGE_CB_ID_PARAM(groupTimelineReportFailed);
481
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupTimelineGroomed);
482
+
483
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupHealthReport);
484
+ ENGAGE_CB_ID_PARAM(groupHealthReportFailed);
485
+
486
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupStatsReport);
487
+ ENGAGE_CB_ID_PARAM(groupStatsReportFailed);
488
+
489
+ void on_groupRxVolumeChanged(const char *id, int16_t leftLevelPerc, int16_t rightLevelPerc, const char * eventExtraJson)
490
+ {
491
+ CrossThreadCallbackWorker *cbw = getCallback("groupRxVolumeChanged");
492
+ if(!cbw)
493
+ {
494
+ return;
495
+ }
496
+
497
+ std::vector<CrossThreadCallbackWorker::Parameter*> *params = new std::vector<CrossThreadCallbackWorker::Parameter*>();
498
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(id));
499
+ params->push_back(new CrossThreadCallbackWorker::IntParameter((int)leftLevelPerc));
500
+ params->push_back(new CrossThreadCallbackWorker::IntParameter((int)rightLevelPerc));
501
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(eventExtraJson));
502
+ cbw->enqueue(params);
503
+
504
+ cbw->RELEASE_OBJECT_REFERENCE();
505
+ }
506
+
507
+ ENGAGE_CB_ID_PLUS_ONE_STRING_PARAM(groupRxDtmf)
508
+
509
+
510
+ //--------------------------------------------------------
511
+ // Registers an event name and the JS callback function
512
+ NAN_METHOD(on)
513
+ {
514
+ bool haveAFunction = false;
515
+ std::string functionName = STRVAL(0);
516
+
517
+ if(info.Length() >= 2 && (!info[1]->IsUndefined()) && (!info[1]->IsNull()))
518
+ {
519
+ haveAFunction = true;
520
+ }
521
+
522
+ g_cbMapLock.lock();
523
+
524
+ // Find our map entry
525
+ CallbackMap_t::iterator itr = g_cbMap.find(functionName.c_str());
526
+
527
+ // We don't yet have an entry in the map
528
+ if(itr == g_cbMap.end())
529
+ {
530
+ // If we don't have a function, then we're done
531
+ if(!haveAFunction)
532
+ {
533
+ g_cbMapLock.unlock();
534
+ return;
535
+ }
536
+
537
+ // We have a function but not yet an entry, make it and plug it into the map
538
+ g_cbMap[functionName.c_str()] = new CrossThreadCallbackWorker(Nan::To<v8::Function>(info[1]).ToLocalChecked());
539
+ }
540
+ else
541
+ {
542
+ // We have an entry ...
543
+
544
+ // ... get rid of it (we're either removing or replacing)
545
+ itr->second->RELEASE_OBJECT_REFERENCE();
546
+ g_cbMap.erase(itr);
547
+
548
+ // ... and maybe put in the new one
549
+ if(haveAFunction)
550
+ {
551
+ g_cbMap[functionName.c_str()] = new CrossThreadCallbackWorker(Nan::To<v8::Function>(info[1]).ToLocalChecked());
552
+ }
553
+ }
554
+
555
+ g_cbMapLock.unlock();
556
+ }
557
+
558
+
559
+ //--------------------------------------------------------
560
+ NAN_METHOD(initialize)
561
+ {
562
+ #if defined(WIN32)
563
+ engageWin32LibraryInit();
564
+ #endif
565
+
566
+ memset(&g_eventCallbacks, 0, sizeof(g_eventCallbacks));
567
+
568
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_ENGINE_STARTED, engineStarted);
569
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_ENGINE_STOPPED, engineStopped);
570
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_ENGINE_AUDIO_DEVICES_REFRESHED, engineAudioDevicesRefreshed);
571
+
572
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_RP_PAUSING_CONNECTION_ATTEMPT, rpPausingConnectionAttempt);
573
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_RP_CONNECTING, rpConnecting);
574
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_RP_CONNECTED, rpConnected);
575
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_RP_DISCONNECTED, rpDisconnected);
576
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_RP_ROUNDTRIP_REPORT, rpRoundtripReport);
577
+
578
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_CREATED, groupCreated);
579
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_CREATE_FAILED, groupCreateFailed);
580
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_DELETED, groupDeleted);
581
+
582
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_CONNECTED, groupConnected);
583
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_CONNECT_FAILED, groupConnectFailed);
584
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_DISCONNECTED, groupDisconnected);
585
+
586
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_JOINED, groupJoined);
587
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_JOIN_FAILED, groupJoinFailed);
588
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_LEFT, groupLeft);
589
+ //ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_MEMBER_COUNT_CHANGED)(const char *pId, size_t newCount);
590
+
591
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_NODE_DISCOVERED, groupNodeDiscovered);
592
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_NODE_REDISCOVERED, groupNodeRediscovered);
593
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_NODE_UNDISCOVERED, groupNodeUndiscovered);
594
+
595
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_STARTED, groupRxStarted);
596
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_ENDED, groupRxEnded);
597
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_SPEAKERS_CHANGED, groupRxSpeakersChanged);
598
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_MUTED, groupRxMuted);
599
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_UNMUTED, groupRxUnmuted);
600
+
601
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_STARTED, groupTxStarted);
602
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_ENDED, groupTxEnded);
603
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_FAILED, groupTxFailed);
604
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_USURPED_BY_PRIORITY, groupTxUsurpedByPriority);
605
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_MAX_TX_TIME_EXCEEDED, groupMaxTxTimeExceeded);
606
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_MUTED, groupTxMuted);
607
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TX_UNMUTED, groupTxUnmuted);
608
+
609
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_ASSET_DISCOVERED, groupAssetDiscovered);
610
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_ASSET_REDISCOVERED, groupAssetRediscovered);
611
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_ASSET_UNDISCOVERED, groupAssetUndiscovered);
612
+
613
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_LICENSE_CHANGED, licenseChanged);
614
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_LICENSE_EXPIRED, licenseExpired);
615
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_LICENSE_EXPIRING, licenseExpiring);
616
+
617
+ // TODO PFN_ENGAGE_GROUP_BLOB_SENT
618
+ // TODO PFN_ENGAGE_GROUP_BLOB_SEND_FAILED
619
+ // TODO PFN_ENGAGE_GROUP_BLOB_RECEIVED
620
+
621
+ // TODO PFN_ENGAGE_GROUP_RTP_SENT
622
+ // TODO PFN_ENGAGE_GROUP_RTP_SEND_FAILED
623
+ // TODO PFN_ENGAGE_GROUP_RTP_RECEIVED
624
+
625
+ // TODO PFN_ENGAGE_GROUP_RAW_SENT
626
+ // TODO PFN_ENGAGE_GROUP_RAW_SEND_FAILED
627
+ // TODO PFN_ENGAGE_GROUP_RAW_RECEIVED
628
+
629
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_EVENT_STARTED, groupTimelineEventStarted);
630
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_EVENT_UPDATED, groupTimelineEventUpdated);
631
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_EVENT_ENDED, groupTimelineEventEnded);
632
+
633
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_REPORT, groupTimelineReport);
634
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_REPORT_FAILED, groupTimelineReportFailed);
635
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_TIMELINE_GROOMED, groupTimelineGroomed);
636
+
637
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_HEALTH_REPORT, groupHealthReport);
638
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_HEALTH_REPORT_FAILED, groupHealthReportFailed);
639
+
640
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_STATS_REPORT, groupStatsReport);
641
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_STATS_REPORT_FAILED, groupStatsReportFailed);
642
+
643
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_VOLUME_CHANGED, groupRxVolumeChanged);
644
+ ENGAGE_CB_TABLE_ENTRY(PFN_ENGAGE_GROUP_RX_DTMF, groupRxDtmf);
645
+
646
+ engageRegisterEventCallbacks(&g_eventCallbacks);
647
+
648
+ engageInitialize(STRVAL(0), STRVAL(1), STRVAL(2));
649
+ }
650
+
651
+ //--------------------------------------------------------
652
+ NAN_METHOD(enableCallbacks)
653
+ {
654
+ g_wantCallbacks = true;
655
+ }
656
+
657
+ //--------------------------------------------------------
658
+ NAN_METHOD(disableCallbacks)
659
+ {
660
+ g_wantCallbacks = false;
661
+ }
662
+
663
+ //--------------------------------------------------------
664
+ NAN_METHOD(setLogLevel)
665
+ {
666
+ engageSetLogLevel(INTVAL(0));
667
+ }
668
+
669
+ //--------------------------------------------------------
670
+ NAN_METHOD(setLogTagExtension)
671
+ {
672
+ engageSetLogTagExtension(STRVAL(0));
673
+ }
674
+
675
+ //--------------------------------------------------------
676
+ NAN_METHOD(engageEnableSyslog)
677
+ {
678
+ engageEnableSyslog(INTVAL(0) == 1 ? ENGAGE_SYSLOG_ENABLE : ENGAGE_SYSLOG_DISABLE);
679
+ }
680
+
681
+ //--------------------------------------------------------
682
+ NAN_METHOD(engageEnableWatchdog)
683
+ {
684
+ engageEnableWatchdog(INTVAL(0) == 1 ? ENGAGE_WATCHDOG_ENABLE : ENGAGE_WATCHDOG_DISABLE);
685
+ }
686
+
687
+ //--------------------------------------------------------
688
+ NAN_METHOD(shutdown)
689
+ {
690
+ engageShutdown();
691
+
692
+ #if defined(WIN32)
693
+ engageWin32LibraryDeinit();
694
+ #endif
695
+ }
696
+
697
+ //--------------------------------------------------------
698
+ NAN_METHOD(start)
699
+ {
700
+ engageStart();
701
+ }
702
+
703
+ //--------------------------------------------------------
704
+ NAN_METHOD(stop)
705
+ {
706
+ engageStop();
707
+ }
708
+
709
+ //--------------------------------------------------------
710
+ NAN_METHOD(createGroup)
711
+ {
712
+ engageCreateGroup(STRVAL(0));
713
+ }
714
+
715
+ //--------------------------------------------------------
716
+ NAN_METHOD(deleteGroup)
717
+ {
718
+ engageDeleteGroup(STRVAL(0));
719
+ }
720
+
721
+ //--------------------------------------------------------
722
+ NAN_METHOD(joinGroup)
723
+ {
724
+ engageJoinGroup(STRVAL(0));
725
+ }
726
+
727
+ //--------------------------------------------------------
728
+ NAN_METHOD(leaveGroup)
729
+ {
730
+ engageLeaveGroup(STRVAL(0));
731
+ }
732
+
733
+ //--------------------------------------------------------
734
+ NAN_METHOD(beginGroupTx)
735
+ {
736
+ engageBeginGroupTx(STRVAL(0), INTVAL(1), INTVAL(2));
737
+ }
738
+
739
+ //--------------------------------------------------------
740
+ NAN_METHOD(beginGroupTxAdvanced)
741
+ {
742
+ engageBeginGroupTxAdvanced(STRVAL(0), STRVAL(1));
743
+ }
744
+
745
+ //--------------------------------------------------------
746
+ NAN_METHOD(endGroupTx)
747
+ {
748
+ engageEndGroupTx(STRVAL(0));
749
+ }
750
+
751
+ //--------------------------------------------------------
752
+ NAN_METHOD(setGroupRxTag)
753
+ {
754
+ engageSetGroupRxTag(STRVAL(0), INTVAL(1));
755
+ }
756
+
757
+ //--------------------------------------------------------
758
+ NAN_METHOD(muteGroupRx)
759
+ {
760
+ engageMuteGroupRx(STRVAL(0));
761
+ }
762
+
763
+ //--------------------------------------------------------
764
+ NAN_METHOD(unmuteGroupRx)
765
+ {
766
+ engageUnmuteGroupRx(STRVAL(0));
767
+ }
768
+
769
+ //--------------------------------------------------------
770
+ NAN_METHOD(muteGroupTx)
771
+ {
772
+ engageMuteGroupTx(STRVAL(0));
773
+ }
774
+
775
+ //--------------------------------------------------------
776
+ NAN_METHOD(unmuteGroupTx)
777
+ {
778
+ engageUnmuteGroupTx(STRVAL(0));
779
+ }
780
+
781
+ //--------------------------------------------------------
782
+ NAN_METHOD(setGroupRxVolume)
783
+ {
784
+ engageSetGroupRxVolume(STRVAL(0), INTVAL(1), INTVAL(2));
785
+ }
786
+
787
+ //--------------------------------------------------------
788
+ NAN_METHOD(updatePresenceDescriptor)
789
+ {
790
+ engageUpdatePresenceDescriptor(STRVAL(0), STRVAL(1), INTVAL(0));
791
+ }
792
+
793
+ //--------------------------------------------------------
794
+ NAN_METHOD(encrypt)
795
+ {
796
+ uint8_t* inputBytes = (uint8_t*) node::Buffer::Data(info[0]);
797
+
798
+ size_t inputOfs = INTVAL(1);
799
+ size_t inputLen = INTVAL(2);
800
+
801
+ // Our output is going to contain encrypted data padded to 16 bytes + another 16 bytes of IV
802
+ uint8_t *outputBytes = new uint8_t[inputLen + 16 * 2];
803
+
804
+ int bytesInOutput = engageEncrypt(inputBytes + inputOfs, inputLen, outputBytes, STRVAL(3));
805
+
806
+ if(bytesInOutput > 0)
807
+ {
808
+ info.GetReturnValue().Set(Nan::CopyBuffer((char*)outputBytes, bytesInOutput).ToLocalChecked());
809
+ }
810
+
811
+ delete[] outputBytes;
812
+ }
813
+
814
+ //--------------------------------------------------------
815
+ NAN_METHOD(decrypt)
816
+ {
817
+ uint8_t* inputBytes = (uint8_t*) node::Buffer::Data(info[0]);
818
+
819
+ size_t inputOfs = INTVAL(1);
820
+ size_t inputLen = INTVAL(2);
821
+
822
+ // Our output is not going to be larger than the input (if anything, it'll be smaller)
823
+ uint8_t *outputBytes = new uint8_t[inputLen];
824
+
825
+ int bytesInOutput = engageDecrypt(inputBytes + inputOfs, inputLen, outputBytes, STRVAL(3));
826
+
827
+ if(bytesInOutput > 0)
828
+ {
829
+ info.GetReturnValue().Set(Nan::CopyBuffer((char*)outputBytes, bytesInOutput).ToLocalChecked());
830
+ }
831
+
832
+ delete[] outputBytes;
833
+ }
834
+
835
+ //--------------------------------------------------------
836
+ NAN_METHOD(getVersion)
837
+ {
838
+ const char *rc = engageGetVersion();
839
+
840
+ if(rc == nullptr)
841
+ {
842
+ rc = "";
843
+ }
844
+
845
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
846
+ }
847
+
848
+ //--------------------------------------------------------
849
+ NAN_METHOD(getHardwareReport)
850
+ {
851
+ const char *rc = engageGetHardwareReport();
852
+
853
+ if(rc == nullptr)
854
+ {
855
+ rc = "";
856
+ }
857
+
858
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
859
+ }
860
+
861
+ //--------------------------------------------------------
862
+ NAN_METHOD(getActiveLicenseDescriptor)
863
+ {
864
+ const char *rc = engageGetActiveLicenseDescriptor();
865
+
866
+ if(rc == nullptr)
867
+ {
868
+ rc = "";
869
+ }
870
+
871
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
872
+ }
873
+
874
+ //--------------------------------------------------------
875
+ NAN_METHOD(getLicenseDescriptor)
876
+ {
877
+ const char *rc = engageGetLicenseDescriptor(STRVAL(0), STRVAL(1), STRVAL(2), STRVAL(3));
878
+
879
+ if(rc == nullptr)
880
+ {
881
+ rc = "";
882
+ }
883
+
884
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
885
+ }
886
+
887
+ //--------------------------------------------------------
888
+ NAN_METHOD(updateLicense)
889
+ {
890
+ engageUpdateLicense(STRVAL(0), STRVAL(1), STRVAL(2), STRVAL(3));
891
+ }
892
+
893
+ // TODO: engageSendGroupRtp
894
+ // TODO: engageRegisterGroupRtpHandler
895
+ // TODO: engageUnregisterGroupRtpHandler
896
+ // TODO: engageSendGroupBlob
897
+ // TODO: engageSendGroupRaw
898
+ // TODO: engagePlatformServiceDiscovered
899
+ // TODO: engagePlatformServiceRediscovered
900
+ // TODO: engagePlatformServiceUndiscovered
901
+
902
+ //--------------------------------------------------------
903
+ NAN_METHOD(queryGroupTimeline)
904
+ {
905
+ engageQueryGroupTimeline(STRVAL(0), STRVAL(1));
906
+ }
907
+
908
+ //--------------------------------------------------------
909
+ NAN_METHOD(queryGroupHealth)
910
+ {
911
+ engageQueryGroupHealth(STRVAL(0));
912
+ }
913
+
914
+ //--------------------------------------------------------
915
+ NAN_METHOD(queryGroupStats)
916
+ {
917
+ engageQueryGroupStats(STRVAL(0));
918
+ }
919
+
920
+ //--------------------------------------------------------
921
+ NAN_METHOD(getNetworkInterfaceDevices)
922
+ {
923
+ const char *rc = engageGetNetworkInterfaceDevices();
924
+
925
+ if(rc == nullptr)
926
+ {
927
+ rc = "";
928
+ }
929
+
930
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
931
+ }
932
+
933
+ //--------------------------------------------------------
934
+ NAN_METHOD(getAudioDevices)
935
+ {
936
+ const char *rc = engageGetAudioDevices();
937
+
938
+ if(rc == nullptr)
939
+ {
940
+ rc = "";
941
+ }
942
+
943
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
944
+ }
945
+
946
+ //--------------------------------------------------------
947
+ NAN_METHOD(generateMission)
948
+ {
949
+ const char *rc = engageGenerateMission(STRVAL(0), INTVAL(1), STRVAL(2), STRVAL(3));
950
+
951
+ if(rc == nullptr)
952
+ {
953
+ rc = "";
954
+ }
955
+
956
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
957
+ }
958
+
959
+ //--------------------------------------------------------
960
+ NAN_METHOD(generateMissionUsingCertStore)
961
+ {
962
+ const char *rc = engageGenerateMissionUsingCertStore(STRVAL(0), INTVAL(1), STRVAL(2), STRVAL(3), STRVAL(4), STRVAL(5), STRVAL(6));
963
+
964
+ if(rc == nullptr)
965
+ {
966
+ rc = "";
967
+ }
968
+
969
+ info.GetReturnValue().Set(New(rc).ToLocalChecked());
970
+ }
971
+
972
+ //--------------------------------------------------------
973
+ NAN_METHOD(setMissionId)
974
+ {
975
+ engageSetMissionId(STRVAL(0));
976
+ }
977
+
978
+ //--------------------------------------------------------
979
+ NAN_METHOD(openCertStore)
980
+ {
981
+ engageOpenCertStore(STRVAL(0), STRVAL(1));
982
+ }
983
+
984
+ //--------------------------------------------------------
985
+ NAN_METHOD(closeCertStore)
986
+ {
987
+ engageCloseCertStore();
988
+ }
989
+
990
+ // TODO: engageSetCertStoreCertificatePem
991
+ // TODO: engageSetCertStoreCertificatePem
992
+ // TODO: engageSetCertStoreCertificateP12
993
+ // TODO: engageDeleteCertStoreCertificate
994
+ // TODO: engageGetCertStoreCertificatePem
995
+ // TODO: engageGetCertificateDescriptorFromPem
996
+ // TODO: engageImportCertStoreElementFromCertStore
997
+
998
+ //--------------------------------------------------------
999
+ NAN_METHOD(logMsg)
1000
+ {
1001
+ engageLogMsg(INTVAL(0), STRVAL(1), STRVAL(2));
1002
+ }
1003
+
1004
+ //--------------------------------------------------------
1005
+ NAN_METHOD(platformNotifyChanges)
1006
+ {
1007
+ engagePlatformNotifyChanges(STRVAL(0));
1008
+ }
1009
+
1010
+ //--------------------------------------------------------
1011
+ static void internalEngageLoggingHook(int level, const char *tag, const char *message)
1012
+ {
1013
+ if(g_loggingHookFn.empty())
1014
+ {
1015
+ return;
1016
+ }
1017
+
1018
+ CrossThreadCallbackWorker *cbw = getCallback(g_loggingHookFn.c_str());
1019
+ if(!cbw)
1020
+ {
1021
+ return;
1022
+ }
1023
+
1024
+ std::vector<CrossThreadCallbackWorker::Parameter*> *params = new std::vector<CrossThreadCallbackWorker::Parameter*>();
1025
+ params->push_back(new CrossThreadCallbackWorker::IntParameter(level));
1026
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(tag));
1027
+ params->push_back(new CrossThreadCallbackWorker::StringParameter(message));
1028
+ cbw->enqueue(params);
1029
+
1030
+ cbw->RELEASE_OBJECT_REFERENCE();
1031
+ }
1032
+
1033
+ NAN_METHOD(hookEngineLogging)
1034
+ {
1035
+ g_loggingHookFn = STRVAL(0);
1036
+
1037
+ if(!g_loggingHookFn.empty())
1038
+ {
1039
+ engageSetLoggingOutputOverride(internalEngageLoggingHook);
1040
+ }
1041
+ else
1042
+ {
1043
+ engageSetLoggingOutputOverride(nullptr);
1044
+ }
1045
+ }
1046
+
1047
+ //--------------------------------------------------------
1048
+ NAN_MODULE_INIT(Init)
1049
+ {
1050
+ ENGAGE_BINDING(on);
1051
+
1052
+ ENGAGE_BINDING(setLogLevel);
1053
+
1054
+ ENGAGE_BINDING(enableCallbacks);
1055
+ ENGAGE_BINDING(disableCallbacks);
1056
+
1057
+ ENGAGE_BINDING(initialize);
1058
+ ENGAGE_BINDING(shutdown);
1059
+
1060
+ ENGAGE_BINDING(start);
1061
+ ENGAGE_BINDING(stop);
1062
+
1063
+ ENGAGE_BINDING(createGroup);
1064
+ ENGAGE_BINDING(deleteGroup);
1065
+ ENGAGE_BINDING(joinGroup);
1066
+ ENGAGE_BINDING(leaveGroup);
1067
+
1068
+ ENGAGE_BINDING(beginGroupTx);
1069
+ ENGAGE_BINDING(beginGroupTxAdvanced);
1070
+ ENGAGE_BINDING(endGroupTx);
1071
+ ENGAGE_BINDING(setGroupRxTag);
1072
+
1073
+ ENGAGE_BINDING(muteGroupRx);
1074
+ ENGAGE_BINDING(unmuteGroupRx);
1075
+
1076
+ ENGAGE_BINDING(muteGroupTx);
1077
+ ENGAGE_BINDING(unmuteGroupTx);
1078
+
1079
+ ENGAGE_BINDING(setGroupRxVolume);
1080
+
1081
+ ENGAGE_BINDING(queryGroupTimeline);
1082
+
1083
+ ENGAGE_BINDING(updatePresenceDescriptor);
1084
+
1085
+ ENGAGE_BINDING(encrypt);
1086
+ ENGAGE_BINDING(decrypt);
1087
+
1088
+ ENGAGE_BINDING(updateLicense);
1089
+ ENGAGE_BINDING(getVersion);
1090
+ ENGAGE_BINDING(getHardwareReport);
1091
+ ENGAGE_BINDING(getNetworkInterfaceDevices);
1092
+ ENGAGE_BINDING(getAudioDevices);
1093
+
1094
+ ENGAGE_BINDING(getActiveLicenseDescriptor);
1095
+ ENGAGE_BINDING(getLicenseDescriptor);
1096
+
1097
+ ENGAGE_BINDING(openCertStore);
1098
+ ENGAGE_BINDING(closeCertStore);
1099
+
1100
+ ENGAGE_BINDING(generateMission);
1101
+ ENGAGE_BINDING(generateMissionUsingCertStore);
1102
+
1103
+ ENGAGE_BINDING(setMissionId);
1104
+ ENGAGE_BINDING(platformNotifyChanges);
1105
+
1106
+ ENGAGE_BINDING(logMsg);
1107
+ ENGAGE_BINDING(hookEngineLogging);
1108
+ }
1109
+
1110
+ NODE_MODULE(engage, Init)