mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
add id to bootloader request actions
This will be used to match didRequest to a requst to avoid issues with messages being queued concurrently
This commit is contained in:
committed by
David Lechner
parent
0d57338d71
commit
ed5bd4728a
+134
-17
@@ -1,5 +1,6 @@
|
||||
import { Action } from 'redux';
|
||||
import { Command, HubType, ProtectionLevel, Result } from '../protocols/bootloader';
|
||||
import { createCountFunc } from '../utils/iter';
|
||||
|
||||
/**
|
||||
* Bootloader BLE connection actions.
|
||||
@@ -128,68 +129,151 @@ export enum BootloaderRequestActionType {
|
||||
Disconnect = 'bootloader.action.request.disconnect',
|
||||
}
|
||||
|
||||
export type BootloaderEraseRequestAction = Action<BootloaderRequestActionType.Erase>;
|
||||
const nextRequestId = createCountFunc();
|
||||
|
||||
export function eraseRequest(): BootloaderEraseRequestAction {
|
||||
return { type: BootloaderRequestActionType.Erase };
|
||||
interface BaseBootloaderRequestAction<T extends BootloaderRequestActionType>
|
||||
extends Action<T> {
|
||||
/**
|
||||
* Unique identifier for this action.
|
||||
*/
|
||||
id: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action that requests to erase the flash memory.
|
||||
*/
|
||||
export type BootloaderEraseRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.Erase
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to erase the flash memory.
|
||||
*/
|
||||
export function eraseRequest(): BootloaderEraseRequestAction {
|
||||
return { type: BootloaderRequestActionType.Erase, id: nextRequestId() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Action that requests to program the flash memory.
|
||||
*/
|
||||
export interface BootloaderProgramRequestAction
|
||||
extends Action<BootloaderRequestActionType.Program> {
|
||||
extends BaseBootloaderRequestAction<BootloaderRequestActionType.Program> {
|
||||
address: number;
|
||||
payload: ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request to program the flash memory.
|
||||
* @param address The starting address in the flash memory.
|
||||
* @param payload The bytes to write (max 14 bytes!)
|
||||
*/
|
||||
export function programRequest(
|
||||
address: number,
|
||||
payload: ArrayBuffer,
|
||||
): BootloaderProgramRequestAction {
|
||||
return { type: BootloaderRequestActionType.Program, address, payload };
|
||||
return {
|
||||
type: BootloaderRequestActionType.Program,
|
||||
id: nextRequestId(),
|
||||
address,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export type BootloaderRebootRequestAction = Action<BootloaderRequestActionType.Reboot>;
|
||||
/**
|
||||
* Action that requests to reboot the hub.
|
||||
*/
|
||||
export type BootloaderRebootRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.Reboot
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to reboot the hub.
|
||||
*/
|
||||
export function rebootRequest(): BootloaderRebootRequestAction {
|
||||
return { type: BootloaderRequestActionType.Reboot };
|
||||
return { type: BootloaderRequestActionType.Reboot, id: nextRequestId() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Action that requests to initialize the firmware flashing process.
|
||||
*/
|
||||
export interface BootloaderInitRequestAction
|
||||
extends Action<BootloaderRequestActionType.Init> {
|
||||
extends BaseBootloaderRequestAction<BootloaderRequestActionType.Init> {
|
||||
firmwareSize: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request to initialize the firmware flashing process.
|
||||
* @param firmwareSize The size of the firmware to written to flash memory.
|
||||
*/
|
||||
export function initRequest(firmwareSize: number): BootloaderInitRequestAction {
|
||||
return { type: BootloaderRequestActionType.Init, firmwareSize };
|
||||
return {
|
||||
type: BootloaderRequestActionType.Init,
|
||||
id: nextRequestId(),
|
||||
firmwareSize,
|
||||
};
|
||||
}
|
||||
|
||||
export type BootloaderInfoRequestAction = Action<BootloaderRequestActionType.Info>;
|
||||
/**
|
||||
* Action that requests information about the hub.
|
||||
*/
|
||||
export type BootloaderInfoRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.Info
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to get information about the hub.
|
||||
*/
|
||||
export function infoRequest(): BootloaderInfoRequestAction {
|
||||
return { type: BootloaderRequestActionType.Info };
|
||||
return { type: BootloaderRequestActionType.Info, id: nextRequestId() };
|
||||
}
|
||||
|
||||
export type BootloaderChecksumRequestAction = Action<
|
||||
/**
|
||||
* Action to get the checksum of the bytes that have been written to flash
|
||||
* so far.
|
||||
*/
|
||||
export type BootloaderChecksumRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.Checksum
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to get the checksum of the bytes that have been written
|
||||
* to flash so far.
|
||||
*/
|
||||
export function checksumRequest(): BootloaderChecksumRequestAction {
|
||||
return { type: BootloaderRequestActionType.Checksum };
|
||||
return { type: BootloaderRequestActionType.Checksum, id: nextRequestId() };
|
||||
}
|
||||
|
||||
export type BootloaderStateRequestAction = Action<BootloaderRequestActionType.State>;
|
||||
/**
|
||||
* Action that requests the bootloader flash memory protection state.
|
||||
*/
|
||||
export type BootloaderStateRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.State
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to get the bootloader flash memory protection state.
|
||||
*/
|
||||
export function stateRequest(): BootloaderStateRequestAction {
|
||||
return { type: BootloaderRequestActionType.State };
|
||||
return { type: BootloaderRequestActionType.State, id: nextRequestId() };
|
||||
}
|
||||
|
||||
export type BootloaderDisconnectRequestAction = Action<
|
||||
/**
|
||||
* Action that requests to disconnect the hub.
|
||||
*/
|
||||
export type BootloaderDisconnectRequestAction = BaseBootloaderRequestAction<
|
||||
BootloaderRequestActionType.Disconnect
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a request to disconnect the hub.
|
||||
*/
|
||||
export function disconnectRequest(): BootloaderDisconnectRequestAction {
|
||||
return { type: BootloaderRequestActionType.Disconnect };
|
||||
return { type: BootloaderRequestActionType.Disconnect, id: nextRequestId() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Common type for all bootloader requests.
|
||||
*/
|
||||
export type BootloaderRequestAction =
|
||||
| BootloaderEraseRequestAction
|
||||
| BootloaderProgramRequestAction
|
||||
@@ -200,6 +284,39 @@ export type BootloaderRequestAction =
|
||||
| BootloaderStateRequestAction
|
||||
| BootloaderDisconnectRequestAction;
|
||||
|
||||
/**
|
||||
* Action type for bootloader did request action.
|
||||
*/
|
||||
export type BootloaderDidRequestType = 'bootloader.action.did.request';
|
||||
|
||||
/**
|
||||
* Action type for bootloader did request action.
|
||||
*/
|
||||
export const BootloaderDidRequestType = 'bootloader.action.did.request';
|
||||
|
||||
/**
|
||||
* Action that indicates a request was sent or failed to send.
|
||||
*/
|
||||
export interface BootloaderDidRequestAction extends Action<BootloaderDidRequestType> {
|
||||
/**
|
||||
* The unique identifier of the action.
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* The error on failure or undefined on success.
|
||||
*/
|
||||
err?: Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an action that indicates a request was sent or failed to send.
|
||||
* @param id The unique identifier of the action.
|
||||
* @param err The error message on failure or undefined on success.
|
||||
*/
|
||||
export function didRequest(id: number, err?: Error): BootloaderDidRequestAction {
|
||||
return { type: BootloaderDidRequestType, id, err };
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootloader response actions for receiving responses from the connection.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Action } from 'redux';
|
||||
import { createCountFunc } from '../utils/iter';
|
||||
|
||||
export enum NotificationActionType {
|
||||
/**
|
||||
@@ -42,7 +43,7 @@ export interface NotificationRemoveAction
|
||||
|
||||
export type NotificationAction = NotificationAddAction | NotificationRemoveAction;
|
||||
|
||||
let nextId = 0;
|
||||
const nextId = createCountFunc();
|
||||
|
||||
/**
|
||||
* Action to add a notification to the list.
|
||||
@@ -55,7 +56,7 @@ export function add(
|
||||
message: string,
|
||||
helpUrl?: string,
|
||||
): NotificationAddAction {
|
||||
return { type: NotificationActionType.Add, id: nextId++, level, message, helpUrl };
|
||||
return { type: NotificationActionType.Add, id: nextId(), level, message, helpUrl };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
BootloaderChecksumRequestAction,
|
||||
BootloaderChecksumResponseAction,
|
||||
BootloaderConnectionActionType,
|
||||
BootloaderDidRequestType,
|
||||
BootloaderDisconnectRequestAction,
|
||||
BootloaderEraseRequestAction,
|
||||
BootloaderEraseResponseAction,
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
eraseRequest,
|
||||
} from '../actions/bootloader';
|
||||
import { Command, HubType, ProtectionLevel, Result } from '../protocols/bootloader';
|
||||
import { createCountFunc } from '../utils/iter';
|
||||
import bootloader from './bootloader';
|
||||
|
||||
describe('message encoder', () => {
|
||||
@@ -165,23 +167,37 @@ describe('message encoder', () => {
|
||||
channel.put(eraseRequest());
|
||||
channel.put(eraseRequest());
|
||||
|
||||
// but only one didSend action meaning only the first one completed
|
||||
// but only two didSend action meaning only the first two completed
|
||||
channel.put(didSend());
|
||||
channel.put(didSend());
|
||||
|
||||
task.cancel();
|
||||
await task.toPromise();
|
||||
|
||||
// so only 2 messages were actually sent and 2 are still waiting for
|
||||
// the second one to complete
|
||||
expect(dispatched.length).toEqual(2);
|
||||
// So only 3 requests were actually sent and two didRequests were
|
||||
// dispatched (making 5 total dispatches). The last request is still
|
||||
// buffered and has not been dispatched.
|
||||
expect(dispatched.length).toEqual(5);
|
||||
|
||||
// every other action is the "send" action
|
||||
const message = new Uint8Array([Command.EraseFlash]);
|
||||
for (const d of dispatched) {
|
||||
expect(d).toEqual({
|
||||
for (let i = 0; i < dispatched.length; i += 2) {
|
||||
expect(dispatched[i]).toEqual({
|
||||
type: BootloaderConnectionActionType.Send,
|
||||
data: message,
|
||||
withResponse: false,
|
||||
});
|
||||
}
|
||||
|
||||
// and the interleaving actions are "did request" actions
|
||||
const nextId = createCountFunc();
|
||||
for (let i = 1; i < dispatched.length; i += 2) {
|
||||
expect(dispatched[i]).toEqual({
|
||||
type: BootloaderDidRequestType,
|
||||
id: nextId(),
|
||||
err: undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
BootloaderConnectionDidConnectAction,
|
||||
BootloaderConnectionDidErrorAction,
|
||||
BootloaderConnectionDidReceiveAction,
|
||||
BootloaderConnectionDidSendAction,
|
||||
BootloaderEraseResponseAction,
|
||||
BootloaderErrorResponseAction,
|
||||
BootloaderFlashFirmwareAction,
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
checksumRequest,
|
||||
checksumResponse,
|
||||
connect,
|
||||
didRequest,
|
||||
eraseRequest,
|
||||
eraseResponse,
|
||||
errorResponse,
|
||||
@@ -120,7 +122,10 @@ function* encodeRequest(): Generator {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield take(BootloaderConnectionActionType.DidSend);
|
||||
const sent = (yield take(
|
||||
BootloaderConnectionActionType.DidSend,
|
||||
)) as BootloaderConnectionDidSendAction;
|
||||
yield put(didRequest(action.id, sent.err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { count, createCountFunc } from './iter';
|
||||
|
||||
test('count', () => {
|
||||
let expected = 0;
|
||||
for (const i of count()) {
|
||||
expect(i).toBe(expected);
|
||||
expected++;
|
||||
if (expected > 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('createCountFunc', () => {
|
||||
const func = createCountFunc();
|
||||
expect(func()).toBe(0);
|
||||
expect(func()).toBe(1);
|
||||
expect(func()).toBe(2);
|
||||
expect(func()).toBe(3);
|
||||
expect(func()).toBe(4);
|
||||
expect(func()).toBe(5);
|
||||
expect(func()).toBe(6);
|
||||
expect(func()).toBe(7);
|
||||
expect(func()).toBe(8);
|
||||
expect(func()).toBe(9);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Creates an iterator that counts infinitely (to Number.MAX_SAFE_INTEGER really)
|
||||
* starting from 0.
|
||||
*/
|
||||
export function* count(): Generator<number, number, void> {
|
||||
let n = 0;
|
||||
while (true) {
|
||||
yield n++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new function that counts infinitely (to Number.MAX_SAFE_INTEGER really)
|
||||
* starting from 0.
|
||||
*/
|
||||
export function createCountFunc(): () => number {
|
||||
const gen = count();
|
||||
return (): number => gen.next().value;
|
||||
}
|
||||
Reference in New Issue
Block a user