11
11
const electron = require ( 'electron' ) ;
12
12
const { clipboard, Menu, dialog, getCurrentWindow} = require ( '@electron/remote' ) ;
13
13
14
+ const util = require ( 'util' ) ;
14
15
const strftime = require ( 'strftime' ) . utc ( ) ;
15
16
const audiomoth = require ( 'audiomoth-hid' ) ;
16
17
17
18
const versionChecker = require ( './versionChecker.js' ) ;
18
19
const nightMode = require ( './nightMode.js' ) ;
20
+ require ( 'worker_threads' ) ;
19
21
20
22
/* UI components */
21
23
@@ -35,9 +37,14 @@ const setTimeButton = document.getElementById('set-time-button');
35
37
36
38
const MILLISECONDS_IN_SECOND = 1000 ;
37
39
40
+ /* Whether or not communication with device is currently happening */
41
+
38
42
let communicating = false ;
39
43
40
- let currentTime , deviceId , firmwareVersion , firmwareDescription ;
44
+ /* Communication constants */
45
+
46
+ const MAXIMUM_RETRIES = 10 ;
47
+ const DEFAULT_RETRY_INTERVAL = 100 ;
41
48
42
49
/* Time display functions */
43
50
@@ -73,11 +80,7 @@ function disableDisplay () {
73
80
74
81
function enableDisplayAndShowTime ( date ) {
75
82
76
- if ( communicating ) {
77
-
78
- return ;
79
-
80
- }
83
+ if ( communicating ) return ;
81
84
82
85
const strftimeUTC = strftime . timezone ( 0 ) ;
83
86
@@ -87,8 +90,6 @@ function enableDisplayAndShowTime (date) {
87
90
88
91
setTimeButton . disabled = false ;
89
92
90
- applicationMenu . getMenuItemById ( 'copyid' ) . enabled = true ;
91
-
92
93
}
93
94
94
95
/* Device information display functions */
@@ -111,6 +112,8 @@ function enableDisplayAndShowID (id) {
111
112
112
113
idLabel . classList . remove ( 'grey' ) ;
113
114
115
+ applicationMenu . getMenuItemById ( 'copyid' ) . enabled = true ;
116
+
114
117
}
115
118
116
119
function enableDisplayAndShowVersionNumber ( version ) {
@@ -133,179 +136,127 @@ function enableDisplayAndShowVersionDescription (description) {
133
136
134
137
}
135
138
136
- /* Error response */
139
+ /* Utility functions */
137
140
138
- function errorOccurred ( err ) {
141
+ async function callWithRetry ( funcSync , argument , milliseconds , repeats ) {
139
142
140
- console . error ( err ) ;
143
+ let result ;
141
144
142
- disableDisplay ( ) ;
145
+ let attempt = 0 ;
143
146
144
- }
147
+ while ( attempt < repeats ) {
145
148
146
- /* Device interaction functions */
149
+ try {
147
150
148
- function requestFirmwareDescription ( ) {
151
+ if ( argument ) {
149
152
150
- audiomoth . getFirmwareDescription ( function ( err , description ) {
153
+ result = await funcSync ( argument ) ;
151
154
152
- if ( communicating ) return ;
155
+ } else {
153
156
154
- if ( err ) {
157
+ result = await funcSync ( ) ;
155
158
156
- errorOccurred ( err ) ;
159
+ }
157
160
158
- } else if ( description === null ) {
161
+ break ;
159
162
160
- disableDisplay ( ) ;
163
+ } catch ( e ) {
161
164
162
- } else {
165
+ const interval = milliseconds / 2 + milliseconds / 2 * Math . random ( ) ;
163
166
164
- firmwareDescription = description ;
167
+ await delay ( interval ) ;
165
168
166
- requestFirmwareVersion ( ) ;
169
+ attempt += 1 ;
167
170
168
171
}
169
172
170
- } ) ;
171
-
172
- }
173
-
174
- function requestFirmwareVersion ( ) {
175
-
176
- audiomoth . getFirmwareVersion ( function ( err , versionArr ) {
177
-
178
- if ( communicating ) return ;
179
-
180
- if ( err ) {
181
-
182
- errorOccurred ( err ) ;
183
-
184
- } else if ( versionArr === null ) {
185
-
186
- disableDisplay ( ) ;
187
-
188
- } else {
189
-
190
- firmwareVersion = versionArr [ 0 ] + '.' + versionArr [ 1 ] + '.' + versionArr [ 2 ] ;
173
+ }
191
174
192
- requestBatteryState ( ) ;
175
+ if ( result === undefined ) throw ( 'Error: Repeated attempts to access the device failed.' ) ;
193
176
194
- }
177
+ if ( result === null ) throw ( 'No device detected' ) ;
195
178
196
- } ) ;
179
+ return result ;
197
180
198
181
}
199
182
200
- function requestBatteryState ( ) {
201
-
202
- audiomoth . getBatteryState ( function ( err , batteryState ) {
203
-
204
- if ( communicating ) return ;
205
-
206
- if ( err ) {
207
-
208
- errorOccurred ( err ) ;
183
+ async function delay ( milliseconds ) {
209
184
210
- } else if ( batteryState === null ) {
211
-
212
- disableDisplay ( ) ;
213
-
214
- } else {
215
-
216
- enableDisplayAndShowTime ( currentTime ) ;
217
- enableDisplayAndShowID ( deviceId ) ;
218
- enableDisplayAndShowVersionDescription ( firmwareDescription ) ;
219
- enableDisplayAndShowVersionNumber ( firmwareVersion ) ;
220
- enableDisplayAndShowBatteryState ( batteryState ) ;
221
-
222
- }
223
-
224
- } ) ;
185
+ return new Promise ( resolve => setTimeout ( resolve , milliseconds ) ) ;
225
186
226
187
}
227
188
228
- function requestID ( ) {
229
-
230
- audiomoth . getID ( function ( err , id ) {
231
-
232
- if ( communicating ) return ;
233
-
234
- if ( err ) {
189
+ /* Promisified versions of AudioMoth-HID calls */
235
190
236
- errorOccurred ( err ) ;
191
+ const getFirmwareDescription = util . promisify ( audiomoth . getFirmwareDescription ) ;
237
192
238
- } else if ( id === null ) {
193
+ const getFirmwareVersion = util . promisify ( audiomoth . getFirmwareVersion ) ;
239
194
240
- disableDisplay ( ) ;
195
+ const getBatteryState = util . promisify ( audiomoth . getBatteryState ) ;
241
196
242
- } else {
197
+ const getID = util . promisify ( audiomoth . getID ) ;
243
198
244
- deviceId = id ;
199
+ const getTime = util . promisify ( audiomoth . getTime ) ;
245
200
246
- requestFirmwareDescription ( ) ;
201
+ const setTime = util . promisify ( audiomoth . setTime ) ;
247
202
248
- }
203
+ /* Device interaction functions */
249
204
250
- } ) ;
205
+ async function requestAudioMothTime ( ) {
251
206
252
- }
207
+ try {
253
208
254
- function requestTime ( ) {
209
+ /* Read from AudioMoth */
255
210
256
- if ( communicating ) return ;
211
+ const date = await callWithRetry ( getTime , null , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
257
212
258
- audiomoth . getTime ( function ( err , date ) {
213
+ const id = await callWithRetry ( getID , null , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
259
214
260
- if ( communicating ) return ;
215
+ const description = await callWithRetry ( getFirmwareDescription , null , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
261
216
262
- if ( err ) {
217
+ const versionArr = await callWithRetry ( getFirmwareVersion , null , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
263
218
264
- errorOccurred ( err ) ;
219
+ const batteryState = await callWithRetry ( getBatteryState , null , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
265
220
266
- } else if ( date === null ) {
221
+ /* No exceptions have occurred so update display */
267
222
268
- disableDisplay ( ) ;
223
+ const firmwareVersion = versionArr [ 0 ] + '.' + versionArr [ 1 ] + '.' + versionArr [ 2 ] ;
269
224
270
- } else {
225
+ enableDisplayAndShowTime ( date ) ;
226
+ enableDisplayAndShowID ( id ) ;
227
+ enableDisplayAndShowVersionDescription ( description ) ;
228
+ enableDisplayAndShowVersionNumber ( firmwareVersion ) ;
229
+ enableDisplayAndShowBatteryState ( batteryState ) ;
271
230
272
- currentTime = date ;
231
+ } catch ( e ) {
273
232
274
- requestID ( ) ;
233
+ /* Problem reading from AudioMoth or no AudioMoth */
275
234
276
- }
235
+ disableDisplay ( ) ;
277
236
278
- } ) ;
237
+ }
279
238
280
239
const milliseconds = Date . now ( ) % MILLISECONDS_IN_SECOND ;
281
240
282
241
let delay = MILLISECONDS_IN_SECOND / 2 - milliseconds ;
283
242
284
243
if ( delay < 0 ) delay += MILLISECONDS_IN_SECOND ;
285
244
286
- setTimeout ( requestTime , delay ) ;
245
+ setTimeout ( requestAudioMothTime , delay ) ;
287
246
288
247
}
289
248
290
- function setTime ( time ) {
291
-
292
- audiomoth . setTime ( time , function ( err , date ) {
293
-
294
- if ( err ) {
249
+ async function setAudioMothTime ( time ) {
295
250
296
- errorOccurred ( err ) ;
251
+ try {
297
252
298
- } else if ( date === null ) {
253
+ await callWithRetry ( setTime , time , DEFAULT_RETRY_INTERVAL , MAXIMUM_RETRIES ) ;
299
254
300
- disableDisplay ( ) ;
255
+ } catch ( e ) {
301
256
302
- } else {
257
+ disableDisplay ( ) ;
303
258
304
- enableDisplayAndShowTime ( date ) ;
305
-
306
- }
307
-
308
- } ) ;
259
+ }
309
260
310
261
}
311
262
@@ -377,8 +328,6 @@ initialiseDisplay();
377
328
378
329
setTimeButton . addEventListener ( 'click' , function ( ) {
379
330
380
- communicating = true ;
381
-
382
331
timeDisplay . classList . add ( 'grey' ) ;
383
332
384
333
const USB_LAG = 20 ;
@@ -387,20 +336,6 @@ setTimeButton.addEventListener('click', function () {
387
336
388
337
const MILLISECONDS_IN_SECOND = 1000 ;
389
338
390
- /* Update button */
391
-
392
- setTimeButton . disabled = true ;
393
-
394
- setTimeout ( function ( ) {
395
-
396
- communicating = false ;
397
-
398
- requestTime ( ) ;
399
-
400
- setTimeButton . disabled = false ;
401
-
402
- } , 1500 ) ;
403
-
404
339
/* Increment to next second transition */
405
340
406
341
const sendTime = new Date ( ) ;
@@ -414,21 +349,38 @@ setTimeButton.addEventListener('click', function () {
414
349
/* Calculate how long to wait until second transition */
415
350
416
351
const now = new Date ( ) ;
352
+
417
353
const sendTimeDiff = sendTime . getTime ( ) - now . getTime ( ) ;
418
354
355
+ /* Calculate when to re-enable time display */
356
+
357
+ communicating = true ;
358
+
359
+ setTimeButton . disabled = true ;
360
+
361
+ const updateDelay = sendTimeDiff <= 0 ? MILLISECONDS_IN_SECOND : sendTimeDiff ;
362
+
363
+ setTimeout ( function ( ) {
364
+
365
+ communicating = false ;
366
+
367
+ } , updateDelay ) ;
368
+
419
369
/* Either send immediately or wait until the transition */
420
370
421
371
if ( sendTimeDiff <= 0 ) {
422
372
423
- setTime ( sendTime ) ;
373
+ console . log ( 'Sending...' ) ;
374
+
375
+ setAudioMothTime ( sendTime ) ;
424
376
425
377
} else {
426
378
427
379
console . log ( 'Sending in' , sendTimeDiff ) ;
428
380
429
381
setTimeout ( function ( ) {
430
382
431
- setTime ( sendTime ) ;
383
+ setAudioMothTime ( sendTime ) ;
432
384
433
385
} , sendTimeDiff ) ;
434
386
@@ -456,4 +408,4 @@ electron.ipcRenderer.on('poll-night-mode', () => {
456
408
457
409
} ) ;
458
410
459
- requestTime ( ) ;
411
+ requestAudioMothTime ( ) ;
0 commit comments