当前位置 博文首页 > 紫之荆:OPC UA 统一架构 (二)

    紫之荆:OPC UA 统一架构 (二)

    作者:紫之荆 时间:2021-02-01 08:18

    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
    
    下一篇:没有了