| | |
| | | using Plugin.DeviceConfig.Entity; |
| | | using Plugin.DeviceConfig.Model; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Collections.ObjectModel; |
| | | using System.Linq; |
| | | using System.Windows; |
| | |
| | | private DeviceDefinition _device; |
| | | private ObservableCollection<DeviceChannel> _channels = new(); |
| | | private bool _isNew; |
| | | private bool _execModeSyncing; |
| | | |
| | | public DeviceEditModal() |
| | | { |
| | |
| | | cbTransport.ItemsSource = Enum.GetValues(typeof(TransportType)).Cast<TransportType>().ToList(); |
| | | cbTransport.SelectionChanged += CbTransport_SelectionChanged; |
| | | |
| | | // 执行位置:仲裁域/BrokerId 候选由配置枚举 |
| | | cbAuthority.ItemsSource = V3AuthorityCatalog.AuthorityIds; |
| | | cbBrokerId.ItemsSource = V3AuthorityCatalog.BrokerIds; |
| | | tbOwnerProcessId.Text = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); |
| | | |
| | | // 填充数据 |
| | | tbDeviceId.Text = device.DeviceId; |
| | | tbDeviceId.IsReadOnly = !_isNew; |
| | |
| | | tbDriverType.Text = device.DriverType; |
| | | cbTransport.SelectedItem = device.Transport; |
| | | tbEndpoint.Text = device.Endpoint; |
| | | |
| | | // 执行位置回显:旧数据 AuthorityId 为空时默认本机 Local 域 |
| | | _execModeSyncing = true; |
| | | rbLocal.IsChecked = device.ExecutionMode == ExecutionModeKind.Local; |
| | | rbBroker.IsChecked = device.ExecutionMode == ExecutionModeKind.Broker; |
| | | cbAuthority.SelectedItem = string.IsNullOrWhiteSpace(device.AuthorityId) |
| | | ? V3AuthorityCatalog.LocalAuthorityId : device.AuthorityId; |
| | | if (cbAuthority.SelectedItem == null) |
| | | { |
| | | // 存量值不在当前候选枚举中:保留展示并追加 |
| | | var list = V3AuthorityCatalog.AuthorityIds.ToList(); |
| | | list.Add(device.AuthorityId); |
| | | cbAuthority.ItemsSource = list; |
| | | cbAuthority.SelectedItem = device.AuthorityId; |
| | | } |
| | | cbBrokerId.SelectedItem = device.BrokerId; |
| | | if (!string.IsNullOrWhiteSpace(device.OwnerProcessId)) |
| | | { |
| | | tbOwnerProcessId.Text = device.OwnerProcessId; |
| | | } |
| | | _execModeSyncing = false; |
| | | RefreshExecModeUi(); |
| | | RefreshSessionKeyCandidates(); |
| | | |
| | | // 默认驱动类型建议 & 端点提示 |
| | | UpdateDriverHint(); |
| | |
| | | _channels = new ObservableCollection<DeviceChannel>(); |
| | | } |
| | | channelListView.ItemsSource = _channels; |
| | | |
| | | // 通道 SessionKey 已加载,重新生成会话键候选(Broker 模式) |
| | | RefreshSessionKeyCandidates(); |
| | | |
| | | return ShowDialog(); |
| | | } |
| | |
| | | |
| | | #endregion |
| | | |
| | | #region 执行位置(V3.0 §05) |
| | | |
| | | /// <summary>当前是否 Broker 模式</summary> |
| | | private bool IsBrokerMode => rbBroker.IsChecked == true; |
| | | |
| | | private void ExecMode_Changed(object sender, RoutedEventArgs e) |
| | | { |
| | | // XAML 初始化阶段 rbLocal.IsChecked=True 会提前触发,此时其余控件字段尚未创建 |
| | | if (_execModeSyncing || rbBroker == null || cbAuthority == null) return; |
| | | _execModeSyncing = true; |
| | | // Broker 设备的 AuthorityId 即其 Broker 域;切回 Local 时使用本机默认域 |
| | | if (IsBrokerMode) |
| | | { |
| | | // 当前仍为 local 域(或未选)→ 切到首个 Broker 域 |
| | | if (V3AuthorityCatalog.IsLocalAuthority(cbAuthority.SelectedItem as string) || cbAuthority.SelectedItem == null) |
| | | { |
| | | cbAuthority.SelectedItem = V3AuthorityCatalog.BrokerIds.FirstOrDefault() |
| | | ?? V3AuthorityCatalog.AuthorityIds.LastOrDefault(a => !V3AuthorityCatalog.IsLocalAuthority(a)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | cbAuthority.SelectedItem = V3AuthorityCatalog.LocalAuthorityId; |
| | | } |
| | | _execModeSyncing = false; |
| | | RefreshExecModeUi(); |
| | | } |
| | | |
| | | private void CbAuthority_SelectionChanged(object sender, SelectionChangedEventArgs e) |
| | | { |
| | | if (_execModeSyncing || rbLocal == null || rbBroker == null || cbBrokerId == null) return; |
| | | var auth = cbAuthority.SelectedItem as string; |
| | | if (string.IsNullOrEmpty(auth)) return; |
| | | _execModeSyncing = true; |
| | | // 选中 local- 域→Local;选中 Broker 域→Broker 并同步 BrokerId |
| | | if (V3AuthorityCatalog.IsLocalAuthority(auth)) |
| | | { |
| | | rbLocal.IsChecked = true; |
| | | } |
| | | else |
| | | { |
| | | rbBroker.IsChecked = true; |
| | | cbBrokerId.SelectedItem = V3AuthorityCatalog.BrokerIds.Contains(auth) ? auth : cbBrokerId.SelectedItem; |
| | | } |
| | | _execModeSyncing = false; |
| | | RefreshExecModeUi(); |
| | | } |
| | | |
| | | /// <summary>根据模式切换 Broker 字段可见性与只读派生的访问策略</summary> |
| | | private void RefreshExecModeUi() |
| | | { |
| | | spBroker.Visibility = IsBrokerMode ? Visibility.Visible : Visibility.Collapsed; |
| | | tbAccessPolicy.Text = (IsBrokerMode ? ExecutionModeKind.Broker : ExecutionModeKind.Local) |
| | | switch |
| | | { |
| | | ExecutionModeKind.Broker => "共享仲裁 · Broker SessionGate 代理", |
| | | _ => "独占直连 · 本进程持有物理会话" |
| | | }; |
| | | if (IsBrokerMode) |
| | | { |
| | | RefreshSessionKeyCandidates(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// PhysicalSessionKey 候选:由端点规范化的写死字符串(V3.0 §04:地址别名必须归一为同一物理身份) |
| | | /// </summary> |
| | | private void RefreshSessionKeyCandidates() |
| | | { |
| | | var deviceId = tbDeviceId.Text?.Trim() ?? string.Empty; |
| | | var transport = cbTransport.SelectedItem is TransportType tt ? tt : TransportType.Tcp; |
| | | var endpoint = tbEndpoint.Text?.Trim() ?? string.Empty; |
| | | |
| | | var candidates = new List<string>(); |
| | | if (transport == TransportType.Can) |
| | | { |
| | | var bus = endpoint.Split(':')[0].Trim(); |
| | | if (!string.IsNullOrEmpty(bus)) candidates.Add($"can/{bus}/main"); |
| | | } |
| | | else if (transport == TransportType.Tcp && endpoint.Contains(":")) |
| | | { |
| | | candidates.Add($"tcp/{endpoint}/main"); |
| | | } |
| | | else if (transport == TransportType.Serial) |
| | | { |
| | | var com = endpoint.Split(':')[0].Trim(); |
| | | if (!string.IsNullOrEmpty(com)) candidates.Add($"serial/{com}/main"); |
| | | } |
| | | else if (transport == TransportType.Visa) |
| | | { |
| | | candidates.Add($"visa/{deviceId}/main"); |
| | | } |
| | | else if (transport == TransportType.SDK) |
| | | { |
| | | candidates.Add($"sdk/{deviceId}/main"); |
| | | } |
| | | if (!string.IsNullOrEmpty(deviceId)) candidates.Add($"{deviceId}/main"); |
| | | |
| | | // 已存通道 SessionKey 也列入候选(去重) |
| | | foreach (var key in _channels.Select(c => c.SessionKey) |
| | | .Where(k => !string.IsNullOrWhiteSpace(k)).Distinct()) |
| | | { |
| | | candidates.Add(key); |
| | | } |
| | | candidates = candidates.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); |
| | | |
| | | string? current = _device?.PhysicalSessionKey; |
| | | cbSessionKey.ItemsSource = candidates; |
| | | cbSessionKey.Text = !string.IsNullOrWhiteSpace(current) ? current |
| | | : (candidates.Count > 0 ? candidates[0] : string.Empty); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 保存/取消 |
| | | |
| | | private void SaveBtn_Click(object sender, RoutedEventArgs e) |
| | |
| | | } |
| | | } |
| | | |
| | | // 执行位置(V3.0 §05)校验 |
| | | var authorityId = cbAuthority.SelectedItem as string ?? string.Empty; |
| | | if (string.IsNullOrWhiteSpace(authorityId)) |
| | | { |
| | | ToastHelper.Warning("仲裁域不能为空,请从候选中选择"); |
| | | return; |
| | | } |
| | | string brokerId = string.Empty, sessionKey = string.Empty, ownerPid = string.Empty; |
| | | if (IsBrokerMode) |
| | | { |
| | | brokerId = cbBrokerId.SelectedItem as string ?? string.Empty; |
| | | if (string.IsNullOrWhiteSpace(brokerId)) |
| | | { |
| | | ToastHelper.Warning("共享仪器(Broker)必须指定 BrokerId"); |
| | | return; |
| | | } |
| | | sessionKey = cbSessionKey.Text?.Trim() ?? string.Empty; |
| | | if (string.IsNullOrWhiteSpace(sessionKey)) |
| | | { |
| | | ToastHelper.Warning("共享仪器(Broker)必须指定物理会话键"); |
| | | return; |
| | | } |
| | | ownerPid = tbOwnerProcessId.Text?.Trim() ?? string.Empty; |
| | | } |
| | | |
| | | // 保存设备 |
| | | _device.DeviceId = deviceId; |
| | | _device.Kind = kind; |
| | | _device.DriverType = tbDriverType.Text?.Trim() ?? string.Empty; |
| | | _device.Transport = transport; |
| | | _device.Endpoint = endpoint; |
| | | _device.ExecutionMode = IsBrokerMode ? ExecutionModeKind.Broker : ExecutionModeKind.Local; |
| | | _device.AuthorityId = authorityId; |
| | | _device.BrokerId = brokerId; |
| | | _device.PhysicalSessionKey = sessionKey; |
| | | _device.OwnerProcessId = ownerPid; |
| | | |
| | | _model.SaveDevice(_device); |
| | | |
| | |
| | | private void CbTransport_SelectionChanged(object sender, SelectionChangedEventArgs e) |
| | | { |
| | | UpdateEndpointHint(); |
| | | if (IsBrokerMode) |
| | | { |
| | | RefreshSessionKeyCandidates(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |