Compare commits

..

No commits in common. "6c5250f9c22849f1fcfce10e0ee7beea3be533d4" and "3da0f284f3e06465bb0e4f6ca16f40f913a4bdab" have entirely different histories.

4 changed files with 127 additions and 118 deletions

View File

@ -86,7 +86,7 @@ public class HttpVideoStreamService : BackgroundService
{ {
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private HttpListener? _httpListener; private HttpListener? _httpListener;
private readonly int _serverPort = 4321; private readonly int _serverPort = 8080;
private readonly int _frameRate = 30; // 30 FPS private readonly int _frameRate = 30; // 30 FPS
// 动态分辨率配置 // 动态分辨率配置
@ -412,7 +412,7 @@ public class HttpVideoStreamService : BackgroundService
{ {
if (_usbCamera == null) if (_usbCamera == null)
{ {
_usbCamera = new VideoCapture(1); _usbCamera = new VideoCapture(0);
_usbCamera.Fps = _frameRate; _usbCamera.Fps = _frameRate;
_usbCamera.FrameWidth = _frameWidth; _usbCamera.FrameWidth = _frameWidth;
_usbCamera.FrameHeight = _frameHeight; _usbCamera.FrameHeight = _frameHeight;

View File

@ -1,4 +1,4 @@
import { autoResetRef, createInjectionState } from "@vueuse/core"; import { createInjectionState } from "@vueuse/core";
import { shallowRef, reactive, ref, computed } from "vue"; import { shallowRef, reactive, ref, computed } from "vue";
import { Mutex } from "async-mutex"; import { Mutex } from "async-mutex";
import { import {
@ -26,7 +26,7 @@ const DEFAULT_CONFIG: OscilloscopeFullConfig = new OscilloscopeFullConfig({
triggerLevel: 128, triggerLevel: 128,
triggerRisingEdge: true, triggerRisingEdge: true,
horizontalShift: 0, horizontalShift: 0,
decimationRate: 50, decimationRate: 0,
autoRefreshRAM: false, autoRefreshRAM: false,
}); });
@ -83,10 +83,6 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
alert.info("配置已重置", 2000); alert.info("配置已重置", 2000);
}; };
const clearOscilloscopeData = () => {
oscData.value = undefined;
}
// 获取数据 // 获取数据
const getOscilloscopeData = async () => { const getOscilloscopeData = async () => {
try { try {
@ -123,28 +119,6 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
} }
}; };
// 定时器引用
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;
isCapturing.value = false;
}
};
// 启动捕获 // 启动捕获
const startCapture = async () => { const startCapture = async () => {
if (operationMutex.isLocked()) { if (operationMutex.isLocked()) {
@ -159,13 +133,14 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
if (!started) throw new Error("无法启动捕获"); if (!started) throw new Error("无法启动捕获");
alert.info("开始捕获...", 2000); alert.info("开始捕获...", 2000);
// 启动定时刷新 // 简单轮询,直到捕获完成(可根据后端实际情况优化)
startAutoRefresh(); await new Promise((resolve) => setTimeout(resolve, 1000));
await getOscilloscopeData();
alert.success("捕获完成", 2000);
} catch (error) { } catch (error) {
alert.error("捕获失败", 3000); alert.error("捕获失败", 3000);
isCapturing.value = false;
stopAutoRefresh();
} finally { } finally {
isCapturing.value = false;
release(); release();
} }
}; };
@ -177,7 +152,6 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
return; return;
} }
isCapturing.value = false; isCapturing.value = false;
stopAutoRefresh();
const release = await operationMutex.acquire(); const release = await operationMutex.acquire();
try { try {
const client = AuthManager.createAuthenticatedOscilloscopeApiClient(); const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
@ -231,7 +205,7 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
try { try {
const ok = await client.refreshRAM(); const ok = await client.refreshRAM();
if (ok) { if (ok) {
// alert.success("RAM已刷新", 2000); alert.success("RAM已刷新", 2000);
} else { } else {
throw new Error(); throw new Error();
} }
@ -262,18 +236,21 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() =
alert.success("测试数据生成成功", 2000); alert.success("测试数据生成成功", 2000);
}; };
const isOperationInProgress = computed(
() => isApplying.value || isCapturing.value || operationMutex.isLocked()
);
return { return {
oscData, oscData,
config, config,
isApplying, isApplying,
isCapturing, isCapturing,
isOperationInProgress,
sampleCount, sampleCount,
samplePeriodNs, samplePeriodNs,
refreshIntervalMs,
applyConfiguration, applyConfiguration,
resetConfiguration, resetConfiguration,
clearOscilloscopeData,
getOscilloscopeData, getOscilloscopeData,
startCapture, startCapture,
stopCapture, stopCapture,

View File

@ -2,7 +2,10 @@
<div class="w-full h-100 flex flex-col"> <div class="w-full h-100 flex flex-col">
<!-- 原有内容 --> <!-- 原有内容 -->
<v-chart v-if="hasData" class="w-full h-full" :option="option" autoresize /> <v-chart v-if="hasData" class="w-full h-full" :option="option" autoresize />
<div v-else class="w-full h-full flex flex-col gap-4 items-center justify-center text-gray-500"> <div
v-else
class="w-full h-full flex flex-col gap-4 items-center justify-center text-gray-500"
>
<span> 暂无数据 </span> <span> 暂无数据 </span>
<!-- 采集控制按钮 --> <!-- 采集控制按钮 -->
<div class="flex justify-center items-center mb-2"> <div class="flex justify-center items-center mb-2">
@ -13,11 +16,13 @@
!oscManager.isCapturing.value, !oscManager.isCapturing.value,
'from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 focus:ring-red-300': 'from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 focus:ring-red-300':
oscManager.isCapturing.value, oscManager.isCapturing.value,
}" @click=" }"
@click="
oscManager.isCapturing.value oscManager.isCapturing.value
? oscManager.stopCapture() ? oscManager.stopCapture()
: oscManager.startCapture() : oscManager.startCapture()
"> "
>
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
<template v-if="oscManager.isCapturing.value"> <template v-if="oscManager.isCapturing.value">
<Square class="w-5 h-5" /> <Square class="w-5 h-5" />
@ -104,11 +109,9 @@ const option = computed((): EChartsOption => {
return {}; return {};
} }
const isCapturing = oscManager.isCapturing.value;
const series: LineSeriesOption[] = []; const series: LineSeriesOption[] = [];
// yChannels number[] // yChannels number[][]
const yChannels: number[][] = Array.isArray(oscData.value.y[0]) const yChannels: number[][] = Array.isArray(oscData.value.y[0])
? (oscData.value.y as number[][]) ? (oscData.value.y as number[][])
: [oscData.value.y as number[]]; : [oscData.value.y as number[]];
@ -128,10 +131,6 @@ const option = computed((): EChartsOption => {
lineStyle: { lineStyle: {
width: 2, width: 2,
}, },
//
animation: !isCapturing,
animationDuration: isCapturing ? 0 : 1000,
animationEasing: isCapturing ? "linear" : "cubicOut",
}); });
}); });
@ -204,10 +203,6 @@ const option = computed((): EChartsOption => {
show: false, show: false,
}, },
}, },
//
animation: !isCapturing,
animationDuration: isCapturing ? 0 : 1000,
animationEasing: isCapturing ? "linear" : "cubicOut",
series: series, series: series,
}; };
}); });

