Skip to content

Commit

Permalink
implement #32 Expose reason for failure of opening Ant+ stick
Browse files Browse the repository at this point in the history
  • Loading branch information
Guido Doumen committed Aug 15, 2023
1 parent cdf941d commit 44d41be
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 9 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ As Incyclist is developed as React app running in Electron, it will require a sp
constructor( props?: {
deviceNo?: number;
startupTimeout?: number;
detailedStartReport?:boolean
debug?: boolean;
logger?: { logEvent?: (event)=>void, log:(...args)=>void};
})
Expand All @@ -125,6 +126,8 @@ _deviceNo_: In case you have multiple sticks connected, identifies the stick num

_startupTimeout_: timeout (in ms) after which the startup attempt should be stopped. If no timeout is given, the `open()`call will be blocking.

_detailedStartReport_: if set to true, the open() method will provide more detailed result (AntOpenResult type), otherwise it will return a boolean

_debug_: enables debug mode ( message logging)

_logger_: logger to be use for debug logging
Expand All @@ -134,10 +137,11 @@ _logger_: logger to be use for debug logging

__open__
```typescript
open():Promise<boolean>
open():Promise<boolean|AntOpenResult>
```

Tries to open the stick.
In case the property _detailedStartReport_ has been set, it will return any of 'Success', 'NoStick', 'StartupError'
Returns `true` on success and `false` on failure.

__close__
Expand Down
77 changes: 77 additions & 0 deletions src/ant-device.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BosDescriptor, Capability, ConfigDescriptor, Device, DeviceDescriptor, Interface, LibUSBException } from 'usb'
import Channel from './ant-channel'
import {AntDevice} from './ant-device'


describe('AntDevice',()=>{
describe('onMessage',()=>{

Expand Down Expand Up @@ -60,4 +62,79 @@ describe('AntDevice',()=>{
})

})

describe('open',()=>{

const usbMock= {}
class MockedAntDeviceSuccess extends AntDevice {

protected getDevices(): { device: Device; inUse: boolean }[] {
return [ {inUse:false,device: usbMock as Device}]
}
protected markAsUsed(deviceNo: number): void {
return
}
async openUSBDevice(device: Device): Promise<boolean> {
return true
}
async startup(timeout?: number | undefined): Promise<boolean> {
return true
}
}



test ('success with detailedReporting',async ()=>{

const logger = { logEvent:jest.fn(), log:jest.fn() }
const channel = new Channel(0,null as any,{})
channel.onMessage = jest.fn()
const device = new MockedAntDeviceSuccess({logger,detailedStartReport:true})
const res = await device.open()
expect(res).toBe('Success')

})
test ('success without detailedReporting',async ()=>{
const logger = { logEvent:jest.fn(), log:jest.fn() }
const channel = new Channel(0,null as any,{})
channel.onMessage = jest.fn()
const device = new MockedAntDeviceSuccess({logger})
const res = await device.open()
expect(res).toBe(true)

})
test ('no stick with detailedReporting',async ()=>{

class MockedDevice extends AntDevice {
protected getDevices(): { device: Device; inUse: boolean }[] {
return []
}
}

const logger = { logEvent:jest.fn(), log:jest.fn() }
const channel = new Channel(0,null as any,{})
channel.onMessage = jest.fn()
const device = new MockedDevice({logger,detailedStartReport:true})
const res = await device.open()
expect(res).toBe('NoStick')


})
test ('no stick without detailedReporting',async ()=>{
class MockedDevice extends AntDevice {
protected getDevices(): { device: Device; inUse: boolean }[] {
return []
}
}

const logger = { logEvent:jest.fn(), log:jest.fn() }
const channel = new Channel(0,null as any,{})
channel.onMessage = jest.fn()
const device = new MockedDevice({logger})
const res = await device.open()
expect(res).toBe(false)

})
})

})
21 changes: 14 additions & 7 deletions src/ant-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import EventEmitter from 'events';
import {usb,getDeviceList,Interface, InEndpoint,OutEndpoint,Device} from 'usb'
import { Constants } from './consts';
import { Messages } from './messages';
import { IAntDevice, AntDeviceProps, IChannel } from './types';
import { IAntDevice, AntDeviceProps, IChannel, AntOpenResult } from './types';
import Channel from './ant-channel';


Expand Down Expand Up @@ -91,19 +91,24 @@ export class AntDevice implements IAntDevice {
return AntDevice.devices;
}

protected markAsUsed(deviceNo:number) {
AntDevice.devices[deviceNo].inUse = true;
}


async open(): Promise<boolean> {
async open(): Promise<boolean|AntOpenResult> {
const available = this.getDevices();

if (!available || available.length===0)
return false;
return this.props.detailedStartReport ? 'NoStick' : false

let found = -1;
const {deviceNo,startupTimeout} = this.props

if (deviceNo!==undefined && deviceNo>=0) {
if (available.length<=deviceNo || available[deviceNo].inUse)
return false;
return this.props.detailedStartReport ? 'NoStick' : false

const opened = await this.openUSBDevice( available[deviceNo].device );
if (opened)
found = deviceNo
Expand All @@ -127,14 +132,16 @@ export class AntDevice implements IAntDevice {
const started = await this.startup(startupTimeout);
if (!started) {
await this.close();
return false;
return this.props.detailedStartReport ? 'StartupError' : false

}

this.deviceNo = found;
AntDevice.devices[found].inUse = true;
this.markAsUsed(found)

this.channels = [];
for (let i =0; i<this.maxChannels; i++) this.channels.push(null)
return true;
return this.props.detailedStartReport ? 'Success' : true
}

}
Expand Down
5 changes: 4 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ export type AntDeviceProps = {
debug?: boolean;
logger?: { logEvent?: (event)=>void, log:(...args)=>void};
startupTimeout?: number;
detailedStartReport?:boolean;
}

export type AntOpenResult = 'Success' | 'AlreadyInUse' | 'NoStick' | 'CommunicationError' | 'StartupError'

export interface IAntDevice {
open(): Promise<boolean>
open(): Promise<boolean|AntOpenResult>
close(): Promise<boolean>

getMaxChannels(): number;
Expand Down

0 comments on commit 44d41be

Please sign in to comment.