Skip to content

Commit 3621cbb

Browse files
frontend: add custom SERVO_FUNCTION widgets
1 parent 644d381 commit 3621cbb

File tree

6 files changed

+556
-35
lines changed

6 files changed

+556
-35
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<template>
2+
<div class="d-flex align-center">
3+
<v-row>
4+
<v-col cols="6">
5+
<span class="text-h6">Increment</span>
6+
</v-col>
7+
<v-col cols="6">
8+
<inline-parameter-editor
9+
:label="actuator_inc_param?.name"
10+
:param="actuator_inc_param"
11+
/>
12+
</v-col>
13+
<v-row>
14+
<v-col cols="12">
15+
<v-alert
16+
v-if="!has_joystick_function_configured"
17+
type="error"
18+
>
19+
<span>Joystick functions not configured for this actuator.<br />
20+
Please configure a joystick button for this actuator using your GCS of choice.</span>
21+
</v-alert>
22+
</v-col>
23+
</v-row>
24+
<servo-function-range-editor
25+
:param="param"
26+
/>
27+
</v-row>
28+
</div>
29+
</template>
30+
31+
<script lang="ts">
32+
import Vue from 'vue'
33+
34+
import autopilot from '@/store/autopilot'
35+
import Parameter from '@/types/autopilot/parameter'
36+
37+
export default Vue.extend({
38+
name: 'ServoFunctionActuatorEditor',
39+
props: {
40+
param: {
41+
type: Object as () => Parameter,
42+
required: true,
43+
},
44+
},
45+
computed: {
46+
actuator_number(): number | undefined {
47+
const option_name = this.param.options?.[this.param.value] as string
48+
const actuator_number = option_name?.match(/\d+/)?.[0]
49+
if (!actuator_number) return undefined
50+
return parseInt(actuator_number, 10)
51+
},
52+
actuator_inc_param(): Parameter | undefined {
53+
return autopilot.parameter(`ACTUATOR${this.actuator_number}_INC`)
54+
},
55+
btn_params(): Parameter[] {
56+
// returns all JS button parameters
57+
return autopilot.parameterRegex('BTN(\\d+)_(S?)FUNCTION') as Parameter[]
58+
},
59+
options(): Record<string, string> {
60+
return this.btn_params[0]?.options as Record<string, string>
61+
},
62+
this_actuator_functions(): number[] {
63+
// returns the joystick button functions for the current actuator
64+
return Object.entries(this.options)
65+
.filter(([_, value]) => value.startsWith(`actuator_${this.actuator_number}_`))
66+
.map(([key]) => parseInt(key, 10))
67+
},
68+
has_joystick_function_configured(): boolean {
69+
return this.btn_params.some((param) => this.this_actuator_functions.includes(param.value as number))
70+
},
71+
},
72+
})
73+
</script>

core/frontend/src/components/parameter-editor/ServoFunctionEditorDialog.vue

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
:param="param"
2424
/>
2525

26-
<servo-function-range-editor :param="param" />
26+
<component
27+
:is="function_type"
28+
v-if="function_type"
29+
:param="param"
30+
/>
2731
</v-card-text>
2832

2933
<v-card-actions>
@@ -40,18 +44,26 @@
4044
</template>
4145

