Advanced ui (#105)

* added keyboard event and logger package

* added logger

* finalized logger

* added object diff filter and undoRedo mixin

* connected undo redo system and cleanup

* updated vuetify version

* [WIP] undo redo integration

* working rollback system

* finished rollback sub components

* finished rollback integration

* lint fix to all client
This commit is contained in:
Ori agranat
2020-06-26 04:39:14 -07:00
committed by GitHub
parent f92bf670de
commit b28d0e046e
31 changed files with 2799 additions and 2059 deletions

View File

@@ -4,7 +4,7 @@ module.exports = {
node: true
},
'extends': [
'plugin:vue/essential',
'plugin:vue/recommended',
'eslint:recommended'
],
rules: {

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,8 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@femessage/log-viewer": "^1.4.1",
"@vue/eslint-config-prettier": "^6.0.0",
"axios": "^0.19.2",
"core-js": "^2.6.11",
"downloadjs": "^1.4.7",
@@ -16,15 +18,15 @@
"vue": "^2.6.11",
"vue-axios": "^2.1.5",
"vue-native-websocket": "^2.0.14",
"vue-router": "^3.1.6",
"vuetify": "^2.2.26",
"vuex": "^3.3.0"
"vue-router": "^3.3.2",
"vuetify": "^2.2.34",
"vuex": "^3.4.0"
},
"devDependencies": {
"@mdi/font": "^4.9.95",
"@vue/cli-plugin-babel": "^3.12.1",
"@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-service": "^4.3.1",
"@vue/cli-plugin-eslint": "^4.4.1",
"@vue/cli-service": "^4.4.1",
"babel-eslint": "^10.1.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
@@ -33,6 +35,6 @@
"sass-loader": "^7.1.0",
"vue-cli-plugin-vuetify": "^0.6.3",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.4.3"
"vuetify-loader": "^1.4.4"
}
}

View File

@@ -1,37 +1,123 @@
<template>
<v-app>
<v-app-bar app dense clipped-left dark>
<img class="imgClass" src="./assets/logo.png">
<v-toolbar-title id="title">Chameleon Vision</v-toolbar-title>
<div class="flex-grow-1"></div>
<v-toolbar-items>
<v-tabs background-color="#272727" dark height="48" slider-color="#4baf62">
<v-tab to="vision">Vision</v-tab>
<v-tab to="settings">Settings</v-tab>
</v-tabs>
</v-toolbar-items>
</v-app-bar>
<v-content>
<v-container fluid fill-height>
<v-layout>
<v-flex>
<router-view @save="startTimer"/>
<v-snackbar :timeout="1000" v-model="saveSnackbar" top color="#4baf62">
<div style="text-align: center;width: 100%;">
<h4>Saved All changes</h4>
</div>
</v-snackbar>
</v-flex>
</v-layout>
</v-container>
</v-content>
</v-app>
<v-app>
<v-app-bar
app
dense
clipped-left
dark
>
<img
class="imgClass"
src="./assets/logo.png"
>
<v-toolbar-title id="title">
Chameleon Vision
</v-toolbar-title>
<div class="flex-grow-1" />
<v-toolbar-items>
<v-tabs
background-color="#272727"
dark
height="48"
slider-color="#4baf62"
>
<v-tab to="vision">
Vision
</v-tab>
<v-tab to="settings">
Settings
</v-tab>
</v-tabs>
</v-toolbar-items>
</v-app-bar>
<v-content>
<v-container
fluid
fill-height
>
<v-layout>
<v-flex>
<router-view @save="startTimer" />
<v-snackbar
v-model="saveSnackbar"
:timeout="1000"
top
color="#4baf62"
>
<div style="text-align: center;width: 100%;">
<h4>Saved All changes</h4>
</div>
</v-snackbar>
<div v-if="isLogger">
<keep-alive>
<log-view
class="loggerClass"
:log="log"
/>
</keep-alive>
</div>
</v-flex>
</v-layout>
</v-container>
</v-content>
</v-app>
</template>
<script>
import logView from '@femessage/log-viewer'
export default {
name: 'App',
components: {},
components: {
logView
},
data: () => ({
timer: undefined,
isLogger: false,
log: ""
}),
computed: {
saveSnackbar: {
get() {
return this.$store.state.saveBar;
},
set(value) {
this.$store.commit("saveBar", value);
}
}
},
created() {
document.addEventListener("keydown", e => {
switch (e.key) {
case '`' :
this.isLogger = !this.isLogger;
break;
case "z":
if (e.ctrlKey && this.$store.getters.canUndo) {
this.$store.dispatch('undo', {vm: this});
}
break;
case "y":
if (e.ctrlKey && this.$store.getters.canRedo) {
this.$store.dispatch('redo', {vm: this});
}
break;
}
});
this.$options.sockets.onmessage = (data) => {
try {
let message = this.$msgPack.decode(data.data);
for (let prop in message) {
if (message.hasOwnProperty(prop)) {
this.handleMessage(prop, message[prop]);
}
}
} catch (error) {
console.error('error: ' + data.data + " , " + error);
}
}
},
methods: {
handleMessage(key, value) {
if (this.$store.state.hasOwnProperty(key)) {
@@ -56,33 +142,11 @@
clearInterval(this.timer);
}
this.timer = setInterval(this.saveSettings, 4000);
}
},
data: () => ({
timer: undefined
}),
created() {
this.$options.sockets.onmessage = (data) => {
try {
let message = this.$msgPack.decode(data.data);
for (let prop in message) {
if (message.hasOwnProperty(prop)) {
this.handleMessage(prop, message[prop]);
}
}
} catch (error) {
console.error('error: ' + data.data + " , " + error);
}
}
},
computed: {
saveSnackbar: {
get() {
return this.$store.state.saveBar;
},
set(value) {
this.$store.commit("saveBar", value);
}
},
logMessage({message, level}) {
const colors = ["\u001b[31m", "\u001b[32m", "\u001b[33m", "\u001b[34m"]
const reset = "\u001b[0m"
this.log += `${colors[level]}${message}${reset}\n`
}
}
};
@@ -100,6 +164,32 @@
padding-right: 5px;
}
.loggerClass {
position: absolute;
bottom: 0;
height: 25% !important;
left: 0;
right: 0;
box-shadow: #282828 0 0 5px 1px;
background-color: #2b2b2b;
}
::-webkit-scrollbar {
width: 0.5em;
border-radius: 5px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #4baf62;
border-radius: 10px;
}
.container {
background-color: #212121;
padding: 0 !important;

View File

@@ -1,12 +1,23 @@
<template>
<div>
<v-tooltip :right="right" :bottom="!right" nudge-right="10">
<template v-slot:activator="{ on }">
<v-icon :class="hoverClass" @click="handleClick" v-on="on" :color="color">{{text}}</v-icon>
</template>
<span>{{tooltip}}</span>
</v-tooltip>
</div>
<div>
<v-tooltip
:right="right"
:bottom="!right"
nudge-right="10"
>
<template v-slot:activator="{ on }">
<v-icon
:class="hoverClass"
:color="color"
@click="handleClick"
v-on="on"
>
{{ text }}
</v-icon>
</template>
<span>{{ tooltip }}</span>
</v-tooltip>
</div>
</template>
<script>
@@ -16,11 +27,6 @@
data() {
return {}
},
methods: {
handleClick() {
this.$emit('click');
}
},
computed: {
hoverClass: {
get() {
@@ -31,6 +37,11 @@
}
}
},
methods: {
handleClick() {
this.$emit('click');
}
},
}
</script>

View File

