OPC UA 統一架構 (二)

紫之荊發表於2021-01-30

OPC UA (二)

 

重頭戲,撈取資料,才是該乾的事。想獲取資料,先有資料來源DataPrivade,DataPrivade的資料集合不能和BaseDataVariableState的集合儲存同一地址,或者稱為淺副本

需要提出下面類重新設計,按照自己的idea來做

public class ReferenceNodeManager : CustomNodeManager2

UA-.NETStandard設計的資料鎖效果好,點數一多,多Client就比較卡。後來發現是Lock問題,Lock時間一長,其他Client就只能等待,目前想到的解決辦法就是深拷貝資料,儘量縮短取值時間。就是上述說的不是同一片記憶體空間。Server的資料和DataPrivade資料不是同一個。

private BaseDataVariableState CreateVariable(NodeState parent, string path, string name, NodeId dataType, int valueRank)
        {
            BaseDataVariableState variable = new BaseDataVariableState(parent);

            variable.SymbolicName = name;
            variable.ReferenceTypeId = ReferenceTypes.Organizes;
            variable.TypeDefinitionId = VariableTypeIds.BaseDataVariableType;
            variable.NodeId = new NodeId(path, NamespaceIndex);
            variable.BrowseName = new QualifiedName(path, NamespaceIndex);
            variable.DisplayName = new LocalizedText("en", name);
            variable.WriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
            variable.UserWriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
            variable.DataType = dataType;
            variable.ValueRank = valueRank;
            variable.AccessLevel = AccessLevels.CurrentReadOrWrite;
            variable.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
            variable.Historizing = false;
            variable.Value = GetNewValue(variable);
            variable.StatusCode = StatusCodes.Good;
            variable.Timestamp = DateTime.UtcNow;

 

、DataPrivade

a)  BaseDataVariableState 的屬性

       SymbolicName: A symbolic name for the node that is not expected to be globally unique.(節點的符號名,不應是全域性唯一的。)(百度翻譯)

       NodeId:The identifier for the node.(節點的識別符號)

  BrowseName:The browse name of the node.(節點的瀏覽名稱)

  DisplayName:The display name for the node.(節點的顯示名稱)

  DataType:The data type for the variable value.(變數值的資料型別)

  ValueRank:The number of array dimensions permitted for the variable value.(變數值允許的陣列維數)

  Value:The value of the variable.(變數的值)(變數:或者稱為節點)

個人認為上面屬性比較重要,加黑字型的是用到的,其他暫時沒用到。

 

 b)  資料儲存在不同區域,下面定義

 可能有的用到,有的沒用到

 1         #region Read
 2         // Real BaseDataVariableState
 3         public Dictionary<string, BaseDataVariableState> BaseDataVariableStateDic = new Dictionary<string, BaseDataVariableState>();
 4         // temp BaseDataVariableState
 5         public Dictionary<string, BaseDataVariableState> BaseDataVariableStateTempDic = new Dictionary<string, BaseDataVariableState>();
 6 
 7         // for example ***_***_*** ,OPC
 8         private readonly Dictionary<string, String> baseDataVariableToXXXXDic = new Dictionary<string, String>();
 9         // for example ***.***.*** ,IOServer
10         private Dictionary<string, String> XXXXToBaseDataVariableDic = new Dictionary<string, String>();
11         #endregion
12 
13         #region Write
14         private List<InterfaceSample.Model.ValueItem> writeDataList = new List<InterfaceSample.Model.ValueItem>();
15         private Dictionary<string, BaseDataVariableState> baseDataVariableDicWrite = new Dictionary<string, BaseDataVariableState>();
16         #endregion
17 
18         public InterfaceSample.OPCUADataProvide OpcuaDataPrivade = null;
19 
20         private object _obj = new object();
21         #endregion

 

 c)  獲取與設定資料Value

  1. 定時器更新資料,不要因為資料太多卡住,多次增加執行緒,爭搶資源。放在了CreateAddressSpace裡,在OPC UA(一)可以看到。Timer:System.Threading.Timer。

