Rename to PhotonVision

This commit is contained in:
Matt
2020-06-27 14:58:03 -07:00
parent b28d0e046e
commit bdbd6b9d18
394 changed files with 1656 additions and 979 deletions

View File

@@ -0,0 +1,208 @@
<template>
<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>
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,
},
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;
}
}
},
methods: {
onImageClick(event) {
if (this.selectedTab === 1) {
this.$refs.component.onClick(event);
}
},
}
}
</script>
<style scoped>
.colsClass {
padding: 0 !important;
}
.videoClass {
text-align: center;
}
th {
width: 80px;
text-align: center;
}
</style>

View File

@@ -0,0 +1,206 @@
<template>
<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>
import Papa from 'papaparse';
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 {
name: "SolvePNP",
components: {
CVswitch,
CVslider,
miniMap
},
props: ['value'],
data() {
return {
is3D: false,
selectedModel: null,
FRCtargets: null,
snackbar: {
color: "success",
text: ""
},
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];
Papa.parse(file, {
complete: this.onParse,
skipEmptyLines: true
});
},
onParse(result) {
if (result.data.length > 0) {
let data = [];
for (let item of result.data) {
let tmp = [];
tmp.push(Number(item[0]));
tmp.push(Number(item[1]));
if (isNaN(tmp[0]) || isNaN(tmp[1])) {
this.snackbar = {
color: "error",
text: "Error: cvs did parse correctly"
};
this.snack = true;
return;
}
data.push(tmp);
}
this.uploadModel(data);
} else {
this.snackbar = {
color: "error",
text: "Error: cvs did not contain any data"
};
this.snack = true;
}
},
uploadPremade() {
this.uploadModel(this.selectedModel);
},
uploadModel(model) {
this.axios.post("http://" + this.$address + "/api/vision/pnpModel", model).then(() => {
this.snackbar = {
color: "success",
text: "File uploaded successfully"
};
this.snack = true;
}).catch(() => {
this.snackbar = {
color: "error",
text: "An error occurred"
};
this.snack = true;
})
}
}
}
</script>
<style scoped>
.miniMapClass {
width: 50% !important;
height: 50% !important;
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<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>
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',
components: {
CVrangeSlider,
CVselect,
CVslider
},
props: ['value'],
data() {
return {}
},
computed: {
isDisabled() {
return this.value.targetGroup === 0;
}
},
methods: {},
}
</script>
<style lang="" scoped>
</style>

View File

