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:
David Lechner
2020-05-21 21:27:47 -05:00
committed by David Lechner
parent 0d57338d71
commit ed5bd4728a
6 changed files with 210 additions and 26 deletions
+134 -17
View File
@@ -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.
*/
+3 -2
View File
@@ -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 };
}
/**
+22 -6
View File
@@ -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,
});
}
});
});
+6 -1
View File
@@ -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));
}
}
+26
View File
@@ -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);
});
+19
View File
@@ -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;
}