using Panuon.WPF.UI; using SWRIS.Core; using SWRIS.Dtos; using SWRIS.Enums; using SWRIS.Events; using SWRIS.Extensions; using SWRIS.Models; using SWRIS.Models.ViewModel; using SWRIS.Pages; using SWRIS.Repository; using System; using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Speech.Synthesis; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.Windows.Threading; using Tools = SWRIS.Extensions.Tools; namespace SWRIS { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : WindowX, IComponentConnector { // 屏幕比例阈值 private const double ASPECT_RATIO_THRESHOLD = 1.5; // 大于此值视为宽屏(16:9),小于此值视为方屏(4:3) // 预状态(PreStop/PreRunning/PreCalibration)等待设备确认的超时阈值 private static readonly long PreStatusTimeoutTicks = TimeSpan.FromSeconds(10).Ticks; private readonly SpeechSynthesizer speech; private readonly DispatcherTimer _preStatusTimer; public MainViewModel MainView { get; set; } private readonly IRecordRepository _recordRepository; private readonly IAlarmRepository _alarmRepository; public MainWindow() { InitializeComponent(); _recordRepository = new RecordRepository(); _alarmRepository = new AlarmRepository(); speech = new SpeechSynthesizer() { Rate = 1, Volume = 100 }; MainView = new MainViewModel(); Application.Current.MainWindow = this; App.TcpServer.ClientConnected += TcpServer_ClientConnected; App.TcpServer.ClientDisconnected += TcpServer_ClientDisconnected; App.TcpServer.LiveStreamReceived += TcpServer_LiveStreamReceived; App.TcpServer.AlarmDataReceived += TcpServer_AlarmDataReceived; App.TcpServer.DetectionDataReceived += TcpServer_DetectionDataReceived; App.TcpServer.RealTimeDataReceived += TcpServer_RealTimeDataReceived; App.TcpServer.FaultDataReceived += TcpServer_FaultDataReceived; App.TcpServer.DetectionRawDataReceived += TcpServer_DetectionRawDataReceived; App.TcpServer.DetectionRawDataResultReceived += TcpServer_DetectionRawDataResultReceived; App.TcpServer.DetectionStatusResultReceived += TcpServer_DetectionStatusResultReceived; App.TcpServer.SetAbsolutePositionDataReceived += TcpServer_SetAbsolutePositionDataReceived; App.TcpServer.DebugMessageReceived += TcpServer_DebugMessageReceived; App.TcpServer.HeartbeatReceived += TcpServer_HeartbeatReceived; App.TcpServer.UpgradedRequestResultReceived += TcpServer_UpgradedRequestResultReceived; App.Calibration.ConnectionStateChanged += Calibration_ConnectionStateChanged; App.Calibration.LimitStateChanged += Calibration_LimitStateChanged; App.TcpServer.Start(); // 周期性检查预状态(PreStop/PreRunning/PreCalibration)是否超时未获设备确认,超时则回退 _preStatusTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; _preStatusTimer.Tick += PreStatusTimer_Tick; _preStatusTimer.Start(); DataContext = MainView; } /// /// 预状态超时检查:设备长时间未确认命令(检测状态结果),清除预状态位,回退为设备实际状态 /// private void PreStatusTimer_Tick(object sender, EventArgs e) { foreach (var equipmentData in App.DataCenter.Equipments) { if (equipmentData.PreStatusTimestamp != 0 && DateTime.UtcNow.Ticks - equipmentData.PreStatusTimestamp > PreStatusTimeoutTicks) { equipmentData.ClearAllPreStatus(); } } } private async void TcpServer_LiveStreamReceived(object sender, LiveStreamReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { if (equipmentData.DetectionData?.DetectionNo == equipmentData.DetectionNo) { equipmentData.ClearData(); } await Task.Run(() => { equipmentData.Chart?.AddDataPoints((equipmentData.AbsolutePosition, e.LiveStream), equipmentData.InUseSensor); equipmentData.AddLiveStream(e.LiveStream); }); } } /// /// 接收心跳数据 /// private void TcpServer_HeartbeatReceived(object sender, HeartbeatReceviedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { equipmentData.IsHeartbeatReceived = true; } } /// /// 接收调试信息 /// private void TcpServer_DebugMessageReceived(object sender, DebugMessageReceivedEventArgs e) { if (e.IpAddress.IsNullOrEmpty() && e.SerialNo.IsNullOrEmpty()) { DebugMessageShow(e.Message); } else { DebugMessageShow(e.Message, e.IpAddress, e.SerialNo); } } /// /// 显示调试信息 /// /// 调试信息 /// IP地址 /// 序列号 public void DebugMessageShow(string message, string ipAddress, string serialNo) { if (App.Config.ShowDebugMessage) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => (!serialNo.IsNullOrEmpty() && c.SerialNo == serialNo) || c.IpAddress == ipAddress); if (equipmentData != null) { equipmentData.DebugMessage.DateTime = DateTime.Now; equipmentData.DebugMessage.Message = message; } } } /// /// 显示调试信息 /// /// 调试信息 public void DebugMessageShow(string message) { if (App.Config.ShowDebugMessage) { MainView.DebugMessage.DateTime = DateTime.Now; MainView.DebugMessage.Message = message; } } /// /// 标定限位状态更改 /// [Obfuscation(Feature = "virtualization", Exclude = false)] private void Calibration_LimitStateChanged(object sender, LimitData limitData) { foreach (var equipment in App.DataCenter.Equipments) { foreach (var limit in equipment.Limits) { if (limit.Name == limitData.Name && limit.IsEnable) { if (limitData.State == LimitState.AtLimit) { limit.State = LimitState.AtLimit; // 当前运行状态添加预标定状态并记录挂起时间,超时未确认时由定时器回退 equipment.MarkPreStatus(RunningStatus.PreCalibration); if (App.TcpServer.SendRealTimeAbsolutePositionData(limit.SwitchIndex, (float)limitData.Position, equipment.SerialNo, equipment.ClientSocket)) { DebugMessageShow($"{limitData.Name}标定成功", equipment.IpAddress, equipment.SerialNo); } } else if (limitData.State == LimitState.NotInLimit) { limit.State = LimitState.NotInLimit; } } } } } /// /// 标定模块连接状态更改 /// private void Calibration_ConnectionStateChanged(object sender, bool isConnect) { foreach (var equipment in App.DataCenter.Equipments) { foreach (var limit in equipment.Limits) { if (isConnect) { if (limit.State == LimitState.Offline) { limit.State = LimitState.NotInLimit; } } else { if (limit.State != LimitState.Offline) { limit.State = LimitState.Offline; } } } } } /// /// 接收检测状态开关结果(0x06 应答):确认 PreStop/PreRunning 预状态 /// private void TcpServer_DetectionStatusResultReceived(object sender, DetectionStatusResultReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { if (equipmentData.RunningStatus.HasFlag(RunningStatus.PreStop)) { // 按位合并,保留报警/故障等其他状态位 equipmentData.ConfirmPreStatus(RunningStatus.PreStop, RunningStatus.Stopped); DebugMessageShow("自动检测停止", e.IpAddress, equipmentData.SerialNo); } else if (equipmentData.RunningStatus.HasFlag(RunningStatus.PreRunning)) { // 用启动命令时记录的请求模式恢复检测形式(时域/空域),避免使用切换前的旧模式位 var mode = equipmentData.PreStatusRunningMode == RunningStatus.TemporalNormal ? RunningStatus.TemporalNormal : RunningStatus.SpatialNormal; var modeName = mode == RunningStatus.TemporalNormal ? "时域检测" : "空域检测"; equipmentData.ConfirmPreStatus(RunningStatus.PreRunning, mode); DebugMessageShow($"自动检测开始,检测形式:{modeName}", e.IpAddress, equipmentData.SerialNo); } } } /// /// 接收设置实时位置(标定)应答(0x15):确认 PreCalibration 预状态 /// private void TcpServer_SetAbsolutePositionDataReceived(object sender, SetAbsolutePositionDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null && e.SetAbsolutePositionData != null) { // 标定命令已获得设备应答(无论成败均视为确认),清除预标定位; // 若还有其他挂起的预状态位则保留其超时时间戳 equipmentData.RunningStatus &= ~RunningStatus.PreCalibration; if ((equipmentData.RunningStatus & (RunningStatus.PreStop | RunningStatus.PreRunning | RunningStatus.PreCalibration)) == 0) { equipmentData.PreStatusTimestamp = 0; } } } /// /// 接收故障数据 /// private void TcpServer_FaultDataReceived(object sender, FaultDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); equipmentData?.AddFault(e.FaultData); } /// /// 接收实时数据 /// [Obfuscation(Feature = "virtualization", Exclude = false)] private void TcpServer_RealTimeDataReceived(object sender, RealTimeDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { equipmentData.Speed = Math.Round(e.RealTimeData.Speed * 60, 2); equipmentData.Position = e.RealTimeData.Position; var preStatusMask = RunningStatus.PreStop | RunningStatus.PreRunning | RunningStatus.PreCalibration; // 设备实时状态已带预状态位 => 设备已确认命令并进入预状态,清除本地挂起时间戳,预状态以设备状态为准 if ((e.RealTimeData.Status & preStatusMask) != 0) { equipmentData.PreStatusTimestamp = 0; equipmentData.RunningStatus = e.RealTimeData.Status; } else if (equipmentData.PreStatusTimestamp != 0) { // 存在待设备确认的预状态且设备尚未确认:保留本地挂起的预状态位,等待确认或超时回退 var pendingFlags = equipmentData.RunningStatus & preStatusMask; equipmentData.RunningStatus = e.RealTimeData.Status | pendingFlags; } else { // 无挂起预状态:0x0B 只更新设备实际状态位 equipmentData.RunningStatus = e.RealTimeData.Status; } equipmentData.AbsolutePosition = e.RealTimeData.AbsolutePosition; if (equipmentData.RunningStatus == RunningStatus.SpatialNormal) { equipmentData.LiftHeight = (equipmentData.RopeLength - equipmentData.AbsolutePosition) / equipmentData.LiftHightRatio; } else if (equipmentData.LiftHeight != 0) { equipmentData.LiftHeight = 0; } equipmentData.Direction = equipmentData.Speed > 0 ? DirectionState.Forward : (equipmentData.Speed == 0f ? DirectionState.Stoped : DirectionState.Reverse); equipmentData.AddSpeedData(equipmentData.Speed); } } /// /// 客户端连接成功 /// [Obfuscation(Feature = "virtualization", Exclude = false)] private void TcpServer_ClientConnected(object sender, ClientConnectedEventArgs e) { var equipment = App.Config.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipment == null) { LogHelper.Error($"未找到设备,IP地址:{e.IpAddress}"); return; } var server = (TcpServerFrame)sender; server.SendTurnLiveStreamData(true, equipment.SerialNo, e.ClientSocket); Thread.Sleep(20); server.SendSetClockData(DateTime.Now.DateTimeToTimestamp(), equipment.SerialNo, e.ClientSocket); var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { equipmentData.IsConnect = true; equipmentData.ClientSocket = e.ClientSocket; if (equipmentData.RunningStatus != RunningStatus.SpatialNormal) { equipmentData.RunningStatus |= RunningStatus.SpatialNormal; } } } /// /// 客户端连接断开 /// private void TcpServer_ClientDisconnected(object sender, ClientDisconnectedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { equipmentData.IsConnect = false; equipmentData.ClientSocket = null; equipmentData.Speed = 0; equipmentData.AbsolutePosition = 0; equipmentData.Position = 0; equipmentData.Direction = DirectionState.Stoped; // 断线时清除挂起的预状态位,避免重连后残留影响状态判断 equipmentData.ClearAllPreStatus(); } } /// /// 接收检测结果 /// private void TcpServer_DetectionDataReceived(object sender, DetectionDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { e.DetectionData.DetectionNo = equipmentData.DetectionNo; equipmentData.DetectionData = e.DetectionData; } } /// /// 接检测原始数据结果 /// private void TcpServer_DetectionRawDataResultReceived(object sender, DetectionRawDataResultReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { // 处理检测原始结果数据 if (e.DetectionRawResultData.Code == 0) { if (equipmentData.DetectionData != null) { equipmentData.DetectionData.RawResultData = e.DetectionRawResultData; } } else { LogHelper.Error($"获取检测原始数据失败,错误码:{e.DetectionRawResultData.Code}"); } } } /// /// 接受检测结果原始数据 /// [Obfuscation(Feature = "virtualization", Exclude = false)] private async void TcpServer_DetectionRawDataReceived(object sender, DetectionRawDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null) { if (equipmentData.DisableAlarm) { DebugMessageShow($"获取检测结果原始数据结束", equipmentData.IpAddress, equipmentData.SerialNo); return; } await Task.Run(() => { if (equipmentData.DetectionData != null && equipmentData.DetectionData.RawResultData != null) { DebugMessageShow($"数据接收:{e.DetectionRawData.PacketNumber}/{e.DetectionRawData.TotalPackets}", equipmentData.IpAddress, equipmentData.SerialNo); if (e.DetectionRawData.PacketNumber == 1) { equipmentData.DetectionData.RawDataReceiving = true; equipmentData.DetectionData.RawData = new MemoryStream(); equipmentData.DetectionData.RawData.Write(e.DetectionRawData.Data, 0, e.DetectionRawData.Data.Length); } else if (e.DetectionRawData.PacketNumber <= e.DetectionRawData.TotalPackets) { // 添加原始数据到检测数据中 equipmentData.DetectionData.RawData.Write(e.DetectionRawData.Data, 0, e.DetectionRawData.Data.Length); // 如果是最后一包数据,处理检测数据 if (e.DetectionRawData.PacketNumber == e.DetectionRawData.TotalPackets) { // 生成数据文件路径 (string absolutePath, string relativePath) = DatFileHandler.GetDatFilePath(equipmentData.SerialNo); // 处理检测数据逻辑 var record = new RecordDto { RopeName = equipmentData.RopeName, RopeNumber = equipmentData.RopeNumber, StartPoint = equipmentData.DetectionData.StartPoint, EndPoint = equipmentData.DetectionData.EndPoint, DetectedSpeed = equipmentData.DetectionData.DetectedSpeed, DetectionLength = equipmentData.DetectionData.DetectionLength, StartTime = equipmentData.DetectionData.StartTime.TimestampToDateTime(), EndTime = equipmentData.DetectionData.EndTime.TimestampToDateTime(), SensorCount = equipmentData.DetectionData.RawResultData.SensorCount, SamplingStep = equipmentData.DetectionData.RawResultData.SamplingStep, DataFilePath = relativePath, InUseSensors = string.Join(",", equipmentData.InUseSensor) }; foreach (var damage in equipmentData.DetectionData.Damages) { var alarmCount = _alarmRepository.AddAlarm(new AlarmDataModel { DamageLevel = damage.DamageLevel, DamagePosition = damage.DamagePoint, DamageValue = damage.DamageValue, RopeNumber = equipmentData.RopeNumber }, AlarmSourceType.Detection, App.Config.DamageExtent); if (alarmCount >= App.Config.AlarmValidCount) { record.Damages.Add(new DamageDto { DamageLevel = damage.DamageLevel, DamagePoint = damage.DamagePoint, DamageValue = damage.DamageValue, }); record.DamageCount++; //纠正健康度 以客户端上传的损伤数据 重新计算 if ((int)record.RiskLevel < (int)damage.DamageLevel) { record.RiskLevel = (RiskLevel)damage.DamageLevel; } } } equipmentData.RiskLevel = record.RiskLevel; if (record.DamageCount > 0) { int? recordId = _recordRepository.CreateRecord(record); if (recordId.HasValue) { record.Id = recordId.Value; AddRecordToPageRecords(record); if (!DatFileHandler.CreateDatFile(absolutePath, equipmentData.DetectionData.RawData)) { LogHelper.Error($"创建检测数据文件失败,路径:{absolutePath}"); } DebugMessageShow($"检测数据记录成功", equipmentData.IpAddress, equipmentData.SerialNo); if ((int)record.RiskLevel >= (int)App.Config.SoundRiskLevel) { Application.Current.Dispatcher.Invoke(() => { speech.SpeakAsync($"{equipmentData.RopeName},检测到{record.DamageCount}处损伤"); }); } } else { LogHelper.Error("检测数据记录失败"); } } else { DebugMessageShow($"检测完成,未发现损伤,本次检测不做记录。", equipmentData.IpAddress, equipmentData.SerialNo); } equipmentData.DetectionData.RawDataReceiving = false; } } } }); } } /// /// 接收报警数据 /// private void TcpServer_AlarmDataReceived(object sender, AlarmDataReceivedEventArgs e) { var equipmentData = App.DataCenter.Equipments.FirstOrDefault(c => c.IpAddress == e.IpAddress); if (equipmentData != null && equipmentData.IsSpeedStable && !equipmentData.DisableAlarm) { Dispatcher.InvokeAsync(() => { e.AlarmData.RopeNumber = equipmentData.RopeNumber; var alarmCount = _alarmRepository.AddAlarm(e.AlarmData, AlarmSourceType.RealTime, App.Config.DamageExtent); if (alarmCount >= App.Config.AlarmValidCount) { equipmentData.AddAlarm(e.AlarmData); } }); } } private void TcpServer_UpgradedRequestResultReceived(object sender, UpgradedResultReceivedEventArgs e) { Dispatcher.InvokeAsync(async () => { if (e.Code == 0) { await Task.Delay(2000); string url = $"http://192.168.1.200"; // 替换为您的网页地址 Tools.OpenUrl(url); } }); } public void AddRecordToPageRecords(RecordDto record) { Application.Current.Dispatcher.Invoke(() => { if (Main_Frame.Content is Page currentPage && currentPage != null) { var simpleRecord = record.ToSimpleRecordDto(); if (currentPage.Title == "RealTimePage") { if (currentPage.DataContext is RealTimeViewModel realTimeView) { realTimeView.AddRecord(simpleRecord); } } } }); } private void WindowX_Loaded(object sender, RoutedEventArgs e) { AdjustViewboxStretch(); NavigateToPage("Home"); // 在程序启动时开启一个后台线程,每隔15秒检测一次 Task.Run(async () => { bool lastIpOk = true; bool lastPortOk = true; while (true) { bool ipOk = Tools.CheckIpAddressExists(App.Config.IpAddress); bool portOk = !Tools.CheckPortInUse(App.Config.Port); // IP异常 if (!ipOk) { NoticeBox.Show($"当前在用网卡的IP地址中没有发现[{App.Config.IpAddress}],请检查网络配置。", "警告", MessageBoxIcon.Warning, true, 5000); } // IP从异常恢复正常 else if (!lastIpOk && ipOk) { if (!HasConnectedDevice()) { NoticeBox.Show($"IP地址[{App.Config.IpAddress}]已恢复,5秒后将自动重启程序", "提示", MessageBoxIcon.Info, true, 5000); if (HasConnectedDevice()) { NoticeBox.Show($"检测到有设备处于连接状态,已取消自动重启", "提示", MessageBoxIcon.Info, true, 5000); } else { await RestartApplication(); } } } // 端口异常 if (!portOk) { NoticeBox.Show("当前配置的端口已被占用,请更换端口或关闭占用该端口的程序。", "警告", MessageBoxIcon.Warning, true, 5000); } // 端口从异常恢复正常 else if (!lastPortOk && portOk) { if (!HasConnectedDevice()) { NoticeBox.Show($"端口[{App.Config.Port}]已释放,5秒后将自动重启程序", "提示", MessageBoxIcon.Info, true, 5000); if (HasConnectedDevice()) { NoticeBox.Show($"检测到有设备处于连接状态,已取消自动重启", "提示", MessageBoxIcon.Info, true, 5000); } else { await RestartApplication(); } } } lastIpOk = ipOk; lastPortOk = portOk; await Task.Delay(15000); // 每15秒检测一次 } }); } private async Task RestartApplication(int delay = 5000) { await Task.Delay(delay); Application.Current.Dispatcher.Invoke(() => { App.RestartApplication(); }); } /// /// 检查是否有设备处于连接状态 /// private bool HasConnectedDevice() { return App.DataCenter.Equipments.Any(e => e.IsConnect); } public void NavigatePage(Uri uri) { Main_Frame.NavigationService.Navigate(uri); } private void NavigateToPage(object pageName) { switch (pageName) { case "Home": MainView.CurrentPage = "Home"; Main_Frame.NavigationService.Navigate(new Uri($"Pages/RealTime/RealTimePage.xaml", uriKind: UriKind.Relative)); break; case "Record": MainView.CurrentPage = "Record"; Main_Frame.NavigationService.Navigate(new Uri($"Pages/RecordPage.xaml", uriKind: UriKind.Relative)); break; } } private void Menu_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (sender is TextBlock menuItem) { CleanNavigation(Main_Frame); NavigateToPage(menuItem?.Tag); } } private void CleanNavigation(Frame frame) { while (frame.CanGoBack) frame.RemoveBackEntry(); } private void StackPanel_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (e.ClickCount == 2) { Left = 0; Top = 0; } } private void Setting_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { SettingDialog settingDialog = new SettingDialog(); if (settingDialog.ShowDialog(true) == true) { if (settingDialog.IsAutoStart) { App.RestartApplication(); } } } private void Window_Closing(object sender, CancelEventArgs e) { App.TcpServer?.Dispose(); speech?.Dispose(); } private void Minimize_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { WindowState = WindowState.Minimized; } private void Close_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { var result = MessageBoxX.Show(this, "退出后将无法进行实时检测,是否确认退出程序?", "警告", MessageBoxButton.YesNo, MessageBoxIcon.Warning, DefaultButton.YesOK); if (result == MessageBoxResult.Yes) { Close(); } } private void SwitchToInstance_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (sender is Border instanceBorder && instanceBorder.Tag != null) { App.SwitchToExistingInstance(instanceBorder.Tag.ToString()); } } private void AdjustViewboxStretch() { var screen = System.Windows.Forms.Screen.FromHandle( new System.Windows.Interop.WindowInteropHelper(this).Handle); double aspectRatio = (double)screen.Bounds.Width / screen.Bounds.Height; RootGrid.Height = aspectRatio <= ASPECT_RATIO_THRESHOLD ? 1440 : 1080; } } }