AI智能
改变未来

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;56cvariable.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 Read2         // Real BaseDataVariableState3         public Dictionary<string, BaseDataVariableState> BaseDataVariableStateDic = new Dictionary<string, BaseDataVariableState>();4         // temp BaseDataVariableState5         public Dictionary<string, BaseDataVariableState> BaseDataVariableStateTempDic = new Dictionary<string, BaseDataVariableState>();67         // for example ***_***_*** ,OPC8         private readonly Dictionary<string, String> baseDataVariableToXXXXDic = new Dictionary<string, String>();9         // for example ***.***.*** ,IOServer10         private Dictionary<string, String> XXXXToBaseDataVariableDic = new Dictionary<string, String>();11         #endregion1213         #region Write14         private List<InterfaceSample.Model.ValueItem> writeDataList = new List<InterfaceSample.Model.ValueItem>();15         private Dictionary<string, BaseDataVariableState> baseDataVariableDicWrite = new Dictionary<string, BaseDataVariableState>();16         #endregion1718         public InterfaceSample.OPCUADataProvide OpcuaDataPrivade = null;1920         private objectad8_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             try4             {5                 #region CancellationTokenSource6                 var source = state as CancellationTokenSource;78                 if (source == null || cts == null)9                 {10                     return;11                 }12                 if (source.Token.IsCancellationRequested && cts.Token.IsCancellationRequested)13                 {14                     return;15                 }16                 else17                 {18                     if (!cts.Token.IsCancellationRequested)19                     {20                         source = cts;21                     }22                 }23                 #endregion2425                 if (m_dynamicNodes_temp == null)26                 {27                     return;28                 }29                 m_simulationTimer.Change(0, Timeout.Infinite);3031                 #region32                 lock (_obj)33                 {34                     string[] strs = new string[baseDataVariableToXXXXDic.Count];35                     baseDataVariableToXXXXDic.Values.CopyTo(56cstrs, 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);4748                     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                 }6364                 #endregion6566                 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;8384                         }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);56                     handle.Node.ClearChangeMasks(systemContext, false);78                 }91015b0// 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();45             while (true)6             {7                 Thread.Sleep(500);8                 try9                 {10                     #region11                     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                             }1920                             baseDataVariableDicWrite.Clear();21                         }22                     }2324                     lock (_obj)25                     {26                         if (writeDataList.Count <= 0)27                         {28                             continue;29                         }3031                         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                         }3940                         if (writeDataList.Count > 0)41                         {42                             OpcuaDataPrivade.SetItemDatas(keys, values);43                             writeDataList.Clear();44                             cts = new CancellationTokenSource();45                         }4647                     }4849                     #endregion5051                 }52                 catch (Exception)53                 {54                     //source = new CancellationTokenSource();55                 }56                 finally57                 {58                     if (cts.Token.IsCancellationRequested)59                     {60                         cts = new CancellationTokenSource();61                     }62                 }63             }64         }

大概就是这样。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » OPC UA 统一架构 (二)