2020-05-25 22:46:44 +03:00
< template >
2020-06-26 04:39:14 -07:00
< div >
2020-07-13 19:34:31 -07:00
< v-container
class = "pa-3"
fluid
>
< v-row
no - gutters
align = "center"
justify = "center"
2020-06-26 04:39:14 -07:00
>
2020-07-13 19:34:31 -07:00
< v-col
cols = "12"
2020-07-31 13:50:50 -07:00
: class = "['pb-3 ', 'pr-lg-3']"
lg = "8"
2020-07-13 19:34:31 -07:00
align - self = "stretch"
2020-06-26 04:39:14 -07:00
>
2020-07-13 19:34:31 -07:00
< v-card
color = "primary"
height = "100%"
2020-07-31 13:50:50 -07:00
style = "display: flex; flex-direction: column"
2020-06-26 04:39:14 -07:00
dark
>
2020-07-13 19:34:31 -07:00
< v-card-title
class = "pb-0 mb-0 pl-4 pt-1"
2020-07-31 13:50:50 -07:00
style = "height: 15%; min-height: 50px;"
2020-07-13 19:34:31 -07:00
>
2020-07-31 13:50:50 -07:00
< div >
Cameras < span
class = "pl-2 caption grey--text text--lighten-2"
style = "line-height: 220%; display: inline-block; vertical-align: bottom;"
> { { parseFloat ( fps ) . toFixed ( 2 ) } } FPS < / span >
< / div >
< v-switch
v - model = "driverMode"
label = "Driver Mode"
style = "margin-left: auto;"
color = "accent"
/ >
2020-07-13 19:34:31 -07:00
< / v-card-title >
< v-row
align = "center"
>
< v-col
v - for = "idx in (selectedOutputs instanceof Array ? selectedOutputs : [selectedOutputs])"
: key = "idx"
cols = "12"
: md = "selectedOutputs.length === 1 ? 12 : Math.floor(12 / selectedOutputs.length)"
2020-07-31 13:50:50 -07:00
class = "pb-0 pt-0"
style = "height: 100%;"
2020-06-26 04:39:14 -07:00
>
2020-07-13 19:34:31 -07:00
< div style = "position: relative; width: 100%; height: 100%;" >
< cvImage
2020-07-31 13:50:50 -07:00
: id = "idx === 0 ? 'normal-stream' : ''"
2020-07-13 19:34:31 -07:00
: address = "$store.getters.streamAddress[idx]"
2020-07-31 13:50:50 -07:00
: disconnected = "!$store.state.backendConnected"
2020-07-13 19:34:31 -07:00
scale = "100"
2020-07-31 13:50:50 -07:00
: max - height = "$store.getters.isDriverMode ? '40vh' : '300px'"
: max - height - md = "$store.getters.isDriverMode ? '50vh' : '320px'"
: max - height - xl = "$store.getters.isDriverMode ? '60vh' : '450px'"
2020-07-13 19:34:31 -07:00
: alt = "'Stream' + idx"
2020-07-31 13:50:50 -07:00
: color - picking = "$store.state.colorPicking && idx == 0"
2020-07-13 19:34:31 -07:00
@ click = "onImageClick"
/ >
2020-07-31 13:50:50 -07:00
<!-- < span class = "fps-indicator" > { { parseFloat ( fps ) . toFixed ( 2 ) } } < / span > -- >
2020-07-13 19:34:31 -07:00
< / div >
< / v-col >
2020-06-26 04:39:14 -07:00
< / v-row >
2020-07-13 19:34:31 -07:00
< / v-card >
< / v-col >
< v-col
cols = "12"
class = "pb-3"
2020-07-31 13:50:50 -07:00
lg = "4"
2020-07-13 19:34:31 -07:00
align - self = "stretch"
>
< v-card
color = "primary"
>
< camera-and-pipeline-select / >
< / v-card >
< v-card
2020-07-31 13:50:50 -07:00
: disabled = "$store.getters.isDriverMode || $store.state.colorPicking"
2020-07-13 19:34:31 -07:00
class = "mt-3"
color = "primary"
>
< v-row
align = "center"
class = "pl-3 pr-3"
>
< v-col lg = "12" >
< p style = "color: white;" >
Processing mode :
< / p >
< v-btn-toggle
v - model = "processingMode"
mandatory
dark
class = "fill"
>
2020-07-31 13:50:50 -07:00
< v-btn
color = "secondary"
>
2020-07-13 19:34:31 -07:00
< v-icon > mdi - crop - square < / v-icon >
< span > 2 D < / span >
< / v-btn >
2020-07-31 13:50:50 -07:00
< v-btn
color = "secondary"
>
< v-icon > mdi - cube - outline < / v-icon >
< span > 3 D < / span >
< / v-btn >
2020-07-13 19:34:31 -07:00
< / v-btn-toggle >
< / v-col >
< v-col lg = "12" >
< p style = "color: white;" >
Stream display :
< / p >
< v-btn-toggle
v - model = "selectedOutputs"
: multiple = "$vuetify.breakpoint.mdAndUp"
mandatory
dark
class = "fill"
>
< v-btn
color = "secondary"
class = "fill"
>
< v-icon > mdi - palette < / v-icon >
< span > Normal < / span >
< / v-btn >
< v-btn
color = "secondary"
class = "fill"
>
< v-icon > mdi - compare < / v-icon >
< span > Threshold < / span >
< / v-btn >
< / v-btn-toggle >
< / v-col >
< / v-row >
< / v-card >
< / v-col >
< / v-row >
< v-row no -gutters >
< v-col
v - for = "(tabs, idx) in tabGroups"
: key = "idx"
: cols = "Math.floor(12 / tabGroups.length)"
: class = "idx != tabGroups.length - 1 ? 'pr-3' : ''"
align - self = "stretch"
>
< v-card
color = "primary"
height = "100%"
class = "pr-4 pl-4"
>
< v-tabs
v - if = "!$store.getters.isDriverMode"
v - model = "selectedTabs[idx]"
grow
background - color = "primary"
dark
height = "48"
slider - color = "accent"
>
< v-tab
2020-07-31 13:50:50 -07:00
v - for = "(tab, i) in tabs.filter(it => it.name !== '3D' || $store.getters.currentPipelineSettings.is3D)"
2020-07-13 19:34:31 -07:00
: key = "i"
>
{ { tab . name } }
< / v-tab >
< / v-tabs >
< div class = "pl-4 pr-4 pt-2" >
< keep-alive >
< component
: is = "(tabs[selectedTabs[idx]] || tabs[0]).component"
2020-07-31 13:50:50 -07:00
: ref = "(tabs[selectedTabs[idx]] || tabs[0]).name"
2020-07-13 19:34:31 -07:00
v - model = "$store.getters.pipeline"
@ update = "$emit('save')"
/ >
< / keep-alive >
< / div >
< / v-card >
< / v-col >
< / v-row >
< / v-container >
2020-06-26 04:39:14 -07:00
<!-- 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 >
2020-05-25 22:46:44 +03:00
< / template >
< script >
import CameraAndPipelineSelect from "../components/pipeline/CameraAndPipelineSelect" ;
2020-07-13 19:34:31 -07:00
import cvImage from '../components/common/cv-image' ;
import InputTab from './PipelineViews/InputTab' ;
import ThresholdTab from './PipelineViews/ThresholdTab' ;
import ContoursTab from './PipelineViews/ContoursTab' ;
import OutputTab from './PipelineViews/OutputTab' ;
import TargetsTab from "./PipelineViews/TargetsTab" ;
import PnPTab from './PipelineViews/PnPTab' ;
2020-05-25 22:46:44 +03:00
export default {
name : 'CameraTab' ,
components : {
CameraAndPipelineSelect ,
cvImage ,
InputTab ,
ThresholdTab ,
ContoursTab ,
OutputTab ,
2020-07-13 19:34:31 -07:00
TargetsTab ,
PnPTab ,
2020-05-25 22:46:44 +03:00
} ,
data ( ) {
return {
2020-07-31 13:50:50 -07:00
selectedTabsData : [ 0 , 0 , 0 , 0 ] ,
2020-05-25 22:46:44 +03:00
snackbar : false ,
2020-07-31 13:50:50 -07:00
counterData : 0 ,
2020-05-25 22:46:44 +03:00
}
} ,
computed : {
2020-07-31 13:50:50 -07:00
selectedTabs : {
get ( ) {
return this . $store . getters . isDriverMode ? [ 0 ] : this . selectedTabsData ;
} ,
set ( value ) {
this . selectedTabsData = value ;
}
} ,
2020-07-13 19:34:31 -07:00
tabGroups : {
2020-05-25 22:46:44 +03:00
get ( ) {
2020-07-13 19:34:31 -07:00
let tabs = {
input : {
name : "Input" ,
component : "InputTab" ,
} ,
threshold : {
name : "Threshold" ,
component : "ThresholdTab" ,
} ,
contours : {
name : "Contours" ,
component : "ContoursTab" ,
} ,
output : {
name : "Output" ,
component : "OutputTab" ,
} ,
targets : {
name : "Target Info" ,
component : "TargetsTab" ,
} ,
pnp : {
name : "3D" ,
component : "PnPTab" ,
}
} ;
// 2D array of tab names and component names; each sub-array is a separate tab group
let ret = [ ] ;
2020-07-31 13:50:50 -07:00
if ( this . $vuetify . breakpoint . smAndDown || this . $store . getters . isDriverMode || ( this . $vuetify . breakpoint . mdAndDown && ! this . $store . state . compactMode ) ) {
2020-07-13 19:34:31 -07:00
// One big tab group with all the tabs
ret [ 0 ] = Object . values ( tabs ) ;
2020-07-31 13:50:50 -07:00
} else if ( this . $vuetify . breakpoint . mdAndDown || ! this . $store . state . compactMode ) {
2020-07-13 19:34:31 -07:00
// Two tab groups, one with "input, threshold, contours, output" and the other with "target info, 3D"
ret [ 0 ] = [ tabs . input , tabs . threshold , tabs . contours , tabs . output ] ;
ret [ 1 ] = [ tabs . targets , tabs . pnp ] ;
} else if ( this . $vuetify . breakpoint . lgAndDown ) {
// Three tab groups, one with "input", one with "threshold, contours, output", and the other with "target info, 3D"
ret [ 0 ] = [ tabs . input ] ;
ret [ 1 ] = [ tabs . threshold , tabs . contours , tabs . output ] ;
ret [ 2 ] = [ tabs . targets , tabs . pnp ] ;
} else if ( this . $vuetify . breakpoint . xl ) {
// Three tab groups, one with "input", one with "threshold, contours", and the other with "output, target info, 3D"
ret [ 0 ] = [ tabs . input ] ;
ret [ 1 ] = [ tabs . threshold ] ;
ret [ 2 ] = [ tabs . contours , tabs . output ]
ret [ 3 ] = [ tabs . targets , tabs . pnp ] ;
}
return ret ;
}
} ,
processingMode : {
get ( ) {
2020-07-31 13:50:50 -07:00
return this . $store . getters . currentPipelineSettings . is3D ? 1 : 0 ;
2020-05-25 22:46:44 +03:00
} ,
set ( value ) {
2020-07-31 13:50:50 -07:00
this . $store . getters . currentPipelineSettings . is3D = value === 1 ;
this . handlePipelineUpdate ( "is3D" , value === 1 ) ;
}
} ,
driverMode : {
get ( ) {
return this . $store . getters . isDriverMode ;
} ,
set ( value ) {
this . $store . getters . currentCameraSettings . currentPipelineIndex = value ? - 1 : 0 ;
this . handleInputWithIndex ( 'currentPipeline' , value ? - 1 : 0 ) ;
2020-05-25 22:46:44 +03:00
}
} ,
2020-07-13 19:34:31 -07:00
selectedOutputs : {
// All this logic exists to deal with the reality that the output select buttons sometimes need an array and sometimes need a number (depending on whether or not they're exclusive)
2020-05-25 22:46:44 +03:00
get ( ) {
2020-07-13 19:34:31 -07:00
// We switch the selector to single-select only on sm-and-down size devices, so we have to return a Number instead of an Array in that state
2020-07-16 16:37:29 -07:00
let ret ;
2020-07-31 13:50:50 -07:00
if ( this . $store . state . colorPicking ) {
ret = [ 0 ] ; // We want the input stream only while color picking
} else if ( ! this . $store . getters . isDriverMode ) {
2020-07-13 19:34:31 -07:00
ret = this . $store . state . selectedOutputs || [ 0 ] ;
2020-07-16 16:37:29 -07:00
} else {
ret = [ 1 ] ; // We want the output stream in driver mode
2020-07-13 19:34:31 -07:00
}
if ( this . $vuetify . breakpoint . mdAndUp ) {
return ret ;
} else {
return ret [ 0 ] || 0 ;
}
} ,
set ( value ) {
let valToCommit = [ 0 ] ;
if ( value instanceof Array ) {
// Value is already an array, we don't need to do anything
value . sort ( ) ; // Sort for visual consistency
valToCommit = value ;
} else if ( value ) {
// Value is assumed to be a number, so we wrap it into an array
valToCommit = [ value ] ;
}
this . $store . commit ( "selectedOutputs" , valToCommit ) ;
// TODO: Currently the backend just sends both streams regardless of the selected outputs value, so we don't need to send anything
// this.handlePipelineUpdate('selectedOutputs', valToCommit);
2020-05-25 22:46:44 +03:00
}
} ,
fps : {
get ( ) {
Bootup sprint (#18)
* Did some stuff
* Fix gradle, start implementing mjpeg frame consumer
* Did some stuff
* bade changes
* rename camera config to USBCameraConfiguration, add name
* unrename cameraconfiguration
* Add pub/sub framework
* Add setResolution to mjpeg frame consumer
* add NTDataConsumer
* Add some totally broken hsv hacks
* Start refactoring UI data
* Update index.js
* Commit and push, he says
* Fix up some errors
* Fix input tab
* Fix fps
* Update index.js
* Add pipeline field setting, update PipelineManager, fix nullpointers and USBCameraSettables
* Change v-model to point to data()
* update hsv to use mutations
* Work on saving, fix hsv
* Rename shouldErode/shouldDilate to erode and dilate
* Hook all the tabs up to the Store
* Change handleData to handlePipelineData
* camera quirk redo, add ICCSub to SocketHandler
* Fix some property names
* Fixed tons of naming in UI, fix backend for multi-val PSCs, fix PSC enums
* change pipeline type to an int in store
* Fix mutation naming
* Attempt threshold fix
* Update SocketHandler.java
* Add truthy data sending
* Start adding logging support
* [UI] Add delay to slider input boxes (#1)
* [UI] [Backend] potentially fix camera settings, various logging tweaks
* Don't release raw input mat
* add setVideoModeIndex to vision settables
* Implement pipeline index in socket handler, add framework for renaming/changing pipes
* (ish) get pipeline change working
* Create index.html
* Cleanups, fix pipeline index bug, fix stream res for MJPG, add dashboard stream (unused)
* Refactor UI to use mutatePipeline, send pipeline results
* Update NetworkConfig.java
* Change double to number
* Run spotless
* Fix reversal of large/small comparators
* Fix left/right
* Fix pitch/yaw calculation bug, fix area bug
* Use Vue.set instead of assignment
This fixes {{ }}
* Update App.vue
* run spotless
* Actually add pipelines and reassign indecies
* Delete old pipeline configs
Fixes duplication on renaming pipeline
* Start working on deleting pipes
* Fix camera nickname change
* run spotless
* Fix some test stuff
* Update VisionModuleManagerTest.java
* vision source manager test is still broken
* Fix VisionSourceManager test
* Apply spotless 2 electric boogaloo
Co-authored-by: Banks Troutman <btrout.dhrs@gmail.com>
Co-authored-by: Declan Freeman-Gleason <declanfreemangleason@gmail.com>
Co-authored-by: Aaryan Agrawal <54345060+13Ducks@users.noreply.github.com>
2020-07-07 01:01:58 -07:00
return this . $store . getters . currentCameraFPS ;
2020-05-25 22:46:44 +03:00
}
2020-07-12 10:37:04 -07:00
} ,
latency : {
get ( ) {
return this . $store . getters . currentPipelineResults . latency ;
}
2020-05-25 22:46:44 +03:00
}
2020-06-26 04:39:14 -07:00
} ,
methods : {
onImageClick ( event ) {
2020-07-31 13:50:50 -07:00
// Only run on the input stream
if ( event . target . alt !== "Stream0" ) return ;
// Get a reference to the threshold tab (if it is shown) and call its "onClick" method
let ref = this . $refs [ "Threshold" ] ;
if ( ref && ref [ 0 ] )
ref [ 0 ] . onClick ( event )
2020-06-26 04:39:14 -07:00
} ,
2020-05-25 22:46:44 +03:00
}
}
< / script >
< style scoped >
2020-07-13 19:34:31 -07:00
. v - btn - toggle . fill {
width : 100 % ;
height : 100 % ;
}
. v - btn - toggle . fill > . v - btn {
width : 50 % ;
height : 100 % ;
}
2020-07-31 13:50:50 -07:00
. fps - indicator {
position : absolute ;
top : 2 % ;
left : 2 % ;
font - size : 1.75 rem ;
text - shadow : 1 px 1 px 5 px rgba ( 1 , 1 , 1 , 0.65 ) ;
2020-05-25 22:46:44 +03:00
}
th {
width : 80 px ;
text - align : center ;
}
< / style >