diff --git a/ShengShengBuXi.ConsoleApp/Services/SignalRService.cs b/ShengShengBuXi.ConsoleApp/Services/SignalRService.cs index eb61d07..eab35e2 100644 --- a/ShengShengBuXi.ConsoleApp/Services/SignalRService.cs +++ b/ShengShengBuXi.ConsoleApp/Services/SignalRService.cs @@ -17,8 +17,8 @@ public class SignalRService : ISignalRService private readonly SemaphoreSlim _semaphore = new(1, 1); private string? _clientId; private bool _isInitialConnection = true; - private const int MAX_RETRY_COUNT = 10; - private const int RETRY_INTERVAL_MS = 1000; + private const int MAX_RETRY_COUNT = 15; + private const int RETRY_INTERVAL_MS = 200; private const int HEARTBEAT_INTERVAL_MS = 3000; // 3秒发送一次心跳 private Timer? _heartbeatTimer; private DateTime _lastHeartbeatTime = DateTime.MinValue; @@ -78,7 +78,7 @@ public class SignalRService : ISignalRService _hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl) - .WithAutomaticReconnect([TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3)]) + .WithAutomaticReconnect() .Build(); // 启动心跳定时器 diff --git a/ShengShengBuXi.ConsoleApp/appsettings.json b/ShengShengBuXi.ConsoleApp/appsettings.json index 7700478..f1dd3dd 100644 --- a/ShengShengBuXi.ConsoleApp/appsettings.json +++ b/ShengShengBuXi.ConsoleApp/appsettings.json @@ -1,5 +1,5 @@ { - "SignalRHubUrl": "http://115.159.44.16/audiohub", + "SignalRHubUrl": "http://localhost:81/audiohub", "ConfigBackupPath": "config.json", "AutoConnectToServer": true, "AllowOfflineStart": false diff --git a/ShengShengBuXi.ConsoleApp/mp3/风铃.wav b/ShengShengBuXi.ConsoleApp/mp3/风铃.wav new file mode 100644 index 0000000..001c391 Binary files /dev/null and b/ShengShengBuXi.ConsoleApp/mp3/风铃.wav differ diff --git a/ShengShengBuXi/Hubs/AudioHub.cs b/ShengShengBuXi/Hubs/AudioHub.cs index 55ff096..70eea15 100644 --- a/ShengShengBuXi/Hubs/AudioHub.cs +++ b/ShengShengBuXi/Hubs/AudioHub.cs @@ -874,44 +874,101 @@ namespace ShengShengBuXi.Hubs /// private void InitializeDisplayTextTimer() { + // 注意:不再需要实际启动定时器,因为已改为客户端主动请求模式 + // 但保留此方法以保持代码结构兼容性 if (!_isDisplayTimerInitialized) { lock (_displayTimerLock) { if (!_isDisplayTimerInitialized) { - _displayTextTimer = new Timer(SendNextDisplayText, null, 0, Timeout.Infinite); + // 不再需要实际启动定时器 + // _displayTextTimer = new Timer(SendNextDisplayText, null, 0, Timeout.Infinite); _isDisplayTimerInitialized = true; - _logger.LogInformation("显示文本定时器已初始化"); + _logger.LogInformation("显示文本系统已初始化(客户端主动获取模式)"); } } } } + // 注意:以下方法不再使用,由客户端主动获取替代,保留代码仅作参考 /// - /// 发送下一条显示文本 + /// 发送下一条显示文本(不再使用,由客户端主动获取替代) /// /// 状态对象 private async void SendNextDisplayText(object state) { + // 这个方法不再使用,由客户端主动获取替代 + // 保留原有逻辑仅作参考 + try { - // 获取随机等待时间(5-10秒) - + // 获取随机等待时间(10-30秒) int nextInterval = new Random().Next(10000, 30000); - // 设置下一次触发时间 - _displayTextTimer.Change(nextInterval, Timeout.Infinite); + // 设置下一次触发时间,此处设置为较长时间,避免频繁触发未使用的方法 + _displayTextTimer?.Change(nextInterval, Timeout.Infinite); + + _logger.LogWarning("使用了已废弃的发送显示文本方法,应改为客户端主动获取模式"); + } + catch (Exception ex) + { + _logger.LogError($"发送显示文本失败: {ex.Message}"); + } + } + + /// + /// 开始接收显示文本(兼容旧客户端,新客户端应使用GetNextDisplayText) + /// + public async Task StartReceivingDisplayText() + { + // 兼容旧版客户端的方法 + _logger.LogWarning($"客户端 {Context.ConnectionId} 使用了已废弃的StartReceivingDisplayText方法,应改为GetNextDisplayText"); + + // 转发到新方法 + await GetNextDisplayText(); + } + + /// + /// 获取下一条要显示的文本(由客户端主动请求) + /// + /// 处理任务 + public async Task GetNextDisplayText() + { + try + { + // 检查客户端是否已注册 + if (!_clients.TryGetValue(Context.ConnectionId, out var clientInfo)) + { + _logger.LogWarning($"未注册的客户端尝试获取显示文本: {Context.ConnectionId}"); + await Clients.Caller.SendAsync("Error", "请先注册客户端"); + return; + } + + // 检查客户端是否为Display类型 + if (clientInfo.ClientType != ClientType.Display) + { + _logger.LogWarning($"非显示端客户端尝试获取显示文本: {Context.ConnectionId}, 类型: {clientInfo.ClientType}"); + await Clients.Caller.SendAsync("Error", "只有显示端客户端可以获取显示文本"); + return; + } + + _logger.LogInformation($"显示端请求获取下一条显示文本: {Context.ConnectionId}"); // 检查队列是否为空 if (_displayTextQueue.IsEmpty) { - // 检查是否需要添加预设文本(如果30秒内没有真实用户说话) + // 检查是否需要添加预设文本 if (DateTime.Now.Subtract(_lastRealUserSpeakTime).TotalSeconds > 30) { AddFakeTextToQueue(); } - return; + else + { + _logger.LogInformation("显示文本队列为空,且不需要添加预设文本"); + // 如果队列为空且不需要添加预设文本,返回空 + return; + } } // 从队列中选择最高优先级的消息 @@ -921,29 +978,28 @@ namespace ShengShengBuXi.Hubs // 检查是否获取到有效的消息 if (highestPriority.Key == Guid.Empty || highestPriority.Value == null) - return; - - // 获取大屏显示客户端数量 - int displayClientsCount = _clients.Values.Count(c => c.ClientType == ClientType.Display); - - if (displayClientsCount > 0) { - // 从队列中移除该消息 - if (_displayTextQueue.TryRemove(highestPriority.Key, out _)) - { - // 发送给所有Display客户端 - await _hubContext.Clients.Group("displays").SendAsync("ReceiveDisplayText", highestPriority.Value.Text); - _logger.LogInformation($"已发送显示文本到大屏: {highestPriority.Value.Text} (来源: {(highestPriority.Value.IsRealUser ? "真实用户" : "预设文本")})"); - } + _logger.LogWarning("无法从队列中获取有效的显示文本"); + return; } + // 从队列中移除该消息 + if (_displayTextQueue.TryRemove(highestPriority.Key, out var textToDisplay)) + { + // 只发送给请求的客户端 + await Clients.Caller.SendAsync("ReceiveDisplayText", textToDisplay.Text); + _logger.LogInformation($"已发送显示文本到客户端: {textToDisplay.Text} (来源: {(textToDisplay.IsRealUser ? "真实用户" : "预设文本")})"); + } + else + { + _logger.LogWarning($"无法从队列中移除显示文本: {highestPriority.Key}"); + } } catch (Exception ex) { - _logger.LogError($"发送显示文本失败: {ex.Message}"); + _logger.LogError($"获取显示文本失败: {ex.Message}"); } } - private static int _presetSentencesIndex = -1; /// /// 添加预设文本到队列 @@ -964,14 +1020,6 @@ namespace ShengShengBuXi.Hubs _presetSentences.Add("时光匆匆流逝,思念却越来越深。"); } } - //if (_presetSentencesIndex == -1) - //{ - // _presetSentencesIndex = new Random().Next(_presetSentences.Count); - //} - //if (_presetSentencesIndex >= _presetSentences.Count) - //{ - // _presetSentencesIndex = new Random().Next(_presetSentences.Count); ; - //} _presetSentences.OrderBy(it => Guid.NewGuid()).ToList().ForEach(item => { var displayText = new DisplayText @@ -982,16 +1030,8 @@ namespace ShengShengBuXi.Hubs IsRealUser = false, RecognitionId = "" }; - //AddRecognizedTextToDisplay(item, false); _displayTextQueue.TryAdd(displayText.Id, displayText); - //_logger.LogInformation($"添加预设文本到显示队列: {text}"); }); - - // 从未最近显示过的句子中随机选择一条 - //string randomText = _presetSentences[_presetSentencesIndex]; - - //_presetSentencesIndex += new Random().Next(3); - } catch (Exception ex) { diff --git a/ShengShengBuXi/Pages/Index.cshtml b/ShengShengBuXi/Pages/Index.cshtml index 847bc05..638ebae 100644 --- a/ShengShengBuXi/Pages/Index.cshtml +++ b/ShengShengBuXi/Pages/Index.cshtml @@ -218,6 +218,12 @@ } else { isAnimating = false; if (callback) callback(); + + // 打字效果完成后,等待随机4-7秒,然后请求下一条文本 + const delay = Math.floor(Math.random() * 3000) + 4000; // 4000-7000毫秒 + console.log(`文本显示完成,将在${delay/1000}秒后请求下一条文本`); + + setTimeout(requestNextText, delay); } } typeWriter(); @@ -377,11 +383,8 @@ console.error("解析显示配置失败:", error); } } - // 开始接收显示文本 - return hubConnection.invoke("StartReceivingDisplayText"); - }) - .then(function() { - console.log("开始接收显示文本"); + // 开始请求第一条显示文本 + requestNextText(); }) .catch(function(err) { console.error("客户端操作失败:", err); @@ -507,12 +510,9 @@ $('#magazine').turn('previous'); else if (e.keyCode == 39) $('#magazine').turn('next'); - // 空格键不再触发说话,改为请求下一条文本 - else if (e.keyCode == 32 && hubConnection && hubConnection.state === signalR.HubConnectionState.Connected) { - hubConnection.invoke("StartReceivingDisplayText") - .catch(function(err) { - console.error("请求下一条文本失败:", err); - }); + // 空格键请求下一条文本 + else if (e.keyCode == 32) { + requestNextText(); } }); @@ -625,6 +625,25 @@ } } }); + + /** + * 请求下一条文本 + */ + function requestNextText() { + if (hubConnection && hubConnection.state === signalR.HubConnectionState.Connected) { + console.log("正在向服务器请求下一条文本..."); + hubConnection.invoke("GetNextDisplayText") + .catch(function(err) { + console.error("请求下一条文本失败:", err); + // 如果请求失败,稍后重试 + setTimeout(requestNextText, 5000); + }); + } else { + console.warn("SignalR连接未就绪,无法请求下一条文本"); + // 如果连接未就绪,稍后重试 + setTimeout(requestNextText, 3000); + } + }