//============================================================================= // Siemens AG // (c)Copyright (2017) All Rights Reserved //----------------------------------------------------------------------------- // Tested with: Windows 7 Ultimate x64 // Engineering: Visual Studio 2013 // Functionality: Wrapps up important classes/methods of the OPC UA .NET Stack to help // with simple client implementation //----------------------------------------------------------------------------- // Change log table: // Version Date Expert in charge Changes applied // 01.00.00 31.08.2016 (Siemens) First released version // 01.01.00 22.02.2017 (Siemens) Implement user authentication, SHA256 Cert, Basic256Rsa256 connection, // Basic256Rsa256 connections, read/write structs/UDTs //============================================================================= using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IdentityModel; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Opc.Ua; using Opc.Ua.Client; using Xylem.Common.Ui.Siemens.UAClientHelper; using NLog; using NLog.Config; namespace Xylem.Common.Ui.Siemens.UAClient1500Demo { public partial class UAClientForm : Form { /// /// Fields /// #region Fields private Session mySession; private Subscription mySubscription; private UAClientHelperAPI myClientHelperAPI; private EndpointDescription mySelectedEndpoint; private MonitoredItem myMonitoredItem; private List myRegisteredNodeIdStrings; private ReferenceDescriptionCollection referenceDescriptionCollection; private List myStructList; private UAClientCertForm myCertForm; private Int16 itemCount; #endregion /// /// Form Construction /// #region Construction public UAClientForm() { InitializeComponent(); myClientHelperAPI = new UAClientHelperAPI(); myRegisteredNodeIdStrings = new List(); browsePage.Enabled = false; rwPage.Enabled = false; subscribePage.Enabled = false; structPage.Enabled = false; itemCount = 0; } #endregion /// /// Event handlers called by the UI /// #region UserInteractionHandlers private void EndpointButton_Click(Object sender, EventArgs e) { endpointListView.Items.Clear(); //The local discovery URL for the discovery server String discoveryUrl = discoveryTextBox.Text; try { ApplicationDescriptionCollection servers = myClientHelperAPI.FindServers(discoveryUrl); foreach (ApplicationDescription ad in servers) { foreach (String url in ad.DiscoveryUrls) { EndpointDescriptionCollection endpoints = myClientHelperAPI.GetEndpoints(url); foreach (EndpointDescription ep in endpoints) { String securityPolicy = ep.SecurityPolicyUri.Remove(0, 42); endpointListView.Items.Add("[" + ad.ApplicationName + "] " + " [" + ep.SecurityMode + "] " + " [" + securityPolicy + "] " + " [" + ep.EndpointUrl + "]").Tag = ep; } } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void SubscribeButton_Click(Object sender, EventArgs e) { //this example only supports one item per subscription; remove the following IF loop to add more items if (myMonitoredItem != null) { try { myMonitoredItem = myClientHelperAPI.RemoveMonitoredItem(mySubscription, myMonitoredItem); } catch { //ignore ; } } try { //use different item names for correct assignment at the notificatino event itemCount++; String monitoredItemName = "myItem" + itemCount.ToString(); if (mySubscription == null) { mySubscription = myClientHelperAPI.Subscribe(2000); } myMonitoredItem = myClientHelperAPI.AddMonitoredItem(mySubscription, subscriptionIdTextBox.Text, monitoredItemName, 1); myClientHelperAPI.ItemChangedNotification += new MonitoredItemNotificationEventHandler(Notification_MonitoredItem); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void EpConnectButton_Click(Object sender, EventArgs e) { if (mySession != null && !mySession.Disposed) { try { mySubscription.Delete(true); } catch { ; } myClientHelperAPI.Disconnect(); mySession = myClientHelperAPI.Session; ResetUI(); } else { try { myClientHelperAPI.KeepAliveNotification += new KeepAliveEventHandler(Notification_KeepAlive); myClientHelperAPI.CertificateValidationNotification += new CertificateValidationEventHandler(Notification_ServerCertificate); myClientHelperAPI.Connect(mySelectedEndpoint, userPwButton.Checked, userTextBox.Text, pwTextBox.Text); mySession = myClientHelperAPI.Session; epConnectServerButton.Text = "Disconnect from server"; browsePage.Enabled = true; rwPage.Enabled = true; subscribePage.Enabled = true; structPage.Enabled = true; myCertForm = null; } catch (Exception ex) { myCertForm = null; MessageBox.Show(ex.Message, "Error"); } } } private void WriteValButton_Click(Object sender, EventArgs e) { List values = new List(); List nodeIdStrings = new List(); values.Add(writeTextBox.Text); nodeIdStrings.Add(writeIdTextBox.Text); try { myClientHelperAPI.WriteValues(values, nodeIdStrings); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void UnsubscribeButton_Click(Object sender, EventArgs e) { myClientHelperAPI.RemoveSubscription(mySubscription); mySubscription = null; itemCount = 0; subscriptionLabel.Text = ""; } private void NodeTreeView_BeforeSelect(Object sender, TreeViewCancelEventArgs e) { descriptionGridView.Rows.Clear(); try { ReferenceDescription refDesc = (ReferenceDescription)e.Node.Tag; Node node = myClientHelperAPI.ReadNode(refDesc.NodeId.ToString()); VariableNode variableNode = new VariableNode(); String[] row1 = new String[] { "Node Id", refDesc.NodeId.ToString() }; String[] row2 = new String[] { "Namespace Index", refDesc.NodeId.NamespaceIndex.ToString() }; String[] row3 = new String[] { "Identifier Type", refDesc.NodeId.IdType.ToString() }; String[] row4 = new String[] { "Identifier", refDesc.NodeId.Identifier.ToString() }; String[] row5 = new String[] { "Browse Name", refDesc.BrowseName.ToString() }; String[] row6 = new String[] { "Display Name", refDesc.DisplayName.ToString() }; String[] row7 = new String[] { "Node Class", refDesc.NodeClass.ToString() }; String[] row8 = new String[] { "Description", "null" }; try { row8 = new String[] { "Description", node.Description.ToString() }; } catch { row8 = new String[] { "Description", "null" }; } String[] row9 = new String[] { "Type Definition", refDesc.TypeDefinition.ToString() }; String[] row10 = new String[] { "Write Mask", node.WriteMask.ToString() }; String[] row11 = new String[] { "User Write Mask", node.UserWriteMask.ToString() }; if (node.NodeClass == NodeClass.Variable) { variableNode = (VariableNode)node.DataLock; List nodeIds = new List(); List displayNames = new List(); List errors = new List(); NodeId nodeId = new NodeId(variableNode.DataType); nodeIds.Add(nodeId); mySession.ReadDisplayName(nodeIds, out displayNames, out errors); String[] row12 = new String[] { "Data Type", displayNames[0] }; String[] row13 = new String[] { "Value Rank", variableNode.ValueRank.ToString() }; String[] row14 = new String[] { "Array Dimensions", variableNode.ArrayDimensions.Capacity.ToString() }; String[] row15 = new String[] { "Access Level", variableNode.AccessLevel.ToString() }; String[] row16 = new String[] { "Minimum Sampling Interval", variableNode.MinimumSamplingInterval.ToString() }; String[] row17 = new String[] { "Historizing", variableNode.Historizing.ToString() }; Object[] rows = new Object[] { row1, row2, row3, row4, row5, row6, row7, row8, row9, row10, row11, row12, row13, row14, row15, row16, row17 }; foreach (String[] rowArray in rows) { descriptionGridView.Rows.Add(rowArray); } } else { Object[] rows = new Object[] { row1, row2, row3, row4, row5, row6, row7, row8, row9, row10, row11 }; foreach (String[] rowArray in rows) { descriptionGridView.Rows.Add(rowArray); } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void ClientForm_FormClosing(Object sender, FormClosingEventArgs e) { try { myClientHelperAPI.Disconnect(); } catch { ; } } private void NodeTreeView_BeforeExpand(Object sender, TreeViewCancelEventArgs e) { e.Node.Nodes.Clear(); ReferenceDescriptionCollection referenceDescriptionCollection; ReferenceDescription refDesc = (ReferenceDescription)e.Node.Tag; try { referenceDescriptionCollection = myClientHelperAPI.BrowseNode(refDesc); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); return; } foreach (ReferenceDescription tempRefDesc in referenceDescriptionCollection) { e.Node.Nodes.Add(tempRefDesc.DisplayName.ToString()).Tag = tempRefDesc; } foreach (TreeNode node in e.Node.Nodes) { node.Nodes.Add(""); } } private void BrowsePage_Enter(Object sender, EventArgs e) { if (referenceDescriptionCollection == null) { try { referenceDescriptionCollection = myClientHelperAPI.BrowseRoot(); foreach (ReferenceDescription refDesc in referenceDescriptionCollection) { nodeTreeView.Nodes.Add(refDesc.DisplayName.ToString()).Tag = refDesc; foreach (TreeNode node in nodeTreeView.Nodes) { node.Nodes.Add(""); } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } } private void ReadValButton_Click(Object sender, EventArgs e) { List nodeIdStrings = new List(); List values = new List(); nodeIdStrings.Add(readIdTextBox.Text); try { values = myClientHelperAPI.ReadValues(nodeIdStrings); readTextBox.Text = values.ElementAt(0); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void EndpointListView_ItemSelectionChanged(Object sender, ListViewItemSelectionChangedEventArgs e) { mySelectedEndpoint = (EndpointDescription)e.Item.Tag; } private void OpcTabControl_Selecting(Object sender, TabControlCancelEventArgs e) { e.Cancel = !e.TabPage.Enabled; if (!e.TabPage.Enabled) { MessageBox.Show("Establish a connection to a server first.", "Error"); } } private void RegisterButton_Click(Object sender, EventArgs e) { List nodeIdStrings = new List(); nodeIdStrings.Add(rgNodeIdTextBox.Text); try { myRegisteredNodeIdStrings = myClientHelperAPI.RegisterNodeIds(nodeIdStrings); regNodeIdTextBox.Text = myRegisteredNodeIdStrings.ElementAt(0); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void UnregisterButton_Click(Object sender, EventArgs e) { try { myClientHelperAPI.UnregisterNodeIds(myRegisteredNodeIdStrings); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } myRegisteredNodeIdStrings.Clear(); regNodeIdTextBox.Text = ""; rgReadTextBox.Text = ""; rgWriteTextBox.Text = ""; } private void RgReadButton_Click(Object sender, EventArgs e) { List values = new List(); try { values = myClientHelperAPI.ReadValues(myRegisteredNodeIdStrings); rgReadTextBox.Text = values.ElementAt(0); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void RgWriteButton_Click(Object sender, EventArgs e) { List values = new List(); values.Add(rgWriteTextBox.Text); try { myClientHelperAPI.WriteValues(values, myRegisteredNodeIdStrings); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } private void UserPwButton_CheckedChanged(Object sender, EventArgs e) { if (userPwButton.Checked) { userTextBox.Enabled = true; pwTextBox.Enabled = true; } } private void UserAnonButton_CheckedChanged(Object sender, EventArgs e) { if (userAnonButton.Checked) { userTextBox.Enabled = false; pwTextBox.Enabled = false; } } private void StructReadButton_Click(Object sender, EventArgs e) { structGridView.Rows.Clear(); myStructList = new List(); try { myStructList = myClientHelperAPI.ReadStructUdt(structNodeIdTextBox.Text); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } foreach (String[] val in myStructList) { String[] row = new String[] { val[0], val[1], val[2] }; structGridView.Rows.Add(row); } } private void StructWriteButton_Click(Object sender, EventArgs e) { if (structGridView.Rows.Count == 0) { MessageBox.Show("Read a struct/UDT first.","Error"); return; } //Clear the list and refill with values from GridView to get value changes myStructList.Clear(); foreach (DataGridViewRow row in structGridView.Rows ) { String[] tempString = new String[3]; try { tempString[0] = structGridView.Rows[row.Index].Cells[0].Value.ToString(); } catch { tempString[0] = ""; } try { tempString[1] = structGridView.Rows[row.Index].Cells[1].Value.ToString(); } catch { tempString[1] = ""; } try { tempString[2] = structGridView.Rows[row.Index].Cells[2].Value.ToString(); } catch { tempString[2] = ""; } myStructList.Add(tempString); } try { myClientHelperAPI.WriteStructUdt(structNodeIdTextBox.Text, myStructList); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } } #endregion /// /// Global OPC UA event handlers /// #region OpcEventHandlers private void Notification_ServerCertificate(CertificateValidator cert, CertificateValidationEventArgs e) { //Handle certificate here //To accept a certificate manually move it to the root folder (Start > mmc.exe > add snap-in > certificates) //Or handle via UAClientCertForm if (this.InvokeRequired) { this.BeginInvoke(new CertificateValidationEventHandler(Notification_ServerCertificate), cert, e); return; } try { //Search for the server's certificate in store; if found -> accept X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); X509CertificateCollection certCol = store.Certificates.Find(X509FindType.FindByThumbprint, e.Certificate.Thumbprint, true); store.Close(); if (certCol.Capacity > 0) { e.Accept = true; } //Show cert dialog if cert hasn't been accepted yet else { if (!e.Accept) { myCertForm = new UAClientCertForm(e); myCertForm.ShowDialog(); } } } catch { ; } } private void Notification_MonitoredItem(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { if (this.InvokeRequired) { this.BeginInvoke(new MonitoredItemNotificationEventHandler(Notification_MonitoredItem), monitoredItem, e); return; } MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification; if (notification == null) { return; } subscriptionLabel.Text = "Item Name: " + monitoredItem.DisplayName + "\nValue: " + Utils.Format("{0}", notification.Value.WrappedValue.ToString()) + "\nStatus Code: " + Utils.Format("{0}", notification.Value.StatusCode.ToString()) + "\nSource timestamp: " + notification.Value.SourceTimestamp.ToString() + "\nServer timestamp: " + notification.Value.ServerTimestamp.ToString(); // Logger.Value..Info($"{DateTime.Now} {notification.Value.WrappedValue.ToString()}"); } private static readonly Lazy Logger = new Lazy(() => { var path = System.IO.Path.Combine("NlogConfig.xml"); return null;// new LogFactory(new XmlLoggingConfiguration(path)).GetCurrentClassLogger(); }); private void Notification_KeepAlive(Session sender, KeepAliveEventArgs e) { if (this.InvokeRequired) { this.BeginInvoke(new KeepAliveEventHandler(Notification_KeepAlive), sender, e); return; } try { // check for events from discarded sessions. if (!Object.ReferenceEquals(sender, mySession)) { return; } // check for disconnected session. if (ServiceResult.IsBad(e.Status)) { // try reconnecting using the existing session state mySession.Reconnect(); } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); ResetUI(); } } #endregion /// /// Private methods for UI handling /// #region PrivateMethods private void ResetUI() { descriptionGridView.Rows.Clear(); nodeTreeView.Nodes.Clear(); referenceDescriptionCollection = null; structGridView.Rows.Clear(); myStructList = null; subscriptionLabel.Text = ""; subscriptionIdTextBox.Text = ""; readIdTextBox.Text = ""; writeIdTextBox.Text = ""; readTextBox.Text = ""; writeTextBox.Text = ""; rgReadTextBox.Text = ""; rgWriteTextBox.Text = ""; rgNodeIdTextBox.Text = ""; regNodeIdTextBox.Text = ""; epConnectServerButton.Text = "Connect to server"; browsePage.Enabled = false; rwPage.Enabled = false; subscribePage.Enabled = false; structPage.Enabled = false; opcTabControl.SelectedIndex = 0; } #endregion private void subscriptionLabel_Click(Object sender, EventArgs e) { } } }