本文將深入探討如何在使用 OPC UA 客戶端(例如 asyncua)調用自定義方法時,解決動態檢測和轉換輸入參數數據類型的問題。核心在于理解如何利用 OPC UA 協議提供的元數據信息,動態地獲取方法參數的類型,并將其轉換為客戶端可用的 python 類,從而實現靈活且類型安全的方法調用。
在 OPC UA 中,方法節點的 “0:InputArguments” 屬性包含了方法參數的詳細信息,包括參數名稱、數據類型等。通過讀取這個屬性的值,我們可以獲得一個 Argument 對象的列表,每個對象描述一個輸入參數。
以下代碼展示了如何從 Argument 對象中提取數據類型,并將其轉換為相應的 Python 類:
from asyncua.ua import ObjectIds from asyncua import ua base_type_list = { ua.NodeId(ObjectIds.Null): None, ua.NodeId(ObjectIds.Boolean): bool, ua.NodeId(ObjectIds.SByte): ua.SByte, ua.NodeId(ObjectIds.Byte): ua.Byte, ua.NodeId(ObjectIds.Int16): ua.Int16, ua.NodeId(ObjectIds.UInt16): ua.UInt16, ua.NodeId(ObjectIds.Int32): ua.Int32, ua.NodeId(ObjectIds.UInt32): ua.UInt32, ua.NodeId(ObjectIds.Int64): ua.Int64, ua.NodeId(ObjectIds.UInt64): ua.UInt64, ua.NodeId(ObjectIds.Float): ua.Float, ua.NodeId(ObjectIds.Double): ua.Double, ua.NodeId(ObjectIds.String): ua.String, ua.NodeId(ObjectIds.DateTime): ua.DateTime, ua.NodeId(ObjectIds.Guid): ua.Guid, ua.NodeId(ObjectIds.ByteString): ua.ByteString, ua.NodeId(ObjectIds.XmlElement): ua.XmlElement, ua.NodeId(ObjectIds.NodeId): ua.NodeId, ua.NodeId(ObjectIds.ExpandedNodeId): ua.ExpandedNodeId, ua.NodeId(ObjectIds.StatusCode): ua.StatusCode, ua.NodeId(ObjectIds.QualifiedName): ua.QualifiedName, ua.NodeId(ObjectIds.LocalizedText): ua.LocalizedText, ua.NodeId(ObjectIds.Structure): ua.ExtensionObject, ua.NodeId(ObjectIds.DataValue): ua.DataValue, ua.NodeId(ObjectIds.BaseVariableType): ua.Variant, ua.NodeId(ObjectIds.DiagnosticInfo): ua.DiagnosticInfo, } def get_class_from_nodeid(datatype_nodeid): """ 從 NodeId 獲取對應的 Python 類。 Args: datatype_nodeid (ua.NodeId): 數據類型的 NodeId。 Returns: type: 對應的 Python 類,如果找不到則返回 None。 """ # 檢查所有類型字典 sources = [base_type_list, ua.basetype_by_datatype, ua.extension_objects_by_datatype, ua.enums_by_datatype] cls = None for src in sources: cls = src.get(datatype_nodeid, None) if cls is not None: return cls return cls
使用方法:
- 讀取 “0:InputArguments” 屬性: 使用 read_value() 方法讀取目標方法的 “0:InputArguments” 屬性。這將返回一個 Argument 對象的列表。
- 提取數據類型 NodeId: 遍歷 Argument 列表,對于每個參數,提取其 dataType 屬性,該屬性是一個 NodeId 對象,表示參數的數據類型。
- 轉換為 Python 類: 使用 get_class_from_nodeid() 函數將 NodeId 轉換為對應的 Python 類。
- 創建參數實例: 使用轉換后的 Python 類,根據實際的參數值創建參數實例。
- 調用方法: 將創建的參數實例作為參數傳遞給 node.call_method() 方法。
示例:
假設我們有一個名為 MyMethod 的方法,它接受一個自定義數據類型 MyDataType 和一個整數作為輸入參數。
# 假設已經連接到 OPC UA 服務器并獲取了 MyMethod 節點的句柄 method_node # 讀取 "0:InputArguments" 屬性 input_arguments = await method_node.read_value() # 遍歷參數 args = [] for arg in input_arguments: data_type_nodeid = arg.dataType cls = get_class_from_nodeid(data_type_nodeid) if cls is not None: # 假設第一個參數是 MyDataType,第二個參數是整數 if arg == input_arguments[0]: # 創建 MyDataType 實例 (假設 MyDataType 有 ID, Value1, Value2 屬性) param_value = cls(ID=99, Value1=100, Value2=199) else: param_value = 10 # 第二個參數是整數 args.append(param_value) else: print(f"未找到數據類型 {data_type_nodeid} 對應的 Python 類") # 調用方法 try: result = await method_node.call_method(*args) print(f"方法調用結果: {result}") except Exception as e: print(f"方法調用失敗: {e}")
注意事項:
- get_class_from_nodeid() 函數需要維護一個包含所有可能的數據類型 NodeId 到 Python 類映射的字典。確保該字典包含了你所使用的自定義數據類型。
- 對于復雜的數據類型(例如結構體),可能需要根據數據類型的定義,手動創建相應的 Python 類。
- 錯誤處理至關重要。確保捕獲可能發生的異常,例如未找到數據類型對應的 Python 類,或者方法調用失敗等。
總結:
通過讀取方法的 “0:InputArguments” 屬性,并使用 get_class_from_nodeid() 函數將數據類型 NodeId 轉換為 Python 類,我們可以實現動態地檢測和轉換 OPC UA 方法的輸入參數。這種方法提高了客戶端的靈活性和可擴展性,使其能夠處理各種自定義數據類型,并調用不同的方法。 重要的是要維護一個完整的數據類型映射字典,并進行適當的錯誤處理,以確保程序的穩定性和可靠性。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END