@@ -0,0 +1,95 @@
<template>
<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>
import CVslider from '../../components/common/cv-slider'
import CVselect from '../../components/common/cv-select'
export default {
name: 'Input',
components: {
CVslider,
CVselect,
},
// eslint-disable-next-line vue/require-prop-types
props: ['value'],
data() {
return {}
},
computed: {
resolutionList: {
get() {
let tmp_list = [];
for (let i of this.$store.state.resolutionList) {
tmp_list.push(`${i['width']} X ${i['height']} at ${i['fps']} FPS, ${i['pixelFormat']}`)
}
return tmp_list;
}
},
streamResolutionList: {
get() {
let cam_res = this.$store.state.resolutionList[this.value.videoModeIndex];
let tmp_list = [];
tmp_list.push(`${Math.floor(cam_res['width'])} X ${Math.floor(cam_res['height'])}`);
for (let x = 2; x <= 6; x += 2) {
tmp_list.push(`${Math.floor(cam_res['width'] / x)} X ${Math.floor(cam_res['height'] / x)}`);
}
return tmp_list;
}
}
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,126 @@
<template>
<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>
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 {
name: 'Output',
components: {
CVselect,
CVswitch,
SingleCalibration,
DualCalibration,
},
props: ['value'],
data() {
return {
snackbar: false,
snackbarText: ""
}
},
computed: {
selectedComponent: {
get() {
switch (this.value.calibrationMode) {
case 0:
return "";
case 1:
return "SingleCalibration";
case 2:
return "DualCalibration"
}
return ""
}
},
rawPoint: {
get() {
return this.$store.state.point.rawPoint;
}
}
},
methods: {
doUpdate() {
this.$emit('update')
},
showSnackbar(message) {
this.snackbarText = message;
this.snackbar = true;
},
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,162 @@
<template>
<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>
import CVrangeSlider from '../../components/common/cv-range-slider'
import CVswitch from '../../components/common/cv-switch'
export default {
name: 'Threshold',
components: {
CVrangeSlider,
CVswitch
},
props: ['value'],
data() {
return {
currentFunction: undefined,
colorPicker: undefined,
currentBinaryState: 0
}
},
computed: {
pipeline: {
get() {
return this.$store.state.pipeline;
}
},
driverState: {
get() {
return this.$store.state.driverMode;
},
set(val) {
this.$store.commit("driverMode", val);
}
}
},
mounted: function () {
const self = this;
this.colorPicker = require('../../plugins/ColorPicker').default;
this.$nextTick(() => {
self.colorPicker.initColorPicker();
});
},
methods: {
onClick(event) {
if (this.currentFunction !== undefined) {
let hsvArray = this.colorPicker.colorPickerClick(event, this.currentFunction,
[[this.value.hue[0], this.value.saturation[0], this.value.value[0]], [this.value.hue[1], this.value.saturation[1], this.value.value[1]]]);
this.currentFunction = undefined;
this.value.hue = [hsvArray[0][0], hsvArray[1][0]];
this.value.saturation = [hsvArray[0][1], hsvArray[1][1]];
this.value.value = [hsvArray[0][2], hsvArray[1][2]];
this.value.isBinary = this.currentBinaryState;
let msg = this.$msgPack.encode({
'hue': this.value.hue,
'saturation': this.value.saturation,
'value': this.value.value,
'isBinary': this.value.isBinary
});
this.$socket.send(msg);
this.$emit('update');
}
},
setFunction(index) {
this.currentBinaryState = this.value.isBinary;
if (this.currentBinaryState === true) {
this.value.isBinary = false;
this.handleData('isBinary')
}
switch (index) {
case 0:
this.currentFunction = undefined;
break;
case 1:
this.currentFunction = this.colorPicker.eyeDrop;
break;
case 2:
this.currentFunction = this.colorPicker.expand;
break;
case 3:
this.currentFunction = this.colorPicker.shrink;
break;
}
}
}
}
</script>
<style lang="" scoped>
</style>

View File

@@ -0,0 +1,88 @@
<template>
<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>
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,
},
data() {
return {
selectedTab: 0,
tabList: [General, Cameras]
}
},
computed: {
selectedComponent: {
get() {
return this.tabList[this.selectedTab];
}
},
}
}
</script>
<style scoped>
.videoClass {
text-align: center;
}
.videoClass img {
padding-top: 10px;
height: auto !important;
vertical-align: middle;
}
.colsClass {
padding: 0 !important;
}
</style>

View File

@@ -0,0 +1,343 @@
<template>
<div>
<div>
<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>
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',
components: {
CVselect,
CVnumberinput,
CVslider
},
data() {
return {
isCalibrating: false,
resolutionIndex: undefined,
calibrationModeButton: {
text: "Start Calibration",
color: "green"
},
cancellationModeButton: {
text: "Cancel Calibration",
color: "red"
},
snackbar: {
color: "success",
text: ""
},
squareSize: 1.0,
snapshotAmount: 0,
hasEnough: false,
snack: false,
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) => {
require('downloadjs')(response.data, "Calibration Board", "image/png")
})
},
changeFps() {
this.handleInput('videoModeIndex', this.filteredFpsList[this.$store.getters.pipeline['videoModeIndex']]['actualIndex']);
},
sendCameraSettings() {
const self = this;
this.axios.post("http://" + this.$address + "/api/settings/camera", this.cameraSettings).then(
function (response) {
if (response.status === 200) {
self.$store.state.saveBar = true;
}
}
)
},
sendCalibrationMode() {
const self = this;
let data = {};
let connection_string = "/api/settings/";
if (self.isCalibrating === true) {
connection_string += "snapshot"
} else {
connection_string += "startCalibration";
data['resolution'] = this.filteredResolutionList[this.resolutionIndex].actualIndex;
data['squareSize'] = this.squareSize;
self.hasEnough = false;
}
this.axios.post("http://" + this.$address + connection_string, data).then(
function (response) {
if (response.status === 200) {
if (self.isCalibrating) {
self.snapshotAmount = response.data['snapshotCount'];
self.hasEnough = response.data['hasEnough'];
if (self.hasEnough === true) {
self.cancellationModeButton.text = "Finish Calibration";
self.cancellationModeButton.color = "green";
}
} else {
self.calibrationModeButton.text = "Take Snapshot";
self.isCalibrating = true;
}
}
}
);
},
sendCalibrationFinish() {
const self = this;
let connection_string = "/api/settings/endCalibration";
let data = {};
data['squareSize'] = this.squareSize;
self.axios.post("http://" + this.$address + connection_string, data).then((response) => {
if (response.status === 200) {
self.snackbar = {
color: "success",
text: "calibration successful. \n" +
"accuracy: " + response.data['accuracy'].toFixed(5)
};
self.snack = true;
}
self.isCalibrating = false;
self.hasEnough = false;
self.snapshotAmount = 0;
self.calibrationModeButton.text = "Start Calibration";
self.cancellationModeButton.text = "Cancel Calibration";
self.cancellationModeButton.color = "red";
}
).catch(() => {
self.snackbar = {
color: "error",
text: "calibration failed"
};
self.snack = true;
self.isCalibrating = false;
self.hasEnough = false;
self.snapshotAmount = 0;
self.calibrationModeButton.text = "Start Calibration";
self.cancellationModeButton.text = "Cancel Calibration";
self.cancellationModeButton.color = "red";
});
}
}
}
</script>
<style lang="" scoped>
</style>

View File

@@ -0,0 +1,188 @@
<template>
<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>
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',
components: {
CVnumberinput,
CVradio,
CVinput
},
data() {
return {
file: undefined,
snackbar: {
color: "success",
text: ""
},
snack: false,
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;
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings).then(
function (response) {
if (response.status === 200) {
self.snackbar = {
color: "success",
text: "Save successful, Please restart for changes to take action"
};
self.snack = true;
}
},
function (error) {
self.snackbar = {
color: "error",
text: error.response.data
};
self.snack = true;
}
)
},
installOrUpdate() {
let formData = new FormData();
formData.append('file', this.file);
if (this.file !== undefined) {
this.isLoading = true;
}
this.axios.post("http://" + this.$address + "/api/install", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(() => {
this.snackbar = {
color: "success",
text: "Installation successful"
};
this.isLoading = false;
this.snack = true;
}).catch(error => {
this.snackbar = {
color: "error",
text: error.response.data
};
this.isLoading = false;
this.snack = true;
})
}
}
}
</script>
<style lang="" scoped>
</style>