OPC UA 統一架構 (二)


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(1000, 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         }

 

大概就是這樣。UA-.NETStandard方案分享出來

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM