-
Notifications
You must be signed in to change notification settings - Fork 175
/
discovery.js
106 lines (87 loc) · 3.25 KB
/
discovery.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// mdns-based api server discovery with direct ethernet connection discovery
import os from 'os'
import net from 'net'
import Bonjour from 'bonjour'
// import directly from health to avoid importing other parts
// of the api client (most importantly, the electron remote)
import {fetchHealth} from '../../http-api-client/health'
import {actions} from '../actions'
// mdns discovery constants
const NAME_RE = /^opentrons/i
const DISCOVERY_TIMEOUT_MS = 30000
const UP_EVENT = 'up'
const DOWN_EVENT = 'down'
// direct discovery constants
// see compute/scripts/setup.sh
const DIRECT_SERVICE = {
name: 'Opentrons USB',
ip: '[fd00:0:cafe:fefe::1]',
port: 31950,
wired: true
}
const DIRECT_POLL_INTERVAL_MS = 1000
const SKIP_WIRED_POLL = process.env.SKIP_WIRED_POLL
export function handleDiscover (dispatch, state, action) {
// disable legacy discovery if new discovery feature flag is set
if (state.config.discovery.enabled) return
// don't duplicate discovery requests
// TODO(mc, 2018-09-10): did not use selector to avoid circular dependency
// this file is getting ditched so ¯\_(ツ)_/¯
if (state.discovery.scanning) return
// TODO(mc, 2017-10-26): we're relying right now on the fact that resin
// advertises an SSH service. Instead, we should be registering an HTTP
// service on port 31950 and listening for that instead
const browser = Bonjour().find({type: 'http'})
.on(UP_EVENT, handleServiceUp)
.on(DOWN_EVENT, handleServiceDown)
let pollInterval
if (!SKIP_WIRED_POLL) {
pollInterval = setInterval(pollDirectConnection, DIRECT_POLL_INTERVAL_MS)
}
setTimeout(finishDiscovery, DISCOVERY_TIMEOUT_MS)
function handleServiceUp (service) {
if (NAME_RE.test(service.name)) {
const serviceWithIp = withIp(service)
dispatch(actions.addDiscovered(serviceWithIp))
// fetchHealth is a thunk action, so give it dispatch
return fetchHealth(serviceWithIp)(dispatch)
}
}
function handleServiceDown (service) {
if (NAME_RE.test(service.name)) {
dispatch(actions.removeDiscovered(service))
}
}
function finishDiscovery () {
browser.removeListener(UP_EVENT, handleServiceUp)
browser.removeListener(DOWN_EVENT, handleServiceDown)
browser.stop()
clearInterval(pollInterval)
dispatch(actions.discoverFinish())
}
function pollDirectConnection () {
fetchHealth(DIRECT_SERVICE)(dispatch).then(result => {
const action = result.type === 'api:SUCCESS'
? actions.addDiscovered(DIRECT_SERVICE)
: actions.removeDiscovered(DIRECT_SERVICE)
dispatch(action)
})
}
}
// grab IP address from service
// prefer IPv4, then IPv6, then hostname (with override for localhost)
function withIp (service) {
if (service.ip) return service
const addresses = service.addresses || []
let ip = addresses.find((address) => net.isIPv4(address))
if (!ip) ip = addresses.find((address) => net.isIP(address))
if (!ip) ip = service.host
// API doesn't listen on all interfaces when running locally
// this hostname check is only for handling that situation
if (service.host && service.host.endsWith(os.hostname())) {
ip = 'localhost'
// emulate a wired robot when running locally
service = {...service, wired: true}
}
return Object.assign({ip}, service)
}