lvfengfree ed9d1d7325 feat: 屏幕监控大规模优化 - 支持60台设备同时监控
- Agent端优化:
  * 添加质量档位定义 (Low: 320x180@3fps, High: 1280x720@15fps)
  * H.264编码器支持动态质量切换
  * 屏幕流服务支持按需推流和质量控制
  * 添加SignalR信令客户端连接服务器

- 服务器端优化:
  * 添加StreamSignalingHub处理质量控制信令
  * 支持设备注册/注销和监控状态管理
  * 支持教师端监控控制和设备选中

- 前端组件:
  * 创建H264VideoPlayer组件支持H.264和JPEG模式
  * 更新学生屏幕监控页面使用新组件

- 性能提升:
  * 带宽从120Mbps降至6-7Mbps (降低95%)
  * 监控墙模式: 60台100kbps=6Mbps
  * 单机放大模式: 1台1Mbps+59台100kbps=6.9Mbps
  * 无人观看时停止推流节省带宽
2026-01-23 15:37:37 +08:00

196 lines
7.4 KiB
C#
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.

using DeviceAgent.Services;
using Microsoft.Extensions.Options;
namespace DeviceAgent;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly DeviceInfoService _deviceInfoService;
private readonly ReportService _reportService;
private readonly ScreenCaptureService _screenCaptureService;
private readonly ScreenStreamService _screenStreamService;
private readonly RemoteDesktopService _remoteDesktopService;
private readonly SignalingClientService _signalingClientService;
private readonly AgentConfig _config;
private string? _cachedUuid;
private int _heartbeatCounter = 0;
private int _screenshotCounter = 0;
public Worker(
ILogger<Worker> logger,
DeviceInfoService deviceInfoService,
ReportService reportService,
ScreenCaptureService screenCaptureService,
ScreenStreamService screenStreamService,
RemoteDesktopService remoteDesktopService,
SignalingClientService signalingClientService,
IOptions<AgentConfig> config)
{
_logger = logger;
_deviceInfoService = deviceInfoService;
_reportService = reportService;
_screenCaptureService = screenCaptureService;
_screenStreamService = screenStreamService;
_remoteDesktopService = remoteDesktopService;
_signalingClientService = signalingClientService;
_config = config.Value;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("DeviceAgent 服务已启动");
_logger.LogInformation("服务器地址: {ServerUrl}", _config.ServerUrl);
_logger.LogInformation("上报间隔: {Interval} 秒", _config.ReportIntervalSeconds);
_logger.LogInformation("屏幕截图: {Enabled}", _config.ScreenCaptureEnabled ? "启用" : "禁用");
_logger.LogInformation("实时屏幕流: {Enabled}", _config.ScreenStreamEnabled ? "启用" : "禁用");
_logger.LogInformation("自动开启远程桌面: {Enabled}", _config.EnableRemoteDesktopOnStart ? "启用" : "禁用");
// 自动开启远程桌面
if (_config.EnableRemoteDesktopOnStart)
{
EnableRemoteDesktopOnStartup();
}
// 设置 SignalingClientService 的 ScreenStreamService 引用(避免循环依赖)
_signalingClientService.SetScreenStreamService(_screenStreamService);
// 启动实时屏幕流服务(在后台任务中)
Task? screenStreamTask = null;
Task? signalingTask = null;
if (_config.ScreenStreamEnabled)
{
screenStreamTask = Task.Run(() => _screenStreamService.StartAsync(stoppingToken), stoppingToken);
signalingTask = Task.Run(() => _signalingClientService.StartAsync(stoppingToken), stoppingToken);
// 等待一小段时间让服务启动
await Task.Delay(500, stoppingToken);
}
// 启动时立即上报一次完整信息
await ReportFullInfoAsync();
while (!stoppingToken.IsCancellationRequested)
{
try
{
_heartbeatCounter++;
_screenshotCounter++;
// 每10次心跳上报一次完整信息其他时候只发心跳
if (_heartbeatCounter >= 10)
{
await ReportFullInfoAsync();
_heartbeatCounter = 0;
}
else if (!string.IsNullOrEmpty(_cachedUuid))
{
await _reportService.SendHeartbeatAsync(_cachedUuid);
}
// 屏幕截图上传(根据配置的间隔)- 已禁用,改用实时流
if (_config.ScreenCaptureEnabled && !string.IsNullOrEmpty(_cachedUuid))
{
var screenshotInterval = _config.ScreenCaptureIntervalSeconds / _config.ReportIntervalSeconds;
if (_screenshotCounter >= screenshotInterval)
{
await UploadScreenshotAsync();
_screenshotCounter = 0;
}
}
await Task.Delay(TimeSpan.FromSeconds(_config.ReportIntervalSeconds), stoppingToken);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "工作循环发生异常");
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
// 停止屏幕流服务
if (_config.ScreenStreamEnabled)
{
await _screenStreamService.StopAsync();
await _signalingClientService.StopAsync();
}
_logger.LogInformation("DeviceAgent 服务已停止");
}
private void EnableRemoteDesktopOnStartup()
{
try
{
if (_remoteDesktopService.IsRemoteDesktopEnabled())
{
_logger.LogInformation("远程桌面已启用,端口: {Port}", _remoteDesktopService.GetRdpPort());
}
else
{
_logger.LogInformation("正在启用远程桌面...");
if (_remoteDesktopService.EnableRemoteDesktop())
{
_logger.LogInformation("远程桌面已成功启用,端口: {Port}", _remoteDesktopService.GetRdpPort());
}
else
{
_logger.LogWarning("启用远程桌面失败,请检查是否以管理员权限运行");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "启用远程桌面时发生异常");
}
}
private async Task ReportFullInfoAsync()
{
try
{
var deviceInfo = _deviceInfoService.GetDeviceInfo();
_cachedUuid = deviceInfo.Uuid;
_logger.LogInformation("收集到设备信息:");
_logger.LogInformation(" UUID: {Uuid}", deviceInfo.Uuid);
_logger.LogInformation(" 主机名: {Hostname}", deviceInfo.Hostname);
_logger.LogInformation(" IP地址: {IpAddress}", deviceInfo.IpAddress);
_logger.LogInformation(" MAC地址: {MacAddress}", deviceInfo.MacAddress);
_logger.LogInformation(" 操作系统: {OsName}", deviceInfo.OsName);
_logger.LogInformation(" CPU: {CpuName}", deviceInfo.CpuName);
_logger.LogInformation(" 内存: {Memory} MB", deviceInfo.TotalMemoryMB);
await _reportService.ReportDeviceInfoAsync(deviceInfo);
}
catch (Exception ex)
{
_logger.LogError(ex, "上报完整信息失败");
}
}
private async Task UploadScreenshotAsync()
{
try
{
var screenshot = _screenCaptureService.CaptureScreen(
_config.ScreenCaptureQuality,
_config.ScreenCaptureMaxWidth);
if (screenshot.Length > 0)
{
_logger.LogDebug("截图大小: {Size} KB", screenshot.Length / 1024);
await _reportService.UploadScreenshotAsync(_cachedUuid!, screenshot);
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "屏幕截图上传失败");
}
}
}