Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
JobNode.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
14using Waher.Things;
18
19namespace Waher.Jobs
20{
24 [CollectionName("Jobs")]
25 [TypeName(TypeNameSerialization.FullName)]
26 [ArchivingTime]
27 [Index("NodeId")]
28 [Index("ParentId", "NodeId")]
29 public abstract class JobNode : IJobNode
30 {
31 private Guid objectId = Guid.Empty;
32 private Guid parentId = Guid.Empty;
33 private JobNode parent = null;
34 private string nodeId = string.Empty;
35 private string oldId = null;
36 private NodeState state = NodeState.None;
37 private List<JobNode> children = null;
38 private int siblingOrdinal = 0;
39 private bool childrenLoaded = false;
40 private readonly object synchObject = new object();
41 private DateTime created = DateTime.Now;
42 private DateTime updated = DateTime.MinValue;
43 private ThingReference thingReference = null;
44
48 public JobNode()
49 {
50 }
51
56 public static implicit operator ThingReference(JobNode Node)
57 {
58 Node.thingReference ??= new ThingReference(Node.nodeId, Node.SourceId, Node.Partition);
59
60 return Node.thingReference;
61 }
62
68 public override bool Equals(object obj)
69 {
70 if (!(obj is IThingReference Ref))
71 return false;
72 else
73 return this.nodeId == Ref.NodeId && this.SourceId == Ref.SourceId && this.Partition == Ref.Partition;
74 }
75
80 public override int GetHashCode()
81 {
82 return this.nodeId.GetHashCode() ^
83 this.SourceId.GetHashCode() ^
84 this.Partition.GetHashCode();
85 }
86
90 [ObjectId]
91 public Guid ObjectId
92 {
93 get => this.objectId;
94 set => this.objectId = value;
95 }
96
100 public Guid ParentId
101 {
102 get => this.parentId;
103 set => this.parentId = value;
104 }
105
109 public DateTime Created
110 {
111 get => this.created;
112 set => this.created = value;
113 }
114
118 [DefaultValueDateTimeMinValue]
119 public DateTime Updated
120 {
121 get => this.updated;
122 set => this.updated = value;
123 }
124
128 [DefaultValue(0)]
129 public int SiblingOrdinal
130 {
131 get => this.siblingOrdinal;
132 set => this.siblingOrdinal = value;
133 }
134
138 [Header(2, "Job ID:", 0)]
139 [Page(3, "Job", 0)]
140 [ToolTip(4, "Job identity in the collection of jobs.")]
141 [Required]
142 public string NodeId
143 {
144 get => this.nodeId;
145 set
146 {
147 this.nodeId = value;
148 this.thingReference = null;
149
150 if (this.oldId is null && !string.IsNullOrEmpty(value))
151 this.oldId = value;
152 }
153 }
154
158 [IgnoreMember]
159 public string SourceId => JobSource.SourceID;
160
164 [IgnoreMember]
165 public string Partition => string.Empty;
166
168 public override string ToString()
169 {
170 StringBuilder sb = new StringBuilder();
172
173 sb.Append(this.nodeId);
174 sb.Append(" (");
175 sb.Append(this.GetTypeNameAsync(Language).Result);
176 sb.Append(")");
177
179 {
180 sb.Append(", ");
181 sb.Append(P.Name);
182 sb.Append("=");
183 sb.Append(P.StringValue);
184 }
185
186 return sb.ToString();
187 }
188
193 public virtual Task LogErrorAsync(string Body)
194 {
195 return this.LogMessageAsync(MessageType.Error, string.Empty, Body);
196 }
197
203 public virtual Task LogErrorAsync(string EventId, string Body)
204 {
205 return this.LogMessageAsync(MessageType.Error, EventId, Body);
206 }
207
212 public virtual Task LogWarningAsync(string Body)
213 {
214 return this.LogMessageAsync(MessageType.Warning, string.Empty, Body);
215 }
216
222 public virtual Task LogWarningAsync(string EventId, string Body)
223 {
224 return this.LogMessageAsync(MessageType.Warning, EventId, Body);
225 }
226
231 public virtual Task LogInformationAsync(string Body)
232 {
233 return this.LogMessageAsync(MessageType.Information, string.Empty, Body);
234 }
235
241 public virtual Task LogInformationAsync(string EventId, string Body)
242 {
243 return this.LogMessageAsync(MessageType.Information, EventId, Body);
244 }
245
251 public virtual Task LogMessageAsync(MessageType Type, string Body)
252 {
253 return this.LogMessageAsync(Type, string.Empty, Body);
254 }
255
262 public virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
263 {
264 if (this.objectId == Guid.Empty)
265 return;
266
267 bool Updated = false;
268
269 foreach (JobMessage Message in await Database.Find<JobMessage>(new FilterAnd(
270 new FilterFieldEqualTo("NodeId", this.objectId),
271 new FilterFieldEqualTo("Type", Type),
272 new FilterFieldEqualTo("EventId", EventId),
273 new FilterFieldEqualTo("Body", Body))))
274 {
275 Message.Updated = DateTime.Now;
276 Message.Count++;
277
278 await Database.Update(Message);
279 Updated = true;
280
281 break;
282 }
283
284 if (!Updated)
285 {
286 JobMessage Msg = new JobMessage(this.objectId, DateTime.Now, Type, EventId, Body)
287 {
288 NodeId = this.objectId
289 };
290
291 await Database.Insert(Msg);
292 }
293
294 switch (Type)
295 {
296 case MessageType.Error:
297 if (this.state < NodeState.ErrorUnsigned)
298 {
299 this.state = NodeState.ErrorUnsigned;
300 await Database.Update(this);
301 await this.RaiseUpdate();
302 }
303 break;
304
305 case MessageType.Warning:
306 if (this.state < NodeState.WarningUnsigned)
307 {
308 this.state = NodeState.WarningUnsigned;
309 await Database.Update(this);
310 await this.RaiseUpdate();
311 }
312 break;
313
314 case MessageType.Information:
315 if (this.state < NodeState.Information)
316 {
317 this.state = NodeState.Information;
318 await Database.Update(this);
319 await this.RaiseUpdate();
320 }
321 break;
322 }
323
324 switch (Type)
325 {
326 case MessageType.Information:
327 Log.Informational(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
328 break;
329
330 case MessageType.Warning:
331 Log.Warning(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
332 break;
333
334 case MessageType.Error:
335 Log.Error(Body, this.nodeId, string.Empty, EventId, EventLevel.Minor);
336 break;
337 }
338
339 await this.NodeStateChanged();
340 }
341
342 internal async Task NodeStateChanged()
343 {
344 await JobSource.NewEvent(new NodeStatusChanged()
345 {
346 Messages = await this.GetMessageArrayAsync(RequestOrigin.Empty),
347 State = this.state,
348 NodeId = this.NodeId,
349 Partition = this.Partition,
350 SourceId = this.SourceId,
351 Timestamp = DateTime.UtcNow
352 });
353 }
354
358 public virtual Task<bool> RemoveErrorAsync()
359 {
360 return this.RemoveMessageAsync(MessageType.Error, string.Empty);
361 }
362
367 public virtual Task<bool> RemoveErrorAsync(string EventId)
368 {
369 return this.RemoveMessageAsync(MessageType.Error, EventId);
370 }
371
375 public virtual Task<bool> RemoveWarningAsync()
376 {
377 return this.RemoveMessageAsync(MessageType.Warning, string.Empty);
378 }
379
384 public virtual Task<bool> RemoveWarningAsync(string EventId)
385 {
386 return this.RemoveMessageAsync(MessageType.Warning, EventId);
387 }
388
392 public virtual Task<bool> RemoveInformationAsync()
393 {
394 return this.RemoveMessageAsync(MessageType.Information, string.Empty);
395 }
396
401 public virtual Task<bool> RemoveInformationAsync(string EventId)
402 {
403 return this.RemoveMessageAsync(MessageType.Information, EventId);
404 }
405
410 public virtual Task<bool> RemoveMessageAsync(MessageType Type)
411 {
412 return this.RemoveMessageAsync(Type, string.Empty);
413 }
414
420 public virtual async Task<bool> RemoveMessageAsync(MessageType Type, string EventId)
421 {
422 if (this.objectId == Guid.Empty)
423 return false;
424
425 bool Removed = false;
426
428 new FilterFieldEqualTo("NodeId", this.objectId),
429 new FilterFieldEqualTo("Type", Type),
430 new FilterFieldEqualTo("EventId", EventId))))
431 {
432 Removed = true;
433
434 switch (Type)
435 {
436 case MessageType.Error:
437 Log.Informational("Error removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
438 break;
439
440 case MessageType.Warning:
441 Log.Informational("Warning removed: " + Message.Body, this.nodeId, string.Empty, string.Empty, EventLevel.Minor);
442 break;
443 }
444 }
445
446 if (Removed)
447 {
448 bool ErrorsFound = false;
449 bool WarningsFound = false;
450 bool InformationFound = false;
451
452 foreach (JobMessage Message in await Database.Find<JobMessage>(new FilterFieldEqualTo("NodeId", this.objectId)))
453 {
454 switch (Type)
455 {
456 case MessageType.Error:
457 ErrorsFound = true;
458 break;
459
460 case MessageType.Warning:
461 WarningsFound = true;
462 break;
463
464 case MessageType.Information:
465 InformationFound = true;
466 break;
467 }
468 }
469
470 NodeState NewStateSigned;
471 NodeState NewStateUnsigned;
472
473 if (ErrorsFound)
474 {
475 NewStateSigned = NodeState.ErrorSigned;
476 NewStateUnsigned = NodeState.ErrorUnsigned;
477 }
478 else if (WarningsFound)
479 {
480 NewStateSigned = NodeState.WarningSigned;
481 NewStateUnsigned = NodeState.WarningUnsigned;
482 }
483 else if (InformationFound)
484 {
485 NewStateSigned = NodeState.Information;
486 NewStateUnsigned = NodeState.Information;
487 }
488 else
489 {
490 NewStateSigned = NodeState.None;
491 NewStateUnsigned = NodeState.None;
492 }
493
494 switch (this.state)
495 {
496 case NodeState.ErrorSigned:
497 case NodeState.WarningSigned:
498 if (this.state != NewStateSigned)
499 {
500 this.state = NewStateSigned;
501 await Database.Update(this);
502 await this.RaiseUpdate();
503 }
504 break;
505
506 default:
507 if (this.state != NewStateUnsigned)
508 {
509 this.state = NewStateUnsigned;
510 await Database.Update(this);
511 await this.RaiseUpdate();
512 }
513 break;
514 }
515
516 await this.NodeStateChanged();
517 }
518
519 return Removed;
520 }
521
525 public event EventHandlerAsync OnUpdate = null;
526
527 internal Task RaiseUpdate()
528 {
529 return this.OnUpdate.Raise(this, EventArgs.Empty);
530 }
531
537 public static async Task<string> GetUniqueJobId(string JobId)
538 {
539 using Semaphore Semaphore = await Semaphores.BeginWrite("Jobs." + JobId);
540 string Suffix = string.Empty;
541 string s;
542 int i = 1;
543
544 while (true)
545 {
546 if (await Database.FindFirstIgnoreRest<JobNode>(
547 new FilterFieldEqualTo("NodeId", s = JobId + 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 JobNode JobNode)
657 Loop = await JobNode.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<JobNode> Children = new List<JobNode>();
719 JobNode[] ToUpdate = null;
720
721 foreach (JobNode Node in await Database.Find<JobNode>(
722 new FilterFieldEqualTo("ParentId", this.objectId)))
723 {
724 Children.Add(JobSource.RegisterNode(Node));
725 }
726
727 lock (this.synchObject)
728 {
729 this.children = null;
730
731 if (Children.Count > 0)
732 {
733 foreach (JobNode 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 JobNode[] SortChildrenAfterLoadLocked(List<JobNode> 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 JobNode[] CheckOrderLocked(List<JobNode> Children)
772 {
773 if (this.ChildrenOrdered)
774 {
775 ChunkedList<JobNode> ToUpdate = null;
776 int Expected = 0;
777
778 foreach (JobNode Child in Children)
779 {
780 if (Child.SiblingOrdinal != Expected)
781 {
782 ToUpdate ??= new ChunkedList<JobNode>();
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<JobNode> 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<JobNode>(this.parentId);
804 JobSource.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." + JobSource.SourceID + ".Node.View"));
817 }
818
824 public virtual Task<bool> CanEditAsync(RequestOrigin Caller)
825 {
826 return Task.FromResult(Caller.HasPrivilege("Source." + JobSource.SourceID + ".Node.Edit"));
827 }
828
834 public virtual Task<bool> CanAddAsync(RequestOrigin Caller)
835 {
836 return Task.FromResult(Caller.HasPrivilege("Source." + JobSource.SourceID + ".Node.Add"));
837 }
838
844 public virtual Task<bool> CanDestroyAsync(RequestOrigin Caller)
845 {
846 return Task.FromResult(Caller.HasPrivilege("Source." + JobSource.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(13, "Job ID"), this.nodeId));
862 Result.AddLast(new StringParameter("Type", await Namespace.GetStringAsync(14, "Type"), await this.GetTypeNameAsync(Language)));
863
864 if (!(this.parent is null))
865 Result.AddLast(new StringParameter("ParentId", await Namespace.GetStringAsync(15, "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(16, "#Children"), i));
880 }
881
882 string s = this.state switch
883 {
884 NodeState.Information => await Namespace.GetStringAsync(5, "Information"),
885 NodeState.WarningUnsigned => await Namespace.GetStringAsync(6, "Unsigned Warning"),
886 NodeState.WarningSigned => await Namespace.GetStringAsync(7, "Warning"),
887 NodeState.ErrorUnsigned => await Namespace.GetStringAsync(8, "Unsigned Error"),
888 NodeState.ErrorSigned => await Namespace.GetStringAsync(9, "Error"),
889 _ => null,
890 };
891
892 if (!string.IsNullOrEmpty(s))
893 Result.AddLast(new StringParameter("State", await Namespace.GetStringAsync(10, "State"), s));
894
895 Result.AddLast(new DateTimeParameter("Created", await Namespace.GetStringAsync(11, "Created"), this.created));
896
897 if (this.updated != DateTime.MinValue)
898 Result.AddLast(new DateTimeParameter("Updated", await Namespace.GetStringAsync(12, "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<JobMessage> Messages = await Database.Find<JobMessage>(
923 new FilterFieldEqualTo("NodeId", this.objectId), "Created");
924 LinkedList<Message> Result = new LinkedList<Message>();
925
926 foreach (JobMessage 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 JobNode 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 JobNode Parent))
967 return false;
968 else
969 return await Parent.MoveDownAsync(this, Caller);
970 }
971
978 public virtual async Task<bool> MoveUpAsync(JobNode 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 JobNode 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 JobSource.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(JobNode 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 JobNode 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 JobSource.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 JobNode Node))
1096 throw new Exception("Child must be a job 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 JobNode[] ToUpdate;
1107 JobNode After = null;
1108 int c;
1109
1110 lock (this.synchObject)
1111 {
1112 if (this.children is null)
1113 this.children = new List<JobNode>();
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 JobSource.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 JobSource.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 JobSource.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 JobSource.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 JobNode Node))
1226 throw new Exception("Child must be a job node.");
1227
1228 if (!this.childrenLoaded)
1229 await this.LoadChildren();
1230
1231 JobNode[] 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 JobSource.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 JobNode[] 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<JobNode> Children = this.children;
1311 this.children = null;
1312
1313 foreach (JobNode 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 JobSource.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
1355 public virtual Task AddedToRoot(Root Root)
1356 {
1357 return Task.CompletedTask;
1358 }
1359
1364 public virtual Task RemovedFromRoot(Root Root)
1365 {
1366 return Task.CompletedTask;
1367 }
1368 }
1369}
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 job.
Logs a message on a job.
Definition: LogMessage.cs:14
Defines a message logged on a job node.
Definition: JobMessage.cs:15
string EventId
Optional Event ID.
Definition: JobMessage.cs:113
DateTime Created
When node was created.
Definition: JobMessage.cs:74
MessageType Type
Message Type
Definition: JobMessage.cs:103
string Body
Message body.
Definition: JobMessage.cs:123
Base class for all job nodes.
Definition: JobNode.cs:30
INode Parent
Parent Node, or null if a root node.
Definition: JobNode.cs:621
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
Definition: JobNode.cs:1210
NodeState State
Current overall state of the node.
Definition: JobNode.cs:685
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: JobNode.cs:695
bool HasChildren
If the source has any child sources.
Definition: JobNode.cs:583
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
Definition: JobNode.cs:563
virtual bool HasCommands
If the node has registered commands or not.
Definition: JobNode.cs:614
virtual bool IsReadable
If the node can be read.
Definition: JobNode.cs:602
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
Definition: JobNode.cs:814
Guid ObjectId
Object ID in persistence layer.
Definition: JobNode.cs:92
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
Definition: JobNode.cs:1174
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: JobNode.cs:855
string Partition
Optional partition in which the Node ID is unique.
Definition: JobNode.cs:165
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
Definition: JobNode.cs:384
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
Definition: JobNode.cs:120
async Task< INode > GetParent()
Gets the parent of the node.
Definition: JobNode.cs:628
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
Definition: JobNode.cs:951
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
Definition: JobNode.cs:1093
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
Definition: JobNode.cs:367
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
Definition: JobNode.cs:1223
string SourceId
Optional ID of source containing node.
Definition: JobNode.cs:159
static async Task< string > GetUniqueJobId(string JobId)
Gets a Job ID, based on JobId that is not already available in the database.
Definition: JobNode.cs:537
Guid ParentId
Object ID of parent node in persistence layer.
Definition: JobNode.cs:101
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
Definition: JobNode.cs:647
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
Definition: JobNode.cs:569
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
Definition: JobNode.cs:1278
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
Definition: JobNode.cs:401
override string ToString()
Definition: JobNode.cs:168
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 LastChanged
When the node was last updated.
Definition: JobNode.cs:670
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
Definition: JobNode.cs:964
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
Definition: JobNode.cs:251
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 LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
Definition: JobNode.cs:262
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
Definition: JobNode.cs:1338
virtual async Task< bool > MoveUpAsync(JobNode Child, RequestOrigin Caller)
Tries to move the child node up.
Definition: JobNode.cs:978
virtual Task RemovedFromRoot(Root Root)
Node has been removed from the root node.
Definition: JobNode.cs:1364
virtual bool IsControllable
If the node can be controlled.
Definition: JobNode.cs:608
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
Definition: JobNode.cs:212
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
Definition: JobNode.cs:193
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
Definition: JobNode.cs:231
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
Definition: JobNode.cs:203
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
Definition: JobNode.cs:420
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
Definition: JobNode.cs:241
DateTime Created
When node was created.
Definition: JobNode.cs:110
override int GetHashCode()
Serves as the default hash function.
Definition: JobNode.cs:80
virtual JobNode[] SortChildrenAfterLoadLocked(List< JobNode > Children)
Method that allows the node to sort its children, after they have been loaded.
Definition: JobNode.cs:752
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from the node.
Definition: JobNode.cs:410
virtual Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
Definition: JobNode.cs:824
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: JobNode.cs:936
JobNode()
Base class for all job nodes.
Definition: JobNode.cs:48
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
Definition: JobNode.cs:834
virtual async Task< bool > MoveDownAsync(JobNode Child, RequestOrigin Caller)
Tries to move the child node down.
Definition: JobNode.cs:1029
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
Definition: JobNode.cs:68
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
Definition: JobNode.cs:358
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Definition: JobNode.cs:596
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
Definition: JobNode.cs:844
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: JobNode.cs:909
string NodeId
ID of node.
Definition: JobNode.cs:143
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
Definition: JobNode.cs:222
EventHandlerAsync OnUpdate
Event raised when node has been updated.
Definition: JobNode.cs:525
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
Definition: JobNode.cs:920
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
Definition: JobNode.cs:130
virtual Task AddedToRoot(Root Root)
Node has been added to the root node.
Definition: JobNode.cs:1355
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
Definition: JobNode.cs:375
virtual JobNode[] CheckOrderLocked(List< JobNode > Children)
Checks the ordering of children.
Definition: JobNode.cs:771
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
Definition: JobNode.cs:392
Defines the Jobs data source. This data source contains a tree structure of jobs of nodes
Definition: JobSource.cs:20
const string SourceID
Source ID for the jobs data source.
Definition: JobSource.cs:24
Class for the root node of the Jobs data source.
Definition: Root.cs:11
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 job nodes.
Definition: IJobNode.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