From f3425e2f0fbc9314798654ab54d1ff09b7d4babb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 11 Jun 2020 16:10:26 -0500 Subject: [PATCH] rename BLEData to BleUart These actions all relate specifically to the nRF UART GATT service --- src/actions/ble-uart.ts | 73 +++++++++++++++++++++++++++++++++++++ src/actions/ble.ts | 65 --------------------------------- src/actions/index.ts | 5 ++- src/sagas/error-log.test.ts | 2 +- src/sagas/error-log.ts | 6 +-- src/sagas/hub.test.ts | 24 ++++++------ src/sagas/hub.ts | 26 ++++++------- src/sagas/terminal.test.ts | 50 ++++++++++++------------- src/sagas/terminal.ts | 20 +++++----- src/services/ble.ts | 10 +++-- 10 files changed, 146 insertions(+), 135 deletions(-) create mode 100644 src/actions/ble-uart.ts diff --git a/src/actions/ble-uart.ts b/src/actions/ble-uart.ts new file mode 100644 index 00000000..c9956194 --- /dev/null +++ b/src/actions/ble-uart.ts @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2020 The Pybricks Authors +// actions/ble-uart.ts: Actions for Bluetooth Low Energy nRF UART service + +import { Action } from 'redux'; +import { assert } from '../utils'; +import { createCountFunc } from '../utils/iter'; + +/** + * BLE nRF UART service actions types. + */ +export enum BleUartActionType { + /** + * Write data. + */ + Write = 'ble.data.action.write', + /** + * Writing completed successfully. + */ + DidWrite = 'ble.data.didWrite', + /** + * Writing failed. + */ + DidFailToWrite = 'ble.data.action.didFailToWrite', + /** + * Notify that data was received. + */ + Notify = 'ble.data.action.receive', +} + +const nextId = createCountFunc(); + +export type BleUartWriteAction = Action & { + id: number; + value: Uint8Array; +}; + +export function write(value: Uint8Array): BleUartWriteAction { + assert(value.length <= 20, 'value can be at most 20 bytes'); + return { type: BleUartActionType.Write, id: nextId(), value }; +} + +export type BleUartDidWriteAction = Action & { + id: number; +}; + +export function didWrite(id: number): BleUartDidWriteAction { + return { type: BleUartActionType.DidWrite, id }; +} + +export type BleUartDidFailToWriteAction = Action & { + id: number; + err: Error; +}; + +export function didFailToWrite(id: number, err: Error): BleUartDidFailToWriteAction { + return { type: BleUartActionType.DidFailToWrite, id, err }; +} + +export type BleUartNotifyAction = Action & { + value: DataView; +}; + +export function notify(value: DataView): BleUartNotifyAction { + return { type: BleUartActionType.Notify, value }; +} + +/** Common type for low-level BLE data actions. */ +export type BleUartAction = + | BleUartWriteAction + | BleUartDidWriteAction + | BleUartDidFailToWriteAction + | BleUartNotifyAction; diff --git a/src/actions/ble.ts b/src/actions/ble.ts index 68d39895..00c45fd0 100644 --- a/src/actions/ble.ts +++ b/src/actions/ble.ts @@ -2,8 +2,6 @@ // Copyright (c) 2020 The Pybricks Authors import { Action } from 'redux'; -import { assert } from '../utils'; -import { createCountFunc } from '../utils/iter'; /** * Bluetooth low energy connection action types. @@ -60,69 +58,6 @@ export function didDisconnect(): BLEConnectAction { return { type: BLEConnectActionType.DidDisconnect }; } -export enum BLEDataActionType { - /** - * Write data. - */ - Write = 'ble.data.action.write', - /** - * Writing completed successfully. - */ - DidWrite = 'ble.data.didWrite', - /** - * Writing failed. - */ - DidFailToWrite = 'ble.data.action.didFailToWrite', - /** - * Notify that data was received. - */ - Notify = 'ble.data.action.receive', -} - -const nextId = createCountFunc(); - -export type BLEDataWriteAction = Action & { - id: number; - value: Uint8Array; -}; - -export function write(value: Uint8Array): BLEDataWriteAction { - assert(value.length <= 20, 'value can be at most 20 bytes'); - return { type: BLEDataActionType.Write, id: nextId(), value }; -} - -export type BLEDataDidWriteAction = Action & { - id: number; -}; - -export function didWrite(id: number): BLEDataDidWriteAction { - return { type: BLEDataActionType.DidWrite, id }; -} - -export type BLEDataDidFailToWriteAction = Action & { - id: number; - err: Error; -}; - -export function didFailToWrite(id: number, err: Error): BLEDataDidFailToWriteAction { - return { type: BLEDataActionType.DidFailToWrite, id, err }; -} - -export type BLEDataNotifyAction = Action & { - value: DataView; -}; - -export function notify(value: DataView): BLEDataNotifyAction { - return { type: BLEDataActionType.Notify, value }; -} - -/** Common type for low-level BLE data actions. */ -export type BLEDataAction = - | BLEDataWriteAction - | BLEDataDidWriteAction - | BLEDataDidFailToWriteAction - | BLEDataNotifyAction; - /** * High-level BLE actions. */ diff --git a/src/actions/index.ts b/src/actions/index.ts index ab9c917a..465b27df 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -2,7 +2,8 @@ // Copyright (c) 2020 The Pybricks Authors import { Dispatch as ReduxDispatch } from 'redux'; -import { BLEAction, BLEConnectAction, BLEDataAction } from './ble'; +import { BLEAction, BLEConnectAction } from './ble'; +import { BleUartAction } from './ble-uart'; import { EditorAction } from './editor'; import { FlashFirmwareAction } from './flash-firmware'; import { HubAction, HubMessageAction } from './hub'; @@ -23,7 +24,7 @@ import { TerminalDataAction } from './terminal'; export type Action = | BLEAction | BLEConnectAction - | BLEDataAction + | BleUartAction | BootloaderConnectionAction | BootloaderDidRequestAction | BootloaderRequestAction diff --git a/src/sagas/error-log.test.ts b/src/sagas/error-log.test.ts index adca5d99..faa314f5 100644 --- a/src/sagas/error-log.test.ts +++ b/src/sagas/error-log.test.ts @@ -2,7 +2,7 @@ // Copyright (c) 2020 The Pybricks Authors import { AsyncSaga } from '../../test'; -import { didFailToWrite } from '../actions/ble'; +import { didFailToWrite } from '../actions/ble-uart'; import { BootloaderConnectionFailureReason, didError, diff --git a/src/sagas/error-log.ts b/src/sagas/error-log.ts index 108b62d3..7c26917f 100644 --- a/src/sagas/error-log.ts +++ b/src/sagas/error-log.ts @@ -2,7 +2,7 @@ // Copyright (c) 2020 The Pybricks Authors import { takeEvery } from 'redux-saga/effects'; -import { BLEDataActionType, BLEDataDidFailToWriteAction } from '../actions/ble'; +import { BleUartActionType, BleUartDidFailToWriteAction } from '../actions/ble-uart'; import { BootloaderConnectionActionType, BootloaderConnectionDidErrorAction, @@ -10,7 +10,7 @@ import { BootloaderConnectionFailureReason, } from '../actions/lwp3-bootloader'; -function bleDataDidFailToWrite(action: BLEDataDidFailToWriteAction): void { +function bleDataDidFailToWrite(action: BleUartDidFailToWriteAction): void { console.error(action.err); } @@ -29,7 +29,7 @@ function bootloaderDidError(action: BootloaderConnectionDidErrorAction): void { } export default function* (): Generator { - yield takeEvery(BLEDataActionType.DidFailToWrite, bleDataDidFailToWrite); + yield takeEvery(BleUartActionType.DidFailToWrite, bleDataDidFailToWrite); yield takeEvery( BootloaderConnectionActionType.DidFailToConnect, bootloaderDidFailToConnect, diff --git a/src/sagas/hub.test.ts b/src/sagas/hub.test.ts index 59a6d27f..380ed6a8 100644 --- a/src/sagas/hub.test.ts +++ b/src/sagas/hub.test.ts @@ -4,7 +4,7 @@ import { Ace } from 'ace-builds'; import { mock } from 'jest-mock-extended'; import { AsyncSaga } from '../../test'; -import { BLEDataActionType, BLEDataWriteAction, didWrite } from '../actions/ble'; +import { BleUartActionType, BleUartWriteAction, didWrite } from '../actions/ble-uart'; import { HubMessageActionType, HubRuntimeStatusMessageAction, @@ -42,23 +42,23 @@ describe('downloadAndRun', () => { // first message is the length const writeAction = await saga.take(); - expect(writeAction.type).toBe(BLEDataActionType.Write); - expect((writeAction as BLEDataWriteAction).value.length).toBe(4); - saga.put(didWrite((writeAction as BLEDataWriteAction).id)); + expect(writeAction.type).toBe(BleUartActionType.Write); + expect((writeAction as BleUartWriteAction).value.length).toBe(4); + saga.put(didWrite((writeAction as BleUartWriteAction).id)); saga.put(checksum(30)); // then the first chunk of 20 bytes const writeAction2 = await saga.take(); - expect(writeAction2.type).toBe(BLEDataActionType.Write); - expect((writeAction2 as BLEDataWriteAction).value.length).toBe(20); - saga.put(didWrite((writeAction2 as BLEDataWriteAction).id)); + expect(writeAction2.type).toBe(BleUartActionType.Write); + expect((writeAction2 as BleUartWriteAction).value.length).toBe(20); + saga.put(didWrite((writeAction2 as BleUartWriteAction).id)); saga.put(checksum(0)); // then last chunk const writeAction3 = await saga.take(); - expect(writeAction3.type).toBe(BLEDataActionType.Write); - expect((writeAction3 as BLEDataWriteAction).value.length).toBe(10); - saga.put(didWrite((writeAction3 as BLEDataWriteAction).id)); + expect(writeAction3.type).toBe(BleUartActionType.Write); + expect((writeAction3 as BleUartWriteAction).value.length).toBe(10); + saga.put(didWrite((writeAction3 as BleUartWriteAction).id)); saga.put(checksum(0)); // Then a status message saying that we are done @@ -80,7 +80,7 @@ test('repl', async () => { saga.put(repl()); const compileAction = await saga.take(); - expect(compileAction.type).toBe(BLEDataActionType.Write); + expect(compileAction.type).toBe(BleUartActionType.Write); await saga.end(); }); @@ -91,7 +91,7 @@ test('stop', async () => { saga.put(stop()); const compileAction = await saga.take(); - expect(compileAction.type).toBe(BLEDataActionType.Write); + expect(compileAction.type).toBe(BleUartActionType.Write); await saga.end(); }); diff --git a/src/sagas/hub.ts b/src/sagas/hub.ts index 833ba7f6..a12eae43 100644 --- a/src/sagas/hub.ts +++ b/src/sagas/hub.ts @@ -15,12 +15,12 @@ import { } from 'redux-saga/effects'; import { Action } from '../actions'; import { - BLEDataActionType, - BLEDataDidFailToWriteAction, - BLEDataDidWriteAction, - BLEDataWriteAction, + BleUartActionType, + BleUartDidFailToWriteAction, + BleUartDidWriteAction, + BleUartWriteAction, write, -} from '../actions/ble'; +} from '../actions/ble-uart'; import { HubActionType, HubChecksumMessageAction, @@ -44,8 +44,8 @@ const downloadChunkSize = 100; function waitForWrite(id: number): RaceEffect { return race([ - take((a: Action) => a.type === BLEDataActionType.DidWrite && a.id === id), - take((a: Action) => a.type === BLEDataActionType.DidFailToWrite && a.id === id), + take((a: Action) => a.type === BleUartActionType.DidWrite && a.id === id), + take((a: Action) => a.type === BleUartActionType.DidFailToWrite && a.id === id), ]); } @@ -82,10 +82,10 @@ function* downloadAndRun(_action: HubDownloadAndRunAction): Generator { const sizeBuf = new Uint8Array(4); const sizeView = new DataView(sizeBuf.buffer); sizeView.setUint32(0, mpy.data.byteLength, true); - const writeAction = (yield put(write(sizeBuf))) as BLEDataWriteAction; + const writeAction = (yield put(write(sizeBuf))) as BleUartWriteAction; const [, didFailToWrite] = (yield waitForWrite(writeAction.id)) as [ - BLEDataDidWriteAction, - BLEDataDidFailToWriteAction, + BleUartDidWriteAction, + BleUartDidFailToWriteAction, ]; if (didFailToWrite) { @@ -112,10 +112,10 @@ function* downloadAndRun(_action: HubDownloadAndRunAction): Generator { for (let j = 0; j < chunk.length; j += 20) { const writeAction = (yield put( write(chunk.slice(j, j + 20)), - )) as BLEDataWriteAction; + )) as BleUartWriteAction; const [, didFailToWrite] = (yield waitForWrite(writeAction.id)) as [ - BLEDataDidWriteAction, - BLEDataDidFailToWriteAction, + BleUartDidWriteAction, + BleUartDidFailToWriteAction, ]; if (didFailToWrite) { diff --git a/src/sagas/terminal.test.ts b/src/sagas/terminal.test.ts index 7a969a63..8f15bc5e 100644 --- a/src/sagas/terminal.test.ts +++ b/src/sagas/terminal.test.ts @@ -4,12 +4,12 @@ import { AsyncSaga, delay } from '../../test'; import { - BLEDataActionType, - BLEDataWriteAction, + BleUartActionType, + BleUartWriteAction, didFailToWrite, didWrite, notify, -} from '../actions/ble'; +} from '../actions/ble-uart'; import { HubChecksumMessageAction, HubMessageActionType, @@ -351,8 +351,8 @@ describe('Terminal data source responds to receive data actions', () => { saga.put(receiveData('test1234')); const action = await saga.take(); - expect(action.type).toBe(BLEDataActionType.Write); - expect((action as BLEDataWriteAction).value).toEqual(expected); + expect(action.type).toBe(BleUartActionType.Write); + expect((action as BleUartWriteAction).value).toEqual(expected); await saga.end(); }); @@ -372,19 +372,19 @@ describe('Terminal data source responds to receive data actions', () => { expect(saga.numPending()).toBe(1); const action = await saga.take(); - expect(action.type).toBe(BLEDataActionType.Write); - expect((action as BLEDataWriteAction).value).toEqual(expected); + expect(action.type).toBe(BleUartActionType.Write); + expect((action as BleUartWriteAction).value).toEqual(expected); // second message is queued until didWrite or didFailToWrite expect(saga.numPending()).toBe(0); - saga.put(didWrite((action as BLEDataWriteAction).id)); + saga.put(didWrite((action as BleUartWriteAction).id)); const action2 = await saga.take(); - expect(action2.type).toBe(BLEDataActionType.Write); - expect((action2 as BLEDataWriteAction).value).toEqual(expected); + expect(action2.type).toBe(BleUartActionType.Write); + expect((action2 as BleUartWriteAction).value).toEqual(expected); - saga.put(didWrite((action2 as BLEDataWriteAction).id)); + saga.put(didWrite((action2 as BleUartWriteAction).id)); await saga.end(); }); @@ -404,21 +404,21 @@ describe('Terminal data source responds to receive data actions', () => { expect(saga.numPending()).toBe(1); const action = await saga.take(); - expect(action.type).toBe(BLEDataActionType.Write); - expect((action as BLEDataWriteAction).value).toEqual(expected); + expect(action.type).toBe(BleUartActionType.Write); + expect((action as BleUartWriteAction).value).toEqual(expected); // second message is queued until didWrite or didFailToWrite expect(saga.numPending()).toBe(0); saga.put( - didFailToWrite((action as BLEDataWriteAction).id, new Error('test error')), + didFailToWrite((action as BleUartWriteAction).id, new Error('test error')), ); const action2 = await saga.take(); - expect(action2.type).toBe(BLEDataActionType.Write); - expect((action2 as BLEDataWriteAction).value).toEqual(expected); + expect(action2.type).toBe(BleUartActionType.Write); + expect((action2 as BleUartWriteAction).value).toEqual(expected); - saga.put(didWrite((action2 as BLEDataWriteAction).id)); + saga.put(didWrite((action2 as BleUartWriteAction).id)); await saga.end(); }); @@ -434,8 +434,8 @@ describe('Terminal data source responds to receive data actions', () => { saga.put(receiveData('test1234')); const action = await saga.take(); - expect(action.type).toBe(BLEDataActionType.Write); - expect((action as BLEDataWriteAction).value).toEqual( + expect(action.type).toBe(BleUartActionType.Write); + expect((action as BleUartWriteAction).value).toEqual( new Uint8Array([...expected, ...expected]), ); @@ -452,16 +452,16 @@ describe('Terminal data source responds to receive data actions', () => { saga.put(receiveData('012345678901234567890123456789')); const action = await saga.take(); - expect(action.type).toBe(BLEDataActionType.Write); - expect((action as BLEDataWriteAction).value.length).toEqual(20); + expect(action.type).toBe(BleUartActionType.Write); + expect((action as BleUartWriteAction).value.length).toEqual(20); - saga.put(didWrite((action as BLEDataWriteAction).id)); + saga.put(didWrite((action as BleUartWriteAction).id)); const action2 = await saga.take(); - expect(action2.type).toBe(BLEDataActionType.Write); - expect((action2 as BLEDataWriteAction).value.length).toEqual(10); + expect(action2.type).toBe(BleUartActionType.Write); + expect((action2 as BleUartWriteAction).value.length).toEqual(10); - saga.put(didWrite((action2 as BLEDataWriteAction).id)); + saga.put(didWrite((action2 as BleUartWriteAction).id)); await saga.end(); }); diff --git a/src/sagas/terminal.ts b/src/sagas/terminal.ts index 012b6341..0a20bdb7 100644 --- a/src/sagas/terminal.ts +++ b/src/sagas/terminal.ts @@ -15,11 +15,11 @@ import { import PushStream from 'zen-push'; import { Action } from '../actions'; import { - BLEDataActionType, - BLEDataNotifyAction, - BLEDataWriteAction, + BleUartActionType, + BleUartNotifyAction, + BleUartWriteAction, write, -} from '../actions/ble'; +} from '../actions/ble-uart'; import { HubRuntimeStatusType, checksum, updateStatus } from '../actions/hub'; import { TerminalActionType, @@ -55,7 +55,7 @@ function* handleMatch( return true; } -function* receiveUartData(action: BLEDataNotifyAction): Generator { +function* receiveUartData(action: BleUartNotifyAction): Generator { const hubState = (yield select((s: RootState) => s.hub.runtime)) as HubRuntimeState; if (hubState === HubRuntimeState.Loading && action.value.buffer.byteLength === 1) { @@ -119,17 +119,17 @@ function* receiveTerminalData(): Generator { for (let i = 0; i < data.length; i += 20) { const { id } = (yield put( write(data.slice(i, i + 20)), - )) as BLEDataWriteAction; + )) as BleUartWriteAction; yield take( (a: Action) => - (a.type === BLEDataActionType.DidWrite || - a.type === BLEDataActionType.DidFailToWrite) && + (a.type === BleUartActionType.DidWrite || + a.type === BleUartActionType.DidFailToWrite) && a.id === id, ); // wait for echo so tht we don't overrun the hub with messages - yield race([take(BLEDataActionType.Notify), delay(100)]); + yield race([take(BleUartActionType.Notify), delay(100)]); } } } @@ -140,7 +140,7 @@ function sendTerminalData(action: TerminalDataReceiveDataAction): void { } export default function* (): Generator { - yield takeEvery(BLEDataActionType.Notify, receiveUartData); + yield takeEvery(BleUartActionType.Notify, receiveUartData); yield fork(receiveTerminalData); yield takeEvery(TerminalActionType.SendData, sendTerminalData); yield put(setDataSource(terminalDataSource.observable)); diff --git a/src/services/ble.ts b/src/services/ble.ts index ff9cd1d8..e729f840 100644 --- a/src/services/ble.ts +++ b/src/services/ble.ts @@ -5,15 +5,17 @@ import { Action, Dispatch } from '../actions'; import { BLEActionType, BLEConnectActionType, - BLEDataActionType, connect as connectAction, didConnect, didDisconnect, + disconnect as disconnectAction, +} from '../actions/ble'; +import { + BleUartActionType, didFailToWrite, didWrite, - disconnect as disconnectAction, notify, -} from '../actions/ble'; +} from '../actions/ble-uart'; import { stop } from '../actions/hub'; import * as notification from '../actions/notification'; import { RootState } from '../reducers'; @@ -118,7 +120,7 @@ function disconnect(action: Action): void { } async function write(action: Action, dispatch: Dispatch): Promise { - if (action.type !== BLEDataActionType.Write) { + if (action.type !== BleUartActionType.Write) { return; } try {