mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
Ui rework (#96)
* updated libs, folder rework * started store modules added data handle mixin * more store rework * name refractor and component split * bug fixes and code cleanup
This commit is contained in:
1088
chameleon-client/package-lock.json
generated
1088
chameleon-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chameleon-client",
|
||||
"version": "0.1.0",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
@@ -17,19 +17,19 @@
|
||||
"vue-axios": "^2.1.5",
|
||||
"vue-native-websocket": "^2.0.14",
|
||||
"vue-router": "^3.1.6",
|
||||
"vuetify": "^2.2.17",
|
||||
"vuex": "^3.1.3"
|
||||
"vuetify": "^2.2.26",
|
||||
"vuex": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdi/font": "^4.9.95",
|
||||
"@vue/cli-plugin-babel": "^3.12.1",
|
||||
"@vue/cli-plugin-eslint": "^4.2.3",
|
||||
"@vue/cli-service": "^4.2.3",
|
||||
"@vue/cli-plugin-eslint": "^4.3.1",
|
||||
"@vue/cli-service": "^4.3.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"papaparse": "^5.1.1",
|
||||
"sass": "^1.26.3",
|
||||
"papaparse": "^5.2.0",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^7.1.0",
|
||||
"vue-cli-plugin-vuetify": "^0.6.3",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
if (this.$store.state.hasOwnProperty(key)) {
|
||||
this.$store.commit(key, value);
|
||||
} else if (this.$store.state.pipeline.hasOwnProperty(key)) {
|
||||
this.$store.commit('setPipeValues', {[key]: value});
|
||||
this.$store.commit('mutatePipeline', {'key': key, 'value': value});
|
||||
} else {
|
||||
switch (key) {
|
||||
default: {
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg3713"
|
||||
width="219"
|
||||
height="230"
|
||||
viewBox="0 0 219 230"
|
||||
sodipodi:docname="robotIcon.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata3719">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3717">
|
||||
<linearGradient
|
||||
id="linearGradient937"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop933"
|
||||
offset="0"
|
||||
style="stop-color:#228a42;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop935"
|
||||
offset="1"
|
||||
style="stop-color:#5cb34a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient931"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop927"
|
||||
offset="0"
|
||||
style="stop-color:#228a42;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop929"
|
||||
offset="1"
|
||||
style="stop-color:#5cb34a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient925">
|
||||
<stop
|
||||
style="stop-color:#228a42;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop921" />
|
||||
<stop
|
||||
style="stop-color:#5cb34a;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop923" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient925"
|
||||
id="linearGradient4820"
|
||||
x1="108.25"
|
||||
y1="186.25"
|
||||
x2="109.5"
|
||||
y2="21"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient937"
|
||||
id="linearGradient4822"
|
||||
x1="108.25"
|
||||
y1="186.25"
|
||||
x2="109.5"
|
||||
y2="21"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient931"
|
||||
id="linearGradient4824"
|
||||
x1="108.25"
|
||||
y1="186.25"
|
||||
x2="109.5"
|
||||
y2="21"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview3715"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="110.62282"
|
||||
inkscape:cy="130.60249"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3713"
|
||||
showguides="false"
|
||||
inkscape:snap-page="false"
|
||||
inkscape:snap-others="true"
|
||||
inkscape:snap-object-midpoints="true" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient4824);fill-opacity:1;stroke:#000000;stroke-width:0.49889764;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 167,156.45722 -41,-2 -14,-17 V 55.457215 Z"
|
||||
id="path3725-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient4822);stroke:#020000;stroke-width:0.49889764;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1;opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 76.866,158.246 13,-0.496 19.567,11.61661 19.567,-11.39151 13,0.2709 -32.5675,19.39302 z"
|
||||
id="path3742"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient4820);stroke:#020000;stroke-width:0.49889764;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1;opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 51.864904,156.45722 41,-2 13.999996,-17 V 55.457209 Z"
|
||||
id="path3725-6-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
28
chameleon-client/src/components/common/cv-image.vue
Normal file
28
chameleon-client/src/components/common/cv-image.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<img id="CameraStream" v-bind:style="styleObject" :src="address" @click="e => $emit('click', e)"
|
||||
crossorigin="Anonymous" alt=""/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "cv-image",
|
||||
data: () => {
|
||||
return {}
|
||||
},
|
||||
props: ['address', 'scale'],
|
||||
computed: {
|
||||
styleObject: {
|
||||
get() {
|
||||
return {
|
||||
width: `${this.scale}%`,
|
||||
height: `${this.scale}%`,
|
||||
display: 'block',
|
||||
margin: 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,263 @@
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVicon from '../common/cv-icon'
|
||||
import CVselect from '../common/cv-select'
|
||||
import CVinput from '../common/cv-input'
|
||||
|
||||
export default {
|
||||
name: "CameraAndPipelineSelect",
|
||||
components: {
|
||||
CVicon,
|
||||
CVselect,
|
||||
CVinput
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
re: RegExp("^[A-Za-z0-9 \\-)(]*[A-Za-z0-9][A-Za-z0-9 \\-)(.]*$"),
|
||||
isCameraNameEdit: false,
|
||||
newCameraName: "",
|
||||
cameraNameError: "",
|
||||
isPipelineNameEdit: false,
|
||||
namingDialog: false,
|
||||
newPipelineName: "",
|
||||
duplicateDialog: false,
|
||||
anotherCamera: false,
|
||||
pipelineDuplicate: {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toCameraNameChange() {
|
||||
this.newCameraName = this.$store.getters.cameraList[this.currentCameraIndex];
|
||||
this.isCameraNameEdit = true;
|
||||
},
|
||||
saveCameraNameChange() {
|
||||
if (this.checkCameraName === "") {
|
||||
this.handleInput("changeCameraName", this.newCameraName);
|
||||
this.discardCameraNameChange();
|
||||
}
|
||||
},
|
||||
discardCameraNameChange() {
|
||||
this.isCameraNameEdit = false;
|
||||
this.newCameraName = "";
|
||||
},
|
||||
toPipelineNameChange() {
|
||||
this.newPipelineName = this.$store.getters.pipelineList[this.currentPipelineIndex - 1];
|
||||
this.isPipelineNameEdit = true;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
toCreatePipeline() {
|
||||
this.newPipelineName = "New Pipeline";
|
||||
this.isPipelineNameEdit = false;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
openDuplicateDialog() {
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: this.currentPipelineIndex - 1,
|
||||
camera: -1
|
||||
};
|
||||
this.duplicateDialog = true;
|
||||
},
|
||||
deleteCurrentPipeline() {
|
||||
if (this.$store.getters.pipelineList.length > 1) {
|
||||
this.handleInput('command', 'deleteCurrentPipeline');
|
||||
} else {
|
||||
this.snackbar = true;
|
||||
}
|
||||
},
|
||||
savePipelineNameChange() {
|
||||
if (this.checkPipelineName === "") {
|
||||
if (this.isPipelineNameEdit) {
|
||||
this.handleInput("changePipelineName", this.newPipelineName);
|
||||
} else {
|
||||
this.handleInput("addNewPipeline", this.newPipelineName);
|
||||
}
|
||||
this.discardPipelineNameChange();
|
||||
}
|
||||
},
|
||||
duplicatePipeline() {
|
||||
if (!this.anotherCamera) {
|
||||
this.pipelineDuplicate.camera = -1
|
||||
}
|
||||
// this.handleInput("duplicatePipeline", this.pipelineDuplicate);
|
||||
this.axios.post("http://" + this.$address + "/api/vision/duplicate", this.pipelineDuplicate);
|
||||
this.closeDuplicateDialog();
|
||||
},
|
||||
closeDuplicateDialog() {
|
||||
this.duplicateDialog = false;
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
}
|
||||
},
|
||||
discardPipelineNameChange() {
|
||||
this.namingDialog = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,9 +1,8 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import store from './store/index'
|
||||
import vuetify from './plugins/vuetify';
|
||||
import VueNativeSock from 'vue-native-websocket';
|
||||
import msgPack from 'msgpack5';
|
||||
import axios from 'axios';
|
||||
import VueAxios from "vue-axios";
|
||||
@@ -16,24 +15,22 @@ if (process.env.NODE_ENV === "production") {
|
||||
Vue.prototype.$address = location.hostname + ":5800";
|
||||
}
|
||||
|
||||
const url = 'ws://' + Vue.prototype.$address + '/websocket';
|
||||
var ws = new WebSocket(url);
|
||||
const wsURL = 'ws://' + Vue.prototype.$address + '/websocket';
|
||||
|
||||
const ws = new WebSocket(wsURL);
|
||||
ws.binaryType = "arraybuffer";
|
||||
|
||||
Vue.use(VueNativeSock, url, {
|
||||
import VueNativeSock from 'vue-native-websocket';
|
||||
|
||||
Vue.use(VueNativeSock, wsURL, {
|
||||
WebSocket: ws
|
||||
});
|
||||
Vue.use(VueAxios, axios);
|
||||
Vue.prototype.$msgPack = msgPack(true);
|
||||
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
handleInput(key, value) {
|
||||
let msg = this.$msgPack.encode({[key]: value});
|
||||
this.$socket.send(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
import {dataHandleMixin} from './mixins/global/dataHandleMixin'
|
||||
|
||||
Vue.mixin(dataHandleMixin);
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
|
||||
13
chameleon-client/src/mixins/global/dataHandleMixin.js
Normal file
13
chameleon-client/src/mixins/global/dataHandleMixin.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export const dataHandleMixin = {
|
||||
methods:{
|
||||
handleInput(key, value) {
|
||||
let msg = this.$msgPack.encode({[key]: value});
|
||||
this.$socket.send(msg);
|
||||
},
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
@@ -1,29 +1,23 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
import Camera from "./views/PipelineView";
|
||||
import Settings from "./views/SettingsView";
|
||||
Vue.use(Router);
|
||||
|
||||
function lazyLoad(view) {
|
||||
return () => import(`@/views/${view}.vue`)
|
||||
}
|
||||
|
||||
export default new Router({
|
||||
// mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/vision'
|
||||
},
|
||||
{
|
||||
path: '/vision',
|
||||
name: 'Vision',
|
||||
component: lazyLoad('Camera')
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: lazyLoad('Settings')
|
||||
},
|
||||
]
|
||||
routes: [{
|
||||
path: '/',
|
||||
redirect: '/vision'
|
||||
}, {
|
||||
path: '/vision',
|
||||
name: 'Vision',
|
||||
component: Camera
|
||||
}, {
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: Settings
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const set = key => (state, val) => {
|
||||
Vue.set(state, key, val);
|
||||
};
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
settings: {
|
||||
teamNumber: 1577,
|
||||
connectionType: 0,
|
||||
ip: "",
|
||||
gateway: "",
|
||||
netmask: "",
|
||||
hostname: "chameleon-vision"
|
||||
},
|
||||
pipeline: {
|
||||
exposure: 0,
|
||||
brightness: 0,
|
||||
rotationMode: 0,
|
||||
hue: [0, 15],
|
||||
saturation: [0, 15],
|
||||
value: [0, 25],
|
||||
erode: false,
|
||||
dilate: false,
|
||||
area: [0, 12],
|
||||
ratio: [0, 12],
|
||||
extent: [0, 12],
|
||||
speckle: 5,
|
||||
targetGrouping: 0,
|
||||
targetIntersection: 0,
|
||||
sortMode: 0,
|
||||
multiple: false,
|
||||
isBinary: 0,
|
||||
calibrationMode: 0,
|
||||
videoModeIndex: 0,
|
||||
streamDivisor: 0,
|
||||
is3D: false,
|
||||
targetRegion: 0,
|
||||
targetOrientation: 1
|
||||
},
|
||||
cameraSettings: {
|
||||
calibration: [],
|
||||
fov: 0,
|
||||
resolution: 0,
|
||||
streamDivisor: 0,
|
||||
tilt: 0
|
||||
},
|
||||
resolutionList: [],
|
||||
port: 1181,
|
||||
currentCameraIndex: 0,
|
||||
currentPipelineIndex: 0,
|
||||
cameraList: [],
|
||||
pipelineList: [],
|
||||
point: {},
|
||||
saveBar: false
|
||||
},
|
||||
mutations: {
|
||||
settings: set('settings'),
|
||||
pipeline: set('pipeline'),
|
||||
cameraSettings: set('cameraSettings'),
|
||||
resolutionList: set('resolutionList'),
|
||||
port: set('port'),
|
||||
currentCameraIndex: set('currentCameraIndex'),
|
||||
currentPipelineIndex: set('currentPipelineIndex'),
|
||||
cameraList: set('cameraList'),
|
||||
pipelineList: set('pipelineList'),
|
||||
point: set('point'),
|
||||
setPipeValues(state, obj) {
|
||||
for (let i in obj) {
|
||||
Vue.set(state.pipeline, i, obj[i]);
|
||||
}
|
||||
},
|
||||
driverMode: set('driverMode'),
|
||||
saveBar: set("saveBar")
|
||||
},
|
||||
actions: {
|
||||
settings: state => state.settings,
|
||||
pipeline: state => state.pipeline,
|
||||
cameraSettings: state => state.cameraSettings,
|
||||
resolutionList: state => state.resolutionList,
|
||||
port: state => state.port,
|
||||
currentCameraIndex: state => state.currentCameraIndex,
|
||||
currentPipelineIndex: state => state.currentPipelineIndex,
|
||||
cameraList: state => state.cameraList,
|
||||
pipelineList: state => state.pipelineList,
|
||||
point: state => state.point,
|
||||
driverMode: state => state.driverMode,
|
||||
saveBar: state => state.saveBar
|
||||
}
|
||||
})
|
||||
64
chameleon-client/src/store/index.js
Normal file
64
chameleon-client/src/store/index.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import pipeline from "./modules/pipeline";
|
||||
import generalSettings from "./modules/generalSettings";
|
||||
import cameraSettings from "./modules/cameraSettings";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const set = key => (state, val) => {
|
||||
Vue.set(state, key, val);
|
||||
};
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
pipeline: pipeline,
|
||||
settings: generalSettings,
|
||||
cameraSettings: cameraSettings
|
||||
},
|
||||
state: {
|
||||
resolutionList: [],
|
||||
port: 1181,
|
||||
currentCameraIndex: 0,
|
||||
currentPipelineIndex: 0,
|
||||
cameraList: [],
|
||||
pipelineList: [],
|
||||
point: {},
|
||||
saveBar: false
|
||||
},
|
||||
mutations: {
|
||||
settings: set('settings'),
|
||||
pipeline: set('pipeline'),
|
||||
cameraSettings: set('cameraSettings'),
|
||||
resolutionList: set('resolutionList'),
|
||||
port: set('port'),
|
||||
currentCameraIndex: set('currentCameraIndex'),
|
||||
currentPipelineIndex: set('currentPipelineIndex'),
|
||||
cameraList: set('cameraList'),
|
||||
pipelineList: set('pipelineList'),
|
||||
point: set('point'),
|
||||
driverMode: set('driverMode'),
|
||||
saveBar: set("saveBar")
|
||||
},
|
||||
getters: {
|
||||
streamAddress: state => {
|
||||
return "http://" + location.hostname + ":" + state.port + "/stream.mjpg";
|
||||
},
|
||||
targets: state => {
|
||||
return state.point['targets']
|
||||
},
|
||||
cameraList: state => {
|
||||
return state.cameraList
|
||||
},
|
||||
pipelineList: state => {
|
||||
return state.pipelineList
|
||||
},
|
||||
currentCameraIndex: state => {
|
||||
return state.currentCameraIndex
|
||||
},
|
||||
currentPipelineIndex: state => {
|
||||
return state.currentPipelineIndex
|
||||
}
|
||||
}
|
||||
})
|
||||
14
chameleon-client/src/store/modules/cameraSettings.js
Normal file
14
chameleon-client/src/store/modules/cameraSettings.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export default {
|
||||
state: {
|
||||
calibration: [],
|
||||
fov: 0,
|
||||
resolution: 0,
|
||||
streamDivisor: 0,
|
||||
tilt: 0
|
||||
},
|
||||
getters: {
|
||||
cameraSettings: state => {
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
10
chameleon-client/src/store/modules/generalSettings.js
Normal file
10
chameleon-client/src/store/modules/generalSettings.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
state:{
|
||||
teamNumber: 1577,
|
||||
connectionType: 0,
|
||||
ip: "",
|
||||
gateway: "",
|
||||
netmask: "",
|
||||
hostname: "chameleon-vision"
|
||||
}
|
||||
}
|
||||
45
chameleon-client/src/store/modules/pipeline.js
Normal file
45
chameleon-client/src/store/modules/pipeline.js
Normal file
@@ -0,0 +1,45 @@
|
||||
export default {
|
||||
state: {
|
||||
exposure: 0,
|
||||
brightness: 0,
|
||||
gain: 0,
|
||||
rotationMode: 0,
|
||||
hue: [0, 15],
|
||||
saturation: [0, 15],
|
||||
value: [0, 25],
|
||||
erode: false,
|
||||
dilate: false,
|
||||
area: [0, 12],
|
||||
ratio: [0, 12],
|
||||
extent: [0, 12],
|
||||
speckle: 5,
|
||||
targetGrouping: 0,
|
||||
targetIntersection: 0,
|
||||
sortMode: 0,
|
||||
multiple: false,
|
||||
isBinary: 0,
|
||||
calibrationMode: 0,
|
||||
videoModeIndex: 0,
|
||||
streamDivisor: 0,
|
||||
is3D: false,
|
||||
targetRegion: 0,
|
||||
targetOrientation: 1
|
||||
},
|
||||
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)
|
||||
}
|
||||
|
||||
},
|
||||
actions: {},
|
||||
getters: {
|
||||
pipeline: state => {
|
||||
return state
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,414 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<v-row align="center">
|
||||
<v-col :cols="3" class="colsClass">
|
||||
<div style="padding-left:30px">
|
||||
<CVselect v-if="isCameraNameEdit === false" name="Camera" v-model="currentCameraIndex"
|
||||
:list="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 text="edit"
|
||||
@click="toCameraNameChange" tooltip="Edit camera name"/>
|
||||
<div v-else>
|
||||
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save"
|
||||
@click="saveCameraNameChange" tooltip="Save Camera Name"/>
|
||||
<CVicon color="error" style="display: inline-block;" hover text="close"
|
||||
@click="discardCameraNameChange" tooltip="Discard Changes"/>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col :cols="3" class="colsClass">
|
||||
<CVselect name="Pipeline"
|
||||
:list="['Driver Mode'].concat(pipelineList)"
|
||||
v-model="currentPipelineIndex"
|
||||
@input="handleInput('currentPipeline',currentPipelineIndex - 1)"/>
|
||||
</v-col>
|
||||
<v-col :cols="1" class="colsClass" 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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<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="currentPipelineIndex !== 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="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',pipeline.isBinary)" v-if="currentPipelineIndex !== 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">
|
||||
<img id="CameraStream" style="display: block;margin: auto; width: 70%;height: 70%;"
|
||||
v-if="cameraList.length > 0"
|
||||
:src="streamAddress" @click="onImageClick"
|
||||
crossorigin="Anonymous"/>
|
||||
<span style="display: block;margin: auto; width: 70%;height: 70%;" v-else>No Cameras Are connected</span>
|
||||
</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 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>
|
||||
<!-- 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="pipelineList" v-model="pipelineDuplicate.pipeline"/>
|
||||
<v-checkbox v-if="cameraList.length > 1" dark :label="'To another camera'" v-model="anotherCamera"/>
|
||||
<CVselect v-if="anotherCamera === true" name="Camera" v-model="pipelineDuplicate.camera"
|
||||
:list="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>
|
||||
<!-- 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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InputTab from './CameraViewes/InputTab'
|
||||
import ThresholdTab from './CameraViewes/ThresholdTab'
|
||||
import ContoursTab from './CameraViewes/ContoursTab'
|
||||
import OutputTab from './CameraViewes/OutputTab'
|
||||
import pnpTab from './CameraViewes/3D'
|
||||
import CVselect from '../components/cv-select'
|
||||
import CVicon from '../components/cv-icon'
|
||||
import CVinput from '../components/cv-input'
|
||||
|
||||
export default {
|
||||
name: 'CameraTab',
|
||||
components: {
|
||||
InputTab,
|
||||
ThresholdTab,
|
||||
ContoursTab,
|
||||
OutputTab,
|
||||
pnpTab,
|
||||
CVselect,
|
||||
CVicon,
|
||||
CVinput
|
||||
},
|
||||
methods: {
|
||||
onImageClick(event) {
|
||||
if (this.selectedTab === 1) {
|
||||
this.$refs.component.onClick(event);
|
||||
}
|
||||
},
|
||||
toCameraNameChange() {
|
||||
this.newCameraName = this.cameraList[this.currentCameraIndex];
|
||||
this.isCameraNameEdit = true;
|
||||
},
|
||||
saveCameraNameChange() {
|
||||
if (this.checkCameraName === "") {
|
||||
this.handleInput("changeCameraName", this.newCameraName);
|
||||
this.discardCameraNameChange();
|
||||
}
|
||||
},
|
||||
discardCameraNameChange() {
|
||||
this.isCameraNameEdit = false;
|
||||
this.newCameraName = "";
|
||||
},
|
||||
toPipelineNameChange() {
|
||||
this.newPipelineName = this.pipelineList[this.currentPipelineIndex - 1];
|
||||
this.isPipelineNameEdit = true;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
toCreatePipeline() {
|
||||
this.newPipelineName = "New Pipeline";
|
||||
this.isPipelineNameEdit = false;
|
||||
this.namingDialog = true;
|
||||
},
|
||||
savePipelineNameChange() {
|
||||
if (this.checkPipelineName === "") {
|
||||
if (this.isPipelineNameEdit) {
|
||||
this.handleInput("changePipelineName", this.newPipelineName);
|
||||
} else {
|
||||
this.handleInput("addNewPipeline", this.newPipelineName);
|
||||
}
|
||||
this.discardPipelineNameChange();
|
||||
}
|
||||
},
|
||||
discardPipelineNameChange() {
|
||||
this.namingDialog = false;
|
||||
this.isPipelineNameEdit = false;
|
||||
this.newPipelineName = "";
|
||||
},
|
||||
duplicatePipeline() {
|
||||
if (!this.anotherCamera) {
|
||||
this.pipelineDuplicate.camera = -1
|
||||
}
|
||||
// this.handleInput("duplicatePipeline", this.pipelineDuplicate);
|
||||
this.axios.post("http://" + this.$address + "/api/vision/duplicate", this.pipelineDuplicate);
|
||||
this.closeDuplicateDialog();
|
||||
},
|
||||
openDuplicateDialog() {
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: this.currentPipelineIndex - 1,
|
||||
camera: -1
|
||||
};
|
||||
this.duplicateDialog = true;
|
||||
},
|
||||
closeDuplicateDialog() {
|
||||
this.duplicateDialog = false;
|
||||
this.pipelineDuplicate = {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
}
|
||||
},
|
||||
deleteCurrentPipeline() {
|
||||
if (this.pipelineList.length > 1) {
|
||||
this.handleInput('command', 'deleteCurrentPipeline');
|
||||
} else {
|
||||
this.snackbar = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
re: RegExp("^[A-Za-z0-9 \\-)(]*[A-Za-z0-9][A-Za-z0-9 \\-)(.]*$"),
|
||||
selectedTab: 0,
|
||||
// camera edit variables
|
||||
isCameraNameEdit: false,
|
||||
newCameraName: "",
|
||||
cameraNameError: "",
|
||||
// pipeline edit variables
|
||||
isPipelineNameEdit: false,
|
||||
namingDialog: false,
|
||||
newPipelineName: "",
|
||||
duplicateDialog: false,
|
||||
anotherCamera: false,
|
||||
pipelineDuplicate: {
|
||||
pipeline: undefined,
|
||||
camera: -1
|
||||
},
|
||||
snackbar: false,
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checkCameraName() {
|
||||
if (this.newCameraName !== this.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.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
|
||||
if (this.re.test(this.newPipelineName)) {
|
||||
for (let pipe in this.pipelineList) {
|
||||
if (this.pipelineList.hasOwnProperty(pipe)) {
|
||||
if (this.newPipelineName === this.pipelineList[pipe]) {
|
||||
return "A pipeline with this name already exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "Pipeline name can only contain letters, numbers, and spaces"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
isBinaryNumber: {
|
||||
get() {
|
||||
return this.pipeline.isBinary ? 1 : 0
|
||||
},
|
||||
set(value) {
|
||||
this.pipeline.isBinary = !!value;
|
||||
}
|
||||
},
|
||||
selectedComponent: {
|
||||
get() {
|
||||
return this.currentPipelineIndex === 0 ? "InputTab" : ["InputTab", "ThresholdTab", "ContoursTab", "OutputTab", "pnpTab"][this.selectedTab];
|
||||
}
|
||||
},
|
||||
targets: {
|
||||
get: function () {
|
||||
return this.$store.state.point.targets;
|
||||
}
|
||||
},
|
||||
fps: {
|
||||
get() {
|
||||
return this.$store.state.point.fps;
|
||||
}
|
||||
},
|
||||
currentCameraIndex: {
|
||||
get() {
|
||||
return this.$store.state.currentCameraIndex;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('currentCameraIndex', value);
|
||||
}
|
||||
},
|
||||
currentPipelineIndex: {
|
||||
get() {
|
||||
return this.$store.state.currentPipelineIndex + 1;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('currentPipelineIndex', value - 1);
|
||||
}
|
||||
},
|
||||
cameraList: {
|
||||
get() {
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
pipelineList: {
|
||||
get() {
|
||||
return this.$store.state.pipelineList;
|
||||
}
|
||||
},
|
||||
pipeline: {
|
||||
get() {
|
||||
return this.$store.state.pipeline;
|
||||
}
|
||||
},
|
||||
streamAddress: {
|
||||
get() {
|
||||
return "http://" + location.hostname + ":" + this.$store.state.port + "/stream.mjpg";
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.colsClass {
|
||||
padding: 0 !important;
|
||||
|
||||
}
|
||||
|
||||
.videoClass {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
th {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
150
chameleon-client/src/views/PipelineView.vue
Normal file
150
chameleon-client/src/views/PipelineView.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CameraAndPipelineSelect from "../components/pipeline/CameraAndPipelineSelect";
|
||||
import cvImage from '../components/common/cv-image'
|
||||
import InputTab from './PipelineViewes/InputTab'
|
||||
import ThresholdTab from './PipelineViewes/ThresholdTab'
|
||||
import ContoursTab from './PipelineViewes/ContoursTab'
|
||||
import OutputTab from './PipelineViewes/OutputTab'
|
||||
import pnpTab from './PipelineViewes/3D'
|
||||
|
||||
export default {
|
||||
name: 'CameraTab',
|
||||
components: {
|
||||
CameraAndPipelineSelect,
|
||||
cvImage,
|
||||
InputTab,
|
||||
ThresholdTab,
|
||||
ContoursTab,
|
||||
OutputTab,
|
||||
pnpTab,
|
||||
},
|
||||
methods: {
|
||||
onImageClick(event) {
|
||||
if (this.selectedTab === 1) {
|
||||
this.$refs.component.onClick(event);
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTab: 0,
|
||||
snackbar: false,
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBinaryNumber: {
|
||||
get() {
|
||||
return this.$store.getters.pipeline.isBinary ? 1 : 0
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('isBinary', !!value);
|
||||
}
|
||||
},
|
||||
selectedComponent: {
|
||||
get() {
|
||||
return (this.$store.getters.currentPipelineIndex + 1) === 0 ? "InputTab" : ["InputTab", "ThresholdTab", "ContoursTab", "OutputTab", "pnpTab"][this.selectedTab];
|
||||
}
|
||||
},
|
||||
fps: {
|
||||
get() {
|
||||
return this.$store.state.point.fps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.colsClass {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.videoClass {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -32,9 +32,9 @@
|
||||
|
||||
<script>
|
||||
import Papa from 'papaparse';
|
||||
import miniMap from '../../components/3D/MiniMap';
|
||||
import CVswitch from '../../components/cv-switch';
|
||||
import CVslider from '../../components/cv-slider'
|
||||
import miniMap from '../../components/pipeline/3D/MiniMap';
|
||||
import CVswitch from '../../components/common/cv-switch';
|
||||
import CVslider from '../../components/common/cv-slider'
|
||||
import FRCtargetsConfig from '../../assets/FRCtargets'
|
||||
|
||||
export default {
|
||||
@@ -58,10 +58,6 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
},
|
||||
readFile(event) {
|
||||
let file = event.target.files[0];
|
||||
Papa.parse(file, {
|
||||
@@ -17,9 +17,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVrangeSlider from '../../components/cv-range-slider'
|
||||
import CVselect from '../../components/cv-select'
|
||||
import CVslider from '../../components/cv-slider'
|
||||
import CVrangeSlider from '../../components/common/cv-range-slider'
|
||||
import CVselect from '../../components/common/cv-select'
|
||||
import CVslider from '../../components/common/cv-slider'
|
||||
|
||||
export default {
|
||||
name: 'Contours',
|
||||
@@ -30,10 +30,6 @@
|
||||
CVslider
|
||||
},
|
||||
methods: {
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -14,8 +14,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVslider from '../../components/cv-slider'
|
||||
import CVselect from '../../components/cv-select'
|
||||
import CVslider from '../../components/common/cv-slider'
|
||||
import CVselect from '../../components/common/cv-select'
|
||||
|
||||
export default {
|
||||
name: 'Input',
|
||||
@@ -25,10 +25,6 @@
|
||||
CVselect,
|
||||
},
|
||||
methods: {
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -21,10 +21,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVselect from '../../components/cv-select'
|
||||
import CVswitch from '../../components/cv-switch'
|
||||
import DualCalibration from "../../components/OutputTab/DualCalibration";
|
||||
import SingleCalibration from "../../components/OutputTab/SingleCalibration";
|
||||
import CVselect from '../../components/common/cv-select'
|
||||
import CVswitch from '../../components/common/cv-switch'
|
||||
import DualCalibration from "../../components/pipeline/OutputTab/DualCalibration";
|
||||
import SingleCalibration from "../../components/pipeline/OutputTab/SingleCalibration";
|
||||
|
||||
|
||||
export default {
|
||||
@@ -38,10 +38,6 @@
|
||||
|
||||
},
|
||||
methods: {
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
},
|
||||
doUpdate() {
|
||||
this.$emit('update')
|
||||
},
|
||||
@@ -24,8 +24,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVrangeSlider from '../../components/cv-range-slider'
|
||||
import CVswitch from '../../components/cv-switch'
|
||||
import CVrangeSlider from '../../components/common/cv-range-slider'
|
||||
import CVswitch from '../../components/common/cv-switch'
|
||||
|
||||
export default {
|
||||
name: 'Threshold',
|
||||
@@ -96,11 +96,7 @@
|
||||
this.currentFunction = this.colorPicker.shrink;
|
||||
break;
|
||||
}
|
||||
},
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.value[val]);
|
||||
this.$emit('update')
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
const self = this;
|
||||
@@ -13,7 +13,7 @@
|
||||
</v-col>
|
||||
<v-col class="colsClass" v-show="selectedTab === 1">
|
||||
<div class="videoClass">
|
||||
<img :src="streamAddress" alt="Camera Stream">
|
||||
<cvImage :address="$store.getters.streamAddress" :scale="75"/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -23,11 +23,13 @@
|
||||
<script>
|
||||
import General from './SettingsViewes/General'
|
||||
import Cameras from './SettingsViewes/Cameras'
|
||||
import cvImage from '../components/common/cv-image'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'SettingsTab',
|
||||
components: {
|
||||
cvImage,
|
||||
General,
|
||||
Cameras,
|
||||
},
|
||||
@@ -43,11 +45,6 @@
|
||||
return this.tabList[this.selectedTab];
|
||||
}
|
||||
},
|
||||
streamAddress: {
|
||||
get: function () {
|
||||
return "http://" + location.hostname + ":" + this.$store.state.port + "/stream.mjpg";
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -60,7 +57,6 @@
|
||||
.videoClass img {
|
||||
padding-top: 10px;
|
||||
height: auto !important;
|
||||
width: 75%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<CVselect name="Camera" :list="cameraList" v-model="currentCameraIndex"
|
||||
<CVselect name="Camera" :list="$store.getters.cameraList" v-model="currentCameraIndex"
|
||||
@input="handleInput('currentCamera',currentCameraIndex)"/>
|
||||
<CVnumberinput name="Diagonal FOV" v-model="cameraSettings.fov"/>
|
||||
<br>
|
||||
@@ -50,14 +50,16 @@
|
||||
</v-row>
|
||||
<div v-if="isCalibrating">
|
||||
<v-checkbox v-model="isAdvanced" label="Advanced Menu" dark/>
|
||||
<div v-if="isAdvanced">
|
||||
<CVslider name="Exposure" v-model="pipeline.exposure" :min="0" :max="100"
|
||||
@input="handleData('exposure')"/>
|
||||
<CVslider name="Brightness" v-model="pipeline.brightness" :min="0" :max="100"
|
||||
@input="handleData('brightness')"/>
|
||||
<CVslider name="Gain" v-if="pipeline.gain !== -1" v-model="pipeline.gain" :min="0" :max="100"
|
||||
@input="handleData('gain')"/>
|
||||
<CVselect name="FPS" v-model="pipeline.videoModeIndex" :list="stringFpsList" @input="changeFps"/>
|
||||
<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>
|
||||
@@ -68,9 +70,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVselect from '../../components/cv-select'
|
||||
import CVnumberinput from '../../components/cv-number-input'
|
||||
import CVslider from '../../components/cv-slider'
|
||||
import CVselect from '../../components/common/cv-select'
|
||||
import CVnumberinput from '../../components/common/cv-number-input'
|
||||
import CVslider from '../../components/common/cv-slider'
|
||||
|
||||
export default {
|
||||
name: 'CameraSettings',
|
||||
@@ -103,16 +105,13 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleData(val) {
|
||||
this.handleInput(val, this.pipeline[val]);
|
||||
},
|
||||
downloadBoard() {
|
||||
this.axios.get("http://" + this.$address + require('../../assets/chessboard.png'), {responseType: 'blob'}).then((response) => {
|
||||
require('downloadjs')(response.data, "Calibration Board", "image/png")
|
||||
})
|
||||
},
|
||||
changeFps() {
|
||||
this.handleInput('videoModeIndex', this.filteredFpsList[this.pipeline['videoModeIndex']]['actualIndex']);
|
||||
this.handleInput('videoModeIndex', this.filteredFpsList[this.$store.getters.pipeline['videoModeIndex']]['actualIndex']);
|
||||
},
|
||||
sendCameraSettings() {
|
||||
const self = this;
|
||||
@@ -211,14 +210,6 @@
|
||||
this.$store.commit('currentCameraIndex', value);
|
||||
}
|
||||
},
|
||||
cameraList: {
|
||||
get() {
|
||||
return this.$store.state.cameraList;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('cameraList', value);
|
||||
}
|
||||
},
|
||||
filteredResolutionList: {
|
||||
get() {
|
||||
let tmp_list = [];
|
||||
@@ -268,16 +259,11 @@
|
||||
},
|
||||
cameraSettings: {
|
||||
get() {
|
||||
return this.$store.state.cameraSettings;
|
||||
return this.$store.getters.cameraSettings;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('cameraSettings', value);
|
||||
}
|
||||
},
|
||||
pipeline: {
|
||||
get() {
|
||||
return this.$store.state.pipeline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVnumberinput from '../../components/cv-number-input'
|
||||
import CVradio from '../../components/cv-radio'
|
||||
import CVinput from '../../components/cv-input'
|
||||
import CVnumberinput from '../../components/common/cv-number-input'
|
||||
import CVradio from '../../components/common/cv-radio'
|
||||
import CVinput from '../../components/common/cv-input'
|
||||
|
||||
export default {
|
||||
name: 'General',
|
||||
|
||||
Reference in New Issue
Block a user