|
| 1 | +import {BehaviorSubject} from 'rxjs'; |
| 2 | + |
| 3 | +import { |
| 4 | + takeUntil, |
| 5 | + filter, |
| 6 | + first |
| 7 | +} from 'rxjs/operators'; |
| 8 | + |
1 | 9 | const versionsMap = {
|
2 | 10 | wifi101: {
|
3 | 11 | latestVersion: 'WINC1500 19.5.4',
|
@@ -29,182 +37,212 @@ const signaturesEnum = Object.freeze({
|
29 | 37 |
|
30 | 38 | export default class FirmwareUpdater {
|
31 | 39 | 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 | + |
32 | 50 | this.Daemon = Daemon;
|
33 | 51 | this.Daemon.downloadingDone.subscribe(() => {
|
34 | 52 | this.FWUToolStatus = FWUToolStatusEnum.OK;
|
35 | 53 | });
|
36 | 54 |
|
37 | 55 | 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)); |
38 | 70 | }
|
39 | 71 |
|
40 | 72 | 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 | + } |
44 | 149 |
|
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 => { |
46 | 173 | 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; |
65 | 174 | 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(); |
68 | 177 | break;
|
69 | 178 | case 'Done':
|
70 |
| - firmwareInfoMessagesSubscription.unsubscribe(); |
71 |
| - return resolve(versionsList); |
| 179 | + this.updating.next({ status: this.updateStatusEnum.DONE}); |
| 180 | + updateFirmwareMessagesSubscription.unsubscribe(); |
72 | 181 | break;
|
73 | 182 | default:
|
74 | 183 | break;
|
75 | 184 | }
|
76 | 185 | }
|
77 |
| - |
78 |
| - if (this.FWUToolStatus !== FWUToolStatusEnum.OK) { |
79 |
| - return reject(`Can't get firmware info: couldn't find firmware updater tool`) |
80 |
| - } |
81 | 186 |
|
82 |
| - firmwareInfoMessagesSubscription = this.Daemon.appMessages.subscribe(message => { |
| 187 | + updateFirmwareMessagesSubscription = this.Daemon.appMessages.subscribe(message => { |
83 | 188 | 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} `; |
85 | 202 | }
|
86 | 203 | });
|
| 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 | + |
87 | 214 | const data = {
|
88 | 215 | board: boardId,
|
89 | 216 | 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: '', |
92 | 219 | extra: {
|
93 | 220 | auth: {
|
94 |
| - password: boardId |
95 |
| - } |
| 221 | + password: loaderPath |
| 222 | + }, |
| 223 | + verbose: true, |
| 224 | + params_verbose: `${versionsMap[wifiModule].latestVersionPath} ${addresses}` // eslint-disable-line camelcase |
96 | 225 | },
|
97 |
| - filename: 'ListFirmwareVersionsInfo.bin' |
| 226 | + signature: isUsingAvrdude ? signaturesEnum.UPLOAD_FIRMWARE_AVRDUDE : signaturesEnum.UPLOAD_FIRMWARE_BOSSAC, |
| 227 | + filename: 'CheckFirmwareVersion.bin' |
98 | 228 | };
|
99 |
| - |
100 |
| - return fetch(`${this.Daemon.pluginURL}/upload`, { |
| 229 | + |
| 230 | + fetch(`${this.Daemon.pluginURL}/upload`, { |
101 | 231 | method: 'POST',
|
102 | 232 | headers: {
|
103 | 233 | 'Content-Type': 'text/plain; charset=utf-8'
|
104 | 234 | },
|
105 | 235 | body: JSON.stringify(data)
|
106 | 236 | }).then(response => {
|
107 | 237 | 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; |
109 | 240 | }
|
110 | 241 | }).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; |
207 | 244 | });
|
208 |
| - }) |
| 245 | + }); |
| 246 | + this.getFirmwareInfo(boardId, port, wifiModule); |
209 | 247 | }
|
210 | 248 | }
|
0 commit comments