Compare commits

...

45 Commits

Author SHA1 Message Date
Justin Nguyen 9d47cf4a06
Merge pull request #14 from Northrop-Grumman-Collaboration-Project/vehicle-status
Vehicle status component for map/static screen
2024-04-04 01:05:10 -07:00
chillwafflez 4bb10cecc4 put VehicleStatus component onto Static/Map screen. Readjusted CSS of components within the VehicleStatusComponent 2024-04-02 23:08:42 -07:00
chillwafflez e010b32e90 Merge branch 'vehicle-status' 2024-04-02 20:45:35 -07:00
chillwafflez ddf806ec35 changed main.ts back to normal 2024-03-31 00:46:29 -07:00
chillwafflez d8146584d6 added text for Connection section of vehicle status. changed viewpoint height of testMapScreen 2024-03-31 00:40:23 -07:00
Steven C 164f1cb5ad
Merge pull request #13 from Northrop-Grumman-Collaboration-Project/FocusCam
Focus cam
2024-03-31 00:15:29 -07:00
Steven C 3f64b430d1 Merge branch 'main' into FocusCam 2024-03-30 23:24:01 -07:00
Steven C a05e663836 merge conflict 2024-03-30 23:21:44 -07:00
Steven C bbdf19bcb0 static screen setup 2024-03-30 23:13:00 -07:00
Justin Nguyen 014b5be490
Merge pull request #11 from Northrop-Grumman-Collaboration-Project/routers
Routers
2024-03-30 22:43:32 -07:00
chillwafflez 4298176dab readjust sizes of components in vehicle status component 2024-03-30 22:36:31 -07:00
Steven C 3ba58ef165 Generalize components for reusablilty: FocusCam, FourCam 2024-03-30 22:24:23 -07:00
chillwafflez 1cf724e933 added Coordinate component to display coordinate in Vehicle Status component 2024-03-23 00:38:44 -07:00
Justin Nguyen 294ffead44 added Open button for Vehicle Status widget. Adjusted sizing of other components in Status widget 2024-03-19 01:43:54 -07:00
chillwafflez 2d8f27cf7a created initial layout for vehicle status component. contains name, status, battery, and connection. created a test basic map layout to display vehicle status components 2024-03-15 23:01:50 -07:00
chillwafflez b644c718f0 created initial vehicle status component 2024-03-15 18:42:11 -07:00
Steven C 6ce48a6923 Added test camServer.py 2024-03-15 12:46:14 -07:00
Steven C 97664a1790 localhost cam ip 2024-03-08 13:19:02 -08:00
Priyatham Sai Chand ce0bcf2117
Merge pull request #7 from Northrop-Grumman-Collaboration-Project/vue-documentation
Vue documentation setup
2024-03-08 12:50:14 -08:00
Priyatham Sai Chand f72f64bab8
Merge branch 'main' into vue-documentation 2024-03-08 12:49:59 -08:00
Steven C e16e44af2f Removed Icon 2024-03-08 12:46:18 -08:00
Steven C 89f0b729c7 Removed Icon 2024-03-08 12:41:45 -08:00
chillwafflez 850623e02d Merge branch 'routers' of https://github.com/Northrop-Grumman-Collaboration-Project/gcs-user-interface into routers 2024-03-08 01:04:57 -08:00
chillwafflez cc9e8a1e96 put battery and connection component in same div as camera component 2024-03-08 01:02:53 -08:00
Steven C 31af224028 fixed issue #10 2024-03-06 21:59:18 -08:00
chillwafflez 138119ed0a put Battery and Connection components onto 4Cam screen. May need to adjust Battery and Connection sizing if interfering with elements in Camera div 2024-03-06 00:16:47 -08:00
chillwafflez 3b8e866e74 fixed latency ranges for connection component 2024-03-05 12:53:41 -08:00
chillwafflez 9ed4c32061 Merge branch 'routers' of https://github.com/Northrop-Grumman-Collaboration-Project/gcs-user-interface into routers 2024-03-05 01:32:44 -08:00
chillwafflez 5c7e9d23bd Added Connection component, moved Battery and Connection to VehicleStatus folder 2024-03-05 01:32:15 -08:00
Priyatham Sai Chand a61b1d7536
Merge pull request #9 from Northrop-Grumman-Collaboration-Project/routers
Routers/Cameras
2024-03-04 11:34:35 -08:00
Priyatham-sai-chand 8dd23ed0d6 package change 2024-03-04 11:24:22 -08:00
Priyatham-sai-chand 85dd727ce4 main package change 2024-03-04 11:24:21 -08:00
chillwafflez 14f35907e2 Added Battery icon component for vehicles 2024-03-03 19:33:37 -08:00
Steven C 7875f44982
Merge pull request #8 from Northrop-Grumman-Collaboration-Project/navbar
Navbar
2024-03-03 00:52:20 -08:00
Steven C 3034820bce polish camera views 2024-03-03 00:43:12 -08:00
Steven C fb19c5b218 Selective Camera Setup 2024-03-03 00:12:50 -08:00
Steven C 4db224bc11 router setup 2024-03-01 03:27:33 -08:00
Steven C 261f682b78 wip 2024-03-01 02:32:29 -08:00
Steven C 6d7c5434d4 4cam layout WIP 2024-02-23 03:42:11 -08:00
Priyatham-sai-chand 0622d77e69 change port for styleguide remove appbutton 2024-02-14 15:08:02 -08:00
Priyatham-sai-chand 81f6ccc990 change package for vue 2024-02-14 15:07:40 -08:00
Priyatham-sai-chand ef46fa914b change package for vue 2024-02-14 15:07:03 -08:00
Steven C bf90c647c2 wip 2024-02-09 13:37:57 -08:00
Steven C ba94f0a568 init navbar layout 2024-02-09 13:32:06 -08:00
Priyatham-sai-chand 7942fbe4bf tauri conf changes 2024-02-09 12:29:48 -08:00
32 changed files with 1177 additions and 38 deletions

