feat: add remote update function

This commit is contained in:
2025-05-14 21:22:20 +08:00
parent 48ae3b5975
commit 00ce79fa7b
6 changed files with 520 additions and 283 deletions

View File

@@ -1,11 +1,20 @@
<template>
<div class="bg-base-200 min-h-screen p-6">
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
<div class="flex flex-row align-middle">
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
<button class="btn btn-ghost self-end" @click="
() => {
isEditMode = !isEditMode;
}
">
编辑
</button>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4">IP 地址列表</h2>
<div class="overflow-x-auto">
<table class="table w-full">
<!-- 表头 -->
@@ -14,89 +23,86 @@
<th>IP 地址</th>
<th>版本号</th>
<th>默认启动位流</th>
<th>黄金位流</th>
<th>应用位流1</th>
<th>应用位流2</th>
<th>应用位流3</th>
<th class="w-80">黄金位流</th>
<th class="w-80">应用位流1</th>
<th class="w-80">应用位流2</th>
<th class="w-80">应用位流3</th>
<th>操作</th>
</tr>
</thead>
<!-- 表格内容 -->
<tbody>
<tr class="hover">
<td class="font-medium">192.168.1.100</td>
<td class="font-medium">
<input v-if="isEditMode" type="text" placeholder="Type here" class="input" />
<span v-else>{{ devAddr }}</span>
</td>
<td>v1.2.3</td>
<td>
<select class="select select-bordered w-full max-w-xs">
<select class="select select-bordered w-full max-w-xs" v-model="selectBitstream">
<option selected>黄金位流</option>
<option>应用位流1</option>
<option>应用位流2</option>
<option>应用位流3</option>
</select>
</td>
<!-- 黄金位流上传区 -->
<!-- 黄金位流上传区 -->
<td>
<div class="flex flex-col items-center gap-2">
<label class="btn btn-outline btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<input type="file" class="hidden" @change="handleFileChange($event, goldBitstreamFile)" />
</label>
<span class="text-xs text-primary truncate max-w-32" v-if="goldBitstreamFile">{{ goldBitstreamFile }}</span>
<input type="file" class="file-input file-input-primary"
@change="handleFileChange($event, goldBitstreamFile)" />
</div>
</td>
<!-- 应用位流1上传区 -->
<!-- 应用位流1上传区 -->
<td>
<div class="flex flex-col items-center gap-2">
<label class="btn btn-outline btn-secondary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream1File)" />
</label>
<span class="text-xs text-secondary truncate max-w-32" v-if="appBitstream1File">{{ appBitstream1File }}</span>
<input type="file" class="file-input file-input-secondary"
@change="handleFileChange($event, appBitstream1File)" />
</div>
</td>
<!-- 应用位流2上传区 -->
<!-- 应用位流2上传区 -->
<td>
<div class="flex flex-col items-center gap-2">
<label class="btn btn-outline btn-accent">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream2File)" />
</label>
<span class="text-xs text-accent truncate max-w-32" v-if="appBitstream2File">{{ appBitstream2File }}</span>
<input type="file" class="file-input file-input-accent"
@change="handleFileChange($event, appBitstream2File)" />
</div>
</td>
<!-- 应用位流3上传区 -->
<!-- 应用位流3上传区 -->
<td>
<div class="flex flex-col items-center gap-2">
<label class="btn btn-outline btn-info">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream3File)" />
</label>
<span class="text-xs text-info truncate max-w-32" v-if="appBitstream3File">{{ appBitstream3File }}</span>
<input type="file" class="file-input file-input-info"
@change="handleFileChange($event, appBitstream3File)" />
</div>
</td>
<!-- 操作按钮 -->
<td class="flex gap-2">
<button class="btn btn-sm btn-warning">固化</button>
<button class="btn btn-sm btn-success">切换并热启动</button>
<button class="btn grow btn-warning" @click="
uploadAndDownloadBitstreams(
devAddr,
goldBitstreamFile,
appBitstream1File,
appBitstream2File,
appBitstream3File,
)
">
固化
</button>
<button class="btn grow btn-success" @click="
hotresetBitstream(devAddr, getSelectedBitstreamNum())
">
切换并热启动
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="mt-6 bg-base-300 p-4 rounded-lg">
<p class="text-sm opacity-80">
<span class="font-semibold text-warning">提示</span>
<span class="font-semibold text-error">提示</span>
请谨慎操作FPGA固化和热启动功能确保上传的位流文件无误以避免设备损坏
</p>
</div>
@@ -106,21 +112,122 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { isNull, isUndefined } from "lodash";
import { ref } from "vue";
import { RemoteUpdaterClient } from "@/APIClient";
import { useDialogStore } from "@/stores/dialog";
import { Common } from "@/Common";
const dialog = useDialogStore();
// 编辑状态
const isEditMode = ref(false);
// 选择热切换的比特流
const selectBitstream = ref("黄金位流");
// 存储上传文件的信息
const goldBitstreamFile = ref<string>('');
const appBitstream1File = ref<string>('');
const appBitstream2File = ref<string>('');
const appBitstream3File = ref<string>('');
const goldBitstreamFile = ref<File>();
const appBitstream1File = ref<File>();
const appBitstream2File = ref<File>();
const appBitstream3File = ref<File>();
// 远程升级相关参数
const devAddr = ref("192.168.1.100");
const devPort = 1234;
const remoteUpdater = new RemoteUpdaterClient();
// 处理文件上传
function handleFileChange(event: Event, fileRef: any) {
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (file) {
fileRef.value = file.name;
const file = target.files?.[0]; // 获取选中的第一个文件
if (!file) {
return;
}
if (!isUndefined(fileRef)) {
fileRef.value = file;
}
}
function getSelectedBitstreamNum(): number {
if (selectBitstream.value == "黄金位流") {
return 0;
} else if (selectBitstream.value == "应用位流1") {
return 1;
} else if (selectBitstream.value == "应用位流2") {
return 2;
} else if (selectBitstream.value == "应用位流3") {
return 3;
}
return 0;
}
async function uploadAndDownloadBitstreams(
devAddr: string,
goldBitstream?: File,
appBitstream1?: File,
appBitstream2?: File,
appBitstream3?: File,
) {
let cnt = 0;
if (isUndefined(goldBitstream)) cnt++;
if (isUndefined(appBitstream1)) cnt++;
if (isUndefined(appBitstream2)) cnt++;
if (isUndefined(appBitstream3)) cnt++;
if ((cnt = 0)) {
dialog.error("未选择比特流");
}
try {
{
const ret = await remoteUpdater.uploadBitstreams(
devAddr,
Common.toFileParameterOrNull(goldBitstream),
Common.toFileParameterOrNull(appBitstream1),
Common.toFileParameterOrNull(appBitstream2),
Common.toFileParameterOrNull(appBitstream3),
);
if (ret) {
dialog.warn("上传比特流出错");
} else {
dialog.info("上传比特流成功");
}
}
{
const ret = await remoteUpdater.downloadMultiBitstreams(
devAddr,
devPort,
getSelectedBitstreamNum(),
);
if (ret != cnt) {
dialog.warn("固化比特流出错");
} else {
dialog.info("固化比特流成功");
}
}
} catch (e) {
dialog.error("比特流上传错误");
console.error(e);
}
}
async function hotresetBitstream(devAddr: string, bitstreamNum: number) {
try {
const ret = await remoteUpdater.hotResetBitstream(
devAddr,
devPort,
bitstreamNum,
);
if (ret) {
dialog.info("切换比特流成功");
} else {
dialog.error("切换比特流失败");
}
} catch (e) {
dialog.error("切换比特流失败");
console.error(e);
}
}
</script>