Skip to content

Commit

Permalink
Merge pull request #59 from robocup-logistics/tviehmann/config-refine…
Browse files Browse the repository at this point in the history
…ments

Configuration Options for Referees
  • Loading branch information
TarikViehmann authored Mar 12, 2024
2 parents 538b61a + cbe8698 commit f352bd0
Show file tree
Hide file tree
Showing 19 changed files with 473 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Follow these steps to get the frontend running locally instead
```
yarn install
yarn run build
yarn run serve test
yarn run serve
```

## Encountered an issue?
Expand Down
19 changes: 19 additions & 0 deletions src/components/referee/LogoAndActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
<div class="content horizontal-flex">
<img src="@/assets/robocupLogo.png" alt="Refbox Logo" />
<div class="horizontal-flex">
<PopupWrapper popup-position="bottom">
<template #reference>
<PillButton description="Reset"
title="Restart the refbox, requires re-connect of frontend">
<font-awesome-icon icon="fa-arrow-rotate-left" />
</PillButton>
</template>
<ConfirmResetPopup />
</PopupWrapper>
<PillButton
description="Menu"
title="Go back to the start menu"
Expand Down Expand Up @@ -43,6 +52,14 @@
</template>
<GameConfigPopup />
</PopupWrapper>
<PopupWrapper popup-position="bottom">
<template #reference>
<PillButton description="Presets" title="Set Config Presets">
<font-awesome-icon icon="fa-gears" />
</PillButton>
</template>
<ConfigPresetsPopup />
</PopupWrapper>
<PopupWrapper popup-position="bottom">
<template #reference>
<PillButton description="Field" title="Randomize Field">
Expand Down Expand Up @@ -73,7 +90,9 @@ import PopupWrapper from '@/components/shared/ui/PopupWrapper.vue'
import ConfirmFieldRandomizationPopup from '@/components/referee/popups/ConfirmFieldRandomizationPopup.vue'
import HelpPopup from '@/components/referee/popups/HelpPopup.vue'
import PillButton from '@/components/shared/ui/PillButton.vue'
import ConfirmResetPopup from '@/components/referee/popups/ConfirmResetPopup.vue'
import GameConfigPopup from '@/components/referee/popups/GameConfigPopup.vue'
import ConfigPresetsPopup from '@/components/referee/popups/ConfigPresetsPopup.vue'
import { useSocketStore } from '@/store/socketStore'
import { storeToRefs } from 'pinia'
Expand Down
201 changes: 201 additions & 0 deletions src/components/referee/popups/ConfigPresetsPopup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// TEMPLATE --------------------------------------------------------------------
<template>
<Popup title="Presets" icon="fa-gears">
<TabGroup :tabs="tabs" v-model:active="activeTab" class="sticky">
<template #[tab] v-for="(tab, i) in tabs">
<div class="horizontal-flex">
<font-awesome-icon :icon="tabIcons[i]" />
<p>{{ tab }}</p>
</div>
</template>
</TabGroup>
<template v-if="activeTab == 'challenges'">
<h2>Challange Presets</h2>
<Accordion title="Navigation Challenge" class="accordion content-box">
<div class="selector-row">
<!-- Input field for editing -->
<Select class="selector-field" :options="['easy', 'medium', 'hard']" v-model="selection['navigation']" />
<!-- Button to apply changes -->
<Button class="apply-button" primary @click="applyChanges('navigation')">Apply</Button>
</div>
</Accordion>
<Accordion title="Exploration Challenge" class="accordion content-box">
<div class="selector-row">
<Select class="selector-field" :options="['easy', 'medium', 'hard']" v-model="selection['exploration']" />
<Button class="apply-button" primary @click="applyChanges('exploration')">Apply</Button>
</div>
</Accordion>
<Accordion title="Grasping Challenge" class="accordion content-box">
<div class="selector-row">
<Button class="apply-button" primary @click="applyChanges('grasping')">Apply</Button>
</div>
</Accordion>
<Accordion title="Production Challenge" class="accordion content-box">
<div class="selector-row">
<Select class="selector-field" :options="['c0', 'c1', 'c2', 'c3']" v-model="selection['production']" />
<Select class="selector-field" :options="['ground truth', 'no ground truth']" v-model="groundTruthValues['production']" />
<Button class="apply-button" primary @click="applyChanges('production')">Apply</Button>
</div>
</Accordion>
</template>
<template v-for="(tab, index) in tabs"">
<template v-if="activeTab === tab && activeTab !== 'challenges'">
<h2>{{ capitalizeFirstLetter(tab) }} Presets</h2>
<div class="selector-row">
<Select class="selector-field" :options="getPresetOptions(tab)" v-model="selection[tab]" />
<Button class="apply-button" primary @click="applyChanges(tab)">Apply</Button>
</div>
</template>
</template>

