OpcUaClient.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462
  1. using Opc.Ua;
  2. using Opc.Ua.Bindings;
  3. using Opc.Ua.Client;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Threading.Tasks;
  8. namespace OpcUaHelper
  9. {
  10. /// <summary>
  11. /// 一个二次封装了的OPC UA库,支持从opc ua服务器读写节点数据,批量读写,订阅,批量订阅,历史数据读取,方法调用操作。
  12. /// </summary>
  13. public class OpcUaClient
  14. {
  15. #region Constructors
  16. /// <summary>
  17. /// 默认的构造函数,实例化一个新的OPC UA类
  18. /// </summary>
  19. public OpcUaClient( )
  20. {
  21. dic_subscriptions = new Dictionary<string, Subscription>( );
  22. var certificateValidator = new CertificateValidator( );
  23. certificateValidator.CertificateValidation += ( sender, eventArgs ) =>
  24. {
  25. if (ServiceResult.IsGood( eventArgs.Error ))
  26. eventArgs.Accept = true;
  27. else if (eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)
  28. eventArgs.Accept = true;
  29. else
  30. throw new Exception( string.Format( "Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo ) );
  31. };
  32. SecurityConfiguration securityConfigurationcv = new SecurityConfiguration
  33. {
  34. AutoAcceptUntrustedCertificates = true,
  35. RejectSHA1SignedCertificates = false,
  36. MinimumCertificateKeySize = 1024,
  37. };
  38. // Build the application configuration
  39. var configuration = new ApplicationConfiguration
  40. {
  41. ApplicationName = OpcUaName,
  42. ApplicationType = ApplicationType.Client,
  43. CertificateValidator = certificateValidator,
  44. ApplicationUri = "urn:MyClient", //Kepp this syntax
  45. ProductUri = "OpcUaClient",
  46. ServerConfiguration = new ServerConfiguration
  47. {
  48. MaxSubscriptionCount = 100000,
  49. MaxMessageQueueSize = 1000000,
  50. MaxNotificationQueueSize = 1000000,
  51. MaxPublishRequestCount = 10000000,
  52. },
  53. SecurityConfiguration = new SecurityConfiguration
  54. {
  55. AutoAcceptUntrustedCertificates = true,
  56. RejectSHA1SignedCertificates = false,
  57. MinimumCertificateKeySize = 1024,
  58. SuppressNonceValidationErrors = true,
  59. ApplicationCertificate = new CertificateIdentifier
  60. {
  61. StoreType = CertificateStoreType.X509Store,
  62. StorePath = "CurrentUser\\My",
  63. SubjectName = OpcUaName,
  64. },
  65. TrustedIssuerCertificates = new CertificateTrustList
  66. {
  67. StoreType = CertificateStoreType.X509Store,
  68. StorePath = "CurrentUser\\Root",
  69. },
  70. TrustedPeerCertificates = new CertificateTrustList
  71. {
  72. StoreType = CertificateStoreType.X509Store,
  73. StorePath = "CurrentUser\\Root",
  74. }
  75. },
  76. TransportQuotas = new TransportQuotas
  77. {
  78. OperationTimeout = 6000000,
  79. MaxStringLength = int.MaxValue,
  80. MaxByteStringLength = int.MaxValue,
  81. MaxArrayLength = 65535,
  82. MaxMessageSize = 419430400,
  83. MaxBufferSize = 65535,
  84. ChannelLifetime = -1,
  85. SecurityTokenLifetime = TcpMessageLimits.DefaultSecurityTokenLifeTime
  86. },
  87. ClientConfiguration = new ClientConfiguration
  88. {
  89. DefaultSessionTimeout = -1,
  90. MinSubscriptionLifetime = -1,
  91. },
  92. DisableHiResClock = true
  93. };
  94. configuration.Validate( ApplicationType.Client );
  95. m_configuration = configuration;
  96. }
  97. #endregion Constructor
  98. #region Connect And Disconnect
  99. /// <summary>
  100. /// connect to server
  101. /// </summary>
  102. /// <param name="serverUrl">remote url</param>
  103. public async Task ConnectServer( string serverUrl )
  104. {
  105. m_session = await Connect( serverUrl );
  106. }
  107. /// <summary>
  108. /// Creates a new session.
  109. /// </summary>
  110. /// <returns>The new session object.</returns>
  111. private async Task<ISession> Connect( string serverUrl )
  112. {
  113. // disconnect from existing session.
  114. Disconnect( );
  115. if (m_configuration == null)
  116. {
  117. throw new ArgumentNullException( "_configuration" );
  118. }
  119. // select the best endpoint.
  120. EndpointDescription endpointDescription = await CoreClientUtils.SelectEndpointAsync( m_configuration, serverUrl, UseSecurity, null );
  121. EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create( m_configuration );
  122. ConfiguredEndpoint endpoint = new ConfiguredEndpoint( null, endpointDescription, endpointConfiguration );
  123. m_session = await Opc.Ua.Client.Session.Create(
  124. m_configuration,
  125. endpoint,
  126. false,
  127. false,
  128. (string.IsNullOrEmpty( OpcUaName )) ? m_configuration.ApplicationName : OpcUaName,
  129. 60000,
  130. UserIdentity,
  131. new string[] { } );
  132. // set up keep alive callback.
  133. m_session.KeepAlive += new KeepAliveEventHandler( Session_KeepAlive );
  134. // update the client status
  135. m_IsConnected = true;
  136. // raise an event.
  137. DoConnectComplete( null );
  138. // return the new session.
  139. return m_session;
  140. }
  141. /// <summary>
  142. /// Disconnects from the server.
  143. /// </summary>
  144. public void Disconnect( )
  145. {
  146. UpdateStatus( false, DateTime.UtcNow, "Disconnected" );
  147. // stop any reconnect operation.
  148. if (m_reConnectHandler != null)
  149. {
  150. m_reConnectHandler.Dispose( );
  151. m_reConnectHandler = null;
  152. }
  153. // disconnect any existing session.
  154. if (m_session != null)
  155. {
  156. m_session.Close( 3000 );
  157. m_session.Dispose( );
  158. m_session = null;
  159. }
  160. // update the client status
  161. m_IsConnected = false;
  162. // raise an event.
  163. DoConnectComplete( null );
  164. }
  165. #endregion Connect And Disconnect
  166. #region Event Handlers
  167. /// <summary>
  168. /// Report the client status
  169. /// </summary>
  170. /// <param name="error">Whether the status represents an error.</param>
  171. /// <param name="time">The time associated with the status.</param>
  172. /// <param name="status">The status message.</param>
  173. /// <param name="args">Arguments used to format the status message.</param>
  174. private void UpdateStatus( bool error, DateTime time, string status, params object[] args )
  175. {
  176. m_OpcStatusChange?.Invoke( this, new OpcUaStatusEventArgs( )
  177. {
  178. Error = error,
  179. Time = time.ToLocalTime( ),
  180. Text = String.Format( status, args ),
  181. } );
  182. }
  183. /// <summary>
  184. /// Handles a keep alive event from a session.
  185. /// </summary>
  186. private void Session_KeepAlive( ISession session, KeepAliveEventArgs e )
  187. {
  188. try
  189. {
  190. // check for events from discarded sessions.
  191. if (!Object.ReferenceEquals( session, m_session ))
  192. {
  193. return;
  194. }
  195. // start reconnect sequence on communication error.
  196. if (ServiceResult.IsBad( e.Status ))
  197. {
  198. if (m_reconnectPeriod <= 0)
  199. {
  200. UpdateStatus( true, e.CurrentTime, "Communication Error ({0})", e.Status );
  201. return;
  202. }
  203. UpdateStatus( true, e.CurrentTime, "Reconnecting in {0}s", m_reconnectPeriod );
  204. if (m_reConnectHandler == null)
  205. {
  206. m_ReconnectStarting?.Invoke( this, e );
  207. m_reConnectHandler = new SessionReconnectHandler( );
  208. m_reConnectHandler.BeginReconnect( m_session, m_reconnectPeriod * 1000, Server_ReconnectComplete );
  209. }
  210. return;
  211. }
  212. // update status.
  213. UpdateStatus( false, e.CurrentTime, "Connected [{0}]", session.Endpoint.EndpointUrl );
  214. // raise any additional notifications.
  215. m_KeepAliveComplete?.Invoke( this, e );
  216. }
  217. catch (Exception exception)
  218. {
  219. #if !NETSTANDARD2_0 && !NETSTANDARD2_1
  220. ClientUtils.HandleException( OpcUaName, exception );
  221. #else
  222. throw;
  223. #endif
  224. }
  225. }
  226. /// <summary>
  227. /// Handles a reconnect event complete from the reconnect handler.
  228. /// </summary>
  229. private void Server_ReconnectComplete( object sender, EventArgs e )
  230. {
  231. try
  232. {
  233. // ignore callbacks from discarded objects.
  234. if (!Object.ReferenceEquals( sender, m_reConnectHandler ))
  235. {
  236. return;
  237. }
  238. m_session = m_reConnectHandler.Session;
  239. m_reConnectHandler.Dispose( );
  240. m_reConnectHandler = null;
  241. // raise any additional notifications.
  242. m_ReconnectComplete?.Invoke( this, e );
  243. }
  244. catch (Exception exception)
  245. {
  246. #if !NETSTANDARD2_0 && !NETSTANDARD2_1
  247. ClientUtils.HandleException( OpcUaName, exception );
  248. #else
  249. throw;
  250. #endif
  251. }
  252. }
  253. #endregion Event Handlers
  254. #region LogOut Setting
  255. /// <summary>
  256. /// 设置OPC客户端的日志输出
  257. /// </summary>
  258. /// <param name="filePath">完整的文件路径</param>
  259. /// <param name="deleteExisting">是否删除原文件</param>
  260. public void SetLogPathName( string filePath, bool deleteExisting )
  261. {
  262. Utils.SetTraceLog( filePath, deleteExisting );
  263. Utils.SetTraceMask( 515 );
  264. }
  265. #endregion LogOut Setting
  266. #region Public Members
  267. /// <summary>
  268. /// a name of application name show on server
  269. /// </summary>
  270. public string OpcUaName { get; set; } = "Opc Ua Helper";
  271. /// <summary>
  272. /// Whether to use security when connecting.
  273. /// </summary>
  274. public bool UseSecurity
  275. {
  276. get { return m_useSecurity; }
  277. set { m_useSecurity = value; }
  278. }
  279. /// <summary>
  280. /// The user identity to use when creating the session.
  281. /// </summary>
  282. public IUserIdentity UserIdentity { get; set; }
  283. /// <summary>
  284. /// The currently active session.
  285. /// </summary>
  286. public ISession Session
  287. {
  288. get { return m_session; }
  289. }
  290. /// <summary>
  291. /// Indicate the connect status
  292. /// </summary>
  293. public bool Connected
  294. {
  295. get { return m_IsConnected; }
  296. }
  297. /// <summary>
  298. /// The number of seconds between reconnect attempts (0 means reconnect is disabled).
  299. /// </summary>
  300. public int ReconnectPeriod
  301. {
  302. get { return m_reconnectPeriod; }
  303. set { m_reconnectPeriod = value; }
  304. }
  305. /// <summary>
  306. /// Raised when a good keep alive from the server arrives.
  307. /// </summary>
  308. public event EventHandler KeepAliveComplete
  309. {
  310. add { m_KeepAliveComplete += value; }
  311. remove { m_KeepAliveComplete -= value; }
  312. }
  313. /// <summary>
  314. /// Raised when a reconnect operation starts.
  315. /// </summary>
  316. public event EventHandler ReconnectStarting
  317. {
  318. add { m_ReconnectStarting += value; }
  319. remove { m_ReconnectStarting -= value; }
  320. }
  321. /// <summary>
  322. /// Raised when a reconnect operation completes.
  323. /// </summary>
  324. public event EventHandler ReconnectComplete
  325. {
  326. add { m_ReconnectComplete += value; }
  327. remove { m_ReconnectComplete -= value; }
  328. }
  329. /// <summary>
  330. /// Raised after successfully connecting to or disconnecing from a server.
  331. /// </summary>
  332. public event EventHandler ConnectComplete
  333. {
  334. add { m_ConnectComplete += value; }
  335. remove { m_ConnectComplete -= value; }
  336. }
  337. /// <summary>
  338. /// Raised after the client status change
  339. /// </summary>
  340. public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange
  341. {
  342. add { m_OpcStatusChange += value; }
  343. remove { m_OpcStatusChange -= value; }
  344. }
  345. /// <summary>
  346. /// 配置信息
  347. /// </summary>
  348. public ApplicationConfiguration AppConfig => m_configuration;
  349. #endregion Public Members
  350. #region Node Write/Read Support
  351. /// <summary>
  352. /// Read a value node from server
  353. /// </summary>
  354. /// <param name="nodeId">node id</param>
  355. /// <returns>DataValue</returns>
  356. public DataValue ReadNode( NodeId nodeId )
  357. {
  358. ReadValueIdCollection nodesToRead = new ReadValueIdCollection
  359. {
  360. new ReadValueId( )
  361. {
  362. NodeId = nodeId,
  363. AttributeId = Attributes.Value
  364. }
  365. };
  366. // read the current value
  367. m_session.Read(
  368. null,
  369. 0,
  370. TimestampsToReturn.Neither,
  371. nodesToRead,
  372. out DataValueCollection results,
  373. out DiagnosticInfoCollection diagnosticInfos );
  374. ClientBase.ValidateResponse( results, nodesToRead );
  375. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  376. return results[0];
  377. }
  378. /// <summary>
  379. /// Read a value node from server
  380. /// </summary>
  381. /// <typeparam name="T">type of value</typeparam>
  382. /// <param name="tag">node id</param>
  383. /// <returns>实际值</returns>
  384. public T ReadNode<T>( string tag )
  385. {
  386. DataValue dataValue = ReadNode( new NodeId( tag ) );
  387. return (T)dataValue.Value;
  388. }
  389. /// <summary>
  390. /// Read a tag asynchronously
  391. /// </summary>
  392. /// <typeparam name="T">The type of tag to read</typeparam>
  393. /// <param name="tag">tag值</param>
  394. /// <returns>The value retrieved from the OPC</returns>
  395. public Task<T> ReadNodeAsync<T>( string tag )
  396. {
  397. ReadValueIdCollection nodesToRead = new ReadValueIdCollection
  398. {
  399. new ReadValueId()
  400. {
  401. NodeId = new NodeId(tag),
  402. AttributeId = Attributes.Value
  403. }
  404. };
  405. // Wrap the ReadAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
  406. var taskCompletionSource = new TaskCompletionSource<T>( );
  407. m_session.BeginRead(
  408. requestHeader: null,
  409. maxAge: 0,
  410. timestampsToReturn: TimestampsToReturn.Neither,
  411. nodesToRead: nodesToRead,
  412. callback: ar =>
  413. {
  414. DataValueCollection results;
  415. DiagnosticInfoCollection diag;
  416. var response = m_session.EndRead(
  417. result: ar,
  418. results: out results,
  419. diagnosticInfos: out diag );
  420. try
  421. {
  422. CheckReturnValue( response.ServiceResult );
  423. CheckReturnValue( results[0].StatusCode );
  424. var val = results[0];
  425. taskCompletionSource.TrySetResult( (T)val.Value );
  426. }
  427. catch (Exception ex)
  428. {
  429. taskCompletionSource.TrySetException( ex );
  430. }
  431. },
  432. asyncState: null );
  433. return taskCompletionSource.Task;
  434. }
  435. /// <summary>
  436. /// read several value nodes from server
  437. /// </summary>
  438. /// <param name="nodeIds">all NodeIds</param>
  439. /// <returns>all values</returns>
  440. public List<DataValue> ReadNodes( NodeId[] nodeIds )
  441. {
  442. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  443. for (int i = 0; i < nodeIds.Length; i++)
  444. {
  445. nodesToRead.Add( new ReadValueId( )
  446. {
  447. NodeId = nodeIds[i],
  448. AttributeId = Attributes.Value
  449. } );
  450. }
  451. // 读取当前的值
  452. m_session.Read(
  453. null,
  454. 0,
  455. TimestampsToReturn.Neither,
  456. nodesToRead,
  457. out DataValueCollection results,
  458. out DiagnosticInfoCollection diagnosticInfos );
  459. ClientBase.ValidateResponse( results, nodesToRead );
  460. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  461. return results.ToList( );
  462. }
  463. /// <summary>
  464. /// read several value nodes from server
  465. /// </summary>
  466. /// <param name="nodeIds">all NodeIds</param>
  467. /// <returns>all values</returns>
  468. public Task<List<DataValue>> ReadNodesAsync( NodeId[] nodeIds )
  469. {
  470. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  471. for (int i = 0; i < nodeIds.Length; i++)
  472. {
  473. nodesToRead.Add( new ReadValueId( )
  474. {
  475. NodeId = nodeIds[i],
  476. AttributeId = Attributes.Value
  477. } );
  478. }
  479. var taskCompletionSource = new TaskCompletionSource<List<DataValue>>( );
  480. // 读取当前的值
  481. m_session.BeginRead(
  482. null,
  483. 0,
  484. TimestampsToReturn.Neither,
  485. nodesToRead,
  486. callback: ar =>
  487. {
  488. DataValueCollection results;
  489. DiagnosticInfoCollection diag;
  490. var response = m_session.EndRead(
  491. result: ar,
  492. results: out results,
  493. diagnosticInfos: out diag );
  494. try
  495. {
  496. CheckReturnValue( response.ServiceResult );
  497. taskCompletionSource.TrySetResult( results.ToList( ) );
  498. }
  499. catch (Exception ex)
  500. {
  501. taskCompletionSource.TrySetException( ex );
  502. }
  503. },
  504. asyncState: null );
  505. return taskCompletionSource.Task;
  506. }
  507. /// <summary>
  508. /// read several value nodes from server
  509. /// </summary>
  510. /// <param name="tags">所以的节点数组信息</param>
  511. /// <returns>all values</returns>
  512. public List<T> ReadNodes<T>( string[] tags )
  513. {
  514. List<T> result = new List<T>( );
  515. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  516. for (int i = 0; i < tags.Length; i++)
  517. {
  518. nodesToRead.Add( new ReadValueId( )
  519. {
  520. NodeId = new NodeId( tags[i] ),
  521. AttributeId = Attributes.Value
  522. } );
  523. }
  524. // 读取当前的值
  525. m_session.Read(
  526. null,
  527. 0,
  528. TimestampsToReturn.Neither,
  529. nodesToRead,
  530. out DataValueCollection results,
  531. out DiagnosticInfoCollection diagnosticInfos );
  532. ClientBase.ValidateResponse( results, nodesToRead );
  533. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  534. foreach (var item in results)
  535. {
  536. result.Add( (T)item.Value );
  537. }
  538. return result;
  539. }
  540. /// <summary>
  541. /// read several value nodes from server
  542. /// </summary>
  543. /// <param name="tags">all NodeIds</param>
  544. /// <returns>all values</returns>
  545. public Task<List<T>> ReadNodesAsync<T>( string[] tags )
  546. {
  547. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  548. for (int i = 0; i < tags.Length; i++)
  549. {
  550. nodesToRead.Add( new ReadValueId( )
  551. {
  552. NodeId = new NodeId( tags[i] ),
  553. AttributeId = Attributes.Value
  554. } );
  555. }
  556. var taskCompletionSource = new TaskCompletionSource<List<T>>( );
  557. // 读取当前的值
  558. m_session.BeginRead(
  559. null,
  560. 0,
  561. TimestampsToReturn.Neither,
  562. nodesToRead,
  563. callback: ar =>
  564. {
  565. DataValueCollection results;
  566. DiagnosticInfoCollection diag;
  567. var response = m_session.EndRead(
  568. result: ar,
  569. results: out results,
  570. diagnosticInfos: out diag );
  571. try
  572. {
  573. CheckReturnValue( response.ServiceResult );
  574. List<T> result = new List<T>( );
  575. foreach (var item in results)
  576. {
  577. result.Add( (T)item.Value );
  578. }
  579. taskCompletionSource.TrySetResult( result );
  580. }
  581. catch (Exception ex)
  582. {
  583. taskCompletionSource.TrySetException( ex );
  584. }
  585. },
  586. asyncState: null );
  587. return taskCompletionSource.Task;
  588. }
  589. /// <summary>
  590. /// write a note to server(you should use try catch)
  591. /// </summary>
  592. /// <typeparam name="T">The type of tag to write on</typeparam>
  593. /// <param name="tag">节点名称</param>
  594. /// <param name="value">值</param>
  595. /// <returns>if success True,otherwise False</returns>
  596. public bool WriteNode<T>( string tag, T value )
  597. {
  598. WriteValue valueToWrite = new WriteValue( )
  599. {
  600. NodeId = new NodeId( tag ),
  601. AttributeId = Attributes.Value
  602. };
  603. valueToWrite.Value.Value = value;
  604. valueToWrite.Value.StatusCode = StatusCodes.Good;
  605. valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
  606. valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
  607. WriteValueCollection valuesToWrite = new WriteValueCollection
  608. {
  609. valueToWrite
  610. };
  611. // 写入当前的值
  612. m_session.Write(
  613. null,
  614. valuesToWrite,
  615. out StatusCodeCollection results,
  616. out DiagnosticInfoCollection diagnosticInfos );
  617. ClientBase.ValidateResponse( results, valuesToWrite );
  618. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, valuesToWrite );
  619. if (StatusCode.IsBad( results[0] ))
  620. {
  621. throw new ServiceResultException( results[0] );
  622. }
  623. return !StatusCode.IsBad( results[0] );
  624. }
  625. /// <summary>
  626. /// Write a value on the specified opc tag asynchronously
  627. /// </summary>
  628. /// <typeparam name="T">The type of tag to write on</typeparam>
  629. /// <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>
  630. /// <param name="value">The value for the item to write</param>
  631. public Task<bool> WriteNodeAsync<T>( string tag, T value )
  632. {
  633. WriteValue valueToWrite = new WriteValue( )
  634. {
  635. NodeId = new NodeId( tag ),
  636. AttributeId = Attributes.Value,
  637. };
  638. valueToWrite.Value.Value = value;
  639. valueToWrite.Value.StatusCode = StatusCodes.Good;
  640. valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
  641. valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
  642. WriteValueCollection valuesToWrite = new WriteValueCollection
  643. {
  644. valueToWrite
  645. };
  646. // Wrap the WriteAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
  647. var taskCompletionSource = new TaskCompletionSource<bool>( );
  648. m_session.BeginWrite(
  649. requestHeader: null,
  650. nodesToWrite: valuesToWrite,
  651. callback: ar =>
  652. {
  653. var response = m_session.EndWrite(
  654. result: ar,
  655. results: out StatusCodeCollection results,
  656. diagnosticInfos: out DiagnosticInfoCollection diag );
  657. try
  658. {
  659. ClientBase.ValidateResponse( results, valuesToWrite );
  660. ClientBase.ValidateDiagnosticInfos( diag, valuesToWrite );
  661. taskCompletionSource.SetResult( StatusCode.IsGood( results[0] ) );
  662. }
  663. catch (Exception ex)
  664. {
  665. taskCompletionSource.TrySetException( ex );
  666. }
  667. },
  668. asyncState: null );
  669. return taskCompletionSource.Task;
  670. }
  671. /// <summary>
  672. /// 所有的节点都写入成功,返回<c>True</c>,否则返回<c>False</c>
  673. /// </summary>
  674. /// <param name="tags">节点名称数组</param>
  675. /// <param name="values">节点的值数据</param>
  676. /// <returns>所有的是否都写入成功</returns>
  677. public bool WriteNodes( string[] tags, object[] values )
  678. {
  679. WriteValueCollection valuesToWrite = new WriteValueCollection( );
  680. for (int i = 0; i < tags.Length; i++)
  681. {
  682. if (i < values.Length)
  683. {
  684. WriteValue valueToWrite = new WriteValue( )
  685. {
  686. NodeId = new NodeId( tags[i] ),
  687. AttributeId = Attributes.Value
  688. };
  689. valueToWrite.Value.Value = values[i];
  690. valueToWrite.Value.StatusCode = StatusCodes.Good;
  691. valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
  692. valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
  693. valuesToWrite.Add( valueToWrite );
  694. }
  695. }
  696. // 写入当前的值
  697. m_session.Write(
  698. null,
  699. valuesToWrite,
  700. out StatusCodeCollection results,
  701. out DiagnosticInfoCollection diagnosticInfos );
  702. ClientBase.ValidateResponse( results, valuesToWrite );
  703. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, valuesToWrite );
  704. bool result = true;
  705. foreach (var r in results)
  706. {
  707. if (StatusCode.IsBad( r ))
  708. {
  709. result = false;
  710. break;
  711. }
  712. }
  713. return result;
  714. }
  715. #endregion Node Write/Read Support
  716. #region DeleteNode Support
  717. /// <summary>
  718. /// 删除一个节点的操作,除非服务器配置允许,否则引发异常,成功返回<c>True</c>,否则返回<c>False</c>
  719. /// </summary>
  720. /// <param name="tag">节点文本描述</param>
  721. /// <returns>是否删除成功</returns>
  722. public bool DeleteExsistNode( string tag )
  723. {
  724. DeleteNodesItemCollection waitDelete = new DeleteNodesItemCollection( );
  725. DeleteNodesItem nodesItem = new DeleteNodesItem( )
  726. {
  727. NodeId = new NodeId( tag ),
  728. };
  729. m_session.DeleteNodes(
  730. null,
  731. waitDelete,
  732. out StatusCodeCollection results,
  733. out DiagnosticInfoCollection diagnosticInfos );
  734. ClientBase.ValidateResponse( results, waitDelete );
  735. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, waitDelete );
  736. return !StatusCode.IsBad( results[0] );
  737. }
  738. #endregion DeleteNode Support
  739. #region Test Function
  740. /// <summary>
  741. /// 新增一个节点数据
  742. /// </summary>
  743. /// <param name="parent">父节点tag名称</param>
  744. [Obsolete( "还未经过测试,无法使用" )]
  745. public void AddNewNode( NodeId parent )
  746. {
  747. // Create a Variable node.
  748. AddNodesItem node2 = new AddNodesItem( );
  749. node2.ParentNodeId = new NodeId( parent );
  750. node2.ReferenceTypeId = ReferenceTypes.HasComponent;
  751. node2.RequestedNewNodeId = null;
  752. node2.BrowseName = new QualifiedName( "DataVariable1" );
  753. node2.NodeClass = NodeClass.Variable;
  754. node2.NodeAttributes = null;
  755. node2.TypeDefinition = VariableTypeIds.BaseDataVariableType;
  756. //specify node attributes.
  757. VariableAttributes node2Attribtues = new VariableAttributes( );
  758. node2Attribtues.DisplayName = "DataVariable1";
  759. node2Attribtues.Description = "DataVariable1 Description";
  760. node2Attribtues.Value = new Variant( 123 );
  761. node2Attribtues.DataType = (uint)BuiltInType.Int32;
  762. node2Attribtues.ValueRank = ValueRanks.Scalar;
  763. node2Attribtues.ArrayDimensions = new UInt32Collection( );
  764. node2Attribtues.AccessLevel = AccessLevels.CurrentReadOrWrite;
  765. node2Attribtues.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
  766. node2Attribtues.MinimumSamplingInterval = 0;
  767. node2Attribtues.Historizing = false;
  768. node2Attribtues.WriteMask = (uint)AttributeWriteMask.None;
  769. node2Attribtues.UserWriteMask = (uint)AttributeWriteMask.None;
  770. node2Attribtues.SpecifiedAttributes = (uint)NodeAttributesMask.All;
  771. node2.NodeAttributes = new ExtensionObject( node2Attribtues );
  772. AddNodesItemCollection nodesToAdd = new AddNodesItemCollection { node2 };
  773. m_session.AddNodes(
  774. null,
  775. nodesToAdd,
  776. out AddNodesResultCollection results,
  777. out DiagnosticInfoCollection diagnosticInfos );
  778. ClientBase.ValidateResponse( results, nodesToAdd );
  779. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToAdd );
  780. }
  781. #endregion Test Function
  782. #region Monitor Support
  783. /// <summary>
  784. /// 新增一个订阅,需要指定订阅的关键字,订阅的tag名,以及回调方法
  785. /// </summary>
  786. /// <param name="key">关键字</param>
  787. /// <param name="tag">tag</param>
  788. /// <param name="callback">回调方法</param>
  789. public void AddSubscription( string key, string tag, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback )
  790. {
  791. AddSubscription( key, new string[] { tag }, callback );
  792. }
  793. /// <inheritdoc cref="AddSubscription(string, string[], Action{string, MonitoredItem, MonitoredItemNotificationEventArgs}, Action{Subscription})"/>
  794. public void AddSubscription( string key, string[] tags, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback )
  795. {
  796. AddSubscription( key, tags, callback, null );
  797. }
  798. /// <summary>
  799. /// 新增一批订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法
  800. /// </summary>
  801. /// <param name="key">关键字</param>
  802. /// <param name="tags">节点名称数组</param>
  803. /// <param name="callback">回调方法</param>
  804. /// <param name="subAction">关于订阅参数的自定义的设置</param>
  805. public void AddSubscription( string key, string[] tags, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback, Action<Subscription> subAction )
  806. {
  807. Subscription m_subscription = new Subscription( m_session.DefaultSubscription );
  808. m_subscription.PublishingEnabled = true;
  809. m_subscription.PublishingInterval = 0;
  810. m_subscription.KeepAliveCount = ushort.MaxValue;
  811. m_subscription.LifetimeCount = 2400;
  812. m_subscription.MaxNotificationsPerPublish = ushort.MaxValue;
  813. m_subscription.Priority = 100;
  814. m_subscription.DisplayName = key;
  815. subAction?.Invoke( m_subscription );
  816. for (int i = 0; i < tags.Length; i++)
  817. {
  818. var item = new MonitoredItem
  819. {
  820. StartNodeId = new NodeId( tags[i] ),
  821. AttributeId = Attributes.Value,
  822. DisplayName = tags[i],
  823. SamplingInterval = 100,
  824. };
  825. item.Notification += ( MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args ) =>
  826. {
  827. callback?.Invoke( key, monitoredItem, args );
  828. };
  829. m_subscription.AddItem( item );
  830. }
  831. m_session.AddSubscription( m_subscription );
  832. m_subscription.Create( );
  833. lock (dic_subscriptions)
  834. {
  835. if (dic_subscriptions.ContainsKey( key ))
  836. {
  837. // remove
  838. dic_subscriptions[key].Delete( true );
  839. m_session.RemoveSubscription( dic_subscriptions[key] );
  840. dic_subscriptions[key].Dispose( );
  841. dic_subscriptions[key] = m_subscription;
  842. }
  843. else
  844. {
  845. dic_subscriptions.Add( key, m_subscription );
  846. }
  847. }
  848. }
  849. /// <summary>
  850. /// 移除订阅消息,如果该订阅消息是批量的,也直接移除
  851. /// </summary>
  852. /// <param name="key">订阅关键值</param>
  853. public void RemoveSubscription( string key )
  854. {
  855. lock (dic_subscriptions)
  856. {
  857. if (dic_subscriptions.ContainsKey( key ))
  858. {
  859. // remove
  860. dic_subscriptions[key].Delete( true );
  861. m_session.RemoveSubscription( dic_subscriptions[key] );
  862. dic_subscriptions[key].Dispose( );
  863. dic_subscriptions.Remove( key );
  864. }
  865. }
  866. }
  867. /// <summary>
  868. /// 移除所有的订阅消息
  869. /// </summary>
  870. public void RemoveAllSubscription( )
  871. {
  872. lock (dic_subscriptions)
  873. {
  874. foreach (var item in dic_subscriptions)
  875. {
  876. item.Value.Delete( true );
  877. m_session.RemoveSubscription( item.Value );
  878. item.Value.Dispose( );
  879. }
  880. dic_subscriptions.Clear( );
  881. }
  882. }
  883. #endregion Monitor Support
  884. #region ReadHistory Support
  885. /// <summary>
  886. /// read History data
  887. /// </summary>
  888. /// <param name="tag">节点的索引</param>
  889. /// <param name="start">开始时间</param>
  890. /// <param name="end">结束时间</param>
  891. /// <param name="count">读取的个数</param>
  892. /// <param name="containBound">是否包含边界</param>
  893. /// <returns>读取的数据列表</returns>
  894. public IEnumerable<DataValue> ReadHistoryRawDataValues( string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false )
  895. {
  896. HistoryReadValueId m_nodeToContinue = new HistoryReadValueId( )
  897. {
  898. NodeId = new NodeId( tag ),
  899. };
  900. ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
  901. {
  902. StartTime = start,
  903. EndTime = end,
  904. NumValuesPerNode = count,
  905. IsReadModified = false,
  906. ReturnBounds = containBound
  907. };
  908. HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection( );
  909. nodesToRead.Add( m_nodeToContinue );
  910. m_session.HistoryRead(
  911. null,
  912. new ExtensionObject( m_details ),
  913. TimestampsToReturn.Both,
  914. false,
  915. nodesToRead,
  916. out HistoryReadResultCollection results,
  917. out DiagnosticInfoCollection diagnosticInfos );
  918. ClientBase.ValidateResponse( results, nodesToRead );
  919. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  920. if (StatusCode.IsBad( results[0].StatusCode ))
  921. {
  922. throw new ServiceResultException( results[0].StatusCode );
  923. }
  924. HistoryData values = ExtensionObject.ToEncodeable( results[0].HistoryData ) as HistoryData;
  925. foreach (var value in values.DataValues)
  926. {
  927. yield return value;
  928. }
  929. }
  930. /// <summary>
  931. /// 读取一连串的历史数据,并将其转化成指定的类型
  932. /// </summary>
  933. /// <param name="tag">节点的索引</param>
  934. /// <param name="start">开始时间</param>
  935. /// <param name="end">结束时间</param>
  936. /// <param name="count">读取的个数</param>
  937. /// <param name="containBound">是否包含边界</param>
  938. /// <returns>读取的数据列表</returns>
  939. public IEnumerable<T> ReadHistoryRawDataValues<T>( string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false )
  940. {
  941. HistoryReadValueId m_nodeToContinue = new HistoryReadValueId( )
  942. {
  943. NodeId = new NodeId( tag ),
  944. };
  945. ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
  946. {
  947. StartTime = start.ToUniversalTime( ),
  948. EndTime = end.ToUniversalTime( ),
  949. NumValuesPerNode = count,
  950. IsReadModified = false,
  951. ReturnBounds = containBound
  952. };
  953. HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection( );
  954. nodesToRead.Add( m_nodeToContinue );
  955. m_session.HistoryRead(
  956. null,
  957. new ExtensionObject( m_details ),
  958. TimestampsToReturn.Both,
  959. false,
  960. nodesToRead,
  961. out HistoryReadResultCollection results,
  962. out DiagnosticInfoCollection diagnosticInfos );
  963. ClientBase.ValidateResponse( results, nodesToRead );
  964. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  965. if (StatusCode.IsBad( results[0].StatusCode ))
  966. {
  967. throw new ServiceResultException( results[0].StatusCode );
  968. }
  969. HistoryData values = ExtensionObject.ToEncodeable( results[0].HistoryData ) as HistoryData;
  970. foreach (var value in values.DataValues)
  971. {
  972. yield return (T)value.Value;
  973. }
  974. }
  975. #endregion ReadHistory Support
  976. #region BrowseNode Support
  977. /// <summary>
  978. /// 浏览一个节点的引用
  979. /// </summary>
  980. /// <param name="tag">节点值</param>
  981. /// <returns>引用节点描述</returns>
  982. public ReferenceDescription[] BrowseNodeReference( string tag )
  983. {
  984. NodeId sourceId = new NodeId( tag );
  985. // 该节点可以读取到方法
  986. BrowseDescription nodeToBrowse1 = new BrowseDescription( );
  987. nodeToBrowse1.NodeId = sourceId;
  988. nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
  989. nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
  990. nodeToBrowse1.IncludeSubtypes = true;
  991. nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method);
  992. nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
  993. // 该节点无论怎么样都读取不到方法
  994. // find all nodes organized by the node.
  995. BrowseDescription nodeToBrowse2 = new BrowseDescription( );
  996. nodeToBrowse2.NodeId = sourceId;
  997. nodeToBrowse2.BrowseDirection = BrowseDirection.Forward;
  998. nodeToBrowse2.ReferenceTypeId = ReferenceTypeIds.Organizes;
  999. nodeToBrowse2.IncludeSubtypes = true;
  1000. nodeToBrowse2.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
  1001. nodeToBrowse2.ResultMask = (uint)BrowseResultMask.All;
  1002. BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
  1003. nodesToBrowse.Add( nodeToBrowse1 );
  1004. nodesToBrowse.Add( nodeToBrowse2 );
  1005. // fetch references from the server.
  1006. ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
  1007. return references.ToArray( );
  1008. }
  1009. #endregion BrowseNode Support
  1010. #region Read Attributes Support
  1011. /// <summary>
  1012. /// 读取一个节点的所有属性
  1013. /// </summary>
  1014. /// <param name="tag">节点信息</param>
  1015. /// <returns>节点的特性值</returns>
  1016. public OpcNodeAttribute[] ReadNoteAttributes( string tag )
  1017. {
  1018. NodeId sourceId = new NodeId( tag );
  1019. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  1020. // attempt to read all possible attributes.
  1021. // 尝试着去读取所有可能的特性
  1022. for (uint ii = Attributes.NodeClass; ii <= Attributes.UserExecutable; ii++)
  1023. {
  1024. ReadValueId nodeToRead = new ReadValueId( );
  1025. nodeToRead.NodeId = sourceId;
  1026. nodeToRead.AttributeId = ii;
  1027. nodesToRead.Add( nodeToRead );
  1028. }
  1029. int startOfProperties = nodesToRead.Count;
  1030. // find all of the pror of the node.
  1031. BrowseDescription nodeToBrowse1 = new BrowseDescription( );
  1032. nodeToBrowse1.NodeId = sourceId;
  1033. nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
  1034. nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
  1035. nodeToBrowse1.IncludeSubtypes = true;
  1036. nodeToBrowse1.NodeClassMask = 0;
  1037. nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
  1038. BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
  1039. nodesToBrowse.Add( nodeToBrowse1 );
  1040. // fetch property references from the server.
  1041. ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
  1042. if (references == null)
  1043. {
  1044. return new OpcNodeAttribute[0];
  1045. }
  1046. for (int ii = 0; ii < references.Count; ii++)
  1047. {
  1048. // ignore external references.
  1049. if (references[ii].NodeId.IsAbsolute)
  1050. {
  1051. continue;
  1052. }
  1053. ReadValueId nodeToRead = new ReadValueId( );
  1054. nodeToRead.NodeId = (NodeId)references[ii].NodeId;
  1055. nodeToRead.AttributeId = Attributes.Value;
  1056. nodesToRead.Add( nodeToRead );
  1057. }
  1058. // read all values.
  1059. DataValueCollection results = null;
  1060. DiagnosticInfoCollection diagnosticInfos = null;
  1061. m_session.Read(
  1062. null,
  1063. 0,
  1064. TimestampsToReturn.Neither,
  1065. nodesToRead,
  1066. out results,
  1067. out diagnosticInfos );
  1068. ClientBase.ValidateResponse( results, nodesToRead );
  1069. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  1070. // process results.
  1071. List<OpcNodeAttribute> nodeAttribute = new List<OpcNodeAttribute>( );
  1072. for (int ii = 0; ii < results.Count; ii++)
  1073. {
  1074. OpcNodeAttribute item = new OpcNodeAttribute( );
  1075. // process attribute value.
  1076. if (ii < startOfProperties)
  1077. {
  1078. // ignore attributes which are invalid for the node.
  1079. if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
  1080. {
  1081. continue;
  1082. }
  1083. // get the name of the attribute.
  1084. item.Name = Attributes.GetBrowseName( nodesToRead[ii].AttributeId );
  1085. // display any unexpected error.
  1086. if (StatusCode.IsBad( results[ii].StatusCode ))
  1087. {
  1088. item.Type = Utils.Format( "{0}", Attributes.GetDataTypeId( nodesToRead[ii].AttributeId ) );
  1089. item.Value = Utils.Format( "{0}", results[ii].StatusCode );
  1090. }
  1091. // display the value.
  1092. else
  1093. {
  1094. TypeInfo typeInfo = TypeInfo.Construct( results[ii].Value );
  1095. item.Type = typeInfo.BuiltInType.ToString( );
  1096. if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
  1097. {
  1098. item.Type += "[]";
  1099. }
  1100. item.Value = results[ii].Value;//Utils.Format("{0}", results[ii].Value);
  1101. }
  1102. }
  1103. // process property value.
  1104. else
  1105. {
  1106. // ignore properties which are invalid for the node.
  1107. if (results[ii].StatusCode == StatusCodes.BadNodeIdUnknown)
  1108. {
  1109. continue;
  1110. }
  1111. // get the name of the property.
  1112. item.Name = Utils.Format( "{0}", references[ii - startOfProperties] );
  1113. // display any unexpected error.
  1114. if (StatusCode.IsBad( results[ii].StatusCode ))
  1115. {
  1116. item.Type = String.Empty;
  1117. item.Value = Utils.Format( "{0}", results[ii].StatusCode );
  1118. }
  1119. // display the value.
  1120. else
  1121. {
  1122. TypeInfo typeInfo = TypeInfo.Construct( results[ii].Value );
  1123. item.Type = typeInfo.BuiltInType.ToString( );
  1124. if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
  1125. {
  1126. item.Type += "[]";
  1127. }
  1128. item.Value = results[ii].Value; //Utils.Format("{0}", results[ii].Value);
  1129. }
  1130. }
  1131. nodeAttribute.Add( item );
  1132. }
  1133. return nodeAttribute.ToArray( );
  1134. }
  1135. /// <summary>
  1136. /// 读取一个节点的所有属性
  1137. /// </summary>
  1138. /// <param name="tag">节点值</param>
  1139. /// <returns>所有的数据</returns>
  1140. public DataValue[] ReadNoteDataValueAttributes( string tag )
  1141. {
  1142. NodeId sourceId = new NodeId( tag );
  1143. ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
  1144. // attempt to read all possible attributes.
  1145. // 尝试着去读取所有可能的特性
  1146. for (uint ii = Attributes.NodeId; ii <= Attributes.UserExecutable; ii++)
  1147. {
  1148. ReadValueId nodeToRead = new ReadValueId( );
  1149. nodeToRead.NodeId = sourceId;
  1150. nodeToRead.AttributeId = ii;
  1151. nodesToRead.Add( nodeToRead );
  1152. }
  1153. int startOfProperties = nodesToRead.Count;
  1154. // find all of the pror of the node.
  1155. BrowseDescription nodeToBrowse1 = new BrowseDescription( );
  1156. nodeToBrowse1.NodeId = sourceId;
  1157. nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
  1158. nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
  1159. nodeToBrowse1.IncludeSubtypes = true;
  1160. nodeToBrowse1.NodeClassMask = 0;
  1161. nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
  1162. BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
  1163. nodesToBrowse.Add( nodeToBrowse1 );
  1164. // fetch property references from the server.
  1165. ReferenceDescriptionCollection references = FormUtils.Browse( m_session, nodesToBrowse, false );
  1166. if (references == null)
  1167. {
  1168. return new DataValue[0];
  1169. }
  1170. for (int ii = 0; ii < references.Count; ii++)
  1171. {
  1172. // ignore external references.
  1173. if (references[ii].NodeId.IsAbsolute)
  1174. {
  1175. continue;
  1176. }
  1177. ReadValueId nodeToRead = new ReadValueId( );
  1178. nodeToRead.NodeId = (NodeId)references[ii].NodeId;
  1179. nodeToRead.AttributeId = Attributes.Value;
  1180. nodesToRead.Add( nodeToRead );
  1181. }
  1182. // read all values.
  1183. DataValueCollection results = null;
  1184. DiagnosticInfoCollection diagnosticInfos = null;
  1185. m_session.Read(
  1186. null,
  1187. 0,
  1188. TimestampsToReturn.Neither,
  1189. nodesToRead,
  1190. out results,
  1191. out diagnosticInfos );
  1192. ClientBase.ValidateResponse( results, nodesToRead );
  1193. ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
  1194. return results.ToArray( );
  1195. }
  1196. #endregion Read Attributes Support
  1197. #region Method Call Support
  1198. /// <summary>
  1199. /// call a server method
  1200. /// </summary>
  1201. /// <param name="tagParent">方法的父节点tag</param>
  1202. /// <param name="tag">方法的节点tag</param>
  1203. /// <param name="args">传递的参数</param>
  1204. /// <returns>输出的结果值</returns>
  1205. public object[] CallMethodByNodeId( string tagParent, string tag, params object[] args )
  1206. {
  1207. if (m_session == null)
  1208. {
  1209. return null;
  1210. }
  1211. IList<object> outputArguments = m_session.Call(
  1212. new NodeId( tagParent ),
  1213. new NodeId( tag ),
  1214. args );
  1215. return outputArguments.ToArray( );
  1216. }
  1217. #endregion Method Call Support
  1218. #region Private Methods
  1219. /// <summary>
  1220. /// Raises the connect complete event on the main GUI thread.
  1221. /// </summary>
  1222. private void DoConnectComplete( object state )
  1223. {
  1224. m_ConnectComplete?.Invoke( this, null );
  1225. }
  1226. private void CheckReturnValue( StatusCode status )
  1227. {
  1228. if (!StatusCode.IsGood( status ))
  1229. throw new Exception( string.Format( "Invalid response from the server. (Response Status: {0})", status ) );
  1230. }
  1231. #endregion Private Methods
  1232. #region Private Fields
  1233. private ApplicationConfiguration m_configuration;
  1234. private ISession m_session;
  1235. private bool m_IsConnected; //是否已经连接过
  1236. private int m_reconnectPeriod = 10; // 重连状态
  1237. private bool m_useSecurity;
  1238. private SessionReconnectHandler m_reConnectHandler;
  1239. private EventHandler m_ReconnectComplete;
  1240. private EventHandler m_ReconnectStarting;
  1241. private EventHandler m_KeepAliveComplete;
  1242. private EventHandler m_ConnectComplete;
  1243. private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange;
  1244. private Dictionary<string, Subscription> dic_subscriptions; // 系统所有的节点信息
  1245. #endregion Private Fields
  1246. }
  1247. }