OPC UA (二)
重頭戲,撈取資料,才是該乾的事。想獲取資料,先有資料來源DataPrivade,DataPrivade的資料集合不能和BaseDataVariableState的集合儲存同一地址,或者稱為淺副本
需要提出下面類重新設計,按照自己的idea來做
public class ReferenceNodeManager : CustomNodeManager2
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的用處體現在這。
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 }
大概就是這樣。