Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ProcessorNode.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
13using Waher.Things;
17
18namespace Waher.Processors
19{
23 [CollectionName("Processors")]
24 [TypeName(TypeNameSerialization.FullName)]
25 [ArchivingTime]
26 [Index("NodeId")]
27 [Index("ParentId", "NodeId")]
28 public abstract class ProcessorNode : IProcessorNode
29 {
30 private Guid objectId = Guid.Empty;
31 private Guid parentId = Guid.Empty;
32 private ProcessorNode parent = null;
33 private string nodeId = string.Empty;
34 private string oldId = null;
35 private NodeState state = NodeState.None;
36 private List<ProcessorNode> 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
48 {
49 }
50
55 public static implicit operator ThingReference(ProcessorNode 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, "Processor", 0)]
139 [ToolTip(16, "Processor identity in the collection of processors.")]
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 protected string OldId => this.oldId;
158
162 [IgnoreMember]
164
168 [IgnoreMember]
169 public string Partition => string.Empty;
170
172 public override string ToString()
173 {
174 StringBuilder sb = new StringBuilder();
176
177 sb.Append(this.nodeId);
178 sb.Append(" (");
179 sb.Append(this.GetTypeNameAsync(Language).Result);
180 sb.Append(")");
181
183 {
184 sb.Append(", ");
185 sb.Append(P.Name);
186 sb.Append("=");
187 sb.Append(P.StringValue);
188 }
189
190 return sb.ToString();
191 }
192
197 public virtual Task LogErrorAsync(string Body)
198 {
199 return this.LogMessageAsync(MessageType.Error, string.Empty, Body);
200 }
201
207 public virtual Task LogErrorAsync(string EventId, string Body)
208 {
209 return this.LogMessageAsync(MessageType.Error, EventId, Body);
210 }
211
216 public virtual Task LogWarningAsync(string Body)
217 {
218 return this.LogMessageAsync(MessageType.Warning, string.Empty, Body);
219 }
220
226 public virtual Task LogWarningAsync(string EventId, string Body)
227 {
228 return this.LogMessageAsync(MessageType.Warning, EventId, Body);
229 }
230
235 public virtual Task LogInformationAsync(string Body)
236 {
237 return this.LogMessageAsync(MessageType.Information, string.Empty, Body);
238 }
239
245 public virtual Task LogInformationAsync(string EventId, string Body)
246 {
247 return this.LogMessageAsync(MessageType.Information, EventId, Body);
248 }
249
255 public virtual Task LogMessageAsync(MessageType Type, string Body)
256 {
257 return this.LogMessageAsync(Type, string.Empty, Body);
258 }
259
266 public virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
267 {
268 if (this.objectId == Guid.Empty)
269 return;
270
271 bool Updated = false;
272
274 new FilterFieldEqualTo("NodeId", this.objectId),
275 new FilterFieldEqualTo("Type", Type),
276 new FilterFieldEqualTo("EventId", EventId),
277 new FilterFieldEqualTo("Body", Body))))
278 {
279 Message.Updated = DateTime.Now;
280 Message.Count++;
281
282 await Database.Update(Message);
283 Updated = true;
284
285 break;
286 }
287
288 if (!Updated)
289 {
290 ProcessorMessage Msg = new ProcessorMessage(this.objectId, DateTime.Now, Type, EventId, Body)
291 {
292 NodeId = this.objectId
293 };
294
295 await Database.Insert(Msg);
296 }
297
298 switch (Type)
299 {
300 case MessageType.Error:
301 if (this.state < NodeState.ErrorUnsigned)
302 {
303 this.state = NodeState.ErrorUnsigned;
304 await Database.Update(this);
305 await this.RaiseUpdate();
306 }
307 break;
308
309 case MessageType.Warning:
310 if (this.state < NodeState.WarningUnsigned)
311 {
312 this.state = NodeState.WarningUnsigned;
313 await Database.Update(this);
314 await this.RaiseUpdate();
315 }
316 break;
317
318 case MessageType.Information:
319 if (this.state < NodeState.Information)
320 {
321 this.state = NodeState.Information;
322 await Database.Update(this);
323 await this.RaiseUpdate();
324 }
325 break;
326 }
327
328 switch (Type)
329 {
330 case MessageType.Information:
331 Log.Informational(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
332 break;
333
334 case MessageType.Warning:
335 Log.Warning(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
336 break;
337
338 case MessageType.Error:
339 Log.Error(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
340 break;
341 }
342
343 await this.NodeStateChanged();
344 }
345
346 internal async Task NodeStateChanged()
347 {
348 await ProcessorSource.NewEvent(new NodeStatusChanged()
349 {
350 Messages = await this.GetMessageArrayAsync(RequestOrigin.Empty),
351 State = this.state,
352 NodeId = this.NodeId,
353 Partition = this.Partition,
354 SourceId = this.SourceId,
355 Timestamp = DateTime.UtcNow
356 });
357 }
358
362 public virtual Task<bool> RemoveErrorAsync()
363 {
364 return this.RemoveMessageAsync(MessageType.Error, string.Empty);
365 }
366
371 public virtual Task<bool> RemoveErrorAsync(string EventId)
372 {
373 return this.RemoveMessageAsync(MessageType.Error, EventId);
374 }
375
379 public virtual Task<bool> RemoveWarningAsync()
380 {
381 return this.RemoveMessageAsync(MessageType.Warning, string.Empty);
382 }
383
388 public virtual Task<bool> RemoveWarningAsync(string EventId)
389 {
390 return this.RemoveMessageAsync(MessageType.Warning, EventId);
391 }
392
396 public virtual Task<bool> RemoveInformationAsync()
397 {
398 return this.RemoveMessageAsync(MessageType.Information, string.Empty);
399 }
400
405 public virtual Task<bool> RemoveInformationAsync(string EventId)
406 {
407 return this.RemoveMessageAsync(MessageType.Information, EventId);
408 }
409
414 public virtual Task<bool> RemoveMessageAsync(MessageType Type)
415 {
416 return this.RemoveMessageAsync(Type, string.Empty);
417 }
418
424 public virtual async Task<bool> RemoveMessageAsync(MessageType Type, string EventId)
425 {
426 if (this.objectId == Guid.Empty)
427 return false;
428
429 bool Removed = false;
430
432 new FilterFieldEqualTo("NodeId", this.objectId),
433 new FilterFieldEqualTo("Type", Type),
434 new FilterFieldEqualTo("EventId", EventId))))
435 {
436 Removed = true;
437
438 switch (Type)
439 {
440 case MessageType.Error:
441 Log.Informational("Error removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
442 break;
443
444 case MessageType.Warning:
445 Log.Informational("Warning removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
446 break;
447 }
448 }
449
450 if (Removed)
451 {
452 bool ErrorsFound = false;
453 bool WarningsFound = false;
454 bool InformationFound = false;
455
456 foreach (ProcessorMessage Message in await Database.Find<ProcessorMessage>(new FilterFieldEqualTo("NodeId", this.objectId)))
457 {
458 switch (Type)
459 {
460 case MessageType.Error:
461 ErrorsFound = true;
462 break;
463
464 case MessageType.Warning:
465 WarningsFound = true;
466 break;
467
468 case MessageType.Information:
469 InformationFound = true;
470 break;
471 }
472 }
473
474 NodeState NewStateSigned;
475 NodeState NewStateUnsigned;
476
477 if (ErrorsFound)
478 {
479 NewStateSigned = NodeState.ErrorSigned;
480 NewStateUnsigned = NodeState.ErrorUnsigned;
481 }
482 else if (WarningsFound)
483 {
484 NewStateSigned = NodeState.WarningSigned;
485 NewStateUnsigned = NodeState.WarningUnsigned;
486 }
487 else if (InformationFound)
488 {
489 NewStateSigned = NodeState.Information;
490 NewStateUnsigned = NodeState.Information;
491 }
492 else
493 {
494 NewStateSigned = NodeState.None;
495 NewStateUnsigned = NodeState.None;
496 }
497
498 switch (this.state)
499 {
500 case NodeState.ErrorSigned:
501 case NodeState.WarningSigned:
502 if (this.state != NewStateSigned)
503 {
504 this.state = NewStateSigned;
505 await Database.Update(this);
506 await this.RaiseUpdate();
507 }
508 break;
509
510 default:
511 if (this.state != NewStateUnsigned)
512 {
513 this.state = NewStateUnsigned;
514 await Database.Update(this);
515 await this.RaiseUpdate();
516 }
517 break;
518 }
519
520 await this.NodeStateChanged();
521 }
522
523 return Removed;
524 }
525
529 public event EventHandlerAsync OnUpdate = null;
530
531 internal Task RaiseUpdate()
532 {
533 return this.OnUpdate.Raise(this, EventArgs.Empty);
534 }
535
541 public static async Task<string> GetUniqueNodeId(string ProcessorId)
542 {
543 using Semaphore Semaphore = await Semaphores.BeginWrite("Processors." + ProcessorId);
544 string Suffix = string.Empty;
545 string s;
546 int i = 1;
547
548 while (true)
549 {
550 if (await Database.FindFirstIgnoreRest<ProcessorNode>(
551 new FilterFieldEqualTo("NodeId", s = ProcessorId + Suffix)) is null)
552 {
553 return s;
554 }
555
556 i++;
557 Suffix = " (" + i.ToString() + ")";
558 }
559 }
560
561 #region INode
562
566 [IgnoreMember]
567 public virtual string LocalId => this.NodeId;
568
572 [IgnoreMember]
573 public virtual string LogId => this.NodeId;
574
580 public abstract Task<string> GetTypeNameAsync(Language Language);
581
585 [IgnoreMember]
586 public bool HasChildren
587 {
588 get
589 {
590 if (!this.childrenLoaded)
591 this.LoadChildren().Wait();
592
593 return !(this.children is null) && this.children.Count > 0;
594 }
595 }
596
600 public virtual bool ChildrenOrdered => false;
601
605 [IgnoreMember]
606 public virtual bool IsReadable => this is ISensor;
607
611 [IgnoreMember]
612 public virtual bool IsControllable => this is IActuator;
613
617 [IgnoreMember]
618 public virtual bool HasCommands => true;
619
623 [IgnoreMember]
624 [Obsolete("Use the asynchronous GetParent() method instead.")]
625 public INode Parent => this.GetParent().Result;
626
632 public async Task<INode> GetParent()
633 {
634 if (!(this.parent is null))
635 return this.parent;
636
637 if (this.parentId == Guid.Empty)
638 return null;
639
640 this.parent = await this.LoadParent();
641 if (this.parent is null)
642 throw new Exception("Parent not found.");
643
644 return this.parent;
645 }
646
651 public async Task<T> GetAncestor<T>()
652 where T : INode
653 {
654 INode Loop = await this.GetParent();
655
656 while (!(Loop is null))
657 {
658 if (Loop is T Ancestor)
659 return Ancestor;
660 else if (Loop is ProcessorNode ProcessorNode)
661 Loop = await ProcessorNode.GetParent();
662 else
663 Loop = Loop.Parent;
664 }
665
666 return default;
667 }
668
672 [IgnoreMember]
673 public DateTime LastChanged
674 {
675 get
676 {
677 if (this.updated == DateTime.MinValue)
678 return this.created;
679 else
680 return this.updated;
681 }
682 }
683
687 [DefaultValue(NodeState.None)]
689 {
690 get => this.state;
691 set => this.state = value;
692 }
693
697 [IgnoreMember]
698 public Task<IEnumerable<INode>> ChildNodes
699 {
700 get
701 {
702 return this.GetChildNodes();
703 }
704 }
705
706 private async Task<IEnumerable<INode>> GetChildNodes()
707 {
708 if (!this.childrenLoaded)
709 await this.LoadChildren();
710
711 lock (this.synchObject)
712 {
713 if (this.children is null)
714 return Array.Empty<INode>();
715 else
716 return this.children.ToArray();
717 }
718 }
719
720 private async Task LoadChildren()
721 {
722 List<ProcessorNode> Children = new List<ProcessorNode>();
723 ProcessorNode[] ToUpdate = null;
724
725 foreach (ProcessorNode Node in await Database.Find<ProcessorNode>(
726 new FilterFieldEqualTo("ParentId", this.objectId)))
727 {
728 Children.Add(ProcessorSource.RegisterNode(Node));
729 }
730
731 lock (this.synchObject)
732 {
733 this.children = null;
734
735 if (Children.Count > 0)
736 {
737 foreach (ProcessorNode Child in Children)
738 Child.parent = this;
739
740 ToUpdate = this.SortChildrenAfterLoadLocked(Children);
741 this.children = Children;
742 }
743
744 this.childrenLoaded = true;
745 }
746
747 if (!(ToUpdate is null))
748 await Database.Update(ToUpdate);
749 }
750
756 protected virtual ProcessorNode[] SortChildrenAfterLoadLocked(List<ProcessorNode> Children)
757 {
758 if (this.ChildrenOrdered)
759 {
760 Children.Sort((n1, n2) => n1.siblingOrdinal.CompareTo(n2.siblingOrdinal));
761 return this.CheckOrderLocked(Children);
762 }
763 else
764 {
765 Children.Sort((n1, n2) => n1.nodeId.CompareTo(n2.nodeId));
766 return null;
767 }
768 }
769
775 protected virtual ProcessorNode[] CheckOrderLocked(List<ProcessorNode> Children)
776 {
777 if (this.ChildrenOrdered)
778 {
779 ChunkedList<ProcessorNode> ToUpdate = null;
780 int Expected = 0;
781
782 foreach (ProcessorNode Child in Children)
783 {
784 if (Child.SiblingOrdinal != Expected)
785 {
786 ToUpdate ??= new ChunkedList<ProcessorNode>();
787 ToUpdate.Add(Child);
788 Child.siblingOrdinal = Expected;
789 }
790
791 Expected++;
792 }
793 return ToUpdate?.ToArray();
794 }
795 else
796 return null;
797 }
798
799 internal async Task<ProcessorNode> LoadParent()
800 {
801 if (!(this.parent is null))
802 return this.parent;
803
804 if (this.parentId == Guid.Empty)
805 return null;
806
807 this.parent = await Database.LoadObject<ProcessorNode>(this.parentId);
808 ProcessorSource.RegisterNode(this.parent);
809
810 return this.parent;
811 }
812
818 public virtual Task<bool> CanViewAsync(RequestOrigin Caller)
819 {
820 return Task.FromResult(Caller.HasPrivilege("Source." + ProcessorSource.SourceID + ".Node.View"));
821 }
822
828 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
829 {
830 return Task.FromResult(Caller.HasPrivilege("Source." + ProcessorSource.SourceID + ".Node.Edit"));
831 }
832
838 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
839 {
840 return Task.FromResult(Caller.HasPrivilege("Source." + ProcessorSource.SourceID + ".Node.Add"));
841 }
842
848 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
849 {
850 return Task.FromResult(Caller.HasPrivilege("Source." + ProcessorSource.SourceID + ".Node.Destroy"));
851 }
852
859 public virtual async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
860 {
863
864 LinkedList<Parameter> Result = new LinkedList<Parameter>();
865 Result.AddLast(new StringParameter("NodeId", await Namespace.GetStringAsync(1, "Processor ID"), this.nodeId));
866 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(4, "Type"), await this.GetTypeNameAsync(Language)));
867
868 if (!(this.parent is null))
869 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(2, "Parent ID"), this.parent.nodeId));
870
871 if (!this.childrenLoaded)
872 await this.LoadChildren();
873
874 if (!(this.children is null))
875 {
876 int i;
877
878 lock (this.synchObject)
879 {
880 i = this.children.Count;
881 }
882
883 Result.AddLast(new Int32Parameter("NrChildren", await Namespace.GetStringAsync(3, "#Children"), i));
884 }
885
886 string s = this.state switch
887 {
888 NodeState.Information => await Namespace.GetStringAsync(8, "Information"),
889 NodeState.WarningUnsigned => await Namespace.GetStringAsync(9, "Unsigned Warning"),
890 NodeState.WarningSigned => await Namespace.GetStringAsync(10, "Warning"),
891 NodeState.ErrorUnsigned => await Namespace.GetStringAsync(11, "Unsigned Error"),
892 NodeState.ErrorSigned => await Namespace.GetStringAsync(12, "Error"),
893 _ => null,
894 };
895
896 if (!string.IsNullOrEmpty(s))
897 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(5, "State"), s));
898
899 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(6, "Created"), this.created));
900
901 if (this.updated != DateTime.MinValue)
902 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(7, "Updated"), this.updated));
903
904 return Result;
905 }
906
913 public async Task<Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
914 {
916 Result.AddRange(await this.GetDisplayableParametersAsync(Language, Caller));
917 return Result.ToArray();
918 }
919
924 public virtual async Task<IEnumerable<Message>> GetMessagesAsync(RequestOrigin Caller)
925 {
926 IEnumerable<ProcessorMessage> Messages = await Database.Find<ProcessorMessage>(
927 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
928 LinkedList<Message> Result = new LinkedList<Message>();
929
930 foreach (ProcessorMessage Msg in Messages)
931 Result.AddLast(new Message(Msg.Created, Msg.Type, Msg.EventId, Msg.Body)); // TODO: Include Updated & Count also.
932
933 return Result;
934 }
935
940 public async Task<Message[]> GetMessageArrayAsync(RequestOrigin Caller)
941 {
942 List<Message> Result = new List<Message>();
943
944 foreach (Message Msg in await this.GetMessagesAsync(Caller))
945 Result.Add(Msg);
946
947 return Result.ToArray();
948 }
949
955 public virtual async Task<bool> MoveUpAsync(RequestOrigin Caller)
956 {
957 if (!(await this.GetParent() is ProcessorNode Parent))
958 return false;
959 else
960 return await Parent.MoveUpAsync(this, Caller);
961 }
962
968 public virtual async Task<bool> MoveDownAsync(RequestOrigin Caller)
969 {
970 if (!(await this.GetParent() is ProcessorNode Parent))
971 return false;
972 else
973 return await Parent.MoveDownAsync(this, Caller);
974 }
975
982 public virtual async Task<bool> MoveUpAsync(ProcessorNode Child, RequestOrigin Caller)
983 {
984 if (!this.ChildrenOrdered)
985 return false;
986
987 if (!this.childrenLoaded)
988 await this.LoadChildren();
989
990 if (this.children is null)
991 return false;
992
993 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
994 return false;
995
996 ProcessorNode Child2;
997
998 lock (this.children)
999 {
1000 int i = this.children.IndexOf(Child);
1001 if (i <= 0)
1002 return false;
1003
1004 Child2 = this.children[i - 1];
1005
1006 this.children.RemoveAt(i);
1007 this.children.Insert(i - 1, Child);
1008
1009 i = Child.siblingOrdinal;
1010 Child.siblingOrdinal = Child2.siblingOrdinal;
1011 Child2.siblingOrdinal = i;
1012 }
1013
1014 await Database.Update(Child, Child2);
1015
1016 await ProcessorSource.NewEvent(new NodeMovedUp()
1017 {
1018 NodeId = Child.NodeId,
1019 Partition = Child.Partition,
1020 SourceId = Child.SourceId,
1021 Timestamp = DateTime.UtcNow
1022 });
1023
1024 return true;
1025 }
1026
1033 public virtual async Task<bool> MoveDownAsync(ProcessorNode Child, RequestOrigin Caller)
1034 {
1035 if (!this.ChildrenOrdered)
1036 return false;
1037
1038 if (!this.childrenLoaded)
1039 await this.LoadChildren();
1040
1041 if (this.children is null)
1042 return false;
1043
1044 if (!await this.CanEditAsync(Caller) || !await Child.CanEditAsync(Caller))
1045 return false;
1046
1047 ProcessorNode Child2;
1048
1049 lock (this.children)
1050 {
1051 int c = this.children.Count;
1052 int i = this.children.IndexOf(Child);
1053 if (i < 0 || i + 1 >= c)
1054 return false;
1055
1056 Child2 = this.children[i + 1];
1057
1058 this.children.RemoveAt(i);
1059 this.children.Insert(i + 1, Child);
1060
1061 i = Child.siblingOrdinal;
1062 Child.siblingOrdinal = Child2.siblingOrdinal;
1063 Child2.siblingOrdinal = i;
1064 }
1065
1066 await Database.Update(Child, Child2);
1067
1068 await ProcessorSource.NewEvent(new NodeMovedDown()
1069 {
1070 NodeId = Child.NodeId,
1071 Partition = Child.Partition,
1072 SourceId = Child.SourceId,
1073 Timestamp = DateTime.UtcNow
1074 });
1075
1076 return true;
1077 }
1078
1084 public abstract Task<bool> AcceptsParentAsync(INode Parent);
1085
1091 public abstract Task<bool> AcceptsChildAsync(INode Child);
1092
1097 public virtual async Task AddAsync(INode Child)
1098 {
1099 if (!(Child is ProcessorNode Node))
1100 throw new Exception("Child must be a processor node.");
1101
1102 if (this.objectId == Guid.Empty)
1103 throw new Exception("Parent node must be persisted before you can add nodes to it.");
1104
1105 if (!this.childrenLoaded)
1106 await this.LoadChildren();
1107
1108 Node.parentId = this.objectId;
1109
1110 ProcessorNode[] ToUpdate;
1111 ProcessorNode After = null;
1112 int c;
1113
1114 lock (this.synchObject)
1115 {
1116 if (this.children is null)
1117 this.children = new List<ProcessorNode>();
1118 else if ((c = this.children.Count) > 0)
1119 After = this.children[c - 1];
1120
1121 ToUpdate = this.CheckOrderLocked(this.children);
1122
1123 Node.siblingOrdinal = this.children.Count;
1124 Node.parent = this;
1125
1126 this.children.Add(Node);
1127 }
1128
1129 if (!(ToUpdate is null))
1130 await Database.Update(ToUpdate);
1131
1132 if (Node.objectId == Guid.Empty)
1133 {
1134 await Database.Insert(Node);
1135 ProcessorSource.RegisterNode(Node);
1136
1138 NodeAdded Event = new NodeAdded()
1139 {
1140 Parameters = await Node.GetDisplayableParameterAraryAsync(Language, RequestOrigin.Empty),
1141 NodeType = Node.GetType().FullName,
1142 Sniffable = false,
1143 DisplayName = await Node.GetTypeNameAsync(Language),
1144 HasChildren = Node.HasChildren,
1145 ChildrenOrdered = Node.ChildrenOrdered,
1146 IsReadable = Node.IsReadable,
1147 IsControllable = Node.IsControllable,
1148 HasCommands = Node.HasCommands,
1149 ParentId = this.NodeId,
1150 ParentPartition = this.Partition,
1151 Updated = Node.Updated,
1152 State = Node.State,
1153 NodeId = Node.NodeId,
1154 Partition = Node.Partition,
1155 LogId = NodeAdded.EmptyIfSame(Node.LogId, Node.NodeId),
1156 LocalId = NodeAdded.EmptyIfSame(Node.LocalId, Node.NodeId),
1157 SourceId = Node.SourceId,
1158 Timestamp = DateTime.UtcNow
1159 };
1160
1161 if (this.ChildrenOrdered && !(After is null))
1162 {
1163 Event.AfterNodeId = After.nodeId;
1164 Event.AfterPartition = After.Partition;
1165 }
1166
1167 await ProcessorSource.NewEvent(Event);
1168 }
1169 else
1170 await Node.NodeUpdated();
1171
1172 await this.RaiseUpdate();
1173 }
1174
1178 protected virtual async Task NodeUpdated()
1179 {
1180 this.updated = DateTime.Now;
1181 await Database.Update(this);
1182
1183 await ProcessorSource.NewEvent(new NodeUpdated()
1184 {
1186 HasChildren = this.HasChildren,
1187 ChildrenOrdered = this.ChildrenOrdered,
1188 IsReadable = this.IsReadable,
1189 IsControllable = this.IsControllable,
1190 HasCommands = this.HasCommands,
1191 ParentId = (await this.GetParent()).NodeId,
1192 ParentPartition = this.Partition,
1193 Updated = this.Updated,
1194 State = this.State,
1195 NodeId = this.NodeId,
1196 OldId = this.oldId,
1197 Partition = this.Partition,
1198 LogId = NodeAdded.EmptyIfSame(this.LogId, this.NodeId),
1199 LocalId = NodeAdded.EmptyIfSame(this.LocalId, this.NodeId),
1200 SourceId = this.SourceId,
1201 Timestamp = DateTime.UtcNow
1202 });
1203
1204 if (this.oldId != this.nodeId)
1205 {
1206 ProcessorSource.RegisterNewNodeId(this, this.oldId);
1207 this.oldId = this.nodeId;
1208 }
1209 }
1210
1214 public virtual async Task UpdateAsync()
1215 {
1216 if (this.objectId != Guid.Empty)
1217 await this.NodeUpdated();
1218
1219 await this.RaiseUpdate();
1220 }
1221
1227 public virtual async Task<bool> RemoveAsync(INode Child)
1228 {
1229 if (!(Child is ProcessorNode Node))
1230 throw new Exception("Child must be a processor node.");
1231
1232 if (!this.childrenLoaded)
1233 await this.LoadChildren();
1234
1235 ProcessorNode[] ToUpdate = null;
1236 int i;
1237
1238 lock (this.synchObject)
1239 {
1240 if (!(this.children is null))
1241 {
1242 i = this.children.IndexOf(Node);
1243 if (i >= 0)
1244 {
1245 this.children.RemoveAt(i);
1246 if (i == 0 && this.children.Count == 0)
1247 this.children = null;
1248 else
1249 ToUpdate = this.CheckOrderLocked(this.children);
1250 }
1251 }
1252 else
1253 i = -1;
1254 }
1255
1256 if (!(ToUpdate is null))
1257 await Database.Update(ToUpdate);
1258
1259 Node.parentId = Guid.Empty;
1260 Node.parent = null;
1261
1262 if (Node.objectId != Guid.Empty)
1263 {
1264 await Database.Update(Child);
1265 await this.RaiseUpdate();
1266
1267 await ProcessorSource.NewEvent(new NodeRemoved()
1268 {
1269 NodeId = Node.NodeId,
1270 Partition = Node.Partition,
1271 SourceId = Node.SourceId,
1272 Timestamp = DateTime.UtcNow
1273 });
1274 }
1275
1276 return i >= 0;
1277 }
1278
1282 public async virtual Task DestroyAsync()
1283 {
1284 if (!(await this.GetParent() is null))
1285 {
1286 if (this.parent.childrenLoaded)
1287 {
1288 ProcessorNode[] ToUpdate = null;
1289
1290 lock (this.parent.synchObject)
1291 {
1292 if (!(this.parent.children is null))
1293 {
1294 if (this.parent.children.Remove(this))
1295 {
1296 if (this.parent.children.Count == 0)
1297 this.parent.children = null;
1298 else
1299 ToUpdate = this.parent.CheckOrderLocked(this.parent.children);
1300 }
1301 }
1302 }
1303
1304 if (!(ToUpdate is null))
1305 await Database.Update(ToUpdate);
1306 }
1307 }
1308
1309 if (!this.childrenLoaded)
1310 await this.LoadChildren();
1311
1312 if (!(this.children is null))
1313 {
1314 List<ProcessorNode> Children = this.children;
1315 this.children = null;
1316
1317 foreach (ProcessorNode Child in Children)
1318 {
1319 Child.parent = null;
1320 Child.parentId = Guid.Empty;
1321
1322 await Child.DestroyAsync();
1323 }
1324
1325 this.children = null;
1326 }
1327
1328 if (this.objectId != Guid.Empty)
1329 {
1330 await Database.Delete(this);
1331 this.objectId = Guid.Empty;
1332 }
1333
1334 ProcessorSource.UnregisterNode(this);
1335 }
1336
1340 [IgnoreMember]
1341 public virtual Task<IEnumerable<ICommand>> Commands
1342 {
1343 get
1344 {
1345 return Task.FromResult<IEnumerable<ICommand>>(new ICommand[]
1346 {
1347 new ClearMessages(this),
1348 new LogMessage(this)
1349 });
1350 }
1351 }
1352
1353 #endregion
1354 }
1355}
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.
Clears all messages for a processor.
Logs a message on a processor.
Definition: LogMessage.cs:14
Defines a message logged on a processor node.
DateTime Created
When node was created.
string EventId
Optional Event ID.
Base class for all processor nodes.
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
string SourceId
Optional ID of source containing node.
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
NodeState State
Current overall state of the node.
DateTime Created
When node was created.
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
DateTime LastChanged
When the node was last updated.
virtual ProcessorNode[] SortChildrenAfterLoadLocked(List< ProcessorNode > Children)
Method that allows the node to sort its children, after they have been loaded.
virtual async Task< bool > MoveUpAsync(ProcessorNode Child, RequestOrigin Caller)
Tries to move the child node up.
virtual bool HasCommands
If the node has registered commands or not.
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
virtual bool IsReadable
If the node can be read.
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
static async Task< string > GetUniqueNodeId(string ProcessorId)
Gets a Processor ID, based on ProcessorId that is not already available in the database.
INode Parent
Parent Node, or null if a root node.
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
async Task< INode > GetParent()
Gets the parent of the node.
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
override int GetHashCode()
Serves as the default hash function.
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
ProcessorNode()
Base class for all processor nodes.
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual bool IsControllable
If the node can be controlled.
Guid ObjectId
Object ID in persistence layer.
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from 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,...
bool HasChildren
If the source has any child sources.
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
Guid ParentId
Object ID of parent node in persistence layer.
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
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 Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
virtual async Task< bool > MoveDownAsync(ProcessorNode Child, RequestOrigin Caller)
Tries to move the child node down.
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on 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...
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
string Partition
Optional partition in which the Node ID is unique.
string OldId
Previous ID of node (during update).
EventHandlerAsync OnUpdate
Event raised when node has been updated.
virtual ProcessorNode[] CheckOrderLocked(List< ProcessorNode > Children)
Checks the ordering of children.
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
Defines the Processors data source. This data source contains a tree structure of processor of nodes
const string SourceID
Source ID for the processors data source.
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 processor nodes.
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