44
camServer.py Normal file
View File

@ -0,0 +1,44 @@
#111!/usr/bin/env python
# -*- coding: utf-8 -*
#sudo apt-get install python3-flask
#pip3 install opencv-python
from flask import Flask, render_template, Response
import cv2
app = Flask(__name__)
#app.config["CACHE_TYPE"] = "null"
@app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
vs = cv2.VideoCapture(0) # Camera 1
vs2 = cv2.VideoCapture(1) # Camera 2
def gen(vs):
"""Video streaming generator function."""
while True:
ret,frame=vs.read()
ret, jpeg = cv2.imencode('.jpg', frame)
frame=jpeg.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
vs.release()
cv2.destroyAllWindows()
@app.route('/video_feed')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(vs),mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/video_feed2')
def video_feed2():
"""Video streaming route for camera 2."""
return Response(gen(vs2), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', port =5000, debug=True, threaded=True)

View File

@ -4,11 +4,14 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + Vue + TS</title> <title>Tauri + Vue + TS</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

22
package-lock.json generated
View File

@ -9,7 +9,8 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1.4.0", "@tauri-apps/api": "^1.4.0",
"vue": "^3.2.45" "vue": "^3.2.45",
"vue-router": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1.4.0", "@tauri-apps/cli": "^1.4.0",
@ -2052,6 +2053,11 @@
"dev": true, "dev": true,
"peer": true "peer": true
}, },
"node_modules/@vue/devtools-api": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz",
"integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA=="
},
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.4.18", "version": "3.4.18",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz",
@ -14297,6 +14303,20 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/vue-router": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz",
"integrity": "sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==",
"dependencies": {
"@vue/devtools-api": "^6.5.1"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/vue-style-loader": { "node_modules/vue-style-loader": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",

View File

@ -12,7 +12,8 @@
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1.4.0", "@tauri-apps/api": "^1.4.0",
"vue": "^3.2.45" "vue": "^3.2.45",
"vue-router": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1.4.0", "@tauri-apps/cli": "^1.4.0",

View File

@ -11,6 +11,9 @@ dependencies:
vue: vue:
specifier: ^3.2.45 specifier: ^3.2.45
version: 3.2.45 version: 3.2.45
vue-router:
specifier: ^4.3.0
version: 4.3.0(vue@3.2.45)
devDependencies: devDependencies:
'@tauri-apps/cli': '@tauri-apps/cli':
@ -1320,6 +1323,10 @@ packages:
- whiskers - whiskers
dev: true dev: true
/@vue/devtools-api@6.6.1:
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
dev: false
/@vue/reactivity-transform@3.2.45: /@vue/reactivity-transform@3.2.45:
resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==} resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==}
dependencies: dependencies:
@ -8899,6 +8906,14 @@ packages:
webpack: 5.90.1 webpack: 5.90.1
dev: true dev: true
/vue-router@4.3.0(vue@3.2.45):
resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
peerDependencies:
vue: ^3.2.0
dependencies:
'@vue/devtools-api': 6.6.1
vue: 3.2.45
dev: false
/vue-style-loader@4.1.3: /vue-style-loader@4.1.3:
resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==} resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==}
dependencies: dependencies:

View File

