Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MeteringTopology.cs
1using System;
3using System.Threading.Tasks;
4using Waher.Events;
13
15{
21 public delegate Task NewMomentaryValuesHandler(IThingReference Reference, IEnumerable<Field> Values);
22
28 {
32 public const string SourceID = "MeteringTopology";
33
34 private static readonly Dictionary<string, MeteringNode> nodes = new Dictionary<string, MeteringNode>();
35 private static Root root = null;
36 private static MeteringTopology instance = null;
37
38 private DateTime lastChanged;
39
45 {
46 this.lastChanged = RuntimeSettings.Get(SourceID + ".LastChanged", DateTime.MinValue);
47 instance ??= this;
48 }
49
53 string IDataSource.SourceID => SourceID;
54
58 public IEnumerable<IDataSource> ChildSources => null;
59
63 public bool HasChildren => false;
64
68 public DateTime LastChanged
69 {
70 get => this.lastChanged;
71 internal set
72 {
73 if (this.lastChanged != value)
74 {
75 this.lastChanged = value;
76 Task _ = RuntimeSettings.SetAsync("MeteringTopology.LastChanged", value);
77 }
78 }
79 }
80
86 public Task<string> GetNameAsync(Language Language)
87 {
88 return Language.GetStringAsync(typeof(MeteringTopology), 13, "Metering Topology");
89 }
90
96 public async Task<INode> GetNodeAsync(IThingReference NodeRef)
97 {
98 return await GetNode(NodeRef);
99 }
100
106 public static Task<MeteringNode> GetNode(string NodeId)
107 {
108 return GetNode(new ThingReference(NodeId, SourceID));
109 }
110
116 public static async Task<MeteringNode> GetNode(IThingReference NodeRef)
117 {
118 if (NodeRef is null || NodeRef.SourceId != SourceID || !string.IsNullOrEmpty(NodeRef.Partition))
119 return null;
120
121 lock (nodes)
122 {
123 if (nodes.TryGetValue(NodeRef.NodeId, out MeteringNode Node))
124 return Node;
125 }
126
127 foreach (MeteringNode Node2 in await Database.Find<MeteringNode>(new FilterFieldEqualTo("NodeId", NodeRef.NodeId)))
128 {
129 lock (nodes)
130 {
131 if (nodes.TryGetValue(NodeRef.NodeId, out MeteringNode Node))
132 return Node;
133 else
134 {
135 nodes[NodeRef.NodeId] = Node2;
136 return Node2;
137 }
138 }
139 }
140
141 return null;
142 }
143
144 internal static MeteringNode RegisterNode(MeteringNode Node)
145 {
146 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
147 {
148 lock (nodes)
149 {
150 if (nodes.TryGetValue(Node.NodeId, out MeteringNode Node2))
151 return Node2;
152 else
153 {
154 nodes[Node.NodeId] = Node;
155 return Node;
156 }
157 }
158 }
159 else
160 return Node;
161 }
162
163 internal static MeteringNode RegisterNewNodeId(MeteringNode Node, string OldId)
164 {
165 if (Node.SourceId != SourceID || !string.IsNullOrEmpty(Node.Partition))
166 return Node;
167
168 lock (nodes)
169 {
170 if (!nodes.TryGetValue(OldId, out MeteringNode Node2) || Node2 != Node)
171 return Node;
172
173 if (nodes.TryGetValue(Node.NodeId, out Node2))
174 return Node2;
175
176 nodes.Remove(OldId);
177 nodes[Node.NodeId] = Node;
178
179 return Node;
180 }
181 }
182
183 internal static void UnregisterNode(MeteringNode Node)
184 {
185 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
186 {
187 lock (nodes)
188 {
189 if (nodes.TryGetValue(Node.NodeId, out MeteringNode Node2) && Node == Node2)
190 nodes.Remove(Node.NodeId);
191 }
192 }
193 }
194
200 public Task<bool> CanViewAsync(RequestOrigin Caller)
201 {
202 return Task.FromResult(Caller.HasPrivilege("Source." + SourceID + ".View"));
203 }
204
208 public IEnumerable<INode> RootNodes
209 {
210 get
211 {
212 if (root is null)
213 LoadRoot().Wait();
214
215 return new INode[] { root };
216 }
217 }
218
222 public static Root Root
223 {
224 get
225 {
226 if (root is null)
227 LoadRoot().Wait();
228
229 return root;
230 }
231 }
232
233 private static async Task LoadRoot()
234 {
235 Root Result = null;
236
237 foreach (MeteringNode Node in await Database.Find<MeteringNode>(new FilterFieldEqualTo("ParentId", Guid.Empty)))
238 {
239 if (Node is Root Root)
240 {
241 if (Result is null)
242 Result = Root;
243 else
244 await Database.Delete(Node);
245 }
246 }
247
248 if (Result is null)
249 {
250 Result = new Root()
251 {
252 NodeId = await (await Translator.GetDefaultLanguageAsync()).GetStringAsync(typeof(MeteringTopology), 14, "Root")
253 };
254
255 await Database.Insert(Result);
256
258 await NewEvent(new NodeAdded()
259 {
261 NodeType = Result.GetType().FullName,
262 Sniffable = Result is ICommunicationLayer,
263 DisplayName = await Result.GetTypeNameAsync(Language),
264 HasChildren = Result.HasChildren,
265 ChildrenOrdered = Result.ChildrenOrdered,
266 IsReadable = Result.IsReadable,
267 IsControllable = Result.IsControllable,
268 HasCommands = Result.HasCommands,
269 ParentId = string.Empty,
270 ParentPartition = string.Empty,
271 Updated = Result.Updated,
272 State = Result.State,
273 NodeId = Result.NodeId,
274 Partition = Result.Partition,
275 LogId = NodeAdded.EmptyIfSame(Result.LogId, Result.NodeId),
276 LocalId = NodeAdded.EmptyIfSame(Result.LocalId, Result.NodeId),
277 SourceId = Result.SourceId,
278 Timestamp = DateTime.UtcNow
279 });
280 }
281
282 lock (nodes)
283 {
284 nodes[Result.NodeId] = Result;
285 }
286
287 root = Result;
288 }
289
295 public static async Task<int> DeleteOldEvents(TimeSpan MaxAge)
296 {
297 if (MaxAge <= TimeSpan.Zero)
298 throw new ArgumentException("Age must be positive.", nameof(MaxAge));
299
300 DateTime Limit = DateTime.Now.Subtract(MaxAge);
301 int NrEvents = await Database.Delete<SourceEvent>(new FilterAnd(
302 new FilterFieldEqualTo("SourceId", SourceID),
303 new FilterFieldLesserOrEqualTo("Timestamp", Limit)));
304
305 if (NrEvents > 0)
306 {
307 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
308 {
309 new KeyValuePair<string, object>("Limit", Limit),
310 new KeyValuePair<string, object>("NrEvents", NrEvents)
311 };
312
313 if (NrEvents == 1)
314 Log.Informational("Deleting 1 metering topology event from the database.", SourceID, Tags);
315 else
316 Log.Informational("Deleting " + NrEvents.ToString() + " metering topology events from the database.", SourceID, Tags);
317 }
318
319 return NrEvents;
320 }
321
325 public event EventHandlerAsync<SourceEvent> OnEvent = null;
326
327 internal static async Task NewEvent(SourceEvent Event)
328 {
329 await Database.Insert(Event);
330 await (instance?.OnEvent?.Raise(instance, Event) ?? Task.CompletedTask);
331 }
332
338 public static Task NewMomentaryValues(IThingReference Reference, IEnumerable<Field> Values)
339 {
341 if (h is null)
342 return Task.CompletedTask;
343 else
344 return h(Reference, Values);
345 }
346
351
356 public static async Task<int> DeleteOrphans()
357 {
358 int Result = 0;
359
360 foreach (MeteringNode Node in await Database.Find<MeteringNode>())
361 {
362 if (Node.ParentId == Guid.Empty)
363 continue;
364
365 MeteringNode ParentNode = await Database.TryLoadObject<MeteringNode>(Node.ParentId);
366 if (ParentNode is null)
367 {
368 await Database.Delete(Node);
369 Result++;
370
371 lock (nodes)
372 {
373 nodes.Remove(Node.NodeId);
374 }
375 }
376 }
377
378 return Result;
379 }
380
381 }
382}
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 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 async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
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
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
Definition: Database.cs:1838
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.
This filter selects objects that have a named field lesser or equal to a given value.
Contains information about a language.
Definition: Language.cs:17
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Definition: Language.cs:209
Basic access point for runtime language localization.
Definition: Translator.cs:16
static async Task< Language > GetDefaultLanguageAsync()
Gets the default language.
Definition: Translator.cs:223
Static class managing persistent settings.
static string Get(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Base class for all metering nodes.
Definition: MeteringNode.cs:30
virtual bool IsControllable
If the node can be controlled.
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.
NodeState State
Current overall state 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 string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
virtual bool HasCommands
If the node has registered commands or not.
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
string SourceId
Optional ID of source containing node.
string Partition
Optional partition in which the Node ID is unique.
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Guid ParentId
Object ID of parent node in persistence layer.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
IEnumerable< IDataSource > ChildSources
Child sources. If no child sources are available, null is returned.
IEnumerable< INode > RootNodes
Root node references. If no root nodes are available, null is returned.
DateTime LastChanged
When the source was last updated.
static Task NewMomentaryValues(IThingReference Reference, IEnumerable< Field > Values)
Reports newly measured values.
async Task< INode > GetNodeAsync(IThingReference NodeRef)
Gets the node, given a reference to it.
static Task< MeteringNode > GetNode(string NodeId)
Gets a node from the Metering Topology
EventHandlerAsync< SourceEvent > OnEvent
Event raised when a data source event has been raised.
Task< bool > CanViewAsync(RequestOrigin Caller)
If the data source is visible to the caller.
static async Task< int > DeleteOrphans()
Deletes orphaned nodes in the metering topology source.
MeteringTopology()
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
bool HasChildren
If the source has any child sources.
const string SourceID
Source ID for the metering topology data source.
Task< string > GetNameAsync(Language Language)
Gets the name of data source.
static NewMomentaryValuesHandler OnNewMomentaryValues
Event raised when a node in the metering topology reports a new momentary value.
static async Task< MeteringNode > GetNode(IThingReference NodeRef)
Gets a node from the Metering Topology
Class for the root node of the Metering topology.
Definition: Root.cs:10
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
Definition: Root.cs:24
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
Abstract base class for all data source events.
Definition: SourceEvent.cs:13
Contains a reference to a thing
Interface for observable classes implementing communication protocols.
Interface for datasources that are published through the concentrator interface.
Definition: IDataSource.cs:14
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Interface for thing references.
string Partition
Optional partition in which the Node ID is unique.
string SourceId
Optional ID of source containing node.
Definition: ImplTypes.g.cs:58
delegate Task NewMomentaryValuesHandler(IThingReference Reference, IEnumerable< Field > Values)
Delegate for new momentary values event handlers.