mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-22 17:50:03 +08:00
d1a20c493b
Added missing JSON definitions. Replaced all instacnes of XmlElement with XElement (XmlElement not serializable). Replaced all instances of XmlAttribute with string (XmlAttribute not serializable). Removed all pInvoke calls not supported in UWP apps (DNS Server lookups, HTTP access rules and X509 extentions no longer supported).
566 lines
20 KiB
C#
566 lines
20 KiB
C#
/* Copyright (c) 1996-2016, OPC Foundation. All rights reserved.
|
|
The source code in this file is covered under a dual-license scenario:
|
|
- RCL: for OPC Foundation members in good-standing
|
|
- GPL V2: everybody else
|
|
RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/
|
|
GNU General Public License as published by the Free Software Foundation;
|
|
version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2
|
|
This source code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
using Opc.Ua.Client;
|
|
using Opc.Ua.Client.Controls;
|
|
using Opc.Ua.Configuration;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Windows.UI.Core;
|
|
using Windows.UI.Xaml.Media;
|
|
using Windows.UI.Text;
|
|
using Windows.UI;
|
|
using System.Threading;
|
|
|
|
namespace Opc.Ua.SampleClient
|
|
{
|
|
/// <summary>
|
|
/// An empty page that can be used on its own or navigated to within a Frame.
|
|
/// </summary>
|
|
public partial class ClientPage : Page
|
|
{
|
|
#region Private Fields
|
|
private Session m_session;
|
|
private ApplicationInstance m_application;
|
|
private Opc.Ua.Server.StandardServer m_server;
|
|
private ConfiguredEndpointCollection m_endpoints;
|
|
private ApplicationConfiguration m_configuration;
|
|
private ServiceMessageContext m_context;
|
|
private ClientPage m_masterPage;
|
|
private List<ClientPage> m_pages;
|
|
#endregion
|
|
|
|
public ClientPage()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
public ClientPage(
|
|
ServiceMessageContext context,
|
|
ApplicationInstance application,
|
|
ClientPage masterPage,
|
|
ApplicationConfiguration configuration)
|
|
{
|
|
InitializeComponent();
|
|
|
|
if (!configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates)
|
|
{
|
|
configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
|
}
|
|
|
|
m_masterPage = masterPage;
|
|
m_context = context;
|
|
m_application = application;
|
|
m_server = application.Server as Opc.Ua.Server.StandardServer;
|
|
|
|
if (m_masterPage == null)
|
|
{
|
|
m_pages = new List<ClientPage>();
|
|
}
|
|
|
|
m_configuration = configuration;
|
|
|
|
SessionsCTRL.Configuration = configuration;
|
|
SessionsCTRL.MessageContext = context;
|
|
SessionsCTRL.AddressSpaceCtrl = BrowseCTRL;
|
|
SessionsCTRL.NodeSelected += SessionCtrl_NodeSelected;
|
|
|
|
// get list of cached endpoints.
|
|
m_endpoints = m_configuration.LoadCachedEndpoints(true);
|
|
m_endpoints.DiscoveryUrls = configuration.ClientConfiguration.WellKnownDiscoveryUrls;
|
|
|
|
// hook up endpoint selector
|
|
EndpointSelectorCTRL.Initialize(m_endpoints, m_configuration);
|
|
EndpointSelectorCTRL.ConnectEndpoint += EndpointSelectorCTRL_ConnectEndpoint;
|
|
EndpointSelectorCTRL.EndpointsChanged += EndpointSelectorCTRL_OnChange;
|
|
|
|
BrowseCTRL.SessionTreeCtrl = SessionsCTRL;
|
|
BrowseCTRL.NodeSelected += BrowseCTRL_NodeSelected;
|
|
|
|
// exception dialog
|
|
GuiUtils.ExceptionMessageDlg += ExceptionMessageDlg;
|
|
|
|
ServerUrlTB.Text = "None";
|
|
}
|
|
|
|
void RemoveAllClickEventsFromButton()
|
|
{
|
|
CommandBTN.Click -= ContextMenu_OnDelete;
|
|
CommandBTN.Click -= ContextMenu_OnCancel;
|
|
CommandBTN.Click -= ContextMenu_OnDisconnect;
|
|
CommandBTN.Click -= ContextMenu_OnReport;
|
|
}
|
|
|
|
private void SessionCtrl_NodeSelected(object sender, TreeNodeActionEventArgs e)
|
|
{
|
|
if (e.Node != null)
|
|
{
|
|
MonitoredItem item = e.Node as MonitoredItem;
|
|
if (e.Node is MonitoredItem)
|
|
{
|
|
CommandBTN.Visibility = Visibility.Visible;
|
|
CommandBTN.Content = "Delete";
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Click += ContextMenu_OnDelete;
|
|
CommandBTN.Tag = e.Node;
|
|
}
|
|
else if (e.Node is Subscription)
|
|
{
|
|
CommandBTN.Visibility = Visibility.Visible;
|
|
CommandBTN.Content = "Cancel";
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Click += ContextMenu_OnCancel;
|
|
CommandBTN.Tag = e.Node;
|
|
}
|
|
else if (e.Node is Session)
|
|
{
|
|
CommandBTN.Visibility = Visibility.Visible;
|
|
CommandBTN.Content = "Disconnect";
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Click += ContextMenu_OnDisconnect;
|
|
CommandBTN.Tag = e.Node;
|
|
|
|
// Update current session object
|
|
m_session = (Session)e.Node;
|
|
}
|
|
else
|
|
{
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Visibility = Visibility.Collapsed;
|
|
CommandBTN.Tag = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BrowseCTRL_NodeSelected(object sender, TreeNodeActionEventArgs e)
|
|
{
|
|
if (e.Node != null)
|
|
{
|
|
ReferenceDescription reference = e.Node as ReferenceDescription;
|
|
if (reference != null && reference.NodeClass == NodeClass.Variable)
|
|
{
|
|
CommandBTN.Visibility = Visibility.Visible;
|
|
CommandBTN.Content = "Report";
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Click += ContextMenu_OnReport;
|
|
CommandBTN.Tag = e.Node;
|
|
}
|
|
else
|
|
{
|
|
RemoveAllClickEventsFromButton();
|
|
CommandBTN.Visibility = Visibility.Collapsed;
|
|
CommandBTN.Tag = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ContextMenu_OnDisconnect(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
SessionsCTRL.Delete(CommandBTN.Tag as Session);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void ContextMenu_OnCancel(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
SessionsCTRL.Delete(CommandBTN.Tag as Subscription);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void ContextMenu_OnDelete(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
var monitoredItem = CommandBTN.Tag as MonitoredItem;
|
|
if (monitoredItem == null)
|
|
return;
|
|
var subscription = monitoredItem.Subscription;
|
|
SessionsCTRL.Delete(monitoredItem);
|
|
if (subscription.MonitoredItemCount == 0)
|
|
{
|
|
// Remove subscription if no more items
|
|
CommandBTN.Tag = subscription;
|
|
ContextMenu_OnCancel(sender, e);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void ContextMenu_OnReport(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// can only subscribe to local variables.
|
|
ReferenceDescription reference = CommandBTN.Tag as ReferenceDescription;
|
|
if (m_session != null && reference != null)
|
|
{
|
|
CreateMonitoredItem(
|
|
m_session, null, (NodeId)reference.NodeId, MonitoringMode.Reporting);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
|
{
|
|
ManualResetEvent ev = new ManualResetEvent(false);
|
|
Dispatcher.RunAsync(
|
|
CoreDispatcherPriority.Normal,
|
|
async () =>
|
|
{
|
|
await GuiUtils.HandleCertificateValidationError(this, validator, e);
|
|
ev.Set();
|
|
}
|
|
).AsTask().Wait();
|
|
ev.WaitOne();
|
|
}
|
|
|
|
public void OpenPage()
|
|
{
|
|
if (m_masterPage == null)
|
|
{
|
|
ClientPage page = new ClientPage(m_context, m_application, this, m_configuration);
|
|
m_pages.Add(page);
|
|
page.Unloaded += Window_PageClosing;
|
|
}
|
|
else
|
|
{
|
|
m_masterPage.OpenPage();
|
|
}
|
|
}
|
|
|
|
async void Window_PageClosing(object sender, RoutedEventArgs e)
|
|
{
|
|
if (m_masterPage == null && m_pages.Count > 0)
|
|
{
|
|
MessageDlg dialog = new MessageDlg("Close all sessions?", MessageDlgButton.Yes, MessageDlgButton.No);
|
|
MessageDlgButton result = await dialog.ShowAsync();
|
|
if (result != MessageDlgButton.Yes)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
BrowseCTRL.Clear();
|
|
|
|
for (int ii = 0; ii < m_pages.Count; ii++)
|
|
{
|
|
if (Object.ReferenceEquals(m_pages[ii], sender))
|
|
{
|
|
m_pages.RemoveAt(ii);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides a user defined method.
|
|
/// </summary>
|
|
protected virtual async void DoTest(Session session)
|
|
{
|
|
MessageDlg dialog = new MessageDlg("A handy place to put test code.");
|
|
await dialog.ShowAsync();
|
|
}
|
|
|
|
async Task EndpointSelectorCTRL_ConnectEndpoint(object sender, ConnectEndpointEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// disable Connect while connecting button
|
|
EndpointSelectorCTRL.IsEnabled = false;
|
|
// Connect
|
|
e.UpdateControl = await Connect(e.Endpoint);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
e.UpdateControl = false;
|
|
}
|
|
finally
|
|
{
|
|
// enable Connect button
|
|
EndpointSelectorCTRL.IsEnabled = true;
|
|
}
|
|
}
|
|
|
|
private void EndpointSelectorCTRL_OnChange(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
m_endpoints.Save();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects to a server.
|
|
/// </summary>
|
|
public async Task<bool> Connect(ConfiguredEndpoint endpoint)
|
|
{
|
|
bool result = false;
|
|
if (endpoint == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// connect dialogs
|
|
Session session = await SessionsCTRL.Connect(endpoint);
|
|
|
|
if (session != null)
|
|
{
|
|
//hook up new session
|
|
session.KeepAlive += new KeepAliveEventHandler(StandardClient_KeepAlive);
|
|
StandardClient_KeepAlive(session, null);
|
|
|
|
// BrowseCTRL.SetView(session, BrowseViewType.Objects, null);
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the status control when a keep alive event occurs.
|
|
/// </summary>
|
|
async void StandardClient_KeepAlive(Session sender, KeepAliveEventArgs e)
|
|
{
|
|
if (!Dispatcher.HasThreadAccess)
|
|
{
|
|
await Dispatcher.RunAsync(
|
|
CoreDispatcherPriority.Normal,
|
|
() =>
|
|
{
|
|
StandardClient_KeepAlive( sender, e);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (sender != null && sender.Endpoint != null)
|
|
{
|
|
ServerUrlTB.Text = Utils.Format(
|
|
"{0} ({1}) {2}",
|
|
sender.Endpoint.EndpointUrl,
|
|
sender.Endpoint.SecurityMode,
|
|
(sender.EndpointConfiguration.UseBinaryEncoding) ? "UABinary" : "XML");
|
|
}
|
|
else
|
|
{
|
|
ServerUrlTB.Text = "None";
|
|
}
|
|
|
|
if (e != null && m_session != null)
|
|
{
|
|
SessionsCTRL.UpdateSessionNode(m_session);
|
|
|
|
if (ServiceResult.IsGood(e.Status))
|
|
{
|
|
ServerStatusTB.Text = Utils.Format(
|
|
"Server Status: {0} {1:yyyy-MM-dd HH:mm:ss} {2}/{3}",
|
|
e.CurrentState,
|
|
e.CurrentTime.ToLocalTime(),
|
|
m_session.OutstandingRequestCount,
|
|
m_session.DefunctRequestCount);
|
|
ServerStatusTB.Foreground = new SolidColorBrush(Colors.Black);
|
|
ServerStatusTB.FontWeight = FontWeights.Normal;
|
|
}
|
|
else
|
|
{
|
|
ServerStatusTB.Text = String.Format(
|
|
"{0} {1}/{2}", e.Status,
|
|
m_session.OutstandingRequestCount,
|
|
m_session.DefunctRequestCount);
|
|
ServerStatusTB.Foreground = new SolidColorBrush(Colors.Red);
|
|
ServerStatusTB.FontWeight = FontWeights.Bold;
|
|
}
|
|
}
|
|
}
|
|
|
|
async void ExceptionMessageDlg(string message)
|
|
{
|
|
await Dispatcher.RunAsync(
|
|
CoreDispatcherPriority.Normal,
|
|
async () =>
|
|
{
|
|
MessageDlg dialog = new MessageDlg(message);
|
|
await dialog.ShowAsync();
|
|
});
|
|
}
|
|
|
|
private void MainPage_PageClosing(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
SessionsCTRL.Close();
|
|
|
|
if (m_masterPage == null)
|
|
{
|
|
m_application.Stop();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void DiscoverServersMI_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
ConfiguredEndpoint endpoint = new ConfiguredServerListDlg().ShowDialog(m_configuration, true);
|
|
|
|
if (endpoint != null)
|
|
{
|
|
EndpointSelectorCTRL.SelectedEndpoint = endpoint;
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void NewWindowMI_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
this.OpenPage();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private void Discovery_RegisterMI_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (m_server != null)
|
|
{
|
|
OnRegister(null);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
private async void OnRegister(object sender)
|
|
{
|
|
try
|
|
{
|
|
Opc.Ua.Server.StandardServer server = m_server;
|
|
|
|
if (server != null)
|
|
{
|
|
await server.RegisterWithDiscoveryServer();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Utils.Trace(exception, "Could not register with the LDS");
|
|
}
|
|
}
|
|
|
|
public void CreateMonitoredItem(
|
|
Session session, Subscription subscription, NodeId nodeId, MonitoringMode mode)
|
|
{
|
|
if (subscription == null)
|
|
{
|
|
subscription = session.DefaultSubscription;
|
|
if (session.AddSubscription(subscription))
|
|
subscription.Create();
|
|
}
|
|
else
|
|
{
|
|
session.AddSubscription(subscription);
|
|
}
|
|
|
|
// add the new monitored item.
|
|
MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem);
|
|
|
|
monitoredItem.StartNodeId = nodeId;
|
|
monitoredItem.AttributeId = Attributes.Value;
|
|
monitoredItem.DisplayName = nodeId.Identifier.ToString();
|
|
monitoredItem.MonitoringMode = mode;
|
|
monitoredItem.SamplingInterval = mode == MonitoringMode.Sampling ? 1000 : 0;
|
|
monitoredItem.QueueSize = 0;
|
|
monitoredItem.DiscardOldest = true;
|
|
|
|
monitoredItem.Notification += new MonitoredItemNotificationEventHandler(MonitoredItem_Notification);
|
|
subscription.AddItem(monitoredItem);
|
|
subscription.ApplyChanges();
|
|
}
|
|
|
|
private async void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
|
|
{
|
|
if (e.NotificationValue == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() =>
|
|
{
|
|
try
|
|
{
|
|
XmlEncoder encoder = new XmlEncoder(monitoredItem.Subscription.Session.MessageContext);
|
|
e.NotificationValue.Encode(encoder);
|
|
ServerStatusTB.Text = encoder.Close();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Utils.Trace(ex, "Error processing monitored item notification.");
|
|
}
|
|
});
|
|
}
|
|
|
|
private void Task_TestMI_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
DoTest(m_session);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
GuiUtils.HandleException(String.Empty, GuiUtils.CallerName(), exception);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|