Skip to content

Commit a47e48a

Browse files
author
Alberto Iannaccone
committed
use rxjs for firmware updater
1 parent 0d9f4b9 commit a47e48a

File tree

2 files changed

+184
-140
lines changed

2 files changed

+184
-140
lines changed

demo/app.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,17 @@ const handleBootloaderMode = (e, port) => {
5454

5555
const handleUpdateFirmware = (e, boardId, port, wifiModule) => {
5656
e.preventDefault();
57-
firmwareUpdater.updateFirmware(boardId, port, wifiModule).then(response => {
57+
if (![firmwareUpdater.updateStatusEnum.NOPE, firmwareUpdater.updateStatusEnum.DONE, firmwareUpdater.updateStatusEnum.ERROR].includes(firmwareUpdater.updating.getValue().status)) {
58+
return;
59+
}
60+
firmwareUpdater.updateFirmware(boardId, port, wifiModule);
61+
firmwareUpdater.updatingDone.subscribe(() => {
5862
console.log('Firmware updated successfully!');
59-
}).catch(reason => {
63+
});
64+
65+
firmwareUpdater.updatingError.subscribe(update => {
6066
console.log('Something went wrong when trying to update the firmware');
61-
console.error(reason);
67+
console.error(update.err);
6268
});
6369
};
6470

src/firmware-updater.js

Lines changed: 175 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import {BehaviorSubject} from 'rxjs';
2+
3+
import {
4+
takeUntil,
5+
filter,
6+
first
7+
} from 'rxjs/operators';
8+
19
const versionsMap = {
210
wifi101: {
311
latestVersion: 'WINC1500 19.5.4',
@@ -29,182 +37,212 @@ const signaturesEnum = Object.freeze({
2937

3038
export default class FirmwareUpdater {
3139
constructor(Daemon) {
40+
this.updateStatusEnum = Object.freeze({
41+
NOPE: 'NOPE',
42+
STARTED: 'STARTED',
43+
GETTING_INFO: 'GETTING_INFO',
44+
GOT_INFO: 'GOT_INFO',
45+
UPLOADING: 'UPLOADING',
46+
DONE: 'DONE',
47+
ERROR: 'ERROR'
48+
});
49+
3250
this.Daemon = Daemon;
3351
this.Daemon.downloadingDone.subscribe(() => {
3452
this.FWUToolStatus = FWUToolStatusEnum.OK;
3553
});
3654

3755
this.FWUToolStatus = FWUToolStatusEnum.NOPE;
56+
this.updating = new BehaviorSubject({ status: this.updateStatusEnum.NOPE });
57+
58+
this.updatingDone = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.DONE))
59+
.pipe(first())
60+
.pipe(takeUntil(this.updating.pipe(filter(update => update.status === this.updateStatusEnum.ERROR))));
61+
62+
this.updatingError = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.ERROR))
63+
.pipe(first())
64+
.pipe(takeUntil(this.updatingDone));
65+
66+
this.gotFWInfo = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.GOT_INFO))
67+
.pipe(first())
68+
.pipe(takeUntil(this.updatingDone))
69+
.pipe(takeUntil(this.updatingError));
3870
}
3971

4072
getFirmwareInfo(boardId, port, wifiModule) {
41-
return new Promise((resolve, reject) => {
42-
let versionsList = [];
43-
let firmwareInfoMessagesSubscription;
73+
this.updating.next({ status: this.updateStatusEnum.GETTING_INFO });
74+
let versionsList = [];
75+
let firmwareInfoMessagesSubscription;
76+
77+
const handleFirmwareInfoMessage = message => {
78+
switch (message.ProgrammerStatus) {
79+
case 'Starting':
80+
break;
81+
case 'Busy':
82+
if (message.Msg.indexOf('Flashing with command:') >= 0) {
83+
return;
84+
}
85+
const versions = JSON.parse(message.Msg);
86+
Object.keys(versions).forEach(v => {
87+
if (versions[v][0].IsLoader) {
88+
versionsMap[wifiModule].loaderPath = versions[v][0].Path;
89+
}
90+
else {
91+
versionsList = [...versionsList, ...versions[v]];
92+
}
93+
});
94+
const latestVersion = versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion)
95+
versionsMap[wifiModule].latestVersionPath = latestVersion.Path;
96+
break;
97+
case 'Error':
98+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Couldn't get firmware info: ${message}` })
99+
firmwareInfoMessagesSubscription.unsubscribe();
100+
break;
101+
case 'Done':
102+
firmwareInfoMessagesSubscription.unsubscribe();
103+
this.updating.next({ status: this.updateStatusEnum.GOT_INFO, versionsList })
104+
break;
105+
default:
106+
break;
107+
}
108+
}
109+
110+
if (this.FWUToolStatus !== FWUToolStatusEnum.OK) {
111+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't get firmware info: couldn't find firmware updater tool` });
112+
return;
113+
}
114+
115+
firmwareInfoMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
116+
if (message.ProgrammerStatus) {
117+
handleFirmwareInfoMessage(message);
118+
}
119+
});
120+
const data = {
121+
board: boardId,
122+
port,
123+
commandline: `"{runtime.tools.fwupdater.path}/updater" -get_available_for {network.password}`,
124+
signature: signaturesEnum.GET_FIRMWARE_INFO,
125+
extra: {
126+
auth: {
127+
password: boardId
128+
}
129+
},
130+
filename: 'ListFirmwareVersionsInfo.bin'
131+
};
132+
133+
return fetch(`${this.Daemon.pluginURL}/upload`, {
134+
method: 'POST',
135+
headers: {
136+
'Content-Type': 'text/plain; charset=utf-8'
137+
},
138+
body: JSON.stringify(data)
139+
}).then(response => {
140+
if (!response.ok) {
141+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Error fetching ${this.Daemon.pluginURL}/upload` });
142+
return;
143+
}
144+
}).catch(reason => {
145+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Coudln't list firmware versions info.` });
146+
return ;
147+
});
148+
}
44149

45-
const handleFirmwareInfoMessage = message => {
150+
/*
151+
wifiModule can be either 'wifiNina' or 'wifi101'
152+
*/
153+
updateFirmware(boardId, port, wifiModule) {
154+
this.updating.next({ status: this.updateStatusEnum.STARTED });
155+
if (!port) {
156+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: no port selected.` });
157+
return;
158+
}
159+
this.gotFWInfo.subscribe(state => {
160+
if (!versionsMap[wifiModule] && !versionsMap[wifiModule].latestVersion) {
161+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: couldn't find module '${wifiModule}'` });
162+
return;
163+
}
164+
const latestVersion = state.versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion);
165+
if (!latestVersion) {
166+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: couldn't find version '${versionsMap[wifiModule].latestVersion}' for module '${versionsMap[wifiModule]}'` });
167+
return;
168+
}
169+
170+
let updateFirmwareMessagesSubscription;
171+
172+
const handleFirmwareUpdateMessage = message => {
46173
switch (message.ProgrammerStatus) {
47-
case 'Starting':
48-
break;
49-
case 'Busy':
50-
if (message.Msg.indexOf('Flashing with command:') >= 0) {
51-
return;
52-
}
53-
const versions = JSON.parse(message.Msg);
54-
Object.keys(versions).forEach(v => {
55-
if (versions[v][0].IsLoader) {
56-
versionsMap[wifiModule].loaderPath = versions[v][0].Path;
57-
}
58-
else {
59-
versionsList = [...versionsList, ...versions[v]];
60-
}
61-
});
62-
const latestVersion = versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion)
63-
versionsMap[wifiModule].latestVersionPath = latestVersion.Path;
64-
break;
65174
case 'Error':
66-
return reject(`Couldn't get firmware info: ${message}`)
67-
firmwareInfoMessagesSubscription.unsubscribe();
175+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: ${message.Msg}`});
176+
updateFirmwareMessagesSubscription.unsubscribe();
68177
break;
69178
case 'Done':
70-
firmwareInfoMessagesSubscription.unsubscribe();
71-
return resolve(versionsList);
179+
this.updating.next({ status: this.updateStatusEnum.DONE});
180+
updateFirmwareMessagesSubscription.unsubscribe();
72181
break;
73182
default:
74183
break;
75184
}
76185
}
77-
78-
if (this.FWUToolStatus !== FWUToolStatusEnum.OK) {
79-
return reject(`Can't get firmware info: couldn't find firmware updater tool`)
80-
}
81186

82-
firmwareInfoMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
187+
updateFirmwareMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
83188
if (message.ProgrammerStatus) {
84-
handleFirmwareInfoMessage(message);
189+
handleFirmwareUpdateMessage(message);
190+
}
191+
});
192+
193+
let addresses = '';
194+
const rootCertificates = [{
195+
domain: 'arduino.cc',
196+
port: 443
197+
}];
198+
199+
rootCertificates.forEach(address => {
200+
if (address.domain && address.port) {
201+
addresses += `-address ${address.domain}:${address.port} `;
85202
}
86203
});
204+
205+
const isUsingAvrdude = boardId === 'uno2018';
206+
const programmer = isUsingAvrdude ? '{runtime.tools.avrdude}/bin/avrdude' : '{runtime.tools.bossac}/bossac';
207+
208+
const loaderPath = versionsMap[wifiModule].loaderPath;
209+
if (!loaderPath) {
210+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: invalid loaderPath` });
211+
return;
212+
}
213+
87214
const data = {
88215
board: boardId,
89216
port,
90-
commandline: `"{runtime.tools.fwupdater.path}/updater" -get_available_for {network.password}`,
91-
signature: signaturesEnum.GET_FIRMWARE_INFO,
217+
commandline: `"{runtime.tools.fwupdater.path}/updater" -flasher {network.password} -firmware {upload.verbose} -port {serial.port} -restore_binary "{build.path}/{build.project_name}.bin" -programmer ${programmer}`,
218+
hex: '',
92219
extra: {
93220
auth: {
94-
password: boardId
95-
}
221+
password: loaderPath
222+
},
223+
verbose: true,
224+
params_verbose: `${versionsMap[wifiModule].latestVersionPath} ${addresses}` // eslint-disable-line camelcase
96225
},
97-
filename: 'ListFirmwareVersionsInfo.bin'
226+
signature: isUsingAvrdude ? signaturesEnum.UPLOAD_FIRMWARE_AVRDUDE : signaturesEnum.UPLOAD_FIRMWARE_BOSSAC,
227+
filename: 'CheckFirmwareVersion.bin'
98228
};
99-
100-
return fetch(`${this.Daemon.pluginURL}/upload`, {
229+
230+
fetch(`${this.Daemon.pluginURL}/upload`, {
101231
method: 'POST',
102232
headers: {
103233
'Content-Type': 'text/plain; charset=utf-8'
104234
},
105235
body: JSON.stringify(data)
106236
}).then(response => {
107237
if (!response.ok) {
108-
return reject(`Error fetching ${this.Daemon.pluginURL}/upload`);
238+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: error fetching ${this.Daemon.pluginURL}/upload` });
239+
return;
109240
}
110241
}).catch(reason => {
111-
return reject(`Coudln't list firmware versions info.`);
112-
});
113-
})
114-
}
115-
116-
/*
117-
wifiModule can be either 'wifiNina' or 'wifi101'
118-
*/
119-
updateFirmware(boardId, port, wifiModule) {
120-
return new Promise((resolve, reject) => {
121-
if (!port) {
122-
return reject(`Can't update Firmware: no port selected.`);
123-
}
124-
this.getFirmwareInfo(boardId, port, wifiModule).then(versionsList => {
125-
if (!versionsMap[wifiModule] && !versionsMap[wifiModule].latestVersion) {
126-
return reject(`Can't update Firmware: couldn't find module '${wifiModule}'`);
127-
}
128-
const latestVersion = versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion);
129-
if (!latestVersion) {
130-
return reject(`Can't update Firmware: couldn't find version '${versionsMap[wifiModule].latestVersion}' for module '${versionsMap[wifiModule]}'`);
131-
}
132-
133-
let updateFirmwareMessagesSubscription;
134-
135-
const handleFirmwareUpdateMessage = message => {
136-
switch (message.ProgrammerStatus) {
137-
case 'Error':
138-
return reject(`Can't update Firmware: ${message.Msg}`)
139-
updateFirmwareMessagesSubscription.unsubscribe();
140-
break;
141-
case 'Done':
142-
return resolve();
143-
updateFirmwareMessagesSubscription.unsubscribe();
144-
break;
145-
default:
146-
break;
147-
}
148-
}
149-
150-
updateFirmwareMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
151-
if (message.ProgrammerStatus) {
152-
handleFirmwareUpdateMessage(message);
153-
}
154-
});
155-
156-
let addresses = '';
157-
const rootCertificates = [{
158-
domain: 'arduino.cc',
159-
port: 443
160-
}];
161-
162-
rootCertificates.forEach(address => {
163-
if (address.domain && address.port) {
164-
addresses += `-address ${address.domain}:${address.port} `;
165-
}
166-
});
167-
168-
const isUsingAvrdude = boardId === 'uno2018';
169-
const programmer = isUsingAvrdude ? '{runtime.tools.avrdude}/bin/avrdude' : '{runtime.tools.bossac}/bossac';
170-
171-
const loaderPath = versionsMap[wifiModule].loaderPath;
172-
if (!loaderPath) {
173-
return reject(`Can't update Firmware: invalid loaderPath`);
174-
}
175-
176-
const data = {
177-
board: boardId,
178-
port,
179-
commandline: `"{runtime.tools.fwupdater.path}/updater" -flasher {network.password} -firmware {upload.verbose} -port {serial.port} -restore_binary "{build.path}/{build.project_name}.bin" -programmer ${programmer}`,
180-
hex: '',
181-
extra: {
182-
auth: {
183-
password: loaderPath
184-
},
185-
verbose: true,
186-
params_verbose: `${versionsMap[wifiModule].latestVersionPath} ${addresses}` // eslint-disable-line camelcase
187-
},
188-
signature: isUsingAvrdude ? signaturesEnum.UPLOAD_FIRMWARE_AVRDUDE : signaturesEnum.UPLOAD_FIRMWARE_BOSSAC,
189-
filename: 'CheckFirmwareVersion.bin'
190-
};
191-
192-
fetch(`${this.Daemon.pluginURL}/upload`, {
193-
method: 'POST',
194-
headers: {
195-
'Content-Type': 'text/plain; charset=utf-8'
196-
},
197-
body: JSON.stringify(data)
198-
}).then(response => {
199-
if (!response.ok) {
200-
return reject(`Can't update Firmware: Error fetching ${this.Daemon.pluginURL}/upload`);
201-
}
202-
}).catch(reason => {
203-
return reject(`Can't update Firmware: ${reason}`)
204-
});
205-
}).catch(reason => {
206-
return reject(reason)
242+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: ${reason}` });
243+
return;
207244
});
208-
})
245+
});
246+
this.getFirmwareInfo(boardId, port, wifiModule);
209247
}
210248
}

0 commit comments

Comments
 (0)