Skip to content

Commit d63b22a

Browse files
author
Stefania
authored
Merge pull request #2 from bcmi-labs/chromeapp_agent_porting
Chromeapp agent porting
2 parents 7e2e812 + 6e15b0d commit d63b22a

File tree

6 files changed

+440
-164
lines changed

6 files changed

+440
-164
lines changed

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"extends": "airbnb-base",
1919
"env": {
2020
"browser": true,
21-
"jest": true
21+
"jest": true,
22+
"webextensions": true
2223
}
2324
}

src/chromeAppDaemon.js

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
/*
2+
* This file is part of create-plugin-communication.
3+
*
4+
* Copyright 2018 Arduino AG (http://www.arduino.cc/)
5+
*
6+
* create-plugin-communication is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
import { Deferred } from './readMessages';
31+
32+
let port = null;
33+
let polling;
34+
35+
let uploading = false;
36+
37+
const callbacks = {
38+
ports: [],
39+
program: [],
40+
serial: []
41+
};
42+
43+
const promises = {
44+
open: [],
45+
close: []
46+
};
47+
48+
let disconnected = true;
49+
50+
let connectCb = null;
51+
let disconnectCb = null;
52+
let errorCb = null;
53+
let boardsCb = null;
54+
55+
// Sends the data to all the registered callbacks
56+
const callback = (name, data) => {
57+
if (!callbacks || !callbacks[name] || !callbacks[name].length) {
58+
return;
59+
}
60+
callbacks[name].forEach(cb => {
61+
cb(data);
62+
});
63+
};
64+
65+
const onMessage = (msg) => {
66+
if (msg.version) {
67+
if (!polling) {
68+
polling = setInterval(() => {
69+
if (!uploading) {
70+
port.postMessage({
71+
command: 'listPorts'
72+
});
73+
}
74+
}, 1500);
75+
}
76+
disconnected = false;
77+
connectCb();
78+
}
79+
else if (msg.supportedBoards) {
80+
boardsCb(msg.supportedBoards);
81+
}
82+
else if (msg.ports) {
83+
const ports = msg.ports.map(p => ({
84+
Name: p.name,
85+
SerialNumber: p.serialNumber,
86+
IsOpen: p.isOpen,
87+
VendorID: p.vendorId,
88+
ProductID: p.productId
89+
}));
90+
91+
callback('ports', ports);
92+
}
93+
else if (msg.uploadStatus) {
94+
if (msg.uploadStatus === 'success') {
95+
uploading = false;
96+
callback('program', { done: true });
97+
}
98+
else if (msg.uploadStatus === 'error') {
99+
uploading = false;
100+
101+
const errorMessage = msg.message;
102+
103+
callback('program', { done: true, err: `${errorMessage}\n` });
104+
105+
if (errorMessage === 'Free trial expired') {
106+
errorCb('freeTrialExpired');
107+
}
108+
else if (errorMessage === 'Unlicensed education user') {
109+
errorCb('unlicensedEducationUser');
110+
}
111+
}
112+
else if (msg.uploadStatus === 'message') {
113+
callback('program', { done: false, msg: `${msg.message}\n` });
114+
}
115+
}
116+
else if (msg.portOpenStatus) {
117+
if (msg.portOpenStatus === 'success') {
118+
if (promises.open.length) {
119+
const promise = promises.open.shift();
120+
promise.resolve(true);
121+
}
122+
}
123+
else if (promises.open.length) {
124+
const promise = promises.open.shift();
125+
promise.reject(msg.message);
126+
}
127+
}
128+
else if (msg.portCloseStatus) {
129+
if (msg.portCloseStatus === 'success') {
130+
if (promises.close.length) {
131+
const promise = promises.close.shift();
132+
promise.resolve(true);
133+
}
134+
}
135+
else if (promises.close.length) {
136+
const promise = promises.close.shift();
137+
138+
promise.reject(msg.message);
139+
}
140+
}
141+
else if (msg.serialData) {
142+
callback('serial', { data: msg.serialData });
143+
}
144+
};
145+
146+
const onChromeDisconnect = () => {
147+
disconnected = true;
148+
149+
if (polling) {
150+
clearInterval(polling);
151+
polling = undefined;
152+
}
153+
disconnectCb();
154+
};
155+
156+
const connect = (chromeExtensionId) => {
157+
if ((port === null || disconnected) && chrome.runtime) {
158+
port = chrome.runtime.connect(chromeExtensionId);
159+
port.onMessage.addListener(onMessage);
160+
port.onDisconnect.addListener(onChromeDisconnect);
161+
}
162+
else {
163+
errorCb('chromeExtensionNotFound');
164+
}
165+
};
166+
167+
const perform = (action, data) => {
168+
const deferred = new Deferred();
169+
170+
if (uploading) {
171+
deferred.reject();
172+
173+
return deferred.promise;
174+
}
175+
176+
if (action === 'req_downloadtool') {
177+
// Chrome app doesn't support downloading tools, just fail
178+
deferred.resolve();
179+
}
180+
else if (action === 'req_serial_monitor_open') {
181+
port.postMessage({
182+
command: 'openPort',
183+
data: {
184+
name: data.com_name,
185+
baudrate: data.baudrate
186+
}
187+
});
188+
189+
promises.open = deferred;
190+
}
191+
else if (action === 'req_serial_monitor_close') {
192+
port.postMessage({
193+
command: 'closePort',
194+
data: {
195+
name: data.com_name
196+
}
197+
});
198+
199+
promises.close = deferred;
200+
}
201+
else if (action === 'req_serial_monitor_write') {
202+
port.postMessage({
203+
command: 'writePort',
204+
data: {
205+
name: data.com_name,
206+
data: data.data
207+
}
208+
});
209+
}
210+
211+
return deferred.promise;
212+
};
213+
214+
// Perform an upload via http on the daemon
215+
// file = {name: 'filename', data: 'base64data'}
216+
// target = {
217+
// board: "name of the board",
218+
// port: "port of the board",
219+
// auth_user: "Optional user to use as authentication",
220+
// auth_pass: "Optional pass to use as authentication"
221+
// network: true or false
222+
// }
223+
// data = {
224+
// commandline: "commandline to execute",
225+
// signature: "signature of the commandline",
226+
// files: [
227+
// {name: "Name of a file to upload on the device", data: 'base64data'}
228+
// ],
229+
// options: {}
230+
// }
231+
// cb = callback function executing everytime a packet of data arrives
232+
const upload = (target, data, cb) => {
233+
uploading = true;
234+
callbacks.program = [cb];
235+
236+
// At least one file to upload
237+
if (data.files.length === 0) {
238+
uploading = false;
239+
callback('program', { done: true, err: 'You need at least one file to upload' });
240+
return;
241+
}
242+
243+
// Main file
244+
const file = data.files[0];
245+
file.name = file.name.split('/');
246+
file.name = file.name[file.name.length - 1];
247+
248+
window.oauth.token().then(token => {
249+
port.postMessage({
250+
command: 'upload',
251+
data: {
252+
filename: file.name,
253+
data: file.data,
254+
board: target.board,
255+
port: target.port,
256+
commandline: data.commandline,
257+
token: token.token
258+
}
259+
});
260+
});
261+
};
262+
263+
const onPortsUpdate = (cb) => {
264+
if (typeof cb === 'function') {
265+
callbacks.ports.push(cb);
266+
}
267+
};
268+
269+
const onSerialOutput = (cb) => {
270+
if (typeof cb === 'function') {
271+
callbacks.serial.push(cb);
272+
}
273+
};
274+
275+
const onConnect = (cb) => {
276+
if (typeof cb === 'function') {
277+
connectCb = cb;
278+
}
279+
};
280+
281+
const onDisconnect = (cb) => {
282+
if (typeof cb === 'function') {
283+
disconnectCb = cb;
284+
}
285+
};
286+
287+
const onError = (cb) => {
288+
if (typeof cb === 'function') {
289+
errorCb = cb;
290+
}
291+
};
292+
293+
const onSupportedBoards = (cb) => {
294+
if (typeof cb === 'function') {
295+
boardsCb = cb;
296+
}
297+
};
298+
299+
const DaemonCromeApp = (params) => {
300+
301+
if (params) {
302+
onPortsUpdate(params.onPortsUpdate);
303+
onSerialOutput(params.onSerialOutput);
304+
onConnect(params.onConnect);
305+
onDisconnect(params.onDisconnect);
306+
onError(params.onError);
307+
onSupportedBoards(params.onSupportedBoards);
308+
}
309+
310+
return {
311+
// Connect the client with the daemon over extension port
312+
connect,
313+
perform,
314+
// Return daemon connection status
315+
connected: () => !disconnected,
316+
stopPlugin() {
317+
// Not supported by the chrome app
318+
},
319+
upload,
320+
stopUpload() {
321+
// Not supported by the chrome app
322+
},
323+
onDisconnect,
324+
onConnect,
325+
onPortsUpdate,
326+
onSerialOutput,
327+
onError,
328+
onSupportedBoards
329+
};
330+
};
331+
332+
export default DaemonCromeApp;

src/daemonChromeApp.js

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
* the GNU General Public License.
2828
*/
2929

30-
import DaemonAgent from './daemonAgent';
31-
import DaemonChromeApp from './daemonChromeApp';
30+
import socketDaemon from './socketDaemon';
31+
import chromeAppDaemon from './chromeAppDaemon';
3232

33-
const Daemon = window.navigator.userAgent.indexOf(' CrOS ') !== -1 ? DaemonChromeApp : DaemonAgent;
33+
const Daemon = window.navigator.userAgent.indexOf(' CrOS ') !== -1 ? chromeAppDaemon : socketDaemon;
3434

3535
export default Daemon;

0 commit comments

Comments
 (0)