2017-11-24 16 views
0

opc-ua 연결 관련 작업에 대해 첨부 된 OpcUaConnector 클래스를 작성했습니다.opcua 세션이 클라이언트에 의해 닫혔습니다.

하지만 세션을 처리하지 않습니다. 예를 들어 :

  1. kepserver 구성에서

  2. 이 실행 한 엔드 포인트를 사용할 OPC UA 구성에서>

을 다시 초기화 창문 서비스를 던지고있다 :

출처 : system.Reactive.Core
InnerException : 세션이 클라이언트에 의해 닫혔습니다.이 오류는 처리되지 않으므로 Windows 서비스를 중지하고

이 종료되었습니다.

opc-ua에서 세션을 처리하는 방법을 제안 할 수 있습니까?

public class OpcUaConnector 
{ 
    private static SimplerAES simplerAES = new SimplerAES(); 

    private DataContainer dataCointainer = null; 

    private UaTcpSessionChannel channel; 

    private string opcServerName = string.Empty; 
    private string opcUserId = string.Empty; 
    private string opcPassword = string.Empty; 

    private static ILog LogOpcStore; 

    private static System.IDisposable token; 

    private static uint id; 
    public OpcConnector(ILog Log) 
    { 
     IntializeLogOpcStore(Log); 
    } 

    private static void IntializeLogOpcStore(ILog Log) 
    { 
     LogOpcStore = Log; 
    } 

