2023-08-21 01:51:35 -04:00
< script setup lang = "ts" >
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore" ;
2024-01-06 09:43:29 -05:00
import { computed , ref , watchEffect } from "vue" ;
2023-10-17 16:32:59 -04:00
import PvInput from "@/components/common/pv-input.vue" ;
import PvRadio from "@/components/common/pv-radio.vue" ;
import PvSwitch from "@/components/common/pv-switch.vue" ;
import PvSelect from "@/components/common/pv-select.vue" ;
2024-01-20 18:46:47 -06:00
import { type ConfigurableNetworkSettings , NetworkConnectionType } from "@/types/SettingTypes" ;
2023-08-21 01:51:35 -04:00
import { useStateStore } from "@/stores/StateStore" ;
2023-12-31 00:14:21 -05:00
// Copy object to remove reference to store
2024-01-20 18:46:47 -06:00
const tempSettingsStruct = ref < ConfigurableNetworkSettings > ( Object . assign ( { } , useSettingsStore ( ) . network ) ) ;
2024-01-06 09:43:29 -05:00
const resetTempSettingsStruct = ( ) => {
tempSettingsStruct . value = Object . assign ( { } , useSettingsStore ( ) . network ) ;
} ;
const settingsValid = ref ( true ) ;
2023-08-21 01:51:35 -04:00
const isValidNetworkTablesIP = ( v : string | undefined ) : boolean => {
// Check if it is a valid team number between 1-9999
const teamNumberRegex = /^[1-9][0-9]{0,3}$/ ;
// Check if it is a team number longer than 5 digits
const badTeamNumberRegex = /^[0-9]{5,}$/ ;
2023-08-31 16:56:58 -04:00
if ( v === undefined ) return false ;
2023-08-21 01:51:35 -04:00
if ( teamNumberRegex . test ( v ) ) return true ;
if ( isValidIPv4 ( v ) ) return true ;
// need to check these before the hostname. "0" and "99999" are valid hostnames, but we don't want to allow then
if ( v === "0" ) return false ;
if ( badTeamNumberRegex . test ( v ) ) return false ;
return isValidHostname ( v ) ;
} ;
const isValidIPv4 = ( v : string | undefined ) => {
// https://stackoverflow.com/a/17871737
const ipv4Regex = /^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$/ ;
2023-08-31 16:56:58 -04:00
if ( v === undefined ) return false ;
2023-08-21 01:51:35 -04:00
return ipv4Regex . test ( v ) ;
} ;
const isValidHostname = ( v : string | undefined ) => {
// https://stackoverflow.com/a/18494710
const hostnameRegex = /^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)+(\.([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*))*$/ ;
2023-08-31 16:56:58 -04:00
if ( v === undefined ) return false ;
2023-08-21 01:51:35 -04:00
return hostnameRegex . test ( v ) ;
} ;
2023-12-31 00:14:21 -05:00
const settingsHaveChanged = ( ) : boolean => {
const a = useSettingsStore ( ) . network ;
const b = tempSettingsStruct . value ;
return (
a . ntServerAddress !== b . ntServerAddress ||
a . connectionType !== b . connectionType ||
a . staticIp !== b . staticIp ||
a . hostname !== b . hostname ||
a . runNTServer !== b . runNTServer ||
a . shouldManage !== b . shouldManage ||
a . shouldPublishProto !== b . shouldPublishProto ||
a . networkManagerIface !== b . networkManagerIface ||
a . setStaticCommand !== b . setStaticCommand ||
2024-02-16 16:05:47 -05:00
a . setDHCPcommand !== b . setDHCPcommand ||
a . matchCamerasOnlyByPath !== b . matchCamerasOnlyByPath
2023-12-31 00:14:21 -05:00
) ;
} ;
2023-08-21 01:51:35 -04:00
const saveGeneralSettings = ( ) => {
const changingStaticIp = useSettingsStore ( ) . network . connectionType === NetworkConnectionType . Static ;
2024-01-08 08:32:56 -05:00
// replace undefined members with empty strings for backend
const payload = {
connectionType : tempSettingsStruct . value . connectionType ,
hostname : tempSettingsStruct . value . hostname ,
networkManagerIface : tempSettingsStruct . value . networkManagerIface || "" ,
ntServerAddress : tempSettingsStruct . value . ntServerAddress ,
runNTServer : tempSettingsStruct . value . runNTServer ,
setDHCPcommand : tempSettingsStruct . value . setDHCPcommand || "" ,
setStaticCommand : tempSettingsStruct . value . setStaticCommand || "" ,
shouldManage : tempSettingsStruct . value . shouldManage ,
shouldPublishProto : tempSettingsStruct . value . shouldPublishProto ,
2024-02-16 16:05:47 -05:00
matchCamerasOnlyByPath : tempSettingsStruct . value . matchCamerasOnlyByPath ,
2024-01-08 08:32:56 -05:00
staticIp : tempSettingsStruct . value . staticIp
} ;
2023-08-31 16:56:58 -04:00
useSettingsStore ( )
2024-01-08 08:32:56 -05:00
. updateGeneralSettings ( payload )
2023-08-31 16:56:58 -04:00
. then ( ( response ) => {
useStateStore ( ) . showSnackbarMessage ( {
message : response . data . text || response . data ,
color : "success"
} ) ;
2024-01-06 09:43:29 -05:00
// Update the local settings cause the backend checked their validity. Assign is to deref value
2024-01-20 18:46:47 -06:00
useSettingsStore ( ) . network = {
... useSettingsStore ( ) . network ,
... Object . assign ( { } , tempSettingsStruct . value )
} ;
2023-08-31 16:56:58 -04:00
} )
. catch ( ( error ) => {
2024-01-06 09:43:29 -05:00
resetTempSettingsStruct ( ) ;
2023-08-31 16:56:58 -04:00
if ( error . response ) {
if ( error . status === 504 || changingStaticIp ) {
2023-08-21 01:51:35 -04:00
useStateStore ( ) . showSnackbarMessage ( {
color : "error" ,
2023-08-31 16:56:58 -04:00
message : ` Connection lost! Try the new static IP at ${ useSettingsStore ( ) . network . staticIp } :5800 or ${
useSettingsStore ( ) . network . hostname
} : 5800 ? `
2023-08-21 01:51:35 -04:00
} ) ;
} else {
useStateStore ( ) . showSnackbarMessage ( {
color : "error" ,
2023-08-31 16:56:58 -04:00
message : error . response . data . text || error . response . data
2023-08-21 01:51:35 -04:00
} ) ;
}
2023-08-31 16:56:58 -04:00
} else if ( error . request ) {
useStateStore ( ) . showSnackbarMessage ( {
color : "error" ,
message : "Error while trying to process the request! The backend didn't respond."
} ) ;
} else {
useStateStore ( ) . showSnackbarMessage ( {
color : "error" ,
message : "An error occurred while trying to process the request."
} ) ;
}
} ) ;
2023-08-21 01:51:35 -04:00
} ;
2023-09-01 12:58:35 -07:00
const currentNetworkInterfaceIndex = computed < number > ( {
get : ( ) => useSettingsStore ( ) . networkInterfaceNames . indexOf ( useSettingsStore ( ) . network . networkManagerIface || "" ) ,
2023-12-31 00:14:21 -05:00
set : ( v ) => ( tempSettingsStruct . value . networkManagerIface = useSettingsStore ( ) . networkInterfaceNames [ v ] )
2023-09-01 12:58:35 -07:00
} ) ;
2024-01-06 09:43:29 -05:00
watchEffect ( ( ) => {
// Reset temp settings on remote network settings change
resetTempSettingsStruct ( ) ;
} ) ;
2023-08-21 01:51:35 -04:00
< / script >
< template >
2023-08-31 16:56:58 -04:00
< v-card dark class = "mb-3 pr-6 pb-3" style = "background-color: #006492" >
2024-02-16 16:05:47 -05:00
< v-card-title > Global Settings < / v-card-title >
< v-divider / >
2023-08-21 01:51:35 -04:00
< v-card-title > Networking < / v-card-title >
< div class = "ml-5" >
2023-08-31 16:56:58 -04:00
< v-form ref = "form" v-model = "settingsValid" >
2023-10-17 16:32:59 -04:00
< pv-input
2023-12-31 00:14:21 -05:00
v - model = "tempSettingsStruct.ntServerAddress"
2023-08-21 01:51:35 -04:00
label = "Team Number/NetworkTables Server Address"
tooltip = "Enter the Team Number or the IP address of the NetworkTables Server"
2023-09-01 12:58:35 -07:00
: label - cols = "4"
2023-12-31 00:14:21 -05:00
: disabled = "tempSettingsStruct.runNTServer"
2023-08-31 16:56:58 -04:00
: rules = " [
( v ) =>
isValidNetworkTablesIP ( v ) ||
'The NetworkTables Server Address must be a valid Team Number, IP address, or Hostname'
] "
2023-08-21 01:51:35 -04:00
/ >
< v-banner
2023-12-31 00:14:21 -05:00
v - show = "!isValidNetworkTablesIP(tempSettingsStruct.ntServerAddress) && !tempSettingsStruct.runNTServer"
2023-08-21 01:51:35 -04:00
rounded
color = "red"
text - color = "white"
style = "margin: 10px 0"
icon = "mdi-alert-circle-outline"
>
The NetworkTables Server Address is not set or is invalid . NetworkTables is unable to connect .
< / v-banner >
2023-10-17 16:32:59 -04:00
< pv-radio
2024-01-20 18:46:47 -06:00
v - show = "!useSettingsStore().network.networkingDisabled"
2023-12-31 00:14:21 -05:00
v - model = "tempSettingsStruct.connectionType"
2023-08-21 01:51:35 -04:00
label = "IP Assignment Mode"
tooltip = "DHCP will make the radio (router) automatically assign an IP address; this may result in an IP address that changes across reboots. Static IP assignment means that you pick the IP address and it won't change."
2023-09-01 12:58:35 -07:00
: input - cols = "12 - 4"
2023-08-31 16:56:58 -04:00
: list = "['DHCP', 'Static']"
2024-01-20 18:46:47 -06:00
: disabled = "
! tempSettingsStruct . shouldManage ||
! useSettingsStore ( ) . network . canManage ||
useSettingsStore ( ) . network . networkingDisabled
"
2023-08-21 01:51:35 -04:00
/ >
2023-10-17 16:32:59 -04:00
< pv-input
2024-01-20 18:46:47 -06:00
v - show = "!useSettingsStore().network.networkingDisabled"
2023-12-31 00:14:21 -05:00
v - if = "tempSettingsStruct.connectionType === NetworkConnectionType.Static"
v - model = "tempSettingsStruct.staticIp"
2023-09-01 12:58:35 -07:00
: input - cols = "12 - 4"
2023-08-21 01:51:35 -04:00
label = "Static IP"
2023-08-31 16:56:58 -04:00
: rules = "[(v) => isValidIPv4(v) || 'Invalid IPv4 address']"
2024-01-20 18:46:47 -06:00
: disabled = "
! tempSettingsStruct . shouldManage ||
! useSettingsStore ( ) . network . canManage ||
useSettingsStore ( ) . network . networkingDisabled
"
2023-08-21 01:51:35 -04:00
/ >
2023-10-17 16:32:59 -04:00
< pv-input
2024-01-20 18:46:47 -06:00
v - show = "!useSettingsStore().network.networkingDisabled"
2023-12-31 00:14:21 -05:00
v - model = "tempSettingsStruct.hostname"
2023-08-21 01:51:35 -04:00
label = "Hostname"
2023-09-01 12:58:35 -07:00
: input - cols = "12 - 4"
2023-08-31 16:56:58 -04:00
: rules = "[(v) => isValidHostname(v) || 'Invalid hostname']"
2024-01-20 18:46:47 -06:00
: disabled = "
! tempSettingsStruct . shouldManage ||
! useSettingsStore ( ) . network . canManage ||
useSettingsStore ( ) . network . networkingDisabled
"
2023-09-01 12:58:35 -07:00
/ >
< v-divider class = "pb-3" / >
< span style = "font-weight: 700" > Advanced Networking < / span >
2023-10-17 16:32:59 -04:00
< pv-switch
2024-01-20 18:46:47 -06:00
v - show = "!useSettingsStore().network.networkingDisabled"
2023-12-31 00:14:21 -05:00
v - model = "tempSettingsStruct.shouldManage"
2024-01-20 18:46:47 -06:00
: disabled = "!useSettingsStore().network.canManage || useSettingsStore().network.networkingDisabled"
2023-09-01 12:58:35 -07:00
label = "Manage Device Networking"
tooltip = "If enabled, Photon will manage device hostname and network settings."
: label - cols = "4"
class = "pt-2"
/ >
2023-10-17 16:32:59 -04:00
< pv-select
2024-01-20 18:46:47 -06:00
v - show = "!useSettingsStore().network.networkingDisabled"
2023-09-01 12:58:35 -07:00
v - model = "currentNetworkInterfaceIndex"
label = "NetworkManager interface"
2024-01-20 18:46:47 -06:00
: disabled = "
! tempSettingsStruct . shouldManage ||
! useSettingsStore ( ) . network . canManage ||
useSettingsStore ( ) . network . networkingDisabled
"
2023-09-01 12:58:35 -07:00
: select - cols = "12 - 4"
tooltip = "Name of the interface PhotonVision should manage the IP address of"
: items = "useSettingsStore().networkInterfaceNames"
2023-08-21 01:51:35 -04:00
/ >
2023-09-01 12:58:35 -07:00
< v-banner
v - show = "
! useSettingsStore ( ) . networkInterfaceNames . length &&
2023-12-31 00:14:21 -05:00
tempSettingsStruct . shouldManage &&
2024-01-20 18:46:47 -06:00
useSettingsStore ( ) . network . canManage &&
! useSettingsStore ( ) . network . networkingDisabled
2023-09-01 12:58:35 -07:00
"
rounded
color = "red"
text - color = "white"
icon = "mdi-information-outline"
>
Photon cannot detect any wired connections ! Please send program logs to the developers for help .
< / v-banner >
2023-10-17 16:32:59 -04:00
< pv-switch
2023-12-31 00:14:21 -05:00
v - model = "tempSettingsStruct.runNTServer"
2023-08-21 01:51:35 -04:00
label = "Run NetworkTables Server (Debugging Only)"
tooltip = "If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
2023-12-31 00:14:21 -05:00
class = "mt-3 mb-2"
2023-09-01 12:58:35 -07:00
: label - cols = "4"
2023-08-21 01:51:35 -04:00
/ >
< v-banner
2023-12-31 00:14:21 -05:00
v - show = "tempSettingsStruct.runNTServer"
2023-08-21 01:51:35 -04:00
rounded
color = "red"
text - color = "white"
icon = "mdi-information-outline"
>
This mode is intended for debugging ; it should be off for proper usage . PhotonLib will NOT work !
< / v-banner >
2024-02-16 16:05:47 -05:00
< v-divider / >
< v-card-title > Miscellaneous < / v-card-title >
2023-12-31 00:14:21 -05:00
< pv-switch
v - model = "tempSettingsStruct.shouldPublishProto"
label = "Also Publish Protobuf"
tooltip = "If enabled, Photon will publish all pipeline results in both the Packet and Protobuf formats. This is useful for visualizing pipeline results from NT viewers such as glass and logging software such as AdvantageScope. Note: photon-lib will ignore this value and is not recommended on the field for performance."
class = "mt-3 mb-2"
: label - cols = "4"
/ >
< v-banner
v - show = "tempSettingsStruct.shouldPublishProto"
rounded
color = "red"
class = "mb-3"
text - color = "white"
icon = "mdi-information-outline"
>
This mode is intended for debugging ; it should be off for field use . You may notice a performance hit by using
this mode .
< / v-banner >
2024-02-16 16:05:47 -05:00
< pv-switch
v - model = "tempSettingsStruct.matchCamerasOnlyByPath"
2024-02-18 21:00:14 -05:00
label = "Strictly match ONLY known cameras"
tooltip = "ONLY match cameras by the USB port they're plugged into + (basename or USB VID/PID), and never only by the device product string. Also disables automatic detection of new cameras."
2024-02-16 16:05:47 -05:00
class = "mt-3 mb-2"
: label - cols = "4"
/ >
2024-02-18 21:00:14 -05:00
< v-banner
v - show = "tempSettingsStruct.matchCamerasOnlyByPath"
rounded
color = "red"
class = "mb-3"
text - color = "white"
icon = "mdi-information-outline"
>
Physical cameras will be strictly matched to camera configurations using physical USB port they are plugged
into , in addition to device name and other USB metadata . Additionally , no new cameras are allowed to be added .
This setting is useful for guaranteeing that an already known and configured camera can never be matched as an
"unknown" / "new" camera , which resets pipelines and calibration data .
< p / >
Cameras will NOT be matched if they change USB ports , and new cameras plugged into this coprocessor will NOT
be automatically recognized or configured for vision processing .
< p / >
To add a new camera to this coprocessor , disable this setting , connect the camera , and re - enable .
< / v-banner >
2024-02-16 16:05:47 -05:00
< v-divider class = "mb-3" / >
2023-08-21 01:51:35 -04:00
< / v-form >
< v-btn
color = "accent"
2023-08-31 16:56:58 -04:00
style = "color: black; width: 100%"
2023-12-31 00:14:21 -05:00
: disabled = "!settingsValid || !settingsHaveChanged()"
2023-08-21 01:51:35 -04:00
@ click = "saveGeneralSettings"
>
Save
< / v-btn >
< / div >
< / v-card >
< / template >
< style >
. v - banner _ _wrapper {
padding : 6 px ! important ;
}
< / style >