Compare commits
No commits in common. "6c5250f9c22849f1fcfce10e0ee7beea3be533d4" and "3da0f284f3e06465bb0e4f6ca16f40f913a4bdab" have entirely different histories.
6c5250f9c2
...
3da0f284f3
|
@ -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,9 +412,9 @@ 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;
|
||||||
_usbCameraEnable = _usbCamera.IsOpened();
|
_usbCameraEnable = _usbCamera.IsOpened();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue