在 .NET 中使用 OPC UA 協議

什麼是 OPC UA

OPC UA(OPC Unified Architecture,開放平臺通信統一架構)是 OPC 基金會應用在自動化技術的機器對機器網絡傳輸協定。OPC UA 不依賴於特定的操作系統或平臺,可以在 Windows、Mac、Linux 等多種系統上運行,而傳統的 OPC(如 OPC DA)通常只能在 Windows 上使用。該協議提供了一個更爲先進、安全和靈活的解決方案,適用於現代工業自動化和物聯網環境中的設備間通信。

OPC UA 通過一個統一的信息模型來實現設備間的無縫數據交換,信息模型來源於面向對象編程,使用了對象作爲過程系統表示數據和活動的基礎。這個模型由節點組成,節點可以是對象、變量或方法,它們通過引用相互連接,構成了一個複雜的網絡。每個節點都有一組屬性和引用,用於描述數據和定義節點間的關係。OPC UA 的地址空間就是這樣一個節點網絡,它爲客戶端提供了一種標準化的方式來訪問服務器上的對象。OPC UA 還提供了一系列服務,使客戶端能夠執行讀取、寫入和訂閱等操作。安全性也是 OPC UA 設計的核心,內置了多種安全機制,包括認證、授權、加密和消息簽名,以確保數據傳輸的安全性。

UaExpert 的使用

UaExpert 是一款 OPC UA 客戶端軟件,用於連接 OPC UA 服務器並與之交互。UaExpert 支持 OPC UA 的所有特性,包括數據視圖、報警視圖、歷史趨勢視圖和診斷視圖等功能。用戶可以通過 UaExpert 訪問服務器上的節點,如設備和傳感器,以及它們的屬性,例如溫度、壓力等數據。UaExpert 還提供了仿真、配置、歷史功能測試和導出節點的功能,大多數功能都是免費使用的。

下載 UaExpert

訪問 Unified Automation 的官網下載 UaExpert,未註冊用戶則需要先註冊才能下載。

首次啓動

安裝完成後,首次運行 UaExpert 會提示創建一個應用程序證書,填寫一些相關信息即可。

啓動後的界面如下。

添加 OPC UA 服務器

依次單擊菜單欄 Server - Add,或者直接單擊工具欄的 圖標,會彈出添加服務器對話框。雙擊 Custom Discovery 下面的文字,輸入 OPC UA 服務器的地址和端口號。

完成後會看到新添加的 OPC UA 服務器信息,選中開鎖狀 🔓 圖標,並單擊 OK 按鈕,即完成服務器添加的操作。

連接 OPC UA 服務器

服務器添加完成後,在左側項目樹的 Servers 會顯示相關信息,此時服務器尚未連接。單擊工具欄的插頭 🔌 圖標,或者右擊服務器點擊 Connect,即可連接服務器。

查看 PLC 數據

成功連接後,會在界面左下側 Address Space 顯示 PLC 中的相關數據。找到想要監控(訂閱)的數據,將其直接拖放到界面中間的 Data Access View 就可以實時觀察數據的變化。界面右側 Attributes 可以顯示選中節點的相關屬性。

使用 C# 讀寫 OPC UA 數據

首先需要在項目中引用 NuGet 包 OPCFoundation.NetStandard.Opc.Ua

連接到 OPC UA 服務器

  1. 創建一個應用配置對象,用於設置應用名稱、唯一標識、類型、證書和安全策略;
    // 創建一個應用配置對象,用於設置應用名稱、唯一標識、類型、證書和安全策略
    var config = new ApplicationConfiguration()
    {
        ApplicationName = "MyClient",
        ApplicationUri = Utils.Format(@"urn:{0}:MyClient", System.Net.Dns.GetHostName()),
        ApplicationType = ApplicationType.Client,
        SecurityConfiguration = new SecurityConfiguration
        {
            ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = "MyClientSubjectName" },
            TrustedIssuerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Certificate Authorities" },
            TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" },
            RejectedCertificateStore = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" },
            AutoAcceptUntrustedCertificates = true,
            RejectSHA1SignedCertificates = false,
            MinimumCertificateKeySize = 1024,
            NonceLength = 32,
        },
        TransportConfigurations = new TransportConfigurationCollection(),
        TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
        ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
        TraceConfiguration = new TraceConfiguration()
    };
    
    // 驗證應用配置對象
    await config.Validate(ApplicationType.Client);
    
    // 設置證書驗證事件,用於自動接受不受信任的證書
    if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
    {
        config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); };
    }
    
  2. 檢查應用實例對象的證書;
    // 創建一個應用實例對象,用於檢查證書
    var application = new ApplicationInstance(config);
    
    // 檢查應用實例對象的證書
    bool check = await application.CheckApplicationInstanceCertificate(false, 2048);
    
  3. 創建一個會話對象,用於連接到 OPC UA 服務器;
    // 創建一個會話對象,用於連接到 OPC UA 服務器
    EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint("opc.tcp://192.168.0.100:4840", true);
    EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
    ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
    Session session = await Session.Create(config, endpoint, false, false, "DataCollector", 60000, new UserIdentity(), null);
    

獲取節點的值

  1. 單次讀取節點的值
    DataValue value = session.ReadValue(nodeId: "ns=6;s=::MyNode");
    Console.WriteLine("{0}, {1}, {2}", value.Value, value.SourceTimestamp, value.StatusCode);
    
  2. 訂閱讀取節點的值
    // 創建一個訂閱對象,用於訂閱節點的值
    var subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 1000 };
    session.AddSubscription(subscription);
    subscription.Create();
    
    // 創建一個監視項對象,用於指定要訂閱的節點
    MonitoredItem monitoredItem = new MonitoredItem()
    {
        DisplayName = "MyNode",
        StartNodeId = "ns=6;s=::MyNode"
    };
    
    // 添加一個通知事件,用於處理節點值的變化
    monitoredItem.Notification += (item, e) =>
    {
        foreach (var value in item.DequeueValues())
        {
            Console.WriteLine("{0}: {1}, {2}, {3}", item.DisplayName, value.Value, value.SourceTimestamp, value.StatusCode);
        }
    };
    
    // 將監視項對象添加到訂閱對象中
    subscription.AddItem(monitoredItem);
    
    // 應用訂閱的變化
    subscription.ApplyChanges();
    

寫入節點的值

// 寫入數據到節點
WriteValue value = new WriteValue()
{
    NodeId = "ns=6;s=::MyNode",
    Value = new DataValue(new Variant(123))
};

ResponseHeader response = session.Write(null, new WriteValueCollection { value }, out StatusCodeCollection statuses, out DiagnosticInfoCollection diagnostics);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章