Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
GroupNode.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
13using Waher.Things;
17
18namespace Waher.Groups
19{
23 [CollectionName("Groups")]
24 [TypeName(TypeNameSerialization.FullName)]
25 [ArchivingTime]
26 [Index("NodeId")]
27 [Index("ParentId", "NodeId")]
28 public abstract class GroupNode : IGroupNode
29 {
30 private Guid objectId = Guid.Empty;
31 private Guid parentId = Guid.Empty;
32 private GroupNode parent = null;
33 private string nodeId = string.Empty;
34 private string oldId = null;
35 private NodeState state = NodeState.None;
36 private List<GroupNode> children = null;
37 private int siblingOrdinal = 0;
38 private bool childrenLoaded = false;
39 private readonly object synchObject = new object();
40 private DateTime created = DateTime.Now;
41 private DateTime updated = DateTime.MinValue;
42 private ThingReference thingReference = null;
43
47 public GroupNode()
48 {
49 }
50
55 public static implicit operator ThingReference(GroupNode Node)
56 {
57 Node.thingReference ??= new ThingReference(Node.nodeId, Node.SourceId, Node.Partition);
58
59 return Node.thingReference;
60 }
61
67 public override bool Equals(object obj)
68 {
69 if (!(obj is IThingReference Ref))
70 return false;
71 else
72 return this.nodeId == Ref.NodeId && this.SourceId == Ref.SourceId && this.Partition == Ref.Partition;
73 }
74
79 public override int GetHashCode()
80 {
81 return this.nodeId.GetHashCode() ^
82 this.SourceId.GetHashCode() ^
83 this.Partition.GetHashCode();
84 }
85
89 [ObjectId]
90 public Guid ObjectId
91 {
92 get => this.objectId;
93 set => this.objectId = value;
94 }
95
99 public Guid ParentId
100 {
101 get => this.parentId;
102 set => this.parentId = value;
103 }
104
108 public DateTime Created
109 {
110 get => this.created;
111 set => this.created = value;
112 }
113
117 [DefaultValueDateTimeMinValue]
118 public DateTime Updated
119 {
120 get => this.updated;
121 set => this.updated = value;
122 }
123
127 [DefaultValue(0)]
128 public int SiblingOrdinal
129 {
130 get => this.siblingOrdinal;
131 set => this.siblingOrdinal = value;
132 }
133
137 [Header(2, "Group ID:", 0)]
138 [Page(3, "Group", 0)]
139 [ToolTip(4, "Group identity in the collection of groups.")]
140 [Required]
141 public string NodeId
142 {
143 get => this.nodeId;
144 set
145 {
146 this.nodeId = value;
147 this.thingReference = null;
148
149 if (this.oldId is null && !string.IsNullOrEmpty(value))
150 this.oldId = value;
151 }
152 }
153
157 [IgnoreMember]
158 public string SourceId => GroupSource.SourceID;
159
163 [IgnoreMember]
164 public string Partition => string.Empty;
165
167 public override string ToString()
168 {
169 StringBuilder sb = new StringBuilder();
171
172 sb.Append(this.nodeId);
173 sb.Append(" (");
174 sb.Append(this.GetTypeNameAsync(Language).Result);
175 sb.Append(")");
176
178 {
179 sb.Append(", ");
180 sb.Append(P.Name);
181 sb.Append("=");
182 sb.Append(P.StringValue);
183 }
184
185 return sb.ToString();
186 }
187
192 public virtual Task LogErrorAsync(string Body)
193 {
194 return this.LogMessageAsync(MessageType.Error, string.Empty, Body);
195 }
196
202 public virtual Task LogErrorAsync(string EventId, string Body)
203 {
204 return this.LogMessageAsync(MessageType.Error, EventId, Body);
205 }
206
211 public virtual Task LogWarningAsync(string Body)
212 {
213 return this.LogMessageAsync(MessageType.Warning, string.Empty, Body);
214 }
215
221 public virtual Task LogWarningAsync(string EventId, string Body)
222 {
223 return this.LogMessageAsync(MessageType.Warning, EventId, Body);
224 }
225
230 public virtual Task LogInformationAsync(string Body)
231 {
232 return this.LogMessageAsync(MessageType.Information, string.Empty, Body);
233 }
234
240 public virtual Task LogInformationAsync(string EventId, string Body)
241 {
242 return this.LogMessageAsync(MessageType.Information, EventId, Body);
243 }
244
250 public virtual Task LogMessageAsync(MessageType Type, string Body)
251 {
252 return this.LogMessageAsync(Type, string.Empty, Body);
253 }
254
261 public virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
262 {
263 if (this.objectId == Guid.Empty)
264 return;
265
266 bool Updated = false;
267
269 new FilterFieldEqualTo("NodeId", this.objectId),
270 new FilterFieldEqualTo("Type", Type),
271 new FilterFieldEqualTo("EventId", EventId),
272 new FilterFieldEqualTo("Body", Body))))
273 {
274 Message.Updated = DateTime.Now;
275 Message.Count++;
276
277 await Database.Update(Message);
278 Updated = true;
279
280 break;
281 }
282
283 if (!Updated)
284 {
285 GroupMessage Msg = new GroupMessage(this.objectId, DateTime.Now, Type, EventId, Body)
286 {
287 NodeId = this.objectId
288 };
289
290 await Database.Insert(Msg);
291 }
292
293 switch (Type)
294 {
295 case MessageType.Error:
296 if (this.state < NodeState.ErrorUnsigned)
297 {
298 this.state = NodeState.ErrorUnsigned;
299 await Database.Update(this);
300 await this.RaiseUpdate();
301 }
302 break;
303
304 case MessageType.Warning:
305 if (this.state < NodeState.WarningUnsigned)
306 {
307 this.state = NodeState.WarningUnsigned;
308 await Database.Update(this);
309 await this.RaiseUpdate();
310 }
311 break;
312
313 case MessageType.Information:
314 if (this.state < NodeState.Information)
315 {
316 this.state = NodeState.Information;
317 await Database.Update(this);
318 await this.RaiseUpdate();
319 }
320 break;
321 }
322
323 switch (Type)
324 {
325 case MessageType.Information:
326 Log.Informational(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
327 break;
328
329 case MessageType.Warning:
330 Log.Warning(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
331 break;
332
333 case MessageType.Error:
334 Log.Error(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
335 break;
336 }
337
338 await this.NodeStateChanged();
339 }
340
341 internal async Task NodeStateChanged()
342 {
343 await GroupSource.NewEvent(new NodeStatusChanged()
344 {
345 Messages = await this.GetMessageArrayAsync(RequestOrigin.Empty),
346 State = this.state,
347 NodeId = this.NodeId,
348 Partition = this.Partition,
349 SourceId = this.SourceId,
350 Timestamp = DateTime.UtcNow
351 });
352 }
353
357 public virtual Task<bool> RemoveErrorAsync()
358 {
359 return this.RemoveMessageAsync(MessageType.Error, string.Empty);
360 }
361
366 public virtual Task<bool> RemoveErrorAsync(string EventId)
367 {
368 return this.RemoveMessageAsync(MessageType.Error, EventId);
369 }
370
374 public virtual Task<bool> RemoveWarningAsync()
375 {
376 return this.RemoveMessageAsync(MessageType.Warning, string.Empty);
377 }
378
383 public virtual Task<bool> RemoveWarningAsync(string EventId)
384 {
385 return this.RemoveMessageAsync(MessageType.Warning, EventId);
386 }
387
391 public virtual Task<bool> RemoveInformationAsync()
392 {
393 return this.RemoveMessageAsync(MessageType.Information, string.Empty);
394 }
395
400 public virtual Task<bool> RemoveInformationAsync(string EventId)
401 {
402 return this.RemoveMessageAsync(MessageType.Information, EventId);
403 }
404
409 public virtual Task<bool> RemoveMessageAsync(MessageType Type)
410 {
411 return this.RemoveMessageAsync(Type, string.Empty);
412 }
413
419 public virtual async Task<bool> RemoveMessageAsync(MessageType Type, string EventId)
420 {
421 if (this.objectId == Guid.Empty)
422 return false;
423
424 bool Removed = false;
425
427 new FilterFieldEqualTo("NodeId", this.objectId),
428 new FilterFieldEqualTo("Type", Type),
429 new FilterFieldEqualTo("EventId", EventId))))
430 {
431 Removed = true;
432
433 switch (Type)
434 {
435 case MessageType.Error:
436 Log.Informational("Error removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
437 break;
438
439 case MessageType.Warning:
440 Log.Informational("Warning removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
441 break;
442 }
443 }
444
445 if (Removed)
446 {
447 bool ErrorsFound = false;
448 bool WarningsFound = false;
449 bool InformationFound = false;
450
451 foreach (GroupMessage Message in await Database.Find<GroupMessage>(new FilterFieldEqualTo("NodeId", this.objectId)))
452 {
453 switch (Type)
454 {
455 case MessageType.Error:
456 ErrorsFound = true;
457 break;
458
459 case MessageType.Warning:
460 WarningsFound = true;
461 break;
462
463 case MessageType.Information:
464 InformationFound = true;
465 break;
466 }
467 }
468
469 NodeState NewStateSigned;
470 NodeState NewStateUnsigned;
471
472 if (ErrorsFound)
473 {
474 NewStateSigned = NodeState.ErrorSigned;
475 NewStateUnsigned = NodeState.ErrorUnsigned;
476 }
477 else if (WarningsFound)
478 {
479 NewStateSigned = NodeState.WarningSigned;
480 NewStateUnsigned = NodeState.WarningUnsigned;
481 }
482 else if (InformationFound)
483 {
484 NewStateSigned = NodeState.Information;
485 NewStateUnsigned = NodeState.Information;
486 }
487 else
488 {
489 NewStateSigned = NodeState.None;
490 NewStateUnsigned = NodeState.None;
491 }
492
493 switch (this.state)
494 {
495 case NodeState.ErrorSigned:
496 case NodeState.WarningSigned:
497 if (this.state != NewStateSigned)
498 {
499 this.state = NewStateSigned;
500 await Database.Update(this);
501 await this.RaiseUpdate();
502 }
503 break;
504
505 default:
506 if (this.state != NewStateUnsigned)
507 {
508 this.state = NewStateUnsigned;
509 await Database.Update(this);
510 await this.RaiseUpdate();
511 }
512 break;
513 }
514
515 await this.NodeStateChanged();
516 }
517
518 return Removed;
519 }
520
524 public event EventHandlerAsync OnUpdate = null;
525
526 internal Task RaiseUpdate()
527 {
528 return this.OnUpdate.Raise(this, EventArgs.Empty);
529 }
530
536 public static async Task<string> GetUniqueGroupId(string GroupId)
537 {
538 using Semaphore Semaphore = await Semaphores.BeginWrite("Groups." + GroupId);
539 string Suffix = string.Empty;
540 string s;
541
542 int i = 1;
543
544 while (true)
545 {
546 if (await Database.FindFirstIgnoreRest<GroupNode>(
547 new FilterFieldEqualTo("NodeId", s = GroupId + Suffix)) is null)
548 {
549 return s;
550 }
551
552 i++;
553 Suffix = " (" + i.ToString() + ")";
554 }
555 }
556
557 #region INode
558
562 [IgnoreMember]
563 public virtual string LocalId => this.NodeId;
564
568 [IgnoreMember]
569 public virtual string LogId => this.NodeId;
570
576 public abstract Task<string> GetTypeNameAsync(Language Language);
577
581 [IgnoreMember]
582 public bool HasChildren
583 {
584 get
585 {
586 if (!this.childrenLoaded)
587 this.LoadChildren().Wait();
588
589 return !(this.children is null) && this.children.Count > 0;
590 }
591 }
592
596 public virtual bool ChildrenOrdered => false;
597
601 [IgnoreMember]
602 public virtual bool IsReadable => this is ISensor;
603
607 [IgnoreMember]
608 public virtual bool IsControllable => this is IActuator;
609
613 [IgnoreMember]
614 public virtual bool HasCommands => true;
615
619 [IgnoreMember]
620 [Obsolete("Use the asynchronous GetParent() method instead.")]
621 public INode Parent => this.GetParent().Result;
622
628 public async Task<INode> GetParent()
629 {
630 if (!(this.parent is null))
631 return this.parent;
632
633 if (this.parentId == Guid.Empty)
634 return null;
635
636 this.parent = await this.LoadParent();
637 if (this.parent is null)
638 throw new Exception("Parent not found.");
639
640 return this.parent;
641 }
642
647 public async Task<T> GetAncestor<T>()
648 where T : INode
649 {
650 INode Loop = await this.GetParent();
651
652 while (!(Loop is null))
653 {
654 if (Loop is T Ancestor)
655 return Ancestor;
656 else if (Loop is GroupNode GroupNode)
657 Loop = await GroupNode.GetParent();
658 else
659 Loop = Loop.Parent;
660 }
661
662 return default;
663 }
664
668 [IgnoreMember]
669 public DateTime LastChanged
670 {
671 get
672 {
673 if (this.updated == DateTime.MinValue)
674 return this.created;
675 else
676 return this.updated;
677 }
678 }
679
683 [DefaultValue(NodeState.None)]
685 {
686 get => this.state;
687 set => this.state = value;
688 }
689
693 [IgnoreMember]
694 public Task<IEnumerable<INode>> ChildNodes
695 {
696 get
697 {
698 return this.GetChildNodes();
699 }
700 }
701
702 private async Task<IEnumerable<INode>> GetChildNodes()
703 {
704 if (!this.childrenLoaded)
705 await this.LoadChildren();
706
707 lock (this.synchObject)
708 {
709 if (this.children is null)
710 return Array.Empty<INode>();
711 else
712 return this.children.ToArray();
713 }
714 }
715
716 private async Task LoadChildren()
717 {
718 List<GroupNode> Children = new List<GroupNode>();
719 GroupNode[] ToUpdate = null;
720
721 foreach (GroupNode Node in await Database.Find<GroupNode>(
722 new FilterFieldEqualTo("ParentId", this.objectId)))
723 {
724 Children.Add(GroupSource.RegisterNode(Node));
725 }
726
727 lock (this.synchObject)
728 {
729 this.children = null;
730
731 if (Children.Count > 0)
732 {
733 foreach (GroupNode Child in Children)
734 Child.parent = this;
735
736 ToUpdate = this.SortChildrenAfterLoadLocked(Children);
737 this.children = Children;
738 }
739
740 this.childrenLoaded = true;
741 }
742
743 if (!(ToUpdate is null))
744 await Database.Update(ToUpdate);
745 }
746
752 protected virtual GroupNode[] SortChildrenAfterLoadLocked(List<GroupNode> Children)
753 {
754 if (this.ChildrenOrdered)
755 {
756 Children.Sort((n1, n2) => n1.siblingOrdinal.CompareTo(n2.siblingOrdinal));
757 return this.CheckOrderLocked(Children);
758 }
759 else
760 {
761 Children.Sort((n1, n2) => n1.nodeId.CompareTo(n2.nodeId));
762 return null;
763 }
764 }
765
771 protected virtual GroupNode[] CheckOrderLocked(List<GroupNode> Children)
772 {
773 if (this.ChildrenOrdered)
774 {
775 ChunkedList<GroupNode> ToUpdate = null;
776 int Expected = 0;
777
778 foreach (GroupNode Child in Children)
779 {
780 if (Child.SiblingOrdinal != Expected)
781 {
782 ToUpdate ??= new ChunkedList<GroupNode>();
783 ToUpdate.Add(Child);
784 Child.siblingOrdinal = Expected;
785 }
786
787 Expected++;
788 }
789 return ToUpdate?.ToArray();
790 }
791 else
792 return null;
793 }
794
795 internal async Task<GroupNode> LoadParent()
796 {
797 if (!(this.parent is null))
798 return this.parent;
799
800 if (this.parentId == Guid.Empty)
801 return null;
802
803 this.parent = await Database.LoadObject<GroupNode>(this.parentId);
804 GroupSource.RegisterNode(this.parent);
805
806 return this.parent;
807 }
808
814 public virtual Task<bool> CanViewAsync(RequestOrigin Caller)
815 {
816 return Task.FromResult(Caller.HasPrivilege("Source." + GroupSource.SourceID + ".Node.View"));
817 }
818
824 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
825 {
826 return Task.FromResult(Caller.HasPrivilege("Source." + GroupSource.SourceID + ".Node.Edit"));
827 }
828
834 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
835 {
836 return Task.FromResult(Caller.HasPrivilege("Source." + GroupSource.SourceID + ".Node.Add"));
837 }
838
844 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
845 {
846 return Task.FromResult(Caller.HasPrivilege("Source." + GroupSource.SourceID + ".Node.Destroy"));
847 }
848
855 public virtual async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
856 {
859
860 LinkedList<Parameter> Result = new LinkedList<Parameter>();
861 Result.AddLast(new StringParameter("NodeId", await Namespace.GetStringAsync(5, "Group ID"), this.nodeId));
862 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(6, "Type"), await this.GetTypeNameAsync(Language)));
863
864 if (!(this.parent is null))
865 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(7, "Parent ID"), this.parent.nodeId));
866
867 if (!this.childrenLoaded)
868 await this.LoadChildren();
869
870 if (!(this.children is null))
871 {
872 int i;
873
874 lock (this.synchObject)
875 {
876 i = this.children.Count;
877 }
878
879 Result.AddLast(new Int32Parameter("NrChildren", await Namespace.GetStringAsync(8, "#Children"), i));
880 }
881
882 string s = this.state switch
883 {
884 NodeState.Information => await Namespace.GetStringAsync(9, "Information"),
885 NodeState.WarningUnsigned => await Namespace.GetStringAsync(10, "Unsigned Warning"),
886 NodeState.WarningSigned => await Namespace.GetStringAsync(11, "Warning"),
887 NodeState.ErrorUnsigned => await Namespace.GetStringAsync(12, "Unsigned Error"),
888 NodeState.ErrorSigned => await Namespace.GetStringAsync(13, "Error"),
889 _ => null,
890 };
891
892 if (!string.IsNullOrEmpty(s))
893 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(14, "State"), s));
894
895 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(15, "Created"), this.created));
896
897 if (this.updated != DateTime.MinValue)
898 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(16, "Updated"), this.updated));
899
900 return Result;
901 }
902
909 public async Task<Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
910 {
912 Result.AddRange(await this.GetDisplayableParametersAsync(Language, Caller));
913 return Result.ToArray();
914 }
915
920 public virtual async Task<IEnumerable<Message>> GetMessagesAsync(RequestOrigin Caller)
921 {
922 IEnumerable<GroupMessage> Messages = await Database.Find<GroupMessage>(
923 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
924 LinkedList<Message> Result = new LinkedList<Message>();
925
926 foreach (GroupMessage Msg in Messages)
927 Result.AddLast(new Message(Msg.Created, Msg.Type, Msg.EventId, Msg.Body)); // TODO: Include Updated & Count also.
928
929 return Result;
930 }
931
936 public async Task<Message[]> GetMessageArrayAsync(RequestOrigin Caller)
937 {
938 List<Message> Result = new List<Message>();
939
940 foreach (Message Msg in await this.GetMessagesAsync(Caller))
941 Result.Add(Msg);
942
943 return Result.ToArray();
944 }
945
951 public virtual async Task<bool> MoveUpAsync(RequestOrigin Caller)
952 {
953 if (!(await this.GetParent() is GroupNode Parent))
954 return false;
955 else
956 return await Parent.MoveUpAsync(this, Caller);
957 }
958
964 public virtual async Task<bool> MoveDownAsync(RequestOrigin Caller)
965 {
966 if (!(await this.GetParent() is GroupNode Parent))
967 return false;
968 else
969 return await Parent.MoveDownAsync(this, Caller);
970 }
971
978 public virtual async Task<bool> MoveUpAsync(GroupNode Child, RequestOrigin Caller)
979 {
980 if (!this.ChildrenOrdered)
981 return false;
982
983 if (!this.childrenLoaded)
984 await this.LoadChildren();
985
986 if (this.children is null)
987 return false;
988
989 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
990 return false;
991
992 GroupNode Child2;
993
994 lock (this.children)
995 {
996 int i = this.children.IndexOf(Child);
997 if (i <= 0)
998 return false;
999
1000 Child2 = this.children[i - 1];
1001
1002 this.children.RemoveAt(i);
1003 this.children.Insert(i - 1, Child);
1004
1005 i = Child.siblingOrdinal;
1006 Child.siblingOrdinal = Child2.siblingOrdinal;
1007 Child2.siblingOrdinal = i;
1008 }
1009
1010 await Database.Update(Child, Child2);
1011
1012 await GroupSource.NewEvent(new NodeMovedUp()
1013 {
1014 NodeId = Child.NodeId,
1015 Partition = Child.Partition,
1016 SourceId = Child.SourceId,
1017 Timestamp = DateTime.UtcNow
1018 });
1019
1020 return true;
1021 }
1022
1029 public virtual async Task<bool> MoveDownAsync(GroupNode Child, RequestOrigin Caller)
1030 {
1031 if (!this.ChildrenOrdered)
1032 return false;
1033
1034 if (!this.childrenLoaded)
1035 await this.LoadChildren();
1036
1037 if (this.children is null)
1038 return false;
1039
1040 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
1041 return false;
1042
1043 GroupNode Child2;
1044
1045 lock (this.children)
1046 {
1047 int c = this.children.Count;
1048 int i = this.children.IndexOf(Child);
1049 if (i < 0 || i + 1 >= c)
1050 return false;
1051
1052 Child2 = this.children[i + 1];
1053
1054 this.children.RemoveAt(i);
1055 this.children.Insert(i + 1, Child);
1056
1057 i = Child.siblingOrdinal;
1058 Child.siblingOrdinal = Child2.siblingOrdinal;
1059 Child2.siblingOrdinal = i;
1060 }
1061
1062 await Database.Update(Child, Child2);
1063
1064 await GroupSource.NewEvent(new NodeMovedDown()
1065 {
1066 NodeId = Child.NodeId,
1067 Partition = Child.Partition,
1068 SourceId = Child.SourceId,
1069 Timestamp = DateTime.UtcNow
1070 });
1071
1072 return true;
1073 }
1074
1080 public abstract Task<bool> AcceptsParentAsync(INode Parent);
1081
1087 public abstract Task<bool> AcceptsChildAsync(INode Child);
1088
1093 public virtual async Task AddAsync(INode Child)
1094 {
1095 if (!(Child is GroupNode Node))
1096 throw new Exception("Child must be a group node.");
1097
1098 if (this.objectId == Guid.Empty)
1099 throw new Exception("Parent node must be persisted before you can add nodes to it.");
1100
1101 if (!this.childrenLoaded)
1102 await this.LoadChildren();
1103
1104 Node.parentId = this.objectId;
1105
1106 GroupNode[] ToUpdate;
1107 GroupNode After = null;
1108 int c;
1109
1110 lock (this.synchObject)
1111 {
1112 if (this.children is null)
1113 this.children = new List<GroupNode>();
1114 else if ((c = this.children.Count) > 0)
1115 After = this.children[c - 1];
1116
1117 ToUpdate = this.CheckOrderLocked(this.children);
1118
1119 Node.siblingOrdinal = this.children.Count;
1120 Node.parent = this;
1121
1122 this.children.Add(Node);
1123 }
1124
1125 if (!(ToUpdate is null))
1126 await Database.Update(ToUpdate);
1127
1128 if (Node.objectId == Guid.Empty)
1129 {
1130 await Database.Insert(Node);
1131 GroupSource.RegisterNode(Node);
1132
1134 NodeAdded Event = new NodeAdded()
1135 {
1136 Parameters = await Node.GetDisplayableParameterAraryAsync(Language, RequestOrigin.Empty),
1137 NodeType = Node.GetType().FullName,
1138 Sniffable = false,
1139 DisplayName = await Node.GetTypeNameAsync(Language),
1140 HasChildren = Node.HasChildren,
1141 ChildrenOrdered = Node.ChildrenOrdered,
1142 IsReadable = Node.IsReadable,
1143 IsControllable = Node.IsControllable,
1144 HasCommands = Node.HasCommands,
1145 ParentId = this.NodeId,
1146 ParentPartition = this.Partition,
1147 Updated = Node.Updated,
1148 State = Node.State,
1149 NodeId = Node.NodeId,
1150 Partition = Node.Partition,
1151 LogId = NodeAdded.EmptyIfSame(Node.LogId, Node.NodeId),
1152 LocalId = NodeAdded.EmptyIfSame(Node.LocalId, Node.NodeId),
1153 SourceId = Node.SourceId,
1154 Timestamp = DateTime.UtcNow
1155 };
1156
1157 if (this.ChildrenOrdered && !(After is null))
1158 {
1159 Event.AfterNodeId = After.nodeId;
1160 Event.AfterPartition = After.Partition;
1161 }
1162
1163 await GroupSource.NewEvent(Event);
1164 }
1165 else
1166 await Node.NodeUpdated();
1167
1168 await this.RaiseUpdate();
1169 }
1170
1174 protected virtual async Task NodeUpdated()
1175 {
1176 this.updated = DateTime.Now;
1177 await Database.Update(this);
1178
1179 await GroupSource.NewEvent(new NodeUpdated()
1180 {
1182 HasChildren = this.HasChildren,
1183 ChildrenOrdered = this.ChildrenOrdered,
1184 IsReadable = this.IsReadable,
1185 IsControllable = this.IsControllable,
1186 HasCommands = this.HasCommands,
1187 ParentId = (await this.GetParent()).NodeId,
1188 ParentPartition = this.Partition,
1189 Updated = this.Updated,
1190 State = this.State,
1191 NodeId = this.NodeId,
1192 OldId = this.oldId,
1193 Partition = this.Partition,
1194 LogId = NodeAdded.EmptyIfSame(this.LogId, this.NodeId),
1195 LocalId = NodeAdded.EmptyIfSame(this.LocalId, this.NodeId),
1196 SourceId = this.SourceId,
1197 Timestamp = DateTime.UtcNow
1198 });
1199
1200 if (this.oldId != this.nodeId)
1201 {
1202 GroupSource.RegisterNewNodeId(this, this.oldId);
1203 this.oldId = this.nodeId;
1204 }
1205 }
1206
1210 public virtual async Task UpdateAsync()
1211 {
1212 if (this.objectId != Guid.Empty)
1213 await this.NodeUpdated();
1214
1215 await this.RaiseUpdate();
1216 }
1217
1223 public virtual async Task<bool> RemoveAsync(INode Child)
1224 {
1225 if (!(Child is GroupNode Node))
1226 throw new Exception("Child must be a group node.");
1227
1228 if (!this.childrenLoaded)
1229 await this.LoadChildren();
1230
1231 GroupNode[] ToUpdate = null;
1232 int i;
1233
1234 lock (this.synchObject)
1235 {
1236 if (!(this.children is null))
1237 {
1238 i = this.children.IndexOf(Node);
1239 if (i >= 0)
1240 {
1241 this.children.RemoveAt(i);
1242 if (i == 0 && this.children.Count == 0)
1243 this.children = null;
1244 else
1245 ToUpdate = this.CheckOrderLocked(this.children);
1246 }
1247 }
1248 else
1249 i = -1;
1250 }
1251
1252 if (!(ToUpdate is null))
1253 await Database.Update(ToUpdate);
1254
1255 Node.parentId = Guid.Empty;
1256 Node.parent = null;
1257
1258 if (Node.objectId != Guid.Empty)
1259 {
1260 await Database.Update(Child);
1261 await this.RaiseUpdate();
1262
1263 await GroupSource.NewEvent(new NodeRemoved()
1264 {
1265 NodeId = Node.NodeId,
1266 Partition = Node.Partition,
1267 SourceId = Node.SourceId,
1268 Timestamp = DateTime.UtcNow
1269 });
1270 }
1271
1272 return i >= 0;
1273 }
1274
1278 public async virtual Task DestroyAsync()
1279 {
1280 if (!(await this.GetParent() is null))
1281 {
1282 if (this.parent.childrenLoaded)
1283 {
1284 GroupNode[] ToUpdate = null;
1285
1286 lock (this.parent.synchObject)
1287 {
1288 if (!(this.parent.children is null))
1289 {
1290 if (this.parent.children.Remove(this))
1291 {
1292 if (this.parent.children.Count == 0)
1293 this.parent.children = null;
1294 else
1295 ToUpdate = this.parent.CheckOrderLocked(this.parent.children);
1296 }
1297 }
1298 }
1299
1300 if (!(ToUpdate is null))
1301 await Database.Update(ToUpdate);
1302 }
1303 }
1304
1305 if (!this.childrenLoaded)
1306 await this.LoadChildren();
1307
1308 if (!(this.children is null))
1309 {
1310 List<GroupNode> Children = this.children;
1311 this.children = null;
1312
1313 foreach (GroupNode Child in Children)
1314 {
1315 Child.parent = null;
1316 Child.parentId = Guid.Empty;
1317
1318 await Child.DestroyAsync();
1319 }
1320
1321 this.children = null;
1322 }
1323
1324 if (this.objectId != Guid.Empty)
1325 {
1326 await Database.Delete(this);
1327 this.objectId = Guid.Empty;
1328 }
1329
1330 GroupSource.UnregisterNode(this);
1331 }
1332
1336 [IgnoreMember]
1337 public virtual Task<IEnumerable<ICommand>> Commands
1338 {
1339 get
1340 {
1341 return Task.FromResult<IEnumerable<ICommand>>(new ICommand[]
1342 {
1343 new ClearMessages(this),
1344 new LogMessage(this)
1345 });
1346 }
1347 }
1348
1349 #endregion
1350 }
1351}
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
Clears all messages for a group.
Logs a message on a group.
Definition: LogMessage.cs:14
Defines a message logged on a group node.
Definition: GroupMessage.cs:15
string Body
Message body.
string EventId
Optional Event ID.
MessageType Type
Message Type
DateTime Created
When node was created.
Definition: GroupMessage.cs:74
Base class for all group nodes.
Definition: GroupNode.cs:29
EventHandlerAsync OnUpdate
Event raised when node has been updated.
Definition: GroupNode.cs:524
virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
Definition: GroupNode.cs:261
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
Definition: GroupNode.cs:374
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
Definition: GroupNode.cs:250
virtual GroupNode[] CheckOrderLocked(List< GroupNode > Children)
Checks the ordering of children.
Definition: GroupNode.cs:771
virtual GroupNode[] SortChildrenAfterLoadLocked(List< GroupNode > Children)
Method that allows the node to sort its children, after they have been loaded.
Definition: GroupNode.cs:752
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
Definition: GroupNode.cs:400
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
Definition: GroupNode.cs:211
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
Definition: GroupNode.cs:1093
virtual async Task< bool > MoveDownAsync(GroupNode Child, RequestOrigin Caller)
Tries to move the child node down.
Definition: GroupNode.cs:1029
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
string NodeId
ID of node.
Definition: GroupNode.cs:142
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
Definition: GroupNode.cs:814
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from the node.
Definition: GroupNode.cs:409
NodeState State
Current overall state of the node.
Definition: GroupNode.cs:685
override int GetHashCode()
Serves as the default hash function.
Definition: GroupNode.cs:79
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
Definition: GroupNode.cs:647
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 Task LogErrorAsync(string Body)
Logs an error message on the node.
Definition: GroupNode.cs:192
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
Definition: GroupNode.cs:391
async Task< INode > GetParent()
Gets the parent of the node.
Definition: GroupNode.cs:628
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
Definition: GroupNode.cs:964
virtual bool IsControllable
If the node can be controlled.
Definition: GroupNode.cs:608
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
Definition: GroupNode.cs:419
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
Definition: GroupNode.cs:563
INode Parent
Parent Node, or null if a root node.
Definition: GroupNode.cs:621
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: GroupNode.cs:936
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
Definition: GroupNode.cs:834
virtual bool IsReadable
If the node can be read.
Definition: GroupNode.cs:602
string Partition
Optional partition in which the Node ID is unique.
Definition: GroupNode.cs:164
virtual bool HasCommands
If the node has registered commands or not.
Definition: GroupNode.cs:614
override string ToString()
Definition: GroupNode.cs:167
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: GroupNode.cs:855
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
Definition: GroupNode.cs:383
DateTime LastChanged
When the node was last updated.
Definition: GroupNode.cs:670
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
Definition: GroupNode.cs:366
Guid ParentId
Object ID of parent node in persistence layer.
Definition: GroupNode.cs:100
string SourceId
Optional ID of source containing node.
Definition: GroupNode.cs:158
DateTime Created
When node was created.
Definition: GroupNode.cs:109
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Definition: GroupNode.cs:596
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
Definition: GroupNode.cs:951
virtual Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
Definition: GroupNode.cs:824
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
Definition: GroupNode.cs:1223
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: GroupNode.cs:920
Guid ObjectId
Object ID in persistence layer.
Definition: GroupNode.cs:91
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
Definition: GroupNode.cs:202
GroupNode()
Base class for all group nodes.
Definition: GroupNode.cs:47
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
Definition: GroupNode.cs:1338
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 > MoveUpAsync(GroupNode Child, RequestOrigin Caller)
Tries to move the child node up.
Definition: GroupNode.cs:978
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
Definition: GroupNode.cs:1174
static async Task< string > GetUniqueGroupId(string GroupId)
Gets a Group ID, based on GroupId that is not already available in the database.
Definition: GroupNode.cs:536
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
Definition: GroupNode.cs:569
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
Definition: GroupNode.cs:357
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
Definition: GroupNode.cs:1278
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: GroupNode.cs:695
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
Definition: GroupNode.cs:240
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
Definition: GroupNode.cs:844
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
Definition: GroupNode.cs:1210
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
Definition: GroupNode.cs:119
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
Definition: GroupNode.cs:129
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
Definition: GroupNode.cs:221
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: GroupNode.cs:909
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
Definition: GroupNode.cs:67
bool HasChildren
If the source has any child sources.
Definition: GroupNode.cs:583
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
Definition: GroupNode.cs:230
Defines the Groups data source. This data source contains a tree structure of groups of nodes
Definition: GroupSource.cs:21
const string SourceID
Source ID for the groups data source.
Definition: GroupSource.cs:25
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
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.
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
Base Interface for all metering nodes.
Definition: IGroupNode.cs:11
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.
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