View File

@ -3,21 +3,9 @@
<!-- 波形展示 --> <!-- 波形展示 -->
<div class="card bg-base-200 shadow-xl mx-5"> <div class="card bg-base-200 shadow-xl mx-5">
<div class="card-body"> <div class="card-body">
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title">
<div class="flex items-center gap-2"> <Activity class="w-5 h-5" />
<Activity class="w-5 h-5" /> 波形显示
波形显示
</div>
<div class="flex items-center gap-2">
<button class="btn btn-sm btn-warning" @click="osc.stopCapture" :disabled="!osc.isCapturing.value">
停止捕获
</button>
<div class="flex items-center gap-2">
<button class="btn btn-sm btn-error" @click="osc.clearOscilloscopeData">
清空
</button>
</div>
</div>
</h2> </h2>
<OscilloscopeWaveformDisplay /> <OscilloscopeWaveformDisplay />
</div> </div>
@ -28,73 +16,122 @@
<div class="card-body"> <div class="card-body">
<h2 class="card-title">示波器配置</h2> <h2 class="card-title">示波器配置</h2>
<form class="flex flex-col gap-2" @submit.prevent="applyConfiguration"> <form class="flex flex-col gap-2" @submit.prevent="applyConfiguration">
<div class="flex flex-row items-center justify-between gap-4"> <div class="flex gap-4">
<label> <label>
边沿触发: 触发电平:
<select v-model="osc.config.triggerRisingEdge" class="select select-bordered w-24"> <input
type="number"
v-model="osc.config.triggerLevel"
min="0"
max="255"
class="input input-bordered w-24"
/>
</label>
<label>
边沿:
<select
v-model="osc.config.triggerRisingEdge"
class="select select-bordered w-24"
>
<option :value="true">上升沿</option> <option :value="true">上升沿</option>
<option :value="false">下降沿</option> <option :value="false">下降沿</option>
</select> </select>
</label> </label>
<label>
触发电平:
<div class="flex items-center gap-2">
<input type="range" min="0" max="255" step="1" v-model="osc.config.triggerLevel"
class="range range-sm w-50" />
<input type="number" v-model="osc.config.triggerLevel" min="0" max="255"
class="input input-bordered w-24" />
</div>
</label>
<label> <label>
水平偏移: 水平偏移:
<div class="flex items-center gap-2"> <input
<input type="range" min="0" max="1000" step="1" v-model="osc.config.horizontalShift" type="number"
class="range range-sm w-50" /> v-model="osc.config.horizontalShift"
<input type="number" v-model="osc.config.horizontalShift" min="0" max="1000" class="input input-bordered w-24"
class="input input-bordered w-24" /> />
</div>
</label> </label>
<label> <label>
抽取率: 抽取率:
<div class="flex items-center gap-2"> <input
<input type="range" min="0" max="100" step="1" v-model="osc.config.decimationRate" type="number"
class="range range-sm w-50" /> v-model="osc.config.decimationRate"
<input type="number" v-model="osc.config.decimationRate" min="0" max="100" min="0"
class="input input-bordered w-24" /> class="input input-bordered w-24"
</div> />
</label> </label>
</div>
<div class="flex gap-4">
</div>
<div class="flex items-center justify-between gap-2 mt-2">
<label> <label>
刷新间隔(ms): 自动刷新RAM:
<div class="flex items-center gap-2"> <input
<input type="range" min="100" max="3000" step="100" v-model="osc.refreshIntervalMs.value" type="checkbox"
class="range range-sm w-50" /> v-model="osc.config.autoRefreshRAM"
<input type="number" min="100" max="3000" step="100" v-model="osc.refreshIntervalMs.value" class="checkbox"
class="input input-bordered w-24" /> />
</div>
</label> </label>
<div class="flex items-center gap-2"> </div>
<button class="btn btn-primary" type="submit" :disabled="osc.isApplying.value || osc.isCapturing.value"> <div class="flex gap-2 mt-2">
应用配置 <button
</button> class="btn btn-primary"
<button class="btn btn-secondary" type="button" @click="osc.resetConfiguration" type="submit"
:disabled="osc.isApplying.value || osc.isCapturing.value"> :disabled="osc.isOperationInProgress.value"
重置 >
</button> 应用配置
<button class="btn btn-outline" @click="osc.refreshRAM" :disabled="osc.isApplying.value || osc.isCapturing.value"> </button>
刷新RAM <button
</button> class="btn btn-secondary"
<!-- <button class="btn btn-accent" @click="osc.generateTestData" :disabled="osc.isOperationInProgress.value"> type="button"
生成测试数据 @click="osc.resetConfiguration"
</button> --> :disabled="osc.isOperationInProgress.value"
</div> >
重置
</button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<!-- 捕获控制 -->
<div class="card bg-base-200 shadow-xl mx-5">
<div class="card-body flex gap-2">
<h2 class="card-title">捕获控制</h2>
<button
class="btn btn-success"
@click="osc.startCapture"
:disabled="osc.isOperationInProgress.value"
>
开始捕获
</button>
<button
class="btn btn-warning"
@click="osc.stopCapture"
:disabled="!osc.isCapturing.value"
>
停止捕获
</button>
<button
class="btn btn-info"
@click="osc.getOscilloscopeData"
:disabled="osc.isOperationInProgress.value"
>
获取数据
</button>
<button
class="btn btn-accent"
@click="osc.generateTestData"
:disabled="osc.isOperationInProgress.value"
>
生成测试数据
</button>
</div>
</div>
<!-- RAM刷新 -->
<div class="card bg-base-200 shadow-xl mx-5">
<div class="card-body flex gap-2">
<h2 class="card-title">RAM 操作</h2>
<button
class="btn btn-outline"
@click="osc.refreshRAM"
:disabled="osc.isOperationInProgress.value"
>
刷新RAM
</button>
</div>
</div>
</div> </div>
</template> </template>