@@ -1,16 +1,21 @@
<template>
<img id="CameraStream" v-bind:style="styleObject" :src="address" @click="e => $emit('click', e)"
crossorigin="Anonymous" alt=""/>
<img
id="CameraStream"
:style="styleObject"
:src="address"
crossorigin="Anonymous"
alt=""
@click="e => $emit('click', e)"
>
</template>
<script>
export default {
name: "cv-image",
name: "CvImage",
props: ['address', 'scale'],
data: () => {
return {}
},
props: ['address', 'scale'],
computed: {
styleObject: {
get() {

View File

@@ -1,15 +1,24 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="3">
<span>{{name}}</span>
</v-col>
<v-col :cols="9">
<v-text-field @keydown="handleKeyboard" dark v-model="localValue" dense :disabled="disabled"
:error-messages="errorMessage"/>
</v-col>
</v-row>
</div>
<div>
<v-row
dense
align="center"
>
<v-col :cols="3">
<span>{{ name }}</span>
</v-col>
<v-col :cols="9">
<v-text-field
v-model="localValue"
dark
dense
:disabled="disabled"
:error-messages="errorMessage"
@keydown="handleKeyboard"
/>
</v-col>
</v-row>
</div>
</template>
s
<script>
@@ -19,13 +28,6 @@ s
data() {
return {}
},
methods: {
handleKeyboard(event) {
if (event.key === "Enter") {
this.$emit("Enter");
}
}
},
computed: {
localValue: {
get() {
@@ -35,6 +37,13 @@ s
this.$emit('input', value);
}
}
},
methods: {
handleKeyboard(event) {
if (event.key === "Enter") {
this.$emit("Enter");
}
}
}
}
</script>

View File

@@ -1,15 +1,26 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="2">
<span>{{name}}</span>
</v-col>
<v-col>
<v-text-field dark v-model="localValue" class="mt-0 pt-0" hide-details single-line type="number"
style="width: 70px" :step="step"/>
</v-col>
</v-row>
</div>
<div>
<v-row
dense
align="center"
>
<v-col :cols="2">
<span>{{ name }}</span>
</v-col>
<v-col>
<v-text-field
v-model="localValue"
dark
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 70px"
:step="step"
/>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,9 +1,20 @@
<template>
<div>
<v-radio-group row v-model="localValue" dark :mandatory="true">
<v-radio color="#4baf62" v-for="(name,index) in list" :label="name" v-bind:key="index" :value="index"/>
</v-radio-group>
</div>
<div>
<v-radio-group
v-model="localValue"
row
dark
:mandatory="true"
>
<v-radio
v-for="(name,index) in list"
:key="index"
color="#4baf62"
:label="name"
:value="index"
/>
</v-radio-group>
</div>
</template>
<script>

View File

@@ -1,27 +1,64 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="2">
<span>{{name}}</span>
</v-col>
<v-col :cols="10">
<v-range-slider :value="localValue" @input="handleInput" :max="max" :min="min" hide-details
class="align-center" dark color="#4baf62" :step="step">
<template v-slot:prepend>
<v-text-field dark :value="localValue[0]" :max="max" :min="min" @input="handleChange"
@focus="prependFocused = true" @blur="prependFocused = false" class="mt-0 pt-0"
hide-details single-line type="number" style="width: 50px" :step="step"/>
</template>
<div>
<v-row
dense
align="center"
>
<v-col :cols="2">
<span>{{ name }}</span>
</v-col>
<v-col :cols="10">
<v-range-slider
:value="localValue"
:max="max"
:min="min"
hide-details
class="align-center"
dark
color="#4baf62"
:step="step"
@input="handleInput"
@mousedown="$emit('rollback', localValue)"
>
<template v-slot:prepend>
<v-text-field
dark
:value="localValue[0]"
:max="max"
:min="min"
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 50px"
:step="step"
@input="handleChange"
@focus="prependFocused = true"
@blur="prependFocused = false"
/>
</template>
<template v-slot:append>
<v-text-field dark :value="localValue[1]" :max="max" :min="min" @input="handleChange"
@focus="appendFocused = true" @blur="appendFocused = false" class="mt-0 pt-0"
hide-details single-line type="number" style="width: 50px" :step="step"/>
</template>
</v-range-slider>
</v-col>
</v-row>
</div>
<template v-slot:append>
<v-text-field
dark
:value="localValue[1]"
:max="max"
:min="min"
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 50px"
:step="step"
@input="handleChange"
@focus="appendFocused = true"
@blur="appendFocused = false"
/>
</template>
</v-range-slider>
</v-col>
</v-row>
</div>
</template>
<script>
@@ -35,22 +72,6 @@
}
},
methods: {
handleChange(val) {
let i = 0;
if (this.prependFocused === false && this.appendFocused === true) {
i = 1;
}
if (this.prependFocused || this.appendFocused) {
this.$set(this.localValue, i, val);
}
},
handleInput(val) {
if (!this.prependFocused || !this.appendFocused) {
this.localValue = val;
}
}
},
computed: {
localValue: {
get() {
@@ -60,6 +81,23 @@
this.$emit('input', value)
}
}
},
methods: {
handleChange(val) {
let i = 0;
if (this.prependFocused === false && this.appendFocused === true) {
i = 1;
}
if (this.prependFocused || this.appendFocused) {
this.$set(this.localValue, i, val);
this.$emit('rollback', this.localValue)
}
},
handleInput(val) {
if (!this.prependFocused || !this.appendFocused) {
this.localValue = val;
}
}
}
}
</script>

View File

@@ -1,15 +1,27 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="3">
<span>{{name}}</span>
</v-col>
<v-col :cols="9">
<v-select v-model="localValue" :items="indexList" item-text="name" item-value="index" dark
color="#4baf62" item-color="green" :disabled="disabled"/>
</v-col>
</v-row>
</div>
<div>
<v-row
dense
align="center"
>
<v-col :cols="3">
<span>{{ name }}</span>
</v-col>
<v-col :cols="9">
<v-select
v-model="localValue"
:items="indexList"
item-text="name"
item-value="index"
dark
color="#4baf62"
item-color="green"
:disabled="disabled"
@change="$emit('rollback', localValue)"
/>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,22 +1,49 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="2">
<span>{{name}}</span>
</v-col>
<v-col :cols="10">
<v-slider :value="localValue" @start="isClicked = true" @end="isClicked = false" @change="handleclick"
@input="handleInput" dark class="align-center" :max="max" :min="min" hide-details
color="#4baf62" :step="step">
<template v-slot:append>
<v-text-field dark :max="max" :min="min" :value="localValue" @input="handleChange"
@focus="isFocused = true" @blur="isFocused = false" class="mt-0 pt-0" hide-details
single-line type="number" style="width: 50px" :step="step"/>
</template>
</v-slider>
</v-col>
</v-row>
</div>
<div>
<v-row
dense
align="center"
>
<v-col :cols="2">
<span>{{ name }}</span>
</v-col>
<v-col :cols="10">
<v-slider
:value="localValue"
dark
class="align-center"
:max="max"
:min="min"
hide-details
color="#4baf62"
:step="step"
@start="isClicked = true"
@end="isClicked = false"
@change="handleclick"
@input="handleInput"
@mousedown="$emit('rollback', localValue)"
>
<template v-slot:append>
<v-text-field
dark
:max="max"
:min="min"
:value="localValue"
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 50px"
:step="step"
@input="handleChange"
@focus="isFocused = true"
@blur="isFocused = false"
/>
</template>
</v-slider>
</v-col>
</v-row>
</div>
</template>
<script>
@@ -29,10 +56,21 @@
isClicked: false
}
},
computed: {
localValue: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value)
}
}
},
methods: {
handleChange(val) {
if (this.isFocused) {
this.localValue = parseFloat(val);
this.$emit('rollback', this.localValue)
}
},
handleInput(val) {
@@ -45,16 +83,6 @@
this.localValue = val;
}
}
},
computed: {
localValue: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value)
}
}
}
}
</script>

View File

@@ -1,14 +1,23 @@
<template>
<div>
<v-row dense align="center">
<v-col :cols="2">
<span>{{name}}</span>
</v-col>
<v-col>
<v-switch dark :disabled="disabled" v-model="localValue" color="#4baf62"/>
</v-col>
</v-row>
</div>
<div>
<v-row
dense
align="center"
>
<v-col :cols="2">
<span>{{ name }}</span>
</v-col>
<v-col>
<v-switch
v-model="localValue"
dark
:disabled="disabled"
color="#4baf62"
@change="$emit('rollback', localValue)"
/>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,34 +1,56 @@
<template>
<div>
<v-row style="width: 400px;" align="center">
<canvas id="canvasId" width="800" height="800"/>
</v-row>
<v-row style="width: 400px;" align="center">
<v-simple-table
style="text-align: center;background-color: transparent; display: block;margin: auto"
dense dark>
<template v-slot:default>
<thead>
<tr>
<th class="text-center">Target</th>
<th class="text-center">X</th>
<th class="text-center">Y</th>
<th class="text-center">Angle</th>
</tr>
</thead>
<tbody>
<tr v-for="(target, index) in targets" :key="index">
<td>{{ index}}</td>
<td>{{ target.pose.translation.x.toFixed(2) }}</td>
<td>{{ target.pose.translation.y.toFixed(2) }}</td>
<td>{{ target.pose.rotation.radians.toFixed(2) }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-row>
</div>
<div>
<v-row
style="width: 400px;"
align="center"
>
<canvas
id="canvasId"
width="800"
height="800"
/>
</v-row>
<v-row
style="width: 400px;"
align="center"
>
<v-simple-table
style="text-align: center;background-color: transparent; display: block;margin: auto"
dense
dark
>
<template v-slot:default>
<thead>
<tr>
<th class="text-center">
Target
</th>
<th class="text-center">
X
</th>
<th class="text-center">
Y
</th>
<th class="text-center">
Angle
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(target, index) in targets"
:key="index"
>
<td>{{ index }}</td>
<td>{{ target.pose.translation.x.toFixed(2) }}</td>
<td>{{ target.pose.translation.y.toFixed(2) }}</td>
<td>{{ target.pose.rotation.radians.toFixed(2) }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-row>
</div>
</template>
<script>
@@ -48,6 +70,13 @@
targetHeight: 6
}
},
computed: {
hLen: {
get() {
return Math.tan(this.horizontalFOV / 2 * Math.PI / 180) * 150;
}
}
},
watch: {
targets: {
deep: true,
@@ -59,6 +88,28 @@
this.draw();
}
},
mounted: function () {
const canvas = document.getElementById("canvasId"); // getting the canvas element
const ctx = canvas.getContext("2d"); // getting the canvas context
this.canvas = canvas; // setting the canvas as a vue variable
this.ctx = ctx; // setting the canvas context as a vue variable
this.grad = this.ctx.createLinearGradient(400, 800, 400, 600);
this.grad.addColorStop(0, "rgb(119,119,119)");
this.grad.addColorStop(0.05, "rgba(14,92,22,0.96)");
this.grad.addColorStop(0.8, 'rgba(43,43,43,0.48)');
// setting canvas context values for drawing
this.ctx.font = "26px Arial";
this.ctx.strokeStyle = "whitesmoke";
this.ctx.lineWidth = 2;
this.$nextTick(function () {
this.drawPlayer();
});
},
methods: {
draw() {
this.clearBoard();
@@ -115,35 +166,6 @@
clearBoard() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // clearing the canvas
}
},
computed: {
hLen: {
get() {
return Math.tan(this.horizontalFOV / 2 * Math.PI / 180) * 150;
}
}
},
mounted: function () {
const canvas = document.getElementById("canvasId"); // getting the canvas element
const ctx = canvas.getContext("2d"); // getting the canvas context
this.canvas = canvas; // setting the canvas as a vue variable
this.ctx = ctx; // setting the canvas context as a vue variable
this.grad = this.ctx.createLinearGradient(400, 800, 400, 600);
this.grad.addColorStop(0, "rgb(119,119,119)");
this.grad.addColorStop(0.05, "rgba(14,92,22,0.96)");
this.grad.addColorStop(0.8, 'rgba(43,43,43,0.48)');
// setting canvas context values for drawing
this.ctx.font = "26px Arial";
this.ctx.strokeStyle = "whitesmoke";
this.ctx.lineWidth = 2;
this.$nextTick(function () {
this.drawPlayer();
});
}
}
</script>

