Merge branch 'master' of ssh://git.swordlost.top:222/SikongJueluo/FPGA_WebLab

This commit is contained in:
alivender 2025-07-13 13:56:39 +08:00
commit f710a66c69
5 changed files with 112 additions and 67 deletions

View File

@ -110,6 +110,46 @@ public class UDPClientPool
return await Task.Run(() => { return SendAddrPack(endPoint, pkg); }); return await Task.Run(() => { return SendAddrPack(endPoint, pkg); });
} }
/// <summary>
/// 发送多个地址包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkgs">地址包集合(最多512 / 8)</param>
/// <returns>是否全部成功</returns>
public static bool SendMultiAddrPack(IPEndPoint endPoint, IEnumerable<WebProtocol.SendAddrPackage> pkgs)
{
const int maxPkgs = 512 / 8;
var pkgList = pkgs.Take(maxPkgs).ToList();
if (pkgList.Count == 0) return false;
// 合并所有包为一个buffer
int totalLen = pkgList.Sum(pkg => pkg.ToBytes().Length);
byte[] buffer = new byte[totalLen];
int offset = 0;
foreach (var pkg in pkgList)
{
var bytes = pkg.ToBytes();
Buffer.BlockCopy(bytes, 0, buffer, offset, bytes.Length);
offset += bytes.Length;
}
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var sendLen = socket.SendTo(buffer.ToArray(), endPoint);
socket.Close();
return sendLen == buffer.Length;
}
/// <summary>
/// 异步发送多个地址包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkgs">地址包集合(最多512 / 8)</param>
/// <returns>是否全部成功</returns>
public async static ValueTask<bool> SendMultiAddrPackAsync(IPEndPoint endPoint, IEnumerable<WebProtocol.SendAddrPackage> pkgs)
{
return await Task.Run(() => SendMultiAddrPack(endPoint, pkgs));
}
/// <summary> /// <summary>
/// 发送数据包 /// 发送数据包
@ -392,14 +432,14 @@ public class UDPClientPool
public static async ValueTask<Result<byte[]>> ReadAddr4BytesAsync( public static async ValueTask<Result<byte[]>> ReadAddr4BytesAsync(
IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, int timeout = 1000) IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, int timeout = 1000)
{ {
var optsList = new List<SendAddrPackOptions>(); var pkgList = new List<SendAddrPackage>();
var resultData = new List<byte>(); var resultData = new List<byte>();
// Check Msg Bus // Check Msg Bus
if (!MsgBus.IsRunning) if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!")); return new(new Exception("Message bus not working!"));
// Prepare options for each segment // Prepare packages for each segment
var max4BytesPerRead = 0x80; // 512 bytes per read var max4BytesPerRead = 0x80; // 512 bytes per read
var rest4Bytes = dataLength % max4BytesPerRead; var rest4Bytes = dataLength % max4BytesPerRead;
var readTimes = (rest4Bytes != 0) ? var readTimes = (rest4Bytes != 0) ?
@ -419,15 +459,15 @@ public class UDPClientPool
BurstLength = (byte)(currentSegmentSize - 1), BurstLength = (byte)(currentSegmentSize - 1),
Address = devAddr + (uint)(i * max4BytesPerRead) Address = devAddr + (uint)(i * max4BytesPerRead)
}; };
optsList.Add(opts); pkgList.Add(new SendAddrPackage(opts));
} }
// Send all address packages first, but keep outstanding < 512 // Send address packages in batches of 128, control outstanding
int sentCount = 0; int sentCount = 0;
var startTime = DateTime.Now; var startTime = DateTime.Now;
while (sentCount < optsList.Count) const int batchSize = 128;
while (sentCount < pkgList.Count)
{ {
// Check how many data packets have been received
var elapsed = DateTime.Now - startTime; var elapsed = DateTime.Now - startTime;
if (elapsed >= TimeSpan.FromMilliseconds(timeout)) if (elapsed >= TimeSpan.FromMilliseconds(timeout))
break; break;
@ -436,17 +476,17 @@ public class UDPClientPool
var found = await MsgBus.UDPServer.GetDataCountAsync(endPoint.Address.ToString(), taskID, timeleft); var found = await MsgBus.UDPServer.GetDataCountAsync(endPoint.Address.ToString(), taskID, timeleft);
int outstanding = sentCount - (found.HasValue ? found.Value : 0); int outstanding = sentCount - (found.HasValue ? found.Value : 0);
// If outstanding >= 512, wait for some data to be received // If outstanding >= 512 - batchSize, wait for some data to be received
if (outstanding >= 512){ if (outstanding >= 512 - batchSize)
logger.Debug($"Outstanding data packets: {outstanding}, waiting for more data to be received...");
continue; continue;
}
// Send next address package // Send next batch of address packages (up to 128)
var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(optsList[sentCount])); int batchSend = Math.Min(batchSize, pkgList.Count - sentCount);
if (!ret) return new(new Exception($"Send address package failed at segment {sentCount}!")); var batchPkgs = pkgList.Skip(sentCount).Take(batchSend);
sentCount++; var ret = await UDPClientPool.SendMultiAddrPackAsync(endPoint, batchPkgs);
if (!ret) return new(new Exception($"Send address package batch failed at segment {sentCount}!"));
sentCount += batchSend;
} }
// Wait until enough data is received or timeout // Wait until enough data is received or timeout
@ -477,10 +517,10 @@ public class UDPClientPool
for (var i = 0; i < udpDatas.Count; i++) for (var i = 0; i < udpDatas.Count; i++)
{ {
var bytes = udpDatas[i].Data; var bytes = udpDatas[i].Data;
var expectedLen = ((optsList[i].BurstLength + 1) * 4); var expectedLen = ((pkgList[i].Options.BurstLength + 1) * 4);
if ((bytes.Length - 4) != expectedLen) if (bytes.Length != expectedLen)
return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length - 4} bytes at segment {i}")); return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length} bytes at segment {i}"));
resultData.AddRange(bytes[4..]); // Skip the first 4 bytes (header) resultData.AddRange(bytes);
} }
// Validate total data length // Validate total data length

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="fixed left-1/2 top-30 z-50 -translate-x-1/2"> <div class="fixed left-1/2 top-30 z-999 -translate-x-1/2">
<transition <transition
name="alert" name="alert"
enter-active-class="alert-enter-active" enter-active-class="alert-enter-active"

