Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified core/frontend/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions core/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"semver": "^7.3.8",
"semver-stable": "^3.0.0",
"simple-icons": "11",
"streamsaver": "^2.0.6",
"three": "0.156.1",
"util": "^0.12.5",
"uuid": "^9.0.0",
Expand Down
62 changes: 38 additions & 24 deletions core/frontend/src/components/app/SettingsMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<v-btn
v-tooltip="'Download log for all services in BlueOS'"
class="ma-2"
:disabled="system_folder_manager.inProgress || deletion_in_progress"
@click="download_service_log_files"
>
<v-icon left>
Expand All @@ -73,7 +74,7 @@
<v-btn
v-tooltip="'Frees up space on the SD card'"
class="ma-2"
:disabled="disable_remove || deletion_in_progress"
:disabled="disable_remove || deletion_in_progress || system_folder_manager.inProgress"
@click="remove_service_log_files"
>
<v-icon left>
Expand All @@ -85,24 +86,22 @@

<v-expand-transition>
<div v-if="deletion_in_progress" class="pa-4">
<v-progress-linear
indeterminate
color="primary"
<progress-information
:type="'deletion'"
:current-size="current_deletion_size"
:total-size="current_deletion_total_size"
:current-path="current_deletion_path"
:status="current_deletion_status"
/>
</div>
<div v-else-if="system_folder_manager.inProgress" class="pa-4">
<progress-information
:type="'download'"
:operation="'Downloading system log files...'"
:current-size="system_folder_manager.downloadedBytes"
:total-size="system_folder_manager.totalBytes"
:download-speed="system_folder_manager.downloadSpeed"
/>
<div class="mt-2">
<div class="text-subtitle-2">
Deleting: {{ current_deletion_path }}
</div>
<div class="text-caption">
Size: {{ formatSize(current_deletion_size / 1024) }}
</div>
<div class="text-caption">
Total: {{ formatSize(current_deletion_total_size / 1024) }}
</div>
<div class="text-caption">
Status: {{ current_deletion_status }}
</div>
</div>
</div>
</v-expand-transition>

Expand All @@ -116,6 +115,7 @@
<v-btn
v-tooltip="'Download logs from MAVLink'"
class="ma-2"
:disabled="mavlink_folder_manager.inProgress || operation_in_progress"
@click="download_mavlink_log_files"
>
<v-icon left>
Expand All @@ -127,7 +127,7 @@
<v-btn
v-tooltip="'Frees up space on the SD card deleting MAVLink logs'"
class="ma-2"
:disabled="disable_remove_mavlink"
:disabled="disable_remove_mavlink || mavlink_folder_manager.inProgress"
@click="remove_mavlink_log_files"
>
<v-icon left>
Expand All @@ -137,6 +137,18 @@
</v-btn>
</v-card-actions>

<v-expand-transition>
<div v-if="mavlink_folder_manager.inProgress" class="pa-4">
<progress-information
:type="'download'"
:operation="'Downloading MAVLink log files...'"
:current-size="mavlink_folder_manager.downloadedBytes"
:total-size="mavlink_folder_manager.totalBytes"
:download-speed="mavlink_folder_manager.downloadSpeed"
/>
</div>
</v-expand-transition>

<v-divider />

<v-card-title class="align-center">
Expand Down Expand Up @@ -175,12 +187,13 @@
<script lang="ts">
import Vue from 'vue'

import ProgressInformation from '@/components/common/ProgressInformation.vue'
import SpinningLogo from '@/components/common/SpinningLogo.vue'
import filebrowser from '@/libs/filebrowser'
import Notifier from '@/libs/notifier'
import bag from '@/store/bag'
import { commander_service } from '@/types/frontend_services'
import back_axios from '@/utils/api'
import { FolderManager } from '@/utils/folder_manager'
import { prettifySize } from '@/utils/helper_functions'
import { parseStreamingResponse } from '@/utils/streaming'

Expand All @@ -192,6 +205,7 @@ export default Vue.extend({
name: 'SettingsMenu',
components: {
SpinningLogo,
ProgressInformation,
},
data() {
return {
Expand All @@ -210,6 +224,8 @@ export default Vue.extend({
current_deletion_size: 0,
current_deletion_total_size: 0,
current_deletion_status: '',
system_folder_manager: new FolderManager(),
mavlink_folder_manager: new FolderManager(),
}
},
computed: {
Expand Down Expand Up @@ -241,12 +257,10 @@ export default Vue.extend({
return prettifySize(bytes)
},
async download_service_log_files(): Promise<void> {
const folder = await filebrowser.fetchFolder('system_logs')
await filebrowser.downloadFolder(folder)
await this.system_folder_manager.downloadFolder('system_logs')
},
async download_mavlink_log_files(): Promise<void> {
const folder = await filebrowser.fetchFolder('ardupilot_logs/logs')
await filebrowser.downloadFolder(folder)
await this.mavlink_folder_manager.downloadFolder('ardupilot_logs/logs')
},
async get_log_folder_size(): Promise<void> {
this.prepare_operation('Checking system log size...')
Expand Down
86 changes: 86 additions & 0 deletions core/frontend/src/components/common/ProgressInformation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<v-container>
<v-progress-linear
indeterminate
color="primary"
/>
<div v-if="type === 'download'" class="mt-2">
<div class="text-subtitle-2">
{{ operation }}
</div>
<div class="text-caption">
Size: {{ formatSize(currentSize / 1024) }}
</div>
<div class="text-caption">
Total: {{ formatSize(totalSize / 1024) }}
</div>
<div class="text-caption">
Speed: {{ formatSize(downloadSpeed / 1024) }}/s
</div>
</div>
<div v-else-if="type === 'deletion'" class="mt-2">
<div class="text-subtitle-2">
Deleting: {{ currentPath }}
</div>
<div class="text-caption">
Size: {{ formatSize(currentSize / 1024) }}
</div>
<div class="text-caption">
Total: {{ formatSize(totalSize / 1024) }}
</div>
<div class="text-caption">
Status: {{ status }}
</div>
</div>
</v-container>
</template>

<script lang="ts">
import Vue from 'vue'

import { prettifySize } from '@/utils/helper_functions'

export default Vue.extend({
name: 'ProgressInformation',
props: {
type: {
type: String,
required: true,
validator: (value: string) => ['download', 'deletion'].includes(value),
},
operation: {
type: String,
required: false,
default: '',
},
currentSize: {
type: Number,
required: true,
},
totalSize: {
type: Number,
required: true,
},
downloadSpeed: {
type: Number,
required: false,
default: 0,
},
currentPath: {
type: String,
required: false,
default: '',
},
status: {
type: String,
required: false,
default: '',
},
},
methods: {
formatSize(kb_bytes: number): string {
return prettifySize(kb_bytes)
},
},
})
</script>
38 changes: 36 additions & 2 deletions core/frontend/src/libs/filebrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Notifier from '@/libs/notifier'
import { FilebrowserCredentials, FilebrowserFile, FilebrowserFolder } from '@/types/filebrowser'
import { filebrowser_service } from '@/types/frontend_services'
import back_axios from '@/utils/api'
import streamSaver from 'streamsaver'

const notifier = new Notifier(filebrowser_service)

Expand Down Expand Up @@ -168,10 +169,43 @@ class Filebrowser {
/* Download folder */
/**
* @param folder - FilebrowserFolder
* @param progressHandler - The progress handler for the download
* */
async downloadFolder(folder: FilebrowserFolder): Promise<void> {
async downloadFolder(
folder: FilebrowserFolder,
progressHandler: (event: any) => void,
): Promise<void> {
const url = `${filebrowser_url}/raw/${folder.path}/?algo=zip&auth=${await this.filebrowserToken()}`
window.open(url)

const response = await fetch(url, {
headers: { Authorization: `Bearer ${await this.filebrowserToken()}` },
})
if (!response.ok || !response.body) throw new Error('Failed to download folder');

const contentLength = Number(response.headers.get('content-length') ?? 0)
const fileStream = streamSaver.createWriteStream(`${folder.name}.zip`, {
size: contentLength || undefined,
})
const writer = fileStream.getWriter()
const reader = response.body.getReader()

let downloaded = 0
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
downloaded += value.length
progressHandler({ bytes: value.byteLength, loaded: downloaded, total: contentLength })
await writer.write(value)
}

await writer.close()
notifier.pushSuccess('DOWNLOAD_COMPLETE', `Folder ${folder.path} downloaded successfully`)
} catch (err) {
await writer.abort(err)
notifier.pushError('DOWNLOAD_FAIL', `Could not download folder ${folder.path}`)
throw err
}
}
}

Expand Down
38 changes: 38 additions & 0 deletions core/frontend/src/utils/folder_manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import filebrowser from "@/libs/filebrowser";

export class FolderManager {
public downloadedBytes: number = 0;
public totalBytes: number = 0;
public startTime: number = 0;
public downloadSpeed: number = 0;

public inProgress: boolean = false;

async downloadFolder(
logs: string,
): Promise<void> {
const folder = await filebrowser.fetchFolder(logs);
this.inProgress = true;
await filebrowser.downloadFolder(folder, (event) => {
if (this.startTime === 0) this.startTime = Date.now()
const current_time = Date.now()
const elapsed = (current_time - this.startTime) / 1000
this.startTime = current_time

this.downloadedBytes = event.bytes
this.totalBytes = event.loaded
this.downloadSpeed = event.bytes / elapsed
})
.finally(() => {
this.resetDownloadVariables()
this.inProgress = false;
})
}

resetDownloadVariables(): void {
this.downloadedBytes = 0;
this.totalBytes = 0;
this.startTime = 0;
this.downloadSpeed = 0;
}
}