Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MeteringNode.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
16
18{
22 [CollectionName("MeteringTopology")]
23 [TypeName(TypeNameSerialization.FullName)]
24 [ArchivingTime]
25 [Index("NodeId")]
26 [Index("ParentId", "NodeId")]
27 public abstract class MeteringNode : IMeteringNode
28 {
29 private Guid objectId = Guid.Empty;
30 private Guid parentId = Guid.Empty;
31 private MeteringNode parent = null;
32 private string nodeId = string.Empty;
33 private string oldId = null;
34 private NodeState state = NodeState.None;
35 private List<MeteringNode> children = null;
36 private bool childrenLoaded = false;
37 private readonly object synchObject = new object();
38 private DateTime created = DateTime.Now;
39 private DateTime updated = DateTime.MinValue;
40 private ThingReference thingReference = null;
41
45 public MeteringNode()
46 {
47 }
48
53 public static implicit operator ThingReference(MeteringNode Node)
54 {
55 if (Node.thingReference is null)
56 Node.thingReference = new ThingReference(Node.nodeId, Node.SourceId, Node.Partition);
57
58 return Node.thingReference;
59 }
60
66 public override bool Equals(object obj)
67 {
68 if (!(obj is IThingReference Ref))
69 return false;
70 else
71 return this.nodeId == Ref.NodeId && this.SourceId == Ref.SourceId && this.Partition == Ref.Partition;
72 }
73
78 public override int GetHashCode()
79 {
80 return this.nodeId.GetHashCode() ^
81 this.SourceId.GetHashCode() ^
82 this.Partition.GetHashCode();
83 }
84
88 [ObjectId]
89 public Guid ObjectId
90 {
91 get => this.objectId;
92 set => this.objectId = value;
93 }
94
98 public Guid ParentId
99 {
100 get => this.parentId;
101 set => this.parentId = value;
102 }
103
107 public DateTime Created
108 {
109 get => this.created;
110 set => this.created = value;
111 }
112
116 [DefaultValueDateTimeMinValue]
117 public DateTime Updated
118 {
119 get => this.updated;
120 set => this.updated = value;
121 }
122
126 [Header(15, "ID:", 0)]
127 [Page(16, "Identity", 0)]
128 [ToolTip(17, "Node identity on the network.")]
129 [Required]
130 public string NodeId
131 {
132 get => this.nodeId;
133 set
134 {
135 this.nodeId = value;
136 this.thingReference = null;
137
138 if (this.oldId is null && !string.IsNullOrEmpty(value))
139 this.oldId = value;
140 }
141 }
142
146 [IgnoreMember]
148
152 [IgnoreMember]
153 public string Partition => string.Empty;
154
156 public override string ToString()
157 {
158 StringBuilder sb = new StringBuilder();
160
161 sb.Append(this.nodeId);
162 sb.Append(" (");
163 sb.Append(this.GetTypeNameAsync(Language).Result);
164 sb.Append(")");
165
167 {
168 sb.Append(", ");
169 sb.Append(P.Name);
170 sb.Append("=");
171 sb.Append(P.StringValue);
172 }
173
174 return sb.ToString();
175 }
176
181 public virtual Task LogErrorAsync(string Body)
182 {
183 return this.LogMessageAsync(MessageType.Error, string.Empty, Body);
184 }
185
191 public virtual Task LogErrorAsync(string EventId, string Body)
192 {
193 return this.LogMessageAsync(MessageType.Error, EventId, Body);
194 }
195
200 public virtual Task LogWarningAsync(string Body)
201 {
202 return this.LogMessageAsync(MessageType.Warning, string.Empty, Body);
203 }
204
210 public virtual Task LogWarningAsync(string EventId, string Body)
211 {
212 return this.LogMessageAsync(MessageType.Warning, EventId, Body);
213 }
214
219 public virtual Task LogInformationAsync(string Body)
220 {
221 return this.LogMessageAsync(MessageType.Information, string.Empty, Body);
222 }
223
229 public virtual Task LogInformationAsync(string EventId, string Body)
230 {
231 return this.LogMessageAsync(MessageType.Information, EventId, Body);
232 }
233
239 public virtual Task LogMessageAsync(MessageType Type, string Body)
240 {
241 return this.LogMessageAsync(Type, string.Empty, Body);
242 }
243
250 public virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
251 {
252 if (this.objectId == Guid.Empty)
253 return;
254
255 bool Updated = false;
256
258 new FilterFieldEqualTo("NodeId", this.objectId),
259 new FilterFieldEqualTo("Type", Type),
260 new FilterFieldEqualTo("EventId", EventId),
261 new FilterFieldEqualTo("Body", Body))))
262 {
263 Message.Updated = DateTime.Now;
264 Message.Count++;
265
266 await Database.Update(Message);
267 Updated = true;
268
269 break;
270 }
271
272 if (!Updated)
273 {
274 MeteringMessage Msg = new MeteringMessage(this.objectId, DateTime.Now, Type, EventId, Body)
275 {
276 NodeId = this.objectId
277 };
278
279 await Database.InsertLazy(Msg);
280 }
281
282 switch (Type)
283 {
284 case MessageType.Error:
285 if (this.state < NodeState.ErrorUnsigned)
286 {
287 this.state = NodeState.ErrorUnsigned;
288 await Database.Update(this);
289 await this.RaiseUpdate();
290 }
291 break;
292
293 case MessageType.Warning:
294 if (this.state < NodeState.WarningUnsigned)
295 {
296 this.state = NodeState.WarningUnsigned;
297 await Database.Update(this);
298 await this.RaiseUpdate();
299 }
300 break;
301
302 case MessageType.Information:
303 if (this.state < NodeState.Information)
304 {
305 this.state = NodeState.Information;
306 await Database.Update(this);
307 await this.RaiseUpdate();
308 }
309 break;
310 }
311
312 switch (Type)
313 {
314 case MessageType.Information:
315 Log.Informational(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
316 break;
317
318 case MessageType.Warning:
319 Log.Warning(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
320 break;
321
322 case MessageType.Error:
323 Log.Error(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
324 break;
325 }
326
327 await this.NodeStateChanged();
328 }
329
330 internal async Task NodeStateChanged()
331 {
332 await MeteringTopology.NewEvent(new NodeStatusChanged()
333 {
334 Messages = await this.GetMessageArrayAsync(RequestOrigin.Empty),
335 State = this.state,
336 NodeId = this.NodeId,
337 Partition = this.Partition,
338 SourceId = this.SourceId,
339 Timestamp = DateTime.Now
340 });
341 }
342
346 public virtual Task<bool> RemoveErrorAsync()
347 {
348 return this.RemoveMessageAsync(MessageType.Error, string.Empty);
349 }
350
355 public virtual Task<bool> RemoveErrorAsync(string EventId)
356 {
357 return this.RemoveMessageAsync(MessageType.Error, EventId);
358 }
359
363 public virtual Task<bool> RemoveWarningAsync()
364 {
365 return this.RemoveMessageAsync(MessageType.Warning, string.Empty);
366 }
367
372 public virtual Task<bool> RemoveWarningAsync(string EventId)
373 {
374 return this.RemoveMessageAsync(MessageType.Warning, EventId);
375 }
376
380 public virtual Task<bool> RemoveInformationAsync()
381 {
382 return this.RemoveMessageAsync(MessageType.Information, string.Empty);
383 }
384
389 public virtual Task<bool> RemoveInformationAsync(string EventId)
390 {
391 return this.RemoveMessageAsync(MessageType.Information, EventId);
392 }
393
398 public virtual Task<bool> RemoveMessageAsync(MessageType Type)
399 {
400 return this.RemoveMessageAsync(Type, string.Empty);
401 }
402
408 public virtual async Task<bool> RemoveMessageAsync(MessageType Type, string EventId)
409 {
410 if (this.objectId == Guid.Empty)
411 return false;
412
413 bool Removed = false;
414
416 new FilterFieldEqualTo("NodeId", this.objectId),
417 new FilterFieldEqualTo("Type", Type),
418 new FilterFieldEqualTo("EventId", EventId))))
419 {
420 Removed = true;
421
422 switch (Type)
423 {
424 case MessageType.Error:
425 Log.Informational("Error removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
426 break;
427
428 case MessageType.Warning:
429 Log.Informational("Warning removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
430 break;
431 }
432 }
433
434 if (Removed)
435 {
436 bool ErrorsFound = false;
437 bool WarningsFound = false;
438 bool InformationFound = false;
439
440 foreach (MeteringMessage Message in await Database.Find<MeteringMessage>(new FilterFieldEqualTo("NodeId", this.objectId)))
441 {
442 switch (Type)
443 {
444 case MessageType.Error:
445 ErrorsFound = true;
446 break;
447
448 case MessageType.Warning:
449 WarningsFound = true;
450 break;
451
452 case MessageType.Information:
453 InformationFound = true;
454 break;
455 }
456 }
457
458 NodeState NewStateSigned;
459 NodeState NewStateUnsigned;
460
461 if (ErrorsFound)
462 {
463 NewStateSigned = NodeState.ErrorSigned;
464 NewStateUnsigned = NodeState.ErrorUnsigned;
465 }
466 else if (WarningsFound)
467 {
468 NewStateSigned = NodeState.WarningSigned;
469 NewStateUnsigned = NodeState.WarningUnsigned;
470 }
471 else if (InformationFound)
472 {
473 NewStateSigned = NodeState.Information;
474 NewStateUnsigned = NodeState.Information;
475 }
476 else
477 {
478 NewStateSigned = NodeState.None;
479 NewStateUnsigned = NodeState.None;
480 }
481
482 switch (this.state)
483 {
484 case NodeState.ErrorSigned:
485 case NodeState.WarningSigned:
486 if (this.state != NewStateSigned)
487 {
488 this.state = NewStateSigned;
489 await Database.Update(this);
490 await this.RaiseUpdate();
491 }
492 break;
493
494 default:
495 if (this.state != NewStateUnsigned)
496 {
497 this.state = NewStateUnsigned;
498 await Database.Update(this);
499 await this.RaiseUpdate();
500 }
501 break;
502 }
503
504 await this.NodeStateChanged();
505 }
506
507 return Removed;
508 }
509
513 public event EventHandlerAsync OnUpdate = null;
514
515 internal Task RaiseUpdate()
516 {
517 return this.OnUpdate.Raise(this, EventArgs.Empty);
518 }
519
525 public static async Task<string> GetUniqueNodeId(string NodeId)
526 {
527 string Suffix = string.Empty;
528 int i = 1;
529 bool Found;
530
531 while (true)
532 {
533 Found = false;
534
535 foreach (MeteringNode Node in await Database.Find<MeteringNode>(0, 1, new FilterFieldEqualTo("NodeId", NodeId + Suffix)))
536 {
537 Found = true;
538 break;
539 }
540
541 if (!Found)
542 return NodeId + Suffix;
543
544 i++;
545 Suffix = " (" + i.ToString() + ")";
546 }
547 }
548
549 #region INode
550
554 [IgnoreMember]
555 public virtual string LocalId => this.NodeId;
556
560 [IgnoreMember]
561 public virtual string LogId => this.NodeId;
562
568 public abstract Task<string> GetTypeNameAsync(Language Language);
569
573 [IgnoreMember]
574 public bool HasChildren
575 {
576 get
577 {
578 if (!this.childrenLoaded)
579 this.LoadChildren().Wait();
580
581 return !(this.children is null) && this.children.Count > 0;
582 }
583 }
584
588 public virtual bool ChildrenOrdered => false;
589
593 [IgnoreMember]
594 public virtual bool IsReadable => this is ISensor;
595
599 [IgnoreMember]
600 public virtual bool IsControllable => this is IActuator;
601
605 [IgnoreMember]
606 public virtual bool HasCommands => true;
607
611 [IgnoreMember]
612 [Obsolete("Use the asynchronous GetParent() method instead.")]
613 public INode Parent => this.GetParent().Result;
614
620 public async Task<INode> GetParent()
621 {
622 if (!(this.parent is null))
623 return this.parent;
624
625 if (this.parentId == Guid.Empty)
626 return null;
627
628 this.parent = await this.LoadParent();
629 if (this.parent is null)
630 throw new Exception("Parent not found.");
631
632 return this.parent;
633 }
634
639 public async Task<T> GetAncestor<T>()
640 where T : INode
641 {
642 INode Loop = await this.GetParent();
643
644 while (!(Loop is null))
645 {
646 if (Loop is T Ancestor)
647 return Ancestor;
648 else if (Loop is MeteringNode MeteringNode)
649 Loop = await MeteringNode.GetParent();
650 else
651 Loop = Loop.Parent;
652 }
653
654 return default;
655 }
656
660 [IgnoreMember]
661 public DateTime LastChanged
662 {
663 get
664 {
665 if (this.updated == DateTime.MinValue)
666 return this.created;
667 else
668 return this.updated;
669 }
670 }
671
675 [DefaultValue(NodeState.None)]
677 {
678 get => this.state;
679 set => this.state = value;
680 }
681
685 [IgnoreMember]
686 public Task<IEnumerable<INode>> ChildNodes
687 {
688 get
689 {
690 return this.GetChildNodes();
691 }
692 }
693
694 private async Task<IEnumerable<INode>> GetChildNodes()
695 {
696 if (!this.childrenLoaded)
697 await this.LoadChildren();
698
699 lock (this.synchObject)
700 {
701 if (this.children is null)
702 return new INode[0];
703 else
704 return this.children.ToArray();
705 }
706 }
707
708 private async Task LoadChildren()
709 {
710 IEnumerable<MeteringNode> Children = await Database.Find<MeteringNode>(new FilterFieldEqualTo("ParentId", this.objectId));
711 LinkedList<MeteringNode> Children2 = new LinkedList<MeteringNode>();
712
713 foreach (MeteringNode Node in Children)
714 Children2.AddLast(MeteringTopology.RegisterNode(Node));
715
716 lock (this.synchObject)
717 {
718 this.children = null;
719
720 foreach (MeteringNode Child in Children2)
721 {
722 if (this.children is null)
723 this.children = new List<MeteringNode>();
724
725 this.children.Add(Child);
726 Child.parent = this;
727 }
728
729 this.SortChildrenAfterLoadLocked(this.children);
730 this.childrenLoaded = true;
731 }
732 }
733
738 protected virtual void SortChildrenAfterLoadLocked(List<MeteringNode> Children)
739 {
740 // Do nothing by default.
741 }
742
743 internal async Task<MeteringNode> LoadParent()
744 {
745 if (!(this.parent is null))
746 return this.parent;
747
748 if (this.parentId == Guid.Empty)
749 return null;
750
751 this.parent = await Database.LoadObject<MeteringNode>(this.parentId);
752 MeteringTopology.RegisterNode(this.parent);
753
754 return this.parent;
755 }
756
762 public virtual Task<bool> CanViewAsync(RequestOrigin Caller)
763 {
764 return Task.FromResult(true); // TODO: Check user privileges
765 }
766
772 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
773 {
774 return Task.FromResult(true); // TODO: Check user privileges
775 }
776
782 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
783 {
784 return Task.FromResult(true); // TODO: Check user privileges
785 }
786
792 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
793 {
794 return Task.FromResult(true); // TODO: Check user privileges
795 }
796
803 public virtual async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
804 {
807
808 LinkedList<Parameter> Result = new LinkedList<Parameter>();
809 string s;
810
811 Result.AddLast(new StringParameter("NodeId", await Namespace.GetStringAsync(1, "Node ID"), this.nodeId));
812 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(4, "Type"), await this.GetTypeNameAsync(Language)));
813
814 if (!(this.parent is null))
815 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(2, "Parent ID"), this.parent.nodeId));
816
817 if (!this.childrenLoaded)
818 await this.LoadChildren();
819
820 if (!(this.children is null))
821 {
822 int i;
823
824 lock (this.synchObject)
825 {
826 i = this.children.Count;
827 }
828
829 Result.AddLast(new Int32Parameter("NrChildren", await Namespace.GetStringAsync(3, "#Children"), i));
830 }
831
832 switch (this.state)
833 {
834 case NodeState.Information:
835 s = await Namespace.GetStringAsync(8, "Information");
836 break;
837
838 case NodeState.WarningUnsigned:
839 s = await Namespace.GetStringAsync(9, "Unsigned Warning");
840 break;
841
842 case NodeState.WarningSigned:
843 s = await Namespace.GetStringAsync(10, "Warning");
844 break;
845
846 case NodeState.ErrorUnsigned:
847 s = await Namespace.GetStringAsync(11, "Unsigned Error");
848 break;
849
850 case NodeState.ErrorSigned:
851 s = await Namespace.GetStringAsync(12, "Error");
852 break;
853
854 case NodeState.None:
855 default:
856 s = null;
857 break;
858 }
859
860 if (!string.IsNullOrEmpty(s))
861 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(5, "State"), s));
862
863 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(6, "Created"), this.created));
864
865 if (this.updated != DateTime.MinValue)
866 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(7, "Updated"), this.updated));
867
868 return Result;
869 }
870
877 public async Task<Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
878 {
879 List<Parameter> Result = new List<Parameter>();
880
881 foreach (Parameter P in await this.GetDisplayableParametersAsync(Language, Caller))
882 Result.Add(P);
883
884 return Result.ToArray();
885 }
886
891 public virtual async Task<IEnumerable<Message>> GetMessagesAsync(RequestOrigin Caller)
892 {
893 IEnumerable<MeteringMessage> Messages = await Database.Find<MeteringMessage>(
894 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
895 LinkedList<Message> Result = new LinkedList<Message>();
896
897 foreach (MeteringMessage Msg in Messages)
898 Result.AddLast(new Message(Msg.Created, Msg.Type, Msg.EventId, Msg.Body)); // TODO: Include Updated & Count also.
899
900 return Result;
901 }
902
907 public async Task<Message[]> GetMessageArrayAsync(RequestOrigin Caller)
908 {
909 List<Message> Result = new List<Message>();
910
911 foreach (Message Msg in await this.GetMessagesAsync(Caller))
912 Result.Add(Msg);
913
914 return Result.ToArray();
915 }
916
922 public virtual async Task<bool> MoveUpAsync(RequestOrigin Caller)
923 {
924 if (!(await this.GetParent() is MeteringNode Parent))
925 return false;
926 else
927 return await Parent.MoveUpAsync(this, Caller);
928 }
929
935 public virtual async Task<bool> MoveDownAsync(RequestOrigin Caller)
936 {
937 if (!(await this.GetParent() is MeteringNode Parent))
938 return false;
939 else
940 return await Parent.MoveDownAsync(this, Caller);
941 }
942
949 public virtual async Task<bool> MoveUpAsync(MeteringNode Child, RequestOrigin Caller)
950 {
951 if (!this.ChildrenOrdered || this.children is null)
952 return false;
953
954 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
955 return false;
956
957 lock (this.children)
958 {
959 int i = this.children.IndexOf(Child);
960 if (i <= 0)
961 return false;
962
963 this.children.RemoveAt(i);
964 this.children.Insert(i - 1, Child);
965 }
966
967 await MeteringTopology.NewEvent(new NodeMovedUp()
968 {
969 NodeId = Child.NodeId,
970 Partition = Child.Partition,
971 SourceId = Child.SourceId,
972 Timestamp = DateTime.Now
973 });
974
975 return true;
976 }
977
984 public virtual async Task<bool> MoveDownAsync(MeteringNode Child, RequestOrigin Caller)
985 {
986 if (!this.ChildrenOrdered || this.children is null)
987 return false;
988
989 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
990 return false;
991
992 lock (this.children)
993 {
994 int c = this.children.Count;
995 int i = this.children.IndexOf(Child);
996 if (i < 0 || i + 1 >= c)
997 return false;
998
999 this.children.RemoveAt(i);
1000 this.children.Insert(i + 1, Child);
1001 }
1002
1003 await MeteringTopology.NewEvent(new NodeMovedDown()
1004 {
1005 NodeId = Child.NodeId,
1006 Partition = Child.Partition,
1007 SourceId = Child.SourceId,
1008 Timestamp = DateTime.Now
1009 });
1010
1011 return true;
1012 }
1013
1019 public abstract Task<bool> AcceptsParentAsync(INode Parent);
1020
1026 public abstract Task<bool> AcceptsChildAsync(INode Child);
1027
1032 public virtual async Task AddAsync(INode Child)
1033 {
1034 if (!(Child is MeteringNode Node))
1035 throw new Exception("Child must be a metering node.");
1036
1037 if (this.objectId == Guid.Empty)
1038 throw new Exception("Parent node must be persisted before you can add nodes to it.");
1039
1040 if (!this.childrenLoaded)
1041 await this.LoadChildren();
1042
1043 Node.parentId = this.objectId;
1044
1045 MeteringNode After = null;
1046 int c;
1047
1048 lock (this.synchObject)
1049 {
1050 if (this.children is null)
1051 this.children = new List<MeteringNode>();
1052 else if ((c = this.children.Count) > 0)
1053 After = this.children[c - 1];
1054
1055 this.children.Add(Node);
1056 Node.parent = this;
1057 }
1058
1059 if (Node.objectId == Guid.Empty)
1060 {
1061 await Database.Insert(Node);
1062 MeteringTopology.RegisterNode(Node);
1063
1065 NodeAdded Event = new NodeAdded()
1066 {
1067 Parameters = await Node.GetDisplayableParameterAraryAsync(Language, RequestOrigin.Empty),
1068 NodeType = Node.GetType().FullName,
1069 Sniffable = Node is ICommunicationLayer,
1070 DisplayName = await Node.GetTypeNameAsync(Language),
1071 HasChildren = Node.HasChildren,
1072 ChildrenOrdered = Node.ChildrenOrdered,
1073 IsReadable = Node.IsReadable,
1074 IsControllable = Node.IsControllable,
1075 HasCommands = Node.HasCommands,
1076 ParentId = this.NodeId,
1077 ParentPartition = this.Partition,
1078 Updated = Node.Updated,
1079 State = Node.State,
1080 NodeId = Node.NodeId,
1081 Partition = Node.Partition,
1082 LogId = NodeAdded.EmptyIfSame(Node.LogId, Node.NodeId),
1083 LocalId = NodeAdded.EmptyIfSame(Node.LocalId, Node.NodeId),
1084 SourceId = Node.SourceId,
1085 Timestamp = DateTime.Now
1086 };
1087
1088 if (this.ChildrenOrdered && !(After is null))
1089 {
1090 Event.AfterNodeId = After.nodeId;
1091 Event.AfterPartition = After.Partition;
1092 }
1093
1094 await MeteringTopology.NewEvent(Event);
1095 }
1096 else
1097 await Node.NodeUpdated();
1098
1099 await this.RaiseUpdate();
1100 }
1101
1105 protected virtual async Task NodeUpdated()
1106 {
1107 this.updated = DateTime.Now;
1108 await Database.Update(this);
1109
1110 await MeteringTopology.NewEvent(new NodeUpdated()
1111 {
1113 HasChildren = this.HasChildren,
1114 ChildrenOrdered = this.ChildrenOrdered,
1115 IsReadable = this.IsReadable,
1116 IsControllable = this.IsControllable,
1117 HasCommands = this.HasCommands,
1118 ParentId = this.NodeId,
1119 ParentPartition = this.Partition,
1120 Updated = this.Updated,
1121 State = this.State,
1122 NodeId = this.NodeId,
1123 OldId = this.oldId,
1124 Partition = this.Partition,
1125 LogId = NodeAdded.EmptyIfSame(this.LogId, this.NodeId),
1126 LocalId = NodeAdded.EmptyIfSame(this.LocalId, this.NodeId),
1127 SourceId = this.SourceId,
1128 Timestamp = DateTime.Now
1129 });
1130
1131 if (this.oldId != this.nodeId)
1132 {
1133 MeteringTopology.RegisterNewNodeId(this, this.oldId);
1134 this.oldId = this.nodeId;
1135 }
1136 }
1137
1141 public virtual async Task UpdateAsync()
1142 {
1143 if (this.objectId != Guid.Empty)
1144 await this.NodeUpdated();
1145
1146 await this.RaiseUpdate();
1147 }
1148
1154 public virtual async Task<bool> RemoveAsync(INode Child)
1155 {
1156 if (!(Child is MeteringNode Node))
1157 throw new Exception("Child must be a metering node.");
1158
1159 if (!this.childrenLoaded)
1160 await this.LoadChildren();
1161
1162 int i;
1163
1164 lock (this.synchObject)
1165 {
1166 if (!(this.children is null))
1167 {
1168 i = this.children.IndexOf(Node);
1169 if (i >= 0)
1170 {
1171 this.children.RemoveAt(i);
1172 if (i == 0 && this.children.Count == 0)
1173 this.children = null;
1174 }
1175 }
1176 else
1177 i = -1;
1178 }
1179
1180 Node.parentId = Guid.Empty;
1181 Node.parent = null;
1182
1183 if (Node.objectId != Guid.Empty)
1184 {
1185 await Database.Update(Child);
1186 await this.RaiseUpdate();
1187
1188 await MeteringTopology.NewEvent(new NodeRemoved()
1189 {
1190 NodeId = Node.NodeId,
1191 Partition = Node.Partition,
1192 SourceId = Node.SourceId,
1193 Timestamp = DateTime.Now
1194 });
1195 }
1196
1197 return i >= 0;
1198 }
1199
1203 public async virtual Task DestroyAsync()
1204 {
1205 if (!(await this.GetParent() is null))
1206 {
1207 if (this.parent.childrenLoaded)
1208 {
1209 lock (this.parent.synchObject)
1210 {
1211 if (!(this.parent.children is null))
1212 {
1213 this.parent.children.Remove(this);
1214 if (this.parent.children.Count == 0)
1215 this.parent.children = null;
1216 }
1217 }
1218 }
1219 }
1220
1221 if (!this.childrenLoaded)
1222 await this.LoadChildren();
1223
1224 if (!(this.children is null))
1225 {
1226 foreach (MeteringNode Child in this.children)
1227 {
1228 Child.parent = null;
1229 Child.parentId = Guid.Empty;
1230
1231 await Child.DestroyAsync();
1232 }
1233
1234 this.children = null;
1235 }
1236
1237 if (this.objectId != Guid.Empty)
1238 {
1239 await Database.Delete(this);
1240 this.objectId = Guid.Empty;
1241 }
1242
1243 MeteringTopology.UnregisterNode(this);
1244 }
1245
1249 [IgnoreMember]
1250 public virtual Task<IEnumerable<ICommand>> Commands
1251 {
1252 get
1253 {
1254 return Task.FromResult<IEnumerable<ICommand>>(new ICommand[]
1255 {
1256 new ClearMessages(this),
1257 new LogMessage(this)
1258 });
1259 }
1260 }
1261
1268 public static async Task<IEnumerable<ICommand>> Join(Task<IEnumerable<ICommand>> Commands, params ICommand[] Commands2)
1269 {
1270 IEnumerable<ICommand> Commands1 = await Commands;
1271
1272 if (Commands1 is null)
1273 return Commands2;
1274
1275 if (!(Commands1 is List<ICommand> Result))
1276 {
1277 Result = new List<ICommand>();
1278
1279 foreach (ICommand Cmd in Commands1)
1280 Result.Add(Cmd);
1281 }
1282
1283 Result.AddRange(Commands2);
1284
1285 return Result;
1286 }
1287
1288 #endregion
1289
1290 #region ILifeCycleManagement
1291
1295 public virtual bool IsProvisioned => false;
1296
1300 public virtual string Owner => string.Empty;
1301
1305 public virtual bool IsPublic => false;
1306
1311 public virtual Task<KeyValuePair<string, object>[]> GetMetaData()
1312 {
1313 return Task.FromResult<KeyValuePair<string, object>[]>(new KeyValuePair<string, object>[0]);
1314 }
1315
1321 public virtual Task Claimed(string Owner, bool IsPublic)
1322 {
1323 throw new NotSupportedException();
1324 }
1325
1329 public virtual Task Disowned()
1330 {
1331 throw new NotSupportedException();
1332 }
1333
1337 public virtual Task Removed()
1338 {
1339 throw new NotSupportedException();
1340 }
1341
1342 #endregion
1343
1344 #region Momentary values
1345
1350 public void NewMomentaryValues(params Field[] Values)
1351 {
1353 }
1354
1359 public void NewMomentaryValues(IEnumerable<Field> Values)
1360 {
1362 }
1363
1364 #endregion
1365
1366 }
1367}
Class representing an event.
Definition: Event.cs:10
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:566
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Definition: Database.cs:879
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
Definition: Database.cs:156
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static async Task< object > LoadObject(string CollectionName, object ObjectId)
Loads an object given its Object ID ObjectId and its collection name CollectionName .
Definition: Database.cs:1121
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
Contains information about a language.
Definition: Language.cs:17
async Task< Namespace > GetNamespaceAsync(string Name)
Gets the namespace object, given its name, if available.
Definition: Language.cs:99
async Task< Namespace > CreateNamespaceAsync(string Name)
Creates a new language namespace, or updates an existing language namespace, if one exist with the sa...
Definition: Language.cs:175
Contains information about a namespace in a language.
Definition: Namespace.cs:17
Task< LanguageString > GetStringAsync(int Id)
Gets the string object, given its ID, if available.
Definition: Namespace.cs:65
Basic access point for runtime language localization.
Definition: Translator.cs:16
static async Task< Language > GetDefaultLanguageAsync()
Gets the default language.
Definition: Translator.cs:223
Contains information about a message logged on a node.
Definition: Message.cs:32
Base class for all node parameters.
Definition: Parameter.cs:10
virtual object StringValue
String representation of parameter value
Definition: Parameter.cs:64
Clears all messages for a node.
Defines a message logged on a metering node.
DateTime Created
When node was created.
Base class for all metering nodes.
Definition: MeteringNode.cs:28
virtual bool IsControllable
If the node can be controlled.
virtual Task Claimed(string Owner, bool IsPublic)
Called when node has been claimed by an owner.
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from the node.
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual bool IsReadable
If the node can be read.
bool HasChildren
If the source has any child sources.
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
NodeState State
Current overall state of the node.
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
virtual async Task< bool > MoveUpAsync(MeteringNode Child, RequestOrigin Caller)
Tries to move the child node up.
DateTime LastChanged
When the node was last updated.
virtual Task< KeyValuePair< string, object >[]> GetMetaData()
Gets meta-data about the node.
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
virtual Task Removed()
Called when node has been removed from the registry.
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
virtual bool IsProvisioned
If node can be provisioned.
virtual bool HasCommands
If the node has registered commands or not.
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
Guid ObjectId
Object ID in persistence layer.
Definition: MeteringNode.cs:90
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
static async Task< IEnumerable< ICommand > > Join(Task< IEnumerable< ICommand > > Commands, params ICommand[] Commands2)
Joins sets of commands.
DateTime Created
When node was created.
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
virtual Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
string SourceId
Optional ID of source containing node.
virtual Task Disowned()
Called when node has been disowned by its owner.
virtual bool IsPublic
If the node is public.
INode Parent
Parent Node, or null if a root node.
virtual async Task< bool > MoveDownAsync(MeteringNode Child, RequestOrigin Caller)
Tries to move the child node down.
MeteringNode()
Base class for all metering nodes.
Definition: MeteringNode.cs:45
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
void NewMomentaryValues(params Field[] Values)
Reports newly measured values.
static async Task< string > GetUniqueNodeId(string NodeId)
Gets a Node ID, based on NodeId that is not already available in the database.
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
void NewMomentaryValues(IEnumerable< Field > Values)
Reports newly measured values.
virtual void SortChildrenAfterLoadLocked(List< MeteringNode > Children)
Method that allows the node to sort its children, after they have been loaded.
EventHandlerAsync OnUpdate
Event raised when node has been updated.
async Task< INode > GetParent()
Gets the parent of the node.
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
abstract Task< bool > AcceptsParentAsync(INode Parent)
If the node accepts a presumptive parent, i.e. can be added to that parent (if that parent accepts th...
virtual string Owner
Who the owner of the node is. The empty string means the node has no owner.
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
Definition: MeteringNode.cs:66
string Partition
Optional partition in which the Node ID is unique.
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
override int GetHashCode()
Serves as the default hash function.
Definition: MeteringNode.cs:78
Guid ParentId
Object ID of parent node in persistence layer.
Definition: MeteringNode.cs:99
abstract Task< bool > AcceptsChildAsync(INode Child)
If the node accepts a presumptive child, i.e. can receive as a child (if that child accepts the node ...
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static Task NewMomentaryValues(IThingReference Reference, IEnumerable< Field > Values)
Reports newly measured values.
const string SourceID
Source ID for the metering topology data source.
Tokens available in request.
Definition: RequestOrigin.cs:9
static readonly RequestOrigin Empty
Empty request origin.
Base class for all sensor data fields.
Definition: Field.cs:20
static string EmptyIfSame(string Id1, string Id2)
Returns Id1 if different, string.Empty if the same.
Definition: NodeAdded.cs:93
Contains a reference to a thing
Interface for observable classes implementing communication protocols.
Interface for actuator nodes.
Definition: IActuator.cs:10
Interface for commands.
Definition: ICommand.cs:32
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
INode Parent
Parent Node, or null if a root node.
Definition: INode.cs:116
Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
Interface for sensor nodes.
Definition: ISensor.cs:9
Interface for thing references.
Base Interface for all metering nodes.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
EventLevel
Event level.
Definition: EventLevel.cs:7
TypeNameSerialization
How the type name should be serialized.
NodeState
State of a node.
Definition: INode.cs:13