• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stephendade / Rpanion-server / 16014010506

02 Jul 2025 01:35AM UTC coverage: 35.71% (-0.2%) from 35.881%
16014010506

push

github

stephendade
Flightcontroller: Allow networked input

278 of 1022 branches covered (27.2%)

Branch coverage included in aggregate %.

27 of 69 new or added lines in 2 files covered. (39.13%)

1 existing line in 1 file now uncovered.

904 of 2288 relevant lines covered (39.51%)

2.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.97
/server/flightController.js
1
const { autoDetect } = require('@serialport/bindings-cpp')
3✔
2
const fs = require('fs')
3✔
3
const events = require('events')
3✔
4
const path = require('path')
3✔
5
const { spawn, spawnSync } = require('child_process')
3✔
6
const si = require('systeminformation')
3✔
7

8
const mavManager = require('../mavlink/mavManager.js')
3✔
9
const logpaths = require('./paths.js')
3✔
10

11
function isPi () {
12
  let cpuInfo = ''
3✔
13
  try {
3✔
14
    cpuInfo = fs.readFileSync('/proc/device-tree/compatible', { encoding: 'utf8' })
3✔
15
  } catch (e) {
16
    // if this fails, this is probably not a pi
17
    return false
3✔
18
  }
19

20
  const model = cpuInfo
×
21
    .split(',')
22
    .filter(line => line.length > 0)
×
23

24
  if (!model || model.length === 0) {
×
25
    return false
×
26
  }
27

28
  return model[0] === 'raspberrypi'
×
29
}
30