View File

@@ -1,108 +1,233 @@
<template>
<div>
<v-row align="center">
<v-col :cols="3" class="">
<div style="padding-left:30px">
<CVselect v-if="isCameraNameEdit === false" name="Camera" v-model="currentCameraIndex"
:list="$store.getters.cameraList"
@input="handleInput('currentCamera',currentCameraIndex)"/>
<CVinput v-else name="Camera" v-model="newCameraName" @Enter="saveCameraNameChange"
:errorMessage="checkCameraName"/>
</div>
</v-col>
<v-col :cols="1">
<CVicon color="#c5c5c5" v-if="isCameraNameEdit === false" :hover="true" text="edit"
@click="toCameraNameChange" tooltip="Edit camera name"/>
<div v-else>
<CVicon color="#c5c5c5" style="display: inline-block;" :hover="true" text="save"
@click="saveCameraNameChange" tooltip="Save Camera Name"/>
<CVicon color="error" style="display: inline-block;" :hover="true" text="close"
@click="discardCameraNameChange" tooltip="Discard Changes"/>
</div>
</v-col>
<v-col :cols="3" class="">
<CVselect name="Pipeline"
:list="['Driver Mode'].concat($store.getters.pipelineList)"
v-model="currentPipelineIndex"
@input="handleInput('currentPipeline',currentPipelineIndex - 1)"/>
</v-col>
<v-col :cols="1" class="" md="3" v-if="currentPipelineIndex !== 0">
<v-menu offset-y dark auto>
<template v-slot:activator="{ on }">
<v-icon color="white" v-on="on">menu</v-icon>
</template>
<v-list dense>
<v-list-item @click="toPipelineNameChange">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="edit" tooltip="Edit pipeline name"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="toCreatePipeline">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="add" tooltip="Add new pipeline"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="deleteCurrentPipeline">
<v-list-item-title>
<CVicon color="red darken-2" :right="true" text="delete"
tooltip="Delete pipeline"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="openDuplicateDialog">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="mdi-content-copy"
tooltip="Duplicate pipeline"/>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<div>
<v-row align="center">
<v-col
:cols="3"
class=""
>
<div style="padding-left:30px">
<CVselect
v-if="isCameraNameEdit === false"
v-model="currentCameraIndex"
name="Camera"
:list="$store.getters.cameraList"
@input="handleInput('currentCamera',currentCameraIndex)"
/>
<CVinput
v-else
v-model="newCameraName"
name="Camera"
:error-message="checkCameraName"
@Enter="saveCameraNameChange"
/>
</div>
</v-col>
<v-col :cols="1">
<CVicon
v-if="isCameraNameEdit === false"
color="#c5c5c5"
:hover="true"
text="edit"
tooltip="Edit camera name"
@click="toCameraNameChange"
/>
<div v-else>
<CVicon
color="#c5c5c5"
style="display: inline-block;"
:hover="true"
text="save"
tooltip="Save Camera Name"
@click="saveCameraNameChange"
/>
<CVicon
color="error"
style="display: inline-block;"
:hover="true"
text="close"
tooltip="Discard Changes"
@click="discardCameraNameChange"
/>
</div>
</v-col>
<v-col
:cols="3"
class=""
>
<CVselect
v-model="currentPipelineIndex"
name="Pipeline"
:list="['Driver Mode'].concat($store.getters.pipelineList)"
@input="handleInput('currentPipeline',currentPipelineIndex - 1)"
/>
</v-col>
<v-col
v-if="currentPipelineIndex !== 0"
:cols="1"
class=""
md="3"
>
<v-menu
offset-y
dark
auto
>
<template v-slot:activator="{ on }">
<v-icon
color="white"
v-on="on"
>
menu
</v-icon>
</template>
<v-list dense>
<v-list-item @click="toPipelineNameChange">
<v-list-item-title>
<CVicon
color="#c5c5c5"
:right="true"
text="edit"
tooltip="Edit pipeline name"
/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="toCreatePipeline">
<v-list-item-title>
<CVicon
color="#c5c5c5"
:right="true"
text="add"
tooltip="Add new pipeline"
/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="deleteCurrentPipeline">
<v-list-item-title>
<CVicon
color="red darken-2"
:right="true"
text="delete"
tooltip="Delete pipeline"
/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="openDuplicateDialog">
<v-list-item-title>
<CVicon
color="#c5c5c5"
:right="true"
text="mdi-content-copy"
tooltip="Duplicate pipeline"
/>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<v-btn style="position: absolute; top:5px;right: 0;" tile color="#4baf62"
@click="handleInput('command','save')">
<v-icon>save</v-icon>
Save
</v-btn>
</v-row>
<!--pipeline duplicate dialog-->
<v-dialog dark v-model="duplicateDialog" width="500" height="357">
<v-card dark>
<v-card-title class="headline" primary-title>Duplicate Pipeline</v-card-title>
<v-card-text>
<CVselect name="Pipeline" :list="$store.getters.pipelineList" v-model="pipelineDuplicate.pipeline"/>
<v-checkbox v-if="$store.getters.cameraList.length > 1" dark :label="'To another camera'"
v-model="anotherCamera"/>
<CVselect v-if="anotherCamera === true" name="Camera" v-model="pipelineDuplicate.camera"
:list="$store.getters.cameraList"/>
</v-card-text>
<v-divider>
</v-divider>
<v-card-actions>
<v-spacer/>
<v-btn color="#4baf62" @click="duplicatePipeline">Duplicate</v-btn>
<v-btn color="error" @click="closeDuplicateDialog">Cancel</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!--pipeline naming dialog-->
<v-dialog dark v-model="namingDialog" width="500" height="357">
<v-card dark>
<v-card-title class="headline" primary-title>Pipeline Name</v-card-title>
<v-card-text>
<CVinput name="Pipeline" :error-message="checkPipelineName" v-model="newPipelineName"
@Enter="savePipelineNameChange"/>
</v-card-text>
<v-divider>
</v-divider>
<v-card-actions>
<v-spacer/>
<v-btn color="#4baf62" @click="savePipelineNameChange" :disabled="checkPipelineName !==''">Save
</v-btn>
<v-btn color="error" @click="discardPipelineNameChange">Cancel</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
<v-btn
style="position: absolute; top:5px;right: 0;"
tile
color="#4baf62"
@click="handleInput('command','save')"
>
<v-icon>save</v-icon>
Save
</v-btn>
</v-row>
<!--pipeline duplicate dialog-->
<v-dialog
v-model="duplicateDialog"
dark
width="500"
height="357"
>
<v-card dark>
<v-card-title
class="headline"
primary-title
>
Duplicate Pipeline
</v-card-title>
<v-card-text>
<CVselect
v-model="pipelineDuplicate.pipeline"
name="Pipeline"
:list="$store.getters.pipelineList"
/>
<v-checkbox
v-if="$store.getters.cameraList.length > 1"
v-model="anotherCamera"
dark
:label="'To another camera'"
/>
<CVselect
v-if="anotherCamera === true"
v-model="pipelineDuplicate.camera"
name="Camera"
:list="$store.getters.cameraList"
/>
</v-card-text>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn
color="#4baf62"
@click="duplicatePipeline"
>
Duplicate
</v-btn>
<v-btn
color="error"
@click="closeDuplicateDialog"
>
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!--pipeline naming dialog-->
<v-dialog
v-model="namingDialog"
dark
width="500"
height="357"
>
<v-card dark>
<v-card-title
class="headline"
primary-title
>
Pipeline Name
</v-card-title>
<v-card-text>
<CVinput
v-model="newPipelineName"
name="Pipeline"
:error-message="checkPipelineName"
@Enter="savePipelineNameChange"
/>
</v-card-text>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn
color="#4baf62"
:disabled="checkPipelineName !==''"
@click="savePipelineNameChange"
>
Save
</v-btn>
<v-btn
color="error"
@click="discardPipelineNameChange"
>
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
@@ -134,6 +259,56 @@
},
}
},
computed: {
checkCameraName() {
if (this.newCameraName !== this.$store.getters.cameraList[this.currentCameraIndex]) {
if (this.re.test(this.newCameraName)) {
for (let cam in this.cameraList) {
if (this.cameraList.hasOwnProperty(cam)) {
if (this.newCameraName === this.cameraList[cam]) {
return "Camera by that name already Exists"
}
}
}
} else {
return "Camera name can only contain letters, numbers and spaces"
}
}
return ""
},
checkPipelineName() {
if (this.newPipelineName !== this.$store.getters.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
if (this.re.test(this.newPipelineName)) {
for (let pipe in this.$store.getters.pipelineList) {
if (this.$store.getters.pipelineList.hasOwnProperty(pipe)) {
if (this.newPipelineName === this.$store.getters.pipelineList[pipe]) {
return "A pipeline with this name already exists"
}
}
}
} else {
return "Pipeline name can only contain letters, numbers, and spaces"
}
}
return ""
},
currentCameraIndex: {
get() {
return this.$store.getters.currentCameraIndex;
},
set(value) {
this.$store.commit('currentPipelineIndex', value - 1);
}
},
currentPipelineIndex: {
get() {
return this.$store.getters.currentPipelineIndex + 1;
},
set(value) {
this.$store.commit('currentPipelineIndex', value - 1);
}
}
},
methods: {
toCameraNameChange() {
this.newCameraName = this.$store.getters.cameraList[this.currentCameraIndex];
@@ -203,56 +378,6 @@
this.isPipelineNameEdit = false;
this.newPipelineName = "";
},
},
computed: {
checkCameraName() {
if (this.newCameraName !== this.$store.getters.cameraList[this.currentCameraIndex]) {
if (this.re.test(this.newCameraName)) {
for (let cam in this.cameraList) {
if (this.cameraList.hasOwnProperty(cam)) {
if (this.newCameraName === this.cameraList[cam]) {
return "Camera by that name already Exists"
}
}
}
} else {
return "Camera name can only contain letters, numbers and spaces"
}
}
return ""
},
checkPipelineName() {
if (this.newPipelineName !== this.$store.getters.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
if (this.re.test(this.newPipelineName)) {
for (let pipe in this.$store.getters.pipelineList) {
if (this.$store.getters.pipelineList.hasOwnProperty(pipe)) {
if (this.newPipelineName === this.$store.getters.pipelineList[pipe]) {
return "A pipeline with this name already exists"
}
}
}
} else {
return "Pipeline name can only contain letters, numbers, and spaces"
}
}
return ""
},
currentCameraIndex: {
get() {
return this.$store.getters.currentCameraIndex;
},
set(value) {
this.$store.commit('currentPipelineIndex', value - 1);
}
},
currentPipelineIndex: {
get() {
return this.$store.getters.currentPipelineIndex + 1;
},
set(value) {
this.$store.commit('currentPipelineIndex', value - 1);
}
}
}
}

View File

@@ -1,17 +1,44 @@
<template>
<div>
<v-row align="center" justify="start">
<v-col style="padding-right:0" :cols="3">
<v-btn small color="#4baf62" @click="takePointA">Take Point A</v-btn>
</v-col>
<v-col style="margin-left:0" :cols="3">
<v-btn small color="#4baf62" @click="takePointB">Take Point B</v-btn>
</v-col>
<v-col>
<v-btn small @click="clearSlope" color="yellow darken-3">Clear All Points</v-btn>
</v-col>
</v-row>
</div>
<div>
<v-row
align="center"
justify="start"
>
<v-col
style="padding-right:0"
:cols="3"
>
<v-btn
small
color="#4baf62"
@click="takePointA"
>
Take Point A
</v-btn>
</v-col>
<v-col
style="margin-left:0"
:cols="3"
>
<v-btn
small
color="#4baf62"
@click="takePointB"
>
Take Point B
</v-btn>
</v-col>
<v-col>
<v-btn
small
color="yellow darken-3"
@click="clearSlope"
>
Clear All Points
</v-btn>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,14 +1,32 @@
<template>
<div>
<v-row align="center" justify="start">
<v-col style="padding-right:0" :cols="3">
<v-btn small color="#4baf62" @click="takePoint">Take Point</v-btn>
</v-col>
<v-col>
<v-btn small @click="clearPoint" color="yellow darken-3">Clear Point</v-btn>
</v-col>
</v-row>
</div>
<div>
<v-row
align="center"
justify="start"
>
<v-col
style="padding-right:0"
:cols="3"
>
<v-btn
small
color="#4baf62"
@click="takePoint"
>
Take Point
</v-btn>
</v-col>
<v-col>
<v-btn
small
color="yellow darken-3"
@click="clearPoint"
>
Clear Point
</v-btn>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,5 +1,5 @@
export const dataHandleMixin = {
methods:{
methods: {
handleInput(key, value) {
let msg = this.$msgPack.encode({[key]: value});
this.$socket.send(msg);
@@ -8,6 +8,9 @@ export const dataHandleMixin = {
this.handleInput(val, this.value[val]);
this.$emit('update')
},
rollback(val, e) {
//TODO UPDATE VALUES INTO WEBSOCKET
this.$store.commit('updatePipeline', {[val]: e})
}
}
};

View File

@@ -0,0 +1,121 @@
//https://gomakethings.com/getting-the-differences-between-two-objects-with-vanilla-js/
export const diff = function (obj1, obj2) {
// Make sure an object to compare is provided
if (!obj2 || Object.prototype.toString.call(obj2) !== '[object Object]') {
return obj1;
}
//
// Variables
//
let diffs = {};
let key;
//
// Methods
//
/**
* Check if two arrays are equal
* @param {Array} arr1 The first array
* @param {Array} arr2 The second array
* @return {Boolean} If true, both arrays are equal
*/
const arraysMatch = function (arr1, arr2) {
// Check if the arrays are the same length
if (arr1.length !== arr2.length) return false;
// Check if all items exist and are in the same order
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
// Otherwise, return true
return true;
};
/**
* Compare two items and push non-matches to object
* @param {*} item1 The first item
* @param {*} item2 The second item
* @param {String} key The key in our object
*/
const compare = function (item1, item2, key) {
// Get the object type
let type1 = Object.prototype.toString.call(item1);
let type2 = Object.prototype.toString.call(item2);
// If type2 is undefined it has been removed
if (type2 === '[object Undefined]') {
diffs[key] = null;
return;
}
// If items are different types
if (type1 !== type2) {
diffs[key] = item2;
return;
}
// If an object, compare recursively
if (type1 === '[object Object]') {
let objDiff = diff(item1, item2);
if (Object.keys(objDiff).length > 1) {
diffs[key] = objDiff;
}
return;
}
// If an array, compare
if (type1 === '[object Array]') {
if (!arraysMatch(item1, item2)) {
diffs[key] = item2;
}
return;
}
// Else if it's a function, convert to a string and compare
// Otherwise, just compare
if (type1 === '[object Function]') {
if (item1.toString() !== item2.toString()) {
diffs[key] = item2;
}
} else {
if (item1 !== item2) {
diffs[key] = item2;
}
}
};
//
// Compare our objects
//
// Loop through the first object
for (key in obj1) {
if (obj1.hasOwnProperty(key)) {
compare(obj1[key], obj2[key], key);
}
}
// Loop through the second object and find missing items
for (key in obj2) {
if (obj2.hasOwnProperty(key)) {
if (!obj1[key] && obj1[key] !== obj2[key] ) {
diffs[key] = obj2[key];
}
}
}
// Return the object of differences
return diffs;
};

View File

@@ -4,6 +4,7 @@ import Vuex from 'vuex'
import pipeline from "./modules/pipeline";
import generalSettings from "./modules/generalSettings";
import cameraSettings from "./modules/cameraSettings";
import undoRedo from "./modules/undoRedo";
Vue.use(Vuex);
@@ -15,7 +16,8 @@ export default new Vuex.Store({
modules: {
pipeline: pipeline,
settings: generalSettings,
cameraSettings: cameraSettings
cameraSettings: cameraSettings,
undoRedo: undoRedo
},
state: {
resolutionList: [],

View File

@@ -1,3 +1,5 @@
import Vue from 'vue'
export default {
state: {
exposure: 0,
@@ -27,12 +29,10 @@ export default {
},
mutations: {
isBinary: (state, value) => {
console.log(value)
state.isBinary = value
},
mutatePipeline: (state, {key, value}) => {
// console.log(`key:${key}, value: ${value}`)
this.set(state, key, value)
Vue.set(state, key, value)
}
},

View File

@@ -0,0 +1,72 @@
// eslint-disable-next-line no-unused-vars
import Vue from 'vue'
export default {
state: {
done: [],
undone: [],
newMutation: true
},
mutations: {
updatePipeline: (state, val) => {
state.done.push(val)
if (state.newMutation) {
state.undone = []
}
},
addUndone: (state, val) => {
state.undone.push(val);
},
removeLastDone: state => {
state.done.pop()
},
removeLastUnDone: state => {
state.undone.pop()
},
updateStatus: (state, bool) => {
state.newMutation = bool;
},
},
actions: {
undo: (context, {vm}) => {
let commit = context.getters.lastDone;
context.commit('removeLastDone')
context.commit('updateStatus', false)
for (let key in commit) {
if (commit.hasOwnProperty(key)) {
context.commit('addUndone', {[key]: context.getters["pipeline"][key]});
context.commit('mutatePipeline', {'key': key, 'value': commit[key]});
vm.handleInput(key, commit[key]);
}
}
context.commit('updateStatus', true)
},
redo: (context, {vm}) => {
let commit = context.getters.lastUnDone;
context.commit('removeLastUnDone');
context.commit('updateStatus', false)
for (let key in commit) {
if (commit.hasOwnProperty(key)) {
context.commit('mutatePipeline', {'key': key, 'value': commit[key]});
vm.handleInput(key, commit[key]);
}
}
context.commit('updateStatus', true)
}
},
getters: {
lastDone: state => {
return state.done[state.done.length - 1]
},
lastUnDone: state => {
return state.undone[state.undone.length - 1]
},
canUndo: state => {
return state.done.length
},
canRedo: state => {
return state.undone.length
}
}
};

View File

@@ -1,79 +1,137 @@
<template>
<div>
<camera-and-pipeline-select/>
<v-row>
<!-- vision tabs -->
<v-col cols="6" class="colsClass">
<v-tabs fixed-tabs background-color="#212121" dark height="48" slider-color="#4baf62"
v-model="selectedTab" v-if="($store.getters.currentPipelineIndex + 1) !== 0">
<v-tab>Input</v-tab>
<v-tab>Threshold</v-tab>
<v-tab>Contours</v-tab>
<v-tab>Output</v-tab>
<v-tab>3D</v-tab>
</v-tabs>
<div v-else style="height: 48px"></div>
<div style="padding-left:30px">
<keep-alive>
<!-- vision component -->
<component v-model="$store.getters.pipeline" :is="selectedComponent" ref="component"
@update="$emit('save')"/>
</keep-alive>
</div>
</v-col>
<v-col cols="6" class="colsClass">
<div>
<!-- camera image tabs -->
<v-tabs background-color="#212121" dark height="48" slider-color="#4baf62" centered
style="padding-bottom:10px" v-model="isBinaryNumber"
@change="handleInput('isBinary',$store.getters.pipeline.isBinary)"
v-if="($store.getters.currentPipelineIndex + 1) !== 0">
<v-tab>Normal</v-tab>
<v-tab>Threshold</v-tab>
</v-tabs>
<div v-else style="height: 58px"></div>
<!-- camera image stream -->
<div class="videoClass">
<v-row align="center">
<cvImage :address="$store.getters.streamAddress" :scale="75" @click="onImageClick"/>
</v-row>
<v-row justify="end">
<span style="margin-right: 45px">FPS:{{parseFloat(fps).toFixed(2)}}</span>
</v-row>
<v-row align="center">
<v-simple-table
style="text-align: center;background-color: transparent; display: block;margin: auto"
dense dark>
<template v-slot:default>
<thead>
<tr>
<th class="text-center">Target</th>
<th class="text-center">Pitch</th>
<th class="text-center">Yaw</th>
<th class="text-center">Area</th>
</tr>
</thead>
<tbody>
<tr v-for="(value, index) in $store.getters.targets" :key="index">
<td>{{ index}}</td>
<td>{{ parseFloat(value['pitch']).toFixed(2)}}</td>
<td>{{ parseFloat(value['yaw']).toFixed(2)}}</td>
<td>{{ parseFloat(value['area']).toFixed(2)}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-row>
</div>
</div>
</v-col>
</v-row>
<!-- snack bar -->
<v-snackbar :timeout="3000" v-model="snackbar" top color="error">
<span style="color:#000">Can not remove the only pipeline!</span>
<v-btn color="black" text @click="snackbar = false">Close</v-btn>
</v-snackbar>
</div>
<div>
<camera-and-pipeline-select />
<v-row>
<!-- vision tabs -->
<v-col
cols="6"
class="colsClass"
>
<v-tabs
v-if="($store.getters.currentPipelineIndex + 1) !== 0"
v-model="selectedTab"
fixed-tabs
background-color="#212121"
dark
height="48"
slider-color="#4baf62"
>
<v-tab>Input</v-tab>
<v-tab>Threshold</v-tab>
<v-tab>Contours</v-tab>
<v-tab>Output</v-tab>
<v-tab>3D</v-tab>
</v-tabs>
<div
v-else
style="height: 48px"
/>
<div style="padding-left:30px">
<keep-alive>
<!-- vision component -->
<component
:is="selectedComponent"
ref="component"
v-model="$store.getters.pipeline"
@update="$emit('save')"
/>
</keep-alive>
</div>
</v-col>
<v-col
cols="6"
class="colsClass"
>
<div>
<!-- camera image tabs -->
<v-tabs
v-if="($store.getters.currentPipelineIndex + 1) !== 0"
v-model="isBinaryNumber"
background-color="#212121"
dark
height="48"
slider-color="#4baf62"
centered
style="padding-bottom:10px"
@change="handleInput('isBinary',$store.getters.pipeline.isBinary)"
>
<v-tab>Normal</v-tab>
<v-tab>Threshold</v-tab>
</v-tabs>
<div
v-else
style="height: 58px"
/>
<!-- camera image stream -->
<div class="videoClass">
<v-row align="center">
<cvImage
:address="$store.getters.streamAddress"
:scale="75"
@click="onImageClick"
/>
</v-row>
<v-row justify="end">
<span style="margin-right: 45px">FPS:{{ parseFloat(fps).toFixed(2) }}</span>
</v-row>
<v-row align="center">
<v-simple-table
style="text-align: center;background-color: transparent; display: block;margin: auto"
dense
dark
>
<template v-slot:default>
<thead>
<tr>
<th class="text-center">
Target
</th>
<th class="text-center">
Pitch
</th>
<th class="text-center">
Yaw
</th>
<th class="text-center">
Area
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(value, index) in $store.getters.targets"
:key="index"
>
<td>{{ index }}</td>
<td>{{ parseFloat(value['pitch']).toFixed(2) }}</td>
<td>{{ parseFloat(value['yaw']).toFixed(2) }}</td>
<td>{{ parseFloat(value['area']).toFixed(2) }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-row>
</div>
</div>
</v-col>
</v-row>
<!-- snack bar -->
<v-snackbar
v-model="snackbar"
:timeout="3000"
top
color="error"
>
<span style="color:#000">Can not remove the only pipeline!</span>
<v-btn
color="black"
text
@click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>
<script>
@@ -96,13 +154,6 @@
OutputTab,
pnpTab,
},
methods: {
onImageClick(event) {
if (this.selectedTab === 1) {
this.$refs.component.onClick(event);
}
},
},
data() {
return {
selectedTab: 0,
@@ -129,6 +180,13 @@
return this.$store.state.point.fps;
}
}
},
methods: {
onImageClick(event) {
if (this.selectedTab === 1) {
this.$refs.component.onClick(event);
}
},
}
}
</script>

View File

@@ -1,33 +1,79 @@
<template>
<div>
<v-row align="center" justify="start" dense>
<v-col :cols="6">
<CVswitch :disabled="allow3D" v-model="value.is3D" name="Enable 3D" @input="handleData('is3D')"/>
</v-col>
<v-col>
<input type="file" ref="file" style="display: none" accept=".csv" @change="readFile">
<v-btn @click="$refs.file.click()" small>
<v-icon>mdi-upload</v-icon>
upload model
</v-btn>
</v-col>
</v-row>
<CVslider name="Contour simplification" v-model="value.accuracy" :min="0" :max="100"
@input="handleData('accuracy')"/>
<v-row>
<v-col>
<mini-map class="miniMapClass" :targets="targets" :horizontal-f-o-v="horizontalFOV"/>
</v-col>
<v-col>
<v-select v-model="selectedModel" :items="FRCtargets" item-text="name" item-value="data" dark
color="#4baf62" item-color="green"/>
<v-btn small v-if="selectedModel !== null" @click="uploadPremade">Upload Premade</v-btn>
</v-col>
</v-row>
<v-snackbar v-model="snack" top :color="snackbar.color">
<span>{{snackbar.text}}</span>
</v-snackbar>
</div>
<div>
<v-row
align="center"
justify="start"
dense
>
<v-col :cols="6">
<CVswitch
v-model="value.is3D"
:disabled="allow3D"
name="Enable 3D"
@input="handleData('is3D')"
@rollback="e=> rollback('is3D',e)"
/>
</v-col>
<v-col>
<input
ref="file"
type="file"
style="display: none"
accept=".csv"
@change="readFile"
>
<v-btn
small
@click="$refs.file.click()"
>
<v-icon>mdi-upload</v-icon>
upload model
</v-btn>
</v-col>
</v-row>
<CVslider
v-model="value.accuracy"
name="Contour simplification"
:min="0"
:max="100"
@input="handleData('accuracy')"
@rollback="e=> rollback('accuracy',e)"
/>
<v-row>
<v-col>
<mini-map
class="miniMapClass"
:targets="targets"
:horizontal-f-o-v="horizontalFOV"
/>
</v-col>
<v-col>
<v-select
v-model="selectedModel"
:items="FRCtargets"
item-text="name"
item-value="data"
dark
color="#4baf62"
item-color="green"
/>
<v-btn
v-if="selectedModel !== null"
small
@click="uploadPremade"
>
Upload Premade
</v-btn>
</v-col>
</v-row>
<v-snackbar
v-model="snack"
top
:color="snackbar.color"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
</div>
</template>
<script>
@@ -38,13 +84,13 @@
import FRCtargetsConfig from '../../assets/FRCtargets'
export default {
name: "solvePNP",
props: ['value'],
name: "SolvePNP",
components: {
CVswitch,
CVslider,
miniMap
},
props: ['value'],
data() {
return {
is3D: false,
@@ -57,6 +103,43 @@
snack: false
}
},
computed: {
targets: {
get() {
return this.$store.state.point.targets;
}
},
horizontalFOV: {
get() {
let index = this.$store.state.cameraSettings.resolution;
let FOV = this.$store.state.cameraSettings.fov;
let resolution = this.$store.state.resolutionList[index];
let diagonalView = FOV * (Math.PI / 180);
let diagonalAspect = Math.hypot(resolution.width, resolution.height);
return Math.atan(Math.tan(diagonalView / 2) * (resolution.width / diagonalAspect)) * 2 * (180 / Math.PI)
}
},
allow3D: {
get() {
let currentRes = this.$store.state.resolutionList[this.$store.state.pipeline.videoModeIndex];
for (let res of this.$store.state.cameraSettings.calibration) {
if (currentRes.width === res.width && currentRes.height === res.height) {
return false;
}
}
return true;
}
}
},
mounted() {
let tmp = [];
for (let t in FRCtargetsConfig) {
if (FRCtargetsConfig.hasOwnProperty(t)) {
tmp.push({name: t, data: FRCtargetsConfig[t]})
}
}
this.FRCtargets = tmp;
},
methods: {
readFile(event) {
let file = event.target.files[0];
@@ -111,43 +194,6 @@
this.snack = true;
})
}
},
computed: {
targets: {
get() {
return this.$store.state.point.targets;
}
},
horizontalFOV: {
get() {
let index = this.$store.state.cameraSettings.resolution;
let FOV = this.$store.state.cameraSettings.fov;
let resolution = this.$store.state.resolutionList[index];
let diagonalView = FOV * (Math.PI / 180);
let diagonalAspect = Math.hypot(resolution.width, resolution.height);
return Math.atan(Math.tan(diagonalView / 2) * (resolution.width / diagonalAspect)) * 2 * (180 / Math.PI)
}
},
allow3D: {
get() {
let currentRes = this.$store.state.resolutionList[this.$store.state.pipeline.videoModeIndex];
for (let res of this.$store.state.cameraSettings.calibration) {
if (currentRes.width === res.width && currentRes.height === res.height) {
return false;
}
}
return true;
}
}
},
mounted() {
let tmp = [];
for (let t in FRCtargetsConfig) {
if (FRCtargetsConfig.hasOwnProperty(t)) {
tmp.push({name: t, data: FRCtargetsConfig[t]})
}
}
this.FRCtargets = tmp;
}
}
</script>

View File

@@ -1,19 +1,55 @@
<template>
<div>
<CVrangeSlider v-model="value.area" name="Area" :min="0" :max="100" :step="0.1"
@input="handleData('area')"/>
<CVrangeSlider v-model="value.ratio" name="Ratio (W/H)" :min="0" :max="100" :step="0.1"
@input="handleData('ratio')"/>
<CVrangeSlider v-model="value.extent" name="Extent" :min="0" :max="100"
@input="handleData('extent')"/>
<CVslider name="Speckle Rejection" :min="0" :max="100" v-model="value.speckle"
@input="handleData('speckle')"/>
<CVselect name="Target Group" :list="['Single','Dual']" v-model="value.targetGroup"
@input="handleData('targetGroup')"/>
<CVselect name="Target Intersection" :list="['None','Up','Down','Left','Right']" :disabled="isDisabled"
v-model="value.targetIntersection" @input="handleData('targetIntersection')"/>
</div>
<div>
<CVrangeSlider
v-model="value.area"
name="Area"
:min="0"
:max="100"
:step="0.1"
@input="handleData('area')"
@rollback="e=> rollback('area',e)"
/>
<CVrangeSlider
v-model="value.ratio"
name="Ratio (W/H)"
:min="0"
:max="100"
:step="0.1"
@input="handleData('ratio')"
@rollback="e=> rollback('ratio',e)"
/>
<CVrangeSlider
v-model="value.extent"
name="Extent"
:min="0"
:max="100"
@input="handleData('extent')"
@rollback="e=> rollback('extent',e)"
/>
<CVslider
v-model="value.speckle"
name="Speckle Rejection"
:min="0"
:max="100"
@input="handleData('speckle')"
@rollback="e=> rollback('speckle',e)"
/>
<CVselect
v-model="value.targetGroup"
name="Target Group"
:list="['Single','Dual']"
@input="handleData('targetGroup')"
@rollback="e=> rollback('targetGroup',e)"
/>
<CVselect
v-model="value.targetIntersection"
name="Target Intersection"
:list="['None','Up','Down','Left','Right']"
:disabled="isDisabled"
@input="handleData('targetIntersection')"
@rollback="e=> rollback('targetIntersection',e)"
/>
</div>
</template>
<script>
@@ -23,14 +59,12 @@
export default {
name: 'Contours',
props: ['value'],
components: {
CVrangeSlider,
CVselect,
CVslider
},
methods: {
},
props: ['value'],
data() {
return {}
@@ -41,6 +75,7 @@
}
},
methods: {},
}
</script>

View File

@@ -1,16 +1,52 @@
<template>
<div>
<CVslider name="Exposure" v-model="value.exposure" :min="0" :max="100" @input="handleData('exposure')"/>
<CVslider name="Brightness" v-model="value.brightness" :min="0" :max="100" @input="handleData('brightness')"/>
<CVslider name="Gain" v-if="value.gain !== -1" v-model="value.gain" :min="0" :max="100"
@input="handleData('gain')"/>
<CVselect name="Orientation" v-model="value.rotationMode" :list="['Normal','90° CW','180°','90° CCW']"
@input="handleData('rotationMode')"/>
<CVselect name="Resolution" v-model="value.videoModeIndex" :list="resolutionList"
@input="handleData('videoModeIndex')"/>
<CVselect name="Stream Resolution" v-model="value.streamDivisor"
:list="streamResolutionList" @input="handleData('streamDivisor')"/>
</div>
<div>
<CVslider
v-model="value.exposure"
name="Exposure"
:min="0"
:max="100"
@input="handleData('exposure')"
@rollback="e => rollback('exposure', e)"
/>
<CVslider
v-model="value.brightness"
name="Brightness"
:min="0"
:max="100"
@input="handleData('brightness')"
@rollback="e => rollback('brightness', e)"
/>
<CVslider
v-if="value.gain !== -1"
v-model="value.gain"
name="Gain"
:min="0"
:max="100"
@input="handleData('gain')"
@rollback="e => rollback('gain', e)"
/>
<CVselect
v-model="value.rotationMode"
name="Orientation"
:list="['Normal','90° CW','180°','90° CCW']"
@input="handleData('rotationMode')"
@rollback="e => e => rollback('rotationMode',e)"
/>
<CVselect
v-model="value.videoModeIndex"
name="Resolution"
:list="resolutionList"
@input="handleData('videoModeIndex')"
@rollback="e => rollback('videoModeIndex', e)"
/>
<CVselect
v-model="value.streamDivisor"
name="Stream Resolution"
:list="streamResolutionList"
@input="handleData('streamDivisor')"
@rollback="e => rollback('streamDivisor', e)"
/>
</div>
</template>
<script>
@@ -19,18 +55,14 @@
export default {
name: 'Input',
props: ['value'],
components: {
CVslider,
CVselect,
},
methods: {
},
// eslint-disable-next-line vue/require-prop-types
props: ['value'],
data() {
return {
t: 0,
a: 1
}
return {}
},
computed: {
resolutionList: {
@@ -53,7 +85,8 @@
return tmp_list;
}
}
}
},
methods: {}
}
</script>

View File

@@ -1,23 +1,69 @@
<template>
<div>
<CVselect name="Sort Mode" v-model="value.sortMode"
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Centermost']"
@input="handleData('sortMode')"/>
<CVselect v-model="value.targetRegion" name="Target Region" :list="['Center','Top','Bottom','Left','Right']"
@input="handleData('targetRegion')"/>
<CVselect name="Target Orientation" :list="['Portrait', 'Landscape']" v-model="value.targetOrientation"
@input="handleData('targetOrientation')"/>
<CVswitch name="Output multiple" v-model="value.multiple" @input="handleData('multiple')"/>
<span>Calibrate:</span>
<v-divider dark color="white"/>
<CVselect name="Calibration Mode" v-model="value.calibrationMode" :list="['None','Single point','Dual point']"
@input="handleData('calibrationMode')"/>
<component :raw-point="rawPoint" :is="selectedComponent" @update="doUpdate" @snackbar="showSnackbar"/>
<v-snackbar :timeout="3000" v-model="snackbar" top color="error">
<span style="color:#000">{{snackbarText}}</span>
<v-btn color="black" text @click="snackbar = false">Close</v-btn>
</v-snackbar>
</div>
<div>
<CVselect
v-model="value.sortMode"
name="Sort Mode"
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Centermost']"
@input="handleData('sortMode')"
@rollback="rollback('sortMode',e)"
/>
<CVselect
v-model="value.targetRegion"
name="Target Region"
:list="['Center','Top','Bottom','Left','Right']"
@input="handleData('targetRegion')"
@rollback="e=> rollback('targetRegion',e)"
/>
<CVselect
v-model="value.targetOrientation"
name="Target Orientation"
:list="['Portrait', 'Landscape']"
@input="handleData('targetOrientation')"
@rollback="e=> rollback('targetOrientation',e)"
/>
<CVswitch
v-model="value.multiple"
name="Output multiple"
@input="handleData('multiple')"
@rollback="e=> rollback('multiple',e)"
/>
<span>Calibrate:</span>
<v-divider
dark
color="white"
/>
<CVselect
v-model="value.calibrationMode"
name="Calibration Mode"
:list="['None','Single point','Dual point']"
@input="handleData('calibrationMode')"
@rollback="e=> rollback('calibrationMode',e)"
/>
<component
:is="selectedComponent"
:raw-point="rawPoint"
@update="doUpdate"
@snackbar="showSnackbar"
/>
<v-snackbar
v-model="snackbar"
:timeout="3000"
top
color="error"
>
<span style="color:#000">{{ snackbarText }}</span>
<v-btn
color="black"
text
@click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>
<script>
@@ -29,7 +75,6 @@
export default {
name: 'Output',
props: ['value'],
components: {
CVselect,
CVswitch,
@@ -37,15 +82,7 @@
DualCalibration,
},
methods: {
doUpdate() {
this.$emit('update')
},
showSnackbar(message) {
this.snackbarText = message;
this.snackbar = true;
},
},
props: ['value'],
data() {
return {
@@ -72,6 +109,15 @@
return this.$store.state.point.rawPoint;
}
}
},
methods: {
doUpdate() {
this.$emit('update')
},
showSnackbar(message) {
this.snackbarText = message;
this.snackbar = true;
},
}
}
</script>