@ -37,9 +37,14 @@
{ {
"fullscreen": false, "fullscreen": false,
"resizable": true, "resizable": true,
"title": "Interface", "title": "Camera Screen",
"width": 800, "width": 800,
"height": 600 "height": 600
},
{
"label": "StaticScreen",
"title": "Over View",
"url": "http://localhost:4000/#/1"
} }
] ]
} }

View File

@ -1,42 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs import Navbar from "./components/Navbar.vue";
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import { RouterView } from "vue-router";
// import Greet from "./components/Greet.vue";
</script> </script>
<template> <template>
<div class="container"> <div>
<h1>Welcome to Tauri!</h1> <Navbar/>
<div class="row">
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" class="logo tauri" alt="Tauri logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<p>Click on the Tauri, Vite, and Vue logos to learn more.</p>
<p>
Recommended IDE setup:
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
+
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
+
<a href="https://github.com/tauri-apps/tauri-vscode" target="_blank"
>Tauri</a
>
+
<a href="https://github.com/rust-lang/rust-analyzer" target="_blank"
>rust-analyzer</a
>
</p>
</div> </div>
<RouterView/>
</template> </template>
<style scoped> <style scoped>
@ -47,4 +18,11 @@
.logo.vue:hover { .logo.vue:hover {
filter: drop-shadow(0 0 2em #249b73); filter: drop-shadow(0 0 2em #249b73);
} }
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr); /* 2 columns */
grid-template-rows: repeat(2, 46vh); /* 2 rows */
max-width: 100vw; /* Limit width to viewport width */
max-height: 100vh; /* Limit height to viewport height */
}
</style> </style>

BIN
src/assets/ERU.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/assets/FRA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/assets/MEA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
src/assets/MRA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

14
src/assets/NGCP.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

63
src/components/Camera.vue Normal file
View File

@ -0,0 +1,63 @@
<script>
export default {
props: ['cameraID'],
data() {
return {
localIp: 'http://localhost', //maybe change accordingly .env?
port: '5000', //maybe change accordingly
cameraUrls: {
1: 'video_feed', //cam url here
2: 'video_feed2',
}
};
},
mounted() {
// this.initCam();
},
methods: {
// async initCam() {
// try {
// const constraints = { video: true };
// const stream = await navigator.mediaDevices.getUserMedia(constraints);
// this.streams.push(stream);
// } catch (error) {
// console.error('Error accessing camera:', error);
// }
// },
getCameraImageUrl(cameraID) {
return `${this.localIp}:${this.port}/${this.cameraUrls[cameraID]}`;
}
}
};
</script>
<template>
<div class="box video-section">
<img :src="getCameraImageUrl(cameraID)" class="video-image">
</div>
</template>
<style scoped>
/* .app {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
} */
.video-section {
width: 100%;
height: 100%;
box-sizing: border-box;
border: 2px solid #ccc;
}
.video-image {
width: 100%;
height: 100%;
/* object-fit: contain; */
}
.box{
border: 2px solid #030303;
}
</style>

90
src/components/Navbar.vue Normal file
View File

