Skip to content

Commit 80b61ba

Browse files
authored
[Enhancement] Manually Entered on Command Sender (#2906)
* [Enhancement] Manually Entered on Command Sender * Add/fix playwright tests
1 parent 3e4532b commit 80b61ba

File tree

4 files changed

+109
-3
lines changed

4 files changed

+109
-3
lines changed

openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-cmdsender/src/tools/CommandSender/CommandSender.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
:states-in-hex="statesInHex"
2727
:show-ignored-params="showIgnoredParams"
2828
:cmd-raw="cmdRaw"
29+
:ignore-range-checks="ignoreRangeChecks"
2930
@command-changed="commandChanged($event)"
3031
@command-loaded="onCommandLoaded($event)"
3132
@build-cmd="buildCmd($event)"
@@ -411,9 +412,14 @@ export default {
411412
methods: {
412413
convertToValue(param) {
413414
if (param.val !== undefined && param.states && !this.cmdRaw) {
414-
return Object.keys(param.states).find(
415+
const stateName = Object.keys(param.states).find(
415416
(state) => param.states[state].value === param.val,
416417
)
418+
if (stateName !== undefined) {
419+
return stateName
420+
}
421+
// No matching state found (manually entered value), fall through
422+
// to parse the raw value below
417423
}
418424
if (typeof param.val !== 'string') {
419425
return param.val

openc3-cosmos-init/plugins/packages/openc3-vue-common/src/components/CommandEditor.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
:states="item.states"
6868
:states-in-hex="statesInHex"
6969
:disabled="item.disabled"
70+
:allow-manual-entry="ignoreRangeChecks"
7071
@hazardous-change="onParameterHazardousChange(item, $event)"
7172
/>
7273
</slot>
@@ -149,6 +150,10 @@ export default {
149150
type: Boolean,
150151
default: true,
151152
},
153+
ignoreRangeChecks: {
154+
type: Boolean,
155+
default: false,
156+
},
152157
},
153158
emits: ['command-changed', 'build-cmd', 'command-loaded'],
154159
data() {

openc3-cosmos-init/plugins/packages/openc3-vue-common/src/components/CommandParameterEditor.vue

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,29 @@
4040
placeholder="Select..."
4141
min-width="120px"
4242
data-test="cmd-param-select"
43+
@update:model-value="handleStateSelect"
44+
/>
45+
<v-text-field
46+
v-if="manuallyEntered"
47+
:model-value="textFieldValue"
48+
hide-details
49+
density="compact"
50+
variant="outlined"
51+
min-width="60px"
52+
data-test="cmd-param-value"
4353
@update:model-value="handleChange"
4454
/>
55+
<v-tooltip v-if="manuallyEntered" location="top">
56+
<template #activator="{ props }">
57+
<v-icon v-bind="props" class="ml-1">mdi-information-outline</v-icon>
58+
</template>
59+
<span
60+
>Enter a raw value (e.g. numeric) for this parameter.<br />State names
61+
are not accepted here.</span
62+
>
63+
</v-tooltip>
4564
<v-text-field
65+
v-else
4666
:model-value="stateValue"
4767
disabled
4868
hide-details
@@ -77,8 +97,17 @@ export default {
7797
type: Boolean,
7898
default: false,
7999
},
100+
allowManualEntry: {
101+
type: Boolean,
102+
default: false,
103+
},
80104
},
81105
emits: ['update:modelValue', 'hazardous-change'],
106+
data() {
107+
return {
108+
manuallyEntered: false,
109+
}
110+
},
82111
computed: {
83112
textFieldValue() {
84113
return this.convertToString(this.modelValue)
@@ -91,19 +120,26 @@ export default {
91120
}
92121
},
93122
selectValue() {
123+
if (this.manuallyEntered) {
124+
return '__manual__'
125+
}
94126
// this makes the placeholder prop work
95127
return this.modelValue === '' ? null : this.modelValue
96128
},
97129
stateOptions() {
98130
if (!this.states) {
99131
return null
100132
}
101-
return Object.keys(this.states).map((label) => {
133+
const options = Object.keys(this.states).map((label) => {
102134
return {
103135
label,
104136
...this.states[label],
105137
}
106138
})
139+
if (this.allowManualEntry) {
140+
options.push({ label: 'MANUALLY_ENTERED', value: '__manual__' })
141+
}
142+
return options
107143
},
108144
hazardous() {
109145
if (!this.states) {
@@ -126,11 +162,25 @@ export default {
126162
this.$emit('hazardous-change', newVal)
127163
},
128164
},
165+
allowManualEntry(newVal) {
166+
if (!newVal) {
167+
this.manuallyEntered = false
168+
}
169+
},
129170
},
130171
methods: {
131172
handleChange(value) {
132173
this.$emit('update:modelValue', value)
133174
},
175+
handleStateSelect(value) {
176+
if (value === '__manual__') {
177+
this.manuallyEntered = true
178+
this.$emit('update:modelValue', '')
179+
} else {
180+
this.manuallyEntered = false
181+
this.$emit('update:modelValue', value)
182+
}
183+
},
134184
},
135185
}
136186
</script>

playwright/tests/command-sender.p.spec.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ test('warns for required parameters', async ({ page, utils }) => {
169169
await expect(page.locator('.v-dialog')).toContainText('Error sending')
170170
await expect(page.locator('.v-dialog')).toContainText('INST COLLECT TYPE')
171171
await expect(page.locator('.v-dialog')).toContainText(
172-
"Required command parameter 'INST COLLECT TYPE' not given",
172+
"not one of NORMAL, SPECIAL",
173173
)
174174
await page.locator('button:has-text("Ok")').click()
175175
})
@@ -750,3 +750,48 @@ for (const target of ['INST', 'INST2']) {
750750
.toMatch(/\[\s*4,\s*5\s*\]/)
751751
})
752752
}
753+
754+
test('sends manually entered state values', async ({ page, utils }) => {
755+
await page.locator('[data-test="clear-history"]').click()
756+
await utils.selectTargetPacketItem('INST', 'COLLECT')
757+
await expect(page.locator('main')).toContainText(
758+
'Starts a collect on the INST target',
759+
)
760+
761+
// Enable Ignore Range Checks to unlock manual entry
762+
await page.locator('[data-test=command-sender-mode]').click()
763+
await page.getByText('Ignore Range Checks').click()
764+
await page.locator('[data-test=command-sender-mode]').click()
765+
766+
// Select MANUALLY_ENTERED from the TYPE state dropdown
767+
let row = page.locator('tr:has(td:text-is("TYPE"))')
768+
await row.locator('[data-test=cmd-param-select]').click({ force: true })
769+
await page.getByRole('option', { name: 'MANUALLY_ENTERED' }).click()
770+
771+
// Enter a raw numeric value in the manual entry text field
772+
await row.locator('[data-test=cmd-param-value] input').first().fill('0')
773+
await row.locator('[data-test=cmd-param-value] input').first().press('Enter')
774+
775+
// Send the command
776+
await page.locator('[data-test="select-send"]').click()
777+
await expect(page.locator('main')).toContainText(
778+
'cmd_no_range_check("INST COLLECT with TYPE 0, DURATION 1, OPCODE 171, TEMP 0") sent',
779+
)
780+
await checkHistory(
781+
page,
782+
'cmd_no_range_check("INST COLLECT with TYPE 0, DURATION 1, OPCODE 171, TEMP 0")',
783+
)
784+
785+
// Disable Ignore Range Checks and verify MANUALLY_ENTERED option is gone
786+
await page.locator('[data-test=command-sender-mode]').click()
787+
await page.getByText('Ignore Range Checks').click()
788+
await page.locator('[data-test=command-sender-mode]').click()
789+
790+
// The TYPE dropdown should no longer have MANUALLY_ENTERED
791+
await row.locator('[data-test=cmd-param-select]').click({ force: true })
792+
await expect(
793+
page.getByRole('option', { name: 'MANUALLY_ENTERED' }),
794+
).not.toBeVisible()
795+
// Close the dropdown by pressing Escape
796+
await page.keyboard.press('Escape')
797+
})

0 commit comments

Comments
 (0)