View File

@@ -1,26 +1,74 @@
<template>
<div>
<CVrangeSlider v-model="value.hue" name="Hue" :min="0" :max="180" @input="handleData('hue')"/>
<CVrangeSlider v-model="value.saturation" name="Saturation" :min="0" :max="255"
@input="handleData('saturation')"/>
<CVrangeSlider v-model="value.value" name="Value" :min="0" :max="255" @input="handleData('value')"/>
<v-divider color="darkgray " style="margin-top: 5px"/>
<v-btn style="margin: 20px;" color="#4baf62" @click="setFunction(1)" small>
<v-icon>colorize</v-icon>
Eye drop
</v-btn>
<v-btn style="margin: 20px;" color="#4baf62" @click="setFunction(2)" small>
<v-icon>add</v-icon>
Expand Selection
</v-btn>
<v-btn style="margin: 20px;" color="#4baf62" @click="setFunction(3)" small>
<v-icon>remove</v-icon>
Shrink Selection
</v-btn>
<v-divider color="darkgray "/>
<CVswitch v-model="value.erode" name="Erode" @input="handleData('erode')"/>
<CVswitch v-model="value.dilate" name="Dilate" @input="handleData('dilate')"/>
</div>
<div>
<CVrangeSlider
v-model="value.hue"
name="Hue"
:min="0"
:max="180"
@input="handleData('hue')"
@rollback="e => rollback('hue',e)"
/>
<CVrangeSlider
v-model="value.saturation"
name="Saturation"
:min="0"
:max="255"
@input="handleData('saturation')"
@rollback="e => rollback('saturation',e)"
/>
<CVrangeSlider
v-model="value.value"
name="Value"
:min="0"
:max="255"
@input="handleData('value')"
@rollback="e => rollback('value',e)"
/>
<v-divider
color="darkgray "
style="margin-top: 5px"
/>
<v-btn
style="margin: 20px;"
color="#4baf62"
small
@click="setFunction(1)"
>
<v-icon>colorize</v-icon>
Eye drop
</v-btn>
<v-btn
style="margin: 20px;"
color="#4baf62"
small
@click="setFunction(2)"
>
<v-icon>add</v-icon>
Expand Selection
</v-btn>
<v-btn
style="margin: 20px;"
color="#4baf62"
small
@click="setFunction(3)"
>
<v-icon>remove</v-icon>
Shrink Selection
</v-btn>
<v-divider color="darkgray " />
<CVswitch
v-model="value.erode"
name="Erode"
@input="handleData('erode')"
@rollback="e => rollback('erode',e)"
/>
<CVswitch
v-model="value.dilate"
name="Dilate"
@input="handleData('dilate')"
@rollback="e => rollback('dilate',e)"
/>
</div>
</template>
<script>
@@ -29,11 +77,11 @@
export default {
name: 'Threshold',
props: ['value'],
components: {
CVrangeSlider,
CVswitch
},
props: ['value'],
data() {
return {
currentFunction: undefined,
@@ -56,6 +104,13 @@
}
}
},
mounted: function () {
const self = this;
this.colorPicker = require('../../plugins/ColorPicker').default;
this.$nextTick(() => {
self.colorPicker.initColorPicker();
});
},
methods: {
onClick(event) {
if (this.currentFunction !== undefined) {
@@ -97,13 +152,6 @@
break;
}
}
},
mounted: function () {
const self = this;
this.colorPicker = require('../../plugins/ColorPicker').default;
this.$nextTick(() => {
self.colorPicker.initColorPicker();
});
}
}

