1*cf78ab8cSAndroid Build Coastguard Workerimport {Capture, Chip, Chip_Radio, Device as ProtoDevice,} from './netsim/model.js'; 2*cf78ab8cSAndroid Build Coastguard Worker 3*cf78ab8cSAndroid Build Coastguard Worker// URL for netsim 4*cf78ab8cSAndroid Build Coastguard Workerconst DEVICES_URL = './v1/devices'; 5*cf78ab8cSAndroid Build Coastguard Workerconst CAPTURES_URL = './v1/captures'; 6*cf78ab8cSAndroid Build Coastguard Worker 7*cf78ab8cSAndroid Build Coastguard Worker/** 8*cf78ab8cSAndroid Build Coastguard Worker * Interface for a method in notifying the subscribed observers. 9*cf78ab8cSAndroid Build Coastguard Worker * Subscribed observers must implement this interface. 10*cf78ab8cSAndroid Build Coastguard Worker */ 11*cf78ab8cSAndroid Build Coastguard Workerexport interface Notifiable { 12*cf78ab8cSAndroid Build Coastguard Worker onNotify(data: {}): void; 13*cf78ab8cSAndroid Build Coastguard Worker} 14*cf78ab8cSAndroid Build Coastguard Worker 15*cf78ab8cSAndroid Build Coastguard Worker/** 16*cf78ab8cSAndroid Build Coastguard Worker * Modularization of Device. 17*cf78ab8cSAndroid Build Coastguard Worker * Contains getters and setters for properties in Device interface. 18*cf78ab8cSAndroid Build Coastguard Worker */ 19*cf78ab8cSAndroid Build Coastguard Workerexport class Device { 20*cf78ab8cSAndroid Build Coastguard Worker device: ProtoDevice; 21*cf78ab8cSAndroid Build Coastguard Worker 22*cf78ab8cSAndroid Build Coastguard Worker constructor(device: ProtoDevice) { 23*cf78ab8cSAndroid Build Coastguard Worker this.device = device; 24*cf78ab8cSAndroid Build Coastguard Worker } 25*cf78ab8cSAndroid Build Coastguard Worker 26*cf78ab8cSAndroid Build Coastguard Worker get name(): string { 27*cf78ab8cSAndroid Build Coastguard Worker return this.device.name; 28*cf78ab8cSAndroid Build Coastguard Worker } 29*cf78ab8cSAndroid Build Coastguard Worker 30*cf78ab8cSAndroid Build Coastguard Worker set name(value: string) { 31*cf78ab8cSAndroid Build Coastguard Worker this.device.name = value; 32*cf78ab8cSAndroid Build Coastguard Worker } 33*cf78ab8cSAndroid Build Coastguard Worker 34*cf78ab8cSAndroid Build Coastguard Worker get position(): {x: number; y: number; z: number} { 35*cf78ab8cSAndroid Build Coastguard Worker const result = {x: 0, y: 0, z: 0}; 36*cf78ab8cSAndroid Build Coastguard Worker if ('position' in this.device && this.device.position && 37*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.position === 'object') { 38*cf78ab8cSAndroid Build Coastguard Worker if ('x' in this.device.position && 39*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.position.x === 'number') { 40*cf78ab8cSAndroid Build Coastguard Worker result.x = this.device.position.x; 41*cf78ab8cSAndroid Build Coastguard Worker } 42*cf78ab8cSAndroid Build Coastguard Worker if ('y' in this.device.position && 43*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.position.y === 'number') { 44*cf78ab8cSAndroid Build Coastguard Worker result.y = this.device.position.y; 45*cf78ab8cSAndroid Build Coastguard Worker } 46*cf78ab8cSAndroid Build Coastguard Worker if ('z' in this.device.position && 47*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.position.z === 'number') { 48*cf78ab8cSAndroid Build Coastguard Worker result.z = this.device.position.z; 49*cf78ab8cSAndroid Build Coastguard Worker } 50*cf78ab8cSAndroid Build Coastguard Worker } 51*cf78ab8cSAndroid Build Coastguard Worker return result; 52*cf78ab8cSAndroid Build Coastguard Worker } 53*cf78ab8cSAndroid Build Coastguard Worker 54*cf78ab8cSAndroid Build Coastguard Worker set position(pos: {x: number; y: number; z: number}) { 55*cf78ab8cSAndroid Build Coastguard Worker this.device.position = pos; 56*cf78ab8cSAndroid Build Coastguard Worker } 57*cf78ab8cSAndroid Build Coastguard Worker 58*cf78ab8cSAndroid Build Coastguard Worker get orientation(): {yaw: number; pitch: number; roll: number} { 59*cf78ab8cSAndroid Build Coastguard Worker const result = {yaw: 0, pitch: 0, roll: 0}; 60*cf78ab8cSAndroid Build Coastguard Worker if ('orientation' in this.device && this.device.orientation && 61*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.orientation === 'object') { 62*cf78ab8cSAndroid Build Coastguard Worker if ('yaw' in this.device.orientation && 63*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.orientation.yaw === 'number') { 64*cf78ab8cSAndroid Build Coastguard Worker result.yaw = this.device.orientation.yaw; 65*cf78ab8cSAndroid Build Coastguard Worker } 66*cf78ab8cSAndroid Build Coastguard Worker if ('pitch' in this.device.orientation && 67*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.orientation.pitch === 'number') { 68*cf78ab8cSAndroid Build Coastguard Worker result.pitch = this.device.orientation.pitch; 69*cf78ab8cSAndroid Build Coastguard Worker } 70*cf78ab8cSAndroid Build Coastguard Worker if ('roll' in this.device.orientation && 71*cf78ab8cSAndroid Build Coastguard Worker typeof this.device.orientation.roll === 'number') { 72*cf78ab8cSAndroid Build Coastguard Worker result.roll = this.device.orientation.roll; 73*cf78ab8cSAndroid Build Coastguard Worker } 74*cf78ab8cSAndroid Build Coastguard Worker } 75*cf78ab8cSAndroid Build Coastguard Worker return result; 76*cf78ab8cSAndroid Build Coastguard Worker } 77*cf78ab8cSAndroid Build Coastguard Worker 78*cf78ab8cSAndroid Build Coastguard Worker set orientation(ori: {yaw: number; pitch: number; roll: number}) { 79*cf78ab8cSAndroid Build Coastguard Worker this.device.orientation = ori; 80*cf78ab8cSAndroid Build Coastguard Worker } 81*cf78ab8cSAndroid Build Coastguard Worker 82*cf78ab8cSAndroid Build Coastguard Worker // TODO modularize getters and setters for Chip Interface 83*cf78ab8cSAndroid Build Coastguard Worker get chips(): Chip[] { 84*cf78ab8cSAndroid Build Coastguard Worker return this.device.chips ?? []; 85*cf78ab8cSAndroid Build Coastguard Worker } 86*cf78ab8cSAndroid Build Coastguard Worker 87*cf78ab8cSAndroid Build Coastguard Worker // TODO modularize getters and setters for Chip Interface 88*cf78ab8cSAndroid Build Coastguard Worker set chips(value: Chip[]) { 89*cf78ab8cSAndroid Build Coastguard Worker this.device.chips = value; 90*cf78ab8cSAndroid Build Coastguard Worker } 91*cf78ab8cSAndroid Build Coastguard Worker 92*cf78ab8cSAndroid Build Coastguard Worker get visible(): boolean { 93*cf78ab8cSAndroid Build Coastguard Worker return Boolean(this.device.visible); 94*cf78ab8cSAndroid Build Coastguard Worker } 95*cf78ab8cSAndroid Build Coastguard Worker 96*cf78ab8cSAndroid Build Coastguard Worker set visible(value: boolean) { 97*cf78ab8cSAndroid Build Coastguard Worker this.device.visible = value; 98*cf78ab8cSAndroid Build Coastguard Worker } 99*cf78ab8cSAndroid Build Coastguard Worker 100*cf78ab8cSAndroid Build Coastguard Worker toggleChipState(radio: Chip_Radio) { 101*cf78ab8cSAndroid Build Coastguard Worker radio.state = !radio.state; 102*cf78ab8cSAndroid Build Coastguard Worker } 103*cf78ab8cSAndroid Build Coastguard Worker 104*cf78ab8cSAndroid Build Coastguard Worker toggleCapture(device: Device, chip: Chip) { 105*cf78ab8cSAndroid Build Coastguard Worker if ('capture' in chip && chip.capture) { 106*cf78ab8cSAndroid Build Coastguard Worker chip.capture = !chip.capture; 107*cf78ab8cSAndroid Build Coastguard Worker simulationState.patchDevice({ 108*cf78ab8cSAndroid Build Coastguard Worker device: { 109*cf78ab8cSAndroid Build Coastguard Worker name: device.name, 110*cf78ab8cSAndroid Build Coastguard Worker chips: device.chips, 111*cf78ab8cSAndroid Build Coastguard Worker }, 112*cf78ab8cSAndroid Build Coastguard Worker }); 113*cf78ab8cSAndroid Build Coastguard Worker } 114*cf78ab8cSAndroid Build Coastguard Worker } 115*cf78ab8cSAndroid Build Coastguard Worker} 116*cf78ab8cSAndroid Build Coastguard Worker 117*cf78ab8cSAndroid Build Coastguard Worker/** 118*cf78ab8cSAndroid Build Coastguard Worker * The most recent state of the simulation. 119*cf78ab8cSAndroid Build Coastguard Worker * Subscribed observers must refer to this info and patch accordingly. 120*cf78ab8cSAndroid Build Coastguard Worker */ 121*cf78ab8cSAndroid Build Coastguard Workerexport interface SimulationInfo { 122*cf78ab8cSAndroid Build Coastguard Worker devices: Device[]; 123*cf78ab8cSAndroid Build Coastguard Worker captures: Capture[]; 124*cf78ab8cSAndroid Build Coastguard Worker selectedId: string; 125*cf78ab8cSAndroid Build Coastguard Worker dimension: {x: number; y: number; z: number}; 126*cf78ab8cSAndroid Build Coastguard Worker lastModified: string; 127*cf78ab8cSAndroid Build Coastguard Worker} 128*cf78ab8cSAndroid Build Coastguard Worker 129*cf78ab8cSAndroid Build Coastguard Workerinterface Observable { 130*cf78ab8cSAndroid Build Coastguard Worker registerObserver(elem: Notifiable): void; 131*cf78ab8cSAndroid Build Coastguard Worker removeObserver(elem: Notifiable): void; 132*cf78ab8cSAndroid Build Coastguard Worker} 133*cf78ab8cSAndroid Build Coastguard Worker 134*cf78ab8cSAndroid Build Coastguard Workerclass SimulationState implements Observable { 135*cf78ab8cSAndroid Build Coastguard Worker private observers: Notifiable[] = []; 136*cf78ab8cSAndroid Build Coastguard Worker 137*cf78ab8cSAndroid Build Coastguard Worker private simulationInfo: SimulationInfo = { 138*cf78ab8cSAndroid Build Coastguard Worker devices: [], 139*cf78ab8cSAndroid Build Coastguard Worker captures: [], 140*cf78ab8cSAndroid Build Coastguard Worker selectedId: '', 141*cf78ab8cSAndroid Build Coastguard Worker dimension: {x: 10, y: 10, z: 0}, 142*cf78ab8cSAndroid Build Coastguard Worker lastModified: '', 143*cf78ab8cSAndroid Build Coastguard Worker }; 144*cf78ab8cSAndroid Build Coastguard Worker 145*cf78ab8cSAndroid Build Coastguard Worker constructor() { 146*cf78ab8cSAndroid Build Coastguard Worker // initial GET 147*cf78ab8cSAndroid Build Coastguard Worker this.invokeGetDevice(); 148*cf78ab8cSAndroid Build Coastguard Worker this.invokeListCaptures(); 149*cf78ab8cSAndroid Build Coastguard Worker } 150*cf78ab8cSAndroid Build Coastguard Worker 151*cf78ab8cSAndroid Build Coastguard Worker async invokeGetDevice() { 152*cf78ab8cSAndroid Build Coastguard Worker await fetch(DEVICES_URL, { 153*cf78ab8cSAndroid Build Coastguard Worker method: 'GET', 154*cf78ab8cSAndroid Build Coastguard Worker }) 155*cf78ab8cSAndroid Build Coastguard Worker .then(response => response.json()) 156*cf78ab8cSAndroid Build Coastguard Worker .then(data => { 157*cf78ab8cSAndroid Build Coastguard Worker this.fetchDevice(data.devices); 158*cf78ab8cSAndroid Build Coastguard Worker this.updateLastModified(data.lastModified); 159*cf78ab8cSAndroid Build Coastguard Worker }) 160*cf78ab8cSAndroid Build Coastguard Worker .catch(error => { 161*cf78ab8cSAndroid Build Coastguard Worker // eslint-disable-next-line 162*cf78ab8cSAndroid Build Coastguard Worker console.log('Cannot connect to netsim web server', error); 163*cf78ab8cSAndroid Build Coastguard Worker }); 164*cf78ab8cSAndroid Build Coastguard Worker } 165*cf78ab8cSAndroid Build Coastguard Worker 166*cf78ab8cSAndroid Build Coastguard Worker async invokeListCaptures() { 167*cf78ab8cSAndroid Build Coastguard Worker await fetch(CAPTURES_URL, { 168*cf78ab8cSAndroid Build Coastguard Worker method: 'GET', 169*cf78ab8cSAndroid Build Coastguard Worker }) 170*cf78ab8cSAndroid Build Coastguard Worker .then(response => response.json()) 171*cf78ab8cSAndroid Build Coastguard Worker .then(data => { 172*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.captures = data.captures; 173*cf78ab8cSAndroid Build Coastguard Worker this.notifyObservers(); 174*cf78ab8cSAndroid Build Coastguard Worker }) 175*cf78ab8cSAndroid Build Coastguard Worker .catch(error => { 176*cf78ab8cSAndroid Build Coastguard Worker console.log('Cannot connect to netsim web server', error); 177*cf78ab8cSAndroid Build Coastguard Worker }); 178*cf78ab8cSAndroid Build Coastguard Worker } 179*cf78ab8cSAndroid Build Coastguard Worker 180*cf78ab8cSAndroid Build Coastguard Worker fetchDevice(devices?: ProtoDevice[]) { 181*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.devices = []; 182*cf78ab8cSAndroid Build Coastguard Worker if (devices) { 183*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.devices = devices.map(device => new Device(device)); 184*cf78ab8cSAndroid Build Coastguard Worker } 185*cf78ab8cSAndroid Build Coastguard Worker this.notifyObservers(); 186*cf78ab8cSAndroid Build Coastguard Worker } 187*cf78ab8cSAndroid Build Coastguard Worker 188*cf78ab8cSAndroid Build Coastguard Worker getLastModified() { 189*cf78ab8cSAndroid Build Coastguard Worker return this.simulationInfo.lastModified; 190*cf78ab8cSAndroid Build Coastguard Worker } 191*cf78ab8cSAndroid Build Coastguard Worker 192*cf78ab8cSAndroid Build Coastguard Worker updateLastModified(timestamp: string) { 193*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.lastModified = timestamp; 194*cf78ab8cSAndroid Build Coastguard Worker } 195*cf78ab8cSAndroid Build Coastguard Worker 196*cf78ab8cSAndroid Build Coastguard Worker patchSelected(id: string) { 197*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.selectedId = id; 198*cf78ab8cSAndroid Build Coastguard Worker this.notifyObservers(); 199*cf78ab8cSAndroid Build Coastguard Worker } 200*cf78ab8cSAndroid Build Coastguard Worker 201*cf78ab8cSAndroid Build Coastguard Worker handleDrop(id: string, x: number, y: number) { 202*cf78ab8cSAndroid Build Coastguard Worker this.simulationInfo.selectedId = id; 203*cf78ab8cSAndroid Build Coastguard Worker for (const device of this.simulationInfo.devices) { 204*cf78ab8cSAndroid Build Coastguard Worker if (id === device.name) { 205*cf78ab8cSAndroid Build Coastguard Worker device.position = {x, y, z: device.position.z}; 206*cf78ab8cSAndroid Build Coastguard Worker this.patchDevice({ 207*cf78ab8cSAndroid Build Coastguard Worker device: { 208*cf78ab8cSAndroid Build Coastguard Worker name: device.name, 209*cf78ab8cSAndroid Build Coastguard Worker position: device.position, 210*cf78ab8cSAndroid Build Coastguard Worker }, 211*cf78ab8cSAndroid Build Coastguard Worker }); 212*cf78ab8cSAndroid Build Coastguard Worker break; 213*cf78ab8cSAndroid Build Coastguard Worker } 214*cf78ab8cSAndroid Build Coastguard Worker } 215*cf78ab8cSAndroid Build Coastguard Worker } 216*cf78ab8cSAndroid Build Coastguard Worker 217*cf78ab8cSAndroid Build Coastguard Worker patchCapture(id: string, state: string) { 218*cf78ab8cSAndroid Build Coastguard Worker fetch(CAPTURES_URL + '/' + id, { 219*cf78ab8cSAndroid Build Coastguard Worker method: 'PATCH', 220*cf78ab8cSAndroid Build Coastguard Worker headers: { 221*cf78ab8cSAndroid Build Coastguard Worker 'Content-Type': 'text/plain', 222*cf78ab8cSAndroid Build Coastguard Worker 'Content-Length': state.length.toString(), 223*cf78ab8cSAndroid Build Coastguard Worker }, 224*cf78ab8cSAndroid Build Coastguard Worker body: state, 225*cf78ab8cSAndroid Build Coastguard Worker }); 226*cf78ab8cSAndroid Build Coastguard Worker this.notifyObservers(); 227*cf78ab8cSAndroid Build Coastguard Worker } 228*cf78ab8cSAndroid Build Coastguard Worker 229*cf78ab8cSAndroid Build Coastguard Worker patchDevice(obj: object) { 230*cf78ab8cSAndroid Build Coastguard Worker const jsonBody = JSON.stringify(obj); 231*cf78ab8cSAndroid Build Coastguard Worker fetch(DEVICES_URL, { 232*cf78ab8cSAndroid Build Coastguard Worker method: 'PATCH', 233*cf78ab8cSAndroid Build Coastguard Worker headers: { 234*cf78ab8cSAndroid Build Coastguard Worker 'Content-Type': 'application/json', 235*cf78ab8cSAndroid Build Coastguard Worker 'Content-Length': jsonBody.length.toString(), 236*cf78ab8cSAndroid Build Coastguard Worker }, 237*cf78ab8cSAndroid Build Coastguard Worker body: jsonBody, 238*cf78ab8cSAndroid Build Coastguard Worker }) 239*cf78ab8cSAndroid Build Coastguard Worker .then(response => response.json()) 240*cf78ab8cSAndroid Build Coastguard Worker .catch(error => { 241*cf78ab8cSAndroid Build Coastguard Worker // eslint-disable-next-line 242*cf78ab8cSAndroid Build Coastguard Worker console.error('Error:', error); 243*cf78ab8cSAndroid Build Coastguard Worker }); 244*cf78ab8cSAndroid Build Coastguard Worker this.notifyObservers(); 245*cf78ab8cSAndroid Build Coastguard Worker } 246*cf78ab8cSAndroid Build Coastguard Worker 247*cf78ab8cSAndroid Build Coastguard Worker registerObserver(elem: Notifiable) { 248*cf78ab8cSAndroid Build Coastguard Worker this.observers.push(elem); 249*cf78ab8cSAndroid Build Coastguard Worker elem.onNotify(this.simulationInfo); 250*cf78ab8cSAndroid Build Coastguard Worker } 251*cf78ab8cSAndroid Build Coastguard Worker 252*cf78ab8cSAndroid Build Coastguard Worker removeObserver(elem: Notifiable) { 253*cf78ab8cSAndroid Build Coastguard Worker const index = this.observers.indexOf(elem); 254*cf78ab8cSAndroid Build Coastguard Worker this.observers.splice(index, 1); 255*cf78ab8cSAndroid Build Coastguard Worker } 256*cf78ab8cSAndroid Build Coastguard Worker 257*cf78ab8cSAndroid Build Coastguard Worker notifyObservers() { 258*cf78ab8cSAndroid Build Coastguard Worker for (const observer of this.observers) { 259*cf78ab8cSAndroid Build Coastguard Worker observer.onNotify(this.simulationInfo); 260*cf78ab8cSAndroid Build Coastguard Worker } 261*cf78ab8cSAndroid Build Coastguard Worker } 262*cf78ab8cSAndroid Build Coastguard Worker 263*cf78ab8cSAndroid Build Coastguard Worker getDeviceList() { 264*cf78ab8cSAndroid Build Coastguard Worker return this.simulationInfo.devices; 265*cf78ab8cSAndroid Build Coastguard Worker } 266*cf78ab8cSAndroid Build Coastguard Worker} 267*cf78ab8cSAndroid Build Coastguard Worker 268*cf78ab8cSAndroid Build Coastguard Worker/** Subscribed observers must register itself to the simulationState */ 269*cf78ab8cSAndroid Build Coastguard Workerexport const simulationState = new SimulationState(); 270*cf78ab8cSAndroid Build Coastguard Worker 271*cf78ab8cSAndroid Build Coastguard Workerasync function subscribeCaptures() { 272*cf78ab8cSAndroid Build Coastguard Worker const delay = (ms: number) => new Promise(res => setTimeout(res, ms)); 273*cf78ab8cSAndroid Build Coastguard Worker while (true) { 274*cf78ab8cSAndroid Build Coastguard Worker await simulationState.invokeListCaptures(); 275*cf78ab8cSAndroid Build Coastguard Worker await simulationState.invokeGetDevice(); 276*cf78ab8cSAndroid Build Coastguard Worker await delay(1000); 277*cf78ab8cSAndroid Build Coastguard Worker } 278*cf78ab8cSAndroid Build Coastguard Worker} 279*cf78ab8cSAndroid Build Coastguard Worker 280*cf78ab8cSAndroid Build Coastguard Workerasync function subscribeDevices() { 281*cf78ab8cSAndroid Build Coastguard Worker await simulationState.invokeGetDevice(); 282*cf78ab8cSAndroid Build Coastguard Worker while (true) { 283*cf78ab8cSAndroid Build Coastguard Worker const jsonBody = JSON.stringify({ 284*cf78ab8cSAndroid Build Coastguard Worker lastModified: simulationState.getLastModified(), 285*cf78ab8cSAndroid Build Coastguard Worker }); 286*cf78ab8cSAndroid Build Coastguard Worker await fetch(DEVICES_URL, { 287*cf78ab8cSAndroid Build Coastguard Worker method: 'SUBSCRIBE', 288*cf78ab8cSAndroid Build Coastguard Worker headers: { 289*cf78ab8cSAndroid Build Coastguard Worker 'Content-Type': 'application/json', 290*cf78ab8cSAndroid Build Coastguard Worker 'Content-Length': jsonBody.length.toString(), 291*cf78ab8cSAndroid Build Coastguard Worker }, 292*cf78ab8cSAndroid Build Coastguard Worker body: jsonBody, 293*cf78ab8cSAndroid Build Coastguard Worker }) 294*cf78ab8cSAndroid Build Coastguard Worker .then(response => response.json()) 295*cf78ab8cSAndroid Build Coastguard Worker .then(data => { 296*cf78ab8cSAndroid Build Coastguard Worker simulationState.fetchDevice(data.devices); 297*cf78ab8cSAndroid Build Coastguard Worker simulationState.updateLastModified(data.lastModified); 298*cf78ab8cSAndroid Build Coastguard Worker }) 299*cf78ab8cSAndroid Build Coastguard Worker .catch(error => { 300*cf78ab8cSAndroid Build Coastguard Worker // eslint-disable-next-line 301*cf78ab8cSAndroid Build Coastguard Worker console.log('Cannot connect to netsim web server', error); 302*cf78ab8cSAndroid Build Coastguard Worker }); 303*cf78ab8cSAndroid Build Coastguard Worker } 304*cf78ab8cSAndroid Build Coastguard Worker} 305*cf78ab8cSAndroid Build Coastguard Worker 306*cf78ab8cSAndroid Build Coastguard WorkersubscribeCaptures(); 307*cf78ab8cSAndroid Build Coastguard WorkersubscribeDevices(); 308