feat: 逻辑分析仪深度可用户输入自定义数字
This commit is contained in:
parent
51b39cee07
commit
5c87204ef6
|
@ -259,7 +259,8 @@ public class LogicAnalyzerController : ControllerBase
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (capture_length < 0 || capture_length > 2048*32)
|
//DDR深度为 32'h01000000 - 32'h0FFFFFFF
|
||||||
|
if (capture_length < 0 || capture_length > 0x10000000 - 0x01000000)
|
||||||
return BadRequest("采样深度设置错误");
|
return BadRequest("采样深度设置错误");
|
||||||
if (pre_capture_length < 0 || pre_capture_length >= capture_length)
|
if (pre_capture_length < 0 || pre_capture_length >= capture_length)
|
||||||
return BadRequest("预采样深度必须小于捕获深度");
|
return BadRequest("预采样深度必须小于捕获深度");
|
||||||
|
|
|
@ -70,7 +70,7 @@ static class AnalyzerAddr
|
||||||
public const UInt32 DMA1_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012;
|
public const UInt32 DMA1_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012;
|
||||||
public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013;
|
public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013;
|
||||||
public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014;
|
public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014;
|
||||||
public const UInt32 STORE_OFFSET_ADDR = DDR_BASE + 0x0010_0000;
|
public const UInt32 STORE_OFFSET_ADDR = DDR_BASE + 0x0100_0000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 0x0100_0000 - 0x0100_03FF 只读 32位波形存储,得到的32位数据中低八位最先捕获,高八位最后捕获。<br/>
|
/// 0x0100_0000 - 0x0100_03FF 只读 32位波形存储,得到的32位数据中低八位最先捕获,高八位最后捕获。<br/>
|
||||||
|
|
|
@ -76,28 +76,12 @@ const channelDivOptions = [
|
||||||
{ value: 32, label: "32通道", description: "启用32个通道 (CH0-CH31)" },
|
{ value: 32, label: "32通道", description: "启用32个通道 (CH0-CH31)" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 捕获深度选项
|
// 捕获深度限制常量
|
||||||
const captureLengthOptions = [
|
const CAPTURE_LENGTH_MIN = 1024; // 最小捕获深度 1024
|
||||||
{ value: 256, label: "256" },
|
const CAPTURE_LENGTH_MAX = 0x10000000 - 0x01000000; // 最大捕获深度
|
||||||
{ value: 512, label: "512" },
|
|
||||||
{ value: 1024, label: "1K" },
|
|
||||||
{ value: 2048, label: "2K" },
|
|
||||||
{ value: 4096, label: "4K" },
|
|
||||||
{ value: 8192, label: "8K" },
|
|
||||||
{ value: 16384, label: "16K" },
|
|
||||||
{ value: 32768, label: "32K" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// 预捕获深度选项
|
// 预捕获深度限制常量
|
||||||
const preCaptureLengthOptions = [
|
const PRE_CAPTURE_LENGTH_MIN = 0; // 最小预捕获深度 0
|
||||||
{ value: 0, label: "0" },
|
|
||||||
{ value: 16, label: "16" },
|
|
||||||
{ value: 32, label: "32" },
|
|
||||||
{ value: 64, label: "64" },
|
|
||||||
{ value: 128, label: "128" },
|
|
||||||
{ value: 256, label: "256" },
|
|
||||||
{ value: 512, label: "512" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// 默认颜色数组
|
// 默认颜色数组
|
||||||
const defaultColors = [
|
const defaultColors = [
|
||||||
|
@ -126,8 +110,8 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
|
||||||
// 触发设置相关状态
|
// 触发设置相关状态
|
||||||
const currentGlobalMode = ref<GlobalCaptureMode>(GlobalCaptureMode.AND);
|
const currentGlobalMode = ref<GlobalCaptureMode>(GlobalCaptureMode.AND);
|
||||||
const currentChannelDiv = ref<number>(8); // 默认启用8个通道
|
const currentChannelDiv = ref<number>(8); // 默认启用8个通道
|
||||||
const captureLength = ref<number>(1024); // 捕获深度,默认1024
|
const captureLength = ref<number>(CAPTURE_LENGTH_MIN); // 捕获深度,默认为最小值
|
||||||
const preCaptureLength = ref<number>(0); // 预捕获深度,默认0
|
const preCaptureLength = ref<number>(PRE_CAPTURE_LENGTH_MIN); // 预捕获深度,默认0
|
||||||
const isApplying = ref(false);
|
const isApplying = ref(false);
|
||||||
const isCapturing = ref(false); // 添加捕获状态标识
|
const isCapturing = ref(false); // 添加捕获状态标识
|
||||||
|
|
||||||
|
@ -181,6 +165,64 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 验证捕获深度
|
||||||
|
const validateCaptureLength = (value: number): { valid: boolean; message?: string } => {
|
||||||
|
if (!Number.isInteger(value)) {
|
||||||
|
return { valid: false, message: "捕获深度必须是整数" };
|
||||||
|
}
|
||||||
|
if (value < CAPTURE_LENGTH_MIN) {
|
||||||
|
return { valid: false, message: `捕获深度不能小于 ${CAPTURE_LENGTH_MIN}` };
|
||||||
|
}
|
||||||
|
if (value > CAPTURE_LENGTH_MAX) {
|
||||||
|
return { valid: false, message: `捕获深度不能大于 ${CAPTURE_LENGTH_MAX.toLocaleString()}` };
|
||||||
|
}
|
||||||
|
return { valid: true };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 验证预捕获深度
|
||||||
|
const validatePreCaptureLength = (value: number, currentCaptureLength: number): { valid: boolean; message?: string } => {
|
||||||
|
if (!Number.isInteger(value)) {
|
||||||
|
return { valid: false, message: "预捕获深度必须是整数" };
|
||||||
|
}
|
||||||
|
if (value < PRE_CAPTURE_LENGTH_MIN) {
|
||||||
|
return { valid: false, message: `预捕获深度不能小于 ${PRE_CAPTURE_LENGTH_MIN}` };
|
||||||
|
}
|
||||||
|
if (value >= currentCaptureLength) {
|
||||||
|
return { valid: false, message: `预捕获深度不能大于等于捕获深度 (${currentCaptureLength})` };
|
||||||
|
}
|
||||||
|
return { valid: true };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置捕获深度
|
||||||
|
const setCaptureLength = (value: number) => {
|
||||||
|
const validation = validateCaptureLength(value);
|
||||||
|
if (!validation.valid) {
|
||||||
|
alert?.error(validation.message!, 3000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查预捕获深度是否仍然有效
|
||||||
|
if (preCaptureLength.value >= value) {
|
||||||
|
preCaptureLength.value = Math.max(0, value - 1);
|
||||||
|
alert?.warn(`预捕获深度已自动调整为 ${preCaptureLength.value}`, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
captureLength.value = value;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置预捕获深度
|
||||||
|
const setPreCaptureLength = (value: number) => {
|
||||||
|
const validation = validatePreCaptureLength(value, captureLength.value);
|
||||||
|
if (!validation.valid) {
|
||||||
|
alert?.error(validation.message!, 3000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
preCaptureLength.value = value;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
// 设置通道组
|
// 设置通道组
|
||||||
const setChannelDiv = (channelCount: number) => {
|
const setChannelDiv = (channelCount: number) => {
|
||||||
// 验证通道数量是否有效
|
// 验证通道数量是否有效
|
||||||
|
@ -717,8 +759,15 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
|
||||||
operators,
|
operators,
|
||||||
signalValues,
|
signalValues,
|
||||||
channelDivOptions, // 导出通道组选项
|
channelDivOptions, // 导出通道组选项
|
||||||
captureLengthOptions, // 导出捕获深度选项
|
|
||||||
preCaptureLengthOptions, // 导出预捕获深度选项
|
// 捕获深度常量和验证
|
||||||
|
CAPTURE_LENGTH_MIN,
|
||||||
|
CAPTURE_LENGTH_MAX,
|
||||||
|
PRE_CAPTURE_LENGTH_MIN,
|
||||||
|
validateCaptureLength,
|
||||||
|
validatePreCaptureLength,
|
||||||
|
setCaptureLength,
|
||||||
|
setPreCaptureLength,
|
||||||
|
|
||||||
// 触发设置方法
|
// 触发设置方法
|
||||||
setChannelDiv, // 导出设置通道组方法
|
setChannelDiv, // 导出设置通道组方法
|
||||||
|
|
|
@ -3,89 +3,182 @@
|
||||||
<!-- 通道配置 -->
|
<!-- 通道配置 -->
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<!-- 全局触发模式选择和通道组配置 -->
|
<!-- 全局触发模式选择和通道组配置 -->
|
||||||
<div class="flex flex-col lg:flex-row justify-between gap-4 my-4 mx-2">
|
<div class="flex flex-col gap-6 my-4 mx-2">
|
||||||
<!-- 左侧:全局触发模式和通道组选择 -->
|
<div class="flex flex-col lg:flex-row gap-6">
|
||||||
<div class="flex flex-col lg:flex-row gap-4">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex flex-row gap-2 items-center">
|
<label class="block text-sm font-semibold antialiased">
|
||||||
<label class="label">
|
全局触发逻辑
|
||||||
<span class="label-text text-sm">全局触发逻辑</span>
|
|
||||||
</label>
|
</label>
|
||||||
<select
|
<div class="relative w-[200px]">
|
||||||
v-model="currentGlobalMode"
|
<button
|
||||||
@change="setGlobalMode(currentGlobalMode)"
|
tabindex="0"
|
||||||
class="select select-sm select-bordered"
|
type="button"
|
||||||
|
class="flex items-center gap-4 justify-between h-max outline-none focus:outline-none bg-transparent ring-transparent border border-slate-200 transition-all duration-300 ease-in disabled:opacity-50 disabled:pointer-events-none select-none text-start text-sm rounded-md py-2 px-2.5 ring shadow-sm hover:border-slate-800 hover:ring-slate-800/10 focus:border-slate-800 focus:ring-slate-800/10 w-full"
|
||||||
|
@click="toggleGlobalModeDropdown"
|
||||||
|
:aria-expanded="showGlobalModeDropdown"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
role="combobox"
|
||||||
>
|
>
|
||||||
<option
|
<span>{{ currentGlobalModeLabel }}</span>
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="h-[1em] w-[1em] translate-x-0.5 stroke-[1.5]">
|
||||||
|
<path d="M17 8L12 3L7 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
<path d="M17 16L12 21L7 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<input readonly style="display:none" :value="currentGlobalMode" />
|
||||||
|
<!-- 下拉菜单 -->
|
||||||
|
<div v-if="showGlobalModeDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-slate-200 rounded-md shadow-lg z-50">
|
||||||
|
<div
|
||||||
v-for="mode in globalModes"
|
v-for="mode in globalModes"
|
||||||
:key="mode.value"
|
:key="mode.value"
|
||||||
:value="mode.value"
|
@click="selectGlobalMode(mode.value)"
|
||||||
|
class="px-3 py-2 text-sm text-slate-700 hover:bg-slate-100 cursor-pointer"
|
||||||
|
:class="{ 'bg-slate-100': mode.value === currentGlobalMode }"
|
||||||
>
|
>
|
||||||
{{ mode.label }} - {{ mode.description }}
|
{{ mode.label }}
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-2 items-center">
|
</div>
|
||||||
<label class="label">
|
<p class="flex items-center text-xs text-slate-400">
|
||||||
<span class="label-text text-sm">通道组</span>
|
{{ currentGlobalModeDescription }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="block text-sm font-semibold antialiased">
|
||||||
|
通道组
|
||||||
</label>
|
</label>
|
||||||
<select
|
<div class="relative w-[200px]">
|
||||||
v-model="currentChannelDiv"
|
<button
|
||||||
@change="setChannelDiv(currentChannelDiv)"
|
tabindex="0"
|
||||||
class="select select-sm select-bordered"
|
type="button"
|
||||||
|
class="flex items-center gap-4 justify-between h-max outline-none focus:outline-none bg-transparent ring-transparent border border-slate-200 transition-all duration-300 ease-in disabled:opacity-50 disabled:pointer-events-none select-none text-start text-sm rounded-md py-2 px-2.5 ring shadow-sm hover:border-slate-800 hover:ring-slate-800/10 focus:border-slate-800 focus:ring-slate-800/10 w-full"
|
||||||
|
@click="toggleChannelDivDropdown"
|
||||||
|
:aria-expanded="showChannelDivDropdown"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
role="combobox"
|
||||||
>
|
>
|
||||||
<option
|
<span>{{ currentChannelDivLabel }}</span>
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="h-[1em] w-[1em] translate-x-0.5 stroke-[1.5]">
|
||||||
|
<path d="M17 8L12 3L7 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
<path d="M17 16L12 21L7 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<input readonly style="display:none" :value="currentChannelDiv" />
|
||||||
|
<!-- 下拉菜单 -->
|
||||||
|
<div v-if="showChannelDivDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-slate-200 rounded-md shadow-lg z-50">
|
||||||
|
<div
|
||||||
v-for="option in channelDivOptions"
|
v-for="option in channelDivOptions"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
:value="option.value"
|
@click="selectChannelDiv(option.value)"
|
||||||
|
class="px-3 py-2 text-sm text-slate-700 hover:bg-slate-100 cursor-pointer"
|
||||||
|
:class="{ 'bg-slate-100': option.value === currentChannelDiv }"
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-2 items-center">
|
</div>
|
||||||
<label class="label">
|
<p class="flex items-center text-xs text-slate-400">
|
||||||
<span class="label-text text-sm">捕获深度</span>
|
{{ currentChannelDivDescription }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="block text-sm font-semibold antialiased">
|
||||||
|
捕获深度
|
||||||
</label>
|
</label>
|
||||||
<select
|
<div class="relative w-[200px]">
|
||||||
v-model="captureLength"
|
<button
|
||||||
class="select select-sm select-bordered"
|
@click="decreaseCaptureLength"
|
||||||
|
class="absolute right-9 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
|
||||||
|
type="button"
|
||||||
|
:disabled="captureLength <= CAPTURE_LENGTH_MIN"
|
||||||
>
|
>
|
||||||
<option
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
|
||||||
v-for="option in captureLengthOptions"
|
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
|
||||||
:key="option.value"
|
</svg>
|
||||||
:value="option.value"
|
|
||||||
>
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row gap-2 items-center">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text text-sm">预捕获深度</span>
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
v-model="preCaptureLength"
|
|
||||||
class="select select-sm select-bordered"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="option in preCaptureLengthOptions"
|
|
||||||
:key="option.value"
|
|
||||||
:value="option.value"
|
|
||||||
>
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧:操作按钮 -->
|
|
||||||
<div class="flex flex-row gap-2">
|
|
||||||
<button @click="resetConfiguration" class="btn btn-outline btn-sm">
|
|
||||||
重置配置
|
|
||||||
</button>
|
</button>
|
||||||
|
<input
|
||||||
|
v-model.number="captureLength"
|
||||||
|
@change="handleCaptureLengthChange"
|
||||||
|
type="number"
|
||||||
|
:min="CAPTURE_LENGTH_MIN"
|
||||||
|
:max="CAPTURE_LENGTH_MAX"
|
||||||
|
class="w-full bg-transparent placeholder:text-sm border border-slate-200 rounded-md pl-3 pr-20 py-2 transition duration-300 ease focus:outline-none focus:border-slate-400 ring ring-transparent hover:ring-slate-800/10 focus:ring-slate-800/10 hover:border-slate-800 shadow-sm focus:shadow appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
:placeholder="CAPTURE_LENGTH_MIN.toString()"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
@click="increaseCaptureLength"
|
||||||
|
class="absolute right-1 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
|
||||||
|
type="button"
|
||||||
|
:disabled="captureLength >= CAPTURE_LENGTH_MAX"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
|
||||||
|
<path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="flex items-center text-xs text-slate-400">
|
||||||
|
范围: {{ CAPTURE_LENGTH_MIN.toLocaleString() }} - {{ CAPTURE_LENGTH_MAX.toLocaleString() }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="block text-sm font-semibold antialiased">
|
||||||
|
预捕获深度
|
||||||
|
</label>
|
||||||
|
<div class="relative w-[200px]">
|
||||||
|
<button
|
||||||
|
@click="decreasePreCaptureLength"
|
||||||
|
class="absolute right-9 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
|
||||||
|
type="button"
|
||||||
|
:disabled="preCaptureLength <= PRE_CAPTURE_LENGTH_MIN"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
|
||||||
|
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
v-model.number="preCaptureLength"
|
||||||
|
@change="handlePreCaptureLengthChange"
|
||||||
|
type="number"
|
||||||
|
:min="PRE_CAPTURE_LENGTH_MIN"
|
||||||
|
:max="Math.max(0, captureLength - 1)"
|
||||||
|
class="w-full bg-transparent placeholder:text-sm border border-slate-200 rounded-md pl-3 pr-20 py-2 transition duration-300 ease focus:outline-none focus:border-slate-400 ring ring-transparent hover:ring-slate-800/10 focus:ring-slate-800/10 hover:border-slate-800 shadow-sm focus:shadow appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
:placeholder="PRE_CAPTURE_LENGTH_MIN.toString()"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
@click="increasePreCaptureLength"
|
||||||
|
class="absolute right-1 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
|
||||||
|
type="button"
|
||||||
|
:disabled="preCaptureLength >= Math.max(0, captureLength - 1)"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
|
||||||
|
<path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="flex items-center text-xs text-slate-400">
|
||||||
|
范围: {{ PRE_CAPTURE_LENGTH_MIN }} - {{ Math.max(0, captureLength - 1) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="block text-sm font-semibold antialiased">
|
||||||
|
重置配置
|
||||||
|
</label>
|
||||||
|
<div class="relative w-[200px]">
|
||||||
|
<button
|
||||||
|
@click="resetConfiguration"
|
||||||
|
class="w-10 h-10 bg-transparent text-red-600 text-sm border border-red-200 rounded-md py-2 px-2.5 transition duration-300 ease ring ring-transparent hover:ring-red-600/10 focus:ring-red-600/10 hover:border-red-600 shadow-sm focus:shadow flex items-center justify-center"
|
||||||
|
type="button"
|
||||||
|
title="重置配置"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" class="w-4 h-4">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="flex items-center text-xs text-slate-400">
|
||||||
|
恢复所有设置到默认值
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 通道列表 -->
|
<!-- 通道列表 -->
|
||||||
|
@ -177,6 +270,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, onMounted, onUnmounted } from "vue";
|
||||||
import { useRequiredInjection } from "@/utils/Common";
|
import { useRequiredInjection } from "@/utils/Common";
|
||||||
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
|
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
|
||||||
|
|
||||||
|
@ -193,10 +287,121 @@ const {
|
||||||
operators,
|
operators,
|
||||||
signalValues,
|
signalValues,
|
||||||
channelDivOptions,
|
channelDivOptions,
|
||||||
captureLengthOptions,
|
CAPTURE_LENGTH_MIN,
|
||||||
preCaptureLengthOptions,
|
CAPTURE_LENGTH_MAX,
|
||||||
|
PRE_CAPTURE_LENGTH_MIN,
|
||||||
|
validateCaptureLength,
|
||||||
|
validatePreCaptureLength,
|
||||||
|
setCaptureLength,
|
||||||
|
setPreCaptureLength,
|
||||||
setChannelDiv,
|
setChannelDiv,
|
||||||
setGlobalMode,
|
setGlobalMode,
|
||||||
resetConfiguration,
|
resetConfiguration,
|
||||||
} = useRequiredInjection(useLogicAnalyzerState);
|
} = useRequiredInjection(useLogicAnalyzerState);
|
||||||
|
|
||||||
|
// 下拉菜单状态
|
||||||
|
const showGlobalModeDropdown = ref(false);
|
||||||
|
const showChannelDivDropdown = ref(false);
|
||||||
|
|
||||||
|
// 处理捕获深度变化
|
||||||
|
const handleCaptureLengthChange = () => {
|
||||||
|
setCaptureLength(captureLength.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理预捕获深度变化
|
||||||
|
const handlePreCaptureLengthChange = () => {
|
||||||
|
setPreCaptureLength(preCaptureLength.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 增加捕获深度
|
||||||
|
const increaseCaptureLength = () => {
|
||||||
|
const newValue = Math.min(captureLength.value + 1024, CAPTURE_LENGTH_MAX);
|
||||||
|
setCaptureLength(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 减少捕获深度
|
||||||
|
const decreaseCaptureLength = () => {
|
||||||
|
const newValue = Math.max(captureLength.value - 1024, CAPTURE_LENGTH_MIN);
|
||||||
|
setCaptureLength(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 增加预捕获深度
|
||||||
|
const increasePreCaptureLength = () => {
|
||||||
|
const maxValue = Math.max(0, captureLength.value - 1);
|
||||||
|
const newValue = Math.min(preCaptureLength.value + 64, maxValue);
|
||||||
|
setPreCaptureLength(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 减少预捕获深度
|
||||||
|
const decreasePreCaptureLength = () => {
|
||||||
|
const newValue = Math.max(preCaptureLength.value - 64, PRE_CAPTURE_LENGTH_MIN);
|
||||||
|
setPreCaptureLength(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算属性:获取当前全局模式的标签
|
||||||
|
const currentGlobalModeLabel = computed(() => {
|
||||||
|
const mode = globalModes.find(m => m.value === currentGlobalMode.value);
|
||||||
|
return mode ? mode.label : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性:获取当前全局模式的描述
|
||||||
|
const currentGlobalModeDescription = computed(() => {
|
||||||
|
const mode = globalModes.find(m => m.value === currentGlobalMode.value);
|
||||||
|
return mode ? mode.description : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性:获取当前通道组的标签
|
||||||
|
const currentChannelDivLabel = computed(() => {
|
||||||
|
const option = channelDivOptions.find(opt => opt.value === currentChannelDiv.value);
|
||||||
|
return option ? option.label : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性:获取当前通道组的描述
|
||||||
|
const currentChannelDivDescription = computed(() => {
|
||||||
|
const option = channelDivOptions.find(opt => opt.value === currentChannelDiv.value);
|
||||||
|
return option ? option.description : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 全局模式下拉菜单相关函数
|
||||||
|
const toggleGlobalModeDropdown = () => {
|
||||||
|
showGlobalModeDropdown.value = !showGlobalModeDropdown.value;
|
||||||
|
if (showGlobalModeDropdown.value) {
|
||||||
|
showChannelDivDropdown.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectGlobalMode = (mode: any) => {
|
||||||
|
setGlobalMode(mode);
|
||||||
|
showGlobalModeDropdown.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 通道组下拉菜单相关函数
|
||||||
|
const toggleChannelDivDropdown = () => {
|
||||||
|
showChannelDivDropdown.value = !showChannelDivDropdown.value;
|
||||||
|
if (showChannelDivDropdown.value) {
|
||||||
|
showGlobalModeDropdown.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectChannelDiv = (value: number) => {
|
||||||
|
setChannelDiv(value);
|
||||||
|
showChannelDivDropdown.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点击其他地方关闭下拉菜单
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (!target.closest('.relative')) {
|
||||||
|
showGlobalModeDropdown.value = false;
|
||||||
|
showChannelDivDropdown.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', handleClickOutside);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('click', handleClickOutside);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -61,13 +61,6 @@
|
||||||
<Settings class="w-5 h-5" />
|
<Settings class="w-5 h-5" />
|
||||||
触发设置
|
触发设置
|
||||||
</div>
|
</div>
|
||||||
<!-- 配置摘要 -->
|
|
||||||
<div class="flex items-center gap-4 text-sm text-gray-500">
|
|
||||||
<span>{{ analyzer.enabledChannelCount.value }}/32 通道</span>
|
|
||||||
<span>捕获: {{ analyzer.captureLength.value }}</span>
|
|
||||||
<span>预捕获: {{ analyzer.preCaptureLength.value }}</span>
|
|
||||||
<span>{{ analyzer.globalModes.find(m => m.value === analyzer.currentGlobalMode.value)?.label || '未知' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<!-- 状态指示 -->
|
<!-- 状态指示 -->
|
||||||
|
|
Loading…
Reference in New Issue