View File

@@ -1,23 +1,45 @@
<template>
<div>
<v-row>
<v-col class="colsClass" cols="6">
<v-tabs background-color="#212121" dark fixed-tabs height="50" slider-color="#4baf62"
v-model="selectedTab">
<v-tab to="">General</v-tab>
<v-tab to="">Cameras</v-tab>
</v-tabs>
<div style="padding-left:30px">
<component :is="selectedComponent" @update="$emit('save')"/>
</div>
</v-col>
<v-col class="colsClass" v-show="selectedTab === 1">
<div class="videoClass">
<cvImage :address="$store.getters.streamAddress" :scale="75"/>
</div>
</v-col>
</v-row>
</div>
<div>
<v-row>
<v-col
class="colsClass"
cols="6"
>
<v-tabs
v-model="selectedTab"
background-color="#212121"
dark
fixed-tabs
height="50"
slider-color="#4baf62"
>
<v-tab to="">
General
</v-tab>
<v-tab to="">
Cameras
</v-tab>
</v-tabs>
<div style="padding-left:30px">
<component
:is="selectedComponent"
@update="$emit('save')"
/>
</div>
</v-col>
<v-col
v-show="selectedTab === 1"
class="colsClass"
>
<div class="videoClass">
<cvImage
:address="$store.getters.streamAddress"
:scale="75"
/>
</div>
</v-col>
</v-row>
</div>
</template>
<script>