@ -0,0 +1,90 @@
<script>
export default {
data() {
return {
isNavbarOpen: false,
misson_one_status: "Done",
misson_two_status: "In Progress",
misson_three_status: "initiated",
};
},
methods: {
toggleNavbar() {
this.isNavbarOpen = !this.isNavbarOpen;
}
},
openNewWindow() {
// Open a new window with specified URL
if (window.tauri) {
window.tauri.open('/');
} else {
console.error('Tauri API is not available.');
}
}
};
</script>
<template>
<nav style="background-color: #011949; padding: 10px;">
<div style="display: flex; align-items: center;">
<router-link to="/" style="text-decoration: none;">
<span style="font-weight: bold; font-size: 1.2rem; margin-left: 10px;">NG</span>
<span style="font-weight: bold; font-size: 1.2rem; color: white">CP</span>
</router-link>
<div style="width: 12rem; height:4rem;border: 2px solid rgb(52, 49, 49); background-color: rgb(255, 255, 255); margin-left: auto; display: flex; flex-direction: column; justify-content: center; align-items: flex-start;">
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Mission 1</span>
<button style="border: 2px solid rgb(0, 0, 0); margin-left: 1.2rem; color: rgb(0, 0, 0); padding: 3px 6px; font-size: 0.8rem;" type="button" @click="">
<router-link to="/1" class="router-link">
<span style="font-weight: bold; font-size: 0.8rem;">OPEN</span>
</router-link>
</button>
</div>
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Status: {{ misson_one_status }}</span>
</div>
</div>
<div style="width: 12rem; height:4rem;border: 2px solid rgb(52, 49, 49); background-color: rgb(255, 255, 255); margin-left: auto; display: flex; flex-direction: column; justify-content: center; align-items: flex-start;">
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Mission 2</span>
<button style="border: 2px solid rgb(0, 0, 0); margin-left: 1.2rem; color: rgb(0, 0, 0); padding: 3px 6px; font-size: 0.8rem;" type="button" @click="">
<router-link to="/2" class="router-link">
<span style="font-weight: bold; font-size: 0.8rem;">OPEN</span>
</router-link>
</button>
</div>
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Status: {{ misson_two_status }}</span>
</div>
</div>
<div style="width: 12rem; height:4rem;border: 2px solid rgb(52, 49, 49); background-color: rgb(255, 255, 255); margin-left: auto; display: flex; flex-direction: column; justify-content: center; align-items: flex-start;">
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Mission 3</span>
<button style="border: 2px solid rgb(0, 0, 0); margin-left: 1.2rem; color: rgb(0, 0, 0); padding: 3px 6px; font-size: 0.8rem;" type="button" @click="">
<span style="font-weight: bold; font-size: 0.8rem;">OPEN</span>
</button>
</div>
<div>
<span style="font-weight: bold; font-size: 1.2rem; color: rgb(0, 0, 0);">Status: {{ misson_three_status }}</span>
</div>
</div>
<button style="border: 2px solid rgb(255, 0, 0); background-color: rgba(255, 0, 0); margin-left: auto; color: rgb(255, 255, 255);" type="button" @click="">
<span style="font-size: 18px;">STOP ALL</span>
</button>
<button style="border: 2px solid rgb(52, 49, 49); background-color: rgba(38, 36, 36, 0.25); margin-left: auto; color: rgb(52, 49, 49);" type="button" @click="toggleNavbar">
<span style="font-size: 18px;">&#9776;</span>
</button>
</div>
<div v-if="isNavbarOpen" style="margin-top: 10px;">
<ul style="list-style-type: none; padding-left: 0;">
<!-- Insert your menu items here -->
<li><a href="#" style="text-decoration: none;">Home</a></li>
<li><a href="#" style="text-decoration: none;" @click="openNewWindow">Link</a></li>
<li><router-link to="/1" style="text-decoration: none;">Disabled</router-link></li>
</ul>
</div>
</nav>
</template>

View File