31
class FCDetails {
32
  constructor (settings) {
33
    // if the device was successfully opend and got packets
34
    this.previousConnection = false
15✔
35

36
    // all detected serial ports and baud rates
37
    this.serialDevices = []
15✔
38
    this.baudRates = [{ value: 9600, label: '9600' },
15✔
39
      { value: 19200, label: '19200' },
40
      { value: 38400, label: '38400' },
41
      { value: 57600, label: '57600' },
42
      { value: 111100, label: '111100' },
43
      { value: 115200, label: '115200' },
44
      { value: 230400, label: '230400' },
45
      { value: 256000, label: '256000' },
46
      { value: 460800, label: '460800' },
47
      { value: 500000, label: '500000' },
48
      { value: 921600, label: '921600' },
49
      { value: 1500000, label: '1500000' }]
50
    this.mavlinkVersions = [{ value: 1, label: '1.0' },
15✔
51
      { value: 2, label: '2.0' }]
52
    this.inputTypes = [{ value: 'UART', label: 'UART' },
15✔
53
      { value: 'UDP', label: 'UDP Client' }]
54
    // JSON of active device (input type, port and baud and mavversion). User selected
55
    // null if user selected no link (or no serial port of that name)
56
    this.activeDevice = null
15✔
57

58
    // mavlink-router process
59
    this.router = null
15✔
60

61
    // the mavlink manager object
62
    this.m = null
15✔
63

64
    // For sending events outside of object
65
    this.eventEmitter = new events.EventEmitter()
15✔
66

67
    // Interval to check connection status and re-connect
68
    // if required
69
    this.intervalObj = null
15✔
70

71
    // UDP Outputs
72
    this.UDPoutputs = []
15✔
73

74
    // Send out MAVLink heartbeat packets?
75
    this.enableHeartbeat = false
15✔
76
  
77
    // Use TCP output?
78
    this.enableTCP = false
15✔
79

80
    // Use UDP Broadcast?
81
    this.enableUDPB = true
15✔
82
    this.UDPBPort = 14550
15✔
83

84
    // Send datastream requests to flight controller?
85
    this.enableDSRequest = false
15✔
86

87
    // Current binlog via mavlink-router
88
    this.binlog = null
15✔
89

90
    this.tlogging = false
15✔
91

92
    // Is the connection active?
93
    this.active = false
15✔
94

95
    // load settings
96
    this.settings = settings
15✔
97
    this.activeDevice = this.settings.value('flightcontroller.activeDevice', null)
15✔
98
    this.UDPoutputs = this.settings.value('flightcontroller.outputs', [])
15✔
99
    this.enableHeartbeat = this.settings.value('flightcontroller.enableHeartbeat', false)
15✔
100
    this.enableTCP = this.settings.value('flightcontroller.enableTCP', false)
15✔
101
    this.enableUDPB = this.settings.value('flightcontroller.enableUDPB', true)
15✔
102
    this.UDPBPort = this.settings.value('flightcontroller.UDPBPort', 14550)
15✔
103
    this.enableDSRequest = this.settings.value('flightcontroller.enableDSRequest', false)
15✔
104
    this.tlogging = this.settings.value('flightcontroller.tlogging', false)
15✔
105
    this.active = this.settings.value('flightcontroller.active', false)
15✔
106

107
    if (this.active) {
15!
108
      // restart link if saved serial device is found
NEW
109
      this.getDeviceSettings((err, devices) => {
×
NEW
110
        if (this.activeDevice.inputType === 'UART') {
×
NEW
111
          let found = false
×
NEW
112
          for (let i = 0, len = devices.length; i < len; i++) {
×
NEW
113
            if (this.activeDevice.serial.value === devices[i].value) {
×
NEW
114
              found = true
×
NEW
115
              this.startLink((err) => {
×
NEW
116
                if (err) {
×
NEW
117
                  console.log("Can't open found FC " + this.activeDevice.serial.value + ', resetting link')
×
NEW
118
                  this.activeDevice = null
×
NEW
119
                  this.active = false
×
120
                }
NEW
121
                this.startInterval()
×
122
              })
NEW
123
              break
×
124
            }
125
          }
NEW
126
          if (!found) {
×
NEW
127
            console.log("Can't open saved connection, resetting")
×
NEW
128
            this.activeDevice = null
×
NEW
129
            this.active = false
×
130
          }
NEW
131
        } else if (this.activeDevice.inputType === 'UDP') {
×
NEW
132
          this.startLink((err) => {
×
NEW
133
            if (err) {
×
NEW
134
              console.log("Can't open UDP port " + this.activeDevice.udpInputPort + ', resetting link')
×
NEW
135
              this.activeDevice = null
×
NEW
136
              this.active = false
×
137
            }
NEW
138
            this.startInterval()
×
139
          })
140
        }
141
      })
142
    }
143
  }
144

145
  validMavlinkRouter () {
146
    // check mavlink-router is installed
147
    const ls = spawnSync('which', ['mavlink-routerd'])
3✔
148
    console.log(ls.stdout.toString())
3✔
149
    if (ls.stdout.toString().trim() == '') {
3!
150
      return false
×
151
    } else {
152
      return true
3✔
153
    }
154
  }
155

156
  isModemManagerInstalled () {
157
    // check ModemManager is installed
158
    const ls = spawnSync('which', ['ModemManager'])
3✔
159
    console.log(ls.stdout.toString())
3✔
160
    if (ls.stdout.toString().includes('ModemManager')) {
3!
161
      return true
×
162
    } else {
163
      return false
3✔
164
    }
165
  }
166

167
  getUDPOutputs () {
168
    // get list of current UDP outputs
169
    const ret = []
33✔
170
    for (let i = 0, len = this.UDPoutputs.length; i < len; i++) {
33✔
171
      ret.push({ IPPort: this.UDPoutputs[i].IP + ':' + this.UDPoutputs[i].port })
36✔
172
    }
173
    return ret
33✔
174
  }
175

176
  addUDPOutput (newIP, newPort) {
177
    // add a new udp output, if not already in
178
    // check if this ip:port is already in the list
179
    for (let i = 0, len = this.UDPoutputs.length; i < len; i++) {
9✔
180
      if (this.UDPoutputs[i].IP === newIP && this.UDPoutputs[i].port === newPort) {
6✔
181
        return this.getUDPOutputs()
3✔
182
      }
183
    }
184

185
    // check that it's not the internal 127.0.0.1:14540
186
    if (newIP === '127.0.0.1' && newPort === 14540) {
6!
187
      return this.getUDPOutputs()
×
188
    }
189

190
    // add it in
191
    this.UDPoutputs.push({ IP: newIP, port: newPort })
6✔
192
    console.log('Added UDP Output ' + newIP + ':' + newPort)
6✔
193

194
    // restart mavlink-router, if link active
195
    if (this.m) {
6!
196
      this.closeLink(() => {
×
197
        this.startLink((err) => {
×
198
          if (err) {
×
199
            console.log(err)
×
200
          }
201
        })
202
      })
203
    }
204

205
    // try to save. Will be invalid if running under test runner
206
    try {
6✔
207
      this.saveSerialSettings()
6✔
208
    } catch (e) {
209
      console.log(e)
×
210
    }
211

212
    return this.getUDPOutputs()
6✔
213
  }
214

215
  removeUDPOutput (remIP, remPort) {
216
    // remove new udp output
217

218
    // check that it's not the internal 127.0.0.1:14540
219
    if (remIP === '127.0.0.1' && remPort === 14540) {
6!
220
      return this.getUDPOutputs()
×
221
    }
222

223
    // check if this ip:port is already in the list
224
    for (let i = 0, len = this.UDPoutputs.length; i < len; i++) {
6✔
225
      if (this.UDPoutputs[i].IP === remIP && this.UDPoutputs[i].port === remPort) {
9✔
226
        // and remove
227
        this.UDPoutputs.splice(i, 1)
3✔
228
        console.log('Removed UDP Output ' + remIP + ':' + remPort)
3✔
229

230
        // restart mavlink-router, if link active
231
        if (this.m) {
3!
232
          this.closeLink(() => {
×
233
            this.startLink((err) => {
×
234
              if (err) {
×
235
                console.log(err)
×
236
              }
237
            })
238
          })
239
        }
240

241
        // try to save. Will be invalid if running under test runner
242
        try {
3✔
243
          this.saveSerialSettings()
3✔
244
        } catch (e) {
245
          console.log(e)
×
246
        }
247

248
        return this.getUDPOutputs()
3✔
249
      }
250
    }
251

252
    return this.getUDPOutputs()
3✔
253
  }
254

255
  getSystemStatus () {
256
    // get the system status
257
    if (this.m !== null) {
3!
258
      return {
×
259
        numpackets: this.m.statusNumRxPackets,
260
        FW: this.m.autopilotFromID(),
261
        vehType: this.m.vehicleFromID(),
262
        conStatus: this.m.conStatusStr(),
263
        statusText: this.m.statusText,
264
        byteRate: this.m.statusBytesPerSec.avgBytesSec,
265
        fcVersion: this.m.fcVersion
266
      }
267
    } else {
268
      return {
3✔
269
        numpackets: 0,
270
        FW: '',
271
        vehType: '',
272
        conStatus: 'Not connected',
273
        statusText: '',
274
        byteRate: 0,
275
        fcVersion: ''
276
      }
277
    }
278
  }
279

280
  rebootFC () {
281
    // command the flight controller to reboot
282
    if (this.m !== null) {
×
283
      console.log('Rebooting FC')
×
284
      this.m.sendReboot()
×
285
    }
286
  }
287

288
  startBinLogging () {
289
    // command the flight controller to start streaming bin log
290
    if (this.m !== null) {
×
291
      console.log('Bin log start request')
×
292
      this.m.sendBinStreamRequest()
×
293
    }
294
  }
295

296
  stopBinLogging () {
297
    // command the flight controller to stop streaming bin log
298
    if (this.m !== null) {
×
299
      console.log('Bin log stop request')
×
300
      this.m.sendBinStreamRequestStop()
×
301
    }
302
  }
303

304
  startLink (callback) {
305
    // start the serial link
306
    if (this.activeDevice.inputType === 'UDP') {
3!
NEW
307
      console.log('Opening UDP Link ' + '0.0.0.0:' + this.activeDevice.udpInputPort + ', MAV v' + this.activeDevice.mavversion.value)
×
308
    } else {
309
      console.log('Opening UART Link ' + this.activeDevice.serial.value + ' @ ' + this.activeDevice.baud.value + ', MAV v' + this.activeDevice.mavversion.value)
3✔
310
    }
311
    // this.outputs.push({ IP: newIP, port: newPort })
312

313
    // build up the commandline for mavlink-router
314
    const cmd = ['-e', '127.0.0.1:14540', '--tcp-port']
3✔
315
    if (this.enableTCP === true) {
3!
316
      cmd.push('5760')
3✔
317
    } else {
318
      cmd.push('0')
×
319
    }
320
    for (let i = 0, len = this.UDPoutputs.length; i < len; i++) {
3✔
321
      cmd.push('-e')
×
322
      cmd.push(this.UDPoutputs[i].IP + ':' + this.UDPoutputs[i].port)
×
323
    }
324
    cmd.push('--log')
3✔
325
    cmd.push(logpaths.flightsLogsDir)
3✔
326
    if (this.tlogging === true) {
3!
327
      cmd.push('--telemetry-log')
×
328
    }
329
    if (this.enableUDPB === true) {
3!
330
      cmd.push('0.0.0.0:' + this.UDPBPort)
×
331
    }
332
    if (this.activeDevice.inputType === 'UART') {
3!
333
      cmd.push(this.activeDevice.serial.value + ':' + this.activeDevice.baud.value)
3✔
NEW
334
    } else if (this.activeDevice.inputType === 'UDP') {
×
NEW
335
      cmd.push('0.0.0.0:' + this.activeDevice.udpInputPort)
×
336
    }
337
    console.log(cmd)
3✔
338

339
    // check mavlink-router exists
340
    if (!this.validMavlinkRouter()) {
3!
341
      console.log('Could not find mavlink-routerd')
×
NEW
342
      this.active = false
×
UNCOV
343
      return callback('Could not find mavlink-routerd', false)
×
344
    }
345

346
    // start mavlink-router
347
    this.router = spawn('mavlink-routerd', cmd)
3✔
348
    this.router.stdout.on('data', (data) => {
3✔
349
      console.log(`stdout: ${data}`)
×
350
    })
351

352
    this.router.stderr.on('data', (data) => {
3✔
353
      console.error(`stderr: ${data}`)
3✔
354
      if (data.toString().includes('Logging target') && data.toString().includes('.bin')) {
3!
355
        // remove old log, if it exists and is >60kB
356
        try {
×
357
          if (this.binlog !== null) {
×
358
            const fileStat = fs.lstatSync(this.binlog)
×
359
            if (Math.round(fileStat.size / 1024) < 60) {
×
360
              fs.unlinkSync(this.binlog)
×
361
            }
362
          }
363
        } catch (err) {
364
          console.log(err)
×
365
        }
366
        const res = data.toString().split(' ')
×
367
        const curLog = (res[res.length - 1]).trim()
×
368
        this.binlog = path.join(logpaths.flightsLogsDir, curLog)
×
369
        console.log('Current log is: ' + this.binlog)
×
370
      }
371
    })
372

373
    this.router.on('close', (code) => {
3✔
374
      console.log(`child process exited with code ${code}`)
3✔
375
      console.log('Closed Router')
3✔
376
      this.eventEmitter.emit('stopLink')
3✔
377
    })
378

379
    console.log('Opened Router')
3✔
380

381
    // only restart the mavlink processor if it's a new link,
382
    // not a reconnect attempt
383
    if (this.m === null) {
3!
384
      this.m = new mavManager(this.activeDevice.mavversion.value, '127.0.0.1', 14540, this.enableDSRequest)
3✔
385
      this.m.eventEmitter.on('gotMessage', (packet, data) => {
3✔
386
        // got valid message - send on to attached classes
387
        this.previousConnection = true
×
388
        this.eventEmitter.emit('gotMessage', packet, data)
×
389
      })
390
    }
391

392
    // arming events - just pass them on
393
    this.m.eventEmitter.on('armed', () => {
3✔
394
      this.eventEmitter.emit('armed')
×
395
    })
396
    this.m.eventEmitter.on('disarmed', () => {
3✔
397
      this.eventEmitter.emit('disarmed')
×
398
    })
399
    this.eventEmitter.emit('newLink')
3✔
400

401
    this.active = true
3✔
402
    return callback(null, true)
3✔
403
  }
404

405
  closeLink (callback) {
406
    // stop the serial link
407
    this.active = false
3✔
408
    if (this.router && this.router.exitCode === null) {
3!
409
      this.router.kill('SIGINT')
3✔
410
      console.log('Trying to close router')
3✔
411
      return callback(null)
3✔
412
    } else {
413
      console.log('Already Closed Router')
×
414
      this.eventEmitter.emit('stopLink')
×
415
      return callback(null)
×
416
    }
417
  }
418

419
  async getDeviceSettings (callback) {
420
    // get all serial devices
421
    this.serialDevices = []
3✔
422
    let retError = null
3✔
423

424
    const Binding = autoDetect()
3✔
425
    const ports = await Binding.list()
3✔
426

427
    for (let i = 0, len = ports.length; i < len; i++) {
3✔
428
      if (ports[i].pnpId !== undefined) {
96!
429
        // usb-ArduPilot_Pixhawk1-1M_32002A000847323433353231-if00
430
        // console.log("Port: ", ports[i].pnpID);
431
        let namePorts = ''
×
432
        if (ports[i].pnpId.split('_').length > 2) {
×
433
          namePorts = ports[i].pnpId.split('_')[1] + ' (' + ports[i].path + ')'
×
434
        } else {
435
          namePorts = ports[i].manufacturer + ' (' + ports[i].path + ')'
×
436
        }
437
        // console.log("Port: ", ports[i].pnpID);
438
        this.serialDevices.push({ value: ports[i].path, label: namePorts, pnpId: ports[i].pnpId })
×
439
      } else if (ports[i].manufacturer !== undefined) {
96!
440
        // on recent RasPiOS, the pnpID is undefined :(
441
        const nameports = ports[i].manufacturer + ' (' + ports[i].path + ')'
×
442
        this.serialDevices.push({ value: ports[i].path, label: nameports, pnpId: nameports })
×
443
      }
444
    }
445
    // if the serial console or modemmanager are active on the Pi, return error
446
    if (this.isModemManagerInstalled()) {
3!
447
      retError = Error('The ModemManager package is installed. This must be uninstalled (via sudo apt remove modemmanager), due to conflicts with serial ports')
×
448
    }
449
    if (fs.existsSync('/boot/cmdline.txt') && isPi()) {
3!
450
      const data = fs.readFileSync('/boot/cmdline.txt', { encoding: 'utf8', flag: 'r' })
×
451
      if (data.includes('console=serial0')) {
×
452
        retError = Error('Serial console is active on /dev/serial0. Use raspi-config to deactivate it')
×
453
      }
454
    }
455
    // for the Ras Pi's inbuilt UART
456
    if (fs.existsSync('/dev/serial0') && isPi()) {
3!
457
      this.serialDevices.push({ value: '/dev/serial0', label: '/dev/serial0', pnpId: '/dev/serial0' })
×
458
    }
459
    if (fs.existsSync('/dev/ttyAMA0') && isPi()) {
3!
460
      //Pi5 uses a different UART name. See https://forums.raspberrypi.com/viewtopic.php?t=359132
461
      this.serialDevices.push({ value: '/dev/ttyAMA0', label: '/dev/ttyAMA0', pnpId: '/dev/ttyAMA0' })
×
462
    }
463
    if (fs.existsSync('/dev/ttyAMA1') && isPi()) {
3!
464
      this.serialDevices.push({ value: '/dev/ttyAMA1', label: '/dev/ttyAMA1', pnpId: '/dev/ttyAMA1' })
×
465
    }
466
    if (fs.existsSync('/dev/ttyAMA2') && isPi()) {
3!
467
      this.serialDevices.push({ value: '/dev/ttyAMA2', label: '/dev/ttyAMA2', pnpId: '/dev/ttyAMA2' })
×
468
    }
469
    if (fs.existsSync('/dev/ttyAMA3') && isPi()) {
3!
470
      this.serialDevices.push({ value: '/dev/ttyAMA3', label: '/dev/ttyAMA3', pnpId: '/dev/ttyAMA3' })
×
471
    }
472
    if (fs.existsSync('/dev/ttyAMA4') && isPi()) {
3!
473
      this.serialDevices.push({ value: '/dev/ttyAMA4', label: '/dev/ttyAMA4', pnpId: '/dev/ttyAMA4' })
×
474
    }
475
    // rpi uart has different name under Ubuntu
476
    const data = await si.osInfo()
3✔
477
    if (data.distro.toString().includes('Ubuntu') && fs.existsSync('/dev/ttyS0') && isPi()) {
3!
478
      // console.log("Running Ubuntu")
479
      this.serialDevices.push({ value: '/dev/ttyS0', label: '/dev/ttyS0', pnpId: '/dev/ttyS0' })
×
480
    }
481
    // jetson serial ports
482
    if (fs.existsSync('/dev/ttyTHS1')) {
3!
483
      this.serialDevices.push({ value: '/dev/ttyTHS1', label: '/dev/ttyTHS1', pnpId: '/dev/ttyTHS1' })
×
484
    }
485
    if (fs.existsSync('/dev/ttyTHS2')) {
3!
486
      this.serialDevices.push({ value: '/dev/ttyTHS2', label: '/dev/ttyTHS2', pnpId: '/dev/ttyTHS2' })
×
487
    }
488
    if (fs.existsSync('/dev/ttyTHS3')) {
3!
489
      this.serialDevices.push({ value: '/dev/ttyTHS3', label: '/dev/ttyTHS3', pnpId: '/dev/ttyTHS3' })
×
490
    }
491

492
    // set the active device as selected
493
    if (this.active && this.activeDevice && this.activeDevice.inputType === 'UART') {
3!
NEW
494
      return callback(retError, this.serialDevices, this.baudRates, this.activeDevice.serial,
×
495
        this.activeDevice.baud, this.mavlinkVersions, this.activeDevice.mavversion,
496
        this.active, this.enableHeartbeat, this.enableTCP, this.enableUDPB, this.UDPBPort, this.enableDSRequest, this.tlogging, this.activeDevice.udpInputPort,
497
        this.inputTypes[0], this.inputTypes)
498
    } else if (this.active && this.activeDevice && this.activeDevice.inputType === 'UDP') {
3!
NEW
499
      return callback(retError, this.serialDevices, this.baudRates, this.serialDevices.length > 0 ? this.serialDevices[0] : [], this.baudRates[3],
×
500
        this.mavlinkVersions, this.activeDevice.mavversion, this.active, this.enableHeartbeat,
501
        this.enableTCP, this.enableUDPB, this.UDPBPort, this.enableDSRequest, this.tlogging, this.activeDevice.udpInputPort,
502
        this.inputTypes[1], this.inputTypes)
503
    } else {
504
      // no connection
505
      return callback(retError, this.serialDevices, this.baudRates, this.serialDevices.length > 0 ? this.serialDevices[0] : [],
3!
506
        this.baudRates[3], this.mavlinkVersions, this.mavlinkVersions[1], this.active, this.enableHeartbeat,
507
        this.enableTCP, this.enableUDPB, this.UDPBPort, this.enableDSRequest, this.tlogging, 9000, this.inputTypes[0], this.inputTypes)
508
    }
509
  }
510

511
  startInterval () {
512
    // start the 1-sec loop checking for disconnects
513
    this.intervalObj = setInterval(() => {
3✔
514
    
515
    // Send heartbeats, if they are enabled
516
    if(this.enableHeartbeat){
×
517
      this.m.sendHeartbeat()
×
518
    }
519
      // check for timeouts in serial link (ie disconnected cable or reboot)
520
      if (this.m && this.m.conStatusInt() === -1) {
×
521
        console.log('Trying to reconnect FC...')
×
522
        this.closeLink(() => {
×
523
          this.startLink((err) => {
×
524
            if (err) {
×
525
              console.log(err)
×
526
            } else {
527
              // DS request is in this.m.restart()
528
              this.m.restart()
×
529
            }
530
          })
531
        })
532
      }
533
    }, 1000)
534
  }
535

536
  startStopTelemetry (device, baud, mavversion, enableHeartbeat, enableTCP, enableUDPB, UDPBPort, enableDSRequest,
537
                      tlogging, inputType, udpInputPort, callback) {
538
    // user wants to start or stop telemetry
539
    // callback is (err, isSuccessful)
540

541
    this.enableHeartbeat = enableHeartbeat
6✔
542
    this.enableTCP = enableTCP
6✔
543
    this.enableUDPB = enableUDPB
6✔
544
    this.UDPBPort = UDPBPort
6✔
545
    this.enableDSRequest = enableDSRequest
6✔
546
    this.tlogging = tlogging
6✔
547

548
    if (this.m) {
6✔
549
      this.m.enableDSRequest = enableDSRequest
3✔
550
    }
551

552
    // check port, mavversion and baud are valid (if starting telem)
553
    if (!this.active) {
6✔
554
      this.activeDevice = { serial: null, baud: null, inputType: 'UART', mavversion: null, udpInputPort: 9000 }
3✔
555
      for (let i = 0, len = this.mavlinkVersions.length; i < len; i++) {
3✔
556
        if (this.mavlinkVersions[i].value === mavversion.value) {
6✔
557
          this.activeDevice.mavversion = this.mavlinkVersions[i]
3✔
558
          break
3✔
559
        }
560
      }
561

562
      if (inputType.value === 'UART') {
3!
563
        this.activeDevice.inputType = 'UART'
3✔
564
        for (let i = 0, len = this.serialDevices.length; i < len; i++) {
3✔
565
          if (this.serialDevices[i].pnpId === device.pnpId) {
3!
566
            this.activeDevice.serial = this.serialDevices[i]
3✔
567
            break
3✔
568
          }
569
        }
570
        for (let i = 0, len = this.baudRates.length; i < len; i++) {
3✔
571
          if (this.baudRates[i].value === baud.value) {
18✔
572
            this.activeDevice.baud = this.baudRates[i]
3✔
573
            break
3✔
574
          }
575
        }
576

577
        if (this.activeDevice.serial === null || this.activeDevice.baud.value === null || this.activeDevice.serial.value === null || this.activeDevice.mavversion.value === null || this.enableTCP === null) {
3!
NEW
578
          this.activeDevice = null
×
NEW
579
          this.active = false
×
NEW
580
          return callback(new Error('Bad serial device or baud'), false)
×
581
        }
NEW
582
      } else if (inputType.value === 'UDP') {
×
583
        // UDP input
NEW
584
        this.activeDevice.inputType = 'UDP'
×
NEW
585
        this.activeDevice.serial = null
×
NEW
586
        this.activeDevice.baud = null
×
NEW
587
        this.activeDevice.mavversion = { value: mavversion.value, label: mavversion.label }
×
NEW
588
        this.activeDevice.udpInputPort = udpInputPort
×
589
      } else {
590
        // unknown input type
591
        this.activeDevice = null
×
NEW
592
        return callback(new Error('Unknown input type'), false)
×
593
      }
594

595
      // this.activeDevice = {inputType, udpInputPort, serial: device, baud: baud};
596
      this.startLink((err) => {
3✔
597
        if (err) {
3!
598
          console.log("Can't open found FC " + this.activeDevice.serial.value + ', resetting link')
×
599
          this.activeDevice = null
×
600
        } else {
601
          // start timeout function for auto-reconnect
602
          this.startInterval()
3✔
603
          this.saveSerialSettings()
3✔
604
        }
605
        return callback(err, this.activeDevice !== null)
3✔
606
      })
607
    } else {
608
      // close link
609
      this.activeDevice = null
3✔
610
      this.closeLink(() => {
3✔
611
        this.saveSerialSettings()
3✔
612
        clearInterval(this.intervalObj)
3✔
613
        this.previousConnection = false
3✔
614
        this.m.close()
3✔
615
        this.m = null
3✔
616
        return callback(null, this.activeDevice !== null)
3✔
617
      })
618
    }
619
  }
620

621
  saveSerialSettings () {
622
    // Save the current settings to file
623
    try {
15✔
624
      this.settings.setValue('flightcontroller.activeDevice', this.activeDevice)
15✔
625
      this.settings.setValue('flightcontroller.outputs', this.UDPoutputs)
15✔
626
      this.settings.setValue('flightcontroller.enableHeartbeat', this.enableHeartbeat)
15✔
627
      this.settings.setValue('flightcontroller.enableTCP', this.enableTCP)
15✔
628
      this.settings.setValue('flightcontroller.enableUDPB', this.enableUDPB)
15✔
629
      this.settings.setValue('flightcontroller.UDPBPort', this.UDPBPort)
15✔
630
      this.settings.setValue('flightcontroller.enableDSRequest', this.enableDSRequest)
15✔
631
      this.settings.setValue('flightcontroller.tlogging', this.tlogging)
15✔
632
      this.settings.setValue('flightcontroller.active', this.active)
15✔
633
      console.log('Saved FC settings')
15✔
634
    } catch (e) {
635
      console.log(e)
×
636
    }
637
  }
638
}
639

640
module.exports = FCDetails
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc