Skip to content

Commit de2a9cb

Browse files
committed
fixes: review comments
1 parent 9d52d9d commit de2a9cb

File tree

4 files changed

+107
-84
lines changed

4 files changed

+107
-84
lines changed

src/firmware/installPybricksDialog/InstallPybricksDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ function getHubTypeNameFromMetadata(metadata: FirmwareMetadata | undefined): str
9292
case HubType.EssentialHub:
9393
return 'SPIKE Essential hub';
9494
case HubType.EV3:
95-
return 'MINDSTORMS EV3 Hub';
95+
return 'MINDSTORMS EV3 hub';
9696
default:
9797
return '?';
9898
}
@@ -462,7 +462,7 @@ export const InstallPybricksDialog: React.FunctionComponent = () => {
462462
);
463463
const dispatch = useDispatch();
464464
const [hubName, setHubName] = useState('');
465-
const [licenseAccepted, setLicenseAccepted] = useState(!false);
465+
const [licenseAccepted, setLicenseAccepted] = useState(false);
466466
const [hubType] = useHubPickerSelectedHub();
467467
const { firmwareData, firmwareError } = useFirmware(hubType);
468468
const [customFirmwareZip, setCustomFirmwareZip] = useState<File>();

src/firmware/installPybricksDialog/hooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function useFirmware(hubType: Hub): State {
7878
const [state, dispatch] = useReducer(fetchReducer, initialState);
7979

8080
useEffect(() => {
81-
// Do nothing if the url is not given
81+
// Raise error if the url is not given, to show that something is wrong instead of a misleading intermediate state.
8282
if (!url) {
8383
dispatch({ type: 'error', payload: new Error('No URL for this hub type') });
8484
return;

src/firmware/sagas.ts

Lines changed: 99 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,6 @@ function* loadFirmware(
230230

231231
const firmwareBase = yield* call(() => reader.readFirmwareBase());
232232
const metadata = yield* call(() => reader.readMetadata());
233-
let checksum: number | undefined = undefined;
234-
let checksum_size = 4;
235-
let firmware: Uint8Array | undefined = undefined;
236-
let firmwareView: DataView | undefined = undefined;
237233

238234
// v1.x allows appending main.py to firmware, later versions do not
239235
if (metadataIsV100(metadata) || metadataIsV110(metadata)) {
@@ -285,8 +281,8 @@ function* loadFirmware(
285281
mpy.data.length +
286282
fmod(-mpy.data.length, 4);
287283

288-
firmware = new Uint8Array(checksumOffset + 4);
289-
firmwareView = new DataView(firmware.buffer);
284+
const firmware = new Uint8Array(checksumOffset + 4);
285+
const firmwareView = new DataView(firmware.buffer);
290286

291287
if (firmware.length > metadata['max-firmware-size']) {
292288
// FIXME: we should return error/throw instead
@@ -312,7 +308,7 @@ function* loadFirmware(
312308
}
313309
}
314310

315-
checksum = (function () {
311+
const checksum = (function () {
316312
switch (metadata['checksum-type']) {
317313
case 'sum':
318314
return sumComplement32(
@@ -348,14 +344,14 @@ function* loadFirmware(
348344
}
349345

350346
// v2.x supports setting the checksum size to 0 to indicate no checksum
351-
else {
347+
{
352348
const metadataV2 = metadata as FirmwareMetadataV200;
353-
checksum_size = metadataV2['checksum-size'];
349+
const checksum_size = metadataV2['checksum-size'];
354350

355-
// TODO: v2.x supports setting checksum size, prior it was a fixed 4 bytes
356-
firmware = new Uint8Array(firmwareBase.length + checksum_size);
357-
firmwareView = new DataView(firmware.buffer);
358-
checksum = (function () {
351+
// TODO: v2.x supports setting checksum size, prior it was a fixed 4 bytes - check if it 4 or checksum_size
352+
const firmware = new Uint8Array(firmwareBase.length + 4);
353+
const firmwareView = new DataView(firmware.buffer);
354+
const checksum = (function () {
359355
switch (metadata['checksum-type']) {
360356
case 'sum':
361357
console.log('computing sum');
@@ -381,28 +377,28 @@ function* loadFirmware(
381377
metadataV2['hub-name-offset'],
382378
);
383379
}
384-
}
385380

386-
if (checksum === undefined) {
387-
// FIXME: we should return error/throw instead
388-
yield* put(
389-
didFailToFinish(
390-
FailToFinishReasonType.BadMetadata,
391-
'checksum-type',
392-
MetadataProblem.NotSupported,
393-
),
394-
);
395-
yield* disconnectAndCancel();
381+
if (checksum === undefined) {
382+
// FIXME: we should return error/throw instead
383+
yield* put(
384+
didFailToFinish(
385+
FailToFinishReasonType.BadMetadata,
386+
'checksum-type',
387+
MetadataProblem.NotSupported,
388+
),
389+
);
390+
yield* disconnectAndCancel();
396391

397-
// istanbul ignore next: needed for typescript flow
398-
throw new Error('unreachable');
399-
}
392+
// istanbul ignore next: needed for typescript flow
393+
throw new Error('unreachable');
394+
}
400395

401-
if (checksum_size) {
402-
firmwareView.setUint32(firmwareBase.length, checksum, true);
403-
}
396+
if (checksum_size) {
397+
firmwareView.setUint32(firmwareBase.length, checksum, true);
398+
}
404399

405-
return { firmware, deviceId: metadata['device-id'] };
400+
return { firmware, deviceId: metadata['device-id'] };
401+
}
406402
}
407403

408404
/**
@@ -417,6 +413,7 @@ function* handleFlashFirmware(action: ReturnType<typeof flashFirmware>): Generat
417413
if (action.data !== null) {
418414
({ firmware, deviceId } = yield* loadFirmware(action.data, action.hubName));
419415
}
416+
console.log('>>> deviceId', deviceId, 'firmware', firmware, action.data);
420417

421418
yield* put(connect());
422419
const connectResult = yield* take([didConnect, didFailToConnect]);
@@ -604,6 +601,7 @@ function* handleFlashFirmware(action: ReturnType<typeof flashFirmware>): Generat
604601
}
605602

606603
if (flash.checksum !== runningChecksum) {
604+
console.error('>>>> checksum', flash.checksum, runningChecksum);
607605
// istanbul ignore next
608606
if (process.env.NODE_ENV !== 'test') {
609607
console.error(
@@ -941,16 +939,28 @@ function* handleInstallPybricks(): Generator {
941939
);
942940
}
943941
break;
944-
case 'usb-ev3':
945-
{
942+
case 'usb-ev3': {
943+
try {
946944
const { firmware } = yield* loadFirmware(
947945
accepted.firmwareZip,
948946
accepted.hubName,
949947
);
950948

951949
yield* put(firmwareFlashEV3(firmware.buffer as ArrayBuffer));
950+
} catch (err) {
951+
// istanbul ignore if
952+
if (process.env.NODE_ENV !== 'test') {
953+
console.error(err);
954+
}
955+
956+
yield* put(
957+
alertsShowAlert('alerts', 'unexpectedError', {
958+
error: ensureError(err),
959+
}),
960+
);
952961
}
953962
break;
963+
}
954964
}
955965
}
956966

@@ -1021,7 +1031,9 @@ function* handleRestoreOfficialDfu(
10211031
}
10221032

10231033
yield* put(
1024-
alertsShowAlert('alerts', 'unexpectedError', { error: ensureError(err) }),
1034+
alertsShowAlert('alerts', 'unexpectedError', {
1035+
error: ensureError(err),
1036+
}),
10251037
);
10261038
yield* put(firmwareDidFailToRestoreOfficialDfu());
10271039
}
@@ -1137,6 +1149,9 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
11371149
);
11381150

11391151
// Create a task that forwards matching actions to our channel
1152+
// Sending the command and setting up the listener are not atomic,
1153+
// so we might receive the reply before we start listening. To handle
1154+
// this, we keep listening until we find the matching reply.
11401155
const forwardTask = yield* fork(function* () {
11411156
while (true) {
11421157
const action = yield* take(firmwareDidReceiveEV3Reply);
@@ -1147,59 +1162,62 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
11471162
}
11481163
});
11491164

1150-
// Send the command
1151-
const dataBuffer = new Uint8Array((payload?.byteLength ?? 0) + 6);
1152-
const data = new DataView(dataBuffer.buffer);
1153-
1154-
data.setInt16(0, (payload?.byteLength ?? 0) + 4, true);
1155-
data.setInt16(2, 0, true); // TODO: reply number
1156-
data.setUint8(4, 0x01); // system command w/ reply
1157-
data.setUint8(5, command);
1158-
if (payload) {
1159-
dataBuffer.set(payload, 6);
1160-
}
1165+
try {
1166+
// Send the command
1167+
const dataBuffer = new Uint8Array((payload?.byteLength ?? 0) + 6);
1168+
const data = new DataView(dataBuffer.buffer);
1169+
1170+
data.setInt16(0, (payload?.byteLength ?? 0) + 4, true);
1171+
data.setInt16(2, 0, true); // TODO: reply number
1172+
data.setUint8(4, 0x01); // system command w/ reply
1173+
data.setUint8(5, command);
1174+
if (payload) {
1175+
dataBuffer.set(payload, 6);
1176+
}
11611177

1162-
const [, sendError] = yield* call(() => maybe(hidDevice.sendReport(0, data)));
1178+
const [, sendError] = yield* call(() =>
1179+
maybe(hidDevice.sendReport(0, data)),
1180+
);
11631181

1164-
if (sendError) {
1165-
yield* cancel(forwardTask);
1166-
return [undefined, sendError];
1167-
}
1182+
if (sendError) {
1183+
return [undefined, sendError];
1184+
}
11681185

1169-
const { reply, timeout } = yield* race({
1170-
reply: take(replyChannel),
1171-
timeout: delay(15000),
1172-
});
1186+
const { reply, timeout } = yield* race({
1187+
reply: take(replyChannel),
1188+
timeout: delay(5000),
1189+
});
11731190

1174-
// Always clean up
1175-
yield* cancel(forwardTask);
1176-
yield* call(() => replyChannel.close());
1191+
if (timeout) {
1192+
return [undefined, new Error('Timeout waiting for EV3 reply')];
1193+
}
11771194

1178-
if (timeout) {
1179-
return [undefined, new Error('Timeout waiting for EV3 reply')];
1180-
}
1195+
defined(reply);
11811196

1182-
defined(reply);
1197+
if (reply.replyCommand !== command) {
1198+
return [
1199+
undefined,
1200+
new Error(
1201+
`EV3 reply command mismatch: expected ${command}, got ${reply.replyCommand}`,
1202+
),
1203+
];
1204+
}
11831205

1184-
if (reply.replyCommand !== command) {
1185-
return [
1186-
undefined,
1187-
new Error(
1188-
`EV3 reply command mismatch: expected ${command}, got ${reply.replyCommand}`,
1189-
),
1190-
];
1191-
}
1206+
if (reply.status !== 0) {
1207+
return [
1208+
undefined,
1209+
new Error(
1210+
`EV3 reply status error: ${reply.status} for command ${command}`,
1211+
),
1212+
];
1213+
}
11921214

1193-
if (reply.status !== 0) {
1194-
return [
1195-
undefined,
1196-
new Error(
1197-
`EV3 reply status error: ${reply.status} for command ${command}`,
1198-
),
1199-
];
1215+
return [new DataView(reply.payload), undefined];
1216+
} finally {
1217+
// Always clean up
1218+
yield* cancel(forwardTask);
1219+
yield* call(() => replyChannel.close());
12001220
}
1201-
1202-
return [new DataView(reply.payload), undefined];
12031221
}
12041222

12051223
const [version, versionError] = yield* sendCommand(0xf6); // get version
@@ -1373,7 +1391,9 @@ function* handleRestoreOfficialEV3(
13731391
}
13741392

13751393
yield* put(
1376-
alertsShowAlert('alerts', 'unexpectedError', { error: ensureError(err) }),
1394+
alertsShowAlert('alerts', 'unexpectedError', {
1395+
error: ensureError(err),
1396+
}),
13771397
);
13781398
yield* put(firmwareDidFailToRestoreOfficialEV3());
13791399
}

src/usb/sagas.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,11 @@ function* handleUsbConnectPybricks(hotPlugDevice?: USBDevice): Generator {
173173
),
174174
);
175175

176-
// FIXME: Implement error handling here
177-
console.error(selectErr);
176+
// TODO: show error message to user here
177+
console.error('Failed to select USB device configuration:', selectErr);
178+
yield* put(usbDidFailToConnectPybricks());
179+
yield* cleanup();
180+
return;
178181
}
179182

180183
assert(

0 commit comments

Comments
 (0)