using Prism.Commands; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows; using TeamAAS.Camera.Interfaces; using TeamAAS.Communication; using TeamAAS.Communication.Base; using TeamAAS.Communication.Config; using TeamAAS.Communication.Interfaces; using TeamAAS.Communication.LightSources; using TeamAAS.Communication.Models; using TeamAAS.Feeder.Interfaces; using TeamAAS.Global.Devices; using TeamAAS.Motion; using TeamAAS.Robot.Interfaces; using TeamAAS.Robot.Models; using TeamAAS.Views; namespace TeamAAS.ViewModels { /// /// 设置页 ViewModel(partial 拆分): /// 本文件 = 导航/通讯/调试 + 构造函数 + 全部命令声明; /// 相机/机器人/光源/全局变量/系统设置分别位于 SettingViewModel.*.cs。 /// public partial class SettingViewModel : TeamAAS.BindableBase { private readonly ICameraManager _cameraManager; private readonly IRobotManager _robotManager; private readonly IFeederManager _feederManager; private readonly CommunicationManager _communicationManager; private readonly MotionManager _motionManager; [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); #region 导航 private string _selectedSection = "Camera"; public string SelectedSection { get { return _selectedSection; } set { if (SetProperty(ref _selectedSection, value)) { RaisePropertyChanged(nameof(IsBasicTab)); RaisePropertyChanged(nameof(IsPlcTab)); RaisePropertyChanged(nameof(IsCommSection)); RaisePropertyChanged(nameof(CommTabDisplayText)); UpdateDisplayedCommunications(); RaisePropertyChanged(nameof(PropertyGridSelectedObject)); UpdateDebugMode(); if (value == "Light") { RefreshLightSerialDevices(); } if (value == "ProductData") { RefreshDatabaseHost(); } if (value == "GlobalVariable") { // 进入全局变量页:从 live 列表克隆 Global 变量到编辑器 InitGlobalVarEditor(); } } } } #endregion #region 通讯管理 - 基础属性 public bool IsCommSection => SelectedSection == "BasicComm" || SelectedSection == "PlcComm"; public bool IsBasicTab => SelectedSection == "BasicComm"; public bool IsPlcTab => SelectedSection == "PlcComm"; public string CommTabDisplayText => IsBasicTab ? "基础通讯设备" : "PLC通讯设备"; public ObservableCollection Communications { get; } public ObservableCollection CommunicationTypes { get; } public ObservableCollection PlcTypes { get; } /// 当前Tab下显示的通讯列表 public ObservableCollection DisplayedCommunications { get; } private ICommunication _selectedCommunication; public ICommunication SelectedCommunication { get { return _selectedCommunication; } set { var old = _selectedCommunication as System.ComponentModel.INotifyPropertyChanged; if (old != null) old.PropertyChanged -= OnSelectedDevicePropertyChanged; SetProperty(ref _selectedCommunication, value); var now = _selectedCommunication as System.ComponentModel.INotifyPropertyChanged; if (now != null) now.PropertyChanged += OnSelectedDevicePropertyChanged; RaisePropertyChanged(nameof(IsTcpClientSelected)); RaisePropertyChanged(nameof(IsTcpServerSelected)); RaisePropertyChanged(nameof(IsSerialSelected)); RaisePropertyChanged(nameof(IsUdpSelected)); RaisePropertyChanged(nameof(IsWebSelected)); RaisePropertyChanged(nameof(IsPlcSelected)); RaisePropertyChanged(nameof(HasSelectedCommunication)); RaisePropertyChanged(nameof(PropertyGridSelectedObject)); RaisePropertyChanged(nameof(IsPropertyGridEnabled)); UpdateDebugMode(); } } private void OnSelectedDevicePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ICommunication.IsConnected)) { RaisePropertyChanged(nameof(IsPropertyGridEnabled)); UpdateDebugMode(); } } public bool HasSelectedCommunication => _selectedCommunication != null; /// /// PropertyGrid 绑定对象 - 直接返回当前选中的配置对象 /// 基础Tab 和 PLCTab 使用不同的 Info 子类,PropertyGrid 自动只显示对应属性 /// public object PropertyGridSelectedObject => _selectedCommunication; /// /// PropertyGrid 是否可用(未选中或已连接时禁用) /// public bool IsPropertyGridEnabled => _selectedCommunication != null && !_selectedCommunication.IsConnected; private bool IsType(string typeKey) => SelectedCommunication?.TypeKey?.Contains(typeKey) ?? false; public bool IsTcpClientSelected => IsType("TcpClientCommunication"); public bool IsTcpServerSelected => IsType("TcpServerCommunication"); public bool IsSerialSelected => IsType("SerialCommunication"); public bool IsUdpSelected => IsType("UdpCommunication"); public bool IsWebSelected => IsType("WebCommunication"); public bool IsPlcSelected => SelectedCommunication is PlcCommunicationBase; private string _addTypeKey = ""; public string AddTypeKey { get { return _addTypeKey; } set { SetProperty(ref _addTypeKey, value); } } private string _commMessage = ""; public string CommMessage { get { return _commMessage; } set { SetProperty(ref _commMessage, value); } } /// 基础通讯类型列表(供基础Tab添加用) public ObservableCollection BasicCommTypes { get; } private CommunicationTypeInfo _selectedPlcType; public CommunicationTypeInfo SelectedPlcType { get { return _selectedPlcType; } set { SetProperty(ref _selectedPlcType, value); } } #endregion #region 视觉引擎与主页画面 /// 可用视觉引擎(启动时反射探测:VisionPro / VM / Halcon ...) public System.Collections.Generic.IReadOnlyList AvailableEngines => TeamAAS.Vision.VisualEngineManager.Instance.Engines; private TeamAAS.Vision.IVisualEngine _selectedEngine; /// 当前选中的视觉引擎(一键切换:主页画面/标定页随引擎变化) public TeamAAS.Vision.IVisualEngine SelectedEngine { get { return _selectedEngine ?? TeamAAS.Vision.VisualEngineManager.Instance.Current ?? TeamAAS.Vision.VisualEngineManager.Instance.GetOrLoad(App.SystemConfig?.Vision?.EngineName); } set { if (value == null) return; _selectedEngine = value; RaisePropertyChanged(nameof(SelectedEngine)); if (!TeamAAS.Vision.VisualEngineManager.Instance.SetCurrent(value.EngineName)) return; App.SystemConfig.Vision.EngineName = value.EngineName; ApplyVisionLayout(); App.SaveSystemConfig(); } } /// 按当前配置重建主页画面布局、重注册画面框并保存配置 private void ApplyVisionLayout() { var vision = App.SystemConfig.Vision; var engine = TeamAAS.Vision.VisualEngineManager.Instance.Current; engine?.ApplyHomeLayout(vision.HomePageFrameCount, vision.HomeFrameArrange); TeamAAS.Vision.VisualFrameManager.Clear(); for (int i = 0; i < vision.HomePageFrameCount; i++) TeamAAS.Vision.VisualFrameManager.Register($"home:{i + 1}", vision.GetFrameName(i), "Home"); // 主页 VM 不一定存活,布局数据已持久化;主页下次导航时按配置重建 App.SaveSystemConfig(); } private DelegateCommand _applyVisionLayoutCommand; public DelegateCommand ApplyVisionLayoutCommand => _applyVisionLayoutCommand ?? (_applyVisionLayoutCommand = new DelegateCommand(ApplyVisionLayout)); /// 主页画面数可选规格:均能被最优行列算法精确铺满(无空白框) public int[] HomePageFrameOptions { get; } = { 1, 2, 4, 6, 9 }; /// 主页画面框水平排列(HomeFrameArrange=1) public bool IsHorizontalArrange { get => (App.SystemConfig?.Vision?.HomeFrameArrange ?? 1) == 1; set { if (value && App.SystemConfig?.Vision != null) App.SystemConfig.Vision.HomeFrameArrange = 1; } } /// 主页画面框垂直排列(HomeFrameArrange=2) public bool IsVerticalArrange { get => (App.SystemConfig?.Vision?.HomeFrameArrange ?? 1) == 2; set { if (value && App.SystemConfig?.Vision != null) App.SystemConfig.Vision.HomeFrameArrange = 2; } } #endregion #region 通讯调试 private ICommunication _subscribedDevice; private bool _isPlcDebugMode; public bool IsPlcDebugMode { get { return _isPlcDebugMode; } set { SetProperty(ref _isPlcDebugMode, value); } } private object _currentCommunicationDebugPage; /// 品牌专属调试页(由 [Communication] 特性 DebugPageType 创建);null = 使用通用通讯测试面板 public object CurrentCommunicationDebugPage { get { return _currentCommunicationDebugPage; } private set { SetProperty(ref _currentCommunicationDebugPage, value); } } private ICommunication _debugPageDevice; /// /// 按选中的设备刷新专属调试页;设备实例未变化时不重建(保留页面状态)。 /// private void RefreshCommunicationDebugPage() { var comm = _selectedCommunication; if (comm == _debugPageDevice && (comm != null || _currentCommunicationDebugPage == null)) return; (_currentCommunicationDebugPage as TeamAAS.Communication.UI.ICommunicationDebugPage)?.UnbindCommunication(); _debugPageDevice = null; CurrentCommunicationDebugPage = null; if (comm == null) return; try { var page = TeamAAS.Communication.UI.CommunicationDebugPageFactory.Create(comm); if (page == null) return; _debugPageDevice = comm; CurrentCommunicationDebugPage = page; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"加载通讯调试页失败: {ex.Message}"); } } #endregion #region 相机命令 public DelegateCommand SearchDevicesCommand { get; } public DelegateCommand DeleteCameraCommand { get; } public DelegateCommand ConnectCommand { get; } public DelegateCommand DisconnectCommand { get; } public DelegateCommand StartGrabbingCommand { get; } public DelegateCommand StopGrabbingCommand { get; } public DelegateCommand SaveCameraConfigCommand { get; } #endregion #region 机器人命令 public DelegateCommand AddRobotCommand { get; } public DelegateCommand DeleteRobotCommand { get; } public DelegateCommand ConnectRobotCommand { get; } public DelegateCommand DisconnectRobotCommand { get; } public DelegateCommand SaveRobotConfigCommand { get; } #endregion #region 供料器命令 public DelegateCommand AddFeederCommand { get; } public DelegateCommand DeleteFeederCommand { get; } public DelegateCommand SaveFeederConfigCommand { get; } public DelegateCommand ConnectFeederCommand { get; } public DelegateCommand DisconnectFeederCommand { get; } public DelegateCommand ToggleBacklightCommand { get; } public DelegateCommand FeederDirectionCommand { get; } public DelegateCommand StopFeederActionCommand { get; } public DelegateCommand ExecuteActionCommand { get; } public DelegateCommand DownloadParamCommand { get; } #region 序列集命令 public DelegateCommand AddSequenceCommand { get; } public DelegateCommand DeleteSequenceCommand { get; } public DelegateCommand RenameSequenceCommand { get; } public DelegateCommand AddSequenceStepCommand { get; } public DelegateCommand MoveUpStepCommand { get; } public DelegateCommand MoveDownStepCommand { get; } public DelegateCommand DeleteStepCommand { get; } public DelegateCommand ExecuteSequenceCommand { get; } public DelegateCommand StopSequenceCommand { get; } #endregion #endregion #region 光源命令 public DelegateCommand LightConnectCommand { get; } public DelegateCommand LightDisconnectCommand { get; } public DelegateCommand LightTurnOnAllCommand { get; } public DelegateCommand LightTurnOffAllCommand { get; } public DelegateCommand LightSetBrightnessCommand { get; } public DelegateCommand LightReadBrightnessCommand { get; } public DelegateCommand LightRefreshDevicesCommand { get; } #endregion #region 通讯命令 public DelegateCommand AddCommunicationCommand { get; } public DelegateCommand DeleteCommunicationCommand { get; } public DelegateCommand ConnectCommunicationCommand { get; } public DelegateCommand DisconnectCommunicationCommand { get; } public DelegateCommand SaveCommunicationsCommand { get; } public DelegateCommand RefreshCommunicationCommand { get; } public DelegateCommand ConnectAllCommand { get; } public DelegateCommand DisconnectAllCommand { get; } #endregion #region 控制卡命令 public DelegateCommand AddMotionCardCommand { get; } public DelegateCommand DeleteMotionCardCommand { get; } public DelegateCommand ConnectMotionCardCommand { get; } public DelegateCommand DisconnectMotionCardCommand { get; } public DelegateCommand SaveMotionCardConfigCommand { get; } #endregion public SettingViewModel(ICameraManager cameraManager, IRobotManager robotManager, CommunicationManager communicationManager, IFeederManager feederManager) { _cameraManager = cameraManager; _robotManager = robotManager; _communicationManager = communicationManager; _feederManager = feederManager; _motionManager=MotionManager.Instance; Cameras = new ObservableCollection(); SdkStatus = new ObservableCollection(); Robots = new ObservableCollection(); Feeders = new ObservableCollection(); Communications = new ObservableCollection(); DisplayedCommunications = new ObservableCollection(); CommunicationTypes = new ObservableCollection(); PlcTypes = new ObservableCollection(); BasicCommTypes = new ObservableCollection(); MotionCards = new ObservableCollection(); // 相机命令 SearchDevicesCommand = new DelegateCommand(OpenSearchDialog); DeleteCameraCommand = new DelegateCommand(DeleteCamera); ConnectCommand = new DelegateCommand(async () => await ConnectAsync()); DisconnectCommand = new DelegateCommand(Disconnect); StartGrabbingCommand = new DelegateCommand(StartGrabbing); StopGrabbingCommand = new DelegateCommand(StopGrabbing); SaveCameraConfigCommand = new DelegateCommand(SaveCameraConfig); // 机器人命令 AddRobotCommand = new DelegateCommand(AddRobot); DeleteRobotCommand = new DelegateCommand(DeleteRobot); ConnectRobotCommand = new DelegateCommand(async () => await ConnectRobotAsync()); DisconnectRobotCommand = new DelegateCommand(DisconnectRobot); SaveRobotConfigCommand = new DelegateCommand(SaveRobotConfig); // 供料器命令 AddFeederCommand = new DelegateCommand(AddFeeder); DeleteFeederCommand = new DelegateCommand(DeleteFeeder, () => HasSelectedFeeder); SaveFeederConfigCommand = new DelegateCommand(SaveFeederConfig); ConnectFeederCommand = new DelegateCommand(async () => await ConnectFeederAsync()); DisconnectFeederCommand = new DelegateCommand(DisconnectFeeder); ToggleBacklightCommand = new DelegateCommand(ToggleBacklight); FeederDirectionCommand = new DelegateCommand(async (p) => await ExecuteDirectionAsync(Convert.ToInt32(p))); StopFeederActionCommand = new DelegateCommand(async () => await StopFeederAsync()); ExecuteActionCommand = new DelegateCommand(async () => await ExecuteCurrentActionAsync()); DownloadParamCommand = new DelegateCommand(async () => await DownloadFeederParamsAsync()); AddSequenceCommand = new DelegateCommand(AddSequence); DeleteSequenceCommand = new DelegateCommand(DeleteSequence); RenameSequenceCommand = new DelegateCommand(RenameSequence); AddSequenceStepCommand = new DelegateCommand(AddSequenceStep); MoveUpStepCommand = new DelegateCommand(MoveUpStep); MoveDownStepCommand = new DelegateCommand(MoveDownStep); DeleteStepCommand = new DelegateCommand(DeleteStep); ExecuteSequenceCommand = new DelegateCommand(async () => await ExecuteSequenceAsync()); StopSequenceCommand = new DelegateCommand(StopSequence); // 光源命令 LightConnectCommand = new DelegateCommand(async () => await LightConnectAsync()); LightDisconnectCommand = new DelegateCommand(LightDisconnect); LightTurnOnAllCommand = new DelegateCommand(async () => await LightTurnOnAllAsync()); LightTurnOffAllCommand = new DelegateCommand(async () => await LightTurnOffAllAsync()); LightSetBrightnessCommand = new DelegateCommand(async () => await LightSetBrightnessAsync()); LightReadBrightnessCommand = new DelegateCommand(async () => await LightReadBrightnessAsync()); LightRefreshDevicesCommand = new DelegateCommand(RefreshLightSerialDevices); // 通讯命令 AddCommunicationCommand = new DelegateCommand(AddCommunication); DeleteCommunicationCommand = new DelegateCommand(DeleteCommunication); ConnectCommunicationCommand = new DelegateCommand(async () => await ConnectCommunicationAsync()); DisconnectCommunicationCommand = new DelegateCommand(DisconnectCommunication); SaveCommunicationsCommand = new DelegateCommand(SaveCommunications); RefreshCommunicationCommand = new DelegateCommand(RefreshCommunications); ConnectAllCommand = new DelegateCommand(ConnectAll); DisconnectAllCommand = new DelegateCommand(DisconnectAll); AddMotionCardCommand = new DelegateCommand(AddMotionCard); DeleteMotionCardCommand = new DelegateCommand(DeleteRobot); ConnectMotionCardCommand = new DelegateCommand(async () => await ConnectRobotAsync()); DisconnectMotionCardCommand = new DelegateCommand(DisconnectRobot); SaveMotionCardConfigCommand = new DelegateCommand(SaveRobotConfig); // 全局变量命令 SaveGlobalVarsCommand = new DelegateCommand(SaveGlobalVars); InitGlobalVarEditor(); // 通用设置命令 SaveSystemConfigCommand = new DelegateCommand(SaveSystemConfig); LoadSystemConfigCommand = new DelegateCommand(LoadSystemConfig); // 语言设置命令 RefreshLanguagesCommand = new DelegateCommand(RefreshLanguages); // 全局事件配置命令 SaveGlobalEventConfigCommand = new DelegateCommand(SaveGlobalEventConfig); LoadGlobalEventConfigCommand = new DelegateCommand(LoadGlobalEventConfig); AddHeartbeatCommand = new DelegateCommand(AddHeartbeat); RemoveHeartbeatCommand = new DelegateCommand(RemoveHeartbeat); AddProductSwitchEventCommand = new DelegateCommand(AddProductSwitchEvent); RemoveProductSwitchEventCommand = new DelegateCommand(RemoveProductSwitchEvent); } /// /// 首次导航到设置页时延迟加载(UI 先渲染,数据后台加载) /// public void DelayedLoad() { try { LoadSdkStatus(); } catch { } try { LoadCommunicationTypes(); } catch { } try { LoadPlcTypes(); } catch { } try { LoadBasicCommTypes(); } catch { } try { RefreshCommunications(); } catch { } try { RefreshLightSerialDevices(); } catch { } try { InitLightModelOptions(); } catch { } try { RefreshCameraList(); } catch { } try { RefreshRobotList(); } catch { } try { RefreshFeederList(); } catch { } try { RefreshRecipeList(); } catch { } try { InitGlobalVarEditor(); } catch { } try { TeamAAS.FlowEditor.Execution.ResultRegistry.RefreshGlobals(); } catch { } try { LoadSystemConfig(); } catch { } try { LoadGlobalEventConfig(); } catch { } try { InitializeLanguageSettings(); } catch { } } private void InitLightModelOptions() { LightModelDisplayOptions.Clear(); foreach (LightModel model in Enum.GetValues(typeof(LightModel))) { var field = typeof(LightModel).GetField(model.ToString()); var attr = field?.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false) .OfType() .FirstOrDefault(); string display = attr?.Description ?? model.ToString(); LightModelDisplayOptions.Add(new LightModelDisplay { Model = model, Display = display }); } SelectedLightModelItem = LightModelDisplayOptions.FirstOrDefault(); } #region 通讯基础操作 private void LoadCommunicationTypes() { CommunicationTypes.Clear(); try { var types = _communicationManager.GetAvailableTypes(); foreach (var t in types) { CommunicationTypes.Add(t); } AddTypeKey = CommunicationTypes.FirstOrDefault()?.TypeKey ?? ""; } catch (Exception ex) { CommMessage = $"加载通讯类型失败: {ex.Message}"; } } private void LoadPlcTypes() { PlcTypes.Clear(); try { var types = _communicationManager.GetPlcTypes(); foreach (var t in types) { PlcTypes.Add(t); } } catch (Exception ex) { CommMessage = $"加载 PLC 协议类型失败: {ex.Message}"; } } private void LoadBasicCommTypes() { BasicCommTypes.Clear(); try { var types = _communicationManager.GetBasicTypes(); foreach (var t in types) { BasicCommTypes.Add(t); } } catch { } } private void RefreshCommunications() { Communications.Clear(); try { foreach (var comm in _communicationManager.Devices) { Communications.Add(comm); } } catch (Exception ex) { CommMessage = $"加载通讯列表失败: {ex.Message}"; } UpdateDisplayedCommunications(); RefreshLightSerialDevices(); } private void UpdateDisplayedCommunications() { DisplayedCommunications.Clear(); var list = Communications.ToList(); if (IsBasicTab) { foreach (var c in list) { if (c is PlcCommunicationBase) continue; DisplayedCommunications.Add(c); } } else if (IsPlcTab) { foreach (var c in list) { if (c is PlcCommunicationBase) DisplayedCommunications.Add(c); } } RaisePropertyChanged(nameof(DisplayedCommunications)); } private void AddCommunication() { try { string typeKey; string baseName; if (IsPlcTab) { var plcType = SelectedPlcType; if (plcType == null) { DialogHelper.ShowWarning("请先选择 PLC 协议类型"); return; } typeKey = plcType.TypeKey; baseName = "PLC" + (DisplayedCommunications.Count + 1); } else { if (string.IsNullOrWhiteSpace(AddTypeKey)) { DialogHelper.ShowWarning("请先选择通讯类型"); return; } typeKey = AddTypeKey; baseName = _communicationManager.GetTypeInfo(typeKey)?.DisplayName ?? "通讯" + (DisplayedCommunications.Count + 1); } var index = Communications.Count + 1; var name = GetUniqueCommunicationName(baseName + index); var device = _communicationManager.Create(typeKey, Guid.NewGuid(), name, index); _communicationManager.Register(device); Communications.Add(device); UpdateDisplayedCommunications(); SelectedCommunication = device; CommMessage = $"已添加 {device.Name}"; RefreshLightSerialDevices(); } catch (Exception ex) { DialogHelper.ShowError("添加通讯失败", ex); } } private string GetUniqueCommunicationName(string baseName) { var names = new HashSet( Communications.Select(c => c.Name), StringComparer.OrdinalIgnoreCase); if (!names.Contains(baseName)) return baseName; int n = 1; string candidate; do { candidate = $"{baseName}({n})"; n++; } while (names.Contains(candidate)); return candidate; } private void DeleteCommunication() { if (SelectedCommunication == null) return; if (!DialogHelper.ConfirmYesNo( $"确认删除 \"{SelectedCommunication.Name}\" 吗?", "确认删除")) { return; } var toRemove = SelectedCommunication; try { _communicationManager.Remove(toRemove.Id); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"移除通讯设备异常: {ex.Message}"); } Communications.Remove(toRemove); DisplayedCommunications.Remove(toRemove); for (int i = 0; i < Communications.Count; i++) { Communications[i].Index = i + 1; } SelectedCommunication = null; RefreshLightSerialDevices(); } private async Task ConnectCommunicationAsync() { if (SelectedCommunication == null) return; try { await SelectedCommunication.ConnectAsync(); UpdateDebugMode(); RaisePropertyChanged(nameof(IsPropertyGridEnabled)); CommMessage = SelectedCommunication.IsConnected ? $"已连接 {SelectedCommunication.Name}" : $"连接失败 {SelectedCommunication.Name}"; } catch (Exception ex) { CommMessage = $"连接异常: {ex.Message}"; } } private void DisconnectCommunication() { if (SelectedCommunication == null) return; try { SelectedCommunication.Disconnect(); RaisePropertyChanged(nameof(IsPropertyGridEnabled)); CommMessage = $"已断开 {SelectedCommunication.Name}"; } catch (Exception ex) { CommMessage = $"断开异常: {ex.Message}"; } } private void ConnectAll() { try { foreach (var device in Communications) { try { device.Connect(); } catch { } } CommMessage = "已连接所有设备"; } catch (Exception ex) { CommMessage = $"批量连接异常: {ex.Message}"; } } private void DisconnectAll() { try { foreach (var device in Communications) { try { device.Disconnect(); } catch { } } CommMessage = "已断开所有设备"; } catch (Exception ex) { CommMessage = $"批量断开异常: {ex.Message}"; } } private void SaveCommunications() { try { if (_communicationManager.SaveConfig()) CommMessage = "通讯配置已保存"; else DialogHelper.ShowError("通讯配置保存失败,请查看日志"); } catch (Exception ex) { DialogHelper.ShowError("保存通讯配置失败", ex); } } #endregion #region 通讯调试 private void UpdateDebugMode() { if (_subscribedDevice != null) { _subscribedDevice.DataReceivedEvent -= OnDataReceived; _subscribedDevice = null; } if (_selectedCommunication == null) { IsPlcDebugMode = false; return; } IsPlcDebugMode = _selectedCommunication is PlcCommunicationBase; var comm = _communicationManager.Get(_selectedCommunication.Id); if (comm != null) { _subscribedDevice = comm; _subscribedDevice.DataReceivedEvent += OnDataReceived; } RefreshCommunicationDebugPage(); } private void OnDataReceived(object sender, string data) { var comm = _selectedCommunication as BindableCommunicationBase; if (comm == null) return; Application.Current.Dispatcher.Invoke(() => { comm.AppendLog($"RX: {data}"); }); } #endregion } }