feat: 新增重置控制端的功能;前端可以显示提交记录 fix: 修复资源数据库sha256计算问题;修复资源数据库无法上传的问题
This commit is contained in:
parent
c8444d1d4e
commit
ec84eeeaa4
|
@ -17,4 +17,13 @@ public class String
|
||||||
return new string(charArray);
|
return new string(charArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string BytesToString(byte[] bytes, string separator = "")
|
||||||
|
{
|
||||||
|
return BitConverter.ToString(bytes).Replace("-", separator.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string BytesToBase64(byte[] bytes)
|
||||||
|
{
|
||||||
|
return Convert.ToBase64String(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,6 @@ public class ResourceController : ControllerBase
|
||||||
if (string.IsNullOrWhiteSpace(request.ResourceType) || file == null)
|
if (string.IsNullOrWhiteSpace(request.ResourceType) || file == null)
|
||||||
return BadRequest("资源类型、资源用途和文件不能为空");
|
return BadRequest("资源类型、资源用途和文件不能为空");
|
||||||
|
|
||||||
// 验证资源用途
|
|
||||||
if (request.ResourcePurpose != ResourcePurpose.Template && request.ResourcePurpose != ResourcePurpose.User)
|
|
||||||
return BadRequest($"无效的资源用途: {request.ResourcePurpose}");
|
|
||||||
|
|
||||||
// 模板资源需要管理员权限
|
// 模板资源需要管理员权限
|
||||||
if (request.ResourcePurpose == ResourcePurpose.Template && !User.IsInRole("Admin"))
|
if (request.ResourcePurpose == ResourcePurpose.Template && !User.IsInRole("Admin"))
|
||||||
return Forbid("只有管理员可以添加模板资源");
|
return Forbid("只有管理员可以添加模板资源");
|
||||||
|
|
|
@ -135,7 +135,8 @@ public class ResourceManager
|
||||||
|
|
||||||
// 验证资源用途
|
// 验证资源用途
|
||||||
if (resourcePurpose != ResourcePurpose.Template &&
|
if (resourcePurpose != ResourcePurpose.Template &&
|
||||||
resourcePurpose != ResourcePurpose.User)
|
resourcePurpose != ResourcePurpose.User &&
|
||||||
|
resourcePurpose != ResourcePurpose.Homework)
|
||||||
{
|
{
|
||||||
logger.Error($"无效的资源用途: {resourcePurpose}");
|
logger.Error($"无效的资源用途: {resourcePurpose}");
|
||||||
return new(new Exception($"无效的资源用途: {resourcePurpose}"));
|
return new(new Exception($"无效的资源用途: {resourcePurpose}"));
|
||||||
|
@ -149,7 +150,8 @@ public class ResourceManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算数据的SHA256
|
// 计算数据的SHA256
|
||||||
var sha256 = SHA256.HashData(data).ToString();
|
var sha256Bytes = SHA256.HashData(data);
|
||||||
|
var sha256 = Common.String.BytesToBase64(sha256Bytes);
|
||||||
if (string.IsNullOrEmpty(sha256))
|
if (string.IsNullOrEmpty(sha256))
|
||||||
{
|
{
|
||||||
logger.Error($"SHA256计算失败");
|
logger.Error($"SHA256计算失败");
|
||||||
|
|
|
@ -429,7 +429,7 @@ public class Jtag
|
||||||
if (!MsgBus.IsRunning)
|
if (!MsgBus.IsRunning)
|
||||||
return new(new Exception("Message Bus not Working!"));
|
return new(new Exception("Message Bus not Working!"));
|
||||||
|
|
||||||
var retPack = await MsgBus.UDPServer.WaitForDataAsync(address, 0, port);
|
var retPack = await MsgBus.UDPServer.WaitForDataAsync(this.ep, 0, this.timeout);
|
||||||
if (!retPack.IsSuccessful || !retPack.Value.IsSuccessful)
|
if (!retPack.IsSuccessful || !retPack.Value.IsSuccessful)
|
||||||
return new(new Exception("Send address package failed"));
|
return new(new Exception("Send address package failed"));
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,21 @@ public sealed class UDPClientPool
|
||||||
return await Task.Run(() => { return SendDataPack(endPoint, pkg); });
|
return await Task.Run(() => { return SendDataPack(endPoint, pkg); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送重置信号
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="endPoint">IP端点(IP地址与端口)</param>
|
||||||
|
/// <returns>是否成功</returns>
|
||||||
|
public async static ValueTask<bool> SendResetSignal(IPEndPoint endPoint)
|
||||||
|
{
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
return SendAddrPack(
|
||||||
|
endPoint,
|
||||||
|
new WebProtocol.SendAddrPackage(BurstType.FixedBurst, 0, true, 0, 0xF0F0F0F0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读取设备地址数据
|
/// 读取设备地址数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -219,8 +234,7 @@ public sealed class UDPClientPool
|
||||||
if (!MsgBus.IsRunning)
|
if (!MsgBus.IsRunning)
|
||||||
return new(new Exception("Message Bus not Working!"));
|
return new(new Exception("Message Bus not Working!"));
|
||||||
|
|
||||||
var retPack = await MsgBus.UDPServer.WaitForDataAsync(
|
var retPack = await MsgBus.UDPServer.WaitForDataAsync(endPoint, taskID, timeout);
|
||||||
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
|
|
||||||
if (!retPack.IsSuccessful) return new(retPack.Error);
|
if (!retPack.IsSuccessful) return new(retPack.Error);
|
||||||
else if (!retPack.Value.IsSuccessful)
|
else if (!retPack.Value.IsSuccessful)
|
||||||
return new(new Exception("Send address package failed"));
|
return new(new Exception("Send address package failed"));
|
||||||
|
@ -389,8 +403,7 @@ public sealed class UDPClientPool
|
||||||
if (!ret) return new(new Exception($"Send address package failed at segment {i}!"));
|
if (!ret) return new(new Exception($"Send address package failed at segment {i}!"));
|
||||||
|
|
||||||
// Wait for data response
|
// Wait for data response
|
||||||
var retPack = await MsgBus.UDPServer.WaitForDataAsync(
|
var retPack = await MsgBus.UDPServer.WaitForDataAsync(endPoint, taskID, timeout);
|
||||||
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
|
|
||||||
if (!retPack.IsSuccessful) return new(retPack.Error);
|
if (!retPack.IsSuccessful) return new(retPack.Error);
|
||||||
|
|
||||||
if (!retPack.Value.IsSuccessful)
|
if (!retPack.Value.IsSuccessful)
|
||||||
|
@ -606,8 +619,7 @@ public sealed class UDPClientPool
|
||||||
return new(new Exception("Message bus not working!"));
|
return new(new Exception("Message bus not working!"));
|
||||||
|
|
||||||
// Wait for Write Ack
|
// Wait for Write Ack
|
||||||
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(
|
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint, taskID, timeout);
|
||||||
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
|
|
||||||
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
|
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
|
||||||
_progressTracker.AdvanceProgress(progressId, 10);
|
_progressTracker.AdvanceProgress(progressId, 10);
|
||||||
|
|
||||||
|
@ -671,7 +683,7 @@ public sealed class UDPClientPool
|
||||||
if (!ret) return new(new Exception("Send data package failed!"));
|
if (!ret) return new(new Exception("Send data package failed!"));
|
||||||
|
|
||||||
// Wait for Write Ack
|
// Wait for Write Ack
|
||||||
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
|
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint, taskID, timeout);
|
||||||
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
|
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
|
||||||
|
|
||||||
if (!udpWriteAck.Value.IsSuccessful)
|
if (!udpWriteAck.Value.IsSuccessful)
|
||||||
|
|
|
@ -194,14 +194,15 @@ public class UDPServer
|
||||||
|
|
||||||
var startTime = DateTime.Now;
|
var startTime = DateTime.Now;
|
||||||
var isTimeout = false;
|
var isTimeout = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
while (!isTimeout)
|
while (!isTimeout)
|
||||||
{
|
{
|
||||||
var elapsed = DateTime.Now - startTime;
|
var elapsed = DateTime.Now - startTime;
|
||||||
isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout);
|
isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout);
|
||||||
if (isTimeout) break;
|
if (isTimeout) break;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (await udpDataLock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(timeout)))
|
using (await udpDataLock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(timeout)))
|
||||||
{
|
{
|
||||||
if (udpData.TryGetValue(key, out var sortedList) && sortedList.Count > 0)
|
if (udpData.TryGetValue(key, out var sortedList) && sortedList.Count > 0)
|
||||||
|
@ -214,6 +215,11 @@ public class UDPServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data is null)
|
||||||
|
throw new TimeoutException("Get nothing even after time out");
|
||||||
|
else return new(data.DeepClone());
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
logger.Trace("Get nothing even after time out");
|
logger.Trace("Get nothing even after time out");
|
||||||
|
@ -221,18 +227,6 @@ public class UDPServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (data is null)
|
|
||||||
{
|
|
||||||
logger.Trace("Get nothing even after time out");
|
|
||||||
return new(null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new(data.DeepClone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步寻找目标发送的所有内容,并清空队列
|
/// 异步寻找目标发送的所有内容,并清空队列
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -367,17 +361,22 @@ public class UDPServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步等待写响应
|
/// 异步等待写响应
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">IP地址</param>
|
/// <param name="endPoint">IP地址及端口</param>
|
||||||
/// <param name="taskID">[TODO:parameter]</param>
|
/// <param name="taskID">[TODO:parameter]</param>
|
||||||
/// <param name="port">UDP 端口</param>
|
|
||||||
/// <param name="timeout">超时时间范围</param>
|
/// <param name="timeout">超时时间范围</param>
|
||||||
/// <returns>接收响应包</returns>
|
/// <returns>接收响应包</returns>
|
||||||
public async ValueTask<Result<WebProtocol.RecvRespPackage>> WaitForAckAsync
|
public async ValueTask<Result<WebProtocol.RecvRespPackage>> WaitForAckAsync
|
||||||
(string address, int taskID, int port = -1, int timeout = 1000)
|
(IPEndPoint endPoint, int taskID, int timeout = 1000)
|
||||||
{
|
{
|
||||||
|
var address = endPoint.Address.ToString();
|
||||||
|
var port = endPoint.Port;
|
||||||
|
|
||||||
var data = await FindDataAsync(address, taskID, timeout);
|
var data = await FindDataAsync(address, taskID, timeout);
|
||||||
if (!data.HasValue)
|
if (!data.HasValue)
|
||||||
|
{
|
||||||
|
await UDPClientPool.SendResetSignal(endPoint);
|
||||||
return new(new Exception("Get None even after time out!"));
|
return new(new Exception("Get None even after time out!"));
|
||||||
|
}
|
||||||
|
|
||||||
var recvData = data.Value;
|
var recvData = data.Value;
|
||||||
if (recvData.Address != address || (port > 0 && recvData.Port != port))
|
if (recvData.Address != address || (port > 0 && recvData.Port != port))
|
||||||
|
@ -393,17 +392,22 @@ public class UDPServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步等待数据
|
/// 异步等待数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">IP地址</param>
|
/// <param name="endPoint">IP地址</param>
|
||||||
/// <param name="taskID">[TODO:parameter]</param>
|
/// <param name="taskID">任务ID</param>
|
||||||
/// <param name="port">UDP 端口</param>
|
|
||||||
/// <param name="timeout">超时时间范围</param>
|
/// <param name="timeout">超时时间范围</param>
|
||||||
/// <returns>接收数据包</returns>
|
/// <returns>接收数据包</returns>
|
||||||
public async ValueTask<Result<RecvDataPackage>> WaitForDataAsync
|
public async ValueTask<Result<RecvDataPackage>> WaitForDataAsync
|
||||||
(string address, int taskID, int port = -1, int timeout = 1000)
|
(IPEndPoint endPoint, int taskID, int timeout = 1000)
|
||||||
{
|
{
|
||||||
|
var address = endPoint.Address.ToString();
|
||||||
|
var port = endPoint.Port;
|
||||||
|
|
||||||
var data = await FindDataAsync(address, taskID, timeout);
|
var data = await FindDataAsync(address, taskID, timeout);
|
||||||
if (!data.HasValue)
|
if (!data.HasValue)
|
||||||
|
{
|
||||||
|
await UDPClientPool.SendResetSignal(endPoint);
|
||||||
return new(new Exception("Get None even after time out!"));
|
return new(new Exception("Get None even after time out!"));
|
||||||
|
}
|
||||||
|
|
||||||
var recvData = data.Value;
|
var recvData = data.Value;
|
||||||
if (recvData.Address != address || (port >= 0 && recvData.Port != port))
|
if (recvData.Address != address || (port >= 0 && recvData.Port != port))
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useRequiredInjection } from "@/utils/Common";
|
||||||
import { templateRef } from "@vueuse/core";
|
import { templateRef } from "@vueuse/core";
|
||||||
import { File, UploadIcon, XIcon } from "lucide-vue-next";
|
import { File, UploadIcon, XIcon } from "lucide-vue-next";
|
||||||
import { isNull } from "mathjs";
|
import { isNull } from "mathjs";
|
||||||
import { useSlots } from "vue";
|
import { useSlots } from "vue";
|
||||||
|
import { useAlertStore } from "./Alert";
|
||||||
|
|
||||||
|
const alert = useRequiredInjection(useAlertStore);
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -17,6 +20,10 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
closeAfterUpload: false,
|
closeAfterUpload: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
finishedUpload: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
const inputFiles = defineModel<File[] | null>("inputFiles", { default: null });
|
const inputFiles = defineModel<File[] | null>("inputFiles", { default: null });
|
||||||
const isShowModal = defineModel<boolean>("isShowModal", { default: false });
|
const isShowModal = defineModel<boolean>("isShowModal", { default: false });
|
||||||
|
|
||||||
|
@ -42,6 +49,8 @@ function handleUpload() {
|
||||||
if (!inputFiles.value) return;
|
if (!inputFiles.value) return;
|
||||||
props.callback(inputFiles.value);
|
props.callback(inputFiles.value);
|
||||||
if (props.closeAfterUpload) close();
|
if (props.closeAfterUpload) close();
|
||||||
|
alert.info("上传成功");
|
||||||
|
emits("finishedUpload");
|
||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
|
|
|
@ -161,17 +161,17 @@
|
||||||
>
|
>
|
||||||
暂无提交记录
|
暂无提交记录
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="overflow-y-auto">
|
<div v-else class="overflow-y-auto fit-content max-h-50">
|
||||||
<ul class="steps steps-vertical">
|
<ul class="steps steps-vertical">
|
||||||
<li
|
<li
|
||||||
class="step"
|
class="step"
|
||||||
:class="{ 'step-primary': _idx === 1 }"
|
:class="{
|
||||||
v-for="(commit, _idx) in commitsList.slice(0, 3)"
|
'step-primary': _idx === commitsList.length - 1,
|
||||||
|
}"
|
||||||
|
v-for="(commit, _idx) in commitsList"
|
||||||
>
|
>
|
||||||
{{ commit.id }} ---
|
{{ commit.uploadTime.toTimeString() }}
|
||||||
{{ commit.uploadTime.toDateString() }}
|
|
||||||
</li>
|
</li>
|
||||||
<li class="step" v-if="commitsList.length > 3">......</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -214,7 +214,9 @@
|
||||||
ref="uploadModal"
|
ref="uploadModal"
|
||||||
class="fixed z-auto"
|
class="fixed z-auto"
|
||||||
:auto-upload="true"
|
:auto-upload="true"
|
||||||
|
:close-after-upload="true"
|
||||||
:callback="submitExam"
|
:callback="submitExam"
|
||||||
|
@finished-upload="handleSubmitFinished"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -234,11 +236,12 @@ import { useRouter } from "vue-router";
|
||||||
import { formatDate } from "@/utils/Common";
|
import { formatDate } from "@/utils/Common";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { watch } from "vue";
|
import { watch } from "vue";
|
||||||
import { isNull, isUndefined } from "lodash";
|
import { delay, isNull, isUndefined } from "lodash";
|
||||||
import { Download, GitGraph, Smile, Upload, XIcon } from "lucide-vue-next";
|
import { Download, GitGraph, Smile, Upload, XIcon } from "lucide-vue-next";
|
||||||
import UploadModal from "@/components/UploadModal.vue";
|
import UploadModal from "@/components/UploadModal.vue";
|
||||||
import { templateRef } from "@vueuse/core";
|
import { templateRef } from "@vueuse/core";
|
||||||
import { toFileParameter } from "@/utils/Common";
|
import { toFileParameter } from "@/utils/Common";
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
const alertStore = useRequiredInjection(useAlertStore);
|
const alertStore = useRequiredInjection(useAlertStore);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -259,11 +262,23 @@ async function updateCommits() {
|
||||||
const list = await client.getCommitsByExamId(props.selectedExam.id);
|
const list = await client.getCommitsByExamId(props.selectedExam.id);
|
||||||
commitsList.value = list;
|
commitsList.value = list;
|
||||||
}
|
}
|
||||||
watch(() => props.selectedExam, updateCommits);
|
watch(
|
||||||
|
() => show.value,
|
||||||
|
() => {
|
||||||
|
if (show.value) {
|
||||||
|
updateCommits();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
onMounted(() => {
|
||||||
|
if (show.value) {
|
||||||
|
updateCommits();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Download resources
|
// Download resources
|
||||||
const downloadingResources = ref(false);
|
const downloadingResources = ref(false);
|
||||||
const downloadResources = async () => {
|
async function downloadResources() {
|
||||||
if (!props.selectedExam || downloadingResources.value) return;
|
if (!props.selectedExam || downloadingResources.value) return;
|
||||||
|
|
||||||
downloadingResources.value = true;
|
downloadingResources.value = true;
|
||||||
|
@ -311,10 +326,10 @@ const downloadResources = async () => {
|
||||||
} finally {
|
} finally {
|
||||||
downloadingResources.value = false;
|
downloadingResources.value = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 开始实验
|
// 开始实验
|
||||||
const startExam = () => {
|
function startExam() {
|
||||||
if (props.selectedExam) {
|
if (props.selectedExam) {
|
||||||
// 跳转到项目页面,传递实验ID
|
// 跳转到项目页面,传递实验ID
|
||||||
console.log("开始实验:", props.selectedExam.id);
|
console.log("开始实验:", props.selectedExam.id);
|
||||||
|
@ -323,9 +338,9 @@ const startExam = () => {
|
||||||
query: { examId: props.selectedExam.id },
|
query: { examId: props.selectedExam.id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const submitExam = (files: File[]) => {
|
function submitExam(files: File[]) {
|
||||||
try {
|
try {
|
||||||
const client = AuthManager.createClient(ResourceClient);
|
const client = AuthManager.createClient(ResourceClient);
|
||||||
|
|
||||||
|
@ -341,11 +356,17 @@ const submitExam = (files: File[]) => {
|
||||||
alertStore.error(err.message || "上传资料失败");
|
alertStore.error(err.message || "上传资料失败");
|
||||||
console.error("上传资料失败:", err);
|
console.error("上传资料失败:", err);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeExamDetail = () => {
|
async function handleSubmitFinished() {
|
||||||
|
delay(async () => {
|
||||||
|
await updateCommits();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeExamDetail() {
|
||||||
show.value = false;
|
show.value = false;
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped></style>
|
<style lang="postcss" scoped></style>
|
||||||
|
|
Loading…
Reference in New Issue