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-08-14 12:39:21 -07:00
Cameras
2020-12-08 02:34:21 -05:00
< v-chip
: class = "fpsTooLow ? 'ml-2 mt-1' : 'mt-2'"
x - small
label
: color = "fpsTooLow ? 'red' : 'transparent'"
: text - color = "fpsTooLow ? 'white' : 'grey'"
>
< span class = "pr-1" > { { Math . round ( $store . state . pipelineResults . fps ) } } & nbsp ; FPS & ndash ; < / span >
2021-01-17 14:57:21 -08:00
< span v-if = "!fpsTooLow" > {{ Math.min ( Math.round ( $ store.state.pipelineResults.latency ) , 100 ) }} ms latency < / span >
2020-12-08 02:34:21 -05:00
< span v-else-if = "!$store.getters.currentPipelineSettings.inputShouldShow" > HSV thresholds are too broad ; narrow them for better performance < / span >
< span v-else > stop viewing the color stream for better performance < / span >
< / v-chip >
2021-11-25 15:43:29 -05:00
< v-chip small label color = "red" text -color = " white " v-if = "!$store.state.ntConnectionInfo.connected || $store.state.settings.networkSettings.runNTServer" >
< span >
{ { $store . state . settings . networkSettings . runNTServer ?
"NetworkTables Server Enabled! Photonlib may not work" :
"NetworkTables not connected!" } }
< / span >
< / v-chip >
2020-07-31 13:50:50 -07:00
< 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%;" >
2020-10-16 16:48:24 -07:00
< cv-image
2020-07-31 13:50:50 -07:00
: id = "idx === 0 ? 'normal-stream' : ''"
2020-10-16 16:48:24 -07:00
ref = "streams"
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-12-08 02:34:21 -05:00
: color - picking = "$store.state.colorPicking && idx === 0"
2020-07-13 19:34:31 -07:00
@ click = "onImageClick"
/ >
< / 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"
2021-11-25 15:43:29 -05:00
class = "mt-3"
2020-07-13 19:34:31 -07:00
>
2020-10-16 16:48:24 -07:00
<!-- < v-btn @click ="onCamNameChange" > - - >
<!-- Reload -- >
<!-- < / v-btn > -- >
< camera-and-pipeline-select @ camera -name -changed = " reloadStreams " / >
2020-07-13 19:34:31 -07:00
< / v-card >
< v-card
2020-07-31 13:50:50 -07:00
: disabled = "$store.getters.isDriverMode || $store.state.colorPicking"
2021-11-25 15:43:29 -05:00
class = "mt-6 mb-3"
2020-07-13 19:34:31 -07:00
color = "primary"
>
< v-row
align = "center"
class = "pl-3 pr-3"
>
2020-09-04 18:18:44 -07:00
<!-- -- >
2020-07-13 19:34:31 -07:00
< 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"
2020-09-04 18:18:44 -07:00
@ click = "on3DClick"
2020-07-31 13:50:50 -07:00
>
< 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)"
2020-12-08 02:34:21 -05:00
: class = "idx !== tabGroups.length - 1 ? 'pr-3' : ''"
2020-07-13 19:34:31 -07:00
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-08-14 12:39:21 -07:00
v - for = "(tab, i) in tabs.filter(it => it.name !== '3D' || $store.getters.currentPipelineSettings.solvePNPEnabled)"
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-09-04 18:18:44 -07:00
<!-- snack bar and modal -- >
2020-06-26 04:39:14 -07:00
< 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 >
2020-09-04 18:18:44 -07:00
< v-dialog
v - model = "dialog"
width = "500"
>
< v-card
color = "primary"
dark
>
< v-card-title >
Current resolution not calibrated
< / v-card-title >
< v-card-text >
2020-12-08 02:34:21 -05:00
Because the current resolution { { this . $store . getters . currentVideoFormat . width } } x { { this . $store . getters . currentVideoFormat . height } } is not yet calibrated , 3 D mode cannot be enabled . Please
2020-09-04 18:18:44 -07:00
< a
href = "/#/cameras"
class = "white--text"
@ click = "$emit('switch-to-cameras')"
> visit the Cameras tab < / a > to calibrate this resolution . For now , SolvePNP will do nothing .
< / v-card-text >
< v-divider / >
< v-card-actions >
< v-spacer / >
< v-btn
color = "white"
text
@ click = "closeUncalibratedDialog"
>
OK
< / v-btn >
< / v-card-actions >
< / v-card >
< / v-dialog >
2020-06-26 04:39:14 -07:00
< / div >
2020-05-25 22:46:44 +03:00
< / template >
< script >
2020-10-16 16:48:24 -07:00
import CameraAndPipelineSelect from "../components/pipeline/CameraAndPipelineSelect" ;
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
2020-10-16 16:48:24 -07:00
export default {
2020-12-08 02:34:21 -05:00
name : 'Pipeline' ,
2020-10-16 16:48:24 -07:00
components : {
CameraAndPipelineSelect ,
cvImage ,
InputTab ,
ThresholdTab ,
ContoursTab ,
OutputTab ,
TargetsTab ,
PnPTab ,
} ,
data ( ) {
return {
selectedTabsData : [ 0 , 0 , 0 , 0 ] ,
snackbar : false ,
counterData : 0 ,
dialog : false ,
processingModeOverride : false
}
} ,
computed : {
selectedTabs : {
get ( ) {
return this . $store . getters . isDriverMode ? [ 0 ] : this . selectedTabsData ;
} ,
set ( value ) {
this . selectedTabsData = value ;
2020-05-25 22:46:44 +03:00
}
} ,
2020-10-16 16:48:24 -07:00
tabGroups : {
get ( ) {
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" ,
2020-07-13 19:34:31 -07:00
}
2020-10-16 16:48:24 -07:00
} ;
2020-07-13 19:34:31 -07:00
2020-10-16 16:48:24 -07:00
// 2D array of tab names and component names; each sub-array is a separate tab group
let ret = [ ] ;
if ( this . $vuetify . breakpoint . smAndDown || this . $store . getters . isDriverMode || ( this . $vuetify . breakpoint . mdAndDown && ! this . $store . state . compactMode ) ) {
// One big tab group with all the tabs
ret [ 0 ] = Object . values ( tabs ) ;
} else if ( this . $vuetify . breakpoint . mdAndDown || ! this . $store . state . compactMode ) {
// 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 ] ;
2020-12-08 02:34:21 -05:00
ret [ 2 ] = [ tabs . contours , tabs . output ] ;
2020-10-16 16:48:24 -07:00
ret [ 3 ] = [ tabs . targets , tabs . pnp ] ;
2020-07-13 19:34:31 -07:00
}
2020-10-16 16:48:24 -07:00
return ret ;
}
} ,
processingMode : {
get ( ) {
return ( this . $store . getters . currentPipelineSettings . solvePNPEnabled || this . processingModeOverride ) ? 1 : 0 ;
2020-07-13 19:34:31 -07:00
} ,
2020-10-16 16:48:24 -07:00
set ( value ) {
if ( this . $store . getters . isCalibrated ) {
this . $store . getters . currentPipelineSettings . solvePNPEnabled = value === 1 ;
this . handlePipelineUpdate ( "solvePNPEnabled" , value === 1 ) ;
2020-07-31 13:50:50 -07:00
}
2020-10-16 16:48:24 -07:00
}
} ,
driverMode : {
get ( ) {
return this . $store . getters . isDriverMode ;
2020-07-31 13:50:50 -07:00
} ,
2020-10-16 16:48:24 -07:00
set ( value ) {
this . $store . getters . currentCameraSettings . currentPipelineIndex = value ? - 1 : 0 ;
this . handleInputWithIndex ( 'currentPipeline' , value ? - 1 : 0 ) ;
}
} ,
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)
get ( ) {
// 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-12-08 02:34:21 -05:00
let ret = [ ] ;
2020-10-16 16:48:24 -07:00
if ( this . $store . state . colorPicking ) {
ret = [ 0 ] ; // We want the input stream only while color picking
2020-12-08 02:34:21 -05:00
} else if ( this . $store . getters . isDriverMode ) {
ret = [ 1 ] ; // We want only the output stream in driver mode
2020-10-16 16:48:24 -07:00
} else {
2020-12-08 02:34:21 -05:00
if ( this . $store . getters . currentPipelineSettings . inputShouldShow ) ret = ret . concat ( [ 0 ] ) ;
if ( this . $store . getters . currentPipelineSettings . outputShouldShow ) ret = ret . concat ( [ 1 ] ) ;
if ( ! ret . length ) ret = [ 0 ] ;
2020-05-25 22:46:44 +03:00
}
2020-07-13 19:34:31 -07:00
2020-10-16 16:48:24 -07:00
if ( this . $vuetify . breakpoint . mdAndUp ) {
return ret ;
} else {
return ret [ 0 ] || 0 ;
2020-05-25 22:46:44 +03:00
}
} ,
2020-10-16 16:48:24 -07:00
set ( value ) {
let valToCommit = [ 0 ] ;
if ( value instanceof Array ) {
2020-12-08 02:34:21 -05:00
// Value is already an array, we don't need to do anything
valToCommit = value ;
2020-10-16 16:48:24 -07:00
} else if ( value ) {
2020-12-08 02:34:21 -05:00
// Value is assumed to be a number, so we wrap it into an array
valToCommit = [ value ] ;
2020-07-12 10:37:04 -07:00
}
2020-12-08 02:34:21 -05:00
this . $store . commit ( "mutatePipeline" , { "inputShouldShow" : valToCommit . includes ( 0 ) } ) ;
this . $store . commit ( "mutatePipeline" , { "outputShouldShow" : valToCommit . includes ( 1 ) } ) ;
this . handlePipelineUpdate ( "inputShouldShow" , valToCommit . includes ( 0 ) ) ;
}
} ,
fpsTooLow : {
get ( ) {
// For now we only show the FPS is too low warning when GPU acceleration is enabled, because we don't really trust the presented video modes otherwise
2020-12-10 11:22:03 -05:00
return this . $store . state . pipelineResults . fps - this . $store . getters . currentVideoFormat . fps < - 5 && this . $store . state . pipelineResults . fps !== 0 && ! this . $store . getters . isDriverMode && this . $store . state . settings . general . gpuAcceleration ;
2020-05-25 22:46:44 +03:00
}
2020-06-26 04:39:14 -07:00
} ,
2020-10-16 16:48:24 -07:00
latency : {
get ( ) {
return this . $store . getters . currentPipelineResults . latency ;
}
2020-12-08 02:34:21 -05:00
} ,
isCalibrated : {
get ( ) {
const resolution = this . $store . getters . videoFormatList [ this . $store . getters . currentPipelineSettings . cameraVideoModeIndex ] ;
return this . $store . getters . currentCameraSettings . calibrations
. some ( e => e . width === resolution . width && e . height === resolution . height )
}
} ,
2021-11-25 15:43:29 -05:00
isRobotConnected : {
get ( ) {
// return this.$store.state.ntConnectionInfo.connected && this.$store.state.backendConnected;
return true ;
}
}
2020-10-16 16:48:24 -07:00
} ,
created ( ) {
this . $store . state . connectedCallbacks . push ( this . reloadStreams )
} ,
methods : {
reloadStreams ( ) {
// Reload the streams as we technically close and reopen them
this . $refs . streams . forEach ( it => it . reload ( ) )
} ,
onImageClick ( event ) {
// 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 )
} ,
on3DClick ( ) {
if ( ! this . $store . getters . isCalibrated ) {
this . dialog = true ;
this . processingModeOverride = true ;
2020-09-04 18:18:44 -07:00
}
2020-10-16 16:48:24 -07:00
} ,
closeUncalibratedDialog ( ) {
this . dialog = false ;
this . processingModeOverride = false ;
// this.$store.getters.currentPipelineSettings.solvePNPEnabled = false;
this . handlePipelineUpdate ( "solvePNPEnabled" , false ) ;
2020-05-25 22:46:44 +03:00
}
}
2020-10-16 16:48:24 -07:00
}
2020-05-25 22:46:44 +03:00
< / script >
< style scoped >
2020-10-16 16:48:24 -07:00
. v - btn - toggle . fill {
width : 100 % ;
height : 100 % ;
}
2020-07-13 19:34:31 -07:00
2020-10-16 16:48:24 -07:00
. v - btn - toggle . fill > . v - btn {
width : 50 % ;
height : 100 % ;
}
2020-07-13 19:34:31 -07:00
2020-10-16 16:48:24 -07:00
th {
width : 80 px ;
text - align : center ;
}
2021-11-21 17:22:56 -08:00
< / style >