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;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
18
20{
24 [CollectionName("MeteringTopology")]
25 [TypeName(TypeNameSerialization.FullName)]
26 [ArchivingTime]
27 [Index("NodeId")]
28 [Index("ParentId", "NodeId")]
29 public abstract class MeteringNode : IMeteringNode
30 {
31 private Guid objectId = Guid.Empty;
32 private Guid parentId = Guid.Empty;
33 private MeteringNode parent = null;
34 private string nodeId = string.Empty;
35 private string oldId = null;
36 private NodeState state = NodeState.None;
37 private List<MeteringNode> children = null;
38 private int siblingOrdinal = 0;
39 private bool childrenLoaded = false;
40 private readonly object synchObject = new object();
41 private DateTime created = DateTime.Now;
42 private DateTime updated = DateTime.MinValue;
43 private ThingReference thingReference = null;
44 private bool disabled = false;
45
49 public MeteringNode()
50 {
51 }
52
57 public static implicit operator ThingReference(MeteringNode Node)
58 {
59 Node.thingReference ??= new ThingReference(Node.nodeId, Node.SourceId, Node.Partition);
60
61 return Node.thingReference;
62 }
63
69 public override bool Equals(object obj)
70 {
71 if (!(obj is IThingReference Ref))
72 return false;
73 else
74 return this.nodeId == Ref.NodeId && this.SourceId == Ref.SourceId && this.Partition == Ref.Partition;
75 }
76
81 public override int GetHashCode()
82 {
83 return this.nodeId.GetHashCode() ^
84 this.SourceId.GetHashCode() ^
85 this.Partition.GetHashCode();
86 }
87
91 [ObjectId]
92 public Guid ObjectId
93 {
94 get => this.objectId;
95 set => this.objectId = value;
96 }
97
101 public Guid ParentId
102 {
103 get => this.parentId;
104 set => this.parentId = value;
105 }
106
110 public DateTime Created
111 {
112 get => this.created;
113 set => this.created = value;
114 }
115
119 [DefaultValueDateTimeMinValue]
120 public DateTime Updated
121 {
122 get => this.updated;
123 set => this.updated = value;
124 }
125
129 [DefaultValue(0)]
130 public int SiblingOrdinal
131 {
132 get => this.siblingOrdinal;
133 set => this.siblingOrdinal = value;
134 }
135
139 [Header(15, "ID:", 0)]
140 [Page(16, "Identity", 0)]
141 [ToolTip(17, "Node identity on the network.")]
142 [Required]
143 public string NodeId
144 {
145 get => this.nodeId;
146 set
147 {
148 this.nodeId = value;
149 this.thingReference = null;
150
151 if (this.oldId is null && !string.IsNullOrEmpty(value))
152 this.oldId = value;
153 }
154 }
155
159 [Header(109, "Disabled node.", 0)]
160 [Page(16, "Identity", 0)]
161 [ToolTip(110, "If checked, node is disabled for readout or control.")]
162 public bool Disabled
163 {
164 get => this.disabled;
165 set => this.disabled = value;
166 }
167
171 [IgnoreMember]
173
177 [IgnoreMember]
178 public string Partition => string.Empty;
179
181 public override string ToString()
182 {
183 StringBuilder sb = new StringBuilder();
185
186 sb.Append(this.nodeId);
187 sb.Append(" (");
188 sb.Append(this.GetTypeNameAsync(Language).Result);
189 sb.Append(")");
190
192 {
193 sb.Append(", ");
194 sb.Append(P.Name);
195 sb.Append("=");
196 sb.Append(P.StringValue);
197 }
198
199 return sb.ToString();
200 }
201
206 public virtual Task LogErrorAsync(string Body)
207 {
208 return this.LogMessageAsync(MessageType.Error, string.Empty, Body);
209 }
210
216 public virtual Task LogErrorAsync(string EventId, string Body)
217 {
218 return this.LogMessageAsync(MessageType.Error, EventId, Body);
219 }
220
225 public virtual Task LogWarningAsync(string Body)
226 {
227 return this.LogMessageAsync(MessageType.Warning, string.Empty, Body);
228 }
229
235 public virtual Task LogWarningAsync(string EventId, string Body)
236 {
237 return this.LogMessageAsync(MessageType.Warning, EventId, Body);
238 }
239
244 public virtual Task LogInformationAsync(string Body)
245 {
246 return this.LogMessageAsync(MessageType.Information, string.Empty, Body);
247 }
248
254 public virtual Task LogInformationAsync(string EventId, string Body)
255 {
256 return this.LogMessageAsync(MessageType.Information, EventId, Body);
257 }
258
264 public virtual Task LogMessageAsync(MessageType Type, string Body)
265 {
266 return this.LogMessageAsync(Type, string.Empty, Body);
267 }
268
275 public virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
276 {
277 if (this.objectId == Guid.Empty)
278 return;
279
280 bool Updated = false;
281
283 new FilterFieldEqualTo("NodeId", this.objectId),
284 new FilterFieldEqualTo("Type", Type),
285 new FilterFieldEqualTo("EventId", EventId),
286 new FilterFieldEqualTo("Body", Body))))
287 {
288 Message.Updated = DateTime.Now;
289 Message.Count++;
290
291 await Database.Update(Message);
292 Updated = true;
293
294 break;
295 }
296
297 if (!Updated)
298 {
299 MeteringMessage Msg = new MeteringMessage(this.objectId, DateTime.Now, Type, EventId, Body)
300 {
301 NodeId = this.objectId
302 };
303
304 await Database.Insert(Msg);
305 }
306
307 switch (Type)
308 {
309 case MessageType.Error:
310 if (this.state < NodeState.ErrorUnsigned)
311 {
312 this.state = NodeState.ErrorUnsigned;
313 await Database.Update(this);
314 await this.RaiseUpdate();
315 }
316 break;
317
318 case MessageType.Warning:
319 if (this.state < NodeState.WarningUnsigned)
320 {
321 this.state = NodeState.WarningUnsigned;
322 await Database.Update(this);
323 await this.RaiseUpdate();
324 }
325 break;
326
327 case MessageType.Information:
328 if (this.state < NodeState.Information)
329 {
330 this.state = NodeState.Information;
331 await Database.Update(this);
332 await this.RaiseUpdate();
333 }
334 break;
335 }
336
337 switch (Type)
338 {
339 case MessageType.Information:
340 Log.Informational(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
341 break;
342
343 case MessageType.Warning:
344 Log.Warning(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
345 break;
346
347 case MessageType.Error:
348 Log.Error(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
349 break;
350 }
351
352 await this.NodeStateChanged();
353 }
354
355 internal async Task NodeStateChanged()
356 {
357 await MeteringTopology.NewEvent(new NodeStatusChanged()
358 {
359 Messages = await this.GetMessageArrayAsync(RequestOrigin.Empty),
360 State = this.state,
361 NodeId = this.NodeId,
362 Partition = this.Partition,
363 SourceId = this.SourceId,
364 Timestamp = DateTime.UtcNow
365 });
366 }
367
371 public virtual Task<bool> RemoveErrorAsync()
372 {
373 return this.RemoveMessageAsync(MessageType.Error, string.Empty);
374 }
375
380 public virtual Task<bool> RemoveErrorAsync(string EventId)
381 {
382 return this.RemoveMessageAsync(MessageType.Error, EventId);
383 }
384
388 public virtual Task<bool> RemoveWarningAsync()
389 {
390 return this.RemoveMessageAsync(MessageType.Warning, string.Empty);
391 }
392
397 public virtual Task<bool> RemoveWarningAsync(string EventId)
398 {
399 return this.RemoveMessageAsync(MessageType.Warning, EventId);
400 }
401
405 public virtual Task<bool> RemoveInformationAsync()
406 {
407 return this.RemoveMessageAsync(MessageType.Information, string.Empty);
408 }
409
414 public virtual Task<bool> RemoveInformationAsync(string EventId)
415 {
416 return this.RemoveMessageAsync(MessageType.Information, EventId);
417 }
418
423 public virtual Task<bool> RemoveMessageAsync(MessageType Type)
424 {
425 return this.RemoveMessageAsync(Type, string.Empty);
426 }
427
433 public virtual async Task<bool> RemoveMessageAsync(MessageType Type, string EventId)
434 {
435 if (this.objectId == Guid.Empty)
436 return false;
437
438 bool Removed = false;
439
441 new FilterFieldEqualTo("NodeId", this.objectId),
442 new FilterFieldEqualTo("Type", Type),
443 new FilterFieldEqualTo("EventId", EventId))))
444 {
445 Removed = true;
446
447 switch (Type)
448 {
449 case MessageType.Error:
450 Log.Informational("Error removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
451 break;
452
453 case MessageType.Warning:
454 Log.Informational("Warning removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
455 break;
456 }
457 }
458
459 if (Removed)
460 {
461 bool ErrorsFound = false;
462 bool WarningsFound = false;
463 bool InformationFound = false;
464
465 foreach (MeteringMessage Message in await Database.Find<MeteringMessage>(new FilterFieldEqualTo("NodeId", this.objectId)))
466 {
467 switch (Type)
468 {
469 case MessageType.Error:
470 ErrorsFound = true;
471 break;
472
473 case MessageType.Warning:
474 WarningsFound = true;
475 break;
476
477 case MessageType.Information:
478 InformationFound = true;
479 break;
480 }
481 }
482
483 NodeState NewStateSigned;
484 NodeState NewStateUnsigned;
485
486 if (ErrorsFound)
487 {
488 NewStateSigned = NodeState.ErrorSigned;
489 NewStateUnsigned = NodeState.ErrorUnsigned;
490 }
491 else if (WarningsFound)
492 {
493 NewStateSigned = NodeState.WarningSigned;
494 NewStateUnsigned = NodeState.WarningUnsigned;
495 }
496 else if (InformationFound)
497 {
498 NewStateSigned = NodeState.Information;
499 NewStateUnsigned = NodeState.Information;
500 }
501 else
502 {
503 NewStateSigned = NodeState.None;
504 NewStateUnsigned = NodeState.None;
505 }
506
507 switch (this.state)
508 {
509 case NodeState.ErrorSigned:
510 case NodeState.WarningSigned:
511 if (this.state != NewStateSigned)
512 {
513 this.state = NewStateSigned;
514 await Database.Update(this);
515 await this.RaiseUpdate();
516 }
517 break;
518
519 default:
520 if (this.state != NewStateUnsigned)
521 {
522 this.state = NewStateUnsigned;
523 await Database.Update(this);
524 await this.RaiseUpdate();
525 }
526 break;
527 }
528
529 await this.NodeStateChanged();
530 }
531
532 return Removed;
533 }
534
538 public event EventHandlerAsync OnUpdate = null;
539
540 internal Task RaiseUpdate()
541 {
542 return this.OnUpdate.Raise(this, EventArgs.Empty);
543 }
544
550 public static async Task<string> GetUniqueNodeId(string NodeId)
551 {
552 using Semaphore Semaphore = await Semaphores.BeginWrite("Metering." + NodeId);
553 string Suffix = string.Empty;
554 string s;
555 int i = 1;
556
557 while (true)
558 {
559 if (await Database.FindFirstIgnoreRest<MeteringNode>(
560 new FilterFieldEqualTo("NodeId", s = NodeId + Suffix)) is null)
561 {
562 return s;
563 }
564
565 i++;
566 Suffix = " (" + i.ToString() + ")";
567 }
568 }
569
570 #region INode
571
575 [IgnoreMember]
576 public virtual string LocalId => this.NodeId;
577
581 [IgnoreMember]
582 public virtual string LogId => this.NodeId;
583
589 public abstract Task<string> GetTypeNameAsync(Language Language);
590
594 [IgnoreMember]
595 public bool HasChildren
596 {
597 get
598 {
599 if (!this.childrenLoaded)
600 this.LoadChildren().Wait();
601
602 return !(this.children is null) && this.children.Count > 0;
603 }
604 }
605
609 public virtual bool ChildrenOrdered => false;
610
614 [IgnoreMember]
615 public virtual bool IsReadable => !this.disabled && this is ISensor;
616
620 [IgnoreMember]
621 public virtual bool IsControllable => !this.disabled && this is IActuator;
622
626 [IgnoreMember]
627 public virtual bool HasCommands => true;
628
632 [IgnoreMember]
633 [Obsolete("Use the asynchronous GetParent() method instead.")]
634 public INode Parent => this.GetParent().Result;
635
641 public async Task<INode> GetParent()
642 {
643 if (!(this.parent is null))
644 return this.parent;
645
646 if (this.parentId == Guid.Empty)
647 return null;
648
649 this.parent = await this.LoadParent();
650 if (this.parent is null)
651 throw new Exception("Parent not found.");
652
653 return this.parent;
654 }
655
660 public async Task<T> GetAncestor<T>()
661 where T : INode
662 {
663 INode Loop = await this.GetParent();
664
665 while (!(Loop is null))
666 {
667 if (Loop is T Ancestor)
668 return Ancestor;
669 else if (Loop is MeteringNode MeteringNode)
670 Loop = await MeteringNode.GetParent();
671 else
672 Loop = Loop.Parent;
673 }
674
675 return default;
676 }
677
681 [IgnoreMember]
682 public DateTime LastChanged
683 {
684 get
685 {
686 if (this.updated == DateTime.MinValue)
687 return this.created;
688 else
689 return this.updated;
690 }
691 }
692
696 [DefaultValue(NodeState.None)]
698 {
699 get => this.state;
700 set => this.state = value;
701 }
702
706 [IgnoreMember]
707 public Task<IEnumerable<INode>> ChildNodes
708 {
709 get
710 {
711 return this.GetChildNodes();
712 }
713 }
714
715 private async Task<IEnumerable<INode>> GetChildNodes()
716 {
717 if (!this.childrenLoaded)
718 await this.LoadChildren();
719
720 lock (this.synchObject)
721 {
722 if (this.children is null)
723 return Array.Empty<INode>();
724 else
725 return this.children.ToArray();
726 }
727 }
728
729 private async Task LoadChildren()
730 {
731 List<MeteringNode> Children = new List<MeteringNode>();
732 MeteringNode[] ToUpdate = null;
733
734 foreach (MeteringNode Node in await Database.Find<MeteringNode>(
735 new FilterFieldEqualTo("ParentId", this.objectId)))
736 {
737 Children.Add(MeteringTopology.RegisterNode(Node));
738 }
739
740 lock (this.synchObject)
741 {
742 this.children = null;
743
744 if (Children.Count > 0)
745 {
746 foreach (MeteringNode Child in Children)
747 Child.parent = this;
748
749 ToUpdate = this.SortChildrenAfterLoadLocked(Children);
750 this.children = Children;
751 }
752
753 this.childrenLoaded = true;
754 }
755
756 if (!(ToUpdate is null))
757 await Database.Update(ToUpdate);
758 }
759
765 protected virtual MeteringNode[] SortChildrenAfterLoadLocked(List<MeteringNode> Children)
766 {
767 if (this.ChildrenOrdered)
768 {
769 Children.Sort((n1, n2) => n1.siblingOrdinal.CompareTo(n2.siblingOrdinal));
770 return this.CheckOrderLocked(Children);
771 }
772 else
773 {
774 Children.Sort((n1, n2) => n1.nodeId.CompareTo(n2.nodeId));
775 return null;
776 }
777 }
778
784 protected virtual MeteringNode[] CheckOrderLocked(List<MeteringNode> Children)
785 {
786 if (this.ChildrenOrdered)
787 {
788 ChunkedList<MeteringNode> ToUpdate = null;
789 int Expected = 0;
790
791 foreach (MeteringNode Child in Children)
792 {
793 if (Child.SiblingOrdinal != Expected)
794 {
795 ToUpdate ??= new ChunkedList<MeteringNode>();
796 ToUpdate.Add(Child);
797 Child.siblingOrdinal = Expected;
798 }
799
800 Expected++;
801 }
802 return ToUpdate?.ToArray();
803 }
804 else
805 return null;
806 }
807
808 internal async Task<MeteringNode> LoadParent()
809 {
810 if (!(this.parent is null))
811 return this.parent;
812
813 if (this.parentId == Guid.Empty)
814 return null;
815
816 this.parent = await Database.LoadObject<MeteringNode>(this.parentId);
817 MeteringTopology.RegisterNode(this.parent);
818
819 return this.parent;
820 }
821
827 public virtual Task<bool> CanViewAsync(RequestOrigin Caller)
828 {
829 return Task.FromResult(Caller.HasPrivilege("Source." + MeteringTopology.SourceID + ".Node.View"));
830 }
831
837 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
838 {
839 return Task.FromResult(Caller.HasPrivilege("Source." + MeteringTopology.SourceID + ".Node.Edit"));
840 }
841
847 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
848 {
849 return Task.FromResult(Caller.HasPrivilege("Source." + MeteringTopology.SourceID + ".Node.Add"));
850 }
851
857 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
858 {
859 return Task.FromResult(Caller.HasPrivilege("Source." + MeteringTopology.SourceID + ".Node.Destroy"));
860 }
861
868 public virtual async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
869 {
872
873 LinkedList<Parameter> Result = new LinkedList<Parameter>();
874 Result.AddLast(new StringParameter("NodeId", await Namespace.GetStringAsync(1, "Node ID"), this.nodeId));
875 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(4, "Type"), await this.GetTypeNameAsync(Language)));
876
877 if (!(this.parent is null))
878 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(2, "Parent ID"), this.parent.nodeId));
879
880 if (this.disabled)
881 Result.AddLast(new BooleanParameter("Disabled", await Namespace.GetStringAsync(111, "Disabled"), this.disabled));
882
883 if (!this.childrenLoaded)
884 await this.LoadChildren();
885
886 if (!(this.children is null))
887 {
888 int i;
889
890 lock (this.synchObject)
891 {
892 i = this.children.Count;
893 }
894
895 Result.AddLast(new Int32Parameter("NrChildren", await Namespace.GetStringAsync(3, "#Children"), i));
896 }
897
898 string s = this.state switch
899 {
900 NodeState.Information => await Namespace.GetStringAsync(8, "Information"),
901 NodeState.WarningUnsigned => await Namespace.GetStringAsync(9, "Unsigned Warning"),
902 NodeState.WarningSigned => await Namespace.GetStringAsync(10, "Warning"),
903 NodeState.ErrorUnsigned => await Namespace.GetStringAsync(11, "Unsigned Error"),
904 NodeState.ErrorSigned => await Namespace.GetStringAsync(12, "Error"),
905 _ => null,
906 };
907
908 if (!string.IsNullOrEmpty(s))
909 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(5, "State"), s));
910
911 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(6, "Created"), this.created));
912
913 if (this.updated != DateTime.MinValue)
914 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(7, "Updated"), this.updated));
915
916 return Result;
917 }
918
925 public async Task<Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
926 {
928 Result.AddRange(await this.GetDisplayableParametersAsync(Language, Caller));
929 return Result.ToArray();
930 }
931
936 public virtual async Task<IEnumerable<Message>> GetMessagesAsync(RequestOrigin Caller)
937 {
938 IEnumerable<MeteringMessage> Messages = await Database.Find<MeteringMessage>(
939 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
940 LinkedList<Message> Result = new LinkedList<Message>();
941
942 foreach (MeteringMessage Msg in Messages)
943 Result.AddLast(new Message(Msg.Created, Msg.Type, Msg.EventId, Msg.Body)); // TODO: Include Updated & Count also.
944
945 return Result;
946 }
947
952 public async Task<Message[]> GetMessageArrayAsync(RequestOrigin Caller)
953 {
954 List<Message> Result = new List<Message>();
955
956 foreach (Message Msg in await this.GetMessagesAsync(Caller))
957 Result.Add(Msg);
958
959 return Result.ToArray();
960 }
961
967 public virtual async Task<bool> MoveUpAsync(RequestOrigin Caller)
968 {
969 if (!(await this.GetParent() is MeteringNode Parent))
970 return false;
971 else
972 return await Parent.MoveUpAsync(this, Caller);
973 }
974
980 public virtual async Task<bool> MoveDownAsync(RequestOrigin Caller)
981 {
982 if (!(await this.GetParent() is MeteringNode Parent))
983 return false;
984 else
985 return await Parent.MoveDownAsync(this, Caller);
986 }
987
994 public virtual async Task<bool> MoveUpAsync(MeteringNode Child, RequestOrigin Caller)
995 {
996 if (!this.ChildrenOrdered)
997 return false;
998
999 if (!this.childrenLoaded)
1000 await this.LoadChildren();
1001
1002 if (this.children is null)
1003 return false;
1004
1005 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
1006 return false;
1007
1008 MeteringNode Child2;
1009
1010 lock (this.children)
1011 {
1012 int i = this.children.IndexOf(Child);
1013 if (i <= 0)
1014 return false;
1015
1016 Child2 = this.children[i - 1];
1017
1018 this.children.RemoveAt(i);
1019 this.children.Insert(i - 1, Child);
1020
1021 i = Child.siblingOrdinal;
1022 Child.siblingOrdinal = Child2.siblingOrdinal;
1023 Child2.siblingOrdinal = i;
1024 }
1025
1026 await Database.Update(Child, Child2);
1027
1028 await MeteringTopology.NewEvent(new NodeMovedUp()
1029 {
1030 NodeId = Child.NodeId,
1031 Partition = Child.Partition,
1032 SourceId = Child.SourceId,
1033 Timestamp = DateTime.UtcNow
1034 });
1035
1036 return true;
1037 }
1038
1045 public virtual async Task<bool> MoveDownAsync(MeteringNode Child, RequestOrigin Caller)
1046 {
1047 if (!this.ChildrenOrdered)
1048 return false;
1049
1050 if (!this.childrenLoaded)
1051 await this.LoadChildren();
1052
1053 if (this.children is null)
1054 return false;
1055
1056 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
1057 return false;
1058
1059 MeteringNode Child2;
1060
1061 lock (this.children)
1062 {
1063 int c = this.children.Count;
1064 int i = this.children.IndexOf(Child);
1065 if (i < 0 || i + 1 >= c)
1066 return false;
1067
1068 Child2 = this.children[i + 1];
1069
1070 this.children.RemoveAt(i);
1071 this.children.Insert(i + 1, Child);
1072
1073 i = Child.siblingOrdinal;
1074 Child.siblingOrdinal = Child2.siblingOrdinal;
1075 Child2.siblingOrdinal = i;
1076 }
1077
1078 await Database.Update(Child, Child2);
1079
1080 await MeteringTopology.NewEvent(new NodeMovedDown()
1081 {
1082 NodeId = Child.NodeId,
1083 Partition = Child.Partition,
1084 SourceId = Child.SourceId,
1085 Timestamp = DateTime.UtcNow
1086 });
1087
1088 return true;
1089 }
1090
1096 public abstract Task<bool> AcceptsParentAsync(INode Parent);
1097
1103 public abstract Task<bool> AcceptsChildAsync(INode Child);
1104
1109 public virtual async Task AddAsync(INode Child)
1110 {
1111 if (!(Child is MeteringNode Node))
1112 throw new Exception("Child must be a metering node.");
1113
1114 if (this.objectId == Guid.Empty)
1115 throw new Exception("Parent node must be persisted before you can add nodes to it.");
1116
1117 if (!this.childrenLoaded)
1118 await this.LoadChildren();
1119
1120 Node.parentId = this.objectId;
1121
1122 MeteringNode[] ToUpdate;
1123 MeteringNode After = null;
1124 int c;
1125
1126 lock (this.synchObject)
1127 {
1128 if (this.children is null)
1129 this.children = new List<MeteringNode>();
1130 else if ((c = this.children.Count) > 0)
1131 After = this.children[c - 1];
1132
1133 ToUpdate = this.CheckOrderLocked(this.children);
1134
1135 Node.siblingOrdinal = this.children.Count;
1136 Node.parent = this;
1137
1138 this.children.Add(Node);
1139 }
1140
1141 if (!(ToUpdate is null))
1142 await Database.Update(ToUpdate);
1143
1144 if (Node.objectId == Guid.Empty)
1145 {
1146 await Database.Insert(Node);
1147 MeteringTopology.RegisterNode(Node);
1148
1150 NodeAdded Event = new NodeAdded()
1151 {
1152 Parameters = await Node.GetDisplayableParameterAraryAsync(Language, RequestOrigin.Empty),
1153 NodeType = Node.GetType().FullName,
1154 Sniffable = Node is ICommunicationLayer,
1155 DisplayName = await Node.GetTypeNameAsync(Language),
1156 HasChildren = Node.HasChildren,
1157 ChildrenOrdered = Node.ChildrenOrdered,
1158 IsReadable = Node.IsReadable,
1159 IsControllable = Node.IsControllable,
1160 HasCommands = Node.HasCommands,
1161 ParentId = this.NodeId,
1162 ParentPartition = this.Partition,
1163 Updated = Node.Updated,
1164 State = Node.State,
1165 NodeId = Node.NodeId,
1166 Partition = Node.Partition,
1167 LogId = NodeAdded.EmptyIfSame(Node.LogId, Node.NodeId),
1168 LocalId = NodeAdded.EmptyIfSame(Node.LocalId, Node.NodeId),
1169 SourceId = Node.SourceId,
1170 Timestamp = DateTime.UtcNow
1171 };
1172
1173 if (this.ChildrenOrdered && !(After is null))
1174 {
1175 Event.AfterNodeId = After.nodeId;
1176 Event.AfterPartition = After.Partition;
1177 }
1178
1179 await MeteringTopology.NewEvent(Event);
1180 }
1181 else
1182 await Node.NodeUpdated();
1183
1184 await this.RaiseUpdate();
1185 }
1186
1190 protected virtual async Task NodeUpdated()
1191 {
1192 this.updated = DateTime.Now;
1193 await Database.Update(this);
1194
1195 await MeteringTopology.NewEvent(new NodeUpdated()
1196 {
1198 HasChildren = this.HasChildren,
1199 ChildrenOrdered = this.ChildrenOrdered,
1200 IsReadable = this.IsReadable,
1201 IsControllable = this.IsControllable,
1202 HasCommands = this.HasCommands,
1203 ParentId = (await this.GetParent()).NodeId,
1204 ParentPartition = this.Partition,
1205 Updated = this.Updated,
1206 State = this.State,
1207 NodeId = this.NodeId,
1208 OldId = this.oldId,
1209 Partition = this.Partition,
1210 LogId = NodeAdded.EmptyIfSame(this.LogId, this.NodeId),
1211 LocalId = NodeAdded.EmptyIfSame(this.LocalId, this.NodeId),
1212 SourceId = this.SourceId,
1213 Timestamp = DateTime.UtcNow
1214 });
1215
1216 if (this.oldId != this.nodeId)
1217 {
1218 MeteringTopology.RegisterNewNodeId(this, this.oldId);
1219 this.oldId = this.nodeId;
1220 }
1221 }
1222
1226 public virtual async Task UpdateAsync()
1227 {
1228 if (this.objectId != Guid.Empty)
1229 await this.NodeUpdated();
1230
1231 await this.RaiseUpdate();
1232 }
1233
1239 public virtual async Task<bool> RemoveAsync(INode Child)
1240 {
1241 if (!(Child is MeteringNode Node))
1242 throw new Exception("Child must be a metering node.");
1243
1244 if (!this.childrenLoaded)
1245 await this.LoadChildren();
1246
1247 MeteringNode[] ToUpdate = null;
1248 int i;
1249
1250 lock (this.synchObject)
1251 {
1252 if (!(this.children is null))
1253 {
1254 i = this.children.IndexOf(Node);
1255 if (i >= 0)
1256 {
1257 this.children.RemoveAt(i);
1258 if (i == 0 && this.children.Count == 0)
1259 this.children = null;
1260 else
1261 ToUpdate = this.CheckOrderLocked(this.children);
1262 }
1263 }
1264 else
1265 i = -1;
1266 }
1267
1268 if (!(ToUpdate is null))
1269 await Database.Update(ToUpdate);
1270
1271 Node.parentId = Guid.Empty;
1272 Node.parent = null;
1273
1274 if (Node.objectId != Guid.Empty)
1275 {
1276 await Database.Update(Child);
1277 await this.RaiseUpdate();
1278
1279 await MeteringTopology.NewEvent(new NodeRemoved()
1280 {
1281 NodeId = Node.NodeId,
1282 Partition = Node.Partition,
1283 SourceId = Node.SourceId,
1284 Timestamp = DateTime.UtcNow
1285 });
1286 }
1287
1288 return i >= 0;
1289 }
1290
1294 public async virtual Task DestroyAsync()
1295 {
1296 if (!(await this.GetParent() is null))
1297 {
1298 if (this.parent.childrenLoaded)
1299 {
1300 MeteringNode[] ToUpdate = null;
1301
1302 lock (this.parent.synchObject)
1303 {
1304 if (!(this.parent.children is null))
1305 {
1306 if (this.parent.children.Remove(this))
1307 {
1308 if (this.parent.children.Count == 0)
1309 this.parent.children = null;
1310 else
1311 ToUpdate = this.parent.CheckOrderLocked(this.parent.children);
1312 }
1313 }
1314 }
1315
1316 if (!(ToUpdate is null))
1317 await Database.Update(ToUpdate);
1318 }
1319 }
1320
1321 if (!this.childrenLoaded)
1322 await this.LoadChildren();
1323
1324 if (!(this.children is null))
1325 {
1326 List<MeteringNode> Children = this.children;
1327 this.children = null;
1328
1329 foreach (MeteringNode Child in Children)
1330 {
1331 Child.parent = null;
1332 Child.parentId = Guid.Empty;
1333
1334 await Child.DestroyAsync();
1335 }
1336 }
1337
1338 if (this.objectId != Guid.Empty)
1339 {
1340 await Database.Delete(this);
1341 this.objectId = Guid.Empty;
1342 }
1343
1344 MeteringTopology.UnregisterNode(this);
1345 }
1346
1350 [IgnoreMember]
1351 public virtual Task<IEnumerable<ICommand>> Commands
1352 {
1353 get
1354 {
1355 return Task.FromResult<IEnumerable<ICommand>>(new ICommand[]
1356 {
1357 new ClearMessages(this),
1358 new LogMessage(this)
1359 });
1360 }
1361 }
1362
1363 #endregion
1364
1365 #region ILifeCycleManagement
1366
1370 public virtual bool IsProvisioned => false;
1371
1375 public virtual string Owner => string.Empty;
1376
1380 public virtual bool IsPublic => false;
1381
1386 public virtual Task<KeyValuePair<string, object>[]> GetMetaData()
1387 {
1388 return Task.FromResult<KeyValuePair<string, object>[]>(Array.Empty<KeyValuePair<string, object>>());
1389 }
1390
1396 public virtual Task Claimed(string Owner, bool IsPublic)
1397 {
1398 throw new NotSupportedException();
1399 }
1400
1404 public virtual Task Disowned()
1405 {
1406 throw new NotSupportedException();
1407 }
1408
1412 public virtual Task Removed()
1413 {
1414 throw new NotSupportedException();
1415 }
1416
1417 #endregion
1418
1419 #region Momentary values
1420
1425 public Task NewMomentaryValues(params Field[] Values)
1426 {
1427 return MeteringTopology.NewMomentaryValues(this, Values);
1428 }
1429
1434 public Task NewMomentaryValues(IEnumerable<Field> Values)
1435 {
1436 return MeteringTopology.NewMomentaryValues(this, Values);
1437 }
1438
1439 #endregion
1440
1441 }
1442}
Class representing an event.
Definition: Event.cs:11
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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:576
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:692
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:344
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
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:1442
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
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:1880
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
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.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
T[] ToArray()
Returns an array containing all elements of the collection.
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
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
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:30
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
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:93
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
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:49
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
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.
Task NewMomentaryValues(IEnumerable< Field > Values)
Reports newly measured values.
virtual MeteringNode[] 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 MeteringNode[] CheckOrderLocked(List< MeteringNode > Children)
Checks the ordering of children.
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:69
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.
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
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:81
Guid ParentId
Object ID of parent node in persistence layer.
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.
bool HasPrivilege(string Privilege)
If the origin has a given privilege.
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.
Definition: ImplTypes.g.cs:58
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