Merge branch 'master' of ssh://git.swordlost.top:222/SikongJueluo/FPGA_WebLab
This commit is contained in:
		@@ -110,6 +110,46 @@ public class UDPClientPool
 | 
			
		||||
        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>
 | 
			
		||||
    /// 发送数据包
 | 
			
		||||
@@ -392,14 +432,14 @@ public class UDPClientPool
 | 
			
		||||
    public static async ValueTask<Result<byte[]>> ReadAddr4BytesAsync(
 | 
			
		||||
        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>();
 | 
			
		||||
 | 
			
		||||
        // Check Msg Bus
 | 
			
		||||
        if (!MsgBus.IsRunning)
 | 
			
		||||
            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 rest4Bytes = dataLength % max4BytesPerRead;
 | 
			
		||||
        var readTimes = (rest4Bytes != 0) ?
 | 
			
		||||
@@ -419,15 +459,15 @@ public class UDPClientPool
 | 
			
		||||
                BurstLength = (byte)(currentSegmentSize - 1),
 | 
			
		||||
                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;
 | 
			
		||||
        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;
 | 
			
		||||
            if (elapsed >= TimeSpan.FromMilliseconds(timeout))
 | 
			
		||||
                break;
 | 
			
		||||
@@ -436,17 +476,17 @@ public class UDPClientPool
 | 
			
		||||
            var found = await MsgBus.UDPServer.GetDataCountAsync(endPoint.Address.ToString(), taskID, timeleft);
 | 
			
		||||
            int outstanding = sentCount - (found.HasValue ? found.Value : 0);
 | 
			
		||||
 | 
			
		||||
            // If outstanding >= 512, wait for some data to be received
 | 
			
		||||
            if (outstanding >= 512){
 | 
			
		||||
                logger.Debug($"Outstanding data packets: {outstanding}, waiting for more data to be received...");
 | 
			
		||||
            // If outstanding >= 512 - batchSize, wait for some data to be received
 | 
			
		||||
            if (outstanding >= 512 - batchSize)
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // Send next address package
 | 
			
		||||
            var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(optsList[sentCount]));
 | 
			
		||||
            if (!ret) return new(new Exception($"Send address package failed at segment {sentCount}!"));
 | 
			
		||||
            sentCount++;
 | 
			
		||||
            // Send next batch of address packages (up to 128)
 | 
			
		||||
            int batchSend = Math.Min(batchSize, pkgList.Count - sentCount);
 | 
			
		||||
            var batchPkgs = pkgList.Skip(sentCount).Take(batchSend);
 | 
			
		||||
            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
 | 
			
		||||
@@ -477,10 +517,10 @@ public class UDPClientPool
 | 
			
		||||
        for (var i = 0; i < udpDatas.Count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            var bytes = udpDatas[i].Data;
 | 
			
		||||
            var expectedLen = ((optsList[i].BurstLength + 1) * 4);
 | 
			
		||||
            if ((bytes.Length - 4) != expectedLen)
 | 
			
		||||
                return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length - 4} bytes at segment {i}"));
 | 
			
		||||
            resultData.AddRange(bytes[4..]); // Skip the first 4 bytes (header)
 | 
			
		||||
            var expectedLen = ((pkgList[i].Options.BurstLength + 1) * 4);
 | 
			
		||||
            if (bytes.Length != expectedLen)
 | 
			
		||||
                return new(new Exception($"Expected {expectedLen} bytes but received {bytes.Length} bytes at segment {i}"));
 | 
			
		||||
            resultData.AddRange(bytes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Validate total data length
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<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
 | 
			
		||||
      name="alert"
 | 
			
		||||
      enter-active-class="alert-enter-active"
 | 
			
		||||
 
 | 
			
		||||
@@ -25,13 +25,12 @@
 | 
			
		||||
              <Plus :size="20" class="text-primary" />
 | 
			
		||||
              添加元器件
 | 
			
		||||
            </h3>
 | 
			
		||||
            <label
 | 
			
		||||
              for="component-drawer"
 | 
			
		||||
            <button
 | 
			
		||||
              class="btn btn-ghost btn-sm btn-circle"
 | 
			
		||||
              @click="closeMenu"
 | 
			
		||||
            >
 | 
			
		||||
              <X :size="20" />
 | 
			
		||||
            </label>
 | 
			
		||||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- 导航栏 -->
 | 
			
		||||
@@ -112,8 +111,8 @@
 | 
			
		||||
import { ref, computed, shallowRef, onMounted } from "vue";
 | 
			
		||||
import { Plus, X, Search } from "lucide-vue-next";
 | 
			
		||||
import ItemList from "./ItemList.vue";
 | 
			
		||||
import { useAlertStore } from "@/components/Alert";
 | 
			
		||||
import {
 | 
			
		||||
  useComponentManager,
 | 
			
		||||
  availableComponents,
 | 
			
		||||
  availableVirtualDevices,
 | 
			
		||||
  availableTemplates,
 | 
			
		||||
@@ -121,6 +120,7 @@ import {
 | 
			
		||||
  type ComponentConfig,
 | 
			
		||||
  type VirtualDeviceConfig,
 | 
			
		||||
  type TemplateConfig,
 | 
			
		||||
  useComponentManager, // 导入 componentManager
 | 
			
		||||
} from "./index.ts";
 | 
			
		||||
 | 
			
		||||
// Props 定义
 | 
			
		||||
@@ -130,16 +130,18 @@ interface Props {
 | 
			
		||||
 | 
			
		||||
const props = defineProps<Props>();
 | 
			
		||||
 | 
			
		||||
const componentManager = useComponentManager();
 | 
			
		||||
 | 
			
		||||
// 定义组件发出的事件
 | 
			
		||||
// 定义组件发出的事件(保留部分必要的事件)
 | 
			
		||||
const emit = defineEmits([
 | 
			
		||||
  "close",
 | 
			
		||||
  "add-component",
 | 
			
		||||
  "add-template",
 | 
			
		||||
  "update:open",
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 使用 componentManager
 | 
			
		||||
const componentManager = useComponentManager();
 | 
			
		||||
 | 
			
		||||
// 使用 Alert 系统
 | 
			
		||||
const alert = useAlertStore();
 | 
			
		||||
 | 
			
		||||
// 当前激活的选项卡
 | 
			
		||||
const activeTab = ref("components");
 | 
			
		||||
 | 
			
		||||
@@ -192,14 +194,19 @@ async function preloadComponentModules() {
 | 
			
		||||
 | 
			
		||||
// 关闭菜单
 | 
			
		||||
function closeMenu() {
 | 
			
		||||
  showComponentsMenu.value = false;
 | 
			
		||||
  emit("update:open", false);
 | 
			
		||||
  emit("close");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 添加新元器件
 | 
			
		||||
// 添加新元器件 - 使用 componentManager
 | 
			
		||||
async function addComponent(
 | 
			
		||||
  componentTemplate: ComponentConfig | VirtualDeviceConfig,
 | 
			
		||||
) {
 | 
			
		||||
  if (!componentManager) {
 | 
			
		||||
    console.error("ComponentManager not available");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 先加载组件模块
 | 
			
		||||
  const moduleRef = await loadComponentModule(componentTemplate.type);
 | 
			
		||||
  let defaultProps: Record<string, any> = {};
 | 
			
		||||
@@ -220,19 +227,32 @@ async function addComponent(
 | 
			
		||||
    console.log(`Failed to load module for ${componentTemplate.type}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 发送添加组件事件给父组件
 | 
			
		||||
  emit("add-component", {
 | 
			
		||||
    type: componentTemplate.type,
 | 
			
		||||
    name: componentTemplate.name,
 | 
			
		||||
    props: defaultProps,
 | 
			
		||||
  });
 | 
			
		||||
  try {
 | 
			
		||||
    // 使用 componentManager 添加组件
 | 
			
		||||
    await componentManager.addComponent({
 | 
			
		||||
      type: componentTemplate.type,
 | 
			
		||||
      name: componentTemplate.name,
 | 
			
		||||
      props: defaultProps,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  // 关闭菜单
 | 
			
		||||
  closeMenu();
 | 
			
		||||
    // 显示成功消息
 | 
			
		||||
    alert?.success(`成功添加元器件: ${componentTemplate.name}`);
 | 
			
		||||
 | 
			
		||||
    // 关闭菜单
 | 
			
		||||
    closeMenu();
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error("添加元器件失败:", error);
 | 
			
		||||
    alert?.error("添加元器件失败,请检查控制台错误信息");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 添加模板
 | 
			
		||||
// 添加模板 - 使用 componentManager
 | 
			
		||||
async function addTemplate(template: TemplateConfig) {
 | 
			
		||||
  if (!componentManager) {
 | 
			
		||||
    console.error("ComponentManager not available");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    // 加载模板JSON文件
 | 
			
		||||
    const response = await fetch(template.path);
 | 
			
		||||
@@ -243,19 +263,27 @@ async function addTemplate(template: TemplateConfig) {
 | 
			
		||||
    const templateData = await response.json();
 | 
			
		||||
    console.log("加载模板:", templateData);
 | 
			
		||||
 | 
			
		||||
    // 发出事件,将模板数据传递给父组件
 | 
			
		||||
    emit("add-template", {
 | 
			
		||||
    // 使用 componentManager 添加模板
 | 
			
		||||
    const result = await componentManager.addTemplate({
 | 
			
		||||
      id: template.id,
 | 
			
		||||
      name: template.name,
 | 
			
		||||
      template: templateData,
 | 
			
		||||
      capsPage: template.capsPage,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (result) {
 | 
			
		||||
      // 使用 Alert 显示结果消息
 | 
			
		||||
      if (result.success) {
 | 
			
		||||
        alert?.success(result.message);
 | 
			
		||||
      } else {
 | 
			
		||||
        alert?.error(result.message);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 关闭菜单
 | 
			
		||||
    closeMenu();
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error("加载模板出错:", error);
 | 
			
		||||
    alert("无法加载模板文件,请检查控制台错误信息");
 | 
			
		||||
    alert?.error("无法加载模板文件,请检查控制台错误信息");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -92,8 +92,6 @@
 | 
			
		||||
    <ComponentSelector
 | 
			
		||||
      :open="showComponentsMenu"
 | 
			
		||||
      @update:open="showComponentsMenu = $event"
 | 
			
		||||
      @add-component="handleAddComponent"
 | 
			
		||||
      @add-template="handleAddTemplate"
 | 
			
		||||
      @close="showComponentsMenu = false"
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
@@ -219,27 +217,6 @@ function openComponentsMenu() {
 | 
			
		||||
  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) {
 | 
			
		||||
  console.log("Diagram data updated:", data);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    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="p-6">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user