    public async Task OpenOpcConnection() 
    { 
     try 
     { 
      if ((!string.IsNullOrEmpty(this.opcServerName) & (this.opcServerName != AppMain.MyAppSettings.OpcServer)) || 
      (!string.IsNullOrEmpty(this.opcUserId) & (this.opcUserId != AppMain.MyAppSettings.OpcUserId)) || 
      (!string.IsNullOrEmpty(this.opcPassword) & (this.opcPassword != AppMain.MyAppSettings.OpcPassword))) 
      { 
       await channel.CloseAsync(); 
       this.opcServerName = AppMain.MyAppSettings.OpcServer; 
       this.opcUserId = AppMain.MyAppSettings.OpcUserId; 
       this.opcPassword = AppMain.MyAppSettings.OpcPassword; 
      } 

      if (channel==null || (channel != null && (channel.State == CommunicationState.Closed || channel.State == CommunicationState.Faulted))) 
      { 
       var appDescription = new ApplicationDescription() 
       { 
        ApplicationName = "MyAppName", 
        ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:MyAppName", 
        ApplicationType = ApplicationType.Client, 
       }; 

       //application data won't be deleted when uninstall 
       var certificateStore = new DirectoryStore(
        Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), MyAppName", "pki"), 
        true, true 
        ); 

       //if the Ethernet cable unplugs or the Wifi drops out, 
       //you have some timeouts that can keep the session open for a while. 
       //There is a SessionTimeout (default of 2 min). 

       this.channel = new UaTcpSessionChannel(
           appDescription, 
           certificateStore, 
           SignInOpc, 
           AppMain.MyAppSettings.OpcServer, 
           null, 
           options: new UaTcpSessionChannelOptions { SessionTimeout = 120000 }); 

       await channel.OpenAsync(); 

       //LogOpcStore.Info(String.Format("Opc connection sucessful")); 
      } 

      this.opcServerName = AppMain.MyAppSettings.OpcServer; 
      this.opcUserId = AppMain.MyAppSettings.OpcUserId; 
      this.opcPassword = AppMain.MyAppSettings.OpcPassword; 
     } 
     catch (Exception ex) 
     { 
      ServiceException serviceException = new ServiceException(ex.HResult + " " + ex.Message, "C052"); 

      throw serviceException; 
     } 
    } 

    private static async Task RecursivelyFindNode(UaTcpSessionChannel channel, NodeId nodeid) 
    { 
     BrowseRequest browseRequest = new BrowseRequest 
     { 
      NodesToBrowse = new BrowseDescription[] { new BrowseDescription { NodeId = nodeid, BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), NodeClassMask = (uint)NodeClass.Variable | (uint)NodeClass.Object, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All } }, 
     }; 

     BrowseResponse browseResponse = await channel.BrowseAsync(browseRequest); 

     foreach (var rd1 in browseResponse.Results[0].References ?? new ReferenceDescription[0]) 
     { 
      uint chid = AppMain.MyTagDatabase.GetClientHandleByTag(rd1.DisplayName.ToString()); 

      if (chid > 0) 
      { 
       AppMain.MyTagDatabase.UpdateNodeByClientHandle(chid, rd1.NodeId.ToString()); 
      } 

      await RecursivelyFindNode(channel, ExpandedNodeId.ToNodeId(rd1.NodeId, channel.NamespaceUris)); 
     } 
    } 

    public async Task CreateSubscription(DataContainer dc) 
    { 
     double curReadingValue; 

     try 
     { 
      dataCointainer = dc; 

      await RecursivelyFindNode(channel, NodeId.Parse(ObjectIds.RootFolder)); 

      if (AppMain.MyTagDatabase.GetCntTagsNotInOpcServer() == AppMain.MyTagDatabase.GetTagCount()) 
      { 
       //no need to create subscription 
       return; 
      } 


      //subscription timeout that is the product of PublishingInterval * LifetimeCount: 

      var subscriptionRequest = new CreateSubscriptionRequest 
      { 
       RequestedPublishingInterval = 1000f, 
       RequestedMaxKeepAliveCount = 30, 
       RequestedLifetimeCount = 30 * 3, 
       PublishingEnabled = true, 
      }; 

      var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest); 

      id = subscriptionResponse.SubscriptionId; 

      var itemsToCreate = new MonitoredItemCreateRequest[AppMain.MyTagDatabase.GetTagHavingNodeCount()]; 

      int i = 0; 

      foreach (var item in AppMain.MyTagDatabase.GetMyTagDatabase()) 
      { 
       var itemKey = item.Key; 
       var itemValue = item.Value; 

       itemsToCreate[i] = new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse(itemValue.NodeId), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = itemKey, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } }; 
       i++; 
      } 

      var itemsRequest = new CreateMonitoredItemsRequest 
      { 
       SubscriptionId = id, 
       ItemsToCreate = itemsToCreate, 
      }; 

      var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest); 

      token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr => 
      { 
      // loop thru all the data change notifications 
      // receiving data change notifications here 
      var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>(); 

       foreach (var dcn in dcns) 
       { 
        foreach (var min in dcn.MonitoredItems) 
        { 
         MyTag MyTag = new MyTag(); 

         bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag); 

         if (hasValue) 
         { 
          if (double.TryParse(min.Value.Value.ToString(), out curReadingValue)) 
          { 
           //LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue)); 
           AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue); 
          } 
         } 
        } 
       } 
      }); 
     } 
     catch (Exception ex) 
     { 
      //If the interruption lasts longer than these timeouts then the SessionChannel and Subscriptions will need to be recreated. 

      channel = null; 

      FatalServiceException fatalserviceException = new FatalServiceException(ex.Message, "C052"); 
      throw fatalserviceException; 
     } 
    } 

    public async Task DeleteSubscription() 
    { 
     try 
     { 
      var request = new DeleteSubscriptionsRequest 
      { 
       SubscriptionIds = new uint[] { id } 
      }; 

      await channel.DeleteSubscriptionsAsync(request); 

      token.Dispose(); 
     } 
     catch (Exception ex) 
     { 
      ServiceException serviceException = new ServiceException(ex.Message, "C052"); 

      throw serviceException; 
     } 
    } 

    private static async Task<IUserIdentity> SignInOpc(EndpointDescription endpoint) 
    { 
     IUserIdentity userIdentity = null; 

     if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.Anonymous)) 
     { 
      userIdentity = new AnonymousIdentity(); 
     } 
     else if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.UserName)) 
     { 
      var userName = AppMain.MyAppSettings.OpcUserId; 
      var password = simplerAES.Decrypt(AppMain.MyAppSettings.OpcPassword); 

      userIdentity = new UserNameIdentity(userName, password); 
     } 

     return userIdentity; 
    } 

    private void AddDataPointToContainer(int dataType, string source, DateTime SourceTimestampUTC, double value) 
    { 
     ConditionValue conditionValue = new ConditionValue(); 

     long timestamp = AppMain.ServerSyncTimeStore.ConvertDateTimeToTimeStampUTC(SourceTimestampUTC); 

     conditionValue.dataType = dataType; 
     conditionValue.source = source; 
     conditionValue.timestamp = timestamp; 
     conditionValue.SourceTimestampUTC = SourceTimestampUTC; 
     conditionValue.LocalTime = SourceTimestampUTC.ToLocalTime(); 
     conditionValue.value = value; 

     //LogOpcStore.Info(String.Format("TagName : {0} SourceTimestampUTC : {1} timestamp : {2} LocalTime : {3} curReadingValue : {4}", source, SourceTimestampUTC, timestamp, SourceTimestampUTC.ToLocalTime(), value)); 

     dataCointainer.AddDataPoint(conditionValue); 
    } 
} 

답변

0

프로젝트 https://github.com/convertersystems/opc-ua-client을 사용하고 있습니다.

서버가 세션과 소켓을 닫으면 (Kepware를 다시 초기화 할 때처럼) 클라이언트는 클라이언트 채널에 오류를 발생시키는 즉시 알림을받습니다. 오류가 발생한 채널은 다시 열 수 없으므로 중단해야하며 새 채널을 만들어야합니다.

예외를 catch하고 채널 및 구독을 다시 만들어야 할 수도 있음을 보여주기 위해이 독립 실행 형 테스트를 만들었습니다. 이 테스트의 요점은 CurrentTime 노드를 구독하고 60 개의 데이터를 수집하는 것입니다. 검사는 1 분 정도 지속되어야합니다. 테스트 도중에 Kepware 서버를 다시 시작하면 코드가 예외를 포착하고 채널과 구독을 다시 작성합니다.

[TestMethod] 
    public async Task OpcConnectorTest() 
    { 
     var count = 0; 
     UaTcpSessionChannel channel = null; 

     while (count < 60) 
     { 
      try 
      { 
       channel = new UaTcpSessionChannel(
         this.localDescription, 
         this.certificateStore, 
         new AnonymousIdentity(), 
         EndpointUrl, 
         SecurityPolicyUris.None, 
         loggerFactory: this.loggerFactory); 

       await channel.OpenAsync(); 

       // create the keep alive subscription. 
       var subscriptionRequest = new CreateSubscriptionRequest 
       { 
        RequestedPublishingInterval = 1000f, 
        RequestedMaxKeepAliveCount = 30, 
        RequestedLifetimeCount = 30 * 3, 
        PublishingEnabled = true, 
       }; 
       var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false); 
       var id = subscriptionResponse.SubscriptionId; 

       var token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr => 
       { 
        // loop thru all the data change notifications 
        var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>(); 
        foreach (var dcn in dcns) 
        { 
         foreach (var min in dcn.MonitoredItems) 
         { 
          Console.WriteLine($"sub: {pr.SubscriptionId}; handle: {min.ClientHandle}; value: {min.Value}"); 
          count++; 
         } 
        } 

       }); 

       var itemsRequest = new CreateMonitoredItemsRequest 
       { 
        SubscriptionId = id, 
        ItemsToCreate = new MonitoredItemCreateRequest[] 
        { 
         new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("i=2258"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 12345, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } } 
        }, 
       }; 
       var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest); 

       while (channel.State == CommunicationState.Opened && count < 60) 
       { 
        await Task.Delay(1000); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine($"Exception: {ex.GetType()}. {ex.Message}"); 
      } 
     } 

     if (channel != null) 
     { 
      Console.WriteLine($"Closing session '{channel.SessionId}'."); 
      await channel.CloseAsync(); 
     } 
    } 
0

나는 이것이 오래된 게시물이라는 것을 알고 있지만, 나는 또한이 문제에 대해서도 우연히 발견했다. 관심있는 사용자 :

구독과 관련된 문제입니다. 다음 코드가 실행

:

token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr => 
{ 
    // loop thru all the data change notifications 
    // receiving data change notifications here 
    var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>(); 

    foreach (var dcn in dcns) 
    { 
     foreach (var min in dcn.MonitoredItems) 
     { 
      MyTag MyTag = new MyTag(); 

      bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag); 

      if (hasValue) 
      { 
       if (double.TryParse(min.Value.Value.ToString(), out curReadingValue)) 
       { 
        //LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue)); 
          AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue); 
       } 
      } 
     } 
    } 
}); 

Observable.subscribe()는 여러 인수를합니다. 오류가 발생할 경우 수행 할 작업을 포함시켜야합니다. 예를 들어 :

token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(
    pr => { code to run normally... }, 
    ex => { Log.Info(ex.Message); }, 
    () => { } 
); 

자세한 내용은 http://reactivex.io/documentation/operators/subscribe.html를 참조하십시오.

+0

예외가 발생하면 이전에 거기에 들어가서 구독을 다시 연결하고 다시 만들려고했습니다. 그러나 한편으로 Windows 서비스는 중지되므로 위의 작업은 실패합니다. –