Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(app-shell,app): Send labware files and runtime parameters over USB #14994

Merged
merged 9 commits into from
Apr 25, 2024
Prev Previous commit
Next Next commit
Nope, they need to be ArrayBuffers.
  • Loading branch information
SyntaxColoring committed Apr 24, 2024
commit 78d1fa4123fd23baca92e92499b184cfee13f325
20 changes: 15 additions & 5 deletions app-shell/src/usb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,18 @@ function isUsbDeviceOt3(device: UsbDevice): boolean {
)
}

interface StructuredCloneableFormDataEntry {
name: string
value: string | Blob | File
}
type StructuredCloneableFormDataEntry =
| {
type: 'string'
name: string
value: string
}
| {
type: 'file'
name: string
value: ArrayBuffer
filename: string
}

type StructuredCloneableFormData = StructuredCloneableFormDataEntry[]

Expand All @@ -96,7 +104,9 @@ function reconstructFormData(
): FormData {
const result = new FormData()
structuredCloneableFormData.forEach(entry => {
result.append(entry.name, entry.value)
entry.type === 'file'
? result.append(entry.name, Buffer.from(entry.value), entry.filename)
: result.append(entry.name, entry.value)
})
return result
}
Expand Down
50 changes: 37 additions & 13 deletions app/src/redux/shell/remote.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// access main process remote modules via attachments to `global`
import type { AxiosRequestConfig } from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import type { ResponsePromise } from '@opentrons/api-client'
import type { Remote, NotifyTopic, NotifyResponseData } from './types'

Expand All @@ -20,31 +20,55 @@ export const remote: Remote = new Proxy(emptyRemote, {
},
})

interface StructuredCloneableFormDataEntry {
name: string
value: string | Blob | File
}
type StructuredCloneableFormDataEntry =
| {
type: 'string'
name: string
value: string
}
| {
type: 'file'
name: string
value: ArrayBuffer
filename: string
}

type StructuredCloneableFormData = StructuredCloneableFormDataEntry[]

function proxyFormData(formData: FormData): StructuredCloneableFormData {
return [...formData.entries()].map(([name, value]) => {
return { name, value }
})
async function proxyFormData(
formData: FormData
): Promise<StructuredCloneableFormData> {
const result: StructuredCloneableFormData = []
for (const [name, value] of formData.entries()) {
if (value instanceof File) {
result.push({
type: 'file',
name,
value: await value.arrayBuffer(),
filename: value.name,
})
} else {
result.push({ type: 'string', name, value })
}
}

return result
}

export function appShellRequestor<Data>(
export async function appShellRequestor<Data>(
config: AxiosRequestConfig
): ResponsePromise<Data> {
): Promise<AxiosResponse<Data>> {
const { data } = config
// Special case: FormData objects can't be sent through invoke().
// Convert it to a structured-cloneable object so it can be.
// app-shell will convert it back.
const formDataProxy =
data instanceof FormData ? { proxiedFormData: proxyFormData(data) } : data
data instanceof FormData
? { proxiedFormData: await proxyFormData(data) }
: data
const configProxy = { ...config, data: formDataProxy }

return remote.ipcRenderer.invoke('usb:request', configProxy)
return await remote.ipcRenderer.invoke('usb:request', configProxy)
SyntaxColoring marked this conversation as resolved.
Show resolved Hide resolved
}

interface CallbackStore {
Expand Down