feat: 完成debugger前后端交互
This commit is contained in:
		@@ -58,15 +58,20 @@ public class DebuggerController : ControllerBase
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> SetMode([FromBody] CaptureMode mode)
 | 
			
		||||
    public async Task<IActionResult> SetMode(int channelNum, CaptureMode mode)
 | 
			
		||||
    {
 | 
			
		||||
        if (channelNum > 0x0F)
 | 
			
		||||
        {
 | 
			
		||||
            return BadRequest($"最多只能建立16个通道");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var debugger = GetDebugger();
 | 
			
		||||
            if (debugger == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            var result = await debugger.SetMode(mode);
 | 
			
		||||
            var result = await debugger.SetMode((byte)channelNum, mode);
 | 
			
		||||
            if (!result.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置捕获模式失败: {result.Error}");
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,12 @@ class DebuggerCmd
 | 
			
		||||
    /// 启动触发器命令
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 Start = 0xFFFF_FFFF;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 刷新命令
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 Fresh = 0x0000_0000;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 清除信号标志命令
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -119,12 +119,18 @@ public class DebuggerClient
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置信号捕获模式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="channelNum">要设置的通道</param>
 | 
			
		||||
    /// <param name="mode">要设置的捕获模式</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,失败返回错误信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetMode(CaptureMode mode)
 | 
			
		||||
    public async ValueTask<Result<bool>> SetMode(byte channelNum, CaptureMode mode)
 | 
			
		||||
    {
 | 
			
		||||
        if (channelNum > 0x0F)
 | 
			
		||||
        {
 | 
			
		||||
            return new(new ArgumentException($"Channel Num can't be over 16, but receive num: {channelNum}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        UInt32 data = ((UInt32)mode);
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, DebuggerAddr.Mode, data, this.timeout);
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, DebuggerAddr.Mode + channelNum, data, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set mode: {ret.Error}");
 | 
			
		||||
 
 | 
			
		||||
@@ -1649,18 +1649,24 @@ export class DebuggerClient {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置捕获模式
 | 
			
		||||
     * @param channelNum (optional) 
 | 
			
		||||
     * @param mode (optional) 
 | 
			
		||||
     */
 | 
			
		||||
    setMode(mode: string): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/Debugger/SetMode";
 | 
			
		||||
    setMode(channelNum: number | undefined, mode: CaptureMode | undefined): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/Debugger/SetMode?";
 | 
			
		||||
        if (channelNum === null)
 | 
			
		||||
            throw new Error("The parameter 'channelNum' cannot be null.");
 | 
			
		||||
        else if (channelNum !== undefined)
 | 
			
		||||
            url_ += "channelNum=" + encodeURIComponent("" + channelNum) + "&";
 | 
			
		||||
        if (mode === null)
 | 
			
		||||
            throw new Error("The parameter 'mode' cannot be null.");
 | 
			
		||||
        else if (mode !== undefined)
 | 
			
		||||
            url_ += "mode=" + encodeURIComponent("" + mode) + "&";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        const content_ = JSON.stringify(mode);
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            body: content_,
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Content-Type": "application/json",
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@@ -5508,6 +5514,15 @@ export interface IArgumentException extends ISystemException {
 | 
			
		||||
    paramName?: string | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 信号捕获模式枚举 */
 | 
			
		||||
export enum CaptureMode {
 | 
			
		||||
    None = 0,
 | 
			
		||||
    Logic0 = 1,
 | 
			
		||||
    Logic1 = 2,
 | 
			
		||||
    Rise = 3,
 | 
			
		||||
    Fall = 4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 逻辑分析仪运行状态枚举 */
 | 
			
		||||
export enum CaptureStatus {
 | 
			
		||||
    None = 0,
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import {
 | 
			
		||||
  LogicAnalyzerClient,
 | 
			
		||||
  NetConfigClient,
 | 
			
		||||
  OscilloscopeApiClient,
 | 
			
		||||
  DebuggerClient,
 | 
			
		||||
} from "@/APIClient";
 | 
			
		||||
 | 
			
		||||
// 支持的客户端类型联合类型
 | 
			
		||||
@@ -28,7 +29,8 @@ type SupportedClient =
 | 
			
		||||
  | LogicAnalyzerClient
 | 
			
		||||
  | UDPClient
 | 
			
		||||
  | NetConfigClient
 | 
			
		||||
  | OscilloscopeApiClient;
 | 
			
		||||
  | OscilloscopeApiClient
 | 
			
		||||
  | DebuggerClient;
 | 
			
		||||
 | 
			
		||||
export class AuthManager {
 | 
			
		||||
  // 存储token到localStorage
 | 
			
		||||
@@ -168,6 +170,10 @@ export class AuthManager {
 | 
			
		||||
  public static createAuthenticatedOscilloscopeApiClient(): OscilloscopeApiClient {
 | 
			
		||||
    return AuthManager.createAuthenticatedClient(OscilloscopeApiClient);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  public static createAuthenticatedDebuggerClient(): DebuggerClient {
 | 
			
		||||
    return AuthManager.createAuthenticatedClient(DebuggerClient);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 登录函数
 | 
			
		||||
  public static async login(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,321 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="card m-5 bg-base-200 shadow-2xl">
 | 
			
		||||
      <WaveformDisplay />
 | 
			
		||||
      <div class="card-body">
 | 
			
		||||
        <h2 class="card-title flex justify-between items-center">
 | 
			
		||||
          <div class="flex items-center gap-2">
 | 
			
		||||
            <Zap class="w-5 h-5" />
 | 
			
		||||
            调试器波形捕获
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex items-center gap-2">
 | 
			
		||||
            <button
 | 
			
		||||
              class="btn btn-sm btn-error"
 | 
			
		||||
              @click="handleDeleteData"
 | 
			
		||||
              :disabled="!captureData"
 | 
			
		||||
            >
 | 
			
		||||
              清空
 | 
			
		||||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </h2>
 | 
			
		||||
        <WaveformDisplay :data="captureData">
 | 
			
		||||
          <button
 | 
			
		||||
            class="group relative px-8 py-3 bg-gradient-to-r text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 ease-in-out focus:outline-none focus:ring-4 active:scale-95"
 | 
			
		||||
            :class="{
 | 
			
		||||
              'from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 focus:ring-blue-300':
 | 
			
		||||
                !isCapturing,
 | 
			
		||||
              'from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 focus:ring-red-300':
 | 
			
		||||
                isCapturing,
 | 
			
		||||
            }"
 | 
			
		||||
            @click="isCapturing ? stopCapture() : startCapture()"
 | 
			
		||||
          >
 | 
			
		||||
            <span class="flex items-center gap-2">
 | 
			
		||||
              <template v-if="isCapturing">
 | 
			
		||||
                <Square class="w-5 h-5" />
 | 
			
		||||
                停止捕获
 | 
			
		||||
              </template>
 | 
			
		||||
              <template v-else>
 | 
			
		||||
                <Play class="w-5 h-5" />
 | 
			
		||||
                开始捕获
 | 
			
		||||
              </template>
 | 
			
		||||
            </span>
 | 
			
		||||
          </button>
 | 
			
		||||
        </WaveformDisplay>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- Debugger 通道配置 -->
 | 
			
		||||
    <div class="card m-5 bg-base-200 shadow-2xl">
 | 
			
		||||
      <div class="card-body">
 | 
			
		||||
        <h2 class="card-title mb-4">调试器通道配置</h2>
 | 
			
		||||
        <div class="overflow-x-auto flex flex-col gap-10">
 | 
			
		||||
          <!-- 通道状态概览 -->
 | 
			
		||||
          <div
 | 
			
		||||
            class="stats stats-horizontal bg-base-100 shadow flex justify-between"
 | 
			
		||||
          >
 | 
			
		||||
            <div class="stat">
 | 
			
		||||
              <div class="stat-title">总通道数</div>
 | 
			
		||||
              <div class="stat-value text-primary">16</div>
 | 
			
		||||
              <div class="stat-desc">逻辑分析仪通道</div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="stat">
 | 
			
		||||
              <div class="stat-title">启用通道</div>
 | 
			
		||||
              <div class="stat-value text-success">
 | 
			
		||||
                {{ channels.filter((ch) => ch.visible).length }}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="stat-desc">当前激活通道</div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="stat">
 | 
			
		||||
              <div class="stat-title">采样率</div>
 | 
			
		||||
              <div class="stat-value text-info">5MHz</div>
 | 
			
		||||
              <div class="stat-desc">最大采样频率</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- 表格 -->
 | 
			
		||||
          <div class="space-y-2">
 | 
			
		||||
            <!-- 表头 -->
 | 
			
		||||
            <div
 | 
			
		||||
              class="grid grid-cols-6 justify-items-center gap-2 p-2 bg-base-300 rounded-lg text-sm font-medium"
 | 
			
		||||
            >
 | 
			
		||||
              <span>名称</span>
 | 
			
		||||
              <span>显示</span>
 | 
			
		||||
              <span>颜色</span>
 | 
			
		||||
              <span>触发模式</span>
 | 
			
		||||
              <span>数据位数</span>
 | 
			
		||||
              <span>操作</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 通道列表 -->
 | 
			
		||||
            <div
 | 
			
		||||
              v-for="(ch, idx) in channels"
 | 
			
		||||
              :key="idx"
 | 
			
		||||
              class="grid grid-cols-6 justify-items-center gap-2 p-2 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
 | 
			
		||||
            >
 | 
			
		||||
              <input
 | 
			
		||||
                v-model="ch.name"
 | 
			
		||||
                class="input input-bordered w-full"
 | 
			
		||||
                :placeholder="`通道${idx + 1}`"
 | 
			
		||||
              />
 | 
			
		||||
              <input
 | 
			
		||||
                type="checkbox"
 | 
			
		||||
                v-model="ch.visible"
 | 
			
		||||
                class="toggle toggle-primary"
 | 
			
		||||
              />
 | 
			
		||||
              <input
 | 
			
		||||
                type="color"
 | 
			
		||||
                v-model="ch.color"
 | 
			
		||||
                class="w-8 h-8 rounded border-2 border-base-300 cursor-pointer"
 | 
			
		||||
              />
 | 
			
		||||
              <select
 | 
			
		||||
                v-model="ch.trigger"
 | 
			
		||||
                class="select select-bordered w-full"
 | 
			
		||||
              >
 | 
			
		||||
                <option
 | 
			
		||||
                  v-for="mode in triggerModes"
 | 
			
		||||
                  :key="mode.value"
 | 
			
		||||
                  :value="mode.value"
 | 
			
		||||
                >
 | 
			
		||||
                  {{ mode.label }}
 | 
			
		||||
                </option>
 | 
			
		||||
              </select>
 | 
			
		||||
              <input
 | 
			
		||||
                type="number"
 | 
			
		||||
                min="1"
 | 
			
		||||
                max="32"
 | 
			
		||||
                v-model.number="ch.width"
 | 
			
		||||
                class="input input-bordered w-full"
 | 
			
		||||
              />
 | 
			
		||||
              <button class="btn btn-error" @click="removeChannel(idx)">
 | 
			
		||||
                删除
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 添加通道按钮 -->
 | 
			
		||||
            <div class="flex justify-center mt-2">
 | 
			
		||||
              <button class="btn btn-primary w-100" @click="addChannel">
 | 
			
		||||
                添加通道
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import WaveformDisplay from '@/components/WaveformDisplay/WaveformDisplay.vue';
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { CaptureMode } from "@/APIClient";
 | 
			
		||||
import { useAlertStore } from "@/components/Alert";
 | 
			
		||||
import type { LogicDataType } from "@/components/WaveformDisplay";
 | 
			
		||||
import WaveformDisplay from "@/components/WaveformDisplay/WaveformDisplay.vue";
 | 
			
		||||
import { AuthManager } from "@/utils/AuthManager";
 | 
			
		||||
import { useRequiredInjection } from "@/utils/Common";
 | 
			
		||||
import { useLocalStorage } from "@vueuse/core";
 | 
			
		||||
import { Play, Square, Zap } from "lucide-vue-next";
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
 | 
			
		||||
interface DebugChannel {
 | 
			
		||||
  name: string;
 | 
			
		||||
  visible: boolean;
 | 
			
		||||
  color: string;
 | 
			
		||||
  trigger: CaptureMode;
 | 
			
		||||
  width: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const triggerModes = [
 | 
			
		||||
  { value: CaptureMode.None, label: "x (无关)" },
 | 
			
		||||
  { value: CaptureMode.Logic0, label: "0 (低电平)" },
 | 
			
		||||
  { value: CaptureMode.Logic1, label: "1 (高电平)" },
 | 
			
		||||
  { value: CaptureMode.Rise, label: "↑ (上升沿)" },
 | 
			
		||||
  { value: CaptureMode.Fall, label: "↓ (下降沿)" },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const channels = useLocalStorage<DebugChannel[]>("debugger-channels", []);
 | 
			
		||||
const captureData = ref<LogicDataType>();
 | 
			
		||||
const alert = useRequiredInjection(useAlertStore);
 | 
			
		||||
 | 
			
		||||
const isCapturing = ref(false);
 | 
			
		||||
 | 
			
		||||
async function startCapture() {
 | 
			
		||||
  if (channels.value.length === 0) {
 | 
			
		||||
    alert.error("请至少添加一个通道");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isCapturing.value = true;
 | 
			
		||||
  const client = AuthManager.createAuthenticatedDebuggerClient();
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < channels.value.length; i++) {
 | 
			
		||||
    const channel = channels.value[i];
 | 
			
		||||
    if (!channel.visible) continue;
 | 
			
		||||
    if (!channel.name) {
 | 
			
		||||
      alert.error(`通道 ${i + 1} 名称不能为空`);
 | 
			
		||||
      isCapturing.value = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (channel.width < 1 || channel.width > 32) {
 | 
			
		||||
      alert.error(`通道 ${i + 1} 数据位数必须在1到32之间`);
 | 
			
		||||
      isCapturing.value = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
      let ret = await client.setMode(i, channel.trigger);
 | 
			
		||||
      if (!ret) {
 | 
			
		||||
        alert.error(`设置通道 ${i + 1} 触发模式失败`);
 | 
			
		||||
        isCapturing.value = false;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
      alert.error(`设置通道 ${i + 1} 触发模式失败: ${error.message}`);
 | 
			
		||||
      isCapturing.value = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    let ret = await client.startTrigger();
 | 
			
		||||
    if (!ret) {
 | 
			
		||||
      alert.error("开始捕获失败,请检查连接");
 | 
			
		||||
      isCapturing.value = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while ((await client.readFlag()) !== 1 && isCapturing.value) {
 | 
			
		||||
      await new Promise((resolve) => setTimeout(resolve, 500));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!isCapturing.value) {
 | 
			
		||||
      alert.info("捕获已停止");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const base64Data = await client.readData(0);
 | 
			
		||||
    const binaryString = atob(base64Data);
 | 
			
		||||
    const bytes = new Uint8Array(binaryString.length);
 | 
			
		||||
    for (let i = 0; i < binaryString.length; i++) {
 | 
			
		||||
      bytes[i] = binaryString.charCodeAt(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 数据分割
 | 
			
		||||
    // 1. 统计每个通道的位宽
 | 
			
		||||
    const enabledChannels = channels.value.filter((ch) => ch.visible);
 | 
			
		||||
    const widths = enabledChannels.map((ch) => ch.width);
 | 
			
		||||
    const totalBitsPerSample = widths.reduce((a, b) => a + b, 0);
 | 
			
		||||
 | 
			
		||||
    // 2. 计算总采样点数
 | 
			
		||||
    const totalBits = bytes.length * 8;
 | 
			
		||||
    const sampleCount = Math.floor(totalBits / totalBitsPerSample);
 | 
			
		||||
 | 
			
		||||
    // 3. 逐采样点解析
 | 
			
		||||
    let bitOffset = 0;
 | 
			
		||||
    const channelValues: number[][] = enabledChannels.map(() => []);
 | 
			
		||||
    for (let sampleIdx = 0; sampleIdx < sampleCount; sampleIdx++) {
 | 
			
		||||
      for (let chIdx = 0; chIdx < enabledChannels.length; chIdx++) {
 | 
			
		||||
        const width = widths[chIdx];
 | 
			
		||||
        let value = 0;
 | 
			
		||||
        for (let w = 0; w < width; w++) {
 | 
			
		||||
          const absBit = bitOffset + w;
 | 
			
		||||
          const byteIdx = Math.floor(absBit / 8);
 | 
			
		||||
          const bitInByte = absBit % 8;
 | 
			
		||||
          const bit = (bytes[byteIdx] >> (7 - bitInByte)) & 1;
 | 
			
		||||
          value = (value << 1) | bit;
 | 
			
		||||
        }
 | 
			
		||||
        channelValues[chIdx].push(value);
 | 
			
		||||
        bitOffset += width;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 4. 构造LogicDataType
 | 
			
		||||
    const x: number[] = [];
 | 
			
		||||
    const xUnit = "us"; // 5MHz -> 0.2us/point, 但WaveformDisplay支持ns/ms/us/s,选us
 | 
			
		||||
    for (let i = 0; i < sampleCount; i++) {
 | 
			
		||||
      x.push(i * 0.2); // 0.2us per sample
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const y = enabledChannels.map((ch, idx) => ({
 | 
			
		||||
      enabled: true,
 | 
			
		||||
      type: ch.width === 1 ? ("logic" as const) : ("number" as const),
 | 
			
		||||
      name: ch.name,
 | 
			
		||||
      color: ch.color,
 | 
			
		||||
      value: channelValues[idx],
 | 
			
		||||
      base: ch.width === 1 ? ("bin" as const) : ("hex" as const),
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    captureData.value = {
 | 
			
		||||
      x: x,
 | 
			
		||||
      y: y,
 | 
			
		||||
      xUnit: "us",
 | 
			
		||||
    };
 | 
			
		||||
  } catch (error: any) {
 | 
			
		||||
    alert.error(`开始捕获失败: ${error.message}`);
 | 
			
		||||
    isCapturing.value = false;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function stopCapture() {
 | 
			
		||||
  isCapturing.value = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleDeleteData() {
 | 
			
		||||
  captureData.value = undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addChannel() {
 | 
			
		||||
  if (channels.value.length >= 16) {
 | 
			
		||||
    alert.error("最多只能添加16个通道");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  channels.value.push({
 | 
			
		||||
    name: `CH${channels.value.length + 1}`,
 | 
			
		||||
    visible: true,
 | 
			
		||||
    color: "#00bcd4",
 | 
			
		||||
    trigger: CaptureMode.None,
 | 
			
		||||
    width: 1,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeChannel(idx: number) {
 | 
			
		||||
  channels.value.splice(idx, 1);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user