FPGA_WebLab/src/components/Oscilloscope/OscilloscopeManager.ts

286 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createInjectionState } from "@vueuse/core";
import { shallowRef, reactive, ref, computed } from "vue";
import { Mutex } from "async-mutex";
import {
OscilloscopeFullConfig,
OscilloscopeDataResponse,
} from "@/APIClient";
import { AuthManager } from "@/utils/AuthManager";
import { useAlertStore } from "@/components/Alert";
import { useRequiredInjection } from "@/utils/Common";
export type OscilloscopeDataType = {
x: number[];
y: number[] | number[][];
xUnit: "s" | "ms" | "us" | "ns";
yUnit: "V" | "mV" | "uV";
adFrequency: number;
adVpp: number;
adMax: number;
adMin: number;
};
// 默认配置
const DEFAULT_CONFIG: OscilloscopeFullConfig = new OscilloscopeFullConfig({
captureEnabled: false,
triggerLevel: 128,
triggerRisingEdge: true,
horizontalShift: 0,
decimationRate: 0,
autoRefreshRAM: false,
});
// 采样频率常量(后端返回)
const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() => {
const oscData = shallowRef<OscilloscopeDataType>();
const alert = useRequiredInjection(useAlertStore);
// 互斥锁
const operationMutex = new Mutex();
// 状态
const isApplying = ref(false);
const isCapturing = ref(false);
// 配置
const config = reactive<OscilloscopeFullConfig>(new OscilloscopeFullConfig({ ...DEFAULT_CONFIG }));
// 采样点数(由后端数据决定)
const sampleCount = ref(0);
// 采样周期ns由adFrequency计算
const samplePeriodNs = computed(() =>
oscData.value?.adFrequency ? 1_000_000_000 / oscData.value.adFrequency : 200
);
// 应用配置
const applyConfiguration = async () => {
if (operationMutex.isLocked()) {
alert.warn("有其他操作正在进行中,请稍后再试", 3000);
return;
}
const release = await operationMutex.acquire();
isApplying.value = true;
try {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
const success = await client.initialize({ ...config });
if (success) {
alert.success("示波器配置已应用", 2000);
} else {
throw new Error("应用失败");
}
} catch (error) {
alert.error("应用配置失败", 3000);
} finally {
isApplying.value = false;
release();
}
};
// 重置配置
const resetConfiguration = () => {
Object.assign(config, { ...DEFAULT_CONFIG });
alert.info("配置已重置", 2000);
};
// 获取数据
const getOscilloscopeData = async () => {
try {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
const resp: OscilloscopeDataResponse = await client.getData();
// 解析波形数据
const binaryString = atob(resp.waveformData);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
sampleCount.value = bytes.length;
// 构建时间轴
const x = Array.from(
{ length: bytes.length },
(_, i) => (i * samplePeriodNs.value) / 1000 // us
);
const y = Array.from(bytes);
oscData.value = {
x,
y,
xUnit: "us",
yUnit: "V",
adFrequency: resp.adFrequency,
adVpp: resp.adVpp,
adMax: resp.adMax,
adMin: resp.adMin,
};
} catch (error) {
alert.error("获取示波器数据失败", 3000);
}
};
// 定时器引用
let refreshIntervalId: number | undefined;
// 刷新间隔(毫秒),可根据需要调整
const refreshIntervalMs = ref(1000);
// 定时刷新函数
const startAutoRefresh = () => {
if (refreshIntervalId !== undefined) return;
refreshIntervalId = window.setInterval(async () => {
await refreshRAM();
await getOscilloscopeData();
}, refreshIntervalMs.value);
};
const stopAutoRefresh = () => {
if (refreshIntervalId !== undefined) {
clearInterval(refreshIntervalId);
refreshIntervalId = undefined;
}
};
// 启动捕获
const startCapture = async () => {
if (operationMutex.isLocked()) {
alert.warn("有其他操作正在进行中,请稍后再试", 3000);
return;
}
isCapturing.value = true;
const release = await operationMutex.acquire();
try {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
const started = await client.startCapture();
if (!started) throw new Error("无法启动捕获");
alert.info("开始捕获...", 2000);
// 启动定时刷新
startAutoRefresh();
} catch (error) {
alert.error("捕获失败", 3000);
isCapturing.value = false;
stopAutoRefresh();
} finally {
isCapturing.value = false;
release();
}
};
// 停止捕获
const stopCapture = async () => {
if (!isCapturing.value) {
alert.warn("当前没有正在进行的捕获操作", 2000);
return;
}
isCapturing.value = false;
const release = await operationMutex.acquire();
try {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
const stopped = await client.stopCapture();
if (!stopped) throw new Error("无法停止捕获");
alert.info("捕获已停止", 2000);
} catch (error) {
alert.error("停止捕获失败", 3000);
} finally {
release();
}
};
// 更新触发参数
const updateTrigger = async (level: number, risingEdge: boolean) => {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
try {
const ok = await client.updateTrigger(level, risingEdge);
if (ok) {
config.triggerLevel = level;
config.triggerRisingEdge = risingEdge;
alert.success("触发参数已更新", 2000);
} else {
throw new Error();
}
} catch {
alert.error("更新触发参数失败", 2000);
}
};
// 更新采样参数
const updateSampling = async (horizontalShift: number, decimationRate: number) => {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
try {
const ok = await client.updateSampling(horizontalShift, decimationRate);
if (ok) {
config.horizontalShift = horizontalShift;
config.decimationRate = decimationRate;
alert.success("采样参数已更新", 2000);
} else {
throw new Error();
}
} catch {
alert.error("更新采样参数失败", 2000);
}
};
// 手动刷新RAM
const refreshRAM = async () => {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
try {
const ok = await client.refreshRAM();
if (ok) {
alert.success("RAM已刷新", 2000);
} else {
throw new Error();
}
} catch {
alert.error("刷新RAM失败", 2000);
}
};
// 生成测试数据
const generateTestData = () => {
const freq = 5_000_000;
const duration = 0.001; // 1ms
const points = Math.floor(freq * duration);
const x = Array.from({ length: points }, (_, i) => (i * 1_000_000_000 / freq) / 1000);
const y = Array.from({ length: points }, (_, i) =>
Math.floor(Math.sin(i * 0.01) * 127 + 128)
);
oscData.value = {
x,
y,
xUnit: "us",
yUnit: "V",
adFrequency: freq,
adVpp: 2.0,
adMax: 255,
adMin: 0,
};
alert.success("测试数据生成成功", 2000);
};
const isOperationInProgress = computed(
() => isApplying.value || isCapturing.value || operationMutex.isLocked()
);
return {
oscData,
config,
isApplying,
isCapturing,
isOperationInProgress,
sampleCount,
samplePeriodNs,
refreshIntervalMs,
applyConfiguration,
resetConfiguration,
getOscilloscopeData,
startCapture,
stopCapture,
updateTrigger,
updateSampling,
refreshRAM,
generateTestData,
};
});
export { useProvideOscilloscope, useOscilloscopeState, DEFAULT_CONFIG };