@ -0,0 +1,139 @@
<template>
<div class="outer_div">
<div class="battery_container">
<!-- <div :class="percentageCSS" :style="{ width: this.percentage + '%' }"></div> -->
<div :class="percentageCSS" :style="[percentage > 15 ? { width: percentage + '%' } : { width: '15%'}]"></div>
</div>
<div class="battery_widget"></div>
<img class="lightingSymbol" :class="batteryStatus" src="..\..\assets\lightning-icon-png-5.png" >
</div>
</template>
<script lang="ts">
export default {
data() {
return {};
},
props: {
percentage: { required: true, type: Number},
charging: { required: true, type: Boolean},
},
computed: {
percentageCSS() {
if (this.percentage <= 0) {
return 'zeroPercent'
} else if ((this.percentage > 0) && (this.percentage <= 15)) {
return 'tenPercent'
} else if ((this.percentage > 15) && (this.percentage <= 30)) {
return 'twentyFivePercent'
} else if ((this.percentage > 30) && (this.percentage <= 50)) {
return 'fiftyPercent'
} else {
return 'normalPercent'
}
},
batteryStatus() {
if (this.charging == true) {
return 'charging'
} else if (this.percentage <= 0) {
return 'dead'
}
}
},
};
</script>
<style scoped>
.outer_div {
display: flex;
position: relative;
height: 18%;
width: 18%;
}
.battery_widget {
position: relative;
height: 50%;
width: 10%;
background-color:black;
top: 30%;
left: 1%;
/* top: 30%; */
/* border-radius: 0 12px 12px 0; */
border-radius: 0 25% 25% 0;
}
/* .battery_icon {
position: absolute;
width: 40%;
left: 30%;
top: 15%;
animation: blinker 1s linear infinite;
visibility: hidden;
} */
.lightingSymbol {
position: absolute;
width: 40%;
left: 30%;
top: 15%;
visibility: hidden;
}
.dead {
visibility: visible;
animation: blinker 1s linear infinite;
}
.charging {
visibility: visible;
}
@keyframes blinker {
50% {
opacity: 0;
}
}
.battery_container {
position: relative;
display: flex;
border: 0.1em solid black;
height: 100%;
width: 100%;
border-radius: 12%;
background-color: white;
}
#battery_progress {
background-color: rgb(83, 255, 83);
border-radius: 12%;
height:100%;
width: 100%;
}
.zeroPercent {
width: 0%;
}
.tenPercent {
background-color: red;
border-radius: 12%;
height:100%;
}
.twentyFivePercent {
background-color: rgb(116, 115, 109);
border-radius: 12%;
height:100%;
}
.fiftyPercent {
background-color: rgb(245, 225, 44);
border-radius: 12%;
height:100%;
}
.normalPercent {
background-color: rgb(87, 255, 87);
border-radius: 12%;
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<div class="outer_div">
<div class="connection-container">
<div v-if="latency == 0" class="grayed_bar" style='height: 20%'></div>
<div v-else class="bar" style='height: 20%'></div>
<div v-if="latency >= 70 || latency == 0" class="grayed_bar" style='height: 40%'></div>
<div v-else class="bar" style='height: 40%'></div>
<div v-if="latency >= 60 || latency == 0" class="grayed_bar" style='height: 60%'></div>
<div v-else class="bar" style='height: 60%'></div>
<div v-if="latency >= 40 || latency == 0" class="grayed_bar" style='height: 80%'></div>
<div v-else class="bar" style='height: 80%'></div>
</div>
<!-- <span class="connection_number">{{ latency }} ms</span> -->
</div>
</template>
<script lang="ts">
export default {
data() {
return {};
},
props: {
latency: { required: true, type: Number},
},
computed: {
}
};
</script>
<style scoped>
.outer_div {
display: flex;
position: relative;
height: 25%;
width: 10%;
}
.connection-container {
position: relative;
justify-content: center;
display: flex;
gap: 0.05em;
height: 100%;
width: 100%;
border-radius: 12%;
/* background-color: white; */
}
.bar {
width:100%;
background-color: white;
border: 0.1em solid black;
margin-top: auto;
border-radius: 20%;
}
.grayed_bar {
width:100%;
background-color: rgb(136, 135, 135);
border: 0.1em solid black;
margin-top: auto;
opacity: 0.2;
border-radius: 20%;
}
.connection_number {
position: absolute;
left: 110%;
bottom: 0%;
width: 180%;
font-size:0.8em;
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div class="coordinate-container">
<div class="coordinate-div">Longitude: {{ coordinates.longitude }}</div>
<div class="coordinate-div">Latitude: {{ coordinates.latitude }}</div>
</div>
</template>
<script lang="ts">
export default {
props: {
coordinates: { required: true, type: Object }
},
computed: {
}
};
</script>
<style scoped>
.coordinate-container {
position: relative;
display: flex;
flex-direction: column;
width: 92%;
}
.coordinate-div {
position: relative;
padding: 1%;
font-size: 1.2em;
color: black;
width: 100%;
}
</style>

View File

@ -0,0 +1,40 @@
<template>
<div class="emergency-stop-div">
<button class="emergency-button"> EMERGENCY STOP </button>
</div>
</template>
<script lang="ts">
export default {
data() {
return {};
}
};
</script>
<style scoped>
.emergency-stop-div {
position: relative;
display: flex;
height: 20%;
width: 78%;
text-align: center;
}
.emergency-button {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
background-color: rgb(255, 57, 57);
font-size: 0.96em;
transition: background-color 0.20s;
}
.emergency-button:hover {
background-color: rgb(192, 40, 40);
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<button class="open-button">OPEN</button>
</template>
<script lang="ts">
export default {
data() {
return {};
}
};
</script>
<style scoped>
.open-button {
position: relative;
display: flex;
justify-content: center;
width: 30%;
height: 18%;
background-color: #011949;
font-size: 1em;
transition: background-color 0.20s;
}
.open-button:hover {
background-color: #194398;
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<div class="vehicle-title-container">
<div class="vehicle-name-div">
{{ vehicleName }}
<!-- <img src="../../assets/MEA.png" class="vehicle-icon"></img>-->
</div>
<div class="vehicle-status-div">Status: {{ vehicleStatus }}</div>
</div>
</template>
<script lang="ts">
export default {
props: {
vehicleName: { required: true, type: String },
vehicleStatus: { required: true, type: String },
vehicleIcon: { type: Object }
}
};
</script>
<style scoped>
.vehicle-title-container {
position: relative;
width: 100%;
padding-top: 8%;
padding-left: 6%;
}
/* .vehicle-name-div {
position: relative;
font-size: 2.3em;
} */
.vehicle-name-div {
position: relative;
display: flex;
font-size: 2.3em;
}
.vehicle-status-div {
position: relative;
font-size: 1.4em;
margin-top: 5%;
}
.vehicle-icon {
position: relative;
width: 22%;
}
</style>

View File

@ -0,0 +1,138 @@
<script lang="ts">
import Battery from './VehicleStatus/Battery.vue';
import Connection from './VehicleStatus/Connection.vue';
import VehicleTitle from './VehicleStatus/VehicleTitle.vue';
import EmergencyStop from './VehicleStatus/EmergencyStop.vue';
import Open from './VehicleStatus/Open.vue';
import Coordinate from './VehicleStatus/Coordinate.vue';
export default {
props: {
vehicleName: { required: true, type: String},
vehicleStatus: { required: true, type: String},
batteryPct: {required: true, type: Number},
latency: { required: true, type: Number },
coordinates: { required: true, type: Object }
},
components: {
VehicleTitle,
Battery,
Connection,
EmergencyStop,
Open,
Coordinate
},
};
</script>
<template>
<div class="status_container">
<!-- Left side of container (Name, Status, Battery, Connection)-->
<div class="left-container">
<VehicleTitle :vehicleName="vehicleName" :vehicleStatus="vehicleStatus"/>
<div class="battery-status-container">
<Battery :percentage = "batteryPct" :charging="false" class="adjust-battery"/>
<span style="margin-top: 4%; font-size: 1.4em;">{{ batteryPct }}%</span>
</div>
<div class="connection-status-container">
<Connection :latency="latency" class="adjust-connection"/>
<div class="connection-status-specifics">
<span style="font-size: 0.9em;">Connection:</span>
<span style="font-size: 0.9em;">Last Packet: {{ latency }} </span>
</div>
</div>
</div>
<!-- Right side of container (Open button, Coordinates, Emergency Stop)-->
<div class="right-container">
<Open class="adjust-open-button"></Open>
<Coordinate :coordinates="coordinates" class="adjust-coordinates"></Coordinate>
<EmergencyStop class="adjust-emergency-button"/>
</div>
</div>
</template>
<style scoped>
.status_container {
display: flex;
position: relative;
height: 100%;
/* width: 25%; */
width: 100%;
border: 0.1em solid black;
background-color: white;
color: black;
}
.left-container {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
width: 46%;
}
.right-container {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
width: 54%;
}
.battery-status-container {
display: flex;
position: relative;
width: 100%;
height: 20%;
gap: 6%;
margin-top: auto;
}
.connection-status-container {
display: flex;
position: relative;
width: 100%;
height: 30%;
margin-bottom: 2%;
/* margin-left: 10%; */
}
.connection-status-specifics {
display: flex;
flex-direction: column;
padding-left: 3%;
padding-top: 6%;
}
.adjust-battery {
height: 84%;
width: 34%;
margin-left: 8%;
}
.adjust-connection {
height: 92%;
width: 26%;
padding-left: 10%;
}
.adjust-emergency-button {
margin-top: auto;
margin-bottom: 5%;
margin-left: 10%;
}
.adjust-open-button {
margin-top: 4%;
margin-left: 58%;
}
.adjust-coordinates {
margin-top: 18%;
margin-left: 8%;
}
</style>

View File

@ -1,5 +1,6 @@
import { createApp } from "vue"; import { createApp } from "vue";
import "./styles.css"; import "./styles.css";
import App from "./App.vue"; import App from "./App.vue";
import router from "./router";
createApp(App).mount("#app"); createApp(App).use(router).mount("#app");

24
src/router/index.js Normal file
View File

@ -0,0 +1,24 @@
import { createRouter, createWebHashHistory } from "vue-router"
import HomeScreen from "../views/HomeScreen.vue";
import StaticScreen from "../views/StaticScreen.vue";
import Cam2Focus from "../views/Cam2Focus.vue";
import CamFocus from "../views/CamFocus.vue";
import test from "../views/test.vue";
const routes = [
{ path: '/', component: HomeScreen },
{ path: '/1', component: StaticScreen },
{ path: '/2', component: Cam2Focus },
{ path: '/CamFocus/:id', component: CamFocus },
{ path: '/test', component: test },
]
const router = createRouter({
// 4. Provide the history implementation to use. We
// are using the hash history for simplicity here.
history: createWebHashHistory(),
routes, // short for `routes: routes`
})
export default router

View File

@ -0,0 +1,50 @@
<!-- Using this to see how the Battery and Connection components look -->
<script setup>
import Battery from './components/VehicleStatus/Battery.vue';
import Connection from './components/VehicleStatus/Connection.vue';
import Status from "./components/VehicleStatusComponent.vue";
import yuh from "./components/VehicleStatus/VehicleTitle.vue"
import Coordinate from './components/VehicleStatus/Coordinate.vue'
let testObject = {
longitude: -177.9325790,
latitude: 33.9325790
}
</script>
<template>
<!-- <Status :batteryPct=48 :vehicleName="'ERU'" :vehicleStatus="'Offline'"/> -->
<Coordinate :coordinates="testObject"></Coordinate>
</template>
<style scoped>
.border_div {
/* display: flex; */
border: 0.4em solid black;
height: 200px;
width: 400px;
/* height: 100px;
width: 200px; */
}
.border_div_2 {
display: flex;
border: 0.4em solid black;
height: 180px;
width: 380px;
/* height: 100px;
width: 200px; */
}
.additional_battery_prop {
top: 4%;
margin-right: 2%;
}
</style>

20
src/views/Cam1Focus.vue Normal file
View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import Camera from "../components/Camera.vue";
</script>
<template>
<div class="camera-container">
<Camera :cameraNumber="1" />
</div>
</template>
<style scoped>
.camera-container {
height: 90vh; /* Set the height of the container to 100% of the viewport height */
width: 75%;
display: flex; /* Use flexbox to align items vertically */
justify-content: center; /* Center the child element horizontally */
align-items: center; /* Center the child element vertically */
}
</style>

20
src/views/Cam2Focus.vue Normal file
View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import Camera from "../components/Camera.vue";
</script>
<template>
<div class="camera-container">
<Camera :cameraNumber="2" />
</div>
</template>
<style scoped>
.camera-container {
height: 90vh; /* Set the height of the container to 100% of the viewport height */
width: 75%;
display: flex; /* Use flexbox to align items vertically */
justify-content: center; /* Center the child element horizontally */
align-items: center; /* Center the child element vertically */
}
</style>

43
src/views/CamFocus.vue Normal file
View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import Camera from "../components/Camera.vue";
import { useRoute } from 'vue-router';
const route = useRoute();
const cameraID = Number(route.params.id); // Assuming we're using camera Number
</script>
<template>
<div class="camera-container">
<div class="camera-wrapper">
<router-link to="/" class="back">Back</router-link>
<Camera :cameraID="cameraID" />
<!-- Back button -->
</div>
</div>
</template>
<style scoped>
.camera-container {
height: 90vh; /* Set the height of the container to 100% of the viewport height */
width: 75%;
display: flex; /* Use flexbox to align items vertically */
justify-content: center; /* Center the child element horizontally */
align-items: center; /* Center the child element vertically */
}
.camera-wrapper {
position: relative; /* For positioning the button relative to the camera */
height: 100%;
width: 100%;
}
.back {
position: absolute; /* Position the button relative to the container */
top: 10px;
left: 10px;
padding: 5px 10px;
border: none;
background-color: lightgray;
color: black;
cursor: pointer;
}
</style>

99
src/views/HomeScreen.vue Normal file
View File

@ -0,0 +1,99 @@
<script setup lang="ts">
import { ref } from 'vue';
import Battery from '../components/VehicleStatus/Battery.vue';
import Connection from '../components/VehicleStatus/Connection.vue';
import Camera from "../components/Camera.vue";
import { useRouter } from 'vue-router';
const router = useRouter();
const handleClick = (cameraID:number) => {
console.log("camera:" , cameraID);
router.push(`/CamFocus/${cameraID}`);}
</script>
<template>
<div class="grid">
<div class="hover" style="position: relative; display: flex;" @click="handleClick(2)">
<Camera :cameraID="2"/>
<Battery :percentage=85 :charging="false" class="battery_test"/>
<Connection :latency=65 class="connection_test"/>
<!-- the Battery and Connection component's size is dependent on its parent element. So changing 'status_div' size will change their size-->
<!-- <div class="status_div">
<Battery :percentage=89 :charging="false" class="adjust_Battery"/>
<Connection :latency=65 />
</div> -->
</div>
<div class="hover" style="position: relative; display: flex;" @click="handleClick(1)">
<Camera :cameraID="1"/>
<Battery :percentage=12 :charging="false" class="battery_test"/>
<Connection :latency=3 class="connection_test"/>
<!-- <div class="status_div">
<Battery :percentage=12 :charging="false" class="adjust_Battery"/>
<Connection :latency=3 />
</div> -->
</div>
<div class="hover" style="position: relative; display: flex;" @click="handleClick(1)">
<Camera :cameraID="1"/>
<Battery :percentage=46 :charging="false" class="battery_test"/>
<Connection :latency=82 class="connection_test"/>
<!-- <div class="status_div">
<Battery :percentage=46 :charging="false" class="adjust_Battery"/>
<Connection :latency=82 />
</div> -->
</div>
<div class="hover" style="position: relative; display: flex;" @click="handleClick(1)">
<Camera :cameraID="1"/>
<Battery :percentage=0 :charging="false" class="battery_test"/>
<Connection :latency=5 class="connection_test"/>
<!-- <div class="status_div">
<Battery :percentage=0 :charging="false" class="adjust_Battery"/>
<Connection :latency=5 />
</div> -->
</div>
</div>
</template>
<style scoped>
/* currently Battery and Connection's sizes are dependent on the parent element (status_div). If we want their own fixed size, can change
it directly in their file component's style*/
.status_div {
display: flex;
position: absolute;
top: 1%;
left:4%;
/* border: 0.4em solid black; */
height: 130px;
width: 260px;
}
.adjust_Battery {
top: 5%;
margin-right: 3%;
}
.battery_test {
position:absolute;
height: 7%;
width: 6.3%;
top: 4%;
left: 2%;
}
.connection_test {
position:absolute;
height: 11%;
width: 4%;
top: 1%;
left: 9.5%;
}
.hover{
cursor: pointer;
}
</style>

View File

@ -0,0 +1,62 @@
<script setup lang="ts">
import Camera from "../components/Camera.vue";
import Status from '../components/VehicleStatusComponent.vue';
import NavBar from '../components/Navbar.vue';
let testCoordinateObject = {
longitude: -177.9325790,
latitude: 33.9325790
}
let testCoordinateObject2 = {
longitude: 40.748440,
latitude: -73.984559
}
</script>
<template>
<div class="screen_div">
<!-- Map component will be placed below -->
<div class="map_div"></div>
<div class="four-status-rightside">
<!-- For final product, pass in a Vehicle Object instead that contains all of the information for the VehicleStatusComponent to display-->
<Status :batteryPct=100 :latency=50 :coordinates="testCoordinateObject2" :vehicleName="'ERU'" :vehicleStatus="'In Use'"/>
<Status :batteryPct=48 :latency=30 :coordinates="testCoordinateObject" :vehicleName="'MEA'" :vehicleStatus="'Standby'"/>
<Status :batteryPct=0 :latency=100 :coordinates="testCoordinateObject2" :vehicleName="'MRA'" :vehicleStatus="'Offline'"/>
<Status :batteryPct=10 :latency=0 :coordinates="testCoordinateObject" :vehicleName="'FRA'" :vehicleStatus="'Offline'"/>
</div>
</div>
<!-- <div class="camera-container">
this is static screen
</div> -->
</template>
<style scoped>
.camera-container {
height: 90vh; /* Set the height of the container to 100% of the viewport height */
width: 75%;
display: flex; /* Use flexbox to align items vertically */
justify-content: center; /* Center the child element horizontally */
align-items: center; /* Center the child element vertically */
}
.screen_div {
display: flex;
border: 0.003em solid black;
height: 89vh;
width: 99vw;
}
.four-status-rightside {
display: flex;
flex-direction: column;
border: 0.003em solid black;
height: 100%;
width: 23%;
margin-left: auto
}
.map_div {
}
</style>

14
src/views/test.vue Normal file
View File

@ -0,0 +1,14 @@
<script setup lang="ts">
</script>
<template>
<div class="grid">
<div>
<h1>
TESTESTES
</h1>
</div>
</div>
</template>

View File

@ -0,0 +1,58 @@
<!-- <script setup> -->
<script setup>
import Status from '../components/VehicleStatusComponent.vue';
import NavBar from '../components/Navbar.vue';
let testCoordinateObject = {
longitude: -177.9325790,
latitude: 33.9325790
}
let testCoordinateObject2 = {
longitude: 40.748440,
latitude: -73.984559
}
</script>
<template>
<NavBar></NavBar>
<div class="screen_div">
<!-- Map component will be placed below -->
<div class="map_div"></div>
<div class="status_rightside">
<Status :batteryPct=100 :latency=50 :coordinates="testCoordinateObject2" :vehicleName="'ERU'" :vehicleStatus="'In Use'"/>
<Status :batteryPct=48 :latency=30 :coordinates="testCoordinateObject" :vehicleName="'MEA'" :vehicleStatus="'Standby'"/>
<Status :batteryPct=0 :latency=97 :coordinates="testCoordinateObject2" :vehicleName="'MRA'" :vehicleStatus="'Offline'"/>
<Status :batteryPct=10 :latency=0 :coordinates="testCoordinateObject" :vehicleName="'FRA'" :vehicleStatus="'Offline'"/>
</div>
</div>
</template>
<style scoped>
.screen_div {
display: flex;
border: 0.003em solid black;
height: 88vh;
width: 99vw;
}
.status_rightside {
display: flex;
flex-direction: column;
border: 0.003em solid black;
height: 100%;
width: 23%;
margin-left: auto
}
.map_div {
background-color: pink;
}
</style>