import assert from "node:assert" ; import { ReadonlyDeep , } from "type-fest" ; import Camera , { ConstructorOptions , UvcControl , } from "uvc-control" ; import Output from "./output.js" ; import toFormattedHex from "./utilities/to-formatted-hex.js" ; import WrappedError from "./utilities/wrapped-error.js" ; interface GetFunctionArguments { address : number | null ; product : number | null ; vendor : number | null ; } export default class CameraFactory { constructor ( private readonly output , private readonly UvcControl ) { assert . strictEqual ( arguments . length , 2 ) ; assert ( typeof this . UvcControl === "function" ) ; } async get ( vendor , product , address ) { assert . strictEqual ( arguments . length , 3 ) ; assert ( vendor === null || ( typeof vendor === "number" && vendor >= 0 ) ) ; assert ( product === null || ( typeof product === "number" && product >= 0 ) ) ; assert ( address === null || ( typeof address === "number" && address >= 0 ) ) ; const constructorOptions = { deviceAddress : address || undefined , pid : product || undefined , vid : vendor || undefined , } ; try { const camera = new this . UvcControl ( constructorOptions ) ; if ( vendor && vendor !== camera . device . deviceDescriptor . idVendor ) { this . output . warning ( "Camera vendor id mismatch." , "Input" , vendor , `(${ toFormattedHex ( vendor , 4 ) })` , "Actual" , camera . device . deviceDescriptor . idVendor , `(${ toFormattedHex ( camera . device . deviceDescriptor . idVendor , 4 ) })` ) ; } if ( product && product !== camera . device . deviceDescriptor . idProduct ) { this . output . warning ( "Camera product id mismatch." , "Input" , product , `(${ toFormattedHex ( product , 4 ) })` , "Actual" , camera . device . deviceDescriptor . idProduct , `(${ toFormattedHex ( camera . device . deviceDescriptor . idProduct , 4 ) })` ) ; } if ( address && address !== camera . device . deviceAddress ) { this . output . warning ( "Camera device address mismatch." , "Input" , address , `(${ toFormattedHex ( address , 1 ) })` , "Actual" , camera . device . deviceAddress , `(${ toFormattedHex ( camera . device . deviceAddress , 1 ) })` ) ; } return camera ; } catch ( error ) { if ( error instanceof Error ) { const getFunctionArguments = { address , product , vendor , } ; const wrappedError = this . createWrappedError ( error , getFunctionArguments , constructorOptions ) ; throw wrappedError ; } throw error ; } } private createWrappedError ( error , getFunctionArguments , constructorOptions ) { let errorMessage = null ; const guessThatUvcDeviceWasNotFound = typeof error . name === "string" && error . name === "TypeError" && typeof error . message === "string" && error . message === "Cannot read property 'interfaces' of undefined" ; errorMessage = guessThatUvcDeviceWasNotFound ? `Could not find UVC device. Is a compatible camera connected? ${ JSON . stringify ( getFunctionArguments ) }` : `Could create uvc-control object: ${ JSON . stringify ( constructorOptions ) }` ; errorMessage += ` (${ JSON . stringify ( String ( error ) ) })` ; const wrappedError = new WrappedError ( error , errorMessage ) ; return wrappedError ; } }