4246
<script lang="ts">
43-
import Vue from 'vue'
47+
import Vue, { VueConstructor } from 'vue'
4448
4549
import Parameter from '@/types/autopilot/parameter'
4650
4751
import InlineParameterEditor from './InlineParameterEditor.vue'
52+
import ServoFunctionActuatorEditor from './ServoFunctionActuatorEditor.vue'
53+
import ServoFunctionGpioEditor from './ServoFunctionGpioEditor.vue'
54+
import ServoFunctionLightsEditor from './ServoFunctionLightsEditor.vue'
55+
import ServoFunctionMotorEditor from './ServoFunctionMotorEditor.vue'
4856
import ServoFunctionRangeEditor from './ServoFunctionRangeEditor.vue'
4957
5058
export default Vue.extend({
5159
name: 'ServoFunctionEditorDialog',
5260
components: {
5361
InlineParameterEditor,
5462
ServoFunctionRangeEditor,
63+
ServoFunctionMotorEditor,
64+
ServoFunctionGpioEditor,
65+
ServoFunctionActuatorEditor,
66+
ServoFunctionLightsEditor,
5567
},
5668
model: {
5769
prop: 'value',
@@ -67,5 +79,26 @@ export default Vue.extend({
6779
required: true,
6880
},
6981
},
82+
computed: {
83+
function_type(): VueConstructor<Vue> | undefined {
84+
const name = this.param.options?.[this.param.value]
85+
if (name?.toLowerCase().includes('motor')) {
86+
return ServoFunctionMotorEditor
87+
}
88+
if (name?.toLowerCase().includes('gpio')) {
89+
return ServoFunctionGpioEditor
90+
}
91+
if (name?.toLowerCase().includes('actuator')) {
92+
return ServoFunctionActuatorEditor
93+
}
94+
if (name?.toLowerCase().includes('lights')) {
95+
return ServoFunctionLightsEditor
96+
}
97+
if (name?.toLowerCase().includes('disabled')) {
98+
return undefined
99+
}
100+
return ServoFunctionRangeEditor
101+
},
102+
},
70103
})
71104
</script>
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<template>
2+
<div class="align-center">
3+
GPIOs can be used for Relays, Leak detection, and other functions not yet supported by this UI.
4+
This servo ({{ servo_number }}) gpio is {{ this_servo_gpio }}.
5+
6+
In use by {{ function_type_using_this_gpio?.name }}
7+
8+
<v-row align="end">
9+
<v-col cols="6">
10+
<span style="font-size: 1.0rem;">Change function for this servo's GPIO</span>
11+
</v-col>
12+
<v-col cols="6">
13+
<v-select
14+
v-model="selected_function_type"
15+
:items="options"
16+
item-text="text"
17+
item-value="value"
18+
return-object
19+
hide-details
20+
@change="onChange"
21+
/>
22+
</v-col>
23+
</v-row>
24+
25+
</div>
26+
</template>
27+
28+
<script lang="ts">
29+
import Vue from 'vue'
30+
31+
import mavlink2rest from '@/libs/MAVLink2Rest'
32+
import autopilot_data from '@/store/autopilot'
33+
import autopilot from '@/store/autopilot_manager'
34+
import Parameter from '@/types/autopilot/parameter'
35+
36+
export default Vue.extend({
37+
name: 'ServoFunctionGpioEditor',
38+
props: {
39+
param: {
40+
type: Object as () => Parameter,
41+
required: true,
42+
},
43+
},
44+
data() {
45+
return {
46+
selected_function_type: undefined as {text: string, value: Parameter, disabled?: boolean} | undefined,
47+
}
48+
},
49+
computed: {
50+
pin_parameters(): Parameter[] {
51+
return autopilot_data.parameterRegex('^.*_PIN$') as Parameter[]
52+
},
53+
servo_number(): number | undefined {
54+
const option_name = this.param.name.match(/SERVO(\d+)_FUNCTION/)?.[1]
55+
return option_name ? parseInt(option_name, 10) : undefined
56+
},
57+
board_name(): string | undefined {
58+
return autopilot.current_board?.name
59+
},
60+
this_servo_gpio(): number | undefined {
61+
if (!this.servo_number) return undefined
62+
if (this.board_name?.startsWith('Navigator')) {
63+
return this.servo_number
64+
}
65+
// We are assuming most boards follow the same GPIO numbering scheme as the Pixhawk1
66+
// Aux channels start at 50, and Main channels start at 101
67+
68+
if (this.servo_number <= 8) {
69+
return this.servo_number + 100
70+
}
71+
return this.servo_number - 9 + 50
72+
},
73+
function_pin_parameter_using_this_gpio(): Parameter | undefined {
74+
return this.pin_parameters.find((param) => param.value === this.this_servo_gpio)
75+
},
76+
function_type_using_this_gpio(): Parameter | undefined {
77+
const param_name = this.function_pin_parameter_using_this_gpio?.name.split('_')[0]
78+
let new_param_name: string | undefined
79+
if (param_name?.includes('RELAY')) {
80+
new_param_name = `${param_name}_FUNCTION`
81+
}
82+
if (param_name?.includes('LEAK')) {
83+
new_param_name = `${param_name}_TYPE`
84+
}
85+
return new_param_name
86+
? autopilot_data.parameter(new_param_name as string)
87+
: this.function_pin_parameter_using_this_gpio
88+
},
89+
relay_parameters(): Parameter[] {
90+
return autopilot_data.parameterRegex('^RELAY(\\d+)_FUNCTION$') as Parameter[]
91+
},
92+
leak_parameters(): Parameter[] {
93+
return autopilot_data.parameterRegex('^LEAK(\\d+)_TYPE$') as Parameter[]
94+
},
95+
options(): {text: string, value: Parameter, disabled?: boolean}[] {
96+
const supportedOptions: {text: string, value: Parameter, disabled?: boolean}[] = [
97+
...this.relay_parameters,
98+
...this.leak_parameters,
99+
].map((param) => ({
100+
text: param.name.split('_')[0].toLowerCase().toTitle(),
101+
value: param,
102+
}))
103+
// If the current GPIO is used by an unsupported parameter, add it to the options
104+
const pinParam = this.function_pin_parameter_using_this_gpio
105+
if (pinParam && !this.isSupportedParam(pinParam)) {
106+
supportedOptions.unshift({
107+
text: `${pinParam.name} (not supported)`,
108+
value: pinParam,
109+
disabled: true,
110+
})
111+
}
112+
return supportedOptions
113+
},
114+
},
115+
watch: {
116+
function_type_using_this_gpio: {
117+
handler(new_value: Parameter | undefined) {
118+
if (!new_value) {
119+
this.selected_function_type = undefined
120+
return
121+
}
122+
const isSupported = this.isSupportedParam(new_value)
123+
this.selected_function_type = {
124+
text: isSupported
125+
? new_value.name.split('_')[0].toLowerCase().toTitle()
126+
: `${new_value.name} (not supported)`,
127+
value: new_value,
128+
}
129+
},
130+
immediate: true,
131+
},
132+
},
133+
methods: {
134+
isSupportedParam(param: Parameter): boolean {
135+
return param.name.includes('RELAY') || param.name.includes('LEAK')
136+
},
137+
onChange(value: {text: string, value: Parameter}) {
138+
const is_relay = value.value.name.includes('RELAY')
139+
const is_leak = value.value.name.includes('LEAK')
140+
if (!this.this_servo_gpio) {
141+
console.warn('No GPIO found for servo', this.servo_number)
142+
return
143+
}
144+
if (!is_relay && !is_leak) {
145+
// Unsupported parameter type, don't handle
146+
return
147+
}
148+
if (is_relay) {
149+
const paramName = value.value.name
150+
const paramType = value.value.paramType.type
151+
mavlink2rest.setParam(paramName, 1 /* Relay */, autopilot_data.system_id, paramType)
152+
const pin_param_name = paramName.replace('FUNCTION', 'PIN')
153+
const gpio = this.this_servo_gpio
154+
mavlink2rest.setParam(pin_param_name, gpio, autopilot_data.system_id, 'MAV_PARAM_TYPE_INT8')
155+
const pinParam = this.function_pin_parameter_using_this_gpio
156+
if (pinParam) {
157+
mavlink2rest.setParam(pinParam.name, -1, autopilot_data.system_id, 'MAV_PARAM_TYPE_INT8')
158+
}
159+
}
160+
if (is_leak) {
161+
const paramName = value.value.name
162+
const paramType = value.value.paramType.type
163+
mavlink2rest.setParam(paramName, 1 /* Digital */, autopilot_data.system_id, paramType)
164+
const pin_param_name = paramName.replace('TYPE', 'PIN')
165+
const gpio = this.this_servo_gpio
166+
mavlink2rest.setParam(pin_param_name, gpio, autopilot_data.system_id, 'MAV_PARAM_TYPE_INT8')
167+
const pinParam = this.function_pin_parameter_using_this_gpio
168+
if (pinParam) {
169+
mavlink2rest.setParam(pinParam.name, -1, autopilot_data.system_id, 'MAV_PARAM_TYPE_INT8')
170+
}
171+
}
172+
},
173+
},
174+
})
175+
</script>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<template>
2+
<div class="d-flex align-center">
3+
<v-row>
4+
<v-col cols="6">
5+
<span class="text-h6">Joystick Steps</span>
6+
</v-col>
7+
<v-col cols="6">
8+
<inline-parameter-editor
9+
:label="steps_param?.name"
10+
:param="steps_param"
11+
/>
12+
</v-col>
13+
<servo-function-range-editor
14+
:param="param"
15+
/>
16+
</v-row>
17+
</div>
18+
</template>
19+
20+
<script lang="ts">
21+
import Vue from 'vue'
22+
23+
import autopilot from '@/store/autopilot'
24+
import Parameter from '@/types/autopilot/parameter'
25+
26+
import InlineParameterEditor from './InlineParameterEditor.vue'
27+
import ServoFunctionRangeEditor from './ServoFunctionRangeEditor.vue'
28+
29+
export default Vue.extend({
30+
name: 'ServoFunctionLightsEditor',
31+
components: {
32+
InlineParameterEditor,
33+
ServoFunctionRangeEditor,
34+
},
35+
props: {
36+
param: {
37+
type: Object as () => Parameter,
38+
required: true,
39+
},
40+
},
41+
computed: {
42+
steps_param(): Parameter | undefined {
43+
return autopilot.parameter('JS_LIGHTS_STEPS')
44+
},
45+
},
46+
})
47+
</script>

0 commit comments

Comments
 (0)