Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@
<v-row no-gutters>
<v-col>Filename:</v-col>
<v-col>
<input type="file" @change="selectRawCmdFile($event)" />
<input
type="file"
data-test="send-raw-file"
@change="selectRawCmdFile($event)"
/>
</v-col>
</v-row>
<v-row>
Expand Down Expand Up @@ -265,18 +269,17 @@ export default {
computed: {
menus: function () {
return [
// TODO: Implement send raw
// {
// label: 'File',
// items: [
// {
// label: 'Send Raw',
// command: () => {
// this.setupRawCmd()
// },
// },
// ],
// },
{
label: 'File',
items: [
{
label: 'Send Raw File',
command: () => {
this.setupRawCmd()
},
},
],
},
{
label: 'Mode',
items: [
Expand Down Expand Up @@ -863,71 +866,71 @@ export default {
})
},

// setupRawCmd() {
// this.api.get_interface_names().then(
// (response) => {
// let interfaces = []
// for (let i = 0; i < response.length; i++) {
// interfaces.push({ label: response[i], value: response[i] })
// }
// this.interfaces = interfaces
// this.selectedInterface = interfaces[0].value
// this.displaySendRaw = true
// },
// (error) => {
// this.displaySendRaw = false
// this.displayError('getting interface names', error, true)
// }
// )
// },

// selectRawCmdFile(event) {
// this.rawCmdFile = event.target.files[0]
// },

// onLoad(event) {
// let bufView = new Uint8Array(event.target.result)
// let jstr = { json_class: 'String', raw: [] }
// for (let i = 0; i < bufView.length; i++) {
// jstr.raw.push(bufView[i])
// }

// this.api.send_raw(this.selectedInterface, jstr).then(
// () => {
// this.displaySendRaw = false
// this.status =
// 'Sent ' +
// bufView.length +
// ' bytes to interface ' +
// this.selectedInterface
// },
// (error) => {
// this.displaySendRaw = false
// this.displayError('sending raw data', error, true)
// }
// )
// },
setupRawCmd() {
this.api.get_interface_names().then(
(response) => {
const interfaces = response.map((name) => ({
label: name,
value: name,
}))
this.interfaces = interfaces
this.selectedInterface = interfaces.length ? interfaces[0].value : ''
this.rawCmdFile = null
this.displaySendRaw = true
},
(error) => {
this.displaySendRaw = false
this.displayError('getting interface names', error, true)
},
)
},

// sendRawCmd() {
// let self = this
// let reader = new FileReader()
// reader.onload = function (e) {
// self.onLoad(e)
// }
// reader.onerror = function (e) {
// self.displaySendRaw = false
// let target = e.target
// self.displayError('sending raw data', target.error, true)
// }
// // TBD - use the other event handlers to implement a progress bar for the
// // file upload. Handle abort as well?
// //reader.onloadstart = function(e) {}
// //reader.onprogress = function(e) {}
// //reader.onloadend = function(e) {}
// //reader.onabort = function(e) {}
selectRawCmdFile(event) {
this.rawCmdFile = event.target.files[0]
},

// reader.readAsArrayBuffer(this.rawCmdFile)
// },
async sendRawCmd() {
if (!this.selectedInterface) {
this.displayError(
'sending raw data',
{ name: 'Error', message: 'No interface selected' },
true,
)
return
}
if (!this.rawCmdFile) {
this.displayError(
'sending raw data',
{ name: 'Error', message: 'No file selected' },
true,
)
return
}
let bufView
try {
const buffer = await this.rawCmdFile.arrayBuffer()
bufView = new Uint8Array(buffer)
} catch (err) {
this.displaySendRaw = false
this.displayError('sending raw data', err, true)
return
}
const jstr = { json_class: 'String', raw: Array.from(bufView) }
this.api.send_raw(this.selectedInterface, jstr).then(
() => {
this.displaySendRaw = false
this.status =
'Sent ' +
bufView.length +
' bytes to interface ' +
this.selectedInterface
},
(error) => {
this.displaySendRaw = false
this.displayError('sending raw data', error, true)
},
)
},

cancelRawCmd() {
this.displaySendRaw = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,22 @@
<v-list-item
v-for="(item, index) in contextMenuOptions"
:key="index"
:data-test="item.dataTest"
@click.stop="item.action"
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>

<input
ref="blockUploadInput"
type="file"
style="display: none"
data-test="block-upload-input"
@change="onBlockUploadFile($event)"
/>

<details-dialog
v-model="viewDetails"
:target-name="targetName"
Expand Down Expand Up @@ -182,23 +191,38 @@ export default {
contextMenuShown: false,
viewDetails: false,
parameterName: '',
selectedRow: null,
hazardousParameters: {},
x: 0,
y: 0,
contextMenuOptions: [
}
},
computed: {
hasHazardousParameter() {
return Object.values(this.hazardousParameters).some((v) => v === true)
},
contextMenuOptions() {
const options = [
{
title: 'Details',
dataTest: 'cmd-param-details',
action: () => {
this.contextMenuShown = false
this.viewDetails = true
},
},
],
}
},
computed: {
hasHazardousParameter() {
return Object.values(this.hazardousParameters).some((v) => v === true)
]
if (this.selectedRow && this.selectedRow.type === 'BLOCK') {
options.push({
title: 'Upload Data',
dataTest: 'cmd-param-upload-data',
action: () => {
this.contextMenuShown = false
this.openBlockUploadPicker()
},
})
}
return options
},
},
created() {
Expand Down Expand Up @@ -232,13 +256,40 @@ export default {
showContextMenu(event, row) {
event.preventDefault()
this.parameterName = row.item.parameter_name
this.selectedRow = row.item
this.contextMenuShown = false
this.x = event.clientX
this.y = event.clientY
this.$nextTick(() => {
this.contextMenuShown = true
})
},
openBlockUploadPicker() {
const input = this.$refs.blockUploadInput
if (!input) return
// Reset so re-selecting the same file still fires @change
input.value = ''
input.click()
},
async onBlockUploadFile(event) {
const file = event.target.files && event.target.files[0]
if (!file || !this.selectedRow) return
const targetRow = this.selectedRow
try {
const buffer = await file.arrayBuffer()
const bytes = new Uint8Array(buffer)
let hex = '0x'
for (const byte of bytes) {
hex += byte.toString(16).padStart(2, '0').toUpperCase()
}
// Setting val on the row updates the bound CommandParameterEditor.
// The "0x..." prefix lets convertToValue parse it as a binary BLOCK.
targetRow.val = hex
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error reading uploaded file:', err)
}
},
updateCmdParams() {
this.ignoredParams = []
this.computedRows = []
Expand Down
59 changes: 59 additions & 0 deletions playwright/tests/command-sender.p.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
Expand Down Expand Up @@ -760,3 +760,62 @@
// Close the dropdown by pressing Escape
await page.keyboard.press('Escape')
})

test('uploads data into a BLOCK parameter via right-click', async ({
page,
utils,
}) => {
await page.locator('[data-test="clear-history"]').click()
// INST MEMLOAD has a single BLOCK parameter named DATA
await utils.selectTargetPacketItem('INST', 'MEMLOAD')
await expect(page.locator('main')).toContainText('Parameters')

const dataRow = page.locator('tr:has(td:text-is("DATA"))')

// Set up the file chooser before right-clicking so the picker is captured
const fileChooserPromise = page.waitForEvent('filechooser')

// Right-click on the DATA row to open the context menu
await dataRow.click({ button: 'right' })
// The new Upload Data entry should be present for BLOCK parameters
const uploadItem = page.locator('[data-test=cmd-param-upload-data]')
await expect(uploadItem).toBeVisible()
await uploadItem.click()

// Provide a small payload via the file chooser
const fileChooser = await fileChooserPromise
await fileChooser.setFiles({
name: 'upload.bin',
mimeType: 'application/octet-stream',
buffer: Buffer.from([0xde, 0xad, 0xbe, 0xef]),
})

// The BLOCK text field should now show the hex representation
await expect(
dataRow.locator('[data-test=cmd-param-value] input').first(),
).toHaveValue('0xDEADBEEF')

// Non-BLOCK parameters should not see Upload Data
await utils.selectTargetPacketItem('INST', 'COLLECT')
await page.locator('tr:has(td:text-is("DURATION"))').click({ button: 'right' })
await expect(
page.locator('[data-test=cmd-param-upload-data]'),
).not.toBeVisible()
await page.keyboard.press('Escape')
})

test('opens the Send Raw File dialog and lists interfaces', async ({
page,
}) => {
// Open File > Send Raw File from the top bar
await page.locator('[data-test=command-sender-file]').click()

Check failure on line 811 in playwright/tests/command-sender.p.spec.ts

View workflow job for this annotation

GitHub Actions / openc3-build-test

[chromium] › tests/command-sender.p.spec.ts:807:5 › opens the Send Raw File dialog and lists interfaces

1) [chromium] › tests/command-sender.p.spec.ts:807:5 › opens the Send Raw File dialog and lists interfaces Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: locator.click: Test timeout of 60000ms exceeded. Call log: - waiting for locator('[data-test=command-sender-file]') 809 | }) => { 810 | // Open File > Send Raw File from the top bar > 811 | await page.locator('[data-test=command-sender-file]').click() | ^ 812 | await page.locator('[data-test=command-sender-file-send-raw-file]').click() 813 | 814 | // Dialog should be visible and show the interface picker at /home/runner/work/cosmos/cosmos/playwright/tests/command-sender.p.spec.ts:811:57

Check failure on line 811 in playwright/tests/command-sender.p.spec.ts

View workflow job for this annotation

GitHub Actions / openc3-build-test

[chromium] › tests/command-sender.p.spec.ts:807:5 › opens the Send Raw File dialog and lists interfaces

1) [chromium] › tests/command-sender.p.spec.ts:807:5 › opens the Send Raw File dialog and lists interfaces Error: locator.click: Test timeout of 60000ms exceeded. Call log: - waiting for locator('[data-test=command-sender-file]') 809 | }) => { 810 | // Open File > Send Raw File from the top bar > 811 | await page.locator('[data-test=command-sender-file]').click() | ^ 812 | await page.locator('[data-test=command-sender-file-send-raw-file]').click() 813 | 814 | // Dialog should be visible and show the interface picker at /home/runner/work/cosmos/cosmos/playwright/tests/command-sender.p.spec.ts:811:57
await page.locator('[data-test=command-sender-file-send-raw-file]').click()

// Dialog should be visible and show the interface picker
await expect(page.getByText('Send Raw')).toBeVisible()
await expect(page.locator('[data-test=send-raw-file]')).toBeVisible()

// Cancel and confirm status is updated
await page.getByRole('button', { name: 'Cancel' }).click()
await expect(page.locator('main')).toContainText('Raw command not sent')
})
Loading