11import {
2- filter , takeUntil
2+ distinctUntilChanged , filter , takeUntil
33} from 'rxjs/operators' ;
4+
45import Daemon from './daemon' ;
56
67/**
78 * WARNING: the WebSerialDaemon with support for the Web Serial API is still in an alpha version.
89 * At the moment it doesn't implement all the features available in the Chrome App Deamon
910 * Use at your own risk.
1011 *
11- * The `uploader ` parameter in the constructor is the component which is
12+ * The `channel ` parameter in the constructor is the component which is
1213 * used to interact with the Web Serial API.
13- * It must provide a method `upload`.
14+ *
15+ * It must provide a `postMessage` method, similarly to the object created with `chrome.runtime.connect` in
16+ * the `chrome-app-daemon.js` module, which is used to send messages to interact with the Web Serial API.
1417 */
1518export default class WebSerialDaemon extends Daemon {
16- constructor ( boardsUrl , uploader ) {
19+ constructor ( boardsUrl , channel ) {
1720 super ( boardsUrl ) ;
21+
1822 this . port = null ;
19- this . agentFound . next ( true ) ;
2023 this . channelOpenStatus . next ( true ) ;
21- this . uploader = uploader ;
24+ this . channel = channel ; // channel is injected from the client app
25+ this . connectedPorts = [ ] ;
26+
27+ this . init ( ) ;
28+ }
29+
30+ init ( ) {
31+ this . agentFound
32+ . pipe ( distinctUntilChanged ( ) )
33+ . subscribe ( found => {
34+ if ( ! found ) {
35+ // Set channelOpen false for the first time
36+ if ( this . channelOpen . getValue ( ) === null ) {
37+ this . channelOpen . next ( false ) ;
38+ }
2239
23- this . _populateSupportedBoards ( ) ;
40+ this . connectToChannel ( ) ;
41+ }
42+ else {
43+ this . openChannel ( ( ) => this . channel . postMessage ( {
44+ command : 'listPorts'
45+ } ) ) ;
46+ }
47+ } ) ;
2448 }
2549
26- _populateSupportedBoards ( ) {
27- const supportedBoards = this . uploader . getSupportedBoards ( ) ;
28- this . appMessages . next ( { supportedBoards } ) ;
50+ connectToChannel ( ) {
51+ this . channel . onMessage ( message => {
52+ if ( message . version ) {
53+ this . agentInfo = message . version ;
54+ this . agentFound . next ( true ) ;
55+ this . channelOpen . next ( true ) ;
56+ }
57+ else {
58+ this . appMessages . next ( message ) ;
59+ }
60+ } ) ;
61+ this . channel . onDisconnect ( ( ) => {
62+ this . channelOpen . next ( false ) ;
63+ this . agentFound . next ( false ) ;
64+ } ) ;
2965 }
3066
31- // eslint-disable-next-line class-methods-use-this
32- closeSerialMonitor ( ) {
33- // TODO: it's a NO OP at the moment
67+ _appConnect ( ) {
68+ this . channel . onMessage ( message => {
69+ if ( message . version ) {
70+ this . agentInfo = {
71+ version : message . version ,
72+ os : 'ChromeOS'
73+ } ;
74+ this . agentFound . next ( true ) ;
75+ this . channelOpen . next ( true ) ;
76+ }
77+ else {
78+ this . appMessages . next ( message ) ;
79+ }
80+ } ) ;
81+ this . channel . onDisconnect ( ( ) => {
82+ this . channelOpen . next ( false ) ;
83+ this . agentFound . next ( false ) ;
84+ } ) ;
3485 }
3586
3687 handleAppMessage ( message ) {
3788 if ( message . ports ) {
89+ this . handleListMessage ( message ) ;
90+ }
91+ else if ( message . supportedBoards ) {
92+ this . supportedBoards . next ( message . supportedBoards ) ;
93+ }
94+ if ( message . serialData ) {
95+ this . serialMonitorMessages . next ( message . serialData ) ;
96+ }
97+
98+ if ( message . uploadStatus ) {
99+ this . handleUploadMessage ( message ) ;
100+ }
101+
102+ if ( message . err ) {
103+ this . uploading . next ( { status : this . UPLOAD_ERROR , err : message . Err } ) ;
104+ }
105+ }
106+
107+ handleUploadMessage ( message ) {
108+ if ( this . uploading . getValue ( ) . status !== this . UPLOAD_IN_PROGRESS ) {
109+ return ;
110+ }
111+ switch ( message . uploadStatus ) {
112+ case 'message' :
113+ this . uploading . next ( {
114+ status : this . UPLOAD_IN_PROGRESS ,
115+ msg : message . message ,
116+ operation : message . operation ,
117+ port : message . port
118+ } ) ;
119+ break ;
120+ case 'error' :
121+ this . uploading . next ( { status : this . UPLOAD_ERROR , err : message . message } ) ;
122+ break ;
123+ case 'success' :
124+ this . uploading . next (
125+ {
126+ status : this . UPLOAD_DONE ,
127+ msg : message . message ,
128+ operation : message . operation ,
129+ port : message . port
130+ }
131+ ) ;
132+ break ;
133+
134+ default :
135+ this . uploading . next ( { status : this . UPLOAD_IN_PROGRESS } ) ;
136+ }
137+ }
138+
139+ handleListMessage ( message ) {
140+ const lastDevices = this . devicesList . getValue ( ) ;
141+ if ( ! Daemon . devicesListAreEquals ( lastDevices . serial , message . ports ) ) {
38142 this . devicesList . next ( {
39- serial : message . ports ,
143+ serial : message . ports
144+ . map ( port => ( {
145+ Name : port . name ,
146+ SerialNumber : port . serialNumber ,
147+ IsOpen : port . isOpen ,
148+ VendorID : port . vendorId ,
149+ ProductID : port . productId
150+ } ) ) ,
40151 network : [ ]
41152 } ) ;
42- // this.handleListMessage(message);
43- }
44-
45- if ( message . supportedBoards ) {
46- this . supportedBoards . next ( message . supportedBoards ) ;
47153 }
48154 }
49155
50156 /**
51157 * Send 'close' command to all the available serial ports
52158 */
53- // eslint-disable-next-line class-methods-use-this
54159 closeAllPorts ( ) {
55- console . log ( 'should be closing serial ports here' ) ;
160+ const devices = this . devicesList . getValue ( ) . serial ;
161+ if ( Array . isArray ( devices ) ) {
162+ devices . forEach ( device => {
163+ this . channel . postMessage ( {
164+ command : 'closePort' ,
165+ data : {
166+ name : device . Name
167+ }
168+ } ) ;
169+ } ) ;
170+ }
56171 }
57172
58173 /**
59174 * Request serial port open
60175 * @param {string } port the port name
61176 */
62- openSerialMonitor ( port ) {
177+ openSerialMonitor ( port , baudrate ) {
63178 if ( this . serialMonitorOpened . getValue ( ) ) {
64179 return ;
65180 }
66- const serialPort = this . devicesList . getValue ( ) . serial [ 0 ] ; // .find(p => p.Name === port);
181+ const serialPort = this . devicesList . getValue ( ) . serial . find ( p => p . Name === port ) ;
67182 if ( ! serialPort ) {
68183 return this . serialMonitorError . next ( `Can't find port ${ port } ` ) ;
69184 }
@@ -77,30 +192,90 @@ export default class WebSerialDaemon extends Daemon {
77192 this . serialMonitorError . next ( `Failed to open serial ${ port } ` ) ;
78193 }
79194 } ) ;
80-
195+ this . channel . postMessage ( {
196+ command : 'openPort' ,
197+ data : {
198+ name : port ,
199+ baudrate
200+ }
201+ } ) ;
81202 }
82203
83- cdcReset ( { fqbn } ) {
84- return this . uploader . cdcReset ( { fqbn } )
85- . then ( ( ) => {
86- this . uploading . next ( { status : this . CDC_RESET_DONE , msg : 'Touch operation succeeded' } ) ;
87- } )
88- . catch ( error => {
89- this . notifyUploadError ( error . message ) ;
204+ closeSerialMonitor ( port ) {
205+ if ( ! this . serialMonitorOpened . getValue ( ) ) {
206+ return ;
207+ }
208+ const serialPort = this . devicesList . getValue ( ) . serial . find ( p => p . Name === port ) ;
209+ if ( ! serialPort ) {
210+ return this . serialMonitorError . next ( `Can't find port ${ port } ` ) ;
211+ }
212+ this . appMessages
213+ . pipe ( takeUntil ( this . serialMonitorOpened . pipe ( filter ( open => ! open ) ) ) )
214+ . subscribe ( message => {
215+ if ( message . portCloseStatus === 'success' ) {
216+ this . serialMonitorOpened . next ( false ) ;
217+ }
218+ if ( message . portCloseStatus === 'error' ) {
219+ this . serialMonitorError . next ( `Failed to close serial ${ port } ` ) ;
220+ }
90221 } ) ;
222+ this . channel . postMessage ( {
223+ command : 'closePort' ,
224+ data : {
225+ name : port
226+ }
227+ } ) ;
228+ }
229+
230+ cdcReset ( { fqbn, port } ) {
231+ this . uploading . next ( { status : this . UPLOAD_IN_PROGRESS , msg : 'CDC reset started' } ) ;
232+ this . channel . postMessage ( {
233+ command : 'cdcReset' ,
234+ data : {
235+ fqbn,
236+ port
237+ }
238+ } ) ;
239+ }
240+
241+ connectToSerialDevice ( { fqbn } ) {
242+ this . uploading . next ( { status : this . UPLOAD_IN_PROGRESS , msg : 'Board selection started' } ) ;
243+ this . channel . postMessage ( {
244+ command : 'connectToSerial' ,
245+ data : {
246+ fqbn
247+ }
248+ } ) ;
91249 }
92250
93251 /**
94252 * @param {object } uploadPayload
95253 * TODO: document param's shape
96254 */
97- _upload ( uploadPayload ) {
98- return this . uploader . upload ( uploadPayload )
99- . then ( ( ) => {
100- this . uploading . next ( { status : this . UPLOAD_DONE , msg : 'Sketch uploaded' } ) ;
101- } )
102- . catch ( error => {
103- this . notifyUploadError ( error . message ) ;
255+ _upload ( uploadPayload , uploadCommandInfo ) {
256+ const {
257+ board, port, commandline, data, pid, vid
258+ } = uploadPayload ;
259+ const extrafiles = uploadCommandInfo && uploadCommandInfo . files && Array . isArray ( uploadCommandInfo . files ) ? uploadCommandInfo . files : [ ] ;
260+ try {
261+ window . oauth . getAccessToken ( ) . then ( token => {
262+ this . channel . postMessage ( {
263+ command : 'upload' ,
264+ data : {
265+ board,
266+ port,
267+ commandline,
268+ data,
269+ token : token . token ,
270+ extrafiles,
271+ pid,
272+ vid
273+ }
274+ } ) ;
104275 } ) ;
276+ }
277+ catch ( err ) {
278+ this . uploading . next ( { status : this . UPLOAD_ERROR , err : 'you need to be logged in on a Create site to upload by Chrome App' } ) ;
279+ }
105280 }
106281}
0 commit comments