View File

@ -25,13 +25,12 @@
<Plus :size="20" class="text-primary" /> <Plus :size="20" class="text-primary" />
添加元器件 添加元器件
</h3> </h3>
<label <button
for="component-drawer"
class="btn btn-ghost btn-sm btn-circle" class="btn btn-ghost btn-sm btn-circle"
@click="closeMenu" @click="closeMenu"
> >
<X :size="20" /> <X :size="20" />
</label> </button>
</div> </div>
<!-- 导航栏 --> <!-- 导航栏 -->
@ -112,8 +111,8 @@
import { ref, computed, shallowRef, onMounted } from "vue"; import { ref, computed, shallowRef, onMounted } from "vue";
import { Plus, X, Search } from "lucide-vue-next"; import { Plus, X, Search } from "lucide-vue-next";
import ItemList from "./ItemList.vue"; import ItemList from "./ItemList.vue";
import { useAlertStore } from "@/components/Alert";
import { import {
useComponentManager,
availableComponents, availableComponents,
availableVirtualDevices, availableVirtualDevices,
availableTemplates, availableTemplates,
@ -121,6 +120,7 @@ import {
type ComponentConfig, type ComponentConfig,
type VirtualDeviceConfig, type VirtualDeviceConfig,
type TemplateConfig, type TemplateConfig,
useComponentManager, // componentManager
} from "./index.ts"; } from "./index.ts";
// Props // Props
@ -130,16 +130,18 @@ interface Props {
const props = defineProps<Props>(); const props = defineProps<Props>();
const componentManager = useComponentManager(); //
//
const emit = defineEmits([ const emit = defineEmits([
"close", "close",
"add-component",
"add-template",
"update:open", "update:open",
]); ]);
// 使 componentManager
const componentManager = useComponentManager();
// 使 Alert
const alert = useAlertStore();
// //
const activeTab = ref("components"); const activeTab = ref("components");
@ -192,14 +194,19 @@ async function preloadComponentModules() {
// //
function closeMenu() { function closeMenu() {
showComponentsMenu.value = false; emit("update:open", false);
emit("close"); emit("close");
} }
// // - 使 componentManager
async function addComponent( async function addComponent(
componentTemplate: ComponentConfig | VirtualDeviceConfig, componentTemplate: ComponentConfig | VirtualDeviceConfig,
) { ) {
if (!componentManager) {
console.error("ComponentManager not available");
return;
}
// //
const moduleRef = await loadComponentModule(componentTemplate.type); const moduleRef = await loadComponentModule(componentTemplate.type);
let defaultProps: Record<string, any> = {}; let defaultProps: Record<string, any> = {};
@ -220,19 +227,32 @@ async function addComponent(
console.log(`Failed to load module for ${componentTemplate.type}`); console.log(`Failed to load module for ${componentTemplate.type}`);
} }
// try {
emit("add-component", { // 使 componentManager
type: componentTemplate.type, await componentManager.addComponent({
name: componentTemplate.name, type: componentTemplate.type,
props: defaultProps, name: componentTemplate.name,
}); props: defaultProps,
});
// //
closeMenu(); alert?.success(`成功添加元器件: ${componentTemplate.name}`);
//
closeMenu();
} catch (error) {
console.error("添加元器件失败:", error);
alert?.error("添加元器件失败,请检查控制台错误信息");
}
} }
// // - 使 componentManager
async function addTemplate(template: TemplateConfig) { async function addTemplate(template: TemplateConfig) {
if (!componentManager) {
console.error("ComponentManager not available");
return;
}
try { try {
// JSON // JSON
const response = await fetch(template.path); const response = await fetch(template.path);
@ -243,19 +263,27 @@ async function addTemplate(template: TemplateConfig) {
const templateData = await response.json(); const templateData = await response.json();
console.log("加载模板:", templateData); console.log("加载模板:", templateData);
// // 使 componentManager
emit("add-template", { const result = await componentManager.addTemplate({
id: template.id, id: template.id,
name: template.name, name: template.name,
template: templateData, template: templateData,
capsPage: template.capsPage,
}); });
if (result) {
// 使 Alert
if (result.success) {
alert?.success(result.message);
} else {
alert?.error(result.message);
}
}
// //
closeMenu(); closeMenu();
} catch (error) { } catch (error) {
console.error("加载模板出错:", error); console.error("加载模板出错:", error);
alert("无法加载模板文件,请检查控制台错误信息"); alert?.error("无法加载模板文件,请检查控制台错误信息");
} }
} }

View File

@ -92,8 +92,6 @@
<ComponentSelector <ComponentSelector
:open="showComponentsMenu" :open="showComponentsMenu"
@update:open="showComponentsMenu = $event" @update:open="showComponentsMenu = $event"
@add-component="handleAddComponent"
@add-template="handleAddTemplate"
@close="showComponentsMenu = false" @close="showComponentsMenu = false"
/> />
@ -219,27 +217,6 @@ function openComponentsMenu() {
showComponentsMenu.value = true; showComponentsMenu.value = true;
} }
// ComponentSelector
async function handleAddComponent(componentData: {
type: string;
name: string;
props: Record<string, any>;
}) {
await componentManager.addComponent(componentData);
}
//
async function handleAddTemplate(templateData: {
id: string;
name: string;
template: any;
}) {
const result = await componentManager.addTemplate(templateData);
if (result) {
alert?.show(result.message, result.success ? "success" : "error");
}
}
// //
function handleDiagramUpdated(data: DiagramData) { function handleDiagramUpdated(data: DiagramData) {
console.log("Diagram data updated:", data); console.log("Diagram data updated:", data);

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
v-if="open" v-if="open"
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50" class="fixed inset-0 z-50 flex items-center justify-center bg-opacity-30 backdrop-blur-sm shadow-2xl"
> >
<div class="bg-base-100 rounded-lg shadow-xl max-w-md w-full mx-4"> <div class="bg-base-100 rounded-lg shadow-xl max-w-md w-full mx-4">
<div class="p-6"> <div class="p-6">