Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
OutputNode.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
13using Waher.Things;
17
18namespace Waher.Output
19{
23 [CollectionName("Output")]
24 [TypeName(TypeNameSerialization.FullName)]
25 [ArchivingTime]
26 [Index("NodeId")]
27 [Index("ParentId", "NodeId")]
28 public abstract class OutputNode : IOutputNode
29 {
30 private Guid objectId = Guid.Empty;
31 private Guid parentId = Guid.Empty;
32 private OutputNode parent = null;
33 private string nodeId = string.Empty;
34 private string oldId = null;
35 private NodeState state = NodeState.None;
36 private List<OutputNode> 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 OutputNode()
48 {
49 }
50
55 public static implicit operator ThingReference(OutputNode 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(14, "ID:", 0)]
138 [Page(15, "Output", 0)]
139 [ToolTip(16, "Output identity in the collection of output.")]
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]
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 OutputMessage Msg = new OutputMessage(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 OutputSource.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 (OutputMessage Message in await Database.Find<OutputMessage>(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> GetUniqueNodeId(string OutputId)
537 {
538 using Semaphore Semaphore = await Semaphores.BeginWrite("Output." + OutputId);
539 string Suffix = string.Empty;
540 string s;
541 int i = 1;
542
543 while (true)
544 {
545 if (await Database.FindFirstIgnoreRest<OutputNode>(
546 new FilterFieldEqualTo("NodeId", s = OutputId + Suffix)) is null)
547 {
548 return s;
549 }
550
551 i++;
552 Suffix = " (" + i.ToString() + ")";
553 }
554 }
555
556 #region INode
557
561 [IgnoreMember]
562 public virtual string LocalId => this.NodeId;
563
567 [IgnoreMember]
568 public virtual string LogId => this.NodeId;
569
575 public abstract Task<string> GetTypeNameAsync(Language Language);
576
580 [IgnoreMember]
581 public bool HasChildren
582 {
583 get
584 {
585 if (!this.childrenLoaded)
586 this.LoadChildren().Wait();
587
588 return !(this.children is null) && this.children.Count > 0;
589 }
590 }
591
595 public virtual bool ChildrenOrdered => false;
596
600 [IgnoreMember]
601 public virtual bool IsReadable => this is ISensor;
602
606 [IgnoreMember]
607 public virtual bool IsControllable => this is IActuator;
608
612 [IgnoreMember]
613 public virtual bool HasCommands => true;
614
618 [IgnoreMember]
619 [Obsolete("Use the asynchronous GetParent() method instead.")]
620 public INode Parent => this.GetParent().Result;
621
627 public async Task<INode> GetParent()
628 {
629 if (!(this.parent is null))
630 return this.parent;
631
632 if (this.parentId == Guid.Empty)
633 return null;
634
635 this.parent = await this.LoadParent();
636 if (this.parent is null)
637 throw new Exception("Parent not found.");
638
639 return this.parent;
640 }
641
646 public async Task<T> GetAncestor<T>()
647 where T : INode
648 {
649 INode Loop = await this.GetParent();
650
651 while (!(Loop is null))
652 {
653 if (Loop is T Ancestor)
654 return Ancestor;
655 else if (Loop is OutputNode OutputNode)
656 Loop = await OutputNode.GetParent();
657 else
658 Loop = Loop.Parent;
659 }
660
661 return default;
662 }
663
667 [IgnoreMember]
668 public DateTime LastChanged
669 {
670 get
671 {
672 if (this.updated == DateTime.MinValue)
673 return this.created;
674 else
675 return this.updated;
676 }
677 }
678
682 [DefaultValue(NodeState.None)]
684 {
685 get => this.state;
686 set => this.state = value;
687 }
688
692 [IgnoreMember]
693 public Task<IEnumerable<INode>> ChildNodes
694 {
695 get
696 {
697 return this.GetChildNodes();
698 }
699 }
700
701 private async Task<IEnumerable<INode>> GetChildNodes()
702 {
703 if (!this.childrenLoaded)
704 await this.LoadChildren();
705
706 lock (this.synchObject)
707 {
708 if (this.children is null)
709 return Array.Empty<INode>();
710 else
711 return this.children.ToArray();
712 }
713 }
714
715 private async Task LoadChildren()
716 {
717 List<OutputNode> Children = new List<OutputNode>();
718 OutputNode[] ToUpdate = null;
719
720 foreach (OutputNode Node in await Database.Find<OutputNode>(
721 new FilterFieldEqualTo("ParentId", this.objectId)))
722 {
723 Children.Add(OutputSource.RegisterNode(Node));
724 }
725
726 lock (this.synchObject)
727 {
728 this.children = null;
729
730 if (Children.Count > 0)
731 {
732 foreach (OutputNode Child in Children)
733 Child.parent = this;
734
735 ToUpdate = this.SortChildrenAfterLoadLocked(Children);
736 this.children = Children;
737 }
738
739 this.childrenLoaded = true;
740 }
741
742 if (!(ToUpdate is null))
743 await Database.Update(ToUpdate);
744 }
745
751 protected virtual OutputNode[] SortChildrenAfterLoadLocked(List<OutputNode> Children)
752 {
753 if (this.ChildrenOrdered)
754 {
755 Children.Sort((n1, n2) => n1.siblingOrdinal.CompareTo(n2.siblingOrdinal));
756 return this.CheckOrderLocked(Children);
757 }
758 else
759 {
760 Children.Sort((n1, n2) => n1.nodeId.CompareTo(n2.nodeId));
761 return null;
762 }
763 }
764
770 protected virtual OutputNode[] CheckOrderLocked(List<OutputNode> Children)
771 {
772 if (this.ChildrenOrdered)
773 {
774 ChunkedList<OutputNode> ToUpdate = null;
775 int Expected = 0;
776
777 foreach (OutputNode Child in Children)
778 {
779 if (Child.SiblingOrdinal != Expected)
780 {
781 ToUpdate ??= new ChunkedList<OutputNode>();
782 ToUpdate.Add(Child);
783 Child.siblingOrdinal = Expected;
784 }
785
786 Expected++;
787 }
788 return ToUpdate?.ToArray();
789 }
790 else
791 return null;
792 }
793
794 internal async Task<OutputNode> LoadParent()
795 {
796 if (!(this.parent is null))
797 return this.parent;
798
799 if (this.parentId == Guid.Empty)
800 return null;
801
802 this.parent = await Database.LoadObject<OutputNode>(this.parentId);
803 OutputSource.RegisterNode(this.parent);
804
805 return this.parent;
806 }
807
813 public virtual Task<bool> CanViewAsync(RequestOrigin Caller)
814 {
815 return Task.FromResult(Caller.HasPrivilege("Source." + OutputSource.SourceID + ".Node.View"));
816 }
817
823 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
824 {
825 return Task.FromResult(Caller.HasPrivilege("Source." + OutputSource.SourceID + ".Node.Edit"));
826 }
827
833 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
834 {
835 return Task.FromResult(Caller.HasPrivilege("Source." + OutputSource.SourceID + ".Node.Add"));
836 }
837
843 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
844 {
845 return Task.FromResult(Caller.HasPrivilege("Source." + OutputSource.SourceID + ".Node.Destroy"));
846 }
847
854 public virtual async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
855 {
858
859 LinkedList<Parameter> Result = new LinkedList<Parameter>();
860 Result.AddLast(new StringParameter("NodeId", await Namespace.GetStringAsync(1, "Output ID"), this.nodeId));
861 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(4, "Type"), await this.GetTypeNameAsync(Language)));
862
863 if (!(this.parent is null))
864 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(2, "Parent ID"), this.parent.nodeId));
865
866 if (!this.childrenLoaded)
867 await this.LoadChildren();
868
869 if (!(this.children is null))
870 {
871 int i;
872
873 lock (this.synchObject)
874 {
875 i = this.children.Count;
876 }
877
878 Result.AddLast(new Int32Parameter("NrChildren", await Namespace.GetStringAsync(3, "#Children"), i));
879 }
880
881 string s = this.state switch
882 {
883 NodeState.Information => await Namespace.GetStringAsync(8, "Information"),
884 NodeState.WarningUnsigned => await Namespace.GetStringAsync(9, "Unsigned Warning"),
885 NodeState.WarningSigned => await Namespace.GetStringAsync(10, "Warning"),
886 NodeState.ErrorUnsigned => await Namespace.GetStringAsync(11, "Unsigned Error"),
887 NodeState.ErrorSigned => await Namespace.GetStringAsync(12, "Error"),
888 _ => null,
889 };
890
891 if (!string.IsNullOrEmpty(s))
892 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(5, "State"), s));
893
894 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(6, "Created"), this.created));
895
896 if (this.updated != DateTime.MinValue)
897 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(7, "Updated"), this.updated));
898
899 return Result;
900 }
901
908 public async Task<Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
909 {
911 Result.AddRange(await this.GetDisplayableParametersAsync(Language, Caller));
912 return Result.ToArray();
913 }
914
919 public virtual async Task<IEnumerable<Message>> GetMessagesAsync(RequestOrigin Caller)
920 {
921 IEnumerable<OutputMessage> Messages = await Database.Find<OutputMessage>(
922 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
923 LinkedList<Message> Result = new LinkedList<Message>();
924
925 foreach (OutputMessage Msg in Messages)
926 Result.AddLast(new Message(Msg.Created, Msg.Type, Msg.EventId, Msg.Body)); // TODO: Include Updated & Count also.
927
928 return Result;
929 }
930
935 public async Task<Message[]> GetMessageArrayAsync(RequestOrigin Caller)
936 {
937 List<Message> Result = new List<Message>();
938
939 foreach (Message Msg in await this.GetMessagesAsync(Caller))
940 Result.Add(Msg);
941
942 return Result.ToArray();
943 }
944
950 public virtual async Task<bool> MoveUpAsync(RequestOrigin Caller)
951 {
952 if (!(await this.GetParent() is OutputNode Parent))
953 return false;
954 else
955 return await Parent.MoveUpAsync(this, Caller);
956 }
957
963 public virtual async Task<bool> MoveDownAsync(RequestOrigin Caller)
964 {
965 if (!(await this.GetParent() is OutputNode Parent))
966 return false;
967 else
968 return await Parent.MoveDownAsync(this, Caller);
969 }
970
977 public virtual async Task<bool> MoveUpAsync(OutputNode Child, RequestOrigin Caller)
978 {
979 if (!this.ChildrenOrdered)
980 return false;
981
982 if (!this.childrenLoaded)
983 await this.LoadChildren();
984
985 if (this.children is null)
986 return false;
987
988 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
989 return false;
990
991 OutputNode Child2;
992
993 lock (this.children)
994 {
995 int i = this.children.IndexOf(Child);
996 if (i <= 0)
997 return false;
998
999 Child2 = this.children[i - 1];
1000
1001 this.children.RemoveAt(i);
1002 this.children.Insert(i - 1, Child);
1003
1004 i = Child.siblingOrdinal;
1005 Child.siblingOrdinal = Child2.siblingOrdinal;
1006 Child2.siblingOrdinal = i;
1007 }
1008
1009 await Database.Update(Child, Child2);
1010
1011 await OutputSource.NewEvent(new NodeMovedUp()
1012 {
1013 NodeId = Child.NodeId,
1014 Partition = Child.Partition,
1015 SourceId = Child.SourceId,
1016 Timestamp = DateTime.UtcNow
1017 });
1018
1019 return true;
1020 }
1021
1028 public virtual async Task<bool> MoveDownAsync(OutputNode Child, RequestOrigin Caller)
1029 {
1030 if (!this.ChildrenOrdered)
1031 return false;
1032
1033 if (!this.childrenLoaded)
1034 await this.LoadChildren();
1035
1036 if (this.children is null)
1037 return false;
1038
1039 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
1040 return false;
1041
1042 OutputNode Child2;
1043
1044 lock (this.children)
1045 {
1046 int c = this.children.Count;
1047 int i = this.children.IndexOf(Child);
1048 if (i < 0 || i + 1 >= c)
1049 return false;
1050
1051 Child2 = this.children[i + 1];
1052
1053 this.children.RemoveAt(i);
1054 this.children.Insert(i + 1, Child);
1055
1056 i = Child.siblingOrdinal;
1057 Child.siblingOrdinal = Child2.siblingOrdinal;
1058 Child2.siblingOrdinal = i;
1059 }
1060
1061 await Database.Update(Child, Child2);
1062
1063 await OutputSource.NewEvent(new NodeMovedDown()
1064 {
1065 NodeId = Child.NodeId,
1066 Partition = Child.Partition,
1067 SourceId = Child.SourceId,
1068 Timestamp = DateTime.UtcNow
1069 });
1070
1071 return true;
1072 }
1073
1079 public abstract Task<bool> AcceptsParentAsync(INode Parent);
1080
1086 public abstract Task<bool> AcceptsChildAsync(INode Child);
1087
1092 public virtual async Task AddAsync(INode Child)
1093 {
1094 if (!(Child is OutputNode Node))
1095 throw new Exception("Child must be an output node.");
1096
1097 if (this.objectId == Guid.Empty)
1098 throw new Exception("Parent node must be persisted before you can add nodes to it.");
1099
1100 if (!this.childrenLoaded)
1101 await this.LoadChildren();
1102
1103 Node.parentId = this.objectId;
1104
1105 OutputNode[] ToUpdate;
1106 OutputNode After = null;
1107 int c;
1108
1109 lock (this.synchObject)
1110 {
1111 if (this.children is null)
1112 this.children = new List<OutputNode>();
1113 else if ((c = this.children.Count) > 0)
1114 After = this.children[c - 1];
1115
1116 ToUpdate = this.CheckOrderLocked(this.children);
1117
1118 Node.siblingOrdinal = this.children.Count;
1119 Node.parent = this;
1120
1121 this.children.Add(Node);
1122 }
1123
1124 if (!(ToUpdate is null))
1125 await Database.Update(ToUpdate);
1126
1127 if (Node.objectId == Guid.Empty)
1128 {
1129 await Database.Insert(Node);
1130 OutputSource.RegisterNode(Node);
1131
1133 NodeAdded Event = new NodeAdded()
1134 {
1135 Parameters = await Node.GetDisplayableParameterAraryAsync(Language, RequestOrigin.Empty),
1136 NodeType = Node.GetType().FullName,
1137 Sniffable = false,
1138 DisplayName = await Node.GetTypeNameAsync(Language),
1139 HasChildren = Node.HasChildren,
1140 ChildrenOrdered = Node.ChildrenOrdered,
1141 IsReadable = Node.IsReadable,
1142 IsControllable = Node.IsControllable,
1143 HasCommands = Node.HasCommands,
1144 ParentId = this.NodeId,
1145 ParentPartition = this.Partition,
1146 Updated = Node.Updated,
1147 State = Node.State,
1148 NodeId = Node.NodeId,
1149 Partition = Node.Partition,
1150 LogId = NodeAdded.EmptyIfSame(Node.LogId, Node.NodeId),
1151 LocalId = NodeAdded.EmptyIfSame(Node.LocalId, Node.NodeId),
1152 SourceId = Node.SourceId,
1153 Timestamp = DateTime.UtcNow
1154 };
1155
1156 if (this.ChildrenOrdered && !(After is null))
1157 {
1158 Event.AfterNodeId = After.nodeId;
1159 Event.AfterPartition = After.Partition;
1160 }
1161
1162 await OutputSource.NewEvent(Event);
1163 }
1164 else
1165 await Node.NodeUpdated();
1166
1167 await this.RaiseUpdate();
1168 }
1169
1173 protected virtual async Task NodeUpdated()
1174 {
1175 this.updated = DateTime.Now;
1176 await Database.Update(this);
1177
1178 await OutputSource.NewEvent(new NodeUpdated()
1179 {
1181 HasChildren = this.HasChildren,
1182 ChildrenOrdered = this.ChildrenOrdered,
1183 IsReadable = this.IsReadable,
1184 IsControllable = this.IsControllable,
1185 HasCommands = this.HasCommands,
1186 ParentId = (await this.GetParent()).NodeId,
1187 ParentPartition = this.Partition,
1188 Updated = this.Updated,
1189 State = this.State,
1190 NodeId = this.NodeId,
1191 OldId = this.oldId,
1192 Partition = this.Partition,
1193 LogId = NodeAdded.EmptyIfSame(this.LogId, this.NodeId),
1194 LocalId = NodeAdded.EmptyIfSame(this.LocalId, this.NodeId),
1195 SourceId = this.SourceId,
1196 Timestamp = DateTime.UtcNow
1197 });
1198
1199 if (this.oldId != this.nodeId)
1200 {
1201 OutputSource.RegisterNewNodeId(this, this.oldId);
1202 this.oldId = this.nodeId;
1203 }
1204 }
1205
1209 public virtual async Task UpdateAsync()
1210 {
1211 if (this.objectId != Guid.Empty)
1212 await this.NodeUpdated();
1213
1214 await this.RaiseUpdate();
1215 }
1216
1222 public virtual async Task<bool> RemoveAsync(INode Child)
1223 {
1224 if (!(Child is OutputNode Node))
1225 throw new Exception("Child must be an output node.");
1226
1227 if (!this.childrenLoaded)
1228 await this.LoadChildren();
1229
1230 OutputNode[] ToUpdate = null;
1231 int i;
1232
1233 lock (this.synchObject)
1234 {
1235 if (!(this.children is null))
1236 {
1237 i = this.children.IndexOf(Node);
1238 if (i >= 0)
1239 {
1240 this.children.RemoveAt(i);
1241 if (i == 0 && this.children.Count == 0)
1242 this.children = null;
1243 else
1244 ToUpdate = this.CheckOrderLocked(this.children);
1245 }
1246 }
1247 else
1248 i = -1;
1249 }
1250
1251 if (!(ToUpdate is null))
1252 await Database.Update(ToUpdate);
1253
1254 Node.parentId = Guid.Empty;
1255 Node.parent = null;
1256
1257 if (Node.objectId != Guid.Empty)
1258 {
1259 await Database.Update(Child);
1260 await this.RaiseUpdate();
1261
1262 await OutputSource.NewEvent(new NodeRemoved()
1263 {
1264 NodeId = Node.NodeId,
1265 Partition = Node.Partition,
1266 SourceId = Node.SourceId,
1267 Timestamp = DateTime.UtcNow
1268 });
1269 }
1270
1271 return i >= 0;
1272 }
1273
1277 public async virtual Task DestroyAsync()
1278 {
1279 if (!(await this.GetParent() is null))
1280 {
1281 if (this.parent.childrenLoaded)
1282 {
1283 OutputNode[] ToUpdate = null;
1284
1285 lock (this.parent.synchObject)
1286 {
1287 if (!(this.parent.children is null))
1288 {
1289 if (this.parent.children.Remove(this))
1290 {
1291 if (this.parent.children.Count == 0)
1292 this.parent.children = null;
1293 else
1294 ToUpdate = this.parent.CheckOrderLocked(this.parent.children);
1295 }
1296 }
1297 }
1298
1299 if (!(ToUpdate is null))
1300 await Database.Update(ToUpdate);
1301 }
1302 }
1303
1304 if (!this.childrenLoaded)
1305 await this.LoadChildren();
1306
1307 if (!(this.children is null))
1308 {
1309 List<OutputNode> Children = this.children;
1310 this.children = null;
1311
1312 foreach (OutputNode Child in Children)
1313 {
1314 Child.parent = null;
1315 Child.parentId = Guid.Empty;
1316
1317 await Child.DestroyAsync();
1318 }
1319
1320 this.children = null;
1321 }
1322
1323 if (this.objectId != Guid.Empty)
1324 {
1325 await Database.Delete(this);
1326 this.objectId = Guid.Empty;
1327 }
1328
1329 OutputSource.UnregisterNode(this);
1330 }
1331
1335 [IgnoreMember]
1336 public virtual Task<IEnumerable<ICommand>> Commands
1337 {
1338 get
1339 {
1340 return Task.FromResult<IEnumerable<ICommand>>(new ICommand[]
1341 {
1342 new ClearMessages(this),
1343 new LogMessage(this)
1344 });
1345 }
1346 }
1347
1348 #endregion
1349 }
1350}
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 an output.
Logs a message on an output.
Definition: LogMessage.cs:14
Defines a message logged on an output node.
DateTime Created
When node was created.
MessageType Type
Message Type
string EventId
Optional Event ID.
string Body
Message body.
Base class for all output nodes.
Definition: OutputNode.cs:29
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
Definition: OutputNode.cs:129
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
Definition: OutputNode.cs:119
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
Definition: OutputNode.cs:383
bool HasChildren
If the source has any child sources.
Definition: OutputNode.cs:582
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: OutputNode.cs:935
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...
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
Definition: OutputNode.cs:646
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
Definition: OutputNode.cs:813
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
Definition: OutputNode.cs:366
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
Definition: OutputNode.cs:562
INode Parent
Parent Node, or null if a root node.
Definition: OutputNode.cs:620
Guid ParentId
Object ID of parent node in persistence layer.
Definition: OutputNode.cs:100
async Task< INode > GetParent()
Gets the parent of the node.
Definition: OutputNode.cs:627
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
Definition: OutputNode.cs:67
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
Definition: OutputNode.cs:963
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
Definition: OutputNode.cs:374
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
Definition: OutputNode.cs:833
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
Definition: OutputNode.cs:221
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
Definition: OutputNode.cs:1337
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
Definition: OutputNode.cs:1222
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
Definition: OutputNode.cs:843
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
Definition: OutputNode.cs:192
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
Definition: OutputNode.cs:400
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
Definition: OutputNode.cs:950
virtual bool HasCommands
If the node has registered commands or not.
Definition: OutputNode.cs:613
EventHandlerAsync OnUpdate
Event raised when node has been updated.
Definition: OutputNode.cs:524
virtual Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
Definition: OutputNode.cs:823
string NodeId
ID of node.
Definition: OutputNode.cs:142
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 ...
DateTime Created
When node was created.
Definition: OutputNode.cs:109
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
Definition: OutputNode.cs:357
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
Definition: OutputNode.cs:202
virtual async Task< bool > MoveDownAsync(OutputNode Child, RequestOrigin Caller)
Tries to move the child node down.
Definition: OutputNode.cs:1028
Guid ObjectId
Object ID in persistence layer.
Definition: OutputNode.cs:91
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: OutputNode.cs:854
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: OutputNode.cs:908
string SourceId
Optional ID of source containing node.
Definition: OutputNode.cs:158
virtual bool IsReadable
If the node can be read.
Definition: OutputNode.cs:601
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
Definition: OutputNode.cs:419
virtual OutputNode[] CheckOrderLocked(List< OutputNode > Children)
Checks the ordering of children.
Definition: OutputNode.cs:770
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Definition: OutputNode.cs:595
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
Definition: OutputNode.cs:240
virtual bool IsControllable
If the node can be controlled.
Definition: OutputNode.cs:607
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
Definition: OutputNode.cs:1277
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
Definition: OutputNode.cs:391
static async Task< string > GetUniqueNodeId(string OutputId)
Gets a Output ID, based on OutputId that is not already available in the database.
Definition: OutputNode.cs:536
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
Definition: OutputNode.cs:1092
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from the node.
Definition: OutputNode.cs:409
OutputNode()
Base class for all output nodes.
Definition: OutputNode.cs:47
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: OutputNode.cs:694
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
Definition: OutputNode.cs:1173
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
virtual async Task< bool > MoveUpAsync(OutputNode Child, RequestOrigin Caller)
Tries to move the child node up.
Definition: OutputNode.cs:977
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
Definition: OutputNode.cs:230
override string ToString()
Definition: OutputNode.cs:167
virtual OutputNode[] SortChildrenAfterLoadLocked(List< OutputNode > Children)
Method that allows the node to sort its children, after they have been loaded.
Definition: OutputNode.cs:751
override int GetHashCode()
Serves as the default hash function.
Definition: OutputNode.cs:79
NodeState State
Current overall state of the node.
Definition: OutputNode.cs:684
DateTime LastChanged
When the node was last updated.
Definition: OutputNode.cs:669
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
Definition: OutputNode.cs:250
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: OutputNode.cs:919
virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
Definition: OutputNode.cs:261
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
Definition: OutputNode.cs:568
string Partition
Optional partition in which the Node ID is unique.
Definition: OutputNode.cs:164
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
Definition: OutputNode.cs:211
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
Definition: OutputNode.cs:1209
Defines the Output data source. This data source contains a tree structure of output of nodes
Definition: OutputSource.cs:20
const string SourceID
Source ID for the output data source.
Definition: OutputSource.cs:24
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 output nodes.
Definition: IOutputNode.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