4using System.Threading.Tasks;
24 [CollectionName(
"MeteringTopology")]
28 [Index(
"ParentId",
"NodeId")]
31 private Guid objectId = Guid.Empty;
32 private Guid parentId = Guid.Empty;
34 private string nodeId =
string.Empty;
35 private string oldId =
null;
37 private List<MeteringNode> children =
null;
38 private int siblingOrdinal = 0;
39 private bool childrenLoaded =
false;
40 private readonly
object synchObject =
new object();
41 private DateTime created = DateTime.Now;
42 private DateTime updated = DateTime.MinValue;
44 private bool disabled =
false;
61 return Node.thingReference;
69 public override bool Equals(
object obj)
74 return this.nodeId == Ref.NodeId && this.SourceId == Ref.SourceId && this.Partition == Ref.Partition;
83 return this.nodeId.GetHashCode() ^
95 set => this.objectId = value;
103 get => this.parentId;
104 set => this.parentId = value;
113 set => this.created = value;
119 [DefaultValueDateTimeMinValue]
123 set => this.updated = value;
132 get => this.siblingOrdinal;
133 set => this.siblingOrdinal = value;
139 [Header(15,
"ID:", 0)]
140 [Page(16,
"Identity", 0)]
141 [ToolTip(17,
"Node identity on the network.")]
149 this.thingReference =
null;
151 if (this.oldId is
null && !
string.IsNullOrEmpty(value))
159 [Header(109,
"Disabled node.", 0)]
160 [Page(16,
"Identity", 0)]
161 [ToolTip(110,
"If checked, node is disabled for readout or control.")]
164 get => this.disabled;
165 set => this.disabled = value;
183 StringBuilder sb =
new StringBuilder();
186 sb.Append(this.nodeId);
199 return sb.ToString();
277 if (this.objectId == Guid.Empty)
288 Message.Updated = DateTime.Now;
310 if (this.state <
NodeState.ErrorUnsigned)
314 await this.RaiseUpdate();
319 if (this.state <
NodeState.WarningUnsigned)
323 await this.RaiseUpdate();
332 await this.RaiseUpdate();
352 await this.NodeStateChanged();
355 internal async Task NodeStateChanged()
364 Timestamp = DateTime.UtcNow
435 if (this.objectId == Guid.Empty)
461 bool ErrorsFound =
false;
462 bool WarningsFound =
false;
463 bool InformationFound =
false;
474 WarningsFound =
true;
478 InformationFound =
true;
489 NewStateUnsigned =
NodeState.ErrorUnsigned;
491 else if (WarningsFound)
493 NewStateSigned =
NodeState.WarningSigned;
494 NewStateUnsigned =
NodeState.WarningUnsigned;
496 else if (InformationFound)
499 NewStateUnsigned =
NodeState.Information;
511 if (this.state != NewStateSigned)
513 this.state = NewStateSigned;
515 await this.RaiseUpdate();
520 if (this.state != NewStateUnsigned)
522 this.state = NewStateUnsigned;
524 await this.RaiseUpdate();
529 await this.NodeStateChanged();
540 internal Task RaiseUpdate()
542 return this.OnUpdate.Raise(
this, EventArgs.Empty);
553 string Suffix =
string.Empty;
566 Suffix =
" (" + i.ToString() +
")";
599 if (!this.childrenLoaded)
600 this.LoadChildren().Wait();
602 return !(this.children is
null) && this.children.Count > 0;
633 [Obsolete(
"Use the asynchronous GetParent() method instead.")]
643 if (!(this.parent is
null))
646 if (this.parentId == Guid.Empty)
649 this.parent = await this.LoadParent();
650 if (this.parent is
null)
651 throw new Exception(
"Parent not found.");
665 while (!(Loop is
null))
667 if (Loop is T Ancestor)
686 if (this.updated == DateTime.MinValue)
700 set => this.state = value;
711 return this.GetChildNodes();
715 private async Task<IEnumerable<INode>> GetChildNodes()
717 if (!this.childrenLoaded)
718 await this.LoadChildren();
720 lock (this.synchObject)
722 if (this.children is
null)
723 return Array.Empty<
INode>();
725 return this.children.ToArray();
729 private async Task LoadChildren()
731 List<MeteringNode> Children =
new List<MeteringNode>();
740 lock (this.synchObject)
742 this.children =
null;
744 if (Children.Count > 0)
750 this.children = Children;
753 this.childrenLoaded =
true;
756 if (!(ToUpdate is
null))
767 if (this.ChildrenOrdered)
769 Children.Sort((n1, n2) => n1.siblingOrdinal.CompareTo(n2.siblingOrdinal));
774 Children.Sort((n1, n2) => n1.nodeId.CompareTo(n2.nodeId));
786 if (this.ChildrenOrdered)
797 Child.siblingOrdinal = Expected;
802 return ToUpdate?.ToArray();
808 internal async Task<MeteringNode> LoadParent()
810 if (!(this.parent is
null))
813 if (this.parentId == Guid.Empty)
873 LinkedList<Parameter> Result =
new LinkedList<Parameter>();
877 if (!(this.parent is
null))
883 if (!this.childrenLoaded)
884 await this.LoadChildren();
886 if (!(this.children is
null))
890 lock (this.synchObject)
892 i = this.children.Count;
898 string s = this.state
switch
908 if (!
string.IsNullOrEmpty(s))
913 if (this.updated != DateTime.MinValue)
940 LinkedList<Message> Result =
new LinkedList<Message>();
954 List<Message> Result =
new List<Message>();
959 return Result.ToArray();
996 if (!this.ChildrenOrdered)
999 if (!this.childrenLoaded)
1000 await this.LoadChildren();
1002 if (this.children is
null)
1010 lock (this.children)
1012 int i = this.children.IndexOf(Child);
1016 Child2 = this.children[i - 1];
1018 this.children.RemoveAt(i);
1019 this.children.Insert(i - 1, Child);
1021 i = Child.siblingOrdinal;
1022 Child.siblingOrdinal = Child2.siblingOrdinal;
1023 Child2.siblingOrdinal = i;
1033 Timestamp = DateTime.UtcNow
1047 if (!this.ChildrenOrdered)
1050 if (!this.childrenLoaded)
1051 await this.LoadChildren();
1053 if (this.children is
null)
1061 lock (this.children)
1063 int c = this.children.Count;
1064 int i = this.children.IndexOf(Child);
1065 if (i < 0 || i + 1 >= c)
1068 Child2 = this.children[i + 1];
1070 this.children.RemoveAt(i);
1071 this.children.Insert(i + 1, Child);
1073 i = Child.siblingOrdinal;
1074 Child.siblingOrdinal = Child2.siblingOrdinal;
1075 Child2.siblingOrdinal = i;
1085 Timestamp = DateTime.UtcNow
1112 throw new Exception(
"Child must be a metering node.");
1114 if (this.objectId == Guid.Empty)
1115 throw new Exception(
"Parent node must be persisted before you can add nodes to it.");
1117 if (!this.childrenLoaded)
1118 await this.LoadChildren();
1120 Node.parentId = this.objectId;
1126 lock (this.synchObject)
1128 if (this.children is
null)
1129 this.children =
new List<MeteringNode>();
1130 else if ((c = this.children.Count) > 0)
1131 After = this.children[c - 1];
1135 Node.siblingOrdinal = this.children.Count;
1138 this.children.Add(Node);
1141 if (!(ToUpdate is
null))
1144 if (Node.objectId == Guid.Empty)
1153 NodeType = Node.GetType().FullName,
1155 DisplayName = await Node.GetTypeNameAsync(
Language),
1170 Timestamp = DateTime.UtcNow
1173 if (this.ChildrenOrdered && !(After is
null))
1175 Event.AfterNodeId = After.nodeId;
1182 await Node.NodeUpdated();
1184 await this.RaiseUpdate();
1192 this.updated = DateTime.Now;
1204 ParentPartition = this.Partition,
1213 Timestamp = DateTime.UtcNow
1216 if (this.oldId != this.nodeId)
1219 this.oldId = this.nodeId;
1228 if (this.objectId != Guid.Empty)
1231 await this.RaiseUpdate();
1242 throw new Exception(
"Child must be a metering node.");
1244 if (!this.childrenLoaded)
1245 await this.LoadChildren();
1250 lock (this.synchObject)
1252 if (!(this.children is
null))
1254 i = this.children.IndexOf(Node);
1257 this.children.RemoveAt(i);
1258 if (i == 0 && this.children.Count == 0)
1259 this.children =
null;
1268 if (!(ToUpdate is
null))
1271 Node.parentId = Guid.Empty;
1274 if (Node.objectId != Guid.Empty)
1277 await this.RaiseUpdate();
1284 Timestamp = DateTime.UtcNow
1298 if (this.parent.childrenLoaded)
1302 lock (this.parent.synchObject)
1304 if (!(this.parent.children is
null))
1306 if (this.parent.children.Remove(
this))
1308 if (this.parent.children.Count == 0)
1309 this.parent.children =
null;
1316 if (!(ToUpdate is
null))
1321 if (!this.childrenLoaded)
1322 await this.LoadChildren();
1324 if (!(this.children is
null))
1326 List<MeteringNode> Children = this.children;
1327 this.children =
null;
1331 Child.parent =
null;
1332 Child.parentId = Guid.Empty;
1338 if (this.objectId != Guid.Empty)
1341 this.objectId = Guid.Empty;
1355 return Task.FromResult<IEnumerable<ICommand>>(
new ICommand[]
1365 #region ILifeCycleManagement
1375 public virtual string Owner =>
string.Empty;
1388 return Task.FromResult<KeyValuePair<string, object>[]>(Array.Empty<KeyValuePair<string, object>>());
1398 throw new NotSupportedException();
1406 throw new NotSupportedException();
1414 throw new NotSupportedException();
1419 #region Momentary values
Class representing an event.
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
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.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static async Task< object > LoadObject(string CollectionName, object ObjectId)
Loads an object given its Object ID ObjectId and its collection name CollectionName .
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
This filter selects objects that conform to all child-filters provided.
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 .
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.
async Task< Namespace > GetNamespaceAsync(string Name)
Gets the namespace object, given its name, if available.
async Task< Namespace > CreateNamespaceAsync(string Name)
Creates a new language namespace, or updates an existing language namespace, if one exist with the sa...
Contains information about a namespace in a language.
Task< LanguageString > GetStringAsync(int Id)
Gets the string object, given its ID, if available.
Basic access point for runtime language localization.
static async Task< Language > GetDefaultLanguageAsync()
Gets the default language.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Static class of application-wide semaphores that can be used to order access to editable objects.
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...
Boolean-valued parameter.
DateTime-valued parameter.
Contains information about a message logged on a node.
Base class for all node parameters.
string Name
Parameter Name.
virtual object StringValue
String representation of parameter value
Clears all messages for a node.
Logs a message on a node.
Defines a message logged on a metering node.
MessageType Type
Message Type
DateTime Created
When node was created.
string EventId
Optional Event ID.
Base class for all metering nodes.
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
virtual bool IsControllable
If the node can be controlled.
virtual Task Claimed(string Owner, bool IsPublic)
Called when node has been claimed by an owner.
virtual Task< bool > RemoveMessageAsync(MessageType Type)
Removes messages with empty event IDs from the node.
virtual Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
virtual Task< bool > RemoveErrorAsync(string EventId)
Removes error messages with a given event ID from the node.
virtual Task< bool > RemoveWarningAsync()
Removes warning messages with an empty event ID from the node.
virtual async Task LogMessageAsync(MessageType Type, string EventId, string Body)
Logs a message on the node.
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual bool IsReadable
If the node can be read.
bool HasChildren
If the source has any child sources.
virtual async Task NodeUpdated()
Persists changes to the node, and generates a node updated event.
virtual Task LogWarningAsync(string EventId, string Body)
Logs an warning message on the node.
NodeState State
Current overall state of the node.
virtual Task< bool > RemoveInformationAsync()
Removes warning messages with an empty event ID from the node.
abstract Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
virtual Task< bool > RemoveInformationAsync(string EventId)
Removes an informational message on the node.
virtual async Task< bool > MoveUpAsync(MeteringNode Child, RequestOrigin Caller)
Tries to move the child node up.
DateTime LastChanged
When the node was last updated.
virtual Task< KeyValuePair< string, object >[]> GetMetaData()
Gets meta-data about the node.
virtual Task LogInformationAsync(string Body)
Logs an informational message on the node.
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
virtual Task Removed()
Called when node has been removed from the registry.
virtual Task< bool > CanAddAsync(RequestOrigin Caller)
If the node can be added to by the caller.
virtual bool IsProvisioned
If node can be provisioned.
virtual bool HasCommands
If the node has registered commands or not.
virtual async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
Guid ObjectId
Object ID in persistence layer.
virtual Task< bool > RemoveWarningAsync(string EventId)
Removes warning messages with a given event ID from the node.
DateTime Created
When node was created.
async Task< T > GetAncestor< T >()
Tries to get an ancestor node of a given type, if one exists.
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
virtual Task< bool > CanEditAsync(RequestOrigin Caller)
If the node can be edited by the caller.
string SourceId
Optional ID of source containing node.
virtual Task Disowned()
Called when node has been disowned by its owner.
virtual bool IsPublic
If the node is public.
INode Parent
Parent Node, or null if a root node.
virtual async Task< bool > MoveDownAsync(MeteringNode Child, RequestOrigin Caller)
Tries to move the child node down.
MeteringNode()
Base class for all metering nodes.
virtual async Task< bool > RemoveMessageAsync(MessageType Type, string EventId)
Logs a message on the node.
static async Task< string > GetUniqueNodeId(string NodeId)
Gets a Node ID, based on NodeId that is not already available in the database.
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Task NewMomentaryValues(IEnumerable< Field > Values)
Reports newly measured values.
virtual MeteringNode[] SortChildrenAfterLoadLocked(List< MeteringNode > Children)
Method that allows the node to sort its children, after they have been loaded.
EventHandlerAsync OnUpdate
Event raised when node has been updated.
async Task< INode > GetParent()
Gets the parent of the node.
virtual async Task UpdateAsync()
Updates the node (in persisted storage).
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
abstract Task< bool > AcceptsParentAsync(INode Parent)
If the node accepts a presumptive parent, i.e. can be added to that parent (if that parent accepts th...
virtual string Owner
Who the owner of the node is. The empty string means the node has no owner.
virtual async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
virtual Task LogWarningAsync(string Body)
Logs an warning message on the node.
virtual async Task< IEnumerable< Message > > GetMessagesAsync(RequestOrigin Caller)
Gets messages logged on the node.
virtual MeteringNode[] CheckOrderLocked(List< MeteringNode > Children)
Checks the ordering of children.
virtual Task< bool > CanViewAsync(RequestOrigin Caller)
If the node is visible to the caller.
virtual async Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
async Task< Message[]> GetMessageArrayAsync(RequestOrigin Caller)
Gets messages logged on the node.
override bool Equals(object obj)
Determines whether the specified object is equal to the current object.
override string ToString()
string Partition
Optional partition in which the Node ID is unique.
virtual Task LogErrorAsync(string EventId, string Body)
Logs an error message on the node.
int SiblingOrdinal
Sibling ordinal, used to order siblings when ordered.
virtual Task LogMessageAsync(MessageType Type, string Body)
Logs a message on the node.
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
virtual async Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
override int GetHashCode()
Serves as the default hash function.
Guid ParentId
Object ID of parent node in persistence layer.
abstract Task< bool > AcceptsChildAsync(INode Child)
If the node accepts a presumptive child, i.e. can receive as a child (if that child accepts the node ...
virtual async Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
virtual Task LogInformationAsync(string EventId, string Body)
Logs an informational message on the node.
virtual Task< bool > CanDestroyAsync(RequestOrigin Caller)
If the node can be destroyed to by the caller.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static Task NewMomentaryValues(IThingReference Reference, IEnumerable< Field > Values)
Reports newly measured values.
const string SourceID
Source ID for the metering topology data source.
Tokens available in request.
static readonly RequestOrigin Empty
Empty request origin.
bool HasPrivilege(string Privilege)
If the origin has a given privilege.
Base class for all sensor data fields.
static string EmptyIfSame(string Id1, string Id2)
Returns Id1 if different, string.Empty if the same.
Node status changed event.
Contains a reference to a thing
Interface for observable classes implementing communication protocols.
Interface for actuator nodes.
Interface for nodes that are published through the concentrator interface.
Task< bool > MoveUpAsync(RequestOrigin Caller)
Tries to move the node up.
INode Parent
Parent Node, or null if a root node.
Task< bool > MoveDownAsync(RequestOrigin Caller)
Tries to move the node down.
Interface for sensor nodes.
Interface for thing references.
Base Interface for all metering nodes.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
TypeNameSerialization
How the type name should be serialized.
MessageType
Type of message.
NodeState
State of a node.