/// <summary> /// <para> This callback method is implemented by the client to receive alarms and events. </para> /// <para> /// Servers send event messages to the client via this interface. Event messages are sent when there has been a /// change to the specified event list. A new alarm or event that has been added to the list, a change to an alarm /// already in the list, or the deletion of an alarm from the list constitutes a change to the list. /// </para> /// <para> /// Once an event has been reported from the list, it is automatically deleted from the list. Alarms are only /// deleted from the list when they transition to inactive and acknowledged. /// </para> /// </summary> /// <param name="contextId"> The context identifier. </param> /// <param name="clientListId"> The client identifier of the list for which alarms/events are being reported. </param> /// <param name="eventsArray"> The list of alarms/events are being reported. </param> void ICallback.EventNotification(string contextId, uint clientListId, EventMessage[] eventsArray) { XiContext?context = XiContext.LookUpContext(contextId); if (context is not null) { context.NotifyCallbackRecieved(); _xiCallbackDoer.BeginInvoke(ct => context.EventMessagesCallback(clientListId, eventsArray)); } }
/// <summary> /// <para> This callback method is implemented by the client to receive data changes. </para> /// <para> /// Servers send data changes to the client that have not been reported to the client via this method. Changes /// consists of: /// </para> /// <para> 1) values for data objects that were added to the list, </para> /// <para> /// 2) values for data objects whose current values have changed since the last time they were reported to the /// client via this interface. If a deadband filter has been defined for the list, floating point values are not /// considered to have changed unless they have changed by the deadband amount. /// </para> /// <para> 3) historical values that meet the list filter criteria, including the deadband. </para> /// <para> /// In addition, the server may insert a special value that indicates the server or one of its wrapped servers /// are shutting down. /// </para> /// <para> /// This value is inserted as the first value in the list of values in the callback. Its ListId and ClientId are /// both 0 and its data type is ServerStatus. /// </para> /// </summary> /// <param name="contextId"> The context identifier. </param> /// <param name="clientListId"> The client identifier of the list for which data changes are being reported. </param> /// <param name="updatedValues"> The values being reported. </param> void ICallback.InformationReport(string contextId, uint clientListId, DataValueArraysWithAlias updatedValues) { XiContext?context = XiContext.LookUpContext(contextId); if (context is not null) { context.NotifyCallbackRecieved(); _xiCallbackDoer.BeginInvoke(ct => context.ElementValuesCallback(clientListId, updatedValues)); } }
/// <summary> /// This method returns the results of invoking an asynchronous passthrough. /// </summary> /// <param name="contextId"> The context identifier. </param> /// <param name="invokeId"> The identifier for this invocation of the passthrough defined by the client in the request. </param> /// <param name="passthroughResult"> /// The result of executing the passthrough, consisting of the result code, the invokeId /// supplied in the request, and a byte array. It is up to the client application to interpret this byte array. /// </param> void ICallback.PassthroughCallback(string contextId, int invokeId, PassthroughResult passthroughResult) { XiContext?context = XiContext.LookUpContext(contextId); if (context is not null) { context.NotifyCallbackRecieved(); _xiCallbackDoer.BeginInvoke(ct => context.PassthroughCallback(invokeId, passthroughResult)); } }
/// <summary> /// <para> /// This callback method is implemented by the client to be notified when the server server state changes to /// Aborting. Clients that use the poll interface instead of this callback interface can add the ServerDescription /// object to a data object list to be notified when the server state transitions to the aborting state. /// </para> /// </summary> /// <param name="contextId"> The context identifier. </param> /// <param name="serverStatus"> The ServerStatus object that describes the server that is shutting down. </param> /// <param name="reason"> The reason the context is being closed. </param> void ICallback.Abort(string contextId, ServerStatus serverStatus, string reason) { XiContext?context = XiContext.LookUpContext(contextId); if (context is not null) { context.ServerContextIsClosing = true; context.NotifyCallbackRecieved(); _xiCallbackDoer.BeginInvoke(ct => context.Abort(serverStatus, reason)); } }
/// <summary> /// Xi List Base is the common base class for all Xi Lists defined within /// the Client Base Assembly. /// </summary> /// <param name="context"> </param> protected XiListRoot(XiContext context) { _context = context; }
//connect to the OPC.NET server and get a read endpoint public bool Connect() { //set this to point to your OPC.Net server string serverUrl = "http://localhost:58080/XiServices/ServerDiscovery"; bool bReturnVal = false; try { Console.WriteLine("Getting Endpoint Discovery from server:\n{0}\n", serverUrl); //This class is used to locate a server and obtain its list of ServiceEndpoints. iEndpointDiscovery = new XiEndpointDiscovery(serverUrl) as IXiEndpointDiscovery; //we have the server...now check for endpoints //there should always be TCP endpoints for a DeltaV OPC.NET server so we do not search HTTP and Named Pipes to find one //and we do not consider choosing the fastest option between the three (TCP/HTTP/NamedPipes). We just use the TCP/IP one. // GetServiceEndpointsByBinding searches the list of endpoints on the XiEndpointDiscovery connection with the specified contractType and binding type. // We use the ResourceManagement endpoint to find the the other open endpoints on the server (some might be disabled) IEnumerable <ServiceEndpoint> resourceEndpoints = iEndpointDiscovery.GetServiceEndpointsByBinding(typeof(IResourceManagement).Name, typeof(System.ServiceModel.NetTcpBinding)); //use the first (probably only) resource endpoint for TCP/IP to open a context between client and server if ((resourceEndpoints != null) && (resourceEndpoints.Count() > 0)) { var serviceEndpoints = resourceEndpoints as IList <ServiceEndpoint> ?? resourceEndpoints.ToList(); //pick the first RM endpoint we found RMSvcEndpt = ((IList <ServiceEndpoint>)serviceEndpoints).First(); //Open the Context using the RM endpoint and some other values including timeout, what we want to read (HDA), and the GUID for this context Console.WriteLine("Opening Client Context with Initiate\n"); iContext = XiContext.Initiate(RMSvcEndpt, iEndpointDiscovery.ServerEntry, 300000, (uint)ContextOptions.EnableJournalDataAccess, //HDA (uint)System.Threading.Thread.CurrentThread.CurrentCulture.LCID, Guid.NewGuid().ToString()); if (iContext != null) { //find a read endpoint using the XiEndpointDiscovery connection IEnumerable <ServiceEndpoint> readseps = iEndpointDiscovery.GetServiceEndpointsByBinding(typeof(IRead).Name, RMSvcEndpt.Binding.GetType()); //if we found at least one read endpoint, connect the context to it readEndpoint = null; if (readseps != null) { Console.WriteLine("Adding Read endpoint to Context\n"); ServiceEndpoint sep = readseps.ElementAt <ServiceEndpoint>(0); readEndpoint = iContext.OpenEndpoint(sep, 30000, new TimeSpan(5000)); if (readEndpoint != null) { bReturnVal = true; //everything went OK } else { Console.WriteLine("Unable to add Read endpoint to Context\n"); bReturnVal = false; //failed } } } else { Console.WriteLine("Unable to open Client Context\n"); bReturnVal = false; } } } catch (Exception) { bReturnVal = false; } return(bReturnVal); }
/// <summary> /// This constructor creates a new Data List. /// </summary> /// <param name="context"> The context to which this data lList belongs. </param> /// <param name="updateRate"> The UpdateRate for this data list. </param> /// <param name="bufferingRate"> The BufferingRate for this data list. Set to 0 if not used. </param> /// <param name="filterSet"> The FilterSet for this data list. Set to null if not used. </param> public XiDataList(XiContext context, uint updateRate, uint bufferingRate, FilterSet?filterSet) : base(context) { StandardListType = global::Xi.Contracts.Constants.StandardListType.DataList; ListAttributes = Context.DefineList(this, updateRate, bufferingRate, filterSet); }
/// <summary> /// This constructor creates a new data journal list for the specified context. /// </summary> /// <param name="context"> The context that owns the data journal list. </param> /// <param name="updateRate"> The update rate for the data journal list. </param> /// <param name="bufferingRate"> The BufferingRate for this data journal list. Set to 0 if not used. </param> /// <param name="filterSet"> The FilterSet for this data journal list. Set to null if not used. </param> internal XiDataJournalList(XiContext context, uint updateRate, uint bufferingRate, FilterSet?filterSet) : base(context) { StandardListType = StandardListType.DataJournalList; ListAttributes = Context.DefineList(this, updateRate, bufferingRate, filterSet); }
/// <summary> /// Xi List Base is the common base class for all Xi Lists defined within /// the Client Base Assembly. /// </summary> /// <param name="context"> </param> protected internal XiDataAndDataJournalListBase(XiContext context) : base(context) { }
/// <summary> /// This method is used to connect to the server and establish a context with it. /// </summary> public void InitiateXiContext(string serverDiscoveryEndpointHttpUrl, string applicationName, string workstationName, IDispatcher xiCallbackDoer) { if (_disposed) { throw new ObjectDisposedException(@"Cannot access a disposed XiServerProxy."); } if (_context is not null) { throw new Exception(@"Xi context already exists."); } var binding = new BasicHttpBinding(BasicHttpSecurityMode.None) { //HostNameComparisonMode = HostNameComparisonMode.StrongWildcard, ReceiveTimeout = new TimeSpan(00, 10, 00), SendTimeout = new TimeSpan(00, 10, 00), OpenTimeout = new TimeSpan(00, 10, 00), CloseTimeout = new TimeSpan(00, 10, 00), MaxReceivedMessageSize = 65536, MaxBufferSize = 65536, MaxBufferPoolSize = 524288, TransferMode = TransferMode.Buffered, //MessageEncoding = WSMessageEncoding.Text, TextEncoding = Encoding.UTF8, BypassProxyOnLocal = false, UseDefaultWebProxy = true }; ServerEntry?serverEntry = null; List <EndpointConfigurationEx> endpointConfigurationExList = new List <EndpointConfigurationEx>(); using (var cfIServerDiscovery = new ChannelFactory <IServerDiscovery>(binding, new EndpointAddress(serverDiscoveryEndpointHttpUrl))) { IServerDiscovery newServer = cfIServerDiscovery.CreateChannel(); // Get its server item serverEntry = newServer.DiscoverServerInfo(); endpointConfigurationExList = newServer.DiscoverAbbreviatedEndpointInfo(); cfIServerDiscovery.Close(); } // Servers behind a NAT firewall may not know their "outside" // IP address, so update the ServerDescription.ServerDiscoveryURL // with the one just used, and make sure there is a Mex URL with // hostname/IP address used in this URL //ServerUri.ReconcileServerEntryWithServerDiscoveryUrl(serverEntry, serverDiscoveryEndpointHttpUrl); if (serverEntry is null) { throw new Exception(@"The requried Server Entry is null."); } var serverDiscoveryEndpointHttpUri = new Uri(serverDiscoveryEndpointHttpUrl); try { _xiServerInfo = new XiServerInfo(serverEntry, endpointConfigurationExList, serverDiscoveryEndpointHttpUri.Host); } catch (Exception ex) { var sb = new StringBuilder("The Xi Server could not be found - it may not be running. \n Details:\n"); sb.Append(ex.Message); sb.Append("\nError During Server Endpoint Discovery"); throw new Exception(sb.ToString(), ex); } try { foreach ( ServiceEndpoint resourceManagementServiceEndpoint in _xiServerInfo.ResourceManagementServiceEndpoints) { try { _context = new XiContext(_xiServerInfo, resourceManagementServiceEndpoint, (uint)_contextTimeout.TotalMilliseconds, _contextOptions, _localeId, applicationName, workstationName, _keepAliveSkipCount, _callbackRate, xiCallbackDoer); _xiServerInfo.RankReadWriteSubscribeEndpoints(resourceManagementServiceEndpoint); _context.ContextNotifyEvent += XiContext_ContextNotifyEvent; break; } catch (ResourceManagementInitiateException) { throw; } catch (SecurityNegotiationException) { throw; } catch { //Logger?.LogDebug(e, "Exception while connecting to {0}", resourceManagementServiceEndpoint.Address); } } if (_context is null) { throw new Exception("Failed to connect to the Xi Server."); } } catch { if (_context is not null) { _context.ContextNotifyEvent -= XiContext_ContextNotifyEvent; _context.Dispose(); _context = null; } _xiServerInfo.Dispose(); _xiServerInfo = null; throw; } }