1 m_simulationTimer = new Timer(DoSimulation, cts, 1000, 1000);
2 

  回撥DoSimulation(TimerCallback),cts(CancellationTokenSource cts)後面解釋,DoSimulation裡要使用Change,至於為什麼需要自己查,不怎麼用,用的不好。

 

 1         private void DoSimulation(object state)
 2         {
 3             try
 4             {
 5                 #region CancellationTokenSource
 6                 var source = state as CancellationTokenSource;
 7 
 8                 if (source == null || cts == null)
 9                 {
10                     return;
11                 }
12                 if (source.Token.IsCancellationRequested && cts.Token.IsCancellationRequested)
13                 {
14                     return;
15                 }
16                 else
17                 {
18                     if (!cts.Token.IsCancellationRequested)
19                     {
20                         source = cts;
21                     }
22                 }
23                 #endregion
24 
25                 if (m_dynamicNodes_temp == null)
26                 {
27                     return;
28                 }
29                 m_simulationTimer.Change(0, Timeout.Infinite);
30 
31                 #region
32                 lock (_obj)
33                 {
34                     string[] strs = new string[baseDataVariableToXXXXDic.Count];
35                     baseDataVariableToXXXXDic.Values.CopyTo(strs, 0);
36                     List<string> li = new List<string>();
37                     foreach (var str in strs)
38                     {
39                         if (source.Token.IsCancellationRequested)
40                         {
41                             m_simulationTimer.Change(1000, 1000);
42                             return;
43                         }
44                         li.Add(str);
45                     }
46                     var obsli = OpcuaDataPrivade.GetItemDatas(li);
47 
48                     if (obsli == null)
49                     {
50                         m_simulationTimer.Change(1000, 1000);
51                         return;
52                     }
53                     for (int i = 0; i < obsli.Count; i++)
54                     {
55                         if (source.Token.IsCancellationRequested)
56                         {
57                             m_simulationTimer.Change(900, 1000);
58                             return;
59                         }
60                         m_dynamicNodes_temp[i].Value = obsli[i];
61                     }
62                 }
63 
64                 #endregion
65 
66                 lock (Lock)
67                 {
68                     int count = 0;
69                     foreach (BaseDataVariableState variable in m_dynamicNodes_temp)
70                     {
71                         if (source.Token.IsCancellationRequested)
72                         {
73                             m_simulationTimer.Change(1000, 1000);
74                             return;
75                         }
76 77                         if (BaseDataVariableStateDic.ContainsKey(variable.NodeId.Identifier.ToString()))
78                         {
79                             m_dynamicNodes[count].Value = variable.Value;
80                             m_dynamicNodes[count].Timestamp = DateTime.UtcNow;
81                             m_dynamicNodes[count].ClearChangeMasks(SystemContext, false);
82                             ++count;
83 
84                         }
85                     }
86                     m_simulationTimer.Change(1000, 1000);
87                     //m_simulationTimer = new Timer(DoSimulation, null, 1000, 1000);
88                 }
89             }
90             catch (Exception e)
91             {
92                 Utils.Trace(e, "Unexpected error doing simulation.");
93             }
94         }

 

   2. 一般地,Client使用者端對Value的賦值(輸入)在優先順序上要高於讀取,所以CancellationTokenSource的用處體現在這。

UA-.NETStandard裡的BaseDataVariableState的Write集合,自己設計的,裡面能到什麼OnWrite或Write之類的,直接把CustomNodeManager2的Write直接拿過來

public override void Write( OperationContext context,IList<WriteValue> nodesToWrite,IList<ServiceResult> errors)

 不整段程式碼,貼插入部分,裡面有前後,找的話會找到,(override不一定成功,呵)

 1                         CheckIfSemanticsHaveChanged(systemContext, propertyState, propertyValue, previousPropertyValue);
 2                     }
 3                     var baseNode = handle.Node as BaseDataVariableState;
 4                     if(baseNode != null) nodesToWriteList.Add(baseNode);
 5 
 6                     handle.Node.ClearChangeMasks(systemContext, false);
 7 
 8                 }
 9 
10                 // check for nothing to do.
11                 if (nodesToValidate.Count == 0)
12                 {
13                     return;
14                 }

 

另外的程式碼,遙相呼應一下,OPC UA(一) 裡的CreateAddressSpace裡的Task。

 

 1 public void WriteProcHandle(CancellationTokenSource source)
 2         {
 3             //CancellationTokenSource source = new CancellationTokenSource();
 4 
 5             while (true)
 6             {
 7                 Thread.Sleep(500);
 8                 try
 9                 {
10                     #region
11                     lock (Lock)
12                     {
13                         if (baseDataVariableDicWrite.Count > 0)
14                         {
15                             foreach (var item in baseDataVariableDicWrite)
16                             {
17                                 writeDataList.Add(new InterfaceSample.Model.ValueItem() { Key = baseDataVariableToRTDBDic[item.Key], Value = item.Value.Value });
18                             }
19 
20                             baseDataVariableDicWrite.Clear();
21                         }
22                     }
23 
24                     lock (_obj)
25                     {
26                         if (writeDataList.Count <= 0)
27                         {
28                             continue;
29                         }
30 
31                         source.Cancel();
32                         List<string> keys = new List<string>();
33                         List<object> values = new List<object>();
34                         foreach (var item in writeDataList)
35                         {
36                             keys.Add(item.Key);
37                             values.Add(item.Value);
38                         }
39 
40                         if (writeDataList.Count > 0)
41                         {
42                             OpcuaDataPrivade.SetItemDatas(keys, values);
43                             writeDataList.Clear();
44                             cts = new CancellationTokenSource();
45                         }
46                         
47                     }
48 
49                     #endregion
50 
51                 }
52                 catch (Exception)
53                 {
54                     //source = new CancellationTokenSource();
55                 }
56                 finally
57                 {
58                     if (cts.Token.IsCancellationRequested)
59                     {
60                         cts = new CancellationTokenSource();
61                     }
62                 }
63             }
64         }

 

大概就是這樣。

 

相關文章