Skip to content

Commit

Permalink
refactoring to handle require errors
Browse files Browse the repository at this point in the history
  • Loading branch information
pelikhan committed Nov 2, 2023
1 parent 0bbc41b commit 4372a14
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 51 deletions.
4 changes: 2 additions & 2 deletions cli/src/devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export async function devtools(
const traceFd = options.trace ? await open(options.trace, "w") : null

// passive bus to sniff packets
const transports = createTransports(options)
const transports = await createTransports(options)
const bus = new JDBus(transports, {
client: false,
disableRoleManager: true,
Expand Down Expand Up @@ -387,7 +387,7 @@ function startDbgServer(port: number, options: DevToolsOptions) {
}

async function connectCmd(req: ConnectReqArgs) {
await connectTransport(devtoolsSelf.bus, req)
await connectTransport(devtoolsSelf, req)
}

async function buildCmd(args: BuildReqArgs) {
Expand Down
3 changes: 3 additions & 0 deletions cli/src/sideprotocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export interface SideAddBoardResp extends SideResp<"addBoard"> {
export interface SideTransportEvent extends SideEvent<"transport"> {
data: TransportStatus
}
export interface SideMissingPackageEvent extends SideEvent<"missingPackage"> {
packageName: string
}

export interface TransportStatus {
autoConnect?: boolean
Expand Down
111 changes: 73 additions & 38 deletions cli/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { DevToolsIface, sendEvent } from "./sidedata"
import {
ConnectReqArgs,
SideMissingPackageEvent,
SideTransportEvent,
TransportStatus,
WebSocketConnectReqArgs,
Expand All @@ -33,29 +34,48 @@ export interface TransportsOptions {
spi?: boolean
}

function tryRequire(name: string) {
return require(name)
export class RequireError extends Error {
constructor(readonly packageName: string) {
super(`failed to require package "${packageName}"`)
}
}

interface RequireOptions {
interactive?: boolean
}

function createSPI() {
async function tryRequire(name: string, options: RequireOptions) {
try {
return require(name)
} catch (e) {
log(`failed to require package "${name}"`)
console.debug(e.stderr?.toString())
if (options.interactive) {
// TODO
}
throw new RequireError(name)
}
}

async function createSPI(options?: RequireOptions) {
log(`adding SPI transport (requires "rpio" package)`)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const RPIO = tryRequire("rpio")
const RPIO = await tryRequire("rpio", options)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const SpiDev = tryRequire("spi-device")
const SpiDev = await tryRequire("spi-device", options)
return createNodeSPITransport(RPIO, SpiDev)
}
function createUSB() {
async function createUSB(options?: RequireOptions) {
log(`adding USB transport (requires "usb" package)`)
const usb = tryRequire("usb")
const options = createNodeUSBOptions(usb.WebUSB)
return createUSBTransport(options)
const usb = await tryRequire("usb", options)
const usbOptions = createNodeUSBOptions(usb.WebUSB)
return createUSBTransport(usbOptions)
}
function createSerial() {
async function createSerial(options?: RequireOptions) {
log(`adding serial transport (requires "serialport" package)`)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const SerialPort = tryRequire("serialport").SerialPort
return createNodeWebSerialTransport(SerialPort)
const sp = await tryRequire("serialport", options)
return createNodeWebSerialTransport(sp.SerialPort)
}

function createWebSocket(url: string, protocol: string) {
Expand Down Expand Up @@ -90,7 +110,11 @@ function createWebSocket(url: string, protocol: string) {
return transport
}

export async function connectTransport(bus: JDBus, req: ConnectReqArgs) {
export async function connectTransport(
devtools: DevToolsIface,
req: ConnectReqArgs
) {
const bus = devtools.bus
const { transport: type, background, resourceGroupId } = req
// no type, reconnect all
if (!type) {
Expand All @@ -110,28 +134,38 @@ export async function connectTransport(bus: JDBus, req: ConnectReqArgs) {

// need to start transport
let newTransport: Transport
switch (type) {
case "websocket": {
const { url, protocol } = req as WebSocketConnectReqArgs
newTransport = createWebSocket(url, protocol)
break
}
case "spi": {
newTransport = createSPI()
break
}
case "serial": {
newTransport = createSerial()
break
}
case "usb": {
newTransport = createUSB()
break
}
case "none": {
// disconnect
break
try {
switch (type) {
case "websocket": {
const { url, protocol } = req as WebSocketConnectReqArgs
newTransport = createWebSocket(url, protocol)
break
}
case "spi": {
newTransport = await createSPI()
break
}
case "serial": {
newTransport = await createSerial()
break
}
case "usb": {
newTransport = await createUSB()
break
}
case "none": {
// disconnect
break
}
}
} catch (e) {
if (e instanceof RequireError) {
sendEvent<SideMissingPackageEvent>(
devtools.mainClient,
"missingPackage",
{ packageName: e.packageName }
)
} else throw e
}

if (newTransport) {
Expand All @@ -142,11 +176,12 @@ export async function connectTransport(bus: JDBus, req: ConnectReqArgs) {
await Promise.all(bus.transports.map(tr => tr.connect(background)))
}

export function createTransports(options: TransportsOptions) {
export async function createTransports(options: TransportsOptions) {
const transports: Transport[] = []
if (options.usb) transports.push(createUSB())
if (options.serial) transports.push(createSerial())
if (options.spi) transports.push(createSPI())
if (options.usb) transports.push(await createUSB({ interactive: true }))
if (options.serial)
transports.push(await createSerial({ interactive: true }))
if (options.spi) transports.push(await createSPI({ interactive: true }))
return transports
}

Expand Down
17 changes: 8 additions & 9 deletions interop/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// generated file, run scripts/builderrors.mjs to update
export const errors: Record<string, string> = {
"loopback rx ovf": "loopback-rx-ovf",
"can't connect, no HF2 nor JDUSB": "no-hf2",
"esptool cannot connect": "esptool-cannot-connect",
"I2C device not found or malfunctioning":
"i2c-device-not-found-or-malfunctioning",
"Unable to locate Node.JS v16+.": "terminal-nodemissing",
"Install @devicescript/cli package": "terminal-notinstalled",
'missing "devicescript" section': "missing-devicescript-section",
}
"loopback rx ovf": "loopback-rx-ovf",
"can't connect, no HF2 nor JDUSB": "no-hf2",
"esptool cannot connect": "esptool-cannot-connect",
"I2C device not found or malfunctioning": "i2c-device-not-found-or-malfunctioning",
"Unable to locate Node.JS v16+.": "terminal-nodemissing",
"Install @devicescript/cli package": "terminal-notinstalled",
"missing \"devicescript\" section": "missing-devicescript-section"
};
2 changes: 1 addition & 1 deletion jacdac-ts
9 changes: 8 additions & 1 deletion vscode/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
SideAddTestReq,
SideAddTestResp,
SideConnectReq,
SideMissingPackageEvent,
SideStartVmReq,
SideStopVmReq,
SideTransportEvent,
Expand All @@ -52,7 +53,6 @@ import { SimulatorsWebView } from "./simulatorWebView"
import { showErrorMessage } from "./telemetry"
import _serverInfo from "./server-info.json"
import { resolvePythonEnvironment } from "./python"
import { resolveDarkMode } from "./assets"

const serverInfo = _serverInfo as ServerInfoFile

Expand Down Expand Up @@ -106,6 +106,13 @@ export class DeviceScriptExtensionState extends JDEventSource {
this.emit(CHANGE)
}
})
subSideEvent<SideMissingPackageEvent>("missingPackage", async msg => {
const packageName = msg.packageName
await showErrorMessage(
"package.missing",
`Failed to require ${packageName} package. Please install manually.`
)
})
}

get state() {
Expand Down

0 comments on commit 4372a14

Please sign in to comment.