View File

@@ -1,72 +1,141 @@
<template>
<div>
<div>
<div>
<CVselect name="Camera" :list="$store.getters.cameraList" v-model="currentCameraIndex"
@input="handleInput('currentCamera',currentCameraIndex)"/>
<CVnumberinput name="Diagonal FOV" v-model="cameraSettings.fov"/>
<br>
<CVnumberinput name="Camera pitch" v-model="cameraSettings.tilt" :step="0.01"/>
<br>
<v-btn style="margin-top:10px" small color="#4baf62" @click="sendCameraSettings">Save Camera Settings
</v-btn>
</div>
<div style="margin-top: 15px">
<span>3D Calibration</span>
<v-divider color="white" style="margin-bottom: 10px"/>
<v-row>
<v-col>
<CVselect name="Resolution" v-model="resolutionIndex" :list="stringResolutionList"/>
</v-col>
<v-col>
<CVnumberinput name="Square Size (in)" v-model="squareSize"/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-btn small :color="calibrationModeButton.color" @click="sendCalibrationMode"
:disabled="checkResolution">
{{calibrationModeButton.text}}
</v-btn>
</v-col>
<v-col>
<v-btn small :color="cancellationModeButton.color" @click="sendCalibrationFinish"
:disabled="checkCancellation">
{{cancellationModeButton.text}}
</v-btn>
</v-col>
<v-col>
<v-btn color="whitesmoke" small @click="downloadBoard">
Download Checkerboard
</v-btn>
<a ref="calibrationFile" style="color: black; text-decoration: none; display: none"
:href="require('../../assets/chessboard.png')"
download="Calibration Board.png"/>
</v-col>
</v-row>
<v-row v-if="isCalibrating">
<v-col>
<span>Snapshot Amount: {{snapshotAmount}}</span>
</v-col>
</v-row>
<div v-if="isCalibrating">
<v-checkbox v-model="isAdvanced" label="Advanced Menu" dark/>
<div v-if="isAdvanced" >
<CVslider name="Exposure" v-model="$store.getters.pipeline.exposure" :min="0" :max="100"
@input="e=> handleInput('exposure', e)"/>
<CVslider name="Brightness" v-model="$store.getters.pipeline.brightness" :min="0" :max="100"
@input="e=> handleInput('brightness', e)"/>
<CVslider name="Gain" v-if="$store.getters.pipeline.gain !== -1"
v-model="$store.getters.pipeline.gain" :min="0" :max="100"
@input="e=> handleInput('gain', e)"/>
<CVselect name="FPS" v-model="$store.getters.pipeline.videoModeIndex" :list="stringFpsList"
@input="changeFps"/>
</div>
</div>
</div>
<v-snackbar v-model="snack" top :color="snackbar.color">
<span>{{snackbar.text}}</span>
</v-snackbar>
<CVselect
v-model="currentCameraIndex"
name="Camera"
:list="$store.getters.cameraList"
@input="handleInput('currentCamera',currentCameraIndex)"
/>
<CVnumberinput
v-model="cameraSettings.fov"
name="Diagonal FOV"
/>
<br>
<CVnumberinput
v-model="cameraSettings.tilt"
name="Camera pitch"
:step="0.01"
/>
<br>
<v-btn
style="margin-top:10px"
small
color="#4baf62"
@click="sendCameraSettings"
>
Save Camera Settings
</v-btn>
</div>
<div style="margin-top: 15px">
<span>3D Calibration</span>
<v-divider
color="white"
style="margin-bottom: 10px"
/>
<v-row>
<v-col>
<CVselect
v-model="resolutionIndex"
name="Resolution"
:list="stringResolutionList"
/>
</v-col>
<v-col>
<CVnumberinput
v-model="squareSize"
name="Square Size (in)"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-btn
small
:color="calibrationModeButton.color"
:disabled="checkResolution"
@click="sendCalibrationMode"
>
{{ calibrationModeButton.text }}
</v-btn>
</v-col>
<v-col>
<v-btn
small
:color="cancellationModeButton.color"
:disabled="checkCancellation"
@click="sendCalibrationFinish"
>
{{ cancellationModeButton.text }}
</v-btn>
</v-col>
<v-col>
<v-btn
color="whitesmoke"
small
@click="downloadBoard"
>
Download Checkerboard
</v-btn>
<a
ref="calibrationFile"
style="color: black; text-decoration: none; display: none"
:href="require('../../assets/chessboard.png')"
download="Calibration Board.png"
/>
</v-col>
</v-row>
<v-row v-if="isCalibrating">
<v-col>
<span>Snapshot Amount: {{ snapshotAmount }}</span>
</v-col>
</v-row>
<div v-if="isCalibrating">
<v-checkbox
v-model="isAdvanced"
label="Advanced Menu"
dark
/>
<div v-if="isAdvanced">
<CVslider
v-model="$store.getters.pipeline.exposure"
name="Exposure"
:min="0"
:max="100"
@input="e=> handleInput('exposure', e)"
/>
<CVslider
v-model="$store.getters.pipeline.brightness"
name="Brightness"
:min="0"
:max="100"
@input="e=> handleInput('brightness', e)"
/>
<CVslider
v-if="$store.getters.pipeline.gain !== -1"
v-model="$store.getters.pipeline.gain"
name="Gain"
:min="0"
:max="100"
@input="e=> handleInput('gain', e)"
/>
<CVselect
v-model="$store.getters.pipeline.videoModeIndex"
name="FPS"
:list="stringFpsList"
@input="changeFps"
/>
</div>
</div>
</div>
<v-snackbar
v-model="snack"
top
:color="snackbar.color"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
</div>
</template>
<script>
@@ -104,6 +173,83 @@
isAdvanced: false
}
},
computed: {
checkResolution() {
return this.resolutionIndex === undefined;
},
checkCancellation() {
if (this.isCalibrating) {
return false
} else if (this.checkResolution) {
return true;
} else {
return true
}
},
currentCameraIndex: {
get() {
return this.$store.state.currentCameraIndex;
},
set(value) {
this.$store.commit('currentCameraIndex', value);
}
},
filteredResolutionList: {
get() {
let tmp_list = [];
for (let i in this.$store.state.resolutionList) {
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
if (!tmp_list.some(e => e.width === res.width && e.height === res.height)) {
res['actualIndex'] = parseInt(i);
tmp_list.push(res);
}
}
}
return tmp_list;
}
},
filteredFpsList() {
let selectedRes = this.$store.state.resolutionList[this.resolutionIndex];
let tmpList = [];
for (let i in this.$store.state.resolutionList) {
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
if (!tmpList.some(e => e['fps'] === res['fps'])) {
if (res.width === selectedRes.width && res.height === selectedRes.height) {
res['actualIndex'] = parseInt(i);
tmpList.push(res);
}
}
}
}
return tmpList;
},
stringFpsList() {
let tmp = [];
for (let i of this.filteredFpsList) {
tmp.push(i['fps']);
}
return tmp;
},
stringResolutionList: {
get() {
let tmp = [];
for (let i of this.filteredResolutionList) {
tmp.push(`${i['width']} X ${i['height']}`)
}
return tmp
}
},
cameraSettings: {
get() {
return this.$store.getters.cameraSettings;
},
set(value) {
this.$store.commit('cameraSettings', value);
}
}
},
methods: {
downloadBoard() {
this.axios.get("http://" + this.$address + require('../../assets/chessboard.png'), {responseType: 'blob'}).then((response) => {
@@ -188,83 +334,6 @@
self.cancellationModeButton.color = "red";
});
}
},
computed: {
checkResolution() {
return this.resolutionIndex === undefined;
},
checkCancellation() {
if (this.isCalibrating) {
return false
} else if (this.checkResolution) {
return true;
} else {
return true
}
},
currentCameraIndex: {
get() {
return this.$store.state.currentCameraIndex;
},
set(value) {
this.$store.commit('currentCameraIndex', value);
}
},
filteredResolutionList: {
get() {
let tmp_list = [];
for (let i in this.$store.state.resolutionList) {
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
if (!tmp_list.some(e => e.width === res.width && e.height === res.height)) {
res['actualIndex'] = parseInt(i);
tmp_list.push(res);
}
}
}
return tmp_list;
}
},
filteredFpsList() {
let selectedRes = this.$store.state.resolutionList[this.resolutionIndex];
let tmpList = [];
for (let i in this.$store.state.resolutionList) {
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
if (!tmpList.some(e => e['fps'] === res['fps'])) {
if (res.width === selectedRes.width && res.height === selectedRes.height) {
res['actualIndex'] = parseInt(i);
tmpList.push(res);
}
}
}
}
return tmpList;
},
stringFpsList() {
let tmp = [];
for (let i of this.filteredFpsList) {
tmp.push(i['fps']);
}
return tmp;
},
stringResolutionList: {
get() {
let tmp = [];
for (let i of this.filteredResolutionList) {
tmp.push(`${i['width']} X ${i['height']}`)
}
return tmp
}
},
cameraSettings: {
get() {
return this.$store.getters.cameraSettings;
},
set(value) {
this.$store.commit('cameraSettings', value);
}
}
}
}
</script>

View File

@@ -1,43 +1,94 @@
<template>
<div>
<div style="margin-top: 15px">
<span>General Settings:</span>
<v-divider color="white"></v-divider>
</div>
<CVnumberinput v-model="settings.teamNumber" name="Team Number"/>
<CVradio v-model="settings.connectionType" :list="['DHCP','Static']"/>
<v-divider color="white"/>
<CVinput name="IP" v-model="settings.ip" :disabled="isDisabled"/>
<CVinput name="NetMask" v-model="settings.netmask" :disabled="isDisabled"/>
<CVinput name="Gateway" v-model="settings.gateway" :disabled="isDisabled"/>
<v-divider color="white"/>
<CVinput name="Hostname" v-model="settings.hostname"/>
<v-btn style="margin-top:10px" small color="#4baf62" @click="sendGeneralSettings">Save General Settings</v-btn>
<div style="margin-top: 20px">
<span>Install or Update:</span>
<v-divider color="white"/>
</div>
<div v-if="!isLoading">
<v-row dense align="center">
<v-col :cols="3">
<span>Choose a newer version: </span>
</v-col>
<v-col :cols="6">
<v-file-input accept=".jar" dark v-model="file"/>
</v-col>
</v-row>
<v-btn small @click="installOrUpdate">{{fileUploadText}}</v-btn>
</div>
<div v-else style="text-align: center; margin-top: 20px">
<v-progress-circular color="white" :indeterminate="true" size="32"
width="4"/>
<br>
<span>Please wait this may take a while</span>
</div>
<v-snackbar v-model="snack" top :color="snackbar.color">
<span>{{snackbar.text}}</span>
</v-snackbar>
<div>
<div style="margin-top: 15px">
<span>General Settings:</span>
<v-divider color="white" />
</div>
<CVnumberinput
v-model="settings.teamNumber"
name="Team Number"
/>
<CVradio
v-model="settings.connectionType"
:list="['DHCP','Static']"
/>
<v-divider color="white" />
<CVinput
v-model="settings.ip"
name="IP"
:disabled="isDisabled"
/>
<CVinput
v-model="settings.netmask"
name="NetMask"
:disabled="isDisabled"
/>
<CVinput
v-model="settings.gateway"
name="Gateway"
:disabled="isDisabled"
/>
<v-divider color="white" />
<CVinput
v-model="settings.hostname"
name="Hostname"
/>
<v-btn
style="margin-top:10px"
small
color="#4baf62"
@click="sendGeneralSettings"
>
Save General Settings
</v-btn>
<div style="margin-top: 20px">
<span>Install or Update:</span>
<v-divider color="white" />
</div>
<div v-if="!isLoading">
<v-row
dense
align="center"
>
<v-col :cols="3">
<span>Choose a newer version: </span>
</v-col>
<v-col :cols="6">
<v-file-input
v-model="file"
accept=".jar"
dark
/>
</v-col>
</v-row>
<v-btn
small
@click="installOrUpdate"
>
{{ fileUploadText }}
</v-btn>
</div>
<div
v-else
style="text-align: center; margin-top: 20px"
>
<v-progress-circular
color="white"
:indeterminate="true"
size="32"
width="4"
/>
<br>
<span>Please wait this may take a while</span>
</div>
<v-snackbar
v-model="snack"
top
:color="snackbar.color"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
</div>
</template>
<script>
@@ -63,6 +114,23 @@
isLoading: false
}
},
computed: {
fileUploadText() {
if (this.file !== undefined) {
return "Update and run at startup"
} else {
return "Run current version at startup"
}
},
isDisabled() {
return this.settings.connectionType === 0;
},
settings: {
get() {
return this.$store.state.settings;
}
}
},
methods: {
sendGeneralSettings() {
const self = this;
@@ -111,23 +179,6 @@
this.snack = true;
})
}
},
computed: {
fileUploadText() {
if (this.file !== undefined) {
return "Update and run at startup"
} else {
return "Run current version at startup"
}
},
isDisabled() {
return this.settings.connectionType === 0;
},
settings: {
get() {
return this.$store.state.settings;
}
}
}
}
</script>