</Popup>
</template>

// SCRIPT ----------------------------------------------------------------------
<script setup lang="ts">
// import - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import Popup from '@/components/shared/ui/Popup.vue'
import TabGroup from '@/components/shared/ui/TabGroup.vue'
import { usePresetStore } from '@/store/presetStore'
import A from '@/components/shared/ui/A.vue'
import { storeToRefs } from 'pinia'
import Accordion from '@/components/shared/ui/Accordion.vue'
import Select from '@/components/shared/ui/Select.vue'
import Input from '@/components/shared/ui/Input.vue'
import Button from '@/components/shared/ui/Button.vue'
import { useKeyboardStore } from '@/store/keyboardStore'
import { type Ref, ref, type ComputedRef, computed, watch } from 'vue'
// use stores - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const presetStore = usePresetStore()
const { configPresets } = storeToRefs(presetStore)
// filter config paths - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const filter: Ref<string> = ref('')
const selection = ref<{ [key: string]: string }>({
exploration: 'hard',
navigation: 'hard',
production: 'c3',
});
const groundTruthValues = ref<{ [key: string]: string }>({
});
// Define a method to capitalize the first letter of a string
const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// Define a method to get preset options based on the category
const getPresetOptions = (category: string) => {
// Filter configPresets to find presets belonging to the specified category
const presetsInCategory = Array.from(configPresets.value)
.filter(preset => preset.category === category);
// Extract unique preset values from the filtered presets
const uniquePresets = [...new Set(presetsInCategory.map(preset => preset.preset))];
// Return the unique preset values as options
return uniquePresets;
}
function applyChanges(category) {
const currSelection = selection._rawValue[category];
if (category === "grasping") {
presetStore.sendSetConfigPreset({ category: "challenges/grasping", preset: "grasping" });
presetStore.sendSetConfigPreset({ category: "challenges/grasping", preset: "grasping_game" });
return;
}
if (currSelection === undefined) {
console.log("Ignoring due to undefined selection");
} else if (category === "exploration") {
const currPreset = `exploration_${currSelection}`;
presetStore.sendSetConfigPreset({ category: "challenges/exp", preset: currPreset });
} else if ( category === "navigation") {
const currPreset = `nav_${currSelection}`;
presetStore.sendSetConfigPreset({ category: "challenges/nav", preset: currPreset });
} else if ( category === "production") {
const currGroundTruth = groundTruthValues._rawValue[category];
if (currGroundTruth === undefined) {
console.log("Ignoring due to undefined selection of ground truth option");
} else if (currGroundTruth === "ground truth") {
presetStore.sendSetConfigPreset({ category: "challenges/prod", preset: currSelection });
} else if (currGroundTruth === "no ground truth") {
presetStore.sendSetConfigPreset({ category: "challenges/prod", preset: currSelection });
} else {
console.log("Ignoring due to unknown ground truth selection", currGroundTruth);
}
} else {
presetStore.sendSetConfigPreset({ category: category, preset: currSelection });
}
}
// tabs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const iconLookupMap = {
challenges: 'fa-medal',
mps: 'fa-industry',
mongodb: 'fa-database',
main: 'fa-house',
game: 'fa-gamepad',
comm: 'fa-walkie-talkie',
team: 'fa-people-group',
simulation: 'fa-atom'
};
const defaultIcon = 'fa-khanda';
//const tabs = Array.from(new Set(Array.from(configPresets._rawValue).map(preset => preset.category)));
const tabs = Array.from(new Set(
Array.from(configPresets._rawValue)
.filter(preset => preset.category !== "") // Exclude entries where category is empty
.map(preset => {
const category = preset.category.startsWith('challenges/') ? 'challenges' : preset.category;
return category;
})
));
//const tabs: string[] = ['challenges','machines', 'mongodb']
const tabIcons = tabs.map(tab => iconLookupMap[tab] || defaultIcon);
const activeTab: Ref<string> = ref('challenges')
const tab = undefined;
</script>

// STYLE -----------------------------------------------------------------------
<style scoped lang="scss">
@use '@/assets/global.scss';
.key-item {
kbd {
display: inline-block;
border-radius: 4px;
padding: 0.3em 0.7em;
border-bottom: 2px solid global.$lighterColor;
box-shadow: 0 1px 0px global.$bgColor, 0 0 0 2px global.$lightestColor inset;
background-color: global.$surfaceColor;
text-transform: capitalize;
}
& + .key-item {
&:before {
content: '+ ';
}
}
}
.tab-group {
align-items: stretch !important;
padding-bottom: 20px;
}
.accordion {
padding: 0;
align-items: stretch !important;
}
.selector-row {
display: flex;
}
.selector-field {
flex: 1;
margin-right: 10px; // Adjust as needed
}
.apply-button {
margin-left: auto; // Align to the right
}
</style>
49 changes: 49 additions & 0 deletions src/components/referee/popups/ConfirmResetPopup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// TEMPLATE --------------------------------------------------------------------
<template>
<Popup title="Reset the refbox?" icon="fa-random">
<div class="horizontal-flex" style="width: 100%">
<Button icon="fa-xmark" @click.prevent="togglePopup()"> Abort </Button>
<Button
primary
icon="fa-check"
v-shortkey.once="(shortcuts.get('confirmPopup') as Shortcut).keys"
@click.prevent="confirmedReset()"
@shortkey="confirmedReset()"
>
Yes
</Button>
</div>
</Popup>
</template>

// SCRIPT ----------------------------------------------------------------------
<script setup lang="ts">
// imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import Popup from '@/components/shared/ui/Popup.vue'
import { inject } from 'vue'
import { useGameStore } from '@/store/gameStore'
import Button from '@/components/shared/ui/Button.vue'
import { useKeyboardStore } from '@/store/keyboardStore'
import { storeToRefs } from 'pinia'
import type Shortcut from '@/types/Shortcut'
// use stores - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const gameStore = useGameStore()
const keyboardStore = useKeyboardStore()
const { shortcuts } = storeToRefs(keyboardStore)
// confirm randomize - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function confirmedReset() {
gameStore.sendReset()
togglePopup()
}
// popup - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// -> close
const togglePopup = inject('togglePopup') as Function
</script>

// STYLE -----------------------------------------------------------------------
<style scoped lang="scss">
@use '@/assets/global.scss';
</style>
33 changes: 32 additions & 1 deletion src/components/referee/popups/GameConfigPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
<Input v-model="filter" placeholder="filter by path" clearable />
</div>
<template v-for="[path, value] in filteredGameConfigValues">
<Accordion :title="path" class="content-box"> {{ value }}</Accordion>
<Accordion :title="`${path}: ${value}`" class="content-box">
<div class="config-row">
<!-- Input field for editing -->
<Input class="edit-field" type="text" v-model="editedValues[path]" placeholder="New Value" clearable />
<!-- Button to apply changes -->
<Button class="apply-button" primary @click="applyChanges(path)">Apply</Button>
</div>
</Accordion>
</template>
</Popup>
</template>
Expand All @@ -25,6 +32,8 @@ import { useConfigStore } from '@/store/configStore'
import { storeToRefs } from 'pinia'
import Accordion from '@/components/shared/ui/Accordion.vue'
import Input from '@/components/shared/ui/Input.vue'
import Button from '@/components/shared/ui/Button.vue'
import { useKeyboardStore } from '@/store/keyboardStore'
import { type Ref, ref, type ComputedRef, computed, watch } from 'vue'
// use stores - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand All @@ -33,6 +42,15 @@ const { gameConfig } = storeToRefs(configStore)
// filter config paths - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const filter: Ref<string> = ref('')
const editedValues = ref({});
function applyChanges(path) {
console.log('editedValues:', editedValues); const newValue = editedValues._rawValue[path];
console.log('Applying changes for', path, 'with value:', newValue);
configStore.sendSetConfigValue({ path: path, value: newValue });
}
const filteredGameConfigValues: ComputedRef<[string, any][]> = computed(() =>
[...gameConfig.value].filter(([path, _]) => path.includes(filter.value))
)
Expand All @@ -57,4 +75,17 @@ watch(
padding: 0;
}
}
.config-row {
display: flex;
align-items: center;
}
.edit-field {
flex: 1;
margin-right: 10px; // Adjust as needed
}
.apply-button {
margin-left: auto; // Align to the right
}
</style>
Loading

0 comments on commit f352bd0

Please sign in to comment.