| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462 |
- using Opc.Ua;
- using Opc.Ua.Bindings;
- using Opc.Ua.Client;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- namespace OpcUaHelper
- {
- /// <summary>
- /// 一个二次封装了的OPC UA库,支持从opc ua服务器读写节点数据,批量读写,订阅,批量订阅,历史数据读取,方法调用操作。
- /// </summary>
- public class OpcUaClient
- {
- #region Constructors
- /// <summary>
- /// 默认的构造函数,实例化一个新的OPC UA类
- /// </summary>
- public OpcUaClient( )
- {
- dic_subscriptions = new Dictionary<string, Subscription>( );
- var certificateValidator = new CertificateValidator( );
- certificateValidator.CertificateValidation += ( sender, eventArgs ) =>
- {
- if (ServiceResult.IsGood( eventArgs.Error ))
- eventArgs.Accept = true;
- else if (eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)
- eventArgs.Accept = true;
- else
- throw new Exception( string.Format( "Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo ) );
- };
- SecurityConfiguration securityConfigurationcv = new SecurityConfiguration
- {
- AutoAcceptUntrustedCertificates = true,
- RejectSHA1SignedCertificates = false,
- MinimumCertificateKeySize = 1024,
- };
- // Build the application configuration
- var configuration = new ApplicationConfiguration
- {
- ApplicationName = OpcUaName,
- ApplicationType = ApplicationType.Client,
- CertificateValidator = certificateValidator,
- ApplicationUri = "urn:MyClient", //Kepp this syntax
- ProductUri = "OpcUaClient",
- ServerConfiguration = new ServerConfiguration
- {
- MaxSubscriptionCount = 100000,
- MaxMessageQueueSize = 1000000,
- MaxNotificationQueueSize = 1000000,
- MaxPublishRequestCount = 10000000,
- },
- SecurityConfiguration = new SecurityConfiguration
- {
- AutoAcceptUntrustedCertificates = true,
- RejectSHA1SignedCertificates = false,
- MinimumCertificateKeySize = 1024,
- SuppressNonceValidationErrors = true,
- ApplicationCertificate = new CertificateIdentifier
- {
- StoreType = CertificateStoreType.X509Store,
- StorePath = "CurrentUser\\My",
- SubjectName = OpcUaName,
- },
- TrustedIssuerCertificates = new CertificateTrustList
- {
- StoreType = CertificateStoreType.X509Store,
- StorePath = "CurrentUser\\Root",
- },
- TrustedPeerCertificates = new CertificateTrustList
- {
- StoreType = CertificateStoreType.X509Store,
- StorePath = "CurrentUser\\Root",
- }
- },
- TransportQuotas = new TransportQuotas
- {
- OperationTimeout = 6000000,
- MaxStringLength = int.MaxValue,
- MaxByteStringLength = int.MaxValue,
- MaxArrayLength = 65535,
- MaxMessageSize = 419430400,
- MaxBufferSize = 65535,
- ChannelLifetime = -1,
- SecurityTokenLifetime = TcpMessageLimits.DefaultSecurityTokenLifeTime
- },
- ClientConfiguration = new ClientConfiguration
- {
- DefaultSessionTimeout = -1,
- MinSubscriptionLifetime = -1,
- },
- DisableHiResClock = true
- };
- configuration.Validate( ApplicationType.Client );
- m_configuration = configuration;
- }
- #endregion Constructor
- #region Connect And Disconnect
- /// <summary>
- /// connect to server
- /// </summary>
- /// <param name="serverUrl">remote url</param>
- public async Task ConnectServer( string serverUrl )
- {
- m_session = await Connect( serverUrl );
- }
- /// <summary>
- /// Creates a new session.
- /// </summary>
- /// <returns>The new session object.</returns>
- private async Task<ISession> Connect( string serverUrl )
- {
- // disconnect from existing session.
- Disconnect( );
- if (m_configuration == null)
- {
- throw new ArgumentNullException( "_configuration" );
- }
- // select the best endpoint.
- EndpointDescription endpointDescription = await CoreClientUtils.SelectEndpointAsync( m_configuration, serverUrl, UseSecurity, null );
- EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create( m_configuration );
- ConfiguredEndpoint endpoint = new ConfiguredEndpoint( null, endpointDescription, endpointConfiguration );
- m_session = await Opc.Ua.Client.Session.Create(
- m_configuration,
- endpoint,
- false,
- false,
- (string.IsNullOrEmpty( OpcUaName )) ? m_configuration.ApplicationName : OpcUaName,
- 60000,
- UserIdentity,
- new string[] { } );
- // set up keep alive callback.
- m_session.KeepAlive += new KeepAliveEventHandler( Session_KeepAlive );
- // update the client status
- m_IsConnected = true;
- // raise an event.
- DoConnectComplete( null );
- // return the new session.
- return m_session;
- }
- /// <summary>
- /// Disconnects from the server.
- /// </summary>
- public void Disconnect( )
- {
- UpdateStatus( false, DateTime.UtcNow, "Disconnected" );
- // stop any reconnect operation.
- if (m_reConnectHandler != null)
- {
- m_reConnectHandler.Dispose( );
- m_reConnectHandler = null;
- }
- // disconnect any existing session.
- if (m_session != null)
- {
- m_session.Close( 3000 );
- m_session.Dispose( );
- m_session = null;
- }
- // update the client status
- m_IsConnected = false;
- // raise an event.
- DoConnectComplete( null );
- }
- #endregion Connect And Disconnect
- #region Event Handlers
- /// <summary>
- /// Report the client status
- /// </summary>
- /// <param name="error">Whether the status represents an error.</param>
- /// <param name="time">The time associated with the status.</param>
- /// <param name="status">The status message.</param>
- /// <param name="args">Arguments used to format the status message.</param>
- private void UpdateStatus( bool error, DateTime time, string status, params object[] args )
- {
- m_OpcStatusChange?.Invoke( this, new OpcUaStatusEventArgs( )
- {
- Error = error,
- Time = time.ToLocalTime( ),
- Text = String.Format( status, args ),
- } );
- }
- /// <summary>
- /// Handles a keep alive event from a session.
- /// </summary>
- private void Session_KeepAlive( ISession session, KeepAliveEventArgs e )
- {
- try
- {
- // check for events from discarded sessions.
- if (!Object.ReferenceEquals( session, m_session ))
- {
- return;
- }
- // start reconnect sequence on communication error.
- if (ServiceResult.IsBad( e.Status ))
- {
- if (m_reconnectPeriod <= 0)
- {
- UpdateStatus( true, e.CurrentTime, "Communication Error ({0})", e.Status );
- return;
- }
- UpdateStatus( true, e.CurrentTime, "Reconnecting in {0}s", m_reconnectPeriod );
- if (m_reConnectHandler == null)
- {
- m_ReconnectStarting?.Invoke( this, e );
- m_reConnectHandler = new SessionReconnectHandler( );
- m_reConnectHandler.BeginReconnect( m_session, m_reconnectPeriod * 1000, Server_ReconnectComplete );
- }
- return;
- }
- // update status.
- UpdateStatus( false, e.CurrentTime, "Connected [{0}]", session.Endpoint.EndpointUrl );
- // raise any additional notifications.
- m_KeepAliveComplete?.Invoke( this, e );
- }
- catch (Exception exception)
- {
- #if !NETSTANDARD2_0 && !NETSTANDARD2_1
- ClientUtils.HandleException( OpcUaName, exception );
- #else
- throw;
- #endif
- }
- }
- /// <summary>
- /// Handles a reconnect event complete from the reconnect handler.
- /// </summary>
- private void Server_ReconnectComplete( object sender, EventArgs e )
- {
- try
- {
- // ignore callbacks from discarded objects.
- if (!Object.ReferenceEquals( sender, m_reConnectHandler ))
- {
- return;
- }
- m_session = m_reConnectHandler.Session;
- m_reConnectHandler.Dispose( );
- m_reConnectHandler = null;
- // raise any additional notifications.
- m_ReconnectComplete?.Invoke( this, e );
- }
- catch (Exception exception)
- {
- #if !NETSTANDARD2_0 && !NETSTANDARD2_1
- ClientUtils.HandleException( OpcUaName, exception );
- #else
- throw;
- #endif
- }
- }
- #endregion Event Handlers
- #region LogOut Setting
- /// <summary>
- /// 设置OPC客户端的日志输出
- /// </summary>
- /// <param name="filePath">完整的文件路径</param>
- /// <param name="deleteExisting">是否删除原文件</param>
- public void SetLogPathName( string filePath, bool deleteExisting )
- {
- Utils.SetTraceLog( filePath, deleteExisting );
- Utils.SetTraceMask( 515 );
- }
- #endregion LogOut Setting
- #region Public Members
- /// <summary>
- /// a name of application name show on server
- /// </summary>
- public string OpcUaName { get; set; } = "Opc Ua Helper";
- /// <summary>
- /// Whether to use security when connecting.
- /// </summary>
- public bool UseSecurity
- {
- get { return m_useSecurity; }
- set { m_useSecurity = value; }
- }
- /// <summary>
- /// The user identity to use when creating the session.
- /// </summary>
- public IUserIdentity UserIdentity { get; set; }
- /// <summary>
- /// The currently active session.
- /// </summary>
- public ISession Session
- {
- get { return m_session; }
- }
- /// <summary>
- /// Indicate the connect status
- /// </summary>
- public bool Connected
- {
- get { return m_IsConnected; }
- }
- /// <summary>
- /// The number of seconds between reconnect attempts (0 means reconnect is disabled).
- /// </summary>
- public int ReconnectPeriod
- {
- get { return m_reconnectPeriod; }
- set { m_reconnectPeriod = value; }
- }
- /// <summary>
- /// Raised when a good keep alive from the server arrives.
- /// </summary>
- public event EventHandler KeepAliveComplete
- {
- add { m_KeepAliveComplete += value; }
- remove { m_KeepAliveComplete -= value; }
- }
- /// <summary>
- /// Raised when a reconnect operation starts.
- /// </summary>
- public event EventHandler ReconnectStarting
- {
- add { m_ReconnectStarting += value; }
- remove { m_ReconnectStarting -= value; }
- }
- /// <summary>
- /// Raised when a reconnect operation completes.
- /// </summary>
- public event EventHandler ReconnectComplete
- {
- add { m_ReconnectComplete += value; }
- remove { m_ReconnectComplete -= value; }
- }
- /// <summary>
- /// Raised after successfully connecting to or disconnecing from a server.
- /// </summary>
- public event EventHandler ConnectComplete
- {
- add { m_ConnectComplete += value; }
- remove { m_ConnectComplete -= value; }
- }
- /// <summary>
- /// Raised after the client status change
- /// </summary>
- public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange
- {
- add { m_OpcStatusChange += value; }
- remove { m_OpcStatusChange -= value; }
- }
- /// <summary>
- /// 配置信息
- /// </summary>
- public ApplicationConfiguration AppConfig => m_configuration;
- #endregion Public Members
- #region Node Write/Read Support
- /// <summary>
- /// Read a value node from server
- /// </summary>
- /// <param name="nodeId">node id</param>
- /// <returns>DataValue</returns>
- public DataValue ReadNode( NodeId nodeId )
- {
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection
- {
- new ReadValueId( )
- {
- NodeId = nodeId,
- AttributeId = Attributes.Value
- }
- };
- // read the current value
- m_session.Read(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- out DataValueCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- return results[0];
- }
- /// <summary>
- /// Read a value node from server
- /// </summary>
- /// <typeparam name="T">type of value</typeparam>
- /// <param name="tag">node id</param>
- /// <returns>实际值</returns>
- public T ReadNode<T>( string tag )
- {
- DataValue dataValue = ReadNode( new NodeId( tag ) );
- return (T)dataValue.Value;
- }
- /// <summary>
- /// Read a tag asynchronously
- /// </summary>
- /// <typeparam name="T">The type of tag to read</typeparam>
- /// <param name="tag">tag值</param>
- /// <returns>The value retrieved from the OPC</returns>
- public Task<T> ReadNodeAsync<T>( string tag )
- {
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection
- {
- new ReadValueId()
- {
- NodeId = new NodeId(tag),
- AttributeId = Attributes.Value
- }
- };
- // Wrap the ReadAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
- var taskCompletionSource = new TaskCompletionSource<T>( );
- m_session.BeginRead(
- requestHeader: null,
- maxAge: 0,
- timestampsToReturn: TimestampsToReturn.Neither,
- nodesToRead: nodesToRead,
- callback: ar =>
- {
- DataValueCollection results;
- DiagnosticInfoCollection diag;
- var response = m_session.EndRead(
- result: ar,
- results: out results,
- diagnosticInfos: out diag );
- try
- {
- CheckReturnValue( response.ServiceResult );
- CheckReturnValue( results[0].StatusCode );
- var val = results[0];
- taskCompletionSource.TrySetResult( (T)val.Value );
- }
- catch (Exception ex)
- {
- taskCompletionSource.TrySetException( ex );
- }
- },
- asyncState: null );
- return taskCompletionSource.Task;
- }
- /// <summary>
- /// read several value nodes from server
- /// </summary>
- /// <param name="nodeIds">all NodeIds</param>
- /// <returns>all values</returns>
- public List<DataValue> ReadNodes( NodeId[] nodeIds )
- {
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- for (int i = 0; i < nodeIds.Length; i++)
- {
- nodesToRead.Add( new ReadValueId( )
- {
- NodeId = nodeIds[i],
- AttributeId = Attributes.Value
- } );
- }
- // 读取当前的值
- m_session.Read(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- out DataValueCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- return results.ToList( );
- }
- /// <summary>
- /// read several value nodes from server
- /// </summary>
- /// <param name="nodeIds">all NodeIds</param>
- /// <returns>all values</returns>
- public Task<List<DataValue>> ReadNodesAsync( NodeId[] nodeIds )
- {
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- for (int i = 0; i < nodeIds.Length; i++)
- {
- nodesToRead.Add( new ReadValueId( )
- {
- NodeId = nodeIds[i],
- AttributeId = Attributes.Value
- } );
- }
- var taskCompletionSource = new TaskCompletionSource<List<DataValue>>( );
- // 读取当前的值
- m_session.BeginRead(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- callback: ar =>
- {
- DataValueCollection results;
- DiagnosticInfoCollection diag;
- var response = m_session.EndRead(
- result: ar,
- results: out results,
- diagnosticInfos: out diag );
- try
- {
- CheckReturnValue( response.ServiceResult );
- taskCompletionSource.TrySetResult( results.ToList( ) );
- }
- catch (Exception ex)
- {
- taskCompletionSource.TrySetException( ex );
- }
- },
- asyncState: null );
- return taskCompletionSource.Task;
- }
- /// <summary>
- /// read several value nodes from server
- /// </summary>
- /// <param name="tags">所以的节点数组信息</param>
- /// <returns>all values</returns>
- public List<T> ReadNodes<T>( string[] tags )
- {
- List<T> result = new List<T>( );
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- for (int i = 0; i < tags.Length; i++)
- {
- nodesToRead.Add( new ReadValueId( )
- {
- NodeId = new NodeId( tags[i] ),
- AttributeId = Attributes.Value
- } );
- }
- // 读取当前的值
- m_session.Read(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- out DataValueCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- foreach (var item in results)
- {
- result.Add( (T)item.Value );
- }
- return result;
- }
- /// <summary>
- /// read several value nodes from server
- /// </summary>
- /// <param name="tags">all NodeIds</param>
- /// <returns>all values</returns>
- public Task<List<T>> ReadNodesAsync<T>( string[] tags )
- {
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- for (int i = 0; i < tags.Length; i++)
- {
- nodesToRead.Add( new ReadValueId( )
- {
- NodeId = new NodeId( tags[i] ),
- AttributeId = Attributes.Value
- } );
- }
- var taskCompletionSource = new TaskCompletionSource<List<T>>( );
- // 读取当前的值
- m_session.BeginRead(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- callback: ar =>
- {
- DataValueCollection results;
- DiagnosticInfoCollection diag;
- var response = m_session.EndRead(
- result: ar,
- results: out results,
- diagnosticInfos: out diag );
- try
- {
- CheckReturnValue( response.ServiceResult );
- List<T> result = new List<T>( );
- foreach (var item in results)
- {
- result.Add( (T)item.Value );
- }
- taskCompletionSource.TrySetResult( result );
- }
- catch (Exception ex)
- {
- taskCompletionSource.TrySetException( ex );
- }
- },
- asyncState: null );
- return taskCompletionSource.Task;
- }
- /// <summary>
- /// write a note to server(you should use try catch)
- /// </summary>
- /// <typeparam name="T">The type of tag to write on</typeparam>
- /// <param name="tag">节点名称</param>
- /// <param name="value">值</param>
- /// <returns>if success True,otherwise False</returns>
- public bool WriteNode<T>( string tag, T value )
- {
- WriteValue valueToWrite = new WriteValue( )
- {
- NodeId = new NodeId( tag ),
- AttributeId = Attributes.Value
- };
- valueToWrite.Value.Value = value;
- valueToWrite.Value.StatusCode = StatusCodes.Good;
- valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
- valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
- WriteValueCollection valuesToWrite = new WriteValueCollection
- {
- valueToWrite
- };
- // 写入当前的值
- m_session.Write(
- null,
- valuesToWrite,
- out StatusCodeCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, valuesToWrite );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, valuesToWrite );
- if (StatusCode.IsBad( results[0] ))
- {
- throw new ServiceResultException( results[0] );
- }
- return !StatusCode.IsBad( results[0] );
- }
- /// <summary>
- /// Write a value on the specified opc tag asynchronously
- /// </summary>
- /// <typeparam name="T">The type of tag to write on</typeparam>
- /// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name. E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>
- /// <param name="value">The value for the item to write</param>
- public Task<bool> WriteNodeAsync<T>( string tag, T value )
- {
- WriteValue valueToWrite = new WriteValue( )
- {
- NodeId = new NodeId( tag ),
- AttributeId = Attributes.Value,
- };
- valueToWrite.Value.Value = value;
- valueToWrite.Value.StatusCode = StatusCodes.Good;
- valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
- valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
- WriteValueCollection valuesToWrite = new WriteValueCollection
- {
- valueToWrite
- };
- // Wrap the WriteAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
- var taskCompletionSource = new TaskCompletionSource<bool>( );
- m_session.BeginWrite(
- requestHeader: null,
- nodesToWrite: valuesToWrite,
- callback: ar =>
- {
- var response = m_session.EndWrite(
- result: ar,
- results: out StatusCodeCollection results,
- diagnosticInfos: out DiagnosticInfoCollection diag );
- try
- {
- ClientBase.ValidateResponse( results, valuesToWrite );
- ClientBase.ValidateDiagnosticInfos( diag, valuesToWrite );
- taskCompletionSource.SetResult( StatusCode.IsGood( results[0] ) );
- }
- catch (Exception ex)
- {
- taskCompletionSource.TrySetException( ex );
- }
- },
- asyncState: null );
- return taskCompletionSource.Task;
- }
- /// <summary>
- /// 所有的节点都写入成功,返回<c>True</c>,否则返回<c>False</c>
- /// </summary>
- /// <param name="tags">节点名称数组</param>
- /// <param name="values">节点的值数据</param>
- /// <returns>所有的是否都写入成功</returns>
- public bool WriteNodes( string[] tags, object[] values )
- {
- WriteValueCollection valuesToWrite = new WriteValueCollection( );
- for (int i = 0; i < tags.Length; i++)
- {
- if (i < values.Length)
- {
- WriteValue valueToWrite = new WriteValue( )
- {
- NodeId = new NodeId( tags[i] ),
- AttributeId = Attributes.Value
- };
- valueToWrite.Value.Value = values[i];
- valueToWrite.Value.StatusCode = StatusCodes.Good;
- valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
- valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
- valuesToWrite.Add( valueToWrite );
- }
- }
- // 写入当前的值
- m_session.Write(
- null,
- valuesToWrite,
- out StatusCodeCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, valuesToWrite );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, valuesToWrite );
- bool result = true;
- foreach (var r in results)
- {
- if (StatusCode.IsBad( r ))
- {
- result = false;
- break;
- }
- }
- return result;
- }
- #endregion Node Write/Read Support
- #region DeleteNode Support
- /// <summary>
- /// 删除一个节点的操作,除非服务器配置允许,否则引发异常,成功返回<c>True</c>,否则返回<c>False</c>
- /// </summary>
- /// <param name="tag">节点文本描述</param>
- /// <returns>是否删除成功</returns>
- public bool DeleteExsistNode( string tag )
- {
- DeleteNodesItemCollection waitDelete = new DeleteNodesItemCollection( );
- DeleteNodesItem nodesItem = new DeleteNodesItem( )
- {
- NodeId = new NodeId( tag ),
- };
- m_session.DeleteNodes(
- null,
- waitDelete,
- out StatusCodeCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, waitDelete );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, waitDelete );
- return !StatusCode.IsBad( results[0] );
- }
- #endregion DeleteNode Support
- #region Test Function
- /// <summary>
- /// 新增一个节点数据
- /// </summary>
- /// <param name="parent">父节点tag名称</param>
- [Obsolete( "还未经过测试,无法使用" )]
- public void AddNewNode( NodeId parent )
- {
- // Create a Variable node.
- AddNodesItem node2 = new AddNodesItem( );
- node2.ParentNodeId = new NodeId( parent );
- node2.ReferenceTypeId = ReferenceTypes.HasComponent;
- node2.RequestedNewNodeId = null;
- node2.BrowseName = new QualifiedName( "DataVariable1" );
- node2.NodeClass = NodeClass.Variable;
- node2.NodeAttributes = null;
- node2.TypeDefinition = VariableTypeIds.BaseDataVariableType;
- //specify node attributes.
- VariableAttributes node2Attribtues = new VariableAttributes( );
- node2Attribtues.DisplayName = "DataVariable1";
- node2Attribtues.Description = "DataVariable1 Description";
- node2Attribtues.Value = new Variant( 123 );
- node2Attribtues.DataType = (uint)BuiltInType.Int32;
- node2Attribtues.ValueRank = ValueRanks.Scalar;
- node2Attribtues.ArrayDimensions = new UInt32Collection( );
- node2Attribtues.AccessLevel = AccessLevels.CurrentReadOrWrite;
- node2Attribtues.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
- node2Attribtues.MinimumSamplingInterval = 0;
- node2Attribtues.Historizing = false;
- node2Attribtues.WriteMask = (uint)AttributeWriteMask.None;
- node2Attribtues.UserWriteMask = (uint)AttributeWriteMask.None;
- node2Attribtues.SpecifiedAttributes = (uint)NodeAttributesMask.All;
- node2.NodeAttributes = new ExtensionObject( node2Attribtues );
- AddNodesItemCollection nodesToAdd = new AddNodesItemCollection { node2 };
- m_session.AddNodes(
- null,
- nodesToAdd,
- out AddNodesResultCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToAdd );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToAdd );
- }
- #endregion Test Function
- #region Monitor Support
- /// <summary>
- /// 新增一个订阅,需要指定订阅的关键字,订阅的tag名,以及回调方法
- /// </summary>
- /// <param name="key">关键字</param>
- /// <param name="tag">tag</param>
- /// <param name="callback">回调方法</param>
- public void AddSubscription( string key, string tag, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback )
- {
- AddSubscription( key, new string[] { tag }, callback );
- }
- /// <inheritdoc cref="AddSubscription(string, string[], Action{string, MonitoredItem, MonitoredItemNotificationEventArgs}, Action{Subscription})"/>
- public void AddSubscription( string key, string[] tags, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback )
- {
- AddSubscription( key, tags, callback, null );
- }
- /// <summary>
- /// 新增一批订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法
- /// </summary>
- /// <param name="key">关键字</param>
- /// <param name="tags">节点名称数组</param>
- /// <param name="callback">回调方法</param>
- /// <param name="subAction">关于订阅参数的自定义的设置</param>
- public void AddSubscription( string key, string[] tags, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback, Action<Subscription> subAction )
- {
- Subscription m_subscription = new Subscription( m_session.DefaultSubscription );
- m_subscription.PublishingEnabled = true;
- m_subscription.PublishingInterval = 0;
- m_subscription.KeepAliveCount = ushort.MaxValue;
- m_subscription.LifetimeCount = 2400;
- m_subscription.MaxNotificationsPerPublish = ushort.MaxValue;
- m_subscription.Priority = 100;
- m_subscription.DisplayName = key;
- subAction?.Invoke( m_subscription );
- for (int i = 0; i < tags.Length; i++)
- {
- var item = new MonitoredItem
- {
- StartNodeId = new NodeId( tags[i] ),
- AttributeId = Attributes.Value,
- DisplayName = tags[i],
- SamplingInterval = 100,
- };
- item.Notification += ( MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args ) =>
- {
- callback?.Invoke( key, monitoredItem, args );
- };
- m_subscription.AddItem( item );
- }
- m_session.AddSubscription( m_subscription );
- m_subscription.Create( );
- lock (dic_subscriptions)
- {
- if (dic_subscriptions.ContainsKey( key ))
- {
- // remove
- dic_subscriptions[key].Delete( true );
- m_session.RemoveSubscription( dic_subscriptions[key] );
- dic_subscriptions[key].Dispose( );
- dic_subscriptions[key] = m_subscription;
- }
- else
- {
- dic_subscriptions.Add( key, m_subscription );
- }
- }
- }
- /// <summary>
- /// 移除订阅消息,如果该订阅消息是批量的,也直接移除
- /// </summary>
- /// <param name="key">订阅关键值</param>
- public void RemoveSubscription( string key )
- {
- lock (dic_subscriptions)
- {
- if (dic_subscriptions.ContainsKey( key ))
- {
- // remove
- dic_subscriptions[key].Delete( true );
- m_session.RemoveSubscription( dic_subscriptions[key] );
- dic_subscriptions[key].Dispose( );
- dic_subscriptions.Remove( key );
- }
- }
- }
- /// <summary>
- /// 移除所有的订阅消息
- /// </summary>
- public void RemoveAllSubscription( )
- {
- lock (dic_subscriptions)
- {
- foreach (var item in dic_subscriptions)
- {
- item.Value.Delete( true );
- m_session.RemoveSubscription( item.Value );
- item.Value.Dispose( );
- }
- dic_subscriptions.Clear( );
- }
- }
- #endregion Monitor Support
- #region ReadHistory Support
- /// <summary>
- /// read History data
- /// </summary>
- /// <param name="tag">节点的索引</param>
- /// <param name="start">开始时间</param>
- /// <param name="end">结束时间</param>
- /// <param name="count">读取的个数</param>
- /// <param name="containBound">是否包含边界</param>
- /// <returns>读取的数据列表</returns>
- public IEnumerable<DataValue> ReadHistoryRawDataValues( string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false )
- {
- HistoryReadValueId m_nodeToContinue = new HistoryReadValueId( )
- {
- NodeId = new NodeId( tag ),
- };
- ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
- {
- StartTime = start,
- EndTime = end,
- NumValuesPerNode = count,
- IsReadModified = false,
- ReturnBounds = containBound
- };
- HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection( );
- nodesToRead.Add( m_nodeToContinue );
- m_session.HistoryRead(
- null,
- new ExtensionObject( m_details ),
- TimestampsToReturn.Both,
- false,
- nodesToRead,
- out HistoryReadResultCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- if (StatusCode.IsBad( results[0].StatusCode ))
- {
- throw new ServiceResultException( results[0].StatusCode );
- }
- HistoryData values = ExtensionObject.ToEncodeable( results[0].HistoryData ) as HistoryData;
- foreach (var value in values.DataValues)
- {
- yield return value;
- }
- }
- /// <summary>
- /// 读取一连串的历史数据,并将其转化成指定的类型
- /// </summary>
- /// <param name="tag">节点的索引</param>
- /// <param name="start">开始时间</param>
- /// <param name="end">结束时间</param>
- /// <param name="count">读取的个数</param>
- /// <param name="containBound">是否包含边界</param>
- /// <returns>读取的数据列表</returns>
- public IEnumerable<T> ReadHistoryRawDataValues<T>( string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false )
- {
- HistoryReadValueId m_nodeToContinue = new HistoryReadValueId( )
- {
- NodeId = new NodeId( tag ),
- };
- ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
- {
- StartTime = start.ToUniversalTime( ),
- EndTime = end.ToUniversalTime( ),
- NumValuesPerNode = count,
- IsReadModified = false,
- ReturnBounds = containBound
- };
- HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection( );
- nodesToRead.Add( m_nodeToContinue );
- m_session.HistoryRead(
- null,
- new ExtensionObject( m_details ),
- TimestampsToReturn.Both,
- false,
- nodesToRead,
- out HistoryReadResultCollection results,
- out DiagnosticInfoCollection diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- if (StatusCode.IsBad( results[0].StatusCode ))
- {
- throw new ServiceResultException( results[0].StatusCode );
- }
- HistoryData values = ExtensionObject.ToEncodeable( results[0].HistoryData ) as HistoryData;
- foreach (var value in values.DataValues)
- {
- yield return (T)value.Value;
- }
- }
- #endregion ReadHistory Support
- #region BrowseNode Support
- /// <summary>
- /// 浏览一个节点的引用
- /// </summary>
- /// <param name="tag">节点值</param>
- /// <returns>引用节点描述</returns>
- public ReferenceDescription[] BrowseNodeReference( string tag )
- {
- NodeId sourceId = new NodeId( tag );
- // 该节点可以读取到方法
- BrowseDescription nodeToBrowse1 = new BrowseDescription( );
- nodeToBrowse1.NodeId = sourceId;
- nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
- nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
- nodeToBrowse1.IncludeSubtypes = true;
- nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method);
- nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
- // 该节点无论怎么样都读取不到方法
- // find all nodes organized by the node.
- BrowseDescription nodeToBrowse2 = new BrowseDescription( );
- nodeToBrowse2.NodeId = sourceId;
- nodeToBrowse2.BrowseDirection = BrowseDirection.Forward;
- nodeToBrowse2.ReferenceTypeId = ReferenceTypeIds.Organizes;
- nodeToBrowse2.IncludeSubtypes = true;
- nodeToBrowse2.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
- nodeToBrowse2.ResultMask = (uint)BrowseResultMask.All;
- BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
- nodesToBrowse.Add( nodeToBrowse1 );
- nodesToBrowse.Add( nodeToBrowse2 );
- // fetch references from the server.
- ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
- return references.ToArray( );
- }
- #endregion BrowseNode Support
- #region Read Attributes Support
- /// <summary>
- /// 读取一个节点的所有属性
- /// </summary>
- /// <param name="tag">节点信息</param>
- /// <returns>节点的特性值</returns>
- public OpcNodeAttribute[] ReadNoteAttributes( string tag )
- {
- NodeId sourceId = new NodeId( tag );
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- // attempt to read all possible attributes.
- // 尝试着去读取所有可能的特性
- for (uint ii = Attributes.NodeClass; ii <= Attributes.UserExecutable; ii++)
- {
- ReadValueId nodeToRead = new ReadValueId( );
- nodeToRead.NodeId = sourceId;
- nodeToRead.AttributeId = ii;
- nodesToRead.Add( nodeToRead );
- }
- int startOfProperties = nodesToRead.Count;
- // find all of the pror of the node.
- BrowseDescription nodeToBrowse1 = new BrowseDescription( );
- nodeToBrowse1.NodeId = sourceId;
- nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
- nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
- nodeToBrowse1.IncludeSubtypes = true;
- nodeToBrowse1.NodeClassMask = 0;
- nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
- BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
- nodesToBrowse.Add( nodeToBrowse1 );
- // fetch property references from the server.
- ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
- if (references == null)
- {
- return new OpcNodeAttribute[0];
- }
- for (int ii = 0; ii < references.Count; ii++)
- {
- // ignore external references.
- if (references[ii].NodeId.IsAbsolute)
- {
- continue;
- }
- ReadValueId nodeToRead = new ReadValueId( );
- nodeToRead.NodeId = (NodeId)references[ii].NodeId;
- nodeToRead.AttributeId = Attributes.Value;
- nodesToRead.Add( nodeToRead );
- }
- // read all values.
- DataValueCollection results = null;
- DiagnosticInfoCollection diagnosticInfos = null;
- m_session.Read(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- out results,
- out diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- // process results.
- List<OpcNodeAttribute> nodeAttribute = new List<OpcNodeAttribute>( );
- for (int ii = 0; ii < results.Count; ii++)
- {
- OpcNodeAttribute item = new OpcNodeAttribute( );
- // process attribute value.
- if (ii < startOfProperties)
- {
- // ignore attributes which are invalid for the node.
- if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
- {
- continue;
- }
- // get the name of the attribute.
- item.Name = Attributes.GetBrowseName( nodesToRead[ii].AttributeId );
- // display any unexpected error.
- if (StatusCode.IsBad( results[ii].StatusCode ))
- {
- item.Type = Utils.Format( "{0}", Attributes.GetDataTypeId( nodesToRead[ii].AttributeId ) );
- item.Value = Utils.Format( "{0}", results[ii].StatusCode );
- }
- // display the value.
- else
- {
- TypeInfo typeInfo = TypeInfo.Construct( results[ii].Value );
- item.Type = typeInfo.BuiltInType.ToString( );
- if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
- {
- item.Type += "[]";
- }
- item.Value = results[ii].Value;//Utils.Format("{0}", results[ii].Value);
- }
- }
- // process property value.
- else
- {
- // ignore properties which are invalid for the node.
- if (results[ii].StatusCode == StatusCodes.BadNodeIdUnknown)
- {
- continue;
- }
- // get the name of the property.
- item.Name = Utils.Format( "{0}", references[ii - startOfProperties] );
- // display any unexpected error.
- if (StatusCode.IsBad( results[ii].StatusCode ))
- {
- item.Type = String.Empty;
- item.Value = Utils.Format( "{0}", results[ii].StatusCode );
- }
- // display the value.
- else
- {
- TypeInfo typeInfo = TypeInfo.Construct( results[ii].Value );
- item.Type = typeInfo.BuiltInType.ToString( );
- if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
- {
- item.Type += "[]";
- }
- item.Value = results[ii].Value; //Utils.Format("{0}", results[ii].Value);
- }
- }
- nodeAttribute.Add( item );
- }
- return nodeAttribute.ToArray( );
- }
- /// <summary>
- /// 读取一个节点的所有属性
- /// </summary>
- /// <param name="tag">节点值</param>
- /// <returns>所有的数据</returns>
- public DataValue[] ReadNoteDataValueAttributes( string tag )
- {
- NodeId sourceId = new NodeId( tag );
- ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
- // attempt to read all possible attributes.
- // 尝试着去读取所有可能的特性
- for (uint ii = Attributes.NodeId; ii <= Attributes.UserExecutable; ii++)
- {
- ReadValueId nodeToRead = new ReadValueId( );
- nodeToRead.NodeId = sourceId;
- nodeToRead.AttributeId = ii;
- nodesToRead.Add( nodeToRead );
- }
- int startOfProperties = nodesToRead.Count;
- // find all of the pror of the node.
- BrowseDescription nodeToBrowse1 = new BrowseDescription( );
- nodeToBrowse1.NodeId = sourceId;
- nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
- nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
- nodeToBrowse1.IncludeSubtypes = true;
- nodeToBrowse1.NodeClassMask = 0;
- nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
- BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
- nodesToBrowse.Add( nodeToBrowse1 );
- // fetch property references from the server.
- ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
- if (references == null)
- {
- return new DataValue[0];
- }
- for (int ii = 0; ii < references.Count; ii++)
- {
- // ignore external references.
- if (references[ii].NodeId.IsAbsolute)
- {
- continue;
- }
- ReadValueId nodeToRead = new ReadValueId( );
- nodeToRead.NodeId = (NodeId)references[ii].NodeId;
- nodeToRead.AttributeId = Attributes.Value;
- nodesToRead.Add( nodeToRead );
- }
- // read all values.
- DataValueCollection results = null;
- DiagnosticInfoCollection diagnosticInfos = null;
- m_session.Read(
- null,
- 0,
- TimestampsToReturn.Neither,
- nodesToRead,
- out results,
- out diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToRead );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
- return results.ToArray( );
- }
- #endregion Read Attributes Support
- #region Method Call Support
- /// <summary>
- /// call a server method
- /// </summary>
- /// <param name="tagParent">方法的父节点tag</param>
- /// <param name="tag">方法的节点tag</param>
- /// <param name="args">传递的参数</param>
- /// <returns>输出的结果值</returns>
- public object[] CallMethodByNodeId( string tagParent, string tag, params object[] args )
- {
- if (m_session == null)
- {
- return null;
- }
- IList<object> outputArguments = m_session.Call(
- new NodeId( tagParent ),
- new NodeId( tag ),
- args );
- return outputArguments.ToArray( );
- }
- #endregion Method Call Support
- #region Private Methods
- /// <summary>
- /// Raises the connect complete event on the main GUI thread.
- /// </summary>
- private void DoConnectComplete( object state )
- {
- m_ConnectComplete?.Invoke( this, null );
- }
- private void CheckReturnValue( StatusCode status )
- {
- if (!StatusCode.IsGood( status ))
- throw new Exception( string.Format( "Invalid response from the server. (Response Status: {0})", status ) );
- }
- #endregion Private Methods
- #region Private Fields
- private ApplicationConfiguration m_configuration;
- private ISession m_session;
- private bool m_IsConnected; //是否已经连接过
- private int m_reconnectPeriod = 10; // 重连状态
- private bool m_useSecurity;
- private SessionReconnectHandler m_reConnectHandler;
- private EventHandler m_ReconnectComplete;
- private EventHandler m_ReconnectStarting;
- private EventHandler m_KeepAliveComplete;
- private EventHandler m_ConnectComplete;
- private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange;
- private Dictionary<string, Subscription> dic_subscriptions; // 系统所有的节点信息
- #endregion Private Fields
- }
- }
|