-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1783ea6
commit a105db5
Showing
6 changed files
with
53 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters