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>

View File

@@ -1,77 +1,88 @@
<template> <div class="bg-base-200 min-h-screen">
<template>
<div class="bg-base-200 min-h-screen">
<main class="hero min-h-screen bg-base-200">
<div class="hero-content flex-col lg:flex-row-reverse gap-8 lg:gap-12 py-10 px-4">
<!-- 图片容器 -->
<div class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
<img src="https://placehold.co/600x400" class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
<div
class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
<img src="https://placehold.co/600x400"
class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
<!-- 这里使用relative定位限制覆盖层只在图片容器内 -->
<div class="absolute inset-0 bg-primary opacity-10 rounded-2xl pointer-events-none"></div>
</div>
<!-- 内容容器 -->
<!-- 内容容器 -->
<div class="content-container max-w-md lg:max-w-2xl transform transition-all duration-500 ease-in-out">
<h1 class="text-4xl md:text-5xl font-bold mb-3 relative group">
<span class="relative z-10 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Welcome to
Welcome to
</span>
<span class="text-base-content">FPGA Web Lab!</span>
<span class="absolute bottom-0 left-0 w-0 h-1 bg-primary transition-all duration-500 ease-in-out group-hover:w-3/4"></span>
<span
class="absolute bottom-0 left-0 w-0 h-1 bg-primary transition-all duration-500 ease-in-out group-hover:w-3/4"></span>
</h1>
<p class="py-6 text-lg opacity-80 leading-relaxed">
Prototype and simulate electronic circuits in your browser with our modern, intuitive interface. Create, test, and share your FPGA designs seamlessly.
Prototype and simulate electronic circuits in your browser with our
modern, intuitive interface. Create, test, and share your FPGA
designs seamlessly.
</p>
<div class="flex flex-wrap gap-4 actions-container">
<router-link to="/project" class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<div class="flex flex-wrap gap-4 actions-container">
<router-link to="/project"
class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
</svg>
进入工程界面
</router-link>
<router-link to="/login" class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<router-link to="/login"
class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
登录
</router-link>
<router-link to="/user" class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<router-link to="/user"
class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
用户中心
</router-link>
<router-link to="/test" class="btn btn-info text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<router-link to="/test"
class="btn btn-info text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
<polyline points="17 21 17 13 7 13 7 21"></polyline>
<polyline points="7 3 7 8 15 8"></polyline>
</svg>
测试功能
</router-link>
<router-link to="/test/jtag" class="btn btn-warning text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
JTAG测试
</router-link>
<router-link to="/admin" class="btn btn-error text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z"></path>
<router-link to="/admin"
class="btn btn-error text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z">
</path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
管理控制台
</router-link>
</div>
<div class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md">
<div
class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md">
<p class="text-sm">
<span class="font-semibold text-primary">提示</span> 您可以在工程界面中创建编辑和测试您的FPGA项目使用我们简洁直观的界面轻松进行硬件设计
<span class="font-semibold text-primary">提示</span>
您可以在工程界面中创建编辑和测试您的FPGA项目使用我们简洁直观的界面轻松进行硬件设计
</p>
</div>
</div>