Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
johntalton committed Jun 3, 2024
1 parent 1783ea6 commit a105db5
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 176 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@johntalton/and-other-delights",
"version": "8.0.1",
"version": "8.1.0",
"license": "MIT",
"main": "./lib/index.js",
"exports": {
Expand Down
153 changes: 40 additions & 113 deletions src/i2c-addressed.ts
Original file line number Diff line number Diff line change
@@ -1,159 +1,86 @@
/* eslint-disable import/exports-last */
import { I2CAddress, I2CBufferSource, I2CBus } from './i2c'

export const DEFAULT_AB = { allocOnRead: true }
import { I2CAddress, I2CBufferSource, I2CBus, I2CWriteResult } from './i2c.js'

const BASE_16 = 16

export type ABOptions = {
sharedReadBuffer?: I2CBufferSource,
allocOnRead: boolean,
allowMixedReadBuffers: boolean,
maxReadLength: number
maxWriteLength: number,
validateReadWriteLengths: boolean
}

export const WARN_READ_LENGTH = 32
export const WARN_WRITE_LENGTH = 32

export function assertBufferSource(bs: I2CBufferSource) {
if (bs === undefined) {
throw new Error('bufferSource undefined')
}
const isView = ArrayBuffer.isView(bs)
const isAB = bs instanceof ArrayBuffer

if (!isView && !isAB) {
throw new Error('bufferSource is not ArrayBuffer or ArrayBufferView')
}
}

export interface _I2CAddressedBus {
close(): void

readI2cBlock(cmd: number, length: number, readBufferSource?: I2CBufferSource): Promise<ArrayBuffer>
writeI2cBlock(cmd: number, bufferSource: I2CBufferSource): Promise<void>
writeI2cBlock(cmd: number, bufferSource: I2CBufferSource): Promise<I2CWriteResult>

sendByte(value: number): Promise<void>

i2cRead(length: number, readBufferSource?: I2CBufferSource): Promise<ArrayBuffer>
i2cWrite(bufferSource: I2CBufferSource): Promise<void>
i2cWrite(bufferSource: I2CBufferSource): Promise<I2CWriteResult>
}

// const DEFAULT_READ_BUFFER = new ArrayBuffer(64)
function defaultReadBuffer(length: number) {
// return undefined
return new ArrayBuffer(length)
// return DEFAULT_READ_BUFFER
}

function assertBufferSource(buffer: unknown): asserts buffer is I2CBufferSource {
if(ArrayBuffer.isView(buffer)) { return }
if(buffer instanceof ArrayBuffer) { return }
// if(buffer instanceof SharedArrayBuffer) { return }

throw new Error('invalid buffer source')
}

/**
* I2CBus layer providing address encapsulation.
**/
export class I2CAddressedBus implements _I2CAddressedBus {
readonly address: I2CAddress
private readonly bus: I2CBus
private readonly options: ABOptions
readonly #address: I2CAddress
readonly #bus: I2CBus

static from(bus: I2CBus, address: I2CAddress, options: Partial<ABOptions> = {}): I2CAddressedBus {
return new I2CAddressedBus(bus, address, options)
static from(bus: I2CBus, address: I2CAddress): I2CAddressedBus {
return new I2CAddressedBus(bus, address)
}

constructor(bus: I2CBus, address: I2CAddress, {
sharedReadBuffer = undefined,
allocOnRead = true,
allowMixedReadBuffers = false,
maxReadLength = WARN_READ_LENGTH,
maxWriteLength = WARN_WRITE_LENGTH,
validateReadWriteLengths = true
}: Partial<ABOptions> = {}) {
this.address = address
this.bus = bus

this.options = {
...DEFAULT_AB,
sharedReadBuffer,
allocOnRead,
allowMixedReadBuffers,
maxReadLength,
maxWriteLength,
validateReadWriteLengths
}
constructor(bus: I2CBus, address: I2CAddress) {
this.#address = address
this.#bus = bus
}

get name(): string {
return this.bus.name + ':0x' + this.address.toString(BASE_16)
return this.#bus.name + ':0x' + this.#address.toString(BASE_16)
}

private _getReadBuffer(length: number, readBufferSource?: I2CBufferSource): I2CBufferSource {
const hasOptionsBuffer = this.options.sharedReadBuffer !== undefined

if (readBufferSource !== undefined && !(hasOptionsBuffer && !this.options.allowMixedReadBuffers)) {
return readBufferSource
}

if (this.options.sharedReadBuffer !== undefined) {
return this.options.sharedReadBuffer
}

if (this.options.allocOnRead) {
return new ArrayBuffer(length)
}

throw new Error('no provided read buffer and allocation disabled')
}

close(): void { return this.bus.close() }
close(): void { return this.#bus.close() }

async readI2cBlock(cmd: number, length: number, readBufferSource?: I2CBufferSource): Promise<ArrayBuffer> {
if (this.options.validateReadWriteLengths && length > this.options.maxReadLength) {
throw new Error('read length greater then max configured')
}

const readBuffer = this._getReadBuffer(length, readBufferSource)
const readBuffer = readBufferSource ?? defaultReadBuffer(length)
const { bytesRead, buffer } = await this.#bus.readI2cBlock(this.#address, cmd, length, readBuffer)
if(bytesRead !== length) { throw new Error('invalid length read') }

const { bytesRead, buffer } = await this.bus.readI2cBlock(this.address, cmd, length, readBuffer)

if (this.options.validateReadWriteLengths && bytesRead !== length) {
throw new Error('read length mismatch - ' + bytesRead + ' / ' + length)
}

//
return buffer.slice(0, bytesRead)
return buffer
}

async writeI2cBlock(cmd: number, bufferSource: I2CBufferSource): Promise<void> {
async writeI2cBlock(cmd: number, bufferSource: I2CBufferSource): Promise<I2CWriteResult> {
assertBufferSource(bufferSource)

if (this.options.validateReadWriteLengths && bufferSource.byteLength > this.options.maxWriteLength) {
throw new Error('write length greater then max configured')
}

const { bytesWritten } = await this.bus.writeI2cBlock(this.address, cmd, bufferSource.byteLength, bufferSource)

if (this.options.validateReadWriteLengths && bytesWritten !== bufferSource.byteLength) {
throw new Error('write length mismatch: ' + bytesWritten + '/' + bufferSource.byteLength)
}
return this.#bus.writeI2cBlock(this.#address, cmd, bufferSource.byteLength, bufferSource)
}

async sendByte(value: number): Promise<void> {
await this.bus.sendByte(this.address, value)
return this.#bus.sendByte(this.#address, value)
}

async i2cRead(length: number, readBufferSource?: I2CBufferSource): Promise<ArrayBuffer> {
const readBuffer = this._getReadBuffer(length, readBufferSource)
const readBuffer = readBufferSource ?? defaultReadBuffer(length)
const { bytesRead, buffer } = await this.#bus.i2cRead(this.#address, length, readBuffer)
if(bytesRead !== length) { throw new Error('invalid length read') }

const { bytesRead, buffer } = await this.bus.i2cRead(this.address, length, readBuffer)

if (this.options.validateReadWriteLengths && bytesRead !== length) {
throw new Error('read length mismatch: ' + bytesRead + '/' + length)
}

//
return buffer.slice(0, bytesRead)
return buffer
}

async i2cWrite(bufferSource: I2CBufferSource): Promise<void> {
async i2cWrite(bufferSource: I2CBufferSource): Promise<I2CWriteResult> {
assertBufferSource(bufferSource)

const { bytesWritten } = await this.bus.i2cWrite(this.address, bufferSource.byteLength, bufferSource)

if (this.options.validateReadWriteLengths && bytesWritten !== bufferSource.byteLength) {
throw new Error('write length mismatch')
}
return this.#bus.i2cWrite(this.#address, bufferSource.byteLength, bufferSource)
}
}
61 changes: 9 additions & 52 deletions src/i2c-addressedtransaction.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,24 @@
/* eslint-disable max-classes-per-file */
import { ABOptions, DEFAULT_AB, I2CAddressedBus, _I2CAddressedBus } from './i2c-addressed.js'
import { I2CAddress, I2CBufferSource } from './i2c.js'
import { I2CAddressedBus, _I2CAddressedBus } from './i2c-addressed.js'
import { I2CAddress } from './i2c.js'
import { I2CTransactionBus, TransactionBusProxy } from './i2c-transactionbus.js'

export class AddressedTransactionBusProxy implements _I2CAddressedBus {
#bus: TransactionBusProxy
#address: number

// eslint-disable-next-line no-useless-constructor
constructor(bus: TransactionBusProxy, address: number) {
this.#bus = bus
this.#address = address
}

get name(): string {
throw new Error('Method not implemented.')
}

close(): void {
throw new Error('Method not implemented.')
}

async readI2cBlock(
cmd: number,
length: number,
_readBufferSource?: I2CBufferSource | undefined
): Promise<ArrayBuffer> {

const { bytesRead, buffer } = await this.#bus.readI2cBlock(this.#address, cmd, length)
return buffer.slice(0, bytesRead)
}

async writeI2cBlock(cmd: number, bufferSource: I2CBufferSource): Promise<void> {
const { bytesWritten } = await this.#bus.writeI2cBlock(this.#address, cmd, bufferSource.byteLength, bufferSource)
if (bytesWritten !== bufferSource.byteLength) {
throw new Error('write mismatch')
}
}
sendByte(_value: number): Promise<void> {
throw new Error('Method not implemented.')
}
i2cRead(_length: number, _readBufferSource?: I2CBufferSource | undefined): Promise<ArrayBuffer> {
throw new Error('Method not implemented.')
}
i2cWrite(_bufferSource: I2CBufferSource): Promise<void> {
throw new Error('Method not implemented.')
}
}

export type AddressedTransactionCallback<T> = (bus: AddressedTransactionBusProxy) => Promise<T>
export type AddressedTransactionCallback<T> = (bus: I2CAddressedBus) => Promise<T>

export class I2CAddressedTransactionBus extends I2CAddressedBus {
#bus: I2CTransactionBus
#address: I2CAddress

constructor(bus: I2CTransactionBus, address: I2CAddress, options: Partial<ABOptions> = DEFAULT_AB) {
super(bus, address, options)
constructor(bus: I2CTransactionBus, address: I2CAddress) {
super(bus, address)

this.#address = address
this.#bus = bus
}

get name() { return super.name }

async transaction<T>(cb: AddressedTransactionCallback<T>): Promise<T> {
// eslint-disable-next-line promise/prefer-await-to-callbacks
return this.#bus.transaction(async (tbus: TransactionBusProxy) => cb(new AddressedTransactionBusProxy(tbus, this.address)))
return this.#bus.transaction(async (tbus: TransactionBusProxy) => cb(new I2CAddressedBus(tbus, this.#address)))
}
}
7 changes: 0 additions & 7 deletions src/i2c-throwbus.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
/* eslint-disable fp/no-this */
/* eslint-disable immutable/no-this */
/* eslint-disable fp/no-throw */
/* eslint-disable fp/no-mutation */
/* eslint-disable immutable/no-mutation */
/* eslint-disable fp/no-nil */
/* eslint-disable fp/no-class */
import { I2CAddress, I2CBufferSource, I2CBus, I2CReadResult, I2CWriteResult } from './i2c'

export class ThrowBus implements I2CBus {
Expand Down
2 changes: 1 addition & 1 deletion src/i2c-transactionbus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class I2CTransactionBus extends I2CProxyBus implements I2CBus {
.then(async () => cb(proxyBus))
// .then(result => { console.log('*** transaction end', id); return result })

this.#queue = nextQ
this.#queue = nextQ.catch(_ => {})

return nextQ
}
Expand Down
4 changes: 2 additions & 2 deletions src/i2c.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type I2CBufferSource = ArrayBuffer | ArrayBufferView | SharedArrayBuffer
export type I2CBufferSource = ArrayBufferLike | ArrayBufferView

//
export type I2CReadResult = {
Expand Down Expand Up @@ -30,6 +30,6 @@ export interface I2CBus extends Bus {
length: number,
bufferSource: I2CBufferSource): Promise<I2CWriteResult>

i2cRead(address: I2CAddress, length: number, bufferSource: I2CBufferSource): Promise<I2CReadResult>
i2cRead(address: I2CAddress, length: number, bufferSource?: I2CBufferSource): Promise<I2CReadResult>
i2cWrite(address: I2CAddress, length: number, bufferSource: I2CBufferSource): Promise<I2CWriteResult>
}

0 comments on commit a105db5

Please sign in to comment.