Skip to content

Commit cf9c70b

Browse files
Distinguish full flash in progress dialog. (#255)
Behaviour should match V2. Closes #254
1 parent 796da85 commit cf9c70b

File tree

6 files changed

+84
-22
lines changed

6 files changed

+84
-22
lines changed

lang/en.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,15 @@
217217
},
218218
"flashing-code": {
219219
"defaultMessage": "Flashing code",
220-
"description": "Progress dialog text telling user that code is being flashed"
220+
"description": "Progress dialog text for partial flashes (quick)"
221+
},
222+
"flashing-full-flash-detail": {
223+
"defaultMessage": "Initial flash might take longer, subsequent flashes will be quicker.",
224+
"description": "Message shown after the title in the progress dialog for a full flash"
225+
},
226+
"flashing-micropython": {
227+
"defaultMessage": "Flashing MicroPython",
228+
"description": "Progress dialog text for full flashes (slower)"
221229
},
222230
"font-size": {
223231
"defaultMessage": "Font size",

src/common/ProgressDialog.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import {
1111
ModalOverlay,
1212
} from "@chakra-ui/modal";
1313
import { Progress } from "@chakra-ui/progress";
14+
import { VStack } from "@chakra-ui/react";
1415
import { ReactNode } from "react";
1516

1617
const doNothing = () => {};
1718

1819
export interface ProgressDialogParameters {
1920
header: ReactNode;
21+
body?: ReactNode;
2022
progress: number | undefined;
2123
}
2224

@@ -27,14 +29,28 @@ interface ProgressDialogProps extends ProgressDialogParameters {
2729
/**
2830
* A progress dialog used for the flashing process.
2931
*/
30-
const ProgressDialog = ({ header, progress }: ProgressDialogProps) => {
32+
const ProgressDialog = ({ header, body, progress }: ProgressDialogProps) => {
3133
return (
32-
<Modal isOpen={progress !== undefined} onClose={doNothing} isCentered>
34+
<Modal
35+
isOpen={progress !== undefined}
36+
onClose={doNothing}
37+
isCentered
38+
size={body ? "xl" : "md"}
39+
>
3340
<ModalOverlay />
3441
<ModalContent>
3542
<ModalHeader>{header}</ModalHeader>
3643
<ModalBody>
37-
<Progress value={progress! * 100} mb={3} />
44+
<VStack
45+
spacing={4}
46+
mb={3}
47+
width="100%"
48+
justifyContent="stretch"
49+
alignItems="flex-start"
50+
>
51+
{body}
52+
<Progress value={progress! * 100} width="100%" />
53+
</VStack>
3854
</ModalBody>
3955
</ModalContent>
4056
</Modal>

src/device/device.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,12 @@ export interface DeviceConnection extends EventEmitter {
151151
partial: boolean;
152152
/**
153153
* A progress callback. Called with undefined when the process is complete or has failed.
154+
*
155+
* Requesting a partial flash doesn't guarantee one is performed. Partial flashes are avoided
156+
* if too many blocks have changed and failed partial flashes are retried as full flashes.
157+
* The partial parameter reports the flash type currently in progress.
154158
*/
155-
progress: (percentage: number | undefined) => void;
159+
progress: (percentage: number | undefined, partial: boolean) => void;
156160
}
157161
): Promise<void>;
158162

@@ -348,7 +352,7 @@ export class MicrobitWebUSBConnection
348352
dataSource: FlashDataSource,
349353
options: {
350354
partial: boolean;
351-
progress: (percentage: number | undefined) => void;
355+
progress: (percentage: number | undefined, partial: boolean) => void;
352356
}
353357
): Promise<void> {
354358
this.log("Stopping serial before flash");
@@ -364,14 +368,15 @@ export class MicrobitWebUSBConnection
364368

365369
const boardId = this.connection.boardSerialInfo.id;
366370
const flashing = new PartialFlashing(this.connection, this.logging);
371+
let wasPartial: boolean = false;
367372
try {
368373
if (partial) {
369-
await flashing.flashAsync(boardId, dataSource, progress);
374+
wasPartial = await flashing.flashAsync(boardId, dataSource, progress);
370375
} else {
371376
await flashing.fullFlashAsync(boardId, dataSource, progress);
372377
}
373378
} finally {
374-
progress(undefined);
379+
progress(undefined, wasPartial);
375380

376381
if (this.disconnectAfterFlash) {
377382
this.log("Disconnecting after flash due to tab visibility");

src/device/partial-flashing.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import {
5858
read32FromUInt8Array,
5959
} from "./partial-flashing-utils";
6060

61-
type ProgressCallback = (n: number, fullFlash?: boolean) => void;
61+
type ProgressCallback = (n: number, partial: boolean) => void;
6262

6363
// Source code for binaries in can be found at https://github.com/microsoft/pxt-microbit/blob/dec5b8ce72d5c2b4b0b20aafefce7474a6f0c7b2/external/sha/source/main.c
6464
// Drawn from https://github.com/microsoft/pxt-microbit/blob/dec5b8ce72d5c2b4b0b20aafefce7474a6f0c7b2/editor/extension.tsx#L243
@@ -191,10 +191,10 @@ export class PartialFlashing {
191191
) {
192192
this.log("Partial flash");
193193
for (let i = 0; i < pages.length; ++i) {
194-
updateProgress(i / pages.length);
194+
updateProgress(i / pages.length, true);
195195
await this.partialFlashPageAsync(pages[i], pages[i + 1], i);
196196
}
197-
updateProgress(1);
197+
updateProgress(1, true);
198198
}
199199

200200
// Flash the micro:bit's ROM with the provided image by only copying over the pages that differ.
@@ -204,7 +204,7 @@ export class PartialFlashing {
204204
boardId: BoardId,
205205
dataSource: FlashDataSource,
206206
updateProgress: ProgressCallback
207-
) {
207+
): Promise<boolean> {
208208
const flashBytes = await dataSource.partialFlashData(boardId);
209209
const checksums = await this.getFlashChecksumsAsync();
210210
await this.dapwrapper.writeBlockAsync(loadAddr, flashPageBIN);
@@ -213,21 +213,26 @@ export class PartialFlashing {
213213
this.log("Total pages: " + totalPages);
214214
aligned = onlyChanged(aligned, checksums, this.dapwrapper.pageSize);
215215
this.log("Changed pages: " + aligned.length);
216+
let partial: boolean | undefined;
216217
if (aligned.length > totalPages / 2) {
217218
try {
218219
await this.fullFlashAsync(boardId, dataSource, updateProgress);
220+
partial = false;
219221
} catch (e) {
220222
this.log(e);
221223
this.log("Full flash failed, attempting partial flash.");
222224
await this.partialFlashCoreAsync(aligned, updateProgress);
225+
partial = true;
223226
}
224227
} else {
225228
try {
226229
await this.partialFlashCoreAsync(aligned, updateProgress);
230+
partial = true;
227231
} catch (e) {
228232
this.log(e);
229233
this.log("Partial flash failed, attempting full flash.");
230234
await this.fullFlashAsync(boardId, dataSource, updateProgress);
235+
partial = false;
231236
}
232237
}
233238

@@ -237,6 +242,7 @@ export class PartialFlashing {
237242
// Allow errors on resetting, user can always manually reset if necessary.
238243
}
239244
this.log("Flashing complete");
245+
return partial;
240246
}
241247

242248
// Perform full flash of micro:bit's ROM using daplink.
@@ -247,10 +253,10 @@ export class PartialFlashing {
247253
) {
248254
this.log("Full flash");
249255

250-
const flashProgressListener = (progress: number) => {
251-
updateProgress(progress, true);
256+
const fullFlashProgress = (progress: number) => {
257+
updateProgress(progress, false);
252258
};
253-
this.dapwrapper.daplink.on(DAPLink.EVENT_PROGRESS, flashProgressListener);
259+
this.dapwrapper.daplink.on(DAPLink.EVENT_PROGRESS, fullFlashProgress);
254260
try {
255261
const data = await dataSource.fullFlashData(boardId);
256262
await this.dapwrapper.transport.open();
@@ -262,7 +268,7 @@ export class PartialFlashing {
262268
} finally {
263269
this.dapwrapper.daplink.removeListener(
264270
DAPLink.EVENT_PROGRESS,
265-
flashProgressListener
271+
fullFlashProgress
266272
);
267273
}
268274
}
@@ -273,7 +279,7 @@ export class PartialFlashing {
273279
boardId: BoardId,
274280
dataSource: FlashDataSource,
275281
updateProgress: ProgressCallback
276-
) {
282+
): Promise<boolean> {
277283
let resetPromise = (async () => {
278284
// Reset micro:bit to ensure interface responds correctly.
279285
this.log("Begin reset");
@@ -291,7 +297,11 @@ export class PartialFlashing {
291297
await withTimeout(resetPromise, 1000);
292298

293299
this.log("Begin flashing");
294-
await this.partialFlashAsync(boardId, dataSource, updateProgress);
300+
return await this.partialFlashAsync(
301+
boardId,
302+
dataSource,
303+
updateProgress
304+
);
295305
} catch (e) {
296306
if (e instanceof TimeoutError) {
297307
this.log("Resetting micro:bit timed out");
@@ -301,6 +311,7 @@ export class PartialFlashing {
301311
message: "flash-failed/attempting-full-flash",
302312
});
303313
await this.fullFlashAsync(boardId, dataSource, updateProgress);
314+
return false;
304315
} else {
305316
throw e;
306317
}

src/messages/en.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,18 @@
429429
"value": "Flashing code"
430430
}
431431
],
432+
"flashing-full-flash-detail": [
433+
{
434+
"type": 0,
435+
"value": "Initial flash might take longer, subsequent flashes will be quicker."
436+
}
437+
],
438+
"flashing-micropython": [
439+
{
440+
"type": 0,
441+
"value": "Flashing MicroPython"
442+
}
443+
],
432444
"font-size": [
433445
{
434446
"type": 0,

src/project/project-actions.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* SPDX-License-Identifier: MIT
55
*/
66
import { Link, List, ListItem } from "@chakra-ui/layout";
7-
import { VStack } from "@chakra-ui/react";
7+
import { Text, VStack } from "@chakra-ui/react";
88
import { saveAs } from "file-saver";
99
import { ReactNode } from "react";
10-
import { IntlShape } from "react-intl";
10+
import { FormattedMessage, IntlShape } from "react-intl";
1111
import { InputDialogBody } from "../common/InputDialog";
1212
import { ActionFeedback } from "../common/use-action-feedback";
1313
import { Dialogs } from "../common/use-dialogs";
@@ -285,9 +285,19 @@ export class ProjectActions {
285285
}
286286

287287
try {
288-
const progress = (value: number | undefined) => {
288+
const flashingCode = this.intl.formatMessage({ id: "flashing-code" });
289+
const flashingMicroPython = this.intl.formatMessage({
290+
id: "flashing-micropython",
291+
});
292+
const firstFlashNotice = (
293+
<Text fontSize="lg">
294+
<FormattedMessage id="flashing-full-flash-detail" />
295+
</Text>
296+
);
297+
const progress = (value: number | undefined, partial: boolean) => {
289298
this.dialogs.progress({
290-
header: this.intl.formatMessage({ id: "flashing-code" }),
299+
header: partial ? flashingCode : flashingMicroPython,
300+
body: partial ? undefined : firstFlashNotice,
291301
progress: value,
292302
});
293303
};

0 commit comments

Comments
 (0)