Merge branch 'master' of ssh://git.swordlost.top:222/SikongJueluo/FPGA_WebLab
This commit is contained in:
commit
f710a66c69
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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("无法加载模板文件,请检查控制台错误信息");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
